Commit d9523565 authored by Jean Felder's avatar Jean Felder

player: Factor out player ui

Move the player ui bits into a new class PlayerToolbar and modify
Player class to deal with the new interaction.
parent 8113a72d
......@@ -51,7 +51,7 @@
<property name="icon_name">media-skip-backward-symbolic</property>
<property name="icon_size">1</property>
</object>
<object class="GtkActionBar" id="actionbar">
<object class="GtkActionBar" id="PlayerToolbar">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<child>
......
......@@ -222,14 +222,17 @@ class MediaPlayer2Service(Server):
self.app = app
self.player = app.get_active_window().player
self.player.connect('song-changed', self._on_current_song_changed)
self.player.connect('thumbnail-updated', self._on_thumbnail_updated)
self.player.connect(
'song-changed', self._on_current_song_changed)
self.player.connect('playback-status-changed', self._on_playback_status_changed)
self.player.connect('repeat-mode-changed', self._on_repeat_mode_changed)
self.player.connect('volume-changed', self._on_volume_changed)
self.player.connect('prev-next-invalidated', self._on_prev_next_invalidated)
self.player.connect('seeked', self._on_seeked)
self.player.connect('playlist-changed', self._on_playlist_changed)
self.player_toolbar = app.get_active_window().player_toolbar
self.player_toolbar.connect(
'thumbnail-updated', self._on_thumbnail_updated)
playlists = Playlists.get_default()
playlists.connect('playlist-created', self._on_playlists_count_changed)
playlists.connect('playlist-deleted', self._on_playlists_count_changed)
......
......@@ -28,7 +28,6 @@ from random import randint
import logging
import time
from gettext import gettext as _
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstAudio', '1.0')
......@@ -36,14 +35,10 @@ gi.require_version('GstPbutils', '1.0')
from gi.repository import Gtk, GLib, Gio, GObject, Gst, GstPbutils
from gnomemusic import log
from gnomemusic.albumartcache import Art
from gnomemusic.gstplayer import GstPlayer, Playback
from gnomemusic.grilo import grilo
from gnomemusic.playlists import Playlists
from gnomemusic.scrobbler import LastFmScrobbler
from gnomemusic.widgets.coverstack import CoverStack
from gnomemusic.widgets.smoothscale import SmoothScale # noqa: F401
import gnomemusic.utils as utils
logger = logging.getLogger(__name__)
......@@ -66,7 +61,7 @@ class DiscoveryStatus:
class Player(GObject.GObject):
"""Main Player object
Contains the UI and logic of playing a song with Music.
Contains the logic of playing a song with Music.
"""
class Field(IntEnum):
......@@ -75,6 +70,7 @@ class Player(GObject.GObject):
DISCOVERY_STATUS = 1
__gsignals__ = {
'clock-tick': (GObject.SignalFlags.RUN_FIRST, None, (int,)),
'playlist-changed': (GObject.SignalFlags.RUN_FIRST, None, ()),
'song-changed': (
GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreeModel, Gtk.TreeIter)
......@@ -84,7 +80,7 @@ class Player(GObject.GObject):
'volume-changed': (GObject.SignalFlags.RUN_FIRST, None, ()),
'prev-next-invalidated': (GObject.SignalFlags.RUN_FIRST, None, ()),
'seeked': (GObject.SignalFlags.RUN_FIRST, None, (int,)),
'thumbnail-updated': (GObject.SignalFlags.RUN_FIRST, None, ()),
'state-changed': (GObject.SignalFlags.RUN_FIRST, None, ()),
}
def __repr__(self):
......@@ -126,7 +122,6 @@ class Player(GObject.GObject):
self._player.connect('eos', self._on_eos)
self._player.connect('notify::state', self._on_state_change)
self._setup_view()
self._lastfm = LastFmScrobbler()
@log
......@@ -168,8 +163,8 @@ class Player(GObject.GObject):
@log
def _on_repeat_setting_changed(self, settings, value):
self.repeat = settings.get_enum('repeat')
self._sync_prev_next()
self._sync_repeat_image()
self.emit('repeat-mode-changed')
self.emit('prev-next-invalidated')
self._validate_next_track()
@log
......@@ -191,7 +186,7 @@ class Player(GObject.GObject):
[self.Field.SONG, self.Field.DISCOVERY_STATUS],
[new_row[5], new_row[11]])
self._validate_next_track()
self._sync_prev_next()
self.emit('prev-next-invalidated')
@log
def remove_song(self, model, path):
......@@ -211,7 +206,7 @@ class Player(GObject.GObject):
self.playlist.remove(iter_remove)
self._validate_next_track()
self._sync_prev_next()
self.emit('prev-next-invalidated')
@log
def _get_random_iter(self, current_track):
......@@ -380,62 +375,25 @@ class Player(GObject.GObject):
@log
def _on_state_change(self, klass, arguments):
self._sync_playing()
self.emit('state-changed')
return True
@log
def _sync_playing(self):
if self._player.state == Playback.PLAYING:
image = self._pause_image
tooltip = _("Pause")
else:
image = self._play_image
tooltip = _("Play")
if self._play_button.get_image() != image:
self._play_button.set_image(image)
self._play_button.set_tooltip_text(tooltip)
@log
def _sync_prev_next(self):
self._next_button.set_sensitive(self.has_next())
self._prev_button.set_sensitive(self.has_previous())
self.emit('prev-next-invalidated')
@log
def set_playing(self, value):
"""Set state
:param bool value: Playing
"""
self.actionbar.show()
if value:
self.play()
else:
self.pause()
self._play_button.set_image(self._pause_image)
self.emit('state-changed')
@log
def _load(self, media):
self._total_time_label.set_label(
utils.seconds_to_string(media.get_duration()))
self._play_button.set_sensitive(True)
self._sync_prev_next()
artist = utils.get_artist_name(media)
self._artist_label.set_label(artist)
self._cover_stack.update(media)
title = utils.get_media_title(media)
self._title_label.set_label(title)
self._time_stamp = int(time.time())
url_ = media.get_url()
......@@ -488,10 +446,6 @@ class Player(GObject.GObject):
return False
@log
def _on_cover_stack_updated(self, klass):
self.emit('thumbnail-updated')
@log
def _on_eos(self, klass):
if self._next_track:
......@@ -610,10 +564,8 @@ class Player(GObject.GObject):
self.playlist_id = id_
if self._player.state == Playback.PLAYING:
self._sync_prev_next()
self.emit('prev-next-invalidated')
current_track = self.playlist.get_iter(playlist_path)
self.emit('song-changed', self.playlist, current_track)
GLib.idle_add(self._validate_next_track)
@log
......@@ -623,53 +575,8 @@ class Player(GObject.GObject):
else:
return None
@log
def _setup_view(self):
self._ui = Gtk.Builder()
self._ui.add_from_resource('/org/gnome/Music/PlayerToolbar.ui')
self.actionbar = self._ui.get_object('actionbar')
self._prev_button = self._ui.get_object('previous_button')
self._play_button = self._ui.get_object('play_button')
self._next_button = self._ui.get_object('next_button')
self._play_image = self._ui.get_object('play_image')
self._pause_image = self._ui.get_object('pause_image')
self._progress_scale = self._ui.get_object('smooth_scale')
self._progress_scale.player = self._player
self._progress_scale.connect('seek-finished', self._on_seek_finished)
self._progress_scale.connect(
'value-changed', self._on_progress_value_changed)
self._progress_time_label = self._ui.get_object('playback')
self._total_time_label = self._ui.get_object('duration')
self._title_label = self._ui.get_object('title')
self._artist_label = self._ui.get_object('artist')
stack = self._ui.get_object('cover')
self._cover_stack = CoverStack(stack, Art.Size.XSMALL)
self._cover_stack.connect('updated', self._on_cover_stack_updated)
self._repeat_button_image = self._ui.get_object('playlistRepeat')
self._sync_repeat_image()
self._prev_button.connect('clicked', self._on_prev_button_clicked)
self._play_button.connect('clicked', self._on_play_button_clicked)
self._next_button.connect('clicked', self._on_next_button_clicked)
def _on_progress_value_changed(self, progress_scale):
seconds = int(progress_scale.get_value() / 60)
self._progress_time_label.set_label(utils.seconds_to_string(seconds))
@log
def _on_seek_finished(self, klass, time):
self._player.state = Playback.PLAYING
@log
def _on_clock_tick(self, klass, tick):
seconds = int(self._player.position)
logger.debug("Clock tick {}, player at {} seconds".format(
tick, self._player.position))
......@@ -679,9 +586,6 @@ class Player(GObject.GObject):
self._new_clock = True
self._lastfm.now_playing(current_media)
self._progress_time_label.set_label(
utils.seconds_to_string(seconds))
duration = self._player.duration
if duration is None:
return
......@@ -704,37 +608,14 @@ class Player(GObject.GObject):
grilo.bump_play_count(current_media)
grilo.set_last_played(current_media)
@log
def _on_play_button_clicked(self, button):
if self._player.state == Playback.PLAYING:
self.pause()
else:
self.play()
@log
def _on_next_button_clicked(self, button):
self.next()
self.emit('clock-tick', int(position))
# MPRIS
@log
def _on_prev_button_clicked(self, button):
self.previous()
def get_gst_player(self):
"""GstPlayer getter"""
return self._player
@log
def _sync_repeat_image(self):
icon = None
if self.repeat == RepeatType.NONE:
icon = 'media-playlist-consecutive-symbolic'
elif self.repeat == RepeatType.SHUFFLE:
icon = 'media-playlist-shuffle-symbolic'
elif self.repeat == RepeatType.ALL:
icon = 'media-playlist-repeat-symbolic'
elif self.repeat == RepeatType.SONG:
icon = 'media-playlist-repeat-song-symbolic'
self._repeat_button_image.set_from_icon_name(icon, Gtk.IconSize.MENU)
self.emit('repeat-mode-changed')
# MPRIS
@log
def get_playback_status(self):
# FIXME: Just a proxy right now.
......@@ -762,7 +643,7 @@ class Player(GObject.GObject):
@log
def set_repeat_mode(self, mode):
self.repeat = mode
self._sync_repeat_image()
self.emit('repeat-mode-changed')
# TODO: used by MPRIS
@log
......
......@@ -147,7 +147,7 @@ class ArtistsView(BaseView):
self._artist_albums_stack.add(new_artist_albums_widget)
artist_albums = ArtistAlbumsWidget(
artist, albums, self.player, self._header_bar,
artist, albums, self.player, self, self._header_bar,
self._selection_toolbar, self._window)
self._artists[artist.casefold()]['widget'] = artist_albums
new_artist_albums_widget.add(artist_albums)
......
......@@ -140,14 +140,14 @@ class BaseView(Gtk.Stack):
if self.selection_mode:
self._header_bar.set_selection_mode(True)
self.player.actionbar.set_visible(False)
self.set_player_visible(False)
select_toolbar = self._selection_toolbar
select_toolbar.set_visible(True)
select_toolbar.add_to_playlist_button.set_sensitive(False)
else:
self._header_bar.set_selection_mode(False)
track_playing = self.player.current_track is not None
self.player.actionbar.set_visible(track_playing)
self.set_player_visible(track_playing)
self._selection_toolbar.set_visible(False)
self.unselect_all()
......@@ -258,3 +258,11 @@ class BaseView(Gtk.Stack):
self._header_bar._selection_menu_label.set_text(
_("Click on items to select them"))
self.queue_draw()
@log
def set_player_visible(self, visible):
"""Set PlayWidget action visibility
:param bool visible: Set actionbar visibility
"""
self._window.player_toolbar.set_visible(visible)
......@@ -683,7 +683,7 @@ class PlaylistView(BaseView):
if self.player.running_playlist('Playlist', playlist_id):
self.player.stop()
self.player.actionbar.set_visible(False)
self.set_player_visible(False)
if row_next:
self._sidebar.select_row(row_next)
......
......@@ -130,7 +130,7 @@ class SearchView(BaseView):
albums = self._artists[artist.casefold()]['albums']
self._artist_albums_widget = ArtistAlbumsWidget(
artist, albums, self.player, self._header_bar,
artist, albums, self.player, self, self._header_bar,
self._selection_toolbar, self._window, True)
self.add(self._artist_albums_widget)
self._artist_albums_widget.show()
......
......@@ -56,6 +56,7 @@ class AlbumWidget(Gtk.EventBox):
self._songs = []
self._parent_view = parent_view
self._player = player
self._iter_to_clean = None
......@@ -204,7 +205,7 @@ class AlbumWidget(Gtk.EventBox):
self._selection_mode = True
self._disc_listbox.set_selection_mode(True)
self._header_bar.set_selection_mode(True)
self._player.actionbar.set_visible(False)
self._parent_view.set_player_visible(False)
self._header_bar.header_bar.set_custom_title(
self._header_bar._selection_menu_button)
else:
......@@ -212,7 +213,7 @@ class AlbumWidget(Gtk.EventBox):
self._disc_listbox.set_selection_mode(False)
self._header_bar.set_selection_mode(False)
if self._player.get_playback_status() != Playback.STOPPED:
self._player.actionbar.set_visible(True)
self._parent_view.set_player_visible(True)
@log
def _create_disc_box(self, disc_nr, disc_songs):
......
......@@ -45,12 +45,13 @@ class ArtistAlbumsWidget(Gtk.Box):
return '<ArtistAlbumsWidget>'
@log
def __init__(self, artist, albums, player, header_bar, selection_toolbar,
window, selection_mode_allowed=False):
def __init__(self, artist, albums, player, parent_view, header_bar,
selection_toolbar, window, selection_mode_allowed=False):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
self._player = player
self.artist = artist
self._window = window
self._parent_view = parent_view
self._selection_mode = False
self._selection_mode_allowed = selection_mode_allowed
self._selection_toolbar = selection_toolbar
......@@ -118,11 +119,10 @@ class ArtistAlbumsWidget(Gtk.Box):
@log
def _add_album(self, album, is_last_album=False):
widget = ArtistAlbumWidget(album, self._player, self._model,
self._header_bar,
self._selection_mode_allowed,
self._songs_grid_size_group,
self._cover_size_group)
widget = ArtistAlbumWidget(
album, self._player, self._model, self._parent_view,
self._header_bar, self._selection_mode_allowed,
self._songs_grid_size_group, self._cover_size_group)
self._cover_size_group.add_widget(widget.cover_stack._stack)
self._album_box.pack_start(widget, False, False, 0)
......
......@@ -43,7 +43,7 @@ class ArtistAlbumWidget(Gtk.Box):
return '<ArtistAlbumWidget>'
@log
def __init__(self, media, player, model, header_bar,
def __init__(self, media, player, model, parent_view, header_bar,
selection_mode_allowed, size_group=None,
cover_size_group=None):
super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
......@@ -56,6 +56,7 @@ class ArtistAlbumWidget(Gtk.Box):
self._artist = utils.get_artist_name(self._media)
self._album_title = utils.get_album_title(self._media)
self._model = model
self._parent_view = parent_view
self._header_bar = header_bar
self._selection_mode = False
self._selection_mode_allowed = selection_mode_allowed
......@@ -122,7 +123,7 @@ class ArtistAlbumWidget(Gtk.Box):
self._selection_mode = True
self._disc_listbox.set_selection_mode(True)
self._header_bar.set_selection_mode(True)
self._player.actionbar.set_visible(False)
self._parent_view.set_player_visible(False)
self._header_bar.header_bar.set_custom_title(
self._header_bar._selection_menu_button)
else:
......@@ -130,7 +131,7 @@ class ArtistAlbumWidget(Gtk.Box):
self._disc_listbox.set_selection_mode(False)
self._header_bar.set_selection_mode(False)
if self._player.get_playback_status() != Playback.STOPPED:
self._player.actionbar.set_visible(True)
self._parent_view.set_player_visible(True)
@log
def _add_item(self, source, prefs, song, remaining, data=None):
......
# 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 gettext import gettext as _
from gi.repository import GObject, Gtk
from gnomemusic import log
from gnomemusic.albumartcache import Art
from gnomemusic.gstplayer import Playback
from gnomemusic.player import RepeatType
from gnomemusic.widgets.coverstack import CoverStack
from gnomemusic.widgets.smoothscale import SmoothScale # noqa: F401
import gnomemusic.utils as utils
class PlayerToolbar(Gtk.ActionBar):
"""Main Player widget object
Contains the ui of playing a song with Music.
"""
__gsignals__ = {
'thumbnail-updated': (GObject.SignalFlags.RUN_FIRST, None, ()),
}
def __repr__(self):
return '<PlayerToolbar>'
@log
def __init__(self, player):
super().__init__()
self._player = player
self._ui = Gtk.Builder()
self._ui.add_from_resource('/org/gnome/Music/PlayerToolbar.ui')
self._prev_button = self._ui.get_object('previous_button')
self._play_button = self._ui.get_object('play_button')
self._next_button = self._ui.get_object('next_button')
self._play_image = self._ui.get_object('play_image')
self._pause_image = self._ui.get_object('pause_image')
self._progress_scale = self._ui.get_object('smooth_scale')
self._progress_scale.player = self._player.get_gst_player()
self._progress_scale.connect('seek-finished', self._on_seek_finished)
self._progress_scale.connect(
'value-changed', self._on_progress_value_changed)
self._progress_time_label = self._ui.get_object('playback')
self._total_time_label = self._ui.get_object('duration')
self._title_label = self._ui.get_object('title')
self._artist_label = self._ui.get_object('artist')
stack = self._ui.get_object('cover')
self._cover_stack = CoverStack(stack, Art.Size.XSMALL)
self._cover_stack.connect('updated', self._on_cover_stack_updated)
self._repeat_button_image = self._ui.get_object('playlistRepeat')
self._sync_repeat_image()
self._prev_button.connect('clicked', self._on_prev_button_clicked)
self._play_button.connect('clicked', self._on_play_button_clicked)
self._next_button.connect('clicked', self._on_next_button_clicked)
self._player.connect('clock-tick', self._on_clock_tick)
self._player.connect('song-changed', self._update_view)
self._player.connect('prev-next-invalidated', self._sync_prev_next)
self._player.connect('repeat-mode-changed', self._sync_repeat_image)
self._player.connect('state-changed', self._sync_playing)
@log
def _on_seek_finished(self, klass, time):
self._player.play()
@log
def _on_progress_value_changed(self, progress_scale):
seconds = int(progress_scale.get_value() / 60)
self._progress_time_label.set_label(utils.seconds_to_string(seconds))
@log
def _on_cover_stack_updated(self, klass):
self.emit('thumbnail-updated')
@log
def _on_prev_button_clicked(self, button):
self._player.previous()
@log
def _on_play_button_clicked(self, button):
if self._player.get_playback_status() == Playback.PLAYING:
self._player.pause()
else:
self._player.play()
@log
def _on_next_button_clicked(self, button):
self._player.next()
@log
def _sync_repeat_image(self, player=None):
icon = None
if self._player.repeat == RepeatType.NONE:
icon = 'media-playlist-consecutive-symbolic'
elif self._player.repeat == RepeatType.SHUFFLE:
icon = 'media-playlist-shuffle-symbolic'
elif self._player.repeat == RepeatType.ALL:
icon = 'media-playlist-repeat-symbolic'
elif self._player.repeat == RepeatType.SONG:
icon = 'media-playlist-repeat-song-symbolic'
self._repeat_button_image.set_from_icon_name(icon, Gtk.IconSize.MENU)
@log
def _sync_playing(self, player):
self.show()
if self._player.get_playback_status() == Playback.PLAYING:
image = self._pause_image
tooltip = _("Pause")
else:
image = self._play_image
tooltip = _("Play")
if self._play_button.get_image() != image:
self._play_button.set_image(image)
self._play_button.set_tooltip_text(tooltip)
@log
def _sync_prev_next(self, player=None):
self._next_button.set_sensitive(self._player.has_next())
self._prev_button.set_sensitive(self._player.has_previous())
@log
def _update_view(self, player, playlist, current_iter):
media = playlist[current_iter][player.Field.SONG]
self._total_time_label.set_label(
utils.seconds_to_string(media.get_duration()))
self._play_button.set_sensitive(True)
self._sync_prev_next()
self._artist_label.set_label(utils.get_artist_name(media))
self._title_label.set_label(utils.get_media_title(media))
self._cover_stack.update(media)
@log
def _on_clock_tick(self, player, seconds):
self._progress_time_label.set_label(utils.seconds_to_string(seconds))
......@@ -46,6 +46,7 @@ from gnomemusic.views.searchview import SearchView
from gnomemusic.views.songsview import SongsView
from gnomemusic.views.playlistview import PlaylistView
from gnomemusic.widgets.notificationspopup import NotificationsPopup
from gnomemusic.widgets.playertoolbar import PlayerToolbar
from gnomemusic.widgets.playlistdialog import PlaylistDialog
from gnomemusic.widgets.selectiontoolbar import SelectionToolbar
from gnomemusic.playlists import Playlists
......@@ -213,6 +214,7 @@ class Window(Gtk.ApplicationWindow):
def _setup_view(self):
self._box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.player = Player(self)
self.player_toolbar = PlayerToolbar(self.player)
self.selection_toolbar = SelectionToolbar()
self.toolbar = Toolbar()
self.views = [None] * len(View)
......@@ -232,7 +234,7 @@ class Window(Gtk.ApplicationWindow):
self.set_titlebar(self.toolbar.header_bar)
self._box.pack_start(self.toolbar.searchbar, False, False, 0)
self._box.pack_start(self._overlay, True, True, 0)
self._box.pack_start(self.player.actionbar, False, False, 0)
self._box.pack_start(self.player_toolbar, False, False, 0)
self._box.pack_start(self.selection_toolbar, False, False, 0)
self.add(self._box)
......@@ -256,7 +258,7 @@ class Window(Gtk.ApplicationWindow):
self.toolbar.set_state(ToolbarState.MAIN)
self.toolbar.header_bar.show()
self._overlay.show()
self.player.actionbar.show_all()
self.player_toolbar.show_all()
self._box.show()
self.show()
......@@ -525,3 +527,11 @@ class Window(Gtk.ApplicationWindow):
playlist_dialog.destroy()
self._stack.get_visible_child().get_selected_songs(callback)
@log
def set_player_visible(self, visible):
"""Set PlayWidget action visibility
:param bool visible: actionbar visibility
"""
self.player_toolbar.set_visible(visible)
......@@ -9,7 +9,6 @@ gnomemusic/application.py
gnomemusic/grilo.py
gnomemusic/gstplayer.py
gnomemusic/mpris.py
gnomemusic/player.py
gnomemusic/playlists.py
gnomemusic/query.py
gnomemusic/searchbar.py
......@@ -29,6 +28,7 @@ gnomemusic/widgets/artistalbumswidget.py
gnomemusic/widgets/artistalbumwidget.py
gnomemusic/widgets/disclistboxwidget.py
gnomemusic/widgets/notificationspopup.py
gnomemusic/widgets/playertoolbar.py
gnomemusic/widgets/playlistdialog.py
gnomemusic/widgets/starhandlerwidget.py
gnomemusic/window.py
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment