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:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user