From 75df46a03a4ab6ddabd48534075a64d64d1b8b9f Mon Sep 17 00:00:00 2001 From: ericek111 Date: Sat, 26 Sep 2026 11:39:15 +0000 Subject: [PATCH] Narrow the desktop contact list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each contact shows just the date it was last seen, without the client dot and at half the usual nesting; the full time and server move under the phonetic nickname as "Last seen … on …". Co-Authored-By: Claude Opus 5.5 --- .../java/com/ts3client/ui/ContactsDialog.java | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/ContactsDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/ContactsDialog.java index 0b9ff02..3a36f9e 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/ContactsDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/ContactsDialog.java @@ -30,6 +30,8 @@ import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; +import javax.swing.border.Border; +import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; @@ -70,6 +72,7 @@ import java.util.regex.Pattern; final class ContactsDialog extends JDialog { private static final DateTimeFormatter SEEN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + private static final DateTimeFormatter SEEN_DAY = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final Pattern CLIENT_LINK = Pattern.compile("client://[^\\]\\s]+"); private final ContactStore contacts; @@ -89,6 +92,7 @@ final class ContactsDialog extends JDialog { private final JRadioButton blockedRadio = new JRadioButton("Blocked"); private final JRadioButton friendRadio = new JRadioButton("Friend"); private final JTextField phoneticField = new JTextField(18); + private final JLabel lastSeenLabel = new JLabel(" "); private final JRadioButton bothRadio = new JRadioButton(NicknameDisplay.BOTH.label()); private final JRadioButton onlyCustomRadio = new JRadioButton(NicknameDisplay.ONLY_CUSTOM.label()); private final JRadioButton onlyNicknameRadio = new JRadioButton(NicknameDisplay.ONLY_NICKNAME.label()); @@ -173,6 +177,12 @@ final class ContactsDialog extends JDialog { root.add(neutral); tree.setRootVisible(false); tree.setShowsRootHandles(true); + // Half the usual nesting, so long names and their dates fit in the narrow list. + if (tree.getUI() instanceof BasicTreeUI) { + BasicTreeUI ui = (BasicTreeUI) tree.getUI(); + ui.setLeftChildIndent(UIScale.scale(4)); + ui.setRightChildIndent(UIScale.scale(5)); + } tree.setRowHeight(UIScale.scale(20)); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); tree.setCellRenderer(new ContactRenderer()); @@ -332,6 +342,8 @@ final class ContactsDialog extends JDialog { c.weightx = 1; c.fill = GridBagConstraints.HORIZONTAL; row.add(phoneticField, c); + c.gridy = 4; + row.add(lastSeenLabel, c); return row; } @@ -426,10 +438,12 @@ final class ContactsDialog extends JDialog { if (c == null) { nicknameField.setText(""); phoneticField.setText(""); + lastSeenLabel.setText(" "); return; } nicknameField.setText(c.nickname); phoneticField.setText(c.phoneticNickname); + lastSeenLabel.setText(lastSeen(c)); (c.type == ContactType.FRIEND ? friendRadio : c.type == ContactType.BLOCKED ? blockedRadio : neutralRadio) .setSelected(true); (c.nicknameDisplay == NicknameDisplay.ONLY_CUSTOM ? onlyCustomRadio @@ -447,6 +461,12 @@ final class ContactsDialog extends JDialog { } } + private static String lastSeen(Contact c) { + if (c.lastSeen == null) return "Never seen"; + String where = c.lastSeenServerName.isEmpty() ? c.lastSeenServerAddress : c.lastSeenServerName; + return "Last seen " + SEEN.format(c.lastSeen) + (where.isEmpty() ? "" : " on " + where); + } + private static void setDetailsEnabled(Component component, boolean enabled) { component.setEnabled(enabled); if (component instanceof java.awt.Container) { @@ -514,6 +534,10 @@ final class ContactsDialog extends JDialog { // ---- rendering ---- private static final class ContactRenderer extends DefaultTreeCellRenderer { + private final Border margin = getBorder(); + /** Keeps a group's name clear of its expand arrow, which the tight nesting brings close. */ + private final Border groupMargin = BorderFactory.createCompoundBorder(margin, new ScaledEmptyBorder(0, 4, 0, 0)); + @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { @@ -522,23 +546,18 @@ final class ContactsDialog extends JDialog { if (obj instanceof Contact) { Contact c = (Contact) obj; setText("" + esc(c.nickname) + "  " + esc(lastSeen(c)) + ""); - setIcon(Icons.clientIdle()); + + "'> " + (c.lastSeen == null ? "Never" : SEEN_DAY.format(c.lastSeen)) + ""); setFont(Theme.uiFont()); + setBorder(margin); } else { setText(String.valueOf(obj)); - setIcon(null); setFont(Theme.uiBold()); + setBorder(groupMargin); } + setIcon(null); return this; } - private static String lastSeen(Contact c) { - if (c.lastSeen == null) return "Never"; - String where = c.lastSeenServerName.isEmpty() ? c.lastSeenServerAddress : c.lastSeenServerName; - return where.isEmpty() ? SEEN.format(c.lastSeen) : SEEN.format(c.lastSeen) + " on " + where; - } - private static String esc(String s) { return s.replace("&", "&").replace("<", "<").replace(">", ">"); }