# HG changeset patch # User Diggory Hardy # Date 1242932110 -7200 # Node ID b06b04c75e866d649872e7ae7d53940a27ec892b # Parent f132e599043fe3646579fcada7b75c32193418fe Finished last commit, rearranged code for the WidgetManager class. There is now a GUI options section. Created a third WidgetManager class called WidgetLoader to handle file loading/saving. Moved most of the code in WMScreen's draw/clickEvent/motionEvent functions to WidgetManager. diff -r f132e599043f -r b06b04c75e86 codeDoc/ideas.txt --- a/codeDoc/ideas.txt Fri Apr 24 17:35:53 2009 +0100 +++ b/codeDoc/ideas.txt Thu May 21 20:55:10 2009 +0200 @@ -13,8 +13,16 @@ -> decent rendering/theme system -> events: -> Click events: widgets only receive clickEvent for left-button press, other button events handled alternatively from WidgetManager? - -> Click callbacks: replace with "drag callback" notifying widget of release position (and widget)? - -> possibly better for drag-and drop support + -> Click/drag handling: + -> on click: + -> clicked widget recieves depress event + -> create drag monitor, which records parent and optionally runs parent method on move (which can find the widget/drop-zone underneath) + -> on release: + -> method from either dragged or drop-zone widget called with reference to the other: + -> perhaps function in dragged widget called, which is passed reference of widget underneath, and can call getDropZoneAncestor on this + -> drag/drop-like possibilities: + -> magnifier which is dragged from icon, creates a magnified window, and disappears on release + -> colour-picker which is dragged from a widget and dropped to choose the colour under it -> Keyboard widget focus/selection: -> a widget is highlighted -> pressing an arrow key replaces widget with widget.nextInDirection (widget, direction) @@ -66,6 +74,9 @@ +> can use a static list widget for each type -> clipboard is type specific; need to decide when to convert, etc. +> enables better copying; e.g. from double 3.5e9 to int 4×10⁹ + > Context menus: + > Context menu serves (editable?) content most directly under mouse cursor + > plus content higher up widget tree? > Non-static content manager > Separate managers for options, GUI symbols, data fields, passwords(?) > Optional saving/loading @@ -82,5 +93,39 @@ -> more work, recreating sub-widgets > dynamic lists (add/remove elts) > have to rebuild lists +> Widget content? Extend content with a validator function/delegate, specific to each class, which takes the new value and returns it or a corrected version of it. Not so good to do it generally from Content, since setting a new value via usual method will re-trigger validator and callbacks (e.g. bad validator could cause infinite loop). + + + +From paper: +Text styles: +> use a few styles + > number and purpose of each hard-coded? + > titlebar, label, button, field, etc. + > renderer or something maps a font (font file, size, style) to each style + +GUI editor: +> Widgets are wrapped with/replaced by drag & drop widgets: + > widgets can be dragged to another drop-zone widget + > widgets can be dragged to a bin or scrap area + > new widgets can be dragged from templates + > widgets can be copied by holding Ctrl + > widgets displaced by another are moved to the scrap area +> side bar/editor controls: + > tree showing current widget structure + > widgets can be moved (by dragging), restructured, etc. from here the same as from the WYSISYG view + > new widget panel + > for creating new single widgets + > for creating new mini-trees from templates + > scrap panel + > widgets can be dragged here from WYSIWYG or tree views + > widgets can even be edited here + > widgets remaining on program/editor exit are lost? + > properties panel + > for setting widget options (initialisation data) + > profile selection (section of config file to save to) +> for easy access to editor (power GUI dev mode) + > include a theme selection box on the GUI + > include a button to turn the editor on/off diff -r f132e599043f -r b06b04c75e86 data/L10n/en-GB.mtt --- a/data/L10n/en-GB.mtt Fri Apr 24 17:35:53 2009 +0100 +++ b/data/L10n/en-GB.mtt Thu May 21 20:55:10 2009 +0200 @@ -47,12 +47,16 @@ +{GUI} + {} + + diff -r f132e599043f -r b06b04c75e86 data/conf/guiDemo.mtt --- a/data/conf/guiDemo.mtt Fri Apr 24 17:35:53 2009 +0100 +++ b/data/conf/guiDemo.mtt Thu May 21 20:55:10 2009 +0200 @@ -14,15 +14,16 @@ - + - + + !{use optBox for no description, optDBox for descriptions under entries} diff -r f132e599043f -r b06b04c75e86 mde/gui/WMScreen.d --- a/mde/gui/WMScreen.d Fri Apr 24 17:35:53 2009 +0100 +++ b/mde/gui/WMScreen.d Thu May 21 20:55:10 2009 +0200 @@ -22,6 +22,7 @@ module mde.gui.WMScreen; import mde.gui.WidgetManager; +import mde.gui.WidgetLoader; import mde.gui.widget.Ifaces; import mde.gui.renderer.createRenderer; @@ -46,9 +47,10 @@ * sense to translate them and possibly drop events for some uses, such as if * the gui is drawn to a texture. * - * Public non IWidget* methods should be thread-safe. + * Public non IWidget* methods should be thread-safe, even to the same + * instance (by locking on a mutex). *****************************************************************************/ -scope class WMScreen : AWidgetManager, Screen.IDrawable { +scope class WMScreen : AWidgetLoader, Screen.IDrawable { /** Construct a new widget manager. * * Must be run after static this. @@ -72,71 +74,27 @@ synchronized(mutex) { debug (mdeDrawEvents) logger.trace ("drawing"); - if (child) - child.draw; - if (childIPPW) - childIPPW.drawPopup; - drawPopup; + wmDrawWidgets(); } } - /** For mouse click events. - * - * Sends the event on to the relevant windows and all click callbacks. */ + /** For mouse click events. */ void clickEvent (ushort usx, ushort usy, ubyte b, bool state) { try { - mutex.lock; - scope(exit) mutex.unlock; - if (child is null) return; - - wdabs cx = cast(wdabs) usx, cy = cast(wdabs) usy; - - // Callbacks have the highest priority receiving events (e.g. a button release) - foreach (dg; clickCallbacks) - if (dg (cx, cy, b, state)) return; - - // Update underMouse to get the widget clicked on - updateUnderMouse (cx, cy, state); - - // Disable keyboard input if on another widget: - if (keyFocus && keyFocus !is underMouse) { - keyFocus.keyFocusLost; - keyFocus = null; - input.setLetterCallback (null); - } - - // Finally, post the actual event: - if (b == 3 && state) { // right click - open context menu - IContent contextContent = underMouse.content; - if (contextContent is null) return; - // NOTE: Creates new widgets every time; not optimal - popupContext = makeWidget (this, "context", contextContent); - popupContext.setup (0, 3); - positionPopup (underMouse, popupContext); - requestRedraw; - } else // post other button presses to clickEvent - if (underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) { - // keyboard input requested - keyFocus = underMouse; - input.setLetterCallback (&underMouse.keyEvent); - } + mutex.lock; + scope(exit) mutex.unlock; + wmMouseClick (cast(wdabs) usx, cast(wdabs) usy, b, state); } catch (Exception e) { logger.error ("clickEvent: exception processing event: {}", e.msg); } } - /** For mouse motion events. - * - * Sends the event on to all motion callbacks. */ + /** For mouse motion events. */ void motionEvent (ushort scx, ushort scy) { try { mutex.lock; scope(exit) mutex.unlock; - wdabs cx = cast(wdabs) scx, cy = cast(wdabs) scy; - foreach (dg; motionCallbacks) - dg (cx, cy); - - updateUnderMouse (cx, cy, false); + wmMouseMotion (cast(wdabs) scx, cast(wdabs) scy); } catch (Exception e) { logger.error ("motionEvent: exception processing event: {}", e.msg); } @@ -166,6 +124,10 @@ } protected: + final override void setLetterCallback(void delegate(ushort, char[]) dlg) { + input.setLetterCallback (dlg); + } + /* Second stage of widget loading. * Note: sizeEvent should be called with window size before this. */ final override void createRootWidget () { diff -r f132e599043f -r b06b04c75e86 mde/gui/WidgetLoader.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/gui/WidgetLoader.d Thu May 21 20:55:10 2009 +0200 @@ -0,0 +1,257 @@ +/* LICENSE BLOCK +Part of mde: a Modular D game-oriented Engine +Copyright © 2007-2008 Diggory Hardy + +This program is free software: you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation, either +version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . */ + +/****************************************************************************** + * A gui manager extension to load widgets from files. + * + * Public methods in this class should be thread safe (by locking on a mutex). + *****************************************************************************/ +module mde.gui.WidgetLoader; + +import mde.gui.WidgetManager; +import mde.gui.WidgetDataSet; +import mde.gui.exception; + +import mt = mde.file.mergetag.DataSet; +import mde.file.mergetag.Reader; +import mde.file.mergetag.Writer; +import mde.file.paths; + +import tango.util.log.Log : Log, Logger; + +private Logger logger; +static this () { + logger = Log.getLogger ("mde.gui.WidgetLoader"); +} + +/****************************************************************************** + * Contains the code for loading and saving an entire gui (more than one may + * exist), but not the code for drawing it or handling user input. + * + * This abstract class exists solely for separating out some of the functionality. + *****************************************************************************/ +abstract scope class AWidgetLoader : AWidgetManager +{ + /** Construct a new widget loader. + * + * params: + * fileName = Name of file specifying the gui, excluding path and extension. + */ + protected this (char[] file) { + mutex = new Mutex; // Used on functions intended to be called from outside the gui package. + fileName = file; + super (file); + } + + /* Load the widgets' data from the file specified to the CTOR. + * + * params: + * allDesigns = Load all sections + */ + private void loadData (bool allDesigns = false) { + if (allLoaded || (defaultDesign !is null && allDesigns == false)) + return; // test if already loaded + + // Set up a reader + scope IReader reader; + try { + reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); + + // Read from the HEADER: + // Get the renderer + char[]* p = "Renderer" in reader.dataset.header._charA; + if (p is null || *p is null) { + logger.warn ("No renderer specified: using \"Simple\""); + rendName = "Simple"; + } + else + rendName = *p; + + // Get which section to use + p = "Design" in reader.dataset.header._charA; + if (p is null || *p is null) { + logger.warn ("No gui design specified: trying \"Default\""); + defaultDesign = "Default"; + } + else + defaultDesign = *p; + + // Read the body: + // Load the chosen design + reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { + WidgetDataSet* p = id in data; + if (p is null) { + data[id] = new WidgetDataSet; + return *(id in data); + } + return *p; + }; + + if (allDesigns) { + reader.read; + allLoaded = true; + } else + reader.read([defaultDesign]); + } catch (NoFileException) { + logger.error ("Unable to load GUI: no config file: "~fileName); + // just return: not a fatal error (so long as the game can run without a GUI!) + } catch (Exception e) { + logger.error ("Unable to load GUI: errors parsing config file ("~confDir.getFileName(fileName,PRIORITY.HIGH_LOW)~"):"); + logger.error (e.msg); + throw new GuiException ("Failure parsing config file"); + } + } + + /** Load the gui from some design. + * + * If a design was previously loaded, its changes are saved first. + * + * Params: + * name = Design to load. If null, the default will be loaded. + */ + void loadDesign (char[] name = null) { + if (changes !is null) // A design was previously loaded + save; // own lock + + mutex.lock; + scope(exit) mutex.unlock; + + // Load data (loadData tests if it's already loaded first): + if (name is null) { + loadData (false); + name = defaultDesign; + } else + loadData (true); + + + // Get data: + auto p = name in data; + while (p is null) { + if (name == defaultDesign) + throw new GuiException ("Unable to load [specified or] default design"); + name = defaultDesign; // try again with the default + p = name in data; + } + curData = *p; + + // Get/create a changes section: + if (changesDS is null) + changesDS = new mt.DataSet; + + mt.IDataSection* q = name in changesDS.sec; + if (!q || ((changes = cast(WidgetDataChanges) *q) is null)) { + changes = new WidgetDataChanges (curData); + changesDS.sec[name] = changes; + } + + // Create the widgets: + createRootWidget; + underMouse = child; // must be something + } + + /** Save changes, if any exist. + * + * Is run when the manager is destroyed, but could be run at other times too. */ + void save () { + preSave; + + mutex.lock; + scope(exit) mutex.unlock; + + // Make all widgets save any changed data: + child.saveChanges; + + if (changes.noChanges) + return; + + if (loadUserFile) { // merge entries from user file into current changes + try { + scope IReader reader = confDir.makeMTReader ( + fileName, PRIORITY.HIGH_ONLY, changesDS, true); + + // Create if necessary, only corresponding to existing designs read: + reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { + WidgetDataSet* p = id in data; + if (p is null) + throw new Exception ("File has changed since it was loaded!"); + return new WidgetDataChanges (*p); + }; + + reader.read; + } catch (NoFileException) { + // No user file exists; not an error. + } catch (Exception e) { + logger.error ("Error reading "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" prior to saving:"); + logger.error (e.msg); + logger.error ("Overwriting the file."); + // Continue... + } + loadUserFile = false; // don't need to do it again + } + + try { // Save + IWriter writer; + writer = confDir.makeMTWriter (fileName, changesDS); + writer.write; + } catch (Exception e) { + logger.error ("Saving to "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" failed:"); + logger.error (e.msg); + // No point in throwing since it doesn't affect anything else. + } + } + + /** Get the names of all designs available. */ + char[][] designs() { + synchronized(mutex) { + loadData (true); + return data.keys; + } + } + +protected: + // Called by derived classes, not thread safe for the same instance + //BEGIN WidgetManagement methods + /** Second stage of loading the widgets. + * + * loadDesign handles the data; this method needs to: + * --- + * // 1. Create the root widget: + * child = makeWidget ("root"); + * child.setup (0, 3); + * // 2. Set the size: + * child.setWidth (child.minWidth, 1); + * child.setHeight (child.minHeight, 1); + * // 3. Set the position (necessary part of initialization): + * child.setPosition (0,0); + * --- + */ + void createRootWidget(); + + /** Called before saving (usually when the GUI is about to be destroyed, although not + * necessarily). */ + void preSave (); + //END WidgetManagement methods + + + // Dataset/design data: + final char[] fileName; + char[] defaultDesign; // The design specified in the file header. + + // Loaded data, indexed by design name. May not be loaded for all gui designs: + scope WidgetDataSet[char[]] data; + private bool allLoaded = false; // applies to data + scope mt.DataSet changesDS; // changes and sections from user file (used for saving) + bool loadUserFile = true; // still need to load user file for saving? +} diff -r f132e599043f -r b06b04c75e86 mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Fri Apr 24 17:35:53 2009 +0100 +++ b/mde/gui/WidgetManager.d Thu May 21 20:55:10 2009 +0200 @@ -26,17 +26,11 @@ import mde.gui.WidgetDataSet; import mde.gui.widget.Ifaces; -import mde.gui.exception; import imde = mde.imde; import mde.content.Content; debug import mde.content.miscContent; // Debug menu -import mt = mde.file.mergetag.DataSet; -import mde.file.mergetag.Reader; -import mde.file.mergetag.Writer; -import mde.file.paths; - // Widgets to create: import mde.gui.widget.layout; import mde.gui.widget.miscWidgets; @@ -46,7 +40,7 @@ import mde.gui.widget.Floating; import mde.gui.widget.ParentContent; -import tango.core.sync.Mutex; +public import tango.core.sync.Mutex; import tango.util.log.Log : Log, Logger; import tango.util.container.SortedMap; @@ -58,20 +52,23 @@ /****************************************************************************** * Contains the code for loading and saving an entire gui (more than one may * exist), but not the code for drawing it or handling user input. + * + * Methods in this class are only intended for use within the gui package, + * either by widgets (the IXXXWidget methods implementing from an interface in + * widgets.Ifaces.d) or by a derived class (back-end methods doing widget + * work). None of these methods are intended to be thread-safe when called + * concurrently on the same WidgetManager instance, but they should be thread- + * safe for calling on separate instances. * * This abstract class exists solely for separating out some of the functionality. *****************************************************************************/ abstract scope class AWidgetManager : IWidgetManager { - /** Construct a new widget loader. - * - * params: - * fileName = Name of file specifying the gui, excluding path and extension. - */ - protected this (char[] file) { - mutex = new Mutex; // Used on functions intended to be called from outside the gui package. - fileName = file; - + /** Construct a new widget manager. + * + * Params: + * name = The file name of the config for this GUI (to identify multiple GUIs). */ + protected this (char[] name) { clickCallbacks = new typeof(clickCallbacks); motionCallbacks = new typeof(motionCallbacks); @@ -79,193 +76,12 @@ assert (p, "MiscOptions.l10n not created!"); p.addCallback (&reloadStrings); debug { // add a debug-mode menu - auto lWS = new EventContent ("menus.debug."~file~".logWidgetSize"); + auto lWS = new EventContent ("menus.debug."~name~".logWidgetSize"); lWS.addCallback (&logWidgetSize); } } - /* Load the widgets' data from the file specified to the CTOR. - * - * params: - * allDesigns = Load all sections - */ - private void loadData (bool allDesigns = false) { - if (allLoaded || (defaultDesign !is null && allDesigns == false)) - return; // test if already loaded - - // Set up a reader - scope IReader reader; - try { - reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); - - // Read from the HEADER: - // Get the renderer - char[]* p = "Renderer" in reader.dataset.header._charA; - if (p is null || *p is null) { - logger.warn ("No renderer specified: using \"Simple\""); - rendName = "Simple"; - } - else - rendName = *p; - - // Get which section to use - p = "Design" in reader.dataset.header._charA; - if (p is null || *p is null) { - logger.warn ("No gui design specified: trying \"Default\""); - defaultDesign = "Default"; - } - else - defaultDesign = *p; - - // Read the body: - // Load the chosen design - reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { - WidgetDataSet* p = id in data; - if (p is null) { - data[id] = new WidgetDataSet; - return *(id in data); - } - return *p; - }; - - if (allDesigns) { - reader.read; - allLoaded = true; - } else - reader.read([defaultDesign]); - } catch (NoFileException) { - logger.error ("Unable to load GUI: no config file: "~fileName); - // just return: not a fatal error (so long as the game can run without a GUI!) - } catch (Exception e) { - logger.error ("Unable to load GUI: errors parsing config file ("~confDir.getFileName(fileName,PRIORITY.HIGH_LOW)~"):"); - logger.error (e.msg); - throw new GuiException ("Failure parsing config file"); - } - } - - /** Load the gui from some design. - * - * If a design was previously loaded, its changes are saved first. - * - * Params: - * name = Design to load. If null, the default will be loaded. - */ - void loadDesign (char[] name = null) { - if (changes !is null) // A design was previously loaded - save; // own lock - - mutex.lock; - scope(exit) mutex.unlock; - - // Load data (loadData tests if it's already loaded first): - if (name is null) { - loadData (false); - name = defaultDesign; - } else - loadData (true); - - - // Get data: - auto p = name in data; - while (p is null) { - if (name == defaultDesign) - throw new GuiException ("Unable to load [specified or] default design"); - name = defaultDesign; // try again with the default - p = name in data; - } - curData = *p; - - // Get/create a changes section: - if (changesDS is null) - changesDS = new mt.DataSet; - - mt.IDataSection* q = name in changesDS.sec; - if (!q || ((changes = cast(WidgetDataChanges) *q) is null)) { - changes = new WidgetDataChanges (curData); - changesDS.sec[name] = changes; - } - - // Create the widgets: - createRootWidget; - underMouse = child; // must be something - } - - /** Save changes, if any exist. - * - * Is run when the manager is destroyed, but could be run at other times too. */ - void save () { - preSave; - - mutex.lock; - scope(exit) mutex.unlock; - - // Make all widgets save any changed data: - child.saveChanges; - - if (changes.noChanges) - return; - - if (loadUserFile) { // merge entries from user file into current changes - try { - scope IReader reader = confDir.makeMTReader ( - fileName, PRIORITY.HIGH_ONLY, changesDS, true); - - // Create if necessary, only corresponding to existing designs read: - reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { - WidgetDataSet* p = id in data; - if (p is null) - throw new Exception ("File has changed since it was loaded!"); - return new WidgetDataChanges (*p); - }; - - reader.read; - } catch (NoFileException) { - // No user file exists; not an error. - } catch (Exception e) { - logger.error ("Error reading "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" prior to saving:"); - logger.error (e.msg); - logger.error ("Overwriting the file."); - // Continue... - } - loadUserFile = false; // don't need to do it again - } - - try { // Save - IWriter writer; - writer = confDir.makeMTWriter (fileName, changesDS); - writer.write; - } catch (Exception e) { - logger.error ("Saving to "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" failed:"); - logger.error (e.msg); - // No point in throwing since it doesn't affect anything else. - } - } - - /** Get the names of all designs available. */ - char[][] designs() { - synchronized(mutex) { - loadData (true); - return data.keys; - } - } - - /** A change callback on MiscOptions.l10n content to update widgets. - * - * Relies on another callback reloading translations to content first! */ - protected void reloadStrings (Content) { - synchronized(mutex) { - if (child is null) return; - child.setup (++setupN, 2); - child.setWidth (w, -1); - child.setHeight (h, -1); - child.setPosition (0,0); - requestRedraw; - } - } - - // These methods are only intended for use within the gui package. - // They are not necessarily thread-safe: - +public: //BEGIN IParentWidget methods // If call reaches the widget manager there isn't any recursion. //NOTE: should be override @@ -471,6 +287,79 @@ } protected: + // These methods are called by derived classes to do the widget-management work + //BEGIN WidgetManagement methods + /** Draw all widgets */ + void wmDrawWidgets() { + if (child) + child.draw; + if (childIPPW) + childIPPW.drawPopup; + drawPopup; + } + + /** For mouse click events. + * + * Sends the event on to the relevant windows and all click callbacks. */ + void wmMouseClick (wdabs cx, wdabs cy, ubyte b, bool state) { + if (child is null) return; + + // Callbacks have the highest priority receiving events (e.g. a button release) + foreach (dg; clickCallbacks) + if (dg (cx, cy, b, state)) return; + + // Update underMouse to get the widget clicked on + updateUnderMouse (cx, cy, state); + + // Disable keyboard input if on another widget: + if (keyFocus && keyFocus !is underMouse) { + keyFocus.keyFocusLost; + keyFocus = null; + setLetterCallback (null); + } + + // Finally, post the actual event: + if (b == 3 && state) { // right click - open context menu + IContent contextContent = underMouse.content; + if (contextContent is null) return; + // NOTE: Creates new widgets every time; not optimal + popupContext = makeWidget (this, "context", contextContent); + popupContext.setup (0, 3); + positionPopup (underMouse, popupContext); + requestRedraw; + } else // post other button presses to clickEvent + if (underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) { + // keyboard input requested + keyFocus = underMouse; + setLetterCallback (&underMouse.keyEvent); + } + } + + /** For mouse motion events. + * + * Lock on mutex before calling. Pass new mouse coordinates. */ + void wmMouseMotion (wdabs cx, wdabs cy) { + foreach (dg; motionCallbacks) + dg (cx, cy); + + updateUnderMouse (cx, cy, false); + } + + + /** A change callback on MiscOptions.l10n content to update widgets. + * + * Relies on another callback reloading translations to content first! */ + void reloadStrings (Content) { + synchronized(mutex) { + if (child is null) return; + child.setup (++setupN, 2); + child.setWidth (w, -1); + child.setHeight (h, -1); + child.setPosition (0,0); + requestRedraw; + } + } + // for internal use void updateUnderMouse (wdabs cx, wdabs cy, bool closePopup) { auto oUM = underMouse; underMouse = getPopupWidget (cx, cy, closePopup); @@ -485,25 +374,9 @@ } } - /** Second stage of loading the widgets. - * - * loadDesign handles the data; this method needs to: - * --- - * // 1. Create the root widget: - * child = makeWidget ("root"); - * child.setup (0, 3); - * // 2. Set the size: - * child.setWidth (child.minWidth, 1); - * child.setHeight (child.minHeight, 1); - * // 3. Set the position (necessary part of initialization): - * child.setPosition (0,0); - * --- - */ - void createRootWidget(); - - /** Called before saving (usually when the GUI is about to be destroyed, although not - * necessarily). */ - void preSave (); + /// This should be overloaded to set a callback receiving keyboard input. + abstract void setLetterCallback(void delegate(ushort, char[])); + //END WidgetManagement methods public: //BEGIN makeWidget metacode @@ -613,19 +486,10 @@ //END makeWidget metacode protected: - // Dataset/design data: - final char[] fileName; - char[] defaultDesign; // The design specified in the file header. - char[] rendName; // Name of renderer; for saving and creating renderers - - // Loaded data, indexed by design name. May not be loaded for all gui designs: - scope WidgetDataSet[char[]] data; - private bool allLoaded = false; // applies to data WidgetDataSet curData; // Current data WidgetDataChanges changes; // Changes for the current design. - scope mt.DataSet changesDS; // changes and sections from user file (used for saving) - bool loadUserFile = true; // still need to load user file for saving? + char[] rendName; // Name of renderer; for saving and creating renderers IRenderer rend; // Widgets: diff -r f132e599043f -r b06b04c75e86 mde/gui/widget/Floating.d --- a/mde/gui/widget/Floating.d Fri Apr 24 17:35:53 2009 +0100 +++ b/mde/gui/widget/Floating.d Thu May 21 20:55:10 2009 +0200 @@ -19,6 +19,7 @@ import mde.gui.widget.AParentWidget; import mde.gui.exception; import mde.content.IContent; +import mde.content.AStringContent; import tango.util.log.Log : Log, Logger; @@ -39,7 +40,7 @@ class FloatingAreaWidget : AParentWidget { static this () { - raiseOnHover = ; + raiseOnHover = new BoolContent ("GUI.raiseOnHover"); } this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent content) { @@ -173,7 +174,7 @@ debug scope (failure) logger.warn ("getWidget: failure; values: click; pos; width: {},{}; {},{}; {},{}", cx, cy, x, y, w, h); - size_t event = getFloatingWidget (cx,cy, raiseOnHover); + size_t event = getFloatingWidget (cx,cy, raiseOnHover()); if (event > sWData.length) return this; // no match