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