I'm planning to build on this widget over the next several releases.
It'll eventually be fully adaptable to window size changes made by the
user.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Replacing our tracklist-specific one that caches textures, but not to
local disk.
Implements #53 ("Convert the TrackList to use the Texture Cache")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I make sure to clear an existing texture before setting a new one in
case the user downloads a new file with the same path. Otherwise we'll
end up using a stale texture in the list.
Implements: #54 ("Convert the SideBar to use the Texture Cache")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I use the filepath of the requested item to derive a cache file name in
the user's xdg cachedirectory. I also add a way to update items in the
cache if we detect that the mtime has changed, and support loading items
from the cache if the source file has been deleted.
Implements #52 ("Save the Texture Cache textures to the .cache directory")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The Texture Cache will be used to map filenames to Gdk.Textures loaded
into memory. The application can then re-use textures instead of making
expensive filesystem calls and loading the same images multiple times.
Implements: #51 ("Texture Cache")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The New Tracks playlist shows tracks that have been added within the
past week. We should automatically reload it a few seconds after
midnight to keep it up to date as tracks drop off the list.
Implements: #58 ("Reload New Tracks playlist at midnight")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
An alarm is a callback that triggers at a specific time, rather than at
a specific interval. I build this using GLib.timeout_add_seconds() and
wrapping it with logic to calculate the amount of time until the alarm
should be triggered next.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I found that deleted and restorted tracks were incorrectly showing up in
the "New Tracks" playlist. I can fix this by saving the track added date
when the tracks is deleted. The only thing I can't do easily is get the
added date for tracks that have already been deleted, so I set this to
the date of the database upgrade.
Fixes: #64 ("Save the tracks.added date when deleting")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Prepare for database modifications. The first step is to bump the
database version, and it's cleaner to do that in a separate patch.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is a wrapper function that takes a pathlib.Path object, reads it,
and calls the sqlite3 executescript() function. I update the main
db.Connection object to call this function to set up our database tables
while I'm at it.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Attempting to set this field gives me "expected enumeration type void,
but got PyGLibOptionArg instead". I'm not sure what's wanted here, so
I'm commenting out this line for now and can revisit later.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Rather than trying to implement this myself through manually moving the
scrolled window. It's much easier to simply let Gtk do the work for us.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I've found that during startup, we sometimes try to select the current
playlist before the Gtk sidebar widgets are completely loaded. This
results showing the right section, but not actually selecting a
playlist. We can fix this by selecting the playlist after a short
timeout to give everything a chance to load.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Rather than activating an action through a GLib.Variant, we can use the
newly added scroll_to() function to do most of the work for us.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
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 simplifies the code a lot by letting the TrackList directly call
OSD functions without going through the TrackView. I can also simplify
the TrackView to just contain our columnview.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I also convert my PlaylistRowWidget into a Gtk.ListBoxRow that has the
same functionality. This looks a little nicer, and lets us keep the same
style as the rest of the app.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I convert my SortRow widget into a Gtk.ListBoxRow that has the same
functionality. The main benefit is that it looks nicer in the
Gtk.Popover compared to the Gtk.ListView that I had been using.
I also connect to the listbox "row-activated" signal so I can handle
clicking a specific sort row in the list. Clicking a disabled sort row
will enable it, and clicking an enabled one will reverse the sort order.
I think this is what feels the most natural to the user.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I create a custom Gtk.ListBoxRow for displaying each individual column
name and visibility status. I then bind it to a listbox placed as the
popover button's popover child. This lets me set the 'boxed-list' style
class on the listbox to give it a nicer appearance, and clicking the
label will also toggle column visibility.
Implements: #57 ("Rework visible columns button")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
If the application changes the active or inactive tooltip text, then we
want to apply that to the button depending on what state it currently
has.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I was using this to set some custom styling for the active playlist and
track inside a ListView. I can accomplish the same thing by adding and
removing a style class from the ListRowWidget, and this doesn't break
Gtk internal stuff that changed in the 4.12 release.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
The following shortcuts are implemented:
- <Control>? (<Shift><Control>/) to focus the "filter playlists" entry
- <Control><Alt>g to go to the current playlist
- <Shift><Control>p to open the Playlists section
- <Shift><Control>a to open the Artists section
- <Shift><Control>g to open the Genres section
- <Shift><Control>d to open the Decades section
- <Shift><Control>l to open the Libraries section
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I'm going to add a button to jump to the current playlist, and the first
step is to add an area to put it in the sidebar.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I change it to inherit from Gtk.Box, and append Sections as they are
added. I also add some stand-alone styling to set it apart as its own
widget.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This applies to both the Table and child Playlists models. I'm doing my
own idle handling already for searching, so we can rely on that instead
of needing Gtk to do it. The benefit to this is that we can select
playlists programmatically during startup, since we don't need to worry
about the Table not being fully loaded yet.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is used to notify the model that the rows have changed when it's
not automatically being detected. I first noticed this when attempting
to disable incremental filtering, due to the Artist list not getting
refiltered when new child Albums were added to the Artist.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I scheduled the filter query with first=True so it can run without a
long delay during scanning. Additionally, I cancel any pending filter
calls to discard stale arguments that otherwise would be processed.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
If we have a high priority task, then we want to push it to the front of
the idle queue so it runs as soon as possible.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
It's sometimes desireable to cancel a pending task and re-add it with
new parameters to cut out some unnecessary work.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I combine this with the table's Filter object to show playlists matching
the current search query.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I implement add_year(), remove_year(), and has_year() functions and make
sure we load the set of yearids during startup. Additionally, I have
Years add and remove themselves from Decades as they are created and
deleted.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I implement add_medium(), remove_medium(), and has_medium() functions
and make sure we load the set of mediumids during startup. Additionally,
I have Mediums add and remove themselves from Albums as they are created
and deleted.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I switch over to adding and removing Albums using the generic
add_child() and remove_child() functions. I also switch from using a
KeySet filter holding albumids to a Gtk.CustomFilter that calls
Artist.has_album() to check if an Album is in the set.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
We want to filter out Medium playlists with empty names in the sidebar,
and the easiest way to do that is through a custom filter attached to
the Media table.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>