PreviousUpNext

15.4.1456  src/lib/x-kit/widget/leaf/horizontal-float-slider.pkg

## horizontal-float-slider.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



# 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 horizontal_float_slider
    :       Horizontal_Float_Slider                                                     # Horizontal_Float_Slider               is from   src/lib/x-kit/widget/leaf/horizontal-float-slider.api
    {
        App_To_Horizontal_Float_Slider
          =
          { id:                         Id,
            #
            get_active:                 Void -> Bool,
            get_value:                  Void -> Float,
            #
            get_lower_limit:            Void -> Float,
            get_upper_limit:            Void -> Float,
            get_coverage:               Void -> Float,
            #
            get_slider_text:            Void -> Null_Or(String),

            set_slider_text:            Null_Or(String) -> Void,
            #
            set_active_to:              Bool  -> Void,
            set_value_to:               Float -> Void,                                  # Also calls gadget_to_guiboss.needs_redraw_gadget_request(id);
            #
            set_lower_limit_to:         Float -> Void,
            set_upper_limit_to:         Float -> Void,
            set_coverage_to:            Float -> Void
          };


        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,

                lower_limit:                    Float,
                upper_limit:                    Float,
                coverage:                       Float,
                #
                show_limits:                    Bool,
                show_value:                     Bool,
                #
                slider_value:                   Float,                                  # A value between lower_limit and upper_limit.
                slider_relief:                  wt::Relief,                             # Is the slider 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),

                no_box:                         Bool,
                margin:                         Int,
                thick:                          Int
              }
        withtype
        Redraw_Fn
          =
          Redraw_Fn_Arg
          ->
          { displaylist:                gd::Gui_Displaylist,
            point_in_gadget:            Null_Or(g2d::Point -> Bool),                    # 
            point_to_value:             g2d::Point -> Float,                            # 
            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,
                #
                lower_limit:                    Float,
                upper_limit:                    Float,
                coverage:                       Float,
                #
                show_limits:                    Bool,
                show_value:                     Bool,
                #
                slider_value:                   Float,                                  # A value between lower_limit and upper_limit.
                slider_relief:                  wt::Relief,                             # Is the slider outline a slope, a ridge, or a flat band?
                point_to_value:                 g2d::Point -> Float,
                #
                initial_value:                  Float,                                  # Original state of slider.
                note_value:                     Float -> Void,                          # Change state of slider. 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 slider 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,
                #
                lower_limit:                    Float,
                upper_limit:                    Float,
                coverage:                       Float,
                #
                show_limits:                    Bool,
                show_value:                     Bool,
                #
                slider_value:                   Float,                                  # A value between lower_limit and upper_limit.
                slider_relief:                  wt::Relief,                             # Is the slider outline a slope, a ridge, or a flat band?
                point_to_value:                 g2d::Point -> Float,
                #
                initial_value:                  Float,                                  # Original state of slider.
                note_value:                     Float -> Void,                          # Change state of slider. 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 slider 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,
                #
                lower_limit:                    Float,
                upper_limit:                    Float,
                coverage:                       Float,
                #
                show_limits:                    Bool,
                show_value:                     Bool,
                #
                slider_value:                   Float,                                  # A value between lower_limit and upper_limit.
                slider_relief:                  wt::Relief,                             # Is the slider outline a slope, a ridge, or a flat band?
                point_to_value:                 g2d::Point -> Float,
                #
                initial_value:                  Float,                                  # Original state of slider.
                note_value:                     Float -> Void,                          # Change state of slider. 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 slider 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,
                #
                lower_limit:                    Float,
                upper_limit:                    Float,
                coverage:                       Float,
                #
                show_limits:                    Bool,
                show_value:                     Bool,
                #
                slider_value:                   Float,                                  # A value between lower_limit and upper_limit.
                slider_relief:                  wt::Relief,                             # Is the slider outline a slope, a ridge, or a flat band?
                point_to_value:                 g2d::Point -> Float,
                #
                initial_value:                  Float,                                  # Original state of slider.
                note_value:                     Float -> Void,                          # Change state of slider. 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 slider 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
                #
                | LOWER_LIMIT           Float                                           # Smallest value which slider value is allowed to assume.   Defaults to 0.0.
                | UPPER_LIMIT           Float                                           # Largest  value which slider value is allowed to assume.   Defaults to 1.0.
                | COVERAGE              Float                                           
                #
                | SHOW_LIMITS           Bool                                            # If TRUE, display limits in decimal on slider widget.      Defaults to TRUE.
                | SHOW_VALUE            Bool                                            # If TRUE, display value  in decimal on slider widget.      Defaults to TRUE.
                #
                | INITIAL_VALUE         Float
                | INITIALLY_ACTIVE      Bool
                #
                | BODY_COLOR                            rgb::Rgb
                | BODY_COLOR_WITH_MOUSEFOCUS            rgb::Rgb
                #
                | ID                    Id
                | DOC                   String
                #
                | RELIEF                wt::Relief                                      # Should slider boundary be drawn flat, raised, sunken, ridged or grooved?
                | MARGIN                Int                                             # How many pixels to inset slider relative to its assigned window site.  Default is 4.
                | THICK                 Int                                             # Thickness of lines (well, polygons) forming slider.  Default is 5.
                | NO_BOX                                                                # Do not draw a box around slider gutter.
                #
                | TEXT                  String                                          # Text to draw inside slider.  Default is "".
                #
                | 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.
                #
                | FLOAT_OUT             (Float -> Void)                                 # Widget's current state              will be sent to these fns each time state changes.
                | PORTWATCHER           ( Null_Or(App_To_Horizontal_Float_Slider)       # Widget's app port                   will be sent to these fns at widget startup.
                                          ->
                                          Void
                                        )
                | 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),
              #
              { body_color,
                body_color_with_mousefocus,
                #
                widget_id,
                widget_doc,
                #
                relief,
                margin,
                thick,
                no_box,
                #
                text,
                #
                fonts,
                font_weight,
                font_size,
                #
                redraw_fn,
                mouse_click_fn,
                mouse_drag_fn,
                mouse_transit_fn,
                key_event_fn,
                #
                lower_limit,
                upper_limit,
                coverage,
                #
                show_limits,
                show_value,
                #
                initial_value,
                initially_active,
                #
                widget_options,
                #
                portwatchers,
                float_outs,
                sitewatchers
              }
            )
            =
            {   my_body_color                           =  REF body_color;
                my_body_color_with_mousefocus           =  REF body_color_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_no_box                               =  REF  no_box;
                #
                my_text                                 =  REF  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_lower_limit                          =       lower_limit;
                my_upper_limit                          =       upper_limit;
                my_coverage                             =       coverage;
                #
                my_show_limits                          =  REF  show_limits;
                my_show_value                           =  REF  show_value;
                #
                my_initial_value                        =  REF  initial_value;
                my_initially_active                     =  REF  initially_active;
                #
                my_widget_options                       =  REF  widget_options;
                #
                my_portwatchers                         =  REF  portwatchers;
                my_float_outs                           =  REF  float_outs;
                my_sitewatchers                         =  REF  sitewatchers;
                #

                apply  do_option  options
                where
                    fun do_option (LOWER_LIMIT                          b) =>   my_lower_limit          :=  b;
                        do_option (UPPER_LIMIT                          b) =>   my_upper_limit          :=  b;
                        do_option (COVERAGE                             b) =>   my_coverage             :=  b;
                        #
                        do_option (SHOW_LIMITS                          b) =>   my_show_limits          :=  b;
                        do_option (SHOW_VALUE                           b) =>   my_show_value           :=  b;
                        #
                        do_option (INITIAL_VALUE                        b) =>   my_initial_value        :=  b;
                        do_option (INITIALLY_ACTIVE                     b) =>   my_initially_active     :=  b;
                        #
                        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 (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 (NO_BOX                                ) =>   my_no_box               :=  TRUE;
                        #
                        do_option (TEXT                                 t) =>   my_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        :=      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 (FLOAT_OUT                            c) =>   my_float_outs           :=  c ! *my_float_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;

                { body_color                            =>  *my_body_color,
                  body_color_with_mousefocus            =>  *my_body_color_with_mousefocus,
                  #
                  widget_id                             =>  *my_widget_id,
                  widget_doc                            =>  *my_widget_doc,
                  #
                  relief                                =>  *my_relief,
                  margin                                =>  *my_margin,
                  thick                                 =>  *my_thick,
                  no_box                                =>  *my_no_box,
                  #
                  text                                  =>  *my_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,
                  #
#                 lower_limit                           =>   my_lower_limit,
#                 upper_limit                           =>   my_upper_limit,
#                 coverage                              =>   my_coverage,
                  #
                  show_limits                           =>  *my_show_limits,
                  show_value                            =>  *my_show_value,
                  #
                  initial_value                         =>  *my_initial_value,
                  initially_active                      =>  *my_initially_active,
                  #
                  widget_options                        =>  *my_widget_options,
                  #
                  portwatchers                          =>  *my_portwatchers,
                  float_outs                            =>  *my_float_outs,
                  #     
                  sitewatchers                          =>  *my_sitewatchers
                };
            };


        fun default_redraw_fn (REDRAW_FN_ARG a)                                                 # Handle a guiboss request to redraw ourself.
            =
            {   background_box =  a.site;
                coverage       =  a.coverage;
                lower_limit    =  a.lower_limit;
                margin         =  a.margin;
                relief         =  a.slider_relief;
                site           =  a.site;
                slider_value   =  a.slider_value;
                thick          =  a.thick;
                upper_limit    =  a.upper_limit;

                background     = [ gd::COLOR (a.palette.surround_color,  [ gd::FILLED_BOXES [ background_box ]]) ];

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

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

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

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

                        fontnames;
                    };


                fun get_text_dimensions (text: String)
                    =
                    {   g =  wti::get__guiboss_to_hostwindow  a.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 point_to_value (point: g2d::Point)
                    =
                    {   gutter_box  ->  { row, col, high, wide };
                        #
                        wide         =  int::max (wide, 1);                                                                     # Prevent divide-by-zero;

                        fpixels      =  float::from_int wide;
                        fvalues      =  upper_limit - lower_limit;

                        p_to_v       =  fvalues / fpixels;

                        value        =  float::from_int (point.col - col)  *  p_to_v;

                        value        =  float::min (value, upper_limit);
                        value        =  float::max (value, lower_limit);

                        value;
                    };

                fun thumb_displaylist { lower_limit, slider_value, upper_limit, gutter_box, coverage }                          # Thumb shows portion of file currently visible in window. If coverage==1.0, all the file is visible and thumb fills gutter.  If coverage==0.5, half the file is visible, and thumb fills half of gutter.
                    =                                                                                                           # Position of thumb shows which part of file is visible: Top, middle, bottom, whatever.
                    {   gutter_box  ->  { row, col, high, wide };
                        #
                        thumb_width  =  float::round ((float::from_int wide) * coverage);                                       # Pixel height of thumb.
                        thumb_range  =  (float::from_int wide) * (1.0 - coverage);                                              # Number of pixels which thumb is free to move.
                        value_range  =   upper_limit - lower_limit;                                                             # Number of values which slider_value is free to range over.
                        fvalue       =   upper_limit - slider_value;                                                            # Zero-based value of slider_value.
                        v_to_p       =  thumb_range / value_range;                                                              # Conversion factor from slider_value range to thumb range.
                        thumb_lo     =  col + wide - (float::round (fvalue * v_to_p));
                        thumb_hi     =  thumb_lo - thumb_width;

                        thumb_box    =  { row => row + 2, col => thumb_hi, high => high - 4, wide => thumb_width };             # 

                        thumb_body   = [ gd::COLOR ( rgb::black, [ gd::FILLED_BOXES [ thumb_box ]]) ];

                        thumb_body;
                    };
                    
                fun cursor_displaylist { lower_limit, slider_value, upper_limit, gutter_box }
                    =
                    {   gutter_box  ->  { row, col, high, wide };
                        #
                        fpixels      =  float::from_int wide;
                        fvalues      =  upper_limit - lower_limit;
                        fvalue       =  slider_value;

                        v_to_p       =  fpixels / fvalues;

                        cursor_mid   =  col  +  (float::round  (fvalue * v_to_p));

                        cursor_wide2 =  10;                                                                                     # Half-width of cursor.
                        cursor_width =  2*cursor_wide2 + 1;

                        cursor_col   =  cursor_mid - cursor_wide2;

                        cursor_box   =  { row => row + 2, col => cursor_col, high => high - 4, wide => cursor_width };          # "+ 2" and "- 4" so the cursor outline is cleanly separated from the gutter frame.

                        (g2d::box::box_corners  cursor_box)
                            ->
                            { upper_left, lower_left, lower_right, upper_right };

                        top_mid = g2d::point::mean [ upper_left, upper_right ];
                        bot_mid = g2d::point::mean [ lower_left, lower_right ];

                        cursor_outline = [ bot_mid, top_mid, upper_left, lower_left, lower_right, upper_right, top_mid ];

                        [ gd::COLOR ( rgb::white, [ gd::FILLED_BOXES [ cursor_box ]]) ]
                        @
                        [ gd::COLOR ( rgb::rgb_mix01(0.9,rgb::black,rgb::white), [ gd::LINE_THICKNESS (0, [ gd::PATH cursor_outline ]) ]) ];
                    };
                    

                foreground =  [ gd::COLOR (a.palette.body_color, [ gd::FILLED_POLYGON (g2d::box::to_points inner_box) ]) ];                                     # Interior of gutter. We draw this first because 3D outline occupies same bounding box:

                foreground =    if (a.coverage == 0.0)   foreground;
                                else                     foreground @ thumb_displaylist { lower_limit, slider_value, upper_limit, gutter_box, coverage };
                                fi;

                foreground =  foreground  @  cursor_displaylist { lower_limit, slider_value, upper_limit, gutter_box };                                         # Draw cursor next because we want it to overwrite gutter interior but be overwritten by gutter frame.

                foreground =    case a.no_box   FALSE =>  foreground @ *a.theme.pictureframe a.palette { box => inner_box, thick, relief };                     # 3-D outline for gutter.
                                                TRUE  =>  foreground;
                                esac;   


                foreground =    {   fontnames =  get_fontnames ();

                                    lotext  =   sprintf "%.3g" lower_limit;
                                    mitext  =   sprintf "%.3g" slider_value;
                                    hitext  =   sprintf "%.3g" upper_limit;

                                    lodims  =   get_text_dimensions  lotext;
                                    hidims  =   get_text_dimensions  hitext;

                                    mipoint =   g2d::box::midpoint inner_box;

                                    textrow =   mipoint.row - lodims.font_descent + ((lodims.font_ascent + lodims.font_descent) / 2); 

                                    lopoint =   { row => textrow, col => site.col + 10             };
                                    mipoint =   { row => textrow, col => mipoint.col               };
                                    hipoint =   { row => textrow, col => site.col + site.wide - 10 };


                                    lodraw  =   [ gd::PUT_TEXT   ( gd::TO_RIGHT_OF_POINT,
                                                                   [ gd::TEXT (lopoint, lotext) ]
                                                                 )
                                                ];      

                                    midraw  =   case (a.text, a.show_value)
                                                    #
                                                    (NULL, FALSE ) =>   [ ];

                                                    (NULL, TRUE  ) =>   [ gd::PUT_TEXT   ( gd::CENTERED_ON_POINT,
                                                                                           [ gd::TEXT (mipoint, mitext) ]
                                                                                         )
                                                                        ];
                                                    (THE t, FALSE) =>   [ gd::PUT_TEXT   ( gd::CENTERED_ON_POINT,
                                                                                           [ gd::TEXT (mipoint, t     ) ]
                                                                                         )
                                                                        ];
                                                    (THE t, TRUE ) =>   [ gd::PUT_TEXT   ( gd::TO_LEFT_OF_POINT,
                                                                                           [ gd::TEXT (mipoint, t + ": ") ]
                                                                                         ),
                                                                          gd::PUT_TEXT   ( gd::TO_RIGHT_OF_POINT,
                                                                                           [ gd::TEXT (mipoint, mitext) ]
                                                                                         )
                                                                        ];
                                                esac;


                                    hidraw  =   [ gd::PUT_TEXT   ( gd::TO_LEFT_OF_POINT,
                                                                   [ gd::TEXT (hipoint, hitext) ]
                                                                 )
                                                ];      


                                    display_list =  if a.show_limits   lodraw @ midraw @ hidraw;
                                                    else                        midraw;
                                                    fi; 



                                    display_list =  case display_list   [] =>  [];
                                                                        _  =>  [ gd::COLOR ( a.palette.text_color, [ gd::FONT (fontnames, display_list) ] ) ];
                                                    esac;

                                    foreground @ display_list;
                                };
                

                fun point_in_gadget (point: g2d::Point)
                    =
                    g2d::point::in_box (point, inner_box);

                point_in_gadget = THE point_in_gadget;


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

        fun default_mouse_click_fn (MOUSE_CLICK_FN_ARG a)
            =
            if (a.modifier_keys_state == evt::no_modifier_keys_were_down)
                #
                button                          =  a.button;
                lower_limit                     =  a.lower_limit;
                needs_redraw_gadget_request     =  a.needs_redraw_gadget_request;
                note_value                      =  a.note_value;
                slider_value                    =  a.slider_value;
                upper_limit                     =  a.upper_limit;

                if (button == evt::button4)                                             # Mousewheel forward.
                    #
                    newval = slider_value  +  0.001 * (upper_limit - lower_limit);      # 0.001 is arbitrary but plausible.  "Arbitrary" usually means "will eventually have to be made configurable." :-)
                    
                    note_value (float::min (newval, upper_limit));                      # Keep slider value between lower_limit and upper_limit.

                    needs_redraw_gadget_request ();                                     # Tell guiboss_imp that we need to be redrawn.
                fi;
                    
                if (button == evt::button5)                                             # Mousewheel backward.
                    #
                    newval = slider_value  -  0.001 * (upper_limit - lower_limit);      # 0.001 is arbitrary but plausible.
                    
                    note_value (float::max (newval, lower_limit));                      # Keep slider value between lower_limit and upper_limit.

                    needs_redraw_gadget_request ();                                     # Tell guiboss_imp that we need to be redrawn.
                fi;

                ();
            fi;

        fun default_mouse_drag_fn
            (
              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,
                  #
                  lower_limit:                  Float,
                  upper_limit:                  Float,
                  coverage:                     Float,
                  #
                  show_limits:                  Bool,
                  show_value:                   Bool,
                  #
                  slider_value:                 Float,                                  # A value between lower_limit and upper_limit.
                  slider_relief:                wt::Relief,                             # Is the slider outline a slope, a ridge, or a flat band?
                  point_to_value:               g2d::Point -> Float,
                  #
                  initial_value:                Float,                                  # Original state of slider.
                  note_value:                   Float -> Void,                          # Change state of slider. 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 slider needs to be redrawn (i.e., sent a redraw_gadget_request()).
                }
            )
            =
            {
                if  (   modifier_keys_state == evt::no_modifier_keys_were_down
                        and
                        mousebuttons_state
                        == 
                        { mousebutton_1_was_down => TRUE,
                          mousebutton_2_was_down => FALSE,
                          mousebutton_3_was_down => FALSE,
                          mousebutton_4_was_down => FALSE,
                          mousebutton_5_was_down => FALSE
                        }
                    )

                    # At the moment we don't care which phase we're in, so we ignore it.
                    # The following at least documents how to key on phase if desired:
                    #
                    case phase
                        #
                        gt::DONE => ();                                                 # 
                        gt::OPEN => ();                                                 #
                        gt::DRAG => ();                                                 # 
                    esac;

                    value =  point_to_value  event_point;

                    note_value value;
                    needs_redraw_gadget_request ();
                fi;

                ();
            };

        fun default_mouse_transit_fn (MOUSE_TRANSIT_FN_ARG a)
            =
            case a.transit
                #
                gt::CAME =>  a.needs_redraw_gadget_request ();                                          # So slider will lighten when mouse enters it.
                gt::LEFT =>  a.needs_redraw_gadget_request ();                                          # So slider 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 'horizontal_float_slider::with { this => that, foo => bar, ... }.'
            =
            {
                textref         =  REF (NULL: Null_Or(String));

                lower_limit     =  REF 0.0;
                upper_limit     =  REF 1.0;
                coverage        =  REF 0.0;

                point_to_value  =  REF (\\ _ = *lower_limit);

                (process_options
                  (
                    options,
                    #
                    { body_color                         =>  NULL,
                      body_color_with_mousefocus         =>  NULL,
                      # 
                      widget_id                         =>  NULL,
                      widget_doc                        =>  "<horizontal_float_slider>",
                      # 
                      relief                            =>  wt::SUNKEN,
                      margin                            =>  0,
                      thick                             =>  5,
                      no_box                            =>  FALSE,
                      #
                      text                              =>  *textref,
                      #
                      fonts                             =>  [],
                      font_weight                       =>  THE wt::BOLD_FONT,                                  # Bold seems to work much better than roman for buttons and sliders.
                      font_size                         =>  (NULL: Null_Or(Int)),
                      #
                      redraw_fn                         =>  default_redraw_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                      =>  NULL,
                      #
                      lower_limit,
                      upper_limit,
                      coverage,
                      #
                      show_limits                       =>  TRUE,
                      show_value                        =>  TRUE,
                      #
                      initial_value                     =>  0.5,
                      initially_active                  =>  TRUE,
                      #
                      widget_options                    =>  [],
                      #
                      portwatchers                      =>  [],
                      float_outs                        =>  [],
                      sitewatchers                      =>  []
                    }
                ) )
                    ->
                    {                                                                                           # These values are globally visible to the subsequenc fns, which can lock them in as needed.
                      body_color,
                      body_color_with_mousefocus,
                      #
                      widget_id,
                      widget_doc,
                      # 
                      relief,
                      margin,
                      thick,
                      no_box,
                      #
                      text,
                      #
                      fonts,
                      font_weight,
                      font_size,
                      #
                      redraw_fn,
                      mouse_click_fn,
                      mouse_drag_fn,
                      mouse_transit_fn,
                      key_event_fn,
                      #
#                     lower_limit,
#                     upper_limit,
#                     coverage,
                      #
                      show_limits,      
                      show_value,       
                      #
                      initial_value,
                      initially_active,
                      #
                      widget_options,
                      #
                      portwatchers,
                      float_outs,
                      sitewatchers
                    };

                textref         := 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
                        );

                slider_value  =  REF initial_value;


                slider_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.
                                        slider_value:           Float,
                                        slider_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_value (state: Float)
                    =
                    if(*slider_value != state)
                        slider_value := state;
                        #
                        apply tell_watcher float_outs
                            where
                                fun tell_watcher float_out
                                    =
                                    float_out state;
                            end;
                    fi;

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


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

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

                fun set_value_to (state: Float)
                    =
                    {   note_value state;
                        #
                        needs_redraw_gadget_request ();
                    };

                fun get_active ()
                    =
                    *slider_active;

                fun get_value ()
                    =
                    *slider_value;



                fun get_slider_text      () =      *textref;
                fun set_slider_text      t  =   {   textref    := t;
                                                    needs_redraw_gadget_request ();
                                                };

                fun get_lower_limit      () =      *lower_limit;
                fun set_lower_limit_to   i  =   {   lower_limit := i;
                                                    if (*slider_value <  *lower_limit)
                                                         slider_value := *lower_limit;
                                                    fi;
                                                    if (*upper_limit  <  *lower_limit)
                                                         upper_limit  := *lower_limit;
                                                    fi;
                                                    needs_redraw_gadget_request ();
                                                };

                fun get_upper_limit      () =      *upper_limit;
                fun set_upper_limit_to   i  =   {   upper_limit := i;
                                                    if (*slider_value >  *upper_limit)
                                                         slider_value := *upper_limit;
                                                    fi;
                                                    if (*lower_limit  >  *upper_limit)
                                                         lower_limit  := *upper_limit;
                                                    fi;
                                                    needs_redraw_gadget_request ();
                                                };

                fun get_coverage         () =      *coverage;
                fun set_coverage_to      f  =   {   f = float::max (0.0, f);
                                                    f = float::min (1.0, f);
                                                    coverage := f;
                                                    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_horizontal_float_slider
                          =
                          { id,
                            #
                            get_active,
                            get_value,
                            #
                            get_lower_limit,
                            get_upper_limit,
                            get_coverage,
                            #
                            get_slider_text,

                            set_slider_text,
                            #   
                            set_active_to,
                            set_value_to,
                            #
                            set_lower_limit_to,
                            set_upper_limit_to,
                            set_coverage_to
                          }
                          : App_To_Horizontal_Float_Slider
                          ;

                        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_horizontal_float_slider);
                                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 => FALSE,
                                                                    gadget_mode,
                                                                    popup_nesting_depth,
                                                                    #
                                                                    body_color,
                                                                    body_color_with_mousefocus,
                                                                    body_color_when_on                 => NULL,
                                                                    body_color_when_on_with_mousefocus => NULL
                                                                  };

                        text        =   *textref;

                        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,      
                                #
                                lower_limit     => *lower_limit,
                                upper_limit     => *upper_limit,
                                coverage        => *coverage,
                                #
                                show_limits,
                                show_value,
                                #
                                slider_value    => *slider_value,
                                slider_relief   => relief,

                                text,
                                fonts,
                                font_weight,
                                font_size,

                                no_box,
                                margin,
                                thick
                              };

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

                        point_to_value :=  p2v;

                        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,
                                #
                                lower_limit     => *lower_limit,
                                upper_limit     => *upper_limit,
                                coverage        => *coverage,
                                #
                                show_limits,
                                show_value,
                                #
                                slider_value    => *slider_value,                                               # We don't pass the refcell here because we want client code to make state changes via note_value(), which will properly notify all state-watchers.
                                slider_relief   =>  relief,
                                point_to_value  => *point_to_value,
                                #
                                initial_value,
                                note_value,
                                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,
                                button,
                                phase,
                                modifier_keys_state,
                                mousebuttons_state,
                                widget_to_guiboss,
                                theme,
                                do,
                                to,
                                #
                                default_mouse_drag_fn,
                                #
                                lower_limit     => *lower_limit,
                                upper_limit     => *upper_limit,
                                coverage        => *coverage,
                                #
                                show_limits,
                                show_value,
                                #
                                slider_value    => *slider_value,                                               # We don't pass the refcell here because we want client code to make state changes via note_value(), which will properly notify all state-watchers.
                                slider_relief   =>  relief,
                                point_to_value  => *point_to_value,
                                #
                                initial_value,
                                note_value,
                                needs_redraw_gadget_request
                              };

                        mouse_drag_fn  mouse_drag_fn_arg;
                    };

                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,
                                #
                                lower_limit     => *lower_limit,
                                upper_limit     => *upper_limit,
                                coverage        => *coverage,
                                #
                                show_limits,
                                show_value,
                                #
                                slider_value    => *slider_value,                                               # We don't pass the refcell here because we want client code to make state changes via note_value(), which will properly notify all state-watchers.
                                slider_relief   =>  relief,
                                point_to_value  => *point_to_value,
                                #
                                initial_value,
                                note_value,
                                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 sliders is to do absolutely nothing.
                                #
                                lower_limit     => *lower_limit,
                                upper_limit     => *upper_limit,
                                coverage        => *coverage,
                                #
                                show_limits,
                                show_value,
                                #
                                slider_value    => *slider_value,                                               # We don't pass the refcell here because we want client code to make state changes via note_value(), which will properly notify all state-watchers.
                                slider_relief   =>  relief,
                                point_to_value  => *point_to_value,
                                #
                                initial_value,
                                note_value,
                                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 key_event_fn
                        #
                        THE _ =>  (wi::KEY_EVENT_FN key_event_fn_wrapper)         ! widget_options;             # Register for key events only if we are going to use them.
                        NULL  =>                                                    widget_options;
                    esac;

                widget_options
                    =
                    case widget_id
                        #
                        THE id =>  (wi::ID id)                                    ! widget_options;             # 
                        NULL   =>                                                   widget_options;
                    esac;

                widget_options
                  =
                  [ wi::STARTUP_FN                      startup_fn,                                             # We always register for these five because our base behavior depends on them.
                    wi::SHUTDOWN_FN                     shutdown_fn,
                    wi::INITIALIZE_GADGET_FN            initialize_gadget_fn,
                    wi::REDRAW_REQUEST_FN               redraw_request_fn_wrapper,
                    wi::MOUSE_CLICK_FN                  mouse_click_fn_wrapper,
                    wi::MOUSE_DRAG_FN                   mouse_drag_fn_wrapper,
                    wi::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