[gnome-music/wip/feborges/playback-popover] player: Introduce the Playback Popover
- From: Felipe Borges <felipeborges src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/feborges/playback-popover] player: Introduce the Playback Popover
- Date: Sun, 28 Jan 2018 19:52:21 +0000 (UTC)
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]