[gnome-music] playlistview: Use Gtk.Template



commit cf34619ead036650838ea73d6104e69b926d2736
Author: Apostol Bakalov <apogza gmail com>
Date:   Sun Jul 29 07:23:46 2018 +0000

    playlistview: Use Gtk.Template

 data/PlaylistContextMenu.ui               |  30 ++++---
 data/PlaylistControls.ui                  | 104 ++++++++++++------------
 gnomemusic/views/playlistview.py          | 127 +++++++++--------------------
 gnomemusic/widgets/playlistcontextmenu.py |  45 +++++++++++
 gnomemusic/widgets/playlistcontrols.py    | 130 ++++++++++++++++++++++++++++++
 po/POTFILES.in                            |   1 +
 6 files changed, 283 insertions(+), 154 deletions(-)
---
diff --git a/data/PlaylistContextMenu.ui b/data/PlaylistContextMenu.ui
index 7be19bb8..107ba577 100644
--- a/data/PlaylistContextMenu.ui
+++ b/data/PlaylistContextMenu.ui
@@ -1,17 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <menu id="song_menu">
-      <item>
-        <attribute name="label" translatable="yes">Play</attribute>
-        <attribute name="action">win.play_song</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">Add to Playlist…</attribute>
-        <attribute name="action">win.add_song_to_playlist</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">Remove From Playlist</attribute>
-        <attribute name="action">win.remove_song</attribute>
-      </item>
+  <menu id="_song_menu">
+    <item>
+      <attribute name="label" translatable="yes">Play</attribute>
+      <attribute name="action">win.play_song</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">Add to Playlist…</attribute>
+      <attribute name="action">win.add_song_to_playlist</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">Remove From Playlist</attribute>
+      <attribute name="action">win.remove_song</attribute>
+    </item>
   </menu>
+  <template class="PlaylistContextMenu" parent="GtkPopover">
+    <property name="position">3</property>
+  </template>
 </interface>
+
diff --git a/data/PlaylistControls.ui b/data/PlaylistControls.ui
index 76302951..64456fdd 100644
--- a/data/PlaylistControls.ui
+++ b/data/PlaylistControls.ui
@@ -15,7 +15,7 @@
       <attribute name="action">win.playlist_rename</attribute>
     </item>
   </menu>
-  <object class="GtkGrid" id="grid">
+  <template class="PlaylistControls" parent="GtkGrid">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="margin_start">18</property>
@@ -23,59 +23,63 @@
     <property name="margin_top">18</property>
     <property name="margin_bottom">18</property>
     <child>
-      <object class="GtkStack" id="stack">
-    <child>
-      <object class="GtkLabel" id="playlist_name">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="hexpand">True</property>
-        <property name="xalign">0</property>
-        <property name="label" translatable="yes">Playlist Name</property>
-        <property name="ellipsize">middle</property>
-        <style>
-          <class name="playlist-name-label"/>
-        </style>
-      </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>
+      <object class="GtkStack" id="_name_stack">
         <child>
-          <object class="GtkEntry" id="playlist_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>
+          <object class="GtkLabel" id="_name_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Playlist Name</property>
+            <property name="ellipsize">middle</property>
+            <style>
+              <class name="playlist-name-label"/>
+            </style>
           </object>
         </child>
         <child>
-          <object class="GtkButton" id="playlist_rename_done_button">
-        <property name="visible">True</property>
-        <property name="no_show_all">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="valign">center</property>
-        <property name="sensitive">True</property>
-        <style>
-          <class name="suggested-action"/>
-        </style>
+          <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"/>
+                <signal name="key-press-event" handler="_on_rename_entry_key_pressed" swapped="no"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="_rename_done_button">
+                <property name="visible">True</property>
+                <property name="no_show_all">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="valign">center</property>
+                <property name="sensitive">True</property>
+                <signal name="clicked" handler="_on_playlist_renamed" swapped="no" />
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+            </child>
           </object>
+          <packing>
+            <property name="name">renaming_dialog</property>
+          </packing>
         </child>
       </object>
