Close the client's dialogs with Escape
Only the option-pane prompts closed on Escape, because Swing installs that binding on the option pane itself. Dialogs.closeOnEscape puts the same window-wide binding on a dialog's root pane, routed to whatever that dialog does when its Close or Cancel button is pressed — reverting the live-previewing settings, or flushing the bookmark being edited. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ public final class AwayMessagesDialog extends JDialog {
|
|||||||
getContentPane().add(scroll, BorderLayout.CENTER);
|
getContentPane().add(scroll, BorderLayout.CENTER);
|
||||||
getContentPane().add(buildButtons(), BorderLayout.SOUTH);
|
getContentPane().add(buildButtons(), BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
Dialogs.closeOnEscape(this, this::closeDialog);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(new Dimension(360, 300));
|
setSize(new Dimension(360, 300));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
package com.ts3client.ui;
|
package com.ts3client.ui;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JRootPane;
|
|
||||||
import javax.swing.JSpinner;
|
import javax.swing.JSpinner;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.KeyStroke;
|
|
||||||
import javax.swing.SpinnerNumberModel;
|
import javax.swing.SpinnerNumberModel;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks for a ban's reason and duration, like the official client's ban dialog:
|
* Asks for a ban's reason and duration, like the official client's ban dialog:
|
||||||
@@ -64,7 +58,7 @@ final class BanDialog extends JDialog {
|
|||||||
getContentPane().add(buttons(), BorderLayout.SOUTH);
|
getContentPane().add(buttons(), BorderLayout.SOUTH);
|
||||||
|
|
||||||
ReasonDialog.focusWhenShown(reasonField);
|
ReasonDialog.focusWhenShown(reasonField);
|
||||||
closeOnEscape();
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setResizable(false);
|
setResizable(false);
|
||||||
@@ -100,19 +94,6 @@ final class BanDialog extends JDialog {
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Escape cancels the dialog, as it does in the kick prompts. */
|
|
||||||
private void closeOnEscape() {
|
|
||||||
JRootPane root = getRootPane();
|
|
||||||
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
|
||||||
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel");
|
|
||||||
root.getActionMap().put("cancel", new AbstractAction() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPermanent() {
|
private boolean isPermanent() {
|
||||||
return UNIT_SECONDS[unit.getSelectedIndex()] == 0;
|
return UNIT_SECONDS[unit.getSelectedIndex()] == 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
if (!listModel.isEmpty()) list.setSelectedIndex(0);
|
if (!listModel.isEmpty()) list.setSelectedIndex(0);
|
||||||
else showBookmark(null);
|
else showBookmark(null);
|
||||||
|
|
||||||
|
Dialogs.closeOnEscape(this, this::closeDialog);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public final class ConnectDialog extends JDialog {
|
|||||||
getContentPane().add(form, BorderLayout.CENTER);
|
getContentPane().add(form, BorderLayout.CENTER);
|
||||||
getContentPane().add(buttons, BorderLayout.SOUTH);
|
getContentPane().add(buttons, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize(new Dimension(340, getHeight()));
|
setMinimumSize(new Dimension(340, getHeight()));
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public final class ConnectionInfoDialog extends JDialog {
|
|||||||
content.add(buildButtons(), BorderLayout.SOUTH);
|
content.add(buildButtons(), BorderLayout.SOUTH);
|
||||||
setContentPane(content);
|
setContentPane(content);
|
||||||
|
|
||||||
|
Dialogs.closeOnEscape(this);
|
||||||
pack();
|
pack();
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
|
|
||||||
|
|||||||
44
ts3-client/swing/src/main/java/com/ts3client/ui/Dialogs.java
Normal file
44
ts3-client/swing/src/main/java/com/ts3client/ui/Dialogs.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package com.ts3client.ui;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
/** Behaviour shared by the client's dialogs. */
|
||||||
|
final class Dialogs {
|
||||||
|
|
||||||
|
private Dialogs() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes Escape close a dialog, the way Swing's own option panes do.
|
||||||
|
*
|
||||||
|
* <p>The binding lives on the root pane's window-wide input map, so it fires
|
||||||
|
* wherever the focus sits — but only where nothing nearer to the focused
|
||||||
|
* component claims Escape first, which is what leaves a table's or combo
|
||||||
|
* box's own "cancel the edit" behaviour intact.
|
||||||
|
*/
|
||||||
|
static void closeOnEscape(JDialog dialog) {
|
||||||
|
closeOnEscape(dialog, dialog::dispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds Escape to the dialog's own way of closing, for dialogs that have to
|
||||||
|
* tidy up (or discard edits) on the way out.
|
||||||
|
*/
|
||||||
|
static void closeOnEscape(JDialog dialog, Runnable close) {
|
||||||
|
JRootPane root = dialog.getRootPane();
|
||||||
|
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
|
||||||
|
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "ts3-close");
|
||||||
|
root.getActionMap().put("ts3-close", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
close.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,6 +95,7 @@ public final class IdentitiesDialog extends JDialog {
|
|||||||
reload();
|
reload();
|
||||||
if (!listModel.isEmpty()) list.setSelectedIndex(0);
|
if (!listModel.isEmpty()) list.setSelectedIndex(0);
|
||||||
|
|
||||||
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize(new Dimension(640, 320));
|
setMinimumSize(new Dimension(640, 320));
|
||||||
|
|||||||
@@ -116,11 +116,7 @@ public final class SettingsDialog extends JDialog {
|
|||||||
apply();
|
apply();
|
||||||
close();
|
close();
|
||||||
});
|
});
|
||||||
cancel.addActionListener(e -> {
|
cancel.addActionListener(e -> cancel());
|
||||||
notificationsPanel.revert();
|
|
||||||
iconPackPanel.revert();
|
|
||||||
close();
|
|
||||||
});
|
|
||||||
right.add(ok);
|
right.add(ok);
|
||||||
right.add(cancel);
|
right.add(cancel);
|
||||||
buttons.add(right, BorderLayout.EAST);
|
buttons.add(right, BorderLayout.EAST);
|
||||||
@@ -129,6 +125,7 @@ public final class SettingsDialog extends JDialog {
|
|||||||
getContentPane().add(tabs, BorderLayout.CENTER);
|
getContentPane().add(tabs, BorderLayout.CENTER);
|
||||||
getContentPane().add(buttons, BorderLayout.SOUTH);
|
getContentPane().add(buttons, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
Dialogs.closeOnEscape(this, this::cancel);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -576,6 +573,13 @@ public final class SettingsDialog extends JDialog {
|
|||||||
if (onApply != null) onApply.run();
|
if (onApply != null) onApply.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Leaves without applying, putting back the settings that preview themselves live. */
|
||||||
|
private void cancel() {
|
||||||
|
notificationsPanel.revert();
|
||||||
|
iconPackPanel.revert();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
private void close() {
|
private void close() {
|
||||||
stopMeter();
|
stopMeter();
|
||||||
dispose();
|
dispose();
|
||||||
|
|||||||
Reference in New Issue
Block a user