v0.1.5: sidebar playlist tree auto-scrolls when dragging near its edges
This commit is contained in:
+62
-6
@@ -2,12 +2,14 @@ from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QPushButton, QTreeWidget, QTreeWidgetItem, QMenu,
|
||||
QInputDialog, QMessageBox, QAbstractItemView,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QTimer, pyqtSignal
|
||||
from PyQt6.QtCore import Qt, QTimer, QPoint, pyqtSignal
|
||||
from PyQt6.QtGui import QBrush, QPainter, QPixmap
|
||||
|
||||
from lintunes.models import PlaylistType
|
||||
from lintunes import tagging
|
||||
from lintunes.gui.track_table import parse_tracks_mime, TRACKS_MIME
|
||||
from lintunes.gui.track_table import (
|
||||
parse_tracks_mime, TRACKS_MIME, autoscroll_direction,
|
||||
)
|
||||
from lintunes.gui.playlist_ops import add_tracks_with_dup_check
|
||||
from lintunes.gui.art_window import ArtWindow
|
||||
from lintunes.gui import drag_ghost
|
||||
@@ -166,6 +168,15 @@ class PlaylistTree(QTreeWidget):
|
||||
self.currentItemChanged.connect(self._on_selection_changed)
|
||||
self.itemChanged.connect(self._on_item_renamed)
|
||||
|
||||
# Edge auto-scroll during a drag (Qt's built-in autoscroll never runs
|
||||
# because our dragMoveEvent accepts the event itself). Mirrors
|
||||
# track_table's Round 13 machinery.
|
||||
self._autoscroll_dir = 0
|
||||
self._autoscroll_pos: QPoint | None = None
|
||||
self._autoscroll_timer = QTimer(self)
|
||||
self._autoscroll_timer.setInterval(40)
|
||||
self._autoscroll_timer.timeout.connect(self._autoscroll_tick)
|
||||
|
||||
manager.playlists_changed.connect(self.rebuild)
|
||||
self.rebuild()
|
||||
|
||||
@@ -377,31 +388,41 @@ class PlaylistTree(QTreeWidget):
|
||||
event.ignore()
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
drag_ghost.move(self.viewport().mapToGlobal(event.position().toPoint()))
|
||||
pos = event.position().toPoint()
|
||||
drag_ghost.move(self.viewport().mapToGlobal(pos))
|
||||
if event.source() is self:
|
||||
# Moving a playlist into/out of folders
|
||||
# Moving a playlist into/out of folders. The tree accepts an
|
||||
# internal move somewhere no matter what (empty area = top level),
|
||||
# so edge auto-scroll stays live even over non-folder rows —
|
||||
# otherwise scrolling down to a distant folder would be nearly
|
||||
# impossible past the playlists in between.
|
||||
self._clear_drop_target()
|
||||
target = self.itemAt(event.position().toPoint())
|
||||
target = self.itemAt(pos)
|
||||
kind = target.data(0, KIND_ROLE) if target else ""
|
||||
if kind in ("folder", "") or target is None:
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
event.ignore()
|
||||
self._update_autoscroll(pos, accepted=True)
|
||||
return
|
||||
if not event.mimeData().hasFormat(TRACKS_MIME):
|
||||
self._clear_drop_target()
|
||||
self._update_autoscroll(pos, accepted=False)
|
||||
event.ignore()
|
||||
return
|
||||
target = self.itemAt(event.position().toPoint())
|
||||
target = self.itemAt(pos)
|
||||
if target and target.data(0, KIND_ROLE) == "playlist":
|
||||
self._set_drop_target(target)
|
||||
event.acceptProposedAction()
|
||||
self._update_autoscroll(pos, accepted=True)
|
||||
else:
|
||||
self._clear_drop_target()
|
||||
event.ignore()
|
||||
self._update_autoscroll(pos, accepted=False)
|
||||
|
||||
def dragLeaveEvent(self, event):
|
||||
self._clear_drop_target()
|
||||
self._stop_autoscroll()
|
||||
super().dragLeaveEvent(event)
|
||||
|
||||
def _set_drop_target(self, item):
|
||||
@@ -422,8 +443,43 @@ class PlaylistTree(QTreeWidget):
|
||||
self._drop_target.setForeground(0, self._drop_target_fg)
|
||||
self._drop_target = None
|
||||
|
||||
# ---- drag auto-scroll ----
|
||||
|
||||
def _update_autoscroll(self, pos: QPoint, accepted: bool):
|
||||
"""Start/stop edge auto-scroll based on the drag's position."""
|
||||
self._autoscroll_pos = pos
|
||||
self._autoscroll_dir = autoscroll_direction(
|
||||
pos.y(), self.viewport().height(), accepted)
|
||||
if self._autoscroll_dir and not self._autoscroll_timer.isActive():
|
||||
self._autoscroll_timer.start()
|
||||
elif not self._autoscroll_dir and self._autoscroll_timer.isActive():
|
||||
self._autoscroll_timer.stop()
|
||||
|
||||
def _autoscroll_tick(self):
|
||||
bar = self.verticalScrollBar()
|
||||
before = bar.value()
|
||||
bar.setValue(before + self._autoscroll_dir)
|
||||
if bar.value() == before: # reached the top/bottom — nothing more to do
|
||||
self._autoscroll_timer.stop()
|
||||
return
|
||||
# The cursor may be held still while rows scroll under it, so keep the
|
||||
# drop-target highlight on the row now beneath it (track drags only;
|
||||
# internal playlist moves never set a drop target).
|
||||
if self._autoscroll_pos is not None and self._drop_target is not None:
|
||||
target = self.itemAt(self._autoscroll_pos)
|
||||
if target and target.data(0, KIND_ROLE) == "playlist":
|
||||
self._set_drop_target(target)
|
||||
else:
|
||||
self._clear_drop_target()
|
||||
|
||||
def _stop_autoscroll(self):
|
||||
self._autoscroll_dir = 0
|
||||
self._autoscroll_pos = None
|
||||
self._autoscroll_timer.stop()
|
||||
|
||||
def dropEvent(self, event):
|
||||
self._clear_drop_target()
|
||||
self._stop_autoscroll()
|
||||
target = self.itemAt(event.position().toPoint())
|
||||
if event.source() is self:
|
||||
item = self.currentItem()
|
||||
|
||||
Reference in New Issue
Block a user