All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.readytalk.swt.widgets.buttons.SquareButton Maven / Gradle / Ivy

There is a newer version: 3.0.17
Show newest version
package com.readytalk.swt.widgets.buttons;

import com.readytalk.swt.util.ColorFactory;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TypedListener;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * The SquareButton class is a simple SWT widget that can be used in place of
 * a native SWT button. Unlike the native button, this should look exactly the
 * same on all platforms, and about a million times better than most native buttons. 
*
* Because I'm lazy and hate boilerplate, 2.0 now uses a Builder to speed up construction * and make things less ugly. Here's an example: *

 *              SquareButton.SquareButtonBuilder builder = new SquareButton.SquareButtonBuilder();
* SquareButton button = builder.setParent(composite).setImage(image).setText("Bon jour!).build();
* button.addMouseListener(new MouseAdapter() { * public void mouseUp(MouseEvent mouseEvent) { * magic(); * } * } * *
* Julian (the OG author) set default colors to a pretty basic pallet that he was modelling after the stock GMail interface. * It's fine, but you'll probably want to use your own, hence the SquareButtonColorGroups. You can specify gradients for all * possible states of the button.
*
* You can also specify a background image to be used, either cropped, centered, stretched, tiled, or set * such that the button is exactly the size of the background image.
*
* Version 2.0 adds the ability to place the image above the text using the ImagePadding enum, more centering options, * as well as some cleaner internal logic (if you find bugs).
*/ /* * This code may be used under the terms of the Apache license, version 2.0. * Version 1.0 Copyright 2009-2010 Strategic Network Applications, Inc. (http://www.snapps.com). * Version 2.0 Copyright 2013 ReadyTalk (http://www.readytalk.com) * * @author Julian Robichaux * @author Matt Weaver * @version 2.0 */ public class SquareButton extends Canvas { protected static final Logger log = Logger.getLogger(SquareButton.class.getName()); protected Listener keyListener; protected Image image, backgroundImage; protected String text; protected Font font; protected Color fontColor, hoverFontColor, clickedFontColor, inactiveFontColor, selectedFontColor; protected Color borderColor, hoverBorderColor, clickedBorderColor, inactiveBorderColor, selectedBorderColor; protected Color currentColor, currentColor2, currentFontColor, currentBorderColor; protected Color backgroundColor, backgroundColor2; protected Color clickedColor, clickedColor2; protected Color hoverColor, hoverColor2; protected Color inactiveColor, inactiveColor2; protected Color selectedColor, selectedColor2; protected int innerMarginWidth = 8; protected int innerMarginHeight = 4; protected int borderWidth = 1; protected int imagePadding = 5; protected boolean roundedCorners = true; protected int cornerRadius = 5; protected boolean isFocused = false; protected boolean selectionBorder = false; protected int lastWidth, lastHeight; public static final int BG_IMAGE_STRETCH = 1; public static final int BG_IMAGE_TILE = 2; public static final int BG_IMAGE_CENTER = 3; public static final int BG_IMAGE_FIT = 4; protected int backgroundImageStyle = 0; protected boolean fillVerticalSpace = false; protected boolean fillHorizontalSpace = false; public enum ImagePosition { ABOVE_TEXT, RIGHT_OF_TEXT, LEFT_OF_TEXT } protected ImagePosition imagePosition = ImagePosition.LEFT_OF_TEXT; protected boolean horizontallyCenterContents = true; protected boolean verticallyCenterContents = true; protected boolean toggleable = false; protected boolean toggled = false; protected SquareButton(Composite parent, int style) { super(parent, style | SWT.NO_BACKGROUND); this.setBackgroundMode(SWT.INHERIT_DEFAULT); setDefaultColors(); addListeners(); } protected void widgetDisposed(DisposeEvent e) { } protected void addListeners() { addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { SquareButton.this.widgetDisposed(e); } }); addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { SquareButton.this.paintControl(e); } }); // MOUSE EVENTS addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent mouseEvent) { if(mouseEvent.count == 1) { if (mouseEvent.button == 1) { doButtonClickedColor(); } super.mouseDown(mouseEvent); } } @Override public void mouseUp(MouseEvent mouseEvent) { if(mouseEvent.count == 1) { if (mouseEvent.button == 1) { if(!toggleable || !toggled) { SquareButton.this.setHoverColor(); } if ((mouseEvent.count == 1) && getEnabled() && (getClientArea().contains(mouseEvent.x, mouseEvent.y))) { doButtonClicked(); } } super.mouseUp(mouseEvent); } } }); addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseEnter(MouseEvent mouseEvent) { if(!toggleable || !toggled) { SquareButton.this.setHoverColor(); } super.mouseEnter(mouseEvent); } @Override public void mouseExit(MouseEvent mouseEvent) { if(!toggleable || !toggled) { if (isFocused) SquareButton.this.setSelectedColor(); else SquareButton.this.setNormalColor(); } super.mouseExit(mouseEvent); } @Override public void mouseHover(MouseEvent mouseEvent) { if(!toggleable || !toggled) { SquareButton.this.setHoverColor(); } super.mouseHover(mouseEvent); } }); // TAB TRAVERSAL (a KeyDown listener is also required) addListener(SWT.Traverse, new Listener() { public void handleEvent(Event e) { switch (e.detail) { case SWT.TRAVERSE_ESCAPE: case SWT.TRAVERSE_RETURN: case SWT.TRAVERSE_TAB_NEXT: case SWT.TRAVERSE_TAB_PREVIOUS: case SWT.TRAVERSE_PAGE_NEXT: case SWT.TRAVERSE_PAGE_PREVIOUS: e.doit = true; break; default: break; } } }); addListener(SWT.FocusIn, new Listener () { public void handleEvent (Event e) { isFocused = true; if(!toggleable || !toggled) { SquareButton.this.setSelectedColor(); } redraw(); } }); addListener(SWT.FocusOut, new Listener () { public void handleEvent (Event e) { isFocused = false; if(!toggleable || !toggled) { SquareButton.this.setNormalColor(); } redraw(); } }); addListener(SWT.KeyUp, new Listener () { public void handleEvent (Event e) { isFocused = true; if(!toggleable || !toggled) { SquareButton.this.setSelectedColor(); } redraw(); } }); keyListener = new Listener() { public void handleEvent(Event e) { // required for tab traversal to work switch (e.character) { case ' ': case '\r': case '\n': doButtonClickedColor(); redraw(); doButtonClicked(); break; default: break; } } }; setTraversable(true); } void doButtonClickedColor() { if(!toggleable){ SquareButton.this.setClickedColor(); } } /** * SelectionListeners are notified when the button is clicked * * @param listener */ public void addSelectionListener(SelectionListener listener) { addListener(SWT.Selection, new TypedListener(listener)); } public void removeSelectionListener(SelectionListener listener) { removeListener(SWT.Selection, listener); } protected void setTraversable(boolean canTraverse) { // TODO is there a better way to do this? try { if (canTraverse) this.addListener (SWT.KeyDown, keyListener); else if (!canTraverse) this.removeListener(SWT.KeyDown, keyListener); } catch (Exception e) {} } protected void doButtonClicked() { Event e = new Event(); e.item = this; e.widget = this; e.type = SWT.Selection; notifyListeners(SWT.Selection, e); } protected void setDefaultColors() { fontColor = ColorFactory.getColor(0, 0, 0); hoverFontColor = ColorFactory.getColor(0, 0, 0); clickedFontColor = ColorFactory.getColor(255, 255, 255); inactiveFontColor = ColorFactory.getColor(187, 187, 187); selectedFontColor = ColorFactory.getColor(160, 107, 38); borderColor = ColorFactory.getColor(187, 187, 187); hoverBorderColor = ColorFactory.getColor(147, 147, 147); clickedBorderColor = ColorFactory.getColor(147, 147, 147); inactiveBorderColor = ColorFactory.getColor(200, 200, 200); selectedBorderColor = ColorFactory.getColor(160, 107, 38); backgroundColor = ColorFactory.getColor(248, 248, 248); backgroundColor2 = ColorFactory.getColor(228, 228, 228); clickedColor = ColorFactory.getColor(120, 120, 120); clickedColor2 = ColorFactory.getColor(150, 150, 150); hoverColor = ColorFactory.getColor(248, 248, 248); hoverColor2 = ColorFactory.getColor(228, 228, 228); inactiveColor = ColorFactory.getColor(248, 248, 248); inactiveColor2 = ColorFactory.getColor(228, 228, 228); selectedColor = ColorFactory.getColor(238, 238, 238); selectedColor2 = ColorFactory.getColor(218, 218, 218); } protected void setNormalColor() { setMouseEventColor(backgroundColor, backgroundColor2, borderColor, fontColor); } protected void setHoverColor() { setMouseEventColor(hoverColor, hoverColor2, hoverBorderColor, hoverFontColor); } protected void setClickedColor() { setMouseEventColor(clickedColor, clickedColor2, clickedBorderColor, clickedFontColor); } protected void setInactiveColor() { setMouseEventColor(inactiveColor, inactiveColor2, inactiveBorderColor, inactiveFontColor); } protected void setSelectedColor() { setMouseEventColor(selectedColor, selectedColor2, selectedBorderColor, selectedFontColor); } protected void setMouseEventColor(Color topBackgroundColor, Color bottomBackgroundColor, Color borderColor, Color fontColor) { if (!getEnabled()) return; if (currentColor == null) { currentColor = backgroundColor; currentColor2 = backgroundColor2; currentBorderColor = borderColor; currentFontColor = fontColor; } boolean redrawFlag = false; if ((topBackgroundColor != null) && (!currentColor.equals(topBackgroundColor) || !currentColor2.equals(bottomBackgroundColor))) { currentColor = currentColor2 = topBackgroundColor; if (bottomBackgroundColor != null) { currentColor2 = bottomBackgroundColor; } redrawFlag = true; } if ((borderColor != null) && (!currentBorderColor.equals(borderColor))) { currentBorderColor = borderColor; redrawFlag = true; } if ((fontColor != null) && (!currentFontColor.equals(fontColor))) { currentFontColor = fontColor; redrawFlag = true; } if (redrawFlag) { redraw(); } } @Override public void setLayoutData(Object layoutData) { /* * set height and width hints... pin the sides if we have set layout data to something that it would care about */ if(layoutData instanceof FormData) { fillVerticalSpace = true; fillHorizontalSpace = true; // we don't want to pack contents if they are using a form } else if(layoutData instanceof GridData) { GridData gridData = (GridData)layoutData; fillVerticalSpace = gridData.grabExcessVerticalSpace; fillHorizontalSpace = gridData.grabExcessHorizontalSpace; } else { fillVerticalSpace = false; fillHorizontalSpace = false; } super.setLayoutData(layoutData); } protected void paintControl(PaintEvent paintEvent) { if (currentColor == null) { currentColor = backgroundColor; currentColor2 = backgroundColor2; currentBorderColor = borderColor; currentFontColor = fontColor; } Point buttonSize = computeSize(); // with certain layouts, the width is sometimes 1 pixel too wide if (buttonSize.x > getClientArea().width) { buttonSize.x = getClientArea().width; } Rectangle buttonRectangle = new Rectangle(0, 0, buttonSize.x, buttonSize.y); GC gc = paintEvent.gc; //try and enable nice looking fonts on windows try { gc.setAdvanced(true); gc.setAntialias(SWT.ON); gc.setTextAntialias(SWT.ON); gc.setInterpolation(SWT.HIGH); } catch (SWTException exception) { log.warning("Couldn't get nice fonts: " + exception.getMessage()); } // add transparency by making the canvas background the same as // the parent background (only needed for rounded corners) if (roundedCorners) { gc.setBackground(getParent().getBackground()); gc.fillRectangle(buttonRectangle); } // draw the background color of the inside of the button. There's no such // thing as a rounded gradient rectangle in SWT, so we need to draw a filled // rectangle that's just the right size to fit inside a rounded rectangle // without spilling out at the corners gc.setForeground(this.currentColor); gc.setBackground(this.currentColor2); Rectangle fillRectangle = (roundedCorners) ? new Rectangle(buttonRectangle.x + 1, buttonRectangle.y + 1, buttonRectangle.width - 2, buttonRectangle.height - 3) : new Rectangle(buttonRectangle.x + 1, buttonRectangle.y + 1, buttonRectangle.width - 1, buttonRectangle.height - 1); gc.fillGradientRectangle(fillRectangle.x, fillRectangle.y, fillRectangle.width, fillRectangle.height, true); // if there's a background image, draw it on top of the interior color // so any image transparency allows the button colors to come through drawBackgroundImage(gc, fillRectangle); // draw the border of the button. If a zero-pixel border was specified, // draw a 1-pixel border the same color as the canvas background instead // so the rounded corners look right gc.setLineStyle(SWT.LINE_SOLID); int arcHeight = Math.max(cornerRadius, buttonSize.y / 10); int arcWidth = arcHeight; int bw = borderWidth; if (borderWidth > 0) { gc.setLineWidth(borderWidth); gc.setForeground(this.currentBorderColor); } else { bw = 1; gc.setLineWidth(1); gc.setForeground(getParent().getBackground()); arcWidth = arcHeight += 1; } if (roundedCorners) { gc.drawRoundRectangle(buttonRectangle.x + (bw - 1), buttonRectangle.y + (bw - 1), buttonRectangle.width - bw, buttonRectangle.height - 4, arcWidth, arcHeight); } else { gc.drawRectangle(buttonRectangle.x, buttonRectangle.y, buttonRectangle.width - bw, buttonRectangle.height - 3); } // dotted line selection border around the text, if any if (this.isFocused && this.selectionBorder) { gc.setForeground(currentFontColor); gc.setLineStyle(SWT.LINE_DASH); gc.setLineWidth(1); gc.drawRectangle(buttonRectangle.x + (bw + 1), buttonRectangle.y + (bw + 1), buttonRectangle.width - (bw + 5), buttonRectangle.height - (bw + 5)); } ContentOriginSet contentOriginSet = getContentsOrigin(buttonRectangle); Point textOrigin = contentOriginSet.getTextOrigin(); Point imageOrigin = contentOriginSet.getImageOrigin(); /* * Order of drawing matters... if the image is above or to the left, * it needs to be drawn first. */ switch(imagePosition) { case LEFT_OF_TEXT: case ABOVE_TEXT: if(imageOrigin != null) { drawImage(gc, imageOrigin.x, imageOrigin.y); } if(textOrigin != null) { drawText(gc, textOrigin.x, textOrigin.y); } break; case RIGHT_OF_TEXT: if(textOrigin != null) { drawText(gc, textOrigin.x, textOrigin.y); } if(imageOrigin != null) { drawImage(gc, imageOrigin.x, imageOrigin.y); } break; } } /* * for LEFT_OF_TEXT and RIGHT_OF_TEXT * widthOfContents = image.width + image.padding * + text.width * for ABOVE_TEXT * widthOfContents = Math.max(image.width, text.width) */ protected int getWidthOfContents() { int widthOfContents = 0; Point textSize = computeTextSize(); Point imageSize = computeImageSize(); int textWidth = (textSize != null) ? textSize.x : 0; int imageWidth = (imageSize != null) ? imageSize.x : 0; switch(imagePosition) { case LEFT_OF_TEXT: case RIGHT_OF_TEXT: widthOfContents = imageWidth + textWidth + ((image != null) ? imagePadding : 0); break; case ABOVE_TEXT: widthOfContents = Math.max(textWidth, imageWidth); break; } return widthOfContents; } /* * for LEFT_OF_TEXT and RIGHT_OF_TEXT * heightOfContents = Math.max(textHeight, imageHeight); * * for ABOVE_TEXT * heightOfContents = imageHeight + imagePadding + textHeight */ protected int getHeightOfContents() { int heightOfContents = 0; Point textSize = computeTextSize(); Point imageSize = computeImageSize(); int textHeight = (textSize != null) ? textSize.y : 0; int imageHeight = (imageSize != null) ? imageSize.y : 0; switch(imagePosition) { case LEFT_OF_TEXT: case RIGHT_OF_TEXT: heightOfContents = Math.max(textHeight, imageHeight); break; case ABOVE_TEXT: heightOfContents = imageHeight + textHeight + ((image != null) ? imagePadding : 0); break; } return heightOfContents; } /* * Trying to consolidate this for sanity's sake. */ protected ContentOriginSet getContentsOrigin(Rectangle rectangle) { int imageX, imageY, textX, textY; imageX = imageY = textX = textY = 0; Point textSize = computeTextSize(); if(textSize == null) { textSize = new Point(0,0); } Point imageSize = computeImageSize(); if(imageSize == null) { imageSize = new Point(0,0); } boolean imageWider = imageSize.x > textSize.x; boolean imageTaller = imageSize.y > textSize.y; int widthOfContents = getWidthOfContents(); int heightOfContents = getHeightOfContents(); /* * For clarity's sake remember a few things... we depend on the button * rectangle being sized at this point, which will take into account the * inner margin values (which we point out here for clarity's sake). * * The functions that calculate the width and height of contents change * how that is done based on the three possible configurations of a button. * * Typically, text will be wider than images, but shorter in height. However, * this won't always be true which is why the getWidthOfContents and getHeightOfContents * will use a Math.max call to determine which one is taller in the cases where they may be different. */ switch (imagePosition) { case LEFT_OF_TEXT: /* * LEFT_OF_TEXT * 1. innerMarginWidth * 2. innerMarginHeight * 3. image.width * 4. image.height * 5. image.padding * 6. text.width * 7. text.height * ________________________________________ * | ^ | * | 2 | * | | | * | ########### ^ | * | # # | | * |<--1-># ^ ^ # | <----6-----><-1->| * | # # 4 CEREAL BAWX ^ | * | # ~~~ #<-5->BEFORE I GET 7 | * | # # | ANGRY!!! | | * | ########### | | * | <-3--^----> | * | 2 | * | | | * |_______________________________________| */ imageX = (horizontallyCenterContents) ? rectangle.width/2 - widthOfContents/2 : innerMarginWidth; textX = imageX + imageSize.x + ((image != null) ? imagePadding : 0); // anchor height based on the image, if the image is the taller of the two... BUT that might not always be the case if(imageTaller) { imageY = (verticallyCenterContents) ? rectangle.height/2 - imageSize.y/2 : innerMarginHeight; textY = imageY + imageSize.y/2 - textSize.y/2; } else { textY = (verticallyCenterContents) ? rectangle.height/2 - textSize.y/2 : innerMarginHeight; imageY = textY + textSize.y/2 - imageSize.y/2; } break; case RIGHT_OF_TEXT: /* RIGHT_OF_TEXT * 1. innerMarginWidth * 2. innerMarginHeight * 3. image.width * 4. image.height * 5. image.padding * 6. text.width * 7. text.height * ________________________________________ * | ^ | * | 2 | * | | | * | ########### ^ | * | # # | | * |<--1-><----6-----><-5-># ^ ^ #<-1->| * | CEREAL BAWX ^ # # 4 | * | BEFORE I GET 7 # ~~~ # | | * | ANGRY!!! | # # | | * | ########### | | * | <-3--^----> | * | 2 | * | | | * |_______________________________________| */ textX = (horizontallyCenterContents) ? rectangle.width/2 - widthOfContents/2 : innerMarginWidth; imageX = textX + textSize.x + ((image != null) ? imagePadding : 0); // anchor height based on the image, if the image is the taller of the two... BUT that might not always be the case if(imageTaller) { imageY = (verticallyCenterContents) ? rectangle.height/2 - imageSize.y/2 : innerMarginHeight; textY = imageY + imageSize.y/2 - textSize.y/2; } else { textY = (verticallyCenterContents) ? rectangle.height/2 - textSize.y/2 : innerMarginHeight; imageY = textY + textSize.y/2 - imageSize.y/2; } break; case ABOVE_TEXT: /* ABOVE_TEXT * 1. innerMarginWidth (Whichever is wider, the image or the text) * 2. innerMarginHeight * 3. image.width * 4. image.height * 5. image.padding * 6. text.width * 7. text.height * ___________________________ * | ^ | * | 2 | * | | | * | ########### ^ | * | # # | | * | # ^ ^ # | | * | # # 4 | * | # ~~~ # | | * | # # | | * | ########### | | * |<--1--><-3--^----> | * | | | * | O 5 | * | R | | * | | | * |<--1-><--6--|----><--1-->| * | CEREAL BAWX ^ | * | BEFORE I GET 7 | * | ANGRY!!! | | * | | * | | * | | * | | * |_________________________| */ imageY = (verticallyCenterContents) ? rectangle.height/2 - heightOfContents/2 : innerMarginHeight; textY = imageY + imageSize.y + ((image != null) ? imagePadding : 0); // anchor width based on the image only if the image is wider, probably not likely but possible if(imageWider) { imageX = (horizontallyCenterContents) ? rectangle.width/2 - imageSize.x/2 : innerMarginWidth; textX = imageX + imageSize.x/2 - textSize.x/2; } else { textX = (horizontallyCenterContents) ? rectangle.width/2 - textSize.x/2 : innerMarginWidth; imageX = textX + textSize.x/2 - imageSize.x/2; } break; } Point imageOrigin = (image != null) ? new Point(imageX, imageY) : null; Point textOrigin = (text != null) ? new Point(textX, textY) : null; return new ContentOriginSet(imageOrigin, textOrigin); } protected void drawText(GC gc, int x, int y) { gc.setFont(font); gc.setForeground(currentFontColor); gc.drawText(text, x, y, SWT.DRAW_TRANSPARENT | SWT.DRAW_DELIMITER); } protected int drawImage(GC gc, int x, int y) { if (image == null) return x; gc.drawImage(image, x, y); return x + image.getBounds().width + imagePadding; } protected void drawBackgroundImage(GC gc, Rectangle rect) { if (backgroundImage == null) return; Rectangle imgBounds = backgroundImage.getBounds(); if (backgroundImageStyle == BG_IMAGE_STRETCH) { gc.drawImage(backgroundImage, 0, 0, imgBounds.width, imgBounds.height, rect.x, rect.y, rect.width, rect.height); } else if (backgroundImageStyle == BG_IMAGE_CENTER) { int x = (imgBounds.width - rect.width) / 2; int y = (imgBounds.height - rect.height) / 2; Rectangle centerRect = new Rectangle(rect.x, rect.y, rect.width, rect.height); if (x < 0) { centerRect.x -= x; x = 0; } if (y < 0) { centerRect.y -= y; y = 0; } drawClippedImage(gc, backgroundImage, x, y, centerRect); } else if (backgroundImageStyle == BG_IMAGE_TILE) { for (int y = 0; y < rect.height; y += imgBounds.height) { Rectangle tileRect = new Rectangle(rect.x, y + rect.y, rect.width, rect.height-y); for (int x = 0; x < rect.width; x += imgBounds.width) { tileRect.x += drawClippedImage(gc, backgroundImage, 0, 0, tileRect); tileRect.width -= x; } } } else { // default is BG_IMAGE_CROP drawClippedImage(gc, backgroundImage, 0, 0, rect); } } protected int drawClippedImage(GC gc, Image image, int x, int y, Rectangle rect) { if (image != null) { Rectangle imageBounds = image.getBounds(); int width = Math.min(imageBounds.width - x, rect.width); int height = Math.min(imageBounds.height - y, rect.height); gc.drawImage(image, x, y, width, height, rect.x, rect.y, width, height); return width; } return 0; } public Point computeSize() { Rectangle clientArea = getClientArea(); return computeSize((fillHorizontalSpace) ? clientArea.width : SWT.DEFAULT, (fillVerticalSpace) ? clientArea.height : SWT.DEFAULT, false); } public Point computeSize(int widthHint, int heightHint, boolean changed) { Point size = null; if ((widthHint == SWT.DEFAULT) && (heightHint == SWT.DEFAULT) && !changed && (lastWidth > 0) && (lastHeight > 0)) { size = new Point(lastWidth, lastHeight); } else { int estimatedWidth = getWidthOfContents() + (innerMarginWidth * 2); int estimatedHeight = getHeightOfContents() + (innerMarginHeight * 2); int width = (widthHint != SWT.DEFAULT && widthHint > estimatedWidth) ? widthHint : estimatedWidth; int height = (heightHint != SWT.DEFAULT && heightHint > estimatedHeight) ? heightHint : estimatedHeight; /* * In this case, it's possible that the background image will be small and * your text will get junky... careful with this one, kid. */ if ((backgroundImage != null) && (backgroundImageStyle == BG_IMAGE_FIT)) { width = backgroundImage.getBounds().width; height = backgroundImage.getBounds().height; } lastWidth = width + 2; lastHeight = height + 2; size = new Point(lastWidth, lastHeight); } return size; } public Point computeTextSize() { Point size = null; if (text != null) { GC gc = new GC(this); gc.setFont(font); size = gc.textExtent(text, SWT.DRAW_DELIMITER); gc.dispose(); } return size; } public Point computeImageSize() { Point size = null; if(image != null) { Rectangle imageBounds = image.getBounds(); size = new Point(imageBounds.width, imageBounds.height); } return size; } /** * This is an image that will be displayed to the side of the * text inside the button (if any). By default the image will be * to the left of the text; however, setImageStyle can be used to * specify that it's either to the right or left. If there is no * text, the image will be centered inside the button. * * @param image */ public void setImage(Image image) { this.image = image; redraw(); } public Image getImage() { return image; } public ImagePosition getImagePosition() { return imagePosition; } public void setImagePosition(ImagePosition imagePosition) { this.imagePosition = imagePosition; } public int getImagePadding() { return imagePadding; } public void setImagePadding(int imagePadding) { this.imagePadding = imagePadding; } /** * This is an image that will be used as a background image for the * button, drawn in the manner specified by the backgroundImageStyle * setting. The order in which the button is drawn is: background * color, then background image, then button image and text. So if the * background image has transparency, the background color will show * through the transparency. */ public void setBackgroundImage(Image backgroundImage) { this.backgroundImage = backgroundImage; redraw(); } public Image getBackgroundImage() { return backgroundImage; } /** * Set the style with which the background image is drawn (default is * BG_IMAGE_CROP). The different styles are: *

    *
  • BG_IMAGE_CROP: the image is drawn once, with the top left corner * of the image at the top left corner of the button. Any part of the image * that is too wide or too tall to fit inside the button area is clipped * (cropped) off.
  • *
  • BG_IMAGE_STRETCH: the image is stretched (or squashed) to exactly * fit the button area.
  • *
  • BG_IMAGE_TILE: the image is tiled vertically and horizontally to * cover the entire button area.
  • *
  • BG_IMAGE_CENTER: the center of the image is placed inside the center * of the button. Any part of the image that is too tall or too wide to fit * will be clipped.
  • *
  • BG_IMAGE_FIT: the button will be the exact size of the image. Note that * this can sometimes truncate the text inside the button.
  • *
