[gnome-music/wip/jfelder/smartplaylists-ui-v2: 2/12] playlistsview: Factor out playlist list widget



commit 574287600a8e1091268a0725f777370f85d68f48
Author: Jean Felder <jfelder src gnome org>
Date:   Sat Jan 18 20:00:35 2020 +0100

    playlistsview: Factor out playlist list widget
    
    This way, it will be easier to integrate the new smart playlist widget
    in PlaylistsView.

 data/org.gnome.Music.gresource.xml    |   1 +
 data/ui/PlaylistsView.ui              |  35 +------
 data/ui/PlaylistsWidget.ui            |  36 +++++++
 gnomemusic/views/playlistsview.py     | 139 +++-----------------------
 gnomemusic/widgets/playlistswidget.py | 180 ++++++++++++++++++++++++++++++++++
 5 files changed, 232 insertions(+), 159 deletions(-)
---
diff --git a/data/org.gnome.Music.gresource.xml b/data/org.gnome.Music.gresource.xml
index 3a0aca4f..80ea92f9 100644
--- a/data/org.gnome.Music.gresource.xml
+++ b/data/org.gnome.Music.gresource.xml
@@ -23,6 +23,7 @@
     <file preprocess="xml-stripblanks">ui/PlaylistDialog.ui</file>
     <file preprocess="xml-stripblanks">ui/PlaylistDialogRow.ui</file>
     <file preprocess="xml-stripblanks">ui/PlaylistsView.ui</file>
+    <file preprocess="xml-stripblanks">ui/PlaylistsWidget.ui</file>
     <file preprocess="xml-stripblanks">ui/PlaylistTile.ui</file>
     <file preprocess="xml-stripblanks">ui/SearchHeaderBar.ui</file>
     <file preprocess="xml-stripblanks">ui/SearchView.ui</file>
diff --git a/data/ui/PlaylistsView.ui b/data/ui/PlaylistsView.ui
index 9842e42f..f1714731 100644
--- a/data/ui/PlaylistsView.ui
+++ b/data/ui/PlaylistsView.ui
@@ -4,7 +4,7 @@
   <template class="PlaylistsView" parent="GtkStack">
     <property name="visible">True</property>
     <child>
-      <object class="GtkBox" id="main_container">
+      <object class="GtkBox" id="_main_container">
         <property name="visible">True</property>
         <child>
           <object class="GtkScrolledWindow" id="all_playlists">
@@ -23,41 +23,8 @@
           </object>
         </child>
         <child>
-          <object class="GtkBox" id="main_view_container">
-            <property name="orientation">vertical</property>
-            <property name="visible">True</property>
-            <child>
-              <object class="PlaylistControls" id="_pl_ctrls">
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkScrolledWindow" id="playlist-container">
-                <property name="vexpand">True</property>
-                <property name="visible">True</property>
-                <child>
-                  <object class="GtkListBox" id="_view">
-                    <property name="margin-left">80</property>
-                    <property name="margin-right">80</property>
-                    <property name="margin-top">20</property>
-                    <property name="valign">start</property>
-                    <property name="visible">True</property>
-                    <style>
-                      <class name="songs-list"/>
-                    </style>
-                  </object>
-                </child>
-              </object>
-            </child>
-          </object>
         </child>
       </object>
     </child>
   </template>
-  <object class="GtkGestureMultiPress" id="_view_ctrlr">
-    <property name="widget">_view</property>
-    <property name="propagation_phase">capture</property>
-    <property name="button">3</property>
-    <signal name="pressed" handler="_on_view_right_clicked" swapped="no"/>
-  </object>
 </interface>
diff --git a/data/ui/PlaylistsWidget.ui b/data/ui/PlaylistsWidget.ui
new file mode 100644
index 00000000..02a0c801
--- /dev/null
+++ b/data/ui/PlaylistsWidget.ui
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="PlaylistsWidget" parent="GtkBox">
+    <property name="orientation">vertical</property>
+    <property name="visible">True</property>
+    <child>
+      <object class="PlaylistControls" id="_pl_ctrls">
+        <property name="visible">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkScrolledWindow" id="playlist-container">
+        <property name="vexpand">True</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkListBox" id="_songs_list">
+            <property name="margin-left">80</property>
+            <property name="margin-right">80</property>
+            <property name="margin-top">20</property>
+            <property name="valign">start</property>
+            <property name="visible">True</property>
+            <style>
+              <class name="songs-list"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkGestureMultiPress" id="_songs_list_ctrlr">
+    <property name="widget">_songs_list</property>
+    <property name="propagation-phase">capture</property>
+    <property name="button">3</property>
+    <signal name="pressed" handler="_songs_list_right_click" swapped="no"/>
+  </object>
+</interface>
diff --git a/gnomemusic/views/playlistsview.py b/gnomemusic/views/playlistsview.py
index 0a0d072c..5ea1b257 100644
--- a/gnomemusic/views/playlistsview.py
+++ b/gnomemusic/views/playlistsview.py
@@ -24,16 +24,12 @@
 
 from gettext import gettext as _
 
