comparison mde/gui/widget/AChildWidget.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 mde/gui/widget/Widget.d@c5c38eaadb64
children 9f035cd139c6
comparison
equal deleted inserted replaced
130:c5c38eaadb64 131:9cff74f68b84
1 /* LICENSE BLOCK
2 Part of mde: a Modular D game-oriented Engine
3 Copyright © 2007-2008 Diggory Hardy
4
5 This program is free software: you can redistribute it and/or modify it under the terms
6 of the GNU General Public License as published by the Free Software Foundation, either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>. */
15
16 /******************************************************************************
17 * Contains the AChildWidget class, intended for IChildWidgets to inherit.
18 *
19 * Abstract widget classes have an 'A' prepended to the name, similar to the
20 * 'I' convention for interfaces.
21 *****************************************************************************/
22 module mde.gui.widget.AChildWidget;
23
24 public import mde.gui.widget.Ifaces;
25 import mde.content.Content;
26 import mde.gui.exception;
27
28 debug {
29 import tango.util.log.Log : Log, Logger;
30 private Logger logger;
31 static this () {
32 logger = Log.getLogger ("mde.gui.widget.AChildWidget");
33 }
34 }
35
36 /******************************************************************************
37 * An abstract base widget class for IChildWidgets.
38 *
39 * This abstract class, and the more concrete FixedWidget and ScalableWidget
40 * classes provides useful basic implementations for widgets.
41 *****************************************************************************/
42 abstract class AChildWidget : IChildWidget
43 {
44 //BEGIN Load and save
45 // Base this() for child Widgets.
46 protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) {
47 this.mgr = mgr;
48 this.parent = parent;
49 this.id = id;
50 }
51
52 // Widgets need to do their initialization either in this() or setup().
53 override bool setup (uint,uint) {
54 return false;
55 }
56
57 // Don't save any data: fine for many widgets.
58 override bool saveChanges () {
59 return false;
60 }
61 //END Load and save
62
63 //BEGIN Size and position
64 // default to not resizable
65 override bool isWSizable () {
66 return false;
67 }
68 override bool isHSizable () {
69 return false;
70 }
71
72 /* Return minimal/fixed size. */
73 override wdim minWidth () {
74 return mw;
75 }
76 override wdim minHeight () {
77 return mh;
78 }
79
80 override wdim width () {
81 return w;
82 }
83 override wdim height() {
84 return h;
85 }
86
87 override wdabs xPos () {
88 return x;
89 }
90 override wdabs yPos () {
91 return y;
92 }
93
94 /* Set size: minimal size is (mw,mh). Note that both resizable and fixed widgets should allow
95 * enlarging, so in both cases this is a correct implementation. */
96 override void setWidth (wdim nw, int) {
97 debug if (nw < mw) logger.warn ("Widget width set below minimal size");
98 w = (nw >= mw ? nw : mw);
99 }
100 override void setHeight (wdim nh, int) {
101 debug if (nh < mh) logger.warn ("Widget height set below minimal size");
102 h = (nh >= mh ? nh : mh);
103 }
104
105 override void setPosition (wdim nx, wdim ny) {
106 x = nx;
107 y = ny;
108 }
109 //END Size and position
110
111 //BEGIN Events
112 /* This method is only called when the location is over this widget; hence for all widgets
113 * without children this method is valid. */
114 override IChildWidget getWidget (wdim cx, wdim cy) {
115 debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)");
116 return this;
117 }
118
119 // Should be valid for any widget.
120 override bool onSelf (wdabs cx, wdabs cy) {
121 return cx >= x && cx < x + w && cy >= y && cy < y + h;
122 }
123
124 /* Dummy event method (suitable for all widgets which don't respond to events). */
125 override int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) {
126 return 0;
127 }
128
129 /* Dummy functions: suitable for widgets with no text input. */
130 override void keyEvent (ushort, char[]) {}
131 override void keyFocusLost () {}
132
133 // Called when mouse moves over or off this
134 override void underMouse (bool state) {}
135
136 // Only useful to widgets creating popups.
137 override void popupClose () {}
138 override bool popupParentClick () {
139 return true;
140 }
141 //END Events
142
143 /* Basic draw method: draw the background (all widgets should do this). */
144 override void draw () {
145 mgr.renderer.drawWidgetBack (x,y, w,h);
146 }
147
148 // Debug function to print size info. Intended to be correct not optimal.
149 debug override void logWidgetSize () {
150 logger.trace ("size: {,4},{,4}; minimal: {,4},{,4}; sizable: {},{} - {,-50} {}", this.width, this.height, this.minWidth, this.minHeight, cast(int)this.isWSizable, cast(int)this.isHSizable, this, id);
151 }
152
153 protected:
154 /**************************************************************************
155 * Widgets may use W*Check as a utility to check for existance of data. Its use is encouraged,
156 * so that the checks can easily be updated should WidgetData be changed.
157 *
158 * Variants:
159 * WDCheck checks the exact length of integer and string data.
160 * WDCCheck checks data as WDCheck and that the content passed is valid.
161 * WDCMinCheck does the same as WDCCheck, but allows more data than required (used by some
162 * generic widgets).
163 *
164 * Params:
165 * data = the WidgetData to check lengths of
166 * n_ints = number of integers wanted
167 * n_strings= number of strings (default 0 since not all widgets use strings)
168 *************************************************************************/
169 void WDCheck (WidgetData data, size_t n_ints, size_t n_strings = 0) {
170 if (data.ints.length != n_ints ||
171 data.strings.length != n_strings)
172 throw new WidgetDataException (this);
173 }
174 /** ditto */
175 void WDCCheck (WidgetData data, size_t n_ints, size_t n_strings, IContent c) {
176 if (data.ints.length != n_ints ||
177 data.strings.length != n_strings)
178 throw new WidgetDataException (this);
179 if (c is null)
180 throw new ContentException (this);
181 }
182 /** ditto */
183 void WDCMinCheck (WidgetData data, size_t n_ints, size_t n_strings, IContent c) {
184 if (data.ints.length < n_ints ||
185 data.strings.length < n_strings)
186 throw new WidgetDataException (this);
187 if (c is null)
188 throw new ContentException (this);
189 }
190
191 IWidgetManager mgr; // the enclosing window
192 IParentWidget parent; // the parent widget
193 wdim x, y; // position
194 widgetID id; // The widget's ID, used for saving data
195 wdim w, h; // size
196 wdim mw = 0, mh = 0; // minimal or fixed size, depending on whether the widget is
197 // resizible; both types of widgets should actually be expandable.
198 }
199
200 /** A base for fixed-size widgets taking their size from the creation data. */
201 class FixedWidget : AChildWidget {
202 // Check data.length is at least 3 before calling!
203 /** Constructor for a fixed-size [blank] widget.
204 *
205 * Widget uses the initialisation data:
206 * [widgetID, w, h]
207 * where w, h is the fixed size. */
208 this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) {
209 super (mgr, parent, id);
210 w = mw = cast(wdim) data.ints[1];
211 h = mh = cast(wdim) data.ints[2];
212 }
213 }
214
215 /** A base for resizable widgets. */
216 class SizableWidget : AChildWidget {
217 // Check data.length is at least 1 before calling!
218 /// Constructor for a completely resizable [blank] widget.
219 this (IWidgetManager mgr, IParentWidget parent, widgetID id) {
220 super (mgr, parent, id);
221 }
222
223 override bool isWSizable () {
224 return true;
225 }
226 override bool isHSizable () {
227 return true;
228 }
229 }
230
231 /** For pressable buttons.
232 *
233 * Overriding classes should implement this() (setting the size), draw() and activated(). */
234 abstract class AButtonWidget : AChildWidget
235 {
236 protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) {
237 super (mgr, parent, id);
238 parentIPPW = parent.getParentIPPW;
239 }
240
241 /// May be over-ridden. Pushed is true if the button has been pushed and not released.
242 override void draw () {
243 mgr.renderer.drawButton (x,y, w,h, pushed);
244 }
245
246 /// Handles the down-click
247 override int clickEvent (wdabs, wdabs, ubyte b, bool state) {
248 if (b != 1) return 0;
249 if (state) {
250 pushed = true;
251 mgr.requestRedraw;
252 mgr.addClickCallback (&clickWhilePushed);
253 mgr.addMotionCallback (&motionWhilePushed);
254 }
255 if (parentIPPW.menuActive) {
256 parentIPPW.menuDone;
257 activated;
258 }
259 return 0;
260 }
261
262 /// When menuActive, highlight on mouse-over
263 override void underMouse (bool state) {
264 if (!parentIPPW.menuActive) return;
265 pushed = state;
266 mgr.requestRedraw;
267 }
268
269 /// Called when a mouse click event occurs while held; handles up-click
270 bool clickWhilePushed (wdabs cx, wdabs cy, ubyte b, bool state) {
271 if (b == 1 && state == false) {
272 if (cx >= x && cx < x+w && cy >= y && cy < y+h) { // button event
273 parentIPPW.menuDone;
274 activated();
275 }
276
277 pushed = false;
278 mgr.requestRedraw;
279 mgr.removeCallbacks (cast(void*) this);
280
281 return true;
282 }
283 return false;
284 }
285 /// Called when a mouse motion event occurs while held; handles pushing in/out on hover
286 void motionWhilePushed (wdabs cx, wdabs cy) {
287 bool oldPushed = pushed;
288 if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true;
289 else pushed = false;
290 if (oldPushed != pushed)
291 mgr.requestRedraw;
292 }
293
294 /// The action triggered when the button is clicked...
295 void activated ();
296
297 protected:
298 bool pushed = false; /// True if button is pushed in (visually)
299 IPopupParentWidget parentIPPW;
300 }