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

1""" 

2Custom WindowBar for OpenHCS that removes left button bar and adds separators. 

3 

4Extends textual-window WindowBar to customize button layout and appearance. 

5""" 

6 

7import logging 

8from typing import Any 

9 

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 

16 

17logger = logging.getLogger(__name__) 

18 

19 

20# ButtonSeparator removed - not using separators anymore 

21 

22 

23class CustomWindowBar(WindowBar): 

24 """ 

25 Custom WindowBar that removes the left button bar and adds separators between buttons. 

26 

27 Layout: [Window1] | [Window2] | [Window3] [Right All Button] 

28 """ 

29 

30 def __init__(self, **kwargs): 

31 """Initialize with logging.""" 

32 super().__init__(**kwargs) 

33 

34 # Verify our methods are being used 

35 

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 """ 

57 

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") 

62 

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] 

66 

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}") 

72 

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}") 

77 

78 

79 

80 @work(group="windowbar") 

81 async def add_window_button(self, window: Window) -> None: 

82 """ 

83 Add a window button with separator. 

84 

85 Override the parent method to add separators between buttons. 

86 """ 

87 try: 

88 

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 

95 

96 display_name = (window.icon + " " + window.name) if window.icon else window.name 

97 logger.debug(f"🔘 BUTTON CREATE: Display name = '{display_name}'") 

98 

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 

106 

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}") 

117 

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}") 

123 

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 

130 

131 except Exception as e: 

132 logger.error(f"🔘 BUTTON CREATE: Button mount failed for {window.id} - {e}") 

133 raise 

134 

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 

140 

141 @work(group="windowbar") 

142 async def remove_window_button(self, window: Window) -> None: 

143 """ 

144 Remove a window button. 

145 

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}") 

153 

154 

155 

156 

157 

158 def update_window_button_state(self, window: Window, state: bool) -> None: 

159 """ 

160 Override to add comprehensive logging for button state updates. 

161 

162 This is called by the WindowManager when a window is minimized or opened. 

163 """ 

164 

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}") 

174 

175 # Try to find the button 

176 button_id = f"#{window.id}_button" 

177 logger.debug(f"🔘 BUTTON UPDATE: Looking for button: {button_id}") 

178 

179 button = self.query_one(button_id, WindowBarButton) 

180 logger.debug(f"🔘 BUTTON UPDATE: Found button: {button}") 

181 

182 # Update the button state 

183 if state: 

184 button.window_state = True 

185 else: 

186 button.window_state = False 

187 

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 

194 

195 def __getattribute__(self, name): 

196 """Override to log when manager accesses our methods.""" 

197 return super().__getattribute__(name)