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



commit ae325b17f5a2265629886ba3ffa35539e208b132
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/PlayerToolbar.ui | 143 +++++++++++++++++++++++++++++++++-----------------
 gnomemusic/player.py  |  64 ++++++++++++++++++++++
 2 files changed, 160 insertions(+), 47 deletions(-)
---
diff --git a/data/PlayerToolbar.ui b/data/PlayerToolbar.ui
index 53028c6..aebdefc 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>
@@ -313,4 +321,45 @@
     <property name="can_focus">False</property>
     <property name="draw_as_radio">True</property>
   </object>
+
+  <object class="GtkPopover" id="playback_popover">
+    <property name="width_request">480</property>
+    <property name="height_request">360</property>
+    <property name="relative_to">nowplaying_button</property>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</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>
+    </child>
+  </object>
 </interface>
diff --git a/gnomemusic/player.py b/gnomemusic/player.py
index e0f98e5..1d1778e 100644
--- a/gnomemusic/player.py
+++ b/gnomemusic/player.py
@@ -603,6 +603,8 @@ class Player(GObject.GObject):
 
         self._validate_next_track()
 
+        self._populate_playback_popover (currentTrack)
+
     def _on_next_item_validated(self, info, error, _iter):
         if error:
             print("Info %s: error: %s" % (info, error))
@@ -765,6 +767,14 @@ class Player(GObject.GObject):
         else:
             return None
 
+    @log
+    def _populate_playback_popover(self, iter):
+        self._playbackPopoverModel.remove_all()
+        while iter != None:
+            item = PlaybackItem(self.playlist[iter], iter)
+            self._playbackPopoverModel.append(item)
+            iter = self.playlist.iter_next(iter)
+
     @log
     def _setup_view(self):
         self._ui = Gtk.Builder()
@@ -775,6 +785,9 @@ 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 = self._ui.get_object('playback_popover')
+        self._playbackPopoverList = self._ui.get_object('playback_popover_list')
         self.progressScale = self._ui.get_object('progress_scale')
         self.songPlaybackTimeLabel = self._ui.get_object('playback')
         self.songTotalTimeLabel = self._ui.get_object('duration')
@@ -792,6 +805,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._nowplayingButton.connect('toggled', self._on_nowplaying_btn_toggled)
         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 +816,17 @@ class Player(GObject.GObject):
         self._old_progress_scale_value = 0.0
         self.progressScale.set_increments(300, 600)
 
+        self._playbackPopoverModel = Gio.ListStore()
+        def create_popover_entry(item, user_data):
+            return PlaybackEntry(item, user_data)
+
+        self._playbackPopoverList.bind_model (self._playbackPopoverModel, create_popover_entry, self)
+        self._playbackPopoverList.connect('row-activated', self._on_playback_popover_row_activated)
+
+    def _on_playback_popover_row_activated(self, box, row):
+        self.currentTrack = Gtk.TreeRowReference.new(self.playlist, self.playlist.get_path(row.iter))
+        self.play()
+
     def _on_progress_scale_seek_finish(self, value):
         """Prevent stutters when seeking with infinitesimal amounts"""
         self._seek_timeout = None
@@ -917,6 +942,10 @@ class Player(GObject.GObject):
     def _on_prev_btn_clicked(self, btn):
         self.play_previous()
 
+    @log
+    def _on_nowplaying_btn_toggled(self, btn):
+        self._playbackPopover.popup()
+
     @log
     def _set_duration(self, duration):
         self.duration = duration
@@ -1068,6 +1097,41 @@ class Player(GObject.GObject):
         return self.playlist.get_value(currentTrack, self.playlistField)
 
 
+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 MissingCodecsDialog(Gtk.MessageDialog):
 
     def __repr__(self):


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