From 662f6b4df30fb636f5d1bc138c9cded031c125fc Mon Sep 17 00:00:00 2001 From: lm Date: Fri, 17 Oct 2025 14:05:10 +0200 Subject: [PATCH] Enable borderless fullscreen window with custom controls --- app/app.py | 13 ++++--- app/gui/ui.py | 97 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 90 insertions(+), 20 deletions(-) diff --git a/app/app.py b/app/app.py index 97ea947..769904b 100644 --- a/app/app.py +++ b/app/app.py @@ -21,11 +21,7 @@ class ICRSApp( def __init__(self, root: tk.Tk): self.root = root self.root.title("ICRS — Interactive Color Range Analyzer") - try: - self.root.state("zoomed") - except Exception: - pass - self.root.configure(bg="#f2f2f7") + self._setup_window() # Theme and styling self.init_theme() @@ -64,6 +60,13 @@ class ICRSApp( self._init_copy_menu() self.bring_to_front() + def _setup_window(self) -> None: + self.root.overrideredirect(True) + screen_width = self.root.winfo_screenwidth() + screen_height = self.root.winfo_screenheight() + self.root.geometry(f"{screen_width}x{screen_height}+0+0") + self.root.configure(bg="#f2f2f7") + def start_app() -> None: """Entry point used by the CLI script.""" diff --git a/app/gui/ui.py b/app/gui/ui.py index eae4edd..c8c9692 100644 --- a/app/gui/ui.py +++ b/app/gui/ui.py @@ -11,8 +11,10 @@ class UIBuilderMixin: """Constructs the Tkinter UI and common widgets.""" def setup_ui(self) -> None: + self._create_titlebar() + toolbar = ttk.Frame(self.root) - toolbar.pack(fill=tk.X, padx=12, pady=8) + toolbar.pack(fill=tk.X, padx=12, pady=0) buttons = [ ("📂 Bild laden", self.load_image), ("📁 Ordner laden", self.load_folder), @@ -71,11 +73,13 @@ class UIBuilderMixin: left_column = ttk.Frame(main) left_column.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 6)) + left_column.grid_columnconfigure(1, weight=1) + left_column.grid_rowconfigure(0, weight=1) - self._create_navigation_button(left_column, "◀", self.show_previous_image) + self._create_navigation_button(left_column, "◀", self.show_previous_image, column=0) self.canvas_orig = tk.Canvas(left_column, bg="#1e1e1e", highlightthickness=0, relief="flat") - self.canvas_orig.pack(fill=tk.BOTH, expand=True) + self.canvas_orig.grid(row=0, column=1, sticky="nsew") self.canvas_orig.bind("", self.on_canvas_click) self.canvas_orig.bind("", self._exclude_start) self.canvas_orig.bind("", self._exclude_drag) @@ -83,10 +87,13 @@ class UIBuilderMixin: right_column = ttk.Frame(main) right_column.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(6, 0)) + right_column.grid_columnconfigure(0, weight=1) + right_column.grid_rowconfigure(0, weight=1) self.canvas_overlay = tk.Canvas(right_column, bg="#1e1e1e", highlightthickness=0, relief="flat") - self.canvas_overlay.pack(fill=tk.BOTH, expand=True) - self._create_navigation_button(right_column, "▶", self.show_next_image) + self.canvas_overlay.grid(row=0, column=0, sticky="nsew") + self._create_navigation_button(right_column, "▶", self.show_next_image, column=1) + info_frame = ttk.Frame(self.root) info_frame.pack(fill=tk.X, padx=12, pady=(0, 12)) @@ -113,6 +120,7 @@ class UIBuilderMixin: self._attach_copy_menu(self.ratio_label) self.root.bind("", self.disable_pick_mode) + self.root.bind("", self._maybe_focus_window) def add_slider_with_value(self, parent, text, var, minimum, maximum, column=0): cell = ttk.Frame(parent) @@ -317,24 +325,83 @@ class UIBuilderMixin: ] return canvas.create_polygon(points, smooth=True, splinesteps=24, **kwargs) - def _create_navigation_button(self, container, symbol: str, command) -> None: - wrapper = ttk.Frame(container) - wrapper.pack(fill=tk.Y) - + def _create_navigation_button(self, container, symbol: str, command, *, column: int) -> None: + bg = self.root.cget("bg") if hasattr(self.root, "cget") else "#f2f2f7" + container.grid_rowconfigure(0, weight=1) btn = tk.Button( - wrapper, + container, text=symbol, command=command, - font=("Segoe UI", 18, "bold"), + font=("Segoe UI", 26, "bold"), relief="flat", borderwidth=0, - background="#00000000", - activebackground="#00000000", + background=bg, + activebackground=bg, highlightthickness=0, cursor="hand2", + width=2, ) - btn.pack(side=tk.TOP, pady=12) - wrapper.pack_propagate(False) + btn.grid(row=0, column=column, sticky="ns", padx=6) + + def _create_titlebar(self) -> None: + bar_bg = "#1f1f1f" + title_bar = tk.Frame(self.root, bg=bar_bg, relief="flat", height=34) + title_bar.pack(fill=tk.X, side=tk.TOP) + title_bar.pack_propagate(False) + + title_label = tk.Label( + title_bar, + text="ICRS — Interactive Color Range Analyzer", + bg=bar_bg, + fg="#f5f5f5", + font=("Segoe UI", 11, "bold"), + anchor="w", + ) + title_label.pack(side=tk.LEFT, padx=12) + + close_btn = tk.Button( + title_bar, + text="✕", + command=self._close_app, + 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) + + for widget in (title_bar, title_label): + widget.bind("", self._start_window_drag) + widget.bind("", self._perform_window_drag) + + def _close_app(self) -> None: + try: + self.root.destroy() + except Exception: + pass + + def _start_window_drag(self, event) -> None: + self._drag_offset = (event.x_root - self.root.winfo_rootx(), event.y_root - self.root.winfo_rooty()) + + def _perform_window_drag(self, event) -> None: + offset = getattr(self, "_drag_offset", None) + if offset is None: + return + x = event.x_root - offset[0] + y = event.y_root - offset[1] + self.root.geometry(f"+{x}+{y}") + + def _maybe_focus_window(self, _event) -> None: + try: + self.root.focus_set() + except Exception: + pass def _toolbar_palette(self) -> dict[str, str]: is_dark = getattr(self, "theme", "light") == "dark"