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.
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.
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.
## Project Layout

View File

@ -24,9 +24,9 @@ class UIBuilderMixin:
("💾", self._t("toolbar.save_overlay"), self.save_overlay),
("", self._t("toolbar.toggle_free_draw"), self.toggle_exclusion_mode),
("🧹", 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.reset_sliders"), self.reset_sliders),
("", self._t("toolbar.cs2_tool"), self.open_cs2_pattern_tool),
("🌓", self._t("toolbar.toggle_theme"), self.toggle_theme),
]
self._toolbar_buttons: list[dict[str, object]] = []

View File

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

View File

@ -7,7 +7,7 @@
"toolbar.save_overlay" = "Save overlay"
"toolbar.clear_excludes" = "Clear exclusions"
"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.reset_sliders" = "Reset sliders"
"toolbar.toggle_theme" = "Toggle theme"

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import json
import threading
from importlib import resources
from pathlib import Path
from typing import Any, Iterable, Optional
@ -11,6 +12,7 @@ import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import requests
from PIL import Image, ImageTk
from urllib.parse import urlparse
@ -176,10 +178,13 @@ class CS2PatternTool(tk.Toplevel):
self.app = app
self.fetcher = CS2PatternFetcher()
self.title(self._t("cs2.title"))
self.geometry("520x320")
self.minsize(480, 300)
self.configure(bg=self._background_colour())
self.geometry("540x360")
self.minsize(520, 320)
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.patterns_var = tk.StringVar()
@ -194,12 +199,12 @@ class CS2PatternTool(tk.Toplevel):
self._start_loading()
self.protocol("WM_DELETE_WINDOW", self._on_close)
self._bring_to_front()
# UI construction --------------------------------------------------
def _init_widgets(self) -> None:
frame = ttk.Frame(self)
frame.pack(fill=tk.BOTH, expand=True, padx=16, pady=16)
frame = self.body
top = ttk.Frame(frame)
top.pack(fill=tk.X, pady=(0, 12))
@ -251,6 +256,78 @@ class CS2PatternTool(tk.Toplevel):
)
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 -----------------------------------------------------
def _start_loading(self) -> None:
@ -376,6 +453,20 @@ class CS2PatternTool(tk.Toplevel):
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:
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)
app._cs2_tool_window = window # type: ignore[attr-defined]
return window