PreviousUpNext

15.4.1373  src/lib/x-kit/widget/fancy/graphviz/text/text-display.pkg

# text-display.pkg

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

stipulate
    package xg =  xgeometry;                            # xgeometry     is from   src/lib/std/2d/xgeometry.pkg
    #
#   package wg =  widget;                               # widget        is from   src/lib/x-kit/widget/basic/widget.pkg
    package tw = text_widget;                           # text_widget   is from   src/lib/x-kit/widget/text/text-widget.pkg
    #
    package vb = view_buffer;                           # view_buffer   is from   src/lib/x-kit/widget/fancy/graphviz/text/view-buffer.pkg
    package tc = text_canvas;                           # text_canvas   is from   src/lib/x-kit/widget/fancy/graphviz/text/text-canvas.pkg
herein

    package  text_display
    : (weak) Text_Display                               # Text_Display  is from   src/lib/x-kit/widget/fancy/graphviz/text/text-display.api
    {
        /* +DEBUG */
        tracing = logger::make_logtree_leaf { parent => xlogger::widgets_logging, name => "text_display::tracing", default => FALSE };
        fun pr s = logger::log_if tracing 0 .{ s; };
        fun prf (format_string, items) = logger::log_if tracing 0 .{ sfprintf::sprintf' format_string items; };
        /* -DEBUG */


        Text_Display
            =
            TEXT_DISPLAY
              {
                canvas:  tc::Text_Canvas,
                text:    vb::Text_Pool,
                size:    Ref( xg::Size )
              };

        #  
        fun make_text_display { canvas, text, size }
            =
            TEXT_DISPLAY
              {
                canvas,
                text,
                size => REF size
              };

        # Update the size of the display:
        #
        fun resize (TEXT_DISPLAY { text, size, ... }, size')
            =
            {   size := size';
                vb::resize (text, size');
            };

        # Return size:
        #
        fun size_of (TEXT_DISPLAY { size, ... } )
            =
            *size;

        # Return a typeball for the display:
        #
        fun make_typeball (TEXT_DISPLAY { canvas, ... }, vl)
            =
            tc::make_typeball (canvas, vl);

        # Return the default typeball
        # for the display:
        #
        fun default_typeball (TEXT_DISPLAY { canvas, ... } )
            =
            tc::default_typeball  canvas;

        # Copy a typeball, updating some attributes:
        #
        copy_typeball = tc::copy_typeball;

        # Scroll a region vertically, returning the
        # vacated rectangle and a list of damaged
        # rectangles that must be redrawn.
        #
        # The region coordinates are in pixels:
        #   "from" is the y-coord of the top of the region;
        #   "ht"   is the height of the region; and
        #   "to" is the y-coord of the new top of the region.
        #
        fun scroll_v (td as TEXT_DISPLAY { canvas, ... } )
            =
            {   blt = tc::blt canvas;

                fun scroll { from, to, high }
                    =
                    {   my xg::SIZE { wide, ... }
                            =
                            size_of td;

                        damage_mailop
                            =
                            blt {
                              to_pos  => xg::POINT     { col=>0, row=>to },
                              from_box => xg::BOX { col=>0, row=>from, wide, high }
                            };

                        my (yv, line)
                            =
                            if (from < to)   (from,    to-from);
                            else             (to+high, from-to);
                            fi;

                        { vacated => xg::BOX { col=>0, row=>yv, wide, high=>line },
                          damage  =>  damage_mailop
                        };
                    };

                scroll;
            };

        # Scroll a region horizontally, returning
        # the vacated rectangle and a list of damaged
        # rectangles that must be redrawn.
        #
        # The region coordinates are in pixels:
        #   "from" is the x-coord of the l.h.s. of the region;
        #   "wide" is the width of the region; and
        #   "to" is the x-coord of new l.h.s. of the region.
        #
        fun scroll_h (td as TEXT_DISPLAY { canvas, ... } )
            =
            scroll
            where
                blt = tc::blt canvas;

                fun scroll { from, to, wide }
                    =
                    {   my xg::SIZE { high, ... }
                            =
                            size_of  td;

                        damage_mailop
                            =
                            blt
                              {
                                to_pos  => xg::POINT     { col=>to,   row=>0 },
                                from_box => xg::BOX { col=>from, row=>0, high, wide }
                              };

                        my (xv, wv)
                            =
                            from < to   ??   (from,    to-from)
                                        ::   (to+wide, from-to);

                        { vacated =>  xg::BOX { col=>xv, row=>0, wide=>wv, high },
                          damage  =>  damage_mailop
                        };
                    };
            end;

        # Scroll the contents of a line horizontally:
        #
        fun scroll_line (TEXT_DISPLAY { canvas, text, ... } )
            =
            scroll
            where
                blt =  tc::blt  canvas;

                fun scroll { from as tw::CHAR_COORD { row, col }, to, wide }
                    =
                    {   my xg::BOX { col=>x, row=>y, high, ... }
                            =
                            vb::coord_to_box (text, from);

                        damage_mailop
                            =
                            blt
                              { to_pos  =>  xg::POINT { col=>to, row=>y },
                                from_box =>  xg::BOX  { col=>x, row=>y, high, wide }
                              };

                        my (xv, wv)
                            =
                            x < to   ??   (x,       to-x)
                                     ::   (to+wide, x-to);

                        { vacated => xg::BOX { col=>xv, row=>y, wide=>wv, high },
                          damage  => damage_mailop
                        };
                    };
            end;

        # Scroll the text vertically so that
        # the specified row is at the top of
        # the display. I.e., scroll the text
        # up by the specified number of rows:
        #
        fun scroll_up (td as TEXT_DISPLAY { text, ... } )
            =
            scroll
            where
                scroll_v =  scroll_v  td;

                fun scroll row
                    =
                    {  from =  vb::row_to_y  (text, row);

                        my xg::SIZE { high, ... }
                            =
                            size_of td;

                        scroll_v { from, to=>0, high=>high-from };
                    };

            end;

        # Scroll the text vertically so that
        # the top of the screen occupies the
        # specified row. I.e., scroll the text
        # down by the specified number of rows:
        #
        fun scroll_down (td as TEXT_DISPLAY { text, ... } )
            =
            scroll
            where
                scroll_v =  scroll_v  td;

                fun scroll row
                    =
                    {   to = vb::row_to_y (text, row);

                        my xg::SIZE { high, ... }
                            =
                            size_of td;

                        scroll_v { from=>0, to, high=>high-to };
                    };

            end;

        # Clear the specified rectangle:
        #
        fun clear_box (TEXT_DISPLAY { canvas, ... } )
            =
            tc::clear_box canvas;


        # Clear from the character coordinate
        # to the end of its line:
        #
        fun clear_to_eol' (clear_box, td as TEXT_DISPLAY { text, ... } )
            =
            clear
            where
                fun clear (cc as tw::CHAR_COORD { row, col } )
                    =
                    {   my xg::POINT { col=>x, row=>y }
                            =
                            vb::coord_to_pt (text, cc);


                        my xg::SIZE { wide, ... }
                            =
                            size_of td;

                        high = vb::get_row_ht (text, row);

                        clear_box (xg::BOX { col=>x, row=>y, wide=>wide-x, high } );
                    };
            end;


        fun clear_to_eol td
            =
            clear_to_eol' (clear_box td, td);


        # Clear the lines [start..stop]:
        #
        fun clear_lines' (clear_box, td as TEXT_DISPLAY { text, ... } )
            =
            clear
            where
                fun clear { start, stop }
                    =
                    {   y = vb::row_to_y (text, start);

                        my xg::SIZE { wide, ... }
                            =
                            size_of  td;

                        fun compute_ht (row, ht)
                            =
                            row <= stop  ??  compute_ht (row+1, ht + vb::get_row_ht (text, row))
                                         ::  ht;

                         clear_box (xg::BOX { col=>0, row=>y, wide, high=>compute_ht (start, 0) } );
                    };
            end;

        fun clear_lines td
            =
            clear_lines' (clear_box td, td);


        # Clear the area from the coordinate start
        # to the coordinate stop:
        #
        fun clear_area (td as TEXT_DISPLAY { text, ... } )
            =
            clear
            where
                clear_box   = clear_box td;

                clear_to_eol = clear_to_eol' (clear_box, td);

                clear_lines  = clear_lines'  (clear_box, td);

                fun clear { start as tw::CHAR_COORD { row=>r1, col=>c1 }, stop=>tw::CHAR_COORD { row=>r2, col=>c2 }}
                    =
                    if (r1 < r2)

                        r1 = if (c1 > 0)
                                 #
                                 clear_to_eol start;
                                 r1+1;
                             else
                                 r1;
                             fi;

                        my xg::POINT { col=>x, row=>y }
                            =
                            vb::coord_to_pt (text, tw::CHAR_COORD { row=>r2, col=>c2+1 } );

                        if (r1 < r2)
                            #
                            clear_lines { start => r1,
                                          stop  => r2 - 1
                                        };
                        fi;

                        clear_box (xg::BOX { col=>0, row=>y, high=>vb::get_row_ht (text, r1), wide=>x } );

                    elif (r1 == r2  and  c1 <= c2)

                        my xg::POINT { col=>x1, row=>y }
                            =
                            vb::coord_to_pt (text, start);

                        x2 = vb::coord_to_x (text, tw::CHAR_COORD { row=>r1, col=>c2+1 } );

                        clear_box (xg::BOX { col=>x1, row=>y, high=>vb::get_row_ht (text, r1), wide=>x2-x1 } );
                    fi;
            end;

        # Redraw the damaged region:
        #
        fun redraw (TEXT_DISPLAY { text, size, ... } ) damage_boxes
            =
            case damage_boxes

                [ xg::BOX { row=>0, col=>0, wide, high } ]
                    =>
                    {   my xg::SIZE { wide=>w, high=>h }
                            =
                            *size;

                        if (wide == w  and  high == h)   draw_all ();
                        else                             redraw' damage_boxes;
                        fi;
                    };

                _ =>  redraw'  damage_boxes;
            esac
            where

                num_rows =  vb::num_rows  text;

                # Redraw the whole canvas:
                #
                fun draw_all ()
                    =
                    draw 0
                    where
                        get_row =  vb::get_row  text;

                        fun draw i
                            =
                            if (i < num_rows)
                                #
                                tc::draw (get_row i);
                                draw (i+1);
                            fi;
                    end;

                # Redraw the damaged regions 
                #
                fun redraw' boxes
                    =
                    draw  min_row
                    where
                        get_text = vb::get_text text;


                        fun pixel_rng_to_row_rng (y1, y2)                       # "rng" here may be "range".
                            =
                            vb::pixel_rng_to_row_rng (text, y1, y2);


                        fun pixel_rng_to_col_rng (row, x1, x2)                  # "rng" here may be "range".
                            =
                            vb::pixel_rng_to_col_rng (text, row, x1, x2);


                        damage = rw_vector::make_rw_vector (num_rows, []);

                        fun min (a: Int, b) =  (a < b  ??  a  ::  b);
                        fun max (a: Int, b) =  (a > b  ??  a  ::  b);

                        fun union (row, x1, x2)
                            = 
                            rw_vector::set
                              ( damage,
                                row,
                                ins (rw_vector::get (damage, row))
                              )
                            where
                                fun ins []
                                        =>
                                        [ (x1,  x2) ];

                                    ins ((rng as (x1', x2')) ! r)
                                        =>
                                        if   (x2  < x1')     (x1, x2) ! rng ! r;
                                        elif (x2' < x1 )     rng ! (ins r);
                                        else                 (min (x1, x1'), max (x2, x2')) ! r;
                                        fi;
                                end; 
                            end;


                        # For each rectangle, compute the
                        # affected rows and add the rectangle's
                        # span to the damaged pixel intervals:
                        #
                        fun mark_pixel_damage ([], min_row, max_row)
                                =>
                                (min_row, max_row);

                            mark_pixel_damage (xg::BOX { row=>x, col=>y, wide, high } ! rest, min_row, max_row)
                                =>
                                {   my (r1, r2)
                                        =
                                        pixel_rng_to_row_rng (y, y + high - 1);

                                    start =  x;
                                    stop  =  x + wide - 1;

                                    fun mark row
                                        =
                                        if (row <= r2)
                                            #
                                            union (row, start, stop);
                                            mark (row+1);
                                        fi;

                                    mark r1;

                                    mark_pixel_damage
                                      ( rest,
                                        min (r1, min_row),
                                        max (r2, max_row)
                                      );
                                };
                        end;


                        my (min_row, max_row)
                            =
                            mark_pixel_damage (boxes, num_rows, -1);

                        # For each damaged row, compute the
                        # damaged region in character coordinates
                        # and redraw:
                        #
                        fun draw row
                            =
                            if (row <= max_row)

                                case (rw_vector::get (damage, row))
                                    #
                                    [] => ();

                                    [ (x1, x2) ]
                                        =>
                                        {   my (c1, c2)
                                                =
                                                pixel_rng_to_col_rng (row, x1, x2);

                                            tc::draw (get_text { row, start=>c1, stop=>c2 } );
                                        };

                                    ((x1, x2) ! r)
                                        =>
                                        convert (c1, c2, r)
                                        where
                                            my (c1, c2)
                                                =
                                                pixel_rng_to_col_rng (row, x1, x2);


                                            fun convert (start, stop, [])
                                                    =>
                                                    tc::draw (get_text { row, start=>c1, stop=>c2 } );

                                                convert (start, stop, (x1, x2) ! r)
                                                    =>
                                                    {   my (c1, c2)
                                                            =
                                                            pixel_rng_to_col_rng (row, x1, x2);


                                                         if (stop < c1 - 1)
                                                             #
                                                             tc::draw (get_text { row, start, stop } );
                                                             convert (c1, c2, r);
                                                         else
                                                             convert (start, c2, r);
                                                         fi;
                                                    };
                                            end;
                                        end;
                                esac;

                                draw (row+1);
                            fi;                         # fun draw
                    end;                                # fun redraw'
            end;                                        # fun redraw
    };                                                  # package text_display

end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext