PreviousUpNext

15.4.1459  src/lib/x-kit/widget/leaf/roundbutton.pkg

## roundbutton.pkg
#
# See also:
#     src/lib/x-kit/widget/leaf/button.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





###                "The problem is to compress a room full
###                 of digital computation equipment into
###                 the size of a suitcase, then a shoe box,
###                 and finally small enough to hold in the
###                 palm of the hand."
###                                    -- Jack Staller, 1959

# 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 wti =  widget_theme_imp;                                            # widget_theme_imp              is from   src/lib/x-kit/widget/xkit/theme/widget/default/widget-theme-imp.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.pkg
herein

    package roundbutton
    :       Roundbutton                                                         # Roundbutton                   is from   src/lib/x-kit/widget/leaf/roundbutton.api
    {
        package t {                                                             # "t" for "type".
            #
            Button_Type         = MOMENTARY_CONTACT
                                | PUSH_ON_PUSH_OFF
                                | IGNORE_MOUSECLICKS
                                ;
        };

        App_To_Roundbutton
          =
          { id:                         Id,
            #
            get_active:                 Void -> Bool,
            get_state:                  Void -> Bool,
            #
            get_button_relief:          Void -> wt::Relief,                     # 
            get_button_type:            Void -> t::Button_Type,                 # 
            #
            get_button_text:            Void -> Null_Or(String),
            get_button_on_text:         Void -> Null_Or(String),
            get_button_off_text:        Void -> Null_Or(String),

            set_button_text:            Null_Or(String) -> Void,
            set_button_on_text:         Null_Or(String) -> Void,
            set_button_off_text:        Null_Or(String) -> Void,
            #
            set_active_to:              Bool -> Void,
            set_state_to:               Bool -> Void,                           # Also calls gadget_to_guiboss.needs_redraw_gadget_request(id);
            set_button_relief_to:       wt::Relief -> Void                      # Also calls gadget_to_guiboss.needs_redraw_gadget_request(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,
                #
                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,
                #
                button_state:                   Bool,                           # Is the button ON or OFF?
                button_type:                    t::Button_Type,                 # Is the button push-on-push-off or momentary-contact?
                button_relief:                  wt::Relief,                     # Is the button outline a slope, a ridge, or a flat band?

                text:                           Null_Or(String),
                fonts:                          List(String),
                font_weight:                    Null_Or(wt::Font_Weight),
                font_size:                      Null_Or(Int),

                margin:                         Int,
                thick:                          Int
              }
        withtype
        Redraw_Fn
          =
          Redraw_Fn_Arg
          ->
          { displaylist:                gd::Gui_Displaylist,
            point_in_gadget:            Null_Or(g2d::Point -> Bool),            # 
            pixels_high_min:            Int,
            pixels_wide_min:            Int
          }
          ;



        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,
                #
                button_state:                   Bool,                           # Is the button ON or OFF?
                button_type:                        t::Button_Type,             # Is the button push-on-push-off or momentary-contact?
                button_relief:                  Ref(wt::Relief),                # Is the button outline a slope, a ridge, or a flat band?
                #
                initial_state:                  Bool,                           # Original state of button.
                note_state:                     Bool -> Void,                   # Change state of button. This takes care of notifying our state-watchers. (Does NOT call needs_redraw_gadget_request.)
                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,
                #
                button_state:                   Bool,                           # Is the button ON or OFF?
                button_type:                        t::Button_Type,             # Is the button push-on-push-off or momentary-contact?
                button_relief:                  Ref(wt::Relief),                # Is the button outline a slope, a ridge, or a flat band?
                #
                initial_state:                  Bool,                           # Original state of button.
                note_state:                     Bool -> Void,                   # Change state of button. This takes care of notifying our state-watchers. (Does NOT call needs_redraw_gadget_request.)
                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,
                #
                button_state:                   Bool,                           # Is the button ON or OFF?
                button_type:                        t::Button_Type,             # Is the button push-on-push-off or momentary-contact?
                button_relief:                  Ref(wt::Relief),                # Is the button outline a slope, a ridge, or a flat band?
                #
                initial_state:                  Bool,                           # Original state of button.
                note_state:                     Bool -> Void,                   # Change state of button. This takes care of notifying our state-watchers. (Does NOT call needs_redraw_gadget_request.)
                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,                         # 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,          # 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,
                #
                button_state:                   Bool,                           # Is the button ON or OFF?
                button_type:                    t::Button_Type,                 # Is the button push-on-push-off or momentary-contact?
                button_relief:                  Ref(wt::Relief),                # Is the button outline a slope, a ridge, or a flat band?
                #
                initial_state:                  Bool,                           # Original state of button.
                note_state:                     Bool -> Void,                   # Change state of button. This takes care of notifying our state-watchers. (Does NOT call needs_redraw_gadget_request.)
                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;



        Option  = PIXELS_SQUARE         Int
                #
                | PIXELS_HIGH_MIN       Int
                | PIXELS_WIDE_MIN       Int
                #
                | PIXELS_HIGH_CUT       Float
                | PIXELS_WIDE_CUT       Float
                #
                | INITIAL_STATE         Bool
                | INITIALLY_ACTIVE      Bool
                #
                | MOMENTARY_CONTACT                                             # State is non-default (opposite of INITIAL_STATE) only between mouse downclick and upclick.
                | PUSH_ON_PUSH_OFF                                              # Mouse downclicks toggle state between TRUE and FALSE.
                | IGNORE_MOUSECLICKS                                            # Mouseclicks to not affect state.
                #
                | BODY_COLOR                            rgb::Rgb
                | BODY_COLOR_WITH_MOUSEFOCUS            rgb::Rgb
                | BODY_COLOR_WHEN_ON                    rgb::Rgb
                | BODY_COLOR_WHEN_ON_WITH_MOUSEFOCUS    rgb::Rgb
                #
                | ID                    Id
                | DOC                   String
                #
                | RELIEF                wt::Relief                              # Should button boundary be drawn flat, raised, sunken, ridged or grooved?
                | MARGIN                Int                                     # How many pixels to inset button relative to its assigned window site.  Default is 4.
                | THICK                 Int                                     # Thickness of lines (well, polygons) forming button.  Default is 5.
                #
                | TEXT                  String                                  # Text to draw inside button.  Default is "".
                | ON_TEXT               String                                  # Text to draw inside button when switch is ON.   Default is TEXT else "".
                | OFF_TEXT              String                                  # Text to draw inside button when switch is OFF.  Default is TEXT else "".
                #
                | FONT_SIZE             Int                                     # Show any text in this pointsize.  Default is 12.
                | FONTS                 List(String)                            # Override theme font:  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).
                #
                | ROMAN                                                         # Show any text in plain  font from widget-theme.  This is the default.
                | ITALIC                                                        # Show any text in italic font from widget-theme.
                | BOLD                                                          # Show any text in bold   font from widget-theme.  NB: Text is either bold or italic, not both.
                #
                | 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.
                #
                | BOOL_OUT              (Bool -> Void)                          # Widget's current state              will be sent to these fns each time state changes.
                | PORTWATCHER           (Null_Or(App_To_Roundbutton) -> 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),
              #
              { button_type,
                #
                body_color,
                body_color_with_mousefocus,
                body_color_when_on,
                body_color_when_on_with_mousefocus,
                #
                widget_id,
                widget_doc,
                #
                relief,
                margin,
                thick,
                #
                text,
                on_text,
                off_text,
                #
                fonts,
                font_weight,
                font_size,
                #
                redraw_fn,
                mouse_click_fn,
                mouse_drag_fn,
                mouse_transit_fn,
                key_event_fn,
                #
                initial_state,
                initially_active,
                #
                widget_options,
                #
                portwatchers,
                bool_outs,
                sitewatchers
              }
            )
            =
            {   my_button_type                          =  REF  button_type;
                #
                my_body_color                           =  REF body_color;
                my_body_color_with_mousefocus           =  REF body_color_with_mousefocus;
                my_body_color_when_on                   =  REF body_color_when_on;
                my_body_color_when_on_with_mousefocus   =  REF body_color_when_on_with_mousefocus;
                #
                my_widget_id                            =  REF  widget_id;
                my_widget_doc                           =  REF  widget_doc;
                #
                my_relief                               =  REF  relief;
                my_margin                               =  REF  margin;
                my_thick                                =  REF  thick;
                #
                my_text                                 =  REF  text;
                my_on_text                              =  REF  on_text;
                my_off_text                             =  REF  off_text;
                #
                my_fonts                                =  REF  fonts;
                my_font_weight                          =  REF  font_weight;
                my_font_size                            =  REF  font_size;
                #
                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_initial_state                        =  REF  initial_state;
                my_initially_active                     =  REF  initially_active;
                #
                my_widget_options                       =  REF  widget_options;
                #
                my_portwatchers                         =  REF  portwatchers;
                my_bool_outs                            =  REF  bool_outs;
                my_sitewatchers                         =  REF  sitewatchers;
                #

                apply  do_option  options
                where
                    fun do_option (INITIAL_STATE                        b) =>   my_initial_state        :=  b;
                        do_option (INITIALLY_ACTIVE                     b) =>   my_initially_active     :=  b;
                        #
                        do_option (MOMENTARY_CONTACT                     ) =>   my_button_type          :=  t::MOMENTARY_CONTACT;
                        do_option (PUSH_ON_PUSH_OFF                      ) =>   my_button_type          :=  t::PUSH_ON_PUSH_OFF;
                        do_option (IGNORE_MOUSECLICKS                    ) =>   my_button_type          :=  t::IGNORE_MOUSECLICKS;
                        #
                        do_option (BODY_COLOR                           c) =>   my_body_color                           :=  THE c;
                        do_option (BODY_COLOR_WITH_MOUSEFOCUS           c) =>   my_body_color_with_mousefocus           :=  THE c;
                        do_option (BODY_COLOR_WHEN_ON                   c) =>   my_body_color_when_on                   :=  THE c;
                        do_option (BODY_COLOR_WHEN_ON_WITH_MOUSEFOCUS   c) =>   my_body_color_when_on_with_mousefocus   :=  THE c;
                        #
                        do_option (ID                                   i) =>   my_widget_id            :=  THE i;
                        do_option (DOC                                  d) =>   my_widget_doc           :=      d;
                        #
                        do_option (RELIEF                               r) =>   my_relief               :=  r;
                        do_option (MARGIN                               i) =>   my_margin               :=  i;
                        do_option (THICK                                i) =>   my_thick                :=  i;
                        #
                        do_option (TEXT                                 t) =>   my_text                 :=  THE t;
                        do_option (ON_TEXT                              t) =>   my_on_text              :=  THE t;
                        do_option (OFF_TEXT                             t) =>   my_off_text             :=  THE t;
                        #
                        do_option (FONT_SIZE                            i) =>   my_font_size            :=  THE i;
                        do_option (FONTS                                t) =>   my_fonts                :=  t;
                        #
                        do_option (ROMAN                                 ) =>   my_font_weight          :=  THE wt::ROMAN_FONT;
                        do_option (ITALIC                                ) =>   my_font_weight          :=  THE wt::ITALIC_FONT;
                        do_option (BOLD                                  ) =>   my_font_weight          :=  THE wt::BOLD_FONT;
                        #
                        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        :=  THE f;
                        do_option (MOUSE_TRANSIT_FN                     f) =>   my_mouse_transit_fn     :=      f;
                        do_option (KEY_EVENT_FN                         f) =>   my_key_event_fn         :=  THE f;
                        #
                        do_option (PORTWATCHER                          c) =>   my_portwatchers         :=  c ! *my_portwatchers;
                        do_option (BOOL_OUT                             c) =>   my_bool_outs            :=  c ! *my_bool_outs;
                        do_option (SITEWATCHER                          c) =>   my_sitewatchers         :=  c ! *my_sitewatchers;
                        #
                        #
                        do_option (PIXELS_HIGH_MIN                      i) =>   my_widget_options       :=  (wi::PIXELS_HIGH_MIN i) ! *my_widget_options;
                        do_option (PIXELS_WIDE_MIN                      i) =>   my_widget_options       :=  (wi::PIXELS_WIDE_MIN i) ! *my_widget_options;
                        #
                        do_option (PIXELS_HIGH_CUT                      f) =>   my_widget_options       :=  (wi::PIXELS_HIGH_CUT f) ! *my_widget_options;
                        do_option (PIXELS_WIDE_CUT                      f) =>   my_widget_options       :=  (wi::PIXELS_WIDE_CUT f) ! *my_widget_options;
                        #
                        do_option (PIXELS_SQUARE                        i) =>   my_widget_options       :=  (wi::PIXELS_HIGH_MIN   i)
                                                                                                        !   (wi::PIXELS_WIDE_MIN   i)
                                                                                                        !   (wi::PIXELS_HIGH_CUT 0.0)
                                                                                                        !   (wi::PIXELS_WIDE_CUT 0.0)
                                                                                                        !   *my_widget_options;
                    end;
                end;

                { button_type                           =>  *my_button_type,
                  #
                  body_color                            =>  *my_body_color,
                  body_color_with_mousefocus            =>  *my_body_color_with_mousefocus,
                  body_color_when_on                    =>  *my_body_color_when_on,
                  body_color_when_on_with_mousefocus    =>  *my_body_color_when_on_with_mousefocus,
                  #
                  widget_id                             =>  *my_widget_id,
                  widget_doc                            =>  *my_widget_doc,
                  #
                  relief                                =>  *my_relief,
                  margin                                =>  *my_margin,
                  thick                                 =>  *my_thick,
                  #
                  text                                  =>  *my_text,
                  on_text                               =>  *my_on_text,
                  off_text                              =>  *my_off_text,
                  #
                  fonts                                 =>  *my_fonts,
                  font_weight                           =>  *my_font_weight,
                  font_size                             =>  *my_font_size,
                  #
                  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,
                  #
                  initial_state                         =>  *my_initial_state,
                  initially_active                      =>  *my_initially_active,
                  #
                  widget_options                        =>  *my_widget_options,
                  #
                  portwatchers                          =>  *my_portwatchers,
                  bool_outs                             =>  *my_bool_outs,
                  sitewatchers                          =>  *my_sitewatchers
                };
            };


        fun default_redraw_fn (REDRAW_FN_ARG a)                                                 # Handle a guiboss request to redraw ourself.
            =
            {   font_size       =  a.font_size;
                font_weight     =  a.font_weight;
                fonts           =  a.fonts;
                margin          =  a.margin;
                palette         =  a.palette;
                site            =  a.site;
                text            =  a.text;
                theme           =  a.theme;
                d               =  a.popup_nesting_depth;
                
                background_box  =  site;
                
                background      = [ gd::COLOR (palette.surround_color,  [ gd::FILLED_BOXES [ background_box ]]) ];

                inner_box = g2d::box::make_nested_box (background_box, margin);                 # 

                fun get_fontnames ()
                    =
                    {   font_size_to_use
                            =
                            case font_size      THE i => i;
                                                NULL  => *theme.default_font_size;
                            esac;

                        fontname_to_use
                            =
                            case font_weight  THE wt::ROMAN_FONT  =>  *theme.get_roman_fontname  font_size_to_use;
                                              THE wt::ITALIC_FONT =>  *theme.get_italic_fontname font_size_to_use;
                                              THE wt::BOLD_FONT   =>  *theme.get_bold_fontname   font_size_to_use;
                                              NULL            =>  *theme.get_roman_fontname  font_size_to_use;
                            esac;

                        fontnames =  fonts  @  [ fontname_to_use, "9x15" ];

                        fontnames;
                    };


                fun get_text_dimensions (text: String)
                    =
                    {   g =  wti::get__guiboss_to_hostwindow  theme;
                        #
                        font = g.get_font (get_fontnames ());

                        { font_ascent      =>  font.font_height.ascent,
                          font_descent     =>  font.font_height.descent,
                          length_in_pixels =>  font.string_length_in_pixels text
                        };
                    };

                fun text_displaylist
                      (
                        text:           String,
                        text_box:       g2d::Box
                      )
                    =
                    {   text_dimensions =  get_text_dimensions  text;
                        #
                        fontnames =  get_fontnames ();

                        (g2d::box::midpoint text_box)
                            ->
                            { row, col };

                        row =  row - text_dimensions.font_descent + ((text_dimensions.font_ascent + text_dimensions.font_descent) / 2); 

                        draw_point = { row, col };

                        [ gd::COLOR ( palette.text_color, 
                                      [ gd::FONT ( fontnames,
                                                   [ gd::PUT_TEXT   ( gd::CENTERED_ON_POINT,
                                                                      [ gd::TEXT (draw_point, text) ]
                                                                    )
                                                   ]
                                                 )
                                      ]
                                    )
                        ];
                    };





                
                stipulate
                    inner_box -> { row, col, high, wide };

                    wide2 = wide / 2;
                    high2 = high / 2;

                    radius = int::min (wide2, high2) - 1;

                    outer_diameter = 2*radius;

                    inner_diameter = (outer_diameter * 9) / 10; 

                    bwid = (outer_diameter - inner_diameter) / 2;
                herein

                    outer_disk = { col=> col,        row=> row,        wide=>outer_diameter, high=>outer_diameter, start_angle=>   0.0,  fill_angle=>  360.0  };
                    inner_disk = { col=> col + bwid, row=> row + bwid, wide=>inner_diameter, high=>inner_diameter, start_angle=>   0.0,  fill_angle=>  360.0  };

                    outline = [ gd::COLOR
                                  (
                                    (*theme.shady_bevel_color)(d),
                                    [ gd::FILLED_ARCS [ outer_disk ]]
                                  ),

                                gd::COLOR
                                  (
#                                   button_state ?? (*theme.sunny_bevel_color)(d) :: palette.body_color,
                                    palette.body_color,
                                    [ gd::FILLED_ARCS [ inner_disk ]]
                                  )
                              ];

                    foreground = outline;

                    midpoint = g2d::box::midpoint  inner_box;

                    fun point_in_gadget (point: g2d::Point)                                             # Mouseclick has hit gadget if it is within 'radius' of button center.
                        =
                        {   d = g2d::point::subtract (point, midpoint);
                            #
                            (d.row * d.row + d.col * d.col)
                            <
                            radius * radius
                            ;
                        };

                    point_in_gadget = THE point_in_gadget;
                end;


                text_box =    inner_box;


                # Maybe incorporate text into button foreground:
                #
                foreground
                    =
                    case text
                        #
                        NULL  => foreground;
                        #
                        THE t =>    {
                                        foreground @ (text_displaylist (t, text_box));
                                    };
                    esac;

                { displaylist => background @ foreground,
                  point_in_gadget,
                  pixels_high_min => 0,
                  pixels_wide_min => 0
                };
            };

        fun default_mouse_click_fn (MOUSE_CLICK_FN_ARG a)
            =
            if (a.button              == evt::button1 
            and a.modifier_keys_state == evt::no_modifier_keys_were_down)
                #
                button_state                    =  a.button_state;
                button_type                     =  a.button_type;
                event                           =  a.event;
                initial_state                   =  a.initial_state;
                needs_redraw_gadget_request     =  a.needs_redraw_gadget_request;
                note_state                      =  a.note_state;
                #
                case event
                    #
                    gt::MOUSEBUTTON_PRESS
                        =>
                        if (button_type != t::IGNORE_MOUSECLICKS)       
                            #
                            note_state  (not button_state);
                            needs_redraw_gadget_request ();
                        fi;

                    gt::MOUSEBUTTON_RELEASE
                        =>
                        if (button_type == t::MOMENTARY_CONTACT)
                            #
                            note_state  initial_state;
                            needs_redraw_gadget_request ();
                        fi;
                esac;

                ();
            fi;

        fun default_mouse_transit_fn (MOUSE_TRANSIT_FN_ARG a)
            =
            case a.transit
                #
                gt::CAME =>  a.needs_redraw_gadget_request ();                                          # So button will lighten when mouse enters it.
                gt::LEFT =>  a.needs_redraw_gadget_request ();                                          # So button will revert  when mosue leaves it.
                _            =>  ();
            esac;

        fun with (options: List(Option))                                                                # PUBLIC.  The point of the 'with' name is that GUI coders can write 'roundbutton::with { this => that, foo => bar, ... }.'
            =
            {
                reliefref       = REF wt::RAISED;                                                       # 
                #
                textref         = REF (NULL: Null_Or(String));
                ontextref       = REF (NULL: Null_Or(String));
                offtextref      = REF (NULL: Null_Or(String));

                (process_options
                  (
                    options,
                    #
                    { button_type                       =>      t::PUSH_ON_PUSH_OFF,
                      # 
                      body_color                         =>  NULL,
                      body_color_with_mousefocus         =>  NULL,
                      body_color_when_on                 =>  NULL,
                      body_color_when_on_with_mousefocus =>  NULL,
                      # 
                      widget_id                         =>  NULL,
                      widget_doc                        =>  "<roundbutton>",
                      # 
                      relief                            =>  *reliefref, 
                      margin                            =>  4,
                      thick                             =>  5,
                      #
                      text                              =>  *textref,
                      on_text                           =>  *ontextref,
                      off_text                          =>  *offtextref,
                      #
                      fonts                             =>  [],
                      font_weight                       =>  (NULL: Null_Or(wt::Font_Weight)),
                      font_size                         =>  (NULL: Null_Or(Int)),
                      #
                      redraw_fn                         =>  default_redraw_fn,
                      mouse_click_fn                    =>  default_mouse_click_fn,
                      mouse_drag_fn                     =>  NULL,
                      mouse_transit_fn                  =>  default_mouse_transit_fn,
                      key_event_fn                      =>  NULL,
                      #
                      initial_state                     =>  FALSE,
                      initially_active                  =>  TRUE,
                      #
                      widget_options                    =>  [],
                      #
                      portwatchers                      =>  [],
                      bool_outs                         =>  [],
                      sitewatchers                      =>  []
                    }
                ) )
                    ->
                    {                                                                                           # These values are globally visible to the subsequenc fns, which can lock them in as needed.
                      button_type,
                      #
                      body_color,
                      body_color_with_mousefocus,
                      body_color_when_on,
                      body_color_when_on_with_mousefocus,
                      #
                      widget_id,
                      widget_doc,
                      # 
                      relief,
                      margin,
                      thick,
                      #
                      text,
                      on_text,
                      off_text,
                      #
                      fonts,
                      font_weight,
                      font_size,
                      #
                      redraw_fn,
                      mouse_click_fn,
                      mouse_drag_fn,
                      mouse_transit_fn,
                      key_event_fn,
                      #
                      initial_state,
                      initially_active,
                      #
                      widget_options,
                      #
                      portwatchers,
                      bool_outs,
                      sitewatchers
                    };

                reliefref       := relief;
                #
                textref         := text;
                ontextref       := on_text;
                offtextref      := off_text;

                #######################################
                # 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
                        );

                button_state  =  REF initial_state;


                button_active
                    =
                    REF initially_active;


                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.
                                        button_state:           Bool,
                                        button_active:          Bool
                                      };        


                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;

                fun note_state (state: Bool)
                    =
                    if(*button_state != state)
                        button_state := state;
                        #
                        apply tell_watcher bool_outs
                            where
                                fun tell_watcher bool_out
                                    =
                                    bool_out state;
                            end;
                    fi;

                #
                # End of state variable section
                ###############################


                #####################
                # Top of port section
                #
                # Here we implement our App_To_Button port:

                fun set_active_to (is_active: Bool)
                    =
                    {   button_active :=  is_active;
                        #
                        note_changed_gadget_activity  is_active;
                    };

                fun set_state_to (state: Bool)
                    =
                    {   note_state state;
                        #
                        needs_redraw_gadget_request ();
                    };

                fun set_button_relief_to (relief: wt::Relief)
                    =
                    {
                        reliefref := relief;
                        #
                        needs_redraw_gadget_request ();
                    };

                fun get_active ()
                    =
                    *button_active;

                fun get_state ()
                    =
                    *button_state;

                fun get_button_relief ()
                    =
                    *reliefref;

                fun get_button_type ()
                    =
                    button_type;


                fun get_button_text      () =  *textref;
                fun get_button_on_text   () =  *ontextref;
                fun get_button_off_text  () =  *offtextref;

                fun set_button_text      t  =   {   textref    := t;    needs_redraw_gadget_request (); };
                fun set_button_on_text   t  =   {   ontextref  := t;    needs_redraw_gadget_request (); };
                fun set_button_off_text  t  =   {   offtextref := t;    needs_redraw_gadget_request (); };


                #
                # 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,                                                 # 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
                    }
                    =
                    {   widget_to_guiboss__global
                            :=  
                            THE (widget_to_guiboss, id);

                        app_to_roundbutton
                          =
                          { id,
                            #
                            get_active,
                            get_state,
                            get_button_relief,
                            get_button_type,
                            #
                            get_button_text,
                            get_button_on_text,
                            get_button_off_text,

                            set_button_text,
                            set_button_on_text,
                            set_button_off_text,

                            set_active_to,
                            set_state_to,
                            set_button_relief_to
                          }
                          : App_To_Roundbutton
                          ;

                        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_roundbutton);
                                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,                                                 # 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.
                    }
                    =
                    {   note_site (id,site);
                        #
                        ();
                    };

                fun redraw_request_fn_wrapper
                    {
                      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-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);
                        #
                        palette =   *theme.current_gadget_colors  { gadget_is_on => *button_state,
                                                                    gadget_mode,
                                                                    popup_nesting_depth,
                                                                    #
                                                                    body_color,
                                                                    body_color_when_on,
                                                                    body_color_with_mousefocus,
                                                                    body_color_when_on_with_mousefocus
                                                                  };

                        text    =   if *button_state
                                        #
                                        case *ontextref
                                            #
                                            THE t =>  THE t;                                                    # Button is ON so use "ON" text.
                                            NULL  =>  *textref;                                                 # Button is ON but no "ON" text so use plain text (or none).
                                        esac;
                                    else
                                        case *offtextref
                                            #
                                            THE t =>  THE t;                                                    # Button is OFF so use "OFF" text.
                                            NULL  =>  *textref;                                                 # Button is OFF but no "OFF" text so use plain text (or none).
                                        esac;
                                    fi;

                        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,
                                theme,
                                do,
                                to,
                                palette,
                                #
                                default_redraw_fn,      
                                #
                                button_state    => *button_state,
                                button_type,
                                button_relief   => *reliefref,

                                text,
                                fonts,
                                font_weight,
                                font_size,

                                margin,
                                thick
                              };

                        (redraw_fn  redraw_fn_arg)
                            ->
                            { displaylist,
                              point_in_gadget,
                              pixels_high_min,
                              pixels_wide_min
                            };

                        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,                                                 # 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.
                      }
                    = 
                    {   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,
                                #
                                button_state    => *button_state,                                               # We don't pass the refcell here because we want client code to make state changes via note_state(), which will properly notify all state-watchers.
                                button_type,
                                button_relief   =>  reliefref,
                                #
                                initial_state,
                                note_state,
                                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,                                                 # 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.
                      }
                    )
                    = 
                    {   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 =>  \\ _ = (),                                            # Default drag behavior for buttons is to do absolutely nothing.
                                #
                                button_state    => *button_state,                                               # We don't pass the refcell here because we want client code to make state changes via note_state(), which will properly notify all state-watchers.
                                button_type,
                                button_relief   =>  reliefref,
                                #
                                initial_state,
                                note_state,
                                needs_redraw_gadget_request
                              };

                        case mouse_drag_fn
                            #
                            THE mouse_drag_fn =>   mouse_drag_fn  mouse_drag_fn_arg;
                            NULL              =>   ();                                                          # We do not expect this case to happen: If mouse_drag_fn is NULL mouse_drag_fn_wrapper should not have been registered with widget-imp so we should never get called.
                        esac;
                    };

                fun mouse_transit_fn_wrapper
                      #
                      ( arg as
                        {
                          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.
                        }
                      ) 
                    = 
                    {   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,
                                #
                                button_state    => *button_state,                                               # We don't pass the refcell here because we want client code to make state changes via note_state(), which will properly notify all state-watchers.
                                button_type,
                                button_relief   =>  reliefref,
                                #
                                initial_state,
                                note_state,
                                needs_redraw_gadget_request
                              };

                        mouse_transit_fn  mouse_transit_fn_arg;

                        ();
                    };

                fun key_event_fn_wrapper
                      {
                        id:                             Id,                                                     # Unique Id for widget.
                        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,                                  # 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 buttons is to do absolutely nothing.
                                #
                                button_state    => *button_state,                                               # We don't pass the refcell here because we want client code to make state changes via note_state(), which will properly notify all state-watchers.
                                button_type,
                                button_relief   =>  reliefref,
                                #
                                initial_state,
                                note_state,
                                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_drag_fn
                        #
                        THE _ =>  (wi::MOUSE_DRAG_FN mouse_drag_fn_wrapper)       ! widget_options;             # Register for drag 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_TRANSIT_FN                mouse_transit_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;




Comments and suggestions to: bugs@mythryl.org

PreviousUpNext