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>
It defaults to a None pointer, but calling add_children() will set one
up with an empty set. I also implement functions for adding, removing,
and verifying child playlists.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is similar to a Gtk.FilterListModel, except we already know exactly
which rows are part of the model or not. So we can skip the entire
filtering step and show rows directly instead.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This includes implementing the __contains__() magic method, and adding
signals that are emitted when rows are added, removed, or directly set.
This will allow us to build a model around the rows represented by the
set.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I'm going to expand on this and use it for more than just filtering
Gtk.FilterListModels. Renaming it to something more generic is the first
step.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
It is much easier to pass a single boolean value instead of a Gtk
constant for specifying the icon size.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is used to set the tooltip text to fixed strings based on the
active status of the button. I also update the tooltip text on the
Favorite button in the Now Playing widget at the same time.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I add accelerators for play, pause, next track, previous track, setting
autopause, adding the current track to the favorites playlist, and
scrolling to the current track in the tracklist.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I create can-activate-* properties to indicate if a specific accelerator
can be activated. At the same time, I introduce functions intended to be
called by accelerators to activate each of our widgets.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
All I needed the frame for was to add rounded corners to the
Gtk.Picture, but this had some problems with the Frame expanding beyond
the edges of the picture in some cases. I can get the same effect by
adding the "card" css class to the Picture so hopefully this will work
better.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is a keybinding function that calls into the primary button
activate() function. At the same time, I add an "activate-primary"
signal that is emitted when the primary button is activated.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I add accelerators for opening files, changing the volume, toggling
background mode, and running the settings editor.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I made these functions part of the public interface with option
arguments so they can be used in accelerator callbacks for increasing or
decreasing the volume.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I use the current focus widget to set the "user-editing" property, which
can be used to enable or disable accelerator actions.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This is inspried by the Gio.ActionEntry struct, which I can't figure out
how to get working in Python. I add on a few extra helpful features,
such as:
- Automatically creating a Gio.SimpleAction
- Tracking the desired accelerator keys
- Binding the "enabled" state to a specificed property at construction
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Otherwise we could lose the changes if the app crashes.
Fixes: #63 ("The database isn't being committed enough")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Otherwise we could have data loss if the application crashes.
Fixes: #63 ("The database isn't being committed enough")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
Leaving the database in a dirty state could cause unintentional data
loss if the app crashes.
Fixes: #63 ("The database isn't being committed enough")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This lets us skip a handful of database calls if we detect that we're
setting the active playlist to the same value.
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
I do this unconditionally, so even if the track is already on the
playlist we set it as active. I think this is what the user would
expect, but I'm open to revising this approach later.
Fixes: #62 ("Queueing tracks doesn't activate the Queued Tracks playlist")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>