Changeset 97:30470bc19ca4
- Timestamp:
- 11/10/08 11:44:44 (2 months ago)
- Files:
-
- codeDoc/jobs.txt (modified) (1 diff)
- data/conf/gui.mtt (modified) (1 diff)
- mde/font/FontTexture.d (modified) (2 diffs)
- mde/font/font.d (modified) (1 diff)
- mde/gl/basic.d (deleted)
- mde/gui/WidgetManager.d (modified) (1 diff)
- mde/gui/renderer/IRenderer.d (modified) (2 diffs)
- mde/gui/renderer/SimpleRenderer.d (modified) (5 diffs)
- mde/gui/widget/Floating.d (modified) (6 diffs)
- mde/setup/Screen.d (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
codeDoc/jobs.txt
r95 r97 9 9 Bugs: 10 10 Sometimes nothing is drawn until a resize, and fonts are blocks. Cause: init-stages always appear to get divided between two threads. If Inpt, Font and SWnd are called by the main thread and not a sub-thread, the bug doesn't occur. A temporary fix is just to set maxThreads=1. A redesign of threading init stages could solve this, but doesn't seem worth the effort right now. 11 Drawing of floating widgets is not restricted to the floating area; see SimpleRenderer.restrict(). 11 12 12 13 data/conf/gui.mtt
r96 r97 15 15 <WidgetData|optVal={0:[0x6030]}> 16 16 <WidgetData|optSep={0:[0x21, 0xff],1:["="]}> 17 <WidgetData|floating={0:[0x8200 ],1:["text","button"]}>17 <WidgetData|floating={0:[0x8200,1,6,14],1:["text","button","blank"]}> 18 18 <WidgetData|text={0:[0x21,0xE0E000],1:["Floating text"]}> 19 <WDims|root=[6,590,196,6,580,6]> 20 <WDims|content=[590,190,364,18]> 21 <WDims|floating=[25,21,103,27,437,23,74,74,217,90,123,100]> 19 22 {Basic} 20 23 <WidgetData|root={0:[0x21,0x90D970],1:["A string!"]}> mde/font/FontTexture.d
r96 r97 218 218 } 219 219 220 glDisable(GL_TEXTURE_2D); 220 221 glDisable(GL_BLEND); 221 222 } … … 322 323 glEnd (); 323 324 325 glDisable(GL_TEXTURE_2D); 324 326 glDisable(GL_BLEND); 325 327 } mde/font/font.d
r89 r97 28 28 29 29 import derelict.freetype.ft; 30 import derelict.opengl.gl;31 30 32 31 import mde.file.deserialize; mde/gui/WidgetManager.d
r96 r97 128 128 h = cast(wdim) nh; 129 129 130 debug logger.trace ("Resize to: {},{}", nw, nh);131 130 if (w < mw || h < mh) 132 131 logger.warn ("Minimal dimensions ({},{}) not met: ({},{}), but I cannot resize myself!",mw,mh,w,h); mde/gui/renderer/IRenderer.d
r95 r97 29 29 interface IRenderer 30 30 { 31 //BEGIN Get dimensions 32 /// A container for the dimensions 33 struct BorderDimensions { 34 /// The dimensions: left, top, right, bottom 35 wdim l, t, r, b; 31 //BEGIN Types 32 /** For floating widget borders. Like TextAdapter above, could be more flexible. */ 33 struct Border { 34 /** Border type. 35 * 36 * No border, small non-functional border, movement only border, resize only border, or 37 * full border. */ 38 enum BTYPE : int { 39 NONE = 0, SMALL = 1, LARGE = 2, MOVE = 4, RESIZE = 8, BOTH = MOVE | RESIZE 40 } 41 /** Which edges of a window are being resized. 42 * 43 * E.g. X1 == left, X2 | Y1 == right and top. */ 44 enum RESIZE : int { 45 NONE = 0x0, X1 = 0x1, X2 = 0x2, Y1 = 0x4, Y2 = 0x8 46 } 36 47 37 void opAddAssign (BorderDimensions d) { 38 l += d.l; 39 t += d.t; 40 r += d.r; 41 b += d.b; 48 wdim x1,x2; /// First and second (lef & right) horizontal borders 49 wdim y1,y2; /// First and second vertical borders 50 RESIZE capability; /// Which resizes are possible. 51 52 void opAddAssign (Border d) { 53 x1 += d.x1; 54 x2 += d.x2; 55 y1 += d.y1; 56 y2 += d.y2; 57 } 58 59 /** Used to tell if a click on a window's border is for resizing or moving. 60 * 61 * Depends on setSizable's parameters. 62 * 63 * Params: 64 * cx = 65 * cy = click coordinates relative to window border 66 * w = 67 * h = window size 68 * 69 * Returns: 70 * RESIZE = Describes whether the click resizes or moves the widget. */ 71 RESIZE getResize (wdim cx, wdim cy, wdim w, wdim h) { 72 RESIZE r = RESIZE.NONE; 73 if (cx + cy < x1 + y1) 74 r = RESIZE.X1 | RESIZE.Y1; 75 else if (cx + h - cy < x1 + y2) 76 r = RESIZE.X1 | RESIZE.Y2; 77 else if (w - cx + cy < x2 + y1) 78 r = RESIZE.X2 | RESIZE.Y1; 79 else if (w - cx + h - cy < x2 + y2) 80 r = RESIZE.X2 | RESIZE.Y2; 81 return r & capability; 42 82 } 43 83 } 44 45 /** Use to set and reset these parameters, and to get the border size (which may depend on46 * them). */47 BorderDimensions setSizable (bool wSizable, bool hSizable);48 49 /// Which edges of a window are being resized50 enum RESIZE_TYPE : ubyte {51 NONE = 0x0, L = 0x1, R = 0x2, T = 0x4, B = 0x852 }53 54 /** Used to tell if a click on a window's border is for resizing or moving.55 *56 * Depends on setSizable's parameters.57 *58 * Params:59 * cx =60 * cy = click coordinates relative to window border61 * w =62 * h = window size63 *64 * Returns:65 * RESIZE_TYPE = NONE for a move, an or'd combination of L/R/T/B for resizing.66 */67 RESIZE_TYPE getResizeType (wdim cx, wdim cy, wdim w, wdim h);68 69 /** Return the renderer's between-widget spacing (for layout widgets). */70 wdim layoutSpacing ();71 //END Get dimensions72 73 //BEGIN draw routines74 /** Restrict following draw operations to given box.75 *76 * Restrict "pushes" a restriction onto a stack; relax must be called afterwards to "pop" the77 * restriction. */78 void restrict (wdim x, wdim y, wdim w, wdim h);79 void relax (); /// ditto80 81 /** Draw a window border plus background. */82 void drawWindow (wdim x, wdim y, wdim w, wdim h);83 84 /** Draws a widget background. Usually doesn't do anything since backgrounds are transparent. */85 void drawWidgetBack (wdim x, wdim y, wdim w, wdim h);86 87 /** Draws a blank widget (temporary) */88 void drawBlank (wdim x, wdim y, wdim w, wdim h);89 90 /** Draws a button frame, in if pushed == true. */91 void drawButton (wdim x, wdim y, wdim w, wdim h, bool pushed);92 93 /** Toggle buttons.94 *95 * These have a fixed size which getToggleSize returns. */96 wdimPair getToggleSize ();97 void drawToggle (wdim x, wdim y, bool state, bool pushed); /// ditto98 99 /** Get a TextAdapter to draw some text.100 *101 * If no colour is passes, a default is used (white). */102 TextAdapter getAdapter (char[] text, int colour = 0xFFFFFF);103 84 104 85 /** For drawing text - one instance per string. … … 127 108 FontStyle font; 128 109 } 129 //END draw routines 110 //END Types 111 112 //BEGIN Methods 113 /** Returns a Border containing dimensions. 114 * 115 * The dimensions may depend on whether the widget is resizable (horizontally and vertically). 116 */ 117 Border getBorder (Border.BTYPE type, bool wSizable, bool hSizable); 118 119 /** Return the renderer's between-widget spacing (for layout widgets). */ 120 wdim layoutSpacing (); 121 122 /** Restrict following draw operations to given box. 123 * 124 * Restrict "pushes" a restriction onto a stack; relax must be called afterwards to "pop" the 125 * restriction. */ 126 void restrict (wdim x, wdim y, wdim w, wdim h); 127 void relax (); /// ditto 128 129 /** Draw a window border plus background. */ 130 void drawWindow (Border* border, wdim x, wdim y, wdim w, wdim h); 131 132 /** Draws a widget background. Usually doesn't do anything since backgrounds are transparent. */ 133 void drawWidgetBack (wdim x, wdim y, wdim w, wdim h); 134 135 /** Draws a blank widget (temporary) */ 136 void drawBlank (wdim x, wdim y, wdim w, wdim h); 137 138 /** Draws a button frame, in if pushed == true. */ 139 void drawButton (wdim x, wdim y, wdim w, wdim h, bool pushed); 140 141 /** Toggle buttons. 142 * 143 * These have a fixed size which getToggleSize returns. */ 144 wdimPair getToggleSize (); 145 void drawToggle (wdim x, wdim y, bool state, bool pushed); /// ditto 146 147 /** Get a TextAdapter to draw some text. 148 * 149 * If no colour is passes, a default is used (white). */ 150 TextAdapter getAdapter (char[] text, int colour = 0xFFFFFF); 151 //END Methods 130 152 } mde/gui/renderer/SimpleRenderer.d
r95 r97 19 19 import mde.gui.renderer.IRenderer; 20 20 21 import gl = mde.gl.basic;21 import derelict.opengl.gl; 22 22 import mde.font.font; 23 23 … … 35 35 } 36 36 37 BorderDimensions setSizable (bool wS, bool hS) { 38 wSizable = wS; 39 hSizable = hS; 40 41 // Set the border size based on the above 37 alias Border.BTYPE BTYPE; 38 Border getBorder (BTYPE type, bool wS, bool hS) { 39 Border border; 42 40 with (border) { 43 l = r = t = b = 14; 41 if (type & BTYPE.RESIZE) { 42 if (wS) capability = RESIZE.X1 | RESIZE.X2; 43 if (hS) capability |= RESIZE.Y1 | RESIZE.Y2; 44 } 45 if (type & BTYPE.LARGE) { 46 y1 = 12; 47 y2 = 6; 48 } 49 else if (type & BTYPE.SMALL) 50 y1 = y2 = 4; 51 x1 = x2 = y2; 44 52 } 45 with (resize) {46 if (wSizable)47 l = r = 6;48 else49 l = r = 0;50 if (hSizable) {51 t = b = 6;52 } else53 t = b = 0;54 }55 border += resize;56 53 return border; 57 }58 59 RESIZE_TYPE getResizeType (wdim cx, wdim cy, wdim w, wdim h) {60 RESIZE_TYPE resizeType = RESIZE_TYPE.NONE;61 if (cx < resize.l || cx >= w - resize.r ||62 cy < resize.t || cy >= h - resize.b) { // window is being resized63 /* check for resizes (different to above; use whole border giving larger area for64 * diagonal resizes). */65 66 if (wSizable) {67 if (cx < border.l)68 resizeType = RESIZE_TYPE.L;69 else if (cx >= w - border.r)70 resizeType = RESIZE_TYPE.R;71 }72 if (hSizable) {73 if (cy < border.t)74 resizeType |= RESIZE_TYPE.T;75 else if (cy >= h - border.b)76 resizeType |= RESIZE_TYPE.B;77 }78 }79 return resizeType;80 54 } 81 55 … … 89 63 void relax () {} 90 64 91 void drawWindow ( wdim x, wdim y, wdim w, wdim h) {92 gl .setColor (0f, 0f, .7f);93 gl .drawBox (x,y, w,h);65 void drawWindow (Border* border, wdim x, wdim y, wdim w, wdim h) { 66 glColor3f (0f, 0f, .8f); 67 glRecti(x, y+h, x+w, y); 94 68 95 gl.setColor (0f, 0f, 1f); 96 gl.drawBox (x+resize.l, y+resize.t, w-resize.l-resize.r, h-resize.t-resize.b); 69 if (border.capability != 0) { 70 glColor3f (0f, 0f, .7f); 71 glBegin (GL_TRIANGLES); 72 wdim t = border.x1 + border.y1; 73 glVertex2i (x, y); 74 glVertex2i (x+t, y); 75 glVertex2i (x, y+t); 76 77 t = border.x2 + border.y1; 78 glVertex2i (x+w, y); 79 glVertex2i (x+w, y+t); 80 glVertex2i (x+w-t, y); 81 82 t = border.x2 + border.y2; 83 glVertex2i (x+w, y+h); 84 glVertex2i (x+w-t, y+h); 85 glVertex2i (x+w, y+h-t); 86 87 t = border.x1 + border.y2; 88 glVertex2i (x, y+h); 89 glVertex2i (x, y+h-t); 90 glVertex2i (x+t, y+h); 91 glEnd (); 92 } 97 93 98 gl .setColor (.3f, .3f, .3f);99 gl .drawBox (x+border.l, y+border.t, w-border.l-border.r, h-border.t-border.b);94 glColor3f (0f, 0f, 0f); 95 glRecti(x+border.x1, y+h-border.y2, x+w-border.x2, y+border.y1); 100 96 } 101 97 102 98 void drawWidgetBack (wdim x, wdim y, wdim w, wdim h) { 103 99 debug { 104 gl .setColor(0f, .2f, .2f);105 gl .drawBox (x,y, w,h);100 glColor3f (0f, .2f, .2f); 101 glRecti (x,y+h, x+w,y); 106 102 } 107 103 } 108 104 109 105 void drawBlank (wdim x, wdim y, wdim w, wdim h) { 110 gl .setColor(.4f, .4f, .4f);111 gl .drawBox (x,y, w,h);106 glColor3f (.4f, .4f, .4f); 107 glRecti(x, y+h, x+w, y); 112 108 } 113 109 114 110 void drawButton (wdim x, wdim y, wdim w, wdim h, bool pushed) { 115 111 if (pushed) 116 gl .setColor(1f, 0f, 1f);112 glColor3f (1f, 0f, 1f); 117 113 else 118 gl .setColor(.6f, 0f, .6f);119 gl .drawBox (x,y, w,h);114 glColor3f (.6f, 0f, .6f); 115 glRecti(x, y+h, x+w, y); 120 116 } 121 117 … … 129 125 float c = pushed ? .7f : .5f; 130 126 if (state) 131 gl .setColor(0f, c, 0f);127 glColor3f (0f, c, 0f); 132 128 else 133 gl .setColor(c, 0f, 0f);134 gl .drawBox (x+2,y+2, 12,12);129 glColor3f (c, 0f, 0f); 130 glRecti (x+2,y+14, x+14,y+2); 135 131 } 136 132 … … 143 139 144 140 protected: 145 bool wSizable, hSizable;146 BorderDimensions border;147 BorderDimensions resize;148 141 FontStyle defaultFont; 149 142 } mde/gui/widget/Floating.d
r96 r97 19 19 import mde.gui.widget.Widget; 20 20 import mde.gui.exception; 21 /+import mde.gui.widget.createWidget; 22 23 import mde.gui.IGui; 24 import mde.gui.renderer.createRenderer; 25 26 import mt = mde.mergetag.DataSet; 27 import mde.mergetag.parse.parseTo : parseTo; 28 import mde.mergetag.parse.parseFrom : parseFrom; 29 +/ 21 30 22 import tango.util.log.Log : Log, Logger; 31 23 … … 48 40 { 49 41 this (IWidgetManager mgr, widgetID id, WidgetData data) { 50 if (data.ints.length != 1 )42 if (data.ints.length != 1 + data.strings.length) 51 43 throw new WidgetDataException (this); 52 44 53 45 subWidgets.length = data.strings.length; // widgets created from string data 54 46 sWOrder.length = subWidgets.length; 47 sWData.length = subWidgets.length; 55 48 foreach (i,s; data.strings) { 56 49 subWidgets[i] = mgr.makeWidget (s); 57 50 sWOrder[i] = i; 58 } 59 60 sWCoords = mgr.dimData (id); 61 if (sWCoords.length != subWidgets.length * 2) { 62 // don't bother logging a warning; correct data will be saved anyway 63 sWCoords.length = subWidgets.length * 2; // maybe some data kept 51 sWData[i].borderType = cast(BTYPE) data.ints[i+1]; 52 } 53 54 wdim[] dd = mgr.dimData (id); 55 if (dd.length == subWidgets.length * 4) { 56 foreach (i, ref d; sWData) 57 (&d.x)[0..4] = dd[i*4..i*4+4]; 64 58 } 65 59 … … 67 61 } 68 62 63 void finalize () { 64 foreach (i, ref d; sWData) with (d) { 65 auto widg = subWidgets[i]; 66 d.border = mgr.renderer.getBorder (borderType, widg.isWSizable, widg.isHSizable); 67 mw = widg.minWidth + border.x1 + border.x2; 68 mh = widg.minHeight + border.y1 + border.y2; 69 if (w < mw) w = mw; 70 if (h < mh) h = mh; 71 widg.setWidth (w - border.x1 - border.x2, -1); 72 widg.setHeight (h - border.y1 - border.y2, -1); 73 } 74 } 75 69 76 bool saveChanges () { 70 foreach (widget; subWidgets) 71 widget.saveChanges (); 72 73 mgr.setDimData (id, sWCoords); // save positions 77 wdim[] dd = new wdim[sWData.length*4]; 78 foreach (i, ref d; sWData) { 79 subWidgets[i].saveChanges (); 80 dd[4*i..4*i+4] = (&d.x)[0..4]; 81 } 82 83 mgr.setDimData (id, dd); // save positions 74 84 return true; 75 85 } … … 78 88 w = nw; 79 89 // check all floating widgets are visible 80 foreach (i, widg; subWidgets) { 81 wdim d; 82 if (sWCoords[i] + (d = widg.width) > w) { 83 if (d > w) 84 sWCoords[i] = 0; 85 else 86 sWCoords[i] = w - d; 87 } 90 foreach (i, ref d; sWData) with (d) { 91 if (x + w > this.w) 92 x = (w > this.w) ? 0 : this.w - w; 88 93 } 89 94 } 90 95 void setHeight (wdim nh, int) { 91 96 h = nh; 92 foreach (i, widg; subWidgets) { 93 wdim d; 94 size_t n = i + subWidgets.length; 95 if (sWCoords[n] + (d = widg.height) > h) { 96 if (d > h) 97 sWCoords[n] = 0; 98 else 99 sWCoords[n] = h - d; 100 } 97 foreach (i, ref d; sWData) with (d) { 98 if (y + h > this.h) 99 y = (h > this.h) ? 0 : this.h - h; 101 100 } 102 101 } … … 105 104 bool isHSizable () { return true; } 106 105 107 void setPosition (wdim x, wdim y) { 108 super.setPosition (x,y); 106 void setPosition (wdim nx, wdim ny) { 107 x = nx; 108 y = ny; 109 109 110 110 size_t n = subWidgets.length; 111 foreach (i, widg; subWidgets)112 widg.setPosition (x+sWCoords[i], y+sWCoords[i+n]);111 foreach (i, ref d; sWData) 112 subWidgets[i].setPosition (x + d.x + d.border.x1, y + d.y + d.border.y1); 113 113 } 114 114 … … 117 117 118 118 mgr.renderer.restrict (x,y, w,h); 119 120 119 foreach (i; sWOrder) 121 subWidgets[i].draw; 120 with (sWData[i]) { 121 mgr.renderer.drawWindow (&border, this.x + x, this.y + y, w, h); 122 subWidgets[i].draw; 123 } 124 mgr.renderer.relax; 122 125 } 123 126 124 127 IChildWidget getWidget (wdim cx, wdim cy) { 128 debug scope (failure) 129 logger.warn ("getWidget: failure; values: click, pos, width - {}, {}, {} - {}, {}, {}", cx, x, w, cy, y, h); 125 130 debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)"); 126 131 127 size_t n = subWidgets.length; 128 foreach_reverse (j,i; sWOrder) { 129 wdim lx = cx - (x + sWCoords[i ]); 130 wdim ly = cy - (y + sWCoords[i+n]); 131 if (lx >= 0 && lx < subWidgets[i].width && 132 ly >= 0 && ly < subWidgets[i].height) 132 foreach_reverse (j,i; sWOrder) with (sWData[i]) { 133 wdim lx = cx - (this.x + x); 134 wdim ly = cy - (this.y + y); 135 if (lx >= 0 && lx < w && 136 ly >= 0 && ly < h) 133 137 { 134 138 sWOrder[j..$-1] = sWOrder[j+1..$].dup; 135 139 sWOrder[$-1] = i; 136 140 mgr.requestRedraw; 137 return subWidgets[i]; 138 } 139 } 141 if (lx >= border.x1 && lx < w-border.x2 && 142 ly >= border.y1 && ly < h-border.y2) 143 return subWidgets[i].getWidget (cx,cy); 144 event = i; 145 return this; 146 } 147 } 148 event = size_t.max; 140 149 return this; // no match 141 150 } 142 151 152 void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { 153 if (event > subWidgets.length) return; 154 if (b == 1 && state == true) { 155 active = event; 156 with (sWData[active]) { 157 resizeType = border.getResize (cx - this.x - x, cy - this.y - y, w,h); 158 alias border.RESIZE RESIZE; 159 160 if (resizeType != RESIZE.NONE) { 161 // Set x/yDrag (unfortunately these need to be different for each edge) 162 if (resizeType & RESIZE.X1) 163 xDrag = w + cx; 164 else if (resizeType & RESIZE.X2) 165 xDrag = w - cx; 166 167 if (resizeType & RESIZE.Y1) 168 yDrag = h + cy; 169 else if (resizeType & RESIZE.Y2) 170 yDrag = h - cy; 171 172 mgr.addClickCallback (&endCallback); 173 mgr.addMotionCallback (&resizeCallback); 174 } else if (borderType & BTYPE.MOVE) { // window is being moved 175 xDrag = cx - x; 176 yDrag = cy - y; 177 178 mgr.addClickCallback (&endCallback); 179 mgr.addMotionCallback (&moveCallback); 180 } 181 } 182 } 183 } 184 143 185 protected: 144 wdim[] sWCoords; // coords for subwidgets, relative to this widget: [x1,x2,...,y1,y2,...] 186 void moveCallback (wdabs cx, wdabs cy) { 187 with (sWData[active]) { 188 x = cx-xDrag; 189 y = cy-yDrag; 190 191 if (x < 0) 192 x = 0; 193 else if (x + w > this.w) 194 x = (w > this.w) ? 0 : this.w - w; 195 if (y < 0) 196 y = 0; 197 else if (y + h > this.h) 198 y = (h > this.h) ? 0 : this.h - h; 199 200 subWidgets[active].setPosition (this.x + x + border.x1, this.y + y + border.y1); 201 } 202 mgr.requestRedraw; 203 } 204 void resizeCallback (wdabs cx, wdabs cy) { 205 with (sWData[active]) { 206 if (resizeType & RESIZE.X1) { 207 wdim ow = w; 208 w = xDrag - cx; 209 if (w < mw) w = mw; 210 x += ow - w; 211 subWidgets[active].setWidth (w - border.x1 - border.x2, 1); 212 } 213 else if (resizeType & RESIZE.X2) { 214 w = xDrag + cx; 215 if (w < mw) w = mw; 216 subWidgets[active].setWidth (w - border.x1 - border.x2, -1); 217 } 218 if (resizeType & RESIZE.Y1) { 219 wdim oh = h; 220 h = yDrag - cy; 221 if (h < mh) h = mh; 222 y += oh - h; 223 subWidgets[active].setHeight (h - border.y1 - border.y2, 1); 224 } 225 else if (resizeType & RESIZE.Y2) { 226 h = yDrag + cy; 227 if (h < mh) h = mh; 228 subWidgets[active].setHeight (h - border.y1 - border.y2, -1); 229 } 230 // Reposition widget and sub-widgets: 231 subWidgets[active].setPosition (this.x + x + border.x1, this.y + y + border.y1); 232 } 233 mgr.requestRedraw; 234 } 235 bool endCallback (wdabs, wdabs, ubyte b, bool state) { 236 if (b == 1 && state == false) { // end of a move/resize 237 mgr.removeCallbacks (cast(void*) this); 238 return true; // we've handled the up-click 239 } 240 return false; // we haven't handled it 241 } 242 243 struct SWData { // NOTE: x,y,w,h must be first elements; search (&d.x) 244 wdim x,y; // position (corner of border) 245 wdim w,h; // size (including border) 246 wdim mw,mh; 247 BTYPE borderType; // what type of border to put around the widget 248 IRenderer.Border border; 249 } 250 static assert (SWData.alignof == 4); // assumptions for optimization; search (&d.x) 251 SWData[] sWData; 145 252 size_t[] sWOrder; // indexes for draw order (top widget at end of list) 146 253 147 /+ 148 /** Call after loading is finished to setup the window and confirm that it's valid. 149 * 150 * Throws: WindowLoadException (or possibly other exceptions). Do not use the instance if an 151 * exception occurs! */ 152 void finalise (IGui gui) 153 in { 154 assert (gui !is null, "Window.finalise ("~name~"): gui is null"); 155 } body { 156 // Check data was loaded: 157 if (widgetData is null) 158 throw new WindowLoadException ("No widget creation data"); 159 160 // Save gui and create the renderer: 161 gui_ = gui; 162 rend = createRenderer (gui.rendererName); 163 164 // Create the primary widget (and indirectly all sub-widgets), throwing on error: 165 // Note: GridLayoutWidget's this relies on rend.layoutSpacing. 166 widget = makeWidget (0); // primary widget always has ID 0. 167 // This data is no longer needed by Window, although its sub-arrays may still be used, so 168 // let the GC collect what it can: 169 widgetData = null; 170 widgetStrings = null; 171 172 // get border sizes: 173 border = rend.setSizable (isWSizable, isHSizable); // depends on widget 174 175 // Note: this should return an empty array, but we shouldn't make a fuss if it's not empty: 176 if ((widget.adjust (mutableData)).length != 0) // adjust/set size, etc., depends on rend 177 logger.warn ("Local widget position data is invalid!"); 178 mutableData = null; // no longer needed 179 180 widget.getCurrentSize (w,h); // and get this size 181 w += border.l + border.r; // Adjust for border 182 h += border.t + border.b; 183 184 widgetX = x + border.l; // widget position 185 widgetY = y + border.t; // must be updated if the window is moved 186 widget.setPosition (widgetX, widgetY); 187 188 // Calculate mw/mh and xw/yh (cached data): 189 mw = widget.minWidth + border.l + border.r; 190 mh = widget.minHeight + border.t + border.b; 191 192 xw = x+w; 193 yh = y+h; 194 } 195 //END Methods for GUI 196 197 //BEGIN IWindow methods 198 /** Get/create a widget by ID. 199 * 200 * Should $(I only) be called internally and by sub-widgets! */ 201 IWidget makeWidget (widgetID i) 202 in { 203 // widgetData is normally left to be garbage collected after widgets have been created: 204 assert (widgetData !is null, "Window.makeWidget ("~name~"): widgetData is null"); 205 } body { 206 /* Each widget returned should be a unique object; if multiple widgets are requested with 207 * the same ID, a new widget is created each time. */ 208 209 int[]* d = i in widgetData; 210 if (d is null) 211 throw new WindowLoadException ("Window.makeWidget ("~name~"): Widget not found"); 212 213 // Throws WidgetDataException (a WindowLoadException) if bad data: 214 return createWidget (this, *d); 215 } 216 217 char[] getWidgetString (int i) 218 in { 219 // widgetStrings is freed at same time as widgetData 220 // but widgetData is guaranteed to be read 221 assert (widgetData !is null, "getWidgetString called after widget creation finished"); 222 } body { 223 char[]* p; 224 if (widgetStrings is null || 225 (p = i in widgetStrings) is null ) 226 throw new WindowLoadException ("Needed widgetStrings not set for Window"); 227 228 return *p; 229 } 230 231 /** Add this widget's data to that to be saved, returning it's widgetID. */ 232 widgetID addCreationData (IWidget widget) 233 { 234 widgetID i; 235 if (widgetData is null) 236 i = 0; 237 else 238 i = widgetData.keys[$-1] + 1; 239 240 /+ Doesn't this have no effect except when getCreationData throws, in which case the data 241 + isn't used anyway? I'm sure it was added for a reason... FIXME and below 242 widgetData[i] = null; // Make sure the same ID doesn't get used by a recursive call 243 +/ 244 widgetData[i] = widget.getCreationData; 245 246 return i; 247 } 248 249 int addWidgetString (char[] str) 250 { 251 int i; 252 if (widgetStrings is null) 253 i = 0; 254 else 255 i = widgetStrings.keys[$-1] + 1; 256 257 /+ See above. FIXME 258 widgetStrings[i] = null; // Make sure the same ID doesn't get used by a recursive call 259 +/ 260 widgetStrings[i] = str; 261 262 return i; 263 } 264 265 IGui gui () { 266 return gui_; 267 } 268 269 void requestRedraw () { 270 gui_.requestRedraw; 271 } 272 273 IRenderer renderer () 274 in { 275 assert (rend !is null, "Window.renderer: rend is null"); 276 } body { 277 return rend; 278 } 279 //END IWindow methods 280 281 //BEGIN IWidget methods 282 //FIXME: how many of these methods are actually needed/used? 283 int[] adjust (int[]) { // simply not relevant (never used) 284 return []; 285 } 286 int[] getCreationData () { // simply not relevant (never used) 287 return []; 288 } 289 int[] getMutableData () { // simply not relevant (never used) 290 return []; 291 } 292 293 bool isWSizable () { 294 return widget.isWSizable; 295 } 296 bool isHSizable () { 297 return widget.isHSizable; 298 } 299 300 wdim minWidth () { 301 return mw; 302 } 303 wdim minHeight () { 304 return mh; 305 } 306 void getCurrentSize (out wdim cw, out wdim ch) { 307 cw = w; 308 ch = h; 309 } 310 311 void setWidth (wdim nw, int dir) { 312 if (nw < mw) w = mw; // clamp 313 else w = nw; 314 xw = x + w; 315 widget.setWidth (w - border.l - border.r, dir); 316 } 317 void setHeight (wdim nh, int dir) { 318 if (nh < mh) h = mh; // clamp 319 else h = nh; 320 yh = y + h; 321 widget.setHeight (h - border.t - border.b, dir); 322 } 323 324 void setPosition (wdim nx, wdim ny) { 325 x = nx; 326 y = ny; 327 328 xw = x+w; 329 yh = y+h; 330 331 widgetX = x + border.l; 332 widgetY = y + border.t; 333 334 widget.setPosition (widgetX, widgetY); 335 336 gui_.requestRedraw (); // necessary whenever the window is moved; setPosition is called after resizes and moves 337 } 338 339 IWidget getWidget (wdim cx, wdim cy) { 340 if (cx < x || cx >= xw || cy < y || cy >= yh) // not over window 341 return null; 342 if (cx >= widgetX && cx < xw-border.r && cy >= widgetY && cy < yh-border.b) 343 // over the widget 344 return widget.getWidget (cx, cy); 345 else // over the window border 346 return this; 347 } 348 void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { 349 if (b == 1 && state == true) { 350 resizeType = rend.getResizeType (cx-x, cy-y, w,h); 351 352 if (resizeType != RESIZE_TYPE.NONE) { // Some type of resize 353 // Set x/yDrag (unfortunately these need to be different for each edge) 354 if (resizeType & RESIZE_TYPE.L) 355 xDrag = w + cx; 356 else if (resizeType & RESIZE_TYPE.R) 357 xDrag = w - cx; 358 359 if (resizeType & RESIZE_TYPE.T) 360 yDrag = h + cy; 361 else if (resizeType & RESIZE_TYPE.B) 362 yDrag = h - cy; 363 364 // Add the callbacks (they use resizeType which has already been set) 365 gui_.addClickCallback (&endCallback); 366 gui_.addMotionCallback (&resizeCallback); 367 } else { // window is being moved 368 xDrag = cx - x; 369 yDrag = cy - y; 370 371 gui_.addClickCallback (&endCallback); 372 gui_.addMotionCallback (&moveCallback); 373 } 374 } 375 } 376 377 void draw () { 378 // background 379 rend.drawWindow (x,y, w,h); 380 381 // Tell the widget to draw itself: 382 widget.draw(); 383 } 384 //END IWidget methods 385 386 private: 387 alias IRenderer.BorderDimensions BorderDimensions; 388 alias IRenderer.RESIZE_TYPE RESIZE_TYPE; 389 390 //BEGIN Window moving and resizing 391 void moveCallback (wdabs cx, wdabs cy) { 392 setPosition (cx-xDrag, cy-yDrag); 393 } 394 void resizeCallback (wdabs cx, wdabs cy) { 395 debug scope(failure) 396 logger.trace ("resizeCallback: failure"); 397 398 // This function is only called if some resize is going to happen. 399 // x,y are used as parameters to setPosition as well as being affected by it; somewhat 400 // pointless but fairly effective. 401 402 if (resizeType & RESIZE_TYPE.L) { 403 wdim xSize = xDrag - cx; 404 if (xSize < mw) xSize = mw; // clamp 405 x += w - xSize; 406 setWidth (xSize, 1); 407 } 408 else if (resizeType & RESIZE_TYPE.R) { 409 setWidth (xDrag + cx, -1); 410 } 411 if (resizeType & RESIZE_TYPE.T) { 412 wdim ySize = yDrag - cy; 413 if (ySize < mh) ySize = mh; 414 y += h - ySize; 415 setHeight (ySize, 1); 416 } 417 else if (resizeType & RESIZE_TYPE.B) { 418 setHeight (yDrag + cy, -1); 419 } 420 421 // Moves lower (x,y) coordinate if necessary and repositions any sub-widgets moved by the 422 // resizing: 423 setPosition (x, y); 424 } 425 bool endCallback (wdabs cx, wdabs cy, ubyte b, bool state) { 426 if (b == 1 && state == false) { 427 // The mouse shouldn't have moved since the motion callback 428 // was last called, so there's nothing else to do now. 429 gui_.removeCallbacks (cast(void*) this); 430 431 return true; // we've handled the up-click 432 } 433 &
