tracklist: Add keyboard shortcuts
The following shortcuts are implemented: - Escape to unselect any selected tracks - Delete to remove selected tracks from the current playlist - <Control>/ to focus the "filter tracks" entry - <Control>l to cycle the loop state of the current playlist - <Control>s to toggle the shuffle state of the current playlist - <Control>Up to move the selected track up one position - <Control>Down to move the selected track down one position I also change the volume up and down shortcuts to use the <Shift> modifier. This matches how other Header shortcuts are triggered, and frees up the non-shifted versions to use here. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
9cf980d967
commit
717fdf39cd
|
@ -195,6 +195,8 @@ class Application(Adw.Application):
|
|||
self.db.settings.bind_setting(f"tracklist.{name}.visible",
|
||||
column, "visible")
|
||||
self.factory.bind_property("visible-playlist", track_list, "playlist")
|
||||
|
||||
self.__add_accelerators(track_list.accelerators)
|
||||
return track_list
|
||||
|
||||
def build_window(self) -> window.Window:
|
||||
|
|
|
@ -124,9 +124,9 @@ class Header(Gtk.HeaderBar):
|
|||
"""Get a list of accelerators for the Header."""
|
||||
res = [ActionEntry("open-file", self._open.activate, "<Control>o"),
|
||||
ActionEntry("decrease-volume", self._volume.decrement,
|
||||
"<Control>Down"),
|
||||
"<Shift><Control>Down"),
|
||||
ActionEntry("increase-volume", self._volume.increment,
|
||||
"<Control>Up"),
|
||||
"<Shift><Control>Up"),
|
||||
ActionEntry("toggle-bg-mode", self._background.activate,
|
||||
"<Shift><Control>b")]
|
||||
if __debug__:
|
||||
|
|
|
@ -4,6 +4,7 @@ from gi.repository import GObject
|
|||
from gi.repository import GLib
|
||||
from gi.repository import Gio
|
||||
from gi.repository import Gtk
|
||||
from ..action import ActionEntry
|
||||
from ..playlist.playlist import Playlist
|
||||
from ..playlist.previous import Previous
|
||||
from .. import db
|
||||
|
@ -143,3 +144,18 @@ class Card(Gtk.Box):
|
|||
self._top_right.set_sensitive(not isinstance(newval, Previous))
|
||||
self.__set_button_state()
|
||||
newval.connect("notify", self.__playlist_notify)
|
||||
|
||||
@property
|
||||
def accelerators(self) -> list[ActionEntry]:
|
||||
"""Get a list of accelerators for the Tracklist."""
|
||||
return [ActionEntry("focus-search-track", self._filter.grab_focus,
|
||||
"<Control>slash"),
|
||||
ActionEntry("clear-selected-tracks", self._unselect.activate,
|
||||
"Escape", enabled=(self._unselect, "sensitive")),
|
||||
ActionEntry("cycle-loop", self._loop.activate,
|
||||
"<Control>l", enabled=(self._top_right,
|
||||
"sensitive")),
|
||||
ActionEntry("toggle-shuffle", self._shuffle.activate,
|
||||
"<Control>s", enabled=(self._top_right,
|
||||
"sensitive"))] + \
|
||||
self._osd.accelerators
|
||||
|
|
|
@ -4,6 +4,7 @@ from gi.repository import GObject
|
|||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Adw
|
||||
from ..action import ActionEntry
|
||||
from ..buttons import PopoverButton
|
||||
from .. import db
|
||||
from .. import playlist
|
||||
|
@ -257,3 +258,15 @@ class OSD(Gtk.Overlay):
|
|||
self.__selection_changed(self.selection, 0, 0)
|
||||
if self.playlist is not None:
|
||||
self._add.popover_child.playlist = self.playlist.playlist
|
||||
|
||||
@property
|
||||
def accelerators(self) -> list[ActionEntry]:
|
||||
"""Get a list of accelerators for the OSD."""
|
||||
return [ActionEntry("remove-selected-tracks", self._remove.activate,
|
||||
"Delete", enabled=(self._remove, "visible")),
|
||||
ActionEntry("move-track-up", self._move._up.activate,
|
||||
"<Control>Up",
|
||||
enabled=(self._move, "can-move-up")),
|
||||
ActionEntry("move-track-down", self._move._down.activate,
|
||||
"<Control>Down",
|
||||
enabled=(self._move, "can-move-down"))]
|
||||
|
|
|
@ -185,9 +185,9 @@ class TestHeader(tests.util.TestCase):
|
|||
"""Check that the accelerators list is set up properly."""
|
||||
entries = [("open-file", self.header._open.activate, "<Control>o"),
|
||||
("decrease-volume", self.header._volume.decrement,
|
||||
"<Control>Down"),
|
||||
"<Shift><Control>Down"),
|
||||
("increase-volume", self.header._volume.increment,
|
||||
"<Control>Up"),
|
||||
"<Shift><Control>Up"),
|
||||
("toggle-bg-mode", self.header._background.activate,
|
||||
"<Shift><Control>b"),
|
||||
("edit-settings", self.header._settings.activate,
|
||||
|
|
|
@ -128,8 +128,8 @@ class TestEmmental(unittest.TestCase):
|
|||
self.application.build_window()
|
||||
|
||||
for action, accel in [("app.open-file", "<Control>o"),
|
||||
("app.decrease-volume", "<Control>Down"),
|
||||
("app.increase-volume", "<Control>Up"),
|
||||
("app.decrease-volume", "<Shift><Control>Down"),
|
||||
("app.increase-volume", "<Shift><Control>Up"),
|
||||
("app.toggle-bg-mode", "<Shift><Control>b"),
|
||||
("app.edit-settings", "<Shift><Control>s")]:
|
||||
self.assertEqual(self.application.get_accels_for_action(action),
|
||||
|
@ -227,6 +227,16 @@ class TestEmmental(unittest.TestCase):
|
|||
self.application.player = emmental.audio.Player()
|
||||
win = self.application.build_window()
|
||||
|
||||
for action, accel in [("app.focus-search-track", "<Control>slash"),
|
||||
("app.clear-selected-tracks", "Escape"),
|
||||
("app.cycle-loop", "<Control>l"),
|
||||
("app.toggle-shuffle", "<Control>s"),
|
||||
("app.remove-selected-tracks", "Delete"),
|
||||
("app.move-track-up", "<Control>Up"),
|
||||
("app.move-track-down", "<Control>Down")]:
|
||||
self.assertEqual(self.application.get_accels_for_action(action),
|
||||
[accel])
|
||||
|
||||
self.assertEqual(win.tracklist.sql, self.application.db)
|
||||
|
||||
playlist = self.application.db.playlists.create("Test Playlist")
|
||||
|
|
|
@ -456,3 +456,28 @@ class TestOsd(tests.util.TestCase):
|
|||
self.osd.reset()
|
||||
mock_unselect.assert_called()
|
||||
self.assertFalse(self.osd.have_selected)
|
||||
|
||||
def test_accelerators(self):
|
||||
"""Test that the accelerators list is set up properly."""
|
||||
entries = [("remove-selected-tracks", self.osd._remove.activate,
|
||||
["Delete"], self.osd._remove, "visible"),
|
||||
("move-track-up", self.osd._move._up.activate,
|
||||
["<Control>Up"], self.osd._move, "can-move-up"),
|
||||
("move-track-down", self.osd._move._down.activate,
|
||||
["<Control>Down"], self.osd._move, "can-move-down")]
|
||||
|
||||
accels = self.osd.accelerators
|
||||
self.assertIsInstance(accels, list)
|
||||
|
||||
for i, (name, func, accel, gobject, prop) in enumerate(entries):
|
||||
with self.subTest(action=name):
|
||||
self.assertIsInstance(accels[i], emmental.action.ActionEntry)
|
||||
self.assertEqual(accels[i].name, name)
|
||||
self.assertEqual(accels[i].func, func)
|
||||
self.assertEqual(accels[i].accels, accel)
|
||||
|
||||
if gobject and prop:
|
||||
enabled = gobject.get_property(prop)
|
||||
self.assertEqual(accels[i].enabled, enabled)
|
||||
gobject.set_property(prop, not enabled)
|
||||
self.assertEqual(accels[i].enabled, not enabled)
|
||||
|
|
|
@ -250,3 +250,40 @@ class TestTracklist(tests.util.TestCase):
|
|||
self.assertEqual(self.tracklist._Card__scroll_idle(None),
|
||||
GLib.SOURCE_REMOVE)
|
||||
mock_scroll.assert_called_with(None)
|
||||
|
||||
def test_accelerators(self):
|
||||
"""Check that the accelerators list is set up properly."""
|
||||
entries = [("focus-search-track", self.tracklist._filter.grab_focus,
|
||||
["<Control>slash"], None, None),
|
||||
("clear-selected-tracks", self.tracklist._unselect.activate,
|
||||
["Escape"], self.tracklist._unselect, "sensitive"),
|
||||
("cycle-loop", self.tracklist._loop.activate,
|
||||
["<Control>l"], self.tracklist._top_right, "sensitive"),
|
||||
("toggle-shuffle", self.tracklist._shuffle.activate,
|
||||
["<Control>s"], self.tracklist._top_right, "sensitive")]
|
||||
|
||||
accels = self.tracklist.accelerators
|
||||
self.assertIsInstance(accels, list)
|
||||
|
||||
for i, (name, func, accel, gobject, prop) in enumerate(entries):
|
||||
with self.subTest(action=name):
|
||||
self.assertIsInstance(accels[i], emmental.action.ActionEntry)
|
||||
self.assertEqual(accels[i].name, name)
|
||||
self.assertEqual(accels[i].func, func)
|
||||
self.assertListEqual(accels[i].accels, accel)
|
||||
|
||||
if gobject and prop:
|
||||
enabled = gobject.get_property(prop)
|
||||
self.assertEqual(accels[i].enabled, enabled)
|
||||
gobject.set_property(prop, not enabled)
|
||||
self.assertEqual(accels[i].enabled, not enabled)
|
||||
|
||||
start = len(entries)
|
||||
osd_accels = self.tracklist._osd.accelerators
|
||||
for i, accel in enumerate(osd_accels):
|
||||
with self.subTest(name=accel.name):
|
||||
self.assertIsInstance(accels[start + i],
|
||||
emmental.action.ActionEntry)
|
||||
self.assertEqual(accels[start + i].name, accel.name)
|
||||
self.assertEqual(accels[start + i].func, accel.func)
|
||||
self.assertListEqual(accels[start + i].accels, accel.accels)
|
||||
|
|
Loading…
Reference in New Issue