Use a real tab strip for servers instead of a button bar
ServerTabPane replaces ServerTabBar: the views live in a JTabbedPane whose strip sits under the toolbar, with the native tab shapes and the same look as the chat tabs. Each tab carries its server name, the microphone marker and a close glyph. With a single connection the server view is mounted directly, so the strip appears only once a second server is open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,6 @@ import com.ts3client.config.Settings;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
@@ -26,7 +25,6 @@ import javax.swing.JToolBar;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.KeyEventDispatcher;
|
||||
@@ -44,9 +42,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
*
|
||||
* <p>Several servers can be connected at once — their incoming voice is mixed
|
||||
* into the same output — but only one of them owns the microphone at a time
|
||||
* ("active" tab, marked in the tab bar and switched from the toolbar).
|
||||
* ("active" tab, marked in the tab strip and switched from the toolbar).
|
||||
*/
|
||||
public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
|
||||
private final Settings settings;
|
||||
private final Bookmarks bookmarks = Bookmarks.load();
|
||||
@@ -54,10 +52,7 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
private final AudioBackend audio = new JavaSoundAudioBackend();
|
||||
|
||||
private final List<ServerTab> tabs = new ArrayList<>();
|
||||
private final CardLayout cards = new CardLayout();
|
||||
private final JPanel cardPanel = new JPanel(cards);
|
||||
private final ServerTabBar tabBar = new ServerTabBar(this);
|
||||
private int tabCounter;
|
||||
private final ServerTabPane tabPane = new ServerTabPane(this);
|
||||
|
||||
/** The tab whose views are on screen. */
|
||||
private ServerTab selected;
|
||||
@@ -105,12 +100,8 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
|
||||
setJMenuBar(buildMenuBar());
|
||||
|
||||
JPanel top = new JPanel();
|
||||
top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
|
||||
top.add(buildToolbar());
|
||||
top.add(tabBar);
|
||||
add(top, BorderLayout.NORTH);
|
||||
add(cardPanel, BorderLayout.CENTER);
|
||||
add(buildToolbar(), BorderLayout.NORTH);
|
||||
add(tabPane, BorderLayout.CENTER);
|
||||
add(buildStatusBar(), BorderLayout.SOUTH);
|
||||
|
||||
codecLabel.setText(audio.description());
|
||||
@@ -292,11 +283,9 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
|
||||
private ServerTab newTab() {
|
||||
ServerTab tab = new ServerTab(this, settings, identities, audio);
|
||||
String name = "tab" + (tabCounter++);
|
||||
tab.component().setName(name); // CardLayout addresses cards by this name
|
||||
tabs.add(tab);
|
||||
cardPanel.add(tab.component(), name);
|
||||
refreshTabBar();
|
||||
tabPane.addTab(tab);
|
||||
refreshTabs();
|
||||
return tab;
|
||||
}
|
||||
|
||||
@@ -312,8 +301,8 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
public void selectTab(ServerTab tab) {
|
||||
if (tab == null || !tabs.contains(tab)) return;
|
||||
selected = tab;
|
||||
cards.show(cardPanel, tab.component().getName());
|
||||
refreshTabBar();
|
||||
tabPane.setSelected(tab);
|
||||
refreshTabs();
|
||||
updateToolbar();
|
||||
updateStatusLabel();
|
||||
}
|
||||
@@ -325,13 +314,13 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
if (tabs.size() == 1) return; // always keep one view around
|
||||
|
||||
tabs.remove(tab);
|
||||
cardPanel.remove(tab.component());
|
||||
tabPane.removeTab(tab);
|
||||
if (micTab == tab) micTab = null;
|
||||
if (selected == tab) {
|
||||
selected = null;
|
||||
selectTab(tabs.get(0));
|
||||
} else {
|
||||
refreshTabBar();
|
||||
selectTab(selected); // the pane rebuilt itself; restore the selection
|
||||
}
|
||||
assignMicrophoneIfFree();
|
||||
}
|
||||
@@ -341,7 +330,7 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
if (micTab == tab) return;
|
||||
ServerTab previous = micTab;
|
||||
micTab = tab;
|
||||
refreshTabBar();
|
||||
refreshTabs();
|
||||
// Closing and reopening the capture line can block briefly; keep it off the EDT.
|
||||
new Thread(() -> {
|
||||
if (previous != null) previous.setMicrophoneActive(false);
|
||||
@@ -359,13 +348,12 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
return;
|
||||
}
|
||||
}
|
||||
refreshTabBar();
|
||||
refreshTabs();
|
||||
}
|
||||
|
||||
private void refreshTabBar() {
|
||||
tabBar.rebuild(tabs, selected, micTab);
|
||||
// The bar appears/disappears with the second connection.
|
||||
getContentPane().revalidate();
|
||||
/** Repaints the tab labels (title, microphone marker). */
|
||||
private void refreshTabs() {
|
||||
tabPane.refresh(micTab);
|
||||
}
|
||||
|
||||
// ---- callbacks from ServerTab ----
|
||||
@@ -373,7 +361,7 @@ public final class MainFrame extends JFrame implements ServerTabBar.Listener {
|
||||
/** A tab's title, status or connection state changed. */
|
||||
void tabUpdated(ServerTab tab) {
|
||||
if (!tabs.contains(tab)) return;
|
||||
refreshTabBar();
|
||||
refreshTabs();
|
||||
if (tab == selected) {
|
||||
updateToolbar();
|
||||
updateStatusLabel();
|
||||
|
||||
Reference in New Issue
Block a user