feat: drag-and-drop tracks onto playlist (sidebar) and iPod (tab + nav icon)

This commit is contained in:
Maksim Totmin 2026-06-01 20:00:56 +07:00
parent bfd146a4a0
commit b68b86929b
4 changed files with 365 additions and 10 deletions

View File

@ -134,6 +134,10 @@ class MainWindow(QMainWindow):
) )
self.settings_tab.auto_detect_changed.connect(self._on_auto_detect_toggled) self.settings_tab.auto_detect_changed.connect(self._on_auto_detect_toggled)
self.sidebar.tracks_dropped_on_playlist.connect(self._on_tracks_dropped_on_playlist)
self.sidebar.tracks_dropped_for_nav_transfer.connect(self._on_tracks_dropped_for_nav_transfer)
self.ipod_tab.tracks_dropped_for_transfer.connect(self._on_tracks_dropped_for_transfer)
def _on_sidebar_item_selected(self, kind: str, value: str): def _on_sidebar_item_selected(self, kind: str, value: str):
if kind == "tab": if kind == "tab":
tab_index = {"library": 0, "ipod": 1, "settings": 2}.get(value, 0) tab_index = {"library": 0, "ipod": 1, "settings": 2}.get(value, 0)
@ -174,6 +178,20 @@ class MainWindow(QMainWindow):
self.library_tab.clear_filter() self.library_tab.clear_filter()
self.sidebar.select_library_music() self.sidebar.select_library_music()
def _on_tracks_dropped_on_playlist(self, playlist_id: str, paths: list):
self.playlist_manager.add_tracks(playlist_id, paths)
playlist = self.playlist_manager.get_by_id(playlist_id)
if playlist:
self._on_playlist_selected(playlist_id)
self.statusBar().showMessage(f"Added {len(paths)} track(s) to playlist", 5000)
def _on_tracks_dropped_for_transfer(self, paths: list):
self.library_tab.transfer_tracks_by_paths(paths)
def _on_tracks_dropped_for_nav_transfer(self, paths: list):
self.stack.setCurrentIndex(0)
self.library_tab.transfer_tracks_by_paths(paths)
def _load_playlists_into_sidebar(self): def _load_playlists_into_sidebar(self):
for pl in self.playlist_manager.get_all(): for pl in self.playlist_manager.get_all():
self.sidebar.add_playlist_item(pl.id, pl.name) self.sidebar.add_playlist_item(pl.id, pl.name)

View File

