Drop the Options dialog's page banner

It only repeated the page selected in the sidebar, and took vertical
space from the page itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 17:11:43 +00:00
parent e06bb82299
commit 76f6a9feed
4 changed files with 17 additions and 117 deletions

View File

@@ -292,7 +292,7 @@ swing/ com.ts3client
├── Spacers TS3 spacer-channel name parsing/rendering ├── Spacers TS3 spacer-channel name parsing/rendering
├── InfoPanel channel description / client group + details view ├── InfoPanel channel description / client group + details view
├── ChatPanel chat log + input ├── ChatPanel chat log + input
├── SettingsDialog the Options dialog: TS3-style page sidebar + banner; one panel per page (ApplicationPanel, …, ChatLogsPanel) ├── SettingsDialog the Options dialog: TS3-style page sidebar; one panel per page (ApplicationPanel, …, ChatLogsPanel)
├── HotkeysPanel hotkey list (Options → Hotkeys) ├── HotkeysPanel hotkey list (Options → Hotkeys)
├── HotkeyDialog add/edit one hotkey: action, combination, trigger, scope ├── HotkeyDialog add/edit one hotkey: action, combination, trigger, scope
├── HotkeyService bindings + engine + input hook, for the dialogs ├── HotkeyService bindings + engine + input hook, for the dialogs

View File

@@ -1,82 +0,0 @@
package com.ts3client.ui;
import com.formdev.flatlaf.util.ScaledEmptyBorder;
import com.formdev.flatlaf.util.UIScale;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
/**
* The heading over an Options page: its title and subtitle on an accent-tinted band, with
* the page's icon at the far end, after TS3's blue page banners.
*/
final class OptionsBanner extends JPanel {
private final JLabel title = new JLabel() {
@Override
public void updateUI() {
super.updateUI();
Font base = UIManager.getFont("Label.font");
if (base != null) setFont(base.deriveFont(Font.BOLD, base.getSize2D() * 1.4f));
}
};
private final JLabel subtitle = new JLabel();
private final JLabel icon = new JLabel();
private OptionsPage page;
OptionsBanner() {
super(new BorderLayout(UIScale.scale(12), 0));
setOpaque(false);
setBorder(new ScaledEmptyBorder(8, 12, 8, 12));
JPanel text = new JPanel();
text.setOpaque(false);
text.setLayout(new BoxLayout(text, BoxLayout.Y_AXIS));
text.add(title);
text.add(subtitle);
add(text, BorderLayout.CENTER);
add(icon, BorderLayout.EAST);
}
void show(OptionsPage page) {
this.page = page;
title.setText(page.title());
subtitle.setText(page.subtitle());
subtitle.setVisible(page.subtitle() != null && !page.subtitle().isEmpty());
refreshIcon();
}
/** Picks the icon up again from the active pack, after it changed. */
void refreshIcon() {
icon.setIcon(page == null ? null : page.icon(UIScale.scale(OptionsSidebar.ICON_SIZE)));
}
@Override
protected void paintComponent(Graphics g) {
Color accent = UIManager.getColor("Component.accentColor");
if (accent == null) accent = UIManager.getColor("List.selectionBackground");
Graphics2D g2 = (Graphics2D) g.create();
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(new GradientPaint(0, 0, withAlpha(accent, 70), 0, getHeight(), withAlpha(accent, 20)));
int arc = UIScale.scale(8);
g2.fillRoundRect(0, 0, getWidth(), getHeight(), arc, arc);
} finally {
g2.dispose();
}
super.paintComponent(g);
}
private static Color withAlpha(Color c, int alpha) {
return new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha);
}
}

View File

