# HG changeset patch # User Diggory Hardy # Date 1210676556 -3600 # Node ID b3a6ca4516b42d1583b43e7673bf1eac22d3caa6 # Parent b28d7adc786b61a4de7108adb25c04cabb848192 The renderer now controls which parts of the window border allow resizing. committer: Diggory Hardy diff -r b28d7adc786b -r b3a6ca4516b4 codeDoc/jobs.txt --- a/codeDoc/jobs.txt Thu May 08 16:05:51 2008 +0100 +++ b/codeDoc/jobs.txt Tue May 13 12:02:36 2008 +0100 @@ -3,7 +3,6 @@ In progress: -Make a widget with multiple sizable rows/cols. Implement row/col sizing. @@ -50,3 +49,4 @@ Done (for git log message): +The renderer now controls which parts of the window border allow resizing. diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/Gui.d --- a/mde/gui/Gui.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/Gui.d Tue May 13 12:02:36 2008 +0100 @@ -24,7 +24,6 @@ import mde.gui.IGui; import mde.gui.widget.Ifaces; import mde.gui.widget.Window; -import mde.gui.renderer.createRenderer; import mde.gui.exception; // For adding the input event callbacks and requesting redraws: @@ -84,7 +83,6 @@ return; } rendName = *p; - rend = createRenderer (rendName); // get list windows.length = reader.dataset.sec.length; // pre-allocate @@ -149,7 +147,7 @@ void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { // NOTE: buttons receive the up-event even when drag-callbacks are in place. foreach (dg; clickCallbacks) - dg (cx, cy, b, state); + if (dg (cx, cy, b, state)) return; // See IGui.addClickCallback's documentation foreach (i,w; windows) { IWidget widg = w.getWidget (cx,cy); @@ -174,18 +172,15 @@ //END Methods for external use //BEGIN IGui methods - IRenderer renderer () - in { - assert (rend !is null, "Gui: rend is null"); - } body { - return rend; + char[] rendererName () { + return rendName; } void requestRedraw () { imde.mainSchedule.request(imde.SCHEDULE.DRAW); } - void addClickCallback (void delegate(ushort, ushort, ubyte, bool) dg) { + void addClickCallback (bool delegate(ushort, ushort, ubyte, bool) dg) { clickCallbacks[dg.ptr] = dg; } void addMotionCallback (void delegate(ushort, ushort) dg) { @@ -200,10 +195,9 @@ private: Window[] windows; // Windows. First window is "on top", others may be obscured. - IRenderer rend; // Renderer (synonymous with theme) - char[] rendName; // Name of renderer; for saving + char[] rendName; // Name of renderer; for saving and creating renderers // callbacks indexed by their frame pointers: - void delegate(ushort cx, ushort cy, ubyte b, bool state) [void*] clickCallbacks; + bool delegate(ushort cx, ushort cy, ubyte b, bool state) [void*] clickCallbacks; void delegate(ushort cx, ushort cy) [void*] motionCallbacks; } diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/IGui.d --- a/mde/gui/IGui.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/IGui.d Tue May 13 12:02:36 2008 +0100 @@ -15,16 +15,14 @@ module mde.gui.IGui; -public import mde.gui.renderer.IRenderer; - /** The Gui interface. * * This contains the functions for use by Windows, not those for external use (use Gui directly for * that). */ interface IGui { - /** Get the Gui's renderer. May be overriden by the window. */ - IRenderer renderer (); + /** Get the GUI's rendererName for creating a renderer (may be overridden by the window). */ + char[] rendererName (); /** Called by a sub-widget when a redraw is necessary (since drawing may sometimes be done on * event. @@ -32,8 +30,13 @@ * Currently forces the whole Gui to be redrawn. */ void requestRedraw (); - /** Add a mouse click callback: delegate will be called for all mouse click events recieved. */ - void addClickCallback (void delegate (ushort cx, ushort cy, ubyte b, bool state) dg); + /** Add a mouse click callback: delegate will be called for all mouse click events recieved. + * + * 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. + * Note that this is not a mechanism to prevent unwanted event handling, and in the future may + * be removed (so event handling cannot be cut short). */ + void addClickCallback (bool delegate (ushort cx, ushort cy, ubyte b, bool state) dg); /** Add a mouse motion callback: delegate will be called for all motion events recieved. */ void addMotionCallback (void delegate (ushort cx, ushort cy) dg); /** Remove all event callbacks with _frame pointer frame. */ diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/renderer/IRenderer.d --- a/mde/gui/renderer/IRenderer.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/renderer/IRenderer.d Tue May 13 12:02:36 2008 +0100 @@ -39,17 +39,29 @@ } } - /// An enum for border types - enum BORDER_TYPES { - WINDOW_TOTAL, - WINDOW_RESIZE, + /** Use to set and reset these parameters, and to get the border size (which may depend on + * them). */ + BorderDimensions setSizable (bool wSizable, bool hSizable); + + /// Which edges of a window are being resized + enum RESIZE_TYPE : ubyte { + NONE = 0x0, L = 0x1, R = 0x2, T = 0x4, B = 0x8 } - /** Return the renderer's window border width. - * - * Type should be an element of BORDER_TYPES; if not the method may throw or simply return - * without an error. */ - BorderDimensions getBorder (BORDER_TYPES type); + /** Used to tell if a click on a window's border is for resizing or moving. + * + * Depends on setSizable's parameters. + * + * Params: + * cx = + * cy = click coordinates relative to window border + * w = + * h = window size + * + * Returns: + * RESIZE_TYPE = NONE for a move, an or'd combination of L/R/T/B for resizing. + */ + RESIZE_TYPE getResizeType (int cx, int cy, int w, int h); /** Return the renderer's between-widget spacing (for layout widgets). */ int layoutSpacing (); diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/renderer/SimpleRenderer.d --- a/mde/gui/renderer/SimpleRenderer.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/renderer/SimpleRenderer.d Tue May 13 12:02:36 2008 +0100 @@ -29,17 +29,50 @@ * The renderer is intended to be per-GUI. */ class SimpleRenderer : IRenderer { - BorderDimensions getBorder (BORDER_TYPES type) { - BorderDimensions dims; - with (BORDER_TYPES) with (dims) { - if (type == WINDOW_TOTAL) { - l = t = r = b = 20; - } else if (type == WINDOW_RESIZE) { - r = t = 5; - l = b = 20; + BorderDimensions setSizable (bool wS, bool hS) { + wSizable = wS; + hSizable = hS; + + // Set the border size based on the above + with (border) { + l = r = t = b = 14; + } + with (resize) { + if (wSizable) + l = r = 6; + else + l = r = 0; + if (hSizable) { + t = 2; + b = 6; + } else + t = b = 0; + } + border += resize; + return border; + } + + RESIZE_TYPE getResizeType (int cx, int cy, int w, int h) { + RESIZE_TYPE resizeType = RESIZE_TYPE.NONE; + if (cx < resize.l || cx >= w - resize.r || + cy < resize.t || cy >= h - resize.b) { // window is being resized + /* check for resizes (different to above; use whole border giving larger area for + * diagonal resizes). */ + + if (wSizable) { + if (cx < border.l) + resizeType = RESIZE_TYPE.L; + else if (cx >= w - border.r) + resizeType = RESIZE_TYPE.R; + } + if (hSizable) { + if (cy < border.t) + resizeType |= RESIZE_TYPE.T; + else if (cy >= h - border.b) + resizeType |= RESIZE_TYPE.B; } } - return dims; + return resizeType; } int layoutSpacing () { @@ -52,10 +85,10 @@ gl.drawBox (x,y, w,h); gl.setColor (0f, 0f, 1f); - gl.drawBox (x+20,y+5, w-25,h-25); + gl.drawBox (x+resize.l, y+resize.t, w-resize.l-resize.r, h-resize.t-resize.b); gl.setColor (.3f, .3f, .3f); - gl.drawBox (x+20, y+20, w-40, h-40); + gl.drawBox (x+border.l, y+border.t, w-border.l-border.r, h-border.t-border.b); } void drawWidgetBack (int x, int y, int w, int h) {} @@ -67,4 +100,9 @@ gl.setColor (.6f, 0f, .6f); gl.drawBox (x,y, w,h); } + +protected: + bool wSizable, hSizable; + BorderDimensions border; + BorderDimensions resize; } diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/widget/Ifaces.d --- a/mde/gui/widget/Ifaces.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/widget/Ifaces.d Tue May 13 12:02:36 2008 +0100 @@ -38,10 +38,13 @@ /** Add widget's saveData to the data to be saved, returning it's widgetID. */ widgetID addCreationData (IWidget widget); - /** Get the managing Gui. */ - //FIXME: remove and add requestRedraw to allow for only redrawing the window + /** Returns the window's gui. */ + //NOTE: was going to remove this, but it's used more than I thought IGui gui (); + /** The widget/window needs redrawing. */ + void requestRedraw (); + /** Get the window's renderer. * * Normally specific to the GUI, but widgets have no direct contact with the GUI and this diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/widget/Widget.d --- a/mde/gui/widget/Widget.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/widget/Widget.d Tue May 13 12:02:36 2008 +0100 @@ -17,8 +17,8 @@ module mde.gui.widget.Widget; public import mde.gui.widget.Ifaces; -import mde.gui.IGui; import mde.gui.exception; +import mde.gui.renderer.IRenderer; import tango.io.Stdout; @@ -168,26 +168,29 @@ void clickEvent (ushort, ushort, ubyte b, bool state) { if (b == 1 && state == true) { pushed = true; - window.gui.requestRedraw; + window.requestRedraw; window.gui.addClickCallback (&clickWhileHeld); window.gui.addMotionCallback (&motionWhileHeld); } } // Called when a mouse motion/click event occurs while (held == true) - void clickWhileHeld (ushort cx, ushort cy, ubyte b, bool state) { + bool clickWhileHeld (ushort cx, ushort cy, ubyte b, bool state) { + //NOTE: which button? test if (cx >= x && cx < x+w && cy >= y && cy < y+h) // button event Stdout ("Button clicked!").newline; pushed = false; - window.gui.requestRedraw; + window.requestRedraw; window.gui.removeCallbacks (cast(void*) this); + + return false; } void motionWhileHeld (ushort cx, ushort cy) { bool oldPushed = pushed; if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true; else pushed = false; if (oldPushed != pushed) - window.gui.requestRedraw; + window.requestRedraw; } } //END Widgets diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/widget/Window.d --- a/mde/gui/widget/Window.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/widget/Window.d Tue May 13 12:02:36 2008 +0100 @@ -22,6 +22,7 @@ import mde.gui.IGui; import mde.gui.exception; +import mde.gui.renderer.createRenderer; import mt = mde.mergetag.DataSet; import tango.scrapple.text.convert.parseTo : parseTo; @@ -61,19 +62,20 @@ if (widgetData is null) throw new WindowLoadException ("No widget creation data"); + // Save gui and create the renderer: gui_ = gui; - rend = gui.renderer; - - // Get border sizes - border = rend.getBorder (BORDER_TYPES.WINDOW_TOTAL); - resize = rend.getBorder (BORDER_TYPES.WINDOW_RESIZE); + rend = createRenderer (gui.rendererName); // Create the primary widget (and indirectly all sub-widgets), throwing on error: - widget = makeWidget (0); // primary widget always has ID 0. + // Note: GridLayoutWidget's this relies on rend.layoutSpacing. + widget = makeWidget (0); // primary widget always has ID 0. widgetData = null; // data is no longer needed: allow GC to collect (cannot safely delete) - // Note: this should return an empty array, but nothing much should happen if it's not empty: - widget.adjust (mutableData); // adjust/set size, etc. + // get border sizes: + border = rend.setSizable (isWSizable, isHSizable); // depends on widget + + // Note: this should return an empty array, but we shouldn't make a fuss if it's not empty: + widget.adjust (mutableData); // adjust/set size, etc., depends on rend mutableData = null; // no longer needed widget.getCurrentSize (w,h); // and get this size @@ -167,12 +169,19 @@ return i; } - + IGui gui () { return gui_; } - IRenderer renderer () { + void requestRedraw () { + gui_.requestRedraw; + } + + IRenderer renderer () + in { + assert (rend !is null, "Window.renderer: rend is null"); + } body { return rend; } //END IWindow methods @@ -249,38 +258,24 @@ } void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { if (b == 1 && state == true) { - if (cx < x + resize.l || cx >= xw - resize.r || - cy < y + resize.t || cy >= yh - resize.b) { // window is being resized - /* check for resizes (different to above; use whole border giving larger area for - * diagonal resizes). */ - resizeType = RESIZE.NONE; + resizeType = rend.getResizeType (cx-x, cy-y, w,h); + + if (resizeType != RESIZE_TYPE.NONE) { // Some type of resize + // Set x/yDrag (unfortunately these need to be different for each edge) + if (resizeType & RESIZE_TYPE.L) + xDrag = w + cx; + else if (resizeType & RESIZE_TYPE.R) + xDrag = w - cx; - if (isWSizable) { - if (cx < x + border.l) { - xDrag = w + cx; - resizeType = RESIZE.L; - } - else if (cx >= xw - border.r) { - xDrag = w - cx; - resizeType = RESIZE.R; - } - } - if (isHSizable) { - if (cy < y + border.t) { - yDrag = h + cy; - resizeType |= RESIZE.T; - } - else if (cy >= yh - border.b) { - yDrag = h - cy; - resizeType |= RESIZE.B; - } - } + if (resizeType & RESIZE_TYPE.T) + yDrag = h + cy; + else if (resizeType & RESIZE_TYPE.B) + yDrag = h - cy; - if (resizeType != RESIZE.NONE) { // only if some valid size is being done - gui_.addClickCallback (&endCallback); - gui_.addMotionCallback (&resizeCallback); - } - } else { // window is being moved + // Add the callbacks (they use resizeType which has already been set) + gui_.addClickCallback (&endCallback); + gui_.addMotionCallback (&resizeCallback); + } else { // window is being moved xDrag = cx - x; yDrag = cy - y; @@ -300,15 +295,15 @@ //END IWidget methods private: - alias IRenderer.BorderDimensions BorderDimensions; - alias IRenderer.BORDER_TYPES BORDER_TYPES; + alias IRenderer.BorderDimensions BorderDimensions; + alias IRenderer.RESIZE_TYPE RESIZE_TYPE; //BEGIN Window moving and resizing void moveCallback (ushort cx, ushort cy) { setPosition (cx-xDrag, cy-yDrag); } void resizeCallback (ushort cx, ushort cy) { - if (resizeType & RESIZE.L) { + if (resizeType & RESIZE_TYPE.L) { int mw, nw; getMinimalSize (mw, nw); // (only want mw) nw = xDrag - cx; @@ -317,11 +312,11 @@ setSize (nw, h); setPosition (mw, y); } - else if (resizeType & RESIZE.R) { + else if (resizeType & RESIZE_TYPE.R) { setSize (xDrag + cx, h); - setPosition (x, y); + setPosition (x, y); // required to call after setSize. } - if (resizeType & RESIZE.T) { + if (resizeType & RESIZE_TYPE.T) { int mh, nh; getMinimalSize (nh, mh); nh = yDrag - cy; @@ -330,28 +325,29 @@ setSize (w, nh); setPosition (x, mh); } - else if (resizeType & RESIZE.B) { + else if (resizeType & RESIZE_TYPE.B) { setSize (w, yDrag + cy); setPosition (x, y); } } - void endCallback (ushort cx, ushort cy, ubyte b, bool state) { + bool endCallback (ushort cx, ushort cy, ubyte b, bool state) { if (b == 1 && state == false) { // The mouse shouldn't have moved since the motion callback // was last called, so there's nothing else to do now. gui_.removeCallbacks (cast(void*) this); + + return true; // we've handled the up-click } + return false; // we haven't handled it } int xDrag, yDrag; // where a drag starts relative to x and y - enum RESIZE : ubyte { - NONE = 0x0, L = 0x1, R = 0x2, T = 0x4, B = 0x8 - } - RESIZE resizeType; // Type of current resize + IRenderer.RESIZE_TYPE resizeType; // Type of current resize //END Window moving and resizing // Load/save data: public char[] name; // The window's name (id from config file) //bool edited = false; // True if any widgets have been edited (excluding scaling) + // Data used for saving and loading (null in between): int[][widgetID] widgetData = null;// Data for all widgets under this window int[] mutableData = null; // Widget's mutable data (adjusted sizes, etc.) @@ -365,8 +361,7 @@ int w,h; // Window size (calculated from Widgets) int xw, yh; // x+w, y+h (frequent use by clickEvent) int widgetX, widgetY; // Widget position (= window position plus BORDER_WIDTH) - int mw = -1, mh = -1; // minimal size + int mw = -1, mh = -1; // minimal size (negative means requires calculation) BorderDimensions border; // Total border size (move plus resize) - BorderDimensions resize; // The outer resize part of the border } diff -r b28d7adc786b -r b3a6ca4516b4 mde/gui/widget/layout.d --- a/mde/gui/widget/layout.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/gui/widget/layout.d Tue May 13 12:02:36 2008 +0100 @@ -45,7 +45,7 @@ // Get all sub-widgets subWidgets.length = rows*cols; - foreach (i, inout subWidget; subWidgets) { + foreach (i, ref subWidget; subWidgets) { subWidget = window.makeWidget (data[i+3]); } @@ -74,9 +74,9 @@ // Check row sizes are valid: //NOTE: this could be made optional - foreach (i, inout w; colW) + foreach (i, ref w; colW) if (w < colWMin[i]) w = colWMin[i]; - foreach (i, inout h; rowH) + foreach (i, ref h; rowH) if (h < rowHMin[i]) h = rowHMin[i]; } @@ -199,7 +199,7 @@ // Calculate the overall minimal size, starting with the spacing: - mh = window.renderer.layoutSpacing; // use temporarily + mh = window.renderer.layoutSpacing; // use mh temporarily mw = mh * (cols - 1); mh *= (rows - 1); diff -r b28d7adc786b -r b3a6ca4516b4 mde/sdl.d --- a/mde/sdl.d Thu May 08 16:05:51 2008 +0100 +++ b/mde/sdl.d Tue May 13 12:02:36 2008 +0100 @@ -53,7 +53,7 @@ // Must be called after SDL has been initialised, so cannot be a separate Init function. openJoysticks (); // after SDL init cleanup.addFunc (&cleanupSDL, "cleanupSDL"); - + setupWindow(); } @@ -83,6 +83,7 @@ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); // Open a window + debug logger.trace ("Opening a window (this can crash if the libraries are messed up)"); if (SDL_SetVideoMode (w, h, 32, flags) is null) { logger.fatal ("Unable to set video mode:"); char* msg = SDL_GetError ();