Mercurial > projects > mde
view mde/gui/widget/Ifaces.d @ 166:55667d048c31
Made content displayable while being dragged.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sun, 21 Jun 2009 12:19:18 +0200 |
parents | 2476790223b8 |
children | 7f7b2011b759 |
line wrap: on
line source
/* 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 <http://www.gnu.org/licenses/>. */ /****************************************************************************** * Widget interfaces. * * This module contains the primary documentation for the declared methods; * also the Widget module has some brief comments and basic implementations. * * Widgets are connected as the nodes of a tree. Widgets know their parent as a * IParentWidget class and their children as IChildWidget classes. The gui * manager is a special widget only implementing IParentWidget; all other * widgets must implement IChildWidget and optionally IParentWidget. * * It's recommended that widgets inherit one of the A*Widget classes rather * than impement I*Widget directly. *****************************************************************************/ module mde.gui.widget.Ifaces; public import mde.gui.types; public import mde.gui.renderer.IRenderer; public import mde.content.IContent; /****************************************************************************** * The root widget interface, for methods required by both IParentWidget and * IChildWidget. *****************************************************************************/ interface IWidget { /** Called on a widget when something is dragged onto it. * * Generally, content editing widgets should implement this as: * --- override bool dropContent (IContent content) { if (content_.setContent (content)) return true; return parent.dropContent (content); } * --- * And other widgets should just: * --- * return parent.dropContent (content); * --- * * Returns: true if the content was received (false if it reaches the * WidgetManager and is still not used). */ bool dropContent (IContent content); } /****************************************************************************** * Interface for parent widgets, including IWidgetManager. * * All widgets implement this via AWidget to make things simpler (code sharing). * * Notation: * Positive/negative direction: along the x/y axis in this direction. * Layout widget: a widget containing multiple sub-widges (which hence * controls how they are laid out). *****************************************************************************/ interface IParentWidget : IWidget { /** Checks for recursion of unsafe widgets to prevent infinite recursion. */ void recursionCheck (widgetID, IContent); /** IPPWs return self, other widgets recurse call on parent. */ IPopupParentWidget getParentIPPW (); /** Child widgets should call this on their parent if their minimal size * changes, since they cannot properly resize themselves. * * Parents $(I must) increase their child's size if the child is too small. * Parents $(I must not) decrease their own size, even if they are not * sizable; they may only decrease their childrens' sizes if it does not * affect their own (i.e. WidgetManager and FloatingAreaWidget). * * Child widgets may depend on setPosition being called afterwards. * * Params: * widget = The child widget calling the function * mw = New minimal width * mh = New minimal height */ void minWChange (IChildWidget widget, wdim mw); void minHChange (IChildWidget widget, wdim mh); /// ditto /** Governor of parent widget resizability, where this is determined by sub-widgets * (e.g. this doesn't apply to FloatingAreaWidget). * * NEVER and ALWAYS often don't result in usable GUIs, and aren't expected to be used except * for debugging. * * Note: ANY_SUBWIDGETS can cause problems like enlarging a menu bar containing a resizable * blank instead of the main part of a window. */ enum SIZABILITY_ENUM { NEVER = 0, /// Parents are never resizable ALL_SUBWIDGETS = 3, /// Parents are only resizable if all sub-widgets are ANY_SUBWIDGETS = 2, /// Parents are resizable if any sub-widgets are ALWAYS = 1, /// Parents are always resizable START_TRUE = 1, /// Flag set by ALWAYS and ALL_SUBWIDGETS SUBWIDGETS = 2, /// Flag set by ALL_SUBWIDGETS and ANY_SUBWIDGETS } static const SIZABILITY = SIZABILITY_ENUM.ANY_SUBWIDGETS; /// ditto } /****************************************************************************** * Interface for parents of popups and the widget manager. * * An IPopupParentWidget (IPPW) may have 0 or 1 child IPPWs; the child may have * its own IPPW. Each IPPW when added to a parent IPPW is responsible for * managing one popup. * * So any child IPPW should also be a descendant widget, and is usually the or * a descendant of the popup. * * An IPPW usually has one popup to manage (the popup being one of its child * widgets), but the popup is only active if the IPPW is added as a child of * its parent IPPW. * * The widget manager is an IPPW, but unlike most IPPWs its popup(s), if * existing, probably have nothing to do with its child IPPWs. * * ---- * // No popups: * WM * // (IPPW) Widget X clicked on, which opens a menu: * WM -> X (manages menu widget) * ---- *****************************************************************************/ interface IPopupParentWidget : IParentWidget { /// menuActive's type enum MenuPosition { INACTIVE = 0, ACTIVE = 1, LEFT = 3, RIGHT = 5 } /** Add caller ippw as current child IPopupParentWidget of called IPPW. * * ippw is added as called IPPW's child IPPW, and its functions are * called to draw popup and pass events. The called IPPW's previous child * IPPW is replaced. * * ippw is then responsible for managing a popup. */ void addChildIPPW (IPopupParentWidget ippw); /** Remove ippw from being the called IPPW's child IPPW and disable * menuActive. * * Do nothing if ippw is not the called IPPW's child IPPW. * * Returns: true if ippw was the child IPPW. * * I.e. this deactivates ippw's popup. */ bool removeChildIPPW (IPopupParentWidget ippw); /** Notify the called IPPW that it has been removed. */ void removedIPPW (); /** Set/get menuActive state, which includes position information. * * This is set on the parent IPPW when a popup menu is opened and unset * when the menu is closed. * If set on the parent IPPW, popup menus can be opened with just a mouse- * over and buttons activated with an up-click. * * It is also used by positionPopup to signal if the popup was left or * right of the previous popup, to enable a further popup to be placed by * positionPopup without overlapping previous popups. */ void menuActive (MenuPosition); MenuPosition menuActive (); /** Returns the IPPW's parent's menuActive (WM returns false). If * (menuActive & MenuPosition.ACTIVE) popup widgets may assume they are * sub-menu popups not top-level menu popups. */ MenuPosition parentMenuActive (); /** Called by descendant widgets such as buttons when an action occurred, * which should close a menu. (But also called when not in a menu.) */ void menuDone (); /** Get the widget under cx,cy. * * The IPPW should first recurse the call to its child IPPW if it exists. * If this doesn't yield a widget, it should try getting one from its popup * and then itself. It should return the first widget found or null. * * If closePopup is true and a widget isn't returned from the childIPPW, * the childIPPW should be removed (to close popups when a click is not on * the popup or its parent) and menuActive set to INACTIVE. */ IChildWidget getPopupWidget (wdabs cx, wdabs cy, bool closePopup); /** Draw. * * The IPPW should first draw its popup, then call on its child IPPW if * that exists. */ void drawPopup (); /// Returns true if ippw is child IPPW. debug bool isChild (IPopupParentWidget ippw); } /****************************************************************************** * Interface for the widget manager. * * This class handles widget rendering, input, loading and saving. *****************************************************************************/ interface IWidgetManager : IPopupParentWidget { // Loading/saving: /** Create a widget by looking up the data for id then looking up data.ints[0] in WIDGET_TYPES. * * Params: * id = Identifier, within data files, of the data for the widget. * data = Pass this data to the widget, not data looked up via id. * content = An IContent may be passed to some widgets on creation. * * When used in a this(), super() should be called before any calls to makeWidget (or at least * parent and id set) due to recursionCheck being called on the widget. * * Creates a widget, using the widget data with index id. Widget data is loaded from files, * and per design (multiple gui layouts, called designs, may exist; data is per design). */ IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null); /** Get or set widget id's WidgetData or dimension data. * * WidgetData is for most data, dimensional data (wdims) is for dimensions. * * Data should only be set from IChildWidget.saveChanges() to * avoid setting multiple times when a widget id has several instances. */ WidgetData widgetData (widgetID id); void widgetData (widgetID id, WidgetData data); /// ditto wdims dimData (widgetID id); /// ditto void dimData (widgetID id, wdims d); /// ditto /** Position popup adjacent to parent. * * If position is 0, place popup below parent, or above if there isn't * space below, and expand popup's width to at least that of parent. * * If (position & MenuPosition.ACTIVE), place popup to the side of parent, * using the side indicated by LEFT or RIGHT, if any. * * Returns: * MenuPosition.ACTIVE if popup placed above or below, and LEFT or RIGHT * if placed left or right of parent. */ MenuPosition positionPopup (IChildWidget parent, IChildWidget popup, MenuPosition position = MenuPosition.INACTIVE); // Rendering: /** For when a widget needs redrawing. * * Must be called because rendering may only be done on events. * * Currently it just causes everything to be redrawn next frame. */ void requestRedraw (); /** Get the window's renderer. * * Normally specific to the GUI, but widgets have no direct contact with the GUI and this * provides the possibility of per-window renderers (if desired). */ IRenderer renderer (); } /****************************************************************************** * Interface for (child) widgets, i.e. all widgets other than the manager. * * A widget is a region of a GUI window which handles rendering and user- * interaction for itself and is able to communicate with its manager and other * widgets as necessary. * * If a widget is to be creatable by IWidgetManager.makeWidget, it must be * listed in the AWidgetManager WIDGET_TYPE enum and WIDGETS list, and have a * constructor of the following form. * It should use Ddoc to explain what initialization data is used. * ---------------------------------- * /++ Constructor for a ... widget. * + * + Widget uses the initialisation data: * + [widgetID, x, y] * + where x is ... and y is ... +/ * this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data); * * /// The CTOR may take an IContent reference: * this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent content); * ---------------------------------- * Where mgr is the widget manager, parent is the widget's _parent (another * child widget or the widget manager), id is the _id passed to makeWidget() * and data is initialisation _data. The method should throw a * WidgetDataException if the data is invalid, and throw other GuiExceptions on * other errors. * * All widgets should set their own size in this() or setup() (must be setup() * if it could change), although some parents may set child-widgets' size * during their creation. *****************************************************************************/ //NOTE: add another this() without the data for default initialization, for the GUI editor? interface IChildWidget : IWidget { //BEGIN Load and save /** 2nd stage of initialization for widgets; also called on some changes. * * Widgets should call recursively on their children, redo anything * indicated by flags, and adjust their size and other cached data * dependant on any thing which may have changed. * Widgets may rely on setPosition being called afterwards. * * Params: * n = Indicates this is the (n+1)-th time the function has been called. * flags = if (flags & 1) the renderer has been changed, * if (flags & 2) translation strings are being reloaded. * These flags are always true on first run. * * Returns: * The method must return true on initial setup and if its dimensions * (may) have changed. */ bool setup (uint n, uint flags); /** When this is called, if the widget has any changed data to save it should call * IWidgetManager.setData (id, data) to set it and return true. Otherwise it should return * false. * * If the widget has subwidgets, it should also be recursively called on these (passing their * ids). */ bool saveChanges (); /+ Use when widget editing is available? Requires widgets to know their parents. /** Called when a child widget's size has changed. * * Should be propegated up to parents. */ void childChanged (); +/ /** Return the widget's content, or null. */ IContent content (); //END Load and save //BEGIN Size and position /** Is the width / height resizable? * * Normally, this means does the widget benifit from being enlarged? If not, the widget has * "fixed" dimensions equal to it's minimal size; however it $(I may) still be enlarged. * * Parents normally take their resizability from sub-widgets; see SIZABILITY for how they do * this. */ 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 (); wdim minHeight (); /// ditto /** Get the current size of the widget. */ wdim width (); wdim height(); /// ditto /** (Smallest) coordinates of widget. */ wdabs xPos (); wdabs yPos (); /// ditto /** Used to adjust the size. * * Params: * nw/nh = The new width/height * dir = Direction to resize from. This is only really applicable to layout widgets. * It must be either -1 (start resizing from highest row/col index, decreasing the * index as necessary), or +1 (resize from the lowest index, i.e. 0). * Most widgets can simply ignore it. * * A widget should never be resized smaller than it's minimal size (if it is, it should assume * it's minimal size and print a warning when in debug mode). * 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 /** Set the current position (called after setup and to move widget). */ void setPosition (wdim x, wdim y); //END Size and position //BEGIN Events /** Recursively scan the widget tree to find the widget under (cx,cy). * * If called on a widget, that widget should assume the location is over itself, and so should * either return itself or the result of calling getWidget on the appropriate child widget. * * In the case of Window this may not be the case; it should check and return null if not under * (cx,cy). * * Note: use global coordinates (cx,cy) not coordinates relative to the widget. */ 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); /** 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. * * Widget may assume coordinates are on the widget (caller must check). * * The return value has the following flags: * $(TABLE * $(TR $(TD 1) $(TD Request keyboard input)) * $(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); /** 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); /** Called at the end of a drag which originated from this widget. * * Params: target = The widget under the mouse when the click was released * * Returns: true if the up-click event should not be passed to * clickEvent on the relevent widget. * * Only called if requested by clickEvent. */ bool dragRelease (wdabs cx, wdabs cy, IChildWidget target); /** Receives keyboard events when requested. * * Params: * sym SDLKey key sym, useful for keys with no character code such as arrow keys * letter The character input, in UTF-8 */ void keyEvent (ushort sym, char[] letter); /** Called when keyboard input focus is lost. */ void keyFocusLost (); /** Called on all widgets when the mouse moves over it (state == true) and * when it leaves (state == false). */ void underMouse (bool state); /** When a pop-up is closed the manager calls requestRedraw and this function on its parent. */ void popupClose (); /** When a click is on the parent of a popup, this function is called instead of the usual * clickEvent. * * Returns: * true to close the popup. * * Note: this means the parent can't receive text input without code changes. */ bool popupParentClick (); //END Events /** Draw, using the stored values of x and y. * * Maybe later enforce clipping of all sub-widget drawing, particularly for cases where only * part of the widget is visible: scroll bars or a hidden window. */ void draw (); /// Logs the current and minimal size of every widget. debug void logWidgetSize (); }