[gnome-music/wip/mschraal/artrework: 13/15] Add CoverStack for smooth art transitions



commit 0cd7131dd222483cec34668da8cc45f22bc73f5e
Author: Marinus Schraal <mschraal gnome org>
Date:   Mon Jan 29 09:56:31 2018 +0100

    Add CoverStack for smooth art transitions
    
    Adds a Gtk.Stack based widget that provides a smooth transition between
    image states.
    
    Closes: #55

 data/AlbumCover.ui                       |  2 +-
 data/ArtistAlbumWidget.ui                |  2 +-
 data/PlayerToolbar.ui                    |  2 +-
 gnomemusic/player.py                     | 10 ++--
 gnomemusic/views/albumsview.py           |  9 ++--
 gnomemusic/widgets/Makefile.am           |  1 +
 gnomemusic/widgets/artistalbumswidget.py |  2 +-
 gnomemusic/widgets/artistalbumwidget.py  | 10 ++--
 gnomemusic/widgets/coverstack.py         | 87 ++++++++++++++++++++++++++++++++
 9 files changed, 109 insertions(+), 16 deletions(-)
---
diff --git a/data/AlbumCover.ui b/data/AlbumCover.ui
index 4309a3d..39a79d2 100644
--- a/data/AlbumCover.ui
+++ b/data/AlbumCover.ui
@@ -16,7 +16,7 @@
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
-              <object class="GtkImage" id="image">
+              <object class="GtkStack" id="stack">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="vexpand">True</property>
diff --git a/data/ArtistAlbumWidget.ui b/data/ArtistAlbumWidget.ui
index a0460ee..ae71f14 100644
--- a/data/ArtistAlbumWidget.ui
+++ b/data/ArtistAlbumWidget.ui
@@ -6,7 +6,7 @@
     <property name="visible">True</property>
     <property name="can_focus">False</property>
     <child>
-      <object class="GtkImage" id="cover">
+      <object class="GtkStack" id="cover">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="valign">start</property>
diff --git a/data/PlayerToolbar.ui b/data/PlayerToolbar.ui
index 53028c6..f42f256 100644
--- a/data/PlayerToolbar.ui
+++ b/data/PlayerToolbar.ui
@@ -122,7 +122,7 @@
         <property name="valign">center</property>
         <property name="spacing">8</property>
         <child>
-          <object class="GtkImage" id="cover">
+          <object class="GtkStack" id="cover">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
           </object>
diff --git a/gnomemusic/player.py b/gnomemusic/player.py
index 6a5a800..75e62e1 100644
--- a/gnomemusic/player.py
+++ b/gnomemusic/player.py
@@ -46,10 +46,11 @@ from gi.repository import Gtk, GLib, Gio, GObject, Gst, GstAudio, GstPbutils
 from gettext import gettext as _, ngettext
 
 from gnomemusic import log
-from gnomemusic.albumartcache import Art, ArtImage
+from gnomemusic.albumartcache import Art
 from gnomemusic.grilo import grilo
 from gnomemusic.playlists import Playlists
 from gnomemusic.scrobbler import LastFmScrobbler
+from gnomemusic.widgets.coverstack import CoverStack
 import gnomemusic.utils as utils
 
 
@@ -579,8 +580,7 @@ class Player(GObject.GObject):
         artist = utils.get_artist_name(media)
         self.artistLabel.set_label(artist)
 
-        art = ArtImage(Art.Size.XSMALL, media)
-        art.image = self._image
+        self._cover_stack.update(media)
 
         title = utils.get_media_title(media)
         self.titleLabel.set_label(title)
@@ -775,7 +775,9 @@ class Player(GObject.GObject):
         self.songTotalTimeLabel = self._ui.get_object('duration')
         self.titleLabel = self._ui.get_object('title')
         self.artistLabel = self._ui.get_object('artist')
-        self._image = self._ui.get_object('cover')
+
+        stack = self._ui.get_object('cover')
+        self._cover_stack = CoverStack(stack, Art.Size.XSMALL)
 
         self.duration = self._ui.get_object('duration')
         self.repeatBtnImage = self._ui.get_object('playlistRepeat')
diff --git a/gnomemusic/views/albumsview.py b/gnomemusic/views/albumsview.py
index b214859..8fef4f0 100644
--- a/gnomemusic/views/albumsview.py
+++ b/gnomemusic/views/albumsview.py
@@ -26,11 +26,12 @@ from gettext import gettext as _
 from gi.repository import GLib, GObject, Gtk, Gdk
 
 from gnomemusic import log
-from gnomemusic.albumartcache import Art, ArtImage
+from gnomemusic.albumartcache import Art
 from gnomemusic.grilo import grilo
 from gnomemusic.toolbar import ToolbarState
 from gnomemusic.views.baseview import BaseView
 from gnomemusic.widgets.albumwidget import AlbumWidget
+from gnomemusic.widgets.coverstack import CoverStack
 import gnomemusic.utils as utils
 
 
@@ -157,7 +158,7 @@ class AlbumsView(BaseView):
             '/org/gnome/Music/AlbumCover.ui')
 
         child = Gtk.FlowBoxChild()
-        child.image = builder.get_object('image')
+        child.stack = builder.get_object('stack')
         child.check = builder.get_object('check')
         child.title = builder.get_object('title')
         child.subtitle = builder.get_object('subtitle')
@@ -183,8 +184,8 @@ class AlbumsView(BaseView):
         child.add(builder.get_object('main_box'))
         child.show()
 
