From 868406589fdf09a6795e804864b9557129c7f736 Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Tue, 14 Jan 2025 21:33:17 +0100 Subject: [PATCH] Extract shared code of FilteredTrees to abstract base class This moves the duplicate code of the E3.x and E4.x based FilteredTree class and moves it into an AbstractFilteredViewerComposite class located in the JFace component. The base class is currently only implementing a tree-based filter, but is structured in such a way that it can easily be extended to also support e.g. table-based filters. Due to being unused, the following fields and methods have been marked as deprecated: - filterToolBar - clearButtonControl - updateToolbar(boolean) Contributes to https://github.com/eclipse-platform/eclipse.platform.ui/pull/2567 --- .../META-INF/MANIFEST.MF | 2 +- .../ui/dialogs/filteredtree/FilteredTree.java | 276 ++------------- .../AbstractFilteredViewerComposite.java | 325 ++++++++++++++++++ .../META-INF/MANIFEST.MF | 2 +- .../org/eclipse/ui/dialogs/FilteredTree.java | 319 ++--------------- 5 files changed, 383 insertions(+), 541 deletions(-) create mode 100644 bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractFilteredViewerComposite.java diff --git a/bundles/org.eclipse.e4.ui.dialogs/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.dialogs/META-INF/MANIFEST.MF index 3be9ae16f8c..2a20ced09c4 100644 --- a/bundles/org.eclipse.e4.ui.dialogs/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.dialogs/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.e4.ui.dialogs -Bundle-Version: 1.5.0.qualifier +Bundle-Version: 1.6.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/FilteredTree.java b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/FilteredTree.java index 383024c9c44..dab531baaaf 100644 --- a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/FilteredTree.java +++ b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/FilteredTree.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2020 vogella GmbH and others. + * Copyright (c) 2014, 2025 vogella GmbH and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -19,6 +19,7 @@ import org.eclipse.core.runtime.jobs.Job; import org.eclipse.e4.ui.dialogs.textbundles.E4DialogMessages; import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.AbstractFilteredViewerComposite; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.TreeViewer; @@ -26,12 +27,8 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.accessibility.AccessibleAdapter; import org.eclipse.swt.accessibility.AccessibleEvent; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; @@ -49,14 +46,7 @@ * * @since 1.2 */ -public class FilteredTree extends Composite { - - /** - * The filter text widget to be used by this tree. This value may be - * null if there is no filter widget, or if the controls have - * not yet been created. - */ - private Text filterText; +public class FilteredTree extends AbstractFilteredViewerComposite { /** * The viewer for the filtered tree. This value should never be @@ -64,35 +54,11 @@ public class FilteredTree extends Composite { */ private TreeViewer treeViewer; - /** - * The Composite on which the filter controls are created. This is used to - * set the background color of the filter controls to match the surrounding - * controls. - */ - private Composite filterComposite; - - /** - * The pattern filter for the tree. This value must not be null - * . - */ - private PatternFilter patternFilter; - - /** - * The text to initially show in the filter text control. - */ - private String initialText = ""; //$NON-NLS-1$ - /** * The job used to refresh the tree. */ private Job refreshJob; - /** - * Whether or not to show the filter controls (text and clear button). The - * default is to show these controls. - */ - private boolean showFilterControls; - private Composite treeComposite; /** @@ -102,12 +68,6 @@ public class FilteredTree extends Composite { */ private static final long SOFT_MAX_EXPAND_TIME = 200; - /** - * Time delay after which the search is triggered, acting as a debounce - * mechanism. - */ - private final long refreshJobDelayInMillis; - /** * Default time for refresh job delay in ms */ @@ -124,8 +84,8 @@ public class FilteredTree extends Composite { * @since 1.5 */ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, long refreshDelayTime) { - super(parent, SWT.NONE); - this.refreshJobDelayInMillis = refreshDelayTime; + super(parent, SWT.NONE, refreshDelayTime); + this.parent = getParent(); init(treeStyle, filter); } @@ -148,52 +108,20 @@ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter) { * @see #init(int, PatternFilter) */ protected FilteredTree(Composite parent) { - super(parent, SWT.NONE); - this.refreshJobDelayInMillis = DEFAULT_REFRESH_TIME; + super(parent, SWT.NONE, DEFAULT_REFRESH_TIME); } - /** - * Create the filtered tree. - * - * @param treeStyle - * the style bits for the Tree - * @param filter - * the filter to be used - * - * @since 3.3 - */ + @Override protected void init(int treeStyle, PatternFilter filter) { - patternFilter = filter; setShowFilterControls(true); - createControl(getParent(), treeStyle); + super.init(treeStyle, filter); createRefreshJob(); setInitialText(E4DialogMessages.FilteredTree_FilterMessage); - setFont(getParent().getFont()); } - /** - * Create the filtered tree's controls. Subclasses should override. - */ + @Override protected void createControl(Composite parent, int treeStyle) { - GridLayout layout = new GridLayout(); - layout.marginHeight = 0; - layout.marginWidth = 0; - setLayout(layout); - setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - - filterComposite = new Composite(this, SWT.NONE); - - GridLayout filterLayout = new GridLayout(); - filterLayout.marginHeight = 0; - filterLayout.marginWidth = 0; - filterComposite.setLayout(filterLayout); - filterComposite.setFont(parent.getFont()); - - createFilterControls(filterComposite); - GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false); - filterComposite.setVisible(isShowFilterControls()); - gridData.exclude = !isShowFilterControls(); - filterComposite.setLayoutData(gridData); + super.createControl(parent, treeStyle); treeComposite = new Composite(this, SWT.NONE); GridLayout treeCompositeLayout = new GridLayout(); @@ -206,20 +134,6 @@ protected void createControl(Composite parent, int treeStyle) { } - /** - * Create the filter controls. By default, a text and corresponding tool bar - * button that clears the contents of the text is created. Subclasses may - * override. - * - * @param parent - * parent Composite of the filter controls - * @return the Composite that contains the filter controls - */ - protected Composite createFilterControls(Composite parent) { - createFilterText(parent); - return parent; - } - /** * Creates and set up the tree and tree viewer. This method calls * {@link #doCreateTreeViewer(Composite, int)} to create the tree viewer. @@ -239,9 +153,9 @@ protected Control createTreeControl(Composite parent, int style) { treeViewer.getControl().setLayoutData(data); treeViewer.getControl().addDisposeListener(e -> refreshJob.cancel()); if (treeViewer instanceof NotifyingTreeViewer) { - patternFilter.setUseCache(true); + getPatternFilter().setUseCache(true); } - treeViewer.addFilter(patternFilter); + treeViewer.addFilter(getPatternFilter()); return treeViewer.getControl(); } @@ -267,8 +181,8 @@ protected TreeViewer doCreateTreeViewer(Composite parent, int style) { */ private TreeItem getFirstMatchingItem(TreeItem[] items) { for (TreeItem item : items) { - if (patternFilter.isLeafMatch(treeViewer, item.getData()) - && patternFilter.isElementSelectable(item.getData())) { + if (getPatternFilter().isLeafMatch(treeViewer, item.getData()) + && getPatternFilter().isElementSelectable(item.getData())) { return item; } TreeItem treeItem = getFirstMatchingItem(item.getItems()); @@ -310,9 +224,9 @@ public IStatus runInUIThread(IProgressMonitor monitor) { boolean initial = initialText != null && initialText.equals(text); if (initial) { - patternFilter.setPattern(null); + getPatternFilter().setPattern(null); } else if (text != null) { - patternFilter.setPattern(text); + getPatternFilter().setPattern(text); } Control redrawFalseControl = treeComposite != null ? treeComposite : treeViewer.getControl(); @@ -396,6 +310,10 @@ private boolean recursiveExpand(TreeItem[] items, IProgressMonitor monitor, long }; } + /** + * @deprecated As of 4.13 not used anymore + */ + @Deprecated(since = "2025-03", forRemoval = true) protected void updateToolbar(boolean visible) { // nothing to do } @@ -409,8 +327,9 @@ protected void updateToolbar(boolean visible) { * @param parent * Composite of the filter text */ + @Override protected void createFilterText(Composite parent) { - filterText = doCreateFilterText(parent); + super.createFilterText(parent); filterText.getAccessible().addAccessibleListener(new AccessibleAdapter() { @Override public void getName(AccessibleEvent e) { @@ -454,26 +373,6 @@ private int itemCount(TreeItem treeItem) { } }); - filterText.addFocusListener(new FocusAdapter() { - - @Override - public void focusLost(FocusEvent e) { - if (filterText.getText().equals(initialText)) { - setFilterText(""); //$NON-NLS-1$ - textChanged(); - } - } - }); - - filterText.addMouseListener(new MouseAdapter() { - @Override - public void mouseDown(MouseEvent e) { - if (filterText.getText().equals(initialText)) { - clearText(); - } - } - }); - filterText.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { @@ -514,23 +413,9 @@ public void keyPressed(KeyEvent e) { } } }); - - filterText.addModifyListener(e -> textChanged()); - - GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); - filterText.setLayoutData(gridData); } - /** - * Creates the text control for entering the filter text. Subclasses may - * override. - * - * @param parent - * the parent composite - * @return the text widget - * - * @since 3.3 - */ + @Override protected Text doCreateFilterText(Composite parent) { return new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH); } @@ -539,9 +424,7 @@ protected Text doCreateFilterText(Composite parent) { private boolean narrowingDown; - /** - * Update the receiver after the text has changed. - */ + @Override protected void textChanged() { narrowingDown = previousFilterText == null || previousFilterText.equals(E4DialogMessages.FilteredTree_FilterMessage) @@ -552,18 +435,6 @@ protected void textChanged() { refreshJob.schedule(getRefreshJobDelay()); } - /** - * Return the time delay that should be used when scheduling the filter - * refresh job. Subclasses may override. - * - * @return a time delay in milliseconds before the job should run - * - * @since 3.5 - */ - protected long getRefreshJobDelay() { - return refreshJobDelayInMillis; - } - /** * Set the background for the widgets that support the filter text area. * @@ -578,109 +449,16 @@ public void setBackground(Color background) { } } - /** - * Clears the text in the filter text widget. - */ - protected void clearText() { - setFilterText(""); //$NON-NLS-1$ - textChanged(); - } - - /** - * Set the text in the filter control. - */ - protected void setFilterText(String string) { - if (filterText != null) { - filterText.setText(string); - selectAll(); - } - } - - /** - * Returns the pattern filter used by this tree. - * - * @return The pattern filter; never null. - */ + @Override public final PatternFilter getPatternFilter() { - return patternFilter; + return (PatternFilter) super.getPatternFilter(); } - /** - * Get the tree viewer of the receiver. - * - * @return the tree viewer - */ + @Override public TreeViewer getViewer() { return treeViewer; } - /** - * Get the filter text for the receiver, if it was created. Otherwise return - * null. - * - * @return the filter Text, or null if it was not created - */ - public Text getFilterControl() { - return filterText; - } - - /** - * Convenience method to return the text of the filter control. If the text - * widget is not created, then null is returned. - * - * @return String in the text, or null if the text does not exist - */ - protected String getFilterString() { - return filterText != null ? filterText.getText() : null; - } - - /** - * Set the text that will be shown until the first focus. A default value is - * provided, so this method only need be called if overriding the default - * initial text is desired. - * - * @param text - * initial text to appear in text field - */ - public void setInitialText(String text) { - initialText = text; - if (filterText != null) { - filterText.setMessage(text); - if (filterText.isFocusControl()) { - setFilterText(initialText); - textChanged(); - } else { - getDisplay().asyncExec(() -> { - if (!filterText.isDisposed() && filterText.isFocusControl()) { - setFilterText(initialText); - textChanged(); - } - }); - } - } else { - setFilterText(initialText); - textChanged(); - } - } - - /** - * Select all text in the filter text field. - */ - protected void selectAll() { - if (filterText != null) { - filterText.selectAll(); - } - } - - /** - * Get the initial text for the receiver. - * - * @return String - */ - protected String getInitialText() { - return initialText; - } - /** * Return a bold font if the given element matches the given pattern. * Clients can opt to call this method from a Viewer's label provider to get diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractFilteredViewerComposite.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractFilteredViewerComposite.java new file mode 100644 index 00000000000..c5618ccc13a --- /dev/null +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractFilteredViewerComposite.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (c) 2025 Patrick Ziegler and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.viewers; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; + +/** + * @since 3.36 + */ +public abstract class AbstractFilteredViewerComposite extends Composite { + + /** + * The pattern filter for the tree. This value must not be null. + */ + private ViewerFilter patternFilter; + + /** + * The filter text widget to be used by this tree. This value may be + * {@code null} if there is no filter widget, or if the controls have not yet + * been created. + */ + protected Text filterText; + + /** + * The Composite on which the filter controls are created. This is used to set + * the background color of the filter controls to match the surrounding + * controls. + */ + protected Composite filterComposite; + + /** + * Whether or not to show the filter controls (text and clear button). The + * default is to show these controls. This can be overridden by providing a + * setting in the product configuration file. For example, the setting to add to + * not show these controls in an 3x based application is: + * + * org.eclipse.ui/SHOW_FILTERED_TEXTS=false + */ + protected boolean showFilterControls; + + /** + * The text to initially show in the filter text control. + */ + protected String initialText = ""; //$NON-NLS-1$ + + /** + * The parent composite of the filtered viewer. + */ + protected Composite parent; + + /** + * Time for refresh job delay in terms of expansion in long value + */ + private final long refreshJobDelayInMillis; + + /** + * Create a new instance of the receiver. + * + * @param parent a widget which will be the parent this + * composite + * @param style the style used to construct this widget + * @param refreshJobDelayInMillis refresh delay in ms, the time to expand the + * tree after debounce + */ + public AbstractFilteredViewerComposite(Composite parent, int style, long refreshJobDelayInMillis) { + super(parent, style); + this.refreshJobDelayInMillis = refreshJobDelayInMillis; + } + + /** + * Create the filtered viewer. + * + * @param style the style bits for the viewer's {@code Control} + * @param filter the filter to be used + */ + protected void init(int style, T filter) { + patternFilter = filter; + createControl(parent, style); + setFont(parent.getFont()); + + } + + /** + * Create the filtered viewer's controls. Subclasses should override. + * + * @param parent the parent + * @param style SWT style bits used to create the control + */ + protected void createControl(Composite parent, int style) { + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + setLayout(layout); + + if (parent.getLayout() instanceof GridLayout) { + setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + } + + if (showFilterControls) { + filterComposite = new Composite(this, SWT.NONE); + GridLayout filterLayout = new GridLayout(); + filterLayout.marginHeight = 0; + filterLayout.marginWidth = 0; + filterComposite.setLayout(filterLayout); + filterComposite.setFont(parent.getFont()); + + createFilterControls(filterComposite); + filterComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + } + } + + /** + * Create the filter controls. By default, a text and corresponding tool bar + * button that clears the contents of the text is created. Subclasses may + * override. + * + * @param parent parent Composite of the filter controls + * @return the Composite that contains the filter controls + */ + protected Composite createFilterControls(Composite parent) { + createFilterText(parent); + return parent; + } + + /** + * Creates the filter text and adds listeners. This method calls + * {@link #doCreateFilterText(Composite)} to create the text control. Subclasses + * should override {@link #doCreateFilterText(Composite)} instead of overriding + * this method. + * + * @param parent Composite of the filter text + */ + protected void createFilterText(Composite parent) { + filterText = doCreateFilterText(parent); + + filterText.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + /* + * Running in an asyncExec because the selectAll() does not appear to work when + * using mouse to give focus to text. + */ + Display display = filterText.getDisplay(); + display.asyncExec(() -> { + if (!filterText.isDisposed()) { + if (getInitialText().equals(filterText.getText().trim())) { + filterText.selectAll(); + } + } + }); + } + + @Override + public void focusLost(FocusEvent e) { + if (filterText.getText().equals(initialText)) { + setFilterText(""); //$NON-NLS-1$ + textChanged(); + } + } + }); + + filterText.addMouseListener(new MouseAdapter() { + @Override + public void mouseDown(MouseEvent e) { + if (filterText.getText().equals(initialText)) { + // XXX: We cannot call clearText() due to + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=260664 + setFilterText(""); //$NON-NLS-1$ + textChanged(); + } + } + }); + + filterText.addModifyListener(e -> textChanged()); + + GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); + filterText.setLayoutData(gridData); + } + + /** + * Creates the text control for entering the filter text. Subclasses may + * override. + * + * @param parent the parent composite + * @return the text widget + */ + protected abstract Text doCreateFilterText(Composite parent); + + /** + * Update the receiver after the text has changed. + */ + protected abstract void textChanged(); + + /** + * Return the time delay that should be used when scheduling the filter refresh + * job. Subclasses may override. + * + * @return a time delay in milliseconds before the job should run + */ + protected long getRefreshJobDelay() { + return refreshJobDelayInMillis; + } + + /** + * Clears the text in the filter text widget. + */ + protected void clearText() { + setFilterText(""); //$NON-NLS-1$ + textChanged(); + } + + /** + * Set the text in the filter control. + * + * @param filterText the text to set. + */ + protected void setFilterText(String filterText) { + if (this.filterText != null) { + this.filterText.setText(filterText); + selectAll(); + } + } + + /** + * Returns the pattern filter used by this tree. + * + * @return The pattern filter; never {@code null}. + */ + public ViewerFilter getPatternFilter() { + return patternFilter; + } + + /** + * Get the structured viewer of the receiver. + * + * @return the structured viewer + */ + public abstract StructuredViewer getViewer(); + + /** + * Get the filter text for the receiver, if it was created. Otherwise return + * {@code null}. + * + * @return the filter Text, or null if it was not created + */ + public Text getFilterControl() { + return filterText; + } + + /** + * Convenience method to return the text of the filter control. If the text + * widget is not created, then null is returned. + * + * @return String in the text, or null if the text does not exist + */ + protected String getFilterString() { + return filterText != null ? filterText.getText() : null; + } + + /** + * Set the text that will be shown until the first focus. A default value is + * provided, so this method only need be called if overriding the default + * initial text is desired. + * + * @param text initial text to appear in text field + */ + public void setInitialText(String text) { + initialText = text; + if (filterText != null) { + filterText.setMessage(text); + if (filterText.isFocusControl()) { + setFilterText(initialText); + textChanged(); + } else { + getDisplay().asyncExec(() -> { + if (!filterText.isDisposed() && filterText.isFocusControl()) { + setFilterText(initialText); + textChanged(); + } + }); + } + } else { + setFilterText(initialText); + textChanged(); + } + } + + /** + * Select all text in the filter text field. + */ + protected void selectAll() { + if (filterText != null) { + filterText.selectAll(); + } + } + + /** + * Get the initial text for the receiver. + * + * @return String + */ + protected String getInitialText() { + return initialText; + } +} diff --git a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF index e80a10825d7..2e8dd48038b 100644 --- a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.ui.workbench; singleton:=true -Bundle-Version: 3.134.100.qualifier +Bundle-Version: 3.135.0.qualifier Bundle-Activator: org.eclipse.ui.internal.WorkbenchPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java index 24e2d884217..f41a283ee63 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2015 IBM Corporation and others. + * Copyright (c) 2004, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -22,6 +22,7 @@ import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.AbstractFilteredViewerComposite; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.TreeViewer; @@ -29,13 +30,8 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.accessibility.AccessibleAdapter; import org.eclipse.swt.accessibility.AccessibleEvent; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; @@ -57,20 +53,16 @@ * @see org.eclipse.ui.dialogs.PatternFilter * @since 3.2 */ -public class FilteredTree extends Composite { - - /** - * The filter text widget to be used by this tree. This value may be - * null if there is no filter widget, or if the controls have not - * yet been created. - */ - protected Text filterText; +public class FilteredTree extends AbstractFilteredViewerComposite { /** *

