view mde/gui/widget/Ifaces.d @ 126:c9843fbaac88

Dynamic minimal size changing improved; works over layouts sharing alignment. EnumContent sub-contents use EnumValueContent instead of BoolContent; fixes a few small bugs. EnumContent substrings get translated (bug fixed). The widget manager no longer attempts to set widget sizes smaller than their minimals, even though some will not be shown. SwitchWidget: has fixed sizableness now.
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 08 Jan 2009 13:05:44 +0000
parents 3e648bc53bde
children ad91de8867a0
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.
 *
 * 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.
 *************************************************************************************************/
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);
    
    /** 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
}


/*************************************************************************************************
 * Interface for the widget manager.
 * 
 * This class handles widget rendering, input, loading and saving.
 *************************************************************************************************/
interface IWidgetManager : IParentWidget
{
    // 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
    
    // 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 ();
    
    /** Creates and positions a pop-up widget (widg),
     *
     * Set flags = 1 to place widg left or right of parent, otherwise it will be placed above or
     * below parent.
     *
     * WidgetManager sets its position, draws it, and passes it click events. If a click event
     * occurs which isn't on the popup or it's parent, it is removed (unless a newer
     * popup is not removed).
     *
     * Popups currently should not change their size while active. */
    void addPopup (IChildWidget parent, IChildWidget popup, int flags = 0);
    /** Remove the popup added by this parent, and any popups added afterwards.
     * If parent added multiple popups, this would just remove the top one. */
    void removePopup (IChildWidget parent);
    
    // 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 : IParentWidget
{
//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?
     *
     * This really means does the widget benifit from being enlarged? Any widget should occupy
     * additional area when expanded.
     *
     * If not, the widget has fixed dimensions equal to it's minimal size. */
    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 when the mouse moves over the button and when it leaves. No need to call
     * requestRedraw. */
    void highlight (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 ();
}