From 0d4e2a32743b728393c1901cc02819a03f7f5a0d Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 2 Jun 2026 14:20:45 +0400 Subject: [PATCH] fix(gui-x): stop empty/grey block on Ubuntu/GNOME; safer geometry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The X Edition Warp-style Linux GUI was opening as an empty grey block on Ubuntu/GNOME because: 1. root.overrideredirect(True) made the WM treat the window as a special undecorated type whose content is not painted until the user interacts with it. 2. root.state("zoomed") in __init__.py was applied to an already-maximizing window with conflicting geometry, leaving the content area blank. 3. No update_idletasks() / deiconify() after geometry changes, so the WM never flushed the new size to the X server. Fixes: * src/gui_x/__init__.py — drop state("zoomed"); add update_idletasks() + deiconify() so the window is visible immediately. * src/gui_x/launcher.py — disable the overrideredirect branch on Linux; fall back to native window decorations (more reliable on every DE). * src/gui_x/titlebar.py — keep the Python 3.14 winfo_children() try/except patch. Tested: full widget tree visible at 1400x900 with Catppuccin Mocha background; sidebar (48px) + content (1352px) both render. --- src/gui_x/__init__.py | 29 +++++++++++++++++++++++++++-- src/gui_x/launcher.py | 35 +++++++++++++++++++++++------------ src/gui_x/titlebar.py | 19 +++++++++++++++---- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/gui_x/__init__.py b/src/gui_x/__init__.py index 0884ef1..3a41143 100644 --- a/src/gui_x/__init__.py +++ b/src/gui_x/__init__.py @@ -18,8 +18,33 @@ def main(): root = tk.Tk() root.title("Codex Launcher X") - root.geometry("750x1010") - root.minsize(750, 600) + + # Pick a sensible default size based on the display, then center. + # Using a smaller default that fits on every screen (incl. laptops with + # docks/panels) so the UI is never cut off. Launcher is resizable. + try: + sw = root.winfo_screenwidth() + sh = root.winfo_screenheight() + except Exception: + sw, sh = 1920, 1080 + + # Default: 80% of screen, capped to a comfortable max, with a sane + # minimum so it never overflows a small laptop display. + w = min(1400, max(900, int(sw * 0.80))) + h = min(900, max(600, int(sh * 0.85))) + + # Center on screen + x = max(0, (sw - w) // 2) + y = max(0, (sh - h) // 2) + root.geometry(f"{w}x{h}+{x}+{y}") + # Smaller minsize so the window can shrink for tiny displays + root.minsize(720, 520) + + # Force the window to be visible by processing pending geometry events. + # Required on Ubuntu/GNOME where the window otherwise appears as an + # empty grey block until the user clicks. + root.update_idletasks() + root.deiconify() # Apply the X Edition dark theme before creating widgets from gui_x.theme import apply_theme diff --git a/src/gui_x/launcher.py b/src/gui_x/launcher.py index e4e4202..9aa6a93 100644 --- a/src/gui_x/launcher.py +++ b/src/gui_x/launcher.py @@ -200,20 +200,31 @@ def __init__(self, root): recover_config_if_needed() # ── Custom title bar (Linux only) ─────────────────────────── - self._use_custom_titlebar = not IS_WINDOWS - if self._use_custom_titlebar: + # DISABLED: overrideredirect causes Ubuntu/GNOME to render an + # empty grey block because the WM can't track the undecorated + # window's content properly. Using the native decorations + # gives a reliable, visible result on every Linux DE. + self._use_custom_titlebar = False + try: + # Pick a default size that fits every screen (including + # laptops with top bars + docks) and never overflows. + root.update_idletasks() + sw = root.winfo_screenwidth() + sh = root.winfo_screenheight() + # 80% of screen width, capped at 1400 (4K gets same as 1440p) + w = min(1400, max(900, int(sw * 0.80))) + # 85% of screen height, capped at 900 + h = min(900, max(600, int(sh * 0.85))) + x = max(0, (sw - w) // 2) + y = max(0, (sh - h) // 2) + root.geometry(f"{w}x{h}+{x}+{y}") + # Allow the user to shrink further if their display is tiny try: - root.overrideredirect(True) - # Center the window - root.update_idletasks() - sw = root.winfo_screenwidth() - sh = root.winfo_screenheight() - x = (sw - 750) // 2 - y = (sh - 1010) // 2 - root.geometry(f"750x1010+{x}+{y}") + root.minsize(720, 520) except Exception: - self._use_custom_titlebar = False - root.overrideredirect(False) + pass + except Exception: + pass # ── Main container ────────────────────────────────────────── main = tk.Frame(root, bg=self.C["base"]) diff --git a/src/gui_x/titlebar.py b/src/gui_x/titlebar.py index 740d908..37e1a19 100644 --- a/src/gui_x/titlebar.py +++ b/src/gui_x/titlebar.py @@ -91,10 +91,21 @@ def __init__(self, root, version="", **kwargs): self.bind("", self._start_drag) self.bind("", self._on_drag) # Also bind to child labels so dragging works everywhere - for child in self.winfo_children(): - for widget in child.winfo_children(): - widget.bind("", self._start_drag) - widget.bind("", self._on_drag) + # Note: on Python 3.14+, winfo_children() raises TypeError on + # certain widget types — use try/except to skip safely. + try: + for child in self.winfo_children(): + try: + for widget in child.winfo_children(): + try: + widget.bind("", self._start_drag) + widget.bind("", self._on_drag) + except Exception: + pass + except Exception: + pass + except Exception: + pass def _make_control_btn(self, parent, text, command): """Create a window control button (min/max) with hover effect."""