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:
parent
07983f292d
commit
5bfdd83e90
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue