[gnome-music/wip/jfelder/playback-status-v3: 8/10] linearplaybox: Introduce LinearPlayBox widget
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/playback-status-v3: 8/10] linearplaybox: Introduce LinearPlayBox widget
- Date: Mon, 4 Feb 2019 07:38:17 +0000 (UTC)
commit eb45b9d1facb1bc5c7306efcfc021f2c3f2d38aa
Author: Jean Felder <jfelder src gnome org>
Date: Thu Sep 13 19:55:00 2018 +0200
linearplaybox: Introduce LinearPlayBox widget
This widget is designed to display a list of songs. Each row is a
TwoLineWidget which displays the album cover, song title and the
artist.
It will be used by PlaybackPopover widget to display a playlist from
Artists, Songs and Playlists views.
data/org.gnome.Music.gresource.xml | 2 +
data/ui/LinearPlaybackWindow.ui | 19 +++++
data/ui/TwoLineWidget.ui | 71 +++++++++++++++++
gnomemusic/widgets/albumwidget.py | 8 +-
gnomemusic/widgets/artistalbumswidget.py | 10 +--
gnomemusic/widgets/playbackpopover.py | 131 +++++++++++++++++++++++++++++++
gnomemusic/widgets/songwidget.py | 102 +++++++++++++++++++++---
7 files changed, 322 insertions(+), 21 deletions(-)
---
diff --git a/data/org.gnome.Music.gresource.xml b/data/org.gnome.Music.gresource.xml
index 97e2dfbd..9ec0b196 100644
--- a/data/org.gnome.Music.gresource.xml
+++ b/data/org.gnome.Music.gresource.xml
@@ -15,6 +15,7 @@
<file preprocess="xml-stripblanks">ui/EmptyView.ui</file>
<file preprocess="xml-stripblanks">ui/FilterView.ui</file>
<file preprocess="xml-stripblanks">ui/HeaderBar.ui</file>
+ <file preprocess="xml-stripblanks">ui/LinearPlaybackWindow.ui</file>
<file preprocess="xml-stripblanks">ui/PlayerToolbar.ui</file>
<file preprocess="xml-stripblanks">ui/PlaylistContextMenu.ui</file>
<file preprocess="xml-stripblanks">ui/PlaylistControls.ui</file>
@@ -25,6 +26,7 @@
<file preprocess="xml-stripblanks">ui/SelectionToolbar.ui</file>
<file preprocess="xml-stripblanks">ui/SidebarRow.ui</file>
<file preprocess="xml-stripblanks">ui/SongWidget.ui</file>
+ <file preprocess="xml-stripblanks">ui/TwoLineWidget.ui</file>
<file preprocess="xml-stripblanks">ui/TwoLineTip.ui</file>
</gresource>
</gresources>
diff --git a/data/ui/LinearPlaybackWindow.ui b/data/ui/LinearPlaybackWindow.ui
new file mode 100644
index 00000000..9cd2d6ba
--- /dev/null
+++ b/data/ui/LinearPlaybackWindow.ui
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="LinearPlaybackWindow" parent="GtkScrolledWindow">
+ <property name="width_request">600</property>
+ <property name="height_request">400</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkListBox" id="_listbox">
+ <property name="selection_mode">none</property>
+ <property name="visible">True</property>
+ <style>
+ <class name="view"/>
+ <class name="content-view"/>
+ </style>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/TwoLineWidget.ui b/data/ui/TwoLineWidget.ui
new file mode 100644
index 00000000..93ee5b02
--- /dev/null
+++ b/data/ui/TwoLineWidget.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="TwoLineWidget" parent="GtkListBoxRow">
+ <property name="can_focus">False</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="hexpand">False</property>
+ <property name="orientation">horizontal</property>
+ <property name="visible">True</property>
+ <property name="margin">6</property>
+ <property name="spacing">8</property>
+ <child>
+ <object class="GtkImage" id="_play_icon">
+ <property name="icon_size">4</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">True</property>
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="CoverStack" id="_cover_stack">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="_main_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">true</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>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_secondary_label">
+ <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>
+ <style>
+ <class name="player-artist-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/gnomemusic/widgets/albumwidget.py b/gnomemusic/widgets/albumwidget.py
index 1814ddc4..6b31fe89 100644
--- a/gnomemusic/widgets/albumwidget.py
+++ b/gnomemusic/widgets/albumwidget.py
@@ -33,7 +33,7 @@ from gnomemusic.grilo import grilo
from gnomemusic.player import PlayerPlaylist
from gnomemusic.widgets.disclistboxwidget import DiscBox
from gnomemusic.widgets.disclistboxwidget import DiscListBox # noqa: F401
-from gnomemusic.widgets.songwidget import SongWidget
+from gnomemusic.widgets.songwidget import WidgetState
import gnomemusic.utils as utils
@@ -276,13 +276,13 @@ class AlbumWidget(Gtk.EventBox):
self._duration += song.get_duration()
if (song.get_id() == current_song.get_id()):
- song_widget.props.state = SongWidget.State.PLAYING
+ song_widget.props.state = WidgetState.PLAYING
song_passed = True
elif (song_passed):
# Counter intuitive, but this is due to call order.
- song_widget.props.state = SongWidget.State.UNPLAYED
+ song_widget.props.state = WidgetState.UNPLAYED
else:
- song_widget.props.state = SongWidget.State.PLAYED
+ song_widget.props.state = WidgetState.PLAYED
self._set_duration_label()
diff --git a/gnomemusic/widgets/artistalbumswidget.py b/gnomemusic/widgets/artistalbumswidget.py
index b9150d71..274766ca 100644
--- a/gnomemusic/widgets/artistalbumswidget.py
+++ b/gnomemusic/widgets/artistalbumswidget.py
@@ -29,7 +29,7 @@ from gi.repository import GObject, Gtk
from gnomemusic import log
from gnomemusic.player import PlayerPlaylist
from gnomemusic.widgets.artistalbumwidget import ArtistAlbumWidget
-from gnomemusic.widgets.songwidget import SongWidget
+from gnomemusic.widgets.songwidget import WidgetState
logger = logging.getLogger(__name__)
@@ -157,13 +157,13 @@ class ArtistAlbumsWidget(Gtk.Box):
song_widget = song.song_widget
if (song.get_id() == current_song.get_id()):
- song_widget.props.state = SongWidget.State.PLAYING
+ song_widget.props.state = WidgetState.PLAYING
song_passed = True
elif (song_passed):
# Counter intuitive, but this is due to call order.
- song_widget.props.state = SongWidget.State.UNPLAYED
+ song_widget.props.state = WidgetState.UNPLAYED
else:
- song_widget.props.state = SongWidget.State.PLAYED
+ song_widget.props.state = WidgetState.PLAYED
itr = self._model.iter_next(itr)
@@ -176,7 +176,7 @@ class ArtistAlbumsWidget(Gtk.Box):
while itr:
song = self._model[itr][5]
song_widget = song.song_widget
- song_widget.props.state = SongWidget.State.UNPLAYED
+ song_widget.props.state = WidgetState.UNPLAYED
itr = self._model.iter_next(itr)
diff --git a/gnomemusic/widgets/playbackpopover.py b/gnomemusic/widgets/playbackpopover.py
new file mode 100644
index 00000000..c48d7283
--- /dev/null
+++ b/gnomemusic/widgets/playbackpopover.py
@@ -0,0 +1,131 @@
+# Copyright 2019 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
+from gnomemusic.widgets.songwidget import WidgetState, TwoLineWidget
+
+
+@Gtk.Template(resource_path='/org/gnome/Music/ui/LinearPlaybackWindow.ui')
+class LinearPlaybackWindow(Gtk.ScrolledWindow):
+
+ __gtype_name__ = 'LinearPlaybackWindow'
+
+ _listbox = Gtk.Template.Child()
+
+ _current_index = 0
+ _playlist_type = None
+ _playlist_id = None
+ _window_height = 0.0
+ _row_height = 0.0
+
+ def __repr__(self):
+ return '<LinearPlaybackWindow>'
+
+ @log
+ def __init__(self, player):
+ super().__init__()
+
+ self._player = player
+ self._player.connect('song-changed', self._on_song_changed)
+ self._player.connect('notify::repeat-mode', self._on_repeat_changed)
+
+ self._listbox.connect('row-activated', self._on_row_activated)
+
+ self.props.vadjustment.connect(
+ 'changed', self._vertical_adjustment_changed)
+
+ @log
+ def _vertical_adjustment_changed(self, klass):
+ v_adjust = self.props.vadjustment
+ if v_adjust.props.upper != self._window_height:
+ self._window_height = v_adjust.props.upper
+ self._row_height = self._window_height / len(self._listbox)
+ v_adjust.props.value = (self._current_index * self._row_height
+ + self._row_height / 2
+ - v_adjust.props.page_size / 2)
+
+ @log
+ def _init_listbox_rows(self):
+ songs = self._player.get_mpris_playlist()
+ for index, song in enumerate(songs):
+ row = TwoLineWidget(song, WidgetState.UNPLAYED)
+ self._listbox.add(row)
+
+ last_song = songs[-1]
+ for index in range(len(songs), 21):
+ row = TwoLineWidget(last_song, WidgetState.UNPLAYED)
+ self._listbox.add(row)
+ row.hide()
+
+ @log
+ def update(self):
+ self._playlist_type = self._player.get_playlist_type()
+ self._playlist_id = self._player.get_playlist_id()
+
+ if len(self._listbox) == 0:
+ self._init_listbox_rows()
+
+ current_song_id = self._player.props.current_song.get_id()
+ songs = self._player.get_mpris_playlist()
+ song_passed = False
+ for index, song in enumerate(songs):
+ state = WidgetState.PLAYED
+ if song.get_id() == current_song_id:
+ song_passed = True
+ self._current_index = index
+ state = WidgetState.PLAYING
+ elif song_passed:
+ # Counter intuitive, but this is due to call order.
+ state = WidgetState.UNPLAYED
+ row = self._listbox.get_row_at_index(index)
+ row.update(song, state)
+ row.show()
+
+ for index in range(len(songs), 21):
+ row = self._listbox.get_row_at_index(index)
+ row.hide()
+
+ @log
+ def _on_song_changed(self, klass, position):
+ if not self._player.playing_playlist(
+ self._playlist_type, self._playlist_id):
+ return
+
+ current_song = self._player.props.current_song
+ playing_row = self._listbox.get_row_at_index(self._current_index)
+ if current_song.get_id() == playing_row.props.song_id:
+ return
+
+ self.update()
+
+ @log
+ def _on_repeat_changed(self, klass, param):
+ self.update()
+
+ @log
+ def _on_row_activated(self, klass, row):
+ index = row.get_index()
+ self._player.play(index - self._current_index)
diff --git a/gnomemusic/widgets/songwidget.py b/gnomemusic/widgets/songwidget.py
index 4322f8a8..f4956efc 100644
--- a/gnomemusic/widgets/songwidget.py
+++ b/gnomemusic/widgets/songwidget.py
@@ -31,11 +31,20 @@ from gi.repository.Dazzle import BoldingLabel # noqa: F401
from gnomemusic import log
from gnomemusic import utils
+from gnomemusic.albumartcache import Art
from gnomemusic.grilo import grilo
from gnomemusic.playlists import Playlists, StaticPlaylists
from gnomemusic.widgets.starimage import StarImage # noqa: F401
+class WidgetState(IntEnum):
+ """The state of the SongWidget
+ """
+ PLAYED = 0
+ PLAYING = 1
+ UNPLAYED = 2
+
+
@Gtk.Template(resource_path='/org/gnome/Music/ui/SongWidget.ui')
class SongWidget(Gtk.EventBox):
"""The single song widget used in DiscListBox
@@ -70,13 +79,6 @@ class SongWidget(Gtk.EventBox):
_star_image = Gtk.Template.Child()
_play_icon = Gtk.Template.Child()
- class State(IntEnum):
- """The state of the SongWidget
- """
- PLAYED = 0
- PLAYING = 1
- UNPLAYED = 2
-
def __repr__(self):
return '<SongWidget>'
@@ -86,7 +88,7 @@ class SongWidget(Gtk.EventBox):
self._media = media
self._selection_mode = False
- self._state = SongWidget.State.UNPLAYED
+ self._state = WidgetState.UNPLAYED
song_number = media.get_track_number()
if song_number == 0:
@@ -183,7 +185,7 @@ class SongWidget(Gtk.EventBox):
"""State of the widget
:returns: Widget state
- :rtype: SongWidget.State
+ :rtype: WidgetState
"""
return self._state
@@ -194,7 +196,7 @@ class SongWidget(Gtk.EventBox):
This influences the look of the widgets label and if there is a
song play indicator being shown.
- :param SongWidget.State value: Widget state
+ :param WidgetState value: Widget state
"""
self._state = value
@@ -204,8 +206,84 @@ class SongWidget(Gtk.EventBox):
style_ctx.remove_class('playing-song-label')
self._play_icon.set_visible(False)
- if value == SongWidget.State.PLAYED:
+ if value == WidgetState.PLAYED:
style_ctx.add_class('dim-label')
- elif value == SongWidget.State.PLAYING:
+ elif value == WidgetState.PLAYING:
self._play_icon.set_visible(True)
style_ctx.add_class('playing-song-label')
+
+
+@Gtk.Template(resource_path='/org/gnome/Music/ui/TwoLineWidget.ui')
+class TwoLineWidget(Gtk.ListBoxRow):
+
+ __gtype_name__ = 'TwoLineWidget'
+
+ _cover_stack = Gtk.Template.Child()
+ _main_label = Gtk.Template.Child()
+ _play_icon = Gtk.Template.Child()
+ _secondary_label = Gtk.Template.Child()
+
+ def __repr__(self):
+ return '<TwoLineWidget>'
+
+ @log
+ def __init__(self, song, state):
+ super().__init__()
+
+ self.update(song, state)
+
+ @log
+ def update(self, song, state):
+ self._song_id = song.get_id()
+
+ self._cover_stack.props.size = Art.Size.SMALL
+ self._cover_stack.update(song)
+
+ self._main_label.props.label = utils.get_media_title(song)
+ self._secondary_label.props.label = utils.get_artist_name(song)
+
+ self.props.state = state
+
+ @GObject.Property(type=int)
+ def state(self):
+ """State of the widget
+
+ :returns: Widget state
+ :rtype: WidgetState
+ """
+ return self._state
+
+ @state.setter
+ def state(self, value):
+ """Set state of the of widget
+
+ This influences the look of the widgets label and if there is a
+ song play indicator being shown.
+
+ :param WidgetState value: Widget state
+ """
+ self._state = value
+
+ main_style_ctx = self._main_label.get_style_context()
+ secondary_style_ctx = self._secondary_label.get_style_context()
+
+ main_style_ctx.remove_class('dim-label')
+ main_style_ctx.remove_class('playing-song-label')
+ secondary_style_ctx.remove_class('dim-label')
+ self._play_icon.props.icon_name = ''
+
+ if value == WidgetState.PLAYED:
+ main_style_ctx.add_class('dim-label')
+ secondary_style_ctx.add_class('dim-label')
+ elif value == WidgetState.PLAYING:
+ self._play_icon.props.icon_name = 'media-playback-start-symbolic'
+ main_style_ctx.add_class('playing-song-label')
+
+ @GObject.Property(type=int, flags=GObject.ParamFlags.READABLE)
+ def song_id(self):
+ """Song id getter
+
+ :returns: the song id
+ :rtype: int
+ """
+ return self._song_id
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]