-        art = ArtImage(Art.Size.MEDIUM, item)
-        art.image = child.image
+        cover_stack = CoverStack(child.stack, Art.Size.MEDIUM)
+        cover_stack.update(item)
 
         return child
 
diff --git a/gnomemusic/widgets/Makefile.am b/gnomemusic/widgets/Makefile.am
index 3b50455..69b259c 100644
--- a/gnomemusic/widgets/Makefile.am
+++ b/gnomemusic/widgets/Makefile.am
@@ -5,6 +5,7 @@ app_PYTHON = \
        albumwidget.py \
        artistalbumswidget.py \
        artistalbumwidget.py \
+       coverstack.py \
        disclistboxwidget.py \
        notificationspopup.py \
        playlistdialog.py \
diff --git a/gnomemusic/widgets/artistalbumswidget.py b/gnomemusic/widgets/artistalbumswidget.py
index 21a45d8..ca5a0b6 100644
--- a/gnomemusic/widgets/artistalbumswidget.py
+++ b/gnomemusic/widgets/artistalbumswidget.py
@@ -124,7 +124,7 @@ class ArtistAlbumsWidget(Gtk.Box):
                                    self._selection_mode_allowed,
                                    self._songs_grid_size_group,
                                    self._cover_size_group)
-        self._cover_size_group.add_widget(widget.cover)
+        self._cover_size_group.add_widget(widget.cover_stack._stack)
 
         self._album_box.pack_start(widget, False, False, 0)
         self._widgets.append(widget)
diff --git a/gnomemusic/widgets/artistalbumwidget.py b/gnomemusic/widgets/artistalbumwidget.py
index a86eca2..c26af1e 100644
--- a/gnomemusic/widgets/artistalbumwidget.py
+++ b/gnomemusic/widgets/artistalbumwidget.py
@@ -25,8 +25,9 @@
 from gi.repository import GObject, Gtk
 
 from gnomemusic import log
-from gnomemusic.albumartcache import Art, ArtImage
+from gnomemusic.albumartcache import Art
 from gnomemusic.grilo import grilo
+from gnomemusic.widgets.coverstack import CoverStack
 from gnomemusic.widgets.disclistboxwidget import DiscBox
 import gnomemusic.utils as utils
 
@@ -67,8 +68,9 @@ class ArtistAlbumWidget(Gtk.Box):
         ui.add_from_resource('/org/gnome/Music/ArtistAlbumWidget.ui')
 
         self.cover = ui.get_object('cover')
-        art = ArtImage(Art.Size.MEDIUM, self._media)
-        art.image = self.cover
+
+        self.cover_stack = CoverStack(self.cover, Art.Size.MEDIUM)
+        self.cover_stack.update(self._media)
 
         self._disc_listbox = ui.get_object('disclistbox')
         self._disc_listbox.set_selection_mode_allowed(
@@ -85,7 +87,7 @@ class ArtistAlbumWidget(Gtk.Box):
             self._size_group.add_widget(ui.get_object('box1'))
 
         if self._cover_size_group:
-            self._cover_size_group.add_widget(self.cover)
+            self._cover_size_group.add_widget(self.cover_stack._stack)
 
         self.pack_start(ui.get_object('ArtistAlbumWidget'), True, True, 0)
 
diff --git a/gnomemusic/widgets/coverstack.py b/gnomemusic/widgets/coverstack.py
new file mode 100644
index 0000000..c5f623f
--- /dev/null
+++ b/gnomemusic/widgets/coverstack.py
@@ -0,0 +1,87 @@
+# 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 GObject, Gtk
+
+from gnomemusic import log
+from gnomemusic.albumartcache import Art, DefaultIcon
+
+
+class CoverStack(GObject.GObject):
+    """Provides a smooth transition between image states
+
+    Uses a Gtk.Stack to provide an in-situ transition between an image
+    state. Either between the 'loading' state versus the 'loaded' state
+    or in between songs.
+    """
+
+    _default_icon = DefaultIcon()
+
+    @log
+    def __init__(self, stack, size):
+        super().__init__()
+
+        self._size = size
+        self._stack = stack
+        self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
+        self._scale = self._stack.get_scale_factor()
+        self._handler_id = None
+
+        self._loading_icon = self._default_icon.get(
+            DefaultIcon.Type.LOADING, self._size, self._scale)
+
+        self._loading_cover = Gtk.Image.new_from_surface(self._loading_icon)
+
+        self._cover_a = Gtk.Image()
+        self._cover_b = Gtk.Image()
+
+        self._stack.add_named(self._loading_cover, "loading")
+        self._stack.add_named(self._cover_a, "A")
+        self._stack.add_named(self._cover_b, "B")
+
+        self._stack.set_visible_child_name("loading")
+        self._stack.show_all()
+
+    @log
+    def update(self, media):
+        """Update the stack with the given media
+
+        Update the stack with the art retrieved from the given media.
+        :param Grl.Media media: The media object
+        """
+        self._active_child = self._stack.get_visible_child_name()
+
+        art = Art(self._size, media, self._scale)
+        self._handler_id = art.connect('finished', self._art_retrieved)
+        art.lookup()
+
+    @log
+    def _art_retrieved(self, klass):
+        klass.disconnect(self._handler_id)
+        if self._active_child == "B":
+            self._cover_a.set_from_surface(klass.surface)
+            self._stack.set_visible_child_name("A")
+        else:
+            self._cover_b.set_from_surface(klass.surface)
+            self._stack.set_visible_child_name("B")


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