Mercurial > projects > dwt-addons
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/FigureCanvas.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,560 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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.FigureCanvas; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Bean; +static import dwtx.dwtxhelper.Collection; + +import dwt.DWT; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.graphics.Font; +import dwt.widgets.Canvas; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwtx.draw2d.geometry.Dimension; +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.Viewport; +import dwtx.draw2d.LightweightSystem; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.Border; +import dwtx.draw2d.RangeModel; +static import dwt.graphics.Point; +static import dwt.graphics.Rectangle; +import dwtx.draw2d.UpdateListener; +import dwtx.draw2d.ScrollPaneSolver; + +/** + * A Canvas that contains {@link Figure Figures}. + * + * <dl> + * <dt><b>Required Styles (when using certain constructors):</b></dt> + * <dd>V_SCROLL, H_SCROLL, NO_REDRAW_RESIZE</dd> + * <dt><b>Optional Styles:</b></dt> + * <dd>DOUBLE_BUFFERED, RIGHT_TO_LEFT, LEFT_TO_RIGHT, NO_BACKGROUND, BORDER</dd> + * </dl> + * <p> + * Note: Only one of the styles RIGHT_TO_LEFT, LEFT_TO_RIGHT may be specified. + * </p> + */ +public class FigureCanvas + : Canvas +{ + +private static final int ACCEPTED_STYLES = + DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT + | DWT.V_SCROLL | DWT.H_SCROLL + | DWT.NO_BACKGROUND + | DWT.NO_REDRAW_RESIZE + | DWT.DOUBLE_BUFFERED + | DWT.BORDER; + +/** + * The default styles are mixed in when certain constructors are used. This + * constant is a bitwise OR of the following DWT style constants: + * <UL> + * <LI>{@link DWT#NO_REDRAW_RESIZE}</LI> + * <LI>{@link DWT#NO_BACKGROUND}</LI> + * <LI>{@link DWT#V_SCROLL}</LI> + * <LI>{@link DWT#H_SCROLL}</LI> + * </UL> + */ +static const int DEFAULT_STYLES = + DWT.NO_REDRAW_RESIZE + | DWT.NO_BACKGROUND + | DWT.V_SCROLL + | DWT.H_SCROLL; + +private static const int REQUIRED_STYLES = + DWT.NO_REDRAW_RESIZE + | DWT.V_SCROLL + | DWT.H_SCROLL; + +/** Never show scrollbar */ +public static int NEVER = 0; +/** Automatically show scrollbar when needed */ +public static int AUTOMATIC = 1; +/** Always show scrollbar */ +public static int ALWAYS = 2; + +private int vBarVisibility; +private int hBarVisibility; +private Viewport viewport; +private Font font; +private int hBarOffset; +private int vBarOffset; + +private PropertyChangeListener horizontalChangeListener; +private PropertyChangeListener verticalChangeListener; +private const LightweightSystem lws; + +/** + * Creates a new FigureCanvas with the given parent and the {@link #DEFAULT_STYLES}. + * + * @param parent the parent + */ +public this(Composite parent) { + this(parent, DWT.DOUBLE_BUFFERED, new LightweightSystem()); +} + +/** + * Constructor which applies the default styles plus any optional styles indicated. + * @param parent the parent composite + * @param style see the class javadoc for optional styles + * @since 3.1 + */ +public this(Composite parent, int style) { + this(parent, style, new LightweightSystem()); +} + +/** + * Constructor which uses the given styles verbatim. Certain styles must be used + * with this class. Refer to the class javadoc for more details. + * @param style see the class javadoc for <b>required</b> and optional styles + * @param parent the parent composite + * @since 3.4 + */ +public this(int style, Composite parent) { + this(style, parent, new LightweightSystem()); +} + +/** + * Constructs a new FigureCanvas with the given parent and LightweightSystem, using + * the {@link #DEFAULT_STYLES}. + * @param parent the parent + * @param lws the LightweightSystem + */ +public this(Composite parent, LightweightSystem lws) { + this(parent, DWT.DOUBLE_BUFFERED, lws); +} + +/** + * Constructor taking a lightweight system and DWT style, which is used verbatim. + * Certain styles must be used with this class. Refer to the class javadoc for + * more details. + * @param style see the class javadoc for <b>required</b> and optional styles + * @param parent the parent composite + * @param lws the LightweightSystem + * @since 3.4 + */ +public this(int style, Composite parent, LightweightSystem lws) { + this.lws = lws; + vBarVisibility = AUTOMATIC; + hBarVisibility = AUTOMATIC; + + horizontalChangeListener = new class() PropertyChangeListener { + public void propertyChange(PropertyChangeEvent event) { + RangeModel model = getViewport().getHorizontalRangeModel(); + hBarOffset = Math.max(0, -model.getMinimum()); + getHorizontalBar().setValues( + model.getValue() + hBarOffset, + model.getMinimum() + hBarOffset, + model.getMaximum() + hBarOffset, + model.getExtent(), + Math.max(1, model.getExtent() / 20), + Math.max(1, model.getExtent() * 3 / 4)); + } + }; + + verticalChangeListener = new class() PropertyChangeListener { + public void propertyChange(PropertyChangeEvent event) { + RangeModel model = getViewport().getVerticalRangeModel(); + vBarOffset = Math.max(0, -model.getMinimum()); + getVerticalBar().setValues( + model.getValue() + vBarOffset, + model.getMinimum() + vBarOffset, + model.getMaximum() + vBarOffset, + model.getExtent(), + Math.max(1, model.getExtent() / 20), + Math.max(1, model.getExtent() * 3 / 4)); + } + }; + super(parent, checkStyle(style)); + getHorizontalBar().setVisible(false); + getVerticalBar().setVisible(false); + lws.setControl(this); + hook(); +} + +/** + * Constructor + * @param parent the parent composite + * @param style look at class javadoc for valid styles + * @param lws the lightweight system + * @since 3.1 + */ +public this(Composite parent, int style, LightweightSystem lws) { + this(style | DEFAULT_STYLES, parent, lws); +} + +private static int checkStyle(int style) { + if ((style & REQUIRED_STYLES) !is REQUIRED_STYLES) + throw new IllegalArgumentException("Required style missing on FigureCanvas"); //$NON-NLS-1$ + if ((style & ~ACCEPTED_STYLES) !is 0) + throw new IllegalArgumentException("Invalid style being set on FigureCanvas"); //$NON-NLS-1$ + return style; +} + +/** + * @see dwt.widgets.Composite#computeSize(int, int, bool) + */ +public dwt.graphics.Point.Point computeSize(int wHint, int hHint, bool changed) { + // TODO not accounting for scrollbars and trim + Dimension size = getLightweightSystem().getRootFigure().getPreferredSize(wHint, hHint); + size.union_(new Dimension(wHint, hHint)); + return new dwt.graphics.Point.Point(size.width, size.height); +} + +/** + * @return the contents of the {@link Viewport}. + */ +public IFigure getContents() { + return getViewport().getContents(); +} + +/** + * @see dwt.widgets.Control#getFont() + */ +public Font getFont() { + if (font is null) + font = super.getFont(); + return font; +} + +/** + * @return the horizontal scrollbar visibility. + */ +public int getHorizontalScrollBarVisibility() { + return hBarVisibility; +} + +/** + * @return the LightweightSystem + */ +public LightweightSystem getLightweightSystem() { + return lws; +} + +/** + * @return the vertical scrollbar visibility. + */ +public int getVerticalScrollBarVisibility() { + return vBarVisibility; +} + +/** + * Returns the Viewport. If it's <code>null</code>, a new one is created. + * @return the viewport + */ +public Viewport getViewport() { + if (viewport is null) + setViewport(new Viewport(true)); + return viewport; +} + +/** + * Adds listeners for scrolling. + */ +private void hook() { + getLightweightSystem().getUpdateManager().addUpdateListener(new class() UpdateListener { + public void notifyPainting(Rectangle damage, dwtx.dwtxhelper.Collection.Map dirtyRegions) { } + public void notifyValidating() { + if (!isDisposed()) + layoutViewport(); + } + }); + + getHorizontalBar().addSelectionListener(new class() SelectionAdapter { + public void widgetSelected(SelectionEvent event) { + scrollToX(getHorizontalBar().getSelection() - hBarOffset); + } + }); + + getVerticalBar().addSelectionListener(new class() SelectionAdapter { + public void widgetSelected(SelectionEvent event) { + scrollToY(getVerticalBar().getSelection() - vBarOffset); + } + }); +} + +private void hookViewport() { + getViewport() + .getHorizontalRangeModel() + .addPropertyChangeListener(horizontalChangeListener); + getViewport() + .getVerticalRangeModel() + .addPropertyChangeListener(verticalChangeListener); +} + +private void unhookViewport() { + getViewport() + .getHorizontalRangeModel() + .removePropertyChangeListener(horizontalChangeListener); + getViewport() + .getVerticalRangeModel() + .removePropertyChangeListener(verticalChangeListener); +} + +private void layoutViewport() { + ScrollPaneSolver.Result result; + result = ScrollPaneSolver.solve((new Rectangle(getBounds())).setLocation(0, 0), + getViewport(), + getHorizontalScrollBarVisibility(), + getVerticalScrollBarVisibility(), + computeTrim(0, 0, 0, 0).width, + computeTrim(0, 0, 0, 0).height); + getLightweightSystem().setIgnoreResize(true); + try { + if (getHorizontalBar().getVisible() !is result.showH) + getHorizontalBar().setVisible(result.showH); + if (getVerticalBar().getVisible() !is result.showV) + getVerticalBar().setVisible(result.showV); + Rectangle r = new Rectangle(getClientArea()); + r.setLocation(0, 0); + getLightweightSystem().getRootFigure().setBounds(r); + } finally { + getLightweightSystem().setIgnoreResize(false); + } +} + +/** + * Scrolls in an animated way to the new x and y location. + * @param x the x coordinate to scroll to + * @param y the y coordinate to scroll to + */ +public void scrollSmoothTo(int x, int y) { + // Ensure newHOffset and newVOffset are within the appropriate ranges + x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x); + y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y); + + int oldX = getViewport().getViewLocation().x; + int oldY = getViewport().getViewLocation().y; + int dx = x - oldX; + int dy = y - oldY; + + if (dx is 0 && dy is 0) + return; //Nothing to do. + + Dimension viewingArea = getViewport().getClientArea().getSize(); + + int minFrames = 3; + int maxFrames = 6; + if (dx is 0 || dy is 0) { + minFrames = 6; + maxFrames = 13; + } + int frames = (Math.abs(dx) + Math.abs(dy)) / 15; + frames = Math.max(frames, minFrames); + frames = Math.min(frames, maxFrames); + + int stepX = Math.min((dx / frames), (viewingArea.width / 3)); + int stepY = Math.min((dy / frames), (viewingArea.height / 3)); + + for (int i = 1; i < frames; i++) { + scrollTo(oldX + i * stepX, oldY + i * stepY); + getViewport().getUpdateManager().performUpdate(); + } + scrollTo(x, y); +} + +/** + * Scrolls the contents to the new x and y location. If this scroll operation only + * consists of a vertical or horizontal scroll, a call will be made to + * {@link #scrollToY(int)} or {@link #scrollToX(int)}, respectively, to increase + * performance. + * + * @param x the x coordinate to scroll to + * @param y the y coordinate to scroll to + */ +public void scrollTo(int x, int y) { + x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x); + y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y); + if (x is getViewport().getViewLocation().x) + scrollToY(y); + else if (y is getViewport().getViewLocation().y) + scrollToX(x); + else + getViewport().setViewLocation(x, y); +} +/** + * Scrolls the contents horizontally so that they are offset by <code>hOffset</code>. + * + * @param hOffset the new horizontal offset + */ +public void scrollToX(int hOffset) { + hOffset = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), hOffset); + int hOffsetOld = getViewport().getViewLocation().x; + if (hOffset is hOffsetOld) + return; + int dx = -hOffset + hOffsetOld; + + Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets()); + Rectangle blit = clientArea.getResized(-Math.abs(dx), 0); + Rectangle expose = clientArea.getCopy(); + Point dest = clientArea.getTopLeft(); + expose.width = Math.abs(dx); + if (dx < 0) { //Moving left? + blit.translate(-dx, 0); //Move blit area to the right + expose.x = dest.x + blit.width; + } else //Moving right + dest.x += dx; //Move expose area to the right + + // fix for bug 41111 + Control[] children = getChildren(); + bool[] manualMove = new bool[children.length]; + for (int i = 0; i < children.length; i++) { + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + manualMove[i] = blit.width <= 0 || bounds.x > blit.x + blit.width + || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x + || bounds.y + bounds.height < blit.y; + } + scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true); + for (int i = 0; i < children.length; i++) { + if (children[i].isDisposed ()) + continue; + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + if (manualMove[i]) + children[i].setBounds(bounds.x + dx, bounds.y, bounds.width, bounds.height); + } + + getViewport().setIgnoreScroll(true); + getViewport().setHorizontalLocation(hOffset); + getViewport().setIgnoreScroll(false); + redraw(expose.x, expose.y, expose.width, expose.height, true); +} + +/** + * Scrolls the contents vertically so that they are offset by <code>vOffset</code>. + * + * @param vOffset the new vertical offset + */ +public void scrollToY(int vOffset) { + vOffset = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), vOffset); + int vOffsetOld = getViewport().getViewLocation().y; + if (vOffset is vOffsetOld) + return; + int dy = -vOffset + vOffsetOld; + + Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets()); + Rectangle blit = clientArea.getResized(0, -Math.abs(dy)); + Rectangle expose = clientArea.getCopy(); + Point dest = clientArea.getTopLeft(); + expose.height = Math.abs(dy); + if (dy < 0) { //Moving up? + blit.translate(0, -dy); //Move blit area down + expose.y = dest.y + blit.height; //Move expose area down + } else //Moving down + dest.y += dy; + + // fix for bug 41111 + Control[] children = getChildren(); + bool[] manualMove = new bool[children.length]; + for (int i = 0; i < children.length; i++) { + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + manualMove[i] = blit.height <= 0 || bounds.x > blit.x + blit.width + || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x + || bounds.y + bounds.height < blit.y; + } + scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true); + for (int i = 0; i < children.length; i++) { + if (children[i].isDisposed ()) + continue; + dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds(); + if (manualMove[i]) + children[i].setBounds(bounds.x, bounds.y + dy, bounds.width, bounds.height); + } + + getViewport().setIgnoreScroll(true); + getViewport().setVerticalLocation(vOffset); + getViewport().setIgnoreScroll(false); + redraw(expose.x, expose.y, expose.width, expose.height, true); +} + +/** + * Sets the given border on the LightweightSystem's root figure. + * + * @param border The new border + */ +public void setBorder(Border border) { + getLightweightSystem().getRootFigure().setBorder(border); +} + +/** + * Sets the contents of the {@link Viewport}. + * + * @param figure the new contents + */ +public void setContents(IFigure figure) { + getViewport().setContents(figure); +} + +/** + * @see dwt.widgets.Control#setFont(dwt.graphics.Font) + */ +public void setFont(Font font) { + this.font = font; + super.setFont(font); +} + +/** + * Sets the horizontal scrollbar visibility. Possible values are {@link #AUTOMATIC}, + * {@link #ALWAYS}, and {@link #NEVER}. + * + * @param v the new visibility + */ +public void setHorizontalScrollBarVisibility(int v) { + hBarVisibility = v; +} + +/** + * Sets both the horizontal and vertical scrollbar visibility to the given value. + * Possible values are {@link #AUTOMATIC}, {@link #ALWAYS}, and {@link #NEVER}. + * @param both the new visibility + */ +public void setScrollBarVisibility(int both) { + setHorizontalScrollBarVisibility(both); + setVerticalScrollBarVisibility(both); +} + +/** + * Sets the vertical scrollbar visibility. Possible values are {@link #AUTOMATIC}, + * {@link #ALWAYS}, and {@link #NEVER}. + * + * @param v the new visibility + */ +public void setVerticalScrollBarVisibility(int v) { + vBarVisibility = v; +} + +/** + * Sets the Viewport. The given Viewport must use "fake" scrolling. That is, it must be + * constructed using <code>new Viewport(true)</code>. + * + * @param vp the new viewport + */ +public void setViewport(Viewport vp) { + if (viewport !is null) + unhookViewport(); + viewport = vp; + lws.setContents(viewport); + hookViewport(); +} + +private int verifyScrollBarOffset(RangeModel model, int value) { + value = Math.max(model.getMinimum(), value); + return Math.min(model.getMaximum() - model.getExtent(), value); +} + +}