# popupframe.pkg
#
# See also:
#
src/lib/x-kit/widget/leaf/frame.pkg#
src/lib/x-kit/widget/leaf/diamondbutton.pkg#
src/lib/x-kit/widget/leaf/roundbutton.pkg# Compiled by:
#
src/lib/x-kit/widget/xkit-widget.sublib# This package gets used in:
#
#
stipulate
include package threadkit; # threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg include package geometry2d; # geometry2d is from
src/lib/std/2d/geometry2d.pkg #
package evt = gui_event_types; # gui_event_types is from
src/lib/x-kit/widget/gui/gui-event-types.pkg package g2p = gadget_to_pixmap; # gadget_to_pixmap is from
src/lib/x-kit/widget/theme/gadget-to-pixmap.pkg package gd = gui_displaylist; # gui_displaylist is from
src/lib/x-kit/widget/theme/gui-displaylist.pkg package gt = guiboss_types; # guiboss_types is from
src/lib/x-kit/widget/gui/guiboss-types.pkg package wt = widget_theme; # widget_theme is from
src/lib/x-kit/widget/theme/widget/widget-theme.pkg package r8 = rgb8; # rgb8 is from
src/lib/x-kit/xclient/src/color/rgb8.pkg package r64 = rgb; # rgb is from
src/lib/x-kit/xclient/src/color/rgb.pkg package wi = widget_imp; # widget_imp is from
src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg package g2d = geometry2d; # geometry2d is from
src/lib/std/2d/geometry2d.pkg package g2j = geometry2d_junk; # geometry2d_junk is from
src/lib/std/2d/geometry2d-junk.pkg package mtx = rw_matrix; # rw_matrix is from
src/lib/std/src/rw-matrix.pkg package pp = standard_prettyprinter; # standard_prettyprinter is from
src/lib/prettyprint/big/src/standard-prettyprinter.pkg package gtg = guiboss_to_guishim; # guiboss_to_guishim is from
src/lib/x-kit/widget/theme/guiboss-to-guishim.pkg nb = log::note_on_stderr; # log is from
src/lib/std/src/log.pkgherein
package popupframe
: Popupframe # Popupframe is from
src/lib/x-kit/widget/leaf/popupframe.api {
App_To_Popupframe
=
{ id: Id
};
Redraw_Fn_Arg
=
REDRAW_FN_ARG
{
id: Id, # Unique Id for widget.
doc: String, # Human-readable description of this widget, for debug and inspection.
frame_number: Int, # 1,2,3,... Purely for convenience of widget, guiboss-imp makes no use of this.
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Window rectangle in which to draw.
popup_nesting_depth: Int, # 0 for gadgets on basewindow, 1 for gadgets on popup on basewindow, 2 for gadgets on popup on popup, etc.
#
duration_in_seconds: Float, # If state has changed look-imp should call note_changed_gadget_foreground() before this time is up. Also useful for motionblur.
widget_to_guiboss: gt::Widget_To_Guiboss,
gadget_mode: gt::Gadget_Mode,
#
frame_width_in_pixels: Int,
#
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue, # Used to call 'pass_*' methods in other imps.
palette: wt::Gadget_Palette,
#
default_redraw_fn: Redraw_Fn
}
withtype
Redraw_Fn
=
Redraw_Fn_Arg
->
{ displaylist: gd::Gui_Displaylist,
point_in_gadget: Null_Or(g2d::Point -> Bool) #
}
;
Mouse_Click_Fn_Arg
=
MOUSE_CLICK_FN_ARG # Needs to be a sumtype because of recursive reference in default_mouse_click_fn.
{ id: Id, # Unique Id for widget.
doc: String, # Human-readable description of this widget, for debug and inspection.
event: gt::Mousebutton_Event, # MOUSEBUTTON_PRESS or MOUSEBUTTON_RELEASE.
button: evt::Mousebutton, # Which mousebutton was pressed/released.
point: g2d::Point, # Where the mouse was.
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
mousebuttons_state: evt::Mousebuttons_State, # State of mouse buttons as a bool record.
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue, # Used to call 'pass_*' methods in other imps.
#
default_mouse_click_fn: Mouse_Click_Fn,
#
needs_redraw_gadget_request: Void -> Void # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
}
withtype
Mouse_Click_Fn = Mouse_Click_Fn_Arg -> Void;
Mouse_Drag_Fn_Arg
=
MOUSE_DRAG_FN_ARG
{
id: Id, # Unique Id for widget.
doc: String, # Human-readable description of this widget, for debug and inspection.
event_point: g2d::Point,
start_point: g2d::Point,
last_point: g2d::Point,
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
phase: gt::Drag_Phase,
button: evt::Mousebutton,
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
mousebuttons_state: evt::Mousebuttons_State, # State of mouse buttons as a bool record.
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue, # Used to call 'pass_*' methods in other imps.
#
default_mouse_drag_fn: Mouse_Drag_Fn,
#
needs_redraw_gadget_request: Void -> Void # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
}
withtype
Mouse_Drag_Fn = Mouse_Drag_Fn_Arg -> Void;
Mouse_Transit_Fn_Arg # Note that buttons are always all up in a mouse-transit event -- otherwise it is a mouse-drag event.
=
MOUSE_TRANSIT_FN_ARG
{
id: Id, # Unique Id for widget.
doc: String, # Human-readable description of this widget, for debug and inspection.
event_point: g2d::Point,
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
transit: gt::Gadget_Transit, # Mouse is entering (CAME) or leaving (LEFT) widget, or moving (MOVE) across it.
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue, # Used to call 'pass_*' methods in other imps.
#
default_mouse_transit_fn: Mouse_Transit_Fn,
#
needs_redraw_gadget_request: Void -> Void # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
}
withtype
Mouse_Transit_Fn = Mouse_Transit_Fn_Arg -> Void;
Key_Event_Fn_Arg
=
KEY_EVENT_FN_ARG
{
id: Id, # Unique Id for widget.
doc: String,
keystroke: gt::Keystroke_Info, # Keystring etc for event.
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
widget_to_guiboss: gt::Widget_To_Guiboss,
guiboss_to_widget: gt::Guiboss_To_Widget, # Used by textpane.pkg keystroke-macro stuff to synthesize fake keystroke events to widget.
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue, # Used to call 'pass_*' methods in other imps.
#
default_key_event_fn: Key_Event_Fn,
#
needs_redraw_gadget_request: Void -> Void # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
}
withtype
Key_Event_Fn = Key_Event_Fn_Arg -> Void;
Dragmode
#
= NO_DRAG
| DRAG_POPUP
| RESIZE_POPUP Ref(Null_Or(g2d::Size))
;
fun dragmode_to_string (NO_DRAG ) => "NO_DRAG";
dragmode_to_string (DRAG_POPUP ) => "DRAG_POPUP";
dragmode_to_string (RESIZE_POPUP s) => sprintf "RESIZE_POPUP %s" case *s THE s => (g2j::size_to_string s); NULL => "NULL"; esac;
end;
Option = FRAME_WIDTH_IN_PIXELS Int
#
| TEXT String
# Text label to draw inside button. Default is "".
| FONT List(String)
# Font to use for text label, e.g. "-*-courier-bold-r-*-*-20-*-*-*-*-*-*-*". We'll use the first font in list which is found on X server, else "9x15" (which X guarantees to have).
#
| ID Id
| DOC String
#
| REDRAW_FN Redraw_Fn
# Application-specific handler for widget redraw.
| MOUSE_CLICK_FN Mouse_Click_Fn
# Application-specific handler for mousebutton clicks.
| MOUSE_DRAG_FN Mouse_Drag_Fn
# Application-specific handler for mouse drags.
| MOUSE_TRANSIT_FN Mouse_Transit_Fn
# Application-specific handler for mouse crossings.
| KEY_EVENT_FN Key_Event_Fn
# Application-specific handler for keyboard input.
#
| PORTWATCHER (Null_Or(App_To_Popupframe) -> Void)
# Widget's app port will be sent to these fns at widget startup.
| SITEWATCHER (Null_Or((Id,g2d::Box)) -> Void)
# Widget's site in window coordinates will be sent to these fns each time it changes.
; # To help prevent deadlock, watcher fns should be fast and nonblocking, typically just setting a var or entering something into a mailqueue.
fun process_options
( options: List(Option),
#
{ text,
font,
#
frame_width_in_pixels,
#
widget_id,
widget_doc,
#
redraw_fn,
mouse_click_fn,
mouse_drag_fn,
mouse_transit_fn,
key_event_fn,
#
widget_options,
#
portwatchers,
sitewatchers
}
)
=
{ my_text = REF text;
my_font = REF font;
my_frame_width_in_pixels= REF frame_width_in_pixels;
#
my_widget_id = REF widget_id;
my_widget_doc = REF widget_doc;
#
my_redraw_fn = REF redraw_fn;
my_mouse_click_fn = REF mouse_click_fn;
my_mouse_drag_fn = REF mouse_drag_fn;
my_mouse_transit_fn = REF mouse_transit_fn;
my_key_event_fn = REF key_event_fn;
#
my_widget_options = REF widget_options;
#
my_portwatchers = REF portwatchers;
my_sitewatchers = REF sitewatchers;
#
apply do_option options
where
fun do_option (TEXT t) => my_text := t;
do_option (FONT t) => my_font := t;
#
do_option (FRAME_WIDTH_IN_PIXELS i) => my_frame_width_in_pixels := i;
#
do_option (ID i) => my_widget_id := THE i;
do_option (DOC d) => my_widget_doc := d;
#
do_option (REDRAW_FN f) => my_redraw_fn := f;
do_option (MOUSE_CLICK_FN f) => my_mouse_click_fn := f;
do_option (MOUSE_DRAG_FN f) => my_mouse_drag_fn := f;
do_option (MOUSE_TRANSIT_FN f) => my_mouse_transit_fn := THE f;
do_option (KEY_EVENT_FN f) => my_key_event_fn := THE f;
#
do_option (PORTWATCHER c) => my_portwatchers := c ! *my_portwatchers;
do_option (SITEWATCHER c) => my_sitewatchers := c ! *my_sitewatchers;
end;
end;
{ text => *my_text,
font => *my_font,
#
frame_width_in_pixels => *my_frame_width_in_pixels,
#
widget_id => *my_widget_id,
widget_doc => *my_widget_doc,
#
redraw_fn => *my_redraw_fn,
mouse_click_fn => *my_mouse_click_fn,
mouse_drag_fn => *my_mouse_drag_fn,
mouse_transit_fn => *my_mouse_transit_fn,
key_event_fn => *my_key_event_fn,
#
widget_options => *my_widget_options,
#
portwatchers => *my_portwatchers,
sitewatchers => *my_sitewatchers
};
};
offset = 1;
fun with (options: List(Option)) # PUBLIC. The point of the 'with' name is that GUI coders can write 'popupframe::with { this => that, foo => bar, ... }.'
=
{
textref = REF ""; # We need this little REF hack
# because default_redraw_fn is an input to process_options() but 'text'
# is an output from process_options(), so something has to give a bit.
fontref = REF [ ]; # Same story.
dragmode = REF NO_DRAG;
fun make_upper_right_box (site: g2d::Box)
=
{ site -> { row, col, high, wide };
#
{ row,
col => col + wide - 9,
high => 9,
wide => 9
};
};
fun make_lower_right_box (site: g2d::Box)
=
{ site -> { row, col, high, wide };
#
{ row => row + high - 9,
col => col + wide - 9,
high => 9,
wide => 9
};
};
fun default_redraw_fn (REDRAW_FN_ARG a)
=
{ frame_width_in_pixels = a.frame_width_in_pixels;
palette = a.palette;
site = a.site;
theme = a.theme;
box0 = site;
box1 = g2d::box::make_nested_box (box0, 3);
box2 = g2d::box::make_nested_box (box1, 3);
box3 = g2d::box::make_nested_box (box2, 3);
stipulate
box0 -> { row, col, high, wide };
herein
upper_right_box = make_upper_right_box box0;
lower_right_box = make_lower_right_box box0;
upper_right_corner
=
[ gd::COLOR
(
r64::white,
[ gd::FILLED_BOXES [ upper_right_box ] ]
),
gd::COLOR
(
r64::black,
#
[ gd::LINE_THICKNESS
(
0,
[ gd::LINES [ ( { row, col => col + wide - 1 }, # Draw 1-pixel black line on top of upper_right_box.
{ row, col => col + wide - 9 }
),
( { row, col => col + wide - 1 }, # Draw 1-pixel black line on right of upper_right_box.
{ row => row + 8, col => col + wide - 1 }
),
( { row, col => col + wide - 9 }, # Draw 1-pixel black line on left of upper_right_box.
{ row => row + 8, col => col + wide - 9 }
),
( { row => row + 9, col => col + wide - 1 }, # Draw 1-pixel black line on bottom of upper_right_box.
{ row => row + 9, col => col + wide - 9 }
),
( { row => row + 9, col => col + wide - 9 }, # Draw 1-pixel black line from lower-left to upper-right of upper_right_box.
{ row => row, col => col + wide - 1 }
),
( { row => row + 8, col => col + wide - 9 }, # Make it double thick by adding one above/left.
{ row => row + 0, col => col + wide - 2 } #
),
( { row => row + 9, col => col + wide - 8 }, # Make it triple thick by adding one below/right.
{ row => row + 1, col => col + wide - 1 } #
),
( { row => row + 9, col => col + wide - 1 }, # Draw 1-pixel black line from lower-right to upper-left of upper_right_box.
{ row => row, col => col + wide - 9 }
),
( { row => row + 9, col => col + wide - 2 }, # Make it double thick by adding one below/left.
{ row => row + 1, col => col + wide - 9 }
),
( { row => row + 8, col => col + wide - 1 }, # Make it triple thick by adding one above/right.
{ row => row, col => col + wide - 8 }
)
]
]
)
]
)
];
lower_right_corner
=
[ gd::COLOR
(
r64::white,
[ gd::FILLED_BOXES [ lower_right_box ] ]
),
gd::COLOR
(
r64::black,
#
[ gd::LINE_THICKNESS
(
0,
[ gd::LINES [ ( { row => row + high - 9, col => col + wide - 1 }, # Draw 1-pixel black line on top of lower_right_box.
{ row => row + high - 9, col => col + wide - 9 }
),
( { row => row + high - 1, col => col + wide - 1 }, # Draw 1-pixel black line on right of lower_right_box.
{ row => row + high - 9, col => col + wide - 1 }
),
( { row => row + high - 1, col => col + wide - 9 }, # Draw 1-pixel black line on left of lower_right_box.
{ row => row + high - 9, col => col + wide - 9 }
),
( { row => row + high - 1, col => col + wide - 1 }, # Draw 1-pixel black line on bottom of lower_right_box.
{ row => row + high - 1, col => col + wide - 9 }
)
]
]
)
]
)
];
end;
displaylist #
= # NB: We do NOT want to draw over the inner rectangle reserved
[
gd::COLOR # for the widgets within the frame.
(
r64::black,
[ gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a { a => box0, b => box1 }),
gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a { a => box2, b => box3 })
]
),
gd::COLOR # for the widgets within the frame.
(
r64::white,
[ gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a { a => box1, b => box2 })
]
)
]
@
upper_right_corner
@
lower_right_corner
;
fun point_in_gadget (point: g2d::Point) # A fn which will return TRUE iff the point is on the 3d frame itself, not the surround -- much less the inner widgets.
=
( (g2d::box::point_in_box (point, box0))) and
(not (g2d::box::point_in_box (point, box3)));
point_in_gadget = THE point_in_gadget;
{ displaylist, point_in_gadget };
};
fun default_mouse_click_fn (MOUSE_CLICK_FN_ARG a)
=
{ a -> { id: Id, # Unique Id for widget.
doc: String,
event: gt::Mousebutton_Event, # MOUSEBUTTON_PRESS or MOUSEBUTTON_RELEASE.
button: evt::Mousebutton, # Which mousebutton was pressed/released.
point: g2d::Point, # Where the mouse was.
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
mousebuttons_state: evt::Mousebuttons_State, # State of mouse buttons as a bool record.
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue, # Used to call 'pass_*' methods in other imps.
#
default_mouse_click_fn: Mouse_Click_Fn,
#
needs_redraw_gadget_request: Void -> Void # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
};
upper_right_box = make_upper_right_box site;
if (g2d::box::point_in_box (point, upper_right_box))
#
widget_to_guiboss.g.kill_popup ();
fi;
();
};
fun default_mouse_drag_fn (MOUSE_DRAG_FN_ARG a)
=
{ a -> { id: Id, # Unique Id for widget.
doc: String,
event_point: g2d::Point,
start_point: g2d::Point,
last_point: g2d::Point,
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
phase: gt::Drag_Phase,
button: evt::Mousebutton,
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
mousebuttons_state: evt::Mousebuttons_State, # State of mouse buttons as a bool record.
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue, # Used to call 'pass_*' methods in other imps.
#
default_mouse_drag_fn: Mouse_Drag_Fn,
#
needs_redraw_gadget_request: Void -> Void # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
};
set_guipane_upperleft = widget_to_guiboss.g.set_guipane_upperleft;
pass_guipane_upperleft = widget_to_guiboss.g.pass_guipane_upperleft;
set_guipane_size = widget_to_guiboss.g.set_guipane_size;
pass_guipane_size = widget_to_guiboss.g.pass_guipane_size;
lower_right_box = make_lower_right_box site;
case phase
#
gt::OPEN => { dragmode := if (g2d::box::point_in_box (event_point, lower_right_box))
#
initial_size = REF NULL;
pass_guipane_size id to {. initial_size := THE #size; };
RESIZE_POPUP initial_size;
else
DRAG_POPUP;
fi;
};
gt::DONE => {
dragmode := NO_DRAG;
};
gt::DRAG => { case *dragmode
#
RESIZE_POPUP initial_size
=>
case *initial_size
#
THE { high, wide }
=>
{ delta = event_point - start_point;
#
new_guipane_size
=
{ high => high + delta.row,
wide => wide + delta.col
};
set_guipane_size (id, new_guipane_size);
};
NULL => (); # We haven't yet gotten our response to the above pass_guipane_size call, so don't do anything.
esac;
DRAG_POPUP
=>
pass_guipane_upperleft id to do_drag
where
fun do_drag (old_guipane_upperleft: g2d::Point)
=
{ delta = event_point - last_point;
#
new_guipane_upperleft
=
old_guipane_upperleft + delta;
set_guipane_upperleft (id, new_guipane_upperleft);
};
end;
NO_DRAG => (); # Shouldn't happen; possibly we should log something here.
esac;
};
esac;
();
};
(process_options
(
options,
#
{ text => *textref,
font => *fontref,
#
frame_width_in_pixels => 9, #
#
widget_id => NULL,
widget_doc => "<popupframe>",
#
redraw_fn => default_redraw_fn,
mouse_click_fn => default_mouse_click_fn,
mouse_drag_fn => default_mouse_drag_fn,
mouse_transit_fn => NULL,
key_event_fn => NULL,
#
widget_options => [],
#
portwatchers => [],
sitewatchers => []
}
) )
->
{ # These values are globally visible to the subsequenc fns, which can lock them in as needed.
text,
font,
#
frame_width_in_pixels,
#
widget_id,
widget_doc,
#
redraw_fn,
mouse_click_fn,
mouse_drag_fn,
mouse_transit_fn,
key_event_fn,
#
widget_options,
#
portwatchers,
sitewatchers
};
textref := text;
fontref := font;
#######################################
# Top of per-imp state variable section
#
widget_to_guiboss__global
=
REF (NULL: Null_Or((gt::Widget_To_Guiboss, Id)));
fun note_changed_gadget_activity (is_active: Bool)
=
case (*widget_to_guiboss__global)
#
THE (widget_to_guiboss, id) => widget_to_guiboss.g.note_changed_gadget_activity { id, is_active };
NULL => ();
esac;
fun needs_redraw_gadget_request ()
=
case (*widget_to_guiboss__global)
#
THE (widget_to_guiboss, id) => widget_to_guiboss.g.needs_redraw_gadget_request(id);
NULL => ();
esac;
last_known_site
=
REF ( { col => -1, wide => -1,
row => -1, high => -1
}: g2d::Box
);
exception SAVED_STATE { last_known_site: g2d::Box # Here we're doing the usual hack of using Exception as an extensible datatype -- nothing to do with actually raising or trapping exceptions.
};
fun note_site (id: Id, site: g2d::Box)
=
if(*last_known_site != site)
last_known_site := site;
#
apply tell_watcher sitewatchers
where
fun tell_watcher sitewatcher
=
sitewatcher (THE (id,site));
end;
fi;
#
# End of state variable section
###############################
#####################
# Top of port section
#
# Here we implement our App_To_Popupframe port:
#
# End of port section
#####################
###############################
# Top of widget hook fn section
#
# These fns get called by widget_imp logic, ultimately # widget_imp is from
src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg # in response to user mouseclicks and keypresses etc:
fun startup_fn
{
id: Id, # Unique Id for widget.
doc: String,
widget_to_guiboss: gt::Widget_To_Guiboss,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue
}
=
{ widget_to_guiboss__global
:=
THE (widget_to_guiboss, id);
app_to_popupframe
=
{ id
}
: App_To_Popupframe
;
apply tell_watcher portwatchers # We do this here rather than (say) above this fn because we don't want the port in circulation until we're running.
where
fun tell_watcher portwatcher
=
portwatcher (THE app_to_popupframe);
end;
();
};
fun shutdown_fn () # Return to widget_imp an exception packaging up our state; this will be returned to guiboss_imp, saved in the
= # Paused_Gui tree, and passed to our startup_fn when/if gui is restarted. This exception will never be raised;
{ apply tell_watcher portwatchers #
where
fun tell_watcher portwatcher
=
portwatcher NULL;
end;
apply tell_watcher sitewatchers
where
fun tell_watcher sitewatcher
=
sitewatcher NULL;
end;
};
fun initialize_gadget_fn
{
id: Id, # Unique Id for widget.
doc: String,
site: g2d::Box, # Window rectangle in which to draw.
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
pass_font: List(String) -> Replyqueue
-> (evt::Font -> Void) -> Void, # Nonblocking version of next, for use in imps.
get_font: List(String) -> evt::Font, # Accepts a list of font names which are tried in order.
make_rw_pixmap: g2d::Size -> g2p::Gadget_To_Rw_Pixmap,
#
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue # Used to call 'pass_*' methods in other imps.
}
=
{ note_site (id,site);
#
();
};
fun redraw_request_fn_wrapper
{
id: Id, # Unique Id for widget.
doc: String,
frame_number: Int, # 1,2,3,... Purely for convenience of widget-imp, guiboss-imp makes no use of this.
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Window rectangle in which to draw.
popup_nesting_depth: Int, # 0 for gadgets on basewindow, 1 for gadgets on popup on basewindow, 2 for gadgets on popup on popup, etc.
#
duration_in_seconds: Float, # If state has changed widget-imp should call redraw_gadget() before this time is up. Also useful for motionblur.
widget_to_guiboss: gt::Widget_To_Guiboss,
gadget_mode: gt::Gadget_Mode,
#
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void,
to: Replyqueue # Used to call 'pass_*' methods in other imps.
}
=
{ note_site (id,site);
#
(*theme.current_gadget_colors { gadget_is_on => FALSE,
gadget_mode,
popup_nesting_depth,
#
body_color => NULL,
body_color_when_on => NULL,
body_color_with_mousefocus => NULL,
body_color_when_on_with_mousefocus => NULL
}
)
->
(palette: wt::Gadget_Palette);
redraw_fn_arg
=
REDRAW_FN_ARG
{ id,
doc,
frame_number,
frame_indent_hint,
site,
popup_nesting_depth,
duration_in_seconds,
widget_to_guiboss,
gadget_mode,
frame_width_in_pixels,
theme,
do,
to,
palette,
#
default_redraw_fn
};
(redraw_fn redraw_fn_arg)
->
{ displaylist, point_in_gadget };
widget_to_guiboss.g.redraw_gadget { id, site, displaylist, point_in_gadget };
};
fun mouse_click_fn_wrapper # This a callback we hand to
src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg {
id: Id, # Unique Id for widget.
doc: String,
event: gt::Mousebutton_Event, # MOUSEBUTTON_PRESS or MOUSEBUTTON_RELEASE.
button: evt::Mousebutton,
point: g2d::Point,
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
mousebuttons_state: evt::Mousebuttons_State, # State of mouse buttons as a bool record.
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue # Used to call 'pass_*' methods in other imps.
}
=
{ note_site (id,site);
#
mouse_click_fn_arg
=
MOUSE_CLICK_FN_ARG
{
id,
doc,
event,
button,
point,
widget_layout_hint,
frame_indent_hint,
site,
modifier_keys_state,
mousebuttons_state,
widget_to_guiboss,
theme,
do,
to,
#
default_mouse_click_fn,
#
needs_redraw_gadget_request
};
mouse_click_fn mouse_click_fn_arg;
};
fun mouse_drag_fn_wrapper # This a callback we hand to
src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg (
{ id: Id, # Unique Id for widget.
doc: String,
event_point: g2d::Point,
start_point: g2d::Point,
last_point: g2d::Point,
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
phase: gt::Drag_Phase,
button: evt::Mousebutton,
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
mousebuttons_state: evt::Mousebuttons_State, # State of mouse buttons as a bool record.
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue # Used to call 'pass_*' methods in other imps.
}
)
=
{ note_site (id,site);
#
mouse_drag_fn_arg
=
MOUSE_DRAG_FN_ARG
{
id,
doc,
event_point,
start_point,
last_point,
widget_layout_hint,
frame_indent_hint,
site,
phase,
button,
modifier_keys_state,
mousebuttons_state,
widget_to_guiboss,
theme,
do,
to,
#
default_mouse_drag_fn,
#
needs_redraw_gadget_request
};
mouse_drag_fn mouse_drag_fn_arg;
};
# This is not currently hooked up to anything. XXX SUCKO FIXME
fun mouse_transit_fn_wrapper
#
( arg as
{
id: Id, # Unique Id for widget.
doc: String,
event_point: g2d::Point,
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
transit: gt::Gadget_Transit, # Mouse is entering (CAME) or leaving (LEFT) widget, or moving (MOVE) across it.
modifier_keys_state: evt::Modifier_Keys_State, # State of the modifier keys (shift, ctrl...).
widget_to_guiboss: gt::Widget_To_Guiboss,
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue # Used to call 'pass_*' methods in other imps.
}
)
=
{ note_site (id,site);
#
mouse_transit_fn_arg
=
MOUSE_TRANSIT_FN_ARG
{
id,
doc,
event_point,
widget_layout_hint,
frame_indent_hint,
site,
transit,
modifier_keys_state,
widget_to_guiboss,
theme,
do,
to,
#
default_mouse_transit_fn => \\ _ = (), # Default transit behavior for buttons is to do absolutely nothing.
#
needs_redraw_gadget_request
};
case mouse_transit_fn
#
THE mouse_transit_fn => mouse_transit_fn mouse_transit_fn_arg;
NULL => (); # We do not expect this case to happen: If mouse_transit_fn is NULL mouse_transit_fn_wrapper should not have been registered with widget-imp so we should never get called.
esac;
();
};
# This is not currently hooked up to anything. XXX SUCKO FIXME
fun key_event_fn_wrapper
{
id: Id, # Unique Id for widget.
doc: String,
keystroke: gt::Keystroke_Info, # Keystring etc for event.
widget_layout_hint: gt::Widget_Layout_Hint,
frame_indent_hint: gt::Frame_Indent_Hint,
site: g2d::Box, # Widget's assigned area in window coordinates.
widget_to_guiboss: gt::Widget_To_Guiboss,
guiboss_to_widget: gt::Guiboss_To_Widget, # Used by textpane.pkg keystroke-macro stuff to synthesize fake keystroke events to widget.
theme: wt::Widget_Theme,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue # Used to call 'pass_*' methods in other imps.
}
=
{ note_site (id,site);
#
key_event_fn_arg
=
KEY_EVENT_FN_ARG
{
id,
doc,
keystroke,
widget_layout_hint,
frame_indent_hint,
site,
widget_to_guiboss,
guiboss_to_widget,
theme,
do,
to,
#
default_key_event_fn => \\ _ = (), # Default key event behavior for frame is to do absolutely nothing.
#
needs_redraw_gadget_request
};
case key_event_fn
#
THE key_event_fn => key_event_fn key_event_fn_arg;
NULL => (); # We do not expect this case to happen: If key_event_fn is NULL key_event_fn_wrapper should not have been registered with widget-imp so we should never get called.
esac;
();
};
#
# End of widget hook fn section
###############################
widget_options
=
case mouse_transit_fn
#
THE _ => (wi::MOUSE_TRANSIT_FN mouse_transit_fn_wrapper) ! widget_options; # Register for transit events only if we are going to use them.
NULL => widget_options;
esac;
widget_options
=
case key_event_fn
#
THE _ => (wi::KEY_EVENT_FN key_event_fn_wrapper) ! widget_options; # Register for key events only if we are going to use them.
NULL => widget_options;
esac;
widget_options
=
case widget_id
#
THE id => (wi::ID id) ! widget_options; #
NULL => widget_options;
esac;
widget_options
=
[ wi::STARTUP_FN startup_fn, # We always register for these five because our base behavior depends on them.
wi::SHUTDOWN_FN shutdown_fn,
wi::INITIALIZE_GADGET_FN initialize_gadget_fn,
wi::REDRAW_REQUEST_FN redraw_request_fn_wrapper,
wi::MOUSE_CLICK_FN mouse_click_fn_wrapper,
wi::MOUSE_DRAG_FN mouse_drag_fn_wrapper,
wi::DOC widget_doc
]
@
widget_options
;
make_widget_fn = wi::make_widget_start_fn widget_options;
gt::WIDGET make_widget_fn; # So caller can write guiplan = gt::ROW [ frame::with [...], frame::with [...], ... ];
}; # PUBLIC
};
end;