* Note: As of 4.13 not used anymore *

+ * + * @deprecated As of 4.13 not used anymore */ + @Deprecated(since = "2025-03", forRemoval = true) protected ToolBarManager filterToolBar; /** @@ -79,7 +71,9 @@ public class FilteredTree extends Composite { *

* * @since 3.5 + * @deprecated As of 4.13 not used anymore */ + @Deprecated(since = "2025-03", forRemoval = true) protected Control clearButtonControl; /** @@ -88,45 +82,11 @@ public class FilteredTree extends Composite { */ protected TreeViewer treeViewer; - /** - * The Composite on which the filter controls are created. This is used to set - * the background color of the filter controls to match the surrounding - * controls. - */ - protected Composite filterComposite; - - /** - * The pattern filter for the tree. This value must not be null. - */ - private PatternFilter patternFilter; - - /** - * The text to initially show in the filter text control. - */ - protected String initialText = ""; //$NON-NLS-1$ - /** * The job used to refresh the tree. */ private Job refreshJob; - /** - * The parent composite of the filtered tree. - * - * @since 3.3 - */ - protected Composite parent; - - /** - * Whether or not to show the filter controls (text and clear button). The - * default is to show these controls. This can be overridden by providing a - * setting in the product configuration file. The setting to add to not show - * these controls is: - * - * org.eclipse.ui/SHOW_FILTERED_TEXTS=false - */ - protected boolean showFilterControls; - /** * @since 3.3 */ @@ -147,11 +107,6 @@ public class FilteredTree extends Composite { */ private static final long SOFT_MAX_EXPAND_TIME = 200; - /** - * Time for refresh job delay in terms of expansion in long value - */ - private final long refreshJobDelayInMillis; - /** * Default time for refresh job delay in ms */ @@ -179,9 +134,8 @@ public class FilteredTree extends Composite { * @since 3.116 */ public FilteredTree(Composite parent, boolean useNewLook, boolean useFastHashLookup) { - super(parent, SWT.NONE); + super(parent, SWT.NONE, DEFAULT_REFRESH_TIME); this.parent = parent; - this.refreshJobDelayInMillis = DEFAULT_REFRESH_TIME; if (treeViewer != null) { treeViewer.setUseHashlookup(useFastHashLookup); } @@ -225,9 +179,8 @@ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boole */ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boolean useNewLook, boolean useFastHashLookup, long refreshJobDelayInMillis) { - super(parent, SWT.NONE); + super(parent, SWT.NONE, refreshJobDelayInMillis); this.parent = parent; - this.refreshJobDelayInMillis = refreshJobDelayInMillis; init(treeStyle, filter); if (treeViewer != null) { treeViewer.setUseHashlookup(useFastHashLookup); @@ -257,8 +210,7 @@ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boole */ @Deprecated protected FilteredTree(Composite parent) { - super(parent, SWT.NONE); - this.refreshJobDelayInMillis = DEFAULT_REFRESH_TIME; + super(parent, SWT.NONE, DEFAULT_REFRESH_TIME); this.parent = parent; } @@ -337,52 +289,18 @@ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boole this(parent, treeStyle, filter); } - /** - * Create the filtered tree. - * - * @param treeStyle the style bits for the Tree - * @param filter the filter to be used - * - * @since 3.3 - */ + @Override protected void init(int treeStyle, PatternFilter filter) { - patternFilter = filter; showFilterControls = PlatformUI.getPreferenceStore() .getBoolean(IWorkbenchPreferenceConstants.SHOW_FILTERED_TEXTS); - createControl(parent, treeStyle); + super.init(treeStyle, filter); createRefreshJob(); setInitialText(WorkbenchMessages.FilteredTree_FilterMessage); - setFont(parent.getFont()); - } - /** - * Create the filtered tree's controls. Subclasses should override. - * - * @param parent the parent - * @param treeStyle SWT style bits used to create the tree - */ + @Override protected void createControl(Composite parent, int treeStyle) { - GridLayout layout = new GridLayout(); - layout.marginHeight = 0; - layout.marginWidth = 0; - setLayout(layout); - - if (parent.getLayout() instanceof GridLayout) { - setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - } - - if (showFilterControls) { - filterComposite = new Composite(this, SWT.NONE); - GridLayout filterLayout = new GridLayout(); - filterLayout.marginHeight = 0; - filterLayout.marginWidth = 0; - filterComposite.setLayout(filterLayout); - filterComposite.setFont(parent.getFont()); - - createFilterControls(filterComposite); - filterComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); - } + super.createControl(parent, treeStyle); treeComposite = new Composite(this, SWT.NONE); GridLayout treeCompositeLayout = new GridLayout(); @@ -394,19 +312,6 @@ protected void createControl(Composite parent, int treeStyle) { createTreeControl(treeComposite, treeStyle); } - /** - * Create the filter controls. By default, a text and corresponding tool bar - * button that clears the contents of the text is created. Subclasses may - * override. - * - * @param parent parent Composite of the filter controls - * @return the Composite that contains the filter controls - */ - protected Composite createFilterControls(Composite parent) { - createFilterText(parent); - return parent; - } - /** * Creates and set up the tree and tree viewer. This method calls * {@link #doCreateTreeViewer(Composite, int)} to create the tree viewer. @@ -423,9 +328,9 @@ protected Control createTreeControl(Composite parent, int style) { treeViewer.getControl().setLayoutData(data); treeViewer.getControl().addDisposeListener(e -> refreshJob.cancel()); if (treeViewer instanceof NotifyingTreeViewer) { - patternFilter.setUseCache(true); + getPatternFilter().setUseCache(true); } - treeViewer.addFilter(patternFilter); + treeViewer.addFilter(getPatternFilter()); return treeViewer.getControl(); } @@ -449,8 +354,8 @@ protected TreeViewer doCreateTreeViewer(Composite parent, int style) { */ private TreeItem getFirstMatchingItem(TreeItem[] items) { for (TreeItem item : items) { - if (patternFilter.isLeafMatch(treeViewer, item.getData()) - && patternFilter.isElementSelectable(item.getData())) { + if (getPatternFilter().isLeafMatch(treeViewer, item.getData()) + && getPatternFilter().isElementSelectable(item.getData())) { return item; } TreeItem treeItem = getFirstMatchingItem(item.getItems()); @@ -492,9 +397,9 @@ public IStatus runInUIThread(IProgressMonitor monitor) { boolean initial = initialText != null && initialText.equals(text); if (initial) { - patternFilter.setPattern(null); + getPatternFilter().setPattern(null); } else if (text != null) { - patternFilter.setPattern(text); + getPatternFilter().setPattern(text); } Control redrawFalseControl = treeComposite != null ? treeComposite : treeViewer.getControl(); @@ -583,7 +488,9 @@ private boolean recursiveExpand(TreeItem[] items, IProgressMonitor monitor, long * override. * * @param visible boolean + * @deprecated As of 4.13 not used anymore */ + @Deprecated(since = "2025-03", forRemoval = true) protected void updateToolbar(boolean visible) { // nothing to do } @@ -596,8 +503,9 @@ protected void updateToolbar(boolean visible) { * * @param parent Composite of the filter text */ + @Override protected void createFilterText(Composite parent) { - filterText = doCreateFilterText(parent); + super.createFilterText(parent); filterText.getAccessible().addAccessibleListener(new AccessibleAdapter() { @Override public void getName(AccessibleEvent e) { @@ -641,44 +549,6 @@ private int itemCount(TreeItem treeItem) { } }); - filterText.addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - /* - * Running in an asyncExec because the selectAll() does not appear to work when - * using mouse to give focus to text. - */ - Display display = filterText.getDisplay(); - display.asyncExec(() -> { - if (!filterText.isDisposed()) { - if (getInitialText().equals(filterText.getText().trim())) { - filterText.selectAll(); - } - } - }); - } - - @Override - public void focusLost(FocusEvent e) { - if (filterText.getText().equals(initialText)) { - setFilterText(""); //$NON-NLS-1$ - textChanged(); - } - } - }); - - filterText.addMouseListener(new MouseAdapter() { - @Override - public void mouseDown(MouseEvent e) { - if (filterText.getText().equals(initialText)) { - // XXX: We cannot call clearText() due to - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=260664 - setFilterText(""); //$NON-NLS-1$ - textChanged(); - } - } - }); - filterText.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { @@ -701,11 +571,6 @@ public void keyPressed(KeyEvent e) { updateTreeSelection(true); } }); - - filterText.addModifyListener(e -> textChanged()); - - GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false); - filterText.setLayoutData(gridData); } /** @@ -740,15 +605,7 @@ protected void updateTreeSelection(boolean setFocus) { } } - /** - * Creates the text control for entering the filter text. Subclasses may - * override. - * - * @param parent the parent composite - * @return the text widget - * - * @since 3.3 - */ + @Override protected Text doCreateFilterText(Composite parent) { return new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL); } @@ -757,9 +614,7 @@ protected Text doCreateFilterText(Composite parent) { private boolean narrowingDown; - /** - * Update the receiver after the text has changed. - */ + @Override protected void textChanged() { narrowingDown = previousFilterText == null || previousFilterText.equals(WorkbenchMessages.FilteredTree_FilterMessage) @@ -770,114 +625,16 @@ protected void textChanged() { refreshJob.schedule(getRefreshJobDelay()); } - /** - * Return the time delay that should be used when scheduling the filter refresh - * job. Subclasses may override. - * - * @return a time delay in milliseconds before the job should run - * - * @since 3.5 - */ - protected long getRefreshJobDelay() { - return refreshJobDelayInMillis; - } - - /** - * Set the background for the widgets that support the filter text area. - * - * @param background background Color to set - */ @Override - public void setBackground(Color background) { - super.setBackground(background); - } - - /** - * Clears the text in the filter text widget. - */ - protected void clearText() { - setFilterText(""); //$NON-NLS-1$ - textChanged(); - } - - /** - * Set the text in the filter control. - * - * @param filterText the text to set. - */ - protected void setFilterText(String filterText) { - if (this.filterText != null) { - this.filterText.setText(filterText); - selectAll(); - } - } - - /** - * Returns the pattern filter used by this tree. - * - * @return The pattern filter; never null. - */ public final PatternFilter getPatternFilter() { - return patternFilter; + return (PatternFilter) super.getPatternFilter(); } - /** - * Get the tree viewer of the receiver. - * - * @return the tree viewer - */ + @Override public TreeViewer getViewer() { return treeViewer; } - /** - * Get the filter text for the receiver, if it was created. Otherwise return - * null. - * - * @return the filter Text, or null if it was not created - */ - public Text getFilterControl() { - return filterText; - } - - /** - * Convenience method to return the text of the filter control. If the text - * widget is not created, then null is returned. - * - * @return String in the text, or null if the text does not exist - */ - protected String getFilterString() { - return filterText != null ? filterText.getText() : null; - } - - /** - * Set the text that will be shown until the first focus. A default value is - * provided, so this method only need be called if overriding the default - * initial text is desired. - * - * @param text initial text to appear in text field - */ - public void setInitialText(String text) { - initialText = text; - if (filterText != null) { - filterText.setMessage(text); - if (filterText.isFocusControl()) { - setFilterText(initialText); - textChanged(); - } else { - getDisplay().asyncExec(() -> { - if (!filterText.isDisposed() && filterText.isFocusControl()) { - setFilterText(initialText); - textChanged(); - } - }); - } - } else { - setFilterText(initialText); - textChanged(); - } - } - /** * Sets whether this filtered tree is used to make quick selections. In this * mode the first match in the tree is automatically selected while filtering @@ -894,24 +651,6 @@ public void setQuickSelectionMode(boolean enabled) { this.quickSelectionMode = enabled; } - /** - * Select all text in the filter text field. - */ - protected void selectAll() { - if (filterText != null) { - filterText.selectAll(); - } - } - - /** - * Get the initial text for the receiver. - * - * @return String - */ - protected String getInitialText() { - return initialText; - } - /** * Return a bold font if the given element matches the given pattern. Clients * can opt to call this method from a Viewer's label provider to get a bold font