Mercurial > projects > mde
view mde/gui/widget/Floating.d @ 161:e3fe6acc16fb
Replaced WidgetManager's click and motion callbacks with a drag event system.
This is less flexible, but much closer to what is required (and is simpler and less open to bugs through unintended use).
The widget under the mouse is now passed (although could just as easily have been before).
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Thu, 21 May 2009 22:15:40 +0200 |
parents | ccd01fde535e |
children | 0dd49f333189 |
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/>. */ /** The "window" class − a widget. */ module mde.gui.widget.Floating; import mde.gui.widget.AParentWidget; import mde.gui.exception; import mde.content.IContent; import mde.content.AStringContent; import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.widget.Floating"); } /** An area to contain floating widgets. * * The position of each sub-widget is set from dimension data, but not the size. * Rationale: parents' need to set subwidgets' positions when its position is set, so it needs to * know their positions. * * Data: Each string item is interpreted as a sub-widget widgetID to add as a floating window. * Ints consist of widget type followed by a border type (flags from IRenderer.Border.BTYPE) for * each sub-widget. */ class FloatingAreaWidget : AParentWidget { static this () { raiseOnHover = new BoolContent ("GUI.raiseOnHover"); } this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent content) { if (data.ints.length != 1 + data.strings.length) throw new WidgetDataException (this); super (mgr, parent, id); subWidgets.length = data.strings.length; // widgets created from string data sWOrder.length = subWidgets.length; sWData.length = subWidgets.length; foreach (i,s; data.strings) { subWidgets[i] = mgr.makeWidget (this, s, content); sWOrder[i] = i; sWData[i].borderType = cast(BTYPE) data.ints[i+1]; } wdim[] dd = mgr.dimData (id); if (dd.length == subWidgets.length * 4) { foreach (i, ref d; sWData) (&d.x)[0..4] = dd[i*4..i*4+4]; } } override bool setup (uint n, uint flags) { debug (mdeWidgets) logger.trace ("FloatingAreaWidget.setup"); foreach (i, ref d; sWData) with (d) { auto widg = subWidgets[i]; if (!widg.setup (n, flags) && !(flags & 1)) continue; // no changes; skip the rest d.border = mgr.renderer.getBorder (borderType, widg.isWSizable, widg.isHSizable); mw = widg.minWidth + border.x1 + border.x2; mh = widg.minHeight + border.y1 + border.y2; if (w < mw || !widg.isWSizable) w = mw; if (h < mh || !widg.isHSizable) h = mh; widg.setWidth (w - border.x1 - border.x2, -1); widg.setHeight (h - border.y1 - border.y2, -1); } return n == 0; // floating area size is not changed } override bool saveChanges () { wdim[] dd = new wdim[sWData.length*4]; foreach (i, ref d; sWData) { subWidgets[i].saveChanges (); dd[4*i..4*i+4] = (&d.x)[0..4]; } mgr.dimData (id, dd); // save positions return true; } override void setWidth (wdim nw, int) { w = nw; // check all floating widgets are visible foreach (i, ref d; sWData) with (d) { if (x + w > this.w) x = (w > this.w) ? 0 : this.w - w; } } override void setHeight (wdim nh, int) { h = nh; foreach (i, ref d; sWData) with (d) { if (y + h > this.h) y = (h > this.h) ? 0 : this.h - h; } } override bool isWSizable () { return true; } override bool isHSizable () { return true; } override void setPosition (wdim nx, wdim ny) { x = nx; y = ny; size_t n = subWidgets.length; foreach (i, ref d; sWData) subWidgets[i].setPosition (x + d.x + d.border.x1, y + d.y + d.border.y1); } override void minWChange (IChildWidget widg, wdim nmw) { with (sWData[getWidgetIndex(widg)]) { mw = nmw + border.x1 + border.x2; if (mw > w || !widg.isWSizable) { w = mw; widg.setWidth (w - border.x1 - border.x2, -1); } if (x + w > this.w) x = (w > this.w) ? 0 : this.w - w; widg.setPosition (this.x + border.x1 + x,this.y + border.y1 + y); mgr.requestRedraw; } } override void minHChange (IChildWidget widg, wdim nmh) { with (sWData[getWidgetIndex(widg)]) { mh = nmh + border.y1 + border.y2; if (mh > h || !widg.isHSizable) { h = mh; widg.setHeight (h - border.y1 - border.y2, -1); } if (y + h > this.h) y = (h > this.h) ? 0 : this.h - h; widg.setPosition (this.x + border.x1 + x,this.y + border.y1 + y); mgr.requestRedraw; } } override void draw () { super.draw; mgr.renderer.restrict (x,y, w,h); foreach (i; sWOrder) with (sWData[i]) { mgr.renderer.drawBorder (&border, this.x + x, this.y + y, w, h); subWidgets[i].draw; } mgr.renderer.relax; } override IChildWidget getWidget (wdim cx, wdim cy) { debug scope (failure) logger.warn ("getWidget: failure; values: click; pos; width: {},{}; {},{}; {},{}", cx, cy, x, y, w, h); size_t event = getFloatingWidget (cx,cy, raiseOnHover()); if (event > sWData.length) return this; // no match with (sWData[event]) { wdim lx = cx - (this.x + x); wdim ly = cy - (this.y + y); if (lx >= border.x1 && lx < w-border.x2 && ly >= border.y1 && ly < h-border.y2) return subWidgets[event].getWidget (cx,cy); return this; } } override int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { size_t event = getFloatingWidget (cx,cy, true); if (event > subWidgets.length) return 0; if (b == 1 && state == true) { active = event; with (sWData[active]) { resizeType = border.getResize (cx - this.x - x, cy - this.y - y, w,h); alias border.RESIZE RESIZE; if (resizeType != RESIZE.NONE) { // Set x/yDrag (unfortunately these need to be different for each edge) if (resizeType & RESIZE.X1) xDrag = w + cx; else if (resizeType & RESIZE.X2) xDrag = w - cx; if (resizeType & RESIZE.Y1) yDrag = h + cy; else if (resizeType & RESIZE.Y2) yDrag = h - cy; return 2; } else if (borderType & BTYPE.MOVE) { // window is being moved xDrag = cx - x; yDrag = cy - y; return 2; } } } return 0; } void dragMotion (wdabs cx, wdabs cy, IChildWidget) { if (resizeType == RESIZE.NONE) { with (sWData[active]) { x = cx-xDrag; y = cy-yDrag; if (x < 0) x = 0; else if (x + w > this.w) x = (w > this.w) ? 0 : this.w - w; if (y < 0) y = 0; else if (y + h > this.h) y = (h > this.h) ? 0 : this.h - h; subWidgets[active].setPosition (this.x + x + border.x1, this.y + y + border.y1); } mgr.requestRedraw; } else { with (sWData[active]) { if (resizeType & RESIZE.X1) { wdim ow = w; w = xDrag - cx; // new width if (w < mw) w = mw; // limit to min width x += ow - w; // move by difference if (x < 0) { // limit to left edge of area w += x; x = 0; } subWidgets[active].setWidth (w - border.x1 - border.x2, 1); } else if (resizeType & RESIZE.X2) { w = xDrag + cx; // new width if (w < mw) w = mw; // limit to min width if (x + w > this.w) // limit to right edge of area w = this.w - x; subWidgets[active].setWidth (w - border.x1 - border.x2, -1); } if (resizeType & RESIZE.Y1) { wdim oh = h; h = yDrag - cy; if (h < mh) h = mh; y += oh - h; if (y < 0) { h += y; y = 0; } subWidgets[active].setHeight (h - border.y1 - border.y2, 1); } else if (resizeType & RESIZE.Y2) { h = yDrag + cy; if (h < mh) h = mh; if (y + h > this.h) h = this.h - y; subWidgets[active].setHeight (h - border.y1 - border.y2, -1); } // Reposition widget and sub-widgets: subWidgets[active].setPosition (this.x + x + border.x1, this.y + y + border.y1); } mgr.requestRedraw; } } bool dragRelease (wdabs, wdabs, IChildWidget) { return true; // we've handled the up-click } protected: /** Return the index of the floating object under (cx,cy). If no widget is, * return size_t.max. * * Params: * raiseWidget = if true, the widget returned is raised to be the top- * most floating widget. */ size_t getFloatingWidget (wdim cx, wdim cy, bool raiseWidget = false) { debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)"); foreach_reverse (j,i; sWOrder) with (sWData[i]) { wdim lx = cx - (this.x + x); wdim ly = cy - (this.y + y); if (lx >= 0 && lx < w && ly >= 0 && ly < h) { if (raiseWidget) { sWOrder[j..$-1] = sWOrder[j+1..$].dup; sWOrder[$-1] = i; mgr.requestRedraw; } return i; } } return size_t.max; // no match } struct SWData { // NOTE: x,y,w,h must be first elements; search (&d.x) wdim x,y; // position (corner of border) wdim w,h; // size (including border) wdim mw,mh; BTYPE borderType; // what type of border to put around the widget IRenderer.Border border; } static assert (SWData.alignof == 4); // assumptions for optimization; search (&d.x) SWData[] sWData; size_t[] sWOrder; // indexes for draw order (top widget at end of list) // Click/drag information: alias IRenderer.Border.BTYPE BTYPE; alias IRenderer.Border.RESIZE RESIZE; size_t active = size_t.max; // refers to widget being moved/resized wdim xDrag, yDrag; // where a drag starts relative to x and y RESIZE resizeType; // Type of current resize static BoolContent raiseOnHover; }