diff --git a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
index 3876266..3d3d21d 100644
--- a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
+++ b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java
@@ -74,6 +74,14 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener,
*/
private AutoCompleteDescWindow descWindow;
+ /**
+ * Whether the description window is currently toggled "on" when
+ * {@link AutoCompletion#getDescWindowVisibility()} is
+ * {@link DescWindowVisibility#ON_DEMAND}. Ignored for other visibility
+ * settings.
+ */
+ private boolean descWindowVisibleOnDemand;
+
/**
* The preferred size of the optional description window. This field
* only exists because the user may (and usually will) set the size of
@@ -271,6 +279,115 @@ protected void doAutocomplete() {
}
+ /**
+ * Returns whether the description window should currently be displayed,
+ * per {@link AutoCompletion#getDescWindowVisibility()}.
+ *
+ * @return Whether the description window should currently be displayed.
+ * @see #toggleDescriptionWindow()
+ */
+ private boolean shouldShowDescWindow() {
+ switch (ac.getDescWindowVisibility()) {
+ case ALWAYS:
+ return true;
+ case ON_DEMAND:
+ return descWindowVisibleOnDemand;
+ case NEVER:
+ default:
+ return false;
+ }
+ }
+
+
+ /**
+ * Toggles whether the description window is displayed, when
+ * {@link AutoCompletion#getDescWindowVisibility()} is
+ * {@link DescWindowVisibility#ON_DEMAND}. Does nothing otherwise, or if
+ * this popup window is not currently visible.
+ */
+ void toggleDescriptionWindow() {
+
+ if (ac.getDescWindowVisibility() != DescWindowVisibility.ON_DEMAND || !isVisible()) {
+ return;
+ }
+
+ descWindowVisibleOnDemand = !descWindowVisibleOnDemand;
+
+ if (descWindowVisibleOnDemand) {
+ if (descWindow == null) {
+ descWindow = createDescriptionWindow();
+ }
+ Completion c = list.getSelectedValue();
+ if (c != null) {
+ descWindow.setDescriptionFor(c);
+ }
+ positionDescWindow();
+ descWindow.setVisible(true);
+ }
+ else if (descWindow != null) {
+ descWindow.setVisible(false);
+ }
+
+ }
+
+
+ /**
+ * The key used in the input map for the description window toggle action.
+ */
+ private static final String DESC_WINDOW_TOGGLE_KEY = "AutoCompletion.ToggleDescWindow";
+
+
+ /**
+ * Installs a "description window toggle key" action onto a text component.
+ *
+ * @param ac The auto-completion instance the text component is installed on.
+ * @param tc The text component.
+ * @param ks The keystroke that should toggle the description window's visibility.
+ * @see #uninstallDescWindowToggleKey(JTextComponent, KeyStroke)
+ */
+ static void installDescWindowToggleKey(AutoCompletion ac, JTextComponent tc, KeyStroke ks) {
+ InputMap im = tc.getInputMap();
+ im.put(ks, DESC_WINDOW_TOGGLE_KEY);
+ ActionMap am = tc.getActionMap();
+ am.put(DESC_WINDOW_TOGGLE_KEY, new ToggleDescWindowAction(ac));
+ }
+
+
+ /**
+ * Removes a previously-installed "description window toggle key" action from a text component.
+ *
+ * @param tc The text component.
+ * @param ks The keystroke previously passed to {@link #installDescWindowToggleKey}.
+ */
+ static void uninstallDescWindowToggleKey(JTextComponent tc, KeyStroke ks) {
+ tc.getInputMap().remove(ks);
+ tc.getActionMap().remove(DESC_WINDOW_TOGGLE_KEY);
+ }
+
+
+ /**
+ * Toggles the description window's visibility when triggered while {@link DescWindowVisibility#ON_DEMAND}
+ * is active; a no-op otherwise.
+ */
+ private static final class ToggleDescWindowAction extends AbstractAction {
+
+ private final AutoCompletion ac;
+
+ ToggleDescWindowAction(AutoCompletion ac) {
+ this.ac = ac;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ AutoCompletePopupWindow popupWindow = ac.getPopupWindow();
+ if (popupWindow != null) {
+ popupWindow.toggleDescriptionWindow();
+ }
+ }
+
+ }
+
+
/**
* Returns the copy keystroke to use for this platform.
*
@@ -316,7 +433,7 @@ AutoCompleteDescWindow getDescWindow() {
* that never gets un-mapped or repainted. Disposing of the native peer
* avoids that.
*
- * @see AutoCompletion#setShowDescWindow(boolean)
+ * @see AutoCompletion#setDescWindowVisibility(DescWindowVisibility)
*/
void disposeDescWindow() {
if (descWindow != null) {
@@ -452,7 +569,7 @@ public void mouseReleased(MouseEvent e) {
*/
private void positionDescWindow() {
- boolean showDescWindow = descWindow!=null && ac.getShowDescWindow();
+ boolean showDescWindow = descWindow!=null && shouldShowDescWindow();
if (!showDescWindow) {
return;
}
@@ -703,7 +820,7 @@ public void setLocationRelativeTo(Rectangle r) {
Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
//Dimension screenSize = getToolkit().getScreenSize();
- boolean showDescWindow = descWindow!=null && ac.getShowDescWindow();
+ boolean showDescWindow = descWindow!=null && shouldShowDescWindow();
int totalH = getHeight();
if (showDescWindow) {
totalH = Math.max(totalH, descWindow.getHeight());
@@ -755,7 +872,9 @@ public void setVisible(boolean visible) {
installKeyBindings();
lastLine = ac.getLineOfCaret();
selectFirstItem();
- if (descWindow==null && ac.getShowDescWindow()) {
+ // ON_DEMAND starts back off each time the popup is (re)shown.
+ descWindowVisibleOnDemand = false;
+ if (descWindow==null && shouldShowDescWindow()) {
descWindow = createDescriptionWindow();
positionDescWindow();
}
@@ -771,6 +890,19 @@ public void setVisible(boolean visible) {
}
else {
uninstallKeyBindings();
+ // Explicitly hide the desc window *before* hiding ourselves.
+ // java.awt.Window#hide() cascades to any owned window that is
+ // still visible at that moment, hiding it too and flagging it
+ // to be automatically re-shown (via Window#show()'s internal
+ // "showWithParent" bookkeeping) the next time we're shown
+ // again - even if that desc window gets disposed in the
+ // meantime. Hiding it first ensures it's already invisible
+ // when our own super.setVisible(false) cascades below, so the
+ // JDK never sets that flag and can't resurrect a disposed
+ // desc window behind our back.
+ if (descWindow != null) {
+ descWindow.setVisible(false);
+ }
}
super.setVisible(visible);
@@ -797,7 +929,7 @@ public void setVisible(boolean visible) {
// because of the way child JWindows' visibility is handled - in
// some ways it's dependent on the parent, in other ways it's not.
if (descWindow!=null) {
- descWindow.setVisible(visible && ac.getShowDescWindow());
+ descWindow.setVisible(visible && shouldShowDescWindow());
}
}
diff --git a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java
index a2a7032..318741e 100644
--- a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java
+++ b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java
@@ -107,10 +107,10 @@ public class AutoCompletion {
private static LinkRedirector linkRedirector;
/**
- * Whether the description window should be displayed along with the
+ * Whether/when the description window should be displayed along with the
* completion choice window.
*/
- private boolean showDescWindow;
+ private DescWindowVisibility descWindowVisibility;
/**
* Whether auto-complete is enabled.
@@ -176,6 +176,12 @@ public class AutoCompletion {
*/
private Action oldParenAction;
+ /**
+ * The keystroke that toggles the description window's visibility in
+ * {@link DescWindowVisibility#ON_DEMAND} mode, or null.
+ */
+ private KeyStroke descWindowToggleKey;
+
/**
* Listens for events in the parent window that affect the visibility of the
* popup windows.
@@ -268,7 +274,8 @@ public AutoCompletion(CompletionProvider provider) {
setAutoCompleteEnabled(true);
setAutoCompleteSingleChoices(true);
setAutoActivationEnabled(false);
- setShowDescWindow(false);
+ setDescWindowVisibility(DescWindowVisibility.NEVER);
+ setDescWindowToggleKey(getDefaultDescWindowToggleKey());
setHideOnCompletionProviderChange(true);
setHideOnNoText(true);
setParameterDescriptionTruncateThreshold(300);
@@ -384,6 +391,18 @@ public static KeyStroke getDefaultTriggerKey() {
}
+ /**
+ * Returns the default desc window toggle keystroke ({@link DescWindowVisibility#ON_DEMAND}).
+ *
+ * @return The default keystroke.
+ * @see #setDescWindowToggleKey(KeyStroke)
+ */
+ public static KeyStroke getDefaultDescWindowToggleKey() {
+ int mask = InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK;
+ return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, mask);
+ }
+
+
/**
* Returns the handler to use when an external URL is clicked in the
* description window.
@@ -462,14 +481,24 @@ protected String getReplacementText(Completion c, Document doc, int start,
/**
- * Returns whether the "description window" should be shown alongside the
- * completion window.
+ * Returns whether/when the "description window" should be shown alongside the completion choices window.
*
- * @return Whether the description window should be shown.
- * @see #setShowDescWindow(boolean)
+ * @return Whether/when the description window should be shown.
+ * @see #setDescWindowVisibility(DescWindowVisibility)
*/
- public boolean getShowDescWindow() {
- return showDescWindow;
+ public DescWindowVisibility getDescWindowVisibility() {
+ return descWindowVisibility;
+ }
+
+
+ /**
+ * Returns the desc window toggle keystroke ({@link DescWindowVisibility#ON_DEMAND}).
+ *
+ * @return The keystroke, or null if none is installed.
+ * @see #setDescWindowToggleKey(KeyStroke)
+ */
+ public KeyStroke getDescWindowToggleKey() {
+ return descWindowToggleKey;
}
@@ -665,6 +694,9 @@ public void install(JTextComponent c) {
this.textComponent = c;
installTriggerKey(getTriggerKey());
+ if (descWindowToggleKey != null) {
+ AutoCompletePopupWindow.installDescWindowToggleKey(this, textComponent, descWindowToggleKey);
+ }
// Install the function completion key, if there is one.
// NOTE: We cannot do this if the start char is ' ' (e.g. just a space
@@ -1148,20 +1180,20 @@ protected void setPopupVisible(boolean visible) {
/**
- * Sets whether the "description window" should be shown beside the
- * completion window.
+ * Sets whether/when the "description window" should be shown beside the completion choices window.
*
- * @param show Whether to show the description window.
- * @see #getShowDescWindow()
+ * @param visibility Whether/when to show the description window.
+ * @see #getDescWindowVisibility()
*/
- public void setShowDescWindow(boolean show) {
+ public void setDescWindowVisibility(DescWindowVisibility visibility) {
+ Objects.requireNonNull(visibility, "visibility cannot be null");
hidePopupWindow(); // Needed to force it to take effect
- if (!show && popupWindow != null) {
+ if (visibility != DescWindowVisibility.ALWAYS && popupWindow != null) {
// Dispose (rather than hide) the desc window on toggle-off, to avoid a
// Linux/X11 "ghost" window bug when hiding it instead; see issue #84.
popupWindow.disposeDescWindow();
}
- showDescWindow = show;
+ descWindowVisibility = visibility;
}
@@ -1186,6 +1218,27 @@ public void setTriggerKey(KeyStroke ks) {
}
+ /**
+ * Sets the desc window toggle keystroke ({@link DescWindowVisibility#ON_DEMAND}).
+ *
+ * @param ks The keystroke, or {@code null} to remove any previously installed toggle keystroke.
+ * @see #getDescWindowToggleKey()
+ */
+ public void setDescWindowToggleKey(KeyStroke ks) {
+ if (!Objects.equals(ks, descWindowToggleKey)) {
+ if (textComponent != null) {
+ if (descWindowToggleKey != null) {
+ AutoCompletePopupWindow.uninstallDescWindowToggleKey(textComponent, descWindowToggleKey);
+ }
+ if (ks != null) {
+ AutoCompletePopupWindow.installDescWindowToggleKey(this, textComponent, ks);
+ }
+ }
+ descWindowToggleKey = ks;
+ }
+ }
+
+
/**
* Displays a "tool tip" detailing the inputs to the function just entered.
*
@@ -1238,6 +1291,9 @@ public void uninstall() {
hidePopupWindow(); // Unregisters listeners, actions, etc.
uninstallTriggerKey();
+ if (descWindowToggleKey != null) {
+ AutoCompletePopupWindow.uninstallDescWindowToggleKey(textComponent, descWindowToggleKey);
+ }
// Uninstall the function completion key.
char start = provider.getParameterListStart();
diff --git a/AutoComplete/src/main/java/org/fife/ui/autocomplete/DescWindowVisibility.java b/AutoComplete/src/main/java/org/fife/ui/autocomplete/DescWindowVisibility.java
new file mode 100644
index 0000000..575a191
--- /dev/null
+++ b/AutoComplete/src/main/java/org/fife/ui/autocomplete/DescWindowVisibility.java
@@ -0,0 +1,38 @@
+/*
+ * This library is distributed under a modified BSD license. See the included
+ * LICENSE.md file for details.
+ */
+package org.fife.ui.autocomplete;
+
+
+/**
+ * Controls when the "description" window (the popup that shows documentation
+ * for the currently selected completion choice) is displayed alongside the
+ * completion choices window.
+ *
+ * @author Robert Futrell
+ * @version 1.0
+ * @see AutoCompletion#setDescWindowVisibility(DescWindowVisibility)
+ */
+public enum DescWindowVisibility {
+
+ /**
+ * The description window is shown automatically whenever the completion
+ * choices window is showing and a description is available. This is the
+ * default (legacy) behavior.
+ */
+ ALWAYS,
+
+ /**
+ * The description window is only shown when the user explicitly requests
+ * it, via the keystroke configured by
+ * {@link AutoCompletion#setDescWindowToggleKey(javax.swing.KeyStroke)}.
+ */
+ ON_DEMAND,
+
+ /**
+ * The description window is never shown.
+ */
+ NEVER
+
+}
diff --git a/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java b/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java
index 003fffb..ad7d098 100644
--- a/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java
+++ b/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java
@@ -5,8 +5,13 @@
package org.fife.ui.autocomplete;
import java.awt.GraphicsEnvironment;
+import java.awt.event.ActionEvent;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JTextArea;
+import javax.swing.KeyStroke;
import javax.swing.text.DefaultCaret;
import javax.swing.text.JTextComponent;
@@ -55,7 +60,7 @@ void setShowDescWindow_false_disposesExistingDescWindow() {
provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary"));
AutoCompletion ac = new AutoCompletion(provider);
- ac.setShowDescWindow(true);
+ ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS);
ac.setAutoCompleteEnabled(true);
JTextArea textArea = new JTextArea();
@@ -79,7 +84,7 @@ void setShowDescWindow_false_disposesExistingDescWindow() {
Assertions.assertTrue(descWindow.isDisplayable(),
"Description window's native peer should exist while showing");
- ac.setShowDescWindow(false);
+ ac.setDescWindowVisibility(DescWindowVisibility.NEVER);
Assertions.assertNull(popupWindow.getDescWindow(),
"Description window should be disposed and discarded, not just hidden");
@@ -89,6 +94,262 @@ void setShowDescWindow_false_disposesExistingDescWindow() {
}
+ @Test
+ void getShowDescWindow_defaultsToNever() {
+ AutoCompletion ac = new AutoCompletion(new DefaultCompletionProvider());
+ Assertions.assertEquals(DescWindowVisibility.NEVER, ac.getDescWindowVisibility());
+ }
+
+
+ /**
+ * Regression test for a bug where a disposed description window could be
+ * silently resurrected by the JDK. {@code java.awt.Window#hide()} has an
+ * internal cascade: any owned window that's still visible when its owner
+ * is hidden gets hidden too and flagged (via a package-private
+ * {@code showWithParent} field) to be automatically re-shown - by calling
+ * {@code show()} directly, bypassing all of our code - the next time the
+ * owner is shown again, even if that owned window was disposed of in the
+ * meantime. If the description window is still visible at the moment the
+ * choices popup is hidden (e.g. the text component loses focus), that
+ * flag gets set; disposing the description window afterward (by
+ * switching to {@code NEVER}) doesn't clear it, so the next time the
+ * choices popup reopens, the JDK recreates the disposed window's native
+ * peer and shows it again with its last (now stale) content - even
+ * though {@code AutoCompletePopupWindow}'s own {@code descWindow} field
+ * is {@code null}. The fix hides the description window *before* hiding
+ * the choices popup, so it's already invisible by the time the JDK's
+ * cascade runs and never sets that flag.
+ */
+ @Test
+ void showDescWindow_disposedWhileHidden_doesNotReappearOnNextShow() {
+
+ DefaultCompletionProvider provider = new DefaultCompletionProvider();
+ provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary"));
+ provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary"));
+
+ AutoCompletion ac = new AutoCompletion(provider);
+ ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS);
+ ac.setAutoCompleteEnabled(true);
+
+ JTextArea textArea = new JTextArea();
+ ac.install(textArea);
+
+ frame = new JFrame();
+ frame.add(textArea);
+ frame.pack();
+ frame.setVisible(true);
+
+ textArea.setText("foo");
+ textArea.setCaretPosition(textArea.getText().length());
+ ac.doCompletion();
+
+ AutoCompletePopupWindow popupWindow = ac.getPopupWindow();
+ AutoCompleteDescWindow descWindow = popupWindow.getDescWindow();
+ Assertions.assertNotNull(descWindow);
+ Assertions.assertTrue(descWindow.isVisible());
+
+ // Hide the choices popup while the desc window is still visible, as
+ // happens e.g. when the text component loses focus. This is what
+ // causes the JDK to internally flag the desc window to be
+ // auto-restored the next time the popup is shown again.
+ popupWindow.setVisible(false);
+
+ // Now dispose the desc window entirely, as setDescWindowVisibility()
+ // does when switching away from ALWAYS.
+ ac.setDescWindowVisibility(DescWindowVisibility.NEVER);
+ Assertions.assertNull(popupWindow.getDescWindow());
+ Assertions.assertFalse(descWindow.isDisplayable());
+
+ // Re-trigger completion; the choices popup reopens, but the disposed
+ // desc window must not be silently resurrected by the JDK.
+ ac.doCompletion();
+
+ Assertions.assertNull(popupWindow.getDescWindow(),
+ "No new description window should have been created (NEVER)");
+ Assertions.assertFalse(descWindow.isVisible(),
+ "The disposed description window must not be resurrected by the JDK");
+ Assertions.assertFalse(descWindow.isDisplayable(),
+ "The disposed description window's peer must not be silently recreated");
+
+ }
+
+
+ @Test
+ void showDescWindow_never_descWindowNeverCreated() {
+
+ DefaultCompletionProvider provider = new DefaultCompletionProvider();
+ provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary"));
+ provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary"));
+
+ AutoCompletion ac = new AutoCompletion(provider);
+ ac.setDescWindowVisibility(DescWindowVisibility.NEVER);
+ ac.setAutoCompleteEnabled(true);
+
+ JTextArea textArea = new JTextArea();
+ ac.install(textArea);
+
+ frame = new JFrame();
+ frame.add(textArea);
+ frame.pack();
+ frame.setVisible(true);
+
+ textArea.setText("foo");
+ textArea.setCaretPosition(textArea.getText().length());
+ ac.doCompletion();
+
+ AutoCompletePopupWindow popupWindow = ac.getPopupWindow();
+ Assertions.assertNotNull(popupWindow, "Choices popup window should still be shown");
+ Assertions.assertNull(popupWindow.getDescWindow(),
+ "Description window should never be created when visibility is NEVER");
+
+ }
+
+
+ @Test
+ void showDescWindow_onDemand_toggleKeyShowsAndHidesDescWindow() {
+
+ DefaultCompletionProvider provider = new DefaultCompletionProvider();
+ provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary"));
+ provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary"));
+
+ AutoCompletion ac = new AutoCompletion(provider);
+ ac.setDescWindowVisibility(DescWindowVisibility.ON_DEMAND);
+ ac.setAutoCompleteEnabled(true);
+
+ JTextArea textArea = new JTextArea();
+ ac.install(textArea);
+
+ frame = new JFrame();
+ frame.add(textArea);
+ frame.pack();
+ frame.setVisible(true);
+
+ textArea.setText("foo");
+ textArea.setCaretPosition(textArea.getText().length());
+ ac.doCompletion();
+
+ AutoCompletePopupWindow popupWindow = ac.getPopupWindow();
+ Assertions.assertNotNull(popupWindow);
+ Assertions.assertNull(popupWindow.getDescWindow(),
+ "Description window should not be created until toggled on, in ON_DEMAND mode");
+
+ fireKeyAction(textArea, ac.getDescWindowToggleKey());
+ AutoCompleteDescWindow descWindow = popupWindow.getDescWindow();
+ Assertions.assertNotNull(descWindow,
+ "Description window should be created the first time it's toggled on");
+ Assertions.assertTrue(descWindow.isVisible(),
+ "Description window should be visible after toggling on");
+
+ fireKeyAction(textArea, ac.getDescWindowToggleKey());
+ Assertions.assertFalse(descWindow.isVisible(),
+ "Description window should be hidden after toggling back off");
+
+ }
+
+
+ @Test
+ void showDescWindow_onDemand_toggleKeyIgnoredWhenNotOnDemand() {
+
+ DefaultCompletionProvider provider = new DefaultCompletionProvider();
+ provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary"));
+ provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary"));
+
+ AutoCompletion ac = new AutoCompletion(provider);
+ ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS);
+ ac.setAutoCompleteEnabled(true);
+
+ JTextArea textArea = new JTextArea();
+ ac.install(textArea);
+
+ frame = new JFrame();
+ frame.add(textArea);
+ frame.pack();
+ frame.setVisible(true);
+
+ textArea.setText("foo");
+ textArea.setCaretPosition(textArea.getText().length());
+ ac.doCompletion();
+
+ AutoCompletePopupWindow popupWindow = ac.getPopupWindow();
+ AutoCompleteDescWindow descWindow = popupWindow.getDescWindow();
+ Assertions.assertNotNull(descWindow, "Description window should already be shown (ALWAYS)");
+
+ fireKeyAction(textArea, ac.getDescWindowToggleKey());
+ Assertions.assertTrue(descWindow.isVisible(),
+ "Toggle key should have no effect when visibility isn't ON_DEMAND");
+
+ }
+
+
+ @Test
+ void setDescWindowToggleKey_customKeystrokeIsInstalledAndUsable() {
+
+ DefaultCompletionProvider provider = new DefaultCompletionProvider();
+ provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary"));
+ provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary"));
+
+ AutoCompletion ac = new AutoCompletion(provider);
+ ac.setDescWindowVisibility(DescWindowVisibility.ON_DEMAND);
+ ac.setAutoCompleteEnabled(true);
+
+ KeyStroke customKey = KeyStroke.getKeyStroke("ctrl alt D");
+ ac.setDescWindowToggleKey(customKey);
+
+ JTextArea textArea = new JTextArea();
+ ac.install(textArea);
+
+ frame = new JFrame();
+ frame.add(textArea);
+ frame.pack();
+ frame.setVisible(true);
+
+ textArea.setText("foo");
+ textArea.setCaretPosition(textArea.getText().length());
+ ac.doCompletion();
+
+ AutoCompletePopupWindow popupWindow = ac.getPopupWindow();
+ Assertions.assertNull(popupWindow.getDescWindow());
+
+ fireKeyAction(textArea, customKey);
+ Assertions.assertNotNull(popupWindow.getDescWindow(),
+ "Description window should toggle on via the custom keystroke");
+
+ }
+
+
+ @Test
+ void setDescWindowToggleKey_null_removesInstalledKeystroke() {
+
+ AutoCompletion ac = new AutoCompletion(new DefaultCompletionProvider());
+ JTextArea textArea = new JTextArea();
+ ac.install(textArea);
+
+ KeyStroke defaultKey = AutoCompletion.getDefaultDescWindowToggleKey();
+ InputMap im = textArea.getInputMap();
+ Assertions.assertNotNull(im.get(defaultKey),
+ "Default toggle key should be installed on install()");
+
+ ac.setDescWindowToggleKey(null);
+ Assertions.assertNull(ac.getDescWindowToggleKey());
+
+ }
+
+
+ /**
+ * Fires the action bound to {@code ks} in {@code textArea}'s input/action maps,
+ * simulating the user pressing that keystroke.
+ */
+ private static void fireKeyAction(JTextArea textArea, KeyStroke ks) {
+ InputMap im = textArea.getInputMap();
+ ActionMap am = textArea.getActionMap();
+ Object key = im.get(ks);
+ Assertions.assertNotNull(key, "No action bound to keystroke " + ks);
+ Action action = am.get(key);
+ Assertions.assertNotNull(action, "No action found for key " + key);
+ action.actionPerformed(new ActionEvent(textArea, ActionEvent.ACTION_PERFORMED, ""));
+ }
+
+
/**
* Regression test for Issue #77 -
* {@code isAutoActivateOkay()} used to look at {@code JTextComponent#getCaretPosition()}
diff --git a/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java b/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java
index 9d7889b..dc691e4 100644
--- a/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java
+++ b/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java
@@ -90,7 +90,7 @@ class DemoRootPane extends JRootPane {
// Install auto-completion onto our text area.
ac = new AutoCompletion(provider);
ac.setListCellRenderer(new CCellRenderer());
- ac.setShowDescWindow(true);
+ ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS);
ac.setParameterAssistanceEnabled(true);
ac.setAutoCompleteEnabled(true);
@@ -449,7 +449,7 @@ private class ShowDescWindowAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
boolean show = showDescWindowItem.isSelected();
- ac.setShowDescWindow(show);
+ ac.setDescWindowVisibility(show ? DescWindowVisibility.ALWAYS : DescWindowVisibility.NEVER);
}
}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index b5fbb99..503627a 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -64,7 +64,7 @@
-
+
diff --git a/gradle.properties b/gradle.properties
index 4b1635a..4f708d1 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
# Note that Maven- and signing-related properties are in /gradle.properties
javaLanguageVersion=11
-version=3.3.4-SNAPSHOT
+version=4.0.0-SNAPSHOT
# Ugh, see https://github.com/gradle/gradle/issues/11308
systemProp.org.gradle.internal.publish.checksums.insecure=true