Mercurial > projects > mde
comparison mde/gui/WidgetManager.d @ 131:9cff74f68b84
Major revisions to popup handling. Buttons can close menus now, plus some smaller impovements. Removed Widget module.
Moved Widget.AWidget to AChildWidget.AChildWidget and Widget.AParentWidget to AParentWidget.AParentWidget.
Removed ASingleParentWidget to improve code sharing.
AChildWidget doesn't implement IParentWidget like AWidget did.
New IPopupParentWidget extending IParentWidget for the WM and some widgets to handle popups.
Cut old popup management code.
New underMouse() function replacing highlight(); called on all widgets.
Separate menu-popup and button widgets aren't needed for menus now.
Functions returning content widgets have been moved to their own module.
Cleaned up jobs.txt.
Switched to 80 line length for Ddoc.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Wed, 21 Jan 2009 13:01:40 +0000 |
parents | ad91de8867a0 |
children | 264028f4115a |
comparison
equal
deleted
inserted
replaced
130:c5c38eaadb64 | 131:9cff74f68b84 |
---|---|
11 See the GNU General Public License for more details. | 11 See the GNU General Public License for more details. |
12 | 12 |
13 You should have received a copy of the GNU General Public License | 13 You should have received a copy of the GNU General Public License |
14 along with this program. If not, see <http://www.gnu.org/licenses/>. */ | 14 along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
15 | 15 |
16 /************************************************************************************************* | 16 /****************************************************************************** |
17 * The gui manager class base. | 17 * The gui manager class base. |
18 * | 18 * |
19 * This contains most of the code required by a window manager, but does not interact with a screen | 19 * This contains most of the code required by a window manager, but does not |
20 * or get user input. Rendering is handled separately by the renderer anyway. | 20 * interact with a screen or get user input. Rendering is handled separately by |
21 *************************************************************************************************/ | 21 * the renderer anyway. |
22 * | |
23 * Public non IWidget* methods should be thread-safe. | |
24 *****************************************************************************/ | |
22 module mde.gui.WidgetManager; | 25 module mde.gui.WidgetManager; |
23 | 26 |
24 import mde.gui.WidgetDataSet; | 27 import mde.gui.WidgetDataSet; |
25 import mde.gui.widget.Ifaces; | 28 import mde.gui.widget.Ifaces; |
26 import mde.gui.exception; | 29 import mde.gui.exception; |
37 | 40 |
38 // Widgets to create: | 41 // Widgets to create: |
39 import mde.gui.widget.layout; | 42 import mde.gui.widget.layout; |
40 import mde.gui.widget.miscWidgets; | 43 import mde.gui.widget.miscWidgets; |
41 import mde.gui.widget.TextWidget; | 44 import mde.gui.widget.TextWidget; |
45 import mde.gui.widget.contentFunctions; | |
42 import mde.gui.widget.miscContent; | 46 import mde.gui.widget.miscContent; |
43 import mde.gui.widget.Floating; | 47 import mde.gui.widget.Floating; |
44 import mde.gui.widget.PopupMenu; | 48 import mde.gui.widget.PopupMenu; |
45 | 49 |
46 import tango.core.sync.Mutex; | 50 import tango.core.sync.Mutex; |
50 private Logger logger; | 54 private Logger logger; |
51 static this () { | 55 static this () { |
52 logger = Log.getLogger ("mde.gui.WidgetManager"); | 56 logger = Log.getLogger ("mde.gui.WidgetManager"); |
53 } | 57 } |
54 | 58 |
55 /************************************************************************************************* | 59 /****************************************************************************** |
56 * Contains the code for loading and saving an entire gui (more than one may exist), but not the | 60 * Contains the code for loading and saving an entire gui (more than one may |
57 * code for drawing it or handling user input. | 61 * exist), but not the code for drawing it or handling user input. |
58 * | 62 * |
59 * This abstract class exists solely for separating out some of the functionality. | 63 * This abstract class exists solely for separating out some of the functionality. |
60 *************************************************************************************************/ | 64 *****************************************************************************/ |
61 abstract scope class AWidgetManager : IWidgetManager | 65 abstract scope class AWidgetManager : IWidgetManager |
62 { | 66 { |
63 /** Construct a new widget loader. | 67 /** Construct a new widget loader. |
64 * | 68 * |
65 * params: | 69 * params: |
183 changesDS.sec[name] = changes; | 187 changesDS.sec[name] = changes; |
184 } | 188 } |
185 | 189 |
186 // Create the widgets: | 190 // Create the widgets: |
187 createRootWidget; | 191 createRootWidget; |
192 underMouse = child; // must be something | |
188 } | 193 } |
189 | 194 |
190 /** Save changes, if any exist. | 195 /** Save changes, if any exist. |
191 * | 196 * |
192 * Is run when the manager is destroyed, but could be run at other times too. */ | 197 * Is run when the manager is destroyed, but could be run at other times too. */ |
245 return data.keys; | 250 return data.keys; |
246 } | 251 } |
247 } | 252 } |
248 | 253 |
249 /** Called when translation strings have been reloaded. */ | 254 /** Called when translation strings have been reloaded. */ |
250 void reloadStrings (Content) { | 255 protected void reloadStrings (Content) { |
251 Items.loadTranslation; | 256 Items.loadTranslation; |
252 child.setup (++setupN, 2); | 257 child.setup (++setupN, 2); |
253 child.setWidth (w, -1); | 258 child.setWidth (w, -1); |
254 child.setHeight (h, -1); | 259 child.setHeight (h, -1); |
255 child.setPosition (0,0); | 260 child.setPosition (0,0); |
256 requestRedraw; | 261 requestRedraw; |
257 } | 262 } |
258 | 263 |
264 // These methods are only intended for use within the gui package. | |
265 // They are not necessarily thread-safe: | |
266 | |
259 //BEGIN IParentWidget methods | 267 //BEGIN IParentWidget methods |
260 // If call reaches the widget manager there isn't any recursion. | 268 // If call reaches the widget manager there isn't any recursion. |
261 //NOTE: should be override | 269 //NOTE: should be override |
262 final void recursionCheck (widgetID) {} | 270 final void recursionCheck (widgetID) {} |
263 | 271 |
280 } | 288 } |
281 child.setPosition (0,0); | 289 child.setPosition (0,0); |
282 requestRedraw; | 290 requestRedraw; |
283 } | 291 } |
284 //END IParentWidget methods | 292 //END IParentWidget methods |
293 | |
294 //BEGIN IPopupParentWidget methods | |
295 override IPopupParentWidget getParentIPPW () { | |
296 return this; | |
297 } | |
298 | |
299 override void addChildIPPW (IPopupParentWidget ippw) { | |
300 if (childIPPW) | |
301 childIPPW.removedIPPW; | |
302 childIPPW = ippw; | |
303 requestRedraw; | |
304 } | |
305 override bool removeChildIPPW (IPopupParentWidget ippw) { | |
306 if (childIPPW !is ippw) return false; | |
307 childIPPW.removedIPPW; | |
308 childIPPW = null; | |
309 mAIPPW = false; | |
310 requestRedraw; | |
311 return false; | |
312 } | |
313 | |
314 override void menuActive (bool mA) { | |
315 mAIPPW = mA; | |
316 if (childIPPW) | |
317 childIPPW.menuActive = mA; | |
318 } | |
319 override bool menuActive () { | |
320 return mAIPPW; | |
321 } | |
322 | |
323 // Don't do anything. E.g. can get called by non-popup buttons. | |
324 override void menuDone () {} | |
325 | |
326 override IChildWidget getPopupWidget (wdabs cx, wdabs cy, bool closePopup) { | |
327 IChildWidget ret; | |
328 if (childIPPW) { | |
329 ret = childIPPW.getPopupWidget (cx, cy, closePopup); | |
330 if (closePopup && ret is null) { | |
331 menuActive = false; | |
332 removeChildIPPW (childIPPW); | |
333 } | |
334 } | |
335 return ret; | |
336 } | |
337 | |
338 debug protected override bool isChild (IPopupParentWidget ippw) { | |
339 return ippw is childIPPW; | |
340 } | |
341 | |
342 override void removedIPPW () {} // irrelevant | |
343 override void drawPopup () {} | |
344 //END IPopupParentWidget methods | |
285 | 345 |
286 //BEGIN IWidgetManager methods | 346 //BEGIN IWidgetManager methods |
287 override IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null) | 347 override IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null) |
288 { | 348 { |
289 debug assert (parent, "makeWidget: parent is null (code error)"); | 349 debug assert (parent, "makeWidget: parent is null (code error)"); |
323 } | 383 } |
324 override void dimData (widgetID id, wdims d) { | 384 override void dimData (widgetID id, wdims d) { |
325 changes.setDims(id, d); // also updates WidgetDataSet in data. | 385 changes.setDims(id, d); // also updates WidgetDataSet in data. |
326 } | 386 } |
327 | 387 |
328 // These methods are only intended for use within the gui package. They are not necessarily | |
329 // thread-safe. | |
330 IRenderer renderer () { | 388 IRenderer renderer () { |
331 assert (rend !is null, "WidgetManager.renderer: rend is null"); | 389 assert (rend !is null, "WidgetManager.renderer: rend is null"); |
332 return rend; | 390 return rend; |
333 } | 391 } |
334 | 392 |
335 void addPopup (IChildWidget parnt, IChildWidget widg, int flags = 0) { | 393 void positionPopup (IChildWidget parent, IChildWidget popup, int flags = 0) { |
336 debug assert (parnt && widg, "addPopup: null widget"); | 394 debug assert (parent && popup, "positionPopup: null widget"); |
337 if (popups.length >= popupsMem.length) | 395 wdim w = popup.width, |
338 popupsMem.length = popupsMem.length * 2 + 2; | 396 h = popup.height, |
339 with (popupsMem[popups.length]) { | 397 x, y; |
340 parent = parnt; | 398 if (flags & 1) { |
341 widget = widg; | 399 y = parent.yPos; |
342 w = widg.width; | 400 if (y+h > this.h) y += parent.height - h; |
343 h = widg.height; | 401 x = parent.xPos + parent.width; |
344 if (flags & 1) { | 402 if (x+w > this.w) x = parent.xPos - w; |
345 y = parent.yPos; | 403 } else { |
346 if (y+h > this.h) y += parent.height - h; | 404 x = parent.xPos; // align on left edge |
347 x = parent.xPos + parent.width; | 405 if (x+w > this.w) x += parent.width - w; // align on right edge |
348 if (x+w > this.w) x = parent.xPos - w; | 406 y = parent.yPos + parent.height; // place below |
349 } else { | 407 if (y+h > this.h) y = parent.yPos - h; // place above |
350 x = parent.xPos; // align on left edge | 408 } |
351 if (x+w > this.w) x += parent.width - w; // align on right edge | 409 popup.setPosition (x, y); |
352 y = parent.yPos + parent.height; // place below | |
353 if (y+h > this.h) y = parent.yPos - h; // place above | |
354 } | |
355 widget.setPosition (x, y); | |
356 } | |
357 popups = popupsMem[0..popups.length+1]; | |
358 requestRedraw; | |
359 } | |
360 void removePopup (IChildWidget parnt) { | |
361 foreach_reverse (i,popup; popups) { | |
362 if (popup.parent is parnt) | |
363 popups = popups[0..i]; | |
364 } | |
365 requestRedraw; | |
366 } | 410 } |
367 | 411 |
368 void requestRedraw () { | 412 void requestRedraw () { |
369 imde.mainSchedule.request(imde.SCHEDULE.DRAW); | 413 imde.mainSchedule.request(imde.SCHEDULE.DRAW); |
370 } | 414 } |
380 motionCallbacks.removeKey(frame); | 424 motionCallbacks.removeKey(frame); |
381 } | 425 } |
382 //END IWidgetManager methods | 426 //END IWidgetManager methods |
383 | 427 |
384 debug void logWidgetSize (Content) { | 428 debug void logWidgetSize (Content) { |
385 logger.trace ("Current size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh); | 429 logger.trace ("size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh); |
386 child.logWidgetSize; | 430 child.logWidgetSize; |
387 } | 431 } |
388 | 432 |
389 protected: | 433 protected: |
434 void updateUnderMouse (wdabs cx, wdabs cy, bool closePopup) { | |
435 auto oUM = underMouse; | |
436 underMouse = getPopupWidget (cx, cy, closePopup); | |
437 if (underMouse is null) { | |
438 debug assert (child.onSelf (cx, cy), "WidgetManager: child doesn't cover whole area"); | |
439 underMouse = child.getWidget (cx, cy); | |
440 } | |
441 if (underMouse !is oUM) { | |
442 debug assert (oUM && underMouse, "no widget under mouse: error"); | |
443 oUM.underMouse (false); | |
444 underMouse.underMouse (true); | |
445 } | |
446 } | |
447 | |
390 /** Second stage of loading the widgets. | 448 /** Second stage of loading the widgets. |
391 * | 449 * |
392 * loadDesign handles the data; this method needs to: | 450 * loadDesign handles the data; this method needs to: |
393 * --- | 451 * --- |
394 * // 1. Create the root widget: | 452 * // 1. Create the root widget: |
424 SizableBlank = 0x2, | 482 SizableBlank = 0x2, |
425 Debug = 0xF, | 483 Debug = 0xF, |
426 | 484 |
427 // popup widgets: 0x10 | 485 // popup widgets: 0x10 |
428 PopupMenu = TAKES_CONTENT | 0x11, | 486 PopupMenu = TAKES_CONTENT | 0x11, |
429 SubMenu = TAKES_CONTENT | 0x12, | |
430 | 487 |
431 // labels: 0x20 | 488 // labels: 0x20 |
432 ContentLabel = TAKES_CONTENT | 0x20, | 489 ContentLabel = TAKES_CONTENT | 0x20, |
433 TextLabel = 0x21, | 490 TextLabel = 0x21, |
434 | 491 |
441 // content widgets: 0x40 | 498 // content widgets: 0x40 |
442 DisplayContent = TAKES_CONTENT | 0x40, | 499 DisplayContent = TAKES_CONTENT | 0x40, |
443 BoolContent = TAKES_CONTENT | 0x41, | 500 BoolContent = TAKES_CONTENT | 0x41, |
444 AStringContent = TAKES_CONTENT | 0x42, | 501 AStringContent = TAKES_CONTENT | 0x42, |
445 ButtonContent = TAKES_CONTENT | 0x43, | 502 ButtonContent = TAKES_CONTENT | 0x43, |
446 MenuButtonContent = TAKES_CONTENT | 0x44, | |
447 | 503 |
448 GridLayout = TAKES_CONTENT | 0x100, | 504 GridLayout = TAKES_CONTENT | 0x100, |
449 ContentList = TAKES_CONTENT | SAFE_RECURSION | 0x110, | 505 ContentList = TAKES_CONTENT | SAFE_RECURSION | 0x110, |
450 | 506 |
451 FloatingArea = TAKES_CONTENT | 0x200, | 507 FloatingArea = TAKES_CONTENT | 0x200, |
458 "SizableBlank", | 514 "SizableBlank", |
459 "Debug", | 515 "Debug", |
460 "TextLabel", | 516 "TextLabel", |
461 "addContent", | 517 "addContent", |
462 "PopupMenu", | 518 "PopupMenu", |
463 "SubMenu", | |
464 "ContentLabel", | 519 "ContentLabel", |
465 "DisplayContent", | 520 "DisplayContent", |
466 "BoolContent", | 521 "BoolContent", |
467 "AStringContent", | 522 "AStringContent", |
468 "ButtonContent", | 523 "ButtonContent", |
469 "MenuButtonContent", | |
470 "GridLayout", | 524 "GridLayout", |
471 "FloatingArea", | 525 "FloatingArea", |
472 "Switch", | 526 "Switch", |
473 "subMenuContent", | 527 "subMenuContent", |
474 "ContentList", | 528 "ContentList", |
538 wdim w,h; // current widget size; should be at least (mw,mh) even if not displayable | 592 wdim w,h; // current widget size; should be at least (mw,mh) even if not displayable |
539 wdim mw,mh; // minimal area required by widgets | 593 wdim mw,mh; // minimal area required by widgets |
540 scope IChildWidget child; // The primary widget. | 594 scope IChildWidget child; // The primary widget. |
541 uint setupN; // n to pass to IChildWidget.setup | 595 uint setupN; // n to pass to IChildWidget.setup |
542 | 596 |
543 struct ActivePopup { | 597 bool mAIPPW; // IPPW variable |
544 IChildWidget widget; | 598 IPopupParentWidget childIPPW; // child IPPW, if any active |
545 IChildWidget parent; | 599 |
546 wdabs x,y; | |
547 wdsize w,h; | |
548 } | |
549 ActivePopup[] popups; // Pop-up [menus] to draw. Last element is top popup. | |
550 ActivePopup[] popupsMem; // allocated memory for popups | |
551 // callbacks indexed by their frame pointers. Must support removal of elements in foreach: | 600 // callbacks indexed by their frame pointers. Must support removal of elements in foreach: |
552 SortedMap!(void*,bool delegate(wdabs cx, wdabs cy, ubyte b, bool state)) clickCallbacks; | 601 SortedMap!(void*,bool delegate(wdabs cx, wdabs cy, ubyte b, bool state)) clickCallbacks; |
553 SortedMap!(void*,void delegate(wdabs cx, wdabs cy)) motionCallbacks; | 602 SortedMap!(void*,void delegate(wdabs cx, wdabs cy)) motionCallbacks; |
554 IChildWidget keyFocus; // widget receiving keyboard input when non-null | 603 IChildWidget keyFocus; // widget receiving keyboard input |
555 IChildWidget highlighted; // NOTE: in some ways should be same as keyFocus | 604 IChildWidget underMouse; // widget under the mouse pointer |
556 | 605 |
557 Mutex mutex; // lock on methods for use outside the package. | 606 Mutex mutex; // lock on methods for use outside the package. |
558 } | 607 } |