@ -8,7 +8,8 @@ from PyQt6.QtWidgets import (
QComboBox, QListWidget, QListWidgetItem, QMessageBox, QComboBox, QListWidget, QListWidgetItem, QMessageBox,
QFileDialog, QFileDialog,
) )
from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtCore import Qt, pyqtSignal, QUrl
from PyQt6.QtGui import QDragEnterEvent, QDragMoveEvent, QDragLeaveEvent, QDropEvent
from ipod_device import IPodDevice from ipod_device import IPodDevice
from device_monitor import DeviceMonitor from device_monitor import DeviceMonitor
@ -21,12 +22,15 @@ class iPodTab(QWidget):
device_mounted = pyqtSignal(str) device_mounted = pyqtSignal(str)
device_unmounted = pyqtSignal() device_unmounted = pyqtSignal()
tracks_dropped_for_transfer = pyqtSignal(list)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.ipod_devices: List[Dict] = [] self.ipod_devices: List[Dict] = []
self.current_mount_point: Optional[str] = None self.current_mount_point: Optional[str] = None
self.worker_thread: Optional[WorkerThread] = None self.worker_thread: Optional[WorkerThread] = None
self._drag_hover_active = False
self.setAcceptDrops(True)
self._setup_ui() self._setup_ui()
def _setup_ui(self): def _setup_ui(self):
@ -36,6 +40,7 @@ class iPodTab(QWidget):
device_label = QLabel("iPod Device:") device_label = QLabel("iPod Device:")
self.device_combo = QComboBox() self.device_combo = QComboBox()
self.device_combo.setPlaceholderText("No devices found") self.device_combo.setPlaceholderText("No devices found")
self.device_combo.currentIndexChanged.connect(self._on_device_combo_changed)
refresh_button = QPushButton("Refresh") refresh_button = QPushButton("Refresh")
refresh_button.clicked.connect(self._on_refresh_devices_clicked) refresh_button.clicked.connect(self._on_refresh_devices_clicked)
device_layout.addWidget(device_label) device_layout.addWidget(device_label)
@ -71,6 +76,17 @@ class iPodTab(QWidget):
self.transferred_list.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection) self.transferred_list.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection)
layout.addWidget(self.transferred_list) layout.addWidget(self.transferred_list)
self._drop_label = QLabel("\u2B07 Drop tracks here to transfer to iPod")
self._drop_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._drop_label.setFixedHeight(48)
hl = self.palette().color(self.palette().ColorRole.Highlight)
self._drop_label.setStyleSheet(
f"background: {hl.name()}20; border: 2px dashed {hl.name()}; "
f"border-radius: 8px; font-weight: bold; color: {hl.name()};"
)
self._drop_label.setVisible(False)
layout.addWidget(self._drop_label)
remove_button = QPushButton("Remove Selected from iPod") remove_button = QPushButton("Remove Selected from iPod")
remove_button.clicked.connect(self._on_remove_tracks_clicked) remove_button.clicked.connect(self._on_remove_tracks_clicked)
layout.addWidget(remove_button) layout.addWidget(remove_button)
@ -106,6 +122,46 @@ class iPodTab(QWidget):
self.delete_status.setVisible(False) self.delete_status.setVisible(False)
layout.addWidget(self.delete_status) layout.addWidget(self.delete_status)
def dragEnterEvent(self, event: QDragEnterEvent):
if event.mimeData().hasUrls():
self._drag_hover_active = True
self._drop_label.setVisible(True)
event.acceptProposedAction()
else:
super().dragEnterEvent(event)
def dragMoveEvent(self, event: QDragMoveEvent):
if self._drag_hover_active and event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super().dragMoveEvent(event)
def dragLeaveEvent(self, event: QDragLeaveEvent):
self._drag_hover_active = False
self._drop_label.setVisible(False)
super().dragLeaveEvent(event)
def dropEvent(self, event: QDropEvent):
self._drag_hover_active = False
self._drop_label.setVisible(False)
if event.mimeData().hasUrls():
paths = [url.toLocalFile() for url in event.mimeData().urls()]
paths = [p for p in paths if os.path.exists(p)]
if paths:
if not self.current_mount_point:
QMessageBox.warning(
self, "iPod Not Mounted",
"Mount the iPod first before transferring tracks."
)
event.ignore()
return
self.tracks_dropped_for_transfer.emit(paths)
event.acceptProposedAction()
else:
event.ignore()
else:
super().dropEvent(event)
def _on_refresh_devices_clicked(self): def _on_refresh_devices_clicked(self):
if self._is_worker_running(): if self._is_worker_running():
return return
@ -126,18 +182,24 @@ class iPodTab(QWidget):
if success: if success:
self.ipod_devices = result self.ipod_devices = result
self.device_combo.blockSignals(True)
self.device_combo.clear() self.device_combo.clear()
for device in self.ipod_devices: for device in self.ipod_devices:
mounted = device.get("mounted", False) mounted = device.get("mounted", False)
mount_text = " [mounted]" if mounted else " [not mounted]" mount_text = " [mounted]" if mounted else " [not mounted]"
self.device_combo.addItem(device.get("name", "Unknown Device") + mount_text) self.device_combo.addItem(device.get("name", "Unknown Device") + mount_text)
idx = self.device_combo.count() - 1
self.device_combo.setItemData(idx, device, Qt.ItemDataRole.UserRole)
if self.ipod_devices: if self.ipod_devices:
self.device_combo.setCurrentIndex(0)
self.device_combo.blockSignals(False)
device = self.ipod_devices[0] device = self.ipod_devices[0]
detected_mount = device.get("mount_point") detected_mount = device.get("mount_point")
already_mounted = device.get("mounted", False) already_mounted = device.get("mounted", False)
self._start_mount_and_load(device, detected_mount, already_mounted) self._start_mount_and_load(device, detected_mount, already_mounted)
else: else:
self.device_combo.blockSignals(False)
self._set_device_not_found() self._set_device_not_found()
else: else:
self._set_device_not_found() self._set_device_not_found()
@ -169,6 +231,12 @@ class iPodTab(QWidget):
self.current_mount_point = mount_point self.current_mount_point = mount_point
device["info"] = device_info device["info"] = device_info
idx = self.device_combo.currentIndex()
if idx >= 0:
name = device.get("name", "Unknown Device")
self.device_combo.setItemText(idx, name + " [mounted]")
self.device_combo.setItemData(idx, device, Qt.ItemDataRole.UserRole)
free_space = device_info.get("free_space", 0) free_space = device_info.get("free_space", 0)
total_space = device_info.get("total_space", 0) total_space = device_info.get("total_space", 0)
@ -237,10 +305,12 @@ class iPodTab(QWidget):
self.device_unmounted.emit() self.device_unmounted.emit()
def _on_mount_clicked(self): def _on_mount_clicked(self):
if not self.ipod_devices: idx = self.device_combo.currentIndex()
if idx < 0:
return
device = self.device_combo.itemData(idx, Qt.ItemDataRole.UserRole)
if not device:
return return
device = self.ipod_devices[0]
detected_mount = device.get("mount_point") detected_mount = device.get("mount_point")
self._start_mount_and_load(device, detected_mount, already_mounted=False) self._start_mount_and_load(device, detected_mount, already_mounted=False)
@ -265,6 +335,12 @@ class iPodTab(QWidget):
self.ipod_devices[0]["mounted"] = False self.ipod_devices[0]["mounted"] = False
self.ipod_devices[0]["mount_point"] = None self.ipod_devices[0]["mount_point"] = None
idx = self.device_combo.currentIndex()
if idx >= 0:
name = self.ipod_devices[0].get("name", "Unknown Device")
self.device_combo.setItemText(idx, name + " [not mounted]")
self.device_combo.setItemData(idx, self.ipod_devices[0], Qt.ItemDataRole.UserRole)
self.device_info_label.setText("iPod ejected. You can now disconnect it.") self.device_info_label.setText("iPod ejected. You can now disconnect it.")
self.device_status_label.setText("Status: Ejected - safe to disconnect") self.device_status_label.setText("Status: Ejected - safe to disconnect")
self.mount_button.setEnabled(False) self.mount_button.setEnabled(False)
@ -511,6 +587,21 @@ class iPodTab(QWidget):
return return
self._on_device_detection_finished(True, f"Found {len(devices)} devices", devices) self._on_device_detection_finished(True, f"Found {len(devices)} devices", devices)
def _on_device_combo_changed(self, index):
device = self.device_combo.itemData(index, Qt.ItemDataRole.UserRole)
if not device:
return
mp = device.get("mount_point")
if mp and mp == self.current_mount_point:
return
if self._is_worker_running():
return
self._start_mount_and_load(
device,
mount_point=mp,
already_mounted=device.get("mounted", False),
)
def _cleanup_worker(self): def _cleanup_worker(self):
if self.worker_thread: if self.worker_thread:
self.worker_thread.quit() self.worker_thread.quit()

