From ac90a7bbc88c826c266caf94bfc5de6b1d819a51 Mon Sep 17 00:00:00 2001 From: Mike Perry <26722177+mikeypdev@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:09:13 -0800 Subject: [PATCH 1/3] Fix playlist window resize stepping --- src/ui/playlist_window.py | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/ui/playlist_window.py b/src/ui/playlist_window.py index 55f9f8b..0d24eb6 100644 --- a/src/ui/playlist_window.py +++ b/src/ui/playlist_window.py @@ -141,6 +141,9 @@ def __init__( # State for scrollbar thumb dragging is now managed by scrollbar_manager + # Flag to prevent recursive resize calls when applying constraints + self._applying_resize_constraints = False + self.setMouseTracking(True) # Enable mouse tracking # Initialize timer for updating time display @@ -150,6 +153,9 @@ def __init__( self.time_display_timer.timeout.connect(self.update) self.time_display_timer.start(1000) # Update every second + # Apply stepped resize constraints to ensure initial size follows proper dimensions + self._apply_stepped_resize_constraints() + self.normal_bg_color = QColor(DEFAULT_NORMAL_BG_COLOR) # Default to black self.selected_bg_color = QColor( DEFAULT_SELECTED_BG_COLOR @@ -2209,7 +2215,12 @@ def _draw_playlist_time_status_display(self, painter): self.text_renderer.render_text(painter, display_text, text_x, text_y) def resizeEvent(self, event): + # Call the parent's resize event first to update the size super().resizeEvent(event) + + # Apply stepped resize constraints to ensure the window maintains proper proportions + self._apply_stepped_resize_constraints() + # Cancel any active thumb dragging when window is resized if self.scrollbar_manager.dragging_thumb: self.scrollbar_manager.end_thumb_drag() @@ -2245,6 +2256,44 @@ def resizeEvent(self, event): self.update() # Request a repaint + def _apply_stepped_resize_constraints(self): + """Apply stepped resize constraints to ensure window size follows proper dimensions.""" + # Prevent recursive calls when applying constraints + if self._applying_resize_constraints: + return + + # Get default size for minimum constraints + default_width = self.playlist_spec["layout"]["window"]["default_size"]["width"] + default_height = self.playlist_spec["layout"]["window"]["default_size"]["height"] + + # Calculate what the constrained size should be based on current size + base_height = 58 # 20 (top bar) + 38 (bottom bar) + current_height = self.height() + if current_height > default_height: + resizable_height = current_height - base_height + num_steps = round(resizable_height / SCROLLBAR_GROOVE_HEIGHT) + constrained_height = base_height + (num_steps * SCROLLBAR_GROOVE_HEIGHT) + else: + constrained_height = default_height # Maintain at least default size + + # For width, snap to multiples of the bottom filler width (25px) + current_width = self.width() + filler_width = BOTTOM_FILLER_WIDTH # PLEDIT_BOTTOM_BAR_FILLER width + if current_width > default_width: + resizable_amount = current_width - default_width + num_steps = round(resizable_amount / filler_width) + constrained_width = default_width + num_steps * filler_width + else: + constrained_width = default_width # Ensure it doesn't go below default + + # Only resize if the current size doesn't match the constraints + if self.width() != constrained_width or self.height() != constrained_height: + # Set flag to prevent recursive calls + self._applying_resize_constraints = True + self.resize(constrained_width, constrained_height) + # Reset flag after resize + self._applying_resize_constraints = False + def scroll_up(self): if self.scroll_offset > 0: self.scroll_offset -= 1 From 8cfb2b9d303817912a5cc9893e92a73fa3720f1f Mon Sep 17 00:00:00 2001 From: Mike Perry <26722177+mikeypdev@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:20:01 -0800 Subject: [PATCH 2/3] Enforce album window size when resizing when docked --- src/ui/album_art_window.py | 178 ++++++++++++++++++++++++++++++------- 1 file changed, 146 insertions(+), 32 deletions(-) diff --git a/src/ui/album_art_window.py b/src/ui/album_art_window.py index 3684003..6bac12f 100644 --- a/src/ui/album_art_window.py +++ b/src/ui/album_art_window.py @@ -543,17 +543,10 @@ def mouseMoveEvent(self, event): ) if is_horizontally_docked: - # When docked horizontally, snap to match the height of the main window group - # For vertical resizing of horizontally docked windows, allow increments of main window height (116) - main_height = self.main_window.height() # Usually 116 - # Round to nearest multiple of main window height - target_size = ( - (new_size + main_height // 2) // main_height - ) * main_height - # Ensure minimum size is maintained - target_size = max(self.minimumWidth(), target_size) + # When docked horizontally, snap to match the height of the main window + target_size = self.main_window.height() elif is_vertically_docked: - # When docked vertically, snap to match the width of the main window group + # When docked vertically, snap to match the width of the main window target_size = self.main_window.width() # Also check for snapping to other docked windows like playlist or equalizer @@ -591,12 +584,8 @@ def mouseMoveEvent(self, event): ) if is_horizontally_aligned_with_playlist: - # When docked horizontally to playlist, snap to multiples of main window height - main_height = self.main_window.height() # Usually 116 - target_size = ( - (new_size + main_height // 2) // main_height - ) * main_height - target_size = max(self.minimumWidth(), target_size) + # When docked horizontally to playlist, snap to match the playlist window height + target_size = playlist_window.height() elif is_vertically_aligned_with_playlist: target_size = playlist_window.width() @@ -632,23 +621,20 @@ def mouseMoveEvent(self, event): ) if is_horizontally_aligned_with_eq: - # When docked horizontally to EQ, snap to multiples of main window height - main_height = self.main_window.height() # Usually 116 - target_size = ( - (new_size + main_height // 2) // main_height - ) * main_height - target_size = max(self.minimumWidth(), target_size) + # When docked horizontally to EQ, snap to match the EQ window height + target_size = eq_window.height() elif is_vertically_aligned_with_eq: target_size = eq_window.width() - # Use the calculated target size - new_size = target_size - - # Make sure it's not smaller than minimum size - new_size = max(self.minimumWidth(), new_size) - - # Apply the new size - self.resize(new_size, new_size) + # Use the target size if we're horizontally docked, otherwise maintain square aspect ratio + if target_size is not None: + # Ensure target size is not smaller than minimum size + target_size = max(self.minimumWidth(), target_size) + self.resize(target_size, target_size) + else: + # Maintain square aspect ratio normally + new_size = max(self.minimumWidth(), new_size) + self.resize(new_size, new_size) event.accept() return elif hasattr(self, "_dragging_window") and self._dragging_window: @@ -729,8 +715,136 @@ def resizeEvent(self, event): # Only enforce square when not actively resizing (to avoid recursion) current_size = event.size() if current_size.width() != current_size.height(): - square_size = min(current_size.width(), current_size.height()) - self.resize(square_size, square_size) + # Check if we should snap to docked window size + target_size = None + + # Check if docked to main window (horizontally or vertically) + if self.main_window: + main_window_right = self.main_window.x() + self.main_window.width() + main_window_bottom = self.main_window.y() + self.main_window.height() + + # Check horizontal docking (to left or right of main window) + is_horizontally_docked = ( + ( + abs(self.x() - main_window_right) <= self.dock_margin + ) # Docked to right of main + or ( + abs((self.x() + self.width()) - self.main_window.x()) + <= self.dock_margin + ) # Docked to left of main + ) and ( + self.y() < main_window_bottom + and (self.y() + self.height()) > self.main_window.y() + ) + + # Check vertical docking (above or below main window) + is_vertically_docked = ( + ( + abs(self.y() - main_window_bottom) <= self.dock_margin + ) # Docked below main + or ( + abs((self.y() + self.height()) - self.main_window.y()) + <= self.dock_margin + ) # Docked above main + ) and ( + self.x() < main_window_right + and (self.x() + self.width()) > self.main_window.x() + ) + + if is_horizontally_docked: + # Snap to main window's height when horizontally docked + target_size = self.main_window.height() + elif is_vertically_docked: + # Snap to main window's width when vertically docked (to maintain square aspect ratio) + target_size = self.main_window.width() + + # Check against playlist window if docked + if ( + not target_size and + hasattr(self.main_window, "playlist_window") + and self.main_window.playlist_window.isVisible() + ): + playlist_window = self.main_window.playlist_window + playlist_right = playlist_window.x() + playlist_window.width() + playlist_bottom = playlist_window.y() + playlist_window.height() + + # Check horizontal docking with playlist + is_horizontally_docked_to_playlist = ( + (abs(self.x() - playlist_right) <= self.dock_margin) + or ( + abs((self.x() + self.width()) - playlist_window.x()) + <= self.dock_margin + ) + ) and ( + self.y() < playlist_bottom + and (self.y() + self.height()) > playlist_window.y() + ) + + # Check vertical docking with playlist + is_vertically_docked_to_playlist = ( + (abs(self.y() - playlist_bottom) <= self.dock_margin) + or ( + abs((self.y() + self.height()) - playlist_window.y()) + <= self.dock_margin + ) + ) and ( + self.x() < playlist_right + and (self.x() + self.width()) > playlist_window.x() + ) + + if is_horizontally_docked_to_playlist: + # Snap to playlist's height when horizontally docked + target_size = playlist_window.height() + elif is_vertically_docked_to_playlist: + # Snap to playlist's width when vertically docked (to maintain square aspect ratio) + target_size = playlist_window.width() + + # Check against equalizer window if docked + if ( + not target_size and + hasattr(self.main_window, "equalizer_window") + and self.main_window.equalizer_window.isVisible() + ): + eq_window = self.main_window.equalizer_window + eq_right = eq_window.x() + eq_window.width() + eq_bottom = eq_window.y() + eq_window.height() + + # Check horizontal docking with equalizer + is_horizontally_docked_to_eq = ( + (abs(self.x() - eq_right) <= self.dock_margin) + or ( + abs((self.x() + self.width()) - eq_window.x()) + <= self.dock_margin + ) + ) and ( + self.y() < eq_bottom and (self.y() + self.height()) > eq_window.y() + ) + + # Check vertical docking with equalizer + is_vertically_docked_to_eq = ( + (abs(self.y() - eq_bottom) <= self.dock_margin) + or ( + abs((self.y() + self.height()) - eq_window.y()) + <= self.dock_margin + ) + ) and ( + self.x() < eq_right and (self.x() + self.width()) > eq_window.x() + ) + + if is_horizontally_docked_to_eq: + # Snap to equalizer's height when horizontally docked + target_size = eq_window.height() + elif is_vertically_docked_to_eq: + # Snap to equalizer's width when vertically docked (to maintain square aspect ratio) + target_size = eq_window.width() + + # If we're horizontally docked to a window, use its height as target size + if target_size is not None: + self.resize(target_size, target_size) + else: + # Otherwise, enforce square aspect ratio by default + square_size = min(current_size.width(), current_size.height()) + self.resize(square_size, square_size) # Save the album art window size to preferences # Only save if the window is visible (to avoid saving during initialization) From e39214d3d2b21c526a673236353ef550b2b6ac80 Mon Sep 17 00:00:00 2001 From: Mike Perry <26722177+mikeypdev@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:20:43 -0800 Subject: [PATCH 3/3] Fix formatting after format-check --- src/ui/album_art_window.py | 18 +++++++++++------- src/ui/playlist_window.py | 4 +++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/ui/album_art_window.py b/src/ui/album_art_window.py index 6bac12f..b98caf7 100644 --- a/src/ui/album_art_window.py +++ b/src/ui/album_art_window.py @@ -721,7 +721,9 @@ def resizeEvent(self, event): # Check if docked to main window (horizontally or vertically) if self.main_window: main_window_right = self.main_window.x() + self.main_window.width() - main_window_bottom = self.main_window.y() + self.main_window.height() + main_window_bottom = ( + self.main_window.y() + self.main_window.height() + ) # Check horizontal docking (to left or right of main window) is_horizontally_docked = ( @@ -760,8 +762,8 @@ def resizeEvent(self, event): # Check against playlist window if docked if ( - not target_size and - hasattr(self.main_window, "playlist_window") + not target_size + and hasattr(self.main_window, "playlist_window") and self.main_window.playlist_window.isVisible() ): playlist_window = self.main_window.playlist_window @@ -801,8 +803,8 @@ def resizeEvent(self, event): # Check against equalizer window if docked if ( - not target_size and - hasattr(self.main_window, "equalizer_window") + not target_size + and hasattr(self.main_window, "equalizer_window") and self.main_window.equalizer_window.isVisible() ): eq_window = self.main_window.equalizer_window @@ -817,7 +819,8 @@ def resizeEvent(self, event): <= self.dock_margin ) ) and ( - self.y() < eq_bottom and (self.y() + self.height()) > eq_window.y() + self.y() < eq_bottom + and (self.y() + self.height()) > eq_window.y() ) # Check vertical docking with equalizer @@ -828,7 +831,8 @@ def resizeEvent(self, event): <= self.dock_margin ) ) and ( - self.x() < eq_right and (self.x() + self.width()) > eq_window.x() + self.x() < eq_right + and (self.x() + self.width()) > eq_window.x() ) if is_horizontally_docked_to_eq: diff --git a/src/ui/playlist_window.py b/src/ui/playlist_window.py index 0d24eb6..c5352f2 100644 --- a/src/ui/playlist_window.py +++ b/src/ui/playlist_window.py @@ -2264,7 +2264,9 @@ def _apply_stepped_resize_constraints(self): # Get default size for minimum constraints default_width = self.playlist_spec["layout"]["window"]["default_size"]["width"] - default_height = self.playlist_spec["layout"]["window"]["default_size"]["height"] + default_height = self.playlist_spec["layout"]["window"]["default_size"][ + "height" + ] # Calculate what the constrained size should be based on current size base_height = 58 # 20 (top bar) + 38 (bottom bar)