[gnome-music/wip/jfelder/playlistsview-template: 2/3] playlistsview: Factor out playlist list widget
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/playlistsview-template: 2/3] playlistsview: Factor out playlist list widget
- Date: Sat, 1 Feb 2020 16:21:16 +0000 (UTC)
commit ca7368526acaca1e5d8aff86afcf145a4208b89e
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 | 179 ++++++++++++++++++++++++++++++++++
5 files changed, 231 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 50598ab8..6c7b67f3 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>'
@@ -72,22 +66,8 @@ class PlaylistsView(Gtk.Stack):
# had no user interaction since.
self._untouched_list = True
- 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)
-
- playlist_play_action = self._window.lookup_action("playlist_play")
- playlist_play_action.connect("activate", self._on_play_playlist)
+ self._playlist_widget = PlaylistsWidget(application, self)
+ self._main_container.add(self._playlist_widget)
self._sidebar.bind_model(self._model, self._add_playlist_to_sidebar)
@@ -126,56 +106,13 @@ class PlaylistsView(Gtk.Stack):
self._sidebar.select_row(row_next)
self._on_playlist_activated(self._sidebar, row_next, True)
- @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
@@ -184,14 +121,7 @@ class PlaylistsView(Gtk.Stack):
if untouched is False:
self._untouched_list = False
- 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
@@ -215,48 +145,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._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.props.active_playlist = current_playlist
- 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..1bff21d3
--- /dev/null
+++ b/gnomemusic/widgets/playlistswidget.py
@@ -0,0 +1,179 @@
+# 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, playlists_view):
+ """Initialize the PlaylistsWidget.
+
+ :param Application application: The application object
+ :param PlaylistsView playlists_view: The parent view
+ """
+ super().__init__()
+
+ self._window = application.props.window
+ self._coremodel = application.props.coremodel
+ self._player = application.props.player
+ self._playlists_view = playlists_view
+
+ self._playlists_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, playlists_view, value):
+ """Update view with content from selected playlist"""
+ playlist = self._playlists_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._player.play(coresong)
+ self._coremodel.disconnect(signal_id)
+
+ current_playlist = self._playlists_view.props.current_playlist
+ signal_id = self._coremodel.connect(
+ "playlist-loaded", _on_playlist_loaded)
+ self._coremodel.props.active_playlist = current_playlist
+ 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()
+ current_playlist = self._playlists_view.props.current_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._playlists_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]