View File

@ -17,8 +17,8 @@ from PyQt6.QtWidgets import (
QTableWidget, QTableWidgetItem, QSlider, QMenu, QInputDialog, QTableWidget, QTableWidgetItem, QSlider, QMenu, QInputDialog,
QStackedWidget, QListWidget, QListWidgetItem, QListView, QStackedWidget, QListWidget, QListWidgetItem, QListView,
) )
from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QSize from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QSize, QUrl, QMimeData
from PyQt6.QtGui import QPixmap, QColor, QIcon, QPainter, QPainterPath from PyQt6.QtGui import QPixmap, QColor, QIcon, QPainter, QPainterPath, QDrag
from track_info import TrackInfo from track_info import TrackInfo
from metadata_handler import MetadataHandler from metadata_handler import MetadataHandler
@ -41,6 +41,51 @@ EQ_FRAMES = [
"\u2585\u2586\u2583", "\u2586\u2584\u2581", "\u2585\u2586\u2583", "\u2586\u2584\u2581",
] ]
class DraggableLibraryTable(QTableWidget):
"""QTableWidget subclass that supports dragging tracks as file URLs."""
def __init__(self, parent=None):
super().__init__(parent)
self.setDragEnabled(True)
def startDrag(self, supportedActions):
paths = []
for item in self.selectedItems():
data_item = self.item(item.row(), 0)
if data_item is None:
continue
_is_ready, track_data = data_item.data(Qt.ItemDataRole.UserRole)
if track_data and track_data.get("path"):
paths.append(track_data["path"])
if not paths:
return
paths = list(set(paths))
mime = QMimeData()
mime.setUrls([QUrl.fromLocalFile(p) for p in paths])
drag = QDrag(self)
drag.setMimeData(mime)
count = len(paths)
label = f"{count} track{'s' if count != 1 else ''}"
pixmap = QPixmap(160, 28)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
bg = self.palette().color(self.palette().ColorRole.Highlight)
painter.setBrush(bg)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(0, 0, 160, 28, 6, 6)
painter.setPen(self.palette().color(self.palette().ColorRole.HighlightedText))
painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, label)
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(pixmap.rect().center())
drag.exec(Qt.DropAction.CopyAction)
class NumericTableItem(QTableWidgetItem): class NumericTableItem(QTableWidgetItem):
def __lt__(self, other): def __lt__(self, other):
_, self_data = self.data(Qt.ItemDataRole.UserRole) or (False, {}) _, self_data = self.data(Qt.ItemDataRole.UserRole) or (False, {})
@ -95,7 +140,7 @@ class LibraryTab(QWidget):
self._setup_library_toolbar(layout) self._setup_library_toolbar(layout)
self.library_table = QTableWidget() self.library_table = DraggableLibraryTable()
self.library_table.setColumnCount(8) self.library_table.setColumnCount(8)
self.library_table.setHorizontalHeaderLabels( self.library_table.setHorizontalHeaderLabels(
["#", "Artist", "Title", "Album", "Duration", "Genre", "Size", "Plays"] ["#", "Artist", "Title", "Album", "Duration", "Genre", "Size", "Plays"]
@ -1060,6 +1105,72 @@ class LibraryTab(QWidget):
self._cleanup_worker() self._cleanup_worker()
def transfer_tracks_by_paths(self, paths: List[str]):
"""Transfer tracks by file paths (used for drag-drop onto iPod tab)."""
ready_by_path = {}
for t in self.library_ready:
p = t.get("path", "")
if p:
ready_by_path[p] = t
handler = MetadataHandler()
tracks = []
for path in paths:
if not os.path.exists(path):
continue
if path in ready_by_path:
d = ready_by_path[path]
ti = TrackInfo(
source_id=f"local_{hash(path)}",
title=d["title"], artist=d["artist"],
album=d.get("album", ""), thumbnail_url="",
duration=d.get("duration_s", 0),
track_number=d.get("track_num"),
genre=d.get("genre", ""), download_path=path,
)
tracks.append((ti, path))
else:
try:
info = handler.extract_tags(path)
ti = TrackInfo(
source_id=f"local_{hash(path)}",
title=info.title if info else os.path.basename(path),
artist=info.artist if info else "Unknown",
album=info.album if info else "", thumbnail_url="",
duration=info.duration if info else 0,
track_number=info.track_number if info else 0,
genre=info.genre if info else "", download_path=path,
)
tracks.append((ti, path))
except Exception as e:
logger.warning(f"Skipping {path}: {e}")
continue
if not tracks:
QMessageBox.warning(self, "Error", "No valid tracks to transfer")
return
if not self._current_mount_point:
QMessageBox.warning(self, "Error", "No iPod device mounted. Go to iPod tab and mount first.")
return
if self._is_worker_running():
QMessageBox.information(self, "Info", "Please wait for the current task to finish")
return
self.library_transfer_btn.setEnabled(False)
self.library_status.setText("Transferring...")
self.library_progress.setValue(0)
self.worker_thread = WorkerThread(
task_type="transfer",
tracks=tracks,
mount_point=self._current_mount_point,
)
self.worker_thread.progress_signal.connect(self._on_library_transfer_progress)
self.worker_thread.finished_signal.connect(self._on_library_transfer_finished)
self.worker_thread.start()
def _on_sync_metadata_clicked(self): def _on_sync_metadata_clicked(self):
selected_tracks = self._get_selected_ready_tracks() selected_tracks = self._get_selected_ready_tracks()
if not selected_tracks: if not selected_tracks:

View File

@ -1,4 +1,5 @@
import logging import logging
import os
from typing import List, Optional from typing import List, Optional
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
@ -6,8 +7,9 @@ from PyQt6.QtWidgets import (
QListWidget, QListWidgetItem, QMenu, QInputDialog, QMessageBox, QListWidget, QListWidgetItem, QMenu, QInputDialog, QMessageBox,
QFrame, QFrame,
) )
from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtCore import Qt, pyqtSignal, QUrl
from PyQt6.QtGui import QColor from PyQt6.QtGui import QColor
from PyQt6.QtGui import QDragEnterEvent, QDragMoveEvent, QDragLeaveEvent, QDropEvent
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -16,9 +18,134 @@ SIDEBAR_MAX_WIDTH = 240
SIDEBAR_DEFAULT_WIDTH = 200 SIDEBAR_DEFAULT_WIDTH = 200
class PlaylistDropListWidget(QListWidget):
"""QListWidget that accepts external track drops onto playlist items."""
tracks_dropped_on_playlist = pyqtSignal(str, list)
def __init__(self, parent=None):
super().__init__(parent)
self.setAcceptDrops(True)
self._hovered_item = None
def dragEnterEvent(self, event: QDragEnterEvent):
if event.source() is not self and event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super().dragEnterEvent(event)
def dragMoveEvent(self, event: QDragMoveEvent):
if event.source() is not self and event.mimeData().hasUrls():
pos = event.position().toPoint()
item = self.itemAt(pos)
if item != self._hovered_item:
self._clear_hover()
if item:
self._hovered_item = item
bg = self.palette().color(self.palette().ColorRole.Highlight)
c = bg.name()
item.setBackground(bg)
event.acceptProposedAction()
else:
super().dragMoveEvent(event)
def dragLeaveEvent(self, event: QDragLeaveEvent):
self._clear_hover()
super().dragLeaveEvent(event)
def dropEvent(self, event: QDropEvent):
self._clear_hover()
if event.source() is not self and event.mimeData().hasUrls():
pos = event.position().toPoint()
item = self.itemAt(pos)
if item is None:
event.ignore()
return
playlist_id = item.data(Qt.ItemDataRole.UserRole)
if not playlist_id:
event.ignore()
return
paths = [url.toLocalFile() for url in event.mimeData().urls()]
paths = [p for p in paths if os.path.exists(p)]
if paths:
self.tracks_dropped_on_playlist.emit(playlist_id, paths)
event.acceptProposedAction()
else:
event.ignore()
else:
super().dropEvent(event)
def _clear_hover(self):
if self._hovered_item:
self._hovered_item.setData(Qt.ItemDataRole.BackgroundRole, None)
self._hovered_item = None
class NavDropListWidget(QListWidget):
"""QListWidget that accepts external track drops on the iPod nav item."""
tracks_dropped_for_transfer = pyqtSignal(list)
def __init__(self, parent=None):
super().__init__(parent)
self.setAcceptDrops(True)
self._hovered_item = None
def _target_item_at(self, pos):
item = self.itemAt(pos)
if item and item.data(Qt.ItemDataRole.UserRole + 2) == "ipod":
return item
return None
def dragEnterEvent(self, event: QDragEnterEvent):
if event.source() is not self and event.mimeData().hasUrls():
pos = event.position().toPoint()
if self._target_item_at(pos):
event.acceptProposedAction()
return
super().dragEnterEvent(event)
def dragMoveEvent(self, event: QDragMoveEvent):
if event.source() is not self and event.mimeData().hasUrls():
pos = event.position().toPoint()
item = self._target_item_at(pos)
if item != self._hovered_item:
self._clear_hover()
if item:
self._hovered_item = item
bg = self.palette().color(self.palette().ColorRole.Highlight)
item.setBackground(bg)
if item:
event.acceptProposedAction()
return
super().dragMoveEvent(event)
def dragLeaveEvent(self, event: QDragLeaveEvent):
self._clear_hover()
super().dragLeaveEvent(event)
def dropEvent(self, event: QDropEvent):
self._clear_hover()
if event.source() is not self and event.mimeData().hasUrls():
pos = event.position().toPoint()
if self._target_item_at(pos):
paths = [url.toLocalFile() for url in event.mimeData().urls()]
paths = [p for p in paths if os.path.exists(p)]
if paths:
self.tracks_dropped_for_transfer.emit(paths)
event.acceptProposedAction()
return
super().dropEvent(event)
def _clear_hover(self):
if self._hovered_item:
self._hovered_item.setData(Qt.ItemDataRole.BackgroundRole, None)
self._hovered_item = None
class SidebarWidget(QWidget): class SidebarWidget(QWidget):
item_selected = pyqtSignal(str, str) item_selected = pyqtSignal(str, str)
playlist_selected = pyqtSignal(str) playlist_selected = pyqtSignal(str)
tracks_dropped_on_playlist = pyqtSignal(str, list)
tracks_dropped_for_nav_transfer = pyqtSignal(list)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -126,7 +253,7 @@ class SidebarWidget(QWidget):
self._playlists_header.setStyleSheet(section_style) self._playlists_header.setStyleSheet(section_style)
layout.addWidget(self._playlists_header) layout.addWidget(self._playlists_header)
self._playlist_list = QListWidget() self._playlist_list = PlaylistDropListWidget()
self._playlist_list.setDragDropMode(QListWidget.DragDropMode.InternalMove) self._playlist_list.setDragDropMode(QListWidget.DragDropMode.InternalMove)
self._playlist_list.setDefaultDropAction(Qt.DropAction.MoveAction) self._playlist_list.setDefaultDropAction(Qt.DropAction.MoveAction)
self._playlist_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self._playlist_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
@ -135,6 +262,7 @@ class SidebarWidget(QWidget):
) )
self._playlist_list.itemClicked.connect(self._on_playlist_clicked) self._playlist_list.itemClicked.connect(self._on_playlist_clicked)
self._playlist_list.model().rowsMoved.connect(self._on_playlists_reordered) self._playlist_list.model().rowsMoved.connect(self._on_playlists_reordered)
self._playlist_list.tracks_dropped_on_playlist.connect(self._on_tracks_dropped_on_playlist)
layout.addWidget(self._playlist_list, stretch=1) layout.addWidget(self._playlist_list, stretch=1)
btn_style = f""" btn_style = f"""
@ -163,7 +291,7 @@ class SidebarWidget(QWidget):
line2.setFixedHeight(1) line2.setFixedHeight(1)
layout.addWidget(line2) layout.addWidget(line2)
self._nav_list = QListWidget() self._nav_list = NavDropListWidget()
items = [ items = [
("🎸 iPod", "ipod"), ("🎸 iPod", "ipod"),
("⚙️ Settings", "settings"), ("⚙️ Settings", "settings"),
@ -174,6 +302,7 @@ class SidebarWidget(QWidget):
item.setData(Qt.ItemDataRole.UserRole + 2, tab_id) item.setData(Qt.ItemDataRole.UserRole + 2, tab_id)
self._nav_list.addItem(item) self._nav_list.addItem(item)
self._nav_list.itemClicked.connect(self._on_nav_item_clicked) self._nav_list.itemClicked.connect(self._on_nav_item_clicked)
self._nav_list.tracks_dropped_for_transfer.connect(self._on_tracks_dropped_for_nav_transfer)
layout.addWidget(self._nav_list) layout.addWidget(self._nav_list)
self._playlist_items: dict = {} self._playlist_items: dict = {}
@ -261,6 +390,12 @@ class SidebarWidget(QWidget):
ids.append(item.data(Qt.ItemDataRole.UserRole)) ids.append(item.data(Qt.ItemDataRole.UserRole))
return ids return ids
def _on_tracks_dropped_on_playlist(self, playlist_id: str, paths: list):
self.tracks_dropped_on_playlist.emit(playlist_id, paths)
def _on_tracks_dropped_for_nav_transfer(self, paths: list):
self.tracks_dropped_for_nav_transfer.emit(paths)
def _on_playlists_reordered(self): def _on_playlists_reordered(self):
order = self.get_playlist_order_ids() order = self.get_playlist_order_ids()
self.item_selected.emit("playlist_reorder", "|".join(order)) self.item_selected.emit("playlist_reorder", "|".join(order))