Mercurial > projects > mde
view mde/gui/widget/Widget.d @ 130:c5c38eaadb64
Changed how sizability is set for parents: can require all sub-widgets resizable or only one to set parent resizable.
Ifaces.IParentWidget.SIZABILITY controlling parent sizability.
TextWidget no longer vertically resizable (since multi-line editing is not supported).
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 17 Jan 2009 16:11:26 +0000 |
parents | ad91de8867a0 |
children |
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/>. */ /************************************************************************************************* * GUI Widget module. * * This module contains some base widget classes suitable for widget classes to inherit. However, * inheriting one of them is by no means necessary for a widget so long as the IWidget interface * is implemented. * * Abstract widget classes have an 'A' prepended to the name, similar to the 'I' convention for * interfaces. *************************************************************************************************/ module mde.gui.widget.Widget; public import mde.gui.widget.Ifaces; import mde.content.Content; import mde.gui.exception; debug { import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.widget.Widget"); } } /************************************************************************************************* * An abstract base widget class. * * This abstract class, and the more concrete FixedWidget and ScalableWidget classes provides a * useful basic implementation for widgets. Widgets need not inherit these (they only need * implement IWidget); they are simply provided for convenience and to promote code reuse. *************************************************************************************************/ abstract class AWidget : IChildWidget { //BEGIN IParentWidget methods // Don't override; use the WIDGET_TYPE.SAFE_RECURSION flag for safe widgets. //NOTE: should be override (compiler bug) final void recursionCheck (widgetID a) { debug assert (id !is null && parent !is null, "recursionCheck called before parent and id set"); if (a is id) throw new GuiException ("Infite recursion of "~a); parent.recursionCheck (a); } // Parent widgets need to implement this. override void minWChange (IChildWidget widget, wdim mw) {} override void minHChange (IChildWidget widget, wdim mh) {} //END IParentWidget methods //BEGIN Load and save // Base this() for child Widgets. protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) { this.mgr = mgr; this.parent = parent; this.id = id; } // Widgets need to do their initialization either in this() or setup(). override bool setup (uint,uint) { return false; } // Don't save any data: fine for many widgets. override bool saveChanges () { return false; } //END Load and save //BEGIN Size and position // default to not resizable override bool isWSizable () { return false; } override bool isHSizable () { return false; } /* Return minimal/fixed size. */ override wdim minWidth () { return mw; } override wdim minHeight () { return mh; } override wdim width () { return w; } override wdim height() { return h; } override wdabs xPos () { return x; } override wdabs yPos () { return y; } /* Set size: minimal size is (mw,mh). Note that both resizable and fixed widgets should allow * enlarging, so in both cases this is a correct implementation. */ override void setWidth (wdim nw, int) { debug if (nw < mw) logger.warn ("Widget width set below minimal size"); w = (nw >= mw ? nw : mw); } override void setHeight (wdim nh, int) { debug if (nh < mh) logger.warn ("Widget height set below minimal size"); h = (nh >= mh ? nh : mh); } override void setPosition (wdim nx, wdim ny) { x = nx; y = ny; } //END Size and position //BEGIN Events /* This method is only called when the location is over this widget; hence for all widgets * without children this method is valid. */ override IChildWidget getWidget (wdim cx, wdim cy) { debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)"); return this; } // Should be valid for any widget. override bool onSelf (wdabs cx, wdabs cy) { return cx >= x && cx < x + w && cy >= y && cy < y + h; } /* Dummy event method (suitable for all widgets which don't respond to events). */ override int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { return 0; } /* Dummy functions: suitable for widgets with no text input. */ override void keyEvent (ushort, char[]) {} override void keyFocusLost () {} // Currently only applies to popup widgets. override void highlight (bool state) {} // Only useful to widgets creating popups. override void popupClose () {} override bool popupParentClick () { return true; } //END Events /* Basic draw method: draw the background (all widgets should do this). */ override void draw () { mgr.renderer.drawWidgetBack (x,y, w,h); } // Debug function to print size info. Intended to be correct not optimal. debug override void logWidgetSize () { logger.trace ("Current size: {,4},{,4}; minimal: {,4},{,4}; sizable: {,5},{,5} - {,-50} {}", this.width, this.height, this.minWidth, this.minHeight, this.isWSizable, this.isHSizable, this, id); } protected: /********************************************************************************************** * Widgets may use W*Check as a utility to check for existance of data. Its use is encouraged, * so that the checks can easily be updated should WidgetData be changed. * * Variants: * WDCheck checks the exact length of integer and string data. * WDCCheck checks data as WDCheck and that the content passed is valid. * WDCMinCheck does the same as WDCCheck, but allows more data than required (used by some * generic widgets). * * Params: * data = the WidgetData to check lengths of * n_ints = number of integers wanted * n_strings= number of strings (default 0 since not all widgets use strings) *********************************************************************************************/ void WDCheck (WidgetData data, size_t n_ints, size_t n_strings = 0) { if (data.ints.length != n_ints || data.strings.length != n_strings) throw new WidgetDataException (this); } /** ditto */ void WDCCheck (WidgetData data, size_t n_ints, size_t n_strings, IContent c) { if (data.ints.length != n_ints || data.strings.length != n_strings) throw new WidgetDataException (this); if (c is null) throw new ContentException (this); } /** ditto */ void WDCMinCheck (WidgetData data, size_t n_ints, size_t n_strings, IContent c) { if (data.ints.length < n_ints || data.strings.length < n_strings) throw new WidgetDataException (this); if (c is null) throw new ContentException (this); } IWidgetManager mgr; // the enclosing window IParentWidget parent; // the parent widget wdim x, y; // position widgetID id; // The widget's ID, used for saving data wdim w, h; // size wdim mw = 0, mh = 0; // minimal or fixed size, depending on whether the widget is // resizible; both types of widgets should actually be expandable. } /************************************************************************************************* * Abstract base widget classes to facilitate parent widgets. * * Parent widgets probably need to overload these functions (from AWidget): * setup, saveChanges, setPosition, getWidget, draw, setWidth and setHeight. *************************************************************************************************/ abstract class AParentWidget : AWidget { this (IWidgetManager mgr, IParentWidget parent, widgetID id) { super (mgr, parent, id); } override bool setup (uint n, uint flags) { debug (mdeWidgets) logger.trace ("AParentWidget.setup"); bool c = false; foreach (w; subWidgets) { debug assert (w, "AParentWidget: w is null"); c |= w.setup (n,flags); } return c; } override bool saveChanges () { bool c = false; foreach (w; subWidgets) c |= w.saveChanges; return c; } size_t getWidgetIndex (IChildWidget widg) { foreach (i,w; subWidgets) if (w is widg) return i; throw new GuiException ("getWidgetIndex: widget not found (code error)"); } debug override void logWidgetSize () { super.logWidgetSize; foreach (widg; subWidgets) widg.logWidgetSize; } protected: IChildWidget[] subWidgets; } /** ditto */ abstract class AParentSingleWidget : AWidget { this (IWidgetManager mgr, IParentWidget parent, widgetID id) { super (mgr, parent, id); } override bool setup (uint n, uint flags) { debug (mdeWidgets) logger.trace ("AParentSingleWidget.setup"); debug assert (subWidget, "AParentSingleWidget: subWidget is null"); return subWidget.setup (n,flags); } override bool saveChanges () { return subWidget.saveChanges; } debug override void logWidgetSize () { super.logWidgetSize; subWidget.logWidgetSize; } protected: IChildWidget subWidget; } /** A base for fixed-size widgets taking their size from the creation data. */ class FixedWidget : AWidget { // Check data.length is at least 3 before calling! /** Constructor for a fixed-size [blank] widget. * * Widget uses the initialisation data: * [widgetID, w, h] * where w, h is the fixed size. */ this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { super (mgr, parent, id); w = mw = cast(wdim) data.ints[1]; h = mh = cast(wdim) data.ints[2]; } } /** A base for resizable widgets. */ class SizableWidget : AWidget { // Check data.length is at least 1 before calling! /// Constructor for a completely resizable [blank] widget. this (IWidgetManager mgr, IParentWidget parent, widgetID id) { super (mgr, parent, id); } override bool isWSizable () { return true; } override bool isHSizable () { return true; } } /** For pressable buttons. * * Overriding classes should implement this() (setting the size), draw() and activated(). */ abstract class AButtonWidget : AWidget { protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) { super (mgr, parent, id); } /// May be over-ridden. Pushed is true if the button has been pushed and not released. override void draw () { mgr.renderer.drawButton (x,y, w,h, pushed); } /// Handles the down-click override int clickEvent (wdabs, wdabs, ubyte b, bool state) { if (b == 1 && state == true) { pushed = true; mgr.requestRedraw; mgr.addClickCallback (&clickWhilePushed); mgr.addMotionCallback (&motionWhilePushed); } return 0; } /// Called when a mouse click event occurs while held; handles up-click bool clickWhilePushed (wdabs cx, wdabs cy, ubyte b, bool state) { if (b == 1 && state == false) { if (cx >= x && cx < x+w && cy >= y && cy < y+h) // button event activated(); pushed = false; mgr.requestRedraw; mgr.removeCallbacks (cast(void*) this); return true; } return false; } /// Called when a mouse motion event occurs while held; handles pushing in/out on hover void motionWhilePushed (wdabs cx, wdabs cy) { bool oldPushed = pushed; if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true; else pushed = false; if (oldPushed != pushed) mgr.requestRedraw; } /// The action triggered when the button is clicked... void activated (); protected: bool pushed = false; /// True if button is pushed in (visually) }