[gnome-music/wip/feborges/playback-popover] player: Introduce the Playback Popover



commit 27db20988296969e9775f89fe3fcc9c6594a53ee
Author: Felipe Borges <felipeborges gnome org>
Date:   Sun Jan 28 20:36:37 2018 +0100

    player: Introduce the Playback Popover
    
    This widget allows the user to browse through the app without
    losing track of the current playlist (what's playing next).
    
    It is based this wireframes
    https://raw.githubusercontent.com/gnome-design-team/gnome-mockups/
    master/music/wire-playback-buffer.png
    
    This feature has been worked on Outreachy by Jordana Luft, but
    that branch differs entirely from the current state of the code
    base. Its been two years.
    
    Fixes #14

 data/PlaybackPopover.ui               |  39 +++++++++++++
 data/PlayerToolbar.ui                 | 102 +++++++++++++++++---------------
 data/gnome-music.gresource.xml        |   1 +
 gnomemusic/player.py                  |   8 +++
 gnomemusic/widgets/Makefile.am        |   1 +
 gnomemusic/widgets/playbackpopover.py | 106 ++++++++++++++++++++++++++++++++++
 6 files changed, 210 insertions(+), 47 deletions(-)
---
diff --git a/data/PlaybackPopover.ui b/data/PlaybackPopover.ui
new file mode 100644
index 0000000..e16d96b
--- /dev/null
+++ b/data/PlaybackPopover.ui
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gnome-music">
+  <requires lib="gtk+" version="3.12"/>
+  <object class="GtkBox" id="playback_popover">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="width-request">480</property>
+    <property name="height-request">360</property>
+    <child>
+      <object class="GtkHeaderBar">
+        <property name="visible">True</property>
+        <property name="title" translatable="yes">Now playing</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkStack">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">True</property>
+            <property name="hscrollbar_policy">never</property>
+            <child>
+              <object class="GtkListBox" id="playback_popover_list">
+                <property name="visible">True</property>
+                <property name="border_width">5</property>
+                <style>
+                  <class name="background"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/data/PlayerToolbar.ui b/data/PlayerToolbar.ui
index 53028c6..ce5d5c5 100644
--- a/data/PlayerToolbar.ui
+++ b/data/PlayerToolbar.ui
@@ -115,65 +115,78 @@
       </object>
     </child>
     <child>
-      <object class="GtkBox" id="nowplaying">
+      <object class="GtkToggleButton" id="nowplaying_button">
         <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="halign">center</property>
-        <property name="valign">center</property>
-        <property name="spacing">8</property>
-        <child>
-          <object class="GtkImage" id="cover">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
+        <style>
+          <class name="flat"/>
+        </style>
         <child>
-          <object class="GtkBox" id="nowplaying_labels">
+          <object class="GtkBox" id="nowplaying">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="halign">center</property>
             <property name="valign">center</property>
-            <property name="orientation">vertical</property>
-            <property name="homogeneous">True</property>
+            <property name="spacing">8</property>
             <child>
-              <object class="GtkLabel" id="artist">
+              <object class="GtkImage" id="cover">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="halign">start</property>
-                <property name="valign">start</property>
-                <property name="xalign">0</property>
-                <property name="ellipsize">middle</property>
-                <property name="width_chars">8</property>
-                <property name="max_width_chars">42</property>
-                <attributes>
-                  <attribute name="weight" value="bold"/>
-                  <attribute name="scale" value="0.90000000000000002"/>
-                </attributes>
               </object>
               <packing>
                 <property name="expand">False</property>
-                <property name="fill">False</property>
+                <property name="fill">True</property>
                 <property name="position">0</property>
               </packing>
             </child>
             <child>
-              <object class="GtkLabel" id="title">
+              <object class="GtkBox" id="nowplaying_labels">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="halign">start</property>
-                <property name="valign">start</property>
-                <property name="xalign">0</property>
-                <property name="ellipsize">middle</property>
-                <property name="width_chars">8</property>
-                <property name="max_width_chars">42</property>
-                <attributes>
-                  <attribute name="scale" value="0.90000000000000002"/>
-                </attributes>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="orientation">vertical</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <object class="GtkLabel" id="artist">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="valign">start</property>
+                    <property name="xalign">0</property>
+                    <property name="ellipsize">middle</property>
+                    <property name="width_chars">8</property>
+                    <property name="max_width_chars">42</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                      <attribute name="scale" value="0.90000000000000002"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="title">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="valign">start</property>
+                    <property name="xalign">0</property>
+                    <property name="ellipsize">middle</property>
+                    <property name="width_chars">8</property>
+                    <property name="max_width_chars">42</property>
+                    <attributes>
+                      <attribute name="scale" value="0.90000000000000002"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -182,11 +195,6 @@
               </packing>
             </child>
           </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">1</property>
-          </packing>
         </child>
       </object>
     </child>
@@ -290,7 +298,7 @@
                     <property name="icon_size">1</property>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
+                      <property name="expand">False</property>
                     <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
diff --git a/data/gnome-music.gresource.xml b/data/gnome-music.gresource.xml
index d98bb47..ffcbb16 100644
--- a/data/gnome-music.gresource.xml
+++ b/data/gnome-music.gresource.xml
@@ -15,6 +15,7 @@
     <file preprocess="xml-stripblanks">headerbar.ui</file>
     <file preprocess="xml-stripblanks">TrackWidget.ui</file>
     <file preprocess="xml-stripblanks">NoMusic.ui</file>
+    <file preprocess="xml-stripblanks">PlaybackPopover.ui</file>
     <file preprocess="xml-stripblanks">PlaylistContextMenu.ui</file>
     <file preprocess="xml-stripblanks">PlaylistControls.ui</file>
     <file preprocess="xml-stripblanks">PlaylistDialog.ui</file>
diff --git a/gnomemusic/player.py b/gnomemusic/player.py
index e0f98e5..b6eb6f7 100644
--- a/gnomemusic/player.py
+++ b/gnomemusic/player.py
@@ -50,6 +50,7 @@ from gnomemusic.albumartcache import AlbumArtCache, DefaultIcon, ArtSize
 from gnomemusic.grilo import grilo
 from gnomemusic.playlists import Playlists
 from gnomemusic.scrobbler import LastFmScrobbler
+from gnomemusic.widgets.playbackpopover import PlaybackPopover
 import gnomemusic.utils as utils
 
 
@@ -775,6 +776,8 @@ class Player(GObject.GObject):
         self.nextBtn = self._ui.get_object('next_button')
         self._playImage = self._ui.get_object('play_image')
         self._pauseImage = self._ui.get_object('pause_image')
+        self._nowplayingButton = self._ui.get_object('nowplaying_button')
+        self._playbackPopover = PlaybackPopover(self._nowplayingButton, self)
         self.progressScale = self._ui.get_object('progress_scale')
         self.songPlaybackTimeLabel = self._ui.get_object('playback')
         self.songTotalTimeLabel = self._ui.get_object('duration')
@@ -792,6 +795,7 @@ class Player(GObject.GObject):
         self.prevBtn.connect('clicked', self._on_prev_btn_clicked)
         self.playBtn.connect('clicked', self._on_play_btn_clicked)
         self.nextBtn.connect('clicked', self._on_next_btn_clicked)
+        self._playbackPopover.connect('current-changed', self._on_playback_popover_current_changed)
         self.progressScale.connect('button-press-event', self._on_progress_scale_event)
         self.progressScale.connect('value-changed', self._on_progress_value_changed)
         self.progressScale.connect('button-release-event', self._on_progress_scale_button_released)
@@ -802,6 +806,10 @@ class Player(GObject.GObject):
         self._old_progress_scale_value = 0.0
         self.progressScale.set_increments(300, 600)
 
+    def _on_playback_popover_current_changed(self, popover, iter):
+        self.currentTrack = Gtk.TreeRowReference.new(self.playlist, self.playlist.get_path(iter))
+        self.play()
+
     def _on_progress_scale_seek_finish(self, value):
         """Prevent stutters when seeking with infinitesimal amounts"""
         self._seek_timeout = None
diff --git a/gnomemusic/widgets/Makefile.am b/gnomemusic/widgets/Makefile.am
index e02db58..9deffc2 100644
--- a/gnomemusic/widgets/Makefile.am
+++ b/gnomemusic/widgets/Makefile.am
@@ -6,5 +6,6 @@ app_PYTHON = \
        artistalbumswidget.py \
        artistalbumwidget.py \
        disclistboxwidget.py \
+       playbackpopover.py \
        playlistdialog.py \
        starhandlerwidget.py
diff --git a/gnomemusic/widgets/playbackpopover.py b/gnomemusic/widgets/playbackpopover.py
new file mode 100644
index 0000000..a144ee8
--- /dev/null
+++ b/gnomemusic/widgets/playbackpopover.py
@@ -0,0 +1,106 @@
+# Copyright (C) 2018 Felipe Borges felipeborges gnome org
+#
+# 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 Gio, GObject, Gtk
+
+from gnomemusic.albumartcache import ArtSize
+import gnomemusic.utils as utils
+
+class PlaybackItem(GObject.Object):
+    def __init__(self, data, iter):
+        super().__init__()
+
+        self.media = data[5]
+        self.iter = iter
+        self.title = utils.get_media_title(self.media)
+        self.artist = utils.get_artist_name(self.media)
+
+
+class PlaybackEntry(Gtk.ListBoxRow):
+    def __init__(self, item, player):
+        super().__init__()
+
+        self.iter = item.iter
+
+        grid = Gtk.Grid(border_width=5, column_spacing=5, row_spacing=2)
+        self.add(grid)
+
+        self.cover = Gtk.Image()
+        artistLabel = Gtk.Label(label=item.artist, halign=Gtk.Align.START)
+        artistLabel.get_style_context().add_class('dim-label')
+
+        grid.attach(self.cover, 1, 0, 1, 2)
+        grid.attach(Gtk.Label(label=item.title, halign=Gtk.Align.START), 2, 0, 1, 1)
+        grid.attach(artistLabel, 2, 1, 1, 1)
+
+        player.cache.lookup(item.media, ArtSize.SMALL, self._on_cache_lookup, None)
+
+        self.show_all()
+
+    def _on_cache_lookup(self, surface, data=None):
+        self.cover.set_from_surface(surface)
+
+class PlaybackPopover(Gtk.Popover):
+
+    __gsignals__ = {
+        'current-changed': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreeIter,)),
+    }
+
+    def __repr__(self):
+        return '<PlaybackPopover>'
+
+    def __init__(self, button, player):
+        super().__init__(relative_to = button)
+
+        self._player = player
+
+        self._setup_view ()
+
+        self._player.connect('playlist-item-changed', self._update_model)
+        button.connect('toggled', self._on_button_toggled)
+
+    def _setup_view(self):
+        self._ui = Gtk.Builder()
+        self._ui.add_from_resource('/org/gnome/Music/PlaybackPopover.ui')
+
+        self.add(self._ui.get_object('playback_popover'))
+        self._trackList = self._ui.get_object('playback_popover_list')
+        self._model = Gio.ListStore()
+        def create_popover_entry(item, user_data):
+            return PlaybackEntry(item, user_data)
+        self._trackList.bind_model(self._model, create_popover_entry, self._player)
+        self._trackList.connect('row-activated', self._on_row_activated)
+
+    def _on_row_activated(self, box, row):
+        self.emit('current-changed', row.iter)
+
+    def _update_model(self, player, playlist, current_iter):
+        self._model.remove_all()
+        while current_iter != None:
+            item = PlaybackItem(playlist[current_iter], current_iter)
+            self._model.append(item)
+            current_iter = playlist.iter_next(current_iter)
+
+    def _on_button_toggled(self, button):
+        self.popup()


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