Mercurial > projects > dwt-addons
comparison dwtx/ui/forms/widgets/SizeCache.d @ 75:5d489b9f966c
Fix continue porting
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 24 May 2008 05:11:16 +0200 |
parents | |
children | 04b47443bb01 |
comparison
equal
deleted
inserted
replaced
74:dad2e11b8ae4 | 75:5d489b9f966c |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2004, 2007 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.ui.forms.widgets.SizeCache; | |
14 | |
15 import dwtx.ui.forms.widgets.ILayoutExtension; | |
16 | |
17 import dwt.DWT; | |
18 import dwt.graphics.Point; | |
19 import dwt.graphics.Rectangle; | |
20 import dwt.widgets.Button; | |
21 import dwt.widgets.Combo; | |
22 import dwt.widgets.Composite; | |
23 import dwt.widgets.Control; | |
24 import dwt.widgets.Label; | |
25 import dwt.widgets.List; | |
26 import dwt.widgets.Layout; | |
27 import dwt.widgets.ProgressBar; | |
28 import dwt.widgets.Sash; | |
29 import dwt.widgets.Scale; | |
30 import dwt.widgets.Scrollable; | |
31 import dwt.widgets.Slider; | |
32 import dwt.widgets.Text; | |
33 import dwt.widgets.ToolBar; | |
34 import dwt.widgets.Tree; | |
35 import dwtx.jface.util.Geometry; | |
36 import dwtx.ui.internal.forms.widgets.FormUtil; | |
37 | |
38 import dwt.dwthelper.utils; | |
39 import tango.util.collection.ArraySeq; | |
40 /** | |
41 * Caches the preferred size of an DWT control | |
42 * | |
43 * @since 3.0 | |
44 */ | |
45 public class SizeCache { | |
46 private Control control; | |
47 | |
48 private Point preferredSize; | |
49 | |
50 private int cachedWidthQuery; | |
51 private int cachedWidthResult; | |
52 | |
53 private int cachedHeightQuery; | |
54 private int cachedHeightResult; | |
55 | |
56 private int minimumWidth; | |
57 private int heightAtMinimumWidth = -1; | |
58 private int maximumWidth; | |
59 | |
60 /** | |
61 * True iff we should recursively flush all children on the next layout | |
62 */ | |
63 private bool flushChildren; | |
64 | |
65 /** | |
66 * True iff changing the height hint does not affect the preferred width and changing | |
67 * the width hint does not change the preferred height | |
68 */ | |
69 private bool independentDimensions = false; | |
70 | |
71 /** | |
72 * True iff the preferred height for any hint larger than the preferred width will not | |
73 * change the preferred height. | |
74 */ | |
75 private bool preferredWidthOrLargerIsMinimumHeight = false; | |
76 | |
77 // HACK: these values estimate how much to subtract from the width and height | |
78 // hints that get passed into computeSize, in order to produce a result | |
79 // that is exactly the desired size. To be removed once bug 46112 is fixed (note: | |
80 // bug 46112 is currently flagged as a duplicate, but there is still no workaround). | |
81 private int widthAdjustment = 0; | |
82 | |
83 private int heightAdjustment = 0; | |
84 | |
85 private int minimumHeight; | |
86 | |
87 private int widthAtMinimumHeight = -1; | |
88 | |
89 // If the layout is dirty, this is the size of the control at the time its | |
90 // layout was dirtied. null if the layout is not dirty. | |
91 private Point dirtySize = null; | |
92 | |
93 | |
94 // END OF HACK | |
95 | |
96 public this() { | |
97 this(null); | |
98 } | |
99 | |
100 /** | |
101 * Creates a cache for size computations on the given control | |
102 * | |
103 * @param control the control for which sizes will be calculated, | |
104 * or null to always return (0,0) | |
105 */ | |
106 public this(Control control) { | |
107 setControl(control); | |
108 } | |
109 | |
110 /** | |
111 * Sets the control whose size is being cached. Does nothing (will not | |
112 * even flush the cache) if this is the same control as last time. | |
113 * | |
114 * @param newControl the control whose size is being cached, or null to always return (0,0) | |
115 */ | |
116 public void setControl(Control newControl) { | |
117 if (newControl !is control) { | |
118 control = newControl; | |
119 if (control is null) { | |
120 independentDimensions = true; | |
121 preferredWidthOrLargerIsMinimumHeight = false; | |
122 widthAdjustment = 0; | |
123 heightAdjustment = 0; | |
124 } else { | |
125 independentDimensions = independentLengthAndWidth(control); | |
126 preferredWidthOrLargerIsMinimumHeight = isPreferredWidthMaximum(control); | |
127 computeHintOffset(control); | |
128 flush(); | |
129 } | |
130 } | |
131 } | |
132 | |
133 /** | |
134 * Returns the control whose size is being cached | |
135 * | |
136 * @return the control whose size is being cached, or null if this cache always returns (0,0) | |
137 */ | |
138 public Control getControl() { | |
139 return control; | |
140 } | |
141 | |
142 /** | |
143 * Flush the cache (should be called if the control's contents may have changed since the | |
144 * last query) | |
145 */ | |
146 public void flush() { | |
147 flush(true); | |
148 } | |
149 | |
150 public void flush(bool recursive) { | |
151 preferredSize = null; | |
152 cachedWidthQuery = -1; | |
153 cachedWidthResult = -1; | |
154 cachedHeightQuery = -1; | |
155 cachedHeightResult = -1; | |
156 minimumWidth = -1; | |
157 maximumWidth = -1; | |
158 minimumHeight = -1; | |
159 heightAtMinimumWidth = -1; | |
160 widthAtMinimumHeight = -1; | |
161 | |
162 if (recursive || dirtySize !is null) { | |
163 if (control is null || control.isDisposed()) { | |
164 dirtySize = new Point(0,0); | |
165 control = null; | |
166 } else { | |
167 dirtySize = control.getSize(); | |
168 } | |
169 } | |
170 | |
171 this.flushChildren = this.flushChildren || recursive; | |
172 } | |
173 | |
174 private Point getPreferredSize() { | |
175 if (preferredSize is null) { | |
176 preferredSize = controlComputeSize(DWT.DEFAULT, DWT.DEFAULT); | |
177 } | |
178 | |
179 return preferredSize; | |
180 } | |
181 | |
182 /** | |
183 * Computes the preferred size of the control. | |
184 * | |
185 * @param widthHint the known width of the control (pixels) or DWT.DEFAULT if unknown | |
186 * @param heightHint the known height of the control (pixels) or DWT.DEFAULT if unknown | |
187 * @return the preferred size of the control | |
188 */ | |
189 public Point computeSize(int widthHint, int heightHint) { | |
190 if (control is null || control.isDisposed()) { | |
191 return new Point(0, 0); | |
192 } | |
193 | |
194 // If we're asking for a result smaller than the minimum width | |
195 int minWidth = computeMinimumWidth(); | |
196 | |
197 if (widthHint !is DWT.DEFAULT && widthHint + widthAdjustment < minWidth) { | |
198 if (heightHint is DWT.DEFAULT) { | |
199 return new Point(minWidth, computeHeightAtMinimumWidth()); | |
200 } | |
201 | |
202 widthHint = minWidth - widthAdjustment; | |
203 } | |
204 | |
205 // If we're asking for a result smaller than the minimum height | |
206 int minHeight = computeMinimumHeight(); | |
207 | |
208 if (heightHint !is DWT.DEFAULT && heightHint + heightAdjustment < minHeight) { | |
209 if (widthHint is DWT.DEFAULT) { | |
210 return new Point(computeWidthAtMinimumHeight(), minHeight); | |
211 } | |
212 | |
213 heightHint = minHeight - heightAdjustment; | |
214 } | |
215 | |
216 // If both dimensions were supplied in the input, compute the trivial result | |
217 if (widthHint !is DWT.DEFAULT && heightHint !is DWT.DEFAULT) { | |
218 return new Point(widthHint + widthAdjustment, heightHint + heightAdjustment); | |
219 } | |
220 | |
221 // No hints given -- find the preferred size | |
222 if (widthHint is DWT.DEFAULT && heightHint is DWT.DEFAULT) { | |
223 return Geometry.copy(getPreferredSize()); | |
224 } | |
225 | |
226 // If the length and width are independent, compute the preferred size | |
227 // and adjust whatever dimension was supplied in the input | |
228 if (independentDimensions) { | |
229 Point result = Geometry.copy(getPreferredSize()); | |
230 | |
231 if (widthHint !is DWT.DEFAULT) { | |
232 result.x = widthHint + widthAdjustment; | |
233 } | |
234 | |
235 if (heightHint !is DWT.DEFAULT) { | |
236 result.y = heightHint + heightAdjustment; | |
237 } | |
238 | |
239 return result; | |
240 } | |
241 | |
242 // Computing a height | |
243 if (heightHint is DWT.DEFAULT) { | |
244 // If we know the control's preferred size | |
245 if (preferredSize !is null) { | |
246 // If the given width is the preferred width, then return the preferred size | |
247 if (widthHint + widthAdjustment is preferredSize.x) { | |
248 return Geometry.copy(preferredSize); | |
249 } | |
250 } | |
251 | |
252 // If we have a cached height measurement | |
253 if (cachedHeightQuery !is -1) { | |
254 // If this was measured with the same width hint | |
255 if (cachedHeightQuery is widthHint) { | |
256 return new Point(widthHint + widthAdjustment, cachedHeightResult); | |
257 } | |
258 } | |
259 | |
260 // If this is a control where any hint larger than the | |
261 // preferred width results in the minimum height, determine if | |
262 // we can compute the result based on the preferred height | |
263 if (preferredWidthOrLargerIsMinimumHeight) { | |
264 // Computed the preferred size (if we don't already know it) | |
265 getPreferredSize(); | |
266 | |
267 // If the width hint is larger than the preferred width, then | |
268 // we can compute the result from the preferred width | |
269 if (widthHint + widthAdjustment >= preferredSize.x) { | |
270 return new Point(widthHint + widthAdjustment, preferredSize.y); | |
271 } | |
272 } | |
273 | |
274 // Else we can't find an existing size in the cache, so recompute | |
275 // it from scratch. | |
276 Point newHeight = controlComputeSize(widthHint - widthAdjustment, DWT.DEFAULT); | |
277 | |
278 cachedHeightQuery = heightHint; | |
279 cachedHeightResult = newHeight.y; | |
280 | |
281 return newHeight; | |
282 } | |
283 | |
284 // Computing a width | |
285 if (widthHint is DWT.DEFAULT) { | |
286 // If we know the control's preferred size | |
287 if (preferredSize !is null) { | |
288 // If the given height is the preferred height, then return the preferred size | |
289 if (heightHint + heightAdjustment is preferredSize.y) { | |
290 return Geometry.copy(preferredSize); | |
291 } | |
292 } | |
293 | |
294 // If we have a cached width measurement with the same height hint | |
295 if (cachedWidthQuery is heightHint) { | |
296 return new Point(cachedWidthResult, heightHint + heightAdjustment); | |
297 } | |
298 | |
299 Point widthResult = controlComputeSize(DWT.DEFAULT, heightHint - heightAdjustment); | |
300 | |
301 cachedWidthQuery = heightHint; | |
302 cachedWidthResult = widthResult.x; | |
303 | |
304 return widthResult; | |
305 } | |
306 | |
307 return controlComputeSize(widthHint, heightHint); | |
308 } | |
309 | |
310 /** | |
311 * Compute the control's size, and ensure that non-default hints are returned verbatim | |
312 * (this tries to compensate for DWT's hints, which aren't really the outer width of the | |
313 * control). | |
314 * | |
315 * @param widthHint the horizontal hint | |
316 * @param heightHint the vertical hint | |
317 * @return the control's size | |
318 */ | |
319 public Point computeAdjustedSize(int widthHint, int heightHint) { | |
320 int adjustedWidthHint = widthHint is DWT.DEFAULT ? DWT.DEFAULT : Math | |
321 .max(0, widthHint - widthAdjustment); | |
322 int adjustedHeightHint = heightHint is DWT.DEFAULT ? DWT.DEFAULT : Math | |
323 .max(0, heightHint - heightAdjustment); | |
324 | |
325 Point result = computeSize(adjustedWidthHint, adjustedHeightHint); | |
326 | |
327 // If the amounts we subtracted off the widthHint and heightHint didn't do the trick, then | |
328 // manually adjust the result to ensure that a non-default hint will return that result verbatim. | |
329 | |
330 return result; | |
331 } | |
332 | |
333 /** | |
334 * Returns true if the preferred length of the given control is | |
335 * independent of the width and visa-versa. If this returns true, | |
336 * then changing the widthHint argument to control.computeSize will | |
337 * never change the resulting height and changing the heightHint | |
338 * will never change the resulting width. Returns false if unknown. | |
339 * <p> | |
340 * This information can be used to improve caching. Incorrectly returning | |
341 * a value of false may decrease performance, but incorrectly returning | |
342 * a value of true will generate incorrect layouts... so always return | |
343 * false if unsure. | |
344 * </p> | |
345 * | |
346 * @param control | |
347 * @return | |
348 */ | |
349 static bool independentLengthAndWidth(Control control) { | |
350 if (control is null || control.isDisposed()) { | |
351 return true; | |
352 } | |
353 | |
354 if (null !is cast(Button)control || null !is cast(ProgressBar)control | |
355 || null !is cast(Sash)control || null !is cast(Scale)control | |
356 || null !is cast(Slider)control || null !is cast(List)control | |
357 || null !is cast(Combo)control || null !is cast(Tree)control ) { | |
358 return true; | |
359 } | |
360 | |
361 if (null !is cast(Label)control || null !is cast(Text)control ) { | |
362 return (control.getStyle() & DWT.WRAP) is 0; | |
363 } | |
364 | |
365 // Unless we're certain that the control has this property, we should | |
366 // return false. | |
367 | |
368 return false; | |
369 } | |
370 | |
371 /** | |
372 * Try to figure out how much we need to subtract from the hints that we | |
373 * pass into the given control's computeSize(...) method. This tries to | |
374 * compensate for bug 46112. To be removed once DWT provides an "official" | |
375 * way to compute one dimension of a control's size given the other known | |
376 * dimension. | |
377 * | |
378 * @param control | |
379 */ | |
380 private void computeHintOffset(Control control) { | |
381 if (null !is cast(Scrollable)control ) { | |
382 // For scrollables, subtract off the trim size | |
383 Scrollable scrollable = cast(Scrollable) control; | |
384 Rectangle trim = scrollable.computeTrim(0, 0, 0, 0); | |
385 | |
386 widthAdjustment = trim.width; | |
387 heightAdjustment = trim.height; | |
388 } else { | |
389 // For non-composites, subtract off 2 * the border size | |
390 widthAdjustment = control.getBorderWidth() * 2; | |
391 heightAdjustment = widthAdjustment; | |
392 } | |
393 } | |
394 | |
395 private Point controlComputeSize(int widthHint, int heightHint) { | |
396 Point result = control.computeSize(widthHint, heightHint, flushChildren); | |
397 flushChildren = false; | |
398 | |
399 return result; | |
400 } | |
401 | |
402 /** | |
403 * Returns true only if the control will return a constant height for any | |
404 * width hint larger than the preferred width. Returns false if there is | |
405 * any situation in which the control does not have this property. | |
406 * | |
407 * <p> | |
408 * Note: this method is only important for wrapping controls, and it can | |
409 * safely return false for anything else. AFAIK, all DWT controls have this | |
410 * property, but to be safe they will only be added to the list once the | |
411 * property has been confirmed. | |
412 * </p> | |
413 * | |
414 * @param control | |
415 * @return | |
416 */ | |
417 private static bool isPreferredWidthMaximum(Control control) { | |
418 return (null !is cast(ToolBar)control | |
419 //|| control instanceof CoolBar | |
420 || null !is cast(Label)control ); | |
421 } | |
422 | |
423 public int computeMinimumWidth() { | |
424 if (minimumWidth is -1) { | |
425 if (null !is cast(Composite)control ) { | |
426 Layout layout = (cast(Composite)control).getLayout(); | |
427 if (null !is cast(ILayoutExtension)layout ) { | |
428 minimumWidth = (cast(ILayoutExtension)layout).computeMinimumWidth(cast(Composite)control, flushChildren); | |
429 flushChildren = false; | |
430 } | |
431 } | |
432 } | |
433 | |
434 if (minimumWidth is -1) { | |
435 Point minWidth = controlComputeSize(FormUtil.getWidthHint(5, control), DWT.DEFAULT); | |
436 minimumWidth = minWidth.x; | |
437 heightAtMinimumWidth = minWidth.y; | |
438 } | |
439 | |
440 return minimumWidth; | |
441 } | |
442 | |
443 public int computeMaximumWidth() { | |
444 if (maximumWidth is -1) { | |
445 if (null !is cast(Composite)control ) { | |
446 Layout layout = (cast(Composite)control).getLayout(); | |
447 if (null !is cast(ILayoutExtension)layout ) { | |
448 maximumWidth = (cast(ILayoutExtension)layout).computeMaximumWidth(cast(Composite)control, flushChildren); | |
449 flushChildren = false; | |
450 } | |
451 } | |
452 } | |
453 | |
454 if (maximumWidth is -1) { | |
455 maximumWidth = getPreferredSize().x; | |
456 } | |
457 | |
458 return maximumWidth; | |
459 } | |
460 | |
461 private int computeHeightAtMinimumWidth() { | |
462 int minimumWidth = computeMinimumWidth(); | |
463 | |
464 if (heightAtMinimumWidth is -1) { | |
465 heightAtMinimumWidth = controlComputeSize(minimumWidth - widthAdjustment, DWT.DEFAULT).y; | |
466 } | |
467 | |
468 return heightAtMinimumWidth; | |
469 } | |
470 | |
471 private int computeWidthAtMinimumHeight() { | |
472 int minimumHeight = computeMinimumHeight(); | |
473 | |
474 if (widthAtMinimumHeight is -1) { | |
475 widthAtMinimumHeight = controlComputeSize(DWT.DEFAULT, minimumHeight - heightAdjustment).x; | |
476 } | |
477 | |
478 return widthAtMinimumHeight; | |
479 } | |
480 | |
481 private int computeMinimumHeight() { | |
482 if (minimumHeight is -1) { | |
483 Point sizeAtMinHeight = controlComputeSize(DWT.DEFAULT, 0); | |
484 | |
485 minimumHeight = sizeAtMinHeight.y; | |
486 widthAtMinimumHeight = sizeAtMinHeight.x; | |
487 } | |
488 | |
489 return minimumHeight; | |
490 } | |
491 | |
492 public Point computeMinimumSize() { | |
493 return new Point(computeMinimumWidth(), computeMinimumHeight()); | |
494 } | |
495 | |
496 public void setSize(Point newSize) { | |
497 if (control !is null) { | |
498 control.setSize(newSize); | |
499 } | |
500 | |
501 layoutIfNecessary(); | |
502 } | |
503 | |
504 public void setSize(int width, int height) { | |
505 if (control !is null) { | |
506 control.setSize(width, height); | |
507 } | |
508 | |
509 layoutIfNecessary(); | |
510 } | |
511 | |
512 public void setBounds(int x, int y, int width, int height) { | |
513 if (control !is null) { | |
514 control.setBounds(x, y, width, height); | |
515 } | |
516 | |
517 layoutIfNecessary(); | |
518 } | |
519 | |
520 public void setBounds(Rectangle bounds) { | |
521 if (control !is null) { | |
522 control.setBounds(bounds); | |
523 } | |
524 | |
525 layoutIfNecessary(); | |
526 } | |
527 | |
528 public void layoutIfNecessary() { | |
529 if (dirtySize !is null && control !is null && null !is cast(Composite)control ) { | |
530 if (control.getSize().opEquals(dirtySize)) { | |
531 (cast(Composite)control).layout(flushChildren); | |
532 flushChildren = false; | |
533 } | |
534 } | |
535 dirtySize = null; | |
536 } | |
537 } |