📄 contentproposaladapter.java
字号:
/******************************************************************************* * Copyright (c) 2005, 2006 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.jface.fieldassist;import java.util.ArrayList;import org.eclipse.core.runtime.ListenerList;import org.eclipse.jface.bindings.keys.KeyStroke;import org.eclipse.jface.dialogs.PopupDialog;import org.eclipse.jface.util.Assert;import org.eclipse.jface.viewers.ILabelProvider;import org.eclipse.swt.SWT;import org.eclipse.swt.events.DisposeEvent;import org.eclipse.swt.events.DisposeListener;import org.eclipse.swt.events.FocusAdapter;import org.eclipse.swt.events.FocusEvent;import org.eclipse.swt.events.SelectionEvent;import org.eclipse.swt.events.SelectionListener;import org.eclipse.swt.graphics.Image;import org.eclipse.swt.graphics.Point;import org.eclipse.swt.graphics.Rectangle;import org.eclipse.swt.layout.GridData;import org.eclipse.swt.widgets.Composite;import org.eclipse.swt.widgets.Control;import org.eclipse.swt.widgets.Event;import org.eclipse.swt.widgets.Listener;import org.eclipse.swt.widgets.ScrollBar;import org.eclipse.swt.widgets.Shell;import org.eclipse.swt.widgets.Table;import org.eclipse.swt.widgets.TableItem;import org.eclipse.swt.widgets.Text;/** * ContentProposalAdapter can be used to attach content proposal behavior to a * control. This behavior includes obtaining proposals, opening a popup dialog, * managing the content of the control relative to the selections in the popup, * and optionally opening up a secondary popup to further describe proposals. * <p> * A number of configurable options are provided to determine how the control * content is altered when a proposal is chosen, how the content proposal popup * is activated, and whether any filtering should be done on the proposals as * the user types characters. * <p> * This class is not intended to be subclassed. * * @since 3.2 */public class ContentProposalAdapter { /* * The lightweight popup used to show content proposals for a text field. If * additional information exists for a proposal, then selecting that * proposal will result in the information being displayed in a secondary * popup. */ class ContentProposalPopup extends PopupDialog { /* * The listener we install on the popup and related controls to * determine when to close the popup. Some events (move, resize, close, * deactivate) trigger closure as soon as they are received, simply * because one of the registered listeners received them. Other events * depend on additional circumstances. */ private final class PopupCloserListener implements Listener { private boolean scrollbarClicked = false; public void handleEvent(final Event e) { // If focus is leaving an important widget or the field's // shell is deactivating if (e.type == SWT.FocusOut) { scrollbarClicked = false; /* * Ignore this event if it's only happening because focus is * moving between the popup shells, their controls, or a * scrollbar. Do this in an async since the focus is not * actually switched when this event is received. */ e.display.asyncExec(new Runnable() { public void run() { if (isValid()) { if (scrollbarClicked || hasFocus() || (infoPopup != null && infoPopup .hasFocus())) { return; } // Workaround a problem on X and Mac, whereby at // this point, the focus control is not known. // This can happen, for example, when resizing // the popup shell on the Mac. // Check the active shell. Shell activeShell = e.display.getActiveShell(); if (activeShell == getShell() || (infoPopup != null && infoPopup .getShell() == activeShell)) { return; } /* * System.out.println(e); * System.out.println(e.display.getFocusControl()); * System.out.println(e.display.getActiveShell()); */ close(); } } }); return; } // Scroll bar has been clicked. Remember this for focus event // processing. if (e.type == SWT.Selection) { scrollbarClicked = true; return; } // For all other events, merely getting them dictates closure. close(); } // Install the listeners for events that need to be monitored for // popup closure. void installListeners() { // Listeners on this popup's table and scroll bar proposalTable.addListener(SWT.FocusOut, this); ScrollBar scrollbar = proposalTable.getVerticalBar(); if (scrollbar != null) { scrollbar.addListener(SWT.Selection, this); } // Listeners on this popup's shell getShell().addListener(SWT.Deactivate, this); getShell().addListener(SWT.Close, this); // Listeners on the target control control.addListener(SWT.MouseDoubleClick, this); control.addListener(SWT.MouseDown, this); control.addListener(SWT.Dispose, this); control.addListener(SWT.FocusOut, this); // Listeners on the target control's shell Shell controlShell = control.getShell(); controlShell.addListener(SWT.Move, this); controlShell.addListener(SWT.Resize, this); } // Remove installed listeners void removeListeners() { if (isValid()) { proposalTable.removeListener(SWT.FocusOut, this); ScrollBar scrollbar = proposalTable.getVerticalBar(); if (scrollbar != null) { scrollbar.removeListener(SWT.Selection, this); } getShell().removeListener(SWT.Deactivate, this); getShell().removeListener(SWT.Close, this); } if (control != null && !control.isDisposed()) { control.removeListener(SWT.MouseDoubleClick, this); control.removeListener(SWT.MouseDown, this); control.removeListener(SWT.Dispose, this); control.removeListener(SWT.FocusOut, this); Shell controlShell = control.getShell(); controlShell.removeListener(SWT.Move, this); controlShell.removeListener(SWT.Resize, this); } } } /* * The listener we will install on the target control. */ private final class TargetControlListener implements Listener { // Key events from the control public void handleEvent(Event e) { if (!isValid()) { return; } char key = e.character; // Traverse events are handled depending on whether the // event has a character. if (e.type == SWT.Traverse) { // If the traverse event contains a legitimate character, // then we must set doit false so that the widget will // receive the key event. We return immediately so that // the character is handled only in the key event. // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132101 if (key != 0) { e.doit = false; return; } // Traversal does not contain a character. Set doit true // to indicate TRAVERSE_NONE will occur and that no key // event will be triggered. We will check for navigation // keys below. e.detail = SWT.TRAVERSE_NONE; e.doit = true; } else { // Default is to only propagate when configured that way. // Some keys will always set doit to false anyway. e.doit = propagateKeys; } // No character. Check for navigation keys. if (key == 0) { int newSelection = proposalTable.getSelectionIndex(); int visibleRows = (proposalTable.getSize().y / proposalTable .getItemHeight()) - 1; switch (e.keyCode) { case SWT.ARROW_UP: newSelection -= 1; if (newSelection < 0) { newSelection = proposalTable.getItemCount() - 1; } // Not typical - usually we get this as a Traverse and // therefore it never propagates. Added for consistency. if (e.type == SWT.KeyDown) { // don't propagate to control e.doit = false; } break; case SWT.ARROW_DOWN: newSelection += 1; if (newSelection > proposalTable.getItemCount() - 1) { newSelection = 0; } // Not typical - usually we get this as a Traverse and // therefore it never propagates. Added for consistency. if (e.type == SWT.KeyDown) { // don't propagate to control e.doit = false; } break; case SWT.PAGE_DOWN: newSelection += visibleRows; if (newSelection >= proposalTable.getItemCount()) { newSelection = proposalTable.getItemCount() - 1; } if (e.type == SWT.KeyDown) { // don't propagate to control e.doit = false; } break; case SWT.PAGE_UP: newSelection -= visibleRows; if (newSelection < 0) { newSelection = 0; } if (e.type == SWT.KeyDown) { // don't propagate to control e.doit = false; } break; case SWT.HOME: newSelection = 0; if (e.type == SWT.KeyDown) { // don't propagate to control e.doit = false; } break; case SWT.END: newSelection = proposalTable.getItemCount() - 1; if (e.type == SWT.KeyDown) { // don't propagate to control e.doit = false; } break; // If received as a Traverse, these should propagate // to the control as keydown. If received as a keydown, // proposals should be recomputed since the cursor // position has changed. case SWT.ARROW_LEFT: case SWT.ARROW_RIGHT: if (e.type == SWT.Traverse) { e.doit = false; } else { e.doit = true; String contents = getControlContentAdapter().getControlContents( getControl()); // If there are no contents, changes in cursor position // have no effect. Note also that we do not affect the filter // text on ARROW_LEFT as we would with BS. if (contents.length() > 0) { asyncRecomputeProposals(filterText); } } break; // Any unknown keycodes will cause the popup to close. // Modifier keys are explicitly checked and ignored because // they are not complete yet (no character). default: if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4) { close(); } return; } // If any of these navigation events caused a new selection, // then handle that now and return. if (newSelection >= 0) { selectProposal(newSelection); } return; } // key != 0 // Check for special keys involved in cancelling, accepting, or // filtering the proposals. switch (key) { case SWT.ESC: e.doit = false; close(); break; case SWT.LF: case SWT.CR: e.doit = false; Object p = getSelectedProposal(); if (p != null) { acceptCurrentProposal(); } close(); break; case SWT.TAB: e.doit = false; getShell().setFocus(); return; case SWT.BS: // Backspace should back out of any stored filter text if (filterStyle != FILTER_NONE) { // We have no filter to back out of, so do nothing if (filterText.length() == 0) { return; } // There is filter to back out of filterText = filterText.substring(0, filterText .length() - 1); asyncRecomputeProposals(filterText); return; } // There is no filtering provided by us, but some // clients provide their own filtering based on content. // Recompute the proposals if the cursor position // will change (is not at 0). int pos = getControlContentAdapter().getCursorPosition(getControl()); // We rely on the fact that the contents and pos do not yet // reflect the result of the BS. If the contents were already empty, then BS should not cause // a recompute. if (pos > 0) { asyncRecomputeProposals(filterText); } break; default: // If the key is a defined unicode character, and not one of // the special cases processed above, update the filter text // and filter the proposals. if (Character.isDefined(key)) { if (filterStyle == FILTER_CUMULATIVE) { filterText = filterText + String.valueOf(key); } else if (filterStyle == FILTER_CHARACTER) { filterText = String.valueOf(key); } // Recompute proposals after processing this event. asyncRecomputeProposals(filterText); } break; } } } /* * Internal class used to implement the secondary popup. */ private class InfoPopupDialog extends PopupDialog { /* * The text control that displays the text. */ private Text text; /* * The String shown in the popup. */ private String contents = EMPTY; /* * Construct an info-popup with the specified parent. */ InfoPopupDialog(Shell parent) { super(parent, PopupDialog.HOVER_SHELLSTYLE, false, false, false, false, null, null); } /* * Create a text control for showing the info about a proposal. */ protected Control createDialogArea(Composite parent) { text = new Text(parent, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.NO_FOCUS); // Use the compact margins employed by PopupDialog. GridData gd = new GridData(GridData.BEGINNING | GridData.FILL_BOTH); gd.horizontalIndent = PopupDialog.POPUP_HORIZONTALSPACING; gd.verticalIndent = PopupDialog.POPUP_VERTICALSPACING; text.setLayoutData(gd); text.setText(contents); // since SWT.NO_FOCUS is only a hint... text.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent event) { ContentProposalPopup.this.close(); } }); return text; } /* * Adjust the bounds so that we appear adjacent to our parent shell */ protected void adjustBounds() { Rectangle parentBounds = getParentShell().getBounds(); Rectangle proposedBounds; // Try placing the info popup to the right Rectangle rightProposedBounds = new Rectangle(parentBounds.x + parentBounds.width + PopupDialog.POPUP_HORIZONTALSPACING, parentBounds.y + PopupDialog.POPUP_VERTICALSPACING, parentBounds.width, parentBounds.height);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -