PreviousUpNext

15.4.1455  src/lib/x-kit/widget/leaf/frame.pkg

# frame.pkg
#
# See also:
#     src/lib/x-kit/widget/leaf/frame.pkg
#     src/lib/x-kit/widget/leaf/diamondbutton.pkg
#     src/lib/x-kit/widget/leaf/roundbutton.pkg

# Compiled by:
#     src/lib/x-kit/widget/xkit-widget.sublib





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

# This package gets used in:
#
#     

stipulate
    include package   threadkit;                                                # threadkit                     is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    include package   geometry2d;                                               # geometry2d                    is from   src/lib/std/2d/geometry2d.pkg
    #
    package evt =  gui_event_types;                                             # gui_event_types               is from   src/lib/x-kit/widget/gui/gui-event-types.pkg
    package g2p =  gadget_to_pixmap;                                            # gadget_to_pixmap              is from   src/lib/x-kit/widget/theme/gadget-to-pixmap.pkg
    package gd  =  gui_displaylist;                                             # gui_displaylist               is from   src/lib/x-kit/widget/theme/gui-displaylist.pkg
    package gt  =  guiboss_types;                                               # guiboss_types                 is from   src/lib/x-kit/widget/gui/guiboss-types.pkg
    package gtj =  guiboss_types_junk;                                          # guiboss_types_junk            is from   src/lib/x-kit/widget/gui/guiboss-types-junk.pkg
    package wt  =  widget_theme;                                                # widget_theme                  is from   src/lib/x-kit/widget/theme/widget/widget-theme.pkg
    package r8  =  rgb8;                                                        # rgb8                          is from   src/lib/x-kit/xclient/src/color/rgb8.pkg
    package r64 =  rgb;                                                         # rgb                           is from   src/lib/x-kit/xclient/src/color/rgb.pkg
    package wi  =  widget_imp;                                                  # widget_imp                    is from   src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg
    package g2d =  geometry2d;                                                  # geometry2d                    is from   src/lib/std/2d/geometry2d.pkg
    package g2j =  geometry2d_junk;                                             # geometry2d_junk               is from   src/lib/std/2d/geometry2d-junk.pkg
    package mtx =  rw_matrix;                                                   # rw_matrix                     is from   src/lib/std/src/rw-matrix.pkg
    package pp  =  standard_prettyprinter;                                      # standard_prettyprinter        is from   src/lib/prettyprint/big/src/standard-prettyprinter.pkg
    package gtg =  guiboss_to_guishim;                                          # guiboss_to_guishim            is from   src/lib/x-kit/widget/theme/guiboss-to-guishim.pkg

    nb =  log::note_on_stderr;                                                  # log                           is from   src/lib/std/src/log.pkg
