[autocomplete] 126/143: Adding support for using fancy smancy Substance renderers if a Substance Look and Feel is installed. We don't want a compile time dependency so everything is done by reflection and delegation. This means we don't get Substance's rollover effects, but that's a minor issue.

Benjamin Mesing ben at alioth.debian.org
Sat Oct 19 12:53:30 UTC 2013


This is an automated email from the git hooks/post-receive script.

ben pushed a commit to branch master
in repository autocomplete.

commit 1f665e5c2d4dc68407d5b8944690170876b9c803
Author: bobbylight <robert at fifesoft.com>
Date:   Sun Jan 20 07:30:01 2013 +0000

    Adding support for using fancy smancy Substance renderers if a Substance Look and Feel is installed.  We don't want a compile time dependency so everything is done by reflection and delegation.  This means we don't get Substance's rollover effects, but that's a minor issue.
---
 .../ui/autocomplete/AutoCompletePopupWindow.java   |   49 +++++-
 .../ui/autocomplete/CompletionCellRenderer.java    |  161 ++++++++++++++++++--
 src/org/fife/ui/autocomplete/TipUtil.java          |    8 +
 src/org/fife/ui/autocomplete/Util.java             |   83 ++++++++++
 4 files changed, 285 insertions(+), 16 deletions(-)

diff --git a/src/org/fife/ui/autocomplete/AutoCompletePopupWindow.java b/src/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
index 4d22903..0956704 100644
--- a/src/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
+++ b/src/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
@@ -123,6 +123,12 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener,
 	 */
 	private static final int VERTICAL_SPACE			= 1;
 
+	/**
+	 * The class name of the Substance List UI.
+	 */
+	private static final String SUBSTANCE_LIST_UI =
+			"org.pushingpixels.substance.internal.ui.SubstanceListUI";
+
 
 	/**
 	 * Constructor.
@@ -130,19 +136,14 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener,
 	 * @param parent The parent window (hosting the text component).
 	 * @param ac The auto-completion instance.
 	 */
