add check for updates banner

This commit is contained in:
Andrew Ward
2025-03-22 11:47:57 +00:00
parent 7633788da6
commit 0e3cb8925a
3 changed files with 115 additions and 36 deletions

View File

@@ -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()

View File

@@ -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("<Configure>", self._reposition_notification)
# Add bindings for window minimize/restore events
self.app.bind("<Unmap>", self._handle_window_unmap)
self.app.bind("<Map>", 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("<Configure>")
self.app.unbind("<Unmap>")
self.app.unbind("<Map>")
# 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