@@ -5,14 +5,14 @@ import javax.swing.JComponent;
import java.util.List; import java.util.List;
/** /**
* One page of the Options dialog, as the sidebar lists it and the banner introduces it. * One page of the Options dialog, as the sidebar lists it.
* *
* @param iconKeys TeamSpeak icon keys to try in turn, since packs differ in what they ship * @param iconKeys TeamSpeak icon keys to try in turn, since packs differ in what they ship
*/ */
record OptionsPage(String title, String subtitle, List<String> iconKeys, JComponent content) { record OptionsPage(String title, List<String> iconKeys, JComponent content) {
OptionsPage(String title, String subtitle, JComponent content, String... iconKeys) { OptionsPage(String title, JComponent content, String... iconKeys) {
this(title, subtitle, List.of(iconKeys), content); this(title, List.of(iconKeys), content);
} }
/** @return the page's icon at {@code size} pixels, or {@code null} when the pack has none */ /** @return the page's icon at {@code size} pixels, or {@code null} when the pack has none */

View File

@@ -26,7 +26,7 @@ import java.util.List;
/** /**
* Options dialog, laid out like TS3's: a sidebar of pages on the left, the chosen page * Options dialog, laid out like TS3's: a sidebar of pages on the left, the chosen page
* under its banner on the right, OK/Cancel/Apply below. Each page owns its controls and * on the right, OK/Cancel/Apply below. Each page owns its controls and
* writes {@link Settings} when applied; this class wires the audio pages, which preview * writes {@link Settings} when applied; this class wires the audio pages, which preview
* themselves live against the running audio subsystem, and owns the apply plumbing. * themselves live against the running audio subsystem, and owns the apply plumbing.
*/ */
@@ -68,26 +68,16 @@ public final class SettingsDialog extends JDialog {
chatLogsPanel = new ChatLogsPanel(settings); chatLogsPanel = new ChatLogsPanel(settings);
List<OptionsPage> pages = List.of( List<OptionsPage> pages = List.of(
new OptionsPage("Application", "General Application Options", scrollable(applicationPanel), new OptionsPage("Application", scrollable(applicationPanel), "SETTINGS"),
"SETTINGS"), new OptionsPage("myTeamSpeak", myTeamSpeak, "MYTS_ACCOUNT"),
new OptionsPage("myTeamSpeak", "Account and Synchronization", myTeamSpeak, new OptionsPage("Playback", scrollable(playbackPanel), "PLAYBACK"),
"MYTS_ACCOUNT"), new OptionsPage("Capture", scrollable(capturePanel), "CAPTURE"),
new OptionsPage("Playback", "Configure the Playback Sound System", scrollable(playbackPanel), new OptionsPage("Design", designPanel, "GUISETUP", "CHANNEL_EDIT"),
"PLAYBACK"), new OptionsPage("Hotkeys", hotkeysPanel, "HOTKEYS"),
new OptionsPage("Capture", "Configure the Capture Sound System", scrollable(capturePanel), new OptionsPage("Contacts", scrollable(contactsPanel), "CONTACT", "EDIT_FRIEND_FOE_STATUS"),
"CAPTURE"), new OptionsPage("Chat", scrollable(chatLogsPanel), "CHANNEL_CHAT", "PLAYER_CHAT"),
new OptionsPage("Design", "Configure the Design of TeamSpeak", designPanel, new OptionsPage("Notifications", notificationsPanel, "NOTIFICATIONS"));
"GUISETUP", "CHANNEL_EDIT"),
new OptionsPage("Hotkeys", "Configure Hotkeys", hotkeysPanel,
"HOTKEYS"),
new OptionsPage("Contacts", "Configure Contacts and Whispers", scrollable(contactsPanel),
"CONTACT", "EDIT_FRIEND_FOE_STATUS"),
new OptionsPage("Chat", "Configure the Chat Tabs", scrollable(chatLogsPanel),
"CHANNEL_CHAT", "PLAYER_CHAT"),
new OptionsPage("Notifications", "Configure the Notifications", notificationsPanel,
"NOTIFICATIONS"));
OptionsBanner banner = new OptionsBanner();
CardLayout cards = new CardLayout(); CardLayout cards = new CardLayout();
JPanel content = new JPanel(cards); JPanel content = new JPanel(cards);
for (OptionsPage page : pages) content.add(page.content(), page.title()); for (OptionsPage page : pages) content.add(page.content(), page.title());
@@ -96,7 +86,6 @@ public final class SettingsDialog extends JDialog {
sidebar.addListSelectionListener(e -> { sidebar.addListSelectionListener(e -> {
OptionsPage page = sidebar.getSelectedValue(); OptionsPage page = sidebar.getSelectedValue();
if (page == null) return; if (page == null) return;
banner.show(page);
cards.show(content, page.title()); cards.show(content, page.title());
lastPage = sidebar.getSelectedIndex(); lastPage = sidebar.getSelectedIndex();
}); });
@@ -104,23 +93,16 @@ public final class SettingsDialog extends JDialog {
JScrollPane sidebarScroll = new JScrollPane(sidebar, JScrollPane sidebarScroll = new JScrollPane(sidebar,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JPanel page = new JPanel(new BorderLayout(0, UIScale.scale(4)));
page.add(banner, BorderLayout.NORTH);
page.add(content, BorderLayout.CENTER);
JPanel body = new JPanel(new BorderLayout(UIScale.scale(8), 0)); JPanel body = new JPanel(new BorderLayout(UIScale.scale(8), 0));
body.setBorder(new ScaledEmptyBorder(10, 10, 0, 10)); body.setBorder(new ScaledEmptyBorder(10, 10, 0, 10));
body.add(sidebarScroll, BorderLayout.WEST); body.add(sidebarScroll, BorderLayout.WEST);
body.add(page, BorderLayout.CENTER); body.add(content, BorderLayout.CENTER);
getContentPane().setLayout(new BorderLayout()); getContentPane().setLayout(new BorderLayout());
getContentPane().add(body, BorderLayout.CENTER); getContentPane().add(body, BorderLayout.CENTER);
getContentPane().add(buildButtons(), BorderLayout.SOUTH); getContentPane().add(buildButtons(), BorderLayout.SOUTH);
Runnable iconsChanged = () -> SwingUtilities.invokeLater(() -> { Runnable iconsChanged = () -> SwingUtilities.invokeLater(sidebar::repaint);
sidebar.repaint();
banner.refreshIcon();
});
IconTheme.get().addListener(iconsChanged); IconTheme.get().addListener(iconsChanged);
// The microphone test hears typing too, so typing attenuation can be tried out here. // The microphone test hears typing too, so typing attenuation can be tried out here.