-      <packing>
-        <property name="name">renaming_dialog</property>
-      </packing>
-    </child>
-      </object>
       <packing>
         <property name="left_attach">0</property>
         <property name="top_attach">0</property>
@@ -84,7 +88,7 @@
       </packing>
     </child>
     <child>
-      <object class="GtkLabel" id="songs_count">
+      <object class="GtkLabel" id="_songs_count_label">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="xalign">0</property>
@@ -101,7 +105,7 @@
       </packing>
     </child>
     <child>
-      <object class="GtkMenuButton" id="playlist_menubutton">
+      <object class="GtkMenuButton" id="_menubutton">
         <property name="visible">True</property>
         <property name="can_focus">True</property>
         <property name="receives_default">True</property>
@@ -123,5 +127,5 @@
         <property name="height">2</property>
       </packing>
     </child>
-  </object>
+  </template>
 </interface>
diff --git a/gnomemusic/views/playlistview.py b/gnomemusic/views/playlistview.py
index 9ed93ec7..c357d925 100644
--- a/gnomemusic/views/playlistview.py
+++ b/gnomemusic/views/playlistview.py
@@ -22,8 +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 gettext import gettext as _, ngettext
-from gi.repository import Gio, GLib, GObject, Gtk, Gdk, Pango
+from gettext import gettext as _
+
+from gi.repository import Gio, GLib, GObject, Gtk, Pango
 
 from gnomemusic import log
 from gnomemusic.grilo import grilo
@@ -31,6 +32,8 @@ from gnomemusic.player import DiscoveryStatus
 from gnomemusic.playlists import Playlists, StaticPlaylists
 from gnomemusic.views.baseview import BaseView
 from gnomemusic.widgets.notificationspopup import PlaylistNotification
+from gnomemusic.widgets.playlistcontextmenu import PlaylistContextMenu
+from gnomemusic.widgets.playlistcontrols import PlaylistControls
 from gnomemusic.widgets.playlistdialog import PlaylistDialog
 import gnomemusic.utils as utils
 
@@ -69,26 +72,10 @@ class PlaylistView(BaseView):
 
         self._add_list_renderers()
 
-        builder = Gtk.Builder()
-        builder.add_from_resource('/org/gnome/Music/PlaylistControls.ui')
-        headerbar = builder.get_object('grid')
-        self._name_stack = builder.get_object('stack')
-        self._name_label = builder.get_object('playlist_name')
-        self._rename_entry = builder.get_object('playlist_rename_entry')
-        self._rename_entry.connect('changed', self._on_rename_entry_changed)
-        self._rename_entry.connect(
-            'key-press-event', self._on_rename_entry_key_pressed)
-        self._rename_done_button = builder.get_object(
-            'playlist_rename_done_button')
-        self._songs_count_label = builder.get_object('songs_count')
-        self._menubutton = builder.get_object('playlist_menubutton')
-
-        builder = Gtk.Builder()
-        builder.add_from_resource('/org/gnome/Music/PlaylistContextMenu.ui')
-        self._popover_menu = builder.get_object('song_menu')
-        self._song_popover = Gtk.Popover.new_from_model(
-            self._view, self._popover_menu)
-        self._song_popover.set_position(Gtk.PositionType.BOTTOM)
+        self._pl_ctrls = PlaylistControls()
+        self._pl_ctrls.connect('playlist-renamed', self._on_playlist_renamed)
+
+        self._song_popover = PlaylistContextMenu(self._view)
 
         play_song = Gio.SimpleAction.new('play_song', None)
         play_song.connect('activate', self._play_song)
@@ -120,7 +107,7 @@ class PlaylistView(BaseView):
         self._window.add_action(self._playlist_rename_action)
 
         self._grid.insert_row(0)
-        self._grid.attach(headerbar, 1, 0, 1, 1)
+        self._grid.attach(self._pl_ctrls, 1, 0, 1, 1)
 
         sidebar_container.set_size_request(220, -1)
         sidebar_container.get_style_context().add_class('side-panel')
