diff options
author | Benjamin Leipold | 2017-08-31 16:02:19 +0000 |
---|---|---|
committer | Mickael Istria | 2017-11-21 20:16:53 +0000 |
commit | 581193006c8ace68b5db141fca66ac661a530576 (patch) | |
tree | 6f943cc380df040e82c413234663e9fd24de5bc0 | |
parent | df133d0b691b0f42d3635adc7cdfcc93fa299ac3 (diff) | |
download | eclipse.platform.ui-581193006c8ace68b5db141fca66ac661a530576.tar.gz eclipse.platform.ui-581193006c8ace68b5db141fca66ac661a530576.tar.xz eclipse.platform.ui-581193006c8ace68b5db141fca66ac661a530576.zip |
Bug 520372 - ContentProposalAdapter with autoActivationDelay pops
up although control has already lost focus
Added lost changes and moved tests to separate test case.
Change-Id: I749a6ef63c610d274028369c47cd697112ffa540
Signed-off-by: Benjamin Leipold <benjamin.leipold@web.de>
2 files changed, 255 insertions, 0 deletions
diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ContentProposalAdapter.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ContentProposalAdapter.java index 513d690e3cf..7d9b80a2384 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ContentProposalAdapter.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ContentProposalAdapter.java @@ -1698,6 +1698,18 @@ public class ContentProposalAdapter { // We were only listening to traverse events for the popup if (e.type == SWT.Traverse) { + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=520372 + // The popup is null so record tab as a means to interrupt + // any autoactivation that is pending due to autoactivation + // delay. + if (popup == null) { + switch (e.detail) { + case SWT.TRAVERSE_TAB_NEXT: + case SWT.TRAVERSE_TAB_PREVIOUS: + receivedKeyDown = true; + break; + } + } return; } @@ -1748,6 +1760,20 @@ public class ContentProposalAdapter { if (triggerKeyStroke == null) { watchModify = true; } + + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=520372 + // mimic close cases of popup in TargetControlListener + if (popup == null) { + switch (e.character) { + case SWT.CR: + case SWT.LF: + case SWT.ESC: + // Interrupt any autoactivation that is pending due to + // autoactivation delay. + receivedKeyDown = true; + break; + } + } } } else { // A non-character key has been pressed. Interrupt any diff --git a/tests/org.eclipse.ui.tests/Eclipse JFace Tests/org/eclipse/jface/tests/fieldassist/ContentProposalAdapterTest.java b/tests/org.eclipse.ui.tests/Eclipse JFace Tests/org/eclipse/jface/tests/fieldassist/ContentProposalAdapterTest.java new file mode 100644 index 00000000000..4d0bb5eb564 --- /dev/null +++ b/tests/org.eclipse.ui.tests/Eclipse JFace Tests/org/eclipse/jface/tests/fieldassist/ContentProposalAdapterTest.java @@ -0,0 +1,229 @@ +/******************************************************************************* +* Copyright (c) 2017 Benjamin Leipold 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: +* Benjamin Leipold - initial API and implementation +*******************************************************************************/ +package org.eclipse.jface.tests.fieldassist; + +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.fieldassist.IContentProposalProvider; +import org.eclipse.jface.fieldassist.SimpleContentProposalProvider; +import org.eclipse.jface.fieldassist.TextContentAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import junit.framework.TestCase; + +public class ContentProposalAdapterTest extends TestCase { + + /** + * A shell that hosts the decorated text control + */ + private Shell controlShell; + + /** + * Text control to be decorated by {@code contentProposalAdapter} + */ + private Text text; + + /** + * {@code ContentProposalAdapter} to test + */ + private ContentProposalAdapter contentProposalAdapter; + + /** + * Display of this test case. + */ + private Display display; + + /** + * {@code true} if {@code display} has to be disposed in {@link #tearDown()} + */ + private boolean disposeDisplay; + + /** + * The original number of shells at the beginning of the test. + */ + private int originalShellCount; + + /** + * bug 520372: ContentProposalAdapter with autoActivationDelay pops up although + * control has already lost focus + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=520372 + * + * Tests whether no proposal popup was opened if TAB was pressed within + * activation delay. + */ + public void testBug520372AutoActivationDelayTab() throws Exception { + sendKeyDownToControl('o'); + sendKeyDownToControl(SWT.TAB); + ensurePopupIsUp(); + + assertOneShellUp(); + } + + /** + * bug 520372: ContentProposalAdapter with autoActivationDelay pops up although + * control has already lost focus + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=520372 + * + * Tests whether no proposal popup was opened if CR was pressed within + * activation delay. + */ + public void testBug520372AutoActivationDelayCR() throws Exception { + sendKeyDownToControl('o'); + sendKeyDownToControl(SWT.CR); + ensurePopupIsUp(); + + assertOneShellUp(); + } + + /** + * bug 520372: ContentProposalAdapter with autoActivationDelay pops up although + * control has already lost focus + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=520372 + * + * Tests whether no proposal popup was opened if ESC was pressed within + * activation delay. + */ + public void testBug520372AutoActivationDelayESC() throws Exception { + sendKeyDownToControl('o'); + sendKeyDownToControl(SWT.ESC); + ensurePopupIsUp(); + + assertOneShellUp(); + } + + // most of the following code is copied from AbstractFieldAssistTestCase + + @Override + final protected void setUp() throws Exception { + super.setUp(); + + Display display = getDisplay(); + originalShellCount = display.getShells().length; + controlShell = new Shell(display); + text = new Text(controlShell, SWT.SINGLE); + controlShell.open(); + spinEventLoop(); + contentProposalAdapter = createContentProposalAdapter(this.text); + assertNotNull(contentProposalAdapter); + } + + @Override + final protected void tearDown() throws Exception { + if (controlShell != null) { + spinEventLoop(); + controlShell.close(); + } + if (display != null) { + if (disposeDisplay) { + display.dispose(); + } + this.display = null; + } + + super.tearDown(); + } + + private Display getDisplay() { + if (display == null) { + Display newDisplay = Display.getCurrent(); + if (newDisplay == null) { + newDisplay = new Display(); + disposeDisplay = true; + } + display = newDisplay; + } + return display; + } + + /** + * Gives focus to the field assist control. + */ + private void sendFocusInToControl() { + text.setFocus(); + spinEventLoop(); + } + + /** + * Sends an SWT KeyDown event for the specified character to the field assist + * control. + * + * @param character + * the character that has been pressed + */ + private void sendKeyDownToControl(char character) { + // fake a KeyDown event + sendFocusInToControl(); + Event event = new Event(); + event.type = SWT.KeyDown; + event.character = character; + assertTrue("unable to post event to display queue for test case", text.getDisplay().post(event)); + spinEventLoop(); + } + + private void spinEventLoop() { + // spin the event loop again because we have some asyncExec calls in the + // ContentProposalAdapter class + + Display disp = getDisplay(); + while (disp.readAndDispatch()) { + } + } + + private ContentProposalAdapter createContentProposalAdapter(Control control) { + ContentProposalAdapter contentProposalAdapter = new ContentProposalAdapter(control, new TextContentAdapter(), + createContentProposalProvider(), null, null); + contentProposalAdapter.setAutoActivationDelay(2000); + return contentProposalAdapter; + } + + private IContentProposalProvider createContentProposalProvider() { + SimpleContentProposalProvider proposalProvider = new SimpleContentProposalProvider(getProposals()); + return proposalProvider; + } + + private String[] getProposals() { + return new String[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; + } + + private void ensurePopupIsUp() { + // if our autoactivation delay is zero, we use an asyncExec to get the + // popup up, hence, we need to spin the event loop + if (contentProposalAdapter.getAutoActivationDelay() == 0) { + spinEventLoop(); + } else { + long time = System.currentTimeMillis(); + long target = time + contentProposalAdapter.getAutoActivationDelay(); + while (target > time) { + spinEventLoop(); // remain responsive + time = System.currentTimeMillis(); + } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + // nothing to do + } + spinEventLoop(); + } + } + + /** + * Checks that there is only one shell up, the original field assist window. + */ + private void assertOneShellUp() { + spinEventLoop(); + assertEquals("There should only be one shell up, the dialog", originalShellCount + 1, + text.getDisplay().getShells().length); + } +} |