Restore custom title bar without native chrome
This commit is contained in:
parent
183b769000
commit
8ed1acc32d
71
app/app.py
71
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("<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)
|
||||
self.root.bind("<Map>", 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue