GP-628 - Dialogs - fixed issue of non-modal dialog appearing over modal

dialog; marked many input dialogs as transient
This commit is contained in:
dragonmacher 2021-01-28 15:50:51 -05:00
parent e983784753
commit a20d77a27b
16 changed files with 67 additions and 42 deletions

View file

@ -1766,41 +1766,45 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
This method seeks to accomplish 2 goals:
1) find a suitable component over which to center, and
2) ensure that the chosen component is in the parent hierarchy
*/
Component bestComponent = centeredOnComponent;
if (SwingUtilities.isDescendingFrom(parent, bestComponent)) {
return bestComponent;
if (SwingUtilities.isDescendingFrom(parent, centeredOnComponent)) {
return centeredOnComponent;
}
// by default, prefer to center over the active window
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Window activeWindow = kfm.getActiveWindow();
bestComponent = activeWindow;
if (SwingUtilities.isDescendingFrom(parent, bestComponent)) {
return bestComponent;
//
// By default, prefer to center over the active window
//
Window activeWindow = getActiveNonTransientWindow();
if (SwingUtilities.isDescendingFrom(parent, activeWindow)) {
//
// Have an active, visible, non-transient window, which may be another dialog.
// We prefer this to be the parent.
//
return activeWindow;
}
//
// The chosen component is not in the parent's hierarchy. See if there exists a
// non-transient parent window for that component.
Window newWindow = getParentWindow(parent);
//
Window newWindow = getParentWindow(centeredOnComponent);
if (newWindow != null) {
// the component is safe to use; the caller of this method will validate the component
// we return, updating the parent as needed
return bestComponent;
return centeredOnComponent;
}
// We were unable to find a suitable parent for the 'best' component. Just return the
// parent as the thing over which to center.
return parent;
}
private static Window getParentWindow(Component parent) {
/*
Note: Which window should be the parent of the dialog when the user does not specify?
Some use cases; a dialog is shown from:
1) A toolbar action
2) A component provider's code
@ -1808,7 +1812,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
4) A background thread
5) The help window
6) A modal password dialog appears over the splash screen
It seems like the parent should be the active window for 1-2.
Case 3 should probably use the window of the dialog provider.
Case 4 should probably use the main tool frame, since the user may be
@ -1816,12 +1820,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
active window, we can default to the tool's frame.
Case 5 should use the help window.
Case 6 should use the splash screen as the parent.
We have not yet solidified how we should parent. This documentation is meant to
move us towards clarity as we find Use Cases that don't make sense. (Once we
finalize our understanding, we should update the javadoc to list exactly where
the given Dialog Component will be shown.)
Use Case
A -The user presses an action on a toolbar from a window on screen 1, while the
main tool frame is on screen 2. We want the popup window to appear on screen
@ -1833,11 +1837,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
-modal - Java handles this correctly, allowing the new dialog to be used
-non-modal - Java prevents the non-modal from being editing if not parented
correctly
For now, the easiest mental model to use is to always prefer the active window so
that a dialog will appear in the user's view. If we find a case where this is
D -The user runs a script that shows an input dialog before the non-modal script
dialog is shown. If the non-modal dialog is parented to the modal input dialog,
then the script progress dialog appears on top (which we do not want) and the
progress dialog goes away when the input dialog is closed.
For now, the easiest mental model to use is to always prefer the active non-transient
window so that a dialog will appear in the user's view. If we find a case where this is
not desired, then document it here.
*/
//

View file

@ -174,6 +174,7 @@ public class OptionDialog extends DialogComponentProvider {
protected OptionDialog(String title, String message, String option1, String option2,
int messageType, Icon icon, boolean addCancel) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, null);
buildButtons(toList(option1, option2), addCancel, null);
}
@ -194,6 +195,7 @@ public class OptionDialog extends DialogComponentProvider {
protected OptionDialog(String title, String message, String option1, String option2,
int messageType, Icon icon, boolean addCancel, String defaultButtonName) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, null);
buildButtons(toList(option1, option2), addCancel, defaultButtonName);
}
@ -233,6 +235,7 @@ public class OptionDialog extends DialogComponentProvider {
protected OptionDialog(String title, String message, String option1, String option2,
String option3, int messageType, Icon icon, boolean addCancel) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, null);
buildButtons(toList(option1, option2, option3), addCancel, null);
}
@ -240,6 +243,7 @@ public class OptionDialog extends DialogComponentProvider {
OptionDialog(String title, String message, int messageType, Icon icon, boolean addCancelButton,
DialogRememberOption savedDialogChoice, List<String> options, String defaultOption) {
super(title, true, false, true, false);
setTransient(true);
buildMainPanel(message, messageType, icon, savedDialogChoice);
buildButtons(options, addCancelButton, defaultOption);
}
@ -256,6 +260,7 @@ public class OptionDialog extends DialogComponentProvider {
private void buildMainPanel(String message, int messageType, Icon icon,
DialogRememberOption rememberOptionChoice) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

View file

@ -75,6 +75,7 @@ public abstract class AbstractNumberInputDialog extends DialogComponentProvider
}
this.max = max;
setTransient(true);
addWorkPanel(buildMainPanel(prompt, showAsHex));
addOKButton();
addCancelButton();

View file

@ -53,7 +53,7 @@ public class InputDialog extends DialogComponentProvider {
* @param label value to use for the label of the text field
*/
public InputDialog(String dialogTitle, String label) {
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, true, null);
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, null);
}
/**
@ -68,7 +68,7 @@ public class InputDialog extends DialogComponentProvider {
* @param initialValue initial value to use for the text field
*/
public InputDialog(String dialogTitle, String label, String initialValue) {
this(dialogTitle, new String[] { label }, new String[] { initialValue }, true, null);
this(dialogTitle, new String[] { label }, new String[] { initialValue }, null);
}
/**
@ -85,7 +85,7 @@ public class InputDialog extends DialogComponentProvider {
*/
public InputDialog(String dialogTitle, String label, String initialValue,
InputDialogListener listener) {
this(dialogTitle, new String[] { label }, new String[] { initialValue }, true, listener);
this(dialogTitle, new String[] { label }, new String[] { initialValue }, listener);
}
/**
@ -101,7 +101,7 @@ public class InputDialog extends DialogComponentProvider {
* @param isModal whether or not the dialog is to be modal
*/
public InputDialog(String dialogTitle, String label, String initialValue, boolean isModal) {
this(dialogTitle, new String[] { label }, new String[] { initialValue }, isModal, null);
this(dialogTitle, new String[] { label }, new String[] { initialValue }, null);
}
/**
@ -116,7 +116,7 @@ public class InputDialog extends DialogComponentProvider {
* @param initialValues initial values to use for the text fields
*/
public InputDialog(String dialogTitle, String[] labels, String[] initialValues) {
this(dialogTitle, labels, initialValues, true, null);
this(dialogTitle, labels, initialValues, null);
}
/**
@ -129,12 +129,11 @@ public class InputDialog extends DialogComponentProvider {
* @param dialogTitle used as the name of the dialog's title bar
* @param labels values to use for the labels of the text fields
* @param initialValues initial values to use for the text fields
* @param isModal whether or not the dialog is to be modal
* @param listener listener that is called when the OK button is hit
*/
public InputDialog(String dialogTitle, String[] labels, String[] initialValues, boolean isModal,
public InputDialog(String dialogTitle, String[] labels, String[] initialValues,
InputDialogListener listener) {
super(dialogTitle, isModal, (listener != null) /* status */, true /* buttons */,
super(dialogTitle, true, (listener != null) /* status */, true /* buttons */,
false /* no tasks */);
this.listener = listener;
@ -152,8 +151,9 @@ public class InputDialog extends DialogComponentProvider {
// put the rest of the dialog together
inputLabels = labels;
this.initialValues = initialValues;
this.addOKButton();
this.addCancelButton();
setTransient(true);
addOKButton();
addCancelButton();
buildMainPanel();
if (initialValues != null && initialValues[0] != null && initialValues[0].length() > 0) {

View file

@ -55,10 +55,11 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
super(dialogTitle, true, false, true, false);
this.addOKButton();
this.addCancelButton();
this.setRememberSize(false);
this.setRememberLocation(false);
setTransient(true);
addOKButton();
addCancelButton();
setRememberSize(false);
setRememberLocation(false);
buildMainPanel(label, optionValues, initialValue, messageIcon);
setFocusComponent(combo);

View file

@ -43,6 +43,7 @@ public class MultiLineInputDialog extends DialogComponentProvider {
setFocusComponent(inputTextArea);
setTransient(true);
addOKButton();
addCancelButton();
}

View file

@ -148,6 +148,7 @@ public class MultiLineMessageDialog extends DialogComponentProvider {
workPanel.add(iconLabel, BorderLayout.WEST);
}
setTransient(true);
addWorkPanel(workPanel);
addOKButton();

View file

@ -36,6 +36,8 @@ public class ObjectChooserDialog<T> extends DialogComponentProvider {
this.objectClass = objectClass;
this.choosableObjects = choosableObjects;
this.methodsForColumns = methodsForColumns;
setTransient(true);
addWorkPanel(buildWorkPanel());
addOKButton();
addCancelButton();

View file

@ -55,8 +55,9 @@ public class ReadTextDialog extends DialogComponentProvider {
}
private void init(JPanel workPanelToInit) {
this.addWorkPanel(workPanelToInit);
this.addOKButton();
setTransient(true);
addWorkPanel(workPanelToInit);
addOKButton();
setRememberLocation(false);
setRememberSize(false);
}

View file

@ -52,6 +52,8 @@ public class SettingsDialog extends DialogComponentProvider {
if (help != null) {
setHelpLocation(help);
}
setTransient(true);
addWorkPanel(buildWorkPanel());
addDismissButton();

View file

@ -234,6 +234,7 @@ public class GhidraFileChooser extends DialogComponentProvider
super(TITLE, true, true, true, false);
this.parent = parent;
setTransient(true);
init(model);
loadRecentList();
loadOptions();