Narrow the desktop contact list
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 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,8 @@ import javax.swing.JTree;
|
|||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.TransferHandler;
|
import javax.swing.TransferHandler;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.plaf.basic.BasicTreeUI;
|
||||||
import javax.swing.tree.DefaultMutableTreeNode;
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||||
import javax.swing.tree.DefaultTreeModel;
|
import javax.swing.tree.DefaultTreeModel;
|
||||||
@@ -70,6 +72,7 @@ import java.util.regex.Pattern;
|
|||||||
final class ContactsDialog extends JDialog {
|
final class ContactsDialog extends JDialog {
|
||||||
|
|
||||||
private static final DateTimeFormatter SEEN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
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 static final Pattern CLIENT_LINK = Pattern.compile("client://[^\\]\\s]+");
|
||||||
|
|
||||||
private final ContactStore contacts;
|
private final ContactStore contacts;
|
||||||
@@ -89,6 +92,7 @@ final class ContactsDialog extends JDialog {
|
|||||||
private final JRadioButton blockedRadio = new JRadioButton("Blocked");
|
private final JRadioButton blockedRadio = new JRadioButton("Blocked");
|
||||||
private final JRadioButton friendRadio = new JRadioButton("Friend");
|
private final JRadioButton friendRadio = new JRadioButton("Friend");
|
||||||
private final JTextField phoneticField = new JTextField(18);
|
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 bothRadio = new JRadioButton(NicknameDisplay.BOTH.label());
|
||||||
private final JRadioButton onlyCustomRadio = new JRadioButton(NicknameDisplay.ONLY_CUSTOM.label());
|
private final JRadioButton onlyCustomRadio = new JRadioButton(NicknameDisplay.ONLY_CUSTOM.label());
|
||||||
private final JRadioButton onlyNicknameRadio = new JRadioButton(NicknameDisplay.ONLY_NICKNAME.label());
|
private final JRadioButton onlyNicknameRadio = new JRadioButton(NicknameDisplay.ONLY_NICKNAME.label());
|
||||||
@@ -173,6 +177,12 @@ final class ContactsDialog extends JDialog {
|
|||||||
root.add(neutral);
|
root.add(neutral);
|
||||||
tree.setRootVisible(false);
|
tree.setRootVisible(false);
|
||||||
tree.setShowsRootHandles(true);
|
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.setRowHeight(UIScale.scale(20));
|
||||||
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||||
tree.setCellRenderer(new ContactRenderer());
|
tree.setCellRenderer(new ContactRenderer());
|
||||||
@@ -332,6 +342,8 @@ final class ContactsDialog extends JDialog {
|
|||||||
c.weightx = 1;
|
c.weightx = 1;
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
row.add(phoneticField, c);
|
row.add(phoneticField, c);
|
||||||
|
c.gridy = 4;
|
||||||
|
row.add(lastSeenLabel, c);
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,10 +438,12 @@ final class ContactsDialog extends JDialog {
|
|||||||
if (c == null) {
|
if (c == null) {
|
||||||
nicknameField.setText("");
|
nicknameField.setText("");
|
||||||
phoneticField.setText("");
|
phoneticField.setText("");
|
||||||
|
lastSeenLabel.setText(" ");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nicknameField.setText(c.nickname);
|
nicknameField.setText(c.nickname);
|
||||||
phoneticField.setText(c.phoneticNickname);
|
phoneticField.setText(c.phoneticNickname);
|
||||||
|
lastSeenLabel.setText(lastSeen(c));
|
||||||
(c.type == ContactType.FRIEND ? friendRadio : c.type == ContactType.BLOCKED ? blockedRadio : neutralRadio)
|
(c.type == ContactType.FRIEND ? friendRadio : c.type == ContactType.BLOCKED ? blockedRadio : neutralRadio)
|
||||||
.setSelected(true);
|
.setSelected(true);
|
||||||
(c.nicknameDisplay == NicknameDisplay.ONLY_CUSTOM ? onlyCustomRadio
|
(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) {
|
private static void setDetailsEnabled(Component component, boolean enabled) {
|
||||||
component.setEnabled(enabled);
|
component.setEnabled(enabled);
|
||||||
if (component instanceof java.awt.Container) {
|
if (component instanceof java.awt.Container) {
|
||||||
@@ -514,6 +534,10 @@ final class ContactsDialog extends JDialog {
|
|||||||
// ---- rendering ----
|
// ---- rendering ----
|
||||||
|
|
||||||
private static final class ContactRenderer extends DefaultTreeCellRenderer {
|
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
|
@Override
|
||||||
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
|
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
|
||||||
boolean leaf, int row, boolean hasFocus) {
|
boolean leaf, int row, boolean hasFocus) {
|
||||||
@@ -522,23 +546,18 @@ final class ContactsDialog extends JDialog {
|
|||||||
if (obj instanceof Contact) {
|
if (obj instanceof Contact) {
|
||||||
Contact c = (Contact) obj;
|
Contact c = (Contact) obj;
|
||||||
setText("<html>" + esc(c.nickname) + " <span style='color:" + hex(sel ? getForeground() : Theme.chatSystem())
|
setText("<html>" + esc(c.nickname) + " <span style='color:" + hex(sel ? getForeground() : Theme.chatSystem())
|
||||||
+ "'> " + esc(lastSeen(c)) + "</span></html>");
|
+ "'> " + (c.lastSeen == null ? "Never" : SEEN_DAY.format(c.lastSeen)) + "</span></html>");
|
||||||
setIcon(Icons.clientIdle());
|
|
||||||
setFont(Theme.uiFont());
|
setFont(Theme.uiFont());
|
||||||
|
setBorder(margin);
|
||||||
} else {
|
} else {
|
||||||
setText(String.valueOf(obj));
|
setText(String.valueOf(obj));
|
||||||
setIcon(null);
|
|
||||||
setFont(Theme.uiBold());
|
setFont(Theme.uiBold());
|
||||||
|
setBorder(groupMargin);
|
||||||
}
|
}
|
||||||
|
setIcon(null);
|
||||||
return this;
|
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) {
|
private static String esc(String s) {
|
||||||
return s.replace("&", "&").replace("<", "<").replace(">", ">");
|
return s.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user