-from gi.repository import Gdk, GObject, Gtk
+from gi.repository import GObject, Gtk
 
 from gnomemusic import log
-from gnomemusic.player import PlayerPlaylist
-from gnomemusic.widgets.playlistcontextmenu import PlaylistContextMenu
-from gnomemusic.widgets.playlistcontrols import PlaylistControls  # noqa: F401
-from gnomemusic.widgets.playlistdialog import PlaylistDialog
-from gnomemusic.widgets.notificationspopup import PlaylistNotification
+from gnomemusic.grilowrappers.grltrackerplaylists import Playlist
+from gnomemusic.widgets.playlistswidget import PlaylistsWidget
 from gnomemusic.widgets.playlisttile import PlaylistTile
-from gnomemusic.widgets.songwidget import SongWidget
 
 
 @Gtk.Template(resource_path="/org/gnome/Music/ui/PlaylistsView.ui")
@@ -42,10 +38,8 @@ class PlaylistsView(Gtk.Stack):
 
     __gtype_name__ = "PlaylistsView"
 
-    _pl_ctrls = Gtk.Template.Child()
+    _main_container = Gtk.Template.Child()
     _sidebar = Gtk.Template.Child()
-    _view = Gtk.Template.Child()
-    _view_ctrlr = Gtk.Template.Child()
 
     def __repr__(self):
         return '<PlaylistsView>'
@@ -68,19 +62,8 @@ class PlaylistsView(Gtk.Stack):
         self._window = application.props.window
         self._player = player
 
-        self._song_popover = PlaylistContextMenu(application, self._view)
-
-        self._pl_ctrls.props.application = application
-
-        play_song = self._window.lookup_action("play_song")
-        play_song.connect("activate", self._play_song)
-
-        add_song = self._window.lookup_action("add_song_to_playlist")
-        add_song.connect("activate", self._add_song_to_playlist)
-
-        self._remove_song_action = self._window.lookup_action("remove_song")
-        self._remove_song_action.connect(
-            "activate", self._stage_song_for_deletion)
+        self._playlist_widget = PlaylistsWidget(application, self)
+        self._main_container.add(self._playlist_widget)
 
         self._sidebar.bind_model(self._model, self._add_playlist_to_sidebar)
 
@@ -89,9 +72,6 @@ class PlaylistsView(Gtk.Stack):
         self._coremodel.connect(
             "notify::active-playlist", self._on_active_playlist_changed)
 
-        playlist_play_action = self._window.lookup_action("playlist_play")
-        playlist_play_action.connect("activate", self._on_play_playlist)
-
     @log
     def _add_playlist_to_sidebar(self, playlist):
         """Add a playlist to sidebar
@@ -120,69 +100,19 @@ class PlaylistsView(Gtk.Stack):
             self._sidebar.select_row(row_next)
             self._on_playlist_activated(self._sidebar, row_next)
 
-    @Gtk.Template.Callback()
-    @log
-    def _on_view_right_clicked(self, gesture, n_press, x, y):
-        requested_row = self._view.get_row_at_y(y)
-        self._view.select_row(requested_row)
-
-        _, y0 = requested_row.translate_coordinates(self._view, 0, 0)
-        row_height = requested_row.get_allocated_height()
-        rect = Gdk.Rectangle()
-        rect.x = x
-        rect.y = y0 + 0.5 * row_height
-
-        self._song_popover.props.relative_to = self._view
-        self._song_popover.props.pointing_to = rect
-        self._song_popover.popup()
-
-    @log
-    def _play_song(self, menuitem, data=None):
-        selected_row = self._view.get_selected_row()
-        song_widget = selected_row.get_child()
-        self._view.unselect_all()
-        self._song_activated(song_widget)
-
-    def _add_song_to_playlist(self, menuitem, data=None):
-        selected_row = self._view.get_selected_row()
-        song_widget = selected_row.get_child()
-        coresong = song_widget.props.coresong
-
-        playlist_dialog = PlaylistDialog(self._window)
-        if playlist_dialog.run() == Gtk.ResponseType.ACCEPT:
-            playlist = playlist_dialog.props.selected_playlist
-            playlist.add_songs([coresong])
-
-        self._view.unselect_all()
-        playlist_dialog.destroy()
-
-    @log
-    def _stage_song_for_deletion(self, menuitem, data=None):
-        selected_row = self._view.get_selected_row()
-        position = selected_row.get_index()
-        song_widget = selected_row.get_child()
-        coresong = song_widget.props.coresong
-
+    @GObject.Property(
+        type=Playlist, default=None, flags=GObject.ParamFlags.READABLE)
+    def current_playlist(self):
         selection = self._sidebar.get_selected_row()
-        selected_playlist = selection.props.playlist
-
-        notification = PlaylistNotification(  # noqa: F841
-            self._window.notifications_popup, self._coremodel,
-            PlaylistNotification.Type.SONG, selected_playlist, position,
-            coresong)
+        if selection is None:
+            return None
+        return selection.props.playlist
 
     @Gtk.Template.Callback()
     @log
     def _on_playlist_activated(self, sidebar, row):
         """Update view with content from selected playlist"""
-        playlist = row.props.playlist
-
-        self._view.bind_model(
-            playlist.props.model, self._create_song_widget, playlist)
-
-        self._pl_ctrls.props.playlist = playlist
-
-        self._remove_song_action.set_enabled(not playlist.props.is_smart)
+        self.notify("current-playlist")
 
     def _on_active_playlist_changed(self, klass, val):
         """Selects the active playlist when an MPRIS client
@@ -206,48 +136,7 @@ class PlaylistsView(Gtk.Stack):
         self._sidebar.select_row(playlist_row)
         self._on_playlist_activated(self._sidebar, playlist_row)
 
-    def _create_song_widget(self, coresong, playlist):
-        can_dnd = not playlist.props.is_smart
-        song_widget = SongWidget(coresong, can_dnd, True)
-        song_widget.props.show_song_number = False
-
-        song_widget.connect('button-release-event', self._song_activated)
-        if can_dnd is True:
-            song_widget.connect("widget_moved", self._on_song_widget_moved)
-
-        return song_widget
-
-    def _song_activated(self, widget=None, event=None):
-        coresong = None
-        if widget is not None:
-            coresong = widget.props.coresong
-
-        signal_id = None
-
-        def _on_playlist_loaded(klass, playlist_type):
-            self._coremodel.props.active_playlist = current_playlist
-            self._player.play(coresong)
-            self._coremodel.disconnect(signal_id)
-
-        selection = self._sidebar.get_selected_row()
-        current_playlist = selection.props.playlist
-        signal_id = self._coremodel.connect(
-            "playlist-loaded", _on_playlist_loaded)
-        self._coremodel.set_player_model(
-            PlayerPlaylist.Type.PLAYLIST, current_playlist.props.model)
-
-        return True
-
-    def _on_play_playlist(self, menuitem, data=None):
-        self._song_activated()
-
     @GObject.Property(type=bool, default=False)
     def rename_active(self):
         """Indicate if renaming dialog is active"""
