# HG changeset patch # User Diggory Hardy # Date 1252695413 -7200 # Node ID 1cbde980729315662790e5b1d53c970a5935f21a # Parent 3d58adc17d20e245022ad1226e5f22e1d953b6cd Compile/link-time fixes for ldc & non-debug builds. Moved WidgetManager to widget/ Reverted IChildWidget to an interface, not an abstract class. Introduced a work-around for a compiler problem. May not cover all cases. diff -r 3d58adc17d20 -r 1cbde9807293 codeDoc/jobs.txt --- a/codeDoc/jobs.txt Mon Aug 31 13:54:23 2009 +0200 +++ b/codeDoc/jobs.txt Fri Sep 11 20:56:53 2009 +0200 @@ -26,14 +26,6 @@ Implement a RootWidget moving functionality out of AWidgetManager, etc., now, or later? RequestRedraw becomes a function of the renderer. -Undefined reference problem: (see ~/d/small/tests/compilerErrors/packageFunc.d) -Solution: access via a class base instead of an interface base. -Two ways to implement: - Leave IChildWidget an interface, but change stored refs to AChildWidget and cast references passed in functions before calling member functions. - Use AChildWidget directly or an abstract base class instead of IChildWidget, so that interfaced functions can pass the same type stored. -Making IChildWidget an abstact class seems to have partially solved it. But IPopupParentWidget (needs to be an interface), etc., still need some help. -Presumably setWidth is getting called from IChildWidget instead of AChildWidget now. ?? -More link errors with ldc. To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): diff -r 3d58adc17d20 -r 1cbde9807293 mde/content/AStringContent.d --- a/mde/content/AStringContent.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/content/AStringContent.d Fri Sep 11 20:56:53 2009 +0200 @@ -184,7 +184,7 @@ * Returns: true if string successfully converted to value. * * Should never throw; should reset sv at least when returning false. */ - bool endEdit (); + abstract bool endEdit (); protected: //TODO: copy-on-assign, copy-on-edit, or what? diff -r 3d58adc17d20 -r 1cbde9807293 mde/file/mergetag/MTTagWriter.d --- a/mde/file/mergetag/MTTagWriter.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/file/mergetag/MTTagWriter.d Fri Sep 11 20:56:53 2009 +0200 @@ -44,10 +44,10 @@ abstract class MTTagWriter { /// Set the current section - void sectionTag (char[] section); + abstract void sectionTag (char[] section); /// Write a data tag - void dataTag (char[] type, char[] id, char[] data); + abstract void dataTag (char[] type, char[] id, char[] data); /// Change the section if necessary and write a data tag void writeTag (char[] section, char[] type, char[] id, char[] data) { @@ -57,7 +57,7 @@ } /// Close the file - void close (); + abstract void close (); protected: char[] sec; // current section diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/WidgetLoader.d --- a/mde/gui/WidgetLoader.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/gui/WidgetLoader.d Fri Sep 11 20:56:53 2009 +0200 @@ -224,7 +224,7 @@ //BEGIN WidgetManagement methods /** Called before saving to make sure no data is being edited (and thus * would not be saved). */ - void preSave (); + abstract void preSave (); //END WidgetManagement methods diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Mon Aug 31 13:54:23 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,651 +0,0 @@ -/* 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 . */ - -/****************************************************************************** - * The gui manager class base. - * - * This contains most of the code required by a window manager, but does not - * interact with a screen or get user input. Rendering is handled separately by - * the renderer anyway. - * - * Public non IWidget* methods should be thread-safe. - *****************************************************************************/ -module mde.gui.WidgetManager; - -import mde.gui.WidgetDataSet; -import mde.gui.widget.Ifaces; -import mde.gui.renderer.createRenderer; - -import imde = mde.imde; -import mde.content.Content; -import mde.content.ServiceContent; -debug import mde.content.miscContent; // Debug menu -debug import mde.content.Debug; - -// Widgets to create: -import mde.gui.widget.layout; -import mde.gui.widget.miscWidgets; -import mde.gui.widget.TextWidget; -import mde.gui.widget.contentFunctions; -import mde.gui.widget.miscContent; -import mde.gui.widget.Floating; -import mde.gui.widget.ParentContent; -import mde.gui.widget.AParentWidget; - -public import tango.core.sync.Mutex; -import tango.util.log.Log : Log, Logger; -import tango.io.Console; // to print exception stack-trace -import tango.util.container.SortedMap; - -private Logger logger; -static this () { - logger = Log.getLogger ("mde.gui.WidgetManager"); -} - -/****************************************************************************** - * 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 manager. - * - * Params: - * name = The file name of the config for this GUI (to identify multiple GUIs). */ - protected this (char[] name) { - auto p = "MiscOptions.l10n" in Content.allContent; - assert (p, "MiscOptions.l10n not created!"); - p.addCallback (&reloadStrings); - - serviceContent = ServiceContentList.createItems (name); - assert (cast (IServiceContent) Content.get ("menus.services."~name)); - - debug { // add a debug-mode menu - auto lWS = new EventContent ("menus.debug."~name~".logWidgetSize"); - lWS.addCallback (&logWidgetSize); - } - } - -public: - //BEGIN IParentWidget methods - // If call reaches the widget manager there isn't any recursion. - //NOTE: should be override - final void recursionCheck (widgetID, IContent) {} - - override void minWChange (IChildWidget widget, wdim nmw) { - if (widget !is childRoot) { // Probably because widget is a popup widget - // This may get called from a CTOR, hence we can't check widget is one of childContext, etc. - if (widget.width < nmw) - widget.setWidth (nmw, -1); - return; - } - mw = nmw; - if (w < nmw) { - childRoot.setWidth (nmw, -1); - w = nmw; - } - childRoot.setPosition (0,0); - requestRedraw; - } - override void minHChange (IChildWidget widget, wdim nmh) { - if (widget !is childRoot) { - if (widget.height < nmh) - widget.setHeight (nmh, -1); - return; - } - mh = nmh; - if (h < nmh) { - childRoot.setHeight (nmh, -1); - h = nmh; - } - childRoot.setPosition (0,0); - requestRedraw; - } - //END IParentWidget methods - - //BEGIN IWidget methods - public override bool saveChanges () { - bool ret = childRoot.saveChanges; - ret |= childContext.saveChanges; - if (childDragged !is null) - ret |= childDragged.saveChanges; - return ret; - } - - override bool dropContent (IContent content) { - return false; - } - //END IWidget methods - - //BEGIN IPopupParentWidget methods - override IPopupParentWidget getParentIPPW () { - return this; - } - - override void addChildIPPW (IPopupParentWidget ippw) { - requestRedraw; - if (ippw is childContext) { // special handling - a separate IPPW - contextActive = true; - return; - } - if (childIPPW) - childIPPW.removedIPPW; - childIPPW = ippw; - } - override bool removeChildIPPW (IPopupParentWidget ippw) { - if (ippw is childContext && contextActive) { - childContext.removedIPPW; - contextActive = false; - return true; - } - if (childIPPW !is ippw) return false; - childIPPW.removedIPPW; - childIPPW = null; - mAIPPW = MenuPosition.INACTIVE; - requestRedraw; - return true; - } - - override void menuActive (MenuPosition mA) { - mAIPPW = mA; - if (childIPPW) - childIPPW.menuActive = mA; - if (contextActive) - childContext.menuActive = mA; - } - override MenuPosition menuActive () { - return mAIPPW; - } - override MenuPosition parentMenuActive () { - return MenuPosition.INACTIVE; - } - - // Note: also triggered by non-popup widgets - override void menuDone () {} - - override IChildWidget getPopupWidget (wdabs cx, wdabs cy, bool closePopup) { - IChildWidget ret; - // Don't bother with childDragged; it has no interaction - if (contextActive) { - ret = childContext.getPopupWidget (cx, cy, closePopup); - if (ret) return ret; - if (closePopup) { - childContext.removedIPPW; - contextActive = false; - requestRedraw; - } - } - if (childIPPW) { - ret = childIPPW.getPopupWidget (cx, cy, closePopup); - if (ret) return ret; - if (closePopup) { - removeChildIPPW (childIPPW); - } - } - return null; - } - - override void drawPopup () { - if (childIPPW) - childIPPW.drawPopup; - if (contextActive) - childContext.drawPopup(); - if (childDragged) - childDragged.draw(); - } - - debug protected override bool isChild (IPopupParentWidget ippw) { - if (contextActive && ippw is childContext) - return true; - return ippw is childIPPW; - } - - override void removedIPPW () {} // irrelevant - //END IPopupParentWidget methods - - //BEGIN IWidgetManager methods - override IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null) - { - debug assert (parent, "makeWidget: parent is null (code error)"); - debug scope (failure) - logger.warn ("Creating widget \""~id~"\" failed."); - - WidgetData data = curData[id]; - if (data.ints.length < 1) { - logger.error ("No int data; creating a debug widget"); - data.ints = [WIDGET_TYPE.Debug]; - } - int type = data.ints[0]; // type is first element of data - - try { - // Statically programmed binary search on type, returning a new widget or calling a - // function: - //pragma (msg, binarySearch ("type", WIDGETS)); - mixin (binarySearch ("type", WIDGETS)); - // Not returned a new widget: - logger.error ("Bad widget type: {}; creating a debug widget instead",type); - } catch (Exception e) { - logger.error ("Error creating widget; creating a debug widget instead. Exception printed to stderr."); - //TODO: find a standard way to output exceptions, and implement everywhere: - e.writeOut(delegate void(char[]s){ Cerr(s); }); - } - - return new DebugWidget (this, parent, id, data, content); - } - - override WidgetData widgetData (widgetID id) { - return curData[id]; - } - override void widgetData (widgetID id, WidgetData d) { - changes[id] = d; // also updates WidgetDataSet in data. - } - - override wdims dimData (widgetID id) { - return curData.dims (id); - } - override void dimData (widgetID id, wdims d) { - changes.setDims(id, d); // also updates WidgetDataSet in data. - } - - IRenderer renderer () { - assert (rend !is null, "WidgetManager.renderer: rend is null"); - return rend; - } - - MenuPosition positionPopup (IChildWidget parent, IChildWidget popup, MenuPosition position = MenuPosition.INACTIVE) { - debug assert (parent && popup, "positionPopup: null widget"); - debug if (Debug.logPopupPositioning()) - logger.trace ("Placing popup {} in relation to parent {}; input position: {}", popup, parent, position); - wdim w = popup.width, - h = popup.height, - x, y; - if (position & MenuPosition.ACTIVE) { - y = parent.yPos; // height flush with top - if (y+h > this.h) y += parent.height - h; // or bottom - if (position & MenuPosition.LEFT) { // previously left - x = parent.xPos - w; // on left - if (x < 0) { - x = parent.xPos + parent.width; // on right - position = MenuPosition.RIGHT; - } - } else { // previously right or above/below - x = parent.xPos + parent.width; // on right - position = MenuPosition.RIGHT; - if (x+w > this.w) { - x = parent.xPos - w; // or left - position = MenuPosition.LEFT; - } - } - } else { - wdim pw = parent.width; - if (popup.minWidth <= pw) - popup.setWidth (pw, -1); // neatness - x = parent.xPos; // align on left edge - if (x+w > this.w) x += pw - w; // align on right edge - y = parent.yPos + parent.height; // place below - if (y+h > this.h) y = parent.yPos - h; // or above - position = MenuPosition.ACTIVE; - } - if (x < 0) x = 0; // may be placed partially off-screen - if (y < 0) y = 0; - popup.setPosition (x, y); - debug if (Debug.logPopupPositioning()) - logger.trace ("Placed popup {} of size ({},{}) at ({},{}); output position: {}", popup, w,h, x,y, position); - return position; - } - - void requestRedraw () { - imde.mainSchedule.request(imde.SCHEDULE.DRAW); - } - //END IWidgetManager methods - - debug void logWidgetSize (IContent) { - logger.trace ("size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh); - logger.trace ("childRoot:"); - childRoot.logWidgetSize; - logger.trace ("childContext:"); - childContext.logWidgetSize; - if (childDragged !is null) { - logger.trace ("childDragged:"); - childDragged.logWidgetSize; - } - } - -protected: - // These methods are called by derived classes to do the widget-management work - //BEGIN WidgetManagement methods - /** Second stage of widget loading. - * - * Widget data should be loaded before this is called. */ - final void createWidgets () { - // The renderer needs to be created on the first load, but not after this. - if (rend is null) - rend = createRenderer (rendName); - - debug (mdeWidgets) logger.trace ("Creating root widget..."); - childRoot = makeWidget (this, "root"); - debug (mdeWidgets) logger.trace ("Setting up root widget..."); - childRoot.setup (0, 3); - - mw = childRoot.minWidth; - mh = childRoot.minHeight; - matchMinimalSize (); - - debug (mdeWidgets) logger.trace ("Setting size and position of root widget..."); - childRoot.setWidth (w, -1); - childRoot.setHeight (h, -1); - childRoot.setPosition (0,0); - debug (mdeWidgets) logger.trace ("Done creating root widget."); - - childContext = new PopupHandlerWidget (this, this, "contextHandler", "context", serviceContent); - childContext.setup (0,3); - debug (mdeWidgets) logger.trace ("Created context handler widget."); - - underMouse = childRoot; // must be something - } - - /** Draw all widgets */ - final void wmDrawWidgets() { - if (childRoot) - childRoot.draw; - drawPopup; - } - - /** For mouse click events. - * - * Sends the event on to the relevant windows and all click callbacks. */ - final void wmMouseClick (wdabs cx, wdabs cy, ubyte b, bool state) { - if (childRoot is null) return; - - // Update underMouse to get the widget clicked on - updateUnderMouse (cx, cy, state); - - // end of a drag? - if (dragStart !is null && b == dragButton && state == false) { - IChildWidget dS = dragStart; - dragStart = null; - childDragged = null; - requestRedraw; - if (dS.dragRelease (cx, cy, underMouse)) - return; - } - - // 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 - Content contextContent = cast(Content)underMouse.content; - if (contextContent !is null) { - serviceContent.setContent (contextContent); - childContext.openMenu (underMouse, contextContent); - } - } else { // post other button presses to clickEvent - int ret = underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state); - if (ret & 1) { // keyboard input requested - keyFocus = underMouse; - setLetterCallback (&underMouse.keyEvent); - } - if (ret & 2 && dragStart is null) { // drag events requested - dragStart = underMouse; - dragButton = b; // currently we allow any button to be used for a drag, but.. ? - if (ret & 4) { - IContent c = underMouse.content(); - if (c) { // NOTE: creates a new widget, not optimal - childDragged = new DisplayContentWidget (this, this, "dragContentDisplay", WidgetData ([0], []), c); - childDragged.setup (0, 3); - dragX = underMouse.xPos - cx; - dragY = underMouse.yPos - cy; - childDragged.setPosition (cx + dragX, cy + dragY); - } - } - } - } - } - - /** For mouse motion events. - * - * Lock on mutex before calling. Pass new mouse coordinates. */ - final void wmMouseMotion (wdabs cx, wdabs cy) { - updateUnderMouse (cx, cy, false); - - if (dragStart !is null) { - dragStart.dragMotion (cx, cy, underMouse); - if (childDragged !is null) { - childDragged.setPosition (cx + dragX, cy + dragY); - requestRedraw; - } - } - } - - - /** A change callback on MiscOptions.l10n content to update widgets. - * - * Relies on another callback reloading translations to content first! */ - final void reloadStrings (IContent) { - synchronized(mutex) { - if (childRoot is null) return; - childRoot.setup (++setupN, 2); - childRoot.setWidth (w, -1); - childRoot.setHeight (h, -1); - childRoot.setPosition (0,0); - childContext.setup (setupN, 2); - //TODO: possibly childDragged? - requestRedraw; - } - } - // for internal use - final void updateUnderMouse (wdabs cx, wdabs cy, bool closePopup) { - auto oUM = underMouse; - underMouse = getPopupWidget (cx, cy, closePopup); - if (underMouse is null) { - debug assert (childRoot.onSelf (cx, cy), "WidgetManager: childRoot doesn't cover whole area"); - underMouse = childRoot.getWidget (cx, cy); - } - if (underMouse !is oUM) { - debug assert (oUM && underMouse, "no widget under mouse: error"); - oUM.underMouse (false); - underMouse.underMouse (true); - debug if (Debug.logUnderMouse()) - logger.trace ("Widget under mouse: {}", underMouse); - } - } - - /** If possible, the screen-interaction derived class should override to - * make sure the window is at least (mw,mh) in size. In any case, this - * method MUST make sure w >= mw and h >= mh even if the window isn't this - * big. - * - * A resize may not be required when this is called, however. */ - void matchMinimalSize () { - if (w < mw) { - logger.warn ("Min width for gui, {}, not met: {}", mw, w); - w = mw; - } - if (h < mh) { - logger.warn ("Min height for gui, {}, not met: {}", mh, h); - h = mh; - } - } - - /// 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 -private static { -/// Widget types. Items match widget names without the "Widget" suffix. -enum WIDGET_TYPE : int { - FUNCTION = 0x2000, // Function called instead of widget created (no "Widget" appended to fct name) - TAKES_CONTENT = 0x4000, // Flag indicates widget's this should be passed an IContent reference. - - // Use widget names rather than usual capitals convention - Unnamed = 0x0, // Only for use by widgets not created with createWidget - - // blank: 0x1 - FixedBlank = 0x1, - SizableBlank = 0x2, - Debug = TAKES_CONTENT | 0xF, - - // popup widgets: 0x10 - PopupMenu = TAKES_CONTENT | 0x11, - - // labels: 0x20 - TextLabel = 0x21, - - // content functions: 0x30 - editContent = FUNCTION | TAKES_CONTENT | 0x30, - addContent = FUNCTION | 0x31, - popupListContent = FUNCTION | TAKES_CONTENT | 0x33, - - // content widgets: 0x40 - DisplayContent = TAKES_CONTENT | 0x40, - BoolContent = TAKES_CONTENT | 0x41, - AStringContent = TAKES_CONTENT | 0x42, - ButtonContent = TAKES_CONTENT | 0x43, - SliderContent = TAKES_CONTENT | 0x44, - - GridLayout = TAKES_CONTENT | 0x100, - ContentList = TAKES_CONTENT | 0x110, - - FloatingArea = TAKES_CONTENT | 0x200, - Border = TAKES_CONTENT | 0x204, - Switch = TAKES_CONTENT | 0x210, - Collapsible = TAKES_CONTENT | 0x214, -} - -// Only used for binarySearch algorithm generation; must be ordered by numerical values. -const char[][] WIDGETS = [ - "FixedBlank", - "SizableBlank", - "TextLabel", - "addContent", - "Debug", - "PopupMenu", - "DisplayContent", - "BoolContent", - "AStringContent", - "ButtonContent", - "SliderContent", - "GridLayout", - "ContentList", - "FloatingArea", - "Border", - "Switch", - "Collapsible", - "editContent", - "popupListContent"]; - -/* Generates a binary search algorithm for makeWidget. */ -char[] binarySearch (char[] var, char[][] consts) { - if (consts.length > 3) { - return `if (`~var~` <= WIDGET_TYPE.`~consts[$/2 - 1]~`) {` ~ - binarySearch (var, consts[0 .. $/2]) ~ - `} else {` ~ - binarySearch (var, consts[$/2 .. $]) ~ - `}`; - } else { - char[] ret; - foreach (c; consts) { - ret ~= `if (` ~ var ~ ` == WIDGET_TYPE.` ~ c ~ `) { - debug (mdeWidgets) logger.trace ("Creating new `~c~`."); - parent.recursionCheck (id, content); - static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.FUNCTION) - return `~c~` (this, parent, id, data, content); - else static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.TAKES_CONTENT) - return new `~c~`Widget (this, parent, id, data, content); - else - return new `~c~`Widget (this, parent, id, data); - } else `; - } - ret = ret[0..$-6]; // remove last else - return ret; - } -} - -debug { // check items in WIDGETS are listed in order - char[] WIDGETS_check () { - char[] ret; - for (int i = WIDGETS.length-2; i > 0; --i) { - ret ~= "WIDGET_TYPE."~WIDGETS[i] ~" >= WIDGET_TYPE."~ WIDGETS[i+1]; - if (i>1) ret ~= " || "; - } - return ret; - } - mixin ("static if ("~WIDGETS_check~") - static assert (false, \"WIDGETS is not in order!\");"); -} -} - //END makeWidget metacode - -protected: - // Main child widget: - IChildWidget childRoot; // Root of the main GUI widget tree - - // Dimensions and child set-up data (fit to childRoot): - wdim w,h; // current widget size; should be at least (mw,mh) even if not displayable - wdim mw,mh; // minimal area required by widgets - uint setupN; // n to pass to IChildWidget.setup - - // IPopupParentWidget stuff for childRoot: - MenuPosition mAIPPW; // IPPW variable - IPopupParentWidget childIPPW; // child IPPW, if any active - - IChildWidget keyFocus; // widget receiving keyboard input - IChildWidget underMouse; // widget under the mouse pointer - - - // Context menu: - // Essentially, we consider childContext a full child IPPW, but handle it separately from - // childIPPW. Instead of providing another ref. for this IPPW, shortcut by using this reference - // and the boolean contextActive: - scope PopupHandlerWidget childContext; // context menu popup (handler) - bool contextActive = false; // If true, consider childContext a child IPPW - scope IServiceContent serviceContent; // context menu content tree - - - // Drag-and-drop data: - //NOTE: could be wrapped with a PopupHandlerWidget, but can't set position then? - scope IChildWidget childDragged; // displays dragged content; no interaction - IChildWidget dragStart; // if non-null, this widget should receive motion and click-release events - int dragButton; // index of button in use for drag - wdrel dragX, dragY; // coordinates of dragged content relative to mouse - - - // Renderer: - char[] rendName; // Name of renderer; for saving and creating renderers - scope IRenderer rend; - - - // Data loaded/to save: - WidgetDataSet curData; // Current data - WidgetDataChanges changes; // Changes for the current design. - - Mutex mutex; // lock on methods for use outside the package. -} diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/widget/AChildWidget.d --- a/mde/gui/widget/AChildWidget.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/gui/widget/AChildWidget.d Fri Sep 11 20:56:53 2009 +0200 @@ -61,7 +61,7 @@ // Widgets with content need to override these override IContent content () { - return null; + return null; } override void setContent (IContent) {} //END Load and save diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/widget/Ifaces.d --- a/mde/gui/widget/Ifaces.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/gui/widget/Ifaces.d Fri Sep 11 20:56:53 2009 +0200 @@ -350,7 +350,7 @@ * during their creation. *****************************************************************************/ //NOTE: add another this() without the data for default initialization, for the GUI editor? -abstract class IChildWidget : IWidget +interface IChildWidget : IWidget { //BEGIN Load and save /** 2nd stage of initialization for widgets; also called on some changes. @@ -369,7 +369,7 @@ * Returns: * The method must return true on initial setup and if its dimensions * (may) have changed. */ - bool setup (uint n, uint flags) {return 0;} + bool setup (uint n, uint flags); /+ Use when widget editing is available? Requires widgets to know their parents. /** Called when a child widget's size has changed. @@ -387,22 +387,22 @@ * * Parents normally take their resizability from sub-widgets; see SIZABILITY for how they do * this. */ - bool isWSizable () {return 0;} - bool isHSizable () {return 0;} /// ditto + bool isWSizable (); + bool isHSizable (); /// ditto /** The minimal size the widget could be shrunk to (or its fixed size). * * Takes into account child-widgets and any other contents. */ - wdim minWidth () {return 0;} - wdim minHeight() {return 0;} /// ditto + wdim minWidth (); + wdim minHeight(); /// ditto /** Get the current size of the widget. */ - wdim width () {return 0;} - wdim height() {return 0;} /// ditto + wdim width (); + wdim height(); /// ditto /** (Smallest) coordinates of widget. */ - wdabs xPos () {return 0;} - wdabs yPos () {return 0;} /// ditto + wdabs xPos (); + wdabs yPos (); /// ditto /** Used to adjust the size. * @@ -418,21 +418,21 @@ * A "fixed" size widget should enlarge itself as requested. * * setPosition must be called after calling either setWidth or setHeight. */ - void setWidth (wdim nw, int dir) {} - void setHeight (wdim nh, int dir) {} /// ditto + void setWidth (wdim nw, int dir); + void setHeight (wdim nh, int dir); /// ditto /** Set the current position (called after setup and to move widget). */ - void setPosition (wdim x, wdim y) {} + void setPosition (wdim x, wdim y); //END Size and position //BEGIN Content /** Return the widget's content, or null. */ - IContent content () {return null;} + IContent content (); /** Set the widget's content, if the widget takes content and changing it * at this stage is feasible. (Also pass to sub-widgets, where the * constructor normally does so.) */ - void setContent (IContent) {} + void setContent (IContent); //END Content //BEGIN Events @@ -445,10 +445,10 @@ * (cx,cy). * * Note: use global coordinates (cx,cy) not coordinates relative to the widget. */ - IChildWidget getWidget (wdabs cx, wdabs cy) {return null;} + IChildWidget getWidget (wdabs cx, wdabs cy); /** Return true if (cx,cy) is on self's box (doesn't matter if actually on a subwidget). */ - bool onSelf (wdabs cx, wdabs cy) {return 0;} + bool onSelf (wdabs cx, wdabs cy); /** Receive a mouse click event at cx,cy from button b (1-5 correspond to L,M,B, wheel up,down) * which is a down-click if state is true. @@ -461,14 +461,14 @@ * $(TR $(TD 2) $(TD Request the functions dragMotion and dragRelease are called)) * $(TR $(TD 4) $(TD Display the widget's content while dragging (requires 2))) * ) */ - int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) {return 0;} + int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state); /** Called when dragging motion occurs, originating from this widget. * * Params: target = The widget under the mouse * * Only called if requested by clickEvent. */ - void dragMotion (wdabs cx, wdabs cy, IChildWidget target) {} + void dragMotion (wdabs cx, wdabs cy, IChildWidget target); /** Called at the end of a drag which originated from this widget. * @@ -478,7 +478,7 @@ * clickEvent on the relevent widget. * * Only called if requested by clickEvent. */ - bool dragRelease (wdabs cx, wdabs cy, IChildWidget target) {return 0;} + bool dragRelease (wdabs cx, wdabs cy, IChildWidget target); /** Receives keyboard events when requested. * @@ -492,7 +492,7 @@ /** Called on all widgets when the mouse moves over it (state == true) and * when it leaves (state == false). */ - void underMouse (bool state) {} + void underMouse (bool state); /** When a pop-up is closed the manager calls requestRedraw and this function on its parent. */ protected void popupClose (); diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/widget/ParentContent.d --- a/mde/gui/widget/ParentContent.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/gui/widget/ParentContent.d Fri Sep 11 20:56:53 2009 +0200 @@ -26,13 +26,11 @@ import mde.content.AStringContent; import mde.gui.exception; -debug { import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.widget.ParentContent"); } -} /****************************************************************************** * Widget which pops up a ContentListWidget created with its content. diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/widget/TextWidget.d --- a/mde/gui/widget/TextWidget.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/gui/widget/TextWidget.d Fri Sep 11 20:56:53 2009 +0200 @@ -48,6 +48,10 @@ } return false; } + //COMPILER BUG: without defining here, I get a SIGSEGV when calling from a TextLabelWidget + override IContent content () { + return null; + } override void draw () { super.draw(); diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/widget/WidgetManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/gui/widget/WidgetManager.d Fri Sep 11 20:56:53 2009 +0200 @@ -0,0 +1,658 @@ +/* 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 . */ + +/****************************************************************************** + * The widget manager; root of the widget tree. + * + * Rendering is handled separately by an IRenderer. + *****************************************************************************/ +module mde.gui.widget.WidgetManager; + +import mde.gui.WidgetDataSet; +import mde.gui.widget.Ifaces; +import mde.gui.renderer.createRenderer; + +import imde = mde.imde; +import mde.content.Content; +import mde.content.ServiceContent; +debug import mde.content.miscContent; // Debug menu +debug import mde.content.Debug; + +// Widgets to create: +import mde.gui.widget.layout; +import mde.gui.widget.miscWidgets; +import mde.gui.widget.TextWidget; +import mde.gui.widget.contentFunctions; +import mde.gui.widget.miscContent; +import mde.gui.widget.Floating; +import mde.gui.widget.ParentContent; +import mde.gui.widget.AParentWidget; + +public import tango.core.sync.Mutex; +import tango.util.log.Log : Log, Logger; +import tango.io.Console; // to print exception stack-trace +import tango.util.container.SortedMap; + +private Logger logger; +static this () { + logger = Log.getLogger ("mde.gui.WidgetManager"); +} + +/****************************************************************************** + * 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. + *****************************************************************************/ +abstract class AWidgetManager : IWidgetManager +{ + //BEGIN Public methods, for use outside the widget package + /** Construct a new widget manager. + * + * Params: + * name = The file name of the config for this GUI (to identify multiple GUIs). */ + this (char[] name) { + auto p = "MiscOptions.l10n" in Content.allContent; + assert (p, "MiscOptions.l10n not created!"); + p.addCallback (&reloadStrings); + + serviceContent = ServiceContentList.createItems (name); + assert (cast (IServiceContent) Content.get ("menus.services."~name)); + + debug { // add a debug-mode menu + auto lWS = new EventContent ("menus.debug."~name~".logWidgetSize"); + lWS.addCallback (&logWidgetSize); + } + } + + /** A change callback on MiscOptions.l10n content to update widgets. + * + * Relies on another callback reloading translations to content first! */ + final void reloadStrings (IContent) { + synchronized(mutex) { + if (childRoot is null) return; + childRoot.setup (++setupN, 2); + childRoot.setWidth (w, -1); + childRoot.setHeight (h, -1); + childRoot.setPosition (0,0); + childContext.setup (setupN, 2); + //TODO: possibly childDragged? + requestRedraw; + } + } + + debug public void logWidgetSize (IContent) { + logger.trace ("size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh); + logger.trace ("childRoot:"); + childRoot.logWidgetSize; + logger.trace ("childContext:"); + childContext.logWidgetSize; + if (childDragged !is null) { + logger.trace ("childDragged:"); + childDragged.logWidgetSize; + } + } + + + //BEGIN Public IWidget methods + override bool saveChanges () { + bool ret = childRoot.saveChanges; + ret |= childContext.saveChanges; + if (childDragged !is null) + ret |= childDragged.saveChanges; + return ret; + } + + /** Draw all widgets */ + override void draw () { + if (childRoot) + childRoot.draw; + drawPopup; + } + //END Public IWidget methods + //END Public methods, for use outside the widget package + + //BEGIN IWidget methods for widgets + public override bool dropContent (IContent content) { + return false; + } + //END IWidget methods for widgets + + //BEGIN IParentWidget methods + // If call reaches the widget manager there isn't any recursion. + //NOTE: should be override + final void recursionCheck (widgetID, IContent) {} + + override void minWChange (IChildWidget widget, wdim nmw) { + if (widget !is childRoot) { // Probably because widget is a popup widget + // This may get called from a CTOR, hence we can't check widget is one of childContext, etc. + if (widget.width < nmw) + widget.setWidth (nmw, -1); + return; + } + mw = nmw; + if (w < nmw) { + childRoot.setWidth (nmw, -1); + w = nmw; + } + childRoot.setPosition (0,0); + requestRedraw; + } + override void minHChange (IChildWidget widget, wdim nmh) { + if (widget !is childRoot) { + if (widget.height < nmh) + widget.setHeight (nmh, -1); + return; + } + mh = nmh; + if (h < nmh) { + childRoot.setHeight (nmh, -1); + h = nmh; + } + childRoot.setPosition (0,0); + requestRedraw; + } + //END IParentWidget methods + + //BEGIN IPopupParentWidget methods + override IPopupParentWidget getParentIPPW () { + return this; + } + + override void addChildIPPW (IPopupParentWidget ippw) { + requestRedraw; + if (ippw is childContext) { // special handling - a separate IPPW + contextActive = true; + return; + } + if (childIPPW) + childIPPW.removedIPPW; + childIPPW = ippw; + } + override bool removeChildIPPW (IPopupParentWidget ippw) { + if (ippw is childContext && contextActive) { + childContext.removedIPPW; + contextActive = false; + return true; + } + if (childIPPW !is ippw) return false; + childIPPW.removedIPPW; + childIPPW = null; + mAIPPW = MenuPosition.INACTIVE; + requestRedraw; + return true; + } + + override void menuActive (MenuPosition mA) { + mAIPPW = mA; + if (childIPPW) + childIPPW.menuActive = mA; + if (contextActive) + childContext.menuActive = mA; + } + override MenuPosition menuActive () { + return mAIPPW; + } + override MenuPosition parentMenuActive () { + return MenuPosition.INACTIVE; + } + + // Note: also triggered by non-popup widgets + override void menuDone () {} + + override IChildWidget getPopupWidget (wdabs cx, wdabs cy, bool closePopup) { + IChildWidget ret; + // Don't bother with childDragged; it has no interaction + if (contextActive) { + ret = childContext.getPopupWidget (cx, cy, closePopup); + if (ret) return ret; + if (closePopup) { + childContext.removedIPPW; + contextActive = false; + requestRedraw; + } + } + if (childIPPW) { + ret = childIPPW.getPopupWidget (cx, cy, closePopup); + if (ret) return ret; + if (closePopup) { + removeChildIPPW (childIPPW); + } + } + return null; + } + + override void drawPopup () { + if (childIPPW) + childIPPW.drawPopup; + if (contextActive) + childContext.drawPopup(); + if (childDragged) + childDragged.draw(); + } + + debug override bool isChild (IPopupParentWidget ippw) { + if (contextActive && ippw is childContext) + return true; + return ippw is childIPPW; + } + + override void removedIPPW () {} // irrelevant + //END IPopupParentWidget methods + + //BEGIN IWidgetManager methods + override IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null) + { + debug assert (parent, "makeWidget: parent is null (code error)"); + debug scope (failure) + logger.warn ("Creating widget \""~id~"\" failed."); + + WidgetData data = curData[id]; + if (data.ints.length < 1) { + logger.error ("No int data; creating a debug widget"); + data.ints = [WIDGET_TYPE.Debug]; + } + int type = data.ints[0]; // type is first element of data + + try { + // Statically programmed binary search on type, returning a new widget or calling a + // function: + //pragma (msg, binarySearch ("type", WIDGETS)); + mixin (binarySearch ("type", WIDGETS)); + // Not returned a new widget: + logger.error ("Bad widget type: {}; creating a debug widget instead",type); + } catch (Exception e) { + logger.error ("Error creating widget; creating a debug widget instead. Exception printed to stderr."); + //TODO: find a standard way to output exceptions, and implement everywhere: + e.writeOut(delegate void(char[]s){ Cerr(s); }); + } + + return new DebugWidget (this, parent, id, data, content); + } + + override WidgetData widgetData (widgetID id) { + return curData[id]; + } + override void widgetData (widgetID id, WidgetData d) { + changes[id] = d; // also updates WidgetDataSet in data. + } + + override wdims dimData (widgetID id) { + return curData.dims (id); + } + override void dimData (widgetID id, wdims d) { + changes.setDims(id, d); // also updates WidgetDataSet in data. + } + + IRenderer renderer () { + assert (rend !is null, "WidgetManager.renderer: rend is null"); + return rend; + } + + MenuPosition positionPopup (IChildWidget parent, IChildWidget popup, MenuPosition position = MenuPosition.INACTIVE) { + debug assert (parent && popup, "positionPopup: null widget"); + debug if (Debug.logPopupPositioning()) + logger.trace ("Placing popup {} in relation to parent {}; input position: {}", popup, parent, position); + wdim w = popup.width, + h = popup.height, + x, y; + if (position & MenuPosition.ACTIVE) { + y = parent.yPos; // height flush with top + if (y+h > this.h) y += parent.height - h; // or bottom + if (position & MenuPosition.LEFT) { // previously left + x = parent.xPos - w; // on left + if (x < 0) { + x = parent.xPos + parent.width; // on right + position = MenuPosition.RIGHT; + } + } else { // previously right or above/below + x = parent.xPos + parent.width; // on right + position = MenuPosition.RIGHT; + if (x+w > this.w) { + x = parent.xPos - w; // or left + position = MenuPosition.LEFT; + } + } + } else { + wdim pw = parent.width; + if (popup.minWidth <= pw) + popup.setWidth (pw, -1); // neatness + x = parent.xPos; // align on left edge + if (x+w > this.w) x += pw - w; // align on right edge + y = parent.yPos + parent.height; // place below + if (y+h > this.h) y = parent.yPos - h; // or above + position = MenuPosition.ACTIVE; + } + if (x < 0) x = 0; // may be placed partially off-screen + if (y < 0) y = 0; + popup.setPosition (x, y); + debug if (Debug.logPopupPositioning()) + logger.trace ("Placed popup {} of size ({},{}) at ({},{}); output position: {}", popup, w,h, x,y, position); + return position; + } + + void requestRedraw () { + imde.mainSchedule.request(imde.SCHEDULE.DRAW); + } + //END IWidgetManager methods + +protected: + // These methods are called by derived classes to do the widget-management work + //BEGIN WidgetManagement methods + /** Second stage of widget loading. + * + * Widget data should be loaded before this is called. */ + final void createWidgets () { + // The renderer needs to be created on the first load, but not after this. + if (rend is null) + rend = createRenderer (rendName); + + debug (mdeWidgets) logger.trace ("Creating root widget..."); + childRoot = makeWidget (this, "root"); + underMouse = childRoot; // don't leave null due to a check + debug (mdeWidgets) logger.trace ("Setting up root widget..."); + childRoot.setup (0, 3); + + mw = childRoot.minWidth; + mh = childRoot.minHeight; + matchMinimalSize (); + + debug (mdeWidgets) logger.trace ("Setting size and position of root widget..."); + childRoot.setWidth (w, -1); + childRoot.setHeight (h, -1); + childRoot.setPosition (0,0); + debug (mdeWidgets) logger.trace ("Done creating root widget."); + + childContext = new PopupHandlerWidget (this, this, "contextHandler", "context", serviceContent); + childContext.setup (0,3); + debug (mdeWidgets) logger.trace ("Created context handler widget."); + + underMouse = childRoot; // must be something + } + + final void wmSizeEvent (int nw, int nh) { + w = cast(wdim) nw; + h = cast(wdim) nh; + matchMinimalSize; + + if (!childRoot) return; // if not created yet. + childRoot.setWidth (w, -1); + childRoot.setHeight (h, -1); + childRoot.setPosition (0,0); + debug logWidgetSize (null); + } + + /** For mouse click events. + * + * Sends the event on to the relevant windows and all click callbacks. */ + final void wmMouseClick (wdabs cx, wdabs cy, ubyte b, bool state) { + if (childRoot is null) return; + + // Update underMouse to get the widget clicked on + updateUnderMouse (cx, cy, state); + + // end of a drag? + if (dragStart !is null && b == dragButton && state == false) { + IChildWidget dS = dragStart; + dragStart = null; + childDragged = null; + requestRedraw; + if (dS.dragRelease (cx, cy, underMouse)) + return; + } + + // 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 + Content contextContent = cast(Content) underMouse.content; + if (contextContent !is null) { + serviceContent.setContent (contextContent); + childContext.openMenu (underMouse, contextContent); + } + } else { // post other button presses to clickEvent + int ret = underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state); + if (ret & 1) { // keyboard input requested + keyFocus = underMouse; + setLetterCallback (&underMouse.keyEvent); + } + if (ret & 2 && dragStart is null) { // drag events requested + dragStart = underMouse; + dragButton = b; // currently we allow any button to be used for a drag, but.. ? + if (ret & 4) { + IContent c = underMouse.content(); + if (c) { // NOTE: creates a new widget, not optimal + childDragged = new DisplayContentWidget (this, this, "dragContentDisplay", WidgetData ([0], []), c); + childDragged.setup (0, 3); + dragX = underMouse.xPos - cx; + dragY = underMouse.yPos - cy; + childDragged.setPosition (cx + dragX, cy + dragY); + } + } + } + } + } + + /** For mouse motion events. + * + * Lock on mutex before calling. Pass new mouse coordinates. */ + final void wmMouseMotion (wdabs cx, wdabs cy) { + updateUnderMouse (cx, cy, false); + + if (dragStart !is null) { + dragStart.dragMotion (cx, cy, underMouse); + if (childDragged !is null) { + childDragged.setPosition (cx + dragX, cy + dragY); + requestRedraw; + } + } + } + + // for internal use + private final void updateUnderMouse (wdabs cx, wdabs cy, bool closePopup) { + auto oUM = underMouse; + underMouse = getPopupWidget (cx, cy, closePopup); + if (underMouse is null) { + debug assert (childRoot.onSelf (cx, cy), "WidgetManager: childRoot doesn't cover whole area"); + underMouse = childRoot.getWidget (cx, cy); + } + debug assert (oUM && underMouse, "no widget under mouse: error"); + if (underMouse !is oUM) { + oUM.underMouse (false); + underMouse.underMouse (true); + debug if (Debug.logUnderMouse()) + logger.trace ("Widget under mouse: {}", underMouse); + } + } + + /** If possible, the screen-interaction derived class should override to + * make sure the window is at least (mw,mh) in size. In any case, this + * method MUST make sure w >= mw and h >= mh even if the window isn't this + * big. + * + * A resize may not be required when this is called, however. */ + void matchMinimalSize () { + if (w < mw) { + logger.warn ("Min width for gui, {}, not met: {}", mw, w); + w = mw; + } + if (h < mh) { + logger.warn ("Min height for gui, {}, not met: {}", mh, h); + h = mh; + } + } + + /// This should be overloaded to set a callback receiving keyboard input. + abstract void setLetterCallback(void delegate(ushort, char[])); + //END WidgetManagement methods + + //BEGIN makeWidget metacode +private static { +/// Widget types. Items match widget names without the "Widget" suffix. +enum WIDGET_TYPE : int { + FUNCTION = 0x2000, // Function called instead of widget created (no "Widget" appended to fct name) + TAKES_CONTENT = 0x4000, // Flag indicates widget's this should be passed an IContent reference. + + // Use widget names rather than usual capitals convention + Unnamed = 0x0, // Only for use by widgets not created with createWidget + + // blank: 0x1 + FixedBlank = 0x1, + SizableBlank = 0x2, + Debug = TAKES_CONTENT | 0xF, + + // popup widgets: 0x10 + PopupMenu = TAKES_CONTENT | 0x11, + + // labels: 0x20 + TextLabel = 0x21, + + // content functions: 0x30 + editContent = FUNCTION | TAKES_CONTENT | 0x30, + addContent = FUNCTION | 0x31, + popupListContent = FUNCTION | TAKES_CONTENT | 0x33, + + // content widgets: 0x40 + DisplayContent = TAKES_CONTENT | 0x40, + BoolContent = TAKES_CONTENT | 0x41, + AStringContent = TAKES_CONTENT | 0x42, + ButtonContent = TAKES_CONTENT | 0x43, + SliderContent = TAKES_CONTENT | 0x44, + + GridLayout = TAKES_CONTENT | 0x100, + ContentList = TAKES_CONTENT | 0x110, + + FloatingArea = TAKES_CONTENT | 0x200, + Border = TAKES_CONTENT | 0x204, + Switch = TAKES_CONTENT | 0x210, + Collapsible = TAKES_CONTENT | 0x214, +} + +// Only used for binarySearch algorithm generation; must be ordered by numerical values. +const char[][] WIDGETS = [ + "FixedBlank", + "SizableBlank", + "TextLabel", + "addContent", + "Debug", + "PopupMenu", + "DisplayContent", + "BoolContent", + "AStringContent", + "ButtonContent", + "SliderContent", + "GridLayout", + "ContentList", + "FloatingArea", + "Border", + "Switch", + "Collapsible", + "editContent", + "popupListContent"]; + +/* Generates a binary search algorithm for makeWidget. */ +char[] binarySearch (char[] var, char[][] consts) { + if (consts.length > 3) { + return `if (`~var~` <= WIDGET_TYPE.`~consts[$/2 - 1]~`) {` ~ + binarySearch (var, consts[0 .. $/2]) ~ + `} else {` ~ + binarySearch (var, consts[$/2 .. $]) ~ + `}`; + } else { + char[] ret; + foreach (c; consts) { + ret ~= `if (` ~ var ~ ` == WIDGET_TYPE.` ~ c ~ `) { + debug (mdeWidgets) logger.trace ("Creating new `~c~`."); + parent.recursionCheck (id, content); + static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.FUNCTION) + return `~c~` (this, parent, id, data, content); + else static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.TAKES_CONTENT) + return new `~c~`Widget (this, parent, id, data, content); + else + return new `~c~`Widget (this, parent, id, data); + } else `; + } + ret = ret[0..$-6]; // remove last else + return ret; + } +} + +debug { // check items in WIDGETS are listed in order + char[] WIDGETS_check () { + char[] ret; + for (int i = WIDGETS.length-2; i > 0; --i) { + ret ~= "WIDGET_TYPE."~WIDGETS[i] ~" >= WIDGET_TYPE."~ WIDGETS[i+1]; + if (i>1) ret ~= " || "; + } + return ret; + } + mixin ("static if ("~WIDGETS_check~") + static assert (false, \"WIDGETS is not in order!\");"); +} +} + //END makeWidget metacode + +protected: + // Main child widget: + IChildWidget childRoot; // Root of the main GUI widget tree + + // Dimensions and child set-up data (fit to childRoot): + wdim w,h; // current widget size; should be at least (mw,mh) even if not displayable + wdim mw,mh; // minimal area required by widgets + uint setupN; // n to pass to IChildWidget.setup + + // IPopupParentWidget stuff for childRoot: + MenuPosition mAIPPW; // IPPW variable + IPopupParentWidget childIPPW; // child IPPW, if any active + + IChildWidget keyFocus; // widget receiving keyboard input + IChildWidget underMouse; // widget under the mouse pointer; should never be null when childRoot is non-null + + + // Context menu: + // Essentially, we consider childContext a full child IPPW, but handle it separately from + // childIPPW. Instead of providing another ref. for this IPPW, shortcut by using this reference + // and the boolean contextActive: + scope PopupHandlerWidget childContext; // context menu popup (handler) + bool contextActive = false; // If true, consider childContext a child IPPW + scope IServiceContent serviceContent; // context menu content tree + + + // Drag-and-drop data: + //NOTE: could be wrapped with a PopupHandlerWidget, but can't set position then? + scope IChildWidget childDragged; // displays dragged content; no interaction + IChildWidget dragStart; // if non-null, this widget should receive motion and click-release events + int dragButton; // index of button in use for drag + wdrel dragX, dragY; // coordinates of dragged content relative to mouse + + + // Renderer: + char[] rendName; // Name of renderer; for saving and creating renderers + scope IRenderer rend; + + + // Data loaded/to save: + WidgetDataSet curData; // Current data + WidgetDataChanges changes; // Changes for the current design. + + Mutex mutex; // lock on methods for use outside the package. +} diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/widget/layout.d --- a/mde/gui/widget/layout.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/gui/widget/layout.d Fri Sep 11 20:56:53 2009 +0200 @@ -23,13 +23,11 @@ import tango.util.container.HashMap; -debug { import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.widget.layout"); } -} /************************************************************************************************* * Encapsulates a grid of Widgets. diff -r 3d58adc17d20 -r 1cbde9807293 mde/gui/widget/miscContent.d --- a/mde/gui/widget/miscContent.d Mon Aug 31 13:54:23 2009 +0200 +++ b/mde/gui/widget/miscContent.d Fri Sep 11 20:56:53 2009 +0200 @@ -25,13 +25,11 @@ import mde.gui.exception; -debug { import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.widget.miscContent"); } -} /// Editable boolean widget class BoolContentWidget : AButtonWidget