Polish CS2 fetcher UI

Rename the toolbar action to “Fetch Images”, give the subtool a custom title bar matching the main window, and refresh docs/translations.
This commit is contained in:
lm 2025-10-18 14:49:20 +02:00
parent ff66aeb3c3
commit 07983f292d
5 changed files with 100 additions and 10 deletions

View File

@ -39,7 +39,7 @@ On macOS/Linux activate with `source .venv/bin/activate` instead.
3. Finetune sliders; watch the overlay update on the right. 3. Finetune sliders; watch the overlay update on the right.
4. Toggle freehand mode (`△`) or stick with rectangles and mark areas to exclude (right mouse drag). 4. Toggle freehand mode (`△`) or stick with rectangles and mark areas to exclude (right mouse drag).
5. Move through folder images with `⬅️` / `➡️`; exclusions stay put unless you opt into automatic resets. 5. Move through folder images with `⬅️` / `➡️`; exclusions stay put unless you opt into automatic resets.
6. Open the CS2 pattern tool (`🎯`) to pull skin artwork when you need visual references. 6. Fetch CS2 pattern images (`⬇`) whenever you need additional references.
7. Save an overlay (`💾`) when ready. 7. Save an overlay (`💾`) when ready.
## Project Layout ## Project Layout

View File

@ -24,9 +24,9 @@ class UIBuilderMixin:
("💾", self._t("toolbar.save_overlay"), self.save_overlay), ("💾", self._t("toolbar.save_overlay"), self.save_overlay),
("", self._t("toolbar.toggle_free_draw"), self.toggle_exclusion_mode), ("", self._t("toolbar.toggle_free_draw"), self.toggle_exclusion_mode),
("🧹", self._t("toolbar.clear_excludes"), self.clear_excludes), ("🧹", self._t("toolbar.clear_excludes"), self.clear_excludes),
("🎯", self._t("toolbar.cs2_tool"), self.open_cs2_pattern_tool),
("", self._t("toolbar.undo_exclude"), self.undo_exclude), ("", self._t("toolbar.undo_exclude"), self.undo_exclude),
("🔄", self._t("toolbar.reset_sliders"), self.reset_sliders), ("🔄", self._t("toolbar.reset_sliders"), self.reset_sliders),
("", self._t("toolbar.cs2_tool"), self.open_cs2_pattern_tool),
("🌓", self._t("toolbar.toggle_theme"), self.toggle_theme), ("🌓", self._t("toolbar.toggle_theme"), self.toggle_theme),
] ]
self._toolbar_buttons: list[dict[str, object]] = [] self._toolbar_buttons: list[dict[str, object]] = []

View File

@ -7,7 +7,7 @@
"toolbar.save_overlay" = "Overlay speichern" "toolbar.save_overlay" = "Overlay speichern"
"toolbar.clear_excludes" = "Ausschlüsse löschen" "toolbar.clear_excludes" = "Ausschlüsse löschen"
"toolbar.toggle_free_draw" = "Freihandmodus umschalten" "toolbar.toggle_free_draw" = "Freihandmodus umschalten"
"toolbar.cs2_tool" = "CS2 Muster laden" "toolbar.cs2_tool" = "Bilder abrufen"
"toolbar.undo_exclude" = "Letzten Ausschluss entfernen" "toolbar.undo_exclude" = "Letzten Ausschluss entfernen"
"toolbar.reset_sliders" = "Slider zurücksetzen" "toolbar.reset_sliders" = "Slider zurücksetzen"
"toolbar.toggle_theme" = "Theme umschalten" "toolbar.toggle_theme" = "Theme umschalten"

View File

