2026-05-28 10:20:17 +08:00
|
|
|
|
"""现代化 UI 样式注册(跟随 ttkbootstrap 当前主题)。
|
|
|
|
|
|
|
|
|
|
|
|
由 backgroud_style_set() 调用一次。这里集中定义"配置项卡片化"、
|
|
|
|
|
|
"现代化标题栏"、"工具条"、"状态栏" 等所需的所有 ttk Style,
|
|
|
|
|
|
保持主题切换时颜色自动跟随。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import ttkbootstrap as ttk
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _hex_to_rgb(h: str) -> tuple[int, int, int]:
|
|
|
|
|
|
h = h.lstrip("#")
|
|
|
|
|
|
return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _rgb_to_hex(r: int, g: int, b: int) -> str:
|
|
|
|
|
|
return f"#{r:02x}{g:02x}{b:02x}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _mix(c1: str, c2: str, ratio: float) -> str:
|
|
|
|
|
|
"""按 ratio (0~1) 将 c1 与 c2 线性混合。"""
|
|
|
|
|
|
r1, g1, b1 = _hex_to_rgb(c1)
|
|
|
|
|
|
r2, g2, b2 = _hex_to_rgb(c2)
|
|
|
|
|
|
return _rgb_to_hex(
|
|
|
|
|
|
int(r1 * (1 - ratio) + r2 * ratio),
|
|
|
|
|
|
int(g1 * (1 - ratio) + g2 * ratio),
|
|
|
|
|
|
int(b1 * (1 - ratio) + b2 * ratio),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _is_dark(color: str) -> bool:
|
|
|
|
|
|
r, g, b = _hex_to_rgb(color)
|
|
|
|
|
|
# ITU-R BT.601 亮度
|
|
|
|
|
|
return (r * 299 + g * 587 + b * 114) / 1000 < 128
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-06-04 10:36:15 +08:00
|
|
|
|
def _contrast_text(color: str, *, dark_text: str, light_text: str) -> str:
|
|
|
|
|
|
return dark_text if _is_dark(color) else light_text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_theme_palette() -> dict[str, str]:
|
|
|
|
|
|
"""返回当前主题的语义色板,供 ttk / tk 自定义控件共用。"""
|
2026-05-28 10:20:17 +08:00
|
|
|
|
style = ttk.Style()
|
2026-06-04 10:36:15 +08:00
|
|
|
|
theme = style.colors
|
2026-05-28 10:20:17 +08:00
|
|
|
|
|
2026-06-04 10:36:15 +08:00
|
|
|
|
bg = theme.bg
|
|
|
|
|
|
fg = theme.fg
|
2026-05-28 10:20:17 +08:00
|
|
|
|
primary = theme.primary
|
|
|
|
|
|
secondary = theme.secondary
|
2026-06-04 10:36:15 +08:00
|
|
|
|
success = theme.success
|
2026-05-28 10:50:52 +08:00
|
|
|
|
info = theme.info
|
2026-06-04 10:36:15 +08:00
|
|
|
|
warning = theme.warning
|
|
|
|
|
|
danger = theme.danger
|
2026-05-28 10:50:52 +08:00
|
|
|
|
dark = theme.dark
|
2026-05-28 10:20:17 +08:00
|
|
|
|
border = theme.border
|
|
|
|
|
|
inputbg = theme.inputbg
|
2026-06-04 10:36:15 +08:00
|
|
|
|
inputfg = getattr(theme, "inputfg", fg)
|
2026-05-28 10:20:17 +08:00
|
|
|
|
|
|
|
|
|
|
dark_theme = _is_dark(bg)
|
2026-06-04 10:36:15 +08:00
|
|
|
|
select_bg = getattr(theme, "selectbg", _mix(primary, bg, 0.30 if dark_theme else 0.12))
|
|
|
|
|
|
select_fg = getattr(theme, "selectfg", "#ffffff" if _is_dark(select_bg) else fg)
|
2026-05-28 10:20:17 +08:00
|
|
|
|
|
2026-06-04 10:36:15 +08:00
|
|
|
|
if dark_theme:
|
|
|
|
|
|
card_bg = _mix(bg, "#ffffff", 0.04)
|
|
|
|
|
|
card_border = _mix(bg, fg, 0.18)
|
|
|
|
|
|
header_fg = _contrast_text(
|
|
|
|
|
|
"#444A51",
|
|
|
|
|
|
dark_text="#ffffff",
|
|
|
|
|
|
light_text="#1a1a1a",
|
|
|
|
|
|
)
|
|
|
|
|
|
sidebar_bg = _mix(dark, bg, 0.18)
|
|
|
|
|
|
sidebar_hover = _mix(sidebar_bg, "#ffffff", 0.07)
|
|
|
|
|
|
sidebar_selected = _mix(sidebar_bg, "#ffffff", 0.14)
|
|
|
|
|
|
sidebar_fg = _mix(fg, "#ffffff", 0.04)
|
|
|
|
|
|
sidebar_muted = _mix(sidebar_fg, sidebar_bg, 0.45)
|
|
|
|
|
|
muted_fg = _mix(fg, bg, 0.32)
|
|
|
|
|
|
disabled_fg = _mix(fg, bg, 0.42)
|
|
|
|
|
|
disabled_bg = _mix(inputbg, bg, 0.18)
|
|
|
|
|
|
disabled_border = _mix(border, fg, 0.22)
|
|
|
|
|
|
readonly_bg = _mix(inputbg, "#ffffff", 0.06)
|
|
|
|
|
|
success_fg = _mix(success, "#ffffff", 0.08)
|
|
|
|
|
|
warning_fg = _mix(warning, "#ffffff", 0.06)
|
|
|
|
|
|
info_fg = _mix(info, "#ffffff", 0.06)
|
|
|
|
|
|
statusbar_bg = _mix(bg, "#ffffff", 0.06)
|
|
|
|
|
|
tooltip_bg = _mix(inputbg, bg, 0.08)
|
|
|
|
|
|
tooltip_fg = inputfg
|
|
|
|
|
|
tooltip_border = _mix(border, fg, 0.20)
|
|
|
|
|
|
surface_alt_bg = _mix(card_bg, "#ffffff", 0.05)
|
|
|
|
|
|
surface_hover_bg = _mix(card_bg, "#ffffff", 0.09)
|
|
|
|
|
|
badge_bg = _mix(danger, bg, 0.12)
|
|
|
|
|
|
badge_fg = "#ffffff"
|
|
|
|
|
|
focus = _mix(primary, "#ffffff", 0.18)
|
|
|
|
|
|
config_bg = _mix("#444A51", bg, 0.30)
|
|
|
|
|
|
else:
|
|
|
|
|
|
card_bg = inputbg
|
|
|
|
|
|
card_border = border
|
|
|
|
|
|
header_fg = bg
|
|
|
|
|
|
config_bg = _mix(primary, bg, 0.25)
|
|
|
|
|
|
sidebar_bg = _mix(primary, bg, 0.82)
|
|
|
|
|
|
sidebar_hover = _mix(primary, bg, 0.72)
|
|
|
|
|
|
sidebar_selected = primary
|
|
|
|
|
|
sidebar_fg = fg
|
|
|
|
|
|
sidebar_muted = _mix(fg, sidebar_bg, 0.35)
|
|
|
|
|
|
muted_fg = _mix(fg, bg, 0.38)
|
|
|
|
|
|
disabled_fg = _mix(fg, bg, 0.55)
|
|
|
|
|
|
disabled_bg = _mix(bg, border, 0.18)
|
|
|
|
|
|
disabled_border = _mix(border, bg, 0.18)
|
|
|
|
|
|
readonly_bg = _mix(inputbg, primary, 0.04)
|
|
|
|
|
|
success_fg = success
|
|
|
|
|
|
warning_fg = _mix(warning, fg, 0.18)
|
|
|
|
|
|
info_fg = info
|
|
|
|
|
|
statusbar_bg = _mix(bg, dark, 0.04)
|
|
|
|
|
|
tooltip_bg = inputbg
|
|
|
|
|
|
tooltip_fg = inputfg
|
|
|
|
|
|
tooltip_border = border
|
|
|
|
|
|
surface_alt_bg = _mix(bg, dark, 0.03)
|
|
|
|
|
|
surface_hover_bg = _mix(bg, dark, 0.05)
|
|
|
|
|
|
badge_bg = danger
|
|
|
|
|
|
badge_fg = "#ffffff"
|
|
|
|
|
|
focus = _mix(primary, bg, 0.20)
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
"bg": bg,
|
|
|
|
|
|
"fg": fg,
|
|
|
|
|
|
"primary": primary,
|
|
|
|
|
|
"secondary": secondary,
|
|
|
|
|
|
"success": success,
|
|
|
|
|
|
"info": info,
|
|
|
|
|
|
"warning": warning,
|
|
|
|
|
|
"danger": danger,
|
|
|
|
|
|
"border": border,
|
|
|
|
|
|
"input_bg": inputbg,
|
|
|
|
|
|
"input_fg": inputfg,
|
|
|
|
|
|
"select_bg": select_bg,
|
|
|
|
|
|
"select_fg": select_fg,
|
|
|
|
|
|
"card_bg": card_bg,
|
|
|
|
|
|
"card_border": card_border,
|
|
|
|
|
|
"header_fg": header_fg,
|
|
|
|
|
|
"sidebar_bg": sidebar_bg,
|
|
|
|
|
|
"sidebar_hover": sidebar_hover,
|
|
|
|
|
|
"sidebar_selected": sidebar_selected,
|
|
|
|
|
|
"sidebar_fg": sidebar_fg,
|
|
|
|
|
|
"sidebar_muted": sidebar_muted,
|
|
|
|
|
|
"muted_fg": muted_fg,
|
|
|
|
|
|
"disabled_fg": disabled_fg,
|
|
|
|
|
|
"disabled_bg": disabled_bg,
|
|
|
|
|
|
"disabled_border": disabled_border,
|
|
|
|
|
|
"readonly_bg": readonly_bg,
|
|
|
|
|
|
"success_fg": success_fg,
|
|
|
|
|
|
"warning_fg": warning_fg,
|
|
|
|
|
|
"info_fg": info_fg,
|
|
|
|
|
|
"statusbar_bg": statusbar_bg,
|
|
|
|
|
|
"tooltip_bg": tooltip_bg,
|
|
|
|
|
|
"tooltip_fg": tooltip_fg,
|
|
|
|
|
|
"tooltip_border": tooltip_border,
|
|
|
|
|
|
"surface_alt_bg": surface_alt_bg,
|
|
|
|
|
|
"surface_hover_bg": surface_hover_bg,
|
|
|
|
|
|
"badge_bg": badge_bg,
|
|
|
|
|
|
"badge_fg": badge_fg,
|
|
|
|
|
|
"focus": focus,
|
|
|
|
|
|
"config_bg": config_bg,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_listbox_theme(widget) -> None:
|
|
|
|
|
|
"""将 tk.Listbox 颜色同步到当前主题。"""
|
|
|
|
|
|
palette = get_theme_palette()
|
|
|
|
|
|
widget.configure(
|
|
|
|
|
|
background=palette["input_bg"],
|
|
|
|
|
|
foreground=palette["input_fg"],
|
|
|
|
|
|
highlightbackground=palette["border"],
|
|
|
|
|
|
highlightcolor=palette["focus"],
|
|
|
|
|
|
selectbackground=palette["select_bg"],
|
|
|
|
|
|
selectforeground=palette["select_fg"],
|
|
|
|
|
|
disabledforeground=palette["disabled_fg"],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_tooltip_theme(toplevel, label) -> None:
|
|
|
|
|
|
"""将 tooltip 的 tk.Toplevel / Label 同步到当前主题。"""
|
|
|
|
|
|
palette = get_theme_palette()
|
|
|
|
|
|
toplevel.configure(background=palette["tooltip_border"])
|
|
|
|
|
|
label.configure(
|
|
|
|
|
|
bg=palette["tooltip_bg"],
|
|
|
|
|
|
fg=palette["tooltip_fg"],
|
|
|
|
|
|
highlightbackground=palette["tooltip_border"],
|
|
|
|
|
|
)
|
2026-05-28 10:20:17 +08:00
|
|
|
|
|
2026-06-04 10:36:15 +08:00
|
|
|
|
|
|
|
|
|
|
def apply_modern_styles() -> None:
|
|
|
|
|
|
"""注册或刷新现代化样式集。可在主题切换后再次调用。"""
|
|
|
|
|
|
style = ttk.Style()
|
|
|
|
|
|
palette = get_theme_palette()
|
|
|
|
|
|
|
|
|
|
|
|
bg = palette["bg"]
|
|
|
|
|
|
fg = palette["fg"]
|
|
|
|
|
|
primary = palette["primary"]
|
|
|
|
|
|
secondary = palette["secondary"]
|
|
|
|
|
|
info = palette["info"]
|
|
|
|
|
|
card_bg = palette["card_bg"]
|
|
|
|
|
|
card_border = palette["card_border"]
|
|
|
|
|
|
header_bg = palette["config_bg"]
|
|
|
|
|
|
header_fg = palette["header_fg"]
|
|
|
|
|
|
dark_theme = _is_dark(bg)
|
|
|
|
|
|
header_hover_bg = _mix(secondary, "#ffffff", 0.08) if _is_dark(secondary) else _mix(secondary, "#000000", 0.08)
|
2026-05-28 10:20:17 +08:00
|
|
|
|
preview_fg = _mix(header_fg, header_bg, 0.35)
|
2026-06-04 10:36:15 +08:00
|
|
|
|
sidebar_bg = palette["sidebar_bg"]
|
|
|
|
|
|
sidebar_hover = palette["sidebar_hover"]
|
|
|
|
|
|
sidebar_selected = palette["sidebar_selected"]
|
|
|
|
|
|
sidebar_fg = palette["sidebar_fg"]
|
|
|
|
|
|
sidebar_muted = palette["sidebar_muted"]
|
|
|
|
|
|
muted_fg = palette["muted_fg"]
|
|
|
|
|
|
disabled_fg = palette["disabled_fg"]
|
|
|
|
|
|
disabled_bg = palette["disabled_bg"]
|
|
|
|
|
|
disabled_border = palette["disabled_border"]
|
|
|
|
|
|
readonly_bg = palette["readonly_bg"]
|
|
|
|
|
|
success_fg = palette["success_fg"]
|
|
|
|
|
|
warning_fg = palette["warning_fg"]
|
2026-05-28 10:20:17 +08:00
|
|
|
|
|
|
|
|
|
|
# ---------------- 卡片 ----------------
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"Card.TFrame",
|
|
|
|
|
|
background=card_bg,
|
|
|
|
|
|
bordercolor=card_border,
|
|
|
|
|
|
relief="solid",
|
|
|
|
|
|
borderwidth=1,
|
|
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"CardTitle.TLabel",
|
|
|
|
|
|
background=card_bg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
foreground=fg,
|
|
|
|
|
|
font=("Segoe UI", 10, "bold"),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"CardBody.TLabel",
|
|
|
|
|
|
background=card_bg,
|
|
|
|
|
|
foreground=fg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 9),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"CardIcon.TLabel",
|
|
|
|
|
|
background=card_bg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
foreground=info if dark_theme else primary,
|
|
|
|
|
|
font=("Segoe UI", 9, "bold"),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 内嵌于 Card 的容器(与 Card.TFrame 同背景,无边框)
|
|
|
|
|
|
style.configure("CardInner.TFrame", background=card_bg, borderwidth=0)
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------- 配置项 Header ----------------
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ConfigHeader.TFrame",
|
|
|
|
|
|
background=header_bg,
|
|
|
|
|
|
borderwidth=0,
|
|
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ConfigHeaderHover.TFrame",
|
|
|
|
|
|
background=header_hover_bg,
|
|
|
|
|
|
borderwidth=0,
|
|
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ConfigHeader.TLabel",
|
|
|
|
|
|
background=header_bg,
|
|
|
|
|
|
foreground=header_fg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 10, "bold"),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ConfigHeaderHover.TLabel",
|
|
|
|
|
|
background=header_hover_bg,
|
|
|
|
|
|
foreground=header_fg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 10, "bold"),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ConfigChevron.TLabel",
|
|
|
|
|
|
background=header_bg,
|
|
|
|
|
|
foreground=header_fg,
|
|
|
|
|
|
font=("Segoe UI Symbol", 12, "bold"),
|
|
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ConfigPreview.TLabel",
|
|
|
|
|
|
background=header_bg,
|
|
|
|
|
|
foreground=preview_fg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 9),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-06-04 10:36:15 +08:00
|
|
|
|
# ---------------- 通用文字语义 ----------------
|
|
|
|
|
|
style.configure("Muted.TLabel", background=bg, foreground=muted_fg)
|
|
|
|
|
|
style.configure("SuccessState.TLabel", background=bg, foreground=success_fg)
|
|
|
|
|
|
style.configure("WarningState.TLabel", background=bg, foreground=warning_fg)
|
|
|
|
|
|
style.configure("InfoState.TLabel", background=bg, foreground=palette["info_fg"])
|
|
|
|
|
|
|
2026-05-28 10:20:17 +08:00
|
|
|
|
# ---------------- 顶部工具条 ----------------
|
|
|
|
|
|
style.configure("Toolbar.TFrame", background=bg, borderwidth=0)
|
|
|
|
|
|
# 工具条上的次要按钮(清理配置等)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ToolbarMuted.TButton",
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 9),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
padding=(10, 5),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------- 区段标题(侧栏 / 卡片外) ----------------
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"SectionTitle.TLabel",
|
|
|
|
|
|
background=bg,
|
|
|
|
|
|
foreground=_mix(fg, bg, 0.45),
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 8, "bold"),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
2026-05-28 10:50:52 +08:00
|
|
|
|
style.configure("Sidebar.TFrame", background=sidebar_bg, borderwidth=0)
|
2026-05-28 10:20:17 +08:00
|
|
|
|
# 侧栏内的小区段标题(侧栏背景是 primary)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"SidebarSection.TLabel",
|
2026-05-28 10:50:52 +08:00
|
|
|
|
background=sidebar_bg,
|
|
|
|
|
|
foreground=sidebar_muted,
|
|
|
|
|
|
font=("Segoe UI", 8, "bold"),
|
|
|
|
|
|
)
|
|
|
|
|
|
# 侧栏顶部品牌区
|
|
|
|
|
|
brand_bg = _mix(sidebar_bg, "#ffffff", 0.05) if dark_theme else _mix(sidebar_bg, "#000000", 0.05)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"SidebarBrand.TFrame",
|
|
|
|
|
|
background=brand_bg,
|
|
|
|
|
|
borderwidth=0,
|
|
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"SidebarBrand.TLabel",
|
|
|
|
|
|
background=brand_bg,
|
2026-06-04 10:36:15 +08:00
|
|
|
|
foreground=palette["badge_fg"],
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI Semibold", 12),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
2026-06-04 10:36:15 +08:00
|
|
|
|
style.configure(
|
|
|
|
|
|
"SidebarBadge.TLabel",
|
|
|
|
|
|
background=palette["badge_bg"],
|
|
|
|
|
|
foreground=palette["badge_fg"],
|
|
|
|
|
|
font=("微软雅黑", 8, "bold"),
|
|
|
|
|
|
anchor="center",
|
|
|
|
|
|
padding=(6, 2),
|
|
|
|
|
|
)
|
2026-05-28 10:20:17 +08:00
|
|
|
|
|
|
|
|
|
|
# ---------------- 结果区无边框标题行 ----------------
|
|
|
|
|
|
style.configure("ResultHeader.TFrame", background=bg, borderwidth=0)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"ResultHeader.TLabel",
|
|
|
|
|
|
background=bg,
|
|
|
|
|
|
foreground=fg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 11, "bold"),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------- 状态栏 ----------------
|
2026-06-04 10:36:15 +08:00
|
|
|
|
statusbar_bg = palette["statusbar_bg"]
|
2026-05-28 10:20:17 +08:00
|
|
|
|
statusbar_fg = _mix(fg, bg, 0.15)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"StatusBar.TFrame",
|
|
|
|
|
|
background=statusbar_bg,
|
|
|
|
|
|
borderwidth=0,
|
|
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"StatusBar.TLabel",
|
|
|
|
|
|
background=statusbar_bg,
|
|
|
|
|
|
foreground=statusbar_fg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI", 9),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
padding=(10, 4),
|
|
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"StatusBarAccent.TLabel",
|
|
|
|
|
|
background=statusbar_bg,
|
2026-05-28 10:50:52 +08:00
|
|
|
|
foreground=info if dark_theme else primary,
|
|
|
|
|
|
font=("Segoe UI", 9, "bold"),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
padding=(10, 4),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-06-04 10:36:15 +08:00
|
|
|
|
# ---------------- 深色禁用态 / 只读态增强 ----------------
|
|
|
|
|
|
style.map(
|
|
|
|
|
|
"TLabel",
|
|
|
|
|
|
foreground=[("disabled", disabled_fg)],
|
|
|
|
|
|
)
|
|
|
|
|
|
style.map(
|
|
|
|
|
|
"TButton",
|
|
|
|
|
|
foreground=[("disabled", disabled_fg)],
|
|
|
|
|
|
background=[("disabled", disabled_bg)],
|
|
|
|
|
|
bordercolor=[("disabled", disabled_border)],
|
|
|
|
|
|
darkcolor=[("disabled", disabled_bg)],
|
|
|
|
|
|
lightcolor=[("disabled", disabled_bg)],
|
|
|
|
|
|
)
|
|
|
|
|
|
style.map(
|
|
|
|
|
|
"TEntry",
|
|
|
|
|
|
foreground=[("disabled", disabled_fg)],
|
|
|
|
|
|
fieldbackground=[("disabled", disabled_bg), ("readonly", readonly_bg)],
|
|
|
|
|
|
bordercolor=[("disabled", disabled_border), ("readonly", disabled_border)],
|
|
|
|
|
|
)
|
|
|
|
|
|
style.map(
|
|
|
|
|
|
"TCombobox",
|
|
|
|
|
|
foreground=[("disabled", disabled_fg), ("readonly", fg)],
|
|
|
|
|
|
fieldbackground=[("disabled", disabled_bg), ("readonly", readonly_bg)],
|
|
|
|
|
|
bordercolor=[("disabled", disabled_border), ("readonly", disabled_border)],
|
|
|
|
|
|
arrowcolor=[("disabled", disabled_fg), ("readonly", muted_fg)],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-28 10:20:17 +08:00
|
|
|
|
# ---------------- Sidebar 按钮(保留兼容名) ----------------
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"Sidebar.TButton",
|
2026-05-28 10:50:52 +08:00
|
|
|
|
background=sidebar_bg,
|
|
|
|
|
|
foreground=sidebar_fg,
|
|
|
|
|
|
font=("Segoe UI", 10),
|
|
|
|
|
|
padding=(18, 9),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
borderwidth=0,
|
|
|
|
|
|
anchor="w",
|
|
|
|
|
|
)
|
|
|
|
|
|
style.map(
|
|
|
|
|
|
"Sidebar.TButton",
|
|
|
|
|
|
background=[
|
2026-05-28 10:50:52 +08:00
|
|
|
|
("active", sidebar_hover),
|
|
|
|
|
|
("pressed", sidebar_selected),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
],
|
2026-05-29 08:32:21 +08:00
|
|
|
|
foreground=[("active", "#ffffff" if _is_dark(sidebar_hover) else sidebar_fg)],
|
2026-05-28 10:20:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
style.configure(
|
|
|
|
|
|
"SidebarSelected.TButton",
|
2026-05-28 10:50:52 +08:00
|
|
|
|
background=sidebar_selected,
|
2026-06-04 10:36:15 +08:00
|
|
|
|
foreground=_contrast_text(sidebar_selected, dark_text=palette["badge_fg"], light_text=sidebar_fg),
|
2026-05-28 10:50:52 +08:00
|
|
|
|
font=("Segoe UI Semibold", 10),
|
|
|
|
|
|
padding=(18, 9),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
borderwidth=0,
|
|
|
|
|
|
anchor="w",
|
|
|
|
|
|
)
|
|
|
|
|
|
style.map(
|
|
|
|
|
|
"SidebarSelected.TButton",
|
|
|
|
|
|
background=[
|
2026-05-28 10:50:52 +08:00
|
|
|
|
("active", _mix(sidebar_selected, "#ffffff", 0.06) if dark_theme else _mix(sidebar_selected, "#000000", 0.06)),
|
|
|
|
|
|
("pressed", _mix(sidebar_selected, "#000000", 0.08)),
|
2026-05-28 10:20:17 +08:00
|
|
|
|
],
|
|
|
|
|
|
)
|