Add Master Volume slider, status bar toggle, and toolbar customization menu

The toolbar now has a right-click menu to hide/show the status bar and
a short Master Volume slider (drag or scroll) that multiplies both
voice and notification volume via new Settings.effectiveOutputVolume()/
effectiveSoundVolume() helpers.

Also splits MainFrame's toolbar, menu bar and status bar construction
into their own MainToolbar/MainMenuBar/StatusBar classes, each talking
back to MainFrame only through a Listener interface.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 13:37:36 +00:00
parent e5fb87a796
commit cf5ba94092
6 changed files with 601 additions and 200 deletions

View File

@@ -0,0 +1,155 @@
package com.ts3client.ui;
import com.ts3client.config.Bookmark;
import com.ts3client.config.Bookmarks;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
/**
* The main window's menu bar: Connections, Bookmarks, Self, Tools and Help.
* Talks to {@link MainFrame} only through {@link Listener}; the away/commander
* item states are pushed in by {@link #refresh}, mirroring how {@link MainToolbar}
* is kept in sync.
*/
final class MainMenuBar extends JMenuBar {
interface Listener {
void onConnect();
void onDisconnect();
void onCloseTab();
void onQuit();
void onToggleMic();
void onToggleSpeaker();
/** The plain toggle carries no message; "Set away status…" is where messages are chosen. */
void onAwayToggle(boolean away);
void onAwayStatus();
void onCommanderToggle(boolean commander);
void onChangeNickname();
void onShowIdentities();
void onShowSettings();
void onShowAbout();
void onConnectBookmark(Bookmark bookmark);
void onAddCurrentServerBookmark();
void onManageBookmarks();
}
private final Bookmarks bookmarks;
private final Listener listener;
private JMenu bookmarksMenu;
private JCheckBoxMenuItem awayItem;
private JMenuItem awayStatusItem;
private JCheckBoxMenuItem commanderItem;
MainMenuBar(Bookmarks bookmarks, Listener listener) {
this.bookmarks = bookmarks;
this.listener = listener;
JMenu connections = new JMenu("Connections");
JMenuItem connect = new JMenuItem("Connect…", Icons.of("CONNECT"));
connect.setAccelerator(KeyStroke.getKeyStroke("control S"));
connect.addActionListener(e -> listener.onConnect());
JMenuItem disconnect = new JMenuItem("Disconnect", Icons.of("DISCONNECT"));
disconnect.addActionListener(e -> listener.onDisconnect());
JMenuItem closeTab = new JMenuItem("Close tab", Icons.of("CLOSE_BUTTON"));
closeTab.setAccelerator(KeyStroke.getKeyStroke("control W"));
closeTab.addActionListener(e -> listener.onCloseTab());
JMenuItem quit = new JMenuItem("Quit", Icons.of("QUIT"));
quit.addActionListener(e -> listener.onQuit());
connections.add(connect);
connections.add(disconnect);
connections.add(closeTab);
connections.addSeparator();
connections.add(quit);
bookmarksMenu = new JMenu("Bookmarks");
rebuildBookmarks();
JMenu self = new JMenu("Self");
JMenuItem mute = new JMenuItem("Toggle microphone", Icons.of("CAPTURE"));
mute.addActionListener(e -> listener.onToggleMic());
JMenuItem deaf = new JMenuItem("Toggle speakers", Icons.of("PLAYBACK"));
deaf.addActionListener(e -> listener.onToggleSpeaker());
awayItem = new JCheckBoxMenuItem("Away", Icons.of("AWAY"), false);
awayItem.addActionListener(e -> listener.onAwayToggle(awayItem.isSelected()));
awayStatusItem = new JMenuItem("Set away status…", Icons.of("EDIT"));
awayStatusItem.addActionListener(e -> listener.onAwayStatus());
commanderItem = new JCheckBoxMenuItem("Channel commander", Icons.of("CHANNEL_COMMANDER"), false);
commanderItem.addActionListener(e -> listener.onCommanderToggle(commanderItem.isSelected()));
JMenuItem nick = new JMenuItem("Change nickname…", Icons.of("CHANGE_NICKNAME"));
nick.addActionListener(e -> listener.onChangeNickname());
self.add(mute);
self.add(deaf);
self.addSeparator();
self.add(awayItem);
self.add(awayStatusItem);
self.add(commanderItem);
self.addSeparator();
self.add(nick);
JMenu tools = new JMenu("Tools");
JMenuItem identitiesItem = new JMenuItem("Identities…", Icons.of("IDENTITY_MANAGER"));
identitiesItem.addActionListener(e -> listener.onShowIdentities());
JMenuItem options = new JMenuItem("Options…", Icons.of("SETTINGS"));
options.addActionListener(e -> listener.onShowSettings());
tools.add(identitiesItem);
tools.addSeparator();
tools.add(options);
JMenu help = new JMenu("Help");
JMenuItem about = new JMenuItem("About", Icons.of("ABOUT"));
about.addActionListener(e -> listener.onShowAbout());
help.add(about);
add(connections);
add(bookmarksMenu);
add(self);
add(tools);
add(help);
}
/** Reflects the current tab's away/commander state on the menu items. */
void refresh(boolean connected, boolean away, boolean commander) {
awayItem.setEnabled(connected);
awayStatusItem.setEnabled(connected);
commanderItem.setEnabled(connected);
awayItem.setSelected(away);
commanderItem.setSelected(commander);
}
/** Re-lists the saved bookmarks; called after they change (add, remove, manage…). */
void rebuildBookmarks() {
bookmarksMenu.removeAll();
for (Bookmark b : bookmarks.all()) {
JMenuItem item = new JMenuItem(b.displayName(), Icons.of("SERVER_GREEN"));
item.addActionListener(e -> listener.onConnectBookmark(b));
bookmarksMenu.add(item);
}
if (!bookmarks.all().isEmpty()) bookmarksMenu.addSeparator();
JMenuItem addCurrent = new JMenuItem("Add current server…", Icons.of("BOOKMARK_ADD"));
addCurrent.addActionListener(e -> listener.onAddCurrentServerBookmark());
JMenuItem manage = new JMenuItem("Manage bookmarks…", Icons.of("BOOKMARK_MANAGER"));
manage.addActionListener(e -> listener.onManageBookmarks());
bookmarksMenu.add(addCurrent);
bookmarksMenu.add(manage);
}
}