PreviousUpNext

15.4.1458  src/lib/x-kit/widget/leaf/popupframe.pkg

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

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





# This package gets used in:
#
#     

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

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

    package popupframe
    :       Popupframe                                                          # Popupframe                    is from   src/lib/x-kit/widget/leaf/popupframe.api
    {
        App_To_Popupframe
          =
          { id:                                 Id
          };


        Redraw_Fn_Arg
            =
            REDRAW_FN_ARG
              {
                id:                             Id,                             # Unique Id for widget.
                doc:                            String,                         # Human-readable description of this widget, for debug and inspection.
                frame_number:                   Int,                            # 1,2,3,... Purely for convenience of widget, guiboss-imp makes no use of this.
                frame_indent_hint:              gt::Frame_Indent_Hint,
                site:                           g2d::Box,                       # Window rectangle in which to draw.
                popup_nesting_depth:            Int,                            # 0 for gadgets on basewindow, 1 for gadgets on popup on basewindow, 2 for gadgets on popup on popup, etc.
                #
                duration_in_seconds:            Float,                          # If state has changed look-imp should call note_changed_gadget_foreground() before this time is up. Also useful for motionblur.
                widget_to_guiboss:              gt::Widget_To_Guiboss,
                gadget_mode:                    gt::Gadget_Mode,
                #
                frame_width_in_pixels:          Int,
                #
                theme:                          wt::Widget_Theme,
                do:                             (Void -> Void) -> Void,         # Used by widget subthreads to execute code in main widget microthread.
                to:                             Replyqueue,                     # Used to call 'pass_*' methods in other imps.
                palette:                        wt::Gadget_Palette,
                #
                default_redraw_fn:              Redraw_Fn
              }
        withtype
        Redraw_Fn
          =
          Redraw_Fn_Arg
          ->
          { displaylist:                gd::Gui_Displaylist,
            point_in_gadget:            Null_Or(g2d::Point -> Bool)             # 
          }
          ;



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



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



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



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

        Dragmode
          #
          = NO_DRAG
          | DRAG_POPUP
          | RESIZE_POPUP Ref(Null_Or(g2d::Size))
          ;

        fun dragmode_to_string (NO_DRAG       ) =>  "NO_DRAG";
            dragmode_to_string (DRAG_POPUP    ) =>  "DRAG_POPUP";
            dragmode_to_string (RESIZE_POPUP s) =>  sprintf "RESIZE_POPUP %s" case *s THE s => (g2j::size_to_string s); NULL => "NULL"; esac;
        end;

        Option  = FRAME_WIDTH_IN_PIXELS Int
                #
                | TEXT                  String                                  # Text label to draw inside button.  Default is "".
                | FONT                  List(String)                            # Font to use for text label, e.g. "-*-courier-bold-r-*-*-20-*-*-*-*-*-*-*".  We'll use the first font in list which is found on X server, else "9x15" (which X guarantees to have).
                #
                | ID                    Id
                | DOC                   String
                #
                | REDRAW_FN             Redraw_Fn                               # Application-specific handler for widget redraw.
                | MOUSE_CLICK_FN        Mouse_Click_Fn                          # Application-specific handler for mousebutton clicks.
                | MOUSE_DRAG_FN         Mouse_Drag_Fn                           # Application-specific handler for mouse drags.
                | MOUSE_TRANSIT_FN      Mouse_Transit_Fn                        # Application-specific handler for mouse crossings.
                | KEY_EVENT_FN          Key_Event_Fn                            # Application-specific handler for keyboard input.
                #
                | PORTWATCHER           (Null_Or(App_To_Popupframe) -> Void)    # Widget's app port                   will be sent to these fns at widget startup.
                | SITEWATCHER           (Null_Or((Id,g2d::Box)) -> Void)        # Widget's site in window coordinates will be sent to these fns each time it changes.
                ;                                                               # To help prevent deadlock, watcher fns should be fast and nonblocking, typically just setting a var or entering something into a mailqueue.
                
        fun process_options
            ( options: List(Option),
              #
              { text,
                font,
                #
                frame_width_in_pixels,
                #
                widget_id,
                widget_doc,
                #
                redraw_fn,
                mouse_click_fn,
                mouse_drag_fn,
                mouse_transit_fn,
                key_event_fn,
                #
                widget_options,
                #
                portwatchers,
                sitewatchers
              }
            )
            =
            {   my_text                 =  REF  text;
                my_font                 =  REF  font;
                my_frame_width_in_pixels=  REF  frame_width_in_pixels;
                #
                my_widget_id            =  REF  widget_id;
                my_widget_doc           =  REF  widget_doc;
                #
                my_redraw_fn            =  REF  redraw_fn;
                my_mouse_click_fn       =  REF  mouse_click_fn;
                my_mouse_drag_fn        =  REF  mouse_drag_fn;
                my_mouse_transit_fn     =  REF  mouse_transit_fn;
                my_key_event_fn         =  REF  key_event_fn;
                #
                my_widget_options       =  REF  widget_options;
                #
                my_portwatchers         =  REF  portwatchers;
                my_sitewatchers         =  REF  sitewatchers;
                #

                apply  do_option  options
                where
                    fun do_option (TEXT                         t) =>   my_text                 :=  t;
                        do_option (FONT                         t) =>   my_font                 :=  t;
                        #
                        do_option (FRAME_WIDTH_IN_PIXELS        i) =>   my_frame_width_in_pixels        :=  i;
                        #
                        do_option (ID                           i) =>   my_widget_id            :=  THE i;
                        do_option (DOC                          d) =>   my_widget_doc           :=      d;
                        #
                        do_option (REDRAW_FN                    f) =>   my_redraw_fn            :=  f;
                        do_option (MOUSE_CLICK_FN               f) =>   my_mouse_click_fn       :=  f;
                        do_option (MOUSE_DRAG_FN                f) =>   my_mouse_drag_fn        :=  f;
                        do_option (MOUSE_TRANSIT_FN             f) =>   my_mouse_transit_fn     :=  THE f;
                        do_option (KEY_EVENT_FN                 f) =>   my_key_event_fn         :=  THE f;
                        #
                        do_option (PORTWATCHER                  c) =>   my_portwatchers         :=  c ! *my_portwatchers;
                        do_option (SITEWATCHER                  c) =>   my_sitewatchers         :=  c ! *my_sitewatchers;
                    end;
                end;

                { text                  =>  *my_text,
                  font                  =>  *my_font,
                  #
                  frame_width_in_pixels =>  *my_frame_width_in_pixels,
                  #
                  widget_id             =>  *my_widget_id,
                  widget_doc            =>  *my_widget_doc,
                  #
                  redraw_fn             =>  *my_redraw_fn,
                  mouse_click_fn        =>  *my_mouse_click_fn,
                  mouse_drag_fn         =>  *my_mouse_drag_fn,
                  mouse_transit_fn      =>  *my_mouse_transit_fn,
                  key_event_fn          =>  *my_key_event_fn,
                  #
                  widget_options        =>  *my_widget_options,
                  #
                  portwatchers          =>  *my_portwatchers,
                  sitewatchers          =>  *my_sitewatchers
                };
            };


        offset = 1;



        fun with (options: List(Option))                                                                # PUBLIC.  The point of the 'with' name is that GUI coders can write 'popupframe::with { this => that, foo => bar, ... }.'
            =
            {
                textref   =  REF "";                                                                    # We need this little REF hack
                                                                                                        # because default_redraw_fn is an input to process_options() but 'text'
                                                                                                        # is an output from process_options(), so something has to give a bit.

                fontref   =  REF [ ];                                                                   # Same story.


                dragmode  =  REF NO_DRAG;

                fun make_upper_right_box (site: g2d::Box)
                    =
                    {   site -> { row, col, high, wide };
                        #
                        { row,
                          col => col + wide - 9,
                          high => 9,
                          wide => 9
                        };
                    };

                fun make_lower_right_box (site: g2d::Box)
                    =
                    {   site -> { row, col, high, wide };
                        #
                        { row => row + high - 9,
                          col => col + wide - 9,
                          high => 9,
                          wide => 9
                        };
                    };

                fun default_redraw_fn (REDRAW_FN_ARG a)
                    =
                    {   frame_width_in_pixels   =  a.frame_width_in_pixels;
                        palette                 =  a.palette;
                        site                    =  a.site;
                        theme                   =  a.theme;

                        box0 =  site;
                        box1 =  g2d::box::make_nested_box (box0, 3);
                        box2 =  g2d::box::make_nested_box (box1, 3);
                        box3 =  g2d::box::make_nested_box (box2, 3);

                        stipulate
                            box0 -> { row, col, high, wide };
                        herein
                            upper_right_box = make_upper_right_box box0;
                            lower_right_box = make_lower_right_box box0;

                            upper_right_corner
                              =
                              [ gd::COLOR
                                  (
                                    r64::white,
                                    [ gd::FILLED_BOXES [ upper_right_box ] ]
                                  ),

                                gd::COLOR
                                  (
                                    r64::black,
                                    #
                                    [ gd::LINE_THICKNESS
                                        (
                                          0,
                                          [ gd::LINES [ ( { row,             col => col + wide -  1 },                          # Draw 1-pixel black line on top of upper_right_box.
                                                          { row,             col => col + wide -  9 }
                                                        ),
                                                        ( { row,             col => col + wide -  1 },                          # Draw 1-pixel black line on right of upper_right_box.
                                                          { row => row +  8, col => col + wide -  1 }
                                                        ),
                                                        ( { row,             col => col + wide -  9 },                          # Draw 1-pixel black line on left of upper_right_box.
                                                          { row => row +  8, col => col + wide -  9 }
                                                        ),
                                                        ( { row => row +  9, col => col + wide -  1 },                          # Draw 1-pixel black line on bottom of upper_right_box.
                                                          { row => row +  9, col => col + wide -  9 }
                                                        ),

                                                        ( { row => row +  9, col => col + wide -  9 },                          # Draw 1-pixel black line from lower-left to upper-right of upper_right_box.
                                                          { row => row,      col => col + wide -  1 }
                                                        ),
                                                        ( { row => row +  8, col => col + wide -  9 },                          # Make it double thick by adding one above/left.
                                                          { row => row +  0, col => col + wide -  2 }                           # 
                                                        ),
                                                        ( { row => row +  9, col => col + wide -  8 },                          # Make it triple thick by adding one below/right.
                                                          { row => row +  1, col => col + wide -  1 }                           # 
                                                        ),

                                                        ( { row => row +  9, col => col + wide -  1 },                          # Draw 1-pixel black line from lower-right to upper-left of upper_right_box.
                                                          { row => row,      col => col + wide -  9 }
                                                        ),
                                                        ( { row => row +  9, col => col + wide -  2 },                          # Make it double thick by adding one below/left.
                                                          { row => row +  1, col => col + wide -  9 }
                                                        ),
                                                        ( { row => row +  8, col => col + wide -  1 },                          # Make it triple thick by adding one above/right.
                                                          { row => row,      col => col + wide -  8 }
                                                        )
                                                      ]
                                          ]
                                        )
                                    ]
                                  )
                              ];        

                            lower_right_corner
                              =
                              [ gd::COLOR
                                  (
                                    r64::white,
                                    [ gd::FILLED_BOXES [ lower_right_box ] ]
                                  ),

                                gd::COLOR
                                  (
                                    r64::black,
                                    #
                                    [ gd::LINE_THICKNESS
                                        (
                                          0,
                                          [ gd::LINES [ ( { row => row + high - 9, col => col + wide -  1 },                    # Draw 1-pixel black line on top of lower_right_box.
                                                          { row => row + high - 9, col => col + wide -  9 }
                                                        ),
                                                        ( { row => row + high - 1, col => col + wide -  1 },                    # Draw 1-pixel black line on right of lower_right_box.
                                                          { row => row + high - 9, col => col + wide -  1 }
                                                        ),
                                                        ( { row => row + high - 1, col => col + wide -  9 },                    # Draw 1-pixel black line on left of lower_right_box.
                                                          { row => row + high - 9, col => col + wide -  9 }
                                                        ),
                                                        ( { row => row + high - 1, col => col + wide -  1 },                    # Draw 1-pixel black line on bottom of lower_right_box.
                                                          { row => row + high - 1, col => col + wide -  9 }
                                                        )
                                                      ]
                                          ]
                                        )
                                    ]
                                  )
                              ];        
                        end;

                        displaylist                                                                                             # 
                            =                                                                                                   # NB: We do NOT want to draw over the inner rectangle reserved
                            [
                              gd::COLOR                                                                                         # for the widgets within the frame.
                                (
                                  r64::black,
                                  [ gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a { a => box0, b => box1 }),
                                    gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a { a => box2, b => box3 })
                                  ]
                                ),

                              gd::COLOR                                                                                         # for the widgets within the frame.
                                (
                                  r64::white,
                                  [ gd::FILLED_BOXES (g2d::box::subtract_box_b_from_box_a { a => box1, b => box2 })
                                  ]
                                )
                            ]
                            @
                            upper_right_corner
                            @
                            lower_right_corner
                            ;



                        fun point_in_gadget (point: g2d::Point)                                                         # A fn which will return TRUE iff the point is on the 3d frame itself, not the surround -- much less the inner widgets.
                            =
                            (    (g2d::box::point_in_box (point, box0)))  and
                            (not (g2d::box::point_in_box (point, box3)));


                        point_in_gadget =  THE  point_in_gadget;

                        { displaylist, point_in_gadget };
                    };

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

                        upper_right_box =  make_upper_right_box  site;

                        if (g2d::box::point_in_box (point, upper_right_box))
                            #
                            widget_to_guiboss.g.kill_popup ();
                        fi;

                        ();
                    };

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

                        set_guipane_upperleft  = widget_to_guiboss.g.set_guipane_upperleft;
                        pass_guipane_upperleft = widget_to_guiboss.g.pass_guipane_upperleft;

                        set_guipane_size       = widget_to_guiboss.g.set_guipane_size;
                        pass_guipane_size      = widget_to_guiboss.g.pass_guipane_size;


                        lower_right_box =  make_lower_right_box  site;

                        case phase
                            #
                            gt::OPEN => {   dragmode  :=    if (g2d::box::point_in_box (event_point, lower_right_box))
                                                                #
                                                                initial_size =  REF NULL;
                                                                pass_guipane_size id to {. initial_size := THE #size; };
                                                                RESIZE_POPUP initial_size;
                                                            else
                                                                DRAG_POPUP;
                                                            fi;
                                        };
                            gt::DONE => {
                                            dragmode  :=  NO_DRAG;
                                        };

                            gt::DRAG => {   case *dragmode
                                                #
                                                RESIZE_POPUP initial_size
                                                    =>
                                                    case *initial_size
                                                        #
                                                        THE { high, wide }
                                                            =>
                                                            {   delta =  event_point - start_point;
                                                                #
                                                                new_guipane_size
                                                                  =
                                                                  { high =>  high + delta.row,
                                                                    wide =>  wide + delta.col
                                                                  };

                                                                set_guipane_size (id, new_guipane_size);        
                                                            };

                                                        NULL  =>    ();                                                 # We haven't yet gotten our response to the above pass_guipane_size call, so don't do anything.
                                                    esac;

                                                DRAG_POPUP
                                                    =>
                                                    pass_guipane_upperleft id to do_drag
                                                        where
                                                            fun do_drag (old_guipane_upperleft: g2d::Point)
                                                                =
                                                                {   delta =  event_point - last_point;
                                                                    #
                                                                    new_guipane_upperleft
                                                                        =
                                                                        old_guipane_upperleft + delta;

                                                                    set_guipane_upperleft (id, new_guipane_upperleft);  
                                                                };
                                                        end;

                                                NO_DRAG => ();                                                          # Shouldn't happen;  possibly we should log something here.
                                            esac;       
                                        };
                        esac;

                        ();
                    };

                (process_options
                  (
                    options,
                    #
                    { text              =>  *textref,
                      font              =>  *fontref,
                      #
                      frame_width_in_pixels =>  9,                                                                      # 
                      #
                      widget_id         =>  NULL,
                      widget_doc        =>  "<popupframe>",
                      # 
                      redraw_fn         =>  default_redraw_fn,
                      mouse_click_fn    =>  default_mouse_click_fn,
                      mouse_drag_fn     =>  default_mouse_drag_fn,
                      mouse_transit_fn  =>  NULL,
                      key_event_fn      =>  NULL,
                      #
                      widget_options    =>  [],
                      #
                      portwatchers      =>  [],
                      sitewatchers      =>  []
                    }
                ) )
                    ->
                    {                                                                                                   # These values are globally visible to the subsequenc fns, which can lock them in as needed.
                      text,
                      font,
                      #
                      frame_width_in_pixels,
                      #
                      widget_id,
                      widget_doc,
                      # 
                      redraw_fn,
                      mouse_click_fn,
                      mouse_drag_fn,
                      mouse_transit_fn,
                      key_event_fn,
                      #
                      widget_options,
                      #
                      portwatchers,
                      sitewatchers
                    };

                textref   := text;
                fontref   := font;


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

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

                fun note_changed_gadget_activity (is_active: Bool)
                    =
                    case (*widget_to_guiboss__global)
                        #
                        THE (widget_to_guiboss, id)     =>  widget_to_guiboss.g.note_changed_gadget_activity { id, is_active };
                        NULL                            =>  ();
                    esac;

                fun needs_redraw_gadget_request ()
                    =
                    case (*widget_to_guiboss__global)
                        #
                        THE (widget_to_guiboss, id)     =>  widget_to_guiboss.g.needs_redraw_gadget_request(id);
                        NULL                            =>  ();
                    esac;


                last_known_site
                    =
                    REF ( { col => -1,  wide => -1,
                            row => -1,  high => -1
                          }:                            g2d::Box
                        );


                exception SAVED_STATE { last_known_site:        g2d::Box                                        # Here we're doing the usual hack of using Exception as an extensible datatype -- nothing to do with actually raising or trapping exceptions.
                                      };        


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

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


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

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


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

                fun startup_fn
                    { 
                      id:                               Id,                                                     # Unique Id for widget.
                      doc:                              String,
                      widget_to_guiboss:                gt::Widget_To_Guiboss,
                      do:                               (Void -> Void) -> Void,                                 # Used by widget subthreads to execute code in main widget microthread.
                      to:                               Replyqueue
                    }
                    =
                    {   widget_to_guiboss__global
                            :=  
                            THE (widget_to_guiboss, id);

                        app_to_popupframe
                          =
                          { id
                          }
                          : App_To_Popupframe
                          ;

                        apply   tell_watcher  portwatchers                                                      # We do this here rather than (say) above this fn because we don't want the port in circulation until we're running.
                                where
                                    fun tell_watcher  portwatcher
                                        =
                                        portwatcher  (THE app_to_popupframe);
                                end;
                        ();
                    };

                fun shutdown_fn ()                                                                              # Return to widget_imp an exception packaging up our state; this will be returned to guiboss_imp, saved in the
                    =                                                                                           # Paused_Gui tree, and passed to our startup_fn when/if gui is restarted. This exception will never be raised;
                    {   apply   tell_watcher  portwatchers                                                      # 
                                where
                                    fun tell_watcher  portwatcher
                                        =
                                        portwatcher  NULL;
                                end;

                        apply tell_watcher sitewatchers
                            where
                                fun tell_watcher sitewatcher
                                    =
                                    sitewatcher NULL;
                            end;
                    };

                fun initialize_gadget_fn
                    {
                      id:                               Id,                                                     # Unique Id for widget.
                      doc:                              String,
                      site:                             g2d::Box,                                               # Window rectangle in which to draw.
                      widget_to_guiboss:                gt::Widget_To_Guiboss,
                      theme:                            wt::Widget_Theme,
                      pass_font:                        List(String) -> Replyqueue
                                                                     -> (evt::Font -> Void) -> Void,            # Nonblocking version of next, for use in imps.
                       get_font:                        List(String) ->  evt::Font,                             # Accepts a list of font names which are tried in order.
                      make_rw_pixmap:                   g2d::Size -> g2p::Gadget_To_Rw_Pixmap,
                      #
                      do:                               (Void -> Void) -> Void,                                 # Used by widget subthreads to execute code in main widget microthread.
                      to:                               Replyqueue                                              # Used to call 'pass_*' methods in other imps.
                    }
                    =
                    {   note_site (id,site);
                        #
                        ();
                    };

                fun redraw_request_fn_wrapper
                    {
                      id:                               Id,                                                     # Unique Id for widget.
                      doc:                              String,
                      frame_number:                     Int,                                                    # 1,2,3,... Purely for convenience of widget-imp, guiboss-imp makes no use of this.
                      frame_indent_hint:                gt::Frame_Indent_Hint,
                      site:                             g2d::Box,                                               # Window rectangle in which to draw.
                      popup_nesting_depth:              Int,                                                    # 0 for gadgets on basewindow, 1 for gadgets on popup on basewindow, 2 for gadgets on popup on popup, etc.
                      #
                      duration_in_seconds:              Float,                                                  # If state has changed widget-imp should call redraw_gadget() before this time is up. Also useful for motionblur.
                      widget_to_guiboss:                gt::Widget_To_Guiboss,
                      gadget_mode:                      gt::Gadget_Mode,
                      #
                      theme:                            wt::Widget_Theme,
                      do:                               (Void -> Void) -> Void,
                      to:                               Replyqueue                                              # Used to call 'pass_*' methods in other imps.
                    }
                    =
                    {   note_site (id,site);
                        #
                        (*theme.current_gadget_colors { gadget_is_on => FALSE,
                                                        gadget_mode,
                                                        popup_nesting_depth,
                                                        #
                                                        body_color                          => NULL,
                                                        body_color_when_on                  => NULL,
                                                        body_color_with_mousefocus          => NULL,
                                                        body_color_when_on_with_mousefocus  => NULL
                                                      }
                        )
                            ->
                            (palette: wt::Gadget_Palette);

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

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

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


                fun mouse_click_fn_wrapper                                                                      # This a callback we hand to   src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg
                      {
                        id:                             Id,                                                     # Unique Id for widget.
                        doc:                            String,
                        event:                          gt::Mousebutton_Event,                                  # MOUSEBUTTON_PRESS or MOUSEBUTTON_RELEASE.
                        button:                         evt::Mousebutton,
                        point:                          g2d::Point,
                        widget_layout_hint:             gt::Widget_Layout_Hint,
                        frame_indent_hint:              gt::Frame_Indent_Hint,
                        site:                           g2d::Box,                                               # Widget's assigned area in window coordinates.
                        modifier_keys_state:            evt::Modifier_Keys_State,                               # State of the modifier keys (shift, ctrl...).
                        mousebuttons_state:             evt::Mousebuttons_State,                                # State of mouse buttons as a bool record.
                        widget_to_guiboss:              gt::Widget_To_Guiboss,
                        theme:                          wt::Widget_Theme,
                        do:                             (Void -> Void) -> Void,                                 # Used by widget subthreads to execute code in main widget microthread.
                        to:                             Replyqueue                                              # Used to call 'pass_*' methods in other imps.
                      }
                    = 
                    {   note_site  (id,site);
                        #
                        mouse_click_fn_arg
                            =
                            MOUSE_CLICK_FN_ARG
                              {
                                id,
                                doc,
                                event,
                                button,
                                point,
                                widget_layout_hint,
                                frame_indent_hint,
                                site,
                                modifier_keys_state,
                                mousebuttons_state,
                                widget_to_guiboss,
                                theme,
                                do,
                                to,
                                #
                                default_mouse_click_fn,
                                #
                                needs_redraw_gadget_request
                              };

                        mouse_click_fn  mouse_click_fn_arg;
                    };

                fun mouse_drag_fn_wrapper                                                                       # This a callback we hand to   src/lib/x-kit/widget/xkit/theme/widget/default/look/widget-imp.pkg
                    (
                      { id:                             Id,                                                     # Unique Id for widget.
                        doc:                            String,
                        event_point:                    g2d::Point,
                        start_point:                    g2d::Point,
                        last_point:                     g2d::Point,
                        widget_layout_hint:             gt::Widget_Layout_Hint,
                        frame_indent_hint:              gt::Frame_Indent_Hint,
                        site:                           g2d::Box,                                               # Widget's assigned area in window coordinates.
                        phase:                          gt::Drag_Phase, 
                        button:                         evt::Mousebutton,
                        modifier_keys_state:            evt::Modifier_Keys_State,                               # State of the modifier keys (shift, ctrl...).
                        mousebuttons_state:             evt::Mousebuttons_State,                                # State of mouse buttons as a bool record.
                        widget_to_guiboss:              gt::Widget_To_Guiboss,
                        theme:                          wt::Widget_Theme,
                        do:                             (Void -> Void) -> Void,                                 # Used by widget subthreads to execute code in main widget microthread.
                        to:                             Replyqueue                                              # Used to call 'pass_*' methods in other imps.
                      }
                    )
                    = 
                    {   note_site  (id,site);
                        #
                        mouse_drag_fn_arg
                            =
                            MOUSE_DRAG_FN_ARG
                              {
                                id,
                                doc,
                                event_point,
                                start_point,
                                last_point,
                                widget_layout_hint,
                                frame_indent_hint,
                                site,
                                phase,
                                button,
                                modifier_keys_state,
                                mousebuttons_state,
                                widget_to_guiboss,
                                theme,
                                do,
                                to,
                                #
                                default_mouse_drag_fn,
                                #
                                needs_redraw_gadget_request
                              };

                        mouse_drag_fn  mouse_drag_fn_arg;
                    };

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

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

                        ();
                    };

