diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java index afc4e9b25da..f562024aaff 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/CTabRendering.java @@ -25,12 +25,9 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.e4.ui.internal.css.swt.ICTabRendering; +import org.eclipse.e4.ui.internal.workbench.PartStackUtil; import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; -import org.eclipse.e4.ui.model.application.ui.MContext; import org.eclipse.e4.ui.model.application.ui.MUIElement; -import org.eclipse.e4.ui.workbench.modeling.EModelService; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabFolderRenderer; @@ -1329,15 +1326,7 @@ private void hideIconsForViewTabsPreferenceChanged() { private boolean isPartOfEditorStack() { MUIElement element = (MUIElement) parent.getData(AbstractPartRenderer.OWNING_ME); - EObject root = EcoreUtil.getRootContainer((EObject) element, true); - if (root instanceof MContext context) { - EModelService eModelService = context.getContext().get(EModelService.class); - if (eModelService != null) { - int location = eModelService.getElementLocation(element); - return (location & EModelService.IN_SHARED_AREA) != 0; - } - } - return false; + return PartStackUtil.isEditorStack(element); } private boolean getSwtRendererPreference(String prefName, boolean defaultValue) { diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRenderer.java b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRenderer.java index e46a4a29c39..9d5eb842923 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRenderer.java +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRenderer.java @@ -43,6 +43,7 @@ import org.eclipse.e4.ui.di.UIEventTopic; import org.eclipse.e4.ui.di.UISynchronize; import org.eclipse.e4.ui.internal.workbench.OpaqueElementUtil; +import org.eclipse.e4.ui.internal.workbench.PartStackUtil; import org.eclipse.e4.ui.internal.workbench.renderers.swt.BasicPartList; import org.eclipse.e4.ui.internal.workbench.renderers.swt.SWTRenderersMessages; import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; @@ -128,8 +129,6 @@ public class StackRenderer extends LazyStackRenderer { private static final String ONBOARDING_IMAGE = "EditorStack.OnboardingImage"; //$NON-NLS-1$ private static final String ONBOARDING_TEXT = "EditorStack.OnboardingText"; //$NON-NLS-1$ - private static final String EDITOR_STACK_ID = "EditorStack"; //$NON-NLS-1$ - /** * Id of a a control. */ @@ -703,7 +702,7 @@ public Object createWidget(MUIElement element, Object parent) { int location = modelService.getElementLocation(element); boolean isInSharedArea = (location & EModelService.IN_SHARED_AREA) != 0; if (isInSharedArea) { - pStack.getTags().add(EDITOR_STACK_ID); + PartStackUtil.makeEditorStack(pStack); } Composite parentComposite = (Composite) parent; @@ -717,7 +716,7 @@ public Object createWidget(MUIElement element, Object parent) { int styleOverride = getStyleOverride(pStack); int style = styleOverride == -1 ? SWT.BORDER : styleOverride; CTabFolder tabFolder = new CTabFolder(parentComposite, style); - if (pStack.getTags().contains("EditorStack")) { //$NON-NLS-1$ + if (PartStackUtil.isEditorStack(element)) { createOnboardingControls(tabFolder); initializeOnboardingInformationInEditorStack(tabFolder); } @@ -1926,7 +1925,8 @@ private Stream getEditorTabFolders(MPerspective perspective) { Predicate tabFolders = CTabFolder.class::isInstance; Function toTabFolder = CTabFolder.class::cast; - List elements = modelService.findElements(perspective, null, MPartStack.class, List.of(EDITOR_STACK_ID)); + List elements = modelService.findElements(perspective, null, MPartStack.class, + List.of(PartStackUtil.EDITOR_STACK_TAG)); return elements.stream().map(MUIElement::getWidget).filter(tabFolders).map(toTabFolder); } diff --git a/bundles/org.eclipse.e4.ui.workbench/src/org/eclipse/e4/ui/internal/workbench/PartStackUtil.java b/bundles/org.eclipse.e4.ui.workbench/src/org/eclipse/e4/ui/internal/workbench/PartStackUtil.java index 49064edd6f8..c9df5991875 100644 --- a/bundles/org.eclipse.e4.ui.workbench/src/org/eclipse/e4/ui/internal/workbench/PartStackUtil.java +++ b/bundles/org.eclipse.e4.ui.workbench/src/org/eclipse/e4/ui/internal/workbench/PartStackUtil.java @@ -28,10 +28,8 @@ private PartStackUtil() { * @param partStack the part stack to make the primary data stack */ public static void initializeAsPrimaryDataStack(MPartStack partStack) { + makeEditorStack(partStack); partStack.getTags().add(PRIMARY_DATA_STACK_ID); - if (!partStack.getTags().contains(EDITOR_STACK_TAG)) { - partStack.getTags().add(EDITOR_STACK_TAG); - } partStack.setElementId(PRIMARY_DATA_STACK_ID); } @@ -42,4 +40,26 @@ public static void initializeAsPrimaryDataStack(MPartStack partStack) { public static boolean isPrimaryDataStack(MApplicationElement element) { return element instanceof MPartStack && PRIMARY_DATA_STACK_ID.equals(element.getElementId()); } + + /** + * @param element the element to check for being an editor stack + * @return whether the given element is marked as an editor stack + */ + public static boolean isEditorStack(MApplicationElement element) { + return element instanceof MPartStack && element.getTags().contains(EDITOR_STACK_TAG); + } + + /** + * Marks the given part stack as an editor stack. In consequence calling + * {{@link #isEditorStack(MApplicationElement)} for the element will return + * {@code true}. + * + * @param partStack the part stack to mark as an editor stack + */ + public static void makeEditorStack(MPartStack partStack) { + if (!partStack.getTags().contains(EDITOR_STACK_TAG)) { + partStack.getTags().add(EDITOR_STACK_TAG); + } + } + } diff --git a/bundles/org.eclipse.ui.genericeditor/plugin.properties b/bundles/org.eclipse.ui.genericeditor/plugin.properties index 9e89bdac80c..490be35fa35 100644 --- a/bundles/org.eclipse.ui.genericeditor/plugin.properties +++ b/bundles/org.eclipse.ui.genericeditor/plugin.properties @@ -39,3 +39,4 @@ gotoMatchingBracketCommand_name = Go to Matching Bracket gotoMatchingBracketCommand_description = Moves the cursor to the matching bracket systemEditorOrGenericEditorStrategy=System Editor; if none: Advanced Text Editor genericEditorStrategy=Advanced Text Editor +PreferencePages.GenericTextEditors=Generic Text Editors diff --git a/bundles/org.eclipse.ui.genericeditor/plugin.xml b/bundles/org.eclipse.ui.genericeditor/plugin.xml index 22c389664d9..107f075c39e 100644 --- a/bundles/org.eclipse.ui.genericeditor/plugin.xml +++ b/bundles/org.eclipse.ui.genericeditor/plugin.xml @@ -276,4 +276,13 @@ label="%genericEditorStrategy"> + + + + diff --git a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java index e6403a38055..ef0d7b84862 100644 --- a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java +++ b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/GenericEditorContentAssistant.java @@ -28,6 +28,8 @@ import org.eclipse.jface.text.contentassist.ContentAssistant; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContentAssistant; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.internal.genericeditor.preferences.GenericEditorPreferenceConstants; @@ -43,11 +45,12 @@ * * @author christoph */ -public class GenericEditorContentAssistant extends ContentAssistant { +public class GenericEditorContentAssistant extends ContentAssistant implements IPropertyChangeListener { private static final DefaultContentAssistProcessor DEFAULT_CONTENT_ASSIST_PROCESSOR = new DefaultContentAssistProcessor(); private ContentTypeRelatedExtensionTracker contentAssistProcessorTracker; private Set types; private List processors; + private final IPreferenceStore preferenceStore; /** * Creates a new GenericEditorContentAssistant instance for the given content @@ -90,20 +93,17 @@ public GenericEditorContentAssistant( this.contentAssistProcessorTracker = contentAssistProcessorTracker; this.processors = Objects.requireNonNullElseGet(processors, () -> Collections.emptyList()); this.types = types; + this.preferenceStore = preferenceStore; setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_BELOW); setProposalPopupOrientation(IContentAssistant.PROPOSAL_REMOVE); enableColoredLabels(true); if (preferenceStore != null) { - enableAutoActivation( - preferenceStore.getBoolean(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION)); - setAutoActivationDelay( - preferenceStore.getInt(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY)); - enableAutoActivateCompletionOnType(preferenceStore - .getBoolean(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_ON_TYPE)); + updateAutoActivationPreferences(); + preferenceStore.addPropertyChangeListener(this); } else { enableAutoActivation(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DEFAULT); - setAutoActivationDelay(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY_DEFUALT); + setAutoActivationDelay(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY_DEFAULT); enableAutoActivateCompletionOnType( GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_ON_TYPE_DEFAULT); } @@ -150,9 +150,21 @@ private void updateProcessorToken(IContentAssistProcessor processor, IDocument d } } + private void updateAutoActivationPreferences() { + enableAutoActivation( + preferenceStore.getBoolean(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION)); + setAutoActivationDelay( + preferenceStore.getInt(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY)); + enableAutoActivateCompletionOnType( + preferenceStore.getBoolean(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_ON_TYPE)); + } + @Override public void uninstall() { contentAssistProcessorTracker.stopTracking(); + if (preferenceStore != null) { + preferenceStore.removePropertyChangeListener(this); + } super.uninstall(); } @@ -175,4 +187,9 @@ public void install(ITextViewer textViewer) { }); contentAssistProcessorTracker.startTracking(); } + + @Override + public void propertyChange(PropertyChangeEvent event) { + updateAutoActivationPreferences(); + } } diff --git a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/Messages.java b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/Messages.java index 9586e006c81..c442d52481d 100644 --- a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/Messages.java +++ b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/Messages.java @@ -26,6 +26,16 @@ public class Messages extends NLS { public static String GotoMatchingBracket_error_bracketOutsideSelectedElement; public static String GenericEditorMergeViewer_title; + public static String ContentAssistant; + public static String ContentAssistant_autoActivation; + public static String ContentAssistant_autoActivation_Tooltip; + public static String ContentAssistant_autoActivationDelay; + public static String ContentAssistant_autoActivationDelay_Tooltip; + public static String ContentAssistant_autoActivationOnType; + public static String ContentAssistant_autoActivationOnType_Tooltip; + public static String ContentAssistant_autoActivationDelay_InvalidInput; + public static String ContentAssistant_autoActivationDelay_EmptyInput; + static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, Messages.class); diff --git a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/messages.properties b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/messages.properties index 290689c51dc..5fb48e83632 100644 --- a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/messages.properties +++ b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/messages.properties @@ -19,4 +19,14 @@ TextViewer_open_hyperlink_error_message=The operation is not applicable to the c GotoMatchingBracket_error_noMatchingBracket=No matching bracket found GotoMatchingBracket_error_bracketOutsideSelectedElement=Matching bracket is outside the selected element +ContentAssistant=Content Assist +ContentAssistant_autoActivation=Enable auto activation +ContentAssistant_autoActivation_Tooltip=The content assist will be activated on typing or on trigger characters if auto activation is enabled. +ContentAssistant_autoActivationDelay=Auto activation delay (ms): +ContentAssistant_autoActivationDelay_Tooltip=Activation delay in milliseconds for content assist. +ContentAssistant_autoActivationOnType=Enable auto activation on typing +ContentAssistant_autoActivationOnType_Tooltip=Controls whether auto activation on typing is enabled. If disabled, content assist will be activated by trigger characters only. +ContentAssistant_autoActivationDelay_InvalidInput=''{0}'' is not a valid input. +ContentAssistant_autoActivationDelay_EmptyInput=Empty input. + GenericEditorMergeViewer_title={0} Compare \ No newline at end of file diff --git a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/preferences/GenericEditorPreferenceConstants.java b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/preferences/GenericEditorPreferenceConstants.java index ba56c7a7995..33ea47dfd72 100644 --- a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/preferences/GenericEditorPreferenceConstants.java +++ b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/preferences/GenericEditorPreferenceConstants.java @@ -101,7 +101,7 @@ private GenericEditorPreferenceConstants() { * @since 1.3 */ public final static String CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY = "contentAssistant.autoActivationDelay"; //$NON-NLS-1$ - public final static int CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY_DEFUALT = 10; + public final static int CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY_DEFAULT = 10; /** * A named preference that controls whether auto activation on typing is enabled @@ -138,7 +138,7 @@ public static void initializeDefaultValues(IPreferenceStore store) { store.setDefault(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION, CONTENT_ASSISTANT_AUTO_ACTIVATION_DEFAULT); store.setDefault(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY, - CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY_DEFUALT); + CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY_DEFAULT); store.setDefault(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_ON_TYPE, CONTENT_ASSISTANT_AUTO_ACTIVATION_ON_TYPE_DEFAULT); // Colors that are set by the current theme diff --git a/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/preferences/GenericEditorPreferencePage.java b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/preferences/GenericEditorPreferencePage.java new file mode 100644 index 00000000000..b76f2b5e360 --- /dev/null +++ b/bundles/org.eclipse.ui.genericeditor/src/org/eclipse/ui/internal/genericeditor/preferences/GenericEditorPreferencePage.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * See git history + *******************************************************************************/ +package org.eclipse.ui.internal.genericeditor.preferences; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.PreferenceMetadata; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.internal.genericeditor.Messages; +import org.eclipse.ui.preferences.ScopedPreferenceStore; + +public class GenericEditorPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + private final ArrayList leadFollowerListeners = new ArrayList<>(); + private final Map, Button> buttons = new HashMap<>(); + private final Map, Text> textFields = new HashMap<>(); + private static final int columns = 2; + private final IPreferenceStore store; + + public GenericEditorPreferencePage() { + this.store = GenericEditorPreferenceConstants.getPreferenceStore(); + } + + @Override + public void init(IWorkbench workbench) { + // nothing to do + } + + @Override + protected Control createContents(Composite parent) { + var control = createAppearancePage(parent); + Dialog.applyDialogFont(control); + initialize(); + return control; + } + + @Override + public boolean performOk() { + if (store instanceof ScopedPreferenceStore scopedStore) { + buttons.entrySet().forEach(e -> scopedStore.setValue(e.getKey().identifer(), e.getValue().getSelection())); + textFields.entrySet().forEach(e -> scopedStore.setValue(e.getKey().identifer(), e.getValue().getText())); + try { + scopedStore.save(); + } catch (IOException e) { + Platform.getLog(getClass()).error("Cannot to save preferences.", e); //$NON-NLS-1$ + return false; + } + } + return true; + } + + @Override + protected void performDefaults() { + buttons.entrySet().forEach(e -> e.getValue().setSelection(store.getDefaultBoolean(e.getKey().identifer()))); + textFields.entrySet().forEach(e -> e.getValue().setText(store.getDefaultString(e.getKey().identifer()))); + updateFollower(); + super.performDefaults(); + } + + private Control createAppearancePage(Composite parent) { + + Composite appearanceComposite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.numColumns = columns; + layout.marginHeight = 0; + layout.marginWidth = 0; + + final var contetAssistGroup = createGroup(appearanceComposite, Messages.ContentAssistant); + + final var autoActivationMetadata = new PreferenceMetadata<>(Boolean.class, // + GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION, // + GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DEFAULT, // + Messages.ContentAssistant_autoActivation, // + Messages.ContentAssistant_autoActivation_Tooltip); + final var autoActivation = createButton(autoActivationMetadata, contetAssistGroup, SWT.CHECK, 0); + + final var activationDelay = new PreferenceMetadata<>(String.class, // + GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY, // + Integer.toString(GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_DELAY_DEFAULT), // + Messages.ContentAssistant_autoActivationDelay, // + Messages.ContentAssistant_autoActivationDelay_Tooltip); + final var activationDelayControl = createTextField(activationDelay, contetAssistGroup, 4, 20); + + final var autoActivationOnTypeMetada = new PreferenceMetadata<>(Boolean.class, // + GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_ON_TYPE, // + GenericEditorPreferenceConstants.CONTENT_ASSISTANT_AUTO_ACTIVATION_ON_TYPE_DEFAULT, // + Messages.ContentAssistant_autoActivationOnType, // + Messages.ContentAssistant_autoActivationOnType_Tooltip); // + final var autoActivationOnType = createButton(autoActivationOnTypeMetada, contetAssistGroup, SWT.CHECK, 20); + List follower = new ArrayList<>(3); + Collections.addAll(follower, activationDelayControl); + follower.add(autoActivationOnType); + + createDependency(autoActivation, autoActivationMetadata.identifer(), follower.toArray(new Control[0])); + + appearanceComposite.setLayout(layout); + return appearanceComposite; + } + + private void initialize() { + buttons.entrySet().forEach(e -> e.getValue().setSelection(store.getBoolean(e.getKey().identifer()))); + textFields.entrySet().forEach(e -> e.getValue().setText(store.getString(e.getKey().identifer()))); + } + + private static Group createGroup(Composite parent, String label) { + Group group = new Group(parent, SWT.NONE); + group.setFont(parent.getFont()); + group.setText(label); + GridLayout layout = new GridLayout(); + layout.numColumns = columns; + group.setLayout(layout); + group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + return group; + } + + private Button createButton(final PreferenceMetadata meta, Composite composite, int style, + int horizontalIndent) { + Button button = new Button(composite, style); + button.setLayoutData(GridDataFactory.fillDefaults().span(columns, 1).indent(horizontalIndent, 0).create()); + button.setData(meta); + button.setText(meta.name()); + button.setToolTipText(meta.description()); + buttons.put(meta, button); + return button; + } + + private Control[] createTextField(final PreferenceMetadata meta, Composite composite, int textLimit, + int horizontalIndent) { + Label labelControl = new Label(composite, SWT.NONE); + labelControl.setText(meta.name()); + GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalIndent = horizontalIndent; + labelControl.setLayoutData(gd); + + final Text textControl = new Text(composite, SWT.BORDER | SWT.SINGLE); + gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.widthHint = convertWidthInCharsToPixels(textLimit + 1); + textControl.setLayoutData(gd); + textControl.setTextLimit(textLimit); + textControl.setToolTipText(meta.description()); + + textControl.addModifyListener(e -> { + updateStatus(validateDelay(textControl.getText())); + }); + textFields.put(meta, textControl); + return new Control[] { labelControl, textControl }; + } + + private static String validateDelay(String value) { + if (value.isEmpty()) { + return Messages.ContentAssistant_autoActivationDelay_EmptyInput; + } + try { + int integer = parseInteger(value); + if (integer < 0) + return NLS.bind(Messages.ContentAssistant_autoActivationDelay_InvalidInput, integer); + } catch (NumberFormatException e) { + return NLS.bind(Messages.ContentAssistant_autoActivationDelay_InvalidInput, value); + } + return null; + } + + private void updateStatus(String errorMessage) { + setValid(errorMessage == null); + var messageType = errorMessage == null ? IMessageProvider.NONE : IMessageProvider.ERROR; + setMessage(errorMessage, messageType); + setErrorMessage(errorMessage); + } + + private static int parseInteger(Object value) throws NumberFormatException { + if (value instanceof Integer) { + return ((Integer) value).intValue(); + } + if (value instanceof String) { + return Integer.parseInt((String) value); + } + throw new NumberFormatException(NLS.bind(Messages.ContentAssistant_autoActivationDelay_InvalidInput, value)); + } + + private void createDependency(final Button lead, String leadKey, final Control[] follower) { + var leadState = store.getBoolean(leadKey); + for (Control f : follower) { + f.setEnabled(leadState); + } + + SelectionListener listener = new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + boolean state = lead.getSelection(); + for (Control f : follower) { + f.setEnabled(state); + } + } + }; + leadFollowerListeners.add(listener); + lead.addSelectionListener(listener); + } + + private void updateFollower() { + for (var listener : leadFollowerListeners) { + listener.widgetSelected(null); + } + } +} diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceLogic.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceLogic.java index 70cbc0a868c..074197dd49d 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceLogic.java +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/FindReplaceLogic.java @@ -114,11 +114,15 @@ private void resetStatus() { status = null; } + @Override + public boolean isIncrementalSearchAvailable() { + return !isRegExSearchAvailableAndActive(); + } + @Override public boolean isWholeWordSearchAvailable(String findString) { return !isRegExSearchAvailableAndActive() && isWord(findString); } - /** * Tests whether each character in the given string is a letter. * @@ -686,7 +690,7 @@ private void statusLineMessage(String message) { public void performIncrementalSearch(String searchString) { resetStatus(); - if (isActive(SearchOptions.INCREMENTAL) && !isRegExSearchAvailableAndActive()) { + if (isActive(SearchOptions.INCREMENTAL) && isIncrementalSearchAvailable()) { if (searchString.equals("") && target != null) { //$NON-NLS-1$ // empty selection at base location int offset = incrementalBaseLocation.x; diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/IFindReplaceLogic.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/IFindReplaceLogic.java index 0c8fb9e430b..1af8eec56bd 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/IFindReplaceLogic.java +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/IFindReplaceLogic.java @@ -66,6 +66,12 @@ public interface IFindReplaceLogic { */ public boolean isRegExSearchAvailableAndActive(); + /** + * {@return whether incremental search may be performed by the + * find/replace-logic based on the currently active options} + */ + public boolean isIncrementalSearchAvailable(); + /** * Updates the search result after the Text was Modified. Used in combination * with setIncrementalSearch(true). This method specifically allows diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java index 7f4553f7183..77cfd132e6e 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java @@ -725,11 +725,11 @@ public void widgetDefaultSelected(SelectionEvent e) { @Override public void widgetSelected(SelectionEvent e) { boolean newState = fIsRegExCheckBox.getSelection(); - fIncrementalCheckBox.setEnabled(!newState); setupFindReplaceLogic(); storeSettings(); updateButtonState(); setContentAssistsEnablement(newState); + fIncrementalCheckBox.setEnabled(findReplaceLogic.isIncrementalSearchAvailable()); } }); storeButtonWithMnemonicInMap(fIsRegExCheckBox); @@ -740,7 +740,7 @@ public void widgetSelected(SelectionEvent e) { updateButtonState(); } }); - fIncrementalCheckBox.setEnabled(!findReplaceLogic.isRegExSearchAvailableAndActive()); + fIncrementalCheckBox.setEnabled(findReplaceLogic.isIncrementalSearchAvailable()); return panel; } @@ -1165,7 +1165,7 @@ public void updateTarget(IFindReplaceTarget target, boolean isTargetEditable, bo } if (okToUse(fIncrementalCheckBox)) { - fIncrementalCheckBox.setEnabled(!findReplaceLogic.isRegExSearchAvailableAndActive()); + fIncrementalCheckBox.setEnabled(findReplaceLogic.isIncrementalSearchAvailable()); } if (okToUse(fReplaceLabel)) { @@ -1264,8 +1264,7 @@ private void setupFindReplaceLogic() { activateInFindReplaceLogicIf(SearchOptions.CASE_SENSITIVE, fCaseCheckBox.getSelection()); activateInFindReplaceLogicIf(SearchOptions.REGEX, fIsRegExCheckBox.getSelection()); activateInFindReplaceLogicIf(SearchOptions.WHOLE_WORD, fWholeWordCheckBox.getSelection()); - activateInFindReplaceLogicIf(SearchOptions.INCREMENTAL, - fIncrementalCheckBox.getEnabled() && fIncrementalCheckBox.getSelection()); + activateInFindReplaceLogicIf(SearchOptions.INCREMENTAL, fIncrementalCheckBox.getSelection()); } /** diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/e4/compatibility/ModeledPageLayout.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/e4/compatibility/ModeledPageLayout.java index e9f827dd1a6..b472abd82b5 100644 --- a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/e4/compatibility/ModeledPageLayout.java +++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/e4/compatibility/ModeledPageLayout.java @@ -74,7 +74,6 @@ public class ModeledPageLayout implements IPageLayout { public static final String PERSP_SHORTCUT_TAG = "persp.perspSC:"; //$NON-NLS-1$ public static final String SHOW_IN_PART_TAG = "persp.showIn:"; //$NON-NLS-1$ public static final String SHOW_VIEW_TAG = "persp.viewSC:"; //$NON-NLS-1$ - public static final String EDITOR_STACK_TAG = "EditorStack"; //$NON-NLS-1$ public static final String HIDDEN_MENU_PREFIX = "persp.hideMenuSC:"; //$NON-NLS-1$ public static final String HIDDEN_TOOLBAR_PREFIX = "persp.hideToolbarSC:"; //$NON-NLS-1$ public static final String HIDDEN_ACTIONSET_PREFIX = "persp.hideActionSetSC:"; //$NON-NLS-1$ diff --git a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRendererTest.java b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRendererTest.java index ab1fe15ce1f..6cc804ce8eb 100644 --- a/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRendererTest.java +++ b/tests/org.eclipse.e4.ui.tests/src/org/eclipse/e4/ui/workbench/renderers/swt/StackRendererTest.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.List; import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.ui.internal.workbench.PartStackUtil; import org.eclipse.e4.ui.internal.workbench.swt.CSSConstants; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor; @@ -411,7 +412,7 @@ public void testBug573518_SharedPartToolbarShown2() { @Test public void testOnboardingRenderedWithCorrectSizeForEditorStack() { - partStack.getTags().add("EditorStack"); + PartStackUtil.makeEditorStack(partStack); contextRule.createAndRunWorkbench(window); @@ -445,7 +446,7 @@ public void testOnboardingNotRenderedForNonEditorStack() { @Test public void testOnboardingIsFilled() { MPerspective perspective = createPerspective(); - partStack.getTags().add("EditorStack"); + PartStackUtil.makeEditorStack(partStack); contextRule.createAndRunWorkbench(window); switchToPerspective(perspective); @@ -500,13 +501,13 @@ private void assertFilledOnboardingInformation(CTabFolder tabFolder) { @Test public void testOnboardingIsFilledForEveryEditorStack() { MPerspective perspective = createPerspective(); - partStack.getTags().add("EditorStack"); + PartStackUtil.makeEditorStack(partStack); contextRule.createAndRunWorkbench(window); // Create second editor stack MPartStack secondPartStack = ems.createModelElement(MPartStack.class); - secondPartStack.getTags().add("EditorStack"); + PartStackUtil.makeEditorStack(secondPartStack); MPlaceholder placeholder = ems.createModelElement(MPlaceholder.class); placeholder.setRef(secondPartStack); perspective.getChildren().add(placeholder); @@ -520,7 +521,7 @@ public void testOnboardingIsFilledForEveryEditorStack() { @Test public void testOnboardingIsHiddenWhenEditorOpened() { - partStack.getTags().add("EditorStack"); + PartStackUtil.makeEditorStack(partStack); contextRule.createAndRunWorkbench(window); diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/dialogs/UIEditWorkingSetWizardAuto.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/dialogs/UIEditWorkingSetWizardAuto.java index 8c68177f998..dd42e31bc4c 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/dialogs/UIEditWorkingSetWizardAuto.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/dialogs/UIEditWorkingSetWizardAuto.java @@ -13,10 +13,14 @@ *******************************************************************************/ package org.eclipse.ui.tests.dialogs; -import java.util.List; +import static org.eclipse.core.resources.ResourcesPlugin.getWorkspace; +import java.util.List; +import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceDescription; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.swt.widgets.Composite; @@ -57,6 +61,29 @@ private IWorkingSetPage getDefaultEditPage() { return registry.getDefaultWorkingSetPage(); } + /** + * Blocks the calling thread until autobuild completes. + */ + private static void waitForBuild() { + ((Workspace) getWorkspace()).getBuildManager().waitForAutoBuild(); + } + + /** + * Enables or disables workspace autobuild. Waits for the build to be finished, + * even if the autobuild value did not change and a previous build is still + * running. + */ + private static void setAutoBuilding(boolean enabled) throws CoreException { + IWorkspace workspace = getWorkspace(); + if (workspace.isAutoBuilding() != enabled) { + IWorkspaceDescription description = workspace.getDescription(); + description.setAutoBuilding(enabled); + workspace.setDescription(description); + } + waitForBuild(); + } + + @Test public void testEditPage() throws Throwable { IWizardPage page = getWizardDialog().getCurrentPage(); @@ -119,4 +146,17 @@ public void testEditPage() throws Throwable { DialogCheck.assertDialogTexts(getWizardDialog()); } + + @Override + public void doSetUp() throws Exception { + super.doSetUp(); + setAutoBuilding(false); + } + + @Override + public void doTearDown() throws Exception { + super.doTearDown(); + ResourcesPlugin.getWorkspace().setDescription(Workspace.defaultWorkspaceDescription()); + } + } diff --git a/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/FindReplaceDialogTest.java b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/FindReplaceDialogTest.java index 749d17617fa..88362c87fb4 100644 --- a/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/FindReplaceDialogTest.java +++ b/tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/FindReplaceDialogTest.java @@ -98,7 +98,8 @@ private class DialogAccess { private Runnable closeOperation; - DialogAccess(Accessor findReplaceDialogAccessor) { + + DialogAccess(Accessor findReplaceDialogAccessor, boolean checkInitialConfiguration) { findReplaceLogic= (FindReplaceLogic) findReplaceDialogAccessor.get("findReplaceLogic"); findCombo= (Combo) findReplaceDialogAccessor.get("fFindField"); forwardRadioButton= (Button) findReplaceDialogAccessor.get("fForwardRadioButton"); @@ -111,7 +112,9 @@ private class DialogAccess { replaceFindButton= (Button) findReplaceDialogAccessor.get("fReplaceFindButton"); shellRetriever= () -> ((Shell) findReplaceDialogAccessor.get("fActiveShell")); closeOperation= () -> findReplaceDialogAccessor.invoke("close", null); - assertInitialConfiguration(); + if (checkInitialConfiguration) { + assertInitialConfiguration(); + } } void restoreInitialConfiguration() { @@ -148,9 +151,13 @@ private void assertInitialConfiguration() { assertFalse(regExCheckBox.getSelection()); } - void close() { + void closeAndRestore() { restoreInitialConfiguration(); assertInitialConfiguration(); + close(); + } + + void close() { closeOperation.run(); } @@ -171,7 +178,7 @@ private void openFindReplaceDialog() { Shell shell= PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); Accessor dialogAccessor= new Accessor("org.eclipse.ui.texteditor.FindReplaceDialog", getClass().getClassLoader(), new Object[] { shell }); dialogAccessor.invoke("create", null); - dialog= new DialogAccess(dialogAccessor); + dialog= new DialogAccess(dialogAccessor, true); } private void openTextViewerAndFindReplaceDialog() { @@ -190,6 +197,10 @@ private void openTextViewer(String content) { } private void openFindReplaceDialogForTextViewer() { + openFindReplaceDialogForTextViewer(true); + } + + private void openFindReplaceDialogForTextViewer(boolean checkInitialConfiguration) { Accessor fFindReplaceAction; fFindReplaceAction= new Accessor("org.eclipse.ui.texteditor.FindReplaceAction", getClass().getClassLoader(), new Class[] { ResourceBundle.class, String.class, Shell.class, IFindReplaceTarget.class }, @@ -203,13 +214,13 @@ private void openFindReplaceDialogForTextViewer() { Accessor fFindReplaceDialogStubAccessor= new Accessor(fFindReplaceDialogStub, "org.eclipse.ui.texteditor.FindReplaceAction$FindReplaceDialogStub", getClass().getClassLoader()); Accessor dialogAccessor= new Accessor(fFindReplaceDialogStubAccessor.invoke("getDialog", null), "org.eclipse.ui.texteditor.FindReplaceDialog", getClass().getClassLoader()); - dialog= new DialogAccess(dialogAccessor); + dialog= new DialogAccess(dialogAccessor, checkInitialConfiguration); } @After public void tearDown() throws Exception { if (dialog != null) { - dialog.close(); + dialog.closeAndRestore(); dialog= null; } @@ -268,10 +279,11 @@ public void testFocusNotChangedWhenEnterPressed() { simulateEnterInFindInputField(false); dialog.ensureHasFocusOnGTK(); - if (Util.isMac()) + if (Util.isMac()) { /* On the Mac, checkboxes only take focus if "Full Keyboard Access" is enabled in the System Preferences. * Let's not assume that someone pressed Ctrl+F7 on every test machine... */ return; + } assertTrue(dialog.findCombo.isFocusControl()); @@ -414,6 +426,48 @@ public void testActivateWholeWordsAndSearchForTwoWords() { assertFalse(dialog.wholeWordCheckBox.getEnabled()); } + @Test + public void testIncrementalSearchOnlyEnabledWhenAllowed() { + openTextViewer("text text text"); + openFindReplaceDialogForTextViewer(); + + dialog.incrementalCheckBox.setSelection(true); + select(dialog.regExCheckBox); + runEventQueue(); + assertTrue(dialog.incrementalCheckBox.getSelection()); + assertFalse(dialog.incrementalCheckBox.getEnabled()); + } + + /* + * Test for https://github.com/eclipse-platform/eclipse.platform.ui/pull/1805#pullrequestreview-1993772378 + */ + @Test + public void testIncrementalSearchOptionRecoveredCorrectly() { + openTextViewer("text text text"); + openFindReplaceDialogForTextViewer(); + + select(dialog.incrementalCheckBox); + assertTrue(dialog.incrementalCheckBox.getSelection()); + assertTrue(dialog.incrementalCheckBox.getEnabled()); + + dialog.close(); + openFindReplaceDialogForTextViewer(false); + + assertTrue(dialog.incrementalCheckBox.getSelection()); + assertTrue(dialog.incrementalCheckBox.getEnabled()); + + select(dialog.incrementalCheckBox); + select(dialog.regExCheckBox); + assertTrue(dialog.incrementalCheckBox.getSelection()); + assertFalse(dialog.incrementalCheckBox.getEnabled()); + + dialog.close(); + openFindReplaceDialogForTextViewer(false); + + assertTrue(dialog.incrementalCheckBox.getSelection()); + assertFalse(dialog.incrementalCheckBox.getEnabled()); + } + private static void select(Button button) { button.setSelection(true); button.notifyListeners(SWT.Selection, null);