Reapply Windows borderless styling without hiding taskbar entry

This commit is contained in:
lm 2025-10-19 18:44:49 +02:00
parent 1984145043
commit 2bf9776076
2 changed files with 78 additions and 15 deletions

View File

@ -5,6 +5,7 @@ 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
@ -84,9 +85,17 @@ class ICRAApp(
default_y = (screen_height - default_height) // 4 default_y = (screen_height - default_height) // 4
self._window_geometry = f"{default_width}x{default_height}+{default_x}+{default_y}" self._window_geometry = f"{default_width}x{default_height}+{default_x}+{default_y}"
self._is_maximized = True self._is_maximized = True
self._use_overrideredirect = platform.system() != "Windows"
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) try:
self.root.overrideredirect(self._use_overrideredirect)
except Exception:
if self._use_overrideredirect:
try:
self.root.attributes("-type", "splash")
except Exception:
pass
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()
@ -145,22 +154,74 @@ 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:
self.root.bind("<Map>", self._restore_borderless) if platform.system() == "Windows":
self.root.after(0, self._restore_borderless) self.root.after(0, self._ensure_taskbar_entry)
self.root.after(0, self._ensure_taskbar_entry) self.root.after(0, self._apply_windows_borderless_style)
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.bind("<Map>", self._restore_borderless)
self.root.after(0, self._restore_borderless)
self.root.after(0, self._ensure_taskbar_entry)
except Exception: except Exception:
try: pass
self.root.overrideredirect(True)
except Exception:
pass
def _restore_borderless(self, _event=None) -> None: def _restore_borderless(self, _event=None) -> None:
try: try:
self.root.overrideredirect(True) if self._use_overrideredirect:
self.root.overrideredirect(True)
self._ensure_taskbar_entry() self._ensure_taskbar_entry()
except Exception: except Exception:
pass pass
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_MINIMIZEBOX = 0x00020000
WS_MAXIMIZEBOX = 0x00010000
WS_SYSMENU = 0x00080000
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_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU
)
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,
)
except Exception:
pass
def start_app() -> None: def start_app() -> None:
"""Entry point used by the CLI script.""" """Entry point used by the CLI script."""

View File

@ -566,17 +566,19 @@ 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"): use_or = getattr(self, "_use_overrideredirect", False)
if use_or and hasattr(self.root, "overrideredirect"):
try: try:
self.root.overrideredirect(False) self.root.overrideredirect(False)
except Exception: except Exception:
pass pass
self.root.iconify() self.root.iconify()
restorer = getattr(self, "_restore_borderless", None) if use_or:
if callable(restorer): restorer = getattr(self, "_restore_borderless", None)
self.root.after(120, restorer) if callable(restorer):
elif hasattr(self.root, "overrideredirect"): self.root.after(120, restorer)
self.root.after(120, lambda: self.root.overrideredirect(True)) # type: ignore[arg-type] elif hasattr(self.root, "overrideredirect"):
self.root.after(120, lambda: self.root.overrideredirect(True)) # type: ignore[arg-type]
except Exception: except Exception:
pass pass