-        return self._pl_ctrls.props.rename_active
-
-    def _on_song_widget_moved(self, target, source_position):
-        target_position = target.get_parent().get_index()
-        selection = self._sidebar.get_selected_row()
-        current_playlist = selection.props.playlist
-        current_playlist.reorder(source_position, target_position)
+        return self._playlist_widget.props.rename_active
diff --git a/gnomemusic/widgets/playlistswidget.py b/gnomemusic/widgets/playlistswidget.py
new file mode 100644
index 00000000..618564f3
--- /dev/null
+++ b/gnomemusic/widgets/playlistswidget.py
@@ -0,0 +1,180 @@
+# Copyright 2020 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 gi.repository import Gdk, GObject, Gtk
+
+from gnomemusic.player import PlayerPlaylist
+from gnomemusic.widgets.notificationspopup import PlaylistNotification
+from gnomemusic.widgets.playlistcontextmenu import PlaylistContextMenu
+from gnomemusic.widgets.playlistcontrols import PlaylistControls  # noqa: F401
+from gnomemusic.widgets.playlistdialog import PlaylistDialog
+from gnomemusic.widgets.songwidget import SongWidget
+
+
+@Gtk.Template(resource_path="/org/gnome/Music/ui/PlaylistsWidget.ui")
+class PlaylistsWidget(Gtk.Box):
+    """Widget to display playlist content a list"""
+
+    __gtype_name__ = "PlaylistsWidget"
+
+    _pl_ctrls = Gtk.Template.Child()
+    _songs_list = Gtk.Template.Child()
+    _songs_list_ctrlr = Gtk.Template.Child()
+
+    def __init__(self, application, parent_view):
+        """Initialize the PlaylistsWidget.
+
+        :param Application application: The application object
+        :param PlaylistsView parent_view: The view this widget is part of
+        """
+        super().__init__()
+
+        self._window = application.props.window
+        self._coremodel = application.props.coremodel
+        self._player = application.props.player
+        self._parent_view = parent_view
+
+        self._parent_view.connect(
+            "notify::current-playlist", self._on_current_playlist_changed)
+
+        self._pl_ctrls.props.application = application
+
+        self._song_popover = PlaylistContextMenu(application, self._songs_list)
+
+        play_song = self._window.lookup_action("play_song")
+        play_song.connect("activate", self._play_song)
+
+        add_song = self._window.lookup_action("add_song_to_playlist")
+        add_song.connect("activate", self._add_song_to_playlist)
+
+        self._remove_song_action = self._window.lookup_action("remove_song")
+        self._remove_song_action.connect(
+            "activate", self._stage_song_for_deletion)
+
+        playlist_play_action = self._window.lookup_action("playlist_play")
+        playlist_play_action.connect("activate", self._on_play_playlist)
+
+    def _on_current_playlist_changed(self, parent_view, value):
+        """Update view with content from selected playlist"""
+        playlist = self._parent_view.props.current_playlist
+
+        self._songs_list.bind_model(
+            playlist.props.model, self._create_song_widget, playlist)
+
+        self._pl_ctrls.props.playlist = playlist
+
+        self._remove_song_action.set_enabled(not playlist.props.is_smart)
+
+    def _create_song_widget(self, coresong, playlist):
+        can_dnd = not playlist.props.is_smart
+        song_widget = SongWidget(coresong, can_dnd, True)
+        song_widget.props.show_song_number = False
+
+        song_widget.connect("button-release-event", self._on_song_activated)
+        if can_dnd is True:
+            song_widget.connect("widget_moved", self._on_song_widget_moved)
+
+        return song_widget
+
+    def _on_song_activated(self, widget, event):
+        coresong = widget.props.coresong
+        self._play(coresong)
+        return True
+
+    def _play(self, coresong=None):
+        signal_id = None
+
+        def _on_playlist_loaded(klass, playlist_type):
+            self._coremodel.props.active_playlist = current_playlist
+            self._player.play(coresong)
+            self._coremodel.disconnect(signal_id)
+
+        current_playlist = self._parent_view.props.current_playlist
+        signal_id = self._coremodel.connect(
+            "playlist-loaded", _on_playlist_loaded)
+        self._coremodel.set_player_model(
+            PlayerPlaylist.Type.PLAYLIST, current_playlist.props.model)
+
+    def _on_song_widget_moved(self, target, source_position):
+        target_position = target.get_parent().get_index()
+        selection = self._sidebar.get_selected_row()
+        current_playlist = selection.props.playlist
+        current_playlist.reorder(source_position, target_position)
+
+    @Gtk.Template.Callback()
+    def _songs_list_right_click(self, gesture, n_press, x, y):
+        requested_row = self._songs_list.get_row_at_y(y)
+        self._songs_list.select_row(requested_row)
+
+        _, y0 = requested_row.translate_coordinates(self._songs_list, 0, 0)
+        row_height = requested_row.get_allocated_height()
+        rect = Gdk.Rectangle()
+        rect.x = x
+        rect.y = y0 + 0.5 * row_height
+
+        self._song_popover.props.relative_to = self._songs_list
+        self._song_popover.props.pointing_to = rect
+        self._song_popover.popup()
+
+    def _play_song(self, menuitem, data=None):
+        selected_row = self._songs_list.get_selected_row()
+        song_widget = selected_row.get_child()
+        coresong = song_widget.props.coresong
+        self._songs_list.unselect_all()
+        self._play(coresong)
+
+    def _add_song_to_playlist(self, menuitem, data=None):
+        selected_row = self._songs_list.get_selected_row()
+        song_widget = selected_row.get_child()
+        coresong = song_widget.props.coresong
+
+        playlist_dialog = PlaylistDialog(self._window)
+        if playlist_dialog.run() == Gtk.ResponseType.ACCEPT:
+            playlist = playlist_dialog.props.selected_playlist
+            playlist.add_songs([coresong])
+
+        self._songs_list.unselect_all()
+        playlist_dialog.destroy()
+
+    def _stage_song_for_deletion(self, menuitem, data=None):
+        selected_row = self._songs_list.get_selected_row()
+        position = selected_row.get_index()
+        song_widget = selected_row.get_child()
+        coresong = song_widget.props.coresong
+
+        current_playlist = self._parent_view.props.current_playlist
+
+        notification = PlaylistNotification(  # noqa: F841
+            self._window.notifications_popup, self._coremodel,
+            PlaylistNotification.Type.SONG, current_playlist, position,
+            coresong)
+
+    def _on_play_playlist(self, menuitem, data=None):
+        self._play()
+
+    @GObject.Property(
+        type=bool, default=False, flags=GObject.ParamFlags.READABLE)
+    def rename_active(self):
+        """Indicate if renaming dialog is active"""
+        return self._pl_ctrls.props.rename_active


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]