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);
+}
+
+}