Changeset 45:0fd51d2c6c8a
- Timestamp:
- 05/22/08 06:34:09 (8 months ago)
- Files:
-
- codeDoc/jobs.txt (modified) (3 diffs)
- data/conf/gui.mtt (modified) (1 diff)
- mde/gl/basic.d (modified) (2 diffs)
- mde/gl/draw.d (modified) (2 diffs)
- mde/gl/tex2d.d (added)
- mde/gui/Gui.d (modified) (2 diffs)
- mde/gui/widget/Ifaces.d (modified) (3 diffs)
- mde/gui/widget/Widget.d (modified) (7 diffs)
- mde/gui/widget/Window.d (modified) (3 diffs)
- mde/gui/widget/createWidget.d (modified) (1 diff)
- mde/gui/widget/layout.d (modified) (12 diffs)
- mde/gui/widget/miscWidgets.d (added)
- mde/mde.d (modified) (1 diff)
- mde/resource/font.d (modified) (4 diffs)
- mde/scheduler/Init.d (modified) (4 diffs)
- mde/sdl.d (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
codeDoc/jobs.txt
r44 r45 5 5 In progress: 6 6 Implementing font rendering 7 Reading ft tutorial 8 Use cartesian coordinates? Or not? 9 Make layout's adjustCellSizes method call setSize? 10 More resizing stuff... 7 11 8 12 … … 10 14 To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): 11 15 Also see todo.txt and FIXME/NOTE comment marks. 16 5 mergetag crashes with no ending > on a tag and doesn't add header data when comments are included! 12 17 4 Not guaranteed to catch up-click ending callback! Appears not to be a problem... 13 18 4 OutOfMemoryException is not currently checked for â it should be at least in critical places (use high-level catching of all errors?). … … 52 57 53 58 Done (for git log message): 59 Moved the implementable widgets from mde.gui.widget.Widget to miscWidgets, leaving base widgets in Widget. 60 Rewrote some of GridLayoutWidget's implementation. Made many operations general to work for either columns or rows. Some optimisations were intended but ended up being removed due to problems. 61 Allowed layout's to resize from either direction (only with window resizes). data/conf/gui.mtt
r44 r45 9 9 <int|y=200> 10 10 <int[][int]|widgetData=[0:[0xB004,5,5,2,1,2,1,2,1,1,1,1,1,2,1,2,1,2,1,1,1,1,1,2,1,2,1,2],1:[0x3001],2:[0x2]]> 11 {WEmbedded} 12 <int|x=20> 13 <int|y=100> 14 <int[][int]|widgetData=[1:[0x4010,50,50],2:[0xB004,3,1,3,1,3],3:[0x3001],0:[0xB004,3,1,3,1,3]]> mde/gl/basic.d
r44 r45 25 25 //BEGIN GL & window setup 26 26 void glSetup () { 27 glDisable(GL_DEPTH_TEST); 28 glEnable (GL_TEXTURE_2D); 29 27 30 glClearColor (0.0f, 0.0f, 0.0f, 0.0f); 28 glDisable(GL_DEPTH_TEST);29 31 30 32 glMatrixMode(GL_MODELVIEW); … … 39 41 40 42 // Make the top-left the origin (see gui/GUI notes.txt): 43 // Note that this only affects vertex operations â direct rasterisation operations are 44 // unaffected! 41 45 glOrtho (0.0,w, h,0.0, -1.0, 1.0); 42 //glOrtho (0.0,1.0,0.0,1.0,-1.0,1.0);43 46 44 47 glMatrixMode(GL_MODELVIEW); mde/gl/draw.d
r32 r45 25 25 26 26 import tango.time.Time; // TimeSpan (type only; unused) 27 import tango.util.log.Log : Log, Logger; 28 29 private Logger logger; 30 static this () { 31 logger = Log.getLogger ("mde.gl.draw"); 32 } 27 33 28 34 //BEGIN Drawing loop … … 33 39 gui.draw (); 34 40 35 glFlush(); 41 GLenum err = glGetError(); 42 if (err != GL_NO_ERROR) { 43 char[128] tmp; 44 logger.error (logger.format (tmp, "GL error: {}", err)); 45 } 46 47 glFinish(); // Use Finish rather than Flush to make sure gl is ready to swap buffers 36 48 SDL_GL_SwapBuffers(); 37 49 } mde/gui/Gui.d
r43 r45 64 64 65 65 IReader reader; 66 try { 67 reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); 68 reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { 69 return new Window (id); 70 }; 71 reader.read; 72 } catch (mt.MTException e) { 73 logger.error ("Loading GUI aborted:"); 74 logger.error (e.msg); 75 76 return; 77 } 66 reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); 67 reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { 68 return new Window (id); 69 }; 70 reader.read; 78 71 79 72 // Get the renderer 80 73 char[]* p = RENDERER in reader.dataset.header.Arg!(char[]).Arg; 81 74 if (p is null || *p is null) { 82 logger.error ("Loading GUI aborted: no renderer specified"); 83 return; 84 } 85 rendName = *p; 75 logger.warn ("no renderer specified: defaulting to Simple"); 76 rendName = "Simple"; 77 } 78 else 79 rendName = *p; 86 80 87 81 // get list … … 95 89 } 96 90 try { 97 w.finalise (this); 98 windows ~= w; // only add if load successful91 w.finalise (this); // if this fails, the window (but nothing else) won't work 92 windows ~= w; // only add if load successful 99 93 } catch (Exception e) { 100 94 logger.error ("Window failed to load: " ~ e.msg); mde/gui/widget/Ifaces.d
r41 r45 111 111 bool isHSizable (); /// ditto 112 112 113 /** Calculate the minimal size the widget could be shrunk to , taking into account114 * child-widgets. */113 /** Calculate the minimal size the widget could be shrunk to (or its fixed size), taking into 114 * account child-widgets or other contents. */ 115 115 void getMinimalSize (out int w, out int h); 116 116 … … 120 120 /** Used to adjust the size. 121 121 * 122 * setPosition should always be called after setSize (for layout widgets). Adding this 123 * restriction appears to be the most efficient approach without a lot more tests. 122 * w,h is the new size. The boolean parameters describe which direction to resize from and is 123 * only really relevent to layout widgets (see GridLayoutWidget's implementation). When 124 * calling, just past true,true if it doesn't matter. 124 125 * 125 126 * Implementation: … … 127 128 * than that given by the parameters. Conversely, the size should not be reduced to the 128 129 * widget's maximal size (if any) but expanded as necessary (alignment to be implemented). 130 * This should be true for both resizable and fixed widgets; fixed widgets may still be scaled 131 * to fill a whole row/column in a layout widget. 129 132 * 130 133 * If the actual size is needed, call getCurrentSize afterwards. */ 131 void setSize (int w, int h );134 void setSize (int w, int h, bool, bool); 132 135 133 136 /** Set the current position (i.e. called on init and move). */ mde/gui/widget/Widget.d
r44 r45 14 14 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 15 16 /// GUI Widget module. 16 /** GUI Widget module. 17 * 18 * This module contains some base widget classes suitable for widget classes to inherit. However, 19 * inheriting one of them is by no means necessary for a widget so long as the IWidget interface is 20 * implemented. */ 17 21 module mde.gui.widget.Widget; 18 22 19 23 public import mde.gui.widget.Ifaces; 20 import mde.gui.exception;21 24 import mde.gui.renderer.IRenderer; 22 23 import mde.resource.font;24 25 import tango.io.Stdout;26 25 27 26 /** An abstract base widget class. … … 32 31 abstract class Widget : IWidget 33 32 { 34 // Base this(); all widgets must at least check data.length is correct. 33 //BEGIN Load and save 34 // Base this(). All widgets must check data.length is correct before calling this method. 35 35 this (IWindow wind, int[] data) { 36 36 window = wind; … … 41 41 // still need to set their size. 42 42 int[] adjust (int[] data) { 43 setSize (0,0 );43 setSize (0,0,true,true); 44 44 return data; 45 45 } 46 46 47 bool isWSizable () { return false; } 48 bool isHSizable () { return false; } 49 50 // Widget type should always be the first value. 47 // Widget type should always be the first value. Any widget using extra creation data will need 48 // to reimplemnt this method. 51 49 int[] getCreationData () { 52 50 return [widgetType]; … … 56 54 return []; 57 55 } 56 //END Load and save 57 58 //BEGIN Size and position 59 bool isWSizable () { return false; } 60 bool isHSizable () { return false; } 61 62 /* Return minimal/fixed size. */ 63 void getMinimalSize (out int a, out int b) { 64 a = mw; 65 b = mh; 66 } 58 67 59 68 void getCurrentSize (out int cw, out int ch) { … … 62 71 } 63 72 73 /* Set size: minimal size is (mw,mh). Note that both resizable and fixed widgets should allow 74 * enlarging, so in both cases this is a correct implementation. */ 75 void setSize (int nw, int nh, bool, bool) { 76 w = (nw >= mw ? nw : mw); 77 h = (nh >= mh ? nh : mh); 78 } 79 64 80 void setPosition (int nx, int ny) { 65 81 x = nx; 66 82 y = ny; 67 83 } 84 //END Size and position 68 85 69 /* Return self, since we don't have child widgets and the method wouldn't have been called 70 * unless the location was over us. Valid for all widgets without children. */ 86 //BEGIN Events 87 /* This method is only called when the location is over this widget; hence for all widgets 88 * without children this method is valid. */ 71 89 IWidget getWidget (int,int) { 72 90 return this; 73 91 } 74 92 75 /* Dummy event method ( widget doesn't respond to events)*/93 /* Dummy event method (suitable for all widgets which don't respond to events). */ 76 94 void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {} 95 //END Events 77 96 78 /* Basic draw method: draw the background (all widgets should do this) */97 /* Basic draw method: draw the background (all widgets should do this). */ 79 98 void draw () { 80 99 window.renderer.drawWidgetBack (x,y, w,h); … … 82 101 83 102 protected: 84 final int widgetType; // the type (stored for saving) 85 IWindow window; // the enclosing window 86 int x, y; // position 87 int w, h; // size 103 final int widgetType; // the type (stored for saving) 104 IWindow window; // the enclosing window 105 int x, y; // position 106 int w, h; // size 107 int mw = 0, mh = 0; // minimal or fixed size, depending on whether the widget is 108 // resizible; both types of widgets should actually be expandable. 88 109 } 89 /** A base for fixed-size widgets. */ 110 111 /** A base for fixed-size widgets taking their size from the creation data. */ 90 112 class FixedWidget : Widget { 113 // Check data.length is at least 3 before calling! 91 114 this (IWindow wind, int[] data) { 92 w = wF;93 h = hF;115 mw = data[1]; 116 mh = data[2]; 94 117 super (wind, data); 118 w = mw; 119 h = mh; 95 120 } 96 121 97 122 int[] getCreationData () { 98 return [widgetType, wF, hF];123 return [widgetType, mw, mh]; 99 124 } 100 101 /* Not resizable, so return current size. */102 void getMinimalSize (out int mw, out int mh) {103 mw = wF;104 mh = hF;105 }106 107 /* Ignore: a fixed size widget. */108 void setSize (int nw, int nh) {109 w = (nw >= wF ? nw : wF);110 h = (nh >= hF ? nh : hF);111 }112 113 protected:114 int wF, hF; // The "fixed" size, i.e. the preferred & minimal size115 125 } 116 126 /** A base for resizable widgets. */ 117 127 class SizableWidget : Widget { 128 // Check data.length is at least 1 before calling! 118 129 this (IWindow wind, int[] data) { 119 130 super (wind, data); … … 122 133 bool isWSizable () { return true; } 123 134 bool isHSizable () { return true; } 124 125 /* Return zero. */126 void getMinimalSize (out int mw, out int mh) {}127 128 /* Set size: a fully resizable widget. */129 void setSize (int nw, int nh) {130 w = (nw >= 0 ? nw : 0);131 h = (nh >= 0 ? nh : 0);132 }133 134 void draw () {135 super.draw;136 137 window.renderer.drawBlank (x,y, w,h);138 }139 135 } 140 141 //BEGIN Widgets142 /// A fixed-size blank widget.143 class FixedBlankWidget : FixedWidget144 {145 this (IWindow wind, int[] data) {146 if (data.length != 3) throw new WidgetDataException;147 wF = data[1];148 hF = data[2];149 super (wind, data);150 }151 void draw () {152 super.draw;153 154 window.renderer.drawBlank (x,y, w,h);155 }156 }157 158 /// A completely resizable blank widget (initial size zero).159 class SizableBlankWidget : SizableWidget160 {161 this (IWindow wind, int[] data) {162 if (data.length != 1) throw new WidgetDataException;163 super (wind, data);164 }165 }166 167 /// First interactible widget168 class ButtonWidget : FixedWidget169 {170 bool pushed = false; // true if button is pushed in (visually)171 // pushed is not the same as the button being clicked but not yet released.172 // it is whether the mouse is over the button after being clicked.173 174 this (IWindow wind, int[] data) {175 if (data.length != 3) throw new WidgetDataException;176 wF = data[1];177 hF = data[2];178 super (wind, data);179 }180 181 void draw () {182 window.renderer.drawButton (x,y, w,h, pushed);183 }184 185 void clickEvent (ushort, ushort, ubyte b, bool state) {186 if (b == 1 && state == true) {187 pushed = true;188 window.requestRedraw;189 window.gui.addClickCallback (&clickWhileHeld);190 window.gui.addMotionCallback (&motionWhileHeld);191 }192 }193 // Called when a mouse motion/click event occurs while (held == true)194 bool clickWhileHeld (ushort cx, ushort cy, ubyte b, bool state) {195 if (b == 1 && state == false) {196 if (cx >= x && cx < x+w && cy >= y && cy < y+h) // button event197 Stdout ("Button clicked!").newline;198 199 pushed = false;200 window.requestRedraw;201 window.gui.removeCallbacks (cast(void*) this);202 203 return true;204 }205 return false;206 }207 void motionWhileHeld (ushort cx, ushort cy) {208 bool oldPushed = pushed;209 if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true;210 else pushed = false;211 if (oldPushed != pushed)212 window.requestRedraw;213 }214 }215 216 /// Basic text widget217 class TextWidget : FixedWidget218 {219 this (IWindow wind, int[] data) {220 if (data.length != 1) throw new WidgetDataException;221 wF = 100; //FIXME: set properly222 hF = 25;223 super (wind,data);224 }225 226 void draw () {227 super.draw();228 if (font is null) font = Font.get("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf");229 font.drawStr (x,y, "Text Widget");230 }231 232 protected:233 static Font font;234 }235 //END Widgetsmde/gui/widget/Window.d
r41 r45 220 220 } 221 221 222 void setSize (int nw, int nh ) {222 void setSize (int nw, int nh, bool wB, bool hB) { 223 223 getMinimalSize (w,h); 224 224 if (nw > w) w = nw; // expand if new size is larger, but don't go smaller … … 228 228 yh = y+h; 229 229 230 widget.setSize (w - border.l - border.r, h - border.t - border.b );230 widget.setSize (w - border.l - border.r, h - border.t - border.b, wB, hB); 231 231 232 232 gui_.requestRedraw (); // obviously necessary whenever the window's size is changed … … 304 304 } 305 305 void resizeCallback (ushort cx, ushort cy) { 306 debug scope(failure) 307 logger.trace ("resizeCallback: failure"); 308 309 // This function is only called if some resize is going to happen. 310 // To improve efficiency, store parameters to resize to. 311 int xSize = w, ySize = h; // new size 312 int xDiff, yDiff; // difference to new position 313 bool xHigh, yHigh; // resize from positive side 314 306 315 if (resizeType & RESIZE_TYPE.L) { 307 int mw, nw; 308 getMinimalSize (mw, nw); // (only want mw) 309 nw = xDrag - cx; 310 if (nw < mw) nw = mw; // clamp 311 mw = x + w - nw; // reuse 312 setSize (nw, h); 313 setPosition (mw, y); 316 getMinimalSize (xDiff, xSize); // (only want xDiff, temporarily used as mw) 317 xSize = xDrag - cx; 318 if (xSize < xDiff) xSize = xDiff; // clamp 319 xDiff = w - xSize; // now used as amount to move 314 320 } 315 321 else if (resizeType & RESIZE_TYPE.R) { 316 setSize (xDrag + cx, h);317 setPosition (x, y); // required to call after setSize.322 xSize = xDrag + cx; 323 xHigh = true; 318 324 } 319 325 if (resizeType & RESIZE_TYPE.T) { 320 int mh, nh; 321 getMinimalSize (nh, mh); 322 nh = yDrag - cy; 323 if (nh < mh) nh = mh; 324 mh = y + h - nh; 325 setSize (w, nh); 326 setPosition (x, mh); 326 getMinimalSize (ySize, yDiff); 327 ySize = yDrag - cy; 328 if (ySize < yDiff) ySize = yDiff; 329 yDiff = h - ySize; 327 330 } 328 331 else if (resizeType & RESIZE_TYPE.B) { 329 setSize (w, yDrag + cy); 330 setPosition (x, y); 331 } 332 ySize = yDrag + cy; 333 yHigh = true; 334 } 335 336 setSize (xSize, ySize, xHigh, yHigh); 337 if (xDiff != 0 || yDiff != 0) 338 setPosition (x + xDiff, y + yDiff); 332 339 } 333 340 bool endCallback (ushort cx, ushort cy, ubyte b, bool state) { mde/gui/widget/createWidget.d
r44 r45 17 17 module mde.gui.widget.createWidget; 18 18 19 import mde.gui.widget.Ifaces; 20 import mde.gui.exception : WidgetDataException; 21 19 22 // Widgets to create: 20 23 import mde.gui.widget.layout; 21 import mde.gui.widget.Widget; 22 23 import mde.gui.exception : WidgetDataException; 24 import mde.gui.widget.miscWidgets; 24 25 25 26 /** Create a widget of type data[0] (see enum WIDGET_TYPES) for _window window, with initialisation mde/gui/widget/layout.d
r43 r45 20 20 import mde.gui.exception; 21 21 22 import tango.io.Stdout;23 22 debug { 24 23 import tango.util.log.Log : Log, Logger; … … 48 47 cols = data[2]; 49 48 if (data.length != 3 + rows * cols) throw new WidgetDataException; 50 /* data.length >= 3so besides checking the length is correct, this tells us:49 /* data.length >= 4 so besides checking the length is correct, this tells us: 51 50 * rows * cols >= 4 - 3 = 1 a free check! 52 51 * The only thing not checked is whether both rows and cols are negative, which would 53 * cause an exception when dynamic arrays are allocated later (and is unlikely). */ 52 * cause an exception when dynamic arrays are allocated by genCachedConstructionData, which 53 * is an acceptible method of failure (and is unlikely anyway). */ 54 54 55 55 // Get all sub-widgets … … 75 75 int lenUsed = 0; 76 76 if (data.length < rows + cols) { // data error; use defaults 77 col W = colWMin.dup;78 row H = rowHMin.dup;77 col.dupMin; 78 row.dupMin; 79 79 } else { // sufficient data 80 80 lenUsed = rows+cols; 81 colW = data[0..cols]; 82 rowH = data[cols..lenUsed]; 83 84 // Check row sizes are valid: 85 //NOTE: this could be made optional 86 //NOTE: could also check non-resizable sizes are not too large 87 foreach (i, ref w; colW) 88 if (w < colWMin[i]) w = colWMin[i]; 89 foreach (i, ref h; rowH) 90 if (h < rowHMin[i]) h = rowHMin[i]; 91 } 92 93 genCachedMutableData; 94 w = colW[$-1] + colX[$-1]; 95 h = rowY[$-1] + rowH[$-1]; 81 col.setCheck (data[0..cols]); 82 row.setCheck (data[cols..lenUsed]); 83 } 84 85 86 // Generate cached mutable data 87 // Calculate column and row locations: 88 w = col.genPositions; 89 h = row.genPositions; 90 91 // Tell subwidgets their new sizes. Positions are given by a later call to setPosition. 92 foreach (i,widget; subWidgets) 93 // Resizing direction is arbitrarily set to "high direction": 94 widget.setSize (col.width[i % cols], row.width[i / cols], true, true); 96 95 97 96 return data[lenUsed..$]; … … 114 113 ret ~= widget.getMutableData; 115 114 116 ret ~= col W ~ rowH;115 ret ~= col.width ~ row.width; 117 116 return ret; 118 117 } 119 118 120 119 bool isWSizable () { 121 return (sizableCols.length != 0); 122 } 123 120 return col.firstSizable >= 0; 121 } 124 122 bool isHSizable () { 125 return (sizableRows.length != 0);123 return row.firstSizable >= 0; 126 124 } 127 125 … … 132 130 } 133 131 134 void setSize (int nw, int nh) { 135 // Step 1: calculate the row/column sizes. 136 w += adjustCellSizes (colW, colWMin, sizableCols, nw - w, true); 137 h += adjustCellSizes (rowH, rowHMin, sizableRows, nh - h, true); 138 139 // Step 2: calculate the row/column offsets (positions) and set the sub-widget's sizes. 140 genCachedMutableData; 141 142 // Step 3: position needs to be set 143 // Currently this happens by specifying that setPosition should be run after setSize. 132 void setSize (int nw, int nh, bool wHigh, bool hHigh) { 133 debug scope (failure) { 134 char[128] tmp; 135 logger.trace ("setSize failed: hHigh = " ~ (hHigh ? "true" : "false")); 136 logger.trace (logger.format (tmp, "rows to resize: {}, {}", row.firstSizable, row.lastSizable)); 137 } 138 // Optimisation (could easily be called with same sizes if a parent layout widget is 139 // resized, since many columns/rows may not be resized). 140 if (nw == w && nh == h) return; 141 142 // calculate the row/column sizes (and new positions) 143 if (wHigh) 144 w += col.adjustCellSizes (nw - w, col.lastSizable, -1); 145 else 146 w += col.adjustCellSizes (nw - w, col.firstSizable, 1); 147 if (hHigh) 148 h += row.adjustCellSizes (nh - h, row.lastSizable, -1); 149 else 150 h += row.adjustCellSizes (nh - h, row.firstSizable, 1); 151 152 // set the sub-widget's sizes & positions 153 setSubWidgetSP (wHigh, hHigh); 144 154 } 145 155 … … 149 159 150 160 foreach (i,widget; subWidgets) 151 widget.setPosition (x + col X[i % cols], y + rowY[i / cols]);161 widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]); 152 162 } 153 163 … … 155 165 // Find the relevant widget. 156 166 IWidget getWidget (int cx, int cy) { 157 int lx = cx - x, ly = cy - y; // use coords relative to this widget 158 159 // Find the column 160 int i = cols - 1; // starting from right... 161 while (lx < colX[i]) { // decrement while left of this column 162 debug assert (i > 0, "getWidget: left of first column"); // should be impossible 163 --i; 164 } // now (lx >= colX[i]) 165 if (lx >= colX[i] + colW[i]) return this; // between columns 166 167 // Find the row; 168 int j = rows - 1; 169 while (ly < rowY[j]) { 170 debug assert (j > 0, "getWidget: above first row"); // should be impossible 171 --j; 172 } 173 if (ly >= rowY[j] + rowH[j]) return this; 174 175 // Now we know it's in widget (i,j)'s cell (but the widget may not take up the whole cell) 176 lx -= colX[i]; 177 ly -= rowY[j]; 178 IWidget widg = subWidgets[i + j*cols]; 179 widg.getCurrentSize (i,j); 180 if (lx < i && ly < j) 181 return widg.getWidget (cx, cy); 182 return this; // wasn't in cell 167 debug scope (failure) 168 logger.warn ("getWidget: failure"); 169 // Find row/column: 170 myDiff i = col.getCell (cx - x); 171 myDiff j = row.getCell (cy - y); 172 if (i < 0 || j < 0) // on a space between widgets 173 return this; 174 175 // On a subwidget; recurse call: 176 return subWidgets[i + j*cols].getWidget (cx, cy); 183 177 } 184 178 … … 192 186 * resizeRow is non-negative). */ 193 187 194 // Find the column 195 if (sizableCols.length != 0) { 196 int l = cx - x; // use relative coords 197 size_t i = cols - 1; // index, from right 198 while (l < colX[i]) { // decrement while left of this column 199 debug assert (i > 0, "clickEvent: left of first column"); 200 --i; 201 } // now (l >= colX[resizeCol]) 202 if (l < colX[i] + colW[i]) i = -1; // on a sub-widget 203 204 // Set resizeColsL / resizeColsH 205 // Want to find j such that [0..j],[j..$] divide sizableCols about i: 206 size_t j = 0; 207 while (j < sizableCols.length && sizableCols[j] <= i) ++j; 208 209 resizeColsL = sizableCols[0..j]; 210 resizeColsH = sizableCols[j..$]; 211 212 // Cannot resize if either list is empty. resizeCallback checks the length of L, 213 // but to save it checking R too, we set L's length zero if R's is. 214 if (resizeColsH.length == 0) 215 resizeColsL = null; 216 } 217 218 // Find the row 219 if (sizableRows.length != 0) { 220 int l = cy - y; 221 size_t i = rows - 1; 222 while (l < rowY[i]) { 223 debug assert (i > 0, "clickEvent: above first row"); 224 --i; 225 } 226 if (l < rowY[i] + rowH[i]) i = -1; 227 228 size_t j = 0; 229 while (j < sizableRows.length && sizableRows[j] <= i) ++j; 230 231 resizeRowsL = sizableRows[0..j]; 232 resizeRowsH = sizableRows[j..$]; 233 234 if (resizeRowsH.length == 0) 235 resizeRowsL = null; 236 } 237 238 if (resizeColsL is null && resizeRowsL is null) 239 return; // no resizing to do 188 // find col/row's resizeD & resizeU 189 if (col.findResize (cx - x) && row.findResize (cy - y)) 190 return; // unable to resize 240 191 241 192 dragX = cx; … … 259 210 * (i.e. to produce cached data calculated from construction data). */ 260 211 void genCachedConstructionData () { 212 col.spacing = row.spacing = window.renderer.layoutSpacing; 213 261 214 // Calculate the minimal column and row sizes: 262 colWMin = new int[cols]; // set length, making sure the arrays are initialised to zero 263 rowHMin = new int[rows]; 215 // set length, making sure the arrays are initialised to zero: 216 col.minWidth = new int[cols]; 217 row.minWidth = new int[rows]; 264 218 int ww, wh; // sub-widget minimal sizes 265 219 foreach (i,widget; subWidgets) { … … 267 221 268 222 // Increase dimensions if current minimal size is larger: 269 uint n = i % cols;// column270 if (col WMin[n] < ww) colWMin[n] = ww;271 n = i / cols; // row272 if (row HMin[n] < wh) rowHMin[n] = wh;223 myIt n = i % cols; // column 224 if (col.minWidth[n] < ww) col.minWidth[n] = ww; 225 n = i / cols; // row 226 if (row.minWidth[n] < wh) row.minWidth[n] = wh; 273 227 } 274 228 275 229 276 230 // Calculate the overall minimal size, starting with the spacing: 277 mh = window.renderer.layoutSpacing; // use mh temporarily231 mh = window.renderer.layoutSpacing; // use mh temporarily 278 232 mw = mh * (cols - 1); 279 233 mh *= (rows - 1); 280 234 281 foreach (x; col WMin)// add the column/row's dimensions235 foreach (x; col.minWidth) // add the column/row's dimensions 282 236 mw += x; 283 foreach (x; row HMin)237 foreach (x; row.minWidth) 284 238 mh += x; 285 239 286 240 287 241 // Find which cols/rows are resizable: 288 sizableCols = sizableRows = null; // reset; we're about to concatenate to them 242 // reset: 243 col.sizable = new bool[cols]; 244 row.sizable = new bool[rows]; 245 col.firstSizable = row.firstSizable = -1; 289 246 290 247 forCols: 291 for ( uint i = 0; i < cols; ++i) {// for each column292 for ( uint j = 0; j < subWidgets.length; j += cols)// for each row293 if (!subWidgets[i+j].isWSizable) // column not resizable294 continue forCols; // continue the outer for loop248 for (myIt i = 0; i < cols; ++i) { // for each column 249 for (myIt j = 0; j < subWidgets.length; j += cols) // for each row 250 if (!subWidgets[i+j].isWSizable) // column not resizable 251 continue forCols; // continue the outer for loop 295 252 296 253 // column is resizable if we get to here 297 sizableCols ~= i; 254 col.sizable[i] = true; 255 if (col.firstSizable < 0) 256 col.firstSizable = i; 257 col.lastSizable = i; 298 258 } 299 259 300 260 forRows: 301 for ( uint i = 0; i < subWidgets.length; i += cols) {// for each row302 for ( uint j = 0; j < cols; ++j)// for each column261 for (myIt i = 0; i < subWidgets.length; i += cols) { // for each row 262 for (myIt j = 0; j < cols; ++j) // for each column 303 263 if (!subWidgets[i+j].isHSizable) 304 264 continue forRows; 305 265 306 sizableRows ~= i / cols; 307 } 308 } 309 310 /* Calculations which need to be run whenever resizing occurs (or deeper alterations) 311 * (i.e. to produce cached data calculated from construction and mutable data). */ 312 void genCachedMutableData () { 313 // Calculate column and row locations: 314 colX.length = cols; 315 rowY.length = rows; 316 int spacing = window.renderer.layoutSpacing; 317 318 int cum = 0; 319 foreach (i, x; rowH) { 320 rowY[i] = cum; 321 cum += x + spacing; 322 } 323 324 cum = 0; 325 foreach (i, x; colW) { 326 colX[i] = cum; 327 cum += x + spacing; 328 } 329 330 // Tell subwidgets their new sizes: 331 foreach (i,widget; subWidgets) 332 widget.setSize (colW[i % cols], rowH[i / cols]); 266 row.lastSizable = i / cols; 267 row.sizable[row.lastSizable] = true; 268 if (row.firstSizable < 0) 269 row.firstSizable = row.lastSizable; 270 } 271 } 272 273 // set sub-widgets size & position (done after resizing widget or rows/columns) 274 void setSubWidgetSP (bool wH, bool hH) { 275 for (myIt i = 0; i < cols; ++i) 276 for (myIt j = 0; j < rows; ++j) 277 { 278 IWidget widget = subWidgets[i + cols*j]; 279 widget.setSize (col.width[i], row.width[j], wH, hH); 280 widget.setPosition (x + col.pos[i], y + row.pos[j]); 281 } 333 282 } 334 283 //END Cache calculation functions 335 284 336 /* Adjust the total size of rows/columns (including spacing) by diff.337 *338 * Params:339 * cellD = current sizes; is adjusted by the function to new sizes340 * cellDMin = minimal sizes (see colWMin/rowHMin)341 * sizableCells= List of indexes in cellD for cells which are resizable342 * diff = amount to increase/decrease the total size343 * startHigh = if true, start resizing from the tail end of cellD, instead of the beginning344 *345 * Returns:346 * The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin.347 */348 int adjustCellSizes (ref int[] cellD, ref int[] cellDMin, ref size_t[] sizableCells, int diff, bool startHigh)349 in {// Could occur if adjust isn't called first, but this would be a code error:350 assert (cellD !is null, "adjustCellSizes: cellD is null");351 } body {352 // Cannot resize if no cells are sizable:353 if (sizableCells.length == 0) return 0;354 355 size_t si = (startHigh ? sizableCells.length-1 : 0); // starting index of sizableCells356 357 // FIXME: could do with an overhaul358 if (diff > 0) { // increase size of first resizable cell359 cellD[sizableCells[si]] += diff;360 return diff;361 }362 else if (diff < 0) { // decrease363 size_t sc = (startHigh ? -1 : 1); // amount to iterate364 size_t ci; // index in cellD365 int rd = diff; // running diff366 while (true) {367 ci = sizableCells[si];368 369
