Mercurial > projects > mde
comparison mde/gui/widget/layout.d @ 37:052df9b2fe07
Allowed widget resizing, changed widget IDs and made Input catch any callback exceptions.
Enabled widget resizing.
Removed IRenderer's temporary drawBox method and added drawButton for ButtonWidget.
Made the Widget class abstract and added FixedWidget and SizableWidget classes.
Rewrote much of createWidget to use meta-code; changed widget IDs.
Made Input catch callback exceptions and report error messages.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Mon, 05 May 2008 14:47:25 +0100 |
parents | 6b4116e6355c |
children | 8c4c96f04e7f |
comparison
equal
deleted
inserted
replaced
36:57d000574d75 | 37:052df9b2fe07 |
---|---|
17 module mde.gui.widget.layout; | 17 module mde.gui.widget.layout; |
18 | 18 |
19 import mde.gui.widget.Widget; | 19 import mde.gui.widget.Widget; |
20 import mde.gui.exception : WidgetDataException; | 20 import mde.gui.exception : WidgetDataException; |
21 | 21 |
22 /// Encapsulates a grid of Widgets | 22 /** Encapsulates a grid of Widgets. |
23 class GridWidget : Widget | 23 * |
24 * Since a grid with either dimension zero is not useful, there must be at least one sub-widget. */ | |
25 class GridLayoutWidget : Widget | |
24 { | 26 { |
25 this (IWindow wind, IWidget, int[] data) { | 27 this (IWindow wind, IWidget, int[] data) { |
26 // Get grid size | 28 // Get grid size and check data |
27 if (data.length < 2) throw new WidgetDataException; | 29 // Check sufficient data for rows, cols, and at least one widget: |
30 if (data.length < 3) throw new WidgetDataException; | |
28 rows = data[0]; | 31 rows = data[0]; |
29 cols = data[1]; | 32 cols = data[1]; |
33 if (data.length != 2 + rows * cols) throw new WidgetDataException; | |
34 /* data.length >= 3 so besides checking the length is correct, this tells us: | |
35 * rows * cols >= 3 - 2 = 1 a free check! | |
36 * The only thing not checked is whether both rows and cols are negative, which would | |
37 * cause an exception when dynamic arrays are allocated later (and is unlikely). */ | |
30 | 38 |
31 window = wind; | 39 window = wind; |
32 | 40 |
33 // Get all sub-widgets | 41 // Get all sub-widgets |
34 // Check: correct data length and rows*cols >= 0 (know data.length - 2 >= 0). | |
35 if (data.length != 2 + rows * cols) throw new WidgetDataException; | |
36 subWidgets.length = rows*cols; | 42 subWidgets.length = rows*cols; |
37 foreach (i, inout subWidget; subWidgets) { | 43 foreach (i, inout subWidget; subWidgets) { |
38 subWidget = window.makeWidget (data[i+2], this); | 44 subWidget = window.makeWidget (data[i+2], this); |
39 } | 45 } |
40 | 46 } |
41 getMinimumSize (w,h); // Calculate the size (current size is not saved) | 47 |
42 } | 48 bool isWSizable () { |
43 | 49 if (colSizable == -2) { // check whether any columns are resizable |
44 // Calculates from all rows and columns of widgets. | 50 for1: |
45 void getMinimumSize (out int w, out int h) { | 51 for (uint i = 0; i < cols; ++i) { // for each column |
46 if (rows*cols == 0) { // special case | 52 for (uint j = 0; j < subWidgets.length; j += cols) // for each row |
47 w = h = 0; | 53 if (!subWidgets[i+j].isWSizable) // column not resizable |
48 return; | 54 continue for1; // continue the outer for loop |
49 } | 55 |
50 | 56 // column is resizable if we get to here |
51 // Find the sizes of all subWidgets | 57 colSizable = i; |
52 int[] widgetW = new int[subWidgets.length]; // dimensions | 58 goto break1; // use goto in lieu of for...else |
53 int[] widgetH = new int[subWidgets.length]; | 59 } |
54 foreach (i,widget; subWidgets) widget.getCurrentSize (widgetW[i],widgetH[i]); | 60 |
55 | 61 // if we get here, no resizable column was found |
56 // Find row heights and column widths (non cumulative) | 62 colSizable = -1; |
57 rowH.length = rows; | 63 |
58 colW.length = cols; //WARNING: code reliant on these being initialised to zero | 64 break1:; |
59 for (uint i = 0; i < subWidgets.length; ++i) { | 65 } |
60 uint n = i / cols; // row | 66 |
61 if (rowH[n] < widgetH[i]) rowH[n] = widgetH[i]; | 67 if (colSizable >= 0) return true; |
62 n = i % cols; // column | 68 else return false; |
63 if (colW[n] < widgetW[i]) colW[n] = widgetW[i]; | 69 } |
64 } | 70 |
65 | 71 bool isHSizable () { |
66 // rowY / colX | 72 if (rowSizable == -2) { // check whether any columns are resizable |
73 for2: | |
74 for (uint i = 0; i < subWidgets.length; i += cols) { // for each row | |
75 for (uint j = 0; j < cols; ++j) // for each column | |
76 if (!subWidgets[i+j].isHSizable) | |
77 continue for2; | |
78 | |
79 rowSizable = i / cols; // the current row | |
80 goto break2; | |
81 } | |
82 | |
83 rowSizable = -1; | |
84 | |
85 break2:; | |
86 } | |
87 | |
88 if (rowSizable >= 0) return true; | |
89 else return false; | |
90 } | |
91 | |
92 /* Calculates the minimal size from all rows and columns of widgets. */ | |
93 void getMinimalSize (out int mw, out int mh) { | |
94 // If rowHMin & colWMin are null, calculate them. They are set null whenever the contents | |
95 // or the contents' minimal size change, as well as when this widget is created. | |
96 if (rowHMin is null) | |
97 genMinRowColSizes; | |
98 | |
99 // Calculate the size, starting with the spacing: | |
100 mh = window.renderer.layoutSpacing; // use temporarily | |
101 mw = mh * (cols - 1); | |
102 mh *= (rows - 1); | |
103 | |
104 foreach (x; colWMin) // add the column/row's dimensions | |
105 mw += x; | |
106 foreach (x; rowHMin) | |
107 mh += x; | |
108 } | |
109 | |
110 void setSize (int nw, int nh) { | |
111 // Step 1: calculate the minimal row/column sizes. | |
112 int mw, mh; // FIXME: use w,h directly? | |
113 getMinimalSize (mw, mh); | |
114 colW = colWMin; // start with these dimensions, and increase if necessary | |
115 rowH = rowHMin; | |
116 | |
117 // Step 2: clamp nw/nh or expand a column/row to achieve the required size | |
118 if (nw <= mw) nw = mw; // clamp to minimal size | |
119 else { | |
120 if (isWSizable) // calculates colSizable; true if any is resizable | |
121 colW[colSizable] += nw - mw; // new width | |
122 else // no resizable column; so force the last one | |
123 colW[$-1] += nw - mw; | |
124 } | |
125 | |
126 if (nh <= mh) nh = mh; | |
127 else { | |
128 if (isHSizable) | |
129 rowH[rowSizable] += nh - mh; | |
130 else | |
131 rowH[$-1] += nh - mh; | |
132 } | |
133 | |
134 // Step 3: set each sub-widget's size. | |
135 foreach (i,widget; subWidgets) | |
136 widget.setSize (colW[i % cols], rowH[i / cols]); | |
137 | |
138 // Step 4: calculate the column and row positions | |
139 colX.length = cols; | |
67 rowY.length = rows; | 140 rowY.length = rows; |
68 colX.length = cols; | |
69 int spacing = window.renderer.layoutSpacing; | 141 int spacing = window.renderer.layoutSpacing; |
70 | 142 |
71 int cum = 0; | 143 int cum = 0; |
72 foreach (i, x; rowH) { | 144 foreach (i, x; rowH) { |
73 rowY[i] = cum; | 145 rowY[i] = cum; |
74 cum += x + spacing; | 146 cum += x + spacing; |
75 } | 147 } |
76 h = cum - spacing; // total height | 148 h = cum - spacing; // set the total height |
149 assert (h == nh); // FIXME: remove and set w/h directly once this is asserted | |
150 | |
77 cum = 0; | 151 cum = 0; |
78 foreach (i, x; colW) { | 152 foreach (i, x; colW) { |
79 colX[i] = cum; | 153 colX[i] = cum; |
80 cum += x + spacing; | 154 cum += x + spacing; |
81 } | 155 } |
82 w = cum - spacing; // total width | 156 w = cum - spacing; // total width |
157 assert (w == nw); | |
83 } | 158 } |
84 | 159 |
85 void setPosition (int x, int y) { | 160 void setPosition (int x, int y) { |
86 this.x = x; | 161 this.x = x; |
87 this.y = y; | 162 this.y = y; |
88 | 163 |
89 foreach (i,widget; subWidgets) | 164 foreach (i,widget; subWidgets) |
90 widget.setPosition (x + colX[i % cols], y + rowY[i / cols]); | 165 widget.setPosition (x + colX[i % cols], y + rowY[i / cols]); |
91 } | 166 } |
92 | 167 |
168 | |
93 // Find the relevant widget. | 169 // Find the relevant widget. |
94 IWidget getWidget (int cx, int cy) { | 170 IWidget getWidget (int cx, int cy) { |
95 if (rows*cols == 0) return this; // special case | |
96 | |
97 int lx = cx - x, ly = cy - y; // use coords relative to this widget | 171 int lx = cx - x, ly = cy - y; // use coords relative to this widget |
98 | 172 |
99 // Find the column | 173 // Find the column |
100 int i = cols - 1; // starting from right... | 174 int i = cols - 1; // starting from right... |
101 while (lx < colX[i]) { // decrement while left of this column | 175 while (lx < colX[i]) { // decrement while left of this column |
117 ly -= rowY[j]; | 191 ly -= rowY[j]; |
118 IWidget widg = subWidgets[i + j*cols]; | 192 IWidget widg = subWidgets[i + j*cols]; |
119 widg.getCurrentSize (i,j); | 193 widg.getCurrentSize (i,j); |
120 if (lx < i && ly < j) | 194 if (lx < i && ly < j) |
121 return widg.getWidget (cx, cy); | 195 return widg.getWidget (cx, cy); |
196 return this; // wasn't in cell | |
122 } | 197 } |
123 | 198 |
124 void draw () { | 199 void draw () { |
125 super.draw (); | 200 super.draw (); |
126 | 201 |
127 foreach (widget; subWidgets) | 202 foreach (widget; subWidgets) |
128 widget.draw (); | 203 widget.draw (); |
129 } | 204 } |
130 | 205 |
206 private: | |
207 void genMinRowColSizes () { | |
208 // Find the sizes of all subWidgets | |
209 int[] widgetW = new int[subWidgets.length]; // dimensions | |
210 int[] widgetH = new int[subWidgets.length]; | |
211 foreach (i,widget; subWidgets) | |
212 widget.getMinimalSize (widgetW[i],widgetH[i]); | |
213 | |
214 // Find the minimal row heights and column widths (non cumulative) | |
215 colWMin = new int[cols]; // set length | |
216 rowHMin = new int[rows]; | |
217 for (uint i = 0; i < subWidgets.length; ++i) { | |
218 uint n; | |
219 n = i % cols; // column | |
220 if (colWMin[n] < widgetW[i]) colWMin[n] = widgetW[i]; | |
221 n = i / cols; // row | |
222 if (rowHMin[n] < widgetH[i]) rowHMin[n] = widgetH[i]; | |
223 } | |
224 } | |
225 | |
131 protected: | 226 protected: |
132 int rows, cols; // number of cells in grid | 227 int cols, rows; // number of cells in grid |
228 | |
229 int colSizable = -2;// 0..cols-1 means this column is resizable | |
230 int rowSizable = -2;// -2 means not calculated yet, -1 means not resizable | |
231 | |
232 int[] colWMin; // minimal column width | |
233 int[] rowHMin; // minimal row height | |
234 int[] colW; // column width (widest widget) | |
133 int[] rowH; // row height (highest widget in the row) | 235 int[] rowH; // row height (highest widget in the row) |
134 int[] colW; // column width (widest widget) | 236 |
135 int[] rowY; // cumulative rowH[i-1] + border and padding | 237 int[] colX; // cumulative colW[i-1] + padding (add x to get column's left x-coord) |
136 int[] colX; // cumulative colW[i-1] + border and padding | 238 int[] rowY; // cumulative rowH[i-1] + padding |
239 | |
137 IWidget[] subWidgets; // all widgets in the grid (by row): | 240 IWidget[] subWidgets; // all widgets in the grid (by row): |
138 /* SubWidget order: [ 0 1 ] | 241 /* SubWidget order: [ 0 1 ] |
139 * [ 2 3 ] */ | 242 * [ 2 3 ] */ |
140 } | 243 } |