Skip to content

Commit 817c217

Browse files
authored
Merge pull request #511 from bohdan-harniuk/entity-creator-enhance-ux-and-do-action-in-the-separate-thread
Enhanced UX with generation process, generation in the separate thread (non-blocking UI), fixed multiple action listener firing
2 parents 45a9286 + 8c00b0c commit 817c217

File tree

2 files changed

+158
-2
lines changed

2 files changed

+158
-2
lines changed

src/com/magento/idea/magento2plugin/actions/generation/dialog/NewEntityDialog.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.magento.idea.magento2plugin.actions.generation.data.dialog.NewEntityDialogData;
1818
import com.magento.idea.magento2plugin.actions.generation.data.ui.ComboBoxItemData;
1919
import com.magento.idea.magento2plugin.actions.generation.dialog.util.ClassPropertyFormatterUtil;
20+
import com.magento.idea.magento2plugin.actions.generation.dialog.util.ProcessWorker;
2021
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidation;
2122
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.RuleRegistry;
2223
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.AclResourceIdRule;
@@ -57,6 +58,7 @@
5758
import com.magento.idea.magento2plugin.util.FirstLetterToLowercaseUtil;
5859
import com.magento.idea.magento2plugin.util.magento.GetAclResourcesListUtil;
5960
import com.magento.idea.magento2plugin.util.magento.GetModuleNameByDirectoryUtil;
61+
import java.awt.Cursor;
6062
import java.awt.event.ActionEvent;
6163
import java.awt.event.KeyAdapter;
6264
import java.awt.event.KeyEvent;
@@ -184,6 +186,7 @@ public class NewEntityDialog extends AbstractDialog {
184186
private JTextPane exampleGridName;
185187
private JPanel uiComponentsPanel;
186188
private JTextField observerName;
189+
private final ProcessWorker.InProgressFlag onOkActionFired;
187190

188191
/**
189192
* Constructor.
@@ -203,7 +206,8 @@ public NewEntityDialog(final @NotNull Project project, final PsiDirectory direct
203206
setTitle(NewEntityAction.ACTION_DESCRIPTION);
204207
getRootPane().setDefaultButton(buttonOK);
205208

206-
buttonOK.addActionListener((final ActionEvent event) -> onOK());
209+
onOkActionFired = new ProcessWorker.InProgressFlag(false);
210+
buttonOK.addActionListener(this::generateNewEntityFiles);
207211
buttonCancel.addActionListener((final ActionEvent event) -> onCancel());
208212

209213
// call onCancel() when cross is clicked
@@ -294,13 +298,35 @@ private void initPropertiesTable() {
294298
entityPropertiesTableGroupWrapper.initTableGroup();
295299
}
296300

301+
/**
302+
* Generate new entity files.
303+
*
304+
* @param event ActionEvent
305+
*/
306+
@SuppressWarnings("PMD.UnusedFormalParameter")
307+
private void generateNewEntityFiles(final @NotNull ActionEvent event) {
308+
if (!onOkActionFired.isInProgress()) {
309+
buttonOK.setEnabled(false);
310+
buttonCancel.setEnabled(false);
311+
312+
new ProcessWorker(
313+
this::onOK,
314+
this::releaseDialogAfterGeneration,
315+
onOkActionFired
316+
).execute();
317+
}
318+
}
319+
297320
/**
298321
* Perform code generation using input data.
299322
*/
300323
private void onOK() {
301324
if (!validateFormFields()) {
325+
onOkActionFired.setInProgress(false);
302326
return;
303327
}
328+
setCursor(new Cursor(Cursor.WAIT_CURSOR));
329+
304330
formatProperties();
305331

306332
final NewEntityDialogData dialogData = getNewEntityDialogData();
@@ -315,8 +341,20 @@ private void onOK() {
315341
);
316342

317343
generatorPoolHandler.run();
344+
onOkActionFired.setFinished(true);
345+
}
346+
347+
/**
348+
* Release dialog buttons and hide.
349+
*/
350+
private void releaseDialogAfterGeneration() {
351+
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
352+
buttonCancel.setEnabled(true);
353+
buttonOK.setEnabled(true);
318354

319-
this.setVisible(false);
355+
if (onOkActionFired.isFinished()) {
356+
this.setVisible(false);
357+
}
320358
}
321359

322360
/**
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2plugin.actions.generation.dialog.util;
7+
8+
import com.intellij.openapi.application.ApplicationManager;
9+
import com.intellij.openapi.application.ModalityState;
10+
import javax.swing.SwingWorker;
11+
import org.jetbrains.annotations.NotNull;
12+
13+
/**
14+
* This worker is used to allow button on click action to be performed in the separate thread.
15+
* It releases UI to not be blocked.
16+
*/
17+
public final class ProcessWorker extends SwingWorker<Boolean, String> {
18+
19+
private final Runnable processExecutorService;
20+
private final Runnable processReleaserService;
21+
private final InProgressFlag inProgressFlag;
22+
23+
/**
24+
* Process worker constructor.
25+
*
26+
* @param processExecutorService Runnable
27+
* @param processReleaserService Runnable
28+
* @param inProgressFlag InProgressFlag
29+
*/
30+
public ProcessWorker(
31+
final @NotNull Runnable processExecutorService,
32+
final @NotNull Runnable processReleaserService,
33+
final InProgressFlag inProgressFlag
34+
) {
35+
super();
36+
this.processExecutorService = processExecutorService;
37+
this.processReleaserService = processReleaserService;
38+
this.inProgressFlag = inProgressFlag;
39+
}
40+
41+
@Override
42+
protected Boolean doInBackground() {
43+
if (!inProgressFlag.isInProgress()) {
44+
inProgressFlag.setInProgress(true);
45+
// Run action event process
46+
ApplicationManager.getApplication().invokeAndWait(
47+
processExecutorService,
48+
ModalityState.defaultModalityState()
49+
);
50+
// Run release dialog action process
51+
ApplicationManager.getApplication().invokeAndWait(
52+
processReleaserService,
53+
ModalityState.defaultModalityState()
54+
);
55+
}
56+
return null;
57+
}
58+
59+
/**
60+
* Inner class used only to stop actionPerformed method executing.
61+
*/
62+
public static final class InProgressFlag {
63+
64+
/**
65+
* Is action in progress flag.
66+
*/
67+
private boolean inProgress;
68+
69+
/**
70+
* Is action finished flag.
71+
*/
72+
private boolean isFinished;
73+
74+
/**
75+
* In progress flag constructor.
76+
*
77+
* @param inProgress boolean
78+
*/
79+
public InProgressFlag(final boolean inProgress) {
80+
this.inProgress = inProgress;
81+
isFinished = false;
82+
}
83+
84+
/**
85+
* Get is in progress value.
86+
*
87+
* @return boolean
88+
*/
89+
public boolean isInProgress() {
90+
return inProgress;
91+
}
92+
93+
/**
94+
* Set is in progress flag value.
95+
*
96+
* @param inProgress boolean
97+
*/
98+
public void setInProgress(final boolean inProgress) {
99+
this.inProgress = inProgress;
100+
}
101+
102+
/**
103+
* Get is progress finished flag value.
104+
*/
105+
public boolean isFinished() {
106+
return isFinished;
107+
}
108+
109+
/**
110+
* Set is progress finished flag value.
111+
*
112+
* @param finished boolean
113+
*/
114+
public void setFinished(boolean finished) {
115+
isFinished = finished;
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)