[gnome-music/wip/jfelder/gtk4-v3] GTK4 port
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/gtk4-v3] GTK4 port
- Date: Thu, 17 Feb 2022 09:23:46 +0000 (UTC)
commit 147d28b51fd5dfcd6ad55c8713a2d1484111d4f3
Author: Jean Felder <jfelder src gnome org>
Date: Thu Feb 17 10:01:25 2022 +0100
GTK4 port
Co-authored-by: Marinus Schraal <mschraal gnome org>
data/org.gnome.Music.css | 66 ++---
data/org.gnome.Music.gresource.xml | 8 +-
data/org.gnome.Music.gschema.xml | 5 -
data/ui/AboutDialog.ui.in | 2 +-
data/ui/AlbumCover.ui | 22 +-
data/ui/AlbumCoverListItem.ui | 51 ++++
data/ui/AlbumWidget.ui | 229 +++++++---------
data/ui/AlbumsView.ui | 69 ++---
data/ui/AppMenu.ui | 36 +--
data/ui/ArtistAlbumsWidget.ui | 20 +-
data/ui/ArtistSearchTile.ui | 26 +-
data/ui/ArtistTile.ui | 12 +-
data/ui/ArtistsView.ui | 31 +--
data/ui/DiscBox.ui | 18 +-
data/ui/DiscListItem.ui | 29 ++
data/ui/EmptyView.ui | 23 +-
data/ui/HeaderBar.ui | 162 +++++-------
data/ui/InitialState.ui | 33 +++
data/ui/LastfmDialog.ui | 16 +-
data/ui/LoadingNotification.ui | 4 +-
data/ui/NotificationsPopup.ui | 3 -
data/ui/PlayerToolbar.ui | 129 +++------
data/ui/PlaylistControls.ui | 107 +++-----
data/ui/PlaylistDialog.ui | 136 +++++-----
data/ui/PlaylistDialogRow.ui | 7 +-
data/ui/PlaylistNotification.ui | 15 +-
data/ui/PlaylistTile.ui | 10 +-
data/ui/PlaylistsView.ui | 25 +-
data/ui/PlaylistsWidget.ui | 13 +-
data/ui/SearchHeaderBar.ui | 97 +++----
data/ui/SearchView.ui | 335 +++++++++++-------------
data/ui/SelectionBarMenuButton.ui | 10 +-
data/ui/SelectionToolbar.ui | 9 +-
data/ui/SongListItem.ui | 72 +++++
data/ui/SongWidget.ui | 159 ++++++-----
data/ui/SongWidgetMenu.ui | 58 ++--
data/ui/SongsView.ui | 124 +--------
data/ui/TwoLineTip.ui | 9 +-
data/ui/Window.ui | 23 +-
data/ui/help-overlay.ui | 18 --
gnome-music.in | 22 +-
gnomemusic/application.py | 16 +-
gnomemusic/artcache.py | 48 ++--
gnomemusic/corealbum.py | 21 +-
gnomemusic/coreartist.py | 19 +-
gnomemusic/coredisc.py | 18 +-
gnomemusic/coregrilo.py | 12 +-
gnomemusic/coremodel.py | 186 ++++++-------
gnomemusic/coverpaintable.py | 118 +++++++++
gnomemusic/defaulticon.py | 105 +-------
gnomemusic/grilowrappers/grlsearchwrapper.py | 8 +-
gnomemusic/grilowrappers/grltrackerplaylists.py | 26 +-
gnomemusic/grilowrappers/grltrackerwrapper.py | 50 ++--
gnomemusic/player.py | 13 +-
gnomemusic/songliststore.py | 116 --------
gnomemusic/utils.py | 34 ++-
gnomemusic/views/albumsview.py | 259 +++++++++---------
gnomemusic/views/artistsview.py | 148 ++++-------
gnomemusic/views/emptyview.py | 12 +-
gnomemusic/views/playlistsview.py | 2 +-
gnomemusic/views/searchview.py | 182 +++----------
gnomemusic/views/songsview.py | 308 ++++++++++++----------
gnomemusic/widgets/albumcover.py | 2 -
gnomemusic/widgets/albumwidget.py | 6 +-
gnomemusic/widgets/appmenu.py | 2 +-
gnomemusic/widgets/artistalbumswidget.py | 58 ++--
gnomemusic/widgets/artistsearchtile.py | 10 +-
gnomemusic/widgets/artisttile.py | 33 ++-
gnomemusic/widgets/artstack.py | 24 +-
gnomemusic/widgets/discbox.py | 24 +-
gnomemusic/widgets/headerbar.py | 42 ++-
gnomemusic/widgets/lastfmdialog.py | 6 +-
gnomemusic/widgets/notificationspopup.py | 9 +-
gnomemusic/widgets/playertoolbar.py | 27 +-
gnomemusic/widgets/playlistdialog.py | 3 +-
gnomemusic/widgets/searchheaderbar.py | 28 +-
gnomemusic/widgets/smoothscale.py | 12 +-
gnomemusic/widgets/songwidget.py | 106 ++++----
gnomemusic/widgets/songwidgetmenu.py | 52 ++--
gnomemusic/widgets/starhandlerwidget.py | 6 +-
gnomemusic/window.py | 27 +-
gnomemusic/windowplacement.py | 39 ++-
meson.build | 15 +-
org.gnome.Music.json | 11 +-
po/POTFILES.skip | 1 -
85 files changed, 1979 insertions(+), 2508 deletions(-)
---
diff --git a/data/org.gnome.Music.css b/data/org.gnome.Music.css
index 9e2cea716..04f4b5e66 100644
--- a/data/org.gnome.Music.css
+++ b/data/org.gnome.Music.css
@@ -25,43 +25,12 @@
}
/* PlayerToolbar */
-.border-solid {
- border-style: solid;
-}
-
-.semi-circular {
- border-radius: 20px;
- -gtk-outline-radius: 20px;
-}
-
-.semi-circular:hover {
- border-top-width: 0px;
- border-bottom-width: 0px;
- border-left-width: 0px;
- border-right-width: 0px;
-}
-
-.pill {
- border-radius: 9999px;
- -gtk-outline-radius: 9999px;
-}
.smooth-scale {
padding-top: 0px;
padding-bottom: 0px;
}
-/* FIXME: Remove once songsview is ported to the new style */
-.songs-list-old {
- box-shadow: inset 0 -1px shade(@borders, 1.30);
- background-color: @theme_bg_color;
-}
-
-.songs-list-old:selected {
- color: @theme_fg_color;
- border-color: mix(@theme_fg_color, @theme_bg_color, 0.5);
-}
-
/* We use background-image as a workaround on the StarImage widget to
enable transitions between the non-starred and starred icons. */
.star {
@@ -80,8 +49,6 @@
transition: 100ms linear;
}
-.content-view { background-color: @theme_bg; }
-
.grey-image {
color: alpha(@theme_fg_color, 0.3);
}
@@ -96,16 +63,6 @@
font-weight: bold;
}
-/* Lists style */
-
-/* workaround to avoid a black background issue
-in AlbumWidget and PlaylistsView
-https://gitlab.gnome.org/GNOME/gtk/issues/694
-*/
-list {
- background-color: transparent;
-}
-
.playlistdialog-row:selected {
color: @theme_fg_color;
background-color: @theme_insensitive_bg_color;
@@ -134,8 +91,27 @@ list {
font-weight: bold;
}
-/* SongWidget */
+/* SongsView */
+/* boxed-list style does not work for
+GtkListView at the moment */
+.songs-list {
+ border-left: 1px solid @borders;
+ border-right: 1px solid @borders;
+ border-top: 1px solid @borders;
+}
+
+.songs-list:first-child {
+ border-top-left-radius: 12px;
+ border-top-right-radius: 12px;
+}
+
+.songs-list:last-child {
+ border-bottom-left-radius: 12px;
+ border-bottom-right-radius: 12px;
+}
-.songwidget {
+/* SongWidget and SongsView rows*/
+.songwidget,
+.songs-list row {
padding: 12px;
}
diff --git a/data/org.gnome.Music.gresource.xml b/data/org.gnome.Music.gresource.xml
index a04c6bee6..d7f26d177 100644
--- a/data/org.gnome.Music.gresource.xml
+++ b/data/org.gnome.Music.gresource.xml
@@ -3,11 +3,10 @@
<gresource prefix="/org/gnome/Music">
<file alias="gtk/help-overlay.ui" preprocess="xml-stripblanks">ui/help-overlay.ui</file>
<file>org.gnome.Music.css</file>
- <file>icons/music-playlist-symbolic.svg</file>
- <file>icons/music-artist-symbolic.svg</file>
<file>icons/welcome-music.svg</file>
<file preprocess="xml-stripblanks">ui/AboutDialog.ui</file>
<file preprocess="xml-stripblanks">ui/AlbumCover.ui</file>
+ <file preprocess="xml-stripblanks">ui/AlbumCoverListItem.ui</file>
<file preprocess="xml-stripblanks">ui/AlbumWidget.ui</file>
<file preprocess="xml-stripblanks">ui/AlbumsView.ui</file>
<file preprocess="xml-stripblanks">ui/ArtistAlbumsWidget.ui</file>
@@ -33,10 +32,15 @@
<file preprocess="xml-stripblanks">ui/SearchView.ui</file>
<file preprocess="xml-stripblanks">ui/SelectionBarMenuButton.ui</file>
<file preprocess="xml-stripblanks">ui/SelectionToolbar.ui</file>
+ <file preprocess="xml-stripblanks">ui/SongListItem.ui</file>
<file preprocess="xml-stripblanks">ui/SongsView.ui</file>
<file preprocess="xml-stripblanks">ui/SongWidget.ui</file>
<file preprocess="xml-stripblanks">ui/SongWidgetMenu.ui</file>
<file preprocess="xml-stripblanks">ui/TwoLineTip.ui</file>
<file preprocess="xml-stripblanks">ui/Window.ui</file>
</gresource>
+ <gresource prefix="/org/gnome/Music/icons/scalable/actions">
+ <file alias="music-playlist-symbolic.svg">icons/music-playlist-symbolic.svg</file>
+ <file alias="music-artist-symbolic.svg">icons/music-artist-symbolic.svg</file>
+ </gresource>
</gresources>
diff --git a/data/org.gnome.Music.gschema.xml b/data/org.gnome.Music.gschema.xml
index 31eed34b4..d92cf2306 100644
--- a/data/org.gnome.Music.gschema.xml
+++ b/data/org.gnome.Music.gschema.xml
@@ -12,11 +12,6 @@
<summary>Window size</summary>
<description>Window size (width and height).</description>
</key>
- <key type="ai" name="window-position">
- <default>[]</default>
- <summary>Window position</summary>
- <description>Window position (x and y).</description>
- </key>
<key type="b" name="window-maximized">
<default>true</default>
<summary>Window maximized</summary>
diff --git a/data/ui/AboutDialog.ui.in b/data/ui/AboutDialog.ui.in
index 5c626a1f8..fc7cb60d9 100644
--- a/data/ui/AboutDialog.ui.in
+++ b/data/ui/AboutDialog.ui.in
@@ -2,7 +2,7 @@
<interface>
<!-- interface-requires gtk+ 3.0 -->
<template class="AboutDialog" parent="GtkAboutDialog">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="modal">True</property>
<property name="program_name">@PROGRAM_NAME@</property>
<property name="version">@PACKAGE_VERSION@</property>
diff --git a/data/ui/AlbumCover.ui b/data/ui/AlbumCover.ui
index a4e1738d7..6ff054e91 100644
--- a/data/ui/AlbumCover.ui
+++ b/data/ui/AlbumCover.ui
@@ -1,24 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.18"/>
<template class="AlbumCover" parent="GtkFlowBoxChild">
<child>
<object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="has_tooltip">True</property>
<property name="valign">start</property>
<property name="orientation">vertical</property>
<signal name="query-tooltip" handler="_on_tooltip_query"/>
<child>
<object class="GtkOverlay">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="margin-bottom">4</property>
<child>
<object class="ArtStack" id="_art_stack">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="vexpand">True</property>
<property name="valign">end</property>
<property name="halign">center</property>
@@ -26,19 +22,20 @@
</child>
<child type="overlay">
<object class="GtkCheckButton" id="_check">
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">False</property>
<property name="halign">end</property>
<property name="valign">end</property>
- <property name="draw_indicator">True</property>
+ <style>
+ <class name="selection-mode"/>
+ </style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="_title_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="ellipsize">middle</property>
@@ -48,8 +45,7 @@
</child>
<child>
<object class="GtkLabel" id="_artist_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="ellipsize">middle</property>
<property name="max_width_chars">20</property>
<style>
diff --git a/data/ui/AlbumCoverListItem.ui b/data/ui/AlbumCoverListItem.ui
new file mode 100644
index 000000000..95329aabe
--- /dev/null
+++ b/data/ui/AlbumCoverListItem.ui
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <object class="GtkBox" id="_album_cover">
+ <property name="focusable">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="valign">start</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkOverlay">
+ <child>
+ <object class="ArtStack" id="_art_stack">
+ <property name="focusable">False</property>
+ <property name="vexpand">True</property>
+ <property name="valign">end</property>
+ <property name="halign">center</property>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkCheckButton" id="_check">
+ <property name="visible">False</property>
+ <property name="halign">end</property>
+ <property name="valign">end</property>
+ <style>
+ <class name="selection-mode"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ <property name="ellipsize">middle</property>
+ <property name="max_width_chars">20</property>
+ <property name="lines">2</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_artist_label">
+ <property name="ellipsize">middle</property>
+ <property name="max_width_chars">20</property>
+ <style>
+ <class name="albumcover-artist-label"/>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/data/ui/AlbumWidget.ui b/data/ui/AlbumWidget.ui
index 0b4388a89..b795601d4 100644
--- a/data/ui/AlbumWidget.ui
+++ b/data/ui/AlbumWidget.ui
@@ -1,163 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.20.0 -->
<interface>
- <requires lib="gtk+" version="3.12"/>
- <template class="AlbumWidget" parent="HdyClamp">
- <property name="margin-bottom">48</property>
- <property name="margin-top">48</property>
- <property name="maximum-size">1000</property>
- <property name="orientation">horizontal</property>
- <property name="visible">True</property>
+ <template class="AlbumWidget" parent="AdwBin">
<child>
- <object class="GtkBox">
- <property name="halign">fill</property>
- <property name="orientation">vertical</property>
- <property name="visible">True</property>
+ <object class="AdwClamp">
+ <property name="margin-bottom">48</property>
+ <property name="margin-top">48</property>
+ <property name="maximum-size">1000</property>
<child>
- <object class="GtkBox" id="albumInfo">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <object class="GtkBox">
<property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="spacing">32</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="ArtStack" id="_art_stack">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="valign">start</property>
- <property name="hhomogeneous">False</property>
- <property name="vhomogeneous">False</property>
- </object>
- </child>
- <child>
- <object class="GtkBox" id="albumDetails">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="valign">start</property>
- <property name="orientation">vertical</property>
- <property name="margin-top">18</property>
- <child>
- <object class="GtkLabel" id="_title_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="ellipsize">middle</property>
- <property name="margin-bottom">18</property>
- <style>
- <class name="title-1"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel" id="_artist_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="ellipsize">middle</property>
- <property name="margin-bottom">12</property>
- <style>
- <class name="title-3"/>
- </style>
- </object>
- </child>
+ <object class="GtkBox" id="albumInfo">
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">32</property>
<child>
- <object class="GtkLabel" id="_released_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="use_markup">True</property>
- <property name="margin-bottom">12</property>
- <style>
- <class name="dim-label"/>
- </style>
+ <object class="ArtStack" id="_art_stack">
+ <property name="halign">center</property>
+ <property name="valign">start</property>
+ <property name="hhomogeneous">False</property>
+ <property name="vhomogeneous">False</property>
</object>
</child>
<child>
- <object class="GtkLabel" id="_composer_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="ellipsize">end</property>
- <property name="margin-bottom">12</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkBox">
- <property name="orientation">horizontal</property>
- <property name="visible">True</property>
- <property name="spacing">12</property>
- <property name="margin-top">6</property>
+ <object class="GtkBox" id="albumDetails">
+ <property name="halign">center</property>
+ <property name="valign">start</property>
+ <property name="orientation">vertical</property>
+ <property name="margin-top">18</property>
+ <child>
+ <object class="GtkLabel" id="_title_label">
+ <property name="focusable">False</property>
+ <property name="halign">start</property>
+ <property name="ellipsize">middle</property>
+ <property name="margin-bottom">18</property>
+ <style>
+ <class name="title-1"/>
+ </style>
+ </object>
+ </child>
<child>
- <object class="GtkButton" id="_play_button">
- <property name="width-request">44</property>
- <property name="height-request">44</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="receives-default">True</property>
- <property name="image">_play_image</property>
- <property name="always_show_image">True</property>
- <property name="tooltip-text" translatable="yes">Play</property>
- <property name="valign">center</property>
- <signal name="clicked" handler="_on_play_button_clicked" swapped="no"/>
+ <object class="GtkLabel" id="_artist_label">
+ <property name="focusable">False</property>
+ <property name="halign">start</property>
+ <property name="ellipsize">middle</property>
+ <property name="margin-bottom">12</property>
<style>
- <class name="circular"/>
+ <class name="title-3"/>
</style>
</object>
</child>
<child>
- <object class="GtkMenuButton" id="_menu_button">
- <property name="width-request">44</property>
- <property name="height-request">44</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
+ <object class="GtkLabel" id="_released_label">
+ <property name="focusable">False</property>
<property name="halign">start</property>
- <property name="valign">center</property>
- <property name="focus_on_click">False</property>
- <property name="menu-model">album_menu</property>
- <property name="direction">none</property>
- <property name="use_popover">True</property>
- <property name="image">_view_more_image</property>
+ <property name="use_markup">True</property>
+ <property name="margin-bottom">12</property>
<style>
- <class name="image-button"/>
- <class name="circular"/>
+ <class name="dim-label"/>
</style>
</object>
</child>
+ <child>
+ <object class="GtkLabel" id="_composer_label">
+ <property name="focusable">False</property>
+ <property name="halign">start</property>
+ <property name="ellipsize">end</property>
+ <property name="margin-bottom">12</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="spacing">12</property>
+ <property name="margin-top">6</property>
+ <child>
+ <object class="GtkButton" id="_play_button">
+ <property name="width-request">44</property>
+ <property name="height-request">44</property>
+ <property name="icon-name">media-playback-start-symbolic</property>
+ <property name="tooltip-text" translatable="yes">Play</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="_on_play_button_clicked" swapped="no"/>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="_menu_button">
+ <property name="width-request">44</property>
+ <property name="height-request">44</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="focus_on_click">False</property>
+ <property name="menu-model">album_menu</property>
+ <property name="direction">none</property>
+ <property name="icon_name">view-more-symbolic</property>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
</child>
</object>
</child>
- </object>
- </child>
- <child>
- <object class="GtkListBox" id="_disc_list_box">
- <property name="can_focus">False</property>
- <property name="margin-top">48</property>
- <property name="selection_mode">0</property>
- <property name="visible">True</property>
+ <child>
+ <object class="GtkListBox" id="_disc_list_box">
+ <property name="focusable">False</property>
+ <property name="margin-top">48</property>
+ <property name="selection_mode">0</property>
+ <style>
+ <class name="background"/>
+ </style>
+ </object>
+ </child>
</object>
</child>
</object>
</child>
</template>
- <object class="GtkImage" id="_view_more_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">view-more-symbolic</property>
- <property name="icon_size">4</property>
- </object>
- <object class="GtkImage" id="_play_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">media-playback-start-symbolic</property>
- <property name="icon_size">4</property>
- </object>
<menu id="album_menu">
<item>
<attribute name="label" translatable="yes">_Play</attribute>
diff --git a/data/ui/AlbumsView.ui b/data/ui/AlbumsView.ui
index a0832384c..259b519c0 100644
--- a/data/ui/AlbumsView.ui
+++ b/data/ui/AlbumsView.ui
@@ -1,55 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.18"/>
+ <requires lib="gtk" version="4.0"/>
<template class="AlbumsView" parent="GtkStack">
- <property name="visible">True</property>
<child>
- <object class="GtkScrolledWindow" id="_scrolled_window">
- <property name="visible">True</property>
- <child>
- <object class="GtkFlowBox" id="_flowbox">
- <property name="column_spacing">20</property>
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="homogeneous">True</property>
- <property name="margin-bottom">18</property>
- <property name="margin-end">18</property>
- <property name="margin-start">18</property>
- <property name="margin-top">18</property>
- <property name="max-children-per-line">20</property>
- <property name="min-children-per-line">1</property>
- <property name="row_spacing">12</property>
- <property name="selection-mode">none</property>
- <property name="valign">start</property>
- <property name="visible">True</property>
- <signal name="selected-children-changed" handler="_on_selected_children_changed" swapped="no"/>
- <style>
- <class name="content-view"/>
- </style>
+ <object class="GtkStackPage">
+ <property name="name">grid</property>
+ <property name="child">
+ <object class="GtkScrolledWindow" id="_scrolled_window">
+ <child>
+ <object class="GtkGridView" id="_gridview">
+ <property name="max-columns">20</property>
+ </object>
+ </child>
</object>
- </child>
+ </property>
</object>
- <packing>
- <property name="name">grid</property>
- </packing>
</child>
<child>
- <object class="GtkScrolledWindow" id="_album_scrolled_window">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="hscrollbar_policy">never</property>
- </object>
- <packing>
+ <object class="GtkStackPage">
<property name="name">widget</property>
- </packing>
+ <property name="child">
+ <object class="GtkScrolledWindow" id="_album_scrolled_window">
+ <property name="focusable">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="scroll-to-focus">True</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
</child>
</template>
- <object class="GtkGestureLongPress" id="_flowbox_long_press">
- <property name="propagation-phase">capture</property>
- <property name="widget">_flowbox</property>
- <signal name="begin" handler="_on_flowbox_press_begin" swapped="no"/>
- <signal name="cancel" handler="_on_flowbox_press_cancel" swapped="no"/>
- </object>
</interface>
diff --git a/data/ui/AppMenu.ui b/data/ui/AppMenu.ui
index 71ae356b9..70f9e7f58 100644
--- a/data/ui/AppMenu.ui
+++ b/data/ui/AppMenu.ui
@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
<interface>
- <requires lib="gtk+" version="3.20"/>
- <template class="AppMenu" parent="GtkPopoverMenu">
- <property name="can_focus">False</property>
+ <template class="AppMenu" parent="GtkPopover">
+ <property name="focusable">False</property>
+ <style>
+ <class name="menu"/>
+ </style>
<child>
<object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="margin-bottom">6</property>
<property name="margin-end">6</property>
<property name="margin-start">6</property>
<property name="margin-top">6</property>
+ <property name="spacing">6</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkModelButton" id="lastfm_account_button">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">fill</property>
<property name="hexpand">False</property>
- <property name="visible">True</property>
<property name="action_name">app.lastfm-configure</property>
<property name="text" translatable="yes">Last.fm Account</property>
</object>
@@ -26,38 +26,33 @@
<child>
<object class="GtkBox" id="_lastfm_box">
<property name="margin-end">5</property>
- <property name="margin-start">5</property>
<property name="visible">False</property>
<child>
<object class="GtkLabel">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="margin-end">12</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
- <property name="visible">True</property>
<property name="label" translatable="yes">Report Music Listening</property>
</object>
</child>
<child>
<object class="GtkSwitch" id="_lastfm_switch">
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="halign">end</property>
- <property name="visible">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkSeparator">
- <property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkModelButton" id="_keyboard_shortcuts_model_button">
<property name="halign">fill</property>
<property name="hexpand">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
<property name="action_name">win.show-help-overlay</property>
<property name="text" translatable="yes">_Keyboard Shortcuts</property>
@@ -67,8 +62,7 @@
<object class="GtkModelButton" id="_help_model_button">
<property name="halign">fill</property>
<property name="hexpand">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.help</property>
<property name="text" translatable="yes">_Help</property>
@@ -78,17 +72,13 @@
<object class="GtkModelButton" id="_about_model_button">
<property name="halign">fill</property>
<property name="hexpand">False</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.about</property>
<property name="text" translatable="yes">_About Music</property>
</object>
</child>
</object>
- <packing>
- <property name="submenu">main</property>
- </packing>
</child>
</template>
</interface>
diff --git a/data/ui/ArtistAlbumsWidget.ui b/data/ui/ArtistAlbumsWidget.ui
index ae6a4cf54..f38c85b91 100644
--- a/data/ui/ArtistAlbumsWidget.ui
+++ b/data/ui/ArtistAlbumsWidget.ui
@@ -1,13 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.18"/>
- <template class="ArtistAlbumsWidget" parent="HdyClamp">
- <property name="maximum-size">1000</property>
- <property name="orientation">horizontal</property>
- <property name="visible">True</property>
+ <requires lib="gtk" version="4.0"/>
+ <template class="ArtistAlbumsWidget" parent="GtkBox">
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkListBox" id="_listbox">
- <property name="visible">True</property>
+ <object class="AdwClamp">
+ <property name="maximum-size">1000</property>
+ <property name="orientation">horizontal</property>
+ <child>
+ <object class="GtkListBox" id="_listbox">
+ <style>
+ <class name="background"/>
+ </style>
+ </object>
+ </child>
</object>
</child>
</template>
diff --git a/data/ui/ArtistSearchTile.ui b/data/ui/ArtistSearchTile.ui
index dac94c566..5d46e8b70 100644
--- a/data/ui/ArtistSearchTile.ui
+++ b/data/ui/ArtistSearchTile.ui
@@ -1,51 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.18"/>
<template class="ArtistSearchTile" parent="GtkFlowBoxChild">
<child>
<object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="has_tooltip">True</property>
<property name="valign">start</property>
<property name="orientation">vertical</property>
<signal name="query-tooltip" handler="_on_tooltip_query" swapped="no"/>
<child>
<object class="GtkOverlay">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="margin-bottom">4</property>
<child>
- <object class="GtkEventBox" id="_events">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <signal name="button-release-event" handler="_on_artist_event" swapped="no"/>
+ <object class="ArtStack" id="_art_stack">
+ <property name="hexpand">True</property>
<child>
- <object class="ArtStack" id="_art_stack">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="vexpand">True</property>
- <property name="valign">end</property>
- <property name="halign">center</property>
+ <object class="GtkGestureClick">
+ <signal name="released" handler="_on_artist_event" swapped="no"/>
</object>
</child>
</object>
</child>
<child type="overlay">
<object class="GtkCheckButton" id="_check">
- <property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="halign">end</property>
<property name="valign">end</property>
- <property name="draw_indicator">True</property>
+ <style>
+ <class name="selection-mode"/>
+ </style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="_artist_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="ellipsize">middle</property>
diff --git a/data/ui/ArtistTile.ui b/data/ui/ArtistTile.ui
index c7af54deb..28d4ecad1 100644
--- a/data/ui/ArtistTile.ui
+++ b/data/ui/ArtistTile.ui
@@ -1,22 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="ArtistTile" parent="GtkListBoxRow">
- <property name="can_focus">False</property>
- <property name="visible">True</property>
+ <template class="ArtistTile" parent="GtkBox">
+ <property name="focusable">False</property>
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
- <property name="visible">True</property>
<property name="margin-start">10</property>
<child>
<object class="ArtStack" id="_art_stack">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
</object>
</child>
<child>
<object class="GtkLabel" id="_label">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="ellipsize">end</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
@@ -24,7 +21,6 @@
<property name="margin-end">10</property>
<property name="margin-start">10</property>
<property name="margin-top">16</property>
- <property name="visible">True</property>
</object>
</child>
</object>
diff --git a/data/ui/ArtistsView.ui b/data/ui/ArtistsView.ui
index 2e82fc833..8053288fe 100644
--- a/data/ui/ArtistsView.ui
+++ b/data/ui/ArtistsView.ui
@@ -1,39 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.18"/>
+ <requires lib="gtk" version="4.0"/>
<template class="ArtistsView" parent="GtkPaned">
- <property name="visible">True</property>
+ <property name="shrink-start-child">0</property>
<child>
<object class="GtkScrolledWindow">
- <property name="visible">True</property>
<property name="width_request">220</property>
- <style>
- <class name="sidebar"/>
- </style>
<child>
- <object class="GtkListBox" id="_sidebar">
- <property name="selection_mode">single</property>
- <property name="visible">True</property>
- <signal name="row-activated" handler="_on_artist_activated" swapped="no"/>
+ <object class="GtkListView" id="_sidebar">
+ <property name="single-click-activate">True</property>
+ <signal name="activate" handler="_on_artist_activated" swapped="no"/>
+ <style>
+ <class name="navigation-sidebar"/>
+ </style>
</object>
</child>
</object>
- <packing>
- <property name="shrink">False</property>
- </packing>
</child>
<child>
- <object class="GtkScrolledWindow" id="_artist_container">
+ <object class="GtkScrolledWindow" id="_artist_view">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkStack" id="_artist_view">
- <property name="transition-type">crossfade</property>
- <property name="vhomogeneous">False</property>
- <property name="visible">True</property>
- </object>
- </child>
</object>
</child>
</template>
diff --git a/data/ui/DiscBox.ui b/data/ui/DiscBox.ui
index 164d8dcbe..4899ecf88 100644
--- a/data/ui/DiscBox.ui
+++ b/data/ui/DiscBox.ui
@@ -1,23 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.20.0 -->
<interface>
- <requires lib="gtk+" version="3.12"/>
+ <requires lib="gtk" version="4.0"/>
<template parent="GtkListBoxRow" class="DiscBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="activatable">False</property>
<property name="selectable">False</property>
<child>
<object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="_disc_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">fill</property>
- <!-- <property name="hexpand">True</property> -->
<property name="xalign">0.0</property>
<style>
<class name="disc-label"/>
@@ -26,13 +21,12 @@
</child>
<child>
<object class="GtkListBox" id="_list_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="valign">start</property>
<property name="selection_mode">none</property>
<signal name="row-activated" handler="_song_activated" swapped="no"/>
<style>
- <class name="content"/>
+ <class name="boxed-list"/>
</style>
</object>
</child>
diff --git a/data/ui/DiscListItem.ui b/data/ui/DiscListItem.ui
new file mode 100644
index 000000000..dd3c85f34
--- /dev/null
+++ b/data/ui/DiscListItem.ui
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="4.0"/>
+ <template class="DiscListItem" parent="GtkListItem">
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <binding name="label">
+ <lookup name="disc_nr" type="CoreDisc">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="DiscBox" id="_disc_box">
+ <binding name="model">
+ <lookup name="model" type="CoreDisc">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
diff --git a/data/ui/EmptyView.ui b/data/ui/EmptyView.ui
index 2b58c3796..2be102a91 100644
--- a/data/ui/EmptyView.ui
+++ b/data/ui/EmptyView.ui
@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 3.0 -->
<template class="EmptyView" parent="GtkStack">
- <property name="transition_type">crossfade</property>
<property name="visible">False</property>
<child>
- <object class="HdyStatusPage" id="_status_page">
- <property name="visible">True</property>
+ <object class="AdwStatusPage" id="_status_page">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="icon_name">emblem-music-symbolic</property>
@@ -14,39 +11,29 @@
</child>
</template>
<object class="GtkBox" id="_initial_state">
- <property name="visible">False</property>
- <property name="can_focus">False</property>
<property name="valign">start</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="resource">/org/gnome/Music/icons/welcome-music.svg</property>
- <property name="height-request">300</property>
- <property name="width-request">400</property>
+ <object class="GtkPicture">
+ <property name="can-shrink">true</property>
+ <property name="keep-aspect-ratio">true</property>
+ <property name="file">resource:///org/gnome/Music/icons/welcome-music.svg</property>
</object>
</child>
<child>
<object class="GtkLabel" id="_title_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="justify">center</property>
<property name="label" translatable="yes">Welcome to Music</property>
<style>
- <class name="title"/>
<class name="large-title"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel" id="_description_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="justify">center</property>
<property name="use-markup">True</property>
<style>
- <class name="description"/>
<class name="body"/>
</style>
</object>
diff --git a/data/ui/HeaderBar.ui b/data/ui/HeaderBar.ui
index 4ffd0db56..3f03a3ef7 100644
--- a/data/ui/HeaderBar.ui
+++ b/data/ui/HeaderBar.ui
@@ -1,123 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.10 -->
- <template class="HeaderBar" parent="HdyHeaderBar">
- <property name="visible">True</property>
- <property name="vexpand">False</property>
- <style>
- <class name="titlebar"/>
- </style>
+ <template class="HeaderBar" parent="AdwBin">
<child>
- <object class="GtkMenuButton" id="_menu_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <property name="tooltip_text" translatable="yes">Menu</property>
+ <object class="AdwHeaderBar" id="_headerbar">
+ <property name="vexpand">False</property>
<style>
- <class name="image-button"/>
+ <class name="titlebar"/>
</style>
- <child>
- <object class="GtkImage" id="_menu_button_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <child type="end">
+ <object class="GtkMenuButton" id="_menu_button">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
<property name="icon-name">open-menu-symbolic</property>
- <property name="icon-size">1</property>
+ <property name="tooltip_text" translatable="yes">Menu</property>
</object>
</child>
- </object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_select_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <property name="tooltip_text" translatable="yes">Select</property>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage" id="_select_button_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon-name">object-select-symbolic</property>
- <property name="icon-size">1</property>
+ <child type="end">
+ <object class="GtkToggleButton" id="_select_button">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
+ <property name="icon-name">selection-mode-symbolic</property>
+ <property name="tooltip_text" translatable="yes">Select</property>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton" id="_cancel_button">
+ <property name="visible">False</property>
+ <property name="focusable">False</property>
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use_underline">True</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
+ <signal name="clicked" handler="_on_cancel_button_clicked" swapped="no"/>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkToggleButton" id="_search_button">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
+ <property name="icon-name">edit-find-symbolic</property>
+ <property name="tooltip_text" translatable="yes">Search</property>
+ </object>
+ </child>
+ <child type="start">
+ <object class="GtkButton" id="_back_button">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
+ <property name="icon-name">go-previous-symbolic</property>
+ <property name="tooltip_text" translatable="yes">Back</property>
+ <signal name="clicked" handler="_on_back_button_clicked" swapped="no"/>
</object>
</child>
</object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="_cancel_button">
- <property name="visible">False</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Cancel</property>
- <property name="use_underline">True</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <signal name="clicked" handler="_on_cancel_button_clicked" swapped="no"/>
- <style>
- <class name="text-button"/>
- </style>
- </object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
</child>
+ </template>
+ <object class="GtkBox" id="_label_title_box">
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkToggleButton" id="_search_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <property name="tooltip_text" translatable="yes">Search</property>
+ <object class="GtkLabel" id="_label_title">
+ <property name="single-line-mode">True</property>
+ <property name="ellipsize">end</property>
+ <property name="width-chars">5</property>
+ <property name="vexpand">True</property>
+ <property name="yalign">1.0</property>
<style>
- <class name="image-button"/>
+ <class name="title"/>
</style>
- <child>
- <object class="GtkImage" id="_search_button_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon-name">edit-find-symbolic</property>
- <property name="icon-size">1</property>
- </object>
- </child>
</object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
</child>
<child>
- <object class="GtkButton" id="_back_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <property name="tooltip_text" translatable="yes">Back</property>
- <signal name="clicked" handler="_on_back_button_clicked" swapped="no"/>
+ <object class="GtkLabel" id="_label_subtitle">
+ <property name="single-line-mode">True</property>
+ <property name="ellipsize">end</property>
+ <property name="width-chars">5</property>
+ <property name="vexpand">True</property>
+ <property name="yalign">0.0</property>
<style>
- <class name="image-button"/>
+ <class name="subtitle"/>
</style>
- <child>
- <object class="GtkImage" id="_back_button_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon-name">go-previous-symbolic</property>
- <property name="icon-size">1</property>
- </object>
- </child>
</object>
- <packing>
- <property name="pack_type">start</property>
- </packing>
</child>
- </template>
+ </object>
<object class="GtkSizeGroup" id="size1">
<property name="mode">vertical</property>
<widgets>
diff --git a/data/ui/InitialState.ui b/data/ui/InitialState.ui
new file mode 100644
index 000000000..1b10fc40b
--- /dev/null
+++ b/data/ui/InitialState.ui
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <object class="InitialState" id="_initial_state">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="_title_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="justify">center</property>
+ <property name="text" translatable="yes">Welcome to Music</property>
+ <style>
+ <class name="title"/>
+ <class name="large-title"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_description_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="justify">center</property>
+ <property name="text" translatable="yes">The contents of your Music Folder will appear
here</property>
+ <style>
+ <class name="description"/>
+ <class name="body"/>
+ </style>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/data/ui/LastfmDialog.ui b/data/ui/LastfmDialog.ui
index 6096f608d..926c78724 100644
--- a/data/ui/LastfmDialog.ui
+++ b/data/ui/LastfmDialog.ui
@@ -1,15 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="LastfmDialog" parent="GtkDialog">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="destroy_with_parent">True</property>
<property name="modal">True</property>
<property name="resizable">False</property>
<property name="title" translatable="yes">Last.fm Account</property>
- <property name="type_hint">dialog</property>
<property name="valign">start</property>
- <property name="window_position">center-on-parent</property>
- <child internal-child="vbox">
+ <child internal-child="content_area">
<object class="GtkBox">
<property name="margin-bottom">16</property>
<property name="margin-end">16</property>
@@ -17,13 +15,11 @@
<property name="margin-top">16</property>
<property name="orientation">vertical</property>
<property name="valign">start</property>
- <property name="visible">True</property>
<child>
<object class="GtkLabel" id="introduction_label">
<property name="halign">start</property>
<property name="label" translatable="yes">Last.fm is a music discovery service that gives you
personalised recommendations based on the music you listen to.</property>
<property name="margin-bottom">16</property>
- <property name="visible">True</property>
<property name="max_width_chars">60</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
@@ -34,7 +30,6 @@
<property name="halign">start</property>
<property name="label" translatable="yes">Music Reporting Not Setup</property>
<property name="margin-bottom">8</property>
- <property name="visible">True</property>
<property name="max_width_chars">60</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
@@ -48,7 +43,6 @@
<property name="halign">start</property>
<property name="label" translatable="yes">Login to your Last.fm account to report your music
listening.</property>
<property name="margin-bottom">12</property>
- <property name="visible">True</property>
<property name="max_width_chars">60</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
@@ -56,17 +50,13 @@
</child>
<child>
<object class="GtkButton" id="_action_button">
+ <signal name="clicked" handler="_on_action_button_clicked" swapped="no"/>
<property name="halign">start</property>
<property name="label" translatable="yes">Login</property>
<property name="margin-bottom">8</property>
- <property name="visible">True</property>
</object>
</child>
</object>
</child>
</template>
- <object class="GtkGestureMultiPress" id="_action_button_gesture">
- <property name="widget">_action_button</property>
- <signal name="released" handler="_on_action_button_clicked" swapped="no"/>
- </object>
</interface>
diff --git a/data/ui/LoadingNotification.ui b/data/ui/LoadingNotification.ui
index 3af3a1acb..ae57d2f69 100644
--- a/data/ui/LoadingNotification.ui
+++ b/data/ui/LoadingNotification.ui
@@ -4,13 +4,11 @@
<property name="column_spacing">18</property>
<child>
<object class="GtkSpinner">
- <property name="visible">True</property>
- <property name="active">True</property>
+ <property name="spinning">True</property>
</object>
</child>
<child>
<object class="GtkLabel">
- <property name="visible">True</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Loading</property>
diff --git a/data/ui/NotificationsPopup.ui b/data/ui/NotificationsPopup.ui
index 5215c413c..b34ea3784 100644
--- a/data/ui/NotificationsPopup.ui
+++ b/data/ui/NotificationsPopup.ui
@@ -1,15 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="NotificationsPopup" parent="GtkRevealer">
- <property name="visible">True</property>
<property name="halign">center</property>
<property name="valign">start</property>
<child>
<object class="GtkFrame">
- <property name="visible">True</property>
<child>
<object class="GtkBox" id="_box">
- <property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
</object>
diff --git a/data/ui/PlayerToolbar.ui b/data/ui/PlayerToolbar.ui
index 3742d8957..c11ce6c4e 100644
--- a/data/ui/PlayerToolbar.ui
+++ b/data/ui/PlayerToolbar.ui
@@ -1,40 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.12"/>
- <object class="GtkImage" id="next_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">media-skip-forward-symbolic</property>
- <property name="icon_size">1</property>
- </object>
- <object class="GtkImage" id="_pause_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">media-playback-pause-symbolic</property>
- <property name="icon_size">3</property>
- <property name="margin-bottom">4</property>
- <property name="margin-top">4</property>
- </object>
- <object class="GtkImage" id="_play_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">media-playback-start-symbolic</property>
- <property name="icon_size">3</property>
- <property name="margin-bottom">4</property>
- <property name="margin-top">4</property>
- </object>
- <object class="GtkImage" id="previous_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">media-skip-backward-symbolic</property>
- <property name="icon_size">1</property>
- </object>
+ <requires lib="gtk" version="4.0"/>
<template class="PlayerToolbar" parent="GtkActionBar">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
+ <property name="revealed">False</property>
<child>
<object class="GtkBox" id="_song_info_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">center</property>
<property name="has_tooltip">True</property>
<property name="valign">center</property>
@@ -46,14 +18,12 @@
<signal name="query-tooltip" handler="_on_tooltip_query"/>
<child>
<object class="ArtStack" id="_art_stack">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
</object>
</child>
<child>
<object class="GtkBox" id="nowplaying_labels">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="orientation">vertical</property>
@@ -61,8 +31,7 @@
<property name="spacing">3</property>
<child>
<object class="GtkLabel" id="_title_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">start</property>
<property name="ellipsize">middle</property>
<property name="max_width_chars">28</property>
@@ -73,8 +42,7 @@
</child>
<child>
<object class="GtkLabel" id="_artist_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">start</property>
<property name="ellipsize">middle</property>
<property name="max_width_chars">28</property>
@@ -86,89 +54,77 @@
</child>
<child type="center">
<object class="GtkBox" id="_buttons_and_scale">
+ <property name="hexpand">True</property>
<property name="orientation">vertical</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="margin-bottom">6</property>
<property name="margin-end">6</property>
<property name="margin-start">6</property>
<property name="margin-top">6</property>
<child>
<object class="GtkBox" id="buttons">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">center</property>
<property name="spacing">4</property>
<child>
<object class="GtkButton" id="_prev_button">
- <property name="width_request">42</property>
- <property name="visible">True</property>
<property name="sensitive">False</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
- <property name="image">previous_image</property>
- <property name="always_show_image">True</property>
+ <property name="icon_name">media-skip-backward-symbolic</property>
<property name="tooltip_text" translatable="yes">Previous</property>
+ <property name="valign">center</property>
<signal name="clicked" handler="_on_prev_button_clicked" swapped="no"/>
<style>
- <class name="flat"/>
- <class name="semi-circular"/>
+ <class name="circular"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="_play_button">
- <property name="width_request">80</property>
- <property name="visible">True</property>
<property name="sensitive">False</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
- <property name="image">_play_image</property>
- <property name="always_show_image">True</property>
<property name="tooltip_text" translatable="yes">Play</property>
<signal name="clicked" handler="_on_play_button_clicked" swapped="no"/>
<style>
- <class name="border-solid"/>
<class name="pill"/>
</style>
+ <child>
+ <object class="GtkImage" id="_play_pause_image">
+ <property name="icon_name">media-playback-start-symbolic</property>
+ <property name="icon_size">2</property>
+ </object>
+ </child>
</object>
</child>
<child>
<object class="GtkButton" id="_next_button">
- <property name="width_request">42</property>
- <property name="visible">True</property>
<property name="sensitive">False</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
- <property name="image">next_image</property>
- <property name="always_show_image">True</property>
+ <property name="icon_name">media-skip-forward-symbolic</property>
<property name="tooltip_text" translatable="yes">Next</property>
+ <property name="valign">center</property>
<signal name="clicked" handler="_on_next_button_clicked" swapped="no"/>
<style>
- <class name="flat"/>
- <class name="semi-circular"/>
+ <class name="circular"/>
</style>
</object>
</child>
- <style>
- <class name="linked"/>
- </style>
</object>
</child>
<child>
- <object class="HdyClamp">
+ <object class="AdwClamp">
<property name="maximum-size">1000</property>
- <property name="visible">True</property>
<child>
<object class="GtkBox" id="scale_and_timer">
- <property name="visible">True</property>
<property name="orientation">horizontal</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="margin_top">12</property>
<child>
<object class="GtkLabel" id="_progress_time_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="label">0∶00</property>
@@ -179,8 +135,7 @@
</child>
<child>
<object class="SmoothScale" id="_progress_scale">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="draw_value">False</property>
@@ -192,8 +147,7 @@
</child>
<child>
<object class="GtkLabel" id="_duration_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="label">0∶00</property>
@@ -208,11 +162,10 @@
</child>
</object>
</child>
- <child>
+ <child type="end">
<object class="GtkBox" id="menuBox">
<property name="height_request">34</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="margin-bottom">6</property>
@@ -221,27 +174,22 @@
<property name="margin-top">6</property>
<child>
<object class="GtkMenuButton" id="_repeat_menu_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
- <property name="use_popover">True</property>
<child>
<object class="GtkBox" id="replayBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="_repeat_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="icon_name">media-playlist-consecutive-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
<child>
<object class="GtkImage" id="downArrow">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="icon_name">pan-down-symbolic</property>
<property name="icon_size">1</property>
</object>
@@ -251,9 +199,6 @@
</object>
</child>
</object>
- <packing>
- <property name="pack-type">end</property>
- </packing>
</child>
</template>
</interface>
diff --git a/data/ui/PlaylistControls.ui b/data/ui/PlaylistControls.ui
index 836241124..04ea750b3 100644
--- a/data/ui/PlaylistControls.ui
+++ b/data/ui/PlaylistControls.ui
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 3.12 -->
<menu id="playlistMenu">
<item>
<attribute name="label" translatable="yes">_Play</attribute>
@@ -15,30 +14,15 @@
<attribute name="action">win.playlist_rename</attribute>
</item>
</menu>
- <object class="GtkImage" id="_view_more_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">view-more-symbolic</property>
- <property name="icon_size">4</property>
- </object>
- <object class="GtkImage" id="_play_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">media-playback-start-symbolic</property>
- <property name="icon_size">4</property>
- </object>
<template class="PlaylistControls" parent="GtkBox">
<property name="orientation">vertical</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="margin-top">30</property>
<child>
<object class="GtkStack" id="_name_stack">
- <property name="visible">True</property>
<child>
<object class="GtkLabel" id="_name_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Playlist Name</property>
@@ -50,50 +34,49 @@
</object>
</child>
<child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">horizontal</property>
- <style>
- <class name="linked"/>
- </style>
- <child>
- <object class="GtkEntry" id="_rename_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="is_focus">True</property>
- <property name="has_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="activate" handler="_on_playlist_renamed" swapped="no"/>
- <signal name="changed" handler="_on_rename_entry_changed" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="_rename_done_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="has_focus">True</property>
- <property name="receives_default">True</property>
- <property name="label" translatable="yes">_Done</property>
- <property name="use_underline">True</property>
- <property name="sensitive">True</property>
- <signal name="clicked" handler="_on_playlist_renamed" swapped="no" />
+ <object class="GtkStackPage">
+ <property name="name">renaming_dialog</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
<style>
- <class name="suggested-action"/>
+ <class name="linked"/>
</style>
+ <child>
+ <object class="GtkEntry" id="_rename_entry">
+ <child>
+ <object class="GtkEventControllerKey" id="_rename_controller">
+ <signal name="key-pressed" handler="_on_rename_entry_key_pressed" swapped="no"/>
+ </object>
+ </child>
+ <property name="focusable">True</property>
+ <property name="receives_default">True</property>
+ <signal name="activate" handler="_on_playlist_renamed" swapped="no"/>
+ <signal name="changed" handler="_on_rename_entry_changed" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="_rename_done_button">
+ <property name="focusable">True</property>
+ <property name="receives_default">True</property>
+ <property name="label" translatable="yes">_Done</property>
+ <property name="use_underline">True</property>
+ <property name="sensitive">True</property>
+ <signal name="clicked" handler="_on_playlist_renamed" swapped="no" />
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
</object>
- </child>
+ </property>
</object>
- <packing>
- <property name="name">renaming_dialog</property>
- </packing>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="_songs_count_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">start</property>
<property name="label"></property>
<property name="margin-bottom">4</property>
@@ -105,7 +88,6 @@
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
- <property name="visible">True</property>
<property name="spacing">12</property>
<property name="margin-top">18</property>
<property name="margin-bottom">8</property>
@@ -113,11 +95,9 @@
<object class="GtkButton" id="_play_button">
<property name="height-request">44</property>
<property name="width-request">44</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
+ <property name="focusable">True</property>
<property name="receives-default">True</property>
- <property name="image">_play_image</property>
- <property name="always_show_image">True</property>
+ <property name="icon_name">media-playback-start-symbolic</property>
<property name="tooltip-text" translatable="yes">Play</property>
<property name="valign">center</property>
<signal name="clicked" handler="_on_play_button_clicked" swapped="no"/>
@@ -130,18 +110,15 @@
<object class="GtkMenuButton" id="_menubutton">
<property name="height-request">44</property>
<property name="width-request">44</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="focus_on_click">False</property>
<property name="menu-model">playlistMenu</property>
<property name="direction">none</property>
- <property name="use_popover">True</property>
- <property name="image">_view_more_image</property>
+ <property name="icon_name">view-more-symbolic</property>
<style>
- <class name="image-button"/>
<class name="circular"/>
</style>
</object>
@@ -149,8 +126,4 @@
</object>
</child>
</template>
- <object class="GtkEventControllerKey" id="_rename_controller">
- <property name="widget">_rename_entry</property>
- <signal name="key-pressed" handler="_on_rename_entry_key_pressed" swapped="no"/>
- </object>
</interface>
diff --git a/data/ui/PlaylistDialog.ui b/data/ui/PlaylistDialog.ui
index 655322c12..f039604dd 100644
--- a/data/ui/PlaylistDialog.ui
+++ b/data/ui/PlaylistDialog.ui
@@ -1,42 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.20.0 -->
<interface>
- <requires lib="gtk+" version="3.10"/>
+ <requires lib="gtk" version="4.0"/>
<template class="PlaylistDialog" parent="GtkDialog">
<property name="width_request">400</property>
<property name="height_request">500</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="modal">True</property>
<property name="destroy_with_parent">True</property>
- <property name="type_hint">dialog</property>
- <child internal-child="vbox">
+ <child internal-child="content_area">
<object class="GtkBox" id="dialog-vbox">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="orientation">vertical</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox">
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
<child>
<object class="GtkStack" id="_add_playlist_stack">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="transition_duration">250</property>
<child>
<object class="GtkBox" id="_empty_box">
<property name="visible">False</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="orientation">vertical</property>
<property name="valign">fill</property>
<property name="vexpand">True</property>
@@ -46,8 +34,7 @@
<property name="margin-top">18</property>
<child>
<object class="GtkImage" id="image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="valign">center</property>
<property name="pixel_size">100</property>
<property name="icon_name">emblem-music-symbolic</property>
@@ -61,8 +48,7 @@
</child>
<child>
<object class="GtkLabel" id="label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="label" translatable="yes">Enter a name for your first
playlist</property>
<property name="valign">end</property>
</object>
@@ -71,8 +57,7 @@
<object class="GtkEntry" id="_first_playlist_entry">
<property name="width_request">300</property>
<property name="height_request">10</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="halign">center</property>
<property name="margin-bottom">16</property>
<property name="margin-end">18</property>
@@ -80,7 +65,11 @@
<property name="margin-top">18</property>
<signal name="activate" handler="_on_editing_done" swapped="no"/>
<signal name="changed" handler="_on_add_playlist_entry_changed" swapped="no"/>
- <signal name="focus-in-event" handler="_on_add_playlist_entry_focused" swapped="no"/>
+ <child>
+ <object class="GtkEventControllerFocus">
+ <signal name="enter" handler="_on_add_playlist_entry_focused" swapped="no"/>
+ </object>
+ </child>
</object>
</child>
<child>
@@ -89,9 +78,8 @@
<property name="use_underline">True</property>
<property name="width_request">120</property>
<property name="height_request">25</property>
- <property name="visible">True</property>
<property name="sensitive">False</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
@@ -105,8 +93,8 @@
</object>
</child>
<child>
- <object class="GtkButtonBox">
- <property name="can_focus">False</property>
+ <object class="GtkBox">
+ <property name="focusable">False</property>
</object>
</child>
</object>
@@ -114,63 +102,65 @@
<child>
<object class="GtkBox" id="_normal_box">
<property name="visible">False</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="vexpand">True</property>
<child>
- <object class="GtkListBox" id="_listbox">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="selection_mode">single</property>
- <property name="valign">start</property>
- <signal name="selected-rows-changed" handler="_on_selected_rows_changed"
swapped="no"/>
+ <object class="GtkViewport">
+ <property name="scroll-to-focus">True</property>
+ <child>
+ <object class="GtkListBox" id="_listbox">
+ <property name="focusable">True</property>
+ <property name="selection_mode">single</property>
+ <property name="valign">start</property>
+ <signal name="selected-rows-changed" handler="_on_selected_rows_changed"
swapped="no"/>
+ </object>
+ </child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkSeparator">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
</object>
</child>
<child>
<object class="GtkBox" id="new-playlist-hbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="margin-bottom">6</property>
<property name="margin-end">6</property>
<property name="margin-start">6</property>
<property name="margin-top">6</property>
<child>
<object class="GtkEntry" id="_new_playlist_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="hexpand">True</property>
<property name="placeholder_text" translatable="yes">New Playlist…</property>
<signal name="activate" handler="_on_editing_done" swapped="no"/>
<signal name="changed" handler="_on_add_playlist_entry_changed" swapped="no"/>
- <signal name="focus-in-event" handler="_on_add_playlist_entry_focused"
swapped="no"/>
<style>
<class name="linked"/>
</style>
+ <child>
+ <object class="GtkEventControllerFocus">
+ <signal name="enter" handler="_on_add_playlist_entry_focused" swapped="no"/>
+ </object>
+ </child>
</object>
</child>
<child>
<object class="GtkButton" id="_new_playlist_button">
<property name="label" translatable="yes">Add</property>
- <property name="visible">True</property>
<property name="sensitive">False</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">False</property>
<signal name="clicked" handler="_on_editing_done" swapped="no"/>
<style>
@@ -185,18 +175,11 @@
</object>
</child>
</object>
- <packing>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
</child>
<child>
- <object class="GtkButtonBox">
- <property name="can_focus">False</property>
+ <object class="GtkBox">
+ <property name="focusable">False</property>
</object>
- <packing>
- <property name="position">2</property>
- </packing>
</child>
</object>
</child>
@@ -204,41 +187,46 @@
</child>
</object>
</child>
+ <child internal-child="action_area">
+ <object class="GtkBox">
+ <property name="focusable">False</property>
+ </object>
+ </child>
</template>
<object class="GtkHeaderBar" id="_title_bar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="title" translatable="yes">Add to Playlist</property>
+ <property name="focusable">False</property>
+ <property name="title-widget">
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Add to Playlist</property>
+ <property name="single-line-mode">True</property>
+ <property name="ellipsize">end</property>
+ <property name="width-chars">5</property>
+ <style>
+ <class name="title"/>
+ </style>
+ </object>
+ </property>
<child>
<object class="GtkButton" id="_cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="use_underline">True</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="receives_default">False</property>
<signal name="clicked" handler="_on_cancel_button_clicked" swapped="no"/>
- <style>
- <class name="text-button"/>
- </style>
</object>
</child>
- <child>
+ <child type="end">
<object class="GtkButton" id="_select_button">
<property name="label" translatable="yes">_Add</property>
<property name="sensitive">False</property>
<property name="use_underline">True</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="receives_default">False</property>
<signal name="clicked" handler="_on_selection" swapped="no"/>
<style>
<class name="suggested-action"/>
- <class name="text-button"/>
</style>
</object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
</child>
</object>
</interface>
diff --git a/data/ui/PlaylistDialogRow.ui b/data/ui/PlaylistDialogRow.ui
index 0810e0f6e..c9c961755 100644
--- a/data/ui/PlaylistDialogRow.ui
+++ b/data/ui/PlaylistDialogRow.ui
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="PlaylistDialogRow" parent="GtkListBoxRow">
- <property name="visible">True</property>
<style>
<class name="playlistdialog-row"/>
</style>
<child>
<object class="GtkBox" id="hbox">
- <property name="visible">True</property>
<child>
<object class="GtkLabel" id="_label">
<property name="ellipsize">end</property>
@@ -15,14 +13,13 @@
<property name="margin-end">8</property>
<property name="margin-start">8</property>
<property name="margin-top">8</property>
- <property name="visible">True</property>
<property name="xalign">0.0</property>
</object>
</child>
<child>
<object class="GtkImage" id="_selection_icon">
- <property name="icon-name">object-select-symbolic</property>
- <property name="icon-size">2</property>
+ <property name="icon-name">selection-mode-symbolic</property>
+ <property name="icon-size">1</property>
</object>
</child>
</object>
diff --git a/data/ui/PlaylistNotification.ui b/data/ui/PlaylistNotification.ui
index b10ee723e..c1ae97c6a 100644
--- a/data/ui/PlaylistNotification.ui
+++ b/data/ui/PlaylistNotification.ui
@@ -1,17 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <object class="GtkImage" id="close_notification_window">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">window-close-symbolic</property>
- <property name="icon_size">1</property>
- </object>
<template class="PlaylistNotification" parent="GtkGrid">
- <property name="visible">True</property>
<property name="column_spacing">18</property>
<child>
<object class="GtkLabel" id="_label">
- <property name="visible">True</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes"></property>
@@ -21,17 +13,12 @@
<object class="GtkButton">
<property name="label" translatable="yes">_Undo</property>
<property name="use_underline">True</property>
- <property name="visible">True</property>
<signal name="clicked" handler="_undo_deletion" swapped="no"/>
</object>
</child>
<child>
<object class="GtkButton">
- <property name="always_show_image">True</property>
- <property name="image">close_notification_window</property>
- <property name="image_position">right</property>
- <property name="relief">none</property>
- <property name="visible">True</property>
+ <property name="icon-name">window-close-symbolic</property>
<signal name="clicked" handler="_close_notification" swapped="no"/>
</object>
</child>
diff --git a/data/ui/PlaylistTile.ui b/data/ui/PlaylistTile.ui
index 16fb590e0..2375857f0 100644
--- a/data/ui/PlaylistTile.ui
+++ b/data/ui/PlaylistTile.ui
@@ -1,17 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="PlaylistTile" parent="GtkListBoxRow">
- <property name="visible">True</property>
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
- <property name="visible">True</property>
<property name="margin-start">10</property>
<child>
<object class="GtkImage" id="_icon">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_size">4</property>
+ <property name="focusable">False</property>
+ <property name="icon-size">normal</property>
<property name="valign">center</property>
<style>
<class name="playlist-icon"/>
@@ -20,7 +17,7 @@
</child>
<child>
<object class="GtkLabel" id="_label">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="ellipsize">end</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
@@ -28,7 +25,6 @@
<property name="margin-end">10</property>
<property name="margin-start">10</property>
<property name="margin-top">16</property>
- <property name="visible">True</property>
</object>
</child>
</object>
diff --git a/data/ui/PlaylistsView.ui b/data/ui/PlaylistsView.ui
index 8cb743e17..a1ac17fc9 100644
--- a/data/ui/PlaylistsView.ui
+++ b/data/ui/PlaylistsView.ui
@@ -1,26 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.18"/>
<template class="PlaylistsView" parent="GtkPaned">
- <property name="visible">True</property>
+ <property name="shrink-start-child">0</property>
<child>
<object class="GtkScrolledWindow">
- <property name="visible">True</property>
<property name="width-request">220</property>
- <style>
- <class name="sidebar"/>
- </style>
<child>
- <object class="GtkListBox" id="_sidebar">
- <property name="selection-mode">single</property>
- <property name="visible">True</property>
- <signal name="row-activated" handler="_on_playlist_activated" swapped="no"/>
+ <object class="GtkViewport">
+ <property name="scroll-to-focus">True</property>
+ <child>
+ <object class="GtkListBox" id="_sidebar">
+ <property name="selection-mode">single</property>
+ <signal name="row-activated" handler="_on_playlist_activated" swapped="no"/>
+ <style>
+ <class name="navigation-sidebar"/>
+ </style>
+ </object>
+ </child>
</object>
</child>
</object>
- <packing>
- <property name="shrink">False</property>
- </packing>
</child>
</template>
</interface>
diff --git a/data/ui/PlaylistsWidget.ui b/data/ui/PlaylistsWidget.ui
index 721e89b55..7a9e3cfbf 100644
--- a/data/ui/PlaylistsWidget.ui
+++ b/data/ui/PlaylistsWidget.ui
@@ -2,14 +2,11 @@
<interface>
<template class="PlaylistsWidget" parent="GtkBox">
<property name="orientation">vertical</property>
- <property name="visible">True</property>
<child>
- <object class="HdyClamp">
+ <object class="AdwClamp">
<property name="maximum-size">1000</property>
- <property name="visible">True</property>
<child>
<object class="PlaylistControls" id="_pl_ctrls">
- <property name="visible">True</property>
</object>
</child>
</object>
@@ -17,19 +14,17 @@
<child>
<object class="GtkScrolledWindow" id="playlist-container">
<property name="vexpand">True</property>
- <property name="visible">True</property>
<child>
- <object class="HdyClamp">
+ <object class="AdwClamp">
<property name="maximum-size">1000</property>
- <property name="visible">True</property>
+ <property name="valign">start</property>
<child>
<object class="GtkListBox" id="_songs_list">
<property name="margin-bottom">20</property>
<property name="margin-top">20</property>
- <property name="visible">True</property>
<signal name="row-activated" handler="_on_song_activated" swapped="no"/>
<style>
- <class name="content"/>
+ <class name="boxed-list"/>
</style>
</object>
</child>
diff --git a/data/ui/SearchHeaderBar.ui b/data/ui/SearchHeaderBar.ui
index 37c70b669..8d26d706a 100644
--- a/data/ui/SearchHeaderBar.ui
+++ b/data/ui/SearchHeaderBar.ui
@@ -1,81 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 3.10 -->
- <template class="SearchHeaderBar" parent="HdyHeaderBar">
- <property name="visible">True</property>
- <property name="vexpand">False</property>
- <style>
- <class name="titlebar"/>
- </style>
+ <template class="SearchHeaderBar" parent="AdwBin">
<child>
- <object class="GtkToggleButton" id="_select_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <property name="tooltip_text" translatable="yes">Select</property>
+ <object class="AdwHeaderBar" id="_headerbar">
+ <property name="vexpand">False</property>
<style>
- <class name="image-button"/>
+ <class name="titlebar"/>
</style>
- <child>
- <object class="GtkImage" id="_select_button_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon-name">object-select-symbolic</property>
- <property name="icon-size">1</property>
+ <child type="end">
+ <object class="GtkToggleButton" id="_select_button">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
+ <property name="icon-name">selection-mode-symbolic</property>
+ <property name="tooltip_text" translatable="yes">Select</property>
</object>
</child>
- </object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="_cancel_button">
- <property name="visible">False</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Cancel</property>
- <property name="use_underline">True</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <signal name="clicked" handler="_on_cancel_button_clicked" swapped="no"/>
- <style>
- <class name="text-button"/>
- </style>
- </object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_search_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="sensitive">True</property>
- <property name="tooltip_text" translatable="yes">Search</property>
- <style>
- <class name="image-button"/>
- </style>
- <child>
- <object class="GtkImage" id="_search_button_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <child type="end">
+ <object class="GtkButton" id="_cancel_button">
+ <property name="focusable">False</property>
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use_underline">True</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
+ <signal name="clicked" handler="_on_cancel_button_clicked" swapped="no"/>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkToggleButton" id="_search_button">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="sensitive">True</property>
<property name="icon-name">edit-find-symbolic</property>
- <property name="icon-size">1</property>
+ <property name="tooltip_text" translatable="yes">Search</property>
</object>
</child>
</object>
- <packing>
- <property name="pack_type">end</property>
- </packing>
</child>
</template>
<object class="GtkSizeGroup" id="size1">
- <property name="mode">vertical</property>
- <widgets>
- <widget name="_search_button"/>
- <widget name="_cancel_button"/>
- </widgets>
+ <property name="mode">vertical</property>
+ <widgets>
+ <widget name="_search_button"/>
+ <widget name="_cancel_button"/>
+ </widgets>
</object>
</interface>
diff --git a/data/ui/SearchView.ui b/data/ui/SearchView.ui
index d7dc09dd1..8a2af34bd 100644
--- a/data/ui/SearchView.ui
+++ b/data/ui/SearchView.ui
@@ -1,170 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <object class="GtkImage" id="view_all_image_artist">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">go-next-symbolic</property>
- <property name="icon_size">1</property>
- </object>
- <object class="GtkImage" id="view_all_image_album">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">go-next-symbolic</property>
- <property name="icon_size">1</property>
- </object>
<template class="SearchView" parent="GtkStack">
- <property name="visible">True</property>
<child>
<object class="GtkScrolledWindow" id="_search_results">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
- <property name="visible">True</property>
<child>
- <object class="HdyClamp">
- <property name="maximum-size">1600</property>
- <property name="visible">True</property>
+ <object class="GtkViewport">
+ <property name="scroll-to-focus">True</property>
<child>
- <object class="GtkBox" id="container">
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="margin-bottom">20</property>
- <property name="margin-end">120</property>
- <property name="margin-start">120</property>
- <property name="margin-top">20</property>
- <property name="orientation">vertical</property>
- <property name="visible">True</property>
+ <object class="AdwClamp">
+ <property name="maximum-size">1600</property>
<child>
- <object class="GtkBox" id="_artist_header">
+ <object class="GtkBox" id="container">
<property name="halign">fill</property>
<property name="hexpand">True</property>
- <property name="homogeneous">True</property>
- <property name="orientation">horizontal</property>
- <property name="visible">True</property>
+ <property name="margin-bottom">20</property>
+ <property name="margin-end">120</property>
+ <property name="margin-start">120</property>
+ <property name="margin-top">20</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkLabel">
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Artists</property>
- <property name="visible">True</property>
- <style>
- <class name="search-header"/>
- </style>
+ <object class="GtkBox" id="_artist_header">
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="homogeneous">True</property>
+ <property name="orientation">horizontal</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="focusable">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Artists</property>
+ <style>
+ <class name="search-header"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="_view_all_artists">
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">View All</property>
+ <property name="icon-name">go-next-symbolic</property>
+ <signal name="clicked" handler="_on_all_artists_clicked" swapped="no"/>
+ </object>
+ </child>
</object>
</child>
<child>
- <object class="GtkButton" id="_view_all_artists">
- <property name="halign">end</property>
- <property name="label" translatable="yes">View All</property>
- <property name="always_show_image">True</property>
- <property name="image">view_all_image_artist</property>
- <property name="image_position">right</property>
- <property name="visible">True</property>
- <signal name="button-release-event" handler="_on_all_artists_clicked" swapped="no"/>
+ <object class="GtkFlowBox" id="_artist_flowbox">
+ <property name="column_spacing">6</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="homogeneous">True</property>
+ <property name="margin-bottom">18</property>
+ <property name="margin-top">18</property>
+ <property name="max-children-per-line">6</property>
+ <property name="min-children-per-line">1</property>
+ <property name="row_spacing">12</property>
+ <property name="selection-mode">none</property>
+ <property name="valign">start</property>
+ <signal name="child-activated" handler="_on_artist_activated" swapped="no"/>
</object>
</child>
- </object>
- </child>
- <child>
- <object class="GtkFlowBox" id="_artist_flowbox">
- <property name="column_spacing">6</property>
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="homogeneous">True</property>
- <property name="margin-bottom">18</property>
- <property name="margin-top">18</property>
- <property name="max-children-per-line">6</property>
- <property name="min-children-per-line">1</property>
- <property name="row_spacing">12</property>
- <property name="selection-mode">none</property>
- <property name="valign">start</property>
- <property name="visible">True</property>
- <signal name="child-activated" handler="_on_artist_activated" swapped="no"/>
- <style>
- <class name="content-view"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkBox" id="_album_header">
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="homogeneous">True</property>
- <property name="orientation">horizontal</property>
- <property name="visible">True</property>
<child>
- <object class="GtkLabel">
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Albums</property>
- <property name="visible">True</property>
- <style>
- <class name="search-header"/>
- </style>
+ <object class="GtkBox" id="_album_header">
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="homogeneous">True</property>
+ <property name="orientation">horizontal</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="focusable">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Albums</property>
+ <style>
+ <class name="search-header"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="_view_all_albums">
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">View All</property>
+ <property name="icon_name">go-next-symbolic</property>
+ <signal name="clicked" handler="_on_all_albums_clicked" swapped="no"/>
+ </object>
+ </child>
</object>
</child>
<child>
- <object class="GtkButton" id="_view_all_albums">
- <property name="halign">end</property>
- <property name="label" translatable="yes">View All</property>
- <property name="always_show_image">True</property>
- <property name="image">view_all_image_album</property>
- <property name="image_position">right</property>
- <property name="visible">True</property>
- <signal name="button-release-event" handler="_on_all_albums_clicked" swapped="no"/>
+ <object class="GtkFlowBox" id="_album_flowbox">
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="valign">start</property>
+ <property name="homogeneous">True</property>
+ <property name="min_children_per_line">1</property>
+ <property name="max_children_per_line">6</property>
+ <property name="margin-bottom">18</property>
+ <property name="margin-top">18</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">6</property>
+ <property name="selection_mode">none</property>
+ <signal name="child-activated" handler="_on_album_activated" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="_songs_header">
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="homogeneous">True</property>
+ <property name="orientation">horizontal</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="focusable">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Songs</property>
+ <style>
+ <class name="search-header"/>
+ </style>
+ </object>
+ </child>
</object>
</child>
- </object>
- </child>
- <child>
- <object class="GtkFlowBox" id="_album_flowbox">
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="valign">start</property>
- <property name="homogeneous">True</property>
- <property name="min_children_per_line">1</property>
- <property name="max_children_per_line">6</property>
- <property name="margin-bottom">18</property>
- <property name="margin-top">18</property>
- <property name="row_spacing">12</property>
- <property name="column_spacing">6</property>
- <property name="selection_mode">none</property>
- <property name="visible">True</property>
- <signal name="child-activated" handler="_on_album_activated" swapped="no"/>
- <style>
- <class name="content-view"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkBox" id="_songs_header">
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="homogeneous">True</property>
- <property name="orientation">horizontal</property>
- <property name="visible">True</property>
<child>
- <object class="GtkLabel">
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Songs</property>
- <property name="visible">True</property>
+ <object class="GtkListBox" id="_songs_listbox">
+ <property name="margin-top">20</property>
+ <signal name="row-activated" handler="_song_activated" swapped="no"/>
<style>
- <class name="search-header"/>
+ <class name="boxed-list"/>
</style>
</object>
</child>
</object>
</child>
- <child>
- <object class="GtkListBox" id="_songs_listbox">
- <property name="margin-top">20</property>
- <property name="visible">True</property>
- <signal name="row-activated" handler="_song_activated" swapped="no"/>
- <style>
- <class name="content"/>
- </style>
- </object>
- </child>
</object>
</child>
</object>
@@ -175,58 +143,53 @@
<object class="GtkScrolledWindow" id="_all_search_results">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
- <property name="visible">True</property>
<child>
- <object class="HdyClamp">
- <property name="maximum-size">1600</property>
- <property name="visible">True</property>
+ <object class="GtkViewport">
+ <property name="scroll-to-focus">True</property>
<child>
- <object class="GtkBox">
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="margin-bottom">20</property>
- <property name="margin-end">120</property>
- <property name="margin-start">120</property>
- <property name="margin-top">20</property>
- <property name="orientation">vertical</property>
- <property name="visible">True</property>
+ <object class="AdwClamp">
+ <property name="maximum-size">1600</property>
<child>
- <object class="GtkFlowBox" id="_artist_all_flowbox">
- <property name="column_spacing">6</property>
+ <object class="GtkBox">
<property name="halign">fill</property>
<property name="hexpand">True</property>
- <property name="homogeneous">True</property>
- <property name="margin-bottom">18</property>
- <property name="margin-top">18</property>
- <property name="max-children-per-line">6</property>
- <property name="min-children-per-line">1</property>
- <property name="row_spacing">12</property>
- <property name="selection-mode">none</property>
- <property name="valign">start</property>
- <signal name="child-activated" handler="_on_artist_activated" swapped="no"/>
- <style>
- <class name="content-view"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkFlowBox" id="_album_all_flowbox">
- <property name="halign">fill</property>
- <property name="hexpand">True</property>
- <property name="valign">start</property>
- <property name="homogeneous">True</property>
- <property name="min_children_per_line">1</property>
- <property name="max_children_per_line">6</property>
- <property name="margin-bottom">18</property>
- <property name="margin-top">18</property>
- <property name="row_spacing">12</property>
- <property name="column_spacing">6</property>
- <property name="selection_mode">none</property>
- <property name="visible">True</property>
- <signal name="child-activated" handler="_on_album_activated" swapped="no"/>
- <style>
- <class name="content-view"/>
- </style>
+ <property name="margin-bottom">20</property>
+ <property name="margin-end">120</property>
+ <property name="margin-start">120</property>
+ <property name="margin-top">20</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFlowBox" id="_artist_all_flowbox">
+ <property name="column_spacing">6</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="homogeneous">True</property>
+ <property name="margin-bottom">18</property>
+ <property name="margin-top">18</property>
+ <property name="max-children-per-line">6</property>
+ <property name="min-children-per-line">1</property>
+ <property name="row_spacing">12</property>
+ <property name="selection-mode">none</property>
+ <property name="valign">start</property>
+ <signal name="child-activated" handler="_on_artist_activated" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFlowBox" id="_album_all_flowbox">
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="valign">start</property>
+ <property name="homogeneous">True</property>
+ <property name="min_children_per_line">1</property>
+ <property name="max_children_per_line">6</property>
+ <property name="margin-bottom">18</property>
+ <property name="margin-top">18</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">6</property>
+ <property name="selection_mode">none</property>
+ <signal name="child-activated" handler="_on_album_activated" swapped="no"/>
+ </object>
+ </child>
</object>
</child>
</object>
@@ -237,8 +200,12 @@
</child>
<child>
<object class="GtkScrolledWindow" id="_scrolled_album_widget">
- <property name="visible">True</property>
<property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="scroll-to-focus">True</property>
+ </object>
+ </child>
</object>
</child>
</template>
diff --git a/data/ui/SelectionBarMenuButton.ui b/data/ui/SelectionBarMenuButton.ui
index 25c3c529c..464311602 100644
--- a/data/ui/SelectionBarMenuButton.ui
+++ b/data/ui/SelectionBarMenuButton.ui
@@ -15,25 +15,19 @@
</menu>
<template class="SelectionBarMenuButton" parent="GtkMenuButton">
<property name="menu-model">selection-menu</property>
- <property name="visible">True</property>
- <property name="can-focus">True</property>
+ <property name="focusable">True</property>
<child>
<object class="GtkBox" id="selection-menu-button-box">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
+ <property name="focusable">True</property>
<property name="orientation">horizontal</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="_menu_label">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
<property name="label" translatable="yes">Click on items to select them</property>
</object>
</child>
<child>
<object class="GtkImage">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
<property name="icon-name">pan-down-symbolic</property>
</object>
</child>
diff --git a/data/ui/SelectionToolbar.ui b/data/ui/SelectionToolbar.ui
index ec64876c1..852b73676 100644
--- a/data/ui/SelectionToolbar.ui
+++ b/data/ui/SelectionToolbar.ui
@@ -1,20 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 3.12 -->
<template class="SelectionToolbar" parent="GtkActionBar">
<property name="visible">False</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<child>
<object class="GtkButton" id="_add_to_playlist_button">
<property name="label" translatable="yes">_Add to Playlist</property>
<property name="use_underline">True</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="focusable">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="_on_add_to_playlist_button_clicked" swapped="no"/>
- <style>
- <class name="text-button"/>
- </style>
</object>
</child>
</template>
diff --git a/data/ui/SongListItem.ui b/data/ui/SongListItem.ui
new file mode 100644
index 000000000..3d906d61a
--- /dev/null
+++ b/data/ui/SongListItem.ui
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="4.0"/>
+ <object class="GtkBox" id="_song_box">
+ <property name="focusable">False</property>
+ <property name="valign">start</property>
+ <child>
+ <object class="GtkCheckButton" id="_check">
+ <property name="visible">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkLabel" id="_title_label">
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <property name="ellipsize">end</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_album_label">
+ <property name="ellipsize">end</property>
+ <property name="halign">start</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_artist_label">
+ <property name="ellipsize">end</property>
+ <property name="halign">start</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_duration_label">
+ <property name="halign">start</property>
+ <property name="single_line_mode">True</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1.0</property>
+ <attributes>
+ <attribute name="font-features" value="tnum=1"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="_star_box">
+ <property name="focusable">0</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="StarImage" id="_star_image">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="margin-end">12</property>
+ <property name="margin-start">12</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="_menu_button">
+ <property name="focusable">True</property>
+ <property name="icon-name">view-more-symbolic</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/data/ui/SongWidget.ui b/data/ui/SongWidget.ui
index 56708a392..482fd763f 100644
--- a/data/ui/SongWidget.ui
+++ b/data/ui/SongWidget.ui
@@ -1,59 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.20.0 -->
<interface>
- <requires lib="gtk+" version="3.10"/>
+ <requires lib="gtk" version="4.0"/>
<template class="SongWidget" parent="GtkListBoxRow">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="selectable">False</property>
- <signal name="drag_data_received" handler="_on_drag_data_received"/>
<child>
<object class="GtkBox" id="box1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="spacing">6</property>
<child>
- <object class="GtkEventBox" id="_dnd_eventbox">
+ <object class="GtkImage" id="_dnd_icon">
<property name="visible">False</property>
- <signal name="drag-begin" handler="_on_drag_begin"/>
- <signal name="drag-end" handler="_on_drag_end"/>
- <signal name="drag_data_get" handler="_on_drag_data_get"/>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="icon-name">list-drag-handle-symbolic</property>
- <style>
- <class name="drag-handle"/>
- </style>
- </object>
- </child>
+ <property name="icon-name">list-drag-handle-symbolic</property>
+ <style>
+ <class name="drag-handle"/>
+ </style>
</object>
</child>
<child>
<object class="GtkBox" id="box3">
<property name="width_request">48</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkImage" id="_play_icon">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="icon_size">1</property>
</object>
</child>
<child>
<object class="GtkCheckButton" id="_select_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="receives_default">False</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="_on_select_button_toggled"/>
</object>
</child>
<child>
<object class="GtkLabel" id="_number_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">end</property>
<property name="justify">right</property>
<style>
@@ -65,13 +48,11 @@
</child>
<child>
<object class="GtkBox" id="title_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="hexpand">True</property>
<child>
- <object class="DzlBoldingLabel" id="_title_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <object class="GtkLabel" id="_title_label">
+ <property name="focusable">False</property>
<property name="xalign">0</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
@@ -86,12 +67,11 @@
<child>
<object class="GtkBox" id="_artist_box">
<property name="visible">False</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkLabel" id="_artist_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="xalign">0</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
@@ -105,14 +85,13 @@
</child>
<child>
<object class="GtkBox" id="_album_duration_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="hexpand">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="_album_label">
<property name="visible">False</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="xalign">0</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
@@ -123,8 +102,7 @@
</child>
<child>
<object class="GtkLabel" id="_duration_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">end</property>
<property name="hexpand">True</property>
<property name="single_line_mode">True</property>
@@ -137,59 +115,84 @@
</child>
<child>
<object class="GtkStack" id="_star_stack">
- <property name="can-focus">False</property>
- <property name="visible">True</property>
+ <property name="focusable">False</property>
<child>
- <object class="GtkEventBox" id="_star_eventbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="valign">center</property>
- <property name="visible_window">True</property>
- <signal name="button-release-event" handler="_on_star_toggle" swapped="no"/>
- <child>
- <object class="StarImage" id="_star_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
+ <object class="GtkStackPage">
+ <property name="name">star</property>
+ <property name="child">
+ <object class="GtkBox" id="_star_box">
+ <property name="focusable">0</property>
+ <property name="halign">end</property>
<property name="valign">center</property>
- <property name="margin-end">12</property>
- <property name="margin-start">12</property>
+ <child>
+ <object class="GtkGestureClick">
+ <property name="button">1</property>
+ <property name="propagation-phase">capture</property>
+ <signal name="released" handler="_on_star_toggle" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEventControllerMotion">
+ <signal name="enter" handler="_on_star_hover" swapped="no"/>
+ <signal name="leave" handler="_on_star_unhover" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="StarImage" id="_star_image">
+ <property name="focusable">False</property>
+ <property name="valign">center</property>
+ <property name="margin-end">12</property>
+ <property name="margin-start">12</property>
+ </object>
+ </child>
</object>
- </child>
+ </property>
</object>
- <packing>
- <property name="name">star</property>
- </packing>
</child>
<child>
- <object class="GtkBox">
- <property name="visible">True</property>
- </object>
- <packing>
+ <object class="GtkStackPage">
<property name="name">empty</property>
- <property name="position">1</property>
- </packing>
+ <property name="child">
+ <object class="GtkBox"/>
+ </property>
+ </object>
</child>
</object>
</child>
<child>
<object class="GtkMenuButton" id="_menu_button">
<property name="visible">False</property>
- <property name="can-focus">True</property>
+ <property name="focusable">True</property>
+ <property name="icon-name">view-more-symbolic</property>
<style>
<class name="flat"/>
</style>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="icon-name">view-more-symbolic</property>
- </object>
- </child>
</object>
</child>
</object>
</child>
+ <child>
+ <object class="GtkGestureClick">
+ <property name="button">1</property>
+ <signal name="released" handler="_on_click" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkDragSource" id="_drag_source">
+ <property name="actions">move</property>
+ <property name="propagation-phase">none</property>
+ <signal name="prepare" handler="_on_drag_prepare" swapped="no"/>
+ <signal name="drag-begin" handler="_on_drag_begin" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkDropTarget">
+ <property name="actions">move</property>
+ <property name="formats">SongWidget</property>
+ <property name="preload">True</property>
+ <signal name="drop" handler="_on_drop" swapped="no"/>
+ </object>
+ </child>
<style>
<class name="songwidget"/>
</style>
@@ -202,10 +205,4 @@
<widget name="_album_duration_box"/>
</widgets>
</object>
- <object class="GtkEventControllerMotion" id="_controller_motion">
- <property name="propagation-phase">target</property>
- <property name="widget">_star_eventbox</property>
- <signal name="enter" handler="_on_star_hover" swapped="no"/>
- <signal name="leave" handler="_on_star_unhover" swapped="no"/>
- </object>
</interface>
diff --git a/data/ui/SongWidgetMenu.ui b/data/ui/SongWidgetMenu.ui
index 8868176f2..17c7a34a1 100644
--- a/data/ui/SongWidgetMenu.ui
+++ b/data/ui/SongWidgetMenu.ui
@@ -1,49 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="SongWidgetMenu" parent="GtkPopoverMenu">
- <property name="modal">True</property>
+ <property name="autohide">True</property>
<property name="position">bottom</property>
<property name="visible">False</property>
- <child>
- <object class="GtkBox">
- <property name="can-focus">False</property>
- <property name="margin-bottom">6</property>
- <property name="margin-end">6</property>
- <property name="margin-start">6</property>
- <property name="margin-top">6</property>
- <property name="orientation">vertical</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkModelButton">
- <property name="visible">True</property>
- <property name="text" translatable="yes">Play</property>
- <signal name="clicked" handler="_on_play_clicked" swapped="no"/>
- <style>
- <class name="flat"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkModelButton">
- <property name="visible">True</property>
- <property name="text" translatable="yes">Add to Playlist…</property>
- <signal name="clicked" handler="_on_add_to_playlist_clicked" swapped="no"/>
- <style>
- <class name="flat"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkModelButton" id="_remove_from_playlist_button">
- <property name="visible">True</property>
- <property name="text" translatable="yes">Remove From Playlist</property>
- <signal name="clicked" handler="_on_remove_from_playlist_clicked" swapped="no"/>
- <style>
- <class name="flat"/>
- </style>
- </object>
- </child>
- </object>
- </child>
+ <property name="menu-model">song-menu</property>
</template>
+ <menu id="song-menu">
+ <item>
+ <attribute name="label" translatable="yes">_Play</attribute>
+ <attribute name="action">songwidget.play</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Add to Playlist…</attribute>
+ <attribute name="action">songwidget.add_playlist</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Remove from Playlist</attribute>
+ <attribute name="action">songwidget.remove_playlist</attribute>
+ </item>
+ </menu>
</interface>
diff --git a/data/ui/SongsView.ui b/data/ui/SongsView.ui
index 223df3758..714423f8a 100644
--- a/data/ui/SongsView.ui
+++ b/data/ui/SongsView.ui
@@ -1,123 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <requires lib="gtk+" version="3.18"/>
- <template class="SongsView" parent="GtkScrolledWindow">
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="visible">True</property>
+ <requires lib="gtk" version="4.0"/>
+ <template class="SongsView" parent="GtkBox">
+ <property name="margin-bottom">48</property>
+ <property name="margin-top">48</property>
<child>
- <object class="GtkTreeView" id="_songs_view">
- <property name="activate-on-single-click">True</property>
- <property name="headers_visible">False</property>
- <property name="valign">start</property>
- <property name="visible">True</property>
- <signal name="row-activated" handler="_on_item_activated" swapped="no"/>
- <style>
- <class name="songs-list-old"/>
- </style>
- <child internal-child="selection">
- <object class="GtkTreeSelection">
- <property name="mode">single</property>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="_now_playing_column">
- <property name="fixed_width">48</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkCellRendererPixbuf" id="_now_playing_cell">
- <property name="xalign">0.5</property>
- <property name="xpad">0</property>
- <property name="yalign">0.5</property>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="_selection_column">
- <property name="fixed_width">48</property>
- <property name="visible">False</property>
- <child>
- <object class="GtkCellRendererToggle">
- </object>
- <attributes>
- <attribute name="active">1</attribute>
- </attributes>
- </child>
- </object>
- </child>
+ <object class="AdwClampScrollable" id="_adw_clamp_scrollable">
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="maximum-size">1000</property>
<child>
- <object class="GtkTreeViewColumn" id="_title_column">
- <property name="expand">True</property>
- <property name="visible">True</property>
+ <object class="GtkScrolledWindow">
<child>
- <object class="GtkCellRendererText">
- <property name="ellipsize">end</property>
- <property name="height">48</property>
- <property name="xalign">0</property>
- <property name="xpad">0</property>
- <property name="yalign">0.5</property>
+ <object class="GtkListView" id="_listview">
+ <property name="show-separators">True</property>
+ <style>
+ <class name="songs-list"/>
+ </style>
</object>
- <attributes>
- <attribute name="text">2</attribute>
- </attributes>
</child>
</object>
</child>
- <child>
- <object class="GtkTreeViewColumn" id="_artist_column">
- <property name="expand">True</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkCellRendererText">
- <property name="ellipsize">end</property>
- <property name="xpad">32</property>
- </object>
- <attributes>
- <attribute name="text">3</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="_album_column">
- <property name="expand">True</property>
- <property name="visible">True</property>
- <child>
- <object class="GtkCellRendererText">
- <property name="ellipsize">end</property>
- <property name="xpad">32</property>
- </object>
- <attributes>
- <attribute name="text">4</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="_duration_column">
- <property name="visible">True</property>
- <child>
- <object class="GtkCellRendererText" id="_duration_renderer">
- <property name="xalign">1</property>
- </object>
- <attributes>
- <attribute name="text">5</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="_star_column">
- <property name="visible">True</property>
- </object>
- </child>
</object>
</child>
</template>
- <object class="GtkGestureMultiPress" id="_songs_ctrlr">
- <property name="widget">_songs_view</property>
- <property name="propagation-phase">capture</property>
- <signal name="released" handler="_on_view_clicked" swapped="no"/>
- </object>
</interface>
diff --git a/data/ui/TwoLineTip.ui b/data/ui/TwoLineTip.ui
index 54cb04e9e..2ebece590 100644
--- a/data/ui/TwoLineTip.ui
+++ b/data/ui/TwoLineTip.ui
@@ -1,15 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="TwoLineTip" parent="GtkBox">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="vexpand">False</property>
<property name="orientation">vertical</property>
- <property name="visible">True</property>
<child>
<object class="GtkLabel" id="_title_label">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">start</property>
- <property name="visible">True</property>
<style>
<class name="tooltip-title"/>
</style>
@@ -17,9 +15,8 @@
</child>
<child>
<object class="GtkLabel" id="_subtitle_label">
- <property name="can_focus">False</property>
+ <property name="focusable">False</property>
<property name="halign">start</property>
- <property name="visible">True</property>
</object>
</child>
</template>
diff --git a/data/ui/Window.ui b/data/ui/Window.ui
index 276952595..92052f1bd 100644
--- a/data/ui/Window.ui
+++ b/data/ui/Window.ui
@@ -1,30 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="Window" parent="HdyApplicationWindow">
+ <template class="Window" parent="AdwApplicationWindow">
<property name="default-height">500</property>
<property name="default-width">300</property>
+ <child>
+ <object class="GtkEventControllerKey">
+ <property name="propagation-phase">capture</property>
+ <signal name="key-pressed" handler="_on_key_press" swapped="no"/>
+ </object>
+ </child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
- <property name="visible">True</property>
<child>
<object class="GtkStack" id="_headerbar_stack">
<property name="transition-type">crossfade</property>
- <property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkOverlay" id="_overlay">
<property name="vexpand">True</property>
- <property name="visible">True</property>
<child>
- <object class="GtkStack" id="_stack">
- <property name="can-focus">False</property>
+ <object class="AdwViewStack" id="_stack">
+ <property name="focusable">False</property>
<property name="hhomogeneous">False</property>
<property name="vhomogeneous">False</property>
- <property name="transition-duration">100</property>
- <property name="transition-type">crossfade</property>
- <property name="visible">True</property>
</object>
</child>
<child type="overlay">
@@ -45,9 +45,4 @@
</object>
</child>
</template>
- <object class="GtkEventControllerKey" id="_key_controller">
- <property name="widget">Window</property>
- <property name="propagation-phase">capture</property>
- <signal name="key-pressed" handler="_on_key_press" swapped="no"/>
- </object>
</interface>
diff --git a/data/ui/help-overlay.ui b/data/ui/help-overlay.ui
index 86b0f0fc8..f7123b775 100644
--- a/data/ui/help-overlay.ui
+++ b/data/ui/help-overlay.ui
@@ -4,37 +4,31 @@
<property name="modal">True</property>
<child>
<object class="GtkShortcutsSection">
- <property name="visible">True</property>
<property name="section-name">shortcuts</property>
<property name="max-height">17</property>
<child>
<object class="GtkShortcutsGroup">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">General</property>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Close window</property>
<property name="accelerator"><Primary>Q</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Search</property>
<property name="accelerator"><Primary>F</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Help</property>
<property name="accelerator">F1</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Shortcuts</property>
<property name="accelerator"><Primary>question</property>
</object>
@@ -43,39 +37,33 @@
</child>
<child>
<object class="GtkShortcutsGroup">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Playback</property>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Play/Pause</property>
<property name="accelerator"><Ctrl>space</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Next song</property>
<property name="accelerator"><Ctrl>N</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Previous song</property>
<property name="accelerator"><Ctrl>B</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Toggle repeat</property>
<property name="accelerator"><Ctrl>R</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Toggle shuffle</property>
<property name="accelerator"><Ctrl>S</property>
</object>
@@ -84,39 +72,33 @@
</child>
<child>
<object class="GtkShortcutsGroup">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Navigation</property>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Go to Albums</property>
<property name="accelerator"><Alt>1</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Go to Artists</property>
<property name="accelerator"><Alt>2</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Go to Songs</property>
<property name="accelerator"><Alt>3</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Go to
Playlists</property>
<property name="accelerator"><Alt>4</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
- <property name="visible">True</property>
<property name="title" translatable="yes" context="shortcut window">Go back</property>
<property name="accelerator"><Alt>Left</property>
</object>
diff --git a/gnome-music.in b/gnome-music.in
index ca1a3ddac..3cc186f00 100755
--- a/gnome-music.in
+++ b/gnome-music.in
@@ -40,30 +40,19 @@ if _LOCAL:
import gi
-gi.require_version('Gtk', '3.0')
+gi.require_version("Adw", "1")
+gi.require_version('Gtk', '4.0')
gi.require_version('GIRepository', '2.0')
gi.require_version('Gst', '1.0')
-gi.require_version("Handy", "1")
-from gi.repository import GIRepository, Gio, Gtk, Gst, Handy
+from gi.repository import Adw, GIRepository, Gio, Gtk, Gst
Gst.init(None)
-Handy.init()
+Adw.init()
LOCALE_DIR = '@localedir@'
PKGDATA_DIR = '@pkgdatadir@'
-def set_gfm():
- """Configures application to use gfm."""
- gfm_libdir = '@gfmlibdir@'
- if _LOCAL:
- gfm_typelibdir = '@gfmlibdir@'
- else:
- gfm_typelibdir = '@gfmlibdir@/girepository-1.0'
-
- GIRepository.Repository.prepend_search_path(gfm_typelibdir)
- GIRepository.Repository.prepend_library_path(gfm_libdir)
-
def set_exception_hook():
"""Configures sys.excepthook to enforce Gtk application exiting."""
@@ -110,8 +99,7 @@ def run_application():
def main():
"""Sets environment and runs GNOME Music."""
- set_gfm()
- set_exception_hook()
+ # set_exception_hook()
set_internationalization()
set_resources()
return run_application()
diff --git a/gnomemusic/application.py b/gnomemusic/application.py
index 341e8e1d5..aeb2da9c3 100644
--- a/gnomemusic/application.py
+++ b/gnomemusic/application.py
@@ -33,7 +33,7 @@
from typing import Optional
from gettext import gettext as _
-from gi.repository import Gtk, Gio, GLib, Gdk, GObject, Handy
+from gi.repository import Adw, Gtk, Gio, GLib, Gdk, GObject
from gnomemusic.coregrilo import CoreGrilo
from gnomemusic.coremodel import CoreModel
@@ -62,7 +62,6 @@ class Application(Gtk.Application):
GLib.set_prgname(application_id)
GLib.setenv("PULSE_PROP_media.role", "music", True)
- self._init_style()
self._window = None
self._log = MusicLogger()
@@ -87,10 +86,10 @@ class Application(Gtk.Application):
def _init_style(self):
css_provider = Gtk.CssProvider()
css_provider.load_from_resource('/org/gnome/Music/org.gnome.Music.css')
- screen = Gdk.Screen.get_default()
- style_context = Gtk.StyleContext()
- style_context.add_provider_for_screen(
- screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+ display = Gdk.Display.get_default()
+ style_context = self._window.get_style_context()
+ style_context.add_provider_for_display(
+ display, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
@GObject.Property(
type=CoreGrilo, default=None, flags=GObject.ParamFlags.READABLE)
@@ -234,8 +233,8 @@ class Application(Gtk.Application):
def do_startup(self):
Gtk.Application.do_startup(self)
- Handy.StyleManager.get_default().set_color_scheme(
- Handy.ColorScheme.PREFER_LIGHT)
+ Adw.StyleManager.get_default().set_color_scheme(
+ Adw.ColorScheme.PREFER_LIGHT)
self._set_actions()
def _quit(self, action=None, param=None):
@@ -246,6 +245,7 @@ class Application(Gtk.Application):
self._window = Window(self)
self.notify("window")
self._window.set_default_icon_name(self.props.application_id)
+ self._init_style()
if self.props.application_id == "org.gnome.Music.Devel":
self._window.get_style_context().add_class('devel')
MPRIS(self)
diff --git a/gnomemusic/artcache.py b/gnomemusic/artcache.py
index 07ef47765..bb7d9c635 100644
--- a/gnomemusic/artcache.py
+++ b/gnomemusic/artcache.py
@@ -24,10 +24,9 @@
from gi.repository import Gdk, GdkPixbuf, Gio, Gtk, GLib, GObject
-from gnomemusic.corealbum import CoreAlbum
from gnomemusic.coreartist import CoreArtist
-from gnomemusic.coresong import CoreSong
-from gnomemusic.defaulticon import DefaultIcon, make_icon_frame
+from gnomemusic.coverpaintable import CoverPaintable
+from gnomemusic.defaulticon import DefaultIcon
from gnomemusic.musiclogger import MusicLogger
from gnomemusic.utils import ArtSize, DefaultIconType
@@ -36,7 +35,7 @@ class ArtCache(GObject.GObject):
"""Handles retrieval of MediaArt cache art
Uses signals to indicate success or failure and always returns a
- Cairo.Surface.
+ CoverPaintable.
"""
__gtype_name__ = "ArtCache"
@@ -58,8 +57,8 @@ class ArtCache(GObject.GObject):
self._widget = widget
self._coreobject = None
- self._default_icon = None
- self._surface = None
+ self._icon_type = DefaultIconType.ALBUM
+ self._paintable = None
def start(self, coreobject, size):
"""Start the cache query
@@ -71,16 +70,14 @@ class ArtCache(GObject.GObject):
self._size = size
if isinstance(coreobject, CoreArtist):
- self._default_icon = DefaultIcon(self._widget).get(
- DefaultIconType.ARTIST, self._size)
- elif (isinstance(coreobject, CoreAlbum)
- or isinstance(coreobject, CoreSong)):
- self._default_icon = DefaultIcon(self._widget).get(
- DefaultIconType.ALBUM, self._size)
+ self._icon_type = DefaultIconType.ARTIST
+
+ self._paintable = DefaultIcon(self._widget).get(
+ self._icon_type, self._size)
thumbnail_uri = coreobject.props.thumbnail
if thumbnail_uri == "generic":
- self.emit("finished", self._default_icon)
+ self.emit("finished", self._paintable)
return
thumb_file = Gio.File.new_for_uri(thumbnail_uri)
@@ -89,7 +86,7 @@ class ArtCache(GObject.GObject):
GLib.PRIORITY_DEFAULT_IDLE, None, self._open_stream, None)
return
- self.emit("finished", self._default_icon)
+ self.emit("finished", self._paintable)
def _open_stream(self, thumb_file, result, arguments):
try:
@@ -97,7 +94,7 @@ class ArtCache(GObject.GObject):
except GLib.Error as error:
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
- self.emit("finished", self._default_icon)
+ self.emit("finished", self._paintable)
return
GdkPixbuf.Pixbuf.new_from_stream_async(
@@ -109,23 +106,18 @@ class ArtCache(GObject.GObject):
except GLib.Error as error:
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
- self.emit("finished", self._default_icon)
+ self.emit("finished", self._paintable)
return
+ texture = Gdk.Texture.new_for_pixbuf(pixbuf)
+ if texture:
+ self._paintable = CoverPaintable(
+ self._size, self._widget, icon_type=self._icon_type,
+ texture=texture)
+
stream.close_async(
GLib.PRIORITY_DEFAULT_IDLE, None, self._close_stream, None)
- scale = self._widget.props.scale_factor
- surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, None)
- if isinstance(self._coreobject, CoreArtist):
- surface = make_icon_frame(
- surface, self._size, scale, round_shape=True)
- elif (isinstance(self._coreobject, CoreAlbum)
- or isinstance(self._coreobject, CoreSong)):
- surface = make_icon_frame(surface, self._size, scale)
-
- self._surface = surface
-
def _close_stream(self, stream, result, data):
try:
stream.close_finish(result)
@@ -133,4 +125,4 @@ class ArtCache(GObject.GObject):
self._log.warning(
"Error: {}, {}".format(error.domain, error.message))
- self.emit("finished", self._surface)
+ self.emit("finished", self._paintable)
diff --git a/gnomemusic/corealbum.py b/gnomemusic/corealbum.py
index d0cb51deb..27008184e 100644
--- a/gnomemusic/corealbum.py
+++ b/gnomemusic/corealbum.py
@@ -22,9 +22,11 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
+from __future__ import annotations
+
import gi
-gi.require_versions({"Gfm": "0.1", "Grl": "0.3"})
-from gi.repository import Gfm, Gio, Grl, GObject
+gi.require_versions({"Grl": "0.3"})
+from gi.repository import Gio, Grl, Gtk, GObject
import gnomemusic.utils as utils
@@ -75,20 +77,21 @@ class CoreAlbum(GObject.GObject):
def _get_album_model(self):
disc_model = Gio.ListStore()
- disc_model_sort = Gfm.SortListModel.new(disc_model)
+ disc_model_sort = Gtk.SortListModel.new(disc_model)
- def _disc_order_sort(disc_a, disc_b):
+ def _disc_order_sort(disc_a, disc_b, data=None):
return disc_a.props.disc_nr - disc_b.props.disc_nr
- disc_model_sort.set_sort_func(
- utils.wrap_list_store_sort_func(_disc_order_sort))
+ disc_sorter = Gtk.CustomSorter()
+ disc_sorter.set_sort_func(_disc_order_sort)
+ disc_model_sort.set_sorter(disc_sorter)
self._coregrilo.get_album_discs(self.props.media, disc_model)
return disc_model_sort
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def model(self):
if self._model is None:
@@ -158,3 +161,7 @@ class CoreAlbum(GObject.GObject):
if self._thumbnail != "generic":
self.props.media.set_thumbnail(self._thumbnail)
+
+ @GObject.Property(type=object, flags=GObject.ParamFlags.READABLE)
+ def corealbum(self) -> CoreAlbum:
+ return self
diff --git a/gnomemusic/coreartist.py b/gnomemusic/coreartist.py
index 62915a9f0..90a38d3d9 100644
--- a/gnomemusic/coreartist.py
+++ b/gnomemusic/coreartist.py
@@ -23,8 +23,8 @@
# delete this exception statement from your version.
import gi
-gi.require_versions({"Gfm": "0.1", "Grl": "0.3"})
-from gi.repository import Gfm, Grl, GObject
+gi.require_versions({"Grl": "0.3"})
+from gi.repository import Grl, Gtk, GObject
from gnomemusic.artistart import ArtistArt
import gnomemusic.utils as utils
@@ -59,24 +59,25 @@ class CoreArtist(GObject.GObject):
self.props.artist = utils.get_artist_name(media)
def _get_artist_album_model(self):
- albums_model_filter = Gfm.FilterListModel.new(
+ albums_model_filter = Gtk.FilterListModel.new(
self._coremodel.props.albums)
- albums_model_filter.set_filter_func(lambda a: False)
+ albums_model_filter.set_filter(Gtk.AnyFilter())
- albums_model_sort = Gfm.SortListModel.new(albums_model_filter)
+ albums_model_sort = Gtk.SortListModel.new(albums_model_filter)
self._coregrilo.get_artist_albums(
self.props.media, albums_model_filter)
- def _album_sort(album_a, album_b):
+ def _album_sort(album_a, album_b, data=None):
return album_a.props.year > album_b.props.year
- albums_model_sort.set_sort_func(
- utils.wrap_list_store_sort_func(_album_sort))
+ albums_sorter = Gtk.CustomSorter()
+ albums_sorter.set_sort_func(_album_sort)
+ albums_model_sort.set_sorter(albums_sorter)
return albums_model_sort
- @GObject.Property(type=Gfm.SortListModel, default=None)
+ @GObject.Property(type=Gtk.SortListModel, default=None)
def model(self):
if self._model is None:
self._model = self._get_artist_album_model()
diff --git a/gnomemusic/coredisc.py b/gnomemusic/coredisc.py
index 4446a6a22..09780018b 100644
--- a/gnomemusic/coredisc.py
+++ b/gnomemusic/coredisc.py
@@ -22,9 +22,7 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
-from gi.repository import GObject, Gio, Gfm, Grl
-
-import gnomemusic.utils as utils
+from gi.repository import GObject, Gio, Grl, Gtk
class CoreDisc(GObject.GObject):
@@ -59,16 +57,18 @@ class CoreDisc(GObject.GObject):
@GObject.Property(type=Gio.ListModel, default=None)
def model(self):
- def _disc_sort(song_a, song_b):
+ def _disc_sort(song_a, song_b, data=None):
return song_a.props.track_number - song_b.props.track_number
if self._model is None:
- self._filter_model = Gfm.FilterListModel.new(
+ self._filter_model = Gtk.FilterListModel.new(
self._coremodel.props.songs)
- self._filter_model.set_filter_func(lambda a: False)
- self._model = Gfm.SortListModel.new(self._filter_model)
- self._model.set_sort_func(
- utils.wrap_list_store_sort_func(_disc_sort))
+ self._filter_model.set_filter(Gtk.AnyFilter())
+
+ self._model = Gtk.SortListModel.new(self._filter_model)
+ disc_sorter = Gtk.CustomSorter()
+ disc_sorter.set_sort_func(_disc_sort)
+ self._model.set_sorter(disc_sorter)
self._model.connect("items-changed", self._on_disc_changed)
diff --git a/gnomemusic/coregrilo.py b/gnomemusic/coregrilo.py
index b94f4d1ff..e5c17acab 100644
--- a/gnomemusic/coregrilo.py
+++ b/gnomemusic/coregrilo.py
@@ -25,8 +25,8 @@
import weakref
import gi
-gi.require_versions({"Grl": "0.3", "Gfm": "0.1"})
-from gi.repository import Grl, GLib, GObject, Gfm
+gi.require_version("Grl", "0.3")
+from gi.repository import Grl, GLib, GObject, Gtk
from gnomemusic.grilowrappers.grlsearchwrapper import GrlSearchWrapper
from gnomemusic.grilowrappers.grltrackerwrapper import GrlTrackerWrapper
@@ -178,7 +178,7 @@ class CoreGrilo(GObject.GObject):
"""Get all album by an artist
:param Grl.Media media: A Grilo Media item that represents Artist
- :param Gfm.FilterListModel filter_model: The model to fill
+ :param Gtk.FilterListModel filter_model: The model to fill
"""
source = media.get_source()
self._wrappers[source].get_artist_albums(media, filter_model)
@@ -187,19 +187,19 @@ class CoreGrilo(GObject.GObject):
"""Get all discs from an album
:param Grl.Media media: A Grilo Media item that represents Album
- :param Gfm.SortListModel disc_model: The model to fill
+ :param Gtk.SortListModel disc_model: The model to fill
"""
source = media.get_source()
self._wrappers[source].get_album_discs(media, disc_model)
def get_album_disc(
self, media: Grl.Media, discnr: int,
- model: Gfm.FilterListModel) -> None:
+ model: Gtk.FilterListModel) -> None:
"""Get all songs from an album disc
:param Grl.Media media: An album
:param int discnr: The disc number
- :param Gfm.FilterListModel model: The model to fill
+ :param Gtk.FilterListModel model: The model to fill
"""
source = media.get_source()
self._wrappers[source].get_album_disc(media, discnr, model)
diff --git a/gnomemusic/coremodel.py b/gnomemusic/coremodel.py
index b9110a8d4..97a930405 100644
--- a/gnomemusic/coremodel.py
+++ b/gnomemusic/coremodel.py
@@ -23,19 +23,16 @@
# delete this exception statement from your version.
from __future__ import annotations
-from typing import Optional, Union
+from typing import Any, Optional, Union
import typing
-import gi
-gi.require_version("Gfm", "0.1")
-from gi.repository import GLib, GObject, Gio, Gfm, Gtk
+from gi.repository import GLib, GObject, Gio, Gtk
from gnomemusic.corealbum import CoreAlbum
from gnomemusic.coreartist import CoreArtist
from gnomemusic.coresong import CoreSong
from gnomemusic.grilowrappers.grltrackerplaylists import Playlist
from gnomemusic.player import PlayerPlaylist
-from gnomemusic.songliststore import SongListStore
from gnomemusic.widgets.songwidget import SongWidget
if typing.TYPE_CHECKING:
from gnomemusic.application import Application
@@ -85,86 +82,91 @@ class CoreModel(GObject.GObject):
"""
super().__init__()
- self._flatten_model: Optional[Gfm.FlattenListModel] = None
+ self._application = application
+ self._flatten_model: Optional[Gtk.FlattenListModel] = None
self._player_signal_id = 0
self._current_playlist_model: Optional[Union[
- Gfm.FlattenListModel, Gfm.SortListModel, Gio.ListModel]] = None
+ Gtk.FlattenListModel, Gtk.SortListModel, Gio.ListModel]] = None
self._previous_playlist_model: Optional[Union[
- Gfm.FlattenListModel, Gfm.SortListModel, Gio.ListModel]] = None
+ Gtk.FlattenListModel, Gtk.SortListModel, Gio.ListModel]] = None
self._songs_model_proxy: Gio.ListStore = Gio.ListStore.new(
Gio.ListModel)
- self._songs_model: Gfm.FlattenListModel = Gfm.FlattenListModel.new(
- CoreSong, self._songs_model_proxy)
- self._songliststore = SongListStore(self._songs_model)
-
- self._application = application
+ self._flatten_songs_model = Gtk.FlattenListModel.new(
+ self._songs_model_proxy)
+ self._songs_model = Gtk.SortListModel.new(self._flatten_songs_model)
+ sorter = Gtk.CustomSorter()
+ sorter.set_sort_func(self._songs_sort)
+ self._songs_model.props.incremental = True
+ self._songs_model.set_sorter(sorter)
self._albums_model_proxy: Gio.ListStore = Gio.ListStore.new(
Gio.ListModel)
- self._albums_model: Gfm.FlattenListModel = Gfm.FlattenListModel.new(
- CoreAlbum, self._albums_model_proxy)
- self._albums_model_sort: Gfm.SortListModel = Gfm.SortListModel.new(
+ self._albums_model: Gtk.FlattenListModel = Gtk.FlattenListModel.new(
+ self._albums_model_proxy)
+ self._albums_model_sort: Gtk.SortListModel = Gtk.SortListModel.new(
self._albums_model)
- self._albums_model_sort.set_sort_func(
- utils.wrap_list_store_sort_func(self._albums_sort))
+ albums_sorter = Gtk.CustomSorter()
+ albums_sorter.set_sort_func(self._albums_sort)
+ self._albums_model_sort.set_sorter(albums_sorter)
self._artists_model_proxy: Gio.ListStore = Gio.ListStore.new(
Gio.ListModel)
- self._artists_model: Gfm.FlattenListModel = Gfm.FlattenListModel.new(
- CoreArtist, self._artists_model_proxy)
- self._artists_model_sort: Gfm.SortListModel = Gfm.SortListModel.new(
+ self._artists_model: Gtk.FlattenListModel = Gtk.FlattenListModel.new(
+ self._artists_model_proxy)
+ self._artists_model_sort: Gtk.SortListModel = Gtk.SortListModel.new(
self._artists_model)
- self._artists_model_sort.set_sort_func(
- utils.wrap_list_store_sort_func(self._artist_sort))
+ artists_sorter = Gtk.CustomSorter()
+ artists_sorter.set_sort_func(self._artist_sort)
+ self._artists_model_sort.set_sorter(artists_sorter)
self._playlist_model: Gio.ListStore = Gio.ListStore.new(CoreSong)
- self._playlist_model_sort: Gfm.SortListModel = Gfm.SortListModel.new(
+ self._playlist_model_sort: Gtk.SortListModel = Gtk.SortListModel.new(
self._playlist_model)
- self._playlist_model_recent: Gfm.SliceListModel = (
- Gfm.SliceListModel.new(
+ self._playlist_model_recent: Gtk.SliceListModel = (
+ Gtk.SliceListModel.new(
self._playlist_model_sort, 0, self._recent_size))
self._active_core_object: Optional[Union[
CoreAlbum, CoreArtist, Playlist]] = None
self._songs_search_proxy: Gio.ListStore = Gio.ListStore.new(
- Gfm.FilterListModel)
- self._songs_search_flatten: Gfm.FlattenListModel = (
- Gfm.FlattenListModel.new(CoreSong))
+ Gtk.FilterListModel)
+ self._songs_search_flatten: Gtk.FlattenListModel = (
+ Gtk.FlattenListModel())
self._songs_search_flatten.set_model(self._songs_search_proxy)
self._albums_search_proxy: Gio.Liststore = Gio.ListStore.new(
- Gfm.FilterListModel)
- self._albums_search_flatten: Gfm.FlattenListModel = (
- Gfm.FlattenListModel.new(CoreAlbum))
+ Gtk.FilterListModel)
+ self._albums_search_flatten: Gtk.FlattenListModel = (
+ Gtk.FlattenListModel())
self._albums_search_flatten.set_model(self._albums_search_proxy)
-
- self._albums_search_filter: Gfm.FilterListModel = (
- Gfm.FilterListModel.new(self._albums_search_flatten))
+ self._albums_search_filter: Gtk.FilterListModel = (
+ Gtk.FilterListModel.new(Gtk.AnyFilter()))
self._artists_search_proxy: Gio.Liststore = Gio.ListStore.new(
- Gfm.FilterListModel)
- self._artists_search_flatten: Gfm.FlattenListModel = (
- Gfm.FlattenListModel.new(CoreArtist))
+ Gtk.FilterListModel)
+ self._artists_search_flatten: Gtk.FlattenListModel = (
+ Gtk.FlattenListModel())
self._artists_search_flatten.set_model(self._artists_search_proxy)
-
- self._artists_search_filter: Gfm.FilterListModel = (
- Gfm.FilterListModel.new(self._artists_search_flatten))
+ self._artists_search_filter: Gtk.FilterListModel = (
+ Gtk.FilterListModel.new(Gtk.AnyFilter()))
self._playlists_model: Gio.ListStore = Gio.ListStore.new(Playlist)
- self._playlists_model_filter: Gfm.FilterListModel = (
- Gfm.FilterListModel.new(self._playlists_model))
- self._playlists_model_sort: Gfm.SortListModel = Gfm.SortListModel.new(
+ self._playlists_model_filter: Gtk.FilterListModel = (
+ Gtk.FilterListModel.new(self._playlists_model))
+ self._playlists_model_sort: Gtk.SortListModel = Gtk.SortListModel.new(
self._playlists_model_filter)
- self._playlists_model_sort.set_sort_func(
- utils.wrap_list_store_sort_func(self._playlists_sort))
-
- self._user_playlists_model_filter: Gfm.FilterListModel = (
- Gfm.FilterListModel.new(self._playlists_model))
- self._user_playlists_model_sort: Gfm.SortListModel = (
- Gfm.SortListModel.new(self._user_playlists_model_filter))
- self._user_playlists_model_sort.set_sort_func(
- utils.wrap_list_store_sort_func(self._playlists_sort))
+ playlists_sorter = Gtk.CustomSorter()
+ playlists_sorter.set_sort_func(self._playlists_sort)
+ self._playlists_model_sort.set_sorter(playlists_sorter)
+
+ self._user_playlists_model_filter: Gtk.FilterListModel = (
+ Gtk.FilterListModel.new(self._playlists_model))
+ self._user_playlists_model_sort: Gtk.SortListModel = (
+ Gtk.SortListModel.new(self._user_playlists_model_filter))
+ user_playlists_sorter = Gtk.CustomSorter()
+ user_playlists_sorter.set_sort_func(self._playlists_sort)
+ self._user_playlists_model_sort.set_sorter(user_playlists_sorter)
self._search: Search = application.props.search
@@ -183,18 +185,33 @@ class CoreModel(GObject.GObject):
else:
self.props.songs_available = False
- def _filter_selected(self, coresong):
- return coresong.props.selected
-
- def _albums_sort(self, album_a, album_b):
+ def _songs_sort(
+ self, song_a: CoreSong, song_b: CoreSong, data: Any = None) -> int:
+ title_a = song_a.props.title
+ title_b = song_b.props.title
+ song_cmp = (utils.normalize_caseless(title_a)
+ == utils.normalize_caseless(title_b))
+ if not song_cmp:
+ return utils.natural_sort_names(title_a, title_b)
+
+ artist_a = song_a.props.artist
+ artist_b = song_b.props.artist
+ artist_cmp = (utils.normalize_caseless(artist_a)
+ == utils.normalize_caseless(artist_b))
+ if not artist_cmp:
+ return utils.natural_sort_names(artist_a, artist_b)
+
+ return utils.natural_sort_names(song_a.props.album, song_b.props.album)
+
+ def _albums_sort(self, album_a, album_b, data=None):
return utils.natural_sort_names(
album_a.props.title, album_b.props.title)
- def _artist_sort(self, artist_a, artist_b):
+ def _artist_sort(self, artist_a, artist_b, data=None):
return utils.natural_sort_names(
artist_a.props.artist, artist_b.props.artist)
- def _playlists_sort(self, playlist_a, playlist_b):
+ def _playlists_sort(self, playlist_a, playlist_b, data=None):
if playlist_a.props.is_smart:
if not playlist_b.props.is_smart:
return -1
@@ -260,8 +277,7 @@ class CoreModel(GObject.GObject):
for disc in model:
proxy_model.append(disc.props.model)
- self._flatten_model = Gfm.FlattenListModel.new(
- CoreSong, proxy_model)
+ self._flatten_model = Gtk.FlattenListModel.new(proxy_model)
self._current_playlist_model = self._flatten_model
for model_song in self._flatten_model:
@@ -276,8 +292,7 @@ class CoreModel(GObject.GObject):
for disc in artist_album.model:
proxy_model.append(disc.props.model)
- self._flatten_model = Gfm.FlattenListModel.new(
- CoreSong, proxy_model)
+ self._flatten_model = Gtk.FlattenListModel.new(proxy_model)
self._current_playlist_model = self._flatten_model
for model_song in self._flatten_model:
@@ -286,9 +301,9 @@ class CoreModel(GObject.GObject):
songs_added.append(song)
elif playlist_type == PlayerPlaylist.Type.SONGS:
- self._current_playlist_model = self._songliststore.props.model
+ self._current_playlist_model = self._songs_model
- for song in self._songliststore.props.model:
+ for song in self._songs_model:
songs_added.append(song)
if song.props.state == SongWidget.State.PLAYING:
@@ -363,7 +378,7 @@ class CoreModel(GObject.GObject):
return self._songs_model
@GObject.Property(
- type=Gfm.FlattenListModel, default=None,
+ type=Gtk.FlattenListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def songs_proxy(self):
return self._songs_model_proxy
@@ -374,7 +389,7 @@ class CoreModel(GObject.GObject):
return self._albums_model
@GObject.Property(
- type=Gfm.FlattenListModel, default=None,
+ type=Gtk.FlattenListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def albums_proxy(self):
return self._albums_model_proxy
@@ -385,7 +400,7 @@ class CoreModel(GObject.GObject):
return self._artists_model
@GObject.Property(
- type=Gfm.FlattenListModel, default=None,
+ type=Gtk.FlattenListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def artists_proxy(self):
return self._artists_model_proxy
@@ -396,25 +411,25 @@ class CoreModel(GObject.GObject):
return self._playlist_model
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def albums_sort(self):
return self._albums_model_sort
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def artists_sort(self):
return self._artists_model_sort
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def playlist_sort(self):
return self._playlist_model_sort
@GObject.Property(
- type=Gfm.SliceListModel, default=None,
+ type=Gtk.SliceListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def recent_playlist(self):
return self._playlist_model_recent
@@ -426,7 +441,7 @@ class CoreModel(GObject.GObject):
return self._recent_size // 2
@GObject.Property(
- type=Gfm.FilterListModel, default=None,
+ type=Gtk.FilterListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def songs_search(self):
return self._songs_search_flatten
@@ -438,13 +453,13 @@ class CoreModel(GObject.GObject):
return self._songs_search_proxy
@GObject.Property(
- type=Gfm.FlattenListModel, default=None,
+ type=Gtk.FlattenListModel, default=None,
flags=GObject.ParamFlags.READABLE)
- def albums_search(self) -> Gfm.FlattenListModel:
+ def albums_search(self) -> Gtk.FlattenListModel:
return self._albums_search_flatten
@GObject.Property(
- type=Gfm.FilterListModel, default=None,
+ type=Gtk.FilterListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def albums_search_filter(self):
return self._albums_search_filter
@@ -455,13 +470,13 @@ class CoreModel(GObject.GObject):
return self._albums_search_proxy
@GObject.Property(
- type=Gfm.FlattenListModel, default=None,
+ type=Gtk.FlattenListModel, default=None,
flags=GObject.ParamFlags.READABLE)
- def artists_search(self) -> Gfm.FlattenListModel:
+ def artists_search(self) -> Gtk.FlattenListModel:
return self._artists_search_flatten
@GObject.Property(
- type=Gfm.FilterListModel, default=None,
+ type=Gtk.FilterListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def artists_search_filter(self):
return self._artists_search_filter
@@ -471,36 +486,31 @@ class CoreModel(GObject.GObject):
def artists_search_proxy(self) -> Gio.ListStore:
return self._artists_search_proxy
- @GObject.Property(
- type=Gtk.ListStore, default=None, flags=GObject.ParamFlags.READABLE)
- def songs_gtkliststore(self):
- return self._songliststore
-
@GObject.Property(
type=Gio.ListStore, default=None, flags=GObject.ParamFlags.READABLE)
def playlists(self):
return self._playlists_model
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def playlists_sort(self):
return self._playlists_model_sort
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def playlists_filter(self):
return self._playlists_model_filter
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def user_playlists_sort(self):
return self._user_playlists_model_sort
@GObject.Property(
- type=Gfm.SortListModel, default=None,
+ type=Gtk.SortListModel, default=None,
flags=GObject.ParamFlags.READABLE)
def user_playlists_filter(self):
return self._user_playlists_model_filter
diff --git a/gnomemusic/coverpaintable.py b/gnomemusic/coverpaintable.py
new file mode 100644
index 000000000..540f2371d
--- /dev/null
+++ b/gnomemusic/coverpaintable.py
@@ -0,0 +1,118 @@
+# Copyright 2022 The GNOME Music developers
+#
+# GNOME Music is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GNOME Music is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The GNOME Music authors hereby grant permission for non-GPL compatible
+# GStreamer plugins to be used and distributed together with GStreamer
+# and GNOME Music. This permission is above and beyond the permissions
+# granted by the GPL license by which GNOME Music is covered. If you
+# modify this code, you may extend this exception to your version of the
+# code, but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version.
+
+from __future__ import annotations
+
+import gi
+gi.require_versions({"Gdk": "4.0", "Gtk": "4.0", "Gsk": "4.0"})
+from gi.repository import Gsk, Gtk, GObject, Graphene, Gdk
+
+from gnomemusic.utils import ArtSize, DefaultIconType
+
+
+class CoverPaintable(GObject.GObject, Gdk.Paintable):
+ """An album/artist cover or placeholder
+
+ Provides the full looks. Rounded corners for albums and round for
+ artists.
+ """
+
+ __gtype_name__ = "CoverPaintable"
+
+ def __init__(
+ self, art_size: ArtSize, widget: Gtk.Widget,
+ icon_type: DefaultIconType = DefaultIconType.ALBUM,
+ texture: Gdk.Texture = None, dark: bool = False) -> None:
+ """Initiliaze CoverPaintable
+
+ :param ArtSize art_size: Size of the cover
+ :param Gtk.Widget widget: Widget using the cover
+ :param DefaultIconType icon_type: Type of cover
+ :param Gdk.Texture texture: Texture to use or None for
+ placeholder
+ :param bool dark: Dark mode
+ """
+ super().__init__()
+
+ self._art_size = art_size
+ self._dark = dark
+ self._icon_theme = Gtk.IconTheme.new().get_for_display(
+ widget.get_display())
+ self._icon_type = icon_type
+ self._texture = texture
+ self._widget = widget
+
+ def do_snapshot(self, snapshot: Gtk.Snapshot, w: int, h: int) -> None:
+ if self._icon_type == DefaultIconType.ARTIST:
+ radius = 90.0
+ elif self._art_size == ArtSize.SMALL:
+ radius = 4.5
+ else:
+ radius = 9.0
+
+ w_s = w
+ h_s = h
+ if self._texture is not None:
+ ratio = self._texture.get_height() / self._texture.get_width()
+ # Scale down the image according to the biggest axis
+ if ratio > 1:
+ w = int(w / ratio)
+ else:
+ h = int(h * ratio)
+
+ rect = Graphene.Rect().init((w_s - w) / 2, (h_s - h) / 2, w, h)
+ rounded_rect = Gsk.RoundedRect()
+ rounded_rect.init_from_rect(rect, radius)
+ snapshot.push_rounded_clip(rounded_rect)
+
+ if self._texture is not None:
+ snapshot.append_texture(self._texture, rect)
+ else:
+ i_s = 1 / 3 # Icon scale
+ icon_pt = self._icon_theme.lookup_icon(
+ self._icon_type.value, None, w * i_s,
+ self._widget.props.scale_factor, 0, 0)
+
+ bg_color = Gdk.RGBA(1, 1, 1, 1)
+ if self._dark:
+ bg_color = Gdk.RGBA(0.3, 0.3, 0.3, 1)
+
+ snapshot.append_color(bg_color, Graphene.Rect().init(0, 0, w, h))
+ snapshot.translate(
+ Graphene.Point().init(
+ (w / 2) - (w * (i_s / 2)), (h / 2) - (h * (i_s / 2))))
+ snapshot.push_opacity(0.7)
+ icon_pt.snapshot(snapshot, w * i_s, h * i_s)
+ snapshot.pop()
+
+ snapshot.pop()
+
+ def do_get_flags(self) -> Gdk.PaintableFlags:
+ return Gdk.PaintableFlags.SIZE | Gdk.PaintableFlags.CONTENTS
+
+ def do_get_intrinsic_height(self) -> int:
+ return self._art_size.height
+
+ def do_get_intrinsic_width(self) -> int:
+ return self._art_size.width
diff --git a/gnomemusic/defaulticon.py b/gnomemusic/defaulticon.py
index b39ac60b7..29377a184 100644
--- a/gnomemusic/defaulticon.py
+++ b/gnomemusic/defaulticon.py
@@ -24,89 +24,18 @@
from __future__ import annotations
-from math import pi
from typing import Dict, Tuple
-import cairo
-from gi.repository import Gtk, GObject, Gdk, Handy
+from gi.repository import Adw, Gtk, GObject
+from gnomemusic.coverpaintable import CoverPaintable
from gnomemusic.utils import ArtSize, DefaultIconType
-def make_icon_frame(
- icon_surface, art_size=None, scale=1, default_icon=False,
- round_shape=False, dark=False):
- """Create an Art frame, square or round.
-
- :param cairo.Surface icon_surface: The surface to use
- :param art_size: The size of the art
- :param int scale: The scale of the art
- :param bool default_icon: Indicates of this is a default icon
- :param bool round_shape: Square or round indicator
- :param bool dark: Theme dark mode
-
- :return: The framed surface
- :rtype: cairo.Surface
- """
- degrees = pi / 180
- if art_size == ArtSize.SMALL:
- radius = 4.5
- else:
- radius = 9
- icon_w = icon_surface.get_width()
- icon_h = icon_surface.get_height()
- ratio = icon_h / icon_w
-
- # Scale down the image according to the biggest axis
- if ratio > 1:
- w = int(art_size.width / ratio)
- h = art_size.height
- else:
- w = art_size.width
- h = int(art_size.height * ratio)
-
- surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w * scale, h * scale)
- surface.set_device_scale(scale, scale)
- ctx = cairo.Context(surface)
-
- if round_shape:
- ctx.arc(w / 2, h / 2, w / 2, 0, 2 * pi)
- else:
- ctx.arc(w - radius, radius, radius, -90 * degrees, 0 * degrees)
- ctx.arc(
- w - radius, h - radius, radius, 0 * degrees, 90 * degrees)
- ctx.arc(radius, h - radius, radius, 90 * degrees, 180 * degrees)
- ctx.arc(radius, radius, radius, 180 * degrees, 270 * degrees)
-
- if dark:
- fill_color = Gdk.RGBA(0.28, 0.28, 0.28, 1.0)
- icon_color = Gdk.RGBA(1.0, 1.0, 1.0, 0.5)
- else:
- fill_color = Gdk.RGBA(1.0, 1.0, 1.0, 1.0)
- icon_color = Gdk.RGBA(0.0, 0.0, 0.0, 0.3)
-
- if default_icon:
- ctx.set_source_rgb(fill_color.red, fill_color.green, fill_color.blue)
- ctx.fill()
- ctx.set_source_rgba(
- icon_color.red, icon_color.green, icon_color.blue,
- icon_color.alpha)
- ctx.mask_surface(icon_surface, w / 3, h / 3)
- ctx.fill()
- else:
- ctx.scale((w * scale) / icon_w, (h * scale) / icon_h)
- ctx.set_source_surface(icon_surface, 0, 0)
- ctx.fill()
-
- return surface
-
-
class DefaultIcon(GObject.GObject):
"""Provides the symbolic fallback icons."""
_cache: Dict[
- Tuple[DefaultIconType, ArtSize, int, bool], cairo.ImageSurface] = {}
-
- _default_theme = Gtk.IconTheme.get_default()
+ Tuple[DefaultIconType, ArtSize, int, bool], CoverPaintable] = {}
def __init__(self, widget: Gtk.Widget) -> None:
"""Initialize DefaultIcon
@@ -118,37 +47,31 @@ class DefaultIcon(GObject.GObject):
self._widget = widget
def _make_default_icon(
- self, icon_type: DefaultIconType, art_size: ArtSize, scale: int,
- dark: bool) -> cairo.ImageSurface:
- icon_info = self._default_theme.lookup_icon_for_scale(
- icon_type.value, art_size.width / 3, scale, 0)
- icon = icon_info.load_surface()
-
- round_shape = icon_type == DefaultIconType.ARTIST
- icon_surface = make_icon_frame(
- icon, art_size, scale, True, round_shape, dark)
+ self, icon_type: DefaultIconType, art_size: ArtSize,
+ dark: bool) -> CoverPaintable:
+ paintable = CoverPaintable(
+ art_size, self._widget, icon_type=icon_type, dark=dark)
- return icon_surface
+ return paintable
def get(self, icon_type: DefaultIconType,
- art_size: ArtSize) -> cairo.ImageSurface:
+ art_size: ArtSize) -> CoverPaintable:
"""Returns the requested symbolic icon
- Returns a cairo surface of the requested symbolic icon in the
- given size and shape.
+ Returns a paintable of the requested symbolic icon in the
+ given size, shape and color.
:param DefaultIconType icon_type: The type of icon
:param ArtSize art_size: The size requested
:return: The symbolic icon
- :rtype: cairo.ImageSurface
+ :rtype: CoverPaintable
"""
- dark = Handy.StyleManager.get_default().props.dark
+ dark = Adw.StyleManager.get_default().props.dark
scale = self._widget.props.scale_factor
if (icon_type, art_size, scale, dark) not in self._cache.keys():
- new_icon = self._make_default_icon(
- icon_type, art_size, scale, dark)
+ new_icon = self._make_default_icon(icon_type, art_size, dark)
self._cache[(icon_type, art_size, scale, dark)] = new_icon
return self._cache[(icon_type, art_size, scale, dark)]
diff --git a/gnomemusic/grilowrappers/grlsearchwrapper.py b/gnomemusic/grilowrappers/grlsearchwrapper.py
index 5a90fa6f9..9db9149ec 100644
--- a/gnomemusic/grilowrappers/grlsearchwrapper.py
+++ b/gnomemusic/grilowrappers/grlsearchwrapper.py
@@ -23,8 +23,8 @@
# delete this exception statement from your version.
import gi
-gi.require_versions({"Gfm": "0.1", "Grl": "0.3"})
-from gi.repository import Gfm, Gio, Grl, GObject
+gi.require_versions({"Grl": "0.3"})
+from gi.repository import Gio, Grl, Gtk, GObject
from gnomemusic.coresong import CoreSong
@@ -75,9 +75,9 @@ class GrlSearchWrapper(GObject.GObject):
self._song_search_store = Gio.ListStore.new(CoreSong)
# FIXME: Workaround for adding the right list type to the proxy
# list model.
- self._song_search_model = Gfm.FilterListModel.new(
+ self._song_search_model = Gtk.FilterListModel.new(
self._song_search_store)
- self._song_search_model.set_filter_func(lambda a: True)
+ self._song_search_model.set_filter(Gtk.AnyFilter())
self._song_search_proxy.append(self._song_search_model)
self._fast_options = Grl.OperationOptions()
diff --git a/gnomemusic/grilowrappers/grltrackerplaylists.py b/gnomemusic/grilowrappers/grltrackerplaylists.py
index 9e8f26344..4d80aa493 100644
--- a/gnomemusic/grilowrappers/grltrackerplaylists.py
+++ b/gnomemusic/grilowrappers/grltrackerplaylists.py
@@ -27,8 +27,8 @@ import time
from gettext import gettext as _
import gi
-gi.require_versions({"Grl": "0.3"})
-from gi.repository import Gio, Grl, GLib, GObject, Tracker
+gi.require_versions({"Grl": "0.3", "Tracker": "3.0"})
+from gi.repository import Gio, Grl, Gtk, GLib, GObject, Tracker
from gnomemusic.coresong import CoreSong
import gnomemusic.utils as utils
@@ -68,7 +68,9 @@ class GrlTrackerPlaylists(GObject.GObject):
self._notificationmanager = application.props.notificationmanager
self._window = application.props.window
- self._user_model_filter.set_filter_func(self._user_playlists_filter)
+ user_playlists_filter = Gtk.CustomFilter()
+ user_playlists_filter.set_filter_func(self._user_playlists_filter)
+ self._user_model_filter.set_filter(user_playlists_filter)
self._fast_options = Grl.OperationOptions()
self._fast_options.set_resolution_flags(
@@ -146,7 +148,9 @@ class GrlTrackerPlaylists(GObject.GObject):
:param Playlist playlist: playlist
"""
self._pls_todelete.append(playlist)
- self._model_filter.set_filter_func(self._playlists_filter)
+ playlists_filter = Gtk.CustomFilter()
+ playlists_filter.set_filter_func(self._playlists_filter)
+ self._model_filter.set_filter(playlists_filter)
def finish_playlist_deletion(self, playlist, deleted):
"""Removes playlist from the list of playlists to delete
@@ -154,11 +158,15 @@ class GrlTrackerPlaylists(GObject.GObject):
:param Playlist playlist: playlist
:param bool deleted: indicates if the playlist has been deleted
"""
+ playlists_filter = Gtk.CustomFilter()
+ playlists_filter.set_filter_func(self._playlists_filter)
+ user_playlists_filter = Gtk.CustomFilter()
+ user_playlists_filter.set_filter_func(self._playlists_filter)
+
self._pls_todelete.remove(playlist)
if deleted is False:
- self._model_filter.set_filter_func(self._playlists_filter)
- self._user_model_filter.set_filter_func(
- self._user_playlists_filter)
+ self._model_filter.set_filter(playlists_filter)
+ self._user_model_filter.set_filter(user_playlists_filter)
return
def _delete_cb(conn, res, data):
@@ -173,7 +181,9 @@ class GrlTrackerPlaylists(GObject.GObject):
self._model.remove(idx)
break
- self._model_filter.set_filter_func(self._playlists_filter)
+ playlists_filter = Gtk.CustomFilter()
+ playlists_filter.set_filter_func(self._playlists_filter)
+ self._model_filter.set_filter(playlists_filter)
self._notificationmanager.pop_loading()
self._notificationmanager.push_loading()
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index a49db09e0..680ba409f 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -27,8 +27,8 @@ from typing import Callable, Dict, List, Optional
import typing
import gi
-gi.require_versions({"Gfm": "0.1", "Grl": "0.3", "Tracker": "3.0"})
-from gi.repository import Gfm, Gio, Grl, GLib, GObject, Tracker
+gi.require_versions({"Grl": "0.3", "Tracker": "3.0"})
+from gi.repository import Grl, Gio, Gtk, GLib, GObject, Tracker
from gnomemusic.asyncqueue import AsyncQueue
from gnomemusic.corealbum import CoreAlbum
@@ -127,19 +127,19 @@ class GrlTrackerWrapper(GObject.GObject):
self._notificationmanager: NotificationManager = (
application.props.notificationmanager)
- self._songs_search: Gfm.FilterListModel = Gfm.FilterListModel.new(
+ self._songs_search: Gtk.FilterListModel = Gtk.FilterListModel.new(
self._songs_model)
- self._songs_search.set_filter_func(lambda a: False)
+ self._songs_search.set_filter(Gtk.AnyFilter())
cm.props.songs_search_proxy.append(self._songs_search)
- self._albums_search: Gfm.FilterListModel = Gfm.FilterListModel.new(
+ self._albums_search: Gtk.FilterListModel = Gtk.FilterListModel.new(
self._albums_model)
- self._albums_search.set_filter_func(lambda a: False)
+ self._albums_search.set_filter(Gtk.AnyFilter())
cm.props.albums_search_proxy.append(self._albums_search)
- self._artists_search: Gfm.FilterListModel = Gfm.FilterListModel.new(
+ self._artists_search: Gtk.FilterListModel = Gtk.FilterListModel.new(
self._artists_model)
- self._artists_search.set_filter_func(lambda a: False)
+ self._artists_search.set_filter(Gtk.AnyFilter())
cm.props.artists_search_proxy.append(self._artists_search)
self._fast_options: Grl.OperationOptions = Grl.OperationOptions()
@@ -457,7 +457,7 @@ class GrlTrackerWrapper(GObject.GObject):
?urn nao:hasTag ?tag .
FILTER (?tag = nao:predefined-tag-favorite)
}}
- }}
+ }} ORDER BY ?title ?artist
""".split())
return query
@@ -668,11 +668,11 @@ class GrlTrackerWrapper(GObject.GObject):
query, metadata_keys, self._fast_options, _add_to_artists_model)
def get_artist_albums(
- self, media: Grl.Source, model: Gfm.FilterListModel) -> None:
+ self, media: Grl.Source, model: Gtk.FilterListModel) -> None:
"""Get all albums by an artist
:param Grl.Media media: The media with the artist id
- :param Gfm.FilterListModel model: The model to fill
+ :param Gtk.FilterListModel model: The model to fill
"""
self._notificationmanager.push_loading()
artist_id = media.get_id()
@@ -721,7 +721,9 @@ class GrlTrackerWrapper(GObject.GObject):
return
if not media:
- model.set_filter_func(albums_filter, albums)
+ custom_filter = Gtk.CustomFilter()
+ custom_filter.set_filter_func(albums_filter, albums)
+ model.set_filter(custom_filter)
self._notificationmanager.pop_loading()
return
@@ -739,11 +741,11 @@ class GrlTrackerWrapper(GObject.GObject):
query, [Grl.METADATA_KEY_TITLE], self._fast_options, query_cb)
def get_album_discs(
- self, media: Grl.Media, disc_model: Gfm.SortListModel) -> None:
+ self, media: Grl.Media, disc_model: Gtk.SortListModel) -> None:
"""Get all discs of an album
:param Grl.Media media: The media with the album id
- :param Gfm.SortListModel disc_model: The model to fill
+ :param Gtk.SortListModel disc_model: The model to fill
"""
self._notificationmanager.push_loading()
album_id = media.get_id()
@@ -798,12 +800,12 @@ class GrlTrackerWrapper(GObject.GObject):
def get_album_disc(
self, media: Grl.Media, disc_nr: int,
- model: Gfm.FilterListModel) -> None:
+ model: Gtk.FilterListModel) -> None:
"""Get all songs of an album disc
:param Grl.Media media: The media with the album id
:param int disc_nr: The disc number
- :param Gfm.FilterListModel model: The model to fill
+ :param Gtk.FilterListModel model: The model to fill
"""
album_id = media.get_id()
@@ -882,7 +884,9 @@ class GrlTrackerWrapper(GObject.GObject):
return
if media is None:
- model.set_filter_func(_filter_func)
+ custom_filter = Gtk.CustomFilter()
+ custom_filter.set_filter_func(_filter_func)
+ model.set_filter(custom_filter)
return
disc_song_ids.append(media.get_source() + media.get_id())
@@ -968,7 +972,9 @@ class GrlTrackerWrapper(GObject.GObject):
return
if not media:
- self._artists_search.set_filter_func(artist_filter)
+ custom_filter = Gtk.CustomFilter()
+ custom_filter.set_filter_func(artist_filter)
+ self._artists_search.set_filter(custom_filter)
self._notificationmanager.pop_loading()
return
@@ -1037,7 +1043,9 @@ class GrlTrackerWrapper(GObject.GObject):
return
if not media:
- self._albums_search.set_filter_func(album_filter)
+ custom_filter = Gtk.CustomFilter()
+ custom_filter.set_filter_func(album_filter)
+ self._albums_search.set_filter(custom_filter)
self._notificationmanager.pop_loading()
return
@@ -1111,7 +1119,9 @@ class GrlTrackerWrapper(GObject.GObject):
return
if not media:
- self._songs_search.set_filter_func(songs_filter)
+ custom_filter = Gtk.CustomFilter()
+ custom_filter.set_filter_func(songs_filter)
+ self._songs_search.set_filter(custom_filter)
self._notificationmanager.pop_loading()
return
diff --git a/gnomemusic/player.py b/gnomemusic/player.py
index 372d30103..4b0bdf162 100644
--- a/gnomemusic/player.py
+++ b/gnomemusic/player.py
@@ -30,12 +30,11 @@ import typing
import gi
gi.require_version('GstPbutils', '1.0')
-from gi.repository import GObject, GstPbutils
+from gi.repository import GObject, GstPbutils, Gtk
from gnomemusic.coresong import CoreSong
from gnomemusic.gstplayer import GstPlayer, Playback
from gnomemusic.widgets.songwidget import SongWidget
-import gnomemusic.utils as utils
class RepeatMode(Enum):
@@ -277,16 +276,18 @@ class PlayerPlaylist(GObject.GObject):
self._model_recent.set_offset(offset)
def _on_repeat_mode_changed(self, klass, param):
- def _shuffle_sort(song_a, song_b):
+ def _shuffle_sort(song_a, song_b, data=None):
return song_a.shuffle_pos < song_b.shuffle_pos
if self.props.repeat_mode == RepeatMode.SHUFFLE:
for idx, coresong in enumerate(self._model):
coresong.update_shuffle_pos()
- self._model.set_sort_func(
- utils.wrap_list_store_sort_func(_shuffle_sort))
+
+ songs_sorter = Gtk.CustomSorter()
+ songs_sorter.set_sort_func(_shuffle_sort)
+ self._model.set_sorter(songs_sorter)
elif self.props.repeat_mode in [RepeatMode.NONE, RepeatMode.ALL]:
- self._model.set_sort_func(None)
+ self._model.set_sorter(None)
def _validate_song(self, coresong):
# Song is being processed or has already been processed.
diff --git a/gnomemusic/utils.py b/gnomemusic/utils.py
index 7a87bb1ff..7cb98a433 100644
--- a/gnomemusic/utils.py
+++ b/gnomemusic/utils.py
@@ -23,12 +23,12 @@
# delete this exception statement from your version.
from enum import Enum, IntEnum
+from typing import List
import re
import unicodedata
from gettext import gettext as _
-from gi.repository import Gio, GLib
-from gi._gi import pygobject_new_full
+from gi.repository import Gio, GLib, Gtk
from gnomemusic.musiclogger import MusicLogger
@@ -55,7 +55,7 @@ class CoreObjectType(Enum):
class DefaultIconType(Enum):
ALBUM = "folder-music-symbolic"
- ARTIST = "avatar-default-symbolic"
+ ARTIST = "music-artist-symbolic"
class SongStateIcon(Enum):
@@ -185,7 +185,7 @@ def normalize_caseless(text):
return unicodedata.normalize("NFKD", text.casefold())
-def natural_sort_names(name_a, name_b):
+def natural_sort_names(name_a: str, name_b: str) -> int:
"""Natural order comparison of two strings.
A natural order is an alphabetical order which takes into account
@@ -196,22 +196,18 @@ def natural_sort_names(name_a, name_b):
:param str name_a: first string to compare
:param str name_b: second string to compare
- :returns: False if name_a should be before name_b. True otherwise.
- :rtype: boolean
+ :returns: Gtk Ordering
+ :rtype: int
"""
- def _extract_numbers(text):
+ def _extract_numbers(text: str) -> List[str]:
return [int(tmp) if tmp.isdigit() else tmp
for tmp in re.split(r"(\d+)", normalize_caseless(text))]
- return _extract_numbers(name_b) < _extract_numbers(name_a)
-
-
-def wrap_list_store_sort_func(func):
- """PyGI wrapper for SortListModel set_sort_func.
- """
- def wrap(a, b, *user_data):
- a = pygobject_new_full(a, False)
- b = pygobject_new_full(b, False)
- return func(a, b, *user_data)
-
- return wrap
+ extract_a = _extract_numbers(name_a)
+ extract_b = _extract_numbers(name_b)
+ if extract_a < extract_b:
+ return Gtk.Ordering.SMALLER
+ elif extract_a > extract_b:
+ return Gtk.Ordering.LARGER
+ else:
+ return Gtk.Ordering.EQUAL
diff --git a/gnomemusic/views/albumsview.py b/gnomemusic/views/albumsview.py
index 507d75bc6..66a5a0baa 100644
--- a/gnomemusic/views/albumsview.py
+++ b/gnomemusic/views/albumsview.py
@@ -23,16 +23,16 @@
# delete this exception statement from your version.
from __future__ import annotations
-import math
import typing
from gettext import gettext as _
-from gi.repository import Gdk, GLib, GObject, Gtk
+from gi.repository import GObject, Gtk
+from typing import Dict, List
from gnomemusic.widgets.headerbar import HeaderBar
-from gnomemusic.widgets.albumcover import AlbumCover
from gnomemusic.widgets.albumwidget import AlbumWidget
if typing.TYPE_CHECKING:
+ from gnomemusic.application import Application
from gnomemusic.corealbum import CoreAlbum
@@ -55,10 +55,9 @@ class AlbumsView(Gtk.Stack):
_album_scrolled_window = Gtk.Template.Child()
_scrolled_window = Gtk.Template.Child()
- _flowbox = Gtk.Template.Child()
- _flowbox_long_press = Gtk.Template.Child()
+ _gridview = Gtk.Template.Child()
- def __init__(self, application):
+ def __init__(self, application: Application) -> None:
"""Initialize AlbumsView
:param application: The Application object
@@ -70,17 +69,31 @@ class AlbumsView(Gtk.Stack):
self._application = application
self._window = application.props.window
self._headerbar = self._window._headerbar
- self._adjustment_timeout_id = 0
- self._viewport = self._scrolled_window.get_child()
- self._widget_counter = 1
- self._ctrl_hold = False
- model = self._application.props.coremodel.props.albums_sort
- self._flowbox.bind_model(model, self._create_album_cover)
- self._flowbox.set_hadjustment(self._scrolled_window.get_hadjustment())
- self._flowbox.set_vadjustment(self._scrolled_window.get_vadjustment())
- self._flowbox.connect("child-activated", self._on_child_activated)
+ self._list_item_bindings: Dict[
+ Gtk.ListItem, List[GObject.Binding]] = {}
+ self._list_item_star_controllers: Dict[
+ Gtk.ListItem, List[GObject.Binding]] = {}
+ list_item_factory = Gtk.SignalListItemFactory()
+ list_item_factory.connect("setup", self._setup_list_item)
+ list_item_factory.connect("bind", self._bind_list_item)
+
+ self._gridview.props.factory = list_item_factory
+
+ self._selection_model = Gtk.MultiSelection.new(
+ self._application.props.coremodel.props.albums_sort)
+ self._gridview.props.model = self._selection_model
+
+ self._gridview.connect("activate", self._on_album_activated)
+
+ self.bind_property(
+ "selection-mode", self._gridview, "single-click-activate",
+ GObject.BindingFlags.SYNC_CREATE
+ | GObject.BindingFlags.INVERT_BOOLEAN)
+ self.bind_property(
+ "selection-mode", self._gridview, "enable-rubberband",
+ GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
"selection-mode", self._window, "selection-mode",
GObject.BindingFlags.DEFAULT)
@@ -93,67 +106,12 @@ class AlbumsView(Gtk.Stack):
"selection-mode", self, "selection-mode",
GObject.BindingFlags.BIDIRECTIONAL)
- self._album_scrolled_window.add(self._album_widget)
+ viewport = self._album_scrolled_window.get_first_child()
+ viewport.set_child(self._album_widget)
self.connect(
"notify::search-mode-active", self._on_search_mode_changed)
- self._scrolled_window.props.vadjustment.connect(
- "value-changed", self._on_vadjustment_changed)
- self._scrolled_window.props.vadjustment.connect(
- "changed", self._on_vadjustment_changed)
-
- def _on_vadjustment_changed(self, adjustment):
- if self._adjustment_timeout_id != 0:
- GLib.source_remove(self._adjustment_timeout_id)
- self._adjustment_timeout_id = 0
-
- self._adjustment_timeout_id = GLib.timeout_add(
- 200, self._retrieve_covers, adjustment.props.value,
- priority=GLib.PRIORITY_DEFAULT - 10)
-
- def _retrieve_covers(self, old_adjustment):
- adjustment = self._scrolled_window.props.vadjustment.props.value
-
- if old_adjustment != adjustment:
- return GLib.SOURCE_CONTINUE
-
- first_cover = self._flowbox.get_child_at_index(0)
- if first_cover is None:
- return GLib.SOURCE_REMOVE
-
- cover_size, _ = first_cover.get_allocated_size()
- if cover_size.width == 0 or cover_size.height == 0:
- return GLib.SOURCE_REMOVE
-
- viewport_size, _ = self._viewport.get_allocated_size()
-
- h_space = self._flowbox.get_column_spacing()
- v_space = self._flowbox.get_row_spacing()
- nr_cols = (
- (viewport_size.width + h_space) // (cover_size.width + h_space))
-
- top_left_cover = self._flowbox.get_child_at_index(
- nr_cols * (adjustment // (cover_size.height + v_space)))
-
- covers_col = math.ceil(viewport_size.width / cover_size.width)
- covers_row = math.ceil(viewport_size.height / cover_size.height)
-
- children = self._flowbox.get_children()
- retrieve_list = []
- for i, albumcover in enumerate(children):
- if top_left_cover == albumcover:
- retrieve_covers = covers_row * covers_col
- retrieve_list = children[i:i + retrieve_covers]
- break
-
- for albumcover in retrieve_list:
- albumcover.retrieve()
-
- self._adjustment_timeout_id = 0
-
- return GLib.SOURCE_REMOVE
-
def _on_selection_mode_changed(self, widget, data=None):
selection_mode = self._window.props.selection_mode
if (selection_mode == self.props.selection_mode
@@ -163,7 +121,6 @@ class AlbumsView(Gtk.Stack):
self.props.selection_mode = selection_mode
if not self.props.selection_mode:
self.deselect_all()
- self._flowbox.props.selection_mode = Gtk.SelectionMode.NONE
def _on_search_mode_changed(self, klass, param):
if (not self.props.search_mode_active
@@ -171,32 +128,17 @@ class AlbumsView(Gtk.Stack):
and self.get_visible_child_name() == "widget"):
self._set_album_headerbar(self._album_widget.props.corealbum)
- def _create_album_cover(self, corealbum: CoreAlbum) -> AlbumCover:
- album_cover = AlbumCover(corealbum)
-
- self.bind_property(
- "selection-mode", album_cover, "selection-mode",
- GObject.BindingFlags.SYNC_CREATE
- | GObject.BindingFlags.BIDIRECTIONAL)
-
- # NOTE: Adding SYNC_CREATE here will trigger all the nested
- # models to be created. This will slow down initial start,
- # but will improve initial 'select all' speed.
- album_cover.bind_property(
- "selected", corealbum, "selected",
- GObject.BindingFlags.BIDIRECTIONAL)
-
- GLib.timeout_add(
- self._widget_counter * 250, album_cover.retrieve,
- priority=GLib.PRIORITY_LOW)
- self._widget_counter = self._widget_counter + 1
-
- return album_cover
-
def _back_button_clicked(self, widget, data=None):
self._headerbar.state = HeaderBar.State.MAIN
self.props.visible_child = self._scrolled_window
+ def _on_album_activated(self, widget, position):
+ corealbum = widget.props.model[position]
+
+ self._album_widget.props.corealbum = corealbum
+ self._set_album_headerbar(corealbum)
+ self.props.visible_child = self._album_scrolled_window
+
def _on_child_activated(self, widget, child, user_data=None):
corealbum = child.props.corealbum
if self.props.selection_mode:
@@ -208,48 +150,10 @@ class AlbumsView(Gtk.Stack):
self._set_album_headerbar(corealbum)
self.set_visible_child_name("widget")
- def _set_album_headerbar(self, corealbum):
+ def _set_album_headerbar(self, corealbum: CoreAlbum) -> None:
self._headerbar.props.state = HeaderBar.State.CHILD
- self._headerbar.props.title = corealbum.props.title
- self._headerbar.props.subtitle = corealbum.props.artist
-
- @Gtk.Template.Callback()
- def _on_flowbox_press_begin(self, gesture, sequence):
- event = gesture.get_last_event(sequence)
- ok, state = event.get_state()
- if ((ok is True
- and state == Gdk.ModifierType.CONTROL_MASK)
- or self.props.selection_mode is True):
- self._flowbox.props.selection_mode = Gtk.SelectionMode.MULTIPLE
- if state == Gdk.ModifierType.CONTROL_MASK:
- self._ctrl_hold = True
-
- @Gtk.Template.Callback()
- def _on_flowbox_press_cancel(self, gesture, sequence):
- self._flowbox.props.selection_mode = Gtk.SelectionMode.NONE
-
- @Gtk.Template.Callback()
- def _on_selected_children_changed(self, flowbox):
- if self._flowbox.props.selection_mode == Gtk.SelectionMode.NONE:
- return
-
- if self.props.selection_mode is False:
- self.props.selection_mode = True
-
- rubberband_selection = len(self._flowbox.get_selected_children()) > 1
- with self._application.props.coreselection.freeze_notify():
- if (rubberband_selection
- and not self._ctrl_hold):
- self.deselect_all()
- for child in self._flowbox.get_selected_children():
- if (self._ctrl_hold is True
- or not rubberband_selection):
- child.props.selected = not child.props.selected
- else:
- child.props.selected = True
-
- self._ctrl_hold = False
- self._flowbox.props.selection_mode = Gtk.SelectionMode.NONE
+ self._headerbar.set_label_title(
+ corealbum.props.title, corealbum.props.artist)
def _toggle_all_selection(self, selected):
"""Selects or deselects all items.
@@ -261,11 +165,90 @@ class AlbumsView(Gtk.Stack):
else:
self._album_widget.deselect_all()
else:
- for child in self._flowbox.get_children():
- child.props.selected = selected
+ if selected:
+ self._selection_model.select_all()
+ else:
+ self._selection_model.unselect_all()
def select_all(self):
self._toggle_all_selection(True)
def deselect_all(self):
self._toggle_all_selection(False)
+
+ def _setup_list_item(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ builder = Gtk.Builder.new_from_resource(
+ "/org/gnome/Music/ui/AlbumCoverListItem.ui")
+ list_item.props.child = builder.get_object("_album_cover")
+
+ self.bind_property(
+ "selection-mode", list_item, "selectable",
+ GObject.BindingFlags.SYNC_CREATE)
+ self.bind_property(
+ "selection-mode", list_item, "activatable",
+ GObject.BindingFlags.SYNC_CREATE
+ | GObject.BindingFlags.INVERT_BOOLEAN)
+
+ def _bind_list_item(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ album_cover = list_item.props.child
+ corealbum = list_item.props.item
+
+ art_stack = album_cover.get_first_child().get_first_child()
+ check = art_stack.get_next_sibling()
+ album_label = album_cover.get_first_child().get_next_sibling()
+ artist_label = album_label.get_next_sibling()
+
+ b1 = corealbum.bind_property(
+ "corealbum", art_stack, "coreobject",
+ GObject.BindingFlags.SYNC_CREATE)
+ b2 = corealbum.bind_property(
+ "title", album_label, "label", GObject.BindingFlags.SYNC_CREATE)
+ b3 = corealbum.bind_property(
+ "artist", artist_label, "label", GObject.BindingFlags.SYNC_CREATE)
+
+ b4 = list_item.bind_property(
+ "selected", corealbum, "selected",
+ GObject.BindingFlags.SYNC_CREATE)
+ b5 = list_item.bind_property(
+ "selected", check, "active", GObject.BindingFlags.SYNC_CREATE)
+ b6 = self.bind_property(
+ "selection-mode", check, "visible",
+ GObject.BindingFlags.SYNC_CREATE)
+
+ def on_activated(widget, value):
+ if check.props.active:
+ self._selection_model.select_item(
+ list_item.get_position(), False)
+ else:
+ self._selection_model.unselect_item(
+ list_item.get_position())
+
+ # the listitem selected property is read-only.
+ # It cannot be bound from the check active property.
+ # It is necessary to update the selection model in order
+ # to update it.
+ check.connect("notify::active", on_activated)
+
+ self._list_item_bindings[list_item] = [b1, b2, b3, b4, b5, b6]
+
+ def _unbind_list_item(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ bindings = self._list_item_bindings.pop(list_item)
+ [binding.unbind() for binding in bindings]
+
+ album_cover = list_item.props.child
+
+ art_stack = album_cover.get_first_child().get_first_child()
+ check = art_stack.get_next_sibling()
+
+ signal_id, detail_id = GObject.signal_parse_name(
+ "notify::active", check, True)
+ handler_id = GObject.signal_handler_find(
+ check, GObject.SignalMatchType.ID, signal_id, detail_id, None, 0,
+ 0)
+ check.disconnect(handler_id)
diff --git a/gnomemusic/views/artistsview.py b/gnomemusic/views/artistsview.py
index 16a736b8a..b4c928099 100644
--- a/gnomemusic/views/artistsview.py
+++ b/gnomemusic/views/artistsview.py
@@ -22,11 +22,16 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
+from __future__ import annotations
+import typing
+
from gettext import gettext as _
-from gi.repository import GLib, GObject, Gtk
+from gi.repository import GObject, Gtk
from gnomemusic.widgets.artistalbumswidget import ArtistAlbumsWidget
from gnomemusic.widgets.artisttile import ArtistTile
+if typing.TYPE_CHECKING:
+ from gnomemusic.application import Application
@Gtk.Template(resource_path="/org/gnome/Music/ui/ArtistsView.ui")
@@ -45,11 +50,10 @@ class ArtistsView(Gtk.Paned):
title = GObject.Property(
type=str, default=_("Artists"), flags=GObject.ParamFlags.READABLE)
- _artist_container = Gtk.Template.Child()
_artist_view = Gtk.Template.Child()
_sidebar = Gtk.Template.Child()
- def __init__(self, application):
+ def __init__(self, application: Application) -> None:
"""Initialize
:param GtkApplication application: The application object
@@ -58,10 +62,6 @@ class ArtistsView(Gtk.Paned):
self.props.name = "artists"
- self._application = application
- self._loaded_artists = []
- self._widget_counter = 1
-
# This indicates if the current list has been empty and has
# had no user interaction since.
self._untouched_list = True
@@ -70,104 +70,58 @@ class ArtistsView(Gtk.Paned):
self._coremodel = application.props.coremodel
self._model = self._coremodel.props.artists_sort
- self._sidebar.bind_model(self._model, self._create_widget)
+ self._selection_model = Gtk.SingleSelection.new(self._model)
+ self._sidebar.props.model = self._selection_model
+ artist_item_factory = Gtk.SignalListItemFactory()
+ artist_item_factory.connect("setup", self._on_list_view_setup)
+ artist_item_factory.connect("bind", self._on_list_view_bind)
+ self._sidebar.props.factory = artist_item_factory
- self._model.connect_after(
- "items-changed", self._on_model_items_changed)
- self._on_model_items_changed(self._model, 0, 0, 0)
-
- self._selection_mode = False
+ self._artist_album = ArtistAlbumsWidget(application)
+ self._artist_view.props.child = self._artist_album
+ self.bind_property(
+ "selection-mode", self._artist_album, "selection-mode",
+ GObject.BindingFlags.SYNC_CREATE
+ | GObject.BindingFlags.BIDIRECTIONAL)
self._window.bind_property(
"selection-mode", self, "selection-mode",
GObject.BindingFlags.BIDIRECTIONAL)
- def _create_widget(self, coreartist):
- row = ArtistTile(coreartist)
- row.props.text = coreartist.props.artist
-
- GLib.timeout_add(
- self._widget_counter * 300, row.retrieve,
- priority=GLib.PRIORITY_LOW)
- self._widget_counter = self._widget_counter + 1
+ self._selection_model.connect_after(
+ "items-changed", self._on_model_items_changed)
+ self._on_model_items_changed(self._selection_model, 0, 0, 0)
- return row
+ self._selection_mode = False
- def _on_model_items_changed(self, model, position, removed, added):
+ def _on_list_view_setup(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ list_item.props.child = ArtistTile()
+
+ def _on_list_view_bind(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ coreartist = list_item.props.item
+ artist_tile = list_item.props.child
+ artist_tile.props.coreartist = coreartist
+
+ def _on_model_items_changed(
+ self, model: Gtk.SingleSelection, position: int, removed: int,
+ added: int) -> None:
if model.get_n_items() == 0:
self._untouched_list = True
- return
+ # FIXME: Add an empty state.
elif self._untouched_list is True:
- first_row = self._sidebar.get_row_at_index(0)
- if first_row is None:
- return
-
- self._sidebar.select_row(first_row)
- self._on_artist_activated(self._sidebar, first_row, True)
- return
-
- if removed == 0:
- return
-
- removed_artist = None
- artists = [coreartist.props.artist for coreartist in model]
- for artist in self._loaded_artists:
- if artist not in artists:
- removed_artist = artist
- break
-
- if removed_artist is None:
- return
-
- self._loaded_artists.remove(removed_artist)
- if self._artist_view.get_visible_child_name() == removed_artist:
- row_next = (self._sidebar.get_row_at_index(position)
- or self._sidebar.get_row_at_index(position - 1))
- if row_next:
- self._sidebar.select_row(row_next)
- self._on_artist_activated(self._sidebar, row_next, True)
-
- removed_artist_page = self._artist_view.get_child_by_name(
- removed_artist)
- self._artist_view.remove(removed_artist_page)
+ self._untouched_list = False
+ self._sidebar.emit("activate", 0)
@Gtk.Template.Callback()
- def _on_artist_activated(self, sidebar, row, data=None, untouched=False):
+ def _on_artist_activated(
+ self, sidebar: Gtk.ListView, position: int) -> None:
"""Initializes new artist album widgets"""
- # On application start the first row of ArtistView is activated
- # to show an intial artist. When this happens while any of the
- # views are in selection mode, this artist will be incorrectly
- # selected.
- # When selecting items check that the current visible view is
- # ArtistsView, to circumvent this issue.
- if (self.props.selection_mode
- and self._window.props.active_view is self):
- return
-
- if untouched is False:
- self._untouched_list = False
-
- # Prepare a new artist_albums_widget here
- coreartist = row.props.coreartist
- if coreartist.props.artist in self._loaded_artists:
- scroll_vadjustment = self._artist_container.props.vadjustment
- scroll_vadjustment.props.value = 0.
- self._artist_view.set_visible_child_name(coreartist.props.artist)
- return
-
- artist_albums = ArtistAlbumsWidget(coreartist, self._application)
-
- self.bind_property(
- "selection-mode", artist_albums, "selection-mode",
- GObject.BindingFlags.SYNC_CREATE
- | GObject.BindingFlags.BIDIRECTIONAL)
-
- self._artist_view.add_named(artist_albums, coreartist.props.artist)
- scroll_vadjustment = self._artist_container.props.vadjustment
- scroll_vadjustment.props.value = 0.
- self._artist_view.set_visible_child(artist_albums)
-
- self._loaded_artists.append(coreartist.props.artist)
+ coreartist = self._selection_model.get_item(position)
+ self._artist_album.props.coreartist = coreartist
@GObject.Property(type=bool, default=False)
def selection_mode(self):
@@ -195,12 +149,12 @@ class ArtistsView(Gtk.Paned):
def select_all(self) -> None:
"""Select all items"""
- artist_tile = self._sidebar.get_selected_row()
- if artist_tile:
- artist_tile.props.coreartist.props.selected = True
+ coreartist = self._selection_model.get_selected_item()
+ if coreartist:
+ coreartist.props.selected = True
def deselect_all(self) -> None:
"""Deselect all items"""
- artist_tile = self._sidebar.get_selected_row()
- if artist_tile:
- artist_tile.props.coreartist.props.selected = False
+ coreartist = self._selection_model.get_selected_item()
+ if coreartist:
+ coreartist.props.selected = False
diff --git a/gnomemusic/views/emptyview.py b/gnomemusic/views/emptyview.py
index 737dbc67d..e1dc068a2 100644
--- a/gnomemusic/views/emptyview.py
+++ b/gnomemusic/views/emptyview.py
@@ -76,12 +76,12 @@ class EmptyView(Gtk.Stack):
folder_text = _("The contents of your {} will appear here.")
self._content_text = folder_text.format(href_text)
- # Hack to get to HdyClamp, so it can be hidden for the
+ # Hack to get to AdwClamp, so it can be hidden for the
# initial state.
- child_of_child = self._status_page.get_child().get_child()
- self._hdy_clamp = child_of_child.get_child().get_children()[0]
+ child_of_child = self._status_page.get_first_child().get_first_child()
+ self._adw_clamp = child_of_child.get_first_child().get_first_child()
- self._status_page.add(self._initial_state)
+ self._status_page.set_child(self._initial_state)
self._state = EmptyView.State.EMPTY
@@ -102,7 +102,7 @@ class EmptyView(Gtk.Stack):
"""
self._state = value
- self._hdy_clamp.props.visible = True
+ self._adw_clamp.props.visible = True
self._initial_state.props.visible = False
if self._state == EmptyView.State.EMPTY:
@@ -115,7 +115,7 @@ class EmptyView(Gtk.Stack):
self._set_tracker_outdated_state()
def _set_empty_state(self):
- self._hdy_clamp.props.visible = False
+ self._adw_clamp.props.visible = False
self._initial_state.props.visible = True
self._description_label.props.label = self._content_text
diff --git a/gnomemusic/views/playlistsview.py b/gnomemusic/views/playlistsview.py
index 4c1a2f782..58c107e7c 100644
--- a/gnomemusic/views/playlistsview.py
+++ b/gnomemusic/views/playlistsview.py
@@ -63,7 +63,7 @@ class PlaylistsView(Gtk.Paned):
self._untouched_list = True
self._playlist_widget = PlaylistsWidget(application, self)
- self.add(self._playlist_widget)
+ self.props.end_child = self._playlist_widget
self._sidebar.set_header_func(self._sidebar_header_func)
self._sidebar.bind_model(self._model, self._add_playlist_to_sidebar)
diff --git a/gnomemusic/views/searchview.py b/gnomemusic/views/searchview.py
index e9d2cca23..617e39405 100644
--- a/gnomemusic/views/searchview.py
+++ b/gnomemusic/views/searchview.py
@@ -28,9 +28,10 @@ from gettext import gettext as _
from typing import Optional
import typing
-from gi.repository import Gdk, GObject, Gtk
+from gi.repository import GObject, Gtk
from gnomemusic.search import Search
+from gnomemusic.utils import ArtSize
from gnomemusic.widgets.albumcover import AlbumCover
from gnomemusic.widgets.albumwidget import AlbumWidget
from gnomemusic.widgets.headerbar import HeaderBar
@@ -94,41 +95,32 @@ class SearchView(Gtk.Stack):
self._application = application
self._coremodel = application.props.coremodel
self._model = self._coremodel.props.songs_search
- self._album_model = self._coremodel.props.albums_search
- self._album_filter = self._coremodel.props.albums_search_filter
- self._album_filter.set_filter_func(
- self._core_filter, self._album_model, 12)
+ self._player = self._application.props.player
+ self._window = application.props.window
+ self._headerbar = self._window._headerbar
+ self._album_model = self._coremodel.props.albums_search
self._artist_model = self._coremodel.props.artists_search
- self._artist_filter = self._coremodel.props.artists_search_filter
- self._artist_filter.set_filter_func(
- self._core_filter, self._artist_model, 6)
+
+ self._album_slice = Gtk.SliceListModel.new(self._album_model, 0, 8)
+ self._artist_slice = Gtk.SliceListModel.new(self._artist_model, 0, 6)
self._model.connect_after(
"items-changed", self._on_model_items_changed)
self._songs_listbox.bind_model(self._model, self._create_song_widget)
self._on_model_items_changed(self._model, 0, 0, 0)
- self._album_filter.connect_after(
+ self._album_slice.connect_after(
"items-changed", self._on_album_model_items_changed)
self._album_flowbox.bind_model(
- self._album_filter, self._create_album_cover)
- self._album_flowbox.connect(
- "size-allocate", self._on_album_flowbox_size_allocate)
- self._on_album_model_items_changed(self._album_filter, 0, 0, 0)
+ self._album_slice, self._create_album_cover)
+ self._application.props.window.connect(
+ "notify::default-width", self._on_window_width_change)
- self._artist_filter.connect_after(
+ self._artist_slice.connect_after(
"items-changed", self._on_artist_model_items_changed)
self._artist_flowbox.bind_model(
- self._artist_filter, self._create_artist_widget)
- self._artist_flowbox.connect(
- "size-allocate", self._on_artist_flowbox_size_allocate)
- self._on_artist_model_items_changed(self._artist_filter, 0, 0, 0)
-
- self._player = self._application.props.player
-
- self._window = application.props.window
- self._headerbar = self._window._headerbar
+ self._artist_slice, self._create_artist_widget)
self.connect("notify::selection-mode", self._on_selection_mode_changed)
@@ -140,7 +132,8 @@ class SearchView(Gtk.Stack):
self._album_widget.bind_property(
"selection-mode", self, "selection-mode",
GObject.BindingFlags.BIDIRECTIONAL)
- self._scrolled_album_widget.add(self._album_widget)
+ viewport = self._scrolled_album_widget.get_first_child()
+ viewport.set_child(self._album_widget)
self._scrolled_artist_window: Optional[Gtk.ScrolledWindow] = None
@@ -206,11 +199,6 @@ class SearchView(Gtk.Stack):
nr_albums = self._album_model.get_n_items()
self._view_all_albums.props.visible = (nr_albums > model.get_n_items())
- def set_child_visible(child):
- child.props.visible = True
-
- self._album_flowbox.foreach(set_child_visible)
-
def _on_artist_model_items_changed(self, model, position, removed, added):
items_found = model.get_n_items() > 0
self._artist_header.props.visible = items_found
@@ -221,11 +209,6 @@ class SearchView(Gtk.Stack):
self._view_all_artists.props.visible = (
nr_artists > model.get_n_items())
- def set_child_visible(child):
- child.props.visible = True
-
- self._artist_flowbox.foreach(set_child_visible)
-
def _on_model_items_changed(self, model, position, removed, added):
items_found = model.get_n_items() > 0
self._songs_header.props.visible = items_found
@@ -247,106 +230,26 @@ class SearchView(Gtk.Stack):
@Gtk.Template.Callback()
def _song_activated(
self, list_box: Gtk.ListBox, song_widget: SongWidget) -> bool:
- if song_widget.props.select_click:
- song_widget.props.select_click = False
- return True
-
- event = Gtk.get_current_event()
- (_, state) = event.get_state()
- mod_mask = Gtk.accelerator_get_default_mod_mask()
- if ((state & mod_mask) == Gdk.ModifierType.CONTROL_MASK
- and not self.props.selection_mode):
- self.props.selection_mode = True
- song_widget.props.select_click = True
- song_widget.props.coresong.props.selected = True
- return True
-
+ coresong = song_widget.props.coresong
if self.props.selection_mode:
- song_widget.props.select_click = True
- selection_state = song_widget.props.selected
+ selection_state = coresong.props.selected
song_widget.props.selected = not selection_state
- song_widget.props.coresong.props.selected = not selection_state
+ coresong.props.selected = not selection_state
return True
- (_, button) = event.get_button()
- if (button == Gdk.BUTTON_PRIMARY
- and not self.props.selection_mode):
- coresong = song_widget.props.coresong
- self._coremodel.props.active_core_object = coresong
- self._player.play(coresong)
+ self._coremodel.props.active_core_object = coresong
+ self._player.play(coresong)
return True
- def _on_album_flowbox_size_allocate(self, widget, allocation, data=None):
- nb_children = self._album_filter.get_n_items()
- if nb_children == 0:
- return
-
- first_child = self._album_flowbox.get_child_at_index(0)
- child_height = first_child.get_allocation().height
- if allocation.height > 2.5 * child_height:
- for i in range(nb_children - 1, -1, -1):
- child = self._album_flowbox.get_child_at_index(i)
- if child.props.visible is True:
- child.props.visible = False
- return
-
- children_hidden = False
- for idx in range(nb_children):
- child = self._album_flowbox.get_child_at_index(idx)
- if not child.props.visible:
- children_hidden = True
- break
- if children_hidden is False:
- return
-
- last_visible_child = self._album_flowbox.get_child_at_index(idx - 1)
- first_row_last = self._album_flowbox.get_child_at_index((idx - 1) // 2)
- second_row_pos = last_visible_child.get_allocation().x
- first_row_pos = first_row_last.get_allocation().x
- child_width = last_visible_child.get_allocation().width
- nb_children_to_add = (first_row_pos - second_row_pos) // child_width
- nb_children_to_add = min(nb_children_to_add + idx, nb_children)
- for i in range(idx, nb_children_to_add):
- child = self._album_flowbox.get_child_at_index(i)
- child.props.visible = True
-
- def _on_artist_flowbox_size_allocate(self, widget, allocation, data=None):
- nb_children = self._artist_filter.get_n_items()
- if nb_children == 0:
- return
-
- first_child = self._artist_flowbox.get_child_at_index(0)
- # FIXME: It looks like it is possible that the widget is not
- # yet created, resulting in a crash with first_child being
- # None.
- # Look for a cleaner solution.
- if first_child is None:
- return
-
- child_height = first_child.get_allocation().height
- if allocation.height > 1.5 * child_height:
- for i in range(nb_children - 1, -1, -1):
- child = self._artist_flowbox.get_child_at_index(i)
- if child.props.visible is True:
- child.props.visible = False
- return
-
- children_hidden = False
- for idx in range(nb_children):
- child = self._artist_flowbox.get_child_at_index(idx)
- if not child.props.visible:
- children_hidden = True
- break
- if children_hidden is False:
- return
+ def _on_window_width_change(self, widget, value):
+ allocation = self._album_flowbox.get_allocation()
+ # FIXME: Just a bit of guesswork here.
+ padding = 32
+ items_per_row = allocation.width // (ArtSize.MEDIUM.width + padding)
- last_child = self._artist_flowbox.get_child_at_index(idx - 1)
- last_child_allocation = last_child.get_allocation()
- child_width = last_child_allocation.width
- if (last_child_allocation.x + 2 * child_width) < allocation.width:
- child = self._artist_flowbox.get_child_at_index(idx)
- child.props.visible = True
+ self._album_slice.props.size = 2 * items_per_row
+ self._artist_slice.props.size = items_per_row
@Gtk.Template.Callback()
def _on_album_activated(self, widget, child, user_data=None):
@@ -360,8 +263,8 @@ class SearchView(Gtk.Stack):
self.props.state = SearchView.State.ALBUM
self._headerbar.props.state = HeaderBar.State.SEARCH
- self._headerbar.props.title = corealbum.props.title
- self._headerbar.props.subtitle = corealbum.props.artist
+ self._headerbar.set_label_title(
+ corealbum.props.title, corealbum.props.artist)
self.set_visible_child(self._scrolled_album_widget)
self.props.search_mode_active = False
@@ -372,15 +275,15 @@ class SearchView(Gtk.Stack):
if self.props.selection_mode:
return
- artist_albums_widget = ArtistAlbumsWidget(
- coreartist, self._application)
+ artist_albums_widget = ArtistAlbumsWidget(self._application)
+ artist_albums_widget.props.coreartist = coreartist
# FIXME: Recreating a view here. Alternate solution is used
# in AlbumsView: one view created and an update function.
# Settle on one design.
self._scrolled_artist_window = Gtk.ScrolledWindow()
- self._scrolled_artist_window.add(artist_albums_widget)
+ self._scrolled_artist_window.props.child = artist_albums_widget
self._scrolled_artist_window.props.visible = True
- self.add(self._scrolled_artist_window)
+ self.add_child(self._scrolled_artist_window)
artist_albums_widget.show()
self.bind_property(
@@ -389,18 +292,16 @@ class SearchView(Gtk.Stack):
self.props.state = SearchView.State.ARTIST
self._headerbar.props.state = HeaderBar.State.SEARCH
- self._headerbar.props.title = coreartist.props.artist
- self._headerbar.props.subtitle = None
+ self._headerbar.set_label_title(coreartist.props.artist, "")
self.set_visible_child(self._scrolled_artist_window)
self.props.search_mode_active = False
@Gtk.Template.Callback()
- def _on_all_artists_clicked(self, widget, event, user_data=None):
+ def _on_all_artists_clicked(self, widget, user_data=None):
self.props.state = SearchView.State.ALL_ARTISTS
self._headerbar.props.state = HeaderBar.State.SEARCH
- self._headerbar.props.title = _("Artists Results")
- self._headerbar.props.subtitle = None
+ self._headerbar.set_label_title(_("Artists Results"), "")
self._artist_all_flowbox.props.visible = True
self._album_all_flowbox.props.visible = False
@@ -411,11 +312,10 @@ class SearchView(Gtk.Stack):
self.props.search_mode_active = False
@Gtk.Template.Callback()
- def _on_all_albums_clicked(self, widget, event, user_data=None):
+ def _on_all_albums_clicked(self, widget, user_data=None):
self.props.state = SearchView.State.ALL_ALBUMS
self._headerbar.props.state = HeaderBar.State.SEARCH
- self._headerbar.props.title = _("Albums Results")
- self._headerbar.props.subtitle = None
+ self._headerbar.set_label_title(_("Albums Results"), "")
self._artist_all_flowbox.props.visible = False
self._album_all_flowbox.props.visible = True
@@ -466,8 +366,6 @@ class SearchView(Gtk.Stack):
return
elif self.get_visible_child() == self._scrolled_artist_window:
self.remove(self._scrolled_artist_window)
- self._scrolled_artist_window.destroy()
- self._scrolled_artist_window = None
self.set_visible_child(self._search_results)
self.props.search_mode_active = True
diff --git a/gnomemusic/views/songsview.py b/gnomemusic/views/songsview.py
index ee7752ee4..c6521660c 100644
--- a/gnomemusic/views/songsview.py
+++ b/gnomemusic/views/songsview.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016 The GNOME Music Developers
+# Copyright 2022 The GNOME Music Developers
#
# GNOME Music is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,16 +22,21 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
+from __future__ import annotations
+import typing
+
from gettext import gettext as _
-from gi.repository import Gdk, GObject, Gtk, Pango
+from gi.repository import GObject, Gtk
+from typing import Dict, List
-from gnomemusic.coresong import CoreSong
-from gnomemusic.utils import SongStateIcon
-from gnomemusic.widgets.starhandlerwidget import StarHandlerWidget
+from gnomemusic.widgets.songwidgetmenu import SongWidgetMenu
+import gnomemusic.utils as utils
+if typing.TYPE_CHECKING:
+ from gnomemusic.application import Application
@Gtk.Template(resource_path="/org/gnome/Music/ui/SongsView.ui")
-class SongsView(Gtk.ScrolledWindow):
+class SongsView(Gtk.Box):
"""Main view of all songs sorted artistwise
Consists all songs along with songname, star, length, artist
@@ -46,14 +51,9 @@ class SongsView(Gtk.ScrolledWindow):
title = GObject.Property(
type=str, default=_("Songs"), flags=GObject.ParamFlags.READABLE)
- _duration_renderer = Gtk.Template.Child()
- _now_playing_column = Gtk.Template.Child()
- _now_playing_cell = Gtk.Template.Child()
- _songs_ctrlr = Gtk.Template.Child()
- _songs_view = Gtk.Template.Child()
- _star_column = Gtk.Template.Child()
+ _listview = Gtk.Template.Child()
- def __init__(self, application):
+ def __init__(self, application: Application) -> None:
"""Initialize
:param GtkApplication window: The application object
@@ -62,50 +62,168 @@ class SongsView(Gtk.ScrolledWindow):
self.props.name = "songs"
- self._window = application.props.window
+ self._application = application
self._coremodel = application.props.coremodel
+ self._coreselection = application.props.coreselection
+ self._player = application.props.player
+ self._window = application.props.window
- self._iter_to_clean = None
- self._set_list_renderers()
+ self._list_item_bindings: Dict[
+ Gtk.ListItem, List[GObject.Binding]] = {}
+ self._list_item_star_controllers: Dict[
+ Gtk.ListItem, List[GObject.Binding]] = {}
- self._playlist_model = self._coremodel.props.playlist_sort
- self._songs_view.props.model = self._coremodel.props.songs_gtkliststore
- self._model = self._songs_view.props.model
+ self._selection_model = Gtk.MultiSelection.new(
+ self._coremodel.props.songs)
- self._player = application.props.player
- self._player.connect('song-changed', self._update_model)
+ list_item_factory = Gtk.SignalListItemFactory()
+ list_item_factory.connect("setup", self._setup_list_item)
+ list_item_factory.connect("bind", self._bind_list_item)
+ list_item_factory.connect("unbind", self._unbind_list_item)
+
+ self._listview.props.factory = list_item_factory
+ self._listview.props.model = self._selection_model
+
+ self._listview.connect("activate", self._on_song_activated)
self._selection_mode = False
+ self.bind_property(
+ "selection-mode", self._listview, "single-click-activate",
+ GObject.BindingFlags.SYNC_CREATE
+ | GObject.BindingFlags.INVERT_BOOLEAN)
+ self.bind_property(
+ "selection-mode", self._listview, "enable-rubberband",
+ GObject.BindingFlags.SYNC_CREATE)
self._window.bind_property(
"selection-mode", self, "selection-mode",
GObject.BindingFlags.BIDIRECTIONAL)
- def _set_list_renderers(self):
- self._now_playing_column.set_cell_data_func(
- self._now_playing_cell, self._on_list_widget_icon_render, None)
+ def _on_song_activated(self, widget, position):
+ coresong = widget.props.model[position]
- self._star_handler = StarHandlerWidget(self, 6)
- self._star_handler.add_star_renderers(self._star_column)
-
- attrs = Pango.AttrList()
- attrs.insert(Pango.AttrFontFeatures.new("tnum=1"))
- self._duration_renderer.props.attributes = attrs
-
- def _on_list_widget_icon_render(self, col, cell, model, itr, data):
- current_song = self._player.props.current_song
- if current_song is None:
- return
+ self._coremodel.props.active_core_object = coresong
+ self._player.play(coresong)
- coresong = model[itr][7]
- if coresong.props.validation == CoreSong.Validation.FAILED:
- cell.props.icon_name = SongStateIcon.ERROR.value
- cell.props.visible = True
- elif coresong.props.grlid == current_song.props.grlid:
- cell.props.icon_name = SongStateIcon.PLAYING.value
- cell.props.visible = True
- else:
- cell.props.visible = False
+ def _setup_list_item(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ builder = Gtk.Builder.new_from_resource(
+ "/org/gnome/Music/ui/SongListItem.ui")
+ list_item.props.child = builder.get_object("_song_box")
+
+ self.bind_property(
+ "selection-mode", list_item, "selectable",
+ GObject.BindingFlags.SYNC_CREATE)
+ self.bind_property(
+ "selection-mode", list_item, "activatable",
+ GObject.BindingFlags.SYNC_CREATE
+ | GObject.BindingFlags.INVERT_BOOLEAN)
+
+ def _bind_list_item(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ list_row = list_item.props.child
+ coresong = list_item.props.item
+
+ check = list_row.get_first_child()
+ info_box = check.get_next_sibling()
+ duration_label = info_box.get_next_sibling()
+ star_box = duration_label.get_next_sibling()
+ star_image = star_box.get_first_child()
+ title_label = info_box.get_first_child()
+ album_label = title_label.get_next_sibling()
+ artist_label = album_label.get_next_sibling()
+ menu_button = star_box.get_next_sibling()
+
+ def _on_star_toggle(
+ controller: Gtk.GestureClick, n_press: int, x: float,
+ y: float) -> None:
+ controller.set_state(Gtk.EventSequenceState.CLAIMED)
+ coresong.props.favorite = not coresong.props.favorite
+ star_image.props.favorite = coresong.props.favorite
+
+ star_click = Gtk.GestureClick()
+ star_click.props.button = 1
+ star_click.connect("released", _on_star_toggle)
+ star_image.add_controller(star_click)
+
+ def _on_star_enter(
+ controller: Gtk.EventControllerMotion, x: float,
+ y: float) -> None:
+ star_image.props.hover = True
+
+ def _on_star_leave(controller: Gtk.EventControllerMotion) -> None:
+ star_image.props.hover = False
+
+ star_hover = Gtk.EventControllerMotion()
+ star_hover.connect("enter", _on_star_enter)
+ star_hover.connect("leave", _on_star_leave)
+ star_image.add_controller(star_hover)
+
+ menu_button.props.popover = SongWidgetMenu(
+ self._application, list_row, coresong)
+
+ b1 = coresong.bind_property(
+ "title", title_label, "label", GObject.BindingFlags.SYNC_CREATE)
+ b2 = coresong.bind_property(
+ "album", album_label, "label", GObject.BindingFlags.SYNC_CREATE)
+ b3 = coresong.bind_property(
+ "artist", artist_label, "label", GObject.BindingFlags.SYNC_CREATE)
+
+ b4 = coresong.bind_property(
+ "favorite", star_image, "favorite")
+
+ duration_label.props.label = utils.seconds_to_string(
+ coresong.props.duration)
+
+ b5 = list_item.bind_property(
+ "selected", coresong, "selected", GObject.BindingFlags.SYNC_CREATE)
+ b6 = list_item.bind_property(
+ "selected", check, "active", GObject.BindingFlags.SYNC_CREATE)
+ b7 = self.bind_property(
+ "selection-mode", check, "visible",
+ GObject.BindingFlags.SYNC_CREATE)
+
+ def on_activated(widget, value):
+ if check.props.active:
+ self._selection_model.select_item(
+ list_item.get_position(), False)
+ else:
+ self._selection_model.unselect_item(
+ list_item.get_position())
+
+ # The listitem selected property is read-only.
+ # It cannot be bound from the check active property.
+ # It is necessary to update the selection model in order
+ # to update it.
+ check.connect("notify::active", on_activated)
+
+ self._list_item_bindings[list_item] = [b1, b2, b3, b4, b5, b6, b7]
+ self._list_item_star_controllers[list_item] = [star_click, star_hover]
+
+ def _unbind_list_item(
+ self, factory: Gtk.SignalListItemFactory,
+ list_item: Gtk.ListItem) -> None:
+ bindings = self._list_item_bindings.pop(list_item)
+ [binding.unbind() for binding in bindings]
+
+ list_row = list_item.props.child
+ check = list_row.get_first_child()
+ info_box = check.get_next_sibling()
+ duration_label = info_box.get_next_sibling()
+ star_box = duration_label.get_next_sibling()
+ star_image = star_box.get_first_child()
+
+ controllers = self._list_item_star_controllers.pop(list_item)
+ [star_image.remove_controller(ctrl) for ctrl in controllers]
+
+ signal_id, detail_id = GObject.signal_parse_name(
+ "notify::active", check, True)
+ handler_id = GObject.signal_handler_find(
+ check, GObject.SignalMatchType.ID, signal_id, detail_id, None, 0,
+ 0)
+ check.disconnect(handler_id)
@GObject.Property(type=bool, default=False)
def selection_mode(self):
@@ -130,101 +248,17 @@ class SongsView(Gtk.ScrolledWindow):
if self._selection_mode is False:
self.deselect_all()
- cols = self._songs_view.get_columns()
- cols[1].props.visible = self._selection_mode
-
- @Gtk.Template.Callback()
- def _on_item_activated(self, treeview, path, column):
- """Action performed when clicking on a song
-
- clicking on star column toggles favorite
- clicking on an other columns launches player
-
- :param Gtk.TreeView treeview: self._songs_view
- :param Gtk.TreePath path: activated row index
- :param Gtk.TreeViewColumn column: activated column
+ def _toggle_all_selection(self, selected):
+ """Selects or deselects all items.
"""
- if self._star_handler.star_renderer_click:
- self._star_handler.star_renderer_click = False
- return
-
- if self.props.selection_mode:
- return
-
- itr = self._model.get_iter(path)
- coresong = self._model[itr][7]
- self._coremodel.props.active_core_object = coresong
-
- self._player.play(coresong)
-
- @Gtk.Template.Callback()
- def _on_view_clicked(self, gesture, n_press, x, y):
- """Ctrl+click on self._songs_view triggers selection mode."""
- _, state = Gtk.get_current_event_state()
- modifiers = Gtk.accelerator_get_default_mod_mask()
- if (state & modifiers == Gdk.ModifierType.CONTROL_MASK
- and not self.props.selection_mode):
- self.props.selection_mode = True
-
- # FIXME: In selection mode, star clicks might still trigger
- # activation.
- if self.props.selection_mode:
- path = self._songs_view.get_path_at_pos(x, y)
- if path is None:
- return
-
- iter_ = self._model.get_iter(path[0])
- new_fav_status = not self._model[iter_][1]
- self._model[iter_][1] = new_fav_status
- self._model[iter_][7].props.selected = new_fav_status
-
- def _update_model(self, player):
- """Updates model when the song changes
-
- :param Player player: The main player object
- """
- # iter_to_clean is necessary because of a bug in GtkTreeView
- # See https://gitlab.gnome.org/GNOME/gtk/issues/503
- if self._iter_to_clean:
- self._model[self._iter_to_clean][9] = False
-
- index = self._player.props.position
- current_coresong = self._playlist_model[index]
- for idx, liststore in enumerate(self._model):
- if liststore[7] == current_coresong:
- break
-
- iter_ = self._model.get_iter_from_string(str(idx))
- path = self._model.get_path(iter_)
- self._model[iter_][9] = True
- self._songs_view.scroll_to_cell(path, None, True, 0.5, 0.5)
-
- if self._model[iter_][0] != SongStateIcon.ERROR.value:
- self._iter_to_clean = iter_.copy()
-
- return False
-
- @GObject.Property(
- type=Gtk.ListStore, default=None, flags=GObject.ParamFlags.READABLE)
- def model(self):
- """Get songs view model
-
- :returns: songs view model
- :rtype: Gtk.ListStore
- """
- return self._model
-
- def _select(self, value):
- with self._model.freeze_notify():
- itr = self._model.iter_children(None)
- while itr is not None:
- self._model[itr][7].props.selected = value
- self._model[itr][1] = value
-
- itr = self._model.iter_next(itr)
+ with self._coreselection.freeze_notify():
+ if selected:
+ self._selection_model.select_all()
+ else:
+ self._selection_model.unselect_all()
def select_all(self):
- self._select(True)
+ self._toggle_all_selection(True)
def deselect_all(self):
- self._select(False)
+ self._toggle_all_selection(False)
diff --git a/gnomemusic/widgets/albumcover.py b/gnomemusic/widgets/albumcover.py
index 6b0dd1dcf..e707f3bc4 100644
--- a/gnomemusic/widgets/albumcover.py
+++ b/gnomemusic/widgets/albumcover.py
@@ -84,8 +84,6 @@ class AlbumCover(Gtk.FlowBoxChild):
self._art_stack.props.size = ArtSize.MEDIUM
self._art_stack.props.art_type = DefaultIconType.ALBUM
- self.show()
-
def retrieve(self):
"""Start retrieving the actual album cover
diff --git a/gnomemusic/widgets/albumwidget.py b/gnomemusic/widgets/albumwidget.py
index 03f8c254e..6b2fb78ba 100644
--- a/gnomemusic/widgets/albumwidget.py
+++ b/gnomemusic/widgets/albumwidget.py
@@ -27,7 +27,7 @@ from gettext import ngettext
from typing import Optional, Union
import typing
-from gi.repository import Gfm, Gio, GLib, GObject, Gtk, Handy
+from gi.repository import Adw, Gio, GLib, GObject, Gtk
from gnomemusic.corealbum import CoreAlbum
from gnomemusic.utils import ArtSize, DefaultIconType
@@ -42,7 +42,7 @@ if typing.TYPE_CHECKING:
@Gtk.Template(resource_path='/org/gnome/Music/ui/AlbumWidget.ui')
-class AlbumWidget(Handy.Clamp):
+class AlbumWidget(Adw.Bin):
"""Album widget.
The album widget consists of an image with the album art
@@ -203,7 +203,7 @@ class AlbumWidget(Handy.Clamp):
return disc_box
def _on_model_items_changed(
- self, model: Gfm.SortListModel, position: int, removed: int,
+ self, model: Gtk.SortListModel, position: int, removed: int,
added: int) -> None:
n_items: int = model.get_n_items()
if n_items == 1:
diff --git a/gnomemusic/widgets/appmenu.py b/gnomemusic/widgets/appmenu.py
index b14641626..c0baecb72 100644
--- a/gnomemusic/widgets/appmenu.py
+++ b/gnomemusic/widgets/appmenu.py
@@ -28,7 +28,7 @@ from gnomemusic.scrobbler import GoaLastFM
@Gtk.Template(resource_path="/org/gnome/Music/ui/AppMenu.ui")
-class AppMenu(Gtk.PopoverMenu):
+class AppMenu(Gtk.Popover):
"""AppMenu shown from the HeaderBar within the main view"""
__gtype_name__ = "AppMenu"
diff --git a/gnomemusic/widgets/artistalbumswidget.py b/gnomemusic/widgets/artistalbumswidget.py
index 4fce09c30..74255d62a 100644
--- a/gnomemusic/widgets/artistalbumswidget.py
+++ b/gnomemusic/widgets/artistalbumswidget.py
@@ -23,18 +23,19 @@
# delete this exception statement from your version.
from __future__ import annotations
+from typing import Optional
import typing
-from gi.repository import GObject, Gtk, Handy
+from gi.repository import GObject, Gtk
+from gnomemusic.coreartist import CoreArtist
from gnomemusic.widgets.albumwidget import AlbumWidget
if typing.TYPE_CHECKING:
from gnomemusic.application import Application
- from gnomemusic.coreartist import CoreArtist
@Gtk.Template(resource_path="/org/gnome/Music/ui/ArtistAlbumsWidget.ui")
-class ArtistAlbumsWidget(Handy.Clamp):
+class ArtistAlbumsWidget(Gtk.Box):
"""Widget containing all albums by an artist
A vertical list of AlbumWidget, containing all the albums
@@ -48,34 +49,33 @@ class ArtistAlbumsWidget(Handy.Clamp):
selection_mode = GObject.Property(type=bool, default=False)
- def __init__(
- self, coreartist: CoreArtist, application: Application) -> None:
+ def __init__(self, application: Application) -> None:
"""Initialize the ArtistAlbumsWidget
- :param CoreArtist coreartist: The CoreArtist object
:param Aplication application: The Application object
"""
super().__init__()
self._application = application
- self._artist = coreartist
- self._model = coreartist.props.model
- self._player = self._application.props.player
+ self._coreartist: Optional[CoreArtist] = None
- self._listbox.bind_model(self._model, self._add_album)
+ def _update_model(self) -> None:
+ if self._coreartist is not None:
+ self._listbox.bind_model(
+ self._coreartist.props.model, self._add_album)
def _add_album(self, corealbum):
row = Gtk.ListBoxRow()
row.props.selectable = False
row.props.activatable = False
- row.props.can_focus = False
+ row.props.focusable = False
widget = AlbumWidget(self._application)
widget.props.corealbum = corealbum
- widget.props.active_coreobject = self._artist
+ widget.props.active_coreobject = self._coreartist
widget.props.show_artist_label = False
- self._artist.bind_property(
+ self._coreartist.bind_property(
"selected", corealbum, "selected",
GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
@@ -83,19 +83,37 @@ class ArtistAlbumsWidget(Handy.Clamp):
GObject.BindingFlags.BIDIRECTIONAL
| GObject.BindingFlags.SYNC_CREATE)
- row.add(widget)
+ row.set_child(widget)
return row
def select_all(self) -> None:
"""Select all items"""
- self._artist.props.selected = True
+ if self._coreartist is not None:
+ self._coreartist.props.selected = True
def deselect_all(self) -> None:
"""Deselect all items"""
- self._artist.props.selected = False
+ if self._coreartist is not None:
+ self._coreartist.props.selected = False
+
+ @GObject.Property(
+ type=CoreArtist, flags=GObject.ParamFlags.READWRITE, default=None)
+ def coreartist(self) -> CoreArtist:
+ """Current CoreArtist object
+
+ :param CoreArtist coreartist: The CoreArtist object
+ :rtype: CoreArtist
+ """
+ return self._coreartist
+
+ @coreartist.setter # type: ignore
+ def coreartist(self, coreartist: CoreArtist) -> None:
+ """Sets the CoreArtist for the widget
+
+ :param CoreArtist coreartist: The CoreArtist to use
+ """
+ if self._coreartist is not coreartist:
+ self._coreartist = coreartist
- @GObject.Property(type=str, flags=GObject.ParamFlags.READABLE)
- def artist(self) -> str:
- """Artist name"""
- return self._artist.props.artist
+ self._update_model()
diff --git a/gnomemusic/widgets/artistsearchtile.py b/gnomemusic/widgets/artistsearchtile.py
index 7bcecfc01..329bf1d36 100644
--- a/gnomemusic/widgets/artistsearchtile.py
+++ b/gnomemusic/widgets/artistsearchtile.py
@@ -42,7 +42,6 @@ class ArtistSearchTile(Gtk.FlowBoxChild):
_artist_label = Gtk.Template.Child()
_art_stack = Gtk.Template.Child()
_check = Gtk.Template.Child()
- _events = Gtk.Template.Child()
coreartist = GObject.Property(
type=CoreArtist, default=None, flags=GObject.ParamFlags.READWRITE)
@@ -83,14 +82,11 @@ class ArtistSearchTile(Gtk.FlowBoxChild):
"selection-mode", self._check, "visible",
GObject.BindingFlags.BIDIRECTIONAL)
- self._events.add_events(Gdk.EventMask.TOUCH_MASK)
-
- self.show()
-
@Gtk.Template.Callback()
- def _on_artist_event(self, evbox, event, data=None):
+ def _on_artist_event(self, gesture_click, n_press, x, y):
+ state = gesture_click.get_current_event_state()
modifiers = Gtk.accelerator_get_default_mod_mask()
- if ((event.get_state() & modifiers) == Gdk.ModifierType.CONTROL_MASK
+ if (state & modifiers == Gdk.ModifierType.CONTROL_MASK
and not self.props.selection_mode):
self.props.selection_mode = True
diff --git a/gnomemusic/widgets/artisttile.py b/gnomemusic/widgets/artisttile.py
index 17ea1b6ef..7ed4d7093 100644
--- a/gnomemusic/widgets/artisttile.py
+++ b/gnomemusic/widgets/artisttile.py
@@ -22,6 +22,9 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
+from __future__ import annotations
+from typing import Optional
+
from gi.repository import GObject, Gtk
from gnomemusic.coreartist import CoreArtist
@@ -29,7 +32,7 @@ from gnomemusic.utils import ArtSize, DefaultIconType
@Gtk.Template(resource_path='/org/gnome/Music/ui/ArtistTile.ui')
-class ArtistTile(Gtk.ListBoxRow):
+class ArtistTile(Gtk.Box):
"""Row for sidebars
Contains a label and an optional checkbox.
@@ -40,13 +43,13 @@ class ArtistTile(Gtk.ListBoxRow):
_art_stack = Gtk.Template.Child()
_label = Gtk.Template.Child()
- coreartist = GObject.Property(type=CoreArtist, default=None)
text = GObject.Property(type=str, default='')
- def __init__(self, coreartist=None):
+ def __init__(self) -> None:
+ """Initialise ArtistTile"""
super().__init__()
- self.props.coreartist = coreartist
+ self._coreartist: Optional[CoreArtist] = None
self._art_stack.props.size = ArtSize.XSMALL
self._art_stack.props.art_type = DefaultIconType.ARTIST
@@ -54,9 +57,23 @@ class ArtistTile(Gtk.ListBoxRow):
self.bind_property('text', self._label, 'label')
self.bind_property('text', self._label, 'tooltip-text')
- self.show()
+ @GObject.Property(
+ type=CoreArtist, flags=GObject.ParamFlags.READWRITE, default=None)
+ def coreartist(self) -> CoreArtist:
+ """CoreArtist to use for ArtistTile
- def retrieve(self):
- """Start retrieving artist art
+ :returns: The artist object
+ :rtype: CoreArtist
"""
- self._art_stack.props.coreobject = self.props.coreartist
+ return self._coreartist
+
+ @coreartist.setter # type: ignore
+ def coreartist(self, coreartist: CoreArtist) -> None:
+ """CoreArtist setter
+
+ :param CoreArtist coreartist: The coreartist to use
+ """
+ self._coreartist = coreartist
+
+ self._art_stack.props.coreobject = coreartist
+ self.props.text = coreartist.props.artist
diff --git a/gnomemusic/widgets/artstack.py b/gnomemusic/widgets/artstack.py
index 2f805f2a4..8daa12c48 100644
--- a/gnomemusic/widgets/artstack.py
+++ b/gnomemusic/widgets/artstack.py
@@ -26,10 +26,7 @@ from __future__ import annotations
from typing import Optional, Union
import typing
-from gi.repository import GObject, Gtk, Handy
-if typing.TYPE_CHECKING:
- from cairo import ImageSurface
-
+from gi.repository import Adw, GObject, Gtk
from gnomemusic.asyncqueue import AsyncQueue
from gnomemusic.artcache import ArtCache
@@ -82,7 +79,7 @@ class ArtStack(Gtk.Stack):
self.props.size = size
self.connect("destroy", self._on_destroy)
- Handy.StyleManager.get_default().connect(
+ Adw.StyleManager.get_default().connect(
"notify::dark", self._on_dark_changed)
@GObject.Property(type=object, flags=GObject.ParamFlags.READWRITE)
@@ -131,6 +128,9 @@ class ArtStack(Gtk.Stack):
@coreobject.setter # type: ignore
def coreobject(self, coreobject: CoreObject) -> None:
+ if coreobject is self._coreobject:
+ return
+
if self._thumbnail_id != 0:
self._coreobject.disconnect(self._thumbnail_id)
self._thumbnail_id = 0
@@ -143,7 +143,7 @@ class ArtStack(Gtk.Stack):
self._on_thumbnail_changed(self._coreobject, None)
def _on_dark_changed(
- self, style_manager: Handy.StyleManager,
+ self, style_manager: Adw.StyleManager,
pspec: GObject.ParamSpecBoolean) -> None:
default_icon = DefaultIcon(self).get(self._art_type, self._size)
@@ -163,24 +163,26 @@ class ArtStack(Gtk.Stack):
self._async_queue.queue(self._cache, coreobject, self._size)
- def _swap_thumbnails(self, surface: ImageSurface, animate: bool) -> None:
+ def _swap_thumbnails(
+ self, paintable: Gtk.Paintable, animate: bool) -> None:
if self.props.visible_child_name == "B":
- self._cover_a.props.surface = surface
+ self._cover_a.props.paintable = paintable
if animate:
self.set_visible_child_full(
"A", Gtk.StackTransitionType.CROSSFADE)
else:
self.props.visible_child_name = "A"
else:
- self._cover_b.props.surface = surface
+ self._cover_b.props.paintable = paintable
if animate:
self.set_visible_child_full(
"B", Gtk.StackTransitionType.CROSSFADE)
else:
self.props.visible_child_name = "B"
- def _on_cache_result(self, cache: ArtCache, surface: ImageSurface) -> None:
- self._swap_thumbnails(surface, True)
+ def _on_cache_result(
+ self, cache: ArtCache, paintable: Gtk.Paintable) -> None:
+ self._swap_thumbnails(paintable, True)
def _on_destroy(self, widget: ArtStack) -> None:
# If the stack is destroyed while the art is updated, an error
diff --git a/gnomemusic/widgets/discbox.py b/gnomemusic/widgets/discbox.py
index 487ac52e1..85bcd379d 100644
--- a/gnomemusic/widgets/discbox.py
+++ b/gnomemusic/widgets/discbox.py
@@ -107,28 +107,10 @@ class DiscBox(Gtk.ListBoxRow):
@Gtk.Template.Callback()
def _song_activated(
self, list_box: Gtk.ListBox, song_widget: SongWidget) -> bool:
- if song_widget.props.select_click:
- song_widget.props.select_click = False
- return True
-
- event = Gtk.get_current_event()
- (_, state) = event.get_state()
- mod_mask = Gtk.accelerator_get_default_mod_mask()
- if ((state & mod_mask) == Gdk.ModifierType.CONTROL_MASK
- and not self.props.selection_mode):
- self.props.selection_mode = True
- song_widget.props.select_click = True
- song_widget.props.coresong.props.selected = True
- return True
-
- (_, button) = event.get_button()
- if (button == Gdk.BUTTON_PRIMARY
- and not self.props.selection_mode):
- self.emit("song-activated", song_widget)
-
if self.props.selection_mode:
- song_widget.props.select_click = True
selection_state = song_widget.props.coresong.props.selected
song_widget.props.coresong.props.selected = not selection_state
+ else:
+ self.emit("song-activated", song_widget)
- return True
+ return Gdk.EVENT_STOP
diff --git a/gnomemusic/widgets/headerbar.py b/gnomemusic/widgets/headerbar.py
index 8190638d4..1b890338a 100644
--- a/gnomemusic/widgets/headerbar.py
+++ b/gnomemusic/widgets/headerbar.py
@@ -25,7 +25,7 @@
from enum import IntEnum
from gettext import gettext as _, ngettext
-from gi.repository import GObject, Gtk, Handy
+from gi.repository import Adw, GObject, Gtk
from gnomemusic.widgets.appmenu import AppMenu
@@ -72,7 +72,7 @@ class SelectionBarMenuButton(Gtk.MenuButton):
@Gtk.Template(resource_path="/org/gnome/Music/ui/HeaderBar.ui")
-class HeaderBar(Handy.HeaderBar):
+class HeaderBar(Adw.Bin):
"""Headerbar of the application"""
class State(IntEnum):
@@ -92,12 +92,16 @@ class HeaderBar(Handy.HeaderBar):
_select_button = Gtk.Template.Child()
_cancel_button = Gtk.Template.Child()
_back_button = Gtk.Template.Child()
+ _headerbar = Gtk.Template.Child()
+ _label_title_box = Gtk.Template.Child()
+ _label_title = Gtk.Template.Child()
+ _label_subtitle = Gtk.Template.Child()
_menu_button = Gtk.Template.Child()
search_mode_active = GObject.Property(type=bool, default=False)
selected_songs_count = GObject.Property(type=int, default=0, minimum=0)
selection_mode_allowed = GObject.Property(type=bool, default=True)
- stack = GObject.Property(type=Gtk.Stack)
+ stack = GObject.Property(type=Adw.ViewStack)
def __init__(self, application):
"""Initialize Headerbar
@@ -108,16 +112,16 @@ class HeaderBar(Handy.HeaderBar):
self._selection_mode = False
- self._stack_switcher = Handy.ViewSwitcher(
- can_focus=False, halign="center")
- self._stack_switcher.show()
+ self._stack_switcher = Adw.ViewSwitcher(
+ focusable=False, halign="center",
+ policy=Adw.ViewSwitcherPolicy.WIDE)
self._selection_menu = SelectionBarMenuButton()
self._menu_button.set_popover(AppMenu(application))
self.bind_property(
- "selection-mode", self, "show-close-button",
+ "selection-mode", self._headerbar, "show-end-title-buttons",
GObject.BindingFlags.INVERT_BOOLEAN
| GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
@@ -205,6 +209,24 @@ class HeaderBar(Handy.HeaderBar):
self._select_button.props.sensitive = True
self._stack_switcher.show()
+ def set_label_title(self, title: str, subtitle: str) -> None:
+ """Set the headerbar title-widget as two labels:
+ a title and a subtitle
+
+ :param str title: headerbar title
+ :param str subtitle: headerbar subtitle
+ :returns:
+ """
+ self._headerbar.props.title_widget = self._label_title_box
+ self._label_title.props.label = title
+ self._label_subtitle.props.label = subtitle
+ if not subtitle:
+ self._label_title.props.valign = Gtk.Align.CENTER
+ self._label_subtitle.props.visible = False
+ else:
+ self._label_title.props.valign = Gtk.Align.FILL
+ self._label_subtitle.props.visible = True
+
@Gtk.Template.Callback()
def _on_back_button_clicked(self, widget=None):
self.emit('back-button-clicked')
@@ -215,11 +237,11 @@ class HeaderBar(Handy.HeaderBar):
def _update(self):
if self.props.selection_mode:
- self.props.custom_title = self._selection_menu
+ self._headerbar.props.title_widget = self._selection_menu
elif self.props.state != HeaderBar.State.MAIN:
- self.props.custom_title = None
+ self._headerbar.props.title_widget = None
else:
- self.props.custom_title = self._stack_switcher
+ self._headerbar.props.title_widget = self._stack_switcher
self._back_button.props.visible = (
not self.props.selection_mode
diff --git a/gnomemusic/widgets/lastfmdialog.py b/gnomemusic/widgets/lastfmdialog.py
index 5ba226563..00d39ed6c 100644
--- a/gnomemusic/widgets/lastfmdialog.py
+++ b/gnomemusic/widgets/lastfmdialog.py
@@ -24,7 +24,7 @@
from gettext import gettext as _
-from gi.repository import Gtk
+from gi.repository import Gdk, Gtk
from gnomemusic.scrobbler import GoaLastFM
@@ -36,7 +36,6 @@ class LastfmDialog(Gtk.Dialog):
__gtype_name__ = "LastfmDialog"
_action_button = Gtk.Template.Child()
- _action_button_gesture = Gtk.Template.Child()
_action_label = Gtk.Template.Child()
_status_label = Gtk.Template.Child()
@@ -73,5 +72,6 @@ class LastfmDialog(Gtk.Dialog):
self._action_label.props.label = action
@Gtk.Template.Callback()
- def _on_action_button_clicked(self, widget, n_press, x, y):
+ def _on_action_button_clicked(self, btn: Gtk.Button) -> bool:
self._lastfm_scrobbler.configure()
+ return Gdk.EVENT_STOP
diff --git a/gnomemusic/widgets/notificationspopup.py b/gnomemusic/widgets/notificationspopup.py
index 3ba9c55ca..f18e48b66 100644
--- a/gnomemusic/widgets/notificationspopup.py
+++ b/gnomemusic/widgets/notificationspopup.py
@@ -47,7 +47,7 @@ class NotificationsPopup(Gtk.Revealer):
self._loading_notification = LoadingNotification()
self._loading_notification.connect('visible', self._set_visibility)
self._loading_notification.connect('invisible', self._set_visibility)
- self._box.add(self._loading_notification)
+ self._box.append(self._loading_notification)
def _hide_notifications(self, notification, remove):
if remove:
@@ -62,8 +62,9 @@ class NotificationsPopup(Gtk.Revealer):
deletion is in progress.
"""
loading_finished = self._loading_notification._counter == 0
- no_other_notif = (len(self._box.get_children()) == 1
- or (len(self._box.get_children()) == 2
+ box_children = [child for child in self._box]
+ no_other_notif = (len(box_children) == 1
+ or (len(box_children) == 2
and notification != self._loading_notification))
invisible = loading_finished and no_other_notif
@@ -98,7 +99,7 @@ class NotificationsPopup(Gtk.Revealer):
:param notification: notification to display
"""
- self._box.add(notification)
+ self._box.append(notification)
self.show()
self.set_reveal_child(True)
diff --git a/gnomemusic/widgets/playertoolbar.py b/gnomemusic/widgets/playertoolbar.py
index f148bd228..829822aee 100644
--- a/gnomemusic/widgets/playertoolbar.py
+++ b/gnomemusic/widgets/playertoolbar.py
@@ -28,6 +28,7 @@ from gi.repository import Gio, GLib, GObject, Gtk
from gnomemusic.gstplayer import Playback
from gnomemusic.utils import ArtSize, DefaultIconType
from gnomemusic.player import Player, RepeatMode
+from gnomemusic.widgets.artstack import ArtStack # noqa: F401
from gnomemusic.widgets.smoothscale import SmoothScale # noqa: F401
from gnomemusic.widgets.twolinetip import TwoLineTip
import gnomemusic.utils as utils
@@ -44,12 +45,10 @@ class PlayerToolbar(Gtk.ActionBar):
_artist_label = Gtk.Template.Child()
_art_stack = Gtk.Template.Child()
- _buttons_and_scale = Gtk.Template.Child()
_duration_label = Gtk.Template.Child()
_next_button = Gtk.Template.Child()
- _pause_image = Gtk.Template.Child()
_play_button = Gtk.Template.Child()
- _play_image = Gtk.Template.Child()
+ _play_pause_image = Gtk.Template.Child()
_prev_button = Gtk.Template.Child()
_progress_scale = Gtk.Template.Child()
_progress_time_label = Gtk.Template.Child()
@@ -68,13 +67,6 @@ class PlayerToolbar(Gtk.ActionBar):
self._tooltip = TwoLineTip()
- # A centered widget has an expand child property set to False
- # by default. It needs to be True to have a progress scale
- # at the correct size.
- main_container = self._buttons_and_scale.get_parent()
- main_container.child_set_property(
- self._buttons_and_scale, "expand", True)
-
repeat_menu = Gio.Menu.new()
for mode in RepeatMode:
item = Gio.MenuItem.new()
@@ -133,7 +125,6 @@ class PlayerToolbar(Gtk.ActionBar):
self._repeat_action.set_state(new_state)
new_mode = new_state.get_string()
self._player.props.repeat_mode = RepeatMode(int(new_mode))
- self._repeat_menu_button.props.active = False
@Gtk.Template.Callback()
def _on_progress_value_changed(self, progress_scale):
@@ -158,26 +149,26 @@ class PlayerToolbar(Gtk.ActionBar):
def _sync_repeat_image(self) -> None:
self._repeat_image.set_from_icon_name(
- self._player.props.repeat_mode.icon, Gtk.IconSize.MENU)
+ self._player.props.repeat_mode.icon)
def _sync_playing(self, player, state):
if (self._player.props.state == Playback.STOPPED
and not self._player.props.has_next
and not self._player.props.has_previous):
- self.hide()
+ self.props.revealed = False
return
- self.show()
+ self.props.revealed = True
if self._player.props.state == Playback.PLAYING:
- image = self._pause_image
+ icon_name = "media-playback-pause-symbolic"
tooltip = _("Pause")
else:
- image = self._play_image
+ icon_name = "media-playback-start-symbolic"
tooltip = _("Play")
- if self._play_button.get_image() != image:
- self._play_button.set_image(image)
+ if self._play_pause_image.props.icon_name != icon_name:
+ self._play_pause_image.props.icon_name = icon_name
self._play_button.set_tooltip_text(tooltip)
diff --git a/gnomemusic/widgets/playlistdialog.py b/gnomemusic/widgets/playlistdialog.py
index 2c67defb9..fa402f767 100644
--- a/gnomemusic/widgets/playlistdialog.py
+++ b/gnomemusic/widgets/playlistdialog.py
@@ -132,5 +132,6 @@ class PlaylistDialog(Gtk.Dialog):
self._add_playlist_button.props.sensitive = False
@Gtk.Template.Callback()
- def _on_add_playlist_entry_focused(self, editable, data=None):
+ def _on_add_playlist_entry_focused(
+ self, controller: Gtk.EventControllerFocus) -> None:
self._listbox.unselect_all()
diff --git a/gnomemusic/widgets/searchheaderbar.py b/gnomemusic/widgets/searchheaderbar.py
index d5efaea55..0e539e0eb 100644
--- a/gnomemusic/widgets/searchheaderbar.py
+++ b/gnomemusic/widgets/searchheaderbar.py
@@ -24,14 +24,14 @@
from enum import IntEnum
-from gi.repository import GLib, GObject, Gtk, Handy
+from gi.repository import Adw, GObject, Gtk
from gnomemusic.search import Search
from gnomemusic.widgets.headerbar import HeaderBar, SelectionBarMenuButton
@Gtk.Template(resource_path="/org/gnome/Music/ui/SearchHeaderBar.ui")
-class SearchHeaderBar(Handy.HeaderBar):
+class SearchHeaderBar(Adw.Bin):
"""SearcnHeaderbar of the application"""
class State(IntEnum):
@@ -43,6 +43,7 @@ class SearchHeaderBar(Handy.HeaderBar):
__gtype_name__ = "SearchHeaderBar"
+ _headerbar = Gtk.Template.Child()
_search_button = Gtk.Template.Child()
_select_button = Gtk.Template.Child()
_cancel_button = Gtk.Template.Child()
@@ -51,14 +52,13 @@ class SearchHeaderBar(Handy.HeaderBar):
search_state = GObject.Property(type=int, default=Search.State.NONE)
selected_songs_count = GObject.Property(type=int, default=0, minimum=0)
selection_mode_allowed = GObject.Property(type=bool, default=True)
- stack = GObject.Property(type=Gtk.Stack)
+ stack = GObject.Property(type=Adw.ViewStack)
def __init__(self, application):
super().__init__()
self._coregrilo = application.props.coregrilo
self._selection_mode = False
- self._timeout = None
self._entry = Gtk.SearchEntry()
self._entry.props.halign = Gtk.Align.CENTER
@@ -68,7 +68,7 @@ class SearchHeaderBar(Handy.HeaderBar):
self._selection_menu = SelectionBarMenuButton()
self.bind_property(
- "selection-mode", self, "show-close-button",
+ "selection-mode", self._headerbar, "show-end-title-buttons",
GObject.BindingFlags.INVERT_BOOLEAN
| GObject.BindingFlags.SYNC_CREATE)
self.bind_property(
@@ -99,7 +99,7 @@ class SearchHeaderBar(Handy.HeaderBar):
"notify::search-mode-active", self._on_search_mode_changed)
self.connect("notify::search-state", self._search_state_changed)
- self._entry.connect("changed", self._search_entry_timeout)
+ self._entry.connect("search-changed", self._search_entry_changed)
@GObject.Property(type=bool, default=False)
def selection_mode(self):
@@ -164,9 +164,9 @@ class SearchHeaderBar(Handy.HeaderBar):
def _update(self):
if self.props.selection_mode:
- self.props.custom_title = self._selection_menu
+ self._headerbar.props.title_widget = self._selection_menu
else:
- self.props.custom_title = self._entry
+ self._headerbar.props.title_widget = self._entry
def _on_selection_mode_allowed_changed(self, widget, data):
if self.props.selection_mode_allowed:
@@ -174,16 +174,7 @@ class SearchHeaderBar(Handy.HeaderBar):
else:
self._select_button.props.sensitive = False
- def _search_entry_timeout(self, widget):
- if self._timeout:
- GLib.source_remove(self._timeout)
-
- self._timeout = GLib.timeout_add(
- 500, self._search_entry_changed, widget)
-
- def _search_entry_changed(self, widget):
- self._timeout = None
-
+ def _search_entry_changed(self, widget: Gtk.SearchEntry) -> bool:
search_term = self._entry.get_text()
if search_term != "":
self.props.stack.set_visible_child_name("search")
@@ -195,7 +186,6 @@ class SearchHeaderBar(Handy.HeaderBar):
def _on_search_mode_changed(self, klass, data):
if self.props.search_mode_active:
- # self._search_entry.realize()
self._entry.grab_focus()
def _search_state_changed(self, klass, data):
diff --git a/gnomemusic/widgets/smoothscale.py b/gnomemusic/widgets/smoothscale.py
index d1f6d3f9d..8892c4c4f 100644
--- a/gnomemusic/widgets/smoothscale.py
+++ b/gnomemusic/widgets/smoothscale.py
@@ -46,10 +46,10 @@ class SmoothScale(Gtk.Scale):
self._timeout = None
- self._controller = Gtk.GestureMultiPress().new(self)
- self._controller.props.propagation_phase = Gtk.PropagationPhase.CAPTURE
- self._controller.connect("pressed", self._on_button_pressed)
- self._controller.connect("released", self._on_button_released)
+ ctrl = Gtk.GestureClick()
+ ctrl.connect("pressed", self._on_button_pressed)
+ ctrl.connect("released", self._on_button_released)
+ self.add_controller(ctrl)
self.connect('change-value', self._on_smooth_scale_seek)
@@ -162,10 +162,8 @@ class SmoothScale(Gtk.Scale):
duration = abs(self._player.props.duration)
style_context = self.get_style_context()
- state = style_context.get_state()
-
width = self.get_allocated_width()
- padding = style_context.get_padding(state)
+ padding = style_context.get_padding()
width = max(width - (padding.left + padding.right), 1)
timeout_period = min(1000 * duration // width, 200)
diff --git a/gnomemusic/widgets/songwidget.py b/gnomemusic/widgets/songwidget.py
index fd255dd31..ff3dbf3f4 100644
--- a/gnomemusic/widgets/songwidget.py
+++ b/gnomemusic/widgets/songwidget.py
@@ -22,13 +22,12 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
+from __future__ import annotations
+
from enum import IntEnum
from typing import Optional
-import gi
-gi.require_version('Dazzle', '1.0')
from gi.repository import Gdk, GObject, Gtk
-from gi.repository.Dazzle import BoldingLabel # noqa: F401
from gnomemusic import utils
from gnomemusic.coresong import CoreSong
@@ -56,7 +55,6 @@ class SongWidget(Gtk.ListBoxRow):
}
coresong = GObject.Property(type=CoreSong, default=None)
- select_click = GObject.Property(type=bool, default=False)
selected = GObject.Property(type=bool, default=False)
show_song_number = GObject.Property(type=bool, default=True)
@@ -64,14 +62,13 @@ class SongWidget(Gtk.ListBoxRow):
_album_duration_box = Gtk.Template.Child()
_artist_box = Gtk.Template.Child()
_artist_label = Gtk.Template.Child()
- _controller_motion = Gtk.Template.Child()
- _dnd_eventbox = Gtk.Template.Child()
+ _dnd_icon = Gtk.Template.Child()
+ _drag_source = Gtk.Template.Child()
_menu_button = Gtk.Template.Child()
_select_button = Gtk.Template.Child()
_number_label = Gtk.Template.Child()
_title_label = Gtk.Template.Child()
_duration_label = Gtk.Template.Child()
- _star_eventbox = Gtk.Template.Child()
_star_image = Gtk.Template.Child()
_star_stack = Gtk.Template.Child()
_play_icon = Gtk.Template.Child()
@@ -126,8 +123,7 @@ class SongWidget(Gtk.ListBoxRow):
self._select_button.set_visible(False)
- self._play_icon.set_from_icon_name(
- 'media-playback-start-symbolic', Gtk.IconSize.SMALL_TOOLBAR)
+ self._play_icon.set_from_icon_name("media-playback-start-symbolic")
self.props.coresong.bind_property(
'selected', self._select_button, 'active',
@@ -149,74 +145,74 @@ class SongWidget(Gtk.ListBoxRow):
if not self.props.coresong.props.is_tracker:
self._star_stack.props.visible_child_name = "empty"
- if can_dnd is True:
- self._dnd_eventbox.props.visible = True
- self._drag_widget = None
- entries = [
- Gtk.TargetEntry.new(
- "GTK_EVENT_BOX", Gtk.TargetFlags.SAME_APP, 0)
- ]
- self._dnd_eventbox.drag_source_set(
- Gdk.ModifierType.BUTTON1_MASK, entries,
- Gdk.DragAction.MOVE)
- self.drag_dest_set(
- Gtk.DestDefaults.ALL, entries, Gdk.DragAction.MOVE)
+ self._drag_x = 0.
+ self._drag_y = 0.
+ self._drag_widget: Optional[Gtk.ListBox] = None
+ if can_dnd:
+ capture_phase = Gtk.PropagationPhase.CAPTURE
+ self._drag_source.props.propagation_phase = capture_phase
+ self._dnd_icon.props.visible = True
@Gtk.Template.Callback()
- def _on_drag_begin(self, klass, context):
- gdk_window = self.get_window()
- _, x, y, _ = gdk_window.get_device_position(context.get_device())
- allocation = self.get_allocation()
+ def _on_drag_prepare(
+ self, drag_source: Gtk.DragSource, x: float,
+ y: float) -> Gdk.ContentProvider:
+ self._drag_x = x
+ self._drag_y = y
+ return Gdk.ContentProvider.new_for_value(self)
+ @Gtk.Template.Callback()
+ def _on_drag_begin(
+ self, drag_source: Gtk.DragSource, drag: Gdk.Drag) -> None:
+ allocation = self.get_allocation()
self._drag_widget = Gtk.ListBox()
self._drag_widget.set_size_request(allocation.width, allocation.height)
- drag_row = SongWidget(self.props.coresong)
+ drag_row = SongWidget(self.props.coresong, False, True)
drag_row.props.show_song_number = self.props.show_song_number
-
- self._drag_widget.add(drag_row)
+ self._drag_widget.append(drag_row)
self._drag_widget.drag_highlight_row(drag_row)
- self._drag_widget.props.visible = True
- Gtk.drag_set_icon_widget(
- context, self._drag_widget, x - allocation.x, y - allocation.y)
- @Gtk.Template.Callback()
- def _on_drag_end(self, klass, context):
- self._drag_widget = None
+ drag_icon = Gtk.DragIcon.get_for_drag(drag)
+ drag_icon.props.child = self._drag_widget
+ drag.set_hotspot(self._drag_x, self._drag_y)
@Gtk.Template.Callback()
- def _on_drag_data_get(self, klass, context, selection_data, info, time_):
- row_position = self.get_index()
- selection_data.set(
- Gdk.Atom.intern("row_position", False), 0,
- bytes(str(row_position), encoding="UTF8"))
+ def _on_drop(
+ self, target: Gtk.DropTarget,
+ source_widget: SongWidget, x: float, y: float) -> bool:
+ self._drag_widget = None
+ self._drag_x = 0.
+ self._drag_y = 0.
- @Gtk.Template.Callback()
- def _on_drag_data_received(
- self, klass, context, x, y, selection_data, info, time_):
- source_position = int(str(selection_data.get_data(), "UTF-8"))
+ source_position = source_widget.get_index()
target_position = self.get_index()
if source_position == target_position:
- return
+ return False
self.emit("widget-moved", source_position)
+ return True
@Gtk.Template.Callback()
- def _on_select_button_toggled(self, widget):
- # This property is used to ignore the second click event
- # (one event in SongWidget and the other one in select_button).
- self.props.select_click = not self.props.select_click
+ def _on_click(
+ self, gesture_click: Gtk.GestureClick, n_click: int, x: int,
+ y: int) -> bool:
+ state = gesture_click.get_current_event_state()
+ modifiers = Gtk.accelerator_get_default_mod_mask()
+ if (state & modifiers == Gdk.ModifierType.CONTROL_MASK
+ and not self.props.selection_mode):
+ self.props.selection_mode = True
- @Gtk.Template.Callback()
- def _on_star_toggle(self, widget, event):
- (_, button) = event.get_button()
- if button != Gdk.BUTTON_PRIMARY:
- return False
+ return Gdk.EVENT_STOP
+ @Gtk.Template.Callback()
+ def _on_star_toggle(
+ self, controller: Gtk.GestureClick, n_press: int, x: float,
+ y: float) -> bool:
+ controller.set_state(Gtk.EventSequenceState.CLAIMED)
favorite = not self._star_image.favorite
self._star_image.props.favorite = favorite
-
- return True
+ return Gdk.EVENT_STOP
@Gtk.Template.Callback()
def _on_star_hover(self, controller, x, y):
diff --git a/gnomemusic/widgets/songwidgetmenu.py b/gnomemusic/widgets/songwidgetmenu.py
index 9c4e75b5e..2eddd7a72 100644
--- a/gnomemusic/widgets/songwidgetmenu.py
+++ b/gnomemusic/widgets/songwidgetmenu.py
@@ -23,19 +23,19 @@
# delete this exception statement from your version.
from __future__ import annotations
-from typing import Optional, Union
+from typing import Any, Optional, Union
import typing
-from gi.repository import Gtk
+from gi.repository import Gio, Gtk
from gnomemusic.grilowrappers.grltrackerplaylists import Playlist
from gnomemusic.widgets.notificationspopup import PlaylistNotification
from gnomemusic.widgets.playlistdialog import PlaylistDialog
+from gnomemusic.widgets.songwidget import SongWidget
if typing.TYPE_CHECKING:
from gnomemusic.application import Application
from gnomemusic.corealbum import CoreAlbum
from gnomemusic.coresong import CoreSong
- from gnomemusic.widgets.songwidget import SongWidget
@Gtk.Template(resource_path="/org/gnome/Music/ui/SongWidgetMenu.ui")
@@ -43,10 +43,9 @@ class SongWidgetMenu(Gtk.PopoverMenu):
__gtype_name__ = "SongWidgetMenu"
- _remove_from_playlist_button = Gtk.Template.Child()
-
def __init__(
- self, application: Application, song_widget: SongWidget,
+ self, application: Application,
+ song_widget: Union[SongWidget, Gtk.ListItem],
coreobject: Union[CoreAlbum, CoreSong, Playlist]) -> None:
"""Menu to interact with the song of a SongWidget.
@@ -64,19 +63,34 @@ class SongWidgetMenu(Gtk.PopoverMenu):
self._coreobject = coreobject
self._song_widget = song_widget
- self._coresong = song_widget.props.coresong
-
- self._playlist_dialog: Optional[PlaylistDialog] = None
- if isinstance(self._coreobject, Playlist):
- self._remove_from_playlist_button.props.visible = True
- self._remove_from_playlist_button.props.sensitive = (
- not self._coreobject.props.is_smart)
+ if isinstance(song_widget, SongWidget):
+ self._coresong = song_widget.props.coresong
else:
- self._remove_from_playlist_button.props.visible = False
+ self._coresong = coreobject
+
+ self._playlist_dialog: Optional[PlaylistDialog] = None
- @Gtk.Template.Callback()
- def _on_play_clicked(self, widget: Gtk.Button) -> None:
+ action_group = Gio.SimpleActionGroup()
+ action_entries = [
+ ("play", self._play_song),
+ ("add_playlist", self._add_to_playlist)
+ ]
+ if (isinstance(self._coreobject, Playlist)
+ and not self._coreobject.props.is_smart):
+ action_entries.append(
+ ("remove_playlist", self._remove_from_playlist))
+ elif not isinstance(self._coreobject, Playlist):
+ self.props.menu_model.remove(2)
+
+ for name, callback in action_entries:
+ action = Gio.SimpleAction.new(name, None)
+ action.connect("activate", callback)
+ action_group.add_action(action)
+
+ self.insert_action_group("songwidget", action_group)
+
+ def _play_song(self, action: Gio.Simple, param: Any) -> None:
self.popdown()
signal_id = 0
@@ -88,8 +102,7 @@ class SongWidgetMenu(Gtk.PopoverMenu):
"playlist-loaded", _on_playlist_loaded)
self._coremodel.props.active_core_object = self._coreobject
- @Gtk.Template.Callback()
- def _on_add_to_playlist_clicked(self, widget: Gtk.Button) -> None:
+ def _add_to_playlist(self, action: Gio.Simple, param: Any) -> None:
def on_response(dialog: PlaylistDialog, response_id: int) -> None:
if not self._playlist_dialog:
@@ -108,8 +121,7 @@ class SongWidgetMenu(Gtk.PopoverMenu):
self._playlist_dialog.connect("response", on_response)
self._playlist_dialog.present()
- @Gtk.Template.Callback()
- def _on_remove_from_playlist_clicked(self, widget: Gtk.Button) -> None:
+ def _remove_from_playlist(self, action: Gio.Simple, param: Any) -> None:
self.popdown()
position = self._song_widget.get_index()
notification = PlaylistNotification( # noqa: F841
diff --git a/gnomemusic/widgets/starhandlerwidget.py b/gnomemusic/widgets/starhandlerwidget.py
index 1ea3193f0..a6881f5f7 100644
--- a/gnomemusic/widgets/starhandlerwidget.py
+++ b/gnomemusic/widgets/starhandlerwidget.py
@@ -38,10 +38,10 @@ class CellRendererStar(Gtk.CellRendererPixbuf):
self.props.mode = Gtk.CellRendererMode.ACTIVATABLE
self.props.xpad = 32
- _, width, height = Gtk.IconSize.lookup(Gtk.IconSize.SMALL_TOOLBAR)
+ # _, width, height = Gtk.IconSize.lookup(Gtk.IconSize.NORMAL)
- self._icon_width = width
- self._icon_height = height
+ self._icon_width = 16
+ self._icon_height = 16
self._show_star = 0
diff --git a/gnomemusic/window.py b/gnomemusic/window.py
index bf4a0b282..a79753817 100644
--- a/gnomemusic/window.py
+++ b/gnomemusic/window.py
@@ -24,7 +24,7 @@
from typing import Optional
-from gi.repository import Gtk, Gdk, Gio, GLib, GObject, Handy
+from gi.repository import Adw, Gtk, Gdk, Gio, GLib, GObject
from gettext import gettext as _
from gnomemusic.gstplayer import Playback
@@ -48,7 +48,7 @@ from gnomemusic.windowplacement import WindowPlacement
@Gtk.Template(resource_path="/org/gnome/Music/ui/Window.ui")
-class Window(Handy.ApplicationWindow):
+class Window(Adw.ApplicationWindow):
__gtype_name__ = "Window"
@@ -58,7 +58,6 @@ class Window(Handy.ApplicationWindow):
notifications_popup = Gtk.Template.Child()
_headerbar_stack = Gtk.Template.Child()
- _key_controller = Gtk.Template.Child()
_overlay = Gtk.Template.Child()
_player_toolbar = Gtk.Template.Child()
_selection_toolbar = Gtk.Template.Child()
@@ -229,11 +228,11 @@ class Window(Handy.ApplicationWindow):
if self.views[View.ALBUM] is not None:
return
- self._btn_ctrl = Gtk.GestureMultiPress().new(self)
- self._btn_ctrl.props.propagation_phase = Gtk.PropagationPhase.CAPTURE
+ ctrl = Gtk.GestureClick().new()
# Mouse button 8 is the back button.
- self._btn_ctrl.props.button = 8
- self._btn_ctrl.connect("pressed", self._on_back_button_pressed)
+ ctrl.props.button = 8
+ ctrl.connect("pressed", self._on_back_button_pressed)
+ self.add_controller(ctrl)
self.views[View.EMPTY].props.state = EmptyView.State.SEARCH
@@ -250,9 +249,9 @@ class Window(Handy.ApplicationWindow):
# from being displayed during startup
for i in self.views[View.ALBUM:]:
if i.props.title:
- self._stack.add_titled(i, i.props.name, i.props.title)
- self._stack.child_set_property(
- i, "icon-name", i.props.icon_name)
+ stackpage = self._stack.add_titled(
+ i, i.props.name, i.props.title)
+ stackpage.props.icon_name = i.props.icon_name
else:
self._stack.add_named(i, i.props.name)
@@ -288,7 +287,7 @@ class Window(Handy.ApplicationWindow):
modifiers = state & Gtk.accelerator_get_default_mod_mask()
control_mask = Gdk.ModifierType.CONTROL_MASK
shift_mask = Gdk.ModifierType.SHIFT_MASK
- mod1_mask = Gdk.ModifierType.MOD1_MASK
+ alt_mask = Gdk.ModifierType.ALT_MASK
shift_ctrl_mask = control_mask | shift_mask
# Ctrl+<KEY>
@@ -328,7 +327,7 @@ class Window(Handy.ApplicationWindow):
if keyval == Gdk.KEY_A:
self._deselect_all()
# Alt+<KEY>
- elif modifiers == mod1_mask:
+ elif modifiers == alt_mask:
# Go back from child view on Alt + Left
if keyval == Gdk.KEY_Left:
self._switch_back_from_childview()
@@ -443,7 +442,7 @@ class Window(Handy.ApplicationWindow):
def _on_selection_mode_changed(self, widget, data=None):
if (not self.props.selection_mode
and self._player.state == Playback.STOPPED):
- self._player_toolbar.hide()
+ self._player_toolbar.props.revealed = False
def _on_add_to_playlist(self, widget: SelectionToolbar) -> None:
@@ -477,4 +476,4 @@ class Window(Handy.ApplicationWindow):
:param bool visible: actionbar visibility
"""
- self._player_toolbar.set_visible(visible)
+ self._player_toolbar.props.revealed = visible
diff --git a/gnomemusic/windowplacement.py b/gnomemusic/windowplacement.py
index 2eb5bff74..ee7889c5c 100644
--- a/gnomemusic/windowplacement.py
+++ b/gnomemusic/windowplacement.py
@@ -22,8 +22,14 @@
# code, but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version.
+from __future__ import annotations
+import typing
+
from gi.repository import GLib, GObject
+if typing.TYPE_CHECKING:
+ from gnomemusic.window import Window
+
class WindowPlacement(GObject.GObject):
"""Main window placement
@@ -47,42 +53,35 @@ class WindowPlacement(GObject.GObject):
self._restore_window_state()
- self._window_placement_update_timeout = None
+ self._window_placement_update_timeout = 0
self._window.connect('notify::is-maximized', self._on_maximized)
- self._window.connect('configure-event', self._on_configure_event)
+ self._window.connect("notify::default-height", self._on_size_change)
+ self._window.connect("notify::default-width", self._on_size_change)
def _restore_window_state(self):
size_setting = self._settings.get_value('window-size')
if (len(size_setting) == 2
and isinstance(size_setting[0], int)
and isinstance(size_setting[1], int)):
- self._window.resize(size_setting[0], size_setting[1])
-
- position_setting = self._settings.get_value('window-position')
- if (len(position_setting) == 2
- and isinstance(position_setting[0], int)
- and isinstance(position_setting[1], int)):
- self._window.move(position_setting[0], position_setting[1])
+ self._window.set_default_size(size_setting[0], size_setting[1])
if self._settings.get_value('window-maximized'):
self._window.maximize()
- def _on_configure_event(self, widget, event):
- if self._window_placement_update_timeout is None:
+ def _on_size_change(
+ self, window: Window, size: GObject.ParamSpecInt) -> None:
+ if self._window_placement_update_timeout == 0:
self._window_placement_update_timeout = GLib.timeout_add(
- 500, self._store_size_and_position, widget)
-
- def _store_size_and_position(self, widget):
- size = widget.get_size()
- self._settings.set_value(
- 'window-size', GLib.Variant('ai', [size[0], size[1]]))
+ 500, self._store_size, window)
- position = widget.get_position()
+ def _store_size(self, window: Window) -> bool:
+ width = window.get_width()
+ height = window.get_height()
self._settings.set_value(
- 'window-position', GLib.Variant('ai', [position[0], position[1]]))
+ "window-size", GLib.Variant("ai", [width, height]))
GLib.source_remove(self._window_placement_update_timeout)
- self._window_placement_update_timeout = None
+ self._window_placement_update_timeout = 0
return False
diff --git a/meson.build b/meson.build
index 6c0db1a3e..b213d6194 100644
--- a/meson.build
+++ b/meson.build
@@ -44,9 +44,8 @@ PKGLIB_DIR = join_paths(get_option('prefix'), get_option('libdir'), APPLICATION_
dependency('glib-2.0', version: '>= 2.67.1')
dependency('goa-1.0', version: '>= 3.35.90')
dependency('gobject-introspection-1.0', version: '>= 1.35.0')
-dependency('gtk+-3.0', version: '>= 3.24.14')
-dependency('libhandy-1', version: '>= 1.5.0')
-dependency('libdazzle-1.0', version: '>= 3.28.0')
+dependency('gtk4', version: '>= 4.5.0')
+dependency('libadwaita-1', version: '>= 1.0')
dependency('libmediaart-2.0', version: '>= 1.9.1')
dependency('libsoup-2.4')
dependency('tracker-sparql-3.0', version: '>= 2.99.3')
@@ -56,12 +55,6 @@ dependency('py3cairo', version: '>= 1.14.0')
dependency('grilo-0.3', version: '>= 0.3.13', fallback: ['grilo', 'libgrl_dep'])
dependency('grilo-plugins-0.3', version: '>= 0.3.12', fallback: ['grilo-plugins', 'grilo_plugins_dep'])
-subproject('gfm',
- default_options: [
- 'pkgdatadir=' + PKGDATA_DIR,
- 'pkglibdir=' + PKGLIB_DIR
- ])
-
subdir('data/ui')
subdir('data')
subdir('help')
@@ -80,8 +73,6 @@ bin_config.set('pkgdatadir', PKGDATA_DIR)
bin_config.set('localedir', join_paths(get_option('prefix'), get_option('datadir'), 'locale'))
bin_config.set('pythondir', PYTHON_DIR)
bin_config.set('schemasdir', PKGDATA_DIR)
-# Used for gfm
-bin_config.set('gfmlibdir', PKGLIB_DIR)
bin_config.set('local_build', 'False')
@@ -100,8 +91,6 @@ local_config.set('pkgdatadir', join_paths(meson.current_build_dir(), 'data'))
local_config.set('localedir', join_paths(get_option('prefix'), get_option('datadir'), 'locale'))
local_config.set('pythondir', meson.current_source_dir())
local_config.set('schemasdir', join_paths(meson.current_build_dir(), 'data'))
-# Used for gfm
-local_config.set('gfmlibdir', join_paths(meson.current_build_dir(), 'subprojects', 'gfm'))
local_config.set('local_build', 'True')
diff --git a/org.gnome.Music.json b/org.gnome.Music.json
index 0a1e070f0..e4d730702 100644
--- a/org.gnome.Music.json
+++ b/org.gnome.Music.json
@@ -7,6 +7,7 @@
"tags": ["devel", "development", "nightly"],
"desktop-file-name-suffix": " ☢️",
"finish-args": [
+ "--device=dri",
"--share=ipc",
"--share=network",
"--socket=wayland",
@@ -43,16 +44,6 @@
}
]
},
- {
- "name": "libdazzle",
- "buildsystem": "meson",
- "sources": [
- {
- "type": "git",
- "url": "https://gitlab.gnome.org/GNOME/libdazzle.git"
- }
- ]
- },
{
"name": "tracker-miners",
"buildsystem": "meson",
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 006a563b1..72df75ebc 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -2,4 +2,3 @@
# Please keep this file sorted alphabetically.
data/ui/AboutDialog.ui
data/org.gnome.Music.appdata.xml
-subprojects/gfm
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]