diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java index 3886f6a4257..c7590d06739 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java @@ -102,6 +102,9 @@ public abstract class QuickAccessContents { */ private static final String QUICK_ACCESS_COMMAND_ID = "org.eclipse.ui.window.quickAccess"; //$NON-NLS-1$ + /** Trailing-edge debounce window collapsing a burst of shell resizes. */ + private static final int RESIZE_DEBOUNCE_MS = 100; + protected Text filterText; private final QuickAccessProvider[] providers; @@ -123,7 +126,7 @@ public abstract class QuickAccessContents { private Color grayColor; private TextLayout textLayout; private boolean showAllMatches = false; - protected boolean resized = false; + private int lastComputedItemCount = -1; private TriggerSequence keySequence; private Job computeProposalsJob; @@ -164,6 +167,7 @@ public void updateProposals(String filter) { String computingMessage = NLS.bind(QuickAccessMessages.QuickaAcessContents_computeMatchingEntries, filter); int maxNumberOfItemsInTable = computeNumberOfItems(); + lastComputedItemCount = maxNumberOfItemsInTable; AtomicReference[]> entries = new AtomicReference<>(); final Job currentComputeEntriesJob = Job.create(computingMessage, theMonitor -> { entries.set( @@ -766,24 +770,36 @@ public Table createTable(Composite composite) { tableComposite = new Composite(composite, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite); table = new Table(tableComposite, SWT.SINGLE | SWT.FULL_SELECTION); + final Runnable resizeUpdate = () -> { + if (showAllMatches || table == null || table.isDisposed() || filterText == null + || filterText.isDisposed()) { + return; + } + // Skip when the layout settled back to the row count we already computed for, + // so an oscillating burst does not cancel the in-flight compute job. + if (computeNumberOfItems() == lastComputedItemCount) { + return; + } + if (Policy.DEBUG_QUICK_ACCESS) { + trace("Resize listener triggering proposals update"); //$NON-NLS-1$ + } + updateProposals(filterText.getText().toLowerCase()); + }; table.getShell().addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { - if (!showAllMatches) { - if (!resized) { - resized = true; - e.display.timerExec(100, () -> { - if (table != null && !table.isDisposed() && filterText != null - && !filterText.isDisposed()) { - if (Policy.DEBUG_QUICK_ACCESS) { - trace("Resize listener triggering proposals update"); //$NON-NLS-1$ - } - updateProposals(filterText.getText().toLowerCase()); - } - resized = false; - }); - } + if (showAllMatches || table == null || table.isDisposed() || filterText == null + || filterText.isDisposed()) { + return; + } + // A resize only matters when the number of visible rows changes. Coalesce a + // burst of resizes (e.g. scrollbar oscillation during initial layout) into a + // single trailing update, and skip it when the row count is unchanged. + if (computeNumberOfItems() == lastComputedItemCount) { + return; } + e.display.timerExec(-1, resizeUpdate); + e.display.timerExec(RESIZE_DEBOUNCE_MS, resizeUpdate); } }); diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/quickaccess/QuickAccessDialogTest.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/quickaccess/QuickAccessDialogTest.java index c9dd5d7b920..d51e07ab510 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/quickaccess/QuickAccessDialogTest.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/quickaccess/QuickAccessDialogTest.java @@ -321,7 +321,7 @@ public void testPreviousChoicesAvailableForExtension() { * wait for the dialog initialization, to avoid race conditions later on in the test, see: * https://github.com/eclipse-platform/eclipse.platform.ui/issues/4009 */ - assertTrue(DisplayHelper.waitForCondition(text.getDisplay(), TIMEOUT * 1000, + assertTrue(DisplayHelper.waitForCondition(text.getDisplay(), TIMEOUT, () -> dialog.infoText != null), "Unexpected dialog info: " + dialog.infoText); text.setText(TestQuickAccessComputer.TEST_QUICK_ACCESS_PROPOSAL_LABEL);