comparison dwtx/draw2d/Clickable.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, 2005 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.Clickable;
14
15 import dwt.dwthelper.utils;
16
17 import dwtx.dwtxhelper.Collection;
18
19 import dwtx.draw2d.geometry.Rectangle;
20 import dwtx.draw2d.Figure;
21 import dwtx.draw2d.ActionListener;
22 import dwtx.draw2d.ActionEvent;
23 import dwtx.draw2d.ChangeListener;
24 import dwtx.draw2d.ClickableEventHandler;
25 import dwtx.draw2d.IFigure;
26 import dwtx.draw2d.ButtonModel;
27 import dwtx.draw2d.ChangeEvent;
28 import dwtx.draw2d.Graphics;
29 import dwtx.draw2d.ToggleModel;
30 import dwtx.draw2d.StackLayout;
31 import dwtx.draw2d.ColorConstants;
32 import dwtx.draw2d.ButtonBorder;
33
34
35 /**
36 * A Clickable responds to mouse clicks in some way (determined by a ClickBehavior) and
37 * fires action events. No visual appearance of feedback is offered. Depends on a model
38 * holder and an event handler which understands the model and updates the model
39 * accordingly. {@link ButtonModel} is used by default. Any figure can be set as contents
40 * to a Clickable. Clickable->EventHandler->Model->ModelObserver->Listeners of actions.
41 */
42 public class Clickable
43 : Figure
44 {
45
46 private static const int
47 ROLLOVER_ENABLED_FLAG = Figure.MAX_FLAG << 1,
48 STYLE_BUTTON_FLAG = Figure.MAX_FLAG << 2,
49 STYLE_TOGGLE_FLAG = Figure.MAX_FLAG << 3;
50
51 /**
52 * The highest reserved flag used by this class
53 */
54 protected static int
55 MAX_FLAG = STYLE_TOGGLE_FLAG;
56
57 /**
58 * Style constant that defines a push button. The button will be pressed when the mouse is
59 * pressed on top of it. The button will be released when the mouse is released or is move
60 * off of the button.
61 *
62 */
63 public static const int STYLE_BUTTON = STYLE_BUTTON_FLAG;
64
65 /**
66 * Style constant that defines a toggle button. The button will toggle between 2 states
67 * when the mouse is clicked on the button.
68 */
69 public static const int STYLE_TOGGLE = STYLE_TOGGLE_FLAG;
70
71 /**
72 * An action is performed every time the mouse is released.
73 */
74 public static const int DEFAULT_FIRING = 0;
75
76 /**
77 * Firing starts as soon as the mouse is pressed on this Clickable, and keeps firing at
78 * prefixed intervals until the mouse is released.
79 */
80 public static const int REPEAT_FIRING = 1;
81
82 /**
83 * Observes the model for action and state changes.
84 *
85 * @see ActionListener
86 * @see ChangeListener
87 */
88 /+static+/ interface ModelObserver
89 : ActionListener, ChangeListener
90 { }
91
92 private ClickableEventHandler eventHandler;
93
94 private ButtonModel model;
95
96 private ModelObserver modelObserver;
97
98 private void instanceInit(){
99 init();
100 setRequestFocusEnabled(true);
101 setFocusTraversable(true);
102 }
103
104 /**
105 * Constructs a Clickable with no contents.
106 */
107 public this() {
108 instanceInit();
109 }
110
111 /**
112 * Constructs a Clickable whose contents are provided as input. The content figure
113 * occupies the entire region of the Clickable.
114 *
115 * @param contents The content figure
116 */
117 public this(IFigure contents) {
118 this(contents, 0);
119 }
120
121 /**
122 * Constructs a Clickable whose contents are provided as input. The content figure
123 * occupies the entire region of the Clickable. Sets the style to the given <i>style</i>
124 * (either {@link #STYLE_BUTTON} or {@link #STYLE_TOGGLE}).
125 *
126 * @param contents The content figure
127 * @param style The button style
128 */
129 public this(IFigure contents, int style) {
130 instanceInit();
131 setContents(contents);
132 setStyle(style);
133 }
134
135 /**
136 * Adds the given listener to the list of action listeners of this Figure. Listener is
137 * called whenever an action is performed.
138 *
139 * @param listener The ActionListener to be added
140 * @since 2.0
141 */
142 public void addActionListener(ActionListener listener) {
143 addListener(ActionListener.classinfo, cast(Object)listener);
144 }
145
146 /**
147 * Adds the given listener to the list of state change listeners of this figure. A
148 * ChangeListener is informed if there is any state change in the model requiring action
149 * by the listener.
150 *
151 * @param listener The ChangeListener to be added
152 * @since 2.0
153 */
154 public void addChangeListener(ChangeListener listener) {
155 addListener(ChangeListener.classinfo, cast(Object)listener);
156 }
157
158 /**
159 * Returns a newly created ButtonModel as the default model to be used by this Clickable
160 * based on the button style.
161 *
162 * @return The model to be used by default
163 * @since 2.0
164 */
165 protected ButtonModel createDefaultModel() {
166 if (isStyle(STYLE_TOGGLE))
167 return new ToggleModel();
168 else
169 return new ButtonModel();
170 }
171
172 /**
173 * Returns a newly created event handler for this Clickable and its model.
174 *
175 * @return The event handler
176 * @since 2.0
177 */
178 protected ClickableEventHandler createEventHandler() {
179 return new ClickableEventHandler();
180 }
181
182 /**
183 * Returns a newly created model observer which listens to the model, and fires any action
184 * or state changes. A ModelObserver holds both an action listener and a state change
185 * listener.
186 *
187 * @return The newly created model observer
188 * @since 2.0
189 */
190 protected ModelObserver createModelObserver() {
191 return new class() ModelObserver {
192 public void actionPerformed(ActionEvent action) {
193 fireActionPerformed();
194 }
195 public void handleStateChanged(ChangeEvent change) {
196 fireStateChanged(change);
197 }
198 };
199 }
200
201 /**
202 * Fires an action performed event.
203 *
204 * @since 2.0
205 */
206 public void doClick() {
207 fireActionPerformed();
208 }
209
210 /**
211 * Called when there has been an action performed by this Clickable, which is determined
212 * by the model. Notifies all ActionListener type listeners of an action performed.
213 *
214 * @since 2.0
215 */
216 protected void fireActionPerformed() {
217 ActionEvent action = new ActionEvent(this);
218 Iterator listeners = getListeners(ActionListener.classinfo);
219 while (listeners.hasNext())
220 (cast(ActionListener)listeners.next()) //Leave newline for debug stepping
221 .actionPerformed(action);
222 }
223
224 /**
225 * Called when there has been a change of state in the model of this clickable. Notifies
226 * all ChangeListener type listeners of the state change.
227 *
228 * @param modelChange The ChangeEvent
229 * @since 2.0
230 */
231 protected void fireStateChanged(ChangeEvent modelChange) {
232 ChangeEvent change = new ChangeEvent(this, modelChange.getPropertyName());
233 Iterator listeners = getListeners(ChangeListener.classinfo);
234 while (listeners.hasNext())
235 (cast(ChangeListener)listeners.next()) //Leave newline for debug stepping
236 .handleStateChanged(change);
237 }
238
239 /**
240 * Returns the behavior model used by this Clickable.
241 *
242 * @return The model used by this Clickable
243 * @since 2.0
244 */
245 public ButtonModel getModel() {
246 return model;
247 }
248
249 /**
250 * Adds the given ClickableEventHandler to this clickable. A ClickableEventHandler
251 * should be a MouseListener, MouseMotionListener, ChangeListener, KeyListener, and
252 * FocusListener.
253 *
254 * @param handler The new event handler
255 * @since 2.0
256 */
257 protected void hookEventHandler(ClickableEventHandler handler) {
258 if (handler is null)
259 return;
260 addMouseListener(handler);
261 addMouseMotionListener(handler);
262 addChangeListener(handler);
263 addKeyListener(handler);
264 addFocusListener(handler);
265 }
266
267 /**
268 * Initializes this Clickable by setting a default model and adding a clickable event
269 * handler for that model.
270 *
271 * @since 2.0
272 */
273 protected void init() {
274 setModel(createDefaultModel());
275 setEventHandler(createEventHandler());
276 }
277
278 /**
279 * Returns <code>true</code> if rollover feedback is enabled.
280 *
281 * @return <code>true</code> rollover feedback is enabled
282 * @since 2.0
283 */
284 public bool isRolloverEnabled() {
285 return (flags & ROLLOVER_ENABLED_FLAG) !is 0;
286 }
287
288 /**
289 * Returns <code>true</code> if this Clickable is in a selected state. The model is the
290 * one which holds all this state based information.
291 *
292 * @return <code>true</code> if this Clickable is in a selected state
293 * @since 2.0
294 */
295 public bool isSelected() {
296 return getModel().isSelected();
297 }
298
299 /**
300 * Returns <code>true</code> if this Clickable's style is the same as the passed style.
301 *
302 * @param style The style to be checked
303 * @return <code>true</code> if this Clickable's style is the same as the passed style
304 * @since 2.0
305 */
306 public bool isStyle(int style) {
307 return ((style & flags) is style);
308 }
309
310 /**
311 * If this Clickable has focus, this method paints a focus rectangle.
312 *
313 * @param graphics Graphics handle for painting
314 */
315 protected void paintBorder(Graphics graphics) {
316 super.paintBorder(graphics);
317 if (hasFocus()) {
318 graphics.setForegroundColor(ColorConstants.black);
319 graphics.setBackgroundColor(ColorConstants.white);
320
321 Rectangle area = getClientArea();
322 if (isStyle(STYLE_BUTTON))
323 graphics.drawFocus(area.x, area.y, area.width, area.height);
324 else
325 graphics.drawFocus(area.x, area.y, area.width - 1, area.height - 1);
326 }
327 }
328
329 /**
330 * Paints the area of this figure excluded by the borders. Induces a (1,1) pixel shift in
331 * the painting if the mouse is armed, giving it the pressed appearance.
332 *
333 * @param graphics Graphics handle for painting
334 * @since 2.0
335 */
336 protected void paintClientArea(Graphics graphics) {
337 if (isStyle(STYLE_BUTTON) && (getModel().isArmed() || getModel().isSelected())) {
338 graphics.translate(1, 1);
339 graphics.pushState();
340 super.paintClientArea(graphics);
341 graphics.popState();
342 graphics.translate(-1, -1);
343 } else
344 super.paintClientArea(graphics);
345 }
346
347 /**
348 * Removes the given listener from the list of ActionListener's of this Clickable.
349 *
350 * @param listener Listener to be removed from this figure
351 * @since 2.0
352 */
353 public void removeActionListener(ActionListener listener) {
354 removeListener(ActionListener.classinfo, cast(Object)listener);
355 }
356
357 /**
358 * Removes the given listener from the list of ChangeListener's of this clickable.
359 *
360 * @param listener Listener to be removed from this figure
361 * @since 2.0
362 */
363 public void removeChangeListener(ChangeListener listener) {
364 removeListener(ChangeListener.classinfo, cast(Object)listener);
365 }
366
367 /**
368 * Sets the Figure which is the contents of this Clickable. This Figure occupies the
369 * entire clickable region.
370 *
371 * @param contents Contents of the clickable
372 * @since 2.0
373 */
374 protected void setContents(IFigure contents) {
375 setLayoutManager(new StackLayout());
376 if (getChildren().size() > 0)
377 remove(cast(IFigure)getChildren().get(0));
378 add(contents);
379 }
380
381 /**
382 * @see dwtx.draw2d.IFigure#setEnabled(bool)
383 */
384 public void setEnabled(bool value) {
385 if (isEnabled() is value)
386 return;
387 super.setEnabled(value);
388 getModel().setEnabled(value);
389 setChildrenEnabled(value);
390 }
391
392 /**
393 * Sets the event handler which interacts with the model to determine the behavior of this
394 * Clickable.
395 *
396 * @param h Event handler for this clickable
397 * @since 2.0
398 */
399 public void setEventHandler(ClickableEventHandler h) {
400 if (eventHandler !is null)
401 unhookEventHandler(eventHandler);
402 eventHandler = h;
403 if (eventHandler !is null)
404 hookEventHandler(eventHandler);
405 }
406
407 /**
408 * Determines how this clickable is to fire notifications to its listeners. In the default
409 * firing method ({@link #DEFAULT_FIRING}), an action is performed every time the mouse
410 * is released. In the repeat firing method ({@link #REPEAT_FIRING}), firing starts as
411 * soon as it is pressed on this clickable, and keeps firing at prefixed intervals until
412 * the mouse is released.
413 *
414 * @param type Type of firing
415 * @since 2.0
416 */
417 public void setFiringMethod(int type) {
418 getModel().setFiringBehavior(type);
419 }
420
421 /**
422 * Sets the model to be used by this clickable for its state and behavior determination.
423 * This clickable removes any observers from the previous model before adding new ones to
424 * the new model.
425 *
426 * @param model The new model of this Clickable
427 * @since 2.0
428 */
429 public void setModel(ButtonModel model) {
430 if (this.model !is null) {
431 this.model.removeChangeListener(modelObserver);
432 this.model.removeActionListener(modelObserver);
433 modelObserver = null;
434 }
435 this.model = model;
436 if (model !is null) {
437 modelObserver = createModelObserver();
438 model.addActionListener(modelObserver);
439 model.addChangeListener(modelObserver);
440 }
441 }
442
443 /**
444 * Enables or disables rollover feedback of this figure. Generally used in conjunction
445 * with the model to determine if feedback is to be shown.
446 *
447 * @param value The rollover state to be set
448 * @since 2.0
449 */
450 public void setRolloverEnabled(bool value) {
451 if (isRolloverEnabled() is value)
452 return;
453 setFlag(ROLLOVER_ENABLED_FLAG, value);
454 repaint();
455 }
456
457 /**
458 * Sets the selected state of this Clickable. Since the model is responsible for all state
459 * based information, it is informed of the state change. Extending classes can choose
460 * selection information, if they do not represent any selection.
461 *
462 * @param value New selected state of this clickable.
463 * @see #isSelected()
464 * @since 2.0
465 */
466 public void setSelected(bool value) {
467 getModel().setSelected(value);
468 }
469
470 /**
471 * Sets this Clickable's style to the passed value, either {@link #STYLE_BUTTON} or
472 * {@link #STYLE_TOGGLE}.
473 *
474 * @param style The button style
475 * @since 2.0
476 */
477 public void setStyle(int style) {
478 if ((style & STYLE_BUTTON) !is 0) {
479 setFlag(STYLE_BUTTON_FLAG, true);
480 if (!(null !is cast(ButtonBorder)getBorder() ))
481 setBorder(new ButtonBorder());
482 setOpaque(true);
483 } else {
484 setFlag(STYLE_BUTTON_FLAG, false);
485 setOpaque(false);
486 }
487
488 if ((style & STYLE_TOGGLE) !is 0) {
489 setFlag(STYLE_TOGGLE_FLAG, true);
490 setModel(createDefaultModel());
491 }
492 }
493
494 /**
495 * Removes the given ClickableEventHandler containing listeners from this Clickable.
496 *
497 * @param handler The event handler to be removed
498 * @since 2.0
499 */
500 protected void unhookEventHandler(ClickableEventHandler handler) {
501 if (handler is null)
502 return;
503 removeMouseListener(handler);
504 removeMouseMotionListener(handler);
505 removeChangeListener(handler);
506 }
507
508 }