diff --git a/utils/__pycache__/text_to_mic.cpython-312.pyc b/utils/__pycache__/text_to_mic.cpython-312.pyc index 5a709b8..c75ce4a 100644 Binary files a/utils/__pycache__/text_to_mic.cpython-312.pyc and b/utils/__pycache__/text_to_mic.cpython-312.pyc differ diff --git a/utils/text_to_mic.py b/utils/text_to_mic.py index 70e3ebe..67a9639 100644 --- a/utils/text_to_mic.py +++ b/utils/text_to_mic.py @@ -195,6 +195,7 @@ class TextToMic(tk.Tk): settings_menu.add_command(label="AI Copy Editing", command=self.show_ai_editor_settings) settings_menu.add_command(label="Hotkey Settings", command=self.show_hotkey_settings) settings_menu.add_command(label="Manage Tone Presets", command=self.show_tone_presets_manager) + settings_menu.add_checkbutton(label="Auto Check for Updates", variable=self.auto_check_version, command=self.toggle_auto_version_check) # Playback menu playback_menu = Menu(self.menubar, tearoff=0) @@ -210,14 +211,12 @@ class TextToMic(tk.Tk): # Help menu help_menu = Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Help", menu=help_menu) + help_menu.add_command(label="Check Version", command=self.check_version) + help_menu.add_command(label="How to Use", command=self.show_instructions) help_menu.add_command(label="Terms of Use and Licence", command=self.show_terms_of_use) - help_menu.add_command(label="Check Version", command=self.check_version) help_menu.add_command(label="Hotkey Instructions", command=self.show_hotkey_instructions) - # Add toggle for automatic version checking - help_menu.add_checkbutton(label="Auto Check for Updates", variable=self.auto_check_version, command=self.toggle_auto_version_check) - # Add toggle for banner visibility - use the existing banner_var from __init__ help_menu.add_checkbutton(label="Hide Banner", variable=self.banner_var, command=self.toggle_banner) @@ -1192,6 +1191,11 @@ class TextToMic(tk.Tk): # Add a new method to toggle banner visibility def toggle_banner(self): """Toggle the visibility of the banner image.""" + # Store notification state before changes + had_notification = False + if hasattr(self, 'version_checker') and self.version_checker.notification_visible: + had_notification = True + settings = self.load_settings() hide_banner = self.banner_var.get() @@ -1239,13 +1243,18 @@ class TextToMic(tk.Tk): # Use grid (not pack) to ensure proper positioning self.presets_manager.presets_button.grid_configure(column=0, row=0, sticky=tk.W, padx=0, pady=2) - # If we have a version notification visible, ensure it remains at the top - if hasattr(self, 'version_checker') and self.version_checker.notification_visible: - self.version_checker.notification_frame.grid(row=0, column=0, sticky="ew") - self.main_frame.grid(row=1, column=0, sticky="nsew") + # If we had a notification, make sure it's correctly positioned after layout changes + if had_notification: + # Need to schedule this after all geometry changes are complete + self.after(100, self._reposition_version_notification) def toggle_presets(self): """Toggle the visibility of the presets panel.""" + # Store notification state before changes + had_notification = False + if hasattr(self, 'version_checker') and self.version_checker.notification_visible: + had_notification = True + if hasattr(self, 'presets_manager'): # Toggle presets via presets manager self.presets_manager.toggle_presets() @@ -1282,10 +1291,10 @@ class TextToMic(tk.Tk): if not self.presets_collapsed: self.presets_manager.refresh_presets_display() - # If we have a version notification visible, ensure it remains at the top - if hasattr(self, 'version_checker') and self.version_checker.notification_visible: - self.version_checker.notification_frame.grid(row=0, column=0, sticky="ew") - self.main_frame.grid(row=1, column=0, sticky="nsew") + # If we had a notification, make sure it's correctly positioned after layout changes + if had_notification: + # Need to schedule this after all geometry changes are complete + self.after(100, self._reposition_version_notification) def update_buttons_for_playback(self, is_playing): """Update button text based on playback state.""" @@ -1362,4 +1371,13 @@ class TextToMic(tk.Tk): settings["auto_check_version"] = self.auto_check_version.get() self.save_settings_to_JSON(settings) + def _reposition_version_notification(self): + """Helper method to reposition version notification after layout changes""" + if hasattr(self, 'version_checker') and self.version_checker.notification_visible: + if hasattr(self.version_checker, 'notification_window') and self.version_checker.notification_window: + # First ensure the notification window is visible + self.version_checker.notification_window.deiconify() + # Then reposition it + self.version_checker._reposition_notification() + diff --git a/utils/version_checker.py b/utils/version_checker.py index fed1a3d..12ec011 100644 --- a/utils/version_checker.py +++ b/utils/version_checker.py @@ -13,7 +13,7 @@ class VersionChecker: self.current_version = version self.version_url = "https://www.scorchsoft.com/public/blog/text-to-mic/leatest-version.json" self.notification_visible = False - self.notification_frame = None + self.notification_window = None def check_version(self, show_result=True): """ @@ -87,17 +87,34 @@ class VersionChecker: )) def show_update_notification(self, latest_version, download_url, message): - """Display an update notification banner in the app""" + """Display an update notification banner as an overlay""" if self.notification_visible: return # Already showing notification - - # Create notification frame - self.notification_frame = ttk.Frame(self.app, style='Notification.TFrame') - # Configure notification style (light yellow background) - self.app.style.configure('Notification.TFrame', background='#fff3cd') - self.app.style.configure('Notification.TLabel', background='#fff3cd', foreground='#856404') - self.app.style.configure('Notification.TButton', background='#fff3cd') + # Create a new toplevel window for the notification + self.notification_window = tk.Toplevel(self.app) + self.notification_window.overrideredirect(True) # Remove window decorations + self.notification_window.attributes('-topmost', True) # Keep on top + + # Calculate position (aligned with top of main window) + app_x = self.app.winfo_rootx() + app_y = self.app.winfo_rooty() + app_width = self.app.winfo_width() + + # Configure the notification window + bg_color = '#fff3cd' # Light yellow background + fg_color = '#856404' # Darker yellow/brown text + + # Create the main frame in the notification window + self.notification_frame = ttk.Frame(self.notification_window, style='Notification.TFrame') + self.notification_frame.pack(fill='both', expand=True) + + # Configure styles + self.app.style.configure('Notification.TFrame', background=bg_color) + self.app.style.configure('Notification.TLabel', background=bg_color, foreground=fg_color) + self.app.style.map('Notification.TButton', + background=[('active', bg_color), ('!active', bg_color)], + foreground=[('active', fg_color), ('!active', fg_color)]) # Create notification content notification_text = message or f"A new version ({latest_version}) is available. You're currently using version {self.current_version}." @@ -106,42 +123,86 @@ class VersionChecker: self.notification_frame, text=notification_text, style='Notification.TLabel', - wraplength=400 + wraplength=app_width - 150 # Allow for button width ) label.grid(row=0, column=0, padx=(10, 5), pady=10, sticky="w") # Create buttons + button_frame = ttk.Frame(self.notification_frame, style='Notification.TFrame') + button_frame.grid(row=0, column=1, padx=5, pady=5) + download_button = ttk.Button( - self.notification_frame, + button_frame, text="Download", + style='Notification.TButton', command=lambda: self.open_download_page(download_url) ) - download_button.grid(row=0, column=1, padx=5, pady=10) + download_button.pack(side='left', padx=5) close_button = ttk.Button( - self.notification_frame, + button_frame, text="×", width=2, + style='Notification.TButton', command=self.dismiss_notification ) - close_button.grid(row=0, column=2, padx=(0, 5), pady=10) + close_button.pack(side='left') - # Insert at the top of the application, below menu - self.notification_frame.grid(row=0, column=0, sticky="ew", padx=0, pady=0) - - # Move other content down - self.app.main_frame.grid(row=1, column=0, sticky="nsew") + # Position the window and set its size + self.notification_window.update_idletasks() # Update to get correct dimensions + notification_height = self.notification_window.winfo_reqheight() + # Mark notification as visible first so _reposition_notification will work self.notification_visible = True + # Setup event binding to follow main window if it's moved + self.app.bind("", self._reposition_notification) + + # Add bindings for window minimize/restore events + self.app.bind("", self._handle_window_unmap) + self.app.bind("", self._handle_window_map) + + # Use the reposition function to set initial position and size + # This ensures consistent sizing between initial load and repositioning + self._reposition_notification() + + def _handle_window_unmap(self, event=None): + """Hide the notification when main window is minimized""" + if self.notification_visible and self.notification_window: + self.notification_window.withdraw() + + def _handle_window_map(self, event=None): + """Show the notification when main window is restored""" + if self.notification_visible and self.notification_window: + self.notification_window.deiconify() + # Reposition after showing + self._reposition_notification() + + def _reposition_notification(self, event=None): + """Reposition the notification window to stay at the top of the main window""" + if self.notification_visible and self.notification_window: + app_x = self.app.winfo_rootx() + app_y = self.app.winfo_rooty() + app_width = self.app.winfo_width() + + # Subtract a small amount to ensure it doesn't extend beyond the window + adjusted_width = app_width - 5 # Adjust by 4 pixels to account for borders + + # Update the width and position + notification_height = self.notification_window.winfo_height() + self.notification_window.geometry(f"{adjusted_width}x{notification_height}+{app_x}+{app_y}") + def dismiss_notification(self): """Remove the notification banner""" - if self.notification_frame: - self.notification_frame.grid_forget() - self.notification_frame = None + if self.notification_window: + # Unbind all the events first + self.app.unbind("") + self.app.unbind("") + self.app.unbind("") - # Move main frame back to top position - self.app.main_frame.grid(row=0, column=0, sticky="nsew") + # Destroy the window + self.notification_window.destroy() + self.notification_window = None self.notification_visible = False