view mde/gui/widget/Ifaces.d @ 131:9cff74f68b84

Major revisions to popup handling. Buttons can close menus now, plus some smaller impovements. Removed Widget module. Moved Widget.AWidget to AChildWidget.AChildWidget and Widget.AParentWidget to AParentWidget.AParentWidget. Removed ASingleParentWidget to improve code sharing. AChildWidget doesn't implement IParentWidget like AWidget did. New IPopupParentWidget extending IParentWidget for the WM and some widgets to handle popups. Cut old popup management code. New underMouse() function replacing highlight(); called on all widgets. Separate menu-popup and button widgets aren't needed for menus now. Functions returning content widgets have been moved to their own module. Cleaned up jobs.txt. Switched to 80 line length for Ddoc.
author Diggory Hardy <diggory.hardy@gmail.com>
date Wed, 21 Jan 2009 13:01:40 +0000
parents c5c38eaadb64
children 9fd705793568
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;
import mde.content.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
{
    /** Checks for recursion of unsafe widgets to prevent infinite recursion. */
    void recursionCheck (widgetID);
    
    /** 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 resize themselves.
     * 
     * Children may depend on their parent resizing them if necessary to keep width valid.
     * Widgets may also 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.ALL_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.
 *****************************************************************************/
interface IPopupParentWidget : IParentWidget
{
    /** 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.
     *
     * 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. */
    void menuActive (bool);
    bool menuActive ();
    
    /** 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 false. */
    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 left or right of parent, or left or right of parent
     * (flags & 1 == 1). */
    void positionPopup (IChildWidget parent, IChildWidget popup, int flags = 0);
    
    // 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 ();
    
    // User input:
    /** Add a mouse click callback.
     * 
     * This is a delegate this will be called for all mouse click events recieved by the gui, not
     * simply all click events on the widget (as clickEvent recieves).
     *
     * The delegate should return true if it accepts the event and no further processing is
     * required (i.e. the event should not be handled by anything else), false otherwise. */
    void addClickCallback (bool delegate (wdabs cx, wdabs cy, ubyte b, bool state) dg);
    
    /** Add a mouse motion callback: delegate will be called for all motion events recieved by the
     * gui. */
    void addMotionCallback (void delegate (wdabs cx, wdabs cy) dg);
    
    /** Remove all event callbacks on this widget (according to the delegate's .ptr). */
    // Note: don't try to pass a reference and cast to void* in the function; it's a different address.
    void removeCallbacks (void* frame);
}


/******************************************************************************
 * 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 child widgets as necessary. (Passing widgets
 * a reference to their parent has not been found useful.)
 *
 * If a widget is to be creatable by IWidgetManager.makeWidget, it must be listed in the
 * createWidget module, and have a constructor of the following form. It should also update it's
 * creation data if necessary, either when changed or when saveChanges() is called, using
 * IWidgetManager.setData().
 * 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, widgetID id, WidgetData data);
 * 
 * /// The CTOR may take an IContent reference:
 * this (IWidgetManager mgr, widgetID id, WidgetData data, IContent content);
 * ----------------------------------
 * Where mgr is the widget manager, id is the _id passed to makeWidget() and data is
 * initialisation data. The method should throw a WidgetDataException (created without
 * parameters) if the data has wrong length or is otherwise invalid.
 *
 * 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
{
//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 should return true if the dimensions (may) have been changed. This may not be
     *	the case on the first run (when n == 0)!.
     */
    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 ();
    +/
    
//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: 1 to request keyboard input. */
    int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state);
    
    /** 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 ();
}