[gnome-music/wip/mschraal/gtk4-pre-squash-backup: 217/254] songsview: Basic working
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/mschraal/gtk4-pre-squash-backup: 217/254] songsview: Basic working
- Date: Thu, 17 Feb 2022 11:35:45 +0000 (UTC)
commit 913550212dca9ee835ea8a72e298146801f67c4d
Author: Marinus Schraal <mschraal gnome org>
Date: Fri Feb 11 16:44:06 2022 +0100
songsview: Basic working
data/org.gnome.Music.gresource.xml | 1 +
data/ui/SongListItem.ui | 37 ++++++
data/ui/SongsView.ui | 109 ++----------------
gnomemusic/views/songsview.py | 227 +++++++++++++++----------------------
4 files changed, 138 insertions(+), 236 deletions(-)
---
diff --git a/data/org.gnome.Music.gresource.xml b/data/org.gnome.Music.gresource.xml
index b2de9b6c8..1bbd8520e 100644
--- a/data/org.gnome.Music.gresource.xml
+++ b/data/org.gnome.Music.gresource.xml
@@ -34,6 +34,7 @@
<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>
diff --git a/data/ui/SongListItem.ui b/data/ui/SongListItem.ui
new file mode 100644
index 000000000..58cee7430
--- /dev/null
+++ b/data/ui/SongListItem.ui
@@ -0,0 +1,37 @@
+<?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>
+ </object>
+</interface>
diff --git a/data/ui/SongsView.ui b/data/ui/SongsView.ui
index 0e0acdd20..ae519e1b2 100644
--- a/data/ui/SongsView.ui
+++ b/data/ui/SongsView.ui
@@ -1,115 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
- <template class="SongsView" parent="GtkScrolledWindow">
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
+ <template class="SongsView" parent="GtkBox">
<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>
- <signal name="row-activated" handler="_on_item_activated" swapped="no"/>
- <style>
- <class name="songs-list-old"/>
- </style>
+ <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="GtkGestureClick" id="_songs_ctrlr">
- <property name="propagation-phase">capture</property>
- <signal name="released" handler="_on_view_clicked" swapped="no"/>
- </object>
- </child>
- <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>
- <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>
- <child>
- <object class="GtkTreeViewColumn" id="_title_column">
- <property name="expand">True</property>
- <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>
- <attributes>
- <attribute name="text">2</attribute>
- </attributes>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="_artist_column">
- <property name="expand">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>
+ <object class="GtkScrolledWindow">
<child>
- <object class="GtkCellRendererText">
- <property name="ellipsize">end</property>
- <property name="xpad">32</property>
+ <object class="GtkListView" id="_listview">
</object>
- <attributes>
- <attribute name="text">4</attribute>
- </attributes>
</child>
</object>
</child>
- <child>
- <object class="GtkTreeViewColumn" id="_duration_column">
- <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">
- </object>
- </child>
</object>
</child>
</template>
diff --git a/gnomemusic/views/songsview.py b/gnomemusic/views/songsview.py
index ee7752ee4..531abef3a 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
@@ -23,7 +23,7 @@
# delete this exception statement from your version.
from gettext import gettext as _
-from gi.repository import Gdk, GObject, Gtk, Pango
+from gi.repository import Adw, GObject, Gtk
from gnomemusic.coresong import CoreSong
from gnomemusic.utils import SongStateIcon
@@ -31,7 +31,7 @@ from gnomemusic.widgets.starhandlerwidget import StarHandlerWidget
@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,12 +46,7 @@ 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):
"""Initialize
@@ -62,50 +57,98 @@ class SongsView(Gtk.ScrolledWindow):
self.props.name = "songs"
- self._window = application.props.window
self._coremodel = application.props.coremodel
-
- self._iter_to_clean = None
- self._set_list_renderers()
+ self._coreselection = application.props.coreselection
+ self._window = application.props.window
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._model = self._coremodel.props.songs
+ self._selection_model = Gtk.MultiSelection.new(self._model)
+
+ list_item_factory = Gtk.SignalListItemFactory()
+ list_item_factory.connect("setup", self._setup_list_item)
+ list_item_factory.connect("bind", self._bind_list_item)
+
+ self._listview.props.factory = list_item_factory
+ self._listview.props.model = self._selection_model
self._player = application.props.player
- self._player.connect('song-changed', self._update_model)
+ # self._player.connect('song-changed', self._update_model)
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)
-
- 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
-
- 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()
+ title_label = info_box.get_first_child()
+ album_label = title_label.get_next_sibling()
+ artist_label = album_label.get_next_sibling()
+
+ coresong.bind_property(
+ "title", title_label, "label",
+ GObject.BindingFlags.SYNC_CREATE)
+ coresong.bind_property(
+ "album", album_label, "label",
+ GObject.BindingFlags.SYNC_CREATE)
+ coresong.bind_property(
+ "artist", artist_label, "label",
+ GObject.BindingFlags.SYNC_CREATE)
+
+ list_item.bind_property(
+ "selected", coresong, "selected",
+ GObject.BindingFlags.SYNC_CREATE)
+ self.bind_property(
+ "selection-mode", check, "visible",
+ GObject.BindingFlags.SYNC_CREATE)
+ check.bind_property(
+ "active", coresong, "selected",
+ GObject.BindingFlags.SYNC_CREATE
+ | GObject.BindingFlags.BIDIRECTIONAL)
+
+ 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)
@GObject.Property(type=bool, default=False)
def selection_mode(self):
@@ -130,101 +173,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
- """
- 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
+ def _toggle_all_selection(self, selected):
+ """Selects or deselects all items.
"""
- 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)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]