# HG changeset patch # User Diggory Hardy # Date 1246303216 -7200 # Node ID e45226d3deaea8d6e455c5bb78c49fb4b4ca005a # Parent bc1cf73dc83550db3f2c8e560c755d235174f5b3 Context menu services not applicable to the current type can now be hidden. Added files missing from previous commits. diff -r bc1cf73dc835 -r e45226d3deae codeDoc/jobs.txt --- a/codeDoc/jobs.txt Mon Jun 29 18:55:50 2009 +0200 +++ b/codeDoc/jobs.txt Mon Jun 29 21:20:16 2009 +0200 @@ -9,6 +9,7 @@ To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): Also search for FIXME/NOTE/BUG/WARNING comment marks. 4 Should only have one instance of context menu widgets. Service menu callbacks don't get removed! +3 Closing menus when release-click is not on menu or parent (ordinary & context). 3 Dragging and dropping of editable data: should content immediately appear as being dragged? 3 Dragging from anything other than AStringContentWidget. 3 Single-line edit: pressing return should lose keyboard focus and change value diff -r bc1cf73dc835 -r e45226d3deae data/L10n/en-GB.mtt --- a/data/L10n/en-GB.mtt Mon Jun 29 18:55:50 2009 +0200 +++ b/data/L10n/en-GB.mtt Mon Jun 29 21:20:16 2009 +0200 @@ -5,11 +5,15 @@ + + + + {Font} diff -r bc1cf73dc835 -r e45226d3deae data/conf/guiDemo.mtt --- a/data/conf/guiDemo.mtt Mon Jun 29 18:55:50 2009 +0200 +++ b/data/conf/guiDemo.mtt Mon Jun 29 21:20:16 2009 +0200 @@ -45,8 +45,11 @@ - - - + + + + + + {Basic} diff -r bc1cf73dc835 -r e45226d3deae mde/content/AStringContent.d --- a/mde/content/AStringContent.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/content/AStringContent.d Mon Jun 29 21:20:16 2009 +0200 @@ -196,7 +196,7 @@ size_t pos; // editing position; used by keyStroke } -class BoolContent : AStringContent +class BoolContent : AStringContent, IBoolContent { /** Create a content with _symbol name symbol. */ this (char[] symbol) { @@ -214,11 +214,11 @@ if (pos > sv.length) pos = sv.length; endEvent; } - void opAssign (bool val) { + override void opAssign (bool val) { assignNoCng (val); endCng; } - bool opCall () { + override bool opCall () { return v; } alias opCall opCast; @@ -532,6 +532,7 @@ * parent Enum. * * Also should not save its value, since the parent stores the value. */ + //NOTE: Could extend IBoolContent instead of BoolContent now. private class EnumValueContent : BoolContent { /** New enumeration of parent with index num. */ this (EnumContent parent, size_t num, char[] symbol) { diff -r bc1cf73dc835 -r e45226d3deae mde/content/Content.d --- a/mde/content/Content.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/content/Content.d Mon Jun 29 21:20:16 2009 +0200 @@ -107,12 +107,12 @@ /** Add a callback. Callbacks are called on a change or event, in the order * added. */ - Content addCallback (void delegate (Content) cb) { + override IContent addCallback (void delegate (IContent) cb) { this.cb ~= cb; return this; } /// ditto - Content addCallback (void function (Content) cb) { + override IContent addCallback (void function (IContent) cb) { this.cb ~= util.toDg (cb); return this; } @@ -148,7 +148,7 @@ protected: char[] symbol; char[] name_, desc_; // translated name and description - void delegate (Content) cb[]; + void delegate (IContent) cb[]; public static: /** Get Content with _symbol name symbol from the list of all content, or diff -r bc1cf73dc835 -r e45226d3deae mde/content/ContentLoader.d --- a/mde/content/ContentLoader.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/content/ContentLoader.d Mon Jun 29 21:20:16 2009 +0200 @@ -95,7 +95,7 @@ private: /* Load translations for all content. * Hooked as a change callback for l10n. */ - void translate (Content) { + void translate (IContent) { logger.info ("loading translations..."); Translation trl = Translation.get (l10n()); diff -r bc1cf73dc835 -r e45226d3deae mde/content/Debug.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/content/Debug.d Mon Jun 29 21:20:16 2009 +0200 @@ -0,0 +1,34 @@ +/* LICENSE BLOCK +Part of mde: a Modular D game-oriented Engine +Copyright © 2007-2009 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 . */ + +/****************************************************************************** + * Options enabling/disabling various debugging facilities. + *****************************************************************************/ +module mde.content.Debug; + +import mde.content.AStringContent; + +/// Wrapper for options +struct Debug { +static: + /// Log a message everytime the widget under the mouse changes + BoolContent logUnderMouse; + BoolContent logPopupPositioning; + + static this () { + logUnderMouse = new BoolContent ("options.Debug.logUnderMouse"); + logPopupPositioning = new BoolContent ("options.Debug.logPopupPositioning"); + } +} diff -r bc1cf73dc835 -r e45226d3deae mde/content/IContent.d --- a/mde/content/IContent.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/content/IContent.d Mon Jun 29 21:20:16 2009 +0200 @@ -51,6 +51,9 @@ /** Similarly, try to set the value directly from a string. * Doesn't do anything for content not storing a value. */ void opAssign (char[]); + + IContent addCallback (void delegate (IContent) cb); + IContent addCallback (void function (IContent) cb); } /** Interface for content which should be interacted with as a button. @@ -59,3 +62,11 @@ * functions a button is desired. */ interface IEventContent : IContent { } + +/// Interface for boolean-value content +interface IBoolContent : IContent { + /// Return the value + bool opCall (); + /// Directly set the value + void opAssign (bool val); +} diff -r bc1cf73dc835 -r e45226d3deae mde/content/ServiceContent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/content/ServiceContent.d Mon Jun 29 21:20:16 2009 +0200 @@ -0,0 +1,140 @@ +/* LICENSE BLOCK +Part of mde: a Modular D game-oriented Engine +Copyright © 2007-2009 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 . */ + +/** Content services. + * + * Each is a Content, so it can be used in a menu. Since the content being + * acted on is not passed when functions are called like this, it is set when + * a context menu is opened, which also allows the services to specify their + * compatibility with the content (type) passed (via BoolContent value and + * callbacks). + */ +module mde.content.ServiceContent; + +import mde.content.AStringContent; + +/** Interface for ServiceContent and ServiceContentList. */ +interface IServiceContent { + void setContent (Content cont); +} + +/** A class for services acting on content. + * + * Provides services for any content usable as type T, where T is some content + * type. + * + * The (boolean) value is true to indicate this service can act on the passed + * content type, false if not. */ +class ServiceContent(T : Content = AStringContent) : Content, IServiceContent, IBoolContent, IEventContent { + this (char[] symbol) { + super (symbol); + } + + /** Pass the content this service should prepare for. + * + * Stores the reference, because it won't be passed later. */ + override void setContent (Content cont) { + activeCont = cast(T)cont; + } + + override bool opCall () { + return activeCont is null; + } + // Doesn't support directly setting the value + override void opAssign (bool val) {} + +package: + T activeCont; +} + +/** Aliases for common types */ +alias ServiceContent!(AStringContent) AStringService; +alias ServiceContent!(IntContent) IntService; ///ditto + +/** A generic way to handle a list of type IContent. */ +class ServiceContentList : ContentList, IServiceContent, IBoolContent +{ + this (char[] symbol) { + super (symbol); + } + + void setContent (Content cont) { + foreach (child; list_) { + debug assert (cast(IServiceContent)child !is null); + (cast(IServiceContent)child).setContent (cont); + } + } + + override bool opCall () { + foreach (child; list_) { + debug assert (cast(IBoolContent)child !is null); + if (!(cast(IBoolContent)child)()) + return false; + } + return true; + } + // Doesn't support directly setting the value + override void opAssign (bool val) {} +} + +/** Create all services. +* +* For now, all are under the menu. +* +* Currently all clipboard operations convert to/from a string. +* TODO: Possible extensions: multi-item clipboard displaying value in menu, +* copying without converting everything to/from a string. */ +static this () { + // Create ContentLists so they have the correct type. + new ServiceContentList ("menus.services"); + // Many use callbacks on a generic class type. For this, we should be sure of the type. + auto copy = new AStringService("menus.services.copy"); + copy.addCallback ((IContent c) { + debug assert (cast(AStringService)c); + with (cast(AStringService)c) { + if (activeCont !is null) + clipboard = activeCont.toString(0); + } + }); + auto paste = new AStringService("menus.services.paste"); + paste.addCallback ((IContent c) { + debug assert (cast(AStringService)c); + with (cast(AStringService)c) { + if (activeCont !is null) + activeCont = clipboard; + } + }); + + new ServiceContentList("menus.services.calculator"); + auto inc = new IntService("menus.services.calculator.increment"); + inc.addCallback ((IContent c) { + debug assert (cast(IntService)c); + with (cast(IntService)c) { + if (activeCont !is null) + activeCont = activeCont()+1; + } + }); + auto dec = new IntService("menus.services.calculator.decrement"); + dec.addCallback ((IContent c) { + debug assert (cast(IntService)c); + with (cast(IntService)c) { + if (activeCont !is null) + activeCont = activeCont()-1; + } + }); +} +private { + char[] clipboard; +} diff -r bc1cf73dc835 -r e45226d3deae mde/content/Services.d --- a/mde/content/Services.d Mon Jun 29 18:55:50 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +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 . */ - -// NOTE: This module is currently unused and untested. -// FIXME: write unittests - -/** Content services. - * - * Services act on a content, potentially modifying it. They have the form: - * --- - * bool service (Content c); - * --- - * where c is the content acted on, and the return value is true if the content was changed (useful - * to know whether caches need updating or not). - */ -module mde.content.Services; - -import mde.content.AStringContent; - -union ContentUnion { - *Content ; -} - -bool copy (Content c) { - // store by type - clipboard = c; -} - -bool paste (StringContent c) { - if (clipboard) - c = clipboard.toString (0); -} - -bool paste (IntContent c) { - -} - -bool link (ref ContentText c) { - if (clipboard is null) - return false; // no item on clipboard, so don't do anything - c = clipboard; - return true; -} - -ContentUnion clipboard; diff -r bc1cf73dc835 -r e45226d3deae mde/font/FontTexture.d --- a/mde/font/FontTexture.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/font/FontTexture.d Mon Jun 29 21:20:16 2009 +0200 @@ -459,7 +459,7 @@ lcdFilter = new EnumContent ("Font.lcdFilter", ["none"[],"default","light"]); defaultSize = new IntContent ("Font.defaultSize"); defaultFont = new StringContent ("Font.defaultFont"); - mode.addCallback (delegate void(Content) { + mode.addCallback (delegate void(IContent) { /* modeFlag should have one of the following values: * FT_LOAD_TARGET_NORMAL (0x00000) * FT_LOAD_TARGET_LIGHT (0x10000) diff -r bc1cf73dc835 -r e45226d3deae mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/gui/WidgetManager.d Mon Jun 29 21:20:16 2009 +0200 @@ -246,6 +246,8 @@ 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; @@ -279,7 +281,8 @@ if (x < 0) x = 0; // may be placed partially off-screen if (y < 0) y = 0; popup.setPosition (x, y); - //debug logger.trace ("placed popup at {},{}; size: {},{}", x,y, w,h); + debug if (Debug.logPopupPositioning()) + logger.trace ("Placed popup {}; output position: {}", popup, position); return position; } @@ -288,7 +291,7 @@ } //END IWidgetManager methods - debug void logWidgetSize (Content) { + debug void logWidgetSize (IContent) { logger.trace ("size: {,4},{,4}; minimal: {,4},{,4} - WidgetManager", w,h, mw,mh); child.logWidgetSize; } @@ -340,8 +343,8 @@ serviceContent.setContent (contextContent); popupContext = makeWidget (this, "context", contextContent); popupContext.setup (0, 3); - positionPopup (underMouse, popupContext); - menuActive = MenuPosition.ACTIVE; + //NOTE: usually set parentIPPW.menuActive: + menuActive = positionPopup (underMouse, popupContext); requestRedraw; } else { // post other button presses to clickEvent int ret = underMouse.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state); @@ -385,7 +388,7 @@ /** A change callback on MiscOptions.l10n content to update widgets. * * Relies on another callback reloading translations to content first! */ - void reloadStrings (Content) { + void reloadStrings (IContent) { synchronized(mutex) { if (child is null) return; child.setup (++setupN, 2); diff -r bc1cf73dc835 -r e45226d3deae mde/gui/widget/AChildWidget.d --- a/mde/gui/widget/AChildWidget.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/gui/widget/AChildWidget.d Mon Jun 29 21:20:16 2009 +0200 @@ -22,7 +22,7 @@ module mde.gui.widget.AChildWidget; public import mde.gui.widget.Ifaces; -import mde.content.Content; +import mde.content.IContent; import mde.gui.exception; debug { @@ -206,7 +206,7 @@ /** Hook this function to a Content callback to cause redraws. * * mgr.requestRedraw could be hooked in directly if the prototype changed. */ - void contentRedraw (Content) { + void contentRedraw (IContent) { mgr.requestRedraw; } diff -r bc1cf73dc835 -r e45226d3deae mde/gui/widget/ParentContent.d --- a/mde/gui/widget/ParentContent.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/gui/widget/ParentContent.d Mon Jun 29 21:20:16 2009 +0200 @@ -124,7 +124,7 @@ } protected: - void updateVal (Content) { // callback + void updateVal (IContent) { // callback adapter.text = content_.toString(cIndex); wdim omw = mw, omh = mh; adapter.getDimensions (mw, mh); @@ -186,6 +186,12 @@ return r; } + override void recursionCheck (widgetID wID, IContent c) { + if (wID is id && c is content_) + throw new WidgetRecursionException (wID); + parent.recursionCheck (wID, c); + } + override void minWChange (IChildWidget widget, wdim nmw) { if (widget !is currentW) return; mw = nmw; @@ -229,7 +235,7 @@ protected: // callback on content_ - void switchWidget (Content) { + void switchWidget (IContent) { currentW = subWidgets[content_()]; mw = currentW.minWidth; mh = currentW.minHeight; @@ -249,7 +255,7 @@ bool isWS, isHS; // no infrastructure for changing sizability, so need to fix it. } -/** A collapsible widget: shows/hides a child dependant on a BoolContent. +/** A collapsible widget: shows/hides a child dependant on an IBoolContent. * * Sizability is set once. Min-size is changed (but cannot force size to 0). * @@ -259,7 +265,7 @@ { this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) { super (mgr, parent, id); - content_ = cast(BoolContent) c; + content_ = cast(IBoolContent) c; WDCCheck (data, 1, 1, content_); subWidgets = [mgr.makeWidget (this, data.strings[0], c)]; @@ -281,6 +287,12 @@ return r; } + override void recursionCheck (widgetID wID, IContent c) { + if (wID is id && c is content_) + throw new WidgetRecursionException (wID); + parent.recursionCheck (wID, c); + } + override IContent content () { return content_; } @@ -331,7 +343,7 @@ protected: // callback on content_ - void collapse (Content) { + void collapse (IContent) { collapsed = content_(); if (collapsed) { mw = mh = 0; @@ -349,7 +361,7 @@ } bool collapsed = false; - BoolContent content_; + IBoolContent content_; } /** Puts a border around its child widget. diff -r bc1cf73dc835 -r e45226d3deae mde/gui/widget/TextWidget.d --- a/mde/gui/widget/TextWidget.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/gui/widget/TextWidget.d Mon Jun 29 21:20:16 2009 +0200 @@ -111,7 +111,7 @@ } protected: - void updateVal (Content) { // callback + void updateVal (IContent) { // callback adapter.text = content_.toString(cIndex); wdim omw = mw, omh = mh; adapter.getDimensions (mw, mh); @@ -192,7 +192,7 @@ } protected: - void update (Content) { // callback + void update (IContent) { // callback adapter.text = content_.toString(0); wdim omw = mw, omh = mh; adapter.getDimensions (mw, mh); diff -r bc1cf73dc835 -r e45226d3deae mde/gui/widget/contentFunctions.d --- a/mde/gui/widget/contentFunctions.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/gui/widget/contentFunctions.d Mon Jun 29 21:20:16 2009 +0200 @@ -54,7 +54,7 @@ if (cast(AStringContent) c) { if (cast(EnumContent) c) // can be PopupMenuWidget or ContentListWidget return new PopupMenuWidget(mgr,parent,id,data,c); - if (cast(BoolContent) c) + if (cast(IBoolContent) c) return new BoolContentWidget(mgr,parent,id,data,c); return new AStringContentWidget(mgr,parent,id,data,c); } diff -r bc1cf73dc835 -r e45226d3deae mde/gui/widget/miscContent.d --- a/mde/gui/widget/miscContent.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/gui/widget/miscContent.d Mon Jun 29 21:20:16 2009 +0200 @@ -37,7 +37,7 @@ class BoolContentWidget : AButtonWidget { this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData, IContent c) { - content_ = cast(BoolContent) c; + content_ = cast(IBoolContent) c; if (content_ is null) throw new ContentException (this); super (mgr, parent, id); wdimPair s = mgr.renderer.getToggleSize; @@ -65,7 +65,7 @@ } protected: - BoolContent content_; + IBoolContent content_; } /// A button connected to an EventContent diff -r bc1cf73dc835 -r e45226d3deae mde/mainLoop.d --- a/mde/mainLoop.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/mainLoop.d Mon Jun 29 21:20:16 2009 +0200 @@ -14,7 +14,7 @@ along with this program. If not, see . */ /****************************************************************************** - * Adds some basic functionality to the main loop. + * Adds some standard functionality to the main loop. *****************************************************************************/ module mde.mainLoop; @@ -29,7 +29,7 @@ mainSchedule.add (mainSchedule.getNewID, &mde.events.pollEvents).frame = true; // Polling interval of main loop; use old name to save renaming: pollInterval = new DoubleContent ("MiscOptions.pollInterval"); - pollInterval.addCallback (delegate void(Content) { + pollInterval.addCallback (delegate void(IContent) { if (pollInterval() !<= 0.1 || pollInterval() !>= 0.0) pollInterval = 0.01; mainInterval = pollInterval(); diff -r bc1cf73dc835 -r e45226d3deae mde/menus.d --- a/mde/menus.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/menus.d Mon Jun 29 21:20:16 2009 +0200 @@ -30,13 +30,13 @@ static this () { auto quit = new EventContent("menus.main.quit"); - quit.addCallback ((Content){ + quit.addCallback ((IContent){ debug logger.trace ("Quit (from menu)"); run = false; }); auto about = new EventContent("menus.help.about"); - about.addCallback ((Content){ + about.addCallback ((IContent){ //TODO: popup dialog box logger.info ("MDE Copyright (C) 2007-2009 Diggory Hardy"); logger.info ("This program comes with ABSOLUTELY NO WARRANTY; for details see COPYING."); diff -r bc1cf73dc835 -r e45226d3deae mde/setup/Init.d --- a/mde/setup/Init.d Mon Jun 29 18:55:50 2009 +0200 +++ b/mde/setup/Init.d Mon Jun 29 21:20:16 2009 +0200 @@ -402,7 +402,7 @@ EnumContent logLevel, logOutput; // Callback on logLevel - void setLogLevel (Content = null) { + void setLogLevel (IContent = null) { int level = logLevel(); if (level < Level.Trace || level > Level.None) { logger.error ("incorrect logging level");