Commit 344585a84f8d17c5ec647986673fa7882ae89fc0
1 parent
6d42d3d6
*** empty log message ***
Showing
1 changed file
with
712 additions
and
0 deletions
Show diff stats
| 1 | + | |
| 2 | + The Anubis Project. | |
| 3 | + | |
| 4 | + A Widget System (4th version). | |
| 5 | + | |
| 6 | + Copyright (c) Alain Proute' 2005. | |
| 7 | + | |
| 8 | + | |
| 9 | + Authors: Alain Proute' | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + This file is the entry point of this widget system if you want to create new sorts of | |
| 14 | + widgets. If you just want to use already existing widgets, see the file | |
| 15 | + 'host_window.anubis' in the same directory. | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + ------------------------------------ Table of Contents -------------------------------- | |
| 20 | + | |
| 21 | + *** (1) How widgets are working. | |
| 22 | + | |
| 23 | + *** (1) Geometry. | |
| 24 | + *** (1.1) Absolute and relative coordinates. | |
| 25 | + *** (1.2) Absolute and relative rectangles. | |
| 26 | + *** (1.3) Widgets are rectangular. | |
| 27 | + | |
| 28 | + *** (2) Drawing. | |
| 29 | + *** (2.1) The draw tool box. | |
| 30 | + *** (2.2) The 'draw method'. | |
| 31 | + *** (2.3) Drawing tools. | |
| 32 | + *** (2.3.1) Drawing a child widget. | |
| 33 | + *** (2.3.2) Drawing rectangles. | |
| 34 | + *** (2.3.3) Drawing images. | |
| 35 | + *** (2.3.4) Drawing character strings. | |
| 36 | + | |
| 37 | + *** (3) Handling events. | |
| 38 | + *** (3.1) Classification of events. | |
| 39 | + *** (3.1.1) 'mouse_move' and 'mouse_click'. | |
| 40 | + *** (3.1.2) 'mouse_gone'. | |
| 41 | + *** (3.1.3) 'captured_mouse_move' and 'captured_mouse_liberated'. | |
| 42 | + *** (3.1.4) 'key_down'. | |
| 43 | + *** (3.1.5) 'changed'. | |
| 44 | + *** (3.2) Classification of answers. | |
| 45 | + *** (3.2.1) 'not_handled' and 'handled'. | |
| 46 | + *** (3.2.2) 'resized'. | |
| 47 | + *** (3.2.3) 'want_to_capture_mouse' and 'want_to_capture_keyboard'. | |
| 48 | + *** (3.3) Transmitting events to childs. | |
| 49 | + *** (3.4) Manipulating areas. | |
| 50 | + *** (3.4.1) Creating areas. | |
| 51 | + *** (3.4.2) Making the union of two areas. | |
| 52 | + *** (3.4.3) Transmitting areas between widgets. | |
| 53 | + | |
| 54 | + *** (4) Monitoring dynamic variables. | |
| 55 | + | |
| 56 | + *** (5) Creating your widget. | |
| 57 | + | |
| 58 | + --------------------------------------------------------------------------------------- | |
| 59 | + | |
| 60 | + | |
| 61 | + *** (1) How widgets are working. | |
| 62 | + | |
| 63 | + A widget is an 'object' (in the sens of the Object Oriented Methodology) by its very | |
| 64 | + nature. This essentially means that a widget has an internal state, and contains | |
| 65 | + methods (which are functions, more precisely 'commands', because their execution is in | |
| 66 | + general non deterministic). | |
| 67 | + | |
| 68 | + A widget may have 'child' widgets, so that widgets are organized in tree form. When a | |
| 69 | + host window is opened, it receives a widget which is called the 'root' widget. This | |
| 70 | + widget may have childs which may themself have childs, and so on. Hence, the window | |
| 71 | + always contains a tree or hierarchy of widgets. | |
| 72 | + | |
| 73 | + Widgets have two main methods: the 'draw method' and the 'event handler'. The role of | |
| 74 | + the first one is to redraw the widget on the screen, and the role of the second one is | |
| 75 | + to handle events. Each widget is responsible of its own childs. When a widget receive a | |
| 76 | + redraw order, i.e. when the draw method of this widget is called, the widget must | |
| 77 | + redraw itself and also call the draw methods of its childs. Similarily, when a widget | |
| 78 | + receives an event, it has the responsability to decide if it should transmit the event | |
| 79 | + to its childs or not. | |
| 80 | + | |
| 81 | + It is important to understand that theses two methods are completely disjoint. The | |
| 82 | + widget system transmits an event (mouse event, keyboard event and other event) to the | |
| 83 | + root widget which eventually transmits it to its childs and so on. Each widget must | |
| 84 | + decide if the event has been handled or not (by itself or by one of its childs), and | |
| 85 | + must return an answer. The widget may have to combine the answers returned by its | |
| 86 | + childs in order to make its own answer. Handling an event does not produce any | |
| 87 | + redrawing, but the answer contains informations on the area which needs to be | |
| 88 | + redrawn. When the widget system receives this answer, it optimizes the area into a | |
| 89 | + disjoint union of rectangles and calls the draw method of the root widget with this | |
| 90 | + area as one of the arguments. | |
| 91 | + | |
| 92 | + Despite the fact that widgets are objects, their position in the host window is not | |
| 93 | + part of their internal state. When a widget needs to redraw a child widget, it calls a | |
| 94 | + method which takes the relative position of the child widget (relative to its father | |
| 95 | + widget) as an argument. The same is true for the transmission of events. Hence, a | |
| 96 | + widget has the responsability to know the positions of its childs relative to | |
| 97 | + itself. If getting these positions require heavy computation, the widget may store them | |
| 98 | + into dynamic variables, but in this case, the widget also has the responsability of | |
| 99 | + keeping these variables up to date. | |
| 100 | + | |
| 101 | + An important feature is the possibility of capturing either the mouse or the | |
| 102 | + keyboard. For example, when a 'text input' widget is clicked upon, it should capture | |
| 103 | + the keyboard. To that end, the widget uses a function which creates a 'capture ticket' | |
| 104 | + and returns an appropriate 'keyboard capture' answer, containing this ticket. The | |
| 105 | + widget must also keep the ticket. Later, when a keyboard event arrives, the widget | |
| 106 | + system transmits this event in the form of a function taking this ticket as an | |
| 107 | + argument. Each widget which is supposed to be able to capture the keyboard, should | |
| 108 | + apply this function to the ticket it has created. The function returns either 'failure' | |
| 109 | + if then ticket is invalid, or 'success(e)' if it is valid, where 'e' is the actual | |
| 110 | + captured event. This event must be handled, and an answer must be returned, which will | |
| 111 | + eventually induce a redrawing. | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + *** (1) Geometry. | |
| 117 | + | |
| 118 | + | |
| 119 | + *** (1.1) Absolute and relative coordinates. | |
| 120 | + | |
| 121 | + We use two sorts of coordinates: absolute cordinates, which referer to the host window, | |
| 122 | + and relative coordinates which refer to a particular widget. In this file, you learn | |
| 123 | + how to create a new widget. This widget will be called 'your widget', and relative | |
| 124 | + coordinates are always relative to your widget. | |
| 125 | + | |
| 126 | + The 'x' coordinate grows from left to right, and the 'y' coordinate grows from top to | |
| 127 | + bottom (the unit of mesure is the pixel): | |
| 128 | + | |
| 129 | + | |
| 130 | + +------------------------> x | |
| 131 | + | | |
| 132 | + | | |
| 133 | + | | |
| 134 | + | | |
| 135 | + | | |
| 136 | + | | |
| 137 | + | | |
| 138 | + | | |
| 139 | + V | |
| 140 | + y | |
| 141 | + | |
| 142 | + The leftmost-topmost pixel of the (client part of the) host window has absolute | |
| 143 | + coordinates (0,0). The leftmost-topmost pixel of your widget has relative coordinates | |
| 144 | + (0,0). You don't have to worry about absolute coordinates. All the tools you have to | |
| 145 | + provide for creating a new widget use coordinates which are relative to your widget. | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + *** (1.2) Absolute and relative rectangles. | |
| 150 | + | |
| 151 | + As for coordinates, rectangles are of two sorts: absolute rectangles and relative | |
| 152 | + rectangles. Absolute rectangles are represented by the type 'Rectangle' defined in | |
| 153 | + 'predefined.anubis'. Relative rectangle (i.e. relative to your widget) are represented | |
| 154 | + by the type: | |
| 155 | + | |
| 156 | +public type WidgetRectangle: | |
| 157 | + rect(Int32 x, | |
| 158 | + Int32 y, | |
| 159 | + Int32 u, | |
| 160 | + Int32 v). | |
| 161 | + | |
| 162 | + | |
| 163 | + A point of relative coordinates '(a,b)' belongs to the relative rectangle | |
| 164 | + 'rect(x,y,u,v)' if and only if: | |
| 165 | + | |
| 166 | + x =< a < u & y =< b < v | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + *** (1.3) Widgets are rectangular. | |
| 172 | + | |
| 173 | + Widgets are rectangular. Hence, each widget has a width and a height (which may vary | |
| 174 | + during the lifetime of the widget). When a widget of width 'w' and height 'h' is drawn | |
| 175 | + at some position '(x,y)' in the host window, the drawing is performed within the | |
| 176 | + absolute rectangle: | |
| 177 | + | |
| 178 | + rect(x,y,xw,y+h) | |
| 179 | + | |
| 180 | + Equivalently, if this widget has relative coordinates '(a,b)' (relative to your | |
| 181 | + widget), the drawing occurs in the relative rectangle 'rect(a,b,a+w,b+h)' (relative to | |
| 182 | + your widget). | |
| 183 | + | |
| 184 | + As already mentioned, your widget must know the relative positions of its childs. The | |
| 185 | + computation of these positions will in most cases make use of the size of the | |
| 186 | + childs. To that end, this system provides a tool for getting the size (width,height) of | |
| 187 | + a child widget. | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + *** (2) Drawing. | |
| 193 | + | |
| 194 | + *** (2.1) The draw tool box. | |
| 195 | + | |
| 196 | + Your widget must be able to redraw itself. This means that when you create your widget, | |
| 197 | + you provide a 'draw method'. When it is called, this method receives an argument of | |
| 198 | + type: | |
| 199 | + | |
| 200 | +public type WidgetDrawToolBox:... | |
| 201 | + | |
| 202 | + This is an opaque type. Data of this type are used to hide all the details of | |
| 203 | + conversions between absolute and relative coordinates, and several other things that | |
| 204 | + you dont need to manipulate directly. | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + *** (2.2) The 'draw method'. | |
| 209 | + | |
| 210 | + The draw method of your widget is a function of type: | |
| 211 | + | |
| 212 | + WidgetDrawToolBox -> One | |
| 213 | + | |
| 214 | + This means that when your widget receives the order to redraw itself, it also receives | |
| 215 | + an appropriate draw tool box. A set of tools are provided in this draw tool box. All | |
| 216 | + these tools have the same name 'draw'. There are distinguished by their type. | |
| 217 | + | |
| 218 | + So for example, if the draw tool box is 'dtb', you can draw a pink rectangle of width | |
| 219 | + 100 pixel and height 20 pixels at relative position (10,10) in your widget, with the | |
| 220 | + following command: | |
| 221 | + | |
| 222 | + draw(dtb)(rect(10,10,110,30),pink) | |
| 223 | + | |
| 224 | + (which returns 'unique' of type 'One'). | |
| 225 | + | |
| 226 | + There are similar tools for drawing child widgets, images and character strings. Here | |
| 227 | + they are. | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + *** (2.3) Drawing tools. | |
| 232 | + | |
| 233 | + Now we describe the tools that you can use in order to construct the draw method of | |
| 234 | + your widget. All these tools have the same name: 'draw', and they must be extracted | |
| 235 | + from the draw tool box as follows: | |
| 236 | + | |
| 237 | + draw(dtb) | |
| 238 | + | |
| 239 | + if 'dtb' is the name of the draw tool box. | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + *** (2.3.1) Drawing a child widget. | |
| 244 | + | |
| 245 | + When it is asked to redraw itself, your widget must also redraw its childs (if any). To | |
| 246 | + redraw a child, use the following tool: | |
| 247 | + | |
| 248 | +public define (Widget child, Int32 x, Int32 y) -> One | |
| 249 | + draw | |
| 250 | + ( | |
| 251 | + WidgetDrawToolBox dtb | |
| 252 | + ). | |
| 253 | + | |
| 254 | + For example, in order to redraw the child 'c' at position '(x,y)' (relative to your | |
| 255 | + widget), you must execute: | |
| 256 | + | |
| 257 | + draw(dtb)(c,x,y) | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + *** (2.3.2) Drawing rectangles. | |
| 262 | + | |
| 263 | + If you want to draw a colored rectangle in your widget, use the following tool: | |
| 264 | + | |
| 265 | +public define (WidgetRectangle,RGB) -> One | |
| 266 | + draw | |
| 267 | + ( | |
| 268 | + WidgetDrawToolBox dtb | |
| 269 | + ). | |
| 270 | + | |
| 271 | + For example, write: | |
| 272 | + | |
| 273 | + draw(dtb)(rect(10,10,20,20),rgb(0,0,0)) | |
| 274 | + | |
| 275 | + for drawing a black square 10 pixels wide at position (10,10). The coordinates are | |
| 276 | + relative to your widget as usual. | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + *** (2.3.3) Drawing images. | |
| 282 | + | |
| 283 | + If you want to draw an image into your widget, use the following tool: | |
| 284 | + | |
| 285 | +public define (HostImage, Int32 x, Int32 y) -> One | |
| 286 | + draw | |
| 287 | + ( | |
| 288 | + WidgetDrawToolBox dtb | |
| 289 | + ). | |
| 290 | + | |
| 291 | + | |
| 292 | + For example, | |
| 293 | + | |
| 294 | + draw(dtb)(image,x,y) | |
| 295 | + | |
| 296 | + where 'image' is of type 'HostImage', where and 'x' and 'y' are of type Int32 will draw | |
| 297 | + the image at position (x,y) in your widget. This means that the upper left corner of | |
| 298 | + the image will be drawn at (x,y). As usual, coordinates are relative to your widget. | |
| 299 | + | |
| 300 | + You may aslo want to clip the image before drawing it. In this case, use the following | |
| 301 | + variant: | |
| 302 | + | |
| 303 | +public define (HostImage, Int32 x, Int32 y, WidgetRectangle clip) -> One | |
| 304 | + draw | |
| 305 | + ( | |
| 306 | + WidgetDrawToolBox dtb | |
| 307 | + ). | |
| 308 | + | |
| 309 | + Again the rectabngle 'clip' is relative to your widget. | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + *** (2.3.4) Drawing character strings. | |
| 314 | + | |
| 315 | + If you want to draw a character string into your widget, use the following tool: | |
| 316 | + | |
| 317 | +public define (String s, SystemFont f, RGB c, Int32 x, Int32 y) -> Int32 | |
| 318 | + draw | |
| 319 | + ( | |
| 320 | + WidgetDrawToolBox dtb | |
| 321 | + ). | |
| 322 | + | |
| 323 | + For example the command: | |
| 324 | + | |
| 325 | + draw(dtb)("gabuzomeu",f,c,10,10) | |
| 326 | + | |
| 327 | + will draw the string "gabuzomeu" at position (10,10) in your widget using the given | |
| 328 | + font 'f' and color 'c'. Note that (x,y) represents the upper left corner of the | |
| 329 | + drawing. If the height of the font is 'h', the base point of the first character will | |
| 330 | + be at position (x,y+h). | |
| 331 | + | |
| 332 | + The value returned is the width of the printed string in pixels. Notice also that this | |
| 333 | + command does not draw any background. Only the pixels which belong to the body of the | |
| 334 | + characters are drawn. | |
| 335 | + | |
| 336 | + You may also want to clip that drawing. In this case, use the following variant: | |
| 337 | + | |
| 338 | +public define (String s, SystemFont f, RGB c, Int32 x, Int32 y, | |
| 339 | + WidgetRectangle clip) -> Int32 | |
| 340 | + draw | |
| 341 | + ( | |
| 342 | + WidgetDrawToolBox dtb | |
| 343 | + ). | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + *** (3) Handling events. | |
| 349 | + | |
| 350 | + Widgets must also handle events. We will have to use the following types: | |
| 351 | + | |
| 352 | +public type WidgetEventToolBox:... | |
| 353 | +public type WidgetEvent:... | |
| 354 | +public type WidgetAnswer:... | |
| 355 | + | |
| 356 | + The first one is opaque, and plays a role analogous to 'WidgetDrawToolBox'. The others | |
| 357 | + are not opaque, and are discussed below. | |
| 358 | + | |
| 359 | + The event handler of your widget is a function of type: | |
| 360 | + | |
| 361 | + (WidgetEventToolBox, WidgetEvent) -> WidgetAnswer | |
| 362 | + | |
| 363 | + That your widget 'receives' an event just means that this function has been called (by | |
| 364 | + its father widget, or by the widget system itself if here is no father, i.e. if your | |
| 365 | + widget is the root widget). | |
| 366 | + | |
| 367 | + The answer (of type 'WidgetAnswer') gives informations on how the event has been | |
| 368 | + handled, and on which area of the host window should be redrawn as a consequence of the | |
| 369 | + event. | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + *** (3.1) Classification of events. | |
| 374 | + | |
| 375 | + The following type describes all the events a widget may have to handle. | |
| 376 | + | |
| 377 | +public type WidgetEvent: | |
| 378 | + mouse_move (KeyboardState ks, Int32 x, Int32 y), | |
| 379 | + mouse_click (KeyboardState ks, MouseClick mc, Int32 x, Int32 y), | |
| 380 | + mouse_gone, | |
| 381 | + captured_mouse_move (Var(One) -> Maybe((Int32 x, Int32 y))), | |
| 382 | + captured_mouse_liberated (Var(One) -> Maybe((Int32 x, Int32 y))), | |
| 383 | + key_down (Var(One) -> Maybe((KeyboardState ks, KeyboardKey kk))), | |
| 384 | + changed (Int32 n). | |
| 385 | + | |
| 386 | + The types 'KeyboardState' and 'MouseClick' are defined in 'predefined.anubis'. | |
| 387 | + | |
| 388 | + Recall that your widget must eventually transmit events to its childs, and construct | |
| 389 | + its answer depending on the answers received from its childs. We now describe the | |
| 390 | + meanings of these events. | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + *** (3.1.1) 'mouse_move' and 'mouse_click'. | |
| 395 | + | |
| 396 | + A widget is concerned by a 'mouse_move' or 'mouse_click' event only if the mouse | |
| 397 | + pointer lies inside the rectangle of the widget. This is a rule of this system that | |
| 398 | + your widget must respect. Hence, when your widget receives a 'mouse_move' or | |
| 399 | + 'mouse_click' event, you may rely on the hypothesis that the mouse pointer lies inside | |
| 400 | + the rectangle of your widget. | |
| 401 | + | |
| 402 | + As a consequence, if your widget has one or several childs, it should transmit such | |
| 403 | + events to a given child only if the mouse pointer lies within the rectangle of this | |
| 404 | + child. However, you don't have to worry about that in general, because the widget | |
| 405 | + system checks this condition before allowing the transmission of 'mouse_move' and | |
| 406 | + 'mouse_click' events. Hence, you may either check the condition yourself, or just | |
| 407 | + transmit such events to all childs. Nevertheless, there are widgets which need to check | |
| 408 | + this condition, as for example the desktop widget, because it may have several childs | |
| 409 | + whose rectangles overlap. The desktop widget must decide which child should receive the | |
| 410 | + event (using the z-order), because the widget system is not able to do it since it does | |
| 411 | + not know anything about the z-order. | |
| 412 | + | |
| 413 | + Notice that if the widget system blocks the transmission of such an event to a given | |
| 414 | + child, it replaces it by a 'mouse_gone' event. | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + *** (3.1.2) 'mouse_gone'. | |
| 419 | + | |
| 420 | + If your widget receives a 'mouse_gone' event, you can deduce that the mouse pointer | |
| 421 | + does not lie within the rectangle of your widget. This may be useful for example to | |
| 422 | + change the visual aspect of the widget when the mouse pointer is leaving the widget. | |
| 423 | + | |
| 424 | + Now, if your widget has child widgets, it is responsible of generating 'mouse_gone' | |
| 425 | + event for its childs. Your widget must send at least one 'mouse_gone' event to a child, | |
| 426 | + if the mouse pointer was within the rectangle of the child at the time of the previous | |
| 427 | + mouse event, and if this condition is no more true. Hence, your widget must eventually | |
| 428 | + keep that information that some child contains the mouse pointer in some variable. | |
| 429 | + This variable may be for example of type 'Maybe(Widget)'. If it contains 'failure', | |
| 430 | + this means that no child contains the mouse pointer. If it contains 'success(c)' this | |
| 431 | + means that the child 'c' contains the mouse pointer. While you keep this variable up to | |
| 432 | + date, you must also generate appropriate 'mouse_gone' events for your childs. | |
| 433 | + | |
| 434 | + Generating extra 'mouse_gone' events (i.e. sending several 'mouse_gone' events to a | |
| 435 | + child consecutively) is of course a waste of time. Nevertheless, it has no other | |
| 436 | + inconvenient. Also remember that the system replace 'mouse_move' and 'mouse_click' | |
| 437 | + events by 'mouse_gone' events if they are transmitted to widgets which do not contain | |
| 438 | + the mouse pointer. This fact may simplify the programming of the event handler of your | |
| 439 | + widget. | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + *** (3.1.3) 'captured_mouse_move' and 'captured_mouse_liberated'. | |
| 446 | + | |
| 447 | + If your widget has captured the mouse (how to capture the mouse will be explained | |
| 448 | + later), it may receive 'captured_mouse_move' and 'captured_mouse_liberated' events. | |
| 449 | + Such events are not submitted to the same rules as the 'mouse_move' and 'mouse_click' | |
| 450 | + events. | |
| 451 | + | |
| 452 | + Your widget captured the mouse using a 'ticket', which is a dynamic variable of type | |
| 453 | + 'Var(One)'. When a 'captured_mouse_move' and 'captured_mouse_liberated' event arrives, | |
| 454 | + what you get is just a function taking such a ticket as its unique argument. The only | |
| 455 | + thing you can do with this event is to apply the function to your ticket. | |
| 456 | + | |
| 457 | + If the result is 'failure', this means that your widget is not concerned by this | |
| 458 | + event. Actually, this also implies that the capture your widget initiated is over. If | |
| 459 | + the result is 'success(p)', this means that your widget is concerned by the event (the | |
| 460 | + capture is still valid), and that 'p' is the pair of coordinats for the mouse pointer | |
| 461 | + (relative to your widget as usual). | |
| 462 | + | |
| 463 | + If your widget is not concerned by such an event, it must transmit it to all of its | |
| 464 | + childs. | |
| 465 | + | |
| 466 | + | |
| 467 | + *** (3.1.4) 'key_down'. | |
| 468 | + | |
| 469 | + This event works like 'captured_mouse_move' and 'captured_mouse_liberated' events. You | |
| 470 | + must apply the function to your ticket in order to know if you are concerned by the | |
| 471 | + event. If it is the case you get the keyboard state and the character which has been | |
| 472 | + entered. | |
| 473 | + | |
| 474 | + If your widget is not concerned by this event it must transmit it to all of its childs. | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + *** (3.1.5) 'changed'. | |
| 479 | + | |
| 480 | + This event is generated by the widget system when a dynamic variable registred at the | |
| 481 | + widget system is reassigned. | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + *** (3.2) Classification of answers. | |
| 487 | + | |
| 488 | + When a widget receives an event, it may handle or not handle the event, but in all | |
| 489 | + cases it must return an answer. An answer may contain an 'area' which represents the | |
| 490 | + part of the host window which needs to be redrawn. Areas are represented as follows: | |
| 491 | + | |
| 492 | +public type WidgetArea:... | |
| 493 | + | |
| 494 | + This is a opaque type, because we want to hide all conversions between absolute and | |
| 495 | + relative coordinates. As far as your are concerned, you manipulate only relative | |
| 496 | + coordinates (and relative rectangles). | |
| 497 | + | |
| 498 | + Answers are the following: | |
| 499 | + | |
| 500 | +public type WidgetAnswer: | |
| 501 | + not_handled (WidgetArea), | |
| 502 | + handled (WidgetArea), | |
| 503 | + resized, | |
| 504 | + want_to_capture_mouse (Var(One),WidgetArea), | |
| 505 | + want_to_capture_keyboard (Var(One),WidgetArea). | |
| 506 | + | |
| 507 | + We will give later several tools for manipulating areas. Now, we discuss the meaning of | |
| 508 | + these answers. | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + *** (3.2.1) 'not_handled' and 'handled'. | |
| 513 | + | |
| 514 | + If your widget handles the event, it should normally return a 'handled(l)' answer, | |
| 515 | + where 'l' is the list of the rectangles (relative to your widget) within which | |
| 516 | + redrawing must occur. 'not_handled(l)' is similar but means that your widget did not | |
| 517 | + handle the event. Nevertheless, even if the event is not handled, the redrawing of | |
| 518 | + several rectangles may be needed. | |
| 519 | + | |
| 520 | + If your widget has transmitted the event to one or several childs, it will receive | |
| 521 | + answers from these childs, and must construct its own answer to be returned to its | |
| 522 | + father. How the answer of your widget is constructed from the answers returned by the | |
| 523 | + childs may vary depending on the behavior of your widget. This is actually a central | |
| 524 | + point of widget programming and you should pay much attention to it. Nevertheless, | |
| 525 | + there is no special rule for this. This is up to you. | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + *** (3.2.2) 'resized'. | |
| 530 | + | |
| 531 | + This answer means that the event has been handled, and that it induced a change of size | |
| 532 | + of the widget. If your widget receives such an answer from one of its childs, it knows | |
| 533 | + that the sier of a childs has changed. This may induce a change of size of your widget, | |
| 534 | + but not necessarily. For example, the size of the 'window' widget is independent of the | |
| 535 | + size of its content. | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + *** (3.2.3) 'want_to_capture_mouse' and 'want_to_capture_keyboard'. | |
| 541 | + | |
| 542 | + Your widget must return one of these answers if it wants to capture either the mouse or | |
| 543 | + the keyboard. This answer requires a component of type 'Var(One)' that we call the | |
| 544 | + 'capture ticket'. Your widget may either create a new such ticket at each capture, or | |
| 545 | + use alway the same one (for example, it may be created before the creation of your | |
| 546 | + widget and be accessible to the event handler). | |
| 547 | + | |
| 548 | + The widget system generates 'captured_mouse_move', 'captures_mouse_liberated' and | |
| 549 | + 'key_down' events which are able to recognize your ticket. | |
| 550 | + | |
| 551 | + These two answers also contain the list of widget rectangles within which redrawing | |
| 552 | + must occur. | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + *** (3.3) Transmitting events to childs. | |
| 557 | + | |
| 558 | + Events are transmitted to childs using a tool extracted from the event tool box: | |
| 559 | + | |
| 560 | +public define (Widget child, Int32 x, Int32 y, WidgetEvent) -> WidgetAnswer | |
| 561 | + transmit | |
| 562 | + ( | |
| 563 | + WidgetEventToolBox etb | |
| 564 | + ). | |
| 565 | + | |
| 566 | + If your event tool box is 'etb', and if you want to transmit the event 'e' to the child | |
| 567 | + 'c', whose position relative to your widget is '(x,y)', use the following: | |
| 568 | + | |
| 569 | + transmit(etb)(c,x,y,e) | |
| 570 | + | |
| 571 | + The returned value is the answer of the child. | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + *** (3.4) Manipulating areas. | |
| 577 | + | |
| 578 | + As explained above the type 'WidgetArea' is opaque. | |
| 579 | + | |
| 580 | + | |
| 581 | + *** (3.4.1) Creating areas. | |
| 582 | + | |
| 583 | + Nevertheless you need to construct areas. This may be done as follows: | |
| 584 | + | |
| 585 | +public define List(WidgetRectangle) -> WidgetArea | |
| 586 | + area | |
| 587 | + ( | |
| 588 | + WidgetEventToolBox etb | |
| 589 | + ). | |
| 590 | + | |
| 591 | + For example if the width of your widget is 'w', and its height is 'h', and if you want | |
| 592 | + to redraw the whole widget after some event, you may construct its area as follows: | |
| 593 | + | |
| 594 | + area(etb)([rect(0,0,w,h)]) | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + *** (3.4.2) Making the union of two areas. | |
| 599 | + | |
| 600 | + You may also need to make the union of two areas. This may for example be the case if | |
| 601 | + several the childs of your widget return answers containing areas. The following tool | |
| 602 | + computes the union of two areas (the event tool box is not required): | |
| 603 | + | |
| 604 | +public define WidgetArea | |
| 605 | + WidgetArea a + WidgetArea b. | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + *** (3.4.3) Transmitting areas between widgets. | |
| 610 | + | |
| 611 | + Areas (of type 'WidgetArea') contain only absolute coordinates. This is the reason why | |
| 612 | + the type 'WidgetArea' is opaque. This has also the advantage that areas may be | |
| 613 | + transmitted from one widget to another one without any conversion. This possibility is | |
| 614 | + used for example by the menu manager widget. | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + *** (4) Monitoring dynamic variables. | |
| 622 | + | |
| 623 | + Widgets need in general to monitor dynamic variables, simply because they are by their | |
| 624 | + very nature the graphical representation of the current state of some | |
| 625 | + 'machine'. However, a widget has no mean to send a message for redrawing itself. A | |
| 626 | + widget may be redrawn only as the consequence of an event. For this reason, widgets | |
| 627 | + should not monitor variables themself, but should ask the widget system to do it. | |
| 628 | + | |
| 629 | + If the widget system has been asked to monitor a dynamic variable 'v', it generates an | |
| 630 | + event 'changed(n)', whenever this variable is reassigned, where 'n' is the identifier | |
| 631 | + of the dynamic variable. When your widget receives this event, it must compare this | |
| 632 | + integer 'n' with the identifier of the variable (say 'v') it depends on. This | |
| 633 | + comparison may be done as follows: | |
| 634 | + | |
| 635 | + if n = var_id(v) | |
| 636 | + then ... I do depend on this variable ... | |
| 637 | + else ... I don't depend on this variable ... | |
| 638 | + | |
| 639 | + Of course, if your widget does not depend on the variable, nothing should be done. But | |
| 640 | + if it depends on the variable, it may return an answer like 'handled(a)', where 'a' is | |
| 641 | + the area to be redrawn. Needless to say, this test must be done with all variables your | |
| 642 | + widget depends on. | |
| 643 | + | |
| 644 | + Several widgets may depend on the same variable. As a consequence, an event like | |
| 645 | + 'changed(n)' must always be transmitted to all childs. | |
| 646 | + | |
| 647 | + Now, a widget asks the system to monitor a dynamic variable at the time this widget is | |
| 648 | + created. The type: | |
| 649 | + | |
| 650 | +public type WidgetRegistration:... | |
| 651 | + | |
| 652 | + which is opaque, is use for representing 'registrations' of dynamic variables at the | |
| 653 | + widget system. In order to register a variable at the widget system, use the following | |
| 654 | + tool: | |
| 655 | + | |
| 656 | +public define WidgetRegistration | |
| 657 | + register | |
| 658 | + ( | |
| 659 | + Var($T) v | |
| 660 | + ). | |
| 661 | + | |
| 662 | + By applying this tool to a dynamic variable, you get a registration. When your widget | |
| 663 | + is created, you just provide the list of all these registrations. That's all. | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + *** (5) Creating your widget. | |
| 670 | + | |
| 671 | + Widgets are of type: | |
| 672 | + | |
| 673 | +public type Widget:... | |
| 674 | + | |
| 675 | + This is an opaque type, but here is the tool which enables to create a widget. | |
| 676 | + | |
| 677 | +public define Widget | |
| 678 | + create_widget | |
| 679 | + ( | |
| 680 | + One -> (Int32,Int32) get_size, | |
| 681 | + (WidgetDrawToolBox) -> One redraw, | |
| 682 | + (WidgetEventToolBox,WidgetEvent) -> WidgetAnswer event_handler, | |
| 683 | + List(WidgetRegistration) registrations | |
| 684 | + ). | |
| 685 | + | |
| 686 | + The function 'get_size' must return the current size '(width,height)' of your | |
| 687 | + widget. Be careful that, if this size is going to change, the function must return the | |
| 688 | + current size, not the initial size. | |
| 689 | + | |
| 690 | + The other arguments have already been explained. | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + --- That's all for the public part ! -------------------------------------------------- | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 0 | 713 | \ No newline at end of file | ... | ... |