@@ -138,11 +125,7 @@ class PlaylistView(BaseView):
         self._current_playlist_index = None
         self.pls_todelete = {}
         self._songs_todelete = {}
-        self._songs_count = 0
-        self._handler_rename_done_button = 0
-        self._handler_rename_entry = 0
-
-        self._update_songs_count()
+        self._update_songs_count(0)
 
         self.model.connect('row-inserted', self._on_song_inserted)
         self.model.connect('row-deleted', self._on_song_deleted)
@@ -156,8 +139,9 @@ class PlaylistView(BaseView):
         self.show_all()
 
     @log
-    def _on_changes_pending(self, data=None):
-        pass
+    def _update_songs_count(self, songs_count):
+        self._songs_count = songs_count
+        self._pl_ctrls.update_songs_count(songs_count)
 
     @log
     def _setup_view(self, view_type):
@@ -373,7 +357,6 @@ class PlaylistView(BaseView):
         self._song_popover.set_relative_to(self._view)
         self._song_popover.set_pointing_to(rect)
         self._song_popover.popup()
-        return
 
     @log
     def _drag_begin(self, widget_, drag_context):
@@ -539,10 +522,10 @@ class PlaylistView(BaseView):
         playlist_name = utils.get_media_title(playlist)
 
         if self.rename_active:
-            self.disable_rename_playlist()
+            self._pl_ctrls.disable_rename_playlist()
 
         self._current_playlist = playlist
-        self._name_label.set_text(playlist_name)
+        self._pl_ctrls.props.playlist_name = playlist_name
         self._current_playlist_index = row.get_index()
 
         # if the active queue has been set by this playlist,
@@ -551,7 +534,7 @@ class PlaylistView(BaseView):
         self.model.clear()
         self._iter_to_clean = None
         self._iter_to_clean_model = None
-        self._songs_count = 0
+        self._update_songs_count(0)
         grilo.populate_playlist_songs(playlist, self._add_song)
 
         if self._current_playlist_is_protected():
