Files
ts3j/ts3-client/swing/src/main/java/com/ts3client/ui/AwayMessagesDialog.java
ericek111 69003ced9a 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>
2026-08-14 14:36:17 +00:00

149 lines
4.8 KiB
Java

package com.ts3client.ui;
import com.ts3client.config.AwayMessages;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.AbstractTableModel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
/**
* Editor for the away-message presets: a plain list of entries that are renamed
* by double-clicking them, plus Add and Remove.
*
* <p>Backed by a one-column table because that is what gives Swing's list look
* inline editing for free. Every change is written straight through to the
* {@link AwayMessages} store.
*/
public final class AwayMessagesDialog extends JDialog {
private final AwayMessages messages;
private final Runnable onChanged;
private final Model model = new Model();
private final JTable table = new JTable(model);
public AwayMessagesDialog(Frame owner, AwayMessages messages, Runnable onChanged) {
super(owner, "Away Message Presets", true);
this.messages = messages;
this.onChanged = onChanged;
table.setTableHeader(null);
table.setShowGrid(false);
table.setFillsViewportHeight(true);
table.setRowHeight(Math.max(20, table.getRowHeight()));
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Single click selects, double click starts editing — like a renameable list.
table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
JScrollPane scroll = new JScrollPane(table);
scroll.setBorder(BorderFactory.createEmptyBorder(8, 8, 4, 8));
getContentPane().setLayout(new BorderLayout());
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);
}
private JPanel buildButtons() {
JPanel buttons = new JPanel();
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
buttons.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
addButton(buttons, "Add", this::addEntry);
addButton(buttons, "Remove", this::removeSelected);
buttons.add(Box.createHorizontalGlue());
addButton(buttons, "Close", this::closeDialog);
return buttons;
}
private void addButton(JPanel panel, String text, Runnable action) {
JButton b = new JButton(text);
b.addActionListener(e -> action.run());
panel.add(b);
panel.add(Box.createHorizontalStrut(4));
}
private void addEntry() {
stopEditing();
messages.add("New away message");
int row = messages.all().size() - 1;
model.fireTableRowsInserted(row, row);
table.setRowSelectionInterval(row, row);
table.scrollRectToVisible(table.getCellRect(row, 0, true));
persist();
table.editCellAt(row, 0);
if (table.getEditorComponent() != null) table.getEditorComponent().requestFocusInWindow();
}
private void removeSelected() {
stopEditing();
int row = table.getSelectedRow();
if (row < 0) return;
messages.remove(row);
model.fireTableRowsDeleted(row, row);
if (!messages.all().isEmpty()) {
int next = Math.min(row, messages.all().size() - 1);
table.setRowSelectionInterval(next, next);
}
persist();
}
private void closeDialog() {
stopEditing();
dispose();
}
/** Flushes a cell that is still being edited so its text is not lost. */
private void stopEditing() {
if (table.isEditing()) table.getCellEditor().stopCellEditing();
}
private void persist() {
messages.save();
if (onChanged != null) onChanged.run();
}
private final class Model extends AbstractTableModel {
@Override
public int getRowCount() {
return messages.all().size();
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public Object getValueAt(int row, int column) {
return messages.all().get(row);
}
@Override
public boolean isCellEditable(int row, int column) {
return true;
}
@Override
public void setValueAt(Object value, int row, int column) {
String text = value == null ? "" : value.toString().trim();
if (text.isEmpty()) return; // an emptied entry keeps its old text
messages.set(row, text);
persist();
}
}
}