Skip to content

Commit

Permalink
Add button to tracks view to change tracks sort order
Browse files Browse the repository at this point in the history
Refs: #156
  • Loading branch information
orontee committed Dec 18, 2023
1 parent 25954f5 commit c23ef0e
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 28 deletions.
3 changes: 3 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ The format is based on `Keep a Changelog
Added
-----

- Button to change tracks order in tracks view `#156
<https://github.com/orontee/argos/issues/156>`_

- Display volume button on playing box (if it makes sense to control
volume) `#145 <https://github.com/orontee/argos/issues/145>`_

Expand Down
5 changes: 5 additions & 0 deletions argos/controllers/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def __init__(self, application: "Application"):
self._on_library_default_uri_changed,
)
self._settings.connect("changed::album-sort", self._on_album_sort_changed)
self._settings.connect("changed::track-sort", self._on_track_sort_changed)

def _on_index_mopidy_local_albums_changed(
self,
Expand All @@ -108,6 +109,10 @@ def _on_album_sort_changed(self, settings: Gio.Settings, key: str) -> None:
album_sort_id = self._settings.get_string("album-sort")
self._model.sort_albums(album_sort_id)

def _on_track_sort_changed(self, settings: Gio.Settings, key: str) -> None:
track_sort_id = self._settings.get_string("track-sort")
self._model.sort_tracks(track_sort_id)

def _get_backend(self, uri: Optional[str]) -> Optional[MopidyBackend]:
for backend in self._model.backends:
if backend.is_responsible_for(uri):
Expand Down
9 changes: 9 additions & 0 deletions argos/model/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ def sort_albums(
for directory in self.directories:
directory.sort_albums(compare_func)

def sort_tracks(
self,
compare_func: Callable[[TrackModel, TrackModel, None], int],
) -> None:
self.tracks.sort(compare_func, None)

for directory in self.directories:
directory.sort_tracks(compare_func)

def is_complete(self) -> bool:
return (
len(self.albums) > 0
Expand Down
6 changes: 6 additions & 0 deletions argos/model/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ def visit_albums(
def get_directory(self, uri: Optional[str]) -> Optional[DirectoryModel]:
return self.props.root_directory.get_directory(uri)

def sort_tracks(
self,
compare_func: Callable[[TrackModel, TrackModel, None], int],
) -> None:
self.props.root_directory.sort_tracks(compare_func)

def get_track(self, uri: Optional[str]) -> Optional[TrackModel]:
return self.props.root_directory.get_track(uri)

Expand Down
35 changes: 30 additions & 5 deletions argos/model/model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import logging
import random
import threading
from datetime import datetime
from functools import partial
Expand Down Expand Up @@ -27,7 +26,11 @@
from argos.model.playback import PlaybackModel
from argos.model.playlist import PlaylistModel, compare_playlists_func
from argos.model.random import RandomTracksChoice, choose_random_tracks
from argos.model.track import TrackModel, compare_tracks_by_name_func
from argos.model.track import (
TrackModel,
compare_tracks_by_name_func,
compare_tracks_by_track_number_func,
)
from argos.model.tracklist import TracklistModel, TracklistTrackModel
from argos.model.utils import WithThreadSafePropertySetter

Expand All @@ -42,6 +45,7 @@ class Model(WithThreadSafePropertySetter, GObject.Object):
"album-completed": (GObject.SIGNAL_RUN_FIRST, None, (str,)),
"album-information-collected": (GObject.SIGNAL_RUN_FIRST, None, (str,)),
"albums-sorted": (GObject.SIGNAL_RUN_FIRST, None, []),
"tracks-sorted": (GObject.SIGNAL_RUN_FIRST, None, []),
"directory-completion-progress": (
GObject.SIGNAL_RUN_FIRST,
None,
Expand Down Expand Up @@ -136,6 +140,27 @@ def _sort_albums() -> None:

GLib.idle_add(_sort_albums)

def _get_track_compare_func(
self, track_sort_id: str
) -> Callable[[TrackModel, TrackModel, None], int]:
if track_sort_id == "by_track_number":
return compare_tracks_by_track_number_func

if track_sort_id != "by_track_name":
LOGGER.warning(f"Unexpecting track sort identifier {track_sort_id!r}")

return compare_tracks_by_name_func

def sort_tracks(self, track_sort_id: str) -> None:
def _sort_tracks() -> None:
compare_func = self._get_track_compare_func(track_sort_id)
self.library.sort_tracks(compare_func)

LOGGER.info(f"Tracks sorted with sort identifier {track_sort_id}")
self.emit("tracks-sorted")

GLib.idle_add(_sort_tracks)

def complete_directory(
self,
uri: str,
Expand Down Expand Up @@ -181,10 +206,10 @@ def _complete_directory():
playlist, compare_playlists_func, None
)

track_sort_id = self._settings.get_string("track-sort")
track_compare_func = self._get_track_compare_func(track_sort_id)
for track in tracks:
directory.tracks.insert_sorted(
track, compare_tracks_by_name_func, None
)
directory.tracks.insert_sorted(track, track_compare_func, None)
else:
LOGGER.debug(f"Won't complete unknown directory with URI {uri}")

Expand Down
13 changes: 13 additions & 0 deletions argos/model/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ def compare_tracks_by_name_func(
return 0


def compare_tracks_by_track_number_func(
a: "TrackModel",
b: "TrackModel",
user_data: None,
) -> int:
if a.track_no < b.track_no:
return -1
elif a.track_no > b.track_no:
return 1

return compare_tracks_by_name_func(a, b, user_data)


class TrackModel(GObject.Object):
"""Model for a track.
Expand Down
27 changes: 26 additions & 1 deletion argos/ui/tracks_view.ui
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkImage" id="play_image">
Expand Down Expand Up @@ -236,6 +236,31 @@
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="sort_tracks_button">
<property name="name">sort-tracks-button</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Sort tracks</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="draw-indicator">True</property>
<child>
<object class="GtkImage" id="sort_image">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-sort-ascending-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
Expand Down
15 changes: 15 additions & 0 deletions argos/widgets/librarywindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ def __init__(self, application: Gtk.Application):
model, self.props.directory_uri, context="album-sorted signal received"
),
)
self._model.connect(
"tracks-sorted",
lambda model: self._update_store(
model, self.props.directory_uri, context="tracks-sorted signal received"
),
)
application.props.download.connect(
"images-downloaded", self._update_store_pixbufs
)
Expand Down Expand Up @@ -472,3 +478,12 @@ def on_sort_albums_activated(
sort_id = target.get_string()
self._settings.set_string("album-sort", sort_id)
action.set_state(target)

def on_sort_tracks_activated(
self,
action: Gio.SimpleAction,
target: GLib.Variant,
) -> None:
sort_id = target.get_string()
self._settings.set_string("track-sort", sort_id)
action.set_state(target)
10 changes: 9 additions & 1 deletion argos/widgets/tracksview.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from argos.model import DirectoryModel, TrackModel
from argos.utils import ms_to_text
from argos.widgets.trackbox import TrackBox
from argos.widgets.utils import default_image_pixbuf
from argos.widgets.utils import TRACK_SORT_CHOICES, default_image_pixbuf

_ = gettext.gettext

Expand Down Expand Up @@ -36,6 +36,8 @@ class TracksView(Gtk.Box):

directory_name_label: Gtk.Label = Gtk.Template.Child()

sort_tracks_button: Gtk.MenuButton = Gtk.Template.Child()

tracks_box: Gtk.ListBox = Gtk.Template.Child()

uri = GObject.Property(type=str, default="")
Expand All @@ -62,12 +64,18 @@ def __init__(self, application: Gtk.Application):

for widget in (
self.play_button,
self.sort_tracks_button,
self.track_selection_button,
self.tracks_box,
):
if self._disable_tooltips:
widget.props.has_tooltip = False

sort_tracks_menu = Gio.Menu()
for id, name in TRACK_SORT_CHOICES.items():
sort_tracks_menu.append(name, f"win.sort-tracks::{id}")
self.sort_tracks_button.set_menu_model(sort_tracks_menu)

self._model.connect(
"notify::network-available", self._handle_connection_changed
)
Expand Down
6 changes: 6 additions & 0 deletions argos/widgets/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,9 @@ def set_list_box_header_with_date_separator(
"by_publication_date": _("Publication date"),
"by_last_modified_date": _("Last modified"),
}


TRACK_SORT_CHOICES = {
"by_track_name": _("Track name"),
"by_track_number": _("Track number"),
}
11 changes: 11 additions & 0 deletions argos/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ def __init__(self, application: Gtk.Application):
"activate", self.props.library_window.on_sort_albums_activated
)

track_sort_id = self._settings.get_string("track-sort")
sort_tracks_action = Gio.SimpleAction.new_stateful(
"sort-tracks",
GLib.VariantType("s"),
GLib.Variant("s", track_sort_id),
)
self.add_action(sort_tracks_action)
sort_tracks_action.connect(
"activate", self.props.library_window.on_sort_tracks_activated
)

self.props.library_window.library_stack.connect(
"notify::visible-child-name", self._on_central_view_or_library_page_changed
)
Expand Down

0 comments on commit c23ef0e

Please sign in to comment.