@ -7,7 +7,7 @@
"toolbar.save_overlay" = "Save overlay" "toolbar.save_overlay" = "Save overlay"
"toolbar.clear_excludes" = "Clear exclusions" "toolbar.clear_excludes" = "Clear exclusions"
"toolbar.toggle_free_draw" = "Toggle free-draw" "toolbar.toggle_free_draw" = "Toggle free-draw"
"toolbar.cs2_tool" = "CS2 pattern fetcher" "toolbar.cs2_tool" = "Fetch Images"
"toolbar.undo_exclude" = "Undo last exclusion" "toolbar.undo_exclude" = "Undo last exclusion"
"toolbar.reset_sliders" = "Reset sliders" "toolbar.reset_sliders" = "Reset sliders"
"toolbar.toggle_theme" = "Toggle theme" "toolbar.toggle_theme" = "Toggle theme"

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import json import json
import threading import threading
from importlib import resources
from pathlib import Path from pathlib import Path
from typing import Any, Iterable, Optional from typing import Any, Iterable, Optional
@ -11,6 +12,7 @@ import tkinter as tk
from tkinter import filedialog, messagebox, ttk from tkinter import filedialog, messagebox, ttk
import requests import requests
from PIL import Image, ImageTk
from urllib.parse import urlparse from urllib.parse import urlparse
@ -176,10 +178,13 @@ class CS2PatternTool(tk.Toplevel):
self.app = app self.app = app
self.fetcher = CS2PatternFetcher() self.fetcher = CS2PatternFetcher()
self.title(self._t("cs2.title")) self.title(self._t("cs2.title"))
self.geometry("520x320") self.geometry("540x360")
self.minsize(480, 300) self.minsize(520, 320)
self.configure(bg=self._background_colour())
self.resizable(True, True) self.resizable(True, True)
self._drag_offset: tuple[int, int] | None = None
self._setup_window()
self.body = ttk.Frame(self)
self.body.pack(fill=tk.BOTH, expand=True, padx=16, pady=(12, 16))
self.weapons_var = tk.StringVar() self.weapons_var = tk.StringVar()
self.patterns_var = tk.StringVar() self.patterns_var = tk.StringVar()
@ -194,12 +199,12 @@ class CS2PatternTool(tk.Toplevel):
self._start_loading() self._start_loading()
self.protocol("WM_DELETE_WINDOW", self._on_close) self.protocol("WM_DELETE_WINDOW", self._on_close)
self._bring_to_front()
# UI construction -------------------------------------------------- # UI construction --------------------------------------------------
def _init_widgets(self) -> None: def _init_widgets(self) -> None:
frame = ttk.Frame(self) frame = self.body
frame.pack(fill=tk.BOTH, expand=True, padx=16, pady=16)
top = ttk.Frame(frame) top = ttk.Frame(frame)
top.pack(fill=tk.X, pady=(0, 12)) top.pack(fill=tk.X, pady=(0, 12))
@ -251,6 +256,78 @@ class CS2PatternTool(tk.Toplevel):
) )
status_label.pack(fill=tk.X) status_label.pack(fill=tk.X)
def _setup_window(self) -> None:
self.overrideredirect(True)
self.configure(bg=self._background_colour())
self._create_titlebar()
def _create_titlebar(self) -> None:
bar_bg = "#1f1f1f"
title_bar = tk.Frame(self, bg=bar_bg, relief="flat", height=34)
title_bar.pack(fill=tk.X, side=tk.TOP)
title_bar.pack_propagate(False)
logo = None
try:
logo_resource = resources.files("app.assets").joinpath("logo.png")
with resources.as_file(logo_resource) as logo_path:
image = Image.open(logo_path).convert("RGBA")
image.thumbnail((26, 26))
logo = ImageTk.PhotoImage(image)
except Exception: # noqa: BLE001
logo = None
if logo is not None:
logo_label = tk.Label(title_bar, image=logo, bg=bar_bg)
logo_label.image = logo
logo_label.pack(side=tk.LEFT, padx=(10, 6), pady=4)
else:
logo_label = None
title_label = tk.Label(
title_bar,
text=self._t("cs2.title"),
bg=bar_bg,
fg="#f5f5f5",
font=("Segoe UI", 11, "bold"),
anchor="w",
)
title_label.pack(side=tk.LEFT, padx=6)
close_btn = tk.Button(
title_bar,
text="",
command=self._on_close,
bg=bar_bg,
fg="#f5f5f5",
activebackground="#ff3b30",
activeforeground="#ffffff",
borderwidth=0,
highlightthickness=0,
relief="flat",
font=("Segoe UI", 10, "bold"),
cursor="hand2",
width=3,
)
close_btn.pack(side=tk.RIGHT, padx=8, pady=4)
close_btn.bind("<Enter>", lambda _e: close_btn.configure(bg="#cf212f"))
close_btn.bind("<Leave>", lambda _e: close_btn.configure(bg=bar_bg))
bind_targets = [title_bar, title_label]
if logo_label is not None:
bind_targets.append(logo_label)
for widget in bind_targets:
widget.bind("<ButtonPress-1>", self._start_window_drag)
widget.bind("<B1-Motion>", self._perform_window_drag)
def _bring_to_front(self) -> None:
try:
self.transient(self.app.root)
self.lift()
self.focus_force()
except Exception: # noqa: BLE001
pass
# Data loading ----------------------------------------------------- # Data loading -----------------------------------------------------
def _start_loading(self) -> None: def _start_loading(self) -> None:
@ -376,6 +453,20 @@ class CS2PatternTool(tk.Toplevel):
parent=self, parent=self,
) )
def _start_window_drag(self, event) -> None: # noqa: ANN001
self._drag_offset = (
event.x_root - self.winfo_rootx(),
event.y_root - self.winfo_rooty(),
)
def _perform_window_drag(self, event) -> None: # noqa: ANN001
offset = getattr(self, "_drag_offset", None)
if offset is None:
return
x = event.x_root - offset[0]
y = event.y_root - offset[1]
self.geometry(f"+{x}+{y}")
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.app, "theme", "light") == "dark" else "#ffffff"
@ -407,4 +498,3 @@ def open_cs2_pattern_tool(app) -> CS2PatternTool:
window = CS2PatternTool(app) window = CS2PatternTool(app)
app._cs2_tool_window = window # type: ignore[attr-defined] app._cs2_tool_window = window # type: ignore[attr-defined]
return window return window