@@ -588,7 +571,6 @@ class PlaylistView(BaseView):
         :param Gtk.ListStore model: model
         """
         if not song:
-            self._update_songs_count()
             self.emit('playlist-songs-loaded')
             return None
 
@@ -599,15 +581,9 @@ class PlaylistView(BaseView):
             index, [2, 3, 5, 9],
             [title, artist, song, song.get_favourite()])
 
-        self._songs_count += 1
+        self._update_songs_count(self._songs_count + 1)
         return iter_
 
-    @log
-    def _update_songs_count(self):
-        self._songs_count_label.set_text(
-            ngettext("%d Song", "%d Songs", self._songs_count)
-            % self._songs_count)
-
     @log
     def _on_play_activate(self, menuitem, data=None):
         _iter = self.model.get_iter_first()
@@ -622,18 +598,15 @@ class PlaylistView(BaseView):
     @log
     def _current_playlist_is_protected(self):
         current_playlist_id = self._current_playlist.get_id()
-        if current_playlist_id in StaticPlaylists().get_ids():
-            return True
-        else:
-            return False
+        return current_playlist_id in StaticPlaylists().get_ids()
 
     @log
     def _is_current_playlist(self, playlist):
         """Check if playlist is currently displayed"""
-        if (self._current_playlist
-                and playlist.get_id() == self._current_playlist.get_id()):
-            return True
-        return False
+        if self._current_playlist is None:
+            return False
+
+        return playlist.get_id() == self._current_playlist.get_id()
 
     @log
     def _get_removal_notification_message(self, type_, media_id):
@@ -717,7 +690,6 @@ class PlaylistView(BaseView):
                         'Playlist', self._current_playlist.get_id()):
                     path = self.model.get_path(iter_)
                     self.player.add_song(self.model, path, iter_)
-                self._update_songs_count()
             self._songs_todelete.pop(media_id)
 
     @log
@@ -739,47 +711,22 @@ class PlaylistView(BaseView):
     @GObject.Property(type=bool, default=False)
     def rename_active(self):
         """Indicate if renaming dialog is active"""
-        return self._name_stack.get_visible_child_name() == 'renaming_dialog'
-
-    @log
-    def _on_rename_entry_changed(self, selection):
-        self._rename_done_button.set_sensitive(selection.get_text_length() > 0)
-
-    @log
-    def _on_rename_entry_key_pressed(self, widget, event):
-        if event.keyval == Gdk.KEY_Escape:
-            self.disable_rename_playlist()
-
-    @log
-    def disable_rename_playlist(self):
-        """disables rename button and entry"""
-        self._name_stack.set_visible_child(self._name_label)
-        self._rename_done_button.disconnect(self._handler_rename_done_button)
-        self._rename_entry.disconnect(self._handler_rename_entry)
+        return self._pl_ctrls.props.rename_active
 
     @log
     def _stage_playlist_for_renaming(self, menuitem, data=None):
         selection = self._sidebar.get_selected_row()
         pl_torename = selection.playlist
+        self._pl_ctrls.enable_rename_playlist(pl_torename)
 
-        def playlist_renamed_callback(widget):
-            new_name = self._rename_entry.get_text()
-            if not new_name:
-                return
-
-            selection.get_child().set_text(new_name)
-            pl_torename.set_title(new_name)
-            playlists.rename(pl_torename, new_name)
-            self._name_label.set_text(new_name)
-            self.disable_rename_playlist()
+    @log
+    def _on_playlist_renamed(self, arguments, new_name):
+        selection = self._sidebar.get_selected_row()
+        selection.get_child().props.label = new_name
 
-        self._name_stack.set_visible_child_name('renaming_dialog')
-        self._rename_entry.set_text(utils.get_media_title(pl_torename))
-        self._rename_entry.grab_focus()
-        self._handler_rename_entry = self._rename_entry.connect(
-            'activate', playlist_renamed_callback)
-        self._handler_rename_done_button = self._rename_done_button.connect(
-            'clicked', playlist_renamed_callback)
+        pl_torename = selection.playlist
+        pl_torename.set_title(new_name)
+        playlists.rename(pl_torename, new_name)
 
     @log
     def _on_playlist_created(self, playlists, playlist):
@@ -797,20 +744,18 @@ class PlaylistView(BaseView):
 
     @log
     def _remove_song_from_playlist(self, playlist, item, index):
-        if (self._is_current_playlist(playlist)):
-            model = self.model
-        else:
+        if not self._is_current_playlist(playlist):
             return
 
+        model = self.model
+
         iter_ = model.get_iter_from_string(str(index))
         if self.player.playing_playlist(
                 'Playlist', self._current_playlist.get_id()):
             self.player.remove_song(model, model.get_path(iter_))
         model.remove(iter_)
 
-        self._songs_count -= 1
-        self._update_songs_count()
-        return
+        self._update_songs_count(self._songs_count - 1)
 
     @log
     def populate(self):
diff --git a/gnomemusic/widgets/playlistcontextmenu.py b/gnomemusic/widgets/playlistcontextmenu.py
new file mode 100644
index 00000000..dfab7b9a
--- /dev/null
+++ b/gnomemusic/widgets/playlistcontextmenu.py
@@ -0,0 +1,45 @@
+# Copyright 2018 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 Gtk
+
+from gnomemusic import log
+
+
+@Gtk.Template(resource_path='/org/gnome/Music/PlaylistContextMenu.ui')
+class PlaylistContextMenu(Gtk.Popover):
+
+    __gtype_name__ = 'PlaylistContextMenu'
+
+    _song_menu = Gtk.Template.Child()
+
+    def __repr__(self):
+        return '<PlaylistContextMenu>'
+
+    @log
+    def __init__(self, view):
+        super().__init__()
+
+        self.props.relative_to = view
+        self.bind_model(self._song_menu, None)
diff --git a/gnomemusic/widgets/playlistcontrols.py b/gnomemusic/widgets/playlistcontrols.py
new file mode 100644
index 00000000..882d9c20
--- /dev/null
+++ b/gnomemusic/widgets/playlistcontrols.py
@@ -0,0 +1,130 @@
+# Copyright 2018 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.
+
+import gettext
+
+from gi.repository import Gdk, GObject, Gtk
+
+from gnomemusic import log
+import gnomemusic.utils as utils
+
+
+@Gtk.Template(resource_path='/org/gnome/Music/PlaylistControls.ui')
+class PlaylistControls(Gtk.Grid):
+    """Widget holding the playlist controls"""
+
+    __gsignals__ = {
+        'playlist-renamed': (GObject.SignalFlags.RUN_FIRST, None, (str,))
+    }
+
+    __gtype_name__ = "PlaylistControls"
+
+    _name_stack = Gtk.Template.Child()
+    _name_label = Gtk.Template.Child()
+    _rename_entry = Gtk.Template.Child()
+    _rename_done_button = Gtk.Template.Child()
+    _songs_count_label = Gtk.Template.Child()
+    _menubutton = Gtk.Template.Child()
+
+    playlist_name = GObject.Property(
+        type=str, default="", flags=GObject.ParamFlags.READWRITE)
+
+    def __repr__(self):
+        return '<PlaylistControls>'
+
+    def __init__(self):
+        super().__init__()
+        self.bind_property("playlist-name", self._name_label, "label")
+
+    @Gtk.Template.Callback()
+    @log
+    def _on_rename_entry_changed(self, selection):
+        selection_length = selection.props.text_length
+        self._rename_done_button.props.sensitive = selection_length > 0
+
+    @Gtk.Template.Callback()
+    @log
+    def _on_rename_entry_key_pressed(self, widget, event):
+        if event.keyval == Gdk.KEY_Escape:
+            self.disable_rename_playlist()
+
+    @Gtk.Template.Callback()
+    @log
+    def _on_playlist_renamed(self, widget):
+        new_name = self._rename_entry.props.text
+
+        if not new_name:
+            return
+
+        self.props.playlist_name = new_name
+        self.disable_rename_playlist()
+        self.emit('playlist-renamed', new_name)
+
+    @log
+    def enable_rename_playlist(self, pl_torename):
+        """Enables rename button and entry
+
+        :param Grl.Media pl_torename : The playlist to rename
+        """
+        self._name_stack.props.visible_child_name = "renaming_dialog"
+        self._set_rename_entry_text_and_focus(
+            utils.get_media_title(pl_torename))
+
+    @log
+    def disable_rename_playlist(self):
+        """Disables rename button and entry"""
+        self._name_stack.props.visible_child = self._name_label
+
+    @log
+    def update_songs_count(self, songs_count):
+        """Updates the number of songs label
+
+        :param int songs_count: The number of songs in the playlist
+        """
+        self._songs_count_label.props.label = gettext.ngettext(
+            "{} Song", "{} Songs", songs_count).format(songs_count)
+
+    @GObject.Property(type=bool, default=False)
+    def rename_active(self):
+        """Indicate if renaming dialog is active
+
+        :return: Renaming dialog active
+        :rtype: bool
+        """
+
+        return self._name_stack.props.visible_child_name == "renaming_dialog"
+
+    @GObject.Property
+    def rename_entry_text(self):
+        """Gets the value of the rename entry input
+
+        :return: Contents of rename entry field
+        :rtype: string
+        """
+        return self._rename_entry.props.text
+
+    @log
+    def _set_rename_entry_text_and_focus(self, text):
+        self._rename_entry.props.text = text
+        self._rename_entry.grab_focus()
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7b70f3db..f18a2eb5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -26,6 +26,7 @@ gnomemusic/widgets/disclistboxwidget.py
 gnomemusic/widgets/headerbar.py
 gnomemusic/widgets/notificationspopup.py
 gnomemusic/widgets/playertoolbar.py
+gnomemusic/widgets/playlistcontrols.py
 gnomemusic/widgets/playlistdialog.py
 gnomemusic/widgets/starhandlerwidget.py
 gnomemusic/window.py


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