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 }