Split ServerTab into focused collaborator classes

Extract mic/speaker/away/commander state handling into
ServerTabSelfState, ServerTreePanel.Actions delegation (context
menus, drag-drop, link clicks) into ServerTabTreeActions, and
ConnectionListener event marshaling onto the EDT into
ServerTabConnectionEvents. ServerTab keeps its full public API
(636 -> 377 lines); no behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 13:37:58 +00:00
parent d722377508
commit e5b607fc29
4 changed files with 524 additions and 315 deletions

View File

@@ -0,0 +1,160 @@
package com.ts3client.ui;
import com.ts3client.net.ConnectionListener;
import com.ts3client.net.TeamspeakConnection;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
/**
* Marshals {@link ConnectionListener} callbacks onto the EDT and fans them out to
* this tab's views (tree, chat, info panel) and to {@link MainFrame}.
*
* <p>Constructed before the {@link TeamspeakConnection} it listens to exists — the
* connection's constructor needs a listener up front — so {@link #attach} wires the
* rest of the tab in once everything else has been built.
*/
final class ServerTabConnectionEvents implements ConnectionListener {
/** The handful of {@link ServerTab} fields this class updates but can't reach directly. */
interface Listener {
void setStatus(String status);
void setTitle(String title);
void setConnecting(boolean connecting);
}
private final MainFrame host;
private final ServerTab tab;
private final Listener listener;
private TeamspeakConnection conn;
private ServerTreePanel treePanel;
private ChatPanel chatPanel;
private ServerTabSelfState selfState;
private ServerTabTreeActions treeActions;
ServerTabConnectionEvents(MainFrame host, ServerTab tab, Listener listener) {
this.host = host;
this.tab = tab;
this.listener = listener;
}
void attach(TeamspeakConnection conn, ServerTreePanel treePanel, ChatPanel chatPanel,
ServerTabSelfState selfState, ServerTabTreeActions treeActions) {
this.conn = conn;
this.treePanel = treePanel;
this.chatPanel = chatPanel;
this.selfState = selfState;
this.treeActions = treeActions;
}
@Override
public void onStatus(String text) {
SwingUtilities.invokeLater(() -> {
listener.setStatus(text);
host.tabUpdated(tab);
});
}
@Override
public void onConnected() {
SwingUtilities.invokeLater(() -> {
listener.setConnecting(false);
treePanel.setSelfClientId(conn.getSelfClientId());
selfState.resetOnConnect();
chatPanel.setInputEnabled(true);
chatPanel.appendSystem("Connected.");
host.tabConnected(tab);
});
}
@Override
public void onDisconnected(String reason) {
SwingUtilities.invokeLater(() -> {
listener.setConnecting(false);
conn.getModel().clear();
treePanel.showDisconnected();
treeActions.clearSelection();
chatPanel.setInputEnabled(false);
chatPanel.closePrivateChats();
chatPanel.closeNoteTabs();
chatPanel.appendSystem("Disconnected" + (reason == null || reason.isEmpty() ? "." : ": " + reason));
host.tabDisconnected(tab);
});
}
@Override
public void onModelChanged() {
SwingUtilities.invokeLater(() -> {
treePanel.rebuild();
treeActions.renderInfo();
String name = conn.getModel().getServerName();
if (conn.isConnected() && name != null && !name.isBlank() && !name.equals(tab.title())) {
listener.setTitle(name);
host.tabUpdated(tab);
}
});
}
@Override
public void onInfoUpdated() {
SwingUtilities.invokeLater(treeActions::renderInfo);
}
@Override
public void onIconsUpdated() {
SwingUtilities.invokeLater(() -> {
treePanel.refreshRowSizes();
treeActions.renderInfo();
});
}
@Override
public void onChat(ChatScope scope, int fromClientId, String fromName, String message) {
switch (scope) {
case PRIVATE:
chatPanel.appendPrivateMessage(fromClientId, fromName, fromClientId, fromName, message);
break;
case SERVER:
chatPanel.appendServerMessage(fromClientId, fromName, message);
break;
default:
chatPanel.appendChannelMessage(fromClientId, fromName, message);
break;
}
}
@Override
public void onTalkStateChanged(int clientId, boolean talking) {
SwingUtilities.invokeLater(() -> {
treePanel.refreshVisual();
if (clientId == conn.getSelfClientId()) host.selfStateChanged(tab);
});
}
@Override
public void onError(String message) {
SwingUtilities.invokeLater(() -> {
chatPanel.appendSystem("Error: " + message);
listener.setStatus(message);
host.tabUpdated(tab);
});
}
@Override
public void onServerLog(String message) {
SwingUtilities.invokeLater(() -> chatPanel.appendServerLog(message));
}
@Override
public void onPoke(String fromName, String message) {
SwingUtilities.invokeLater(() -> {
chatPanel.appendSystem("You were poked by " + fromName + ": " + message);
host.selectTab(tab);
JOptionPane.showMessageDialog(host, fromName + " poked you:\n\n" + message,
"Poke", JOptionPane.INFORMATION_MESSAGE);
});
}
}