/******************************************************************************* * Copyright (c) 2000, 2015 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 *******************************************************************************/ package org.eclipse.ui.forms; import java.util.HashMap; import java.util.Map; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; /** * Manages colors that will be applied to forms and form widgets. The colors are * chosen to make the widgets look correct in the editor area. If a different * set of colors is needed, subclass this class and override 'initialize' and/or * 'initializeColors'. * * @since 3.0 */ public class FormColors { /** * Key for the form title foreground color. * * @deprecated use <code>IFormColors.TITLE</code>. */ @Deprecated public static final String TITLE = IFormColors.TITLE; /** * Key for the tree/table border color. * * @deprecated use <code>IFormColors.BORDER</code> */ @Deprecated public static final String BORDER = IFormColors.BORDER; /** * Key for the section separator color. * * @deprecated use <code>IFormColors.SEPARATOR</code>. */ @Deprecated public static final String SEPARATOR = IFormColors.SEPARATOR; /** * Key for the section title bar background. * * @deprecated use <code>IFormColors.TB_BG */ @Deprecated public static final String TB_BG = IFormColors.TB_BG; /** * Key for the section title bar foreground. * * @deprecated use <code>IFormColors.TB_FG</code> */ @Deprecated public static final String TB_FG = IFormColors.TB_FG; /** * Key for the section title bar gradient. * * @deprecated use <code>IFormColors.TB_GBG</code> */ @Deprecated public static final String TB_GBG = IFormColors.TB_GBG; /** * Key for the section title bar border. * * @deprecated use <code>IFormColors.TB_BORDER</code>. */ @Deprecated public static final String TB_BORDER = IFormColors.TB_BORDER; /** * Key for the section toggle color. Since 3.1, this color is used for all * section styles. * * @deprecated use <code>IFormColors.TB_TOGGLE</code>. */ @Deprecated public static final String TB_TOGGLE = IFormColors.TB_TOGGLE; /** * Key for the section toggle hover color. * * @since 3.1 * @deprecated use <code>IFormColors.TB_TOGGLE_HOVER</code>. */ @Deprecated public static final String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER; protected Map<String, Color> colorRegistry = new HashMap<>(10); private LocalResourceManager resources; protected Color background; protected Color foreground; private boolean shared; protected Display display; protected Color border; /** * Creates form colors using the provided display. * * @param display * the display to use */ public FormColors(Display display) { this.display = display; initialize(); } /** * Returns the display used to create colors. * * @return the display */ public Display getDisplay() { return display; } /** * Initializes the colors. Subclasses can override this method to change the * way colors are created. Alternatively, only the color table can be * modified by overriding <code>initializeColorTable()</code>. * * @see #initializeColorTable */ protected void initialize() { background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); initializeColorTable(); updateBorderColor(); } /** * Allocates colors for the following keys: BORDER, SEPARATOR and * TITLE. Subclasses can override to allocate these colors differently. */ protected void initializeColorTable() { createTitleColor(); createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB()); RGB black = getSystemColor(SWT.COLOR_BLACK); RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); createColor(IFormColors.BORDER, blend(borderRGB, black, 80)); } /** * Allocates colors for the section tool bar (all the keys that start with * TB). Since these colors are only needed when TITLE_BAR style is used with * the Section widget, they are not needed all the time and are allocated on * demand. Consequently, this method will do nothing if the colors have been * already initialized. Call this method prior to using colors with the TB * keys to ensure they are available. */ public void initializeSectionToolBarColors() { if (colorRegistry.containsKey(IFormColors.TB_BG)) return; createTitleBarGradientColors(); createTitleBarOutlineColors(); createTwistieColors(); } /** * Allocates additional colors for the form header, namely background * gradients, bottom separator keylines and DND highlights. Since these * colors are only needed for clients that want to use these particular * style of header rendering, they are not needed all the time and are * allocated on demand. Consequently, this method will do nothing if the * colors have been already initialized. Call this method prior to using * color keys with the H_ prefix to ensure they are available. * * @since 3.3 */ protected void initializeFormHeaderColors() { if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2)) return; createFormHeaderColors(); } /** * Returns the RGB value of the system color represented by the code * argument, as defined in <code>SWT</code> class. * * @param code * the system color constant as defined in <code>SWT</code> * class. * @return the RGB value of the system color */ public RGB getSystemColor(int code) { return getDisplay().getSystemColor(code).getRGB(); } /** * Creates the color for the specified key using the provided RGB object. * The color object will be returned and also put into the registry. When * the class is disposed, the color will be disposed with it. * * @param key * the unique color key * @param rgb * the RGB object * @return the allocated color object */ public Color createColor(String key, RGB rgb) { Color c = getResourceManager().createColor(rgb); Color prevC = colorRegistry.get(key); if (prevC != null && !prevC.isDisposed()) getResourceManager().destroyColor(prevC.getRGB()); colorRegistry.put(key, c); return c; } /** * Creates a color that can be used for areas of the form that is inactive. * These areas can contain images, links, controls and other content but are * considered auxilliary to the main content area. * * <p> * The color should not be disposed because it is managed by this class. * * @return the inactive form color * @since 3.1 */ public Color getInactiveBackground() { String key = "__ncbg__"; //$NON-NLS-1$ Color color = getColor(key); if (color == null) { RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION); // a blend of 95% white and 5% list selection system color RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5); color = createColor(key, ncbg); } return color; } /** * Creates the color for the specified key using the provided RGB values. * The color object will be returned and also put into the registry. If * there is already another color object under the same key in the registry, * the existing object will be disposed. When the class is disposed, the * color will be disposed with it. * * @param key * the unique color key * @param r * red value * @param g * green value * @param b * blue value * @return the allocated color object */ public Color createColor(String key, int r, int g, int b) { return createColor(key, new RGB(r,g,b)); } /** * Computes the border color relative to the background. Allocated border * color is designed to work well with white. Otherwise, standard widget * background color will be used. */ protected void updateBorderColor() { if (isWhiteBackground()) border = getColor(IFormColors.BORDER); else { border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); Color bg = getImpliedBackground(); if (border.getRed() == bg.getRed() && border.getGreen() == bg.getGreen() && border.getBlue() == bg.getBlue()) border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW); } } /** * Sets the background color. All the toolkits that use this class will * share the same background. * * @param bg * background color */ public void setBackground(Color bg) { this.background = bg; updateBorderColor(); updateFormHeaderColors(); } /** * Sets the foreground color. All the toolkits that use this class will * share the same foreground. * * @param fg * foreground color */ public void setForeground(Color fg) { this.foreground = fg; } /** * Returns the current background color. * * @return the background color */ public Color getBackground() { return background; } /** * Returns the current foreground color. * * @return the foreground color */ public Color getForeground() { return foreground; } /** * Returns the computed border color. Border color depends on the background * and is recomputed whenever the background changes. * * @return the current border color */ public Color getBorderColor() { return border; } /** * Tests if the background is white. White background has RGB value * 255,255,255. * * @return <samp>true</samp> if background is white, <samp>false</samp> * otherwise. */ public boolean isWhiteBackground() { Color bg = getImpliedBackground(); return bg.getRed() == 255 && bg.getGreen() == 255 && bg.getBlue() == 255; } /** * Returns the color object for the provided key or <samp>null </samp> if * not in the registry. * * @param key * the color key * @return color object if found, or <samp>null </samp> if not. */ public Color getColor(String key) { if (key.startsWith(IFormColors.TB_PREFIX)) initializeSectionToolBarColors(); else if (key.startsWith(IFormColors.H_PREFIX)) initializeFormHeaderColors(); return colorRegistry.get(key); } /** * Disposes all the colors in the registry. */ public void dispose() { if (resources != null) resources.dispose(); resources = null; colorRegistry = null; } /** * Marks the colors shared. This prevents toolkits that share this object * from disposing it. */ public void markShared() { this.shared = true; } /** * Tests if the colors are shared. * * @return <code>true</code> if shared, <code>false</code> otherwise. */ public boolean isShared() { return shared; } /** * Blends c1 and c2 based in the provided ratio. * * @param c1 * first color * @param c2 * second color * @param ratio * percentage of the first color in the blend (0-100) * @return the RGB value of the blended color * @since 3.1 */ public static RGB blend(RGB c1, RGB c2, int ratio) { int r = blend(c1.red, c2.red, ratio); int g = blend(c1.green, c2.green, ratio); int b = blend(c1.blue, c2.blue, ratio); return new RGB(r, g, b); } /** * Tests the source RGB for range. * * @param rgb * the tested RGB * @param from * range start (excluding the value itself) * @param to * range end (excluding the value itself) * @return <code>true</code> if at least one of the primary colors in the * source RGB are within the provided range, <code>false</code> * otherwise. * @since 3.1 */ public static boolean testAnyPrimaryColor(RGB rgb, int from, int to) { if (testPrimaryColor(rgb.red, from, to)) return true; if (testPrimaryColor(rgb.green, from, to)) return true; if (testPrimaryColor(rgb.blue, from, to)) return true; return false; } /** * Tests the source RGB for range. * * @param rgb * the tested RGB * @param from * range start (excluding the value itself) * @param to * range end (excluding the value itself) * @return <code>true</code> if at least two of the primary colors in the * source RGB are within the provided range, <code>false</code> * otherwise. * @since 3.1 */ public static boolean testTwoPrimaryColors(RGB rgb, int from, int to) { int total = 0; if (testPrimaryColor(rgb.red, from, to)) total++; if (testPrimaryColor(rgb.green, from, to)) total++; if (testPrimaryColor(rgb.blue, from, to)) total++; return total >= 2; } /** * Blends two primary color components based on the provided ratio. * * @param v1 * first component * @param v2 * second component * @param ratio * percentage of the first component in the blend * @return the blended color component */ private static int blend(int v1, int v2, int ratio) { int b = (ratio * v1 + (100 - ratio) * v2) / 100; return Math.min(255, b); } private Color getImpliedBackground() { if (getBackground() != null) return getBackground(); return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); } private static boolean testPrimaryColor(int value, int from, int to) { return value > from && value < to; } private void createTitleColor() { /* * RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light * if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80); * else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK, * 50); createColor(TITLE, rgb); */ RGB bg = getImpliedBackground().getRGB(); RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION); RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND); RGB rgb = listSelection; // Group 1 // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or // between 0 and 120, then use 100% LIST_SELECTION as it is (no // additions) // Examples: XP Default, Win Classic Standard, Win High Con White, Win // Classic Marine if (testTwoPrimaryColors(listSelection, -1, 121)) rgb = listSelection; // Group 2 // When LIST_BACKGROUND = white (255, 255, 255) or not black, text // colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over // LIST_BACKGROUND // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or // between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION // foreground colour // Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX // Aqua, OSX Graphite, Linux GTK else if (testTwoPrimaryColors(listSelection, 120, 256) || (bg.red == 0 && bg.green == 0 && bg.blue == 0)) rgb = blend(listSelection, listForeground, 50); // Group 3 // When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION // @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND // Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to // LIST_SELECTION foreground colour // Examples: Win High Con Black, Win High Con #1, Win High Con #2 // (covered in the second part of the OR clause above) createColor(IFormColors.TITLE, rgb); } private void createTwistieColors() { RGB rgb = getColor(IFormColors.TITLE).getRGB(); RGB white = getSystemColor(SWT.COLOR_WHITE); createColor(TB_TOGGLE, rgb); rgb = blend(rgb, white, 60); createColor(TB_TOGGLE_HOVER, rgb); } private void createTitleBarGradientColors() { RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); RGB bg = getImpliedBackground().getRGB(); // Group 1 // Rule: If at least 2 of the RGB values are equal to or between 180 and // 255, then apply specified opacity for Group 1 // Examples: Vista, XP Silver, Wn High Con #2 // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND if (testTwoPrimaryColors(tbBg, 179, 256)) tbBg = blend(tbBg, bg, 30); // Group 2 // Rule: If at least 2 of the RGB values are equal to or between 121 and // 179, then apply specified opacity for Group 2 // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND else if (testTwoPrimaryColors(tbBg, 120, 180)) tbBg = blend(tbBg, bg, 20); // Group 3 // Rule: Everything else // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX // Aqua, Wn High Con White, Wn High Con #1 // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND else { tbBg = blend(tbBg, bg, 10); } createColor(IFormColors.TB_BG, tbBg); // for backward compatibility createColor(TB_GBG, tbBg); } private void createTitleBarOutlineColors() { // title bar outline - border color RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); RGB bg = getImpliedBackground().getRGB(); // Group 1 // Rule: If at least 2 of the RGB values are equal to or between 180 and // 255, then apply specified opacity for Group 1 // Examples: Vista, XP Silver, Wn High Con #2 // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND if (testTwoPrimaryColors(tbBorder, 179, 256)) tbBorder = blend(tbBorder, bg, 70); // Group 2 // Rule: If at least 2 of the RGB values are equal to or between 121 and // 179, then apply specified opacity for Group 2 // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND else if (testTwoPrimaryColors(tbBorder, 120, 180)) tbBorder = blend(tbBorder, bg, 50); // Group 3 // Rule: Everything else // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX // Aqua, Wn High Con White, Wn High Con #1 // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND else { tbBorder = blend(tbBorder, bg, 30); } createColor(FormColors.TB_BORDER, tbBorder); } private void updateFormHeaderColors() { if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) { disposeIfFound(IFormColors.H_GRADIENT_END); disposeIfFound(IFormColors.H_GRADIENT_START); disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1); disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2); disposeIfFound(IFormColors.H_HOVER_LIGHT); disposeIfFound(IFormColors.H_HOVER_FULL); initializeFormHeaderColors(); } } private void disposeIfFound(String key) { Color color = getColor(key); if (color != null) { colorRegistry.remove(key); color.dispose(); } } private void createFormHeaderColors() { createFormHeaderGradientColors(); createFormHeaderKeylineColors(); createFormHeaderDNDColors(); } private void createFormHeaderGradientColors() { RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); Color bgColor = getImpliedBackground(); RGB bg = bgColor.getRGB(); RGB bottom, top; // Group 1 // Rule: If at least 2 of the RGB values are equal to or between 180 and // 255, then apply specified opacity for Group 1 // Examples: Vista, XP Silver, Wn High Con #2 // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND if (testTwoPrimaryColors(titleBg, 179, 256)) { bottom = blend(titleBg, bg, 30); top = bg; } // Group 2 // Rule: If at least 2 of the RGB values are equal to or between 121 and // 179, then apply specified opacity for Group 2 // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND else if (testTwoPrimaryColors(titleBg, 120, 180)) { bottom = blend(titleBg, bg, 20); top = bg; } // Group 3 // Rule: If at least 2 of the RGB values are equal to or between 0 and // 120, then apply specified opacity for Group 3 // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX // Aqua, Wn High Con White, Wn High Con #1 // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND else { bottom = blend(titleBg, bg, 10); top = bg; } createColor(IFormColors.H_GRADIENT_END, top); createColor(IFormColors.H_GRADIENT_START, bottom); } private void createFormHeaderKeylineColors() { RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); Color bgColor = getImpliedBackground(); RGB bg = bgColor.getRGB(); RGB keyline2; // H_BOTTOM_KEYLINE1 createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255)); // H_BOTTOM_KEYLINE2 // Group 1 // Rule: If at least 2 of the RGB values are equal to or between 180 and // 255, then apply specified opacity for Group 1 // Examples: Vista, XP Silver, Wn High Con #2 // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND if (testTwoPrimaryColors(titleBg, 179, 256)) keyline2 = blend(titleBg, bg, 70); // Group 2 // Rule: If at least 2 of the RGB values are equal to or between 121 and // 179, then apply specified opacity for Group 2 // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND else if (testTwoPrimaryColors(titleBg, 120, 180)) keyline2 = blend(titleBg, bg, 50); // Group 3 // Rule: If at least 2 of the RGB values are equal to or between 0 and // 120, then apply specified opacity for Group 3 // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX // Aqua, Wn High Con White, Wn High Con #1 // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND else keyline2 = blend(titleBg, bg, 30); // H_BOTTOM_KEYLINE2 createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2); } private void createFormHeaderDNDColors() { RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT); Color bgColor = getImpliedBackground(); RGB bg = bgColor.getRGB(); RGB light, full; // ALL Themes // // Light Highlight // When *near* the 'hot' area // Rule: If near the title in the 'hot' area, show background highlight // TITLE_BACKGROUND_GRADIENT @ 40% light = blend(titleBg, bg, 40); // Full Highlight // When *on* the title area (regions 1 and 2) // Rule: If near the title in the 'hot' area, show background highlight // TITLE_BACKGROUND_GRADIENT @ 60% full = blend(titleBg, bg, 60); // H_DND_LIGHT // H_DND_FULL createColor(IFormColors.H_HOVER_LIGHT, light); createColor(IFormColors.H_HOVER_FULL, full); } private LocalResourceManager getResourceManager() { if (resources == null) resources = new LocalResourceManager(JFaceResources.getResources()); return resources; } }