Mercurial > projects > dwt2
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 } |