Mercurial > projects > dwt-addons
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Clickable.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,508 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.draw2d.Clickable; + +import dwt.dwthelper.utils; + +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Figure; +import dwtx.draw2d.ActionListener; +import dwtx.draw2d.ActionEvent; +import dwtx.draw2d.ChangeListener; +import dwtx.draw2d.ClickableEventHandler; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.ButtonModel; +import dwtx.draw2d.ChangeEvent; +import dwtx.draw2d.Graphics; +import dwtx.draw2d.ToggleModel; +import dwtx.draw2d.StackLayout; +import dwtx.draw2d.ColorConstants; +import dwtx.draw2d.ButtonBorder; + + +/** + * A Clickable responds to mouse clicks in some way (determined by a ClickBehavior) and + * fires action events. No visual appearance of feedback is offered. Depends on a model + * holder and an event handler which understands the model and updates the model + * accordingly. {@link ButtonModel} is used by default. Any figure can be set as contents + * to a Clickable. Clickable->EventHandler->Model->ModelObserver->Listeners of actions. + */ +public class Clickable + : Figure +{ + +private static const int + ROLLOVER_ENABLED_FLAG = Figure.MAX_FLAG << 1, + STYLE_BUTTON_FLAG = Figure.MAX_FLAG << 2, + STYLE_TOGGLE_FLAG = Figure.MAX_FLAG << 3; + +/** + * The highest reserved flag used by this class + */ +protected static int + MAX_FLAG = STYLE_TOGGLE_FLAG; + +/** + * Style constant that defines a push button. The button will be pressed when the mouse is + * pressed on top of it. The button will be released when the mouse is released or is move + * off of the button. + * + */ +public static const int STYLE_BUTTON = STYLE_BUTTON_FLAG; + +/** + * Style constant that defines a toggle button. The button will toggle between 2 states + * when the mouse is clicked on the button. + */ +public static const int STYLE_TOGGLE = STYLE_TOGGLE_FLAG; + +/** + * An action is performed every time the mouse is released. + */ +public static const int DEFAULT_FIRING = 0; + +/** + * Firing starts as soon as the mouse is pressed on this Clickable, and keeps firing at + * prefixed intervals until the mouse is released. + */ +public static const int REPEAT_FIRING = 1; + +/** + * Observes the model for action and state changes. + * + * @see ActionListener + * @see ChangeListener + */ +/+static+/ interface ModelObserver + : ActionListener, ChangeListener +{ } + +private ClickableEventHandler eventHandler; + +private ButtonModel model; + +private ModelObserver modelObserver; + +private void instanceInit(){ + init(); + setRequestFocusEnabled(true); + setFocusTraversable(true); +} + +/** + * Constructs a Clickable with no contents. + */ +public this() { + instanceInit(); +} + +/** + * Constructs a Clickable whose contents are provided as input. The content figure + * occupies the entire region of the Clickable. + * + * @param contents The content figure + */ +public this(IFigure contents) { + this(contents, 0); +} + +/** + * Constructs a Clickable whose contents are provided as input. The content figure + * occupies the entire region of the Clickable. Sets the style to the given <i>style</i> + * (either {@link #STYLE_BUTTON} or {@link #STYLE_TOGGLE}). + * + * @param contents The content figure + * @param style The button style + */ +public this(IFigure contents, int style) { + instanceInit(); + setContents(contents); + setStyle(style); +} + +/** + * Adds the given listener to the list of action listeners of this Figure. Listener is + * called whenever an action is performed. + * + * @param listener The ActionListener to be added + * @since 2.0 + */ +public void addActionListener(ActionListener listener) { + addListener(ActionListener.classinfo, cast(Object)listener); +} + +/** + * Adds the given listener to the list of state change listeners of this figure. A + * ChangeListener is informed if there is any state change in the model requiring action + * by the listener. + * + * @param listener The ChangeListener to be added + * @since 2.0 + */ +public void addChangeListener(ChangeListener listener) { + addListener(ChangeListener.classinfo, cast(Object)listener); +} + +/** + * Returns a newly created ButtonModel as the default model to be used by this Clickable + * based on the button style. + * + * @return The model to be used by default + * @since 2.0 + */ +protected ButtonModel createDefaultModel() { + if (isStyle(STYLE_TOGGLE)) + return new ToggleModel(); + else + return new ButtonModel(); +} + +/** + * Returns a newly created event handler for this Clickable and its model. + * + * @return The event handler + * @since 2.0 + */ +protected ClickableEventHandler createEventHandler() { + return new ClickableEventHandler(); +} + +/** + * Returns a newly created model observer which listens to the model, and fires any action + * or state changes. A ModelObserver holds both an action listener and a state change + * listener. + * + * @return The newly created model observer + * @since 2.0 + */ +protected ModelObserver createModelObserver() { + return new class() ModelObserver { + public void actionPerformed(ActionEvent action) { + fireActionPerformed(); + } + public void handleStateChanged(ChangeEvent change) { + fireStateChanged(change); + } + }; +} + +/** + * Fires an action performed event. + * + * @since 2.0 + */ +public void doClick() { + fireActionPerformed(); +} + +/** + * Called when there has been an action performed by this Clickable, which is determined + * by the model. Notifies all ActionListener type listeners of an action performed. + * + * @since 2.0 + */ +protected void fireActionPerformed() { + ActionEvent action = new ActionEvent(this); + Iterator listeners = getListeners(ActionListener.classinfo); + while (listeners.hasNext()) + (cast(ActionListener)listeners.next()) //Leave newline for debug stepping + .actionPerformed(action); +} + +/** + * Called when there has been a change of state in the model of this clickable. Notifies + * all ChangeListener type listeners of the state change. + * + * @param modelChange The ChangeEvent + * @since 2.0 + */ +protected void fireStateChanged(ChangeEvent modelChange) { + ChangeEvent change = new ChangeEvent(this, modelChange.getPropertyName()); + Iterator listeners = getListeners(ChangeListener.classinfo); + while (listeners.hasNext()) + (cast(ChangeListener)listeners.next()) //Leave newline for debug stepping + .handleStateChanged(change); +} + +/** + * Returns the behavior model used by this Clickable. + * + * @return The model used by this Clickable + * @since 2.0 + */ +public ButtonModel getModel() { + return model; +} + +/** + * Adds the given ClickableEventHandler to this clickable. A ClickableEventHandler + * should be a MouseListener, MouseMotionListener, ChangeListener, KeyListener, and + * FocusListener. + * + * @param handler The new event handler + * @since 2.0 + */ +protected void hookEventHandler(ClickableEventHandler handler) { + if (handler is null) + return; + addMouseListener(handler); + addMouseMotionListener(handler); + addChangeListener(handler); + addKeyListener(handler); + addFocusListener(handler); +} + +/** + * Initializes this Clickable by setting a default model and adding a clickable event + * handler for that model. + * + * @since 2.0 + */ +protected void init() { + setModel(createDefaultModel()); + setEventHandler(createEventHandler()); +} + +/** + * Returns <code>true</code> if rollover feedback is enabled. + * + * @return <code>true</code> rollover feedback is enabled + * @since 2.0 + */ +public bool isRolloverEnabled() { + return (flags & ROLLOVER_ENABLED_FLAG) !is 0; +} + +/** + * Returns <code>true</code> if this Clickable is in a selected state. The model is the + * one which holds all this state based information. + * + * @return <code>true</code> if this Clickable is in a selected state + * @since 2.0 + */ +public bool isSelected() { + return getModel().isSelected(); +} + +/** + * Returns <code>true</code> if this Clickable's style is the same as the passed style. + * + * @param style The style to be checked + * @return <code>true</code> if this Clickable's style is the same as the passed style + * @since 2.0 + */ +public bool isStyle(int style) { + return ((style & flags) is style); +} + +/** + * If this Clickable has focus, this method paints a focus rectangle. + * + * @param graphics Graphics handle for painting + */ +protected void paintBorder(Graphics graphics) { + super.paintBorder(graphics); + if (hasFocus()) { + graphics.setForegroundColor(ColorConstants.black); + graphics.setBackgroundColor(ColorConstants.white); + + Rectangle area = getClientArea(); + if (isStyle(STYLE_BUTTON)) + graphics.drawFocus(area.x, area.y, area.width, area.height); + else + graphics.drawFocus(area.x, area.y, area.width - 1, area.height - 1); + } +} + +/** + * Paints the area of this figure excluded by the borders. Induces a (1,1) pixel shift in + * the painting if the mouse is armed, giving it the pressed appearance. + * + * @param graphics Graphics handle for painting + * @since 2.0 + */ +protected void paintClientArea(Graphics graphics) { + if (isStyle(STYLE_BUTTON) && (getModel().isArmed() || getModel().isSelected())) { + graphics.translate(1, 1); + graphics.pushState(); + super.paintClientArea(graphics); + graphics.popState(); + graphics.translate(-1, -1); + } else + super.paintClientArea(graphics); +} + +/** + * Removes the given listener from the list of ActionListener's of this Clickable. + * + * @param listener Listener to be removed from this figure + * @since 2.0 + */ +public void removeActionListener(ActionListener listener) { + removeListener(ActionListener.classinfo, cast(Object)listener); +} + +/** + * Removes the given listener from the list of ChangeListener's of this clickable. + * + * @param listener Listener to be removed from this figure + * @since 2.0 + */ +public void removeChangeListener(ChangeListener listener) { + removeListener(ChangeListener.classinfo, cast(Object)listener); +} + +/** + * Sets the Figure which is the contents of this Clickable. This Figure occupies the + * entire clickable region. + * + * @param contents Contents of the clickable + * @since 2.0 + */ +protected void setContents(IFigure contents) { + setLayoutManager(new StackLayout()); + if (getChildren().size() > 0) + remove(cast(IFigure)getChildren().get(0)); + add(contents); +} + +/** + * @see dwtx.draw2d.IFigure#setEnabled(bool) + */ +public void setEnabled(bool value) { + if (isEnabled() is value) + return; + super.setEnabled(value); + getModel().setEnabled(value); + setChildrenEnabled(value); +} + +/** + * Sets the event handler which interacts with the model to determine the behavior of this + * Clickable. + * + * @param h Event handler for this clickable + * @since 2.0 + */ +public void setEventHandler(ClickableEventHandler h) { + if (eventHandler !is null) + unhookEventHandler(eventHandler); + eventHandler = h; + if (eventHandler !is null) + hookEventHandler(eventHandler); +} + +/** + * Determines how this clickable is to fire notifications to its listeners. In the default + * firing method ({@link #DEFAULT_FIRING}), an action is performed every time the mouse + * is released. In the repeat firing method ({@link #REPEAT_FIRING}), firing starts as + * soon as it is pressed on this clickable, and keeps firing at prefixed intervals until + * the mouse is released. + * + * @param type Type of firing + * @since 2.0 + */ +public void setFiringMethod(int type) { + getModel().setFiringBehavior(type); +} + +/** + * Sets the model to be used by this clickable for its state and behavior determination. + * This clickable removes any observers from the previous model before adding new ones to + * the new model. + * + * @param model The new model of this Clickable + * @since 2.0 + */ +public void setModel(ButtonModel model) { + if (this.model !is null) { + this.model.removeChangeListener(modelObserver); + this.model.removeActionListener(modelObserver); + modelObserver = null; + } + this.model = model; + if (model !is null) { + modelObserver = createModelObserver(); + model.addActionListener(modelObserver); + model.addChangeListener(modelObserver); + } +} + +/** + * Enables or disables rollover feedback of this figure. Generally used in conjunction + * with the model to determine if feedback is to be shown. + * + * @param value The rollover state to be set + * @since 2.0 + */ +public void setRolloverEnabled(bool value) { + if (isRolloverEnabled() is value) + return; + setFlag(ROLLOVER_ENABLED_FLAG, value); + repaint(); +} + +/** + * Sets the selected state of this Clickable. Since the model is responsible for all state + * based information, it is informed of the state change. Extending classes can choose + * selection information, if they do not represent any selection. + * + * @param value New selected state of this clickable. + * @see #isSelected() + * @since 2.0 + */ +public void setSelected(bool value) { + getModel().setSelected(value); +} + +/** + * Sets this Clickable's style to the passed value, either {@link #STYLE_BUTTON} or + * {@link #STYLE_TOGGLE}. + * + * @param style The button style + * @since 2.0 + */ +public void setStyle(int style) { + if ((style & STYLE_BUTTON) !is 0) { + setFlag(STYLE_BUTTON_FLAG, true); + if (!(null !is cast(ButtonBorder)getBorder() )) + setBorder(new ButtonBorder()); + setOpaque(true); + } else { + setFlag(STYLE_BUTTON_FLAG, false); + setOpaque(false); + } + + if ((style & STYLE_TOGGLE) !is 0) { + setFlag(STYLE_TOGGLE_FLAG, true); + setModel(createDefaultModel()); + } +} + +/** + * Removes the given ClickableEventHandler containing listeners from this Clickable. + * + * @param handler The event handler to be removed + * @since 2.0 + */ +protected void unhookEventHandler(ClickableEventHandler handler) { + if (handler is null) + return; + removeMouseListener(handler); + removeMouseMotionListener(handler); + removeChangeListener(handler); +} + +}