herein

    package frame
    :       Frame                                                               # Frame                 is from   src/lib/x-kit/widget/leaf/frame.api
    {
        App_To_Frame
          =
          { id:                                 Id
          };


        Redraw_Fn_Arg
            =
            REDRAW_FN_ARG
              {
                id:                             Id,                             # Unique Id for widget.
                doc:                            String,                         # Human-readable description of this widget, for debug and inspection.
                frame_number:                   Int,                            # 1,2,3,... Purely for convenience of widget, guiboss-imp makes no use of this.
                frame_indent_hint:              gt::Frame_Indent_Hint,
                frame_relief:                   wt::Relief,
                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
              }
        withtype
        Redraw_Fn
          =
          Redraw_Fn_Arg
          ->
          { displaylist:                gd::Gui_Displaylist,
            point_in_gadget:            Null_Or(g2d::Point -> Bool)             # 
          }
          ;



        Mouse_Click_Fn_Arg
            =
            MOUSE_CLICK_FN_ARG                                                  # Needs to be a sumtype because of recursive reference in default_mouse_click_fn.
              { id:                             Id,                             # Unique Id for widget.
                doc:                            String,                         # Human-readable description of this widget, for debug and inspection.
                event:                          gt::Mousebutton_Event,          # MOUSEBUTTON_PRESS or MOUSEBUTTON_RELEASE.
                button:                         evt::Mousebutton,               # Which mousebutton was pressed/released.
                point:                          g2d::Point,                     # Where the mouse was.
                widget_layout_hint:             gt::Widget_Layout_Hint,
                frame_indent_hint:              gt::Frame_Indent_Hint,
                frame_relief:                   wt::Relief,
                site:                           g2d::Box,                       # Widget's assigned area in window coordinates.
                modifier_keys_state:            evt::Modifier_Keys_State,       # State of the modifier keys (shift, ctrl...).
                mousebuttons_state:             evt::Mousebuttons_State,        # State of mouse buttons as a bool record.
                widget_to_guiboss:              gt::Widget_To_Guiboss,
                theme:                          wt::Widget_Theme,
                do:                             (Void -> Void) -> Void,         # Used by widget subthreads to execute code in main widget microthread.
                to:                             Replyqueue,                     # Used to call 'pass_*' methods in other imps.
                #
                default_mouse_click_fn:         Mouse_Click_Fn,
                #
                needs_redraw_gadget_request:    Void -> Void                    # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
              }
        withtype
        Mouse_Click_Fn = Mouse_Click_Fn_Arg -> Void;



        Mouse_Drag_Fn_Arg
            =
            MOUSE_DRAG_FN_ARG
              {
                id:                             Id,                             # Unique Id for widget.
                doc:                            String,                         # Human-readable description of this widget, for debug and inspection.
                event_point:                    g2d::Point,
                start_point:                    g2d::Point,
                last_point:                     g2d::Point,
                widget_layout_hint:             gt::Widget_Layout_Hint,
                frame_indent_hint:              gt::Frame_Indent_Hint,
                frame_relief:                   wt::Relief,
                site:                           g2d::Box,                       # Widget's assigned area in window coordinates.
                phase:                          gt::Drag_Phase, 
                button:                         evt::Mousebutton,
                modifier_keys_state:            evt::Modifier_Keys_State,       # State of the modifier keys (shift, ctrl...).
                mousebuttons_state:             evt::Mousebuttons_State,        # State of mouse buttons as a bool record.
                widget_to_guiboss:              gt::Widget_To_Guiboss,
                theme:                          wt::Widget_Theme,
                do:                             (Void -> Void) -> Void,         # Used by widget subthreads to execute code in main widget microthread.
                to:                             Replyqueue,                     # Used to call 'pass_*' methods in other imps.
                #
                default_mouse_drag_fn:          Mouse_Drag_Fn,
                #
                needs_redraw_gadget_request:    Void -> Void                    # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
              }
        withtype
        Mouse_Drag_Fn =  Mouse_Drag_Fn_Arg -> Void;



        Mouse_Transit_Fn_Arg                                                    # Note that buttons are always all up in a mouse-transit event -- otherwise it is a mouse-drag event.
            =
            MOUSE_TRANSIT_FN_ARG
              {
                id:                             Id,                             # Unique Id for widget.
                doc:                            String,                         # Human-readable description of this widget, for debug and inspection.
                event_point:                    g2d::Point,
                widget_layout_hint:             gt::Widget_Layout_Hint,
                frame_indent_hint:              gt::Frame_Indent_Hint,
                frame_relief:                   wt::Relief,
                site:                           g2d::Box,                       # Widget's assigned area in window coordinates.
                transit:                        gt::Gadget_Transit,             # Mouse is entering (CAME) or leaving (LEFT) widget, or moving (MOVE) across it.
                modifier_keys_state:            evt::Modifier_Keys_State,       # State of the modifier keys (shift, ctrl...).
                widget_to_guiboss:              gt::Widget_To_Guiboss,
                theme:                          wt::Widget_Theme,
                do:                             (Void -> Void) -> Void,         # Used by widget subthreads to execute code in main widget microthread.
                to:                             Replyqueue,                     # Used to call 'pass_*' methods in other imps.
                #
                default_mouse_transit_fn:       Mouse_Transit_Fn,
                #
                needs_redraw_gadget_request:    Void -> Void                    # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
              }
        withtype
        Mouse_Transit_Fn =  Mouse_Transit_Fn_Arg -> Void;



        Key_Event_Fn_Arg
            =
            KEY_EVENT_FN_ARG
              {
                id:                             Id,                             # Unique Id for widget.
                doc:                            String,                         # 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,
                frame_relief:                   wt::Relief,
                site:                           g2d::Box,                       # Widget's assigned area in window coordinates.
                widget_to_guiboss:              gt::Widget_To_Guiboss,
                guiboss_to_widget:              gt::Guiboss_To_Widget,          # Used by textpane.pkg keystroke-macro stuff to synthesize fake keystroke events to widget.
                theme:                          wt::Widget_Theme,
                do:                             (Void -> Void) -> Void,         # Used by widget subthreads to execute code in main widget microthread.
                to:                             Replyqueue,                     # Used to call 'pass_*' methods in other imps.
                #
                default_key_event_fn:           Key_Event_Fn,
                #
                needs_redraw_gadget_request:    Void -> Void                    # Notify guiboss-imp that this button needs to be redrawn (i.e., sent a redraw_gadget_request()).
              }
        withtype
        Key_Event_Fn =  Key_Event_Fn_Arg -> Void;



        Option  = ID                    Id
                | DOC                   String
                #
                | FRAME_INDENT_HINT     gt::Frame_Indent_Hint
                | FRAME_RELIEF          wt::Relief
                #
                | REDRAW_FN             Redraw_Fn                               # Application-specific handler for widget redraw.
                | MOUSE_CLICK_FN        Mouse_Click_Fn                          # Application-specific handler for mousebutton clicks.
                | MOUSE_DRAG_FN         Mouse_Drag_Fn                           # Application-specific handler for mouse drags.
                | MOUSE_TRANSIT_FN      Mouse_Transit_Fn                        # Application-specific handler for mouse crossings.
                | KEY_EVENT_FN          Key_Event_Fn                            # Application-specific handler for keyboard input.
                #
                | PORTWATCHER           (Null_Or(App_To_Frame) -> Void)         # Widget's app port                   will be sent to these fns at widget startup.
                | SITEWATCHER           (Null_Or((Id,g2d::Box)) -> Void)        # Widget's site in window coordinates will be sent to these fns each time it changes.
                ;                                                               # To help prevent deadlock, watcher fns should be fast and nonblocking, typically just setting a var or entering something into a mailqueue.
                
        fun process_options
            ( options: List(Option),
              #
              { widget_id,
                widget_doc,
                #
                frame_indent_hint,
                frame_relief,
                #
                redraw_fn,
                mouse_click_fn,
                mouse_drag_fn,
                mouse_transit_fn,
                key_event_fn,
                #
                widget_options,
                #
                portwatchers,
                sitewatchers
              }
            )
            =
            {   my_widget_id            =  REF  widget_id;
                my_widget_doc           =  REF  widget_doc;
                #
                my_frame_indent_hint    =  REF  frame_indent_hint;
                my_frame_relief         =  REF  frame_relief;
                #
                my_redraw_fn            =  REF  redraw_fn;
                my_mouse_click_fn       =  REF  mouse_click_fn;
                my_mouse_drag_fn        =  REF  mouse_drag_fn;
                my_mouse_transit_fn     =  REF  mouse_transit_fn;
                my_key_event_fn         =  REF  key_event_fn;
                #
                my_widget_options       =  REF  widget_options;
                #
                my_portwatchers         =  REF  portwatchers;
                my_sitewatchers         =  REF  sitewatchers;
                #

                apply  do_option  options
                where
                    fun do_option (ID                           i) =>   my_widget_id            :=  THE i;
                        do_option (DOC                          d) =>   my_widget_doc           :=      d;
                        #
                        do_option (FRAME_INDENT_HINT            h) =>   my_frame_indent_hint    :=  THE h;
                        do_option (FRAME_RELIEF                 r) =>   my_frame_relief         :=      r;
                        #
                        do_option (REDRAW_FN                    f) =>   my_redraw_fn            :=  f;
                        do_option (MOUSE_CLICK_FN               f) =>   my_mouse_click_fn       :=  f;
                        do_option (MOUSE_DRAG_FN                f) =>   my_mouse_drag_fn        :=  THE f;
                        do_option (MOUSE_TRANSIT_FN             f) =>   my_mouse_transit_fn     :=  THE f;
                        do_option (KEY_EVENT_FN                 f) =>   my_key_event_fn         :=  THE f;
                        #
                        do_option (PORTWATCHER                  c) =>   my_portwatchers         :=  c ! *my_portwatchers;
                        do_option (SITEWATCHER                  c) =>   my_sitewatchers         :=  c ! *my_sitewatchers;
                    end;
                end;

                { widget_id             =>  *my_widget_id,
                  widget_doc            =>  *my_widget_doc,
                  #
                  frame_indent_hint     =>  *my_frame_indent_hint,
                  frame_relief          =>  *my_frame_relief,
                  #
                  redraw_fn             =>  *my_redraw_fn,
                  mouse_click_fn        =>  *my_mouse_click_fn,
                  mouse_drag_fn         =>  *my_mouse_drag_fn,
                  mouse_transit_fn      =>  *my_mouse_transit_fn,
                  key_event_fn          =>  *my_key_event_fn,
                  #
                  widget_options        =>  *my_widget_options,
                  #
                  portwatchers          =>  *my_portwatchers,
                  sitewatchers          =>  *my_sitewatchers
                };
            };


        offset = 1;

        fun with (options: List(Option))                                                                                        # PUBLIC.  The point of the 'with' name is that GUI coders can write 'frame::with { this => that, foo => bar, ... }.'
            =
            {   our_frame_relief = REF wt::RIDGE;
                #
                fun default_redraw_fn (REDRAW_FN_ARG a)
                    =
                    {
                        palette                 =  a.palette;
                        frame_indent_hint       =  a.frame_indent_hint;
                        site                    =  a.site;
                        theme                   =  a.theme;

                        relief                  =  *our_frame_relief;

                        frame_indent_hint
                          ->
                          { pixels_for_top_of_frame:    Int,                                                                    # Vertical   pixels to allocate for top    side of frame.
                            pixels_for_bottom_of_frame: Int,                                                                    # Vertical   pixels to allocate for bottom side of frame.
                            #
                            pixels_for_left_of_frame:   Int,                                                                    # Horizontal pixels to allocate for left   side of frame.
                            pixels_for_right_of_frame:  Int                                                                     # Horizontal pixels to allocate for right  side of frame.
                          };
                          
                        if (pixels_for_top_of_frame == pixels_for_bottom_of_frame
                        and pixels_for_top_of_frame == pixels_for_left_of_frame
                        and pixels_for_top_of_frame == pixels_for_right_of_frame
                        and pixels_for_top_of_frame >  8)
                            #
                            # This branch of the 'if' is basically Compatibility Mode:
                            # it is what we used to do when frame.pkg was hardwired to
                            # always draw a frame 9 pixels thick on every side:

#                           relief                      =  wt::RIDGE;
                            thick                       =  5;

                            stipulate                                                                                           # then carefully work through the code here based on that.  XXX SUCKO FIXME.
                                inset = 6;
                            herein
                                fun frame_vertices ({ row, col, wide, high }: g2d::Box)                                         #
                                        =                                                                                       #
                                        [ { col=> col + inset - 1,        row=> row + inset                },                   # upper-left
                                          { col=> col + inset - 1,        row=> row + high - (inset+1) },                       # lower-left
                                          { col=> col + wide - (inset+1), row=> row + high - (inset+1) },                       # lower-right
                                          { col=> col + wide - (inset+1), row=> row + inset                }                    # upper-right
                                        ];
                            end;

                            background_box =  site;

                            foreground_indent = 9;

                            foreground_box    =  g2d::box::make_nested_box (background_box, foreground_indent);                 # This is the window area reserved for the widgets we're framing.

                            background_displaylist                                                                                      # The 'background' for the frame is the part not covered by the 3d polygon.
                                =                                                                                                       # In particular, we do NOT want to draw over the inner rectangle reserved
                                [ gd::COLOR                                                                                             # for the widgets within the frame.
                                    (
                                      palette.surround_color,
                                      #
                                      [ gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a
                                                           {
                                                             a => background_box,
                                                             b => foreground_box
                                                           }
                                                         )
                                      ]
                                    )
                                ];

                            points =  frame_vertices  background_box;

                            foreground_displaylist
                                =
                                (*theme.polygon3d  palette
                                  {
                                    points,
                                    thick,
                                    relief
                                  }
                                );


                            stipulate
                                frame_outer_limit =  g2d::box::make_nested_box (background_box, 3 );
                                frame_inner_limit =  g2d::box::make_nested_box (background_box, 6 );
                            herein
                                fun point_in_gadget (point: g2d::Point)                                                         # A fn which will return TRUE iff the point is on the 3d frame itself, not the surround -- much less the inner widgets.
                                    =
                                    (    (g2d::box::point_in_box (point, frame_outer_limit)))  and
                                    (not (g2d::box::point_in_box (point, frame_inner_limit)));
                            end;

                            point_in_gadget =  THE  point_in_gadget;
                            displaylist     =  background_displaylist @ foreground_displaylist;

                            { displaylist, point_in_gadget };

                        else
                            # This branch of the 'if' handles all the frame_indent_hint
                            # cases that the original code really wasn't set up to handle:
                            #
                            if (pixels_for_top_of_frame    == 0
                            and pixels_for_bottom_of_frame == 0
                            and pixels_for_left_of_frame   == 0
                            and pixels_for_right_of_frame  == 0)

                                fun point_in_gadget (point: g2d::Point)                                                         # A fn which will return TRUE iff the point is on the frame itself -- not on inner widgets.
                                    =
                                    FALSE;

                                point_in_gadget =  THE  point_in_gadget;
                                displaylist     =  [ gd::FILLED_BOXES [] ];

                                { displaylist, point_in_gadget };
                            else
                                background_box =  site;
                                foreground_box =  gtj::make_nested_box (background_box, frame_indent_hint);                             # This is the window area reserved for the widgets we're framing.

                                background_displaylist                                                                                  # The 'background' for the frame is the part not covered by the 3d polygon.
                                    =                                                                                                   # In particular, we do NOT want to draw over the inner rectangle reserved
                                    [ gd::COLOR                                                                                         # for the widgets within the frame.
                                        (
                                          palette.surround_color,
                                          #
                                          [ gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a
                                                               {
                                                                 a => background_box,
                                                                 b => foreground_box
                                                               }
                                                             )
                                          ]
                                        )
                                    ];

                                foreground_displaylist
                                  =
                                  [ gd::COLOR
                                      (
                                        a.palette.text_color,
                                        [ gd::BOXES [ foreground_box, background_box ] ]
                                      )
                                  ];

                                fun point_in_gadget (point: g2d::Point)                                                         # A fn which will return TRUE iff the point is on the frame itself -- not on inner widgets.
                                    =
                                    (    (g2d::box::point_in_box (point, background_box)))  and
                                    (not (g2d::box::point_in_box (point, foreground_box)));

                                point_in_gadget =  THE  point_in_gadget;
                                displaylist     =  background_displaylist @ foreground_displaylist;

                                { displaylist, point_in_gadget };
                            fi;
                        fi;


                    };

                fun default_mouse_click_fn (MOUSE_CLICK_FN_ARG a)
                    =
                    {
                        ();
                    };

                (process_options
                  (
                    options,
                    #
                    { widget_id         =>  NULL,
                      widget_doc        =>  "<frame>",
                      # 
                      frame_indent_hint =>  NULL,
                      frame_relief      =>  *our_frame_relief,
                      # 
                      redraw_fn         =>  default_redraw_fn,
                      mouse_click_fn    =>  default_mouse_click_fn,
                      mouse_drag_fn     =>  NULL,
                      mouse_transit_fn  =>  NULL,
                      key_event_fn      =>  NULL,
                      #
                      widget_options    =>  [],
                      #
                      portwatchers      =>  [],
                      sitewatchers      =>  []
                    }
                ) )
                    ->
                    {                                                                                           # These values are globally visible to the subsequenc fns, which can lock them in as needed.
                      widget_id,
                      widget_doc,
                      #
                      frame_indent_hint,
                      frame_relief,
                      #
                      redraw_fn,
                      mouse_click_fn,
                      mouse_drag_fn,
                      mouse_transit_fn,
                      key_event_fn,
                      #
                      widget_options,
                      #
                      portwatchers,
                      sitewatchers
                    };

                our_frame_relief :=  frame_relief;


                #######################################
                # Top of per-imp state variable section
                #

                widget_to_guiboss__global
                    =
                    REF (NULL:  Null_Or((gt::Widget_To_Guiboss, Id)));

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

                fun note_site  (id: Id,  site: g2d::Box)
                    =
                    if(*last_known_site != site)
                        last_known_site := site;
                        #
                        apply tell_watcher sitewatchers
                            where
                                fun tell_watcher sitewatcher
                                    =
                                    sitewatcher (THE (id,site));
                            end;
                    fi;

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


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

                #
                # End of port section
                #####################


                ###############################
                # Top of widget hook fn section
                #
                # These fns get called by widget_imp logic, ultimately                                          # widget_imp            is from   src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg
                # in response to user mouseclicks and keypresses etc:

                fun startup_fn
                    { 
                      id:                               Id,                                                     # Unique Id for widget.
                      doc:                              String,                                                 # 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_frame
                          =
                          { id
                          }
                          : App_To_Frame
                          ;

                        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_frame);
                                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);
                        #
                        (*theme.current_gadget_colors { gadget_is_on => FALSE,
                                                        gadget_mode,
                                                        popup_nesting_depth,
                                                        #
                                                        body_color                          => NULL,
                                                        body_color_when_on                  => NULL,
                                                        body_color_with_mousefocus          => NULL,
                                                        body_color_when_on_with_mousefocus  => NULL
                                                      }
                        )
                            ->
                            (palette: wt::Gadget_Palette);

                        redraw_fn_arg
                            =
                            REDRAW_FN_ARG
                              { id,
                                doc,
                                frame_number,
                                frame_indent_hint,
                                frame_relief => *our_frame_relief,
                                site,
                                popup_nesting_depth,
                                duration_in_seconds,
                                widget_to_guiboss,
                                gadget_mode,
                                theme,
                                do,
                                to,
                                palette,
                                #
                                default_redraw_fn
                              };

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

                        widget_to_guiboss.g.redraw_gadget { id, site, displaylist, point_in_gadget };
                    };


                fun mouse_click_fn_wrapper                                                                      # This a callback we hand to   src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg
                      {
                        id:                             Id,                                                     # Unique Id for widget.
                        doc:                            String,                                                 # 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,
                                frame_relief => *our_frame_relief,
                                site,
                                modifier_keys_state,
                                mousebuttons_state,
                                widget_to_guiboss,
                                theme,
                                do,
                                to,
                                #
                                default_mouse_click_fn,
                                #
                                needs_redraw_gadget_request
                              };

                        mouse_click_fn  mouse_click_fn_arg;
                    };

                fun mouse_drag_fn_wrapper                                                                       # This a callback we hand to   src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg
                    (
                      { id:                             Id,                                                     # Unique Id for widget.
                        doc:                            String,                                                 # 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,
                                frame_relief => *our_frame_relief,
                                site,
                                phase,
                                button,
                                modifier_keys_state,
                                mousebuttons_state,
                                widget_to_guiboss,
                                theme,
                                do,
                                to,
                                #
                                default_mouse_drag_fn =>  \\ _ = (),                                            # Default drag behavior for buttons is to do absolutely nothing.
                                #
                                needs_redraw_gadget_request
                              };

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

                fun mouse_transit_fn_wrapper
                      #
                      ( arg as
                        {
                          id:                           Id,                                                     # Unique Id for widget.
                          doc:                          String,                                                 # Human-readable description of this widget, for debug and inspection.
                          event_point:                  g2d::Point,
                          widget_layout_hint:           gt::Widget_Layout_Hint,
                          frame_indent_hint:            gt::Frame_Indent_Hint,
                          site:                         g2d::Box,                                               # Widget's assigned area in window coordinates.
                          transit:                      gt::Gadget_Transit,                                     # Mouse is entering (CAME) or leaving (LEFT) widget, or moving (MOVE) across it.
                          modifier_keys_state:          evt::Modifier_Keys_State,                               # State of the modifier keys (shift, ctrl...).
                          widget_to_guiboss:            gt::Widget_To_Guiboss,
                          theme:                        wt::Widget_Theme,
                          do:                           (Void -> Void) -> Void,                                 # Used by widget subthreads to execute code in main widget microthread.
                          to:                           Replyqueue                                              # Used to call 'pass_*' methods in other imps.
                        }
                      ) 
                    = 
                    {   note_site (id,site);
                        #
                        mouse_transit_fn_arg
                            =
                            MOUSE_TRANSIT_FN_ARG
                              {
                                id,
                                doc,
                                event_point,
                                widget_layout_hint,
                                frame_indent_hint,
                                frame_relief => *our_frame_relief,
                                site,
                                transit,
                                modifier_keys_state,
                                widget_to_guiboss,
                                theme,
                                do,
                                to,
                                #
                                default_mouse_transit_fn =>  \\ _ = (),                                         # Default transit behavior for buttons is to do absolutely nothing.
                                #
                                needs_redraw_gadget_request
                              };

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

                        ();
                    };

                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,
                                frame_relief => *our_frame_relief,
                                site,
                                widget_to_guiboss,
                                guiboss_to_widget,
                                theme,
                                do,
                                to,
                                #
                                default_key_event_fn =>  \\ _ = (),                                             # Default key event behavior for frame is to do absolutely nothing.
                                #
                                needs_redraw_gadget_request
                              };

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

                       ();
                    };


                #
                # End of widget hook fn section
                ###############################

                widget_options
                    =
                    case mouse_drag_fn
                        #
                        THE _ =>  (wi::MOUSE_DRAG_FN mouse_drag_fn_wrapper)       ! widget_options;             # Register for drag events only if we are going to use them.
                        NULL  =>                                                    widget_options;
                    esac;

                widget_options
                    =
                    case mouse_transit_fn
                        #
                        THE _ =>  (wi::MOUSE_TRANSIT_FN mouse_transit_fn_wrapper) ! widget_options;             # Register for transit events only if we are going to use them.
                        NULL  =>                                                    widget_options;
                    esac;

                widget_options
                    =
                    case key_event_fn
                        #
                        THE _ =>  (wi::KEY_EVENT_FN key_event_fn_wrapper)         ! widget_options;             # Register for key events only if we are going to use them.
                        NULL  =>                                                    widget_options;
                    esac;

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

                widget_options
                    =
                    case frame_indent_hint
                        #
                        THE h  =>  (wi::FRAME_INDENT_HINT h)                      ! 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::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