/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG 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: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.ui.swt.lnf.renderer; import org.eclipse.core.runtime.Assert; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontMetrics; 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.widgets.Control; import org.eclipse.riena.core.util.StringUtils; import org.eclipse.riena.ui.swt.ModuleTitleBar; import org.eclipse.riena.ui.swt.facades.GCFacade; import org.eclipse.riena.ui.swt.lnf.AbstractLnfRenderer; import org.eclipse.riena.ui.swt.lnf.FlasherSupportForRenderer; import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants; import org.eclipse.riena.ui.swt.lnf.LnfManager; import org.eclipse.riena.ui.swt.lnf.rienadefault.RienaDefaultLnf; import org.eclipse.riena.ui.swt.utils.SwtUtilities; /** * Renderer of the title bar of an embedded view. */ public class EmbeddedTitlebarRenderer extends AbstractLnfRenderer { private final static int TITLEBAR_LABEL_PADDING_X = SwtUtilities.convertXToDpiTruncate(5); private final static int TITLEBAR_LABEL_PADDING_TOP = SwtUtilities.convertYToDpiTruncate(4); private final static int TITLEBAR_ICON_TEXT_GAP = SwtUtilities.convertXToDpiTruncate(4); private Color defaultColor = null; private Control control; private Image image; private String title; // private Color edgeColor; private boolean active; private boolean pressed; private boolean hover; private boolean closeable; private boolean closeButtonPressed; private boolean closeButtonHover; private final FlasherSupportForRenderer flasherSupport; /** * Creates a new instance of the renderer for an embedded title bar. */ public EmbeddedTitlebarRenderer() { super(); image = null; active = false; pressed = false; hover = false; closeable = false; flasherSupport = new FlasherSupportForRenderer(this, new MarkerUpdater()); } /** * @see org.eclipse.riena.navigation.ui.swt.lnf.ILnfRenderer#dispose() */ public void dispose() { SwtUtilities.dispose(getImage()); SwtUtilities.dispose(defaultColor); control = null; } /** * Computes the size (height) of the title bar. * * @param gc * <code>GC</code> of the component <code>Control</code> * @param wHint * the width hint * @param hHint * the height hint * @return a Point representing the size of the title bar */ public Point computeSize(final GC gc, final int wHint, final int hHint) { final Font font = getTitlebarFont(); gc.setFont(font); final FontMetrics fontMetrics = gc.getFontMetrics(); final int h = fontMetrics.getHeight() + TITLEBAR_LABEL_PADDING_TOP * 2; return new Point(wHint, h); } /** * Returns the font of the title bar. * * @return font */ protected Font getTitlebarFont() { final RienaDefaultLnf lnf = LnfManager.getLnf(); final Font font = lnf.getFont(LnfKeyConstants.EMBEDDED_TITLEBAR_FONT); return font; } /** * Draws the background in the specified region with the specified {@link GC}. */ protected void drawBackground(final GC gc, final int x, final int y, final int w, final int h) { final Color startColor = getColor(LnfKeyConstants.EMBEDDED_TITLEBAR_ACTIVE_BACKGROUND_START_COLOR, LnfKeyConstants.EMBEDDED_TITLEBAR_PASSIVE_BACKGROUND_START_COLOR, null); final Color endColor = getColor(LnfKeyConstants.EMBEDDED_TITLEBAR_ACTIVE_BACKGROUND_END_COLOR, LnfKeyConstants.EMBEDDED_TITLEBAR_PASSIVE_BACKGROUND_END_COLOR, null); gc.setForeground(startColor); gc.setBackground(endColor); if (isPressed() && !isActive() && !isCloseButtonPressed()) { gc.fillRectangle(x, y, w, h); } else { gc.fillGradientRectangle(x, y, w, h, true); } } /** * Draws the border of the title bar with the specified {@link GC}. */ protected void drawBorder(final GC gc) { gc.setForeground(getBorderColor()); final Rectangle b = getBounds(); gc.drawRectangle(b.x, b.y, b.width - 1, b.height - 1); } /** * @param value * title text * * @see org.eclipse.riena.ui.swt.lnf.AbstractLnfRenderer#paint(org.eclipse.swt.graphics.GC, java.lang.Object) */ @Override public void paint(final GC gc, final Object value) { Assert.isNotNull(gc); Assert.isNotNull(value); Assert.isTrue(value instanceof Control); control = (Control) value; if (getBounds() == null) { return; } final GCFacade gcFacade = GCFacade.getDefault(); gcFacade.setAdvanced(gc, true); gcFacade.setAntialias(gc, SWT.ON); final Font font = getTitlebarFont(); gc.setFont(font); int x = getBounds().x; int y = getBounds().y; int w = getBounds().width; int h = getBounds().height; // Background + border drawBackground(gc, x, y, w, h); drawBorder(gc); // // Edges // if ((edgeColor == null) || (edgeColor.isDisposed())) { // edgeColor = SwtUtilities.makeBrighter(borderColor, 1.15f); // } // gc.setForeground(edgeColor); // x = getBounds().x; // y = getBounds().y; // gc.drawPoint(x, y); // x = getBounds().x + getWidth(); // y = getBounds().y; // gc.drawPoint(x, y); // x = getBounds().x; // y = getBounds().y + getHeight(); // gc.drawPoint(x, y); // x = getBounds().x + getWidth(); // y = getBounds().y + getHeight(); // gc.drawPoint(x, y); // Close icon final Rectangle closeBounds = computeCloseButtonBounds(); if (isCloseable()) { final Image closeImage = getCloseButtonImage(); gc.drawImage(closeImage, closeBounds.x, closeBounds.y); } else { closeBounds.x = 0; closeBounds.y = 0; closeBounds.width = 0; closeBounds.height = 0; } // Icon x = getBounds().x + TITLEBAR_LABEL_PADDING_X; if (getImage() != null) { final Rectangle imageBounds = getImage().getBounds(); y = getBounds().y + (getHeight() - imageBounds.height) / 2; y++; gc.drawImage(getImage(), x, y); x += imageBounds.width + TITLEBAR_ICON_TEXT_GAP; } // Text String text = getTitle(); if (!StringUtils.isEmpty(text)) { final Color fgColor = getColor(LnfKeyConstants.EMBEDDED_TITLEBAR_ACTIVE_FOREGROUND, LnfKeyConstants.EMBEDDED_TITLEBAR_PASSIVE_FOREGROUND, LnfKeyConstants.EMBEDDED_TITLEBAR_DISABLED_FOREGROUND); gc.setForeground(fgColor); final int y2 = (getHeight() - gc.getFontMetrics().getHeight()) / 2; y = getBounds().y + y2; y++; text = getClippedText(gc, text); gc.drawText(text, x, y, true); } // Hover border -> it would be better to have a separated renderer for the ModuleTitleBar if (control instanceof ModuleTitleBar && isHover() && (!isPressed() || isActive())) { x = getBounds().x; y = getBounds().y; w = getBounds().width; h = getHeight(); getHoverBorderRenderer().setBounds(x, y, w, h); getHoverBorderRenderer().paint(gc, null); } getFlasherSupport().startFlasher(); } public boolean isActive() { return active; } public void setActive(final boolean active) { this.active = active; } public boolean isPressed() { return pressed; } public void setPressed(final boolean pressed) { this.pressed = pressed; } public boolean isHover() { return hover; } public void setHover(final boolean hover) { this.hover = hover; } public Image getImage() { return image; } public void setImage(final Image image) { this.image = image; } public void setTitle(final String title) { this.title = title; } public String getTitle() { return title; } protected int getHeight() { return getBounds().height - 1; } protected int getWidth() { return getBounds().width - 1; } protected HoverBorderRenderer getHoverBorderRenderer() { return (HoverBorderRenderer) LnfManager.getLnf().getRenderer(LnfKeyConstants.SUB_MODULE_VIEW_HOVER_BORDER_RENDERER); } /** * @return the closeable */ public boolean isCloseable() { return closeable; } /** * @param closeable * the closeable to set */ public void setCloseable(final boolean closeable) { this.closeable = closeable; } /** * Computes the bounds of the close "button". * * @return bounds */ protected Rectangle computeCloseButtonBounds() { final Rectangle closeBounds = new Rectangle(0, 0, 0, 0); final Image closeImage = getCloseButtonImage(); // if no close icon was found, return 0 sized bounds if (closeImage == null) { return closeBounds; } final Rectangle bounds = getBounds(); if (bounds != null) { closeBounds.width = closeImage.getBounds().width; closeBounds.height = closeImage.getBounds().height; closeBounds.x = bounds.x + getWidth() - closeBounds.width - TITLEBAR_LABEL_PADDING_X; closeBounds.y = bounds.y + (getHeight() - closeBounds.height) / 2; } return closeBounds; } /** * Returns <code>true</code> if the given point is inside the bounds of the close button, and <code>false</code> otherwise. * * @param pt * the point to test * @return <code>true</code> if the button bounds contains the point and <code>false</code> otherwise */ public boolean isInsideCloseButton(final Point pt) { final Rectangle closeBounds = computeCloseButtonBounds(); return closeBounds.contains(pt); } /** * Returns the image of the close button according of the current button state. * * @return button image */ protected Image getCloseButtonImage() { final RienaDefaultLnf lnf = LnfManager.getLnf(); String key = LnfKeyConstants.EMBEDDED_TITLEBAR_CLOSE_ICON; if (isEnabled()) { if (isCloseButtonPressed()) { key = LnfKeyConstants.EMBEDDED_TITLEBAR_CLOSE_HOVER_SELECTED_ICON; } else if (isCloseButtonHover()) { key = LnfKeyConstants.EMBEDDED_TITLEBAR_CLOSE_HOVER_ICON; } } else { key = LnfKeyConstants.EMBEDDED_TITLEBAR_CLOSE_INACTIVE_ICON; } final Image closeImage = lnf.getImage(key); return closeImage; } /** * @return the pressed */ public boolean isCloseButtonPressed() { return closeButtonPressed; } /** * @param pressed * the pressed to set */ public void setCloseButtonPressed(final boolean pressed) { closeButtonPressed = pressed; } /** * @return the hover */ public boolean isCloseButtonHover() { return closeButtonHover; } /** * @param hover * the hover to set */ public void setCloseButtonHover(final boolean hover) { closeButtonHover = hover; } /** * Computes the bounds of the text. * * @return bounds */ public Rectangle computeTextBounds(final GC gc) { final Rectangle textBounds = new Rectangle(0, 0, 0, 0); final Rectangle bounds = getBounds(); if (bounds != null) { textBounds.x = bounds.x + TITLEBAR_LABEL_PADDING_X; if (getImage() != null) { textBounds.x += getImage().getBounds().width + TITLEBAR_ICON_TEXT_GAP; } textBounds.width = getWidth() - (textBounds.x - bounds.x) - TITLEBAR_LABEL_PADDING_TOP; final Font font = getTitlebarFont(); gc.setFont(font); final FontMetrics fontMetrics = gc.getFontMetrics(); textBounds.height = fontMetrics.getHeight(); textBounds.y = bounds.y + (bounds.height - textBounds.height) / 2; } return textBounds; } /** * Clips the given text if the text it too long for the title bar. * * @param gc * @param text * text to clip (if necessary) * @return text */ public String getClippedText(final GC gc, final String text) { if (StringUtils.isEmpty(text)) { return text; } final Rectangle textBounds = computeTextBounds(gc); int maxWidth = textBounds.width; if (isCloseable()) { final Rectangle closeBounds = computeCloseButtonBounds(); maxWidth -= closeBounds.width; } final Font font = getTitlebarFont(); gc.setFont(font); return SwtUtilities.clipText(gc, text, maxWidth); } /** * This class updates (redraws) the title bar, so that the marker are also updated (redrawn). */ private class MarkerUpdater implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { if (control != null && !control.isDisposed()) { control.redraw(); } } } protected Color getBorderColor() { return getColor(LnfKeyConstants.EMBEDDED_TITLEBAR_ACTIVE_BORDER_COLOR, LnfKeyConstants.EMBEDDED_TITLEBAR_PASSIVE_BORDER_COLOR, LnfKeyConstants.EMBEDDED_TITLEBAR_DISABLED_BORDER_COLOR); } /** * Returns according to the states of the title bar the color of one of the given key.<br> * If one key is not needed, the parameter can be {@code null}. * * @param activeColorKey * @param passiveColorKey * @param disabeldColorKey * @return color TODO same code in SubApplicationTabRenderer Returns according to the */ protected Color getColor(final String activeColorKey, final String passiveColorKey, final String disabeldColorKey) { Color color = null; String colorKey = getKey(activeColorKey, passiveColorKey, disabeldColorKey); if (colorKey == null) { colorKey = activeColorKey; } final RienaDefaultLnf lnf = LnfManager.getLnf(); color = lnf.getColor(colorKey); if (color == null) { return getDefaultColor(); } return color; } private Color getDefaultColor() { // this was added so that the class loading no longer accesses the UIThread if (defaultColor == null) { defaultColor = new Color(null, 0, 0, 0); // black } return defaultColor; } /** * Selects according to the state of the title bar one of the given keys.<br> * If one key is not needed, the parameter can be {@code null}. * * @param activeKey * @param passiveKey * @param disabledKey * @return key * @TODO same code in SubApplicationTabRenderer Returns according to the */ private String getKey(final String activeKey, final String passiveKey, final String disabledKey) { String key = null; if (isEnabled()) { if (isActive() || getFlasherSupport().isProcessMarkerVisible()) { key = activeKey; } else { key = passiveKey; } } else { key = disabledKey; } if (key == null) { key = activeKey; } if (key == null) { key = passiveKey; } if (key == null) { key = disabledKey; } return key; } protected FlasherSupportForRenderer getFlasherSupport() { return flasherSupport; } }