From 69003ced9af9497d485619b48fff6a6489db131c Mon Sep 17 00:00:00 2001 From: ericek111 Date: Fri, 14 Aug 2026 14:36:17 +0000 Subject: [PATCH] Close the client's dialogs with Escape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../com/ts3client/ui/AwayMessagesDialog.java | 1 + .../main/java/com/ts3client/ui/BanDialog.java | 21 +-------- .../com/ts3client/ui/BookmarksDialog.java | 1 + .../java/com/ts3client/ui/ConnectDialog.java | 1 + .../ts3client/ui/ConnectionInfoDialog.java | 1 + .../main/java/com/ts3client/ui/Dialogs.java | 44 +++++++++++++++++++ .../com/ts3client/ui/IdentitiesDialog.java | 1 + .../java/com/ts3client/ui/SettingsDialog.java | 14 +++--- 8 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 ts3-client/swing/src/main/java/com/ts3client/ui/Dialogs.java diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/AwayMessagesDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/AwayMessagesDialog.java index e0454c1..34104c4 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/AwayMessagesDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/AwayMessagesDialog.java @@ -51,6 +51,7 @@ public final class AwayMessagesDialog extends JDialog { getContentPane().add(scroll, BorderLayout.CENTER); getContentPane().add(buildButtons(), BorderLayout.SOUTH); + Dialogs.closeOnEscape(this, this::closeDialog); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setSize(new Dimension(360, 300)); setLocationRelativeTo(owner); diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/BanDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/BanDialog.java index 7c43fac..e58c45d 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/BanDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/BanDialog.java @@ -1,24 +1,18 @@ package com.ts3client.ui; -import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComboBox; -import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JPanel; -import javax.swing.JRootPane; import javax.swing.JSpinner; import javax.swing.JTextField; -import javax.swing.KeyStroke; import javax.swing.SpinnerNumberModel; import java.awt.BorderLayout; import java.awt.Component; 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: @@ -64,7 +58,7 @@ final class BanDialog extends JDialog { getContentPane().add(buttons(), BorderLayout.SOUTH); ReasonDialog.focusWhenShown(reasonField); - closeOnEscape(); + Dialogs.closeOnEscape(this); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pack(); setResizable(false); @@ -100,19 +94,6 @@ final class BanDialog extends JDialog { 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() { return UNIT_SECONDS[unit.getSelectedIndex()] == 0; } diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/BookmarksDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/BookmarksDialog.java index c0a3245..add7b19 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/BookmarksDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/BookmarksDialog.java @@ -88,6 +88,7 @@ public final class BookmarksDialog extends JDialog { if (!listModel.isEmpty()) list.setSelectedIndex(0); else showBookmark(null); + Dialogs.closeOnEscape(this, this::closeDialog); setDefaultCloseOperation(DISPOSE_ON_CLOSE); addWindowListener(new java.awt.event.WindowAdapter() { @Override diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectDialog.java index 6a76337..a7d2dca 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectDialog.java @@ -80,6 +80,7 @@ public final class ConnectDialog extends JDialog { getContentPane().add(form, BorderLayout.CENTER); getContentPane().add(buttons, BorderLayout.SOUTH); + Dialogs.closeOnEscape(this); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pack(); setMinimumSize(new Dimension(340, getHeight())); diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectionInfoDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectionInfoDialog.java index 5793a2f..b4d1d44 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectionInfoDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/ConnectionInfoDialog.java @@ -70,6 +70,7 @@ public final class ConnectionInfoDialog extends JDialog { content.add(buildButtons(), BorderLayout.SOUTH); setContentPane(content); + Dialogs.closeOnEscape(this); pack(); setLocationRelativeTo(owner); diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/Dialogs.java b/ts3-client/swing/src/main/java/com/ts3client/ui/Dialogs.java new file mode 100644 index 0000000..d9a5560 --- /dev/null +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/Dialogs.java @@ -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. + * + *

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(); + } + }); + } +} diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/IdentitiesDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/IdentitiesDialog.java index 7a7cae4..252b477 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/IdentitiesDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/IdentitiesDialog.java @@ -95,6 +95,7 @@ public final class IdentitiesDialog extends JDialog { reload(); if (!listModel.isEmpty()) list.setSelectedIndex(0); + Dialogs.closeOnEscape(this); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pack(); setMinimumSize(new Dimension(640, 320)); diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java index 5281383..08f8236 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java @@ -116,11 +116,7 @@ public final class SettingsDialog extends JDialog { apply(); close(); }); - cancel.addActionListener(e -> { - notificationsPanel.revert(); - iconPackPanel.revert(); - close(); - }); + cancel.addActionListener(e -> cancel()); right.add(ok); right.add(cancel); buttons.add(right, BorderLayout.EAST); @@ -129,6 +125,7 @@ public final class SettingsDialog extends JDialog { getContentPane().add(tabs, BorderLayout.CENTER); getContentPane().add(buttons, BorderLayout.SOUTH); + Dialogs.closeOnEscape(this, this::cancel); setDefaultCloseOperation(DISPOSE_ON_CLOSE); addWindowListener(new java.awt.event.WindowAdapter() { @Override @@ -576,6 +573,13 @@ public final class SettingsDialog extends JDialog { 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() { stopMeter(); dispose();