-	public AutoCompletePopupWindow(Window parent, AutoCompletion ac) {
+	public AutoCompletePopupWindow(Window parent, final AutoCompletion ac) {
 
 		super(parent);
 		ComponentOrientation o = ac.getTextComponentOrientation();
 
 		this.ac = ac;
 		model = new CompletionListModel();
-		list = new JList(model) {
-			public void setUI(ListUI ui) {
-				// Keep our special UI, no matter what
-				super.setUI(new FastListUI());
-			}
-		};
+		list = new PopupList(model);
 
 		list.setCellRenderer(new DelegatingCellRenderer());
 		list.addListSelectionListener(this);
@@ -920,6 +921,40 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener,
 	}
 
 
+	/**
+	 * The actual list of completion choices in this popup window.
+	 */
+	private class PopupList extends JList {
+
+		public PopupList(CompletionListModel model) {
+			super(model);
+		}
+
+		public void setUI(ListUI ui) {
+			if (Util.getUseSubstanceRenderers() &&
+					SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) {
+				// Substance requires its special ListUI be installed for
+				// its renderers to actually render (!), but long completion
+				// lists (e.g. PHPCompletionProvider in RSTALanguageSupport)
+				// will simply populate too slowly on initial display (when
+				// calculating preferred size of all items), so in this case
+				// we give a prototype cell value.
+				CompletionProvider p = ac.getCompletionProvider();
+				BasicCompletion bc = new BasicCompletion(p, "Hello world");
+				setPrototypeCellValue(bc);
+			}
+			else {
+				// Our custom UI that is faster for long HTML completion
+				// lists.
+				ui = new FastListUI();
+				setPrototypeCellValue(null);
+			}
+			super.setUI(ui);
+		}
+
+	}
+
+
 	class RightAction extends AbstractAction {
 
 		public void actionPerformed(ActionEvent e) {
diff --git a/src/org/fife/ui/autocomplete/CompletionCellRenderer.java b/src/org/fife/ui/autocomplete/CompletionCellRenderer.java
index 5a82a25..af6365a 100644
--- a/src/org/fife/ui/autocomplete/CompletionCellRenderer.java
+++ b/src/org/fife/ui/autocomplete/CompletionCellRenderer.java
@@ -17,6 +17,7 @@ import java.awt.Rectangle;
 import javax.swing.DefaultListCellRenderer;
 import javax.swing.Icon;
 import javax.swing.JList;
+import javax.swing.SwingUtilities;
 import javax.swing.plaf.basic.BasicHTML;
 import javax.swing.text.View;
 
@@ -58,6 +59,11 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 	private boolean showTypes;
 
 	/**
+	 * The color to use when rendering types in completion text.
+	 */
+	private String typeColor;
+
+	/**
 	 * During rendering, whether the item being rendered is selected.
 	 */
 	private boolean selected;
@@ -79,6 +85,14 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 	private Rectangle paintTextR;
 
 	/**
+	 * An optional delegate renderer (primarily for Substance).
+	 */
+	private DefaultListCellRenderer delegate;
+
+	private static final String SUBSTANCE_RENDERER_CLASS_NAME =
+			"org.pushingpixels.substance.api.renderers.SubstanceDefaultListCellRenderer";
+
+	/**
 	 * Keeps the HTML descriptions from "wrapping" in the list, which cuts off
 	 * words.
 	 */
@@ -89,10 +103,63 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 	 * Constructor.
 	 */
 	public CompletionCellRenderer() {
-		//setDisplayFont(new Font("Monospaced", Font.PLAIN, 12));
-		setShowTypes(true);
-		paramColor = "#aa0077";
-		paintTextR = new Rectangle();
+		init();
+	}
+
+
+	/**
+	 * Constructor.  This is primarily a hook for Substance, or any other
+	 * Look and Feel whose renderers look drastically different than standard
+	 * <code>DefaultListCellRenderer</code>s.  Everything except for the text
+	 * rendering will be done by the delegate.  In almost all scenarios, you
+	 * will want to use the no-argument constructor instead of this one.
+	 *
+	 * @param delegate The delegate renderer.
+	 * @see #delegateToSubstanceRenderer()
+	 */
+	public CompletionCellRenderer(DefaultListCellRenderer delegate) {
+		setDelegateRenderer(delegate);
+		init();
+	}
+
+
+	/**
+	 * Returns a decent "parameter" color based on the current default
+	 * foreground color.
+	 *
+	 * @return The parameter color to use.
+	 */
+	private String createParamColor() {
+		return Util.isLightForeground(getForeground()) ? 
+				Util.getHexString(Util.getHyperlinkForeground()): "#aa0077";
+	}
+
+
+	/**
+	 * Returns a decent "type" color based on the current default foreground
+	 * color.
+	 *
+	 * @return The type color to use.
+	 */
+	private String createTypeColor() {
+		return "#808080";
+	}
+
+
+	/**
+	 * Attempts to delegate rendering to a Substance cell renderer.  This
+	 * should only be called if Substance is known to be on the classpath.
+	 *
+	 * @throws Exception If Substance is not on the classpath, or some other
+	 *         error occurs creating the Substance cell renderer.
+	 * @see Util#getUseSubstanceRenderers()
+	 * @see #setDelegateRenderer(DefaultListCellRenderer)
+	 */
+	public void delegateToSubstanceRenderer() throws Exception {
+		Class clazz = Class.forName(SUBSTANCE_RENDERER_CLASS_NAME);
+		DefaultListCellRenderer delegate =
+				(DefaultListCellRenderer)clazz.newInstance();
+		setDelegateRenderer(delegate);
 	}
 
 
@@ -109,6 +176,17 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 
 
 	/**
+	 * Returns the delegate renderer, or <code>null</code> if there is none.
+	 *
+	 * @return The delegate renderer.
+	 * @see #setDelegateRenderer(DefaultListCellRenderer)
+	 */
+	public DefaultListCellRenderer getDelegateRenderer() {
+		return delegate;
+	}
+
+
+	/**
 	 * Returns the font used when rendering completions.
 	 *
 	 * @return The font.  If this is <code>null</code>, then the default list
@@ -137,7 +215,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 			setFont(font); // Overrides super's setFont(list.getFont()).
 		}
 		this.selected = selected;
-		this.realBG = altBG!=null && (index&1)==0 ? altBG : list.getBackground();
+		this.realBG = altBG!=null && (index&1)==1 ? altBG : list.getBackground();
 
 		Completion c = (Completion)value;
 		setIcon(c.getIcon());
@@ -162,7 +240,17 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 			prepareForOtherCompletion(list, c, index, selected, hasFocus);
 		}
 
-		if (!selected && (index&1)==0 && altBG!=null) {
+		// A delegate renderer might do its own alternate row striping
+		// (Substance does).
+		if (delegate!=null) {
+			delegate.getListCellRendererComponent(list, getText(), index,
+					selected, hasFocus);
+			delegate.setFont(getFont());
+			delegate.setIcon(getIcon());
+			return delegate;
+		}
+
+		if (!selected && (index&1)==1 && altBG!=null) {
 			setBackground(altBG);
 		}
 
@@ -183,6 +271,15 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 	}
 
 
+	private void init() {
+		//setDisplayFont(new Font("Monospaced", Font.PLAIN, 12));
+		setShowTypes(true);
+		typeColor = createTypeColor();
+		paramColor = createParamColor();
+		paintTextR = new Rectangle();
+	}
+
+
 	protected void paintComponent(Graphics g) {
 
 		//super.paintComponent(g);
@@ -282,7 +379,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 		if (getShowTypes() && fc.getType()!=null) {
 			sb.append(" : ");
 			if (!selected) {
-				sb.append("<font color='#808080'>");
+				sb.append("<font color='").append(typeColor).append("'>");
 			}
 			sb.append(fc.getType());
 			if (!selected) {
@@ -355,7 +452,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 		if (definition!=null) {
 			sb.append(" - ");
 			if (!selected) {
-				sb.append("<font color='#808080'>");
+				sb.append("<font color='").append(typeColor).append("'>");
 			}
 			sb.append(definition);
 			if (!selected) {
@@ -386,7 +483,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 		if (getShowTypes() && vc.getType()!=null) {
 			sb.append(" : ");
 			if (!selected) {
-				sb.append("<font color='#808080'>");
+				sb.append("<font color='").append(typeColor).append("'>");
 			}
 			sb.append(vc.getType());
 			if (!selected) {
@@ -413,6 +510,22 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 
 
 	/**
+	 * Sets the delegate renderer.  Most users will never use this method; it
+	 * is primarily a hook for Substance and other Look and Feels whose
+	 * renderers look drastically different from the standard
+	 * <code>DefaultListCellRenderer</code>.
+	 * 
+	 * @param delegate The new delegate renderer.  If this is <code>null</code>,
+	 *        the default rendering of this component is used.
+	 * @see #getDelegateRenderer()
+	 * @see #delegateToSubstanceRenderer()
+	 */
+	public void setDelegateRenderer(DefaultListCellRenderer delegate) {
+		this.delegate = delegate;
+	}
+
+
+	/**
 	 * Sets the font to use when rendering completion items.
 	 *
 	 * @param font The font to use.  If this is <code>null</code>, then
@@ -442,6 +555,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 	 * Sets the color to use for function arguments.
 	 *
 	 * @param color The color to use.  This is ignored if <code>null</code>.
+	 * @see #setTypeColor(Color)
 	 */
 	public void setParamColor(Color color) {
 		if (color!=null) {
@@ -462,4 +576,33 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
 	}
 
 
+	/**
+	 * Sets the color to use for function/field types.  Note that if
+	 * {@link #getShowTypes()} returns <code>false</code>, this property
+	 * effectively does nothing.
+	 *
+	 * @param color The color to use for types.  This is ignored if
+	 *        <code>null</code>.
+	 * @see #setShowTypes(boolean)
+	 * @see #setParamColor(Color)
+	 */
+	public void setTypeColor(Color color) {
+		if (color!=null) {
+			typeColor = Util.getHexString(color);
+		}
+	}
+
+
+	/**
+	 * Overridden to update our delegate, if necessary.
+	 */
+	public void updateUI() {
+		super.updateUI();
+		if (delegate!=null) {
+			SwingUtilities.updateComponentTreeUI(delegate);
+		}
+		paramColor = createParamColor();
+	}
+
+
 }
\ No newline at end of file
diff --git a/src/org/fife/ui/autocomplete/TipUtil.java b/src/org/fife/ui/autocomplete/TipUtil.java
index 2caa0d4..7ee8664 100644
--- a/src/org/fife/ui/autocomplete/TipUtil.java
+++ b/src/org/fife/ui/autocomplete/TipUtil.java
@@ -159,6 +159,14 @@ class TipUtil {
 				"body { font-family: " + font.getFamily() +
 						"; font-size: " + font.getSize() + "pt" +
 						"; color: " + Util.getHexString(fg) + "; }");
+
+		// Always add link foreground rule.  Unfortunately these CSS rules
+		// stack each time the LaF is changed (how can we overwrite them
+		// without clearing out the important "standard" ones?).
+		Color linkFG = Util.getHyperlinkForeground();
+		doc.getStyleSheet().addRule(
+				"a { color: " + Util.getHexString(linkFG) + "; }");
+
 		URL url = TipUtil.class.getResource("bullet_black.png");
 		if (url!=null) {
 			doc.getStyleSheet().addRule(
diff --git a/src/org/fife/ui/autocomplete/Util.java b/src/org/fife/ui/autocomplete/Util.java
index ceab7f5..c994b60 100644
--- a/src/org/fife/ui/autocomplete/Util.java
+++ b/src/org/fife/ui/autocomplete/Util.java
@@ -16,6 +16,8 @@ import java.awt.Rectangle;
 import java.lang.reflect.Method;
 import java.net.URI;
 import java.security.AccessControlException;
+import javax.swing.JLabel;
+import javax.swing.UIManager;
 
 import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
 
@@ -29,6 +31,19 @@ import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
 public class Util {
 
 	/**
+	 * If a system property is defined with this name and set, ignoring case,
+	 * to <code>true</code>, this library will not attempt to use Substance
+	 * renderers.  Otherwise, if a Substance Look and Feel is installed, we
+	 * will attempt to use Substance cell renderers in all of our dropdowns.<p>
+	 * 
+	 * Note that we do not have a build dependency on Substance, so all access
+	 * to Substance stuff is done via reflection.  We will fall back onto
+	 * default renderers if something goes horribly wrong.
+	 */
+	public static final String PROPERTY_DONT_USE_SUBSTANCE_RENDERERS =
+			"org.fife.ui.autocomplete.DontUseSubstanceRenderers";
+
+	/**
 	 * If this system property is <code>true</code>, then even the "main" two
 	 * auto-complete windows will allow window decorations via
 	 * {@link PopupWindowDecorator}.  If this property is undefined or
@@ -39,6 +54,13 @@ public class Util {
 	public static final String PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS =
 		"org.fife.ui.autocomplete.allowDecoratedAutoCompleteWindows";
 
+	/**
+	 * Used for the color of hyperlinks when a LookAndFeel uses light text
+	 * against a dark background.
+	 */
+	private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff);
+
+	private static final boolean useSubstanceRenderers;
 	private static boolean desktopCreationAttempted;
 	private static Object desktop;
 	private static final Object LOCK_DESKTOP_CREATION = new Object();
@@ -158,6 +180,28 @@ public class Util {
 
 
 	/**
+	 * Returns the color to use for hyperlink-style components.  This method
+	 * will return <code>Color.blue</code> unless it appears that the current
+	 * LookAndFeel uses light text on a dark background, in which case a
+	 * brighter alternative is returned.
+	 *
+	 * @return The color to use for hyperlinks.
+	 */
+	static final Color getHyperlinkForeground() {
+
+		// This property is defined by all standard LaFs, even Nimbus (!),
+		// but you never know what crazy LaFs there are...
+		Color fg = UIManager.getColor("Label.foreground");
+		if (fg==null) {
+			fg = new JLabel().getForeground();
+		}
+
+		return isLightForeground(fg) ? LIGHT_HYPERLINK_FG : Color.blue;
+
+	}
+
+
+	/**
 	 * Returns the screen coordinates for the monitor that contains the
 	 * specified point.  This is useful for setups with multiple monitors,
 	 * to ensure that popup windows are positioned properly.
@@ -202,6 +246,32 @@ public class Util {
 
 
 	/**
+	 * Returns whether we should attempt to use Substance cell renderers and
+	 * styles for things such as completion choices, if a Substance Look and
+	 * Feel is installed.  If this is <code>false</code>, we'll use our
+	 * standard rendering for completions, even when Substance is being used.
+	 *
+	 * @return Whether to use Substance renderers if Substance is installed.
+	 */
+	public static boolean getUseSubstanceRenderers() {
+		return useSubstanceRenderers;
+	}
+
+
+	/**
+	 * Returns whether the specified color is "light" to use as a foreground.
+	 * Colors that return <code>true</code> indicate that the current Look and
+	 * Feel probably uses light text colors on a dark background.
+	 *
+	 * @param fg The foreground color.
+	 * @return Whether it is a "light" foreground color.
+	 */
+	public static final boolean isLightForeground(Color fg) {
+		return fg.getRed()>0xa0 && fg.getGreen()>0xa0 && fg.getBlue()>0xa0;
+	}
+
+
+	/**
 	 * Returns whether <code>str</code> starts with <code>start</code>,
 	 * ignoring case.
 	 *
@@ -226,4 +296,17 @@ public class Util {
 	}
 
 
+	static {
+
+		boolean use = true;
+		try {
+			use = !Boolean.getBoolean(PROPERTY_DONT_USE_SUBSTANCE_RENDERERS);
+		} catch (AccessControlException ace) { // We're in an applet.
+			use = true;
+		}
+		useSubstanceRenderers = use;
+
+	}
+
+
 }
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/autocomplete.git



More information about the pkg-java-commits mailing list