Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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();
}
Expand All @@ -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);
Expand All @@ -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());
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 <code>null</code>.
*/
private KeyStroke descWindowToggleKey;

/**
* Listens for events in the parent window that affect the visibility of the
* popup windows.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 <code>null</code> if none is installed.
* @see #setDescWindowToggleKey(KeyStroke)
*/
public KeyStroke getDescWindowToggleKey() {
return descWindowToggleKey;
}


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}


Expand All @@ -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.
*
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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

}
Loading
Loading