Coverage for openhcs/textual_tui/widgets/custom_window_bar.py: 0.0%
84 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
1"""
2Custom WindowBar for OpenHCS that removes left button bar and adds separators.
4Extends textual-window WindowBar to customize button layout and appearance.
5"""
7import logging
8from typing import Any
10from textual import work
11from textual.app import ComposeResult
12from textual.widgets import Static
13from textual_window import WindowBar
14from textual_window.windowbar import WindowBarAllButton, WindowBarButton
15from textual_window.window import Window
17logger = logging.getLogger(__name__)
20# ButtonSeparator removed - not using separators anymore
23class CustomWindowBar(WindowBar):
24 """
25 Custom WindowBar that removes the left button bar and adds separators between buttons.
27 Layout: [Window1] | [Window2] | [Window3] [Right All Button]
28 """
30 def __init__(self, **kwargs):
31 """Initialize with logging."""
32 super().__init__(**kwargs)
34 # Verify our methods are being used
36 DEFAULT_CSS = """
37 CustomWindowBar {
38 align: center bottom;
39 background: $panel;
40 }
41 WindowBarButton {
42 height: 1; width: auto;
43 padding: 0 1;
44 border-left: solid $panel-lighten-2;
45 &:hover { background: $panel-lighten-1; }
46 &.pressed { background: $primary; color: $text; }
47 &.right_pressed { background: $accent-darken-3; color: $text; }
48 }
49 WindowBarAllButton {
50 height: 1; width: 1fr; /* Keep 1fr to fill remaining space */
51 padding: 0 1;
52 border-left: solid $panel-lighten-2;
53 &:hover { background: $boost; }
54 &.pressed { background: $panel-lighten-1; }
55 }
56 """
58 def compose(self) -> ComposeResult:
59 """Compose the window bar with only the right button (no left button)."""
60 # Only yield the right button - no left button
61 yield WindowBarAllButton(window_bar=self, id="windowbar_button_right")
63 def on_mount(self) -> None:
64 """Log children after mounting to see what actually exists."""
65 all_children = [f"{child.__class__.__name__}(id={getattr(child, 'id', 'no-id')})" for child in self.children]
67 # Check if both buttons exist
68 try:
69 left_button = self.query_one("#windowbar_button_left")
70 except Exception as e:
71 logger.error(f"🔘 WINDOWBAR MOUNT: Left button missing: {e}")
73 try:
74 right_button = self.query_one("#windowbar_button_right")
75 except Exception as e:
76 logger.error(f"🔘 WINDOWBAR MOUNT: Right button missing: {e}")
80 @work(group="windowbar")
81 async def add_window_button(self, window: Window) -> None:
82 """
83 Add a window button with separator.
85 Override the parent method to add separators between buttons.
86 """
87 try:
89 # Check if button already exists
90 try:
91 existing_button = self.query_one(f"#{window.id}_button")
92 return
93 except Exception:
94 pass # Button doesn't exist, continue with creation
96 display_name = (window.icon + " " + window.name) if window.icon else window.name
97 logger.debug(f"🔘 BUTTON CREATE: Display name = '{display_name}'")
99 # Check if right button exists
100 try:
101 right_button = self.query_one("#windowbar_button_right")
102 logger.debug(f"🔘 BUTTON CREATE: Right button found: {right_button}")
103 except Exception as e:
104 logger.error(f"🔘 BUTTON CREATE: Right button missing! {e}")
105 raise
107 # Add the window button directly (no separators)
108 logger.debug(f"🔘 BUTTON CREATE: Creating WindowBarButton for {window.id}")
109 try:
110 button = WindowBarButton(
111 content=display_name,
112 window=window,
113 window_bar=self,
114 id=f"{window.id}_button",
115 )
116 logger.debug(f"🔘 BUTTON CREATE: WindowBarButton created: {button}")
118 await self.mount(
119 button,
120 before=self.query_one("#windowbar_button_right"),
121 )
122 logger.debug(f"🔘 BUTTON CREATE: Button mounted for {window.id}")
124 # Verify button was actually added
125 try:
126 verify_button = self.query_one(f"#{window.id}_button")
127 except Exception as e:
128 logger.error(f"🔘 BUTTON CREATE: Button not found after mount! {window.id} - {e}")
129 raise
131 except Exception as e:
132 logger.error(f"🔘 BUTTON CREATE: Button mount failed for {window.id} - {e}")
133 raise
135 except Exception as e:
136 logger.error(f"🔘 BUTTON CREATE FAILED: {window.id} - {type(e).__name__}: {e}")
137 import traceback
138 logger.error(f"🔘 BUTTON CREATE TRACEBACK: {traceback.format_exc()}")
139 raise # Re-raise to expose the actual error
141 @work(group="windowbar")
142 async def remove_window_button(self, window: Window) -> None:
143 """
144 Remove a window button.
146 Simplified version without separators.
147 """
148 # Remove the window button
149 try:
150 self.query_one(f"#{window.id}_button").remove()
151 except Exception as e:
152 logger.warning(f"🔘 BUTTON REMOVE FAILED: {window.id} - {e}")
158 def update_window_button_state(self, window: Window, state: bool) -> None:
159 """
160 Override to add comprehensive logging for button state updates.
162 This is called by the WindowManager when a window is minimized or opened.
163 """
165 try:
166 # Log current WindowBar state
167 all_children = [child.id for child in self.children if hasattr(child, 'id')]
168 button_children = [
169 child.id for child in self.children
170 if isinstance(child, WindowBarButton)
171 ]
172 logger.debug(f"🔘 BUTTON UPDATE: All children: {all_children}")
173 logger.debug(f"🔘 BUTTON UPDATE: Button children: {button_children}")
175 # Try to find the button
176 button_id = f"#{window.id}_button"
177 logger.debug(f"🔘 BUTTON UPDATE: Looking for button: {button_id}")
179 button = self.query_one(button_id, WindowBarButton)
180 logger.debug(f"🔘 BUTTON UPDATE: Found button: {button}")
182 # Update the button state
183 if state:
184 button.window_state = True
185 else:
186 button.window_state = False
188 except Exception as e:
189 # Button doesn't exist yet - this might be normal during window creation
190 # But let's log it to see what's happening
191 logger.warning(f"🔘 BUTTON UPDATE FAILED: {window.id} - {type(e).__name__}: {e}")
192 logger.debug(f"🔘 BUTTON UPDATE: Current children: {[child.id for child in self.children if hasattr(child, 'id')]}")
193 # The button should be added later via add_window_button
195 def __getattribute__(self, name):
196 """Override to log when manager accesses our methods."""
197 return super().__getattribute__(name)