PreviousUpNext

15.4.1518  src/lib/x-kit/widget/old/leaf/message.pkg

## message.pkg
#
# Text message widget.

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






stipulate
    include package   threadkit;                # threadkit             is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
    package d3 =  three_d;                      # three_d               is from   src/lib/x-kit/widget/old/lib/three-d.pkg
    package wg =  widget;                       # widget                is from   src/lib/x-kit/widget/old/basic/widget.pkg
    package wa =  widget_attribute_old;         # widget_attribute_old  is from   src/lib/x-kit/widget/old/lib/widget-attribute-old.pkg
    package wt =  widget_types;                 # widget_types          is from   src/lib/x-kit/widget/old/basic/widget-types.pkg
    #
    package xc =  xclient;                      # xclient               is from   src/lib/x-kit/xclient/xclient.pkg
    #
    package g2d=  geometry2d;                   # geometry2d            is from   src/lib/std/2d/geometry2d.pkg
herein

    package   message
    : (weak)  Message                           # Message               is from   src/lib/x-kit/widget/old/leaf/message.api
    {
        Plea_Mail
          = SET_TEXT      String
          | GET_TEXT      Oneshot_Maildrop( String )
          | GET_SIZE_CONSTRAINT    Oneshot_Maildrop( wg::Widget_Size_Preference )
          #
          | DO_REALIZE  { kidplug:      xc::Kidplug,
                          window:       xc::Window,
                          window_size:  g2d::Size
                        }
          ;

        Message
            =
            MESSAGE 
              { widget:     wg::Widget,
                plea_slot:  Mailslot( Plea_Mail )
              };

        Textinfo = { text:       String,
                     text_wide:  Int,
                     text_high:  Int
                   };

        fun get_line (font, text, starti, maxx)
            =
            loop (starti, 0, starti, 0)
            where  

                char_info = xc::char_info_of font;

                endi = size text;

                fun loop (i, curx, end_word, end_word_x)
                    =
                      if (endi == i)
                          #
                          (i, curx);
                      else 
                          c = string::get_byte_as_char (text, i);
                          #
                          if (c == '\n')
                              #
                              (i, curx);
                          else
                              (char_info (char::to_int c))
                                  ->
                                  xc::CHAR_INFO { char_width, ... };

                              nextx = curx + char_width;

                              if (nextx > maxx)
                                  #
                                  if (end_word > starti)
                                      #
                                      (end_word, end_word_x);
                                  else
                                      if (i > starti)  (i,   curx);
                                      else             (i+1, nextx);
                                      fi;
                                  fi;
                              else
                                  my (end_word, end_word_x)
                                      =
                                      if (char::is_space c)
                                          #
                                          (i+1, nextx);
                                      else
                                          (end_word, end_word_x);
                                      fi;

                                  loop (i+1, nextx, end_word, end_word_x);
                              fi;
                          fi;
                      fi;
            end;

        fun make_text_info (root, aspect, text, width, fontinfo, bw, padx, pady)
            =
            { text, text_wide, text_high }
            where 
                fontinfo -> (font, font_ascent, font_descent);

                font_high = font_ascent + font_descent;

                xdelta  = 2*(bw + padx);
                ydelta  = 2*(bw + pady);

                aspect_delta = int::max (5, aspect / 10);

                lower_bound = aspect - aspect_delta;
                upper_bound = aspect + aspect_delta;

                (xc::size_of_screen (wg::screen_of root))
                    ->
                    { wide=>screen_width, ... };

                wi = if (width > 0)
                         (width, 0);
                     else
                          width = screen_width / 2;
                         (width, width / 2);
                     fi;

                endi = size text;

                fun get_size (i, maxw, txtht, width)
                    =
                    if (i == endi)
                        #
                        (maxw, txtht);

                    elif (string::get_byte_as_char (text, i) == '\n' )
                        #
                        get_size (i+1, maxw, txtht+font_high, width);

                    else 
                        (get_line (font, text, i, width))
                            ->
                            (nexti, linex);

                        maxw = int::max (linex, maxw);

                        fun skip_ws i
                            =
                            {   c = string::get_byte_as_char (text, i);
                                #
                                if   (c == '\n')         i+1;
                                elif (char::is_space c)  skip_ws (i+1);
                                else                     i;
                                fi;
                            };

                          get_size
                            ( (skip_ws nexti) except _ = nexti,
                              maxw,
                              txtht + font_high,
                              width
                            );
                      fi;

                fun do_layout (width, inc)
                    =
                    {   (get_size (0, 0, 0, width))
                            ->
                            answer as (text_wide, text_high);
                            

                        if (inc <= 2)
                            #
                            answer;
                        else
                            aspect = (100*(text_wide + xdelta)) / (text_high + ydelta);

                            if (aspect < lower_bound )
                                #
                                do_layout (width+inc, inc / 2);

                            elif (aspect > upper_bound )
                                #
                                do_layout (width-inc, inc / 2);
                            else
                                answer;
                            fi;
                        fi;
                    };

                (do_layout wi)
                    ->
                    (text_wide, text_high);
              end;

        Fontinfo = (xc::Font, Int, Int);

        fun make_font_info font
            =
            {   (xc::font_high  font)
                    ->
                    { ascent=>font_ascent, descent=>font_descent };

                (font, font_ascent, font_descent);
            };


        attributes
            =
            [ (wa::aspect,         wa::INT,        wa::INT_VAL 150),
              (wa::background,     wa::COLOR,      wa::STRING_VAL "white"),
              (wa::border_thickness,   wa::INT,        wa::INT_VAL 2),
              (wa::font,           wa::FONT,       wa::STRING_VAL "8x13"),
              (wa::foreground,     wa::COLOR,      wa::STRING_VAL "black"),
              (wa::gravity,        wa::GRAVITY,    wa::GRAVITY_VAL wt::CENTER),
              (wa::halign,         wa::HALIGN,     wa::HALIGN_VAL  wt::HLEFT),
              (wa::padx,           wa::INT,        wa::NO_VAL),
              (wa::pady,           wa::INT,        wa::NO_VAL),
              (wa::relief,         wa::RELIEF,     wa::RELIEF_VAL wg::FLAT),
              (wa::text,           wa::STRING,     wa::STRING_VAL " "),
              (wa::width,          wa::INT,        wa::INT_VAL 0)
            ];


        Result = { aspect:  Int,

                   bg:  xc::Rgb,
                   fg:  xc::Rgb,

                   border_thickness:  Int,
                   fontinfo:  Fontinfo,
                   gravity:  wt::Gravity,
                   justify:  wt::Horizontal_Alignment,

                   padx:  Int,
                   pady:  Int,

                   relief:  wg::Relief,
                   shades:  wg::Shades,

                   textinfo:  Ref( Textinfo ),
                   width:  Int
                 };

        fun get_resources (root, attributes) : Result
            =
            {   aspect = wa::get_int   (attributes wa::aspect    );
                bg     = wa::get_color (attributes wa::background);
                font   = wa::get_font  (attributes wa::font      );

                my fontinfo as (_, font_ascent, _)
                    =
                    make_font_info font;

                padx = case (wa::get_int_opt (attributes wa::padx))   
                           #
                           THE i => i;
                           NULL => font_ascent / 2;
                       esac;

                pady = case (wa::get_int_opt (attributes wa::pady))   
                           #
                           THE i => i;
                           NULL => font_ascent / 4;
                       esac;

                text         = wa::get_string (attributes wa::text);
                width        = wa::get_int    (attributes wa::width);
                border_thickness = wa::get_int    (attributes wa::border_thickness);

                { aspect,
                  bg,
                  border_thickness,
                  fontinfo,

                  fg      => wa::get_color   (attributes wa::foreground),
                  gravity => wa::get_gravity (attributes wa::gravity),
                  justify => wa::get_halign  (attributes wa::halign),

                  padx,
                  pady,

                  relief   => wa::get_relief (attributes wa::relief),
                  shades   => wg::shades root bg,
                  textinfo => REF (make_text_info (root, aspect, text, width, fontinfo,
                                border_thickness, padx, pady)),
                  width
                };
            };

        fun size_preference_thunk_of ( { textinfo, padx, pady, border_thickness, ... } : Result)
            =
            {   (*textinfo) ->  { text_high, text_wide, ... };

                x = text_wide + 2*(border_thickness + padx);
                y = text_high  + 2*(border_thickness + pady);

                { col_preference => wg::loose_preference x,
                  row_preference => wg::loose_preference y
                };
            };

        fun drawf
            ( d,
              size as { wide, high },
              result:  Result
            )
            =
            {   result ->  { border_thickness=>bw, pady, padx, ... };
                #
                result.fontinfo ->  (font, font_ascent, font_descent);

                (*result.textinfo)
                    ->
                    { text, text_high, text_wide };

                y = case result.gravity
                        #
                        (wt::NORTH_WEST | wt::NORTH  | wt::NORTH_EAST) =>  bw + pady;
                        (wt::WEST       | wt::CENTER | wt::EAST)       => (high - text_high) / 2;
                        _                                              =>  high - bw - pady - text_high;
                    esac
                    +
                    font_ascent;

                r =  g2d::box::make (g2d::point::zero, size);

                font_high  = font_ascent + font_descent;

                txt_pen =  xc::make_pen [ xc::p::FOREGROUND (xc::rgb8_from_rgb result.fg) ];

                fun do_text (y, i)
                    = 
                    if (string::get_byte_as_char (text, i) == '\n')
                        #
                        do_text (y+font_high, i+1);
                    else
                        (get_line (font, text, i, text_wide))
                            ->
                            (nexti, linewid);

                        x = case result.gravity
                                #                                                  
                                (wt::NORTH_WEST | wt::WEST   | wt::SOUTH_WEST) =>  bw + result.padx;
                                (wt::NORTH      | wt::CENTER | wt::SOUTH)      => (wide - text_wide) / 2;
                                _                                              =>  wide - bw - padx - text_wide;
                            esac;

                        x = case result.justify
                                #
                                wt::HCENTER => x + (text_wide - linewid) / 2;
                                wt::HRIGHT  => x + (text_wide - linewid);
                                wt::HLEFT   => x;
                            esac;

                        fun skip_ws i
                            =
                            {   c = string::get_byte_as_char (text, i);
                                #
                                if   (c == '\n')         i+1;
                                elif (char::is_space c)  skip_ws (i+1);
                                else                     i;
                                fi;
                            };

                          xc::draw_transparent_string d txt_pen font 
                            ({ col=>x, row=>y }, substring (text, i, nexti-i));

                          do_text (y+font_high, skip_ws nexti);
                    fi;

                  \\ () = {   do_text (y, 0)            except _ = ();
                              #
                              case result.relief
                                  #     
                                   wg::FLAT => ();
                                   relief  => d3::draw_box d { width=>bw, relief, box=>r } result.shades;
                              esac;
                          };
              };

        fun get_text ( { textinfo, ... } : Result)
            =
           (*textinfo).text;

        fun realize (root, { kidplug, window, window_size }, result, plea_slot)
            =
            {   d =  xc::drawable_of_window  window;
                #
                plea' =  take_from_mailslot'  plea_slot;


                (xc::ignore_mouse_and_keyboard  kidplug)
                    ->
                    xc::KIDPLUG { from_other', to_mom, ... };


                fun do_mom (xc::ETC_REDRAW _, state as (draw, _))
                        =>
                        {   draw ();
                            state;
                        };

                    do_mom (xc::ETC_RESIZE ({ wide, high, ... }: g2d::Box), _)
                        =>
                        {   size = { wide, high };
                            #
                            xc::clear_drawable  d;

                            (drawf (d, size, result), size);
                         };

                    do_mom (_, state)
                        =>
                        state;
                end;


                fun do_plea (SET_TEXT t, (draw, size))
                        =>
                        {   ti = make_text_info (root, result.aspect, t, result.width, result.fontinfo,
                                       result.border_thickness, result.padx, result.pady);

                            result ->  { textinfo, ... };

                            textinfo := ti;

                            xc::clear_drawable  d;

                            block_until_mailop_fires  (to_mom  xc::REQ_RESIZE);

                            draw = drawf (d, size, result);

                            draw();

                            (draw, size);
                        };

                    do_plea (GET_TEXT reply_1shot, state)
                        =>
                        {   put_in_oneshot (reply_1shot, get_text result);
                            #
                            state;
                        };

                    do_plea (GET_SIZE_CONSTRAINT reply_1shot, state)
                        => 
                        {   put_in_oneshot (reply_1shot, size_preference_thunk_of result);
                            #
                            state;
                        };

                    do_plea (_, state)
                        =>
                        state;
                end;


                fun loop state
                    =
                    do_one_mailop [
                        plea'       ==>  (\\ plea     =  loop (do_plea (plea, state))),
                        from_other' ==>  (\\ envelope =  loop (do_mom (xc::get_contents_of_envelope envelope, state)))
                    ];

                loop (drawf (d, window_size, result), window_size);
            };                                                                                          # fun realize

        fun init (root, result as { textinfo, ... } : Result, plea_slot)
            =
            loop ()
            where 
                fun do_plea (SET_TEXT t)
                    =>
                    {   ti = make_text_info (root, result.aspect, t, result.width, result.fontinfo,
                                   result.border_thickness, result.padx, result.pady);
                        textinfo := ti;
                    };

                    do_plea (GET_TEXT            reply_1shot) =>   put_in_oneshot (reply_1shot, get_text result);
                    do_plea (GET_SIZE_CONSTRAINT reply_1shot) =>   put_in_oneshot (reply_1shot, size_preference_thunk_of result);
                    do_plea (DO_REALIZE arg                 ) =>   realize (root, arg, result, plea_slot);
                end;

                fun loop ()
                    =
                    for (;;) {
                        #
                        do_plea  (take_from_mailslot  plea_slot);
                    };
            end;

        fun message (root_window, view, args)
            =
            {   attributes = wg::find_attribute (wg::attributes (view, attributes, args));
                #
                result = get_resources (root_window, attributes);

                plea_slot = make_mailslot ();

                fun size_preference_thunk_of ()
                    =
                    {   reply_1shot =   make_oneshot_maildrop ();
                        #
                        put_in_mailslot  (plea_slot,  GET_SIZE_CONSTRAINT reply_1shot);

                        get_from_oneshot  reply_1shot;
                    };

                make_thread  "message"  {.
                    #
                    init (root_window, result, plea_slot);
                };

                MESSAGE
                  {
                    plea_slot,
                    #
                    widget =>   wg::make_widget
                                  {
                                    root_window,
                                    size_preference_thunk_of,
                                    #   
                                    args           =>  \\ () =    { background => THE result.bg },
                                    #   
                                    realize_widget =>  \\ arg =   put_in_mailslot  (plea_slot,  DO_REALIZE arg)
                                  }
                  };
            };


        fun as_widget (MESSAGE { widget, ... } )
            =
            widget;


        fun set_text (MESSAGE { plea_slot, ... }, v)
            =
            put_in_mailslot  (plea_slot,  SET_TEXT v);


        fun get_text (MESSAGE { plea_slot, ... } )
            =
            {   reply_1shot =   make_oneshot_maildrop ();
                #
                put_in_mailslot  (plea_slot,  GET_TEXT reply_1shot);

                get_from_oneshot  reply_1shot;
            };
    };                                                                                  # package message 

end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext