Mercurial > projects > dwt-addons
comparison dwtx/draw2d/FigureCanvas.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
96:b492ba44e44d | 98:95307ad235d9 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2008 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.draw2d.FigureCanvas; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 import dwtx.dwtxhelper.Bean; | |
17 static import dwtx.dwtxhelper.Collection; | |
18 | |
19 import dwt.DWT; | |
20 import dwt.events.SelectionAdapter; | |
21 import dwt.events.SelectionEvent; | |
22 import dwt.graphics.Font; | |
23 import dwt.widgets.Canvas; | |
24 import dwt.widgets.Composite; | |
25 import dwt.widgets.Control; | |
26 import dwtx.draw2d.geometry.Dimension; | |
27 import dwtx.draw2d.geometry.Point; | |
28 import dwtx.draw2d.geometry.Rectangle; | |
29 import dwtx.draw2d.Viewport; | |
30 import dwtx.draw2d.LightweightSystem; | |
31 import dwtx.draw2d.IFigure; | |
32 import dwtx.draw2d.Border; | |
33 import dwtx.draw2d.RangeModel; | |
34 static import dwt.graphics.Point; | |
35 static import dwt.graphics.Rectangle; | |
36 import dwtx.draw2d.UpdateListener; | |
37 import dwtx.draw2d.ScrollPaneSolver; | |
38 | |
39 /** | |
40 * A Canvas that contains {@link Figure Figures}. | |
41 * | |
42 * <dl> | |
43 * <dt><b>Required Styles (when using certain constructors):</b></dt> | |
44 * <dd>V_SCROLL, H_SCROLL, NO_REDRAW_RESIZE</dd> | |
45 * <dt><b>Optional Styles:</b></dt> | |
46 * <dd>DOUBLE_BUFFERED, RIGHT_TO_LEFT, LEFT_TO_RIGHT, NO_BACKGROUND, BORDER</dd> | |
47 * </dl> | |
48 * <p> | |
49 * Note: Only one of the styles RIGHT_TO_LEFT, LEFT_TO_RIGHT may be specified. | |
50 * </p> | |
51 */ | |
52 public class FigureCanvas | |
53 : Canvas | |
54 { | |
55 | |
56 private static final int ACCEPTED_STYLES = | |
57 DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT | |
58 | DWT.V_SCROLL | DWT.H_SCROLL | |
59 | DWT.NO_BACKGROUND | |
60 | DWT.NO_REDRAW_RESIZE | |
61 | DWT.DOUBLE_BUFFERED | |
62 | DWT.BORDER; | |
63 | |
64 /** | |
65 * The default styles are mixed in when certain constructors are used. This | |
66 * constant is a bitwise OR of the following DWT style constants: | |
67 * <UL> | |
68 * <LI>{@link DWT#NO_REDRAW_RESIZE}</LI> | |
69 * <LI>{@link DWT#NO_BACKGROUND}</LI> | |
70 * <LI>{@link DWT#V_SCROLL}</LI> | |
71 * <LI>{@link DWT#H_SCROLL}</LI> | |
72 * </UL> | |
73 */ | |
74 static const int DEFAULT_STYLES = | |
75 DWT.NO_REDRAW_RESIZE | |
76 | DWT.NO_BACKGROUND | |
77 | DWT.V_SCROLL | |
78 | DWT.H_SCROLL; | |
79 | |
80 private static const int REQUIRED_STYLES = | |
81 DWT.NO_REDRAW_RESIZE | |
82 | DWT.V_SCROLL | |
83 | DWT.H_SCROLL; | |
84 | |
85 /** Never show scrollbar */ | |
86 public static int NEVER = 0; | |
87 /** Automatically show scrollbar when needed */ | |
88 public static int AUTOMATIC = 1; | |
89 /** Always show scrollbar */ | |
90 public static int ALWAYS = 2; | |
91 | |
92 private int vBarVisibility; | |
93 private int hBarVisibility; | |
94 private Viewport viewport; | |
95 private Font font; | |
96 private int hBarOffset; | |
97 private int vBarOffset; | |
98 | |
99 private PropertyChangeListener horizontalChangeListener; | |
100 private PropertyChangeListener verticalChangeListener; | |
101 private const LightweightSystem lws; | |
102 | |
103 /** | |
104 * Creates a new FigureCanvas with the given parent and the {@link #DEFAULT_STYLES}. | |
105 * | |
106 * @param parent the parent | |
107 */ | |
108 public this(Composite parent) { | |
109 this(parent, DWT.DOUBLE_BUFFERED, new LightweightSystem()); | |
110 } | |
111 | |
112 /** | |
113 * Constructor which applies the default styles plus any optional styles indicated. | |
114 * @param parent the parent composite | |
115 * @param style see the class javadoc for optional styles | |
116 * @since 3.1 | |
117 */ | |
118 public this(Composite parent, int style) { | |
119 this(parent, style, new LightweightSystem()); | |
120 } | |
121 | |
122 /** | |
123 * Constructor which uses the given styles verbatim. Certain styles must be used | |
124 * with this class. Refer to the class javadoc for more details. | |
125 * @param style see the class javadoc for <b>required</b> and optional styles | |
126 * @param parent the parent composite | |
127 * @since 3.4 | |
128 */ | |
129 public this(int style, Composite parent) { | |
130 this(style, parent, new LightweightSystem()); | |
131 } | |
132 | |
133 /** | |
134 * Constructs a new FigureCanvas with the given parent and LightweightSystem, using | |
135 * the {@link #DEFAULT_STYLES}. | |
136 * @param parent the parent | |
137 * @param lws the LightweightSystem | |
138 */ | |
139 public this(Composite parent, LightweightSystem lws) { | |
140 this(parent, DWT.DOUBLE_BUFFERED, lws); | |
141 } | |
142 | |
143 /** | |
144 * Constructor taking a lightweight system and DWT style, which is used verbatim. | |
145 * Certain styles must be used with this class. Refer to the class javadoc for | |
146 * more details. | |
147 * @param style see the class javadoc for <b>required</b> and optional styles | |
148 * @param parent the parent composite | |
149 * @param lws the LightweightSystem | |
150 * @since 3.4 | |
151 */ | |
152 public this(int style, Composite parent, LightweightSystem lws) { | |
153 this.lws = lws; | |
154 vBarVisibility = AUTOMATIC; | |
155 hBarVisibility = AUTOMATIC; | |
156 | |
157 horizontalChangeListener = new class() PropertyChangeListener { | |
158 public void propertyChange(PropertyChangeEvent event) { | |
159 RangeModel model = getViewport().getHorizontalRangeModel(); | |
160 hBarOffset = Math.max(0, -model.getMinimum()); | |
161 getHorizontalBar().setValues( | |
162 model.getValue() + hBarOffset, | |
163 model.getMinimum() + hBarOffset, | |
164 model.getMaximum() + hBarOffset, | |
165 model.getExtent(), | |
166 Math.max(1, model.getExtent() / 20), | |
167 Math.max(1, model.getExtent() * 3 / 4)); | |
168 } | |
169 }; | |
170 | |
171 verticalChangeListener = new class() PropertyChangeListener { | |
172 public void propertyChange(PropertyChangeEvent event) { | |
173 RangeModel model = getViewport().getVerticalRangeModel(); | |
174 vBarOffset = Math.max(0, -model.getMinimum()); | |
175 getVerticalBar().setValues( | |
176 model.getValue() + vBarOffset, | |
177 model.getMinimum() + vBarOffset, | |
178 model.getMaximum() + vBarOffset, | |
179 model.getExtent(), | |
180 Math.max(1, model.getExtent() / 20), | |
181 Math.max(1, model.getExtent() * 3 / 4)); | |
182 } | |
183 }; | |
184 super(parent, checkStyle(style)); | |
185 getHorizontalBar().setVisible(false); | |
186 getVerticalBar().setVisible(false); | |
187 lws.setControl(this); | |
188 hook(); | |
189 } | |
190 | |
191 /** | |
192 * Constructor | |
193 * @param parent the parent composite | |
194 * @param style look at class javadoc for valid styles | |
195 * @param lws the lightweight system | |
196 * @since 3.1 | |
197 */ | |
198 public this(Composite parent, int style, LightweightSystem lws) { | |
199 this(style | DEFAULT_STYLES, parent, lws); | |
200 } | |
201 | |
202 private static int checkStyle(int style) { | |
203 if ((style & REQUIRED_STYLES) !is REQUIRED_STYLES) | |
204 throw new IllegalArgumentException("Required style missing on FigureCanvas"); //$NON-NLS-1$ | |
205 if ((style & ~ACCEPTED_STYLES) !is 0) | |
206 throw new IllegalArgumentException("Invalid style being set on FigureCanvas"); //$NON-NLS-1$ | |
207 return style; | |
208 } | |
209 | |
210 /** | |
211 * @see dwt.widgets.Composite#computeSize(int, int, bool) | |
212 */ | |
213 public dwt.graphics.Point.Point computeSize(int wHint, int hHint, bool changed) { | |
214 // TODO not accounting for scrollbars and trim | |
215 Dimension size = getLightweightSystem().getRootFigure().getPreferredSize(wHint, hHint); | |
216 size.union_(new Dimension(wHint, hHint)); | |
217 return new dwt.graphics.Point.Point(size.width, size.height); | |
218 } | |
219 | |
220 /** | |
221 * @return the contents of the {@link Viewport}. | |
222 */ | |
223 public IFigure getContents() { | |
224 return getViewport().getContents(); | |
225 } | |
226 | |
227 /** | |
228 * @see dwt.widgets.Control#getFont() | |
229 */ | |
230 public Font getFont() { | |
231 if (font is null) | |
232 font = super.getFont(); | |
233 return font; | |
234 } | |
235 | |
236 /** | |
237 * @return the horizontal scrollbar visibility. | |
238 */ | |
239 public int getHorizontalScrollBarVisibility() { | |
240 return hBarVisibility; | |
241 } | |
242 | |
243 /** | |
244 * @return the LightweightSystem | |
245 */ | |
246 public LightweightSystem getLightweightSystem() { | |
247 return lws; | |
248 } | |
249 | |
250 /** | |
251 * @return the vertical scrollbar visibility. | |
252 */ | |
253 public int getVerticalScrollBarVisibility() { | |
254 return vBarVisibility; | |
255 } | |
256 | |
257 /** | |
258 * Returns the Viewport. If it's <code>null</code>, a new one is created. | |
259 * @return the viewport | |
260 */ | |
261 public Viewport getViewport() { | |
262 if (viewport is null) | |
263 setViewport(new Viewport(true)); | |
264 return viewport; | |
265 } | |
266 | |
267 /** | |
268 * Adds listeners for scrolling. | |
269 */ | |
270 private void hook() { | |
271 getLightweightSystem().getUpdateManager().addUpdateListener(new class() UpdateListener { | |
272 public void notifyPainting(Rectangle damage, dwtx.dwtxhelper.Collection.Map dirtyRegions) { } | |
273 public void notifyValidating() { | |
274 if (!isDisposed()) | |
275 layoutViewport(); | |
276 } | |
277 }); | |
278 | |
279 getHorizontalBar().addSelectionListener(new class() SelectionAdapter { | |
280 public void widgetSelected(SelectionEvent event) { | |
281 scrollToX(getHorizontalBar().getSelection() - hBarOffset); | |
282 } | |
283 }); | |
284 | |
285 getVerticalBar().addSelectionListener(new class() SelectionAdapter { | |
286 public void widgetSelected(SelectionEvent event) { | |
287 scrollToY(getVerticalBar().getSelection() - vBarOffset); | |
288 } | |
289 }); | |
290 } | |
291 | |
292 private void hookViewport() { | |
293 getViewport() | |
294 .getHorizontalRangeModel() | |
295 .addPropertyChangeListener(horizontalChangeListener); | |
296 getViewport() | |
297 .getVerticalRangeModel() | |
298 .addPropertyChangeListener(verticalChangeListener); | |
299 } | |
300 | |
301 private void unhookViewport() { | |
302 getViewport() | |
303 .getHorizontalRangeModel() | |
304 .removePropertyChangeListener(horizontalChangeListener); | |
305 getViewport() | |
306 .getVerticalRangeModel() | |
307 .removePropertyChangeListener(verticalChangeListener); | |
308 } | |
309 | |
310 private void layoutViewport() { | |
311 ScrollPaneSolver.Result result; | |
312 result = ScrollPaneSolver.solve((new Rectangle(getBounds())).setLocation(0, 0), | |
313 getViewport(), | |
314 getHorizontalScrollBarVisibility(), | |
315 getVerticalScrollBarVisibility(), | |
316 computeTrim(0, 0, 0, 0).width, | |
317 computeTrim(0, 0, 0, 0).height); | |
318 getLightweightSystem().setIgnoreResize(true); | |
319 try { | |
320 if (getHorizontalBar().getVisible() !is result.showH) | |
321 getHorizontalBar().setVisible(result.showH); | |
322 if (getVerticalBar().getVisible() !is result.showV) | |
323 getVerticalBar().setVisible(result.showV); | |
324 Rectangle r = new Rectangle(getClientArea()); | |
325 r.setLocation(0, 0); | |
326 getLightweightSystem().getRootFigure().setBounds(r); | |
327 } finally { | |
328 getLightweightSystem().setIgnoreResize(false); | |
329 } | |
330 } | |
331 | |
332 /** | |
333 * Scrolls in an animated way to the new x and y location. | |
334 * @param x the x coordinate to scroll to | |
335 * @param y the y coordinate to scroll to | |
336 */ | |
337 public void scrollSmoothTo(int x, int y) { | |
338 // Ensure newHOffset and newVOffset are within the appropriate ranges | |
339 x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x); | |
340 y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y); | |
341 | |
342 int oldX = getViewport().getViewLocation().x; | |
343 int oldY = getViewport().getViewLocation().y; | |
344 int dx = x - oldX; | |
345 int dy = y - oldY; | |
346 | |
347 if (dx is 0 && dy is 0) | |
348 return; //Nothing to do. | |
349 | |
350 Dimension viewingArea = getViewport().getClientArea().getSize(); | |
351 | |
352 int minFrames = 3; | |
353 int maxFrames = 6; | |
354 if (dx is 0 || dy is 0) { | |
355 minFrames = 6; | |
356 maxFrames = 13; | |
357 } | |
358 int frames = (Math.abs(dx) + Math.abs(dy)) / 15; | |
359 frames = Math.max(frames, minFrames); | |
360 frames = Math.min(frames, maxFrames); | |
361 | |
362 int stepX = Math.min((dx / frames), (viewingArea.width / 3)); | |
363 int stepY = Math.min((dy / frames), (viewingArea.height / 3)); | |
364 | |
365 for (int i = 1; i < frames; i++) { | |
366 scrollTo(oldX + i * stepX, oldY + i * stepY); | |
367 getViewport().getUpdateManager().performUpdate(); | |
368 } | |
369 scrollTo(x, y); | |
370 } | |
371 | |
372 /** | |
373 * Scrolls the contents to the new x and y location. If this scroll operation only | |
374 * consists of a vertical or horizontal scroll, a call will be made to | |
375 * {@link #scrollToY(int)} or {@link #scrollToX(int)}, respectively, to increase | |
376 * performance. | |
377 * | |
378 * @param x the x coordinate to scroll to | |
379 * @param y the y coordinate to scroll to | |
380 */ | |
381 public void scrollTo(int x, int y) { | |
382 x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x); | |
383 y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y); | |
384 if (x is getViewport().getViewLocation().x) | |
385 scrollToY(y); | |
386 else if (y is getViewport().getViewLocation().y) | |
387 scrollToX(x); | |
388 else | |
389 getViewport().setViewLocation(x, y); | |
390 } | |
391 /** | |
392 * Scrolls the contents horizontally so that they are offset by <code>hOffset</code>. | |
393 * | |
394 * @param hOffset the new horizontal offset | |
395 */ | |
396 public void scrollToX(int hOffset) { | |
397 hOffset = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), hOffset); | |
398 int hOffsetOld = getViewport().getViewLocation().x; | |
399 if (hOffset is hOffsetOld) | |
400 return; | |
401 int dx = -hOffset + hOffsetOld; | |
402 | |
403 Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets()); | |
404 Rectangle blit = clientArea.getResized(-Math.abs(dx), 0); | |
405 Rectangle expose = clientArea.getCopy(); | |
406 Point dest = clientArea.getTopLeft(); | |
407 expose.width = Math.abs(dx); | |
408 if (dx < 0) { //Moving left? | |
409 blit.translate(-dx, 0); //Move blit area to the right | |
410 expose.x = dest.x + blit.width; | |
411 } else //Moving right | |
412 dest.x += dx; //Move expose area to the right | |
413 | |
414 // fix for bug 41111 | |
415 Control[] children = getChildren(); | |
416 bool[] manualMove = new bool[children.length]; | |
417 for (int i = 0; i < children.length; i++) { | |
418 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); | |
419 manualMove[i] = blit.width <= 0 || bounds.x > blit.x + blit.width | |
420 || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x | |
421 || bounds.y + bounds.height < blit.y; | |
422 } | |
423 scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true); | |
424 for (int i = 0; i < children.length; i++) { | |
425 if (children[i].isDisposed ()) | |
426 continue; | |
427 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); | |
428 if (manualMove[i]) | |
429 children[i].setBounds(bounds.x + dx, bounds.y, bounds.width, bounds.height); | |
430 } | |
431 | |
432 getViewport().setIgnoreScroll(true); | |
433 getViewport().setHorizontalLocation(hOffset); | |
434 getViewport().setIgnoreScroll(false); | |
435 redraw(expose.x, expose.y, expose.width, expose.height, true); | |
436 } | |
437 | |
438 /** | |
439 * Scrolls the contents vertically so that they are offset by <code>vOffset</code>. | |
440 * | |
441 * @param vOffset the new vertical offset | |
442 */ | |
443 public void scrollToY(int vOffset) { | |
444 vOffset = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), vOffset); | |
445 int vOffsetOld = getViewport().getViewLocation().y; | |
446 if (vOffset is vOffsetOld) | |
447 return; | |
448 int dy = -vOffset + vOffsetOld; | |
449 | |
450 Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets()); | |
451 Rectangle blit = clientArea.getResized(0, -Math.abs(dy)); | |
452 Rectangle expose = clientArea.getCopy(); | |
453 Point dest = clientArea.getTopLeft(); | |
454 expose.height = Math.abs(dy); | |
455 if (dy < 0) { //Moving up? | |
456 blit.translate(0, -dy); //Move blit area down | |
457 expose.y = dest.y + blit.height; //Move expose area down | |
458 } else //Moving down | |
459 dest.y += dy; | |
460 | |
461 // fix for bug 41111 | |
462 Control[] children = getChildren(); | |
463 bool[] manualMove = new bool[children.length]; | |
464 for (int i = 0; i < children.length; i++) { | |
465 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); | |
466 manualMove[i] = blit.height <= 0 || bounds.x > blit.x + blit.width | |
467 || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x | |
468 || bounds.y + bounds.height < blit.y; | |
469 } | |
470 scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true); | |
471 for (int i = 0; i < children.length; i++) { | |
472 if (children[i].isDisposed ()) | |
473 continue; | |
474 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); | |
475 if (manualMove[i]) | |
476 children[i].setBounds(bounds.x, bounds.y + dy, bounds.width, bounds.height); | |
477 } | |
478 | |
479 getViewport().setIgnoreScroll(true); | |
480 getViewport().setVerticalLocation(vOffset); | |
481 getViewport().setIgnoreScroll(false); | |
482 redraw(expose.x, expose.y, expose.width, expose.height, true); | |
483 } | |
484 | |
485 /** | |
486 * Sets the given border on the LightweightSystem's root figure. | |
487 * | |
488 * @param border The new border | |
489 */ | |
490 public void setBorder(Border border) { | |
491 getLightweightSystem().getRootFigure().setBorder(border); | |
492 } | |
493 | |
494 /** | |
495 * Sets the contents of the {@link Viewport}. | |
496 * | |
497 * @param figure the new contents | |
498 */ | |
499 public void setContents(IFigure figure) { | |
500 getViewport().setContents(figure); | |
501 } | |
502 | |
503 /** | |
504 * @see dwt.widgets.Control#setFont(dwt.graphics.Font) | |
505 */ | |
506 public void setFont(Font font) { | |
507 this.font = font; | |
508 super.setFont(font); | |
509 } | |
510 | |
511 /** | |
512 * Sets the horizontal scrollbar visibility. Possible values are {@link #AUTOMATIC}, | |
513 * {@link #ALWAYS}, and {@link #NEVER}. | |
514 * | |
515 * @param v the new visibility | |
516 */ | |
517 public void setHorizontalScrollBarVisibility(int v) { | |
518 hBarVisibility = v; | |
519 } | |
520 | |
521 /** | |
522 * Sets both the horizontal and vertical scrollbar visibility to the given value. | |
523 * Possible values are {@link #AUTOMATIC}, {@link #ALWAYS}, and {@link #NEVER}. | |
524 * @param both the new visibility | |
525 */ | |
526 public void setScrollBarVisibility(int both) { | |
527 setHorizontalScrollBarVisibility(both); | |
528 setVerticalScrollBarVisibility(both); | |
529 } | |
530 | |
531 /** | |
532 * Sets the vertical scrollbar visibility. Possible values are {@link #AUTOMATIC}, | |
533 * {@link #ALWAYS}, and {@link #NEVER}. | |
534 * | |
535 * @param v the new visibility | |
536 */ | |
537 public void setVerticalScrollBarVisibility(int v) { | |
538 vBarVisibility = v; | |
539 } | |
540 | |
541 /** | |
542 * Sets the Viewport. The given Viewport must use "fake" scrolling. That is, it must be | |
543 * constructed using <code>new Viewport(true)</code>. | |
544 * | |
545 * @param vp the new viewport | |
546 */ | |
547 public void setViewport(Viewport vp) { | |
548 if (viewport !is null) | |
549 unhookViewport(); | |
550 viewport = vp; | |
551 lws.setContents(viewport); | |
552 hookViewport(); | |
553 } | |
554 | |
555 private int verifyScrollBarOffset(RangeModel model, int value) { | |
556 value = Math.max(model.getMinimum(), value); | |
557 return Math.min(model.getMaximum() - model.getExtent(), value); | |
558 } | |
559 | |
560 } |