The look-and-feel is FlatLaf now, chosen light or dark on the new Design tab and remembered in the settings. Theme keeps only the colours TeamSpeak gives meaning to (talking, away, muted, channel and server names) and takes its surfaces and fonts from the look-and-feel, so a switch recolours the whole window, chat logs included: the HTML panes share one themed stylesheet and re-parse their content when the theme changes. Also fixes the channel tree not scrolling: the tree listened for the mouse wheel to re-check its hover row, and AWT only forwards a wheel event to the enclosing scroll pane when the component under the pointer has no wheel listener of its own. The hover row now follows the viewport instead, which also covers scrollbar drags and keyboard scrolling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
145 lines
3.8 KiB
Java
145 lines
3.8 KiB
Java
package com.ts3client.ui;
|
|
|
|
import javax.swing.UIManager;
|
|
import java.awt.Color;
|
|
import java.awt.Font;
|
|
|
|
/**
|
|
* The palette and fonts the client paints with, on top of the active look-and-feel.
|
|
*
|
|
* <p>Surfaces (window, tree, chat background) and fonts come straight from the
|
|
* look-and-feel, so the client follows whatever FlatLaf theme is installed. The
|
|
* status colours TeamSpeak gives meaning to — talking, away, muted, channel and
|
|
* server names — are ours, in a light and a dark variant; {@link #refresh()} picks
|
|
* the variant matching the installed theme.
|
|
*/
|
|
public final class Theme {
|
|
|
|
private static boolean dark;
|
|
|
|
private Theme() {
|
|
}
|
|
|
|
/** Re-reads the installed look-and-feel; call after changing it. */
|
|
static void refresh() {
|
|
Color bg = UIManager.getColor("Panel.background");
|
|
dark = bg != null && luminance(bg) < 0.5;
|
|
}
|
|
|
|
public static boolean isDark() {
|
|
return dark;
|
|
}
|
|
|
|
// ---- fonts ----
|
|
|
|
public static Font uiFont() {
|
|
Font f = UIManager.getFont("Label.font");
|
|
return f != null ? f : new Font("SansSerif", Font.PLAIN, 12);
|
|
}
|
|
|
|
public static Font uiBold() {
|
|
return uiFont().deriveFont(Font.BOLD);
|
|
}
|
|
|
|
// ---- surfaces, from the look-and-feel ----
|
|
|
|
public static Color windowBg() {
|
|
return ui("Panel.background", 0xF0F0F0, 0x3C3F41);
|
|
}
|
|
|
|
public static Color toolbarBg() {
|
|
return ui("ToolBar.background", 0xE6E9ED, 0x3C3F41);
|
|
}
|
|
|
|
public static Color statusBg() {
|
|
return windowBg();
|
|
}
|
|
|
|
public static Color treeBg() {
|
|
return ui("Tree.background", 0xFFFFFF, 0x2B2B2B);
|
|
}
|
|
|
|
public static Color treeText() {
|
|
return ui("Tree.foreground", 0x1E1E1E, 0xDFE1E5);
|
|
}
|
|
|
|
public static Color treeSelection() {
|
|
return ui("Tree.selectionBackground", 0xCFE3FB, 0x2F5075);
|
|
}
|
|
|
|
/** Outline drawn around the row the pointer is over. */
|
|
public static Color treeHover() {
|
|
return pick(0x9CC4EE, 0x4A6E96);
|
|
}
|
|
|
|
public static Color chatBg() {
|
|
return ui("TextPane.background", 0xFAFAFA, 0x2B2B2B);
|
|
}
|
|
|
|
public static Color border() {
|
|
return ui("Component.borderColor", 0xD0D0D0, 0x4B4B4B);
|
|
}
|
|
|
|
public static Color chatText() {
|
|
return ui("TextPane.foreground", 0x202020, 0xDFE1E5);
|
|
}
|
|
|
|
// ---- status colours ----
|
|
|
|
public static Color channelText() {
|
|
return pick(0x21486B, 0x8FBCE6);
|
|
}
|
|
|
|
public static Color serverText() {
|
|
return pick(0x123456, 0xB9D4F0);
|
|
}
|
|
|
|
public static Color talking() {
|
|
return pick(0x33B35A, 0x4CC479);
|
|
}
|
|
|
|
public static Color idleClient() {
|
|
return pick(0x6E7B87, 0x9AA6B2);
|
|
}
|
|
|
|
public static Color away() {
|
|
return pick(0xC98A1B, 0xE0A83C);
|
|
}
|
|
|
|
public static Color muted() {
|
|
return pick(0xC0392B, 0xE05C4C);
|
|
}
|
|
|
|
public static Color accent() {
|
|
return pick(0x2C7BE5, 0x5C9BEE);
|
|
}
|
|
|
|
public static Color chatSystem() {
|
|
return pick(0x8A8A8A, 0x9A9A9A);
|
|
}
|
|
|
|
public static Color chatName() {
|
|
return accent();
|
|
}
|
|
|
|
/** Links that leave the client, distinct from the colour used for identities. */
|
|
public static Color link() {
|
|
return pick(0x1A5FB4, 0x6EA8FF);
|
|
}
|
|
|
|
private static Color pick(int light, int darkRgb) {
|
|
return new Color(dark ? darkRgb : light);
|
|
}
|
|
|
|
private static Color ui(String key, int lightFallback, int darkFallback) {
|
|
// Returned as the look-and-feel's own UIResource colour, so components painted with
|
|
// it are recoloured automatically when the look-and-feel is switched.
|
|
Color c = UIManager.getColor(key);
|
|
return c != null ? c : pick(lightFallback, darkFallback);
|
|
}
|
|
|
|
private static double luminance(Color c) {
|
|
return (0.2126 * c.getRed() + 0.7152 * c.getGreen() + 0.0722 * c.getBlue()) / 255.0;
|
|
}
|
|
}
|