diff --git a/src/discussions/ui/gui.py b/src/discussions/ui/gui.py index c22ab86..134d081 100644 --- a/src/discussions/ui/gui.py +++ b/src/discussions/ui/gui.py @@ -489,22 +489,17 @@ class DiscussionGUI: if dpg.does_item_exist("dictate_tooltip_window"): return # Already visible - # Get button position for tooltip placement - if dpg.does_item_exist("dictate_btn"): - btn_pos = dpg.get_item_pos("dictate_btn") - # Get the parent window position to calculate absolute position - parent_pos = dpg.get_item_pos("comment_dialog") if dpg.does_item_exist("comment_dialog") else (0, 0) - tooltip_x = parent_pos[0] + btn_pos[0] + 110 # Right of button - tooltip_y = parent_pos[1] + btn_pos[1] + 30 - else: - tooltip_x, tooltip_y = 500, 300 + # Get mouse position for tooltip placement (show near cursor) + mouse_pos = dpg.get_mouse_pos(local=False) + tooltip_x = mouse_pos[0] + 15 + tooltip_y = mouse_pos[1] + 15 self._tooltip_visible = True self._tooltip_fading = False self._tooltip_alpha = 1.0 - self._tooltip_last_mouse_pos = dpg.get_mouse_pos(local=False) + self._tooltip_last_mouse_pos = mouse_pos - # Create tooltip window + # Create tooltip as a popup window (appears above modal) with dpg.window( tag="dictate_tooltip_window", no_title_bar=True, @@ -513,21 +508,26 @@ class DiscussionGUI: no_scrollbar=True, no_collapse=True, no_background=False, + no_focus_on_appearing=True, pos=[tooltip_x, tooltip_y], - autosize=True + autosize=True, + no_open_over_existing_popup=False ): - dpg.add_text("Voice Dictation", tag="tt_title", color=(200, 200, 255)) + dpg.add_text("Voice Dictation", color=(200, 200, 255)) dpg.add_separator() - dpg.add_text("Double-click: Continuous mode", tag="tt_line1") - dpg.add_text(" Transcribed text appends to end.", tag="tt_line2", color=(150, 150, 150)) - dpg.add_text(" Click again to stop.", tag="tt_line3", color=(150, 150, 150)) + dpg.add_text("Double-click: Continuous mode") + dpg.add_text(" Transcribed text appends to end.", color=(150, 150, 150)) + dpg.add_text(" Click again to stop.", color=(150, 150, 150)) dpg.add_spacer(height=5) - dpg.add_text("Click & hold: Walkie-talkie mode", tag="tt_line4") - dpg.add_text(" Inserts at last edited position.", tag="tt_line5", color=(150, 150, 150)) - dpg.add_text(" Release to stop.", tag="tt_line6", color=(150, 150, 150)) + dpg.add_text("Click & hold: Walkie-talkie mode") + dpg.add_text(" Inserts at last edited position.", color=(150, 150, 150)) + dpg.add_text(" Release to stop.", color=(150, 150, 150)) dpg.add_spacer(height=5) - dpg.add_text("Tip: Type at insertion point first", tag="tt_line7", color=(255, 200, 100)) - dpg.add_text("to set cursor position.", tag="tt_line8", color=(255, 200, 100)) + dpg.add_text("Tip: Type at insertion point first", color=(255, 200, 100)) + dpg.add_text("to set cursor position.", color=(255, 200, 100)) + + # Bring tooltip to front + dpg.focus_item("dictate_tooltip_window") def _hide_dictate_tooltip(self): """Hide the custom dictate button tooltip immediately.""" @@ -543,16 +543,29 @@ class DiscussionGUI: self._tooltip_fading = True self._tooltip_fade_start_time = time.time() - def _update_tooltip_fade(self): - """Update tooltip fade animation. Call this from frame callback.""" + def _update_tooltip(self): + """Update tooltip visibility and fade animation. Call this from frame callback.""" import time + # Check if dictate button exists and mouse is over it + if dpg.does_item_exist("dictate_btn"): + # Check if button is hovered using item state + state = dpg.get_item_state("dictate_btn") + is_hovered = state.get("hovered", False) if state else False + + if is_hovered and not self._tooltip_visible: + # Mouse entered button - show tooltip + self._show_dictate_tooltip() + elif not is_hovered and self._tooltip_visible and not self._tooltip_fading: + # Mouse left button - start fade + self._start_tooltip_fade() + if not self._tooltip_visible: return current_mouse = dpg.get_mouse_pos(local=False) - # Check if mouse moved significantly + # Check if mouse moved significantly while tooltip is visible dx = abs(current_mouse[0] - self._tooltip_last_mouse_pos[0]) dy = abs(current_mouse[1] - self._tooltip_last_mouse_pos[1]) @@ -571,21 +584,14 @@ class DiscussionGUI: else: # Calculate alpha (1.0 to 0.0 over fade_duration) self._tooltip_alpha = 1.0 - (elapsed / fade_duration) - # Apply alpha to window - if dpg.does_item_exist("dictate_tooltip_window"): - # DearPyGui doesn't have direct alpha control, so we hide at threshold - if self._tooltip_alpha < 0.1: - self._hide_dictate_tooltip() + # DearPyGui doesn't have direct alpha control, so we hide at threshold + if self._tooltip_alpha < 0.1: + self._hide_dictate_tooltip() def _on_dictate_hover(self, sender, app_data): - """Handle mouse hovering over dictate button.""" + """Handle mouse hovering over dictate button (backup, main check is in _update_tooltip).""" self._show_dictate_tooltip() - def _on_dictate_unhover(self): - """Handle mouse leaving dictate button area - start fade.""" - if self._tooltip_visible: - self._start_tooltip_fade() - def _start_dictation(self): """Start continuous recording with chunked transcription.""" # Create recorder with callbacks @@ -4246,8 +4252,8 @@ final = json.dumps(parsed)''', # Poll for updates from background threads (output, turn completion) self._poll_background_tasks() - # Update tooltip fade animation - self._update_tooltip_fade() + # Update tooltip visibility and fade animation + self._update_tooltip() # Render frame dpg.render_dearpygui_frame()