desktop.anubis 14.8 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420

   
   
                             The Anubis/Paradize Project.
   
                                  The desktop widget. 
   
                          Copyright (c) Alain Prouté 2004-2005. 

   
   Authors:   Alain Prouté
              Olivier Duvernois

read tools/basis.anubis   
read widget.anubis   

   
   This is the 'desktop'  widget.  A 'desktop' is just a rectangular  area on top of which
   several widgets  may be stacked in arbitrary  positions and order.  Each  widget is put
   into an invisible  so-called 'support'.  Each support may move  within the desktop, and
   may receives commands through a variable containing a:
   
public type WidgetSupportCommand:
   hide,               // hide the widget in the support (if it is visible)
   show,               // show the widget in the support (if it is not visible)
   expose,             // move the support to top of stack
   delete.             // delete the support
   
   
   The desktop itself may also receive  commands through a variable. These commands have the
   following type:
   
public type WidgetDesktopCommand:
   none,                       // may be used as initial value
   add        (Int32 x,        // add a new support at position (x,y)
               Int32 y,        
               Int32 z,        // and stacking order z
               Widget,
               Var(WidgetSupportCommand)),    // the initial command will be executed
   delete_all.                 // delete all supports
   
   
   The initial stack  of widgets is given as  a list of 'WidgetSupport', the  head of list
   being on top of the stack:

public type WidgetSupport:
   support  (Int32                              x,   // position relative to desktop
             Int32                              y, 
             Widget                       content,
             Var(WidgetSupportCommand)    command). 
   
   The desktop  also has a  background color, and  a non mandatory background  widget. The
   background  handler is  executed when  the background  (not the  background  widget) is
   clicked. The desktop is created by:
 
   
public define Widget
   create_desktop
     (
       Int32                    width_of_desktop,
       Int32                   height_of_desktop,
       RGB                      background_color, 
       Maybe(Widget)           background_widget, 
       One -> WidgetAnswer    background_handler, 
       List(WidgetSupport)      stack_of_widgets,
       Var(WidgetDesktopCommand)        commands
     ). 
   
      
   If one  of the widgets in  the stack is transmitted  a mouse left down  click event and
   does not handle  it, the desktop captures the mouse. While  the mouse remains captured,
   moving the mouse  moves the widget. When the  mouse is liberated, the widget  is put on
   top of the stack if it has not  been moved (actually, if its final position is the same
   as its initial position).

   
   
   
   --- That's all for the public part ! --------------------------------------------------
   

   
   
read tools.anubis   
   
   
   *** (1) Setting positions of childs in a desktop. 
   
   The initial list of childs is  of type List(WidgetSupport).  However, since each widget
   contains its own position (absolute and  relative to its father), the relative position
   of  a child  relative to  the  desktop will  be used  as  the official  source for  the
   position. Hence, when the desktop is created, the list of 'WidgetSupport' (within which
   all widgets have relative position (0,0)), is transformed into a list of 'Widget', with
   relative positions correctly set. Also a monitor is attached to the command variable. 

type Child:
   child(Var(Maybe(MonitoringTicket(WidgetSupportCommand))),
         Var(WidgetSupportCommand), 
         Widget).
   
define List(Child)
   prepare_childs
     (
       List(WidgetSupport) l
     ) =
   if l is 
     {
       [ ] then [ ], 
       [supp1 . supps] then 
          if supp1 is support(x,y,wid,vc) then
          store_relative_position(wid,position(x,y));
          [child(var(failure),vc,wid) . prepare_childs(supps)]
     }.
   
   
   Now, setting  the position of a  child amounts to  setting its position to  its current
   position. The background widget is centered within the desktop. 
   
define One   
   set_childs_positions
     (
       WidgetPositionToolBox ptb,
       Int32 width,   // of desktop
       Int32 height, 
       List(Child) childs,
       Maybe(Widget) mbbgwidget
     ) =
   if childs is 
     {
       [ ] then if mbbgwidget is 
         {
           failure then unique, 
           success(bgw) then 
             if get_size(bgw)(unique) is (w,h) then
             set_position(bgw)(ptb,position((width-w)>>1,(height-h)>>1))
         },
   
       [c1 . cs] then if c1 is child(tick,vc,w1) then 
         set_position(w1)(ptb,get_position(w1)(unique));
         set_childs_positions(ptb,width,height,cs,mbbgwidget)
     }.
   
   
   
   
   
   
   *** (2) Desktop redraw function. 
   
   Redrawing the  desktop consists in  redrawing the first  widget of the stack,  then the
   second widget of the stack, but only outside the first one, and so on to the background
   widget and the background color.
            
   
