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 }