Mercurial > projects > mde
view mde/gui/widget/Floating.d @ 97:30470bc19ca4
Floating widgets now work nicely: customizable borders added, resizing, moving.
gl.basic abstraction module removed (seemed pointless).
Some changes to SimpleRenderer (largely to accomodate floating widgets).
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Mon, 10 Nov 2008 16:44:44 +0000 |
parents | dbf332403c6e |
children | 5de5810e3516 |
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. Becoming a widget. */ module mde.gui.widget.Floating; import mde.gui.widget.Widget; import mde.gui.exception; 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 subwidget widgetID. * Ints supplied may consist of just the widget type or * additionally an (x,y) coordinate for each subwidget (all x coords first, then all y coords). */ class FloatingAreaWidget : AParentWidget { this (IWidgetManager mgr, widgetID id, WidgetData data) { if (data.ints.length != 1 + data.strings.length) throw new WidgetDataException (this); 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 (s); 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]; } super (mgr, id, data); } void finalize () { foreach (i, ref d; sWData) with (d) { auto widg = subWidgets[i]; 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) w = mw; if (h < mh) h = mh; widg.setWidth (w - border.x1 - border.x2, -1); widg.setHeight (h - border.y1 - border.y2, -1); } } 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.setDimData (id, dd); // save positions return true; } 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; } } 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; } } bool isWSizable () { return true; } bool isHSizable () { return true; } 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); } void draw () { super.draw; mgr.renderer.restrict (x,y, w,h); foreach (i; sWOrder) with (sWData[i]) { mgr.renderer.drawWindow (&border, this.x + x, this.y + y, w, h); subWidgets[i].draw; } mgr.renderer.relax; } IChildWidget getWidget (wdim cx, wdim cy) { debug scope (failure) logger.warn ("getWidget: failure; values: click, pos, width - {}, {}, {} - {}, {}, {}", cx, x, w, cy, y, h); 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) { sWOrder[j..$-1] = sWOrder[j+1..$].dup; sWOrder[$-1] = i; mgr.requestRedraw; if (lx >= border.x1 && lx < w-border.x2 && ly >= border.y1 && ly < h-border.y2) return subWidgets[i].getWidget (cx,cy); event = i; return this; } } event = size_t.max; return this; // no match } void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { if (event > subWidgets.length) return; 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; mgr.addClickCallback (&endCallback); mgr.addMotionCallback (&resizeCallback); } else if (borderType & BTYPE.MOVE) { // window is being moved xDrag = cx - x; yDrag = cy - y; mgr.addClickCallback (&endCallback); mgr.addMotionCallback (&moveCallback); } } } } protected: void moveCallback (wdabs cx, wdabs cy) { 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; } void resizeCallback (wdabs cx, wdabs cy) { with (sWData[active]) { if (resizeType & RESIZE.X1) { wdim ow = w; w = xDrag - cx; if (w < mw) w = mw; x += ow - w; subWidgets[active].setWidth (w - border.x1 - border.x2, 1); } else if (resizeType & RESIZE.X2) { w = xDrag + cx; if (w < mw) w = mw; 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; subWidgets[active].setHeight (h - border.y1 - border.y2, 1); } else if (resizeType & RESIZE.Y2) { h = yDrag + cy; if (h < mh) h = mh; 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 endCallback (wdabs, wdabs, ubyte b, bool state) { if (b == 1 && state == false) { // end of a move/resize mgr.removeCallbacks (cast(void*) this); return true; // we've handled the up-click } return false; // we haven't handled it } 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 event = size_t.max; // Border with last click/release: size_t.max: main area (no subwidget), i: subwidget[i] size_t active = size_t.max; // Like event, but refers to widget being moved/resized wdim xDrag, yDrag; // where a drag starts relative to x and y RESIZE resizeType; // Type of current resize }