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

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 

8 

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 

14 

15logger = logging.getLogger(__name__) 

16 

17 

18# ButtonSeparator removed - not using separators anymore 

19 

20 

21class CustomWindowBar(WindowBar): 

22 """ 

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

24 

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

26 """ 

27 

28 def __init__(self, **kwargs): 

29 """Initialize with logging.""" 

30 super().__init__(**kwargs) 

31 

32 # Verify our methods are being used 

33 

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

55 

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

60 

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] 

64 

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

70 

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

75 

76 

77 

78 @work(group="windowbar") 

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

80 """ 

81 Add a window button with separator. 

82 

83 Override the parent method to add separators between buttons. 

84 """ 

85 try: 

86 

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 

93 

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

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

96 

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 

104 

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

115 

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

121 

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 

128 

129 except Exception as e: 

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

131 raise 

132 

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 

138 

139 @work(group="windowbar") 

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

141 """ 

142 Remove a window button. 

143 

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

151 

152 

153 

154 

155 

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

157 """ 

158 Override to add comprehensive logging for button state updates. 

159 

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

161 """ 

162 

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

172 

173 # Try to find the button 

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

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

176 

177 button = self.query_one(button_id, WindowBarButton) 

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

179 

180 # Update the button state 

181 if state: 

182 button.window_state = True 

183 else: 

184 button.window_state = False 

185 

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 

192 

193 def __getattribute__(self, name): 

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

195 return super().__getattribute__(name)