Restore custom title bar without native chrome

This commit is contained in:
lm 2025-10-19 18:38:13 +02:00
parent 183b769000
commit 8ed1acc32d
2 changed files with 20 additions and 61 deletions

View File

@ -5,7 +5,6 @@ from __future__ import annotations
import ctypes import ctypes
import platform import platform
import tkinter as tk import tkinter as tk
from ctypes import wintypes
from importlib import resources from importlib import resources
from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin
@ -87,6 +86,7 @@ class ICRAApp(
self._is_maximized = True self._is_maximized = True
self.root.geometry(f"{screen_width}x{screen_height}+0+0") self.root.geometry(f"{screen_width}x{screen_height}+0+0")
self.root.configure(bg="#f2f2f7") self.root.configure(bg="#f2f2f7")
self.root.overrideredirect(True)
self._window_icon_ref = None self._window_icon_ref = None
self._apply_window_icon() self._apply_window_icon()
self._init_window_chrome() self._init_window_chrome()
@ -144,70 +144,19 @@ class ICRAApp(
def _init_window_chrome(self) -> None: def _init_window_chrome(self) -> None:
"""Configure a borderless window while retaining a taskbar entry.""" """Configure a borderless window while retaining a taskbar entry."""
try: try:
system = platform.system() self.root.bind("<Map>", self._restore_borderless)
if system == "Windows": self.root.after(0, self._restore_borderless)
self.root.after(0, self._apply_windows_borderless_style) self.root.after(0, self._ensure_taskbar_entry)
self.root.after(0, self._ensure_taskbar_entry)
self.root.bind("<Map>", lambda _e: self._apply_windows_borderless_style(), add="+")
self.root.bind("<Map>", lambda _e: self._ensure_taskbar_entry(), add="+")
else:
self.root.overrideredirect(True)
except Exception: 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: try:
dwmapi = ctypes.windll.dwmapi # type: ignore[attr-defined] self.root.overrideredirect(True)
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),
)
except Exception: except Exception:
pass pass
def _restore_borderless(self, _event=None) -> None:
try:
self.root.overrideredirect(True)
self._ensure_taskbar_entry()
except Exception: except Exception:
pass pass

View File

@ -566,7 +566,17 @@ class UIBuilderMixin:
def _minimize_window(self) -> None: def _minimize_window(self) -> None:
try: try:
self._remember_window_geometry() self._remember_window_geometry()
if hasattr(self.root, "overrideredirect"):
try:
self.root.overrideredirect(False)
except Exception:
pass
self.root.iconify() 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: except Exception:
pass pass