define One
   redraw_desktop
     (
       WidgetDrawToolBox           dtb, 
       RGB                      bcolor,     // background color
       Maybe(Widget)             backw,     // background widget
       List(Child)               stack,     // stack of child widgets
       List(WidgetRectangle)     where      // where to draw
     ) =
   if stack is 
     {
       [ ] then 
         if backw is 
           {
             failure then 
               forget(map((WidgetRectangle r) |-> draw(dtb)(r,bcolor),where)), 
   
             success(wid) then 
               forget(map((WidgetRectangle r) |-> redraw(wid)(dtb,r),where));
               if get_position(wid)(unique) is position(x1,y1) then 
               if get_size(wid)(unique) is (w1,h1) then 
               redraw_desktop(dtb,bcolor,failure,[],where - rect(x1,y1,x1+w1,y1+h1))
           },
   
       [c1 . cs] then if c1 is child(ticket,cv1,wid1) then 
         if *cv1 is 
           {
             hide   then unique, 
             show   then forget(map((WidgetRectangle r) |-> redraw(wid1)(dtb,r),where)),
             expose then forget(map((WidgetRectangle r) |-> redraw(wid1)(dtb,r),where)),
             delete then alert,
           }; 
         if *cv1 = hide
         then 
           redraw_desktop(dtb,bcolor,backw,cs,where)
         else
           (if get_position(wid1)(unique) is position(x1,y1) then 
            if get_size(wid1)(unique) is (w1,h1) then 
            redraw_desktop(dtb,bcolor,backw,cs,where - rect(x1,y1,x1+w1,y1+h1)))
     }.
   
   
   
   
   *** (3) Desktop event handler. 
   
   
   The next function removes the n-th element from a list of childs. 
   
define List($T)
   remove_nth
     (
       Int32 n, 
       List($T) l
     ) =
   if n < 0 then alert else
   if l is 
     {
       [ ] then alert,
       [w1 . ws] then
         if n = 0 
         then ws
         else [w1 . remove_nth(n-1,ws)]
     }.
   
   
   
   Below is  the event  handler for  the captured mouse.   It is  called with  the initial
   position of the mouse (at the moment it was captured), and its previous position (taken
   from the previous mouse event). The widget of the stack which has been selected is also
   given, together  with its depth in the  stack. Finally, the variable  holding the whole
   stack is also given. This is useful when putting a widget on top of the stack. 
   
   While the mouse is not liberated, the  selected widget is just moved within the desktop
   (following the  mouse movements). When the  mouse is liberated, we  compare the current
   position with the initial  position. If they are equal the widget is  put on top of the
   stack.
   
define (KeyboardState,WidgetMouseCapturedEvent) -> WidgetAnswer
   captured_mouse_handler
     (
       Int32 ix,             // initial mouse position
       Int32 iy,    
       Var(Int32) px,        // previous mouse position 
       Var(Int32) py,
       Int32 n,              // depth of 'wid' in stack
       Child ch,             // mouse impacted widget
       Var(List(Child)) childs_loc
     ) =
   (KeyboardState ks, WidgetMouseCapturedEvent e) |->
     if e is 
       {
         moved(x,y)     then if ch is child(_,_,wid) then 
                             if get_position(wid)(unique) is position(wx,wy) then
                             with dx = x-*px, 
                                  dy = y-*py,
                                 nwx = wx+dx, 
                                 nwy = wy+dy, 
                               store_relative_position(wid,position(nwx,nwy));
                               px <- x; 
                               py <- y; 
                               if get_size(wid)(unique) is (w,h) then 
                                 handled(union([rect(wx,wy,wx+w,wy+h),
                                                rect(nwx,nwy,nwx+w,nwy+h)])), 
         liberated(x,y) then 
           if (x = ix & y = iy)
           then (childs_loc <- [ch . remove_nth(n,*childs_loc)]; 
                 if ch is child(_,_,wid) then 
                 if get_size(wid)(unique) is (w,h) then 
                 if get_position(wid)(unique) is position(wx,wy) then
                 handled([rect(wx,wy,wx+w,wy+h)]))
           else handled([])
       }.
   
   
   Below is  a function used for  dispatching the events  amongh the widgets of  the stack
   (and the background widget). The coordinates of the mouse pointer are compared with the
   rectangles of all widgets of the stack beginning by the top widget. The first widget in
   the rectangle of which  the mouse lies receives the event. If  the event is not handled
   by the widget, and if  it is a left down click, the mouse is  captured. If no widget in
   the stack  receives the event,  the background  widget (if any)  is tryied out.  If the
   background widget does not receive the event, the background handler is called. 
   
   
