Show all of a contact's options at once on desktop
The contacts window drops its tabs for one column of short groups beside the list: contact type, display and ignore, where whisper becomes a "Deny whisper" box, since allowing is the default. The names get their labels on a line of their own, and the window is never shorter than the options, leaving the list the only thing that scrolls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import com.ts3client.contacts.NicknameDisplay;
|
|||||||
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
import com.ts3client.teamspeak.TeamSpeakSettingsDb;
|
||||||
import com.ts3client.text.TsLink;
|
import com.ts3client.text.TsLink;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.ButtonGroup;
|
import javax.swing.ButtonGroup;
|
||||||
@@ -24,7 +25,6 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JRadioButton;
|
import javax.swing.JRadioButton;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTabbedPane;
|
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JTree;
|
import javax.swing.JTree;
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
@@ -59,10 +59,10 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The contact list window, laid out like TeamSpeak's Contacts: the contacts grouped
|
* The contact list window: the contacts grouped as friends, blocked and neutral on
|
||||||
* as friends, blocked and neutral on the left, and on the right the selected one's
|
* the left, and on the right all of the selected one's settings — TeamSpeak's
|
||||||
* custom nickname and its General / Display / Ignore / Whisper pages. Every edit is
|
* General / Display / Ignore / Whisper pages, in one column rather than as tabs
|
||||||
* stored as it is made.
|
* since each holds only a few options. Every edit is stored as it is made.
|
||||||
*
|
*
|
||||||
* <p>Clients can be dragged in from the server tree — the drag carries the
|
* <p>Clients can be dragged in from the server tree — the drag carries the
|
||||||
* client's TS3 link, which names its unique id.
|
* client's TS3 link, which names its unique id.
|
||||||
@@ -98,8 +98,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
private final JCheckBox ignorePokes = new JCheckBox("Ignore Pokes");
|
private final JCheckBox ignorePokes = new JCheckBox("Ignore Pokes");
|
||||||
private final JCheckBox ignoreAvatar = new JCheckBox("Hide avatar");
|
private final JCheckBox ignoreAvatar = new JCheckBox("Hide avatar");
|
||||||
private final JCheckBox ignoreAway = new JCheckBox("Hide away message");
|
private final JCheckBox ignoreAway = new JCheckBox("Hide away message");
|
||||||
private final JRadioButton allowWhisper = new JRadioButton("Allow whisper");
|
private final JCheckBox denyWhisper = new JCheckBox("Deny whisper");
|
||||||
private final JRadioButton denyWhisper = new JRadioButton("Deny whisper");
|
|
||||||
private final JPanel details = new JPanel(new BorderLayout(0, UIScale.scale(8)));
|
private final JPanel details = new JPanel(new BorderLayout(0, UIScale.scale(8)));
|
||||||
|
|
||||||
/** Set while the fields are being filled from a contact, so that does not count as an edit. */
|
/** Set while the fields are being filled from a contact, so that does not count as an edit. */
|
||||||
@@ -122,7 +121,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
|
|
||||||
details.setBorder(new ScaledEmptyBorder(8, 4, 8, 8));
|
details.setBorder(new ScaledEmptyBorder(8, 4, 8, 8));
|
||||||
details.add(buildNicknameRow(), BorderLayout.NORTH);
|
details.add(buildNicknameRow(), BorderLayout.NORTH);
|
||||||
details.add(buildPages(), BorderLayout.CENTER);
|
details.add(buildOptions(), BorderLayout.CENTER);
|
||||||
|
|
||||||
JPanel buttons = new JPanel(new BorderLayout());
|
JPanel buttons = new JPanel(new BorderLayout());
|
||||||
buttons.setBorder(new ScaledEmptyBorder(0, 8, 8, 8));
|
buttons.setBorder(new ScaledEmptyBorder(0, 8, 8, 8));
|
||||||
@@ -161,7 +160,8 @@ final class ContactsDialog extends JDialog {
|
|||||||
Dialogs.closeOnEscape(this);
|
Dialogs.closeOnEscape(this);
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
pack();
|
pack();
|
||||||
setMinimumSize(UIScale.scale(new Dimension(620, 400)));
|
// Only the list scrolls, so the window never gets shorter than the options column.
|
||||||
|
setMinimumSize(new Dimension(UIScale.scale(620), getHeight()));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,35 +299,45 @@ final class ContactsDialog extends JDialog {
|
|||||||
|
|
||||||
// ---- details ----
|
// ---- details ----
|
||||||
|
|
||||||
|
/** The names, each label on its own line above its field. */
|
||||||
private JComponent buildNicknameRow() {
|
private JComponent buildNicknameRow() {
|
||||||
JPanel row = new JPanel(new GridBagLayout());
|
JPanel row = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
c.insets = UIScale.scale(new Insets(2, 2, 2, 2));
|
||||||
c.anchor = GridBagConstraints.WEST;
|
c.anchor = GridBagConstraints.WEST;
|
||||||
c.gridy = 0;
|
|
||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
|
c.gridy = 0;
|
||||||
|
c.gridwidth = 2;
|
||||||
row.add(new JLabel("Custom Nickname:"), c);
|
row.add(new JLabel("Custom Nickname:"), c);
|
||||||
c.gridx = 1;
|
|
||||||
|
c.gridy = 1;
|
||||||
|
c.gridwidth = 1;
|
||||||
c.weightx = 1;
|
c.weightx = 1;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
nicknameField.setToolTipText("Enter custom nickname");
|
nicknameField.setToolTipText("Enter custom nickname");
|
||||||
nicknameField.addActionListener(e -> applyNickname());
|
nicknameField.addActionListener(e -> applyNickname());
|
||||||
row.add(nicknameField, c);
|
row.add(nicknameField, c);
|
||||||
c.gridx = 2;
|
c.gridx = 1;
|
||||||
c.weightx = 0;
|
c.weightx = 0;
|
||||||
c.fill = GridBagConstraints.NONE;
|
c.fill = GridBagConstraints.NONE;
|
||||||
nicknameApply.setToolTipText("Apply nickname change");
|
nicknameApply.setToolTipText("Apply nickname change");
|
||||||
nicknameApply.addActionListener(e -> applyNickname());
|
nicknameApply.addActionListener(e -> applyNickname());
|
||||||
row.add(nicknameApply, c);
|
row.add(nicknameApply, c);
|
||||||
|
|
||||||
|
c.gridx = 0;
|
||||||
|
c.gridy = 2;
|
||||||
|
c.gridwidth = 2;
|
||||||
|
row.add(new JLabel("Phonetic Nickname:"), c);
|
||||||
|
c.gridy = 3;
|
||||||
|
c.weightx = 1;
|
||||||
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
row.add(phoneticField, c);
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JComponent buildPages() {
|
/** Every option at once, one short group under the other. */
|
||||||
JTabbedPane pages = new JTabbedPane();
|
private JComponent buildOptions() {
|
||||||
pages.addTab("General", page(
|
JComponent contact = group("Contact", radioGroup(neutralRadio, blockedRadio, friendRadio));
|
||||||
radioGroup(neutralRadio, blockedRadio, friendRadio),
|
|
||||||
Box.createVerticalStrut(UIScale.scale(8)),
|
|
||||||
labelled("Phonetic Nickname:", phoneticField)));
|
|
||||||
neutralRadio.setToolTipText("Mark as neutral");
|
neutralRadio.setToolTipText("Mark as neutral");
|
||||||
blockedRadio.setToolTipText("Mark as blocked");
|
blockedRadio.setToolTipText("Mark as blocked");
|
||||||
friendRadio.setToolTipText("Mark as friend");
|
friendRadio.setToolTipText("Mark as friend");
|
||||||
@@ -343,7 +353,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pages.addTab("Display", page(radioGroup(bothRadio, onlyCustomRadio, onlyNicknameRadio)));
|
JComponent display = group("Display", radioGroup(bothRadio, onlyCustomRadio, onlyNicknameRadio));
|
||||||
bothRadio.setToolTipText("Show custom name and nickname");
|
bothRadio.setToolTipText("Show custom name and nickname");
|
||||||
onlyCustomRadio.setToolTipText("Show only custom name");
|
onlyCustomRadio.setToolTipText("Show only custom name");
|
||||||
onlyNicknameRadio.setToolTipText("Show nickname only");
|
onlyNicknameRadio.setToolTipText("Show nickname only");
|
||||||
@@ -351,7 +361,8 @@ final class ContactsDialog extends JDialog {
|
|||||||
onlyCustomRadio.addActionListener(e -> edit(c -> c.nicknameDisplay = NicknameDisplay.ONLY_CUSTOM));
|
onlyCustomRadio.addActionListener(e -> edit(c -> c.nicknameDisplay = NicknameDisplay.ONLY_CUSTOM));
|
||||||
onlyNicknameRadio.addActionListener(e -> edit(c -> c.nicknameDisplay = NicknameDisplay.ONLY_NICKNAME));
|
onlyNicknameRadio.addActionListener(e -> edit(c -> c.nicknameDisplay = NicknameDisplay.ONLY_NICKNAME));
|
||||||
|
|
||||||
pages.addTab("Ignore", page(automute, ignorePublic, ignorePrivate, ignorePokes, ignoreAvatar, ignoreAway));
|
JComponent ignore = group("Ignore", automute, ignorePublic, ignorePrivate, ignorePokes, ignoreAvatar, ignoreAway,
|
||||||
|
denyWhisper);
|
||||||
automute.setToolTipText("Automatically mute this person");
|
automute.setToolTipText("Automatically mute this person");
|
||||||
ignorePublic.setToolTipText("Ignore messages this person sends to the server or in channels");
|
ignorePublic.setToolTipText("Ignore messages this person sends to the server or in channels");
|
||||||
ignorePrivate.setToolTipText("Ignore private chat by this person");
|
ignorePrivate.setToolTipText("Ignore private chat by this person");
|
||||||
@@ -365,26 +376,35 @@ final class ContactsDialog extends JDialog {
|
|||||||
ignoreAvatar.addActionListener(e -> edit(c -> c.ignoreAvatar = ignoreAvatar.isSelected()));
|
ignoreAvatar.addActionListener(e -> edit(c -> c.ignoreAvatar = ignoreAvatar.isSelected()));
|
||||||
ignoreAway.addActionListener(e -> edit(c -> c.ignoreAwayMessage = ignoreAway.isSelected()));
|
ignoreAway.addActionListener(e -> edit(c -> c.ignoreAwayMessage = ignoreAway.isSelected()));
|
||||||
|
|
||||||
pages.addTab("Whisper", page(radioGroup(allowWhisper, denyWhisper)));
|
denyWhisper.setToolTipText("This contact is always denied to whisper to you");
|
||||||
allowWhisper.setToolTipText("This contact is allowed to whisper to you");
|
denyWhisper.addActionListener(e -> edit(c -> c.whisperAllowed = !denyWhisper.isSelected()));
|
||||||
denyWhisper.setToolTipText("This contact is always denied to whisper to you.");
|
return column(contact, display, ignore);
|
||||||
allowWhisper.addActionListener(e -> edit(c -> c.whisperAllowed = true));
|
|
||||||
denyWhisper.addActionListener(e -> edit(c -> c.whisperAllowed = false));
|
|
||||||
return pages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JPanel page(Component... rows) {
|
private static JPanel column(JComponent... groups) {
|
||||||
JPanel p = new JPanel();
|
JPanel p = new JPanel();
|
||||||
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
|
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
|
||||||
p.setBorder(new ScaledEmptyBorder(10, 10, 10, 10));
|
for (JComponent g : groups) {
|
||||||
for (Component row : rows) {
|
g.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
if (row instanceof JComponent) ((JComponent) row).setAlignmentX(Component.LEFT_ALIGNMENT);
|
g.setMaximumSize(new Dimension(Integer.MAX_VALUE, g.getPreferredSize().height));
|
||||||
p.add(row);
|
p.add(g);
|
||||||
|
p.add(Box.createVerticalStrut(UIScale.scale(6)));
|
||||||
}
|
}
|
||||||
p.add(Box.createVerticalGlue());
|
p.add(Box.createVerticalGlue());
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static JPanel group(String title, JComponent... rows) {
|
||||||
|
JPanel p = new JPanel();
|
||||||
|
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
|
||||||
|
p.setBorder(BorderFactory.createTitledBorder(title));
|
||||||
|
for (JComponent row : rows) {
|
||||||
|
row.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
|
p.add(row);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
private static JPanel radioGroup(JRadioButton... buttons) {
|
private static JPanel radioGroup(JRadioButton... buttons) {
|
||||||
ButtonGroup group = new ButtonGroup();
|
ButtonGroup group = new ButtonGroup();
|
||||||
JPanel p = new JPanel();
|
JPanel p = new JPanel();
|
||||||
@@ -397,14 +417,6 @@ final class ContactsDialog extends JDialog {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JPanel labelled(String label, JComponent field) {
|
|
||||||
JPanel p = new JPanel(new BorderLayout(UIScale.scale(6), 0));
|
|
||||||
p.add(new JLabel(label), BorderLayout.WEST);
|
|
||||||
p.add(field, BorderLayout.CENTER);
|
|
||||||
p.setMaximumSize(new Dimension(Integer.MAX_VALUE, field.getPreferredSize().height));
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Fills the right-hand side from the selected contact, or greys it out. */
|
/** Fills the right-hand side from the selected contact, or greys it out. */
|
||||||
private void showSelected() {
|
private void showSelected() {
|
||||||
Contact c = selectedContact();
|
Contact c = selectedContact();
|
||||||
@@ -429,7 +441,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
ignorePokes.setSelected(c.ignorePokes);
|
ignorePokes.setSelected(c.ignorePokes);
|
||||||
ignoreAvatar.setSelected(c.ignoreAvatar);
|
ignoreAvatar.setSelected(c.ignoreAvatar);
|
||||||
ignoreAway.setSelected(c.ignoreAwayMessage);
|
ignoreAway.setSelected(c.ignoreAwayMessage);
|
||||||
(c.whisperAllowed ? allowWhisper : denyWhisper).setSelected(true);
|
denyWhisper.setSelected(!c.whisperAllowed);
|
||||||
} finally {
|
} finally {
|
||||||
filling = false;
|
filling = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user