diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/livexp/ui/DynamicSection.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/livexp/ui/DynamicSection.java index 2f9142f20..3947d504e 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/livexp/ui/DynamicSection.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/livexp/ui/DynamicSection.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017 Pivotal, Inc. + * Copyright (c) 2017, 2023 Pivotal, Inc. * 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 @@ -40,6 +40,8 @@ public class DynamicSection extends ReflowableSection { private Integer widthHint = DEFAULT_MIN_SIZE.y; private Integer heightHint = DEFAULT_MIN_SIZE.x; + private boolean isFocused; + public DynamicSection(IPageWithSections owner, LiveExpression content) { super(owner); this.content = content; @@ -90,6 +92,9 @@ public class DynamicSection extends ReflowableSection { } else { validator.setDelegate(null); } + if (isFocused) { + newContents.setFocus(); + } } @Override @@ -104,4 +109,12 @@ public class DynamicSection extends ReflowableSection { super.dispose(); } + @Override + public void setFocus() { + isFocused = true; + if (content.getValue() != null) { + content.getValue().setFocus(); + } + } + } diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/MultipleViewsDependencyPage.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/MultipleViewsDependencyPage.java index 22557c6f3..d619d941a 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/MultipleViewsDependencyPage.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/MultipleViewsDependencyPage.java @@ -15,7 +15,6 @@ import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; -import org.eclipse.ui.PlatformUI; import org.springframework.ide.eclipse.boot.core.initializr.InitializrServiceSpec.Dependency; import org.springframework.ide.eclipse.boot.livexp.ui.DynamicSection; import org.springframework.ide.eclipse.boot.wizard.CheckBoxesSection.CheckBoxModel; @@ -74,12 +73,12 @@ public class MultipleViewsDependencyPage extends WizardPageWithSections { sections.add(createFrequentlyUsedSection(model)); sections.add(createTwoColumnSection(model)); - return new GroupSection(this, null, sections.toArray(new WizardPageSection[0])).grabVertical(true); + return new GroupSection(this, null, 2, sections.toArray(new WizardPageSection[0])).grabVertical(true); } public WizardPageSection createTwoColumnSection(final NewSpringBootWizardModel model) { - return new GroupSection(this,null, - new GroupSection(this, null, + return new GroupSection(this, null, 0, + new GroupSection(this, null, 1, new CommentSection(this, "Available:"), getSearchSection(model), new GroupSection(this, "", @@ -113,14 +112,12 @@ public class MultipleViewsDependencyPage extends WizardPageWithSections { } protected WizardPageSection getSearchSection(final NewSpringBootWizardModel model) { - final SearchBoxSection searchBoxSection = new SearchBoxSection(this, model.getDependencyFilterBoxText()) { + return new SearchBoxSection(this, model.getDependencyFilterBoxText()) { @Override protected String getSearchHint() { return "Type to search dependencies"; } }; - PlatformUI.getWorkbench().getDisplay().asyncExec(() -> getControl().addListener(SWT.Show, event -> searchBoxSection.focusControl())); - return new GroupSection(this, null, searchBoxSection.grabFocus(true)); } protected WizardPageSection createFrequentlyUsedSection(NewSpringBootWizardModel model) { @@ -134,4 +131,12 @@ public class MultipleViewsDependencyPage extends WizardPageWithSections { return frequentlyUsedSection; } + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) { + getSections().get(0).setFocus(); + } + } + } diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/NewSpringBootWizard.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/NewSpringBootWizard.java index f3fb562ca..3b734ca13 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/NewSpringBootWizard.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/NewSpringBootWizard.java @@ -101,6 +101,7 @@ public class NewSpringBootWizard extends Wizard implements INewWizard, IImportWi super(owner, null); this.model = model; addSections(createSections().toArray(new WizardPageSection[0])); + focusSectionIndex = 0; } protected List createSections() { @@ -174,10 +175,19 @@ public class NewSpringBootWizard extends Wizard implements INewWizard, IImportWi return new ProjectDetailsSection(this, dynamicModel); } return new CommentSection(this, NO_CONTENT_AVAILABLE); - } )); + })); return ImmutableList.of(comboSection, dynamicSection); } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) { + getSections().get(1).setFocus(); + } + } + } diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/SearchBoxSection.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/SearchBoxSection.java index 06f23dc88..e9532eb35 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/SearchBoxSection.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/SearchBoxSection.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015, 2020 Pivotal Software, Inc. + * Copyright (c) 2015, 2023 Pivotal Software, Inc. * 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 @@ -14,8 +14,6 @@ import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Text; @@ -39,7 +37,6 @@ public class SearchBoxSection extends WizardPageSection implements Disposable { private Text searchBox; private LiveVariable model; private ValueListener modelListener; - private boolean grabFocus; public SearchBoxSection(IPageWithSections owner, LiveVariable model) { super(owner); @@ -76,9 +73,6 @@ public class SearchBoxSection extends WizardPageSection implements Disposable { } } }); - if (grabFocus) { - focusControl(); - } // IContentProposalProvider proposalProvider = new TagContentProposalProvider(viewModel); // ContentProposalAdapter caAdapter = new ContentProposalAdapter(searchBox, new TextContentAdapter(), proposalProvider, UIUtils.CTRL_SPACE, null); // caAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); @@ -97,31 +91,15 @@ public class SearchBoxSection extends WizardPageSection implements Disposable { searchBox.dispose(); } - /** - * When the control for this section is created, it will attempt to immediately grab - * keyboard focus. - */ - public SearchBoxSection grabFocus(boolean grab) { - this.grabFocus = grab; - return this; - } - - public void focusControl() { - if (searchBox != null) { - if (!searchBox.setFocus()) { - searchBox.addPaintListener(new PaintListener() { - @Override - public void paintControl(PaintEvent e) { - if (searchBox.setFocus()) { - searchBox.removePaintListener(this); - } - } - }); - } - } - } - protected Control getControl() { return searchBox; } + + @Override + public void setFocus() { + if (searchBox != null && !searchBox.isDisposed()) { + searchBox.setFocus(); + } + } + } diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/ChooseTypedContentSection.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/ChooseTypedContentSection.java index 57f8bed31..55a556e9d 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/ChooseTypedContentSection.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/ChooseTypedContentSection.java @@ -288,8 +288,6 @@ public class ChooseTypedContentSection extends WizardPageSection { } }; - // PT 164654725 - IMPORTANT: set grab focus BEFORE creating content - searchBox.grabFocus(true); searchBox.createContents(field); Label fieldNameLabel = null; @@ -431,6 +429,14 @@ public class ChooseTypedContentSection extends WizardPageSection { } + @Override + public void setFocus() { + if (searchBox != null) { + searchBox.setFocus(); + } + } + + // private String[] getLabels() { // String[] labels = new String[options.length]; // for (int i = 0; i < labels.length; i++) { @@ -439,4 +445,6 @@ public class ChooseTypedContentSection extends WizardPageSection { // return labels; // } + + } diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/GSImportWizard.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/GSImportWizard.java index 073f6823e..5ff0b8e22 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/GSImportWizard.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/guides/GSImportWizard.java @@ -127,6 +127,7 @@ public class GSImportWizard extends Wizard implements IImportWizard, INewWizard // } // } + @Override public void init(IWorkbench workbench, IStructuredSelection selection) { // this.workbench = workbench; // super.init(workbench, selection); diff --git a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/starters/DependencyPage.java b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/starters/DependencyPage.java index 930e3dc01..c40ce4dc2 100644 --- a/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/starters/DependencyPage.java +++ b/eclipse-extensions/org.springframework.ide.eclipse.boot.wizard/src/org/springframework/ide/eclipse/boot/wizard/starters/DependencyPage.java @@ -97,9 +97,10 @@ public class DependencyPage extends WizardPageWithSections { createDynamicSections(model, sections); } - GroupSection groupSection = new GroupSection(DependencyPage.this, null, + GroupSection groupSection = new GroupSection(DependencyPage.this, null, 1, sections.toArray(new WizardPageSection[0])); groupSection.grabVertical(true); + groupSection.setFocus(); return groupSection; } }; @@ -193,6 +194,12 @@ public class DependencyPage extends WizardPageWithSections { String message = ExceptionUtil.getMessage(e); setErrorMessage(message); Log.log(e); + } finally { + // After loading of starters data is completed set the focus to the search box. + // It is highly unlikely that use had the time to switch focus to anything else. + if (dynamicControlCreation.getValue() != null) { + dynamicControlCreation.getValue().setFocus(); + } } }); } @@ -204,8 +211,8 @@ public class DependencyPage extends WizardPageWithSections { @SuppressWarnings("resource") public WizardPageSection createTwoColumnSection(final InitializrModel model) { - return new GroupSection(this,null, - new GroupSection(this, null, + return new GroupSection(this, null, 0, + new GroupSection(this, null, 1, new CommentSection(this, "Available:"), getSearchSection(model), new GroupSection(this, "", @@ -239,39 +246,13 @@ public class DependencyPage extends WizardPageWithSections { } protected WizardPageSection getSearchSection(final InitializrModel model) { - final SearchBoxSection searchBoxSection = new SearchBoxSection(this, model.searchBox.getText()) { + return new SearchBoxSection(this, model.searchBox.getText()) { @Override protected String getSearchHint() { return "Type to search dependencies"; } - @Override - public void focusControl() { - Control searchControl = getControl(); - if (searchControl != null && !searchControl.isDisposed()) { - searchControl.setFocus(); - } - } - }.grabFocus(true); - - - // PT 174313406 - Async focus on search box. Dynamic creation seems to interfere with grabbing focus on the search box upon control creation - // therefore set it asynchronously - asyncSetFocus(searchBoxSection); - return searchBoxSection; - } - - private void asyncSetFocus(SearchBoxSection searchBoxSection) { - UIJob showJob = new UIJob("Focus search box") { - - @Override - public IStatus runInUIThread(IProgressMonitor monitor) { - searchBoxSection.focusControl(); - return Status.OK_STATUS; - } }; - showJob.setSystem(true); - showJob.schedule(1000); } @SuppressWarnings("resource") @@ -326,6 +307,17 @@ public class DependencyPage extends WizardPageWithSections { } } + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) { + // Restore focus to search box when going back a page. There is no control that has the focus going back hence set it. + if (dynamicControlCreation.getValue() != null) { + dynamicControlCreation.getValue().setFocus(); + } + } + } + private static class ErrorGroupSection { private static final String NO_CONTENT = "No content available"; diff --git a/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/GroupSection.java b/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/GroupSection.java index 3a16008f2..fdd69da6d 100644 --- a/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/GroupSection.java +++ b/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/GroupSection.java @@ -12,7 +12,6 @@ package org.springsource.ide.eclipse.commons.livexp.ui; import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.layout.GridDataFactory; @@ -23,7 +22,6 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; -import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Shell; import org.springsource.ide.eclipse.commons.livexp.core.CompositeValidator; import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; @@ -56,6 +54,8 @@ public class GroupSection extends WizardPageSection { private boolean noMargins; + protected int focusSectionIndex = 0; + /** * If title is null then it creates a normal composite without a box around it. Otherwise * it creates a 'group' and uses the title as label for the group. @@ -67,6 +67,14 @@ public class GroupSection extends WizardPageSection { addSections(_sections); } + public GroupSection(IPageWithSections owner, String title, int focusSectionIndex, WizardPageSection... _sections) { + super(owner); + this.groupTitle = title; + this.sections = new ArrayList<>(); + addSections(_sections); + this.focusSectionIndex = focusSectionIndex; + } + public void addSections(WizardPageSection... _sections) { Assert.isLegal(!contentsCreated); for (WizardPageSection s : _sections) { @@ -179,5 +187,12 @@ public class GroupSection extends WizardPageSection { this.background = c; return this; } + + @Override + public void setFocus() { + if (focusSectionIndex >= 0 && focusSectionIndex < sections.size()) { + sections.get(focusSectionIndex).setFocus(); + } + } } diff --git a/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/IPageSection.java b/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/IPageSection.java index 6a130b809..7e875cea7 100644 --- a/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/IPageSection.java +++ b/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/IPageSection.java @@ -21,5 +21,7 @@ public interface IPageSection { public void createContents(Composite page); public LiveExpression getValidator(); + default void setFocus() {}; + } diff --git a/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/StringFieldSection.java b/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/StringFieldSection.java index 1a7e0b049..e237ff5e8 100644 --- a/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/StringFieldSection.java +++ b/eclipse-language-servers/org.springsource.ide.eclipse.commons.livexp/src/org/springsource/ide/eclipse/commons/livexp/ui/StringFieldSection.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2021 VMware Software, Inc. + * Copyright (c) 2013, 2023 VMware Software, Inc. * 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 @@ -164,4 +164,11 @@ public class StringFieldSection extends WizardPageSection { return this; } + @Override + public void setFocus() { + if (text != null && !text.isDisposed()) { + text.setFocus(); + } + } + }