Restyle CS2 tool buttons

Replace ttk actions with custom rounded buttons matching the main toolbar and adjust the window layout accordingly.
This commit is contained in:
lm 2025-10-18 14:52:39 +02:00
parent 07983f292d
commit 5bfdd83e90
1 changed files with 191 additions and 22 deletions

View File

@ -9,6 +9,7 @@ from pathlib import Path
from typing import Any, Iterable, Optional from typing import Any, Iterable, Optional
import tkinter as tk import tkinter as tk
import tkinter.font as tkfont
from tkinter import filedialog, messagebox, ttk from tkinter import filedialog, messagebox, ttk
import requests import requests
@ -177,14 +178,16 @@ class CS2PatternTool(tk.Toplevel):
super().__init__(app.root) super().__init__(app.root)
self.app = app self.app = app
self.fetcher = CS2PatternFetcher() self.fetcher = CS2PatternFetcher()
self.theme = getattr(self.app, "theme", "light")
self.title(self._t("cs2.title")) self.title(self._t("cs2.title"))
self.geometry("540x360") self.geometry("560x380")
self.minsize(520, 320) self.minsize(520, 320)
self.resizable(True, True) self.resizable(True, True)
self._drag_offset: tuple[int, int] | None = None self._drag_offset: tuple[int, int] | None = None
self._setup_window() self._setup_window()
self.body = ttk.Frame(self) self.body = ttk.Frame(self)
self.body.pack(fill=tk.BOTH, expand=True, padx=16, pady=(12, 16)) self.body.pack(fill=tk.BOTH, expand=True, padx=16, pady=(12, 16))
self._toolbar_buttons: list[dict[str, Any]] = []
self.weapons_var = tk.StringVar() self.weapons_var = tk.StringVar()
self.patterns_var = tk.StringVar() self.patterns_var = tk.StringVar()
@ -206,6 +209,11 @@ class CS2PatternTool(tk.Toplevel):
def _init_widgets(self) -> None: def _init_widgets(self) -> None:
frame = self.body frame = self.body
toolbar = ttk.Frame(frame)
toolbar.pack(fill=tk.X, pady=(0, 16))
for icon, label, command in self._toolbar_button_defs():
self._add_toolbar_button(toolbar, icon, label, command)
top = ttk.Frame(frame) top = ttk.Frame(frame)
top.pack(fill=tk.X, pady=(0, 12)) top.pack(fill=tk.X, pady=(0, 12))
ttk.Label(top, text=self._t("cs2.weapon_label")).grid(row=0, column=0, sticky="w") ttk.Label(top, text=self._t("cs2.weapon_label")).grid(row=0, column=0, sticky="w")
@ -233,28 +241,13 @@ class CS2PatternTool(tk.Toplevel):
) )
entry = ttk.Entry(dir_frame, textvariable=self.directory_var) entry = ttk.Entry(dir_frame, textvariable=self.directory_var)
entry.grid(row=1, column=0, sticky="we", padx=(0, 8)) entry.grid(row=1, column=0, sticky="we", padx=(0, 8))
ttk.Button(
dir_frame, text=self._t("cs2.browse_button"), command=self._browse_directory
).grid(row=1, column=1, sticky="e")
dir_frame.columnconfigure(0, weight=1) dir_frame.columnconfigure(0, weight=1)
buttons = ttk.Frame(frame)
buttons.pack(fill=tk.X, pady=(0, 12))
ttk.Button(
buttons, text=self._t("cs2.refresh_button"), command=self._refresh_data
).pack(side=tk.LEFT)
self.download_btn = ttk.Button(
buttons,
text=self._t("cs2.download_button"),
command=self._download_selected,
state="disabled",
)
self.download_btn.pack(side=tk.RIGHT)
status_label = ttk.Label( status_label = ttk.Label(
frame, textvariable=self.status_var, anchor="w", justify="left" frame, textvariable=self.status_var, anchor="w", justify="left"
) )
status_label.pack(fill=tk.X) status_label.pack(fill=tk.X)
self._refresh_toolbar_buttons_theme()
def _setup_window(self) -> None: def _setup_window(self) -> None:
self.overrideredirect(True) self.overrideredirect(True)
@ -388,10 +381,8 @@ class CS2PatternTool(tk.Toplevel):
self.pattern_combo.configure(state="readonly", values=patterns) self.pattern_combo.configure(state="readonly", values=patterns)
if patterns: if patterns:
self.pattern_combo.set(patterns[0]) self.pattern_combo.set(patterns[0])
self.download_btn.configure(state="normal")
else: else:
self.pattern_combo.set("") self.pattern_combo.set("")
self.download_btn.configure(state="disabled")
# Event handlers --------------------------------------------------- # Event handlers ---------------------------------------------------
@ -401,8 +392,7 @@ class CS2PatternTool(tk.Toplevel):
self._populate_patterns(weapon) self._populate_patterns(weapon)
def _on_pattern_selected(self, event=None) -> None: # noqa: ANN001 def _on_pattern_selected(self, event=None) -> None: # noqa: ANN001
if self.patterns_var.get(): return
self.download_btn.configure(state="normal")
def _browse_directory(self) -> None: def _browse_directory(self) -> None:
directory = filedialog.askdirectory(parent=self, mustexist=True) directory = filedialog.askdirectory(parent=self, mustexist=True)
@ -467,8 +457,187 @@ class CS2PatternTool(tk.Toplevel):
y = event.y_root - offset[1] y = event.y_root - offset[1]
self.geometry(f"+{x}+{y}") self.geometry(f"+{x}+{y}")
def _toolbar_button_defs(self) -> list[tuple[str, str, Any]]:
return [
("🔄", self._t("cs2.refresh_button"), self._refresh_data),
("", self._t("cs2.download_button"), self._download_selected),
("📁", self._t("cs2.browse_button"), self._browse_directory),
]
def _toolbar_palette(self) -> dict[str, str]:
if getattr(self, "theme", "light") == "dark":
return {
"normal": "#2f2f35",
"hover": "#3a3a40",
"active": "#1f1f25",
"outline": "#4d4d50",
"outline_focus": "#7c7c88",
"text": "#f1f1f5",
}
return {
"normal": "#ffffff",
"hover": "#ededf4",
"active": "#dcdce6",
"outline": "#d0d0d8",
"outline_focus": "#a9a9b2",
"text": "#1f1f1f",
}
def _add_toolbar_button(self, parent, icon: str, label: str, command) -> None:
font = tkfont.Font(root=self, family="Segoe UI", size=9)
padding_x = 12
gap = font.measure(" ")
icon_width = font.measure(icon) or font.measure(" ")
label_width = font.measure(label)
width = padding_x * 2 + icon_width + gap + label_width
height = 28
radius = 9
bg = self._background_colour()
canvas = tk.Canvas(
parent,
width=width,
height=height,
bd=0,
highlightthickness=0,
bg=bg,
relief="flat",
cursor="hand2",
takefocus=1,
)
canvas.pack(side=tk.LEFT, padx=4, pady=1)
palette = self._toolbar_palette()
rect_id = self._create_round_rect(
canvas,
1,
1,
width - 1,
height - 1,
radius,
fill=palette["normal"],
outline=palette["outline"],
width=1,
)
icon_id = canvas.create_text(
padding_x,
height / 2,
text=icon,
font=font,
fill=palette["text"],
anchor="w",
)
label_id = canvas.create_text(
padding_x + icon_width + gap,
height / 2,
text=label,
font=font,
fill=palette["text"],
anchor="w",
)
button_data = {
"canvas": canvas,
"rect": rect_id,
"text_ids": (icon_id, label_id),
"command": command,
"palette": palette.copy(),
}
self._toolbar_buttons.append(button_data)
def set_fill(state: str) -> None:
pal = button_data["palette"]
canvas.itemconfigure(rect_id, fill=pal[state])
def execute():
command()
def on_press(_event=None):
set_fill("active")
def on_release(event=None):
if event is not None and (
event.x < 0 or event.y < 0 or event.x > width or event.y > height
):
set_fill("normal")
return
set_fill("hover")
canvas.after_idle(execute)
def on_enter(_event):
set_fill("hover")
def on_leave(_event):
set_fill("normal")
def on_focus_in(_event):
pal = button_data["palette"]
canvas.itemconfigure(rect_id, outline=pal["outline_focus"])
def on_focus_out(_event):
pal = button_data["palette"]
canvas.itemconfigure(rect_id, outline=pal["outline"])
def invoke_keyboard(_event=None):
set_fill("active")
canvas.after(120, lambda: set_fill("hover"))
canvas.after_idle(execute)
canvas.bind("<ButtonPress-1>", on_press)
canvas.bind("<ButtonRelease-1>", on_release)
canvas.bind("<Enter>", on_enter)
canvas.bind("<Leave>", on_leave)
canvas.bind("<FocusIn>", on_focus_in)
canvas.bind("<FocusOut>", on_focus_out)
canvas.bind("<space>", invoke_keyboard)
canvas.bind("<Return>", invoke_keyboard)
def _refresh_toolbar_buttons_theme(self) -> None:
if not self._toolbar_buttons:
return
palette = self._toolbar_palette()
bg = self._background_colour()
for data in self._toolbar_buttons:
canvas = data["canvas"]
rect = data["rect"]
text_ids = data["text_ids"]
data["palette"] = palette.copy()
canvas.configure(bg=bg)
canvas.itemconfigure(rect, fill=palette["normal"], outline=palette["outline"])
for text_id in text_ids:
canvas.itemconfigure(text_id, fill=palette["text"])
@staticmethod
def _create_round_rect(canvas: tk.Canvas, x1, y1, x2, y2, radius, **kwargs):
points = [
x1 + radius,
y1,
x2 - radius,
y1,
x2,
y1,
x2,
y1 + radius,
x2,
y2 - radius,
x2,
y2,
x2 - radius,
y2,
x1 + radius,
y2,
x1,
y2,
x1,
y2 - radius,
x1,
y1 + radius,
x1,
y1,
]
return canvas.create_polygon(points, smooth=True, splinesteps=24, **kwargs)
def _background_colour(self) -> str: def _background_colour(self) -> str:
return "#0f0f10" if getattr(self.app, "theme", "light") == "dark" else "#ffffff" return "#0f0f10" if getattr(self, "theme", "light") == "dark" else "#ffffff"
def _t(self, key: str) -> str: def _t(self, key: str) -> str:
translator = getattr(self.app, "translator", None) translator = getattr(self.app, "translator", None)