From e2bf3f3f9182b16bbe71197ffb2ffd38d16bfd80 Mon Sep 17 00:00:00 2001 From: Andrew Ward Date: Fri, 21 Mar 2025 18:29:43 +0000 Subject: [PATCH] update presets area to scale to fit scaled window sizes --- .gitignore | 4 +- .../presets_manager.cpython-312.pyc | Bin 29658 -> 32551 bytes utils/presets_manager.py | 103 +++++++++++++++--- 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 15798d0..ed871af 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ last_output.wav vs/ .zip -config/ \ No newline at end of file +config/ + +**/__pycache__/ diff --git a/utils/__pycache__/presets_manager.cpython-312.pyc b/utils/__pycache__/presets_manager.cpython-312.pyc index 2f35cbb555980b6f5865d6e08dceda7e166be633..f61c29d2d327c1005325affbce6cd4e6adcad0f5 100644 GIT binary patch delta 8357 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 diff --git a/utils/presets_manager.py b/utils/presets_manager.py index daa3e0a..f60f373 100644 --- a/utils/presets_manager.py +++ b/utils/presets_manager.py @@ -36,9 +36,15 @@ class PresetsManager: self.chevron_left = self.get_icon("assets/icons/chevron-left-black.png", 16) self.chevron_right_small = self.get_icon("assets/icons/chevron-right-black.png", 16) + # Store the original row weights to restore when presets are collapsed + self.original_row_weights = {} + # Initialize the UI components self.create_presets_section() + # Bind to window resize for responsive layout + self.parent.bind("", self.on_window_resize) + def create_presets_section(self): """Create the presets section UI with accordion behavior.""" # Accordion frame to show/hide presets section @@ -59,7 +65,8 @@ class PresetsManager: ) self.presets_button.pack(side=tk.LEFT, padx=0, pady=2) - self.presets_frame.grid(column=0, row=7, columnspan=2, sticky=(tk.W, tk.E)) + # Make presets frame expandable + self.presets_frame.grid(column=0, row=7, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S)) # Tabs for categories with scrolling arrows self.tab_frame = ttk.Frame(self.presets_frame) @@ -110,10 +117,13 @@ class PresetsManager: ) self.right_arrow.pack(side=tk.RIGHT, padx=1) - # Presets display area with a fixed height and vertical scrollbar - self.presets_canvas = Canvas(self.presets_frame, height=250, width=self.presets_frame.winfo_width(), - bg=bg_color, highlightthickness=0) - self.presets_scrollbar = Scrollbar(self.presets_frame, orient="vertical", command=self.presets_canvas.yview) + # Presets display area - now using pack with fill=BOTH and expand=True for responsiveness + self.presets_container = ttk.Frame(self.presets_frame) + self.presets_container.pack(fill=tk.BOTH, expand=True) + + # Presets canvas that will expand with the window + self.presets_canvas = Canvas(self.presets_container, bg=bg_color, highlightthickness=0) + self.presets_scrollbar = Scrollbar(self.presets_container, orient="vertical", command=self.presets_canvas.yview) self.presets_canvas.configure(yscrollcommand=self.presets_scrollbar.set) # Frame inside the canvas to hold presets @@ -121,7 +131,7 @@ class PresetsManager: self.presets_canvas.create_window((0, 0), window=self.presets_scrollable_frame, anchor="nw") # Pack the canvas and scrollbar - self.presets_canvas.pack(side=tk.LEFT, fill=tk.X, expand=True) + self.presets_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.presets_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # Configure the scroll region to update when the frame changes @@ -136,6 +146,38 @@ class PresetsManager: self.toggle_presets() self.enable_mouse_wheel_scrolling() + def on_window_resize(self, event=None): + """Handler for window resize events to adjust the presets layout.""" + # Only proceed if event is from the main window and presets are visible + if event and event.widget == self.parent and not self.presets_collapsed: + # Schedule a refresh after a short delay to prevent excessive updates during resize + if hasattr(self, 'resize_timer') and self.resize_timer: + self.parent.after_cancel(self.resize_timer) + self.resize_timer = self.parent.after(100, self.refresh_presets_display) + + def _adjust_row_weights(self): + """Adjust row weights to prioritize presets area expansion.""" + # Store current weights if we haven't already + if not self.original_row_weights: + for i in range(7): # Store weights for rows 0-6 (main content area) + self.original_row_weights[i] = self.parent.grid_rowconfigure(i, "weight") + + # Set all main content rows to have weight 0 (fixed height) + for i in range(7): + self.parent.grid_rowconfigure(i, weight=0) + + # Set presets row to have all the weight (expandable) + self.parent.grid_rowconfigure(7, weight=1) + + def _restore_row_weights(self): + """Restore original row weights when presets are collapsed.""" + if self.original_row_weights: + for row, weight in self.original_row_weights.items(): + self.parent.grid_rowconfigure(row, weight=weight) + + # Reset presets row weight + self.parent.grid_rowconfigure(7, weight=0) + def setup_preset_card_styles(self, bg_color, accent_color): """Set up common styles for preset cards once to avoid recreating them repeatedly.""" preset_bg_color = "#f0f4f8" # Light blue-gray that contrasts with the main background @@ -251,13 +293,26 @@ class PresetsManager: else: display_phrases = next((cat["phrases"] for cat in self.presets if cat["category"] == self.current_category), []) - # Update canvas width to calculate dynamic column width - self.presets_canvas.update_idletasks() - preset_width = max(self.presets_canvas.winfo_width() // 3, 150) # Minimum width of 150 + # Clear existing preset layout + for widget in self.presets_scrollable_frame.winfo_children(): + widget.destroy() + + # Calculate number of columns based on available width + canvas_width = self.presets_canvas.winfo_width() + # Ensure we have a minimum width to calculate with + if canvas_width < 50: # If the canvas is too narrow or not yet realized + canvas_width = self.parent.winfo_width() - 30 # Estimate canvas width + + # Calculate number of columns (minimum 1, maximum 20) + min_card_width = 140 # Minimum width for each card + num_columns = max(1, min(20, canvas_width // min_card_width)) + + # Dynamically adjust card width based on available space + preset_width = max(min_card_width, canvas_width // num_columns - 8) preset_height = 100 # Configure columns to fill available space - for col in range(3): + for col in range(num_columns): self.presets_scrollable_frame.columnconfigure(col, weight=1) # Populate filtered presets in grid layout @@ -265,17 +320,21 @@ class PresetsManager: # Create a unique identifier for the card card_id = f"{phrase['text']}" + # Calculate row and column based on the dynamic column count + row = i // num_columns + col = i % num_columns + # Create a frame with no border for cleaner look frame = ttk.Frame(self.presets_scrollable_frame, width=preset_width, height=preset_height) - frame.grid(row=i // 4, column=i % 4, padx=3, pady=3, sticky="nsew") + frame.grid(row=row, column=col, padx=3, pady=3, sticky="nsew") frame.grid_propagate(False) # Create inner frame with distinct background and no border - use common style inner_frame = ttk.Frame(frame, style='PresetCard.TFrame') inner_frame.pack(fill=tk.BOTH, expand=True, padx=1, pady=1) - self.presets_scrollable_frame.grid_columnconfigure(i % 4, weight=1) # Make columns expandable - self.presets_scrollable_frame.grid_rowconfigure(i // 4, weight=1) # Make rows expandable + # Make sure all rows are expandable too + self.presets_scrollable_frame.grid_rowconfigure(row, weight=1) # Text label with truncation for long text - use common style wrapped_text = self.wrap_text(phrase["text"], max_lines=3, max_chars_per_line=20) @@ -448,12 +507,26 @@ class PresetsManager: self.presets_frame.grid() # Update button icon to down chevron while preserving text self.presets_button.configure(image=self.chevron_down, text=" Presets") - self.parent.geometry(self.parent.default_geometry) + self.parent.geometry(self.parent.default_geometry) + # Adjust row weights to give all expansion space to presets + self._adjust_row_weights() + # Refresh the presets display to adjust for the new window size + self.refresh_presets_display() else: self.presets_frame.grid_remove() # Update button icon to right chevron while preserving text self.presets_button.configure(image=self.chevron_right, text=" Presets") - self.parent.geometry(self.parent.untoggled_geometry) + + # Add extra padding to the untoggled geometry to prevent button from being cut off + # Parse the current untoggled geometry + width, height = map(int, self.parent.untoggled_geometry.split('x')) + # Add 15 pixels to the height to prevent button cutoff + adjusted_height = height + 15 + adjusted_geometry = f"{width}x{adjusted_height}" + + self.parent.geometry(adjusted_geometry) + # Restore original row weights + self._restore_row_weights() self.presets_collapsed = not self.presets_collapsed def save_current_text_as_preset(self):