Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 150 additions & 32 deletions src/ui/album_art_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -729,8 +715,140 @@ 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)
Expand Down
51 changes: 51 additions & 0 deletions src/ui/playlist_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -2245,6 +2256,46 @@ 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
Expand Down