# widget-imp.pkg
#
# For background see comments at top of
#
src/lib/x-kit/widget/gui/guiboss-imp.pkg#
# For more general background see the comments and diagrams in
#
#
src/lib/x-kit/xclient/src/window/xclient-ximps.pkg#
#
# Application programmers do not interface directly with widget_imp
# itself unless they are implementing custom widgets; instead they
# interface with various concrete widgets built on top of widget_imp
# such as:
#
#
src/lib/x-kit/widget/leaf/arrowbutton.pkg#
src/lib/x-kit/widget/leaf/button.pkg#
src/lib/x-kit/widget/leaf/diamondbutton.pkg#
src/lib/x-kit/widget/leaf/roundbutton.pkg#
# widget_imp builds on top of the fundamental widget-support
# infrastructure layer provided by
#
#
src/lib/x-kit/widget/gui/guiboss-imp.pkg#
src/lib/x-kit/widget/gui/guiboss-types.pkg#
src/lib/x-kit/widget/space/widget/widgetspace-imp.pkg#
# The primary design goal here is to achieve good separation
# of concerns between the app and guibosss worlds.
#
# In particular:
#
# o The guiboss world shouldn't know or care about any
# of the state information managed by widgets or the
# communication interfaces between those widgets and
# the application logic at large.
#
# o The individual widgets like arrowbutton should know
# as little as possible about the mechanics of event
# delivery, gui impnet startup and shutdown etc etc.
# Compiled by:
#
src/lib/x-kit/widget/xkit-widget.sublibstipulate
include package threadkit; # threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg #
# package ap = client_to_atom; # client_to_atom is from
src/lib/x-kit/xclient/src/iccc/client-to-atom.pkg# package au = authentication; # authentication is from
src/lib/x-kit/xclient/src/stuff/authentication.pkg# package cpm = cs_pixmap; # cs_pixmap is from
src/lib/x-kit/xclient/src/window/cs-pixmap.pkg# package cpt = cs_pixmat; # cs_pixmat is from
src/lib/x-kit/xclient/src/window/cs-pixmat.pkg# package dy = display; # display is from
src/lib/x-kit/xclient/src/wire/display.pkg# package xet = xevent_types; # xevent_types is from
src/lib/x-kit/xclient/src/wire/xevent-types.pkg# package w2x = windowsystem_to_xserver; # windowsystem_to_xserver is from
src/lib/x-kit/xclient/src/window/windowsystem-to-xserver.pkg# package fil = file__premicrothread; # file__premicrothread is from
src/lib/std/src/posix/file--premicrothread.pkg# package fti = font_index; # font_index is from
src/lib/x-kit/xclient/src/window/font-index.pkg# package r2k = xevent_router_to_keymap; # xevent_router_to_keymap is from
src/lib/x-kit/xclient/src/window/xevent-router-to-keymap.pkg# package mtx = rw_matrix; # rw_matrix is from
src/lib/std/src/rw-matrix.pkg# package rgb = rgb; # rgb is from
src/lib/x-kit/xclient/src/color/rgb.pkg# package rop = ro_pixmap; # ro_pixmap is from
src/lib/x-kit/xclient/src/window/ro-pixmap.pkg# package rw = root_window; # root_window is from
src/lib/x-kit/widget/lib/root-window.pkg# package rwv = rw_vector; # rw_vector is from
src/lib/std/src/rw-vector.pkg# package sep = client_to_selection; # client_to_selection is from
src/lib/x-kit/xclient/src/window/client-to-selection.pkg# package shp = shade; # shade is from
src/lib/x-kit/widget/lib/shade.pkg# package sj = socket_junk; # socket_junk is from
src/lib/internet/socket-junk.pkg# package x2s = xclient_to_sequencer; # xclient_to_sequencer is from
src/lib/x-kit/xclient/src/wire/xclient-to-sequencer.pkg# package tr = logger; # logger is from
src/lib/src/lib/thread-kit/src/lib/logger.pkg# package tsr = thread_scheduler_is_running; # thread_scheduler_is_running is from
src/lib/src/lib/thread-kit/src/core-thread-kit/thread-scheduler-is-running.pkg# package u1 = one_byte_unt; # one_byte_unt is from
src/lib/std/one-byte-unt.pkg# package v1u = vector_of_one_byte_unts; # vector_of_one_byte_unts is from
src/lib/std/src/vector-of-one-byte-unts.pkg# package v2w = value_to_wire; # value_to_wire is from
src/lib/x-kit/xclient/src/wire/value-to-wire.pkg# package wg = widget; # widget is from
src/lib/x-kit/widget/old/basic/widget.pkg# package wi = window; # window is from
src/lib/x-kit/xclient/src/window/window.pkg# package wme = window_map_event_sink; # window_map_event_sink is from
src/lib/x-kit/xclient/src/window/window-map-event-sink.pkg# package wpp = client_to_window_watcher; # client_to_window_watcher is from
src/lib/x-kit/xclient/src/window/client-to-window-watcher.pkg# package wy = widget_style; # widget_style is from
src/lib/x-kit/widget/lib/widget-style.pkg# package e2s = xevent_to_string; # xevent_to_string is from
src/lib/x-kit/xclient/src/to-string/xevent-to-string.pkg# package xc = xclient; # xclient is from
src/lib/x-kit/xclient/xclient.pkg# package xj = xsession_junk; # xsession_junk is from
src/lib/x-kit/xclient/src/window/xsession-junk.pkg# package xt = xtypes; # xtypes is from
src/lib/x-kit/xclient/src/wire/xtypes.pkg# package xtr = xlogger; # xlogger is from
src/lib/x-kit/xclient/src/stuff/xlogger.pkg package gtg = guiboss_to_guishim; # guiboss_to_guishim is from
src/lib/x-kit/widget/theme/guiboss-to-guishim.pkg package gd = gui_displaylist; # gui_displaylist is from
src/lib/x-kit/widget/theme/gui-displaylist.pkg package pp = standard_prettyprinter; # standard_prettyprinter is from
src/lib/prettyprint/big/src/standard-prettyprinter.pkg package r8 = rgb8; # rgb8 is from
src/lib/x-kit/xclient/src/color/rgb8.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 evt = gui_event_types; # gui_event_types is from
src/lib/x-kit/widget/gui/gui-event-types.pkg package gts = gui_event_to_string; # gui_event_to_string is from
src/lib/x-kit/widget/gui/gui-event-to-string.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 g2p = gadget_to_pixmap; # gadget_to_pixmap is from
src/lib/x-kit/widget/theme/gadget-to-pixmap.pkg #
tracefile = "widget-unit-test.trace.log";
nb = log::note_on_stderr; # log is from
src/lib/std/src/log.pkgherein
# This package is referenced in:
#
#
package widget_imp
: Widget_Imp # Widget_Imp is from
src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.api {
include package widget_imp_types; # widget_imp_types is from
src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp-types.pkg# pprint_widget_arg: pp::Prettyprinter -> Widget_Arg -> Void;
#
Runstate = { # These values will be statically globally visible throughout the code body for the imp.
to: Replyqueue, # The name makes foo::pass_something(imp) to {. ... } syntax read well.
id: Id, # Unique Id for this widget.
doc: String, # Human-readable description of this widget, for debug and inspection.
#
startup_fn: Startup_Fn, #
shutdown_fn: Shutdown_Fn, #
#
initialize_gadget_fn: Initialize_Gadget_Fn,
redraw_request_fn: Redraw_Request_Fn,
#
mouse_click_fn: Mouse_Click_Fn,
#
mouse_drag_fn: Mouse_Drag_Fn,
mouse_transit_fn: Mouse_Transit_Fn,
#
key_event_fn: Key_Event_Fn,
note_keyboard_focus_fn: Note_Keyboard_Focus_Fn,
#
wants_keystrokes: Bool,
wants_mouseclicks: Bool,
# These five provide generic widget connectivity with the guiboss world.
widget_to_guiboss: gt::Widget_To_Guiboss, #
guiboss_to_widget: gt::Guiboss_To_Widget, # Added to give keystroke-macro stuff a way to synthesize keystroke events to our own widget via Guiboss_To_Widget.g.note_key_event().
widget_callbacks: List( Null_Or(Widget) -> Void ), # In shut_down_widget_imp' () we use these to inform guiboss that our widget ports are no longer valid. DO WE ACTUALLY NEED THIS? XXX QUERO FIXME
shutdown_oneshot: Oneshot_Maildrop ( Void )
};
Mailq = Mailqueue( Runstate -> Void );
fun default_startup_fn
{
id: Id, # Unique id.
doc: String, # Human-readable description of this widget, for debug and inspection.
widget_to_guiboss: gt::Widget_To_Guiboss,
do: (Void -> Void) -> Void, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue
}
=
();
fun default_shutdown_fn ()
=
();
fun default_initialize_gadget_fn
{
id: Id, # Unique id.
doc: String, # Human-readable description of this widget, for debug and inspection.
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.
}
=
{
};
fun default_redraw_request_fn
{
id: Id, # Unique id.
doc: String, # Human-readable description of this widget, for debug and inspection.
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, # Used by widget subthreads to execute code in main widget microthread.
to: Replyqueue # Used to call 'pass_*' methods in other imps.
}
=
{
};
fun default_mouse_click_fn
{
id: Id, # Unique id.
doc: String, # Human-readable description of this widget, for debug and inspection.
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.
}
=
();
fun default_mouse_drag_fn
{
id: Id, # Unique id.
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.
}
=
();
fun default_mouse_transit_fn # Note that buttons are always all up in a mouse motion -- otherwise it is a mouse-drag event.
{
id: Id, # Unique id.
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.
}
=
();
fun default_key_event_fn
{
id: Id, # Unique id.
doc: String, # Human-readable description of this widget, for debug and inspection.
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,
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.
}
=
();
fun default_note_keyboard_focus_fn
{
id: Id, # Unique id.
doc: String, # Human-readable description of this widget, for debug and inspection.
have_keyboard_focus: Bool, #
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.
}
=
();
fun shut_down_widget_imp (r: Runstate)
=
{ r.shutdown_fn (); # Let application-specific code handle shutdown however it likes.
#
apply {. #callback NULL; } r.widget_callbacks; # Tell guiboss that our widget port is no longer valid.
put_in_oneshot (r.shutdown_oneshot, ()); # Let guiboss know that we've completed shutdown.
thread_exit { success => TRUE }; # Will not return.
};
fun run (
mailq: Mailq, #
#
runstate as
{ # These values will be statically globally visible throughout the code body for the imp.
to: Replyqueue, # The name makes foo::pass_something(imp) to {. ... } syntax read well.
id: Id,
doc: String, # Human-readable description of this widget, for debug and inspection.
#
startup_fn: Startup_Fn, #
shutdown_fn: Shutdown_Fn, #
#
initialize_gadget_fn: Initialize_Gadget_Fn,
redraw_request_fn: Redraw_Request_Fn,
#
mouse_click_fn: Mouse_Click_Fn,
#
mouse_drag_fn: Mouse_Drag_Fn,
mouse_transit_fn: Mouse_Transit_Fn,
#
key_event_fn: Key_Event_Fn,
note_keyboard_focus_fn: Note_Keyboard_Focus_Fn,
#
wants_keystrokes: Bool,
wants_mouseclicks: Bool,
# These five provide generic widget connectivity with the guiboss world.
widget_to_guiboss: gt::Widget_To_Guiboss, #
widget_callbacks: List( Null_Or(Widget) -> Void ), # In shut_down_widget_imp' () we use these to inform guiboss that our ports are no longer valid.
shutdown_oneshot: Oneshot_Maildrop( Void ),
guiboss_to_widget: gt::Guiboss_To_Widget # Added to give keystroke-macro stuff a way to synthesize keystroke events to our own widget via Guiboss_To_Widget.g.note_key_event().
}
)
=
{
loop ();
}
where
fun loop () # Outer loop for the imp.
=
{
do_one_mailop' to [
#
(take_from_mailqueue' mailq ==> do_plea)
];
loop ();
}
where
fun do_plea thunk
=
thunk runstate;
# fun shut_down_widget_imp' ()
# =
# shut_down_widget_imp runstate;
end;
end;
fun startup # Root fn of imp microthread.
{ id: Id,
doc: String, # Human-readable description of this widget, for debug and inspection.
reply_oneshot: Oneshot_Maildrop( gt::Widget_Exports ),
#
widget_callbacks: List( Null_Or(Widget) -> Void ), # We use these to pass our ports to app code.
widget_control_callbacks,
startup_fn: Startup_Fn, #
shutdown_fn: Shutdown_Fn, #
#
initialize_gadget_fn: Initialize_Gadget_Fn,
redraw_request_fn: Redraw_Request_Fn,
#
mouse_click_fn: Mouse_Click_Fn,
#
mouse_drag_fn: Mouse_Drag_Fn,
mouse_transit_fn: Mouse_Transit_Fn,
#
key_event_fn: Key_Event_Fn,
note_keyboard_focus_fn: Note_Keyboard_Focus_Fn,
#
wants_keystrokes: Bool,
wants_mouseclicks: Bool,
pixels_high_min: Int,
pixels_wide_min: Int,
#
pixels_high_cut: Float,
pixels_wide_cut: Float,
frame_indent_hint: gt::Frame_Indent_Hint,
# These five provide generic widget connectivity with the guiboss world.
widget_to_guiboss: gt::Widget_To_Guiboss, #
run_gun': Run_Gun,
shutdown_oneshot: Oneshot_Maildrop( Void )
}
() # Note currying.
=
{ widget = { id, do_something, pass_something, do };
#
guiboss_to_gadget = { id,
doc,
#
wants_keystrokes,
wants_mouseclicks,
#
initialize_gadget,
redraw_gadget_request,
#
note_keyboard_focus,
note_key_event,
#
note_mousebutton_event,
#
note_mouse_drag_event,
note_mouse_transit,
#
wakeup,
die
}: gt::Guiboss_To_Gadget;
guiboss_to_widget = { id,
doc,
g => guiboss_to_gadget,
get_widget_layout_hint, # This call is required to be O(1) and non-blocking, so guiboss_imp/widgetspace_imp can use it to quickly and reliably harvest initial widget layout hints.
get_frame_indent_hint, # This call is required to be O(1) and non-blocking, so guiboss_imp/widgetspace_imp can use it to quickly and reliably harvest frame indentation values.
do_something,
pass_something
}: gt::Guiboss_To_Widget;
exports = { guiboss_to_widget
};
widget_to_guiboss.note_widget_layout_hint
{
id,
widget_layout_hint => get_widget_layout_hint ()
};
put_in_oneshot (reply_oneshot, exports); # Return value from widget_start_fn().
apply {. #callback (THE widget); } widget_callbacks; # Pass our widget port to everyone who asked for it.
apply {. #callback guiboss_to_widget; } widget_control_callbacks; # Pass our port to everyone who asked for it.
block_until_mailop_fires run_gun'; # Wait for the starting gun.
widget_to_guiboss' = widget_to_guiboss; # Construct a version of widget_to_guiboss that updates our widget_layout_hint refcell when widget_to_guiboss.note_widget_layout_hint is called.
widget_to_guiboss
=
{ id => widget_to_guiboss'.id,
g => widget_to_guiboss'.g,
note_widget_layout_hint => hooked_note_widget_layout_hint
}
where
# XXX QUERO FIXME Should we be hooking this here or waiting for guiboss to ship it back to us and noting it then?
fun hooked_note_widget_layout_hint
{
id: Id,
widget_layout_hint: gt::Widget_Layout_Hint
}
=
{ set_widget_layout_hint widget_layout_hint; # Update our private cache of current widget_layout_hint value.
#
widget_to_guiboss'.note_widget_layout_hint { id, widget_layout_hint }; # Tell guiboss-imp about new value of widget_layout_hint.
};
end;
startup_fn # Let application-specific code handle startup however it likes.
{ #
id,
doc,
widget_to_guiboss,
do,
to
};
run (mailq, { # Will not return.
to,
id,
doc,
startup_fn, #
shutdown_fn, #
#
initialize_gadget_fn,
redraw_request_fn,
#
mouse_click_fn,
#
mouse_drag_fn,
mouse_transit_fn,
#
key_event_fn,
note_keyboard_focus_fn,
#
wants_keystrokes,
wants_mouseclicks,
# These provide generic widget connectivity with the guiboss world.
widget_to_guiboss, #
widget_callbacks, # In shut_down_widget_imp' () we use these to inform guiboss code that our ports are no longer valid.
shutdown_oneshot,
guiboss_to_widget # Added to give keystroke-macro stuff a way to synthesize keystroke events to our own widget via Guiboss_To_Widget.g.note_key_event().
}
);
}
where
mailq = make_mailqueue (get_current_microthread()): Mailq;
to = make_replyqueue();
stipulate
widget_layout_hint
=
REF { pixels_high_min,
pixels_wide_min,
#
pixels_high_cut,
pixels_wide_cut
};
herein
fun get_widget_layout_hint () = *widget_layout_hint;
fun set_widget_layout_hint lh = widget_layout_hint := lh;
end;
stipulate
ref_frame_indent_hint
=
REF frame_indent_hint;
herein
fun get_frame_indent_hint () = *ref_frame_indent_hint;
fun set_frame_indent_hint ih = ref_frame_indent_hint := ih; # Currently unused; retained because we'll probably eventually support dynamic changes here parallel to layout hints.
end;
fun do (thunk: Void -> Void) # PUBLIC.
=
put_in_mailqueue (mailq,
#
\\ ({ widget_to_guiboss, ... }: Runstate)
=
thunk ()
);
#######################################################################
# guiboss_to_gadget fns:
fun initialize_gadget #
{
site: g2d::Box, # Window rectangle in which to draw.
theme: wt::Widget_Theme,
get_font: List(String) -> evt::Font, # Accepts a list of font names which are tried in order; returns font 'ascent' and 'descent' in pixels -- sum them to get font height.
pass_font: List(String) -> Replyqueue #
-> (evt::Font -> Void) #
-> Void, # Nonblocking version of next, for use in imps.
make_rw_pixmap: g2d::Size -> g2p::Gadget_To_Rw_Pixmap
}
=
put_in_mailqueue (mailq,
#
\\ ({ id, widget_to_guiboss, ... }: Runstate)
=
{
initialize_gadget_fn # Let application-specific code handle background setup
{
id,
doc,
site,
#
widget_to_guiboss,
theme,
get_font,
pass_font,
make_rw_pixmap,
do,
to
};
}
);
fun redraw_gadget_request # We get this call at the start of every frame from
src/lib/x-kit/widget/gui/guiboss-imp.pkg {
frame_number: Int, # 1,2,3,... Purely for convenience of widget, guiboss-imp makes no use of this.
site: g2d::Box, # Window rectangle in which to draw.
duration_in_seconds: Float, # If state has changed look-imp should call redraw_gadget() before this time is up. Also useful for motionblur.
gadget_mode: gt::Gadget_Mode, # is_active/has_keyboard_focus/has_mouse_focus flags.
theme: wt::Widget_Theme,
popup_nesting_depth: Int # 0 for gadgets on basewindow, 1 for gadgets on popup on basewindow, 2 for gadgets on popup on popup, etc.
}
=
put_in_mailqueue (mailq,
#
\\ ({ id, widget_to_guiboss, ... }: Runstate)
=
{
redraw_request_fn # Let application-specific code handle please-redraw-yourself however it likes.
{
id,
doc,
frame_number,
frame_indent_hint => get_frame_indent_hint (),
site,
duration_in_seconds,
popup_nesting_depth,
#
widget_to_guiboss,
gadget_mode,
theme,
do,
to
};
}
);
fun wakeup # These calls are scheduled via gadget_to_guiboss.wake_me.
{
wakeup_arg: gt::Wakeup_Arg, #
wakeup_fn: gt::Wakeup_Arg -> Void
}
=
put_in_mailqueue (mailq,
#
\\ ({ id, widget_to_guiboss, ... }: Runstate)
=
wakeup_fn wakeup_arg
);
fun die ()
=
put_in_mailqueue (mailq,
#
\\ (r: Runstate)
=
shut_down_widget_imp r
);
fun note_keyboard_focus
(
have_keyboard_focus: Bool, # TRUE means we now have keyboard focus, FALSE means we no longer have it. Allows gadget to visually display focus locus, typically via a black outline and/or dis/abling cursor. See also Gadget_To_Guiboss.request_keyboard_focus
theme: wt::Widget_Theme
)
=
{
put_in_mailqueue (mailq,
#
\\ ({ id, widget_to_guiboss, ... }: Runstate)
=
put_in_mailqueue (mailq,
#
\\ ({ widget_to_guiboss, ... }: Runstate)
=
note_keyboard_focus_fn
{
id,
doc,
have_keyboard_focus,
widget_to_guiboss,
theme,
do,
to
}
)
);
();
};
fun note_mouse_transit # Note that buttons are always all up in a mouse-transit event -- otherwise it is a mouse-drag event.
{
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...).
event_point: g2d::Point,
site: g2d::Box, # Widget's assigned area in window coordinates.
theme: wt::Widget_Theme
} # Note keyboard keypress at 'point'.
= # ^ # 'point' ise the click point the window's coordinate system.
{ # Keyboard key just pressed down. #
put_in_mailqueue (mailq,
#
\\ ({ id, widget_to_guiboss, ... }: Runstate)
=
mouse_transit_fn
{
id,
doc,
event_point,
widget_layout_hint => get_widget_layout_hint (),
frame_indent_hint => get_frame_indent_hint (),
site,
transit,
modifier_keys_state,
widget_to_guiboss,
theme,
do,
to
}
);
();
};
fun note_mouse_drag_event
{
phase: gt::Drag_Phase, # LAUNCH/MOTION/FINISH.
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.
event_point: g2d::Point,
start_point: g2d::Point,
last_point: g2d::Point,
site: g2d::Box, # Widget's assigned area in window coordinates.
theme: wt::Widget_Theme
} # Note keyboard keypress at 'point'.
= # ^ # 'point' ise the click point the window's coordinate system.
{ # Keyboard key just pressed down. #
put_in_mailqueue (mailq,
#
\\ ({ id, widget_to_guiboss, ... }: Runstate)
=
mouse_drag_fn
{
id,
doc,
event_point,
start_point,
last_point,
widget_layout_hint => get_widget_layout_hint (),
frame_indent_hint => get_frame_indent_hint (),
site,
phase,
button,
modifier_keys_state,
mousebuttons_state,
widget_to_guiboss,
theme,
do,
to
}
);
();
};
fun note_key_event
{
keystroke
as
{ key_event: gt::Key_Event, # KEY_PRESS or KEY_RELEASE.
keycode: evt::Keycode, # Keycode of the depressed key.
keysym: evt::Keysym, # Keysym of the depressed key.
keystring: String, # Ascii for the depressed key.
keychar: Char, # First char of 'string' ('\0' if string-length != 1).
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.
}: gt::Keystroke_Info,
site: g2d::Box, # Widget's assigned area in window coordinates.
theme: wt::Widget_Theme
} # Note keyboard keypress at 'point'.
= # ^ # 'point' ise the click point the window's coordinate system.
{ # Keyboard key just pressed down. #
put_in_mailqueue (mailq,
#
\\ ({ widget_to_guiboss, guiboss_to_widget, ... }: Runstate)
=
key_event_fn
{
id,
doc,
keystroke,
widget_layout_hint => get_widget_layout_hint (),
frame_indent_hint => get_frame_indent_hint (),
site,
widget_to_guiboss,
guiboss_to_widget,
theme,
do,
to
}
);
};
fun note_mousebutton_event
{
mousebutton_event: gt::Mousebutton_Event, # MOUSEBUTTON_PRESS or MOUSEBUTTON_RELEASE.
mouse_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.
event_point: g2d::Point,
site: g2d::Box, # Widget's assigned area in window coordinates.
theme: wt::Widget_Theme
} # Note mousebutton click at 'point'.
= # ^ # 'point' is the click point in the window's coordinate system.
{ # Mouse button just clicked down. #
put_in_mailqueue (mailq,
#
\\ ({ widget_to_guiboss, ... }: Runstate)
=
mouse_click_fn
{
id,
doc,
event => mousebutton_event,
button => mouse_button,
point => event_point,
widget_layout_hint => get_widget_layout_hint (),
frame_indent_hint => get_frame_indent_hint (),
site,
modifier_keys_state, # State of the modifier keys (shift, ctrl...).
mousebuttons_state, # State of mouse buttons as a bool record.
widget_to_guiboss,
theme,
do,
to
}
);
};
#######################################################################
# guiboss_to_widget fns:
fun do_something (i: Int) # PUBLIC.
=
put_in_mailqueue (mailq,
#
\\ ({ widget_to_guiboss, ... }: Runstate)
=
()
);
fun pass_something (replyqueue: Replyqueue) (reply_handler: Int -> Void) # PUBLIC.
=
{ reply_oneshot = make_oneshot_maildrop(): Oneshot_Maildrop( Int );
#
put_in_mailqueue (mailq,
#
\\ (_: Runstate)
=
put_in_oneshot (reply_oneshot, 0)
);
put_in_replyqueue (replyqueue, (get_from_oneshot' reply_oneshot) ==> reply_handler);
};
# fun pass_draw_done_flag (replyqueue: Replyqueue) (reply_handler: Void -> Void) # PUBLIC.
# =
# { reply_oneshot = make_oneshot_maildrop(): Oneshot_Maildrop( Void );
# #
# put_in_mailqueue (mailq,
# #
# \\ (_: Runstate)
# =
# put_in_oneshot (reply_oneshot, ())
# );
#
# put_in_replyqueue (replyqueue, (get_from_oneshot' reply_oneshot) ==> reply_handler);
# };
end;
fun process_options
( options: List(Widget_Option),
#
{ name,
id,
doc,
#
widget_callbacks,
widget_control_callbacks,
#
startup_fn,
shutdown_fn,
#
initialize_gadget_fn,
redraw_request_fn,
#
mouse_click_fn,
#
mouse_drag_fn,
mouse_transit_fn,
#
key_event_fn,
note_keyboard_focus_fn,
#
wants_keystrokes,
wants_mouseclicks,
pixels_high_min,
pixels_wide_min,
#
pixels_high_cut,
pixels_wide_cut,
#
frame_indent_hint
}
)
=
{ my_name = REF name;
my_id = REF id;
my_doc = REF doc;
#
my_widget_callbacks = REF widget_callbacks;
my_widget_control_callbacks = REF widget_control_callbacks;
#
my_startup_fn = REF startup_fn;
my_shutdown_fn = REF shutdown_fn;
#
my_initialize_gadget_fn = REF initialize_gadget_fn;
my_redraw_request_fn = REF redraw_request_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_note_keyboard_focus_fn = REF note_keyboard_focus_fn;
#
my_wants_keystrokes = REF wants_keystrokes;
my_wants_mouseclicks = REF wants_mouseclicks;
#
my_pixels_high_min = REF pixels_high_min;
my_pixels_wide_min = REF pixels_wide_min;
#
my_pixels_high_cut = REF pixels_high_cut;
my_pixels_wide_cut = REF pixels_wide_cut;
#
my_frame_indent_hint = REF frame_indent_hint;
#
apply do_option options
where
fun do_option (MICROTHREAD_NAME n) => my_name := n;
do_option (ID i) => my_id := i;
do_option (DOC i) => my_doc := i;
#
do_option (WIDGET_CALLBACK c) => my_widget_callbacks := c ! *my_widget_callbacks;
do_option (WIDGET_CONTROL_CALLBACK c) => my_widget_control_callbacks := c ! *my_widget_control_callbacks;
#
do_option (STARTUP_FN fn) => my_startup_fn := fn;
do_option (SHUTDOWN_FN fn) => my_shutdown_fn := fn;
#
do_option (INITIALIZE_GADGET_FN fn) => my_initialize_gadget_fn := fn;
do_option (REDRAW_REQUEST_FN fn) => my_redraw_request_fn := fn;
#
do_option (MOUSE_CLICK_FN fn) => { my_mouse_click_fn := fn; my_wants_mouseclicks := TRUE; };
#
do_option (MOUSE_DRAG_FN fn) => { my_mouse_drag_fn := fn; };
do_option (MOUSE_TRANSIT_FN fn) => { my_mouse_transit_fn := fn; };
#
do_option (KEY_EVENT_FN fn) => { my_key_event_fn := fn; my_wants_keystrokes := TRUE; }; # I think the wants_keystrokes stuff was all stillborn and should likely go...
do_option (NOTE_KEYBOARD_FOCUS_FN fn) => { my_note_keyboard_focus_fn := fn; };
#
do_option (PIXELS_HIGH_MIN i) => my_pixels_high_min := i;
do_option (PIXELS_WIDE_MIN i) => my_pixels_wide_min := i;
#
do_option (PIXELS_HIGH_CUT f) => my_pixels_high_cut := f;
do_option (PIXELS_WIDE_CUT f) => my_pixels_wide_cut := f;
#
do_option (FRAME_INDENT_HINT f) => my_frame_indent_hint := f;
end;
end;
{ name => *my_name,
id => *my_id,
doc => *my_doc,
#
widget_callbacks => *my_widget_callbacks,
widget_control_callbacks => *my_widget_control_callbacks,
#
startup_fn => *my_startup_fn,
shutdown_fn => *my_shutdown_fn,
#
initialize_gadget_fn => *my_initialize_gadget_fn,
redraw_request_fn => *my_redraw_request_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,
note_keyboard_focus_fn => *my_note_keyboard_focus_fn,
#
wants_keystrokes => *my_wants_keystrokes,
wants_mouseclicks => *my_wants_mouseclicks,
#
pixels_high_min => *my_pixels_high_min,
pixels_wide_min => *my_pixels_wide_min,
#
pixels_high_cut => *my_pixels_high_cut,
pixels_wide_cut => *my_pixels_wide_cut,
#
frame_indent_hint => *my_frame_indent_hint
};
};
fun make_widget_start_fn (widget_options: List(Widget_Option)) # PUBLIC, exported to app clients.
=
# Here we have a critical linkage in the GUI
# code factorization design.
#
# make_widget_start_fn is called by the
# various client-level widgets like
#
#
src/lib/x-kit/widget/leaf/button.pkg #
src/lib/x-kit/widget/leaf/roundbutton.pkg #
src/lib/x-kit/widget/leaf/diamondbutton.pkg #
src/lib/x-kit/widget/leaf/arrowbutton.pkg #
# The resulting widget_start_fn values
# then get stored in
#
# guiboss_types::Widget::WIDGET # guiboss_types is from
src/lib/x-kit/widget/gui/guiboss-types.pkg #
# nodes and eventually called by # guiboss_imp is from
src/lib/x-kit/widget/gui/guiboss-imp.pkg # # translate_guiplan_to_guipane is from
src/lib/x-kit/widget/gui/translate-guiplan-to-guipane.pkg # guiplan_to_guipane
#
# to actually start the widgets running.
#
# We do not use our usual Imports/Exports driven
# imp startup protocol here because we want
# guiboss_imp to do the actual widget-imp but we
# do now want guiboss_imp to know anything about
# the state types of widgets (to avoid an explosion
# of cases in guiboss_imp, one per widget).
#
{
(process_options
( widget_options,
{ name => "widget",
id => id_zero,
doc => "<widget>", # This should always get overridden by specific widgets (subclasses, essentially), since their value can be more specific and informative.
#
widget_callbacks => [],
widget_control_callbacks => [],
#
startup_fn => default_startup_fn,
shutdown_fn => default_shutdown_fn,
#
initialize_gadget_fn => default_initialize_gadget_fn,
redraw_request_fn => default_redraw_request_fn,
#
mouse_click_fn => default_mouse_click_fn,
#
mouse_drag_fn => default_mouse_drag_fn,
mouse_transit_fn => default_mouse_transit_fn,
#
key_event_fn => default_key_event_fn,
note_keyboard_focus_fn => default_note_keyboard_focus_fn,
#
wants_keystrokes => FALSE,
wants_mouseclicks => FALSE,
#
pixels_high_min => gt::default_widget_layout_hint.pixels_high_min,
pixels_wide_min => gt::default_widget_layout_hint.pixels_wide_min,
#
pixels_high_cut => gt::default_widget_layout_hint.pixels_high_cut,
pixels_wide_cut => gt::default_widget_layout_hint.pixels_wide_cut,
#
frame_indent_hint => gt::default_frame_indent_hint
}
) )
->
{ name,
id,
doc,
#
widget_callbacks,
widget_control_callbacks,
#
startup_fn,
shutdown_fn,
#
initialize_gadget_fn,
redraw_request_fn,
#
mouse_click_fn,
#
mouse_drag_fn,
mouse_transit_fn,
#
key_event_fn,
note_keyboard_focus_fn,
#
wants_keystrokes,
wants_mouseclicks,
#
pixels_high_min,
pixels_wide_min,
#
pixels_high_cut,
pixels_wide_cut,
#
frame_indent_hint
};
id = if (id_to_int(id) == 0) issue_unique_id(); # Allocate unique imp id.
else id;
fi;
fun widget_start_fn # This fn will get called by paused_gui__to__guipane() in
src/lib/x-kit/widget/gui/guiboss-imp.pkg { #
widget_to_guiboss: gt::Widget_To_Guiboss, #
run_gun': Run_Gun,
shutdown_oneshot: Oneshot_Maildrop( Void )
}
: gt::Widget_Exports
=
{ reply_oneshot = make_oneshot_maildrop (): Oneshot_Maildrop( gt::Widget_Exports );
#
xlogger::make_thread
name
(startup { id, # Note that startup() is curried.
doc,
reply_oneshot,
#
widget_callbacks,
widget_control_callbacks,
startup_fn, # Pass in widget-specific args.
shutdown_fn, # Save state for possible widget restart.
#
initialize_gadget_fn,
redraw_request_fn,
#
mouse_click_fn,
#
mouse_drag_fn,
mouse_transit_fn,
#
key_event_fn,
note_keyboard_focus_fn,
#
wants_keystrokes,
wants_mouseclicks,
#
pixels_high_min,
pixels_wide_min,
#
pixels_high_cut,
pixels_wide_cut,
#
frame_indent_hint,
# These five args pass in the ports etc that guiboss-imp gave us.
widget_to_guiboss, #
run_gun',
shutdown_oneshot
}
);
(get_from_oneshot reply_oneshot); # Return gt::Widget_Exports to guiboss-imp.
};
gt::WIDGET_START_FN widget_start_fn; # The value-added is that we've locked in the values of *_fn etc, and guiboss-imp can be agnostic about their types.
};
fun pprint_widget_arg # PUBLIC.
(pp: pp::Prettyprinter)
(widget_arg: Widget_Arg)
=
{
widget_arg
->
( options: List(Widget_Option)
);
pp.box {.
pp.txt " [";
pp::seqx {. pp.txt ", "; }
pprint_option
options
;
pp.txt " ]";
pp.txt " )";
};
}
where
fun pprint_option option
=
case option
#
MICROTHREAD_NAME name => { pp.lit (sprintf "MICROTHREAD_NAME \"%s\"" name); };
ID id => { pp.lit (sprintf "ID %d" (id_to_int id) ); };
DOC docstring => { pp.lit (sprintf "DOC \"%s\"" docstring ); };
#
WIDGET_CONTROL_CALLBACK _ => { pp.lit "WIDGET_CONTROL_CALLBACK (callback)"; };
WIDGET_CALLBACK _ => { pp.lit "WIDGET_CALLBACK (callback)"; };
#
STARTUP_FN _ => { pp.lit "STARTUP_FN _"; };
SHUTDOWN_FN _ => { pp.lit "SHUTDOWN_FN _"; };
#
INITIALIZE_GADGET_FN _ => { pp.lit "INITIALIZE_GADGET_FN _"; };
REDRAW_REQUEST_FN _ => { pp.lit "REDRAW_REQUEST_FN _"; };
#
MOUSE_CLICK_FN _ => { pp.lit "MOUSE_CLICK_FN _"; };
#
MOUSE_DRAG_FN _ => { pp.lit "MOUSE_DRAG_FN _"; };
MOUSE_TRANSIT_FN _ => { pp.lit "MOUSE_TRANSIT_FN _"; };
#
KEY_EVENT_FN _ => { pp.lit "KEY_EVENT_FN _"; };
NOTE_KEYBOARD_FOCUS_FN _ => { pp.lit "NOTE_KEYBOARD_FOCUS_FN _"; };
#
PIXELS_HIGH_MIN i => { pp.lit (sprintf "PIXELS_HIGH_MIN %d" i); };
PIXELS_WIDE_MIN i => { pp.lit (sprintf "PIXELS_WIDE_MIN %d" i); };
#
PIXELS_HIGH_CUT f => { pp.lit (sprintf "PIXELS_HIGH_CUT %f" f); };
PIXELS_WIDE_CUT f => { pp.lit (sprintf "PIXELS_WIDE_CUT %g" f); };
#
FRAME_INDENT_HINT h => { pp.lit (sprintf "FRAME_INDENT_HINT { pixels_for_top_of_frame => %d, pixels_for_bottom_of_frame => %d, pixels_for_left_of_frame => %d, pixels_for_right_of_frame => %d }"
h.pixels_for_top_of_frame
h.pixels_for_bottom_of_frame
h.pixels_for_left_of_frame
h.pixels_for_right_of_frame
);
};
esac;
end;
};
end;