From 8ed1acc32da14052889975df4263b50e97a1a4e1 Mon Sep 17 00:00:00 2001 From: lm Date: Sun, 19 Oct 2025 18:38:13 +0200 Subject: [PATCH] Restore custom title bar without native chrome --- app/app.py | 71 ++++++++------------------------------------------- app/gui/ui.py | 10 ++++++++ 2 files changed, 20 insertions(+), 61 deletions(-) diff --git a/app/app.py b/app/app.py index d0f6151..fb96a34 100644 --- a/app/app.py +++ b/app/app.py @@ -5,7 +5,6 @@ from __future__ import annotations import ctypes import platform import tkinter as tk -from ctypes import wintypes from importlib import resources from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin @@ -87,6 +86,7 @@ class ICRAApp( self._is_maximized = True self.root.geometry(f"{screen_width}x{screen_height}+0+0") self.root.configure(bg="#f2f2f7") + self.root.overrideredirect(True) self._window_icon_ref = None self._apply_window_icon() self._init_window_chrome() @@ -144,70 +144,19 @@ class ICRAApp( def _init_window_chrome(self) -> None: """Configure a borderless window while retaining a taskbar entry.""" try: - system = platform.system() - if system == "Windows": - self.root.after(0, self._apply_windows_borderless_style) - self.root.after(0, self._ensure_taskbar_entry) - self.root.bind("", lambda _e: self._apply_windows_borderless_style(), add="+") - self.root.bind("", lambda _e: self._ensure_taskbar_entry(), add="+") - else: - self.root.overrideredirect(True) + self.root.bind("", self._restore_borderless) + self.root.after(0, self._restore_borderless) + self.root.after(0, self._ensure_taskbar_entry) except Exception: - self.root.overrideredirect(True) - - def _apply_windows_borderless_style(self) -> None: - try: - if platform.system() != "Windows": - return - - hwnd = self.root.winfo_id() - if not hwnd: - self.root.after(50, self._apply_windows_borderless_style) - return - - user32 = ctypes.windll.user32 # type: ignore[attr-defined] - GWL_STYLE = -16 - WS_CAPTION = 0x00C00000 - WS_THICKFRAME = 0x00040000 - WS_BORDER = 0x00800000 - SWP_NOSIZE = 0x0001 - SWP_NOMOVE = 0x0002 - SWP_NOZORDER = 0x0004 - SWP_FRAMECHANGED = 0x0020 - - set_window_long = getattr(user32, "SetWindowLongPtrW", user32.SetWindowLongW) - get_window_long = getattr(user32, "GetWindowLongPtrW", user32.GetWindowLongW) - ptr_type = ctypes.c_longlong if ctypes.sizeof(ctypes.c_void_p) == 8 else ctypes.c_long - get_window_long.restype = ptr_type # type: ignore[attr-defined] - get_window_long.argtypes = [wintypes.HWND, ctypes.c_int] # type: ignore[attr-defined] - set_window_long.restype = ptr_type # type: ignore[attr-defined] - set_window_long.argtypes = [wintypes.HWND, ctypes.c_int, ptr_type] # type: ignore[attr-defined] - - style = get_window_long(hwnd, GWL_STYLE) - new_style = style & ~(WS_CAPTION | WS_THICKFRAME | WS_BORDER) - if new_style != style: - set_window_long(hwnd, GWL_STYLE, ptr_type(new_style)) - user32.SetWindowPos( - hwnd, - 0, - 0, - 0, - 0, - 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED, - ) try: - dwmapi = ctypes.windll.dwmapi # type: ignore[attr-defined] - DWMWA_USE_IMMERSIVE_DARK_MODE = 20 - value = ctypes.c_int(1) - dwmapi.DwmSetWindowAttribute( - hwnd, - ctypes.c_uint(DWMWA_USE_IMMERSIVE_DARK_MODE), - ctypes.byref(value), - ctypes.sizeof(value), - ) + self.root.overrideredirect(True) except Exception: pass + + def _restore_borderless(self, _event=None) -> None: + try: + self.root.overrideredirect(True) + self._ensure_taskbar_entry() except Exception: pass diff --git a/app/gui/ui.py b/app/gui/ui.py index a347e20..575d766 100644 --- a/app/gui/ui.py +++ b/app/gui/ui.py @@ -566,7 +566,17 @@ class UIBuilderMixin: def _minimize_window(self) -> None: try: self._remember_window_geometry() + if hasattr(self.root, "overrideredirect"): + try: + self.root.overrideredirect(False) + except Exception: + pass self.root.iconify() + restorer = getattr(self, "_restore_borderless", None) + if callable(restorer): + self.root.after(120, restorer) + elif hasattr(self.root, "overrideredirect"): + self.root.after(120, lambda: self.root.overrideredirect(True)) # type: ignore[arg-type] except Exception: pass