/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.elementclass.button; import java.awt.AlphaComposite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import org.simantics.g2d.element.ElementClass; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.SceneGraphNodeKey; import org.simantics.g2d.element.handler.Clickable; import org.simantics.g2d.element.handler.SceneGraph; import org.simantics.g2d.element.handler.Stateful; import org.simantics.g2d.element.handler.Text; import org.simantics.g2d.element.handler.Clickable.PressStatus; import org.simantics.g2d.element.handler.impl.AbstractClickable; import org.simantics.g2d.element.handler.impl.AbstractTogglable; import org.simantics.g2d.element.handler.impl.DefaultTransform; import org.simantics.g2d.element.handler.impl.Resizeable; import org.simantics.scenegraph.Node; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.utils.datastructures.hints.HintListenerAdapter; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.datastructures.hints.IHintContext.Key; /** * @author Toni Kalajainen */ public class ButtonClass { public static final ElementClass BUTTON_CLASS = ElementClass.compile( Clickable.INSTANCE, Text.INSTANCE, Resizeable.UNCONSTRICTED, DefaultTransform.INSTANCE, ButtonPaint.INSTANCE, Stateful.ENABLED_BY_DEFAULT ); static class ButtonPaint implements SceneGraph { private static final long serialVersionUID = 5484741334876153022L; public static final ButtonPaint INSTANCE = new ButtonPaint(); public static final Key SG_NODE = new SceneGraphNodeKey(Node.class, "SUB_SG_NODE"); @Override public void cleanup(IElement e) { Node node = e.removeHint(SG_NODE); if (node != null) node.remove(); } @Override public void init(IElement e, G2DParentNode parent) { ButtonNode node = (ButtonNode) e.getHint(SG_NODE); if (node == null) { node = parent.addNode(ButtonNode.class); e.setHint(SG_NODE, node); e.addKeyHintListener(ElementHints.KEY_TEXT, new HintListenerAdapter() { @Override public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { if (sender instanceof IElement) { ButtonNode node = (ButtonNode) ((IElement)sender).getHint(SG_NODE); node.buttonText = (String)newValue; node.repaint(); } } }); e.addKeyHintListener(AbstractClickable.PRESS_STATUS_KEY, new HintListenerAdapter() { @Override public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { if (sender instanceof IElement) { ButtonNode node = (ButtonNode) ((IElement)sender).getHint(SG_NODE); node.status = (PressStatus)newValue; node.repaint(); } } }); } Rectangle2D rect = ElementUtils.getElementBounds(e); ButtonColorProfile colors = ButtonColorProfile.DEFAULT; double height = rect.getHeight(); boolean enabled = isEnabled(e); // FIXME: context not supported by scenegraph PressStatus status = PressStatus.NORMAL;//ElementUtils.getPressStatus(e, ctx); Font font = new Font("Tahoma", 0, (int) height-6); Font selFont = new Font("Tahoma", Font.BOLD, (int) height-6); Text text = e.getElementClass().getAtMostOneItemOfClass(Text.class); String buttonText = null; if (text!=null) buttonText = text.getText(e); if (buttonText==null) buttonText = ""; boolean checked = isChecked(e); System.out.println("ButtonClass.init " + e + " " + status + " " + checked); node.init(status, rect, enabled, checked, colors, font, selFont, buttonText); // e.addKeyHintListener(AbstractClickable.PRESS_STATUS_KEY, new HintListenerAdapter() { // // @Override // public void hintChanged(IHintObservable sender, Key key, Object oldValue, // Object newValue) { // if (sender instanceof IElement) { // ButtonNode node = (ButtonNode) ((IElement)sender).getHint(SG_NODE); // node.setPressStatus((PressStatus)newValue); // node.repaint(); // } // } // }); } public static class ButtonNode extends G2DNode { private static final long serialVersionUID = 2291569851866542945L; PressStatus status = null; Rectangle2D rect = null; Boolean enabled = null; Boolean checked = null; ButtonColorProfile colors = null; Font font = null; Font selFont = null; String buttonText = null; @Override public Rectangle2D getBoundsInLocal() { return rect; } @SyncField("pressStatus") void setPressStatus(PressStatus pressStatus) { this.status = pressStatus; } public void init(PressStatus status, Rectangle2D rect, boolean enabled, boolean checked, ButtonColorProfile colors, Font font, Font selFont, String buttonText) { this.status = status; this.rect = rect; this.enabled = enabled; this.checked = checked; this.colors = colors; this.font = font; this.selFont = selFont; this.buttonText = buttonText; } @Override public void render(Graphics2D g) { AffineTransform ot = g.getTransform(); switch (status) { case NORMAL: drawNormal(g, rect, enabled, checked, colors, font, selFont, buttonText); break; case HELD: drawHeld(g, rect, enabled, colors, font, selFont, buttonText); break; case PRESSED: drawPressed(g, rect, enabled, colors, font, selFont, buttonText); break; case HOVER: drawHover(g, rect, enabled, colors, font, selFont, buttonText); break; } g.setTransform(ot); } protected void drawNormal(Graphics2D g, Rectangle2D rect, boolean enabled, boolean checked, ButtonColorProfile colors, Font font, Font selectedFont, String text) { if(checked) { drawPressed(g, rect, enabled, colors, font, selectedFont, text); return; } g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.BACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); g.setColor(colors.BORDER1); Line2D line = new Line2D.Double(); line.setLine(0, 0, width-1, 0); g.draw(line); line.setLine(0, 0, 0, height-1); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, height-2, width-2, height-2); g.draw(line); line.setLine(width-2, height-2, width-2, 1); g.draw(line); g.setColor(colors.BORDER3); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); if (enabled) { g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x, (float)height-6); } else { Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(selectedFont); FontMetrics fm = g.getFontMetrics(selectedFont); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g.setColor(colors.TEXT_WHITE); g.drawString(text, x+1, (float)height-6+1); g.setColor(colors.TEXT_BLACK); g.drawString(text, x, (float)height-6); } } protected void drawHeld(Graphics2D g, Rectangle2D rect, boolean enabled, ButtonColorProfile colors, Font font, Font selectedFont, String text) { g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.BACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); Line2D line = new Line2D.Double(); g.setColor(colors.BORDER3); line.setLine(0, 0, width-2, 0); g.draw(line); line.setLine(0, 0, 0, height-2); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, 1, width-3, 1); g.draw(line); line.setLine(1, 1, 1, height-3); g.draw(line); g.setColor(colors.BORDER1); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x+1, (float)height-6+1); } protected void drawPressed(Graphics2D g, Rectangle2D rect, boolean enabled, ButtonColorProfile colors, Font font, Font selectedFont, String text) { g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.SELECTEDBACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); Line2D line = new Line2D.Double(); g.setColor(colors.BORDER3); line.setLine(0, 0, width-2, 0); g.draw(line); line.setLine(0, 0, 0, height-2); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, 1, width-3, 1); g.draw(line); line.setLine(1, 1, 1, height-3); g.draw(line); g.setColor(colors.BORDER1); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); if (enabled) { g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x+1, (float)height-6+1); } else { Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(selectedFont); FontMetrics fm = g.getFontMetrics(selectedFont); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g.setColor(colors.TEXT_WHITE); g.drawString(text, x+2, (float)height-6+2); g.setColor(colors.TEXT_BLACK); g.drawString(text, x+1, (float)height-6+1); } } protected void drawHover(Graphics2D g, Rectangle2D rect, boolean enabled, ButtonColorProfile colors, Font font, Font selectedFont, String text) { g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.HOVERBACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); //g.setStroke(HOVER_STROKE); g.setColor(colors.BORDER1); Line2D line = new Line2D.Double(); line.setLine(0, 0, width-1, 0); g.draw(line); line.setLine(0, 0, 0, height-1); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, height-2, width-2, height-2); g.draw(line); line.setLine(width-2, height-2, width-2, 1); g.draw(line); g.setColor(colors.BORDER3); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); if (enabled) { g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x, (float)height-6); } else { Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(selectedFont); FontMetrics fm = g.getFontMetrics(selectedFont); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g.setColor(colors.TEXT_WHITE); g.drawString(text, x+1, (float)height-6+1); g.setColor(colors.TEXT_BLACK); g.drawString(text, x, (float)height-6); } } } protected ButtonColorProfile getColorProfile(IElement e) { return ButtonColorProfile.DEFAULT; } public boolean isEnabled(IElement e) { Stateful enabled = e.getElementClass().getAtMostOneItemOfClass(Stateful.class); if (enabled==null) return true; return enabled.isEnabled(e); } public boolean isChecked(IElement e){ Boolean b = e.getHint(AbstractTogglable.TOGGLE_KEY); if (b == null) return false; return b; } } /* static class ButtonPaint implements Paint { private static final long serialVersionUID = 7339681670484169461L; public static final ButtonPaint INSTANCE = new ButtonPaint(); @Override public void paint(IElement e, ICanvasContext ctx, GraphicsContext elementGC, GraphicsContext controlGC) { // Graphics2D g = elementGC.createClone(); // Rectangle2D rect = elementGC.getBounds(); // ButtonColorProfile colors = ButtonColorProfile.DEFAULT; // double height = rect.getHeight(); // boolean enabled = isEnabled(e); // // PressStatus status = ElementUtils.getPressStatus(e, ctx); // // Font font = new Font("Tahoma", 0, (int) height-6); // Font selFont = new Font("Tahoma", Font.BOLD, (int) height-6); // Text text = e.getElementClass().getAtMostOneItemOfClass(Text.class); // String buttonText = null; // if (text!=null) buttonText = text.getText(e); // if (buttonText==null) buttonText = ""; // // switch (status) { // case NORMAL: // drawNormal(g, rect, enabled, colors, font, selFont, buttonText); // break; // case HELD: // drawHeld(g, rect, enabled, colors, font, selFont, buttonText); // break; // case PRESSED: // drawPressed(g, rect, enabled, colors, font, selFont, buttonText); // break; // case HOVER: // drawHover(g, rect, enabled, colors, font, selFont, buttonText); // break; // } // } protected ButtonColorProfile getColorProfile(IElement e) { return ButtonColorProfile.DEFAULT; } protected boolean isToggleButton() { return false; } public boolean isEnabled(IElement e) { Stateful enabled = e.getElementClass().getAtMostOneItemOfClass(Stateful.class); if (enabled==null) return true; return enabled.isEnabled(e); } protected void drawNormal(Graphics2D g, Rectangle2D rect, boolean enabled, ButtonColorProfile colors, Font font, Font selectedFont, String text) { g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.BACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); g.setColor(colors.BORDER1); Line2D line = new Line2D.Double(); line.setLine(0, 0, width-1, 0); g.draw(line); line.setLine(0, 0, 0, height-1); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, height-2, width-2, height-2); g.draw(line); line.setLine(width-2, height-2, width-2, 1); g.draw(line); g.setColor(colors.BORDER3); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); if (enabled) { g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x, (float)height-6); } else { Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(selectedFont); FontMetrics fm = g.getFontMetrics(selectedFont); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g.setColor(colors.TEXT_WHITE); g.drawString(text, x+1, (float)height-6+1); g.setColor(colors.TEXT_BLACK); g.drawString(text, x, (float)height-6); } } protected void drawHeld(Graphics2D g, Rectangle2D rect, boolean enabled, ButtonColorProfile colors, Font font, Font selectedFont, String text) { g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.BACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); Line2D line = new Line2D.Double(); g.setColor(colors.BORDER3); line.setLine(0, 0, width-2, 0); g.draw(line); line.setLine(0, 0, 0, height-2); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, 1, width-3, 1); g.draw(line); line.setLine(1, 1, 1, height-3); g.draw(line); g.setColor(colors.BORDER1); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x+1, (float)height-6+1); } protected void drawPressed(Graphics2D g, Rectangle2D rect, boolean enabled, ButtonColorProfile colors, Font font, Font selectedFont, String text) { g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.SELECTEDBACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); Line2D line = new Line2D.Double(); g.setColor(colors.BORDER3); line.setLine(0, 0, width-2, 0); g.draw(line); line.setLine(0, 0, 0, height-2); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, 1, width-3, 1); g.draw(line); line.setLine(1, 1, 1, height-3); g.draw(line); g.setColor(colors.BORDER1); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); if (enabled) { g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x+1, (float)height-6+1); } else { Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(selectedFont); FontMetrics fm = g.getFontMetrics(selectedFont); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g.setColor(colors.TEXT_WHITE); g.drawString(text, x+2, (float)height-6+2); g.setColor(colors.TEXT_BLACK); g.drawString(text, x+1, (float)height-6+1); } } protected void drawHover(Graphics2D g, Rectangle2D rect, boolean enabled, ButtonColorProfile colors, Font font, Font selectedFont, String text) { g.translate(rect.getX(), rect.getY()); double width = rect.getWidth(); double height = rect.getHeight(); g.setColor(colors.HOVERBACKGROUND); g.fill(new Rectangle2D.Double(0, 0, width-1, height-1)); //g.setStroke(HOVER_STROKE); g.setColor(colors.BORDER1); Line2D line = new Line2D.Double(); line.setLine(0, 0, width-1, 0); g.draw(line); line.setLine(0, 0, 0, height-1); g.draw(line); g.setColor(colors.BORDER2); line.setLine(1, height-2, width-2, height-2); g.draw(line); line.setLine(width-2, height-2, width-2, 1); g.draw(line); g.setColor(colors.BORDER3); line.setLine(0, height-1, width-1, height-1); g.draw(line); line.setLine(width-1, height-1, width-1, 0); g.draw(line); if (enabled) { g.setColor(colors.TEXT_BLACK); FontMetrics fm = g.getFontMetrics(font); Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(font); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.drawString(text, x, (float)height-6); } else { Rectangle2D clipRect = new Rectangle2D.Double(2, 2, width-4, height-4); g.clip(clipRect); g.setFont(selectedFont); FontMetrics fm = g.getFontMetrics(selectedFont); Rectangle2D stringRect = fm.getStringBounds(text, g); float x = ((float)((width - 4) / 2)) - ((float)stringRect.getWidth()) / 2; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); g.setColor(colors.TEXT_WHITE); g.drawString(text, x+1, (float)height-6+1); g.setColor(colors.TEXT_BLACK); g.drawString(text, x, (float)height-6); } } } */ }