Name the server and channel chat tabs after where we are

Both carry an icon now, as the private chats do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 06:57:38 +00:00
parent 966f3fe9b1
commit 8e3e9401d2
3 changed files with 56 additions and 6 deletions

View File

@@ -58,8 +58,11 @@ public final class ChatPanel extends JPanel {
private final JTextField input = new JTextField();
private final SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
private final Tab serverTab = new Tab(Target.SERVER, 0, "Server");
private final Tab channelTab = new Tab(Target.CHANNEL, 0, "Channel");
private static final String NO_SERVER = "Server";
private static final String NO_CHANNEL = "Channel";
private final Tab serverTab = new Tab(Target.SERVER, 0, NO_SERVER, Icons.server());
private final Tab channelTab = new Tab(Target.CHANNEL, 0, NO_CHANNEL, Icons.channelChat());
/** Private chats keyed by the peer's client id. */
private final Map<Integer, Tab> privateTabs = new LinkedHashMap<>();
/** Static-content tabs (e.g. a moved-out description), keyed by caller-chosen id. */
@@ -116,6 +119,16 @@ public final class ChatPanel extends JPanel {
});
}
/** Names the server tab after the server we are on; blank falls back to "Server". */
public void setServerName(String name) {
edt(() -> serverTab.setTitle(name == null || name.isBlank() ? NO_SERVER : name));
}
/** Names the channel tab after the channel we are in; blank falls back to "Channel". */
public void setChannelName(String name) {
edt(() -> channelTab.setTitle(name == null || name.isBlank() ? NO_CHANNEL : name));
}
public void setInputEnabled(boolean enabled) {
input.setEnabled(enabled);
}
@@ -218,7 +231,7 @@ public final class ChatPanel extends JPanel {
private Tab privateTab(int clientId, String nickname) {
Tab tab = privateTabs.get(clientId);
if (tab == null) {
tab = new Tab(Target.PRIVATE, clientId, nickname);
tab = new Tab(Target.PRIVATE, clientId, nickname, Icons.privateChat());
privateTabs.put(clientId, tab);
addTab(tab, true);
}
@@ -285,7 +298,7 @@ public final class ChatPanel extends JPanel {
final Target target;
final int clientId;
final String title;
private String title;
final JEditorPane log = HtmlStyles.pane("font-family:sans-serif; font-size:11px; margin:4px 6px;");
final JScrollPane scroll;
/** Set for a static-content tab (e.g. a moved-out description); null for a conversation. */
@@ -294,8 +307,8 @@ public final class ChatPanel extends JPanel {
private final Icon icon;
private JLabel titleLabel;
Tab(Target target, int clientId, String title) {
this(target, clientId, title, null, null, null);
Tab(Target target, int clientId, String title, Icon icon) {
this(target, clientId, title, icon, null, null);
}
/** A static-content tab: no send target, closing it runs {@code onClose}. */
@@ -369,6 +382,15 @@ public final class ChatPanel extends JPanel {
return p;
}
/** Renames the tab in place — the server and channel tabs follow where we are. */
void setTitle(String title) {
if (title.equals(this.title)) return;
this.title = title;
int index = tabs.indexOfComponent(scroll);
if (index >= 0) tabs.setTitleAt(index, title);
if (titleLabel != null) titleLabel.setText(title);
}
void setUnread(boolean unread) {
if (titleLabel == null) return;
titleLabel.setFont(unread ? Theme.uiBold() : Theme.uiFont());

View File

@@ -114,6 +114,18 @@ public final class Icons {
return subscribed ? new String[]{base + "_SUBSCRIBED", base} : new String[]{base};
}
/** The chat tab for the current channel. */
public static ImageIcon channelChat() {
ImageIcon icon = of("CHANNEL_CHAT");
return icon != null ? icon : channel(true);
}
/** The chat tab of a private conversation. */
public static ImageIcon privateChat() {
ImageIcon icon = ofAny("PLAYER_CHAT", "NEW_CHAT");
return icon != null ? icon : clientIdle();
}
// ---- client status icons ----
public static ImageIcon clientIdle() {

View File

@@ -1,5 +1,7 @@
package com.ts3client.ui;
import com.ts3client.net.ChannelNode;
import com.ts3client.net.ClientEntry;
import com.ts3client.net.ConnectionListener;
import com.ts3client.net.TeamspeakConnection;
@@ -50,6 +52,14 @@ final class ServerTabConnectionEvents implements ConnectionListener {
this.treeActions = treeActions;
}
/** Name of the channel we are in, or null while that is not known yet. */
private String currentChannelName() {
ClientEntry self = conn.getModel().getClient(conn.getSelfClientId());
if (self == null) return null;
ChannelNode ch = conn.getModel().getChannel(self.channelId);
return ch == null ? null : ch.name;
}
@Override
public void onStatus(String text) {
SwingUtilities.invokeLater(() -> {
@@ -80,6 +90,8 @@ final class ServerTabConnectionEvents implements ConnectionListener {
chatPanel.setInputEnabled(false);
chatPanel.closePrivateChats();
chatPanel.closeNoteTabs();
chatPanel.setServerName(null);
chatPanel.setChannelName(null);
chatPanel.appendSystem("Disconnected" + (reason == null || reason.isEmpty() ? "." : ": " + reason));
host.tabDisconnected(tab);
});
@@ -96,6 +108,10 @@ final class ServerTabConnectionEvents implements ConnectionListener {
listener.setTitle(name);
host.tabUpdated(tab);
}
if (conn.isConnected()) {
chatPanel.setServerName(name);
chatPanel.setChannelName(currentChannelName());
}
});
}