comparison org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/SizeCache.d @ 12:bc29606a740c

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