* * @param backgroundImageStyle */ public void setBackgroundImageStyle(int backgroundImageStyle) { this.backgroundImageStyle = backgroundImageStyle; } public int getBackgroundImageStyle() { return backgroundImageStyle; } public String getText() { return text; } public void setText(String text) { this.text = text; redraw(); } public Font getFont() { return font; } public void setFont(Font font) { if (font != null) this.font = font; } public boolean isHorizontallyCenterContents() { return horizontallyCenterContents; } public void setHorizontallyCenterContents(boolean horizontallyCenterContents) { this.horizontallyCenterContents = horizontallyCenterContents; } public boolean isVerticallyCenterContents() { return verticallyCenterContents; } public void setVerticallyCenterContents(boolean verticallyCenterContents) { this.verticallyCenterContents = verticallyCenterContents; } /** * Set whether or not this button is enabled (active) or * not (inactive). This setting can be changed dynamically * after the button has been drawn. *

* An inactive button does not change color * when it is hovered over or clicked, does not receive focus * or participate in the tab order of the widget container, * and does not notify listeners when clicked. */ public void setEnabled(boolean enabled) { boolean oldSetting = getEnabled(); super.setEnabled(enabled); if (oldSetting != enabled) { if (!enabled) { super.setEnabled(true); this.setInactiveColor(); this.setTraversable(false); super.setEnabled(false); } else { this.setNormalColor(); this.setTraversable(true); } } } // public boolean isEnabled() { // return enabled; // } /** * Set the inner margin between the left and right of the text * inside the button and the button borders, in pixels. Like the * left and right padding for the text. Default is 8 pixels. * * @param innerMarginWidth */ public void setInnerMarginWidth(int innerMarginWidth) { if (innerMarginWidth >= 0) this.innerMarginWidth = innerMarginWidth; } public int getInnerMarginWidth() { return innerMarginWidth; } /** * Set the inner margin between the top and bottom of the text * inside the button and the button borders, in pixels. Like the * top and bottom padding for the text. Default is 4 pixels. * * @param innerMarginHeight */ public void setInnerMarginHeight(int innerMarginHeight) { if (innerMarginHeight >= 0) this.innerMarginHeight = innerMarginHeight; } public int getInnerMarginHeight() { return innerMarginHeight; } /** * Set whether or not the button should have rounded corners * (default is true). * * @param roundedCorners */ public void setRoundedCorners(boolean roundedCorners) { this.roundedCorners = roundedCorners; } public boolean hasRoundedCorners() { return roundedCorners; } /** * Set whether or not a dotted-line border should be drawn around * the text inside the button when the button has tab focus. Default is * false (no selection border). If a selection border is used, it will * be the same color as the font color. Note that you can also use * setSelectedColors() to change the look of the button when it has * focus. * * @param selectionBorder */ public void setSelectionBorder(boolean selectionBorder) { this.selectionBorder = selectionBorder; } public boolean hasSelectionBorder() { return selectionBorder; } /** * Set the width of the button border, in pixels (default is 1). * * @param borderWidth */ public void setBorderWidth(int borderWidth) { this.borderWidth = borderWidth; } public int getBorderWidth() { return borderWidth; } /** * The colors of the button in its "default" state (not clicked, selected, etc.) * * @param topBackgroundColor the gradient color at the top of the button * @param bottomBackgroundColor the gradient color at the bottom of the button (if you don't want a gradient, set this to topBackgroundColor) * @param borderColor the color of the border around the button (if you don't want a border, use getBackground()) * @param fontColor the color of the font inside the button */ public void setDefaultColors(Color topBackgroundColor, Color bottomBackgroundColor, Color borderColor, Color fontColor) { if (topBackgroundColor != null) { this.backgroundColor = topBackgroundColor; } if (bottomBackgroundColor != null) { this.backgroundColor2 = bottomBackgroundColor; } if (borderColor != null) { this.borderColor = borderColor; } if (fontColor != null) { this.fontColor = fontColor; } } public void setDefaultColors(SquareButtonColorGroup squareButtonColorGroup) { setSelectedColors(squareButtonColorGroup.getTopBackgroundColor(), squareButtonColorGroup.getBottomBackgroundColor(), squareButtonColorGroup.getBorderColor(), squareButtonColorGroup.getFontColor()); } /** * The colors of the button when the mouse is hovering over it * * @param topBackgroundColor the gradient color at the top of the button * @param bottomBackgroundColor the gradient color at the bottom of the button (if you don't want a gradient, set this to topBackgroundColor) * @param borderColor the color of the border around the button (if you don't want a border, use getBackground()) * @param fontColor the color of the font inside the button */ public void setHoverColors(Color topBackgroundColor, Color bottomBackgroundColor, Color borderColor, Color fontColor) { if (topBackgroundColor != null) { this.hoverColor = topBackgroundColor; } if (bottomBackgroundColor != null) { this.hoverColor2 = bottomBackgroundColor; } if (borderColor != null) { this.hoverBorderColor = borderColor; } if (fontColor != null) { this.hoverFontColor = fontColor; } } public void setHoverColors(SquareButtonColorGroup squareButtonColorGroup) { setHoverColors(squareButtonColorGroup.getTopBackgroundColor(), squareButtonColorGroup.getBottomBackgroundColor(), squareButtonColorGroup.getBorderColor(), squareButtonColorGroup.getFontColor()); } /** * The colors of the button when it is being clicked (MouseDown) * * @param topBackgroundColor the gradient color at the top of the button * @param bottomBackgroundColor the gradient color at the bottom of the button (if you don't want a gradient, set this to topBackgroundColor) * @param borderColor the color of the border around the button (if you don't want a border, use getBackground()) * @param fontColor the color of the font inside the button */ public void setClickedColors(Color topBackgroundColor, Color bottomBackgroundColor, Color borderColor, Color fontColor) { if (topBackgroundColor != null) { this.clickedColor = topBackgroundColor; } if (bottomBackgroundColor != null) { this.clickedColor2 = bottomBackgroundColor; } if (borderColor != null) { this.clickedBorderColor = borderColor; } if (fontColor != null) { this.clickedFontColor = fontColor; } } public void setClickedColors(SquareButtonColorGroup squareButtonColorGroup) { setClickedColors(squareButtonColorGroup.getTopBackgroundColor(), squareButtonColorGroup.getBottomBackgroundColor(), squareButtonColorGroup.getBorderColor(), squareButtonColorGroup.getFontColor()); } /** * The colors of the button when it has focus * * @param topBackgroundColor the gradient color at the top of the button * @param bottomBackgroundColor the gradient color at the bottom of the button (if you don't want a gradient, set this to topBackgroundColor) * @param borderColor the color of the border around the button (if you don't want a border, use getBackground()) * @param fontColor the color of the font inside the button */ public void setSelectedColors(Color topBackgroundColor, Color bottomBackgroundColor, Color borderColor, Color fontColor) { if (topBackgroundColor != null) { this.selectedColor = topBackgroundColor; } if (bottomBackgroundColor != null) { this.selectedColor2 = bottomBackgroundColor; } if (borderColor != null) { this.selectedBorderColor = borderColor; } if (fontColor != null) { this.selectedFontColor = fontColor; } } /** * Easier to go this route, son. * * @param squareButtonColorGroup */ public void setSelectedColors(SquareButtonColorGroup squareButtonColorGroup) { setSelectedColors(squareButtonColorGroup.getTopBackgroundColor(), squareButtonColorGroup.getBottomBackgroundColor(), squareButtonColorGroup.getBorderColor(), squareButtonColorGroup.getFontColor()); } /** * The colors of the button when it is in an inactive (not enabled) state * * @param topBackgroundColor the gradient color at the top of the button * @param bottomBackgroundColor the gradient color at the bottom of the button (if you don't want a gradient, set this to topBackgroundColor) * @param borderColor the color of the border around the button (if you don't want a border, use getBackground()) * @param fontColor the color of the font inside the button */ public void setInactiveColors(Color topBackgroundColor, Color bottomBackgroundColor, Color borderColor, Color fontColor) { if (topBackgroundColor != null) { this.inactiveColor = topBackgroundColor; } if (bottomBackgroundColor != null) { this.inactiveColor2 = bottomBackgroundColor; } if (borderColor != null) { this.inactiveBorderColor = borderColor; } if (fontColor != null) { this.inactiveFontColor = fontColor; } } /** * Much easier to use the inner class. * * @param squareButtonColorGroup */ public void setInactiveColors(SquareButtonColorGroup squareButtonColorGroup) { setInactiveColors(squareButtonColorGroup.getTopBackgroundColor(), squareButtonColorGroup.getBottomBackgroundColor(), squareButtonColorGroup.getBorderColor(), squareButtonColorGroup.getFontColor()); } public int getCornerRadius() { return cornerRadius; } public void setCornerRadius(int cornerRadius) { this.cornerRadius = cornerRadius; } protected void setAccessibilityName(final String name) { getAccessible().addAccessibleListener (new AccessibleAdapter() { public void getName(AccessibleEvent e) { e.result = name; } }); } public boolean isFillVerticalSpace() { return fillVerticalSpace; } public void setFillVerticalSpace(boolean fillVerticalSpace) { this.fillVerticalSpace = fillVerticalSpace; } public boolean isFillHorizontalSpace() { return fillHorizontalSpace; } public void setFillHorizontalSpace(boolean fillHorizontalSpace) { this.fillHorizontalSpace = fillHorizontalSpace; } public boolean isToggleable() { return toggleable; } public void setToggleable(boolean toggleable) { this.toggleable = toggleable; } public boolean isToggled() { return toggled; } /** * Setting toggle states explicitly may work against what you might be trying to do, if using a SquareButtonGroup. If not * it will work as expected. * * @param toggled */ public void setToggled(boolean toggled) { this.toggled = toggled; if(toggleable) { if(toggled) { SquareButton.this.setClickedColor(); } else { SquareButton.this.setNormalColor(); } } redraw(); } public static class SquareButtonColorGroup { protected final Color topBackgroundColor; protected final Color bottomBackgroundColor; protected final Color borderColor; protected final Color fontColor; public SquareButtonColorGroup(Color topBackgroundColor, Color bottomBackgroundColor, Color borderColor, Color fontColor) { this.topBackgroundColor = topBackgroundColor; this.bottomBackgroundColor = bottomBackgroundColor; this.borderColor = borderColor; this.fontColor = fontColor; } public Color getTopBackgroundColor() { return topBackgroundColor; } public Color getBottomBackgroundColor() { return bottomBackgroundColor; } public Color getBorderColor() { return borderColor; } public Color getFontColor() { return fontColor; } } /** * For convenience */ protected class ContentOriginSet { protected final Point imageOrigin; protected final Point textOrigin; protected ContentOriginSet(Point imageOrigin, Point textOrigin) { this.imageOrigin = imageOrigin; this.textOrigin = textOrigin; } protected Point getImageOrigin() { return imageOrigin; } protected Point getTextOrigin() { return textOrigin; } } private List strategies = new ArrayList(); private DefaultButtonClickHandler strategyHandler; private DefaultButtonClickHandler defaultToggleClickHandler = new DefaultButtonClickHandler(this) { @Override void clicked() { if(!disableDefaultToggleClickHandler && isToggleable()) { if(toggled) { setNormalColor(); toggled = false; } else { setClickedColor(); toggled = true; } } } }; private boolean disableDefaultToggleClickHandler; void setDisableDefaultToggleClickHandler(boolean disableDefaultToggleClickHandler) { this.disableDefaultToggleClickHandler = disableDefaultToggleClickHandler; } /** * Strategies are more likely to be used in SquareButtonGroup implementations, but you can feel free to add * any and they will all be evaluated and applied individually. * * @param strategy */ public void addStrategy(ButtonClickStrategy strategy) { strategies.add(strategy); addStrategyHandler(); } public void clearStrategies() { strategies.clear(); } public void removeStrategy(ButtonClickStrategy strategy) { strategies.remove(strategy); } void executeStrategies() { for(ButtonClickStrategy strategy : strategies) { boolean validStrategy = strategy.isStrategyValid(); if(validStrategy) { strategy.executeStrategy(); } } } void addStrategyHandler() { if(strategyHandler == null) { strategyHandler = new DefaultButtonClickHandler(this) { @Override void clicked() { executeStrategies(); } }; } } static interface ButtonClickStrategy { /** * Do whatever button logic you want to do here. * * @return whether or not this button click should be allowed */ boolean isStrategyValid(); void executeStrategy(); } /** * Use this instead of using an explicit mouse adapter, unless you have other reason to. */ public static abstract class DefaultButtonClickHandler { public DefaultButtonClickHandler(final SquareButton button) { button.addMouseListener(new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { clicked(); } }); button.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent keyEvent) { switch (keyEvent.character) { case ' ': case '\r': case '\n': clicked(); break; default: break; } } }); } abstract void clicked(); } public static interface ButtonClickHandler { void clicked(); } /** * Not totally necessary, but it's lovely to chain a bunch of calls together for efficiency and brevity of code. Looks bulky here, * but so much faster in implementation. */ public static class SquareButtonBuilder { protected Composite parent; protected int style = SWT.NONE; protected String text; protected String toolTipText; protected Font font; protected Image image; protected ImagePosition imagePosition = ImagePosition.LEFT_OF_TEXT; // this is the default value of course protected boolean horizontallyCenterContents = true; protected boolean verticallyCenterContents = true; protected boolean fillVerticalSpace = false; protected boolean fillHorizontalSpace = false; protected int imagePadding; protected int cornerRadius; protected SquareButtonColorGroup defaultColors; protected SquareButtonColorGroup selectedColors; protected SquareButtonColorGroup hoverColors; protected SquareButtonColorGroup clickedColors; protected SquareButtonColorGroup inactiveColors; protected String accessibilityName; protected boolean toggleable; protected ButtonClickHandler defaultMouseClickAndReturnKeyHandler; public SquareButtonBuilder setParent(Composite parent) { this.parent = parent; return this; } public SquareButtonBuilder setStyle(int style) { this.style = style; return this; } public SquareButtonBuilder setText(String text) { this.text = text; return this; } public SquareButtonBuilder setToolTipText(String toolTipText) { this.toolTipText = toolTipText; return this; } public SquareButtonBuilder setFont(Font font) { this.font = font; return this; } public SquareButtonBuilder setImage(Image image) { this.image = image; return this; } public SquareButtonBuilder setImagePosition(ImagePosition imagePosition) { this.imagePosition = imagePosition; return this; } public SquareButtonBuilder setImagePadding(int imagePadding) { this.imagePadding = imagePadding; return this; } public SquareButtonBuilder setDefaultColors(SquareButtonColorGroup defaultColors) { this.defaultColors = defaultColors; return this; } public SquareButtonBuilder setSelectedColors(SquareButtonColorGroup selectedColors) { this.selectedColors = selectedColors; return this; } public SquareButtonBuilder setHoverColors(SquareButtonColorGroup hoverColors) { this.hoverColors = hoverColors; return this; } public SquareButtonBuilder setClickedColors(SquareButtonColorGroup clickedColors) { this.clickedColors = clickedColors; return this; } public SquareButtonBuilder setInactiveColors(SquareButtonColorGroup inactiveColors) { this.inactiveColors = inactiveColors; return this; } public SquareButtonBuilder setCornerRadius(int cornerRadius) { this.cornerRadius = cornerRadius; return this; } public SquareButtonBuilder setHorizontallyCenterContents(boolean horizontallyCenterContents) { this.horizontallyCenterContents = horizontallyCenterContents; return this; } public SquareButtonBuilder setVerticallyCenterContents(boolean verticallyCenterContents) { this.verticallyCenterContents = verticallyCenterContents; return this; } public SquareButtonBuilder setAccessibilityName(String accessibilityName) { this.accessibilityName = accessibilityName; return this; } public SquareButtonBuilder setFillVerticalSpace(boolean fillVerticalSpace) { this.fillVerticalSpace = fillVerticalSpace; return this; } public SquareButtonBuilder setFillHorizontalSpace(boolean fillHorizontalSpace) { this.fillHorizontalSpace = fillHorizontalSpace; return this; } public SquareButtonBuilder setToggleable(boolean toggleable) { this.toggleable = toggleable; return this; } public SquareButtonBuilder setDefaultMouseClickAndReturnKeyHandler(ButtonClickHandler defaultMouseClickAndReturnKeyHandler) { this.defaultMouseClickAndReturnKeyHandler = defaultMouseClickAndReturnKeyHandler; return this; } public SquareButton build() throws IllegalArgumentException { SquareButton button = null; if(parent == null) { throw new IllegalArgumentException("You must set a parent."); } button = new SquareButton(parent, style); if(text != null) { button.setText(text); } if(toolTipText != null) { button.setToolTipText(toolTipText); } if(image != null) { button.setImage(image); } button.setImagePosition(imagePosition); button.setHorizontallyCenterContents(horizontallyCenterContents); button.setVerticallyCenterContents(verticallyCenterContents); button.setFillVerticalSpace(fillVerticalSpace); button.setFillHorizontalSpace(fillHorizontalSpace); button.setToggleable(toggleable); if(imagePadding != 0) { button.setImagePadding(imagePadding); } if(cornerRadius != 0) { button.setRoundedCorners(true); button.setCornerRadius(cornerRadius); } else { button.setRoundedCorners(false); // not sure i want to do this: the default is ON, but I like the idea that the builder lets you do what you want } if(font != null) { button.setFont(font); } if(defaultColors != null) { button.setDefaultColors(defaultColors.getTopBackgroundColor(), defaultColors.getBottomBackgroundColor(), defaultColors.getBorderColor(), defaultColors.getFontColor()); } if(selectedColors != null) { button.setSelectedColors(selectedColors.getTopBackgroundColor(), selectedColors.getBottomBackgroundColor(), selectedColors.getBorderColor(), selectedColors.getFontColor()); } if(hoverColors != null) { button.setHoverColors(hoverColors.getTopBackgroundColor(), hoverColors.getBottomBackgroundColor(), hoverColors.getBorderColor(), hoverColors.getFontColor()); } if(clickedColors != null) { button.setClickedColors(clickedColors.getTopBackgroundColor(), clickedColors.getBottomBackgroundColor(), clickedColors.getBorderColor(), clickedColors.getFontColor()); } if(inactiveColors != null) { button.setInactiveColors(inactiveColors.getTopBackgroundColor(), inactiveColors.getBottomBackgroundColor(), inactiveColors.getBorderColor(), inactiveColors.getFontColor()); } if(accessibilityName != null) { button.setAccessibilityName(accessibilityName); } if(defaultMouseClickAndReturnKeyHandler != null) { new DefaultButtonClickHandler(button) { @Override void clicked() { defaultMouseClickAndReturnKeyHandler.clicked(); } }; } return button; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy