## viewport.pkg
#
# Viewport widget, for panning over a child widget.
#
#
# Two ways of providing a viewport with scrollbars:
# widget_with_scrollbars:
#
src/lib/x-kit/widget/old/layout/widget-with-scrollbars.api# scrolled_widget:
#
src/lib/x-kit/widget/old/layout/scrolled-widget.api#
# TODO: XXX BUGGO FIXME
# Allow child window to vary per size preferences.
# Parameterize by child (granularity, specific scroll function)
# Compiled by:
#
src/lib/x-kit/widget/xkit-widget.sublib# Viewport widget, for panning over a child widget.
#
# TODO: XXX BUGGO FIXME
# Allow child window to vary per size preference.
# Parameterize by child (granularity, specific scroll function)
stipulate
include package threadkit; # threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg #
package g2d= geometry2d; # geometry2d is from
src/lib/std/2d/geometry2d.pkg #
package xc = xclient; # xclient is from
src/lib/x-kit/xclient/xclient.pkg #
package wg = widget; # widget is from
src/lib/x-kit/widget/old/basic/widget.pkg package wa = widget_attribute_old; # widget_attribute_old is from
src/lib/x-kit/widget/old/lib/widget-attribute-old.pkg package mr = xevent_mail_router; # xevent_mail_router is from
src/lib/x-kit/widget/old/basic/xevent-mail-router.pkgherein
package viewport
: (weak) Viewport # Viewport is from
src/lib/x-kit/widget/old/layout/viewport.api {
Plea_Mail
= REALIZE {
kidplug: xc::Kidplug,
window: xc::Window,
window_size: g2d::Size
}
| GET
| SET { horz: Null_Or( Int ),
vert: Null_Or( Int )
}
;
Geometry = { box: g2d::Box,
child_size: g2d::Size
};
Reply_Mail = GEOMETRY Geometry;
Viewport = VIEWPORT
{ child: wg::Widget,
configuration_change': Mailop( Geometry ),
#
plea_slot: Mailslot( Plea_Mail ),
reply_slot: Mailslot( Reply_Mail )
};
fun preferred_size { col_preference, row_preference }
=
{ wide => wg::preferred_length col_preference,
high => wg::preferred_length row_preference
};
fun preferred_size_box arg
=
g2d::box::make (g2d::point::zero, preferred_size arg);
fun view_size_preference (wide, high, child_size_preference)
=
size_preference
where
fun loose_preference v
=
wg::INT_PREFERENCE
{ start_at => 0,
step_by => 1,
#
min_steps => 1,
best_steps => v,
max_steps => NULL
};
fun size_preference ()
=
{ my { col_preference, row_preference }
=
child_size_preference ();
cols = case wide NULL => wg::preferred_length col_preference; THE cols => cols; esac;
rows = case high NULL => wg::preferred_length row_preference; THE rows => rows; esac;
{ col_preference => loose_preference cols,
row_preference => loose_preference rows
};
};
end;
# Adjust view's box:
#
fun new_origin ( { horz, vert }, { col, row, wide, high } )
=
{ col = case horz THE h => h; _ => col; esac;
row = case vert THE v => v; _ => row; esac;
{ col, row, wide, high };
};
# Handle child's resize plea: UNIMPLEMENTED
#
fun do_resize_plea g
=
g; # XXX BUGGO FIXME
fun filter (in_mailop, outslot)
=
main ()
where
timeout' = timeout_in' 0.03;
#
filter_count = 10;
fun opt_give (i, v)
=
if (i != filter_count) put_in_mailslot (outslot, v); fi;
fun main ()
=
case (block_until_mailop_fires in_mailop)
#
v as SET _ => { put_in_mailslot (outslot, v); counter (filter_count, v); };
GET => { put_in_mailslot (outslot, GET); main (); };
_ => main ();
esac
also
fun counter (0, v)
=>
{ put_in_mailslot (outslot, v);
#
counter (filter_count, v);
};
counter (arg as (i, v))
=>
do_one_mailop [
#
timeout'
==>
{. opt_give arg;
main ();
},
in_mailop
==>
(\\ mailop
=
case mailop
#
v' as SET _ => counter (i - 1, v');
GET => { opt_give arg; put_in_mailslot (outslot, GET); main (); };
_ => { opt_give arg; main (); };
esac
)
];
end;
end;
fun new_geometry (wide, high,{ box=>{ col=>x, row=>y, ... }: g2d::Box, child_size } )
=
{ my { wide=>cw, high=>ch }
=
child_size;
fun normal (x, w, cw)
=
if (x < 0) 0;
elif (x+w <= cw) x;
else int::max (0, cw-w);
fi;
x' = normal (x, wide, cw);
y' = normal (y, high, ch);
box' = { wide, high, col=>x', row=>y'};
box';
};
fun make_viewport' (wide, high, child)
=
{ root_window = wg::root_window_of child;
screen = wg::screen_of root_window;
plea_slot = make_mailslot ();
reply_slot = make_mailslot ();
mailop_slot = make_mailslot ();
fun realize_view
{ window, window_size, kidplug as xc::KIDPLUG { to_mom=>myco, ... }}
(geometry: Geometry)
=
{ my_window = window;
#
filter_slot = make_mailslot ();
(xc::make_widget_cable ())
->
{ kidplug => my_kidplug,
momplug => my_momplug
};
my xc::KIDPLUG { from_other', ... }
=
xc::ignore_mouse_and_keyboard my_kidplug;
geometry.box
->
r as { col => x,
row => y,
...
}: g2d::Box;
child_box = preferred_size_box (wg::size_preference_of child);
child_window = wg::make_child_window (my_window, child_box, wg::args_of child);
(xc::make_widget_cable ())
->
{ kidplug => ckidplug,
momplug => cmomplug as xc::MOMPLUG { from_kid'=>childco, ... }
};
fun do_mom (xc::ETC_RESIZE ({ wide, high, ... } ), geometry)
=>
{ box => new_geometry (wide, high, geometry),
child_size => geometry.child_size
};
do_mom (_, geometry)
=>
geometry;
end;
fun handle_co (xc::REQ_RESIZE, { box, child_size })
=>
{ box,
#
child_size
=>
g2d::box::size
(preferred_size_box
(wg::size_preference_of child)
)
};
handle_co (xc::REQ_DESTRUCTION, g)
=>
{ xc::destroy_window child_window;
g;
};
end;
fun do_plea (SET arg,{ box, child_size } : Geometry)
=>
{ (new_origin (arg, box))
->
r as { col=>x, row=>y, ... }: g2d::Box;
if (r != box)
xc::move_window child_window ({ col=> -x, row=> -y });
fi;
{ box => r, child_size };
};
do_plea (GET, geometry)
=>
{ put_in_mailslot (reply_slot, GEOMETRY geometry);
geometry;
};
do_plea (_, geometry)
=>
geometry;
end;
fun loop (geometry as { child_size, box } )
=
{ fun do_mom2 mail
=
{ my geometry as { box=>box', ... }
=
do_mom (xc::get_contents_of_envelope mail, geometry);
my origin' as { col=>x, row=>y }
=
g2d::box::upperleft box';
if (origin' != g2d::box::upperleft box)
#
xc::move_window child_window ({ col=> -x, row=> -y } );
#
changed { box=>box', child_size };
#
else
if (g2d::box::size box != g2d::box::size box')
#
changed { box=>box', child_size };
else
loop geometry;
fi;
fi;
};
fun do_co2 mailop # This function was added in SML/NJ 110.59
=
{ my geometry' as { box=>box', child_size => child_size' }
=
handle_co (mailop, geometry);
my origin' as { col, row }
=
g2d::box::upperleft box';
if (child_size == child_size')
#
loop geometry;
else
child_size' -> { wide, high };
child_box
=
{ col => -col,
row => -row,
#
wide,
high
};
xc::move_and_resize_window
#
child_window
child_box;
changed geometry';
fi;
};
do_one_mailop [
from_other' ==> do_mom2,
childco ==> do_co2,
take_from_mailslot' filter_slot ==> (\\ arg = loop (do_plea (arg, geometry)))
];
}
also
fun changed geometry
=
do_one_mailop [
put_in_mailslot' (mailop_slot, geometry)
==>
{. loop geometry; },
from_other'
==>
(\\ mail = changed (do_mom (xc::get_contents_of_envelope mail, geometry))),
childco
==>
(\\ arg = changed (handle_co (arg, geometry))),
take_from_mailslot' filter_slot
==>
(\\ arg = changed (do_plea (arg, geometry)))
];
mr::route_pair (kidplug, my_momplug, cmomplug);
xc::move_window child_window ({ col=> -x, row=> -y } );
wg::realize_widget child
{
kidplug => ckidplug,
window => child_window,
window_size => g2d::box::size child_box
};
make_thread "viewport" {.
#
filter (take_from_mailslot' plea_slot, filter_slot);
};
xc::show_window child_window;
changed { box => g2d::box::make ({ col=>x, row=>y }, window_size),
child_size => g2d::box::size child_box
};
};
fun init_geometry ()
=
{ (preferred_size (wg::size_preference_of child))
->
{ wide => cwid, high => cht };
wide = case wide NULL => cwid; THE w => w; esac;
high = case high NULL => cht; THE h => h; esac;
{ box => { col=>0, row=>0, wide, high },
child_size => { wide=>cwid, high=>cht }
};
};
fun init_loop (geometry: Geometry)
=
case (take_from_mailslot plea_slot)
#
REALIZE arg => realize_view arg geometry;
GET => { put_in_mailslot (reply_slot, GEOMETRY geometry); init_loop geometry; };
SET arg => init_loop (update arg);
esac
where
fun update ( { horz, vert } )
=
{ geometry.box -> { col=>x, row=>y, wide, high };
#
x' = case horz THE h => h; _ => x; esac;
y' = case vert THE v => v; _ => y; esac;
(preferred_size (wg::size_preference_of child))
->
{ wide=>cwid, high=>cht };
{ box => { col=>x', row=>y', wide, high },
child_size => { wide=>cwid, high=>cht }
};
};
end;
make_thread "viewport init" {.
#
init_loop (init_geometry ());
};
VIEWPORT
{
plea_slot,
reply_slot,
configuration_change' => take_from_mailslot' mailop_slot,
child => wg::make_widget
{
root_window,
args => wg::args_fn child,
realize_widget => \\ arg = put_in_mailslot (plea_slot, REALIZE arg),
size_preference_thunk_of
=>
view_size_preference
( wide,
high,
wg::size_preference_thunk_of child
)
}
};
};
fun make_viewport child
=
make_viewport' (NULL, NULL, child);
attributes
=
[ (wa::width, wa::INT, wa::NO_VAL),
(wa::height, wa::INT, wa::NO_VAL),
(wa::background, wa::COLOR, wa::STRING_VAL "white")
];
fun viewport (root_window, view, args) child
=
{ attributes = wg::find_attribute (wg::attributes (view, attributes, args));
#
wide = wa::get_int_opt (attributes wa::width);
high = wa::get_int_opt (attributes wa::height);
color = wa::get_color (attributes wa::background);
make_viewport' (wide, high, child);
};
fun as_widget (VIEWPORT { child, ... } )
=
child;
fun set_horizontal_position (VIEWPORT { plea_slot, reply_slot, ... } ) arg
=
put_in_mailslot (plea_slot, SET { horz=>THE arg, vert=>NULL } );
fun set_vertical_position (VIEWPORT { plea_slot, reply_slot, ... } ) arg
=
put_in_mailslot (plea_slot, SET { vert=>THE arg, horz=>NULL } );
fun set_origin (VIEWPORT { plea_slot, reply_slot, ... } ) ({ col, row } )
=
put_in_mailslot (plea_slot, SET { vert=>THE row, horz=>THE col } );
fun get_geometry (VIEWPORT { plea_slot, reply_slot, ... } )
=
{ put_in_mailslot (plea_slot, GET);
case (take_from_mailslot reply_slot) GEOMETRY g => g; esac;
};
fun get_viewport_configuration_change_mailop (VIEWPORT { configuration_change', ... } )
=
configuration_change';
}; # Viewport
end;