# This is not currently hooked up to anything. XXX SUCKO FIXME
                fun key_event_fn_wrapper
                      {
                        id:                             Id,                                                     # Unique Id for widget.
                        doc:                            String,
                        keystroke:                      gt::Keystroke_Info,                                     # Keystring etc for event.
                        widget_layout_hint:             gt::Widget_Layout_Hint,
                        frame_indent_hint:              gt::Frame_Indent_Hint,
                        site:                           g2d::Box,                                               # Widget's assigned area in window coordinates.
                        widget_to_guiboss:              gt::Widget_To_Guiboss,
                        guiboss_to_widget:              gt::Guiboss_To_Widget,                                  # Used by textpane.pkg keystroke-macro stuff to synthesize fake keystroke events to widget.
                        theme:                          wt::Widget_Theme,
                        do:                             (Void -> Void) -> Void,                                 # Used by widget subthreads to execute code in main widget microthread.
                        to:                             Replyqueue                                              # Used to call 'pass_*' methods in other imps.
                      }
                    = 
                    {   note_site (id,site);
                        #
                        key_event_fn_arg
                            =
                            KEY_EVENT_FN_ARG
                              {
                                id,
                                doc,
                                keystroke,
                                widget_layout_hint,
                                frame_indent_hint,
                                site,
                                widget_to_guiboss,
                                guiboss_to_widget,
                                theme,
                                do,
                                to,
                                #
                                default_key_event_fn =>  \\ _ = (),                                             # Default key event behavior for frame is to do absolutely nothing.
                                #
                                needs_redraw_gadget_request
                              };

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

                       ();
                    };


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

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

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

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

                widget_options
                  =
                  [ wi::STARTUP_FN                      startup_fn,                                             # We always register for these five because our base behavior depends on them.
                    wi::SHUTDOWN_FN                     shutdown_fn,
                    wi::INITIALIZE_GADGET_FN            initialize_gadget_fn,
                    wi::REDRAW_REQUEST_FN               redraw_request_fn_wrapper,
                    wi::MOUSE_CLICK_FN                  mouse_click_fn_wrapper,
                    wi::MOUSE_DRAG_FN                   mouse_drag_fn_wrapper,
                    wi::DOC                             widget_doc
                  ]
                  @
                  widget_options
                  ;

                make_widget_fn =  wi::make_widget_start_fn  widget_options;

                gt::WIDGET  make_widget_fn;                                                                     # So caller can write   guiplan = gt::ROW [ frame::with [...], frame::with [...], ... ];
            };                                                                                                  # PUBLIC
    };
end;




Comments and suggestions to: bugs@mythryl.org

PreviousUpNext