define WidgetAnswer
   dispatch_event
     (
       WidgetEventToolBox                             etb,
       WidgetNormalEvent                                e,
       Int32                                            x, 
       Int32                                            y, 
       Int32                                            n,   // current depth in stack
       List(Child)                                 childs,
       Var(List(Child))                        childs_loc, 
       Maybe(Widget)                           mbbgwidget,
       One -> WidgetAnswer                      bghandler
     ) =
   if childs is 
     {
       [ ] then if mbbgwidget is 
         {
           failure then bghandler(unique), 
           success(bgwid) then 
             if get_position(bgwid)(unique) is position(x1,y1) then 
             if get_size(bgwid)(unique) is (w1,h1) then 
             if (x1 =< x & x < x1+w1 & y1 =< y & y < y1+h1) 
             then main_event_handler(bgwid)(etb,e)
             else dispatch_event(etb,e,x,y,n+1,[],childs_loc,failure,bghandler)
         }, 
   
       [ch1 . chs] then if ch1 is child(tv1,cv1,wid1) then 
          if *tv1 is
            {
              failure then 
                tv1 <- success(register_monitor(cv1,
                  (One u) |-> if *cv1 is 
                    {
                      hide     then forget(queue_event(etb,expose)),
                      show     then forget(queue_event(etb,expose)),
                      expose   then forget(queue_event(etb,expose)),
                      delete   then unique
                    })),
   
              success(_) then unique
             };
          if get_position(wid1)(unique) is position(x1,y1) then 
          if get_size(wid1)(unique) is (w1,h1) then 
          if (x1 =< x & x < x1+w1 & y1 =< y & y < y1+h1 & *cv1 /= hide) 
          then with answer = main_event_handler(wid1)(etb,e),
          if answer is 
            {
              not_handled(l)    then if e is 
                {
                  mouse_gone then handled([]), 
                  mouse_move(ks,x2,y2) then answer,
                  mouse_click(ks,mc,x2,y2) then if mc is 
                    {
                      left_down   then forget(capture_mouse(etb,
                                         captured_mouse_handler(x2,y2,var(x2),var(y2),n,ch1,childs_loc)));
                                       handled([]), 
                      left_up     then not_handled([]),
                      middle_down then not_handled([]),
                      middle_up   then not_handled([]),
                      right_down  then not_handled([]), 
                      right_up    then not_handled([])
                    }
                }
              handled(l)     then answer,
              resized        then answer
            }
          else dispatch_event(etb,e,x,y,n+1,chs,childs_loc,mbbgwidget,bghandler)
     }.
   
   
   
   Finally, below is the event handler for the desktop. 
   
define WidgetAnswer   
   desktop_event_handler
     (
       WidgetEventToolBox                   etb,
       WidgetNormalEvent                      e,
       Var(List(Child))              childs_loc,
       Maybe(Widget)                 mbbgwidget,
       One -> WidgetAnswer            bghandler
     ) =
   if e is 
     {
       mouse_gone             then handled([]), 
       mouse_move(ks,x,y)     then dispatch_event(etb,e,x,y,0,*childs_loc,childs_loc,mbbgwidget,bghandler),
       mouse_click(ks,mc,x,y) then dispatch_event(etb,e,x,y,0,*childs_loc,childs_loc,mbbgwidget,bghandler)
     }.
   
   
   

   *** (4) Creating the desktop. 
   
   
   We just  have to prepare  the childs as  explained above, and put  the result in  a new
   variable.
   
public define Widget
   create_desktop
     (
       Int32                  width, 
       Int32                  height, 
       RGB                    bgcolor, 
       Maybe(Widget)          mbbgwidget, 
       One -> WidgetAnswer    bghandler, 
       List(WidgetSupport)    stack,
       Var(WidgetDesktopCommand) commands, 
     ) =
   with childs = var(prepare_childs(stack)), 
   create_widget(
   
   /* set childs positions */ 
   (WidgetPositionToolBox ptb) |-> set_childs_positions(ptb,width,height,*childs,mbbgwidget), 
   
   /* get size */ 
   (One u) |-> (width,height),
   
   /* redraw */ 
   (WidgetDrawToolBox dtb) |-> 
     redraw_desktop(dtb,
                    bgcolor, 
                    mbbgwidget, 
                    *childs,
                    [rect(0,0,width,height)]), 
    
   
   /* duplicate */ 
   (One u) |-> create_desktop(width,height,bgcolor,mbbgwidget,bghandler,stack,commands), 
   
   /* change size */ 
   (Int32 w, Int32 h) |-> unique, 
   
   /* handle normal events */ 
   (WidgetEventToolBox etb, WidgetNormalEvent e) |-> 
     desktop_event_handler(etb,e,childs,mbbgwidget,bghandler),
   
   /* registrations */ 
   [ ]
   ).