Skip to content

Commit 7b38726

Browse files
authored
Fixes #1351 - Add functionality to project selector to create new project (#1472)
* add button and dialog * extract project creation dialog into its own class * moved create project dialog to deploy.ui, reload project list after dialog closes * revert changes to ProjectSelector * Remove dialog/button, add link to Cloud Console * cleanup * test fix * reorganize layout, fix missing refresh icon on Oxygen, change create project link text * fix SharedImagesTest * fix message and comment * blank line before @return * tweak dialog layout * enable project list refresh button only if an account is selected * font style bit operation into helper method, changed create project link text * use ShellTestResource in FontUtilTest * remove 'bold' from FontUtil.convertFont(), some code cleanup in StandardDeployPreferencesPanel * make FontUtil.hasStyle() safer if more than on style bit is set
1 parent 40a53da commit 7b38726

14 files changed

Lines changed: 245 additions & 31 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
eclipse.preferences.version=1
2+
encoding/<project>=UTF-8
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
activeProfiles=
2+
eclipse.preferences.version=1
3+
resolveWorkspaceProjects=true
4+
version=1

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui.test/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/StandardDeployPreferencesPanelTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@
3636
import com.google.cloud.tools.eclipse.projectselector.model.GcpProject;
3737
import com.google.cloud.tools.eclipse.test.util.ui.ShellTestResource;
3838
import com.google.cloud.tools.login.Account;
39+
import java.util.ArrayDeque;
3940
import java.util.Arrays;
4041
import java.util.HashSet;
42+
import java.util.Queue;
4143
import org.eclipse.core.databinding.ValidationStatusProvider;
4244
import org.eclipse.core.resources.IProject;
4345
import org.eclipse.core.resources.ProjectScope;
@@ -186,13 +188,17 @@ public void testProjectSavedInPreferencesSelected() throws ProjectRepositoryExce
186188
StandardDeployPreferencesPanel deployPanel =
187189
new StandardDeployPreferencesPanel(parent, project, loginService, layoutChangedHandler,
188190
true, projectRepository);
189-
for (Control control : deployPanel.getChildren()) {
191+
Queue<Control> children = new ArrayDeque<>(Arrays.asList(deployPanel.getChildren()));
192+
while (!children.isEmpty()) {
193+
Control control = children.poll();
190194
if (control instanceof ProjectSelector) {
191195
ProjectSelector projectSelector = (ProjectSelector) control;
192196
IStructuredSelection selection = projectSelector.getViewer().getStructuredSelection();
193197
assertThat(selection.size(), is(1));
194198
assertThat(((GcpProject) selection.getFirstElement()).getId(), is("projectId1"));
195199
return;
200+
} else if (control instanceof Composite) {
201+
children.addAll(Arrays.asList(((Composite) control).getChildren()));
196202
}
197203
};
198204
fail("Did not find ProjectSelector widget");

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui/META-INF/MANIFEST.MF

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Import-Package: com.google.api.client.auth.oauth2;version="[1.22.0,1.23.0)",
2626
com.google.cloud.tools.eclipse.ui.util,
2727
com.google.cloud.tools.eclipse.ui.util.console,
2828
com.google.cloud.tools.eclipse.ui.util.databinding,
29+
com.google.cloud.tools.eclipse.ui.util.event,
30+
com.google.cloud.tools.eclipse.ui.util.images,
2931
com.google.cloud.tools.eclipse.usagetracker,
3032
com.google.cloud.tools.eclipse.util,
3133
com.google.cloud.tools.eclipse.util.status,

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/StandardDeployPreferencesPanel.java

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import com.google.cloud.tools.eclipse.ui.util.databinding.BucketNameValidator;
3030
import com.google.cloud.tools.eclipse.ui.util.databinding.ProjectSelectorValidator;
3131
import com.google.cloud.tools.eclipse.ui.util.databinding.ProjectVersionValidator;
32+
import com.google.cloud.tools.eclipse.ui.util.event.OpenUriSelectionListener;
33+
import com.google.cloud.tools.eclipse.ui.util.event.OpenUriSelectionListener.ErrorDialogErrorHandler;
34+
import com.google.cloud.tools.eclipse.ui.util.images.SharedImages;
3235
import com.google.cloud.tools.eclipse.util.status.StatusUtil;
3336
import com.google.common.annotations.VisibleForTesting;
3437
import com.google.common.base.Preconditions;
@@ -63,14 +66,16 @@
6366
import org.eclipse.jface.layout.GridDataFactory;
6467
import org.eclipse.jface.layout.GridLayoutFactory;
6568
import org.eclipse.swt.SWT;
66-
import org.eclipse.swt.events.ControlAdapter;
67-
import org.eclipse.swt.events.ControlEvent;
69+
import org.eclipse.swt.events.SelectionAdapter;
70+
import org.eclipse.swt.events.SelectionEvent;
6871
import org.eclipse.swt.graphics.Font;
72+
import org.eclipse.swt.graphics.Image;
6973
import org.eclipse.swt.layout.GridData;
7074
import org.eclipse.swt.layout.GridLayout;
7175
import org.eclipse.swt.widgets.Button;
7276
import org.eclipse.swt.widgets.Composite;
7377
import org.eclipse.swt.widgets.Label;
78+
import org.eclipse.swt.widgets.Link;
7479
import org.eclipse.swt.widgets.Text;
7580
import org.eclipse.ui.forms.events.ExpansionAdapter;
7681
import org.eclipse.ui.forms.events.ExpansionEvent;
@@ -81,6 +86,8 @@ public class StandardDeployPreferencesPanel extends DeployPreferencesPanel {
8186

8287
private static final String APPENGINE_VERSIONS_URL =
8388
"https://console.cloud.google.com/appengine/versions";
89+
private static final String CREATE_GCP_PROJECT_WITH_GAE_URL =
90+
"https://console.cloud.google.com/projectselector/appengine/create?lang=java";
8491

8592
private static final Logger logger = Logger.getLogger(
8693
StandardDeployPreferencesPanel.class.getName());
@@ -99,6 +106,8 @@ public class StandardDeployPreferencesPanel extends DeployPreferencesPanel {
99106

100107
private ExpandableComposite expandableComposite;
101108

109+
private Image refreshIcon;
110+
102111
@VisibleForTesting
103112
DeployPreferencesModel model;
104113
private ObservablesManager observables;
@@ -122,6 +131,8 @@ public StandardDeployPreferencesPanel(Composite parent, IProject project,
122131

123132
this.projectRepository = projectRepository;
124133

134+
refreshIcon = SharedImages.createRefreshIcon(getDisplay());
135+
125136
createCredentialSection(loginService);
126137

127138
createProjectIdSection();
@@ -316,17 +327,45 @@ private void createProjectIdSection() {
316327
Label projectIdLabel = new Label(this, SWT.LEAD);
317328
projectIdLabel.setText(Messages.getString("project"));
318329
projectIdLabel.setToolTipText(Messages.getString("tooltip.project.id"));
319-
GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).applyTo(projectIdLabel);
320-
projectSelector = new ProjectSelector(this);
321-
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
322-
.grab(true, false).hint(400, 150).applyTo(projectSelector);
323-
accountSelector.addSelectionListener(new Runnable() {
330+
GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING).span(1, 3)
331+
.applyTo(projectIdLabel);
332+
333+
Label select = new Label(this, SWT.WRAP);
334+
select.setText(Messages.getString("projectselector.selectProject"));
335+
GridDataFactory.swtDefaults().align(SWT.FILL, SWT.BEGINNING)
336+
.applyTo(select);
337+
338+
Link createNewProject = new Link(this, SWT.NONE);
339+
createNewProject.setText(Messages.getString("projectselector.createproject",
340+
CREATE_GCP_PROJECT_WITH_GAE_URL));
341+
createNewProject.setToolTipText(Messages.getString("projectselector.createproject.tooltip"));
342+
FontUtil.convertFontToItalic(createNewProject);
343+
createNewProject.addSelectionListener(
344+
new OpenUriSelectionListener(new ErrorDialogErrorHandler(getShell())));
345+
GridDataFactory.swtDefaults().align(SWT.FILL, SWT.BEGINNING)
346+
.applyTo(createNewProject);
347+
348+
Composite projectSelectorComposite = new Composite(this, SWT.NONE);
349+
GridLayoutFactory.fillDefaults().numColumns(2).spacing(0, 0).applyTo(projectSelectorComposite);
350+
GridDataFactory.fillDefaults().grab(true, false).applyTo(projectSelectorComposite);
351+
352+
projectSelector = new ProjectSelector(projectSelectorComposite);
353+
GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, 200)
354+
.applyTo(projectSelector);
355+
356+
final Button refreshProjectsButton = new Button(projectSelectorComposite, SWT.NONE);
357+
refreshProjectsButton.setImage(refreshIcon);
358+
GridDataFactory.swtDefaults().align(SWT.END, SWT.BEGINNING).applyTo(refreshProjectsButton);
359+
refreshProjectsButton.addSelectionListener(new SelectionAdapter() {
324360
@Override
325-
public void run() {
326-
Credential selectedCredential = accountSelector.getSelectedCredential();
327-
projectSelector.setProjects(retrieveProjects(selectedCredential));
361+
public void widgetSelected(SelectionEvent event) {
362+
refreshProjectsForSelectedCredential();
328363
}
329364
});
365+
366+
accountSelector.addSelectionListener(
367+
new RefreshProjectOnAccountSelection(refreshProjectsButton));
368+
330369
projectSelector.addSelectionChangedListener(
331370
new ProjectSelectorSelectionChangedListener(accountSelector,
332371
projectRepository,
@@ -371,7 +410,7 @@ private void createAdvancedSection() {
371410
expandableComposite.setClient(bucketComposite);
372411
expandableComposite.addExpansionListener(new ExpansionAdapter() {
373412
@Override
374-
public void expansionStateChanged(ExpansionEvent e) {
413+
public void expansionStateChanged(ExpansionEvent event) {
375414
handleExpansionStateChanged();
376415
}
377416
});
@@ -407,6 +446,11 @@ private Composite createBucketSection(Composite parent) {
407446
return bucketComposite;
408447
}
409448

449+
private void refreshProjectsForSelectedCredential() {
450+
Credential selectedCredential = accountSelector.getSelectedCredential();
451+
projectSelector.setProjects(retrieveProjects(selectedCredential));
452+
}
453+
410454
private List<GcpProject> retrieveProjects(Credential selectedCredential) {
411455
try {
412456
if (selectedCredential == null) {
@@ -425,6 +469,21 @@ private List<GcpProject> retrieveProjects(Credential selectedCredential) {
425469
}
426470
}
427471

472+
public final class RefreshProjectOnAccountSelection implements Runnable {
473+
474+
private final Button refreshProjectsButton;
475+
476+
public RefreshProjectOnAccountSelection(Button refreshProjectsButton) {
477+
this.refreshProjectsButton = refreshProjectsButton;
478+
}
479+
480+
@Override
481+
public void run() {
482+
refreshProjectsForSelectedCredential();
483+
refreshProjectsButton.setEnabled(accountSelector.getSelectedCredential() != null);
484+
}
485+
}
486+
428487
private final class ProjectIdToGcpProjectConverter extends Converter {
429488

430489
private ProjectIdToGcpProjectConverter() {
@@ -441,7 +500,7 @@ public Object convert(Object fromObject) {
441500
try {
442501
return projectRepository.getProject(accountSelector.getSelectedCredential(),
443502
(String) fromObject);
444-
} catch (ProjectRepositoryException e) {
503+
} catch (ProjectRepositoryException ex) {
445504
return null;
446505
}
447506
}
@@ -528,6 +587,9 @@ public void dispose() {
528587
if (observables != null) {
529588
observables.dispose();
530589
}
590+
if (refreshIcon != null) {
591+
refreshIcon.dispose();
592+
}
531593
super.dispose();
532594
}
533595

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/messages.properties

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ tooltip.stop.previous.version=If checked, stops the previously running version w
4040
new version that receives all traffic.
4141
tooltip.staging.bucket=The Google Cloud Storage bucket used to stage files for deployment. \
4242
If not specified, the application''s default code bucket is used.
43+
44+
#Project selector
45+
projectselector.selectProject=Select a Google Cloud Platform project
46+
projectselector.createproject=Create a project in the <a href="{0}">Cloud Console</a>. Then refresh this list.
47+
projectselector.createproject.tooltip=Opens the Cloud Console in a browser. You will need to refresh the project list \
48+
after the project is created.
4349
projectselector.retrieveproject.error.title=Failed to retrieve projects
4450
projectselector.retrieveproject.error.message=An error occurred while retrieving projects: {0}
4551
projectselector.retrieveapplication.error.title=Failed to retrieve App Engine application

plugins/com.google.cloud.tools.eclipse.projectselector/META-INF/MANIFEST.MF

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,9 @@ Import-Package: com.google.api.client.auth.oauth2;version="[1.22.0,1.23.0)",
4040
org.eclipse.core.databinding.property.value,
4141
org.eclipse.core.runtime;bundle-symbolic-name="org.eclipse.core.runtime",
4242
org.eclipse.jface.databinding.viewers,
43-
org.eclipse.jface.dialogs,
4443
org.eclipse.jface.layout,
4544
org.eclipse.jface.viewers,
4645
org.eclipse.swt,
4746
org.eclipse.swt.events,
4847
org.eclipse.swt.layout,
49-
org.eclipse.swt.widgets,
50-
org.eclipse.ui,
51-
org.eclipse.ui.browser;ui.workbench=split
48+
org.eclipse.swt.widgets

plugins/com.google.cloud.tools.eclipse.projectselector/src/com/google/cloud/tools/eclipse/projectselector/ProjectSelector.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.eclipse.core.databinding.observable.list.WritableList;
2626
import org.eclipse.jface.databinding.viewers.ViewerSupport;
2727
import org.eclipse.jface.layout.GridDataFactory;
28+
import org.eclipse.jface.layout.GridLayoutFactory;
2829
import org.eclipse.jface.layout.TableColumnLayout;
2930
import org.eclipse.jface.viewers.ColumnWeightData;
3031
import org.eclipse.jface.viewers.ISelection;
@@ -35,7 +36,6 @@
3536
import org.eclipse.jface.viewers.ViewerComparator;
3637
import org.eclipse.swt.SWT;
3738
import org.eclipse.swt.layout.GridData;
38-
import org.eclipse.swt.layout.GridLayout;
3939
import org.eclipse.swt.widgets.Composite;
4040
import org.eclipse.swt.widgets.Link;
4141

@@ -47,14 +47,14 @@ public class ProjectSelector extends Composite {
4747

4848
public ProjectSelector(Composite parent) {
4949
super(parent, SWT.NONE);
50-
setLayout(new GridLayout());
50+
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(this);
5151

5252
Composite tableComposite = new Composite(this, SWT.NONE);
5353
TableColumnLayout tableColumnLayout = new TableColumnLayout();
5454
tableComposite.setLayout(tableColumnLayout);
5555
GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);
5656

57-
tableViewer = new TableViewer(tableComposite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
57+
tableViewer = new TableViewer(tableComposite, SWT.SINGLE | SWT.BORDER);
5858
createColumns(tableColumnLayout);
5959
tableViewer.getTable().setHeaderVisible(true);
6060
input = WritableList.withElementType(GcpProject.class);
@@ -68,7 +68,7 @@ public ProjectSelector(Composite parent) {
6868
statusLink.addSelectionListener(
6969
new OpenUriSelectionListener(new ErrorDialogErrorHandler(getShell())));
7070
statusLink.setText("");
71-
GridDataFactory.fillDefaults().applyTo(statusLink);
71+
GridDataFactory.fillDefaults().span(2, 1).applyTo(statusLink);
7272
}
7373

7474
private void createColumns(TableColumnLayout tableColumnLayout) {
@@ -89,6 +89,7 @@ public TableViewer getViewer() {
8989
public void setProjects(List<GcpProject> projects) {
9090
ISelection selection = tableViewer.getSelection();
9191
input.clear();
92+
clearStatusLink(); // otherwise revealing selection is off sometimes
9293
if (projects != null) {
9394
input.addAll(projects);
9495
}

plugins/com.google.cloud.tools.eclipse.ui.util.test/META-INF/MANIFEST.MF

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Require-Bundle: com.google.guava;bundle-version="[20.0.0,21.0.0)",
1111
org.hamcrest;bundle-version="1.1.0",
1212
org.junit;bundle-version="4.12.0"
1313
Import-Package: com.google.cloud.tools.eclipse.test.util,
14+
com.google.cloud.tools.eclipse.test.util.ui,
1415
org.mockito;provider=google;version="1.10.19",
1516
org.mockito.runners;provider=google;version="1.10.19",
1617
org.mockito.stubbing;provider=google;version="1.10.19",

plugins/com.google.cloud.tools.eclipse.ui.util.test/src/com/google/cloud/tools/eclipse/ui/util/FontUtilTest.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,20 @@
2020
import static org.hamcrest.CoreMatchers.not;
2121
import static org.junit.Assert.assertThat;
2222

23+
import com.google.cloud.tools.eclipse.test.util.ui.ShellTestResource;
2324
import org.eclipse.swt.SWT;
2425
import org.eclipse.swt.graphics.FontData;
2526
import org.eclipse.swt.widgets.Label;
26-
import org.eclipse.swt.widgets.Shell;
27+
import org.junit.Rule;
2728
import org.junit.Test;
2829

2930
public class FontUtilTest {
3031

32+
@Rule public ShellTestResource shellTestResource = new ShellTestResource();
33+
3134
@Test
3235
public void testConvertFontToBold() {
33-
Shell shell = new Shell();
34-
Label label = new Label(shell, SWT.NONE);
36+
Label label = new Label(shellTestResource.getShell(), SWT.NONE);
3537
for (FontData fontData : label.getFont().getFontData()) {
3638
assertThat(fontData.getStyle(), is(not(SWT.BOLD)));
3739
}
@@ -41,4 +43,15 @@ public void testConvertFontToBold() {
4143
}
4244
}
4345

46+
@Test
47+
public void testConvertFontToItalic() {
48+
Label label = new Label(shellTestResource.getShell(), SWT.NONE);
49+
for (FontData fontData : label.getFont().getFontData()) {
50+
assertThat(fontData.getStyle(), is(not(SWT.ITALIC)));
51+
}
52+
FontUtil.convertFontToItalic(label);
53+
for (FontData fontData : label.getFont().getFontData()) {
54+
assertThat(fontData.getStyle(), is(SWT.ITALIC));
55+
}
56+
}
4457
}

0 commit comments

Comments
 (0)