def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_title("Lollypop") PlaybackController.__init__(self) ProgressController.__init__(self) self.set_application(app) self.__timeout1 = None self.__timeout2 = None self.__signal1_id = None self.__signal2_id = None self.set_decorated(False) self.__parent = parent builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/FullScreen.ui') builder.connect_signals(self) # Calculate cover size screen = Gdk.Screen.get_default() monitor = screen.get_primary_monitor() geometry = screen.get_monitor_geometry(monitor) # We want 500 and 200 in full hd scale = self.get_scale_factor() if geometry.width > geometry.height: artsize = int(ArtSize.FULLSCREEN * geometry.width / 1920 / scale) else: artsize = int(ArtSize.FULLSCREEN * geometry.height / 1920 / scale) InfosController.__init__(self, artsize) self._play_btn = builder.get_object('play_btn') self._next_btn = builder.get_object('next_btn') self._prev_btn = builder.get_object('prev_btn') self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object('play_image') self._pause_image = builder.get_object('pause_image') close_btn = builder.get_object('close_btn') close_btn.connect('clicked', self.__destroy) self._cover = builder.get_object('cover') self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._album_label = builder.get_object('album') self._datetime = builder.get_object('datetime') self._progress = builder.get_object('progress_scale') self._timelabel = builder.get_object('playback') self._total_time_label = builder.get_object('duration') self.connect('key-release-event', self.__on_key_release_event) self.add(builder.get_object('widget'))
def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self.connect('show', self.__on_show) self.connect('hide', self.__on_hide) self.set_hexpand(True) self.__next_popover = NextPopover() self.__search = None self.__next_was_inhibited = False builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self.__grid_next = builder.get_object('grid-next') self.__shuffle_button = builder.get_object('shuffle-button') self.__shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self.__activate_shuffle_button) app.add_action(shuffleAction) app.set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self.__on_shuffle_changed) self.__party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self.__activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) self.__search_button = builder.get_object('search-button') search_action = Gio.SimpleAction.new('search', None) search_action.connect('activate', self._on_search_button_clicked) app.add_action(search_action) app.set_accels_for_action("app.search", ["<Control>f"]) self.__settings_button = builder.get_object('settings-button') self.__list_button = builder.get_object('list-button') self.__list_button.set_property('has-tooltip', True) self.__list_button.connect('query-tooltip', self.__on_list_button_query_tooltip) list_action = Gio.SimpleAction.new('list', None) list_action.connect('activate', self._on_list_button_clicked) app.add_action(list_action) app.set_accels_for_action("app.list", ["<Control>i"]) self.__list_popover = None Lp().player.connect('party-changed', self.__on_party_changed) Lp().player.connect('lock-changed', self.__on_lock_changed)
def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) self.connect('show', self.__on_show) self.connect('hide', self.__on_hide) self.set_hexpand(True) self.__next_popover = NextPopover() self.__search = None self.__next_was_inhibited = False self.__timeout_id = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self.__grid_next = builder.get_object('grid-next') self.__shuffle_button = builder.get_object('shuffle-button') self.__shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self.__activate_shuffle_button) Lp().add_action(shuffleAction) Lp().set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self.__on_playback_changed) Lp().settings.connect('changed::playback', self.__on_playback_changed) self.__party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self.__activate_party_button) Lp().add_action(party_action) Lp().set_accels_for_action("app.party", ["<Control>p"]) self.__search_button = builder.get_object('search-button') self.__helper = TouchHelper(self.__search_button, "search", "<Control>f") self.__helper.set_long_func(self.__on_search_long) self.__helper.set_short_func(self.__on_search_short) self.__settings_button = builder.get_object('settings-button') self.__list_button = builder.get_object('list-button') self.__list_button.set_property('has-tooltip', True) self.__list_button.connect('query-tooltip', self.__on_list_button_query_tooltip) self.__list_popover = None Lp().player.connect('party-changed', self.__on_party_changed) Lp().player.connect('lock-changed', self.__on_lock_changed)
def __init__(self, app): Gtk.Bin.__init__(self) self._pop_next = NextPopover() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._shuffle_btn = builder.get_object('shuffle-button') self._shuffle_btn_image = builder.get_object('shuffle-button-image') self._set_shuffle_icon() Lp.settings.connect('changed::shuffle', self._shuffle_btn_aspect) self._party_btn = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) search_button = builder.get_object('search-button') self._search = SearchWidget(self) self._search.set_relative_to(search_button) searchAction = Gio.SimpleAction.new('search', None) searchAction.connect('activate', self._on_search_btn_clicked) app.add_action(searchAction) app.set_accels_for_action("app.search", ["<Control>f"]) queue_button = builder.get_object('queue-button') self._queue = QueueWidget() self._queue.set_relative_to(queue_button) self._settings_button = builder.get_object('settings-button') Lp.player.connect('party-changed', self._on_party_changed)
def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_application(app) self._timeout1 = None self._timeout2 = None self._seeking = False self._signal1_id = None self._signal2_id = None self.set_decorated(False) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/FullScreen.ui') main_widget = builder.get_object('fs') self.add(main_widget) self._prev_btn = builder.get_object('prev_btn') self._prev_btn.connect('clicked', self._on_prev_btn_clicked) self._play_btn = builder.get_object('play_btn') self._play_btn.connect('clicked', self._on_play_btn_clicked) self._next_btn = builder.get_object('next_btn') self._next_btn.connect('clicked', self._on_next_btn_clicked) self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object('play_image') self._pause_image = builder.get_object('pause_image') close_btn = builder.get_object('close_btn') close_btn.connect('clicked', self._destroy) self._cover = builder.get_object('cover') self._title = builder.get_object('title') self._artist = builder.get_object('artist') self._album = builder.get_object('album') self._datetime = builder.get_object('datetime') self._progress = builder.get_object('progress_scale') self._progress.connect('button-release-event', self._on_progress_release_button) self._progress.connect('button-press-event', self._on_progress_press_button) self._timelabel = builder.get_object('playback') self._total_time_label = builder.get_object('duration') self.connect('key-release-event', self._on_key_release_event)
def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_title("Lollypop") PlaybackController.__init__(self) ProgressController.__init__(self) self.set_application(app) self._timeout1 = None self._timeout2 = None self._seeking = False self._signal1_id = None self._signal2_id = None self.set_decorated(False) self._parent = parent builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/FullScreen.ui') builder.connect_signals(self) # Calculate cover size screen = Gdk.Screen.get_default() monitor = screen.get_primary_monitor() geometry = screen.get_monitor_geometry(monitor) # We want 500 and 200 in full hd scale = self.get_scale_factor() if geometry.width > geometry.height: artsize = int(ArtSize.FULLSCREEN*geometry.width/1920/scale) else: artsize = int(ArtSize.FULLSCREEN*geometry.height/1920/scale) InfosController.__init__(self, artsize) self._play_btn = builder.get_object('play_btn') self._next_btn = builder.get_object('next_btn') self._prev_btn = builder.get_object('prev_btn') self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object('play_image') self._pause_image = builder.get_object('pause_image') close_btn = builder.get_object('close_btn') close_btn.connect('clicked', self._destroy) self._cover = builder.get_object('cover') self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._album_label = builder.get_object('album') self._datetime = builder.get_object('datetime') self._progress = builder.get_object('progress_scale') self._timelabel = builder.get_object('playback') self._total_time_label = builder.get_object('duration') self.connect('key-release-event', self._on_key_release_event) self.add(builder.get_object('widget'))
def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self.connect('show', self._on_show) self.connect('hide', self._on_hide) self._next_popover = NextPopover() self._search = None self._next_was_inhibited = False builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._grid_next = builder.get_object('grid-next') self._shuffle_button = builder.get_object('shuffle-button') self._shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self._activate_shuffle_button) app.add_action(shuffleAction) app.set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self._on_shuffle_changed) self._party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) self._search_button = builder.get_object('search-button') search_action = Gio.SimpleAction.new('search', None) search_action.connect('activate', self._on_search_button_clicked) app.add_action(search_action) app.set_accels_for_action("app.search", ["<Control>f"]) self._settings_button = builder.get_object('settings-button') self._list_button = builder.get_object('list-button') self._list_button.set_property('has-tooltip', True) self._list_button.connect('query-tooltip', self._on_list_button_query_tooltip) list_action = Gio.SimpleAction.new('list', None) list_action.connect('activate', self._on_list_button_clicked) app.add_action(list_action) app.set_accels_for_action("app.list", ["<Control>i"]) self._list_popover = None Lp().player.connect('party-changed', self._on_party_changed) Lp().player.connect('state-changed', self._on_lock_changed)
def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self.connect('show', self._on_show) self.connect('hide', self._on_hide) self._pop_next = NextPopover() self._queue = None self._search = None self._timeout_id = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._grid_next = builder.get_object('grid-next') self._shuffle_button = builder.get_object('shuffle-button') self._shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self._activate_shuffle_button) app.add_action(shuffleAction) app.set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self._on_shuffle_changed) self._party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) self._search_button = builder.get_object('search-button') searchAction = Gio.SimpleAction.new('search', None) searchAction.connect('activate', self._on_search_button_clicked) app.add_action(searchAction) app.set_accels_for_action("app.search", ["<Control>f"]) self._queue_button = builder.get_object('queue-button') queueAction = Gio.SimpleAction.new('queue', None) queueAction.connect('activate', self._on_queue_button_clicked) app.add_action(queueAction) app.set_accels_for_action("app.queue", ["<Control>l"]) self._settings_button = builder.get_object('settings-button') Lp().player.connect('party-changed', self._on_party_changed) Lp().player.connect('queue-changed', self._on_queue_changed)
def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self._pop_next = NextPopover() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._shuffle_btn = builder.get_object('shuffle-button') self._shuffle_btn_image = builder.get_object('shuffle-button-image') Lp().settings.connect('changed::shuffle', self._shuffle_btn_aspect) self._party_btn = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) search_button = builder.get_object('search-button') self._search = SearchPopover(self) self._search.set_relative_to(search_button) searchAction = Gio.SimpleAction.new('search', None) searchAction.connect('activate', self._on_search_btn_clicked) app.add_action(searchAction) app.set_accels_for_action("app.search", ["<Control>f"]) self._queue_button = builder.get_object('queue-button') self._settings_button = builder.get_object('settings-button') Lp().player.connect('party-changed', self._on_party_changed)
def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self._pop_next = NextPopover() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/ToolbarEnd.ui") builder.connect_signals(self) self.add(builder.get_object("end")) self._shuffle_btn = builder.get_object("shuffle-button") self._shuffle_btn_image = builder.get_object("shuffle-button-image") Lp.settings.connect("changed::shuffle", self._shuffle_btn_aspect) self._party_btn = builder.get_object("party-button") party_action = Gio.SimpleAction.new("party", None) party_action.connect("activate", self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) search_button = builder.get_object("search-button") self._search = SearchPopover(self) self._search.set_relative_to(search_button) searchAction = Gio.SimpleAction.new("search", None) searchAction.connect("activate", self._on_search_btn_clicked) app.add_action(searchAction) app.set_accels_for_action("app.search", ["<Control>f"]) queue_button = builder.get_object("queue-button") self._queue = QueueWidget() self._queue.set_relative_to(queue_button) self._settings_button = builder.get_object("settings-button") Lp.player.connect("party-changed", self._on_party_changed)
def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_title("Lollypop") PlaybackController.__init__(self) ProgressController.__init__(self) self.set_application(app) self.__timeout1 = None self.__timeout2 = None self.__signal1_id = None self.__signal2_id = None self.set_decorated(False) self.__parent = parent builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/FullScreen.ui") builder.connect_signals(self) # Calculate cover size screen = Gdk.Screen.get_default() monitor = screen.get_primary_monitor() geometry = screen.get_monitor_geometry(monitor) # We want 500 and 200 in full hd if geometry.width > geometry.height: artsize = int(ArtSize.FULLSCREEN * geometry.width / 1920) else: artsize = int(ArtSize.FULLSCREEN * geometry.height / 1920) InfoController.__init__(self, artsize) widget = builder.get_object("widget") self._play_btn = builder.get_object("play_btn") self._next_btn = builder.get_object("next_btn") self._prev_btn = builder.get_object("prev_btn") self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object("play_image") self._pause_image = builder.get_object("pause_image") close_btn = builder.get_object("close_btn") preferences = Gio.Settings.new("org.gnome.desktop.wm.preferences") layout = preferences.get_value("button-layout").get_string() if layout.split(":")[0] == "close": widget.attach(close_btn, 0, 0, 1, 1) close_btn.set_property("halign", Gtk.Align.START) else: widget.attach(close_btn, 2, 0, 1, 1) close_btn.set_property("halign", Gtk.Align.END) self._cover = builder.get_object("cover") self._title_label = builder.get_object("title") self._artist_label = builder.get_object("artist") self._album_label = builder.get_object("album") self._datetime = builder.get_object("datetime") self._progress = builder.get_object("progress_scale") self._timelabel = builder.get_object("playback") self._total_time_label = builder.get_object("duration") self.connect("key-release-event", self.__on_key_release_event) self.add(widget)
class FullScreen(Gtk.Window): """ Show a fullscreen window showing current track context """ def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_application(app) self._timeout1 = None self._timeout2 = None self._seeking = False self._signal1_id = None self._signal2_id = None self.set_decorated(False) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/FullScreen.ui') main_widget = builder.get_object('fs') self.add(main_widget) self._prev_btn = builder.get_object('prev_btn') self._prev_btn.connect('clicked', self._on_prev_btn_clicked) self._play_btn = builder.get_object('play_btn') self._play_btn.connect('clicked', self._on_play_btn_clicked) self._next_btn = builder.get_object('next_btn') self._next_btn.connect('clicked', self._on_next_btn_clicked) self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object('play_image') self._pause_image = builder.get_object('pause_image') close_btn = builder.get_object('close_btn') close_btn.connect('clicked', self._destroy) self._cover = builder.get_object('cover') self._title = builder.get_object('title') self._artist = builder.get_object('artist') self._album = builder.get_object('album') self._datetime = builder.get_object('datetime') self._progress = builder.get_object('progress_scale') self._progress.connect('button-release-event', self._on_progress_release_button) self._progress.connect('button-press-event', self._on_progress_press_button) self._timelabel = builder.get_object('playback') self._total_time_label = builder.get_object('duration') self.connect('key-release-event', self._on_key_release_event) def do_show(self): """ Init signals, set color and go party mode if nothing is playing """ is_playing = Lp.player.is_playing() self._signal1_id = Lp.player.connect('current-changed', self._on_current_changed) self._signal2_id = Lp.player.connect('status-changed', self._on_status_changed) if is_playing: self._change_play_btn_status(self._pause_image, _('Pause')) self._on_current_changed(Lp.player) else: Lp.player.set_party(True) if self._timeout1 is None: self._timeout1 = GLib.timeout_add(1000, self._update_position) Gtk.Window.do_show(self) now = datetime.now() self._datetime.set_label(now.strftime('%a %d %b, %X')[:-3]) if self._timeout2 is None: second = datetime.now().second if 60 - second > 0: GLib.timeout_add((60 - second) * 1000, self._update_datetime) else: self._timeout2 = GLib.timeout_add(60000, self._update_datetime) self._update_position() self.fullscreen() self._next_popover.set_relative_to(self._album) if Lp.player.next_track.id != Type.RADIOS: self._next_popover.show() def do_hide(self): """ Remove signals and unset color """ Gtk.Window.do_hide(self) if self._signal1_id is not None: Lp.player.disconnect(self._signal1_id) self._signal1_id = None if self._signal2_id is not None: Lp.player.disconnect(self._signal2_id) self._signal2_id = None if self._timeout1 is not None: GLib.source_remove(self._timeout1) self._timeout1 = None if self._timeout2 is not None: GLib.source_remove(self._timeout2) self._next_popover.set_relative_to(None) self._next_popover.hide() ####################### # PRIVATE # ####################### def _update_datetime(self): """ Update datetime in headerbar """ now = datetime.now() self._datetime.set_label(now.strftime('%a %d %b, %X')[:-3]) if self._timeout2 is None: self._timeout2 = GLib.timeout_add(60000, self._update_datetime) return False return True def _change_play_btn_status(self, image, status): """ Update play button with image and status as tooltip @param image as Gtk.Image @param status as str """ self._play_btn.set_image(image) self._play_btn.set_tooltip_text(status) def _update_position(self, value=None): """ Update progress bar position @param value as int """ if not self._seeking and self._progress.is_visible(): if value is None: value = Lp.player.get_position_in_track() / 1000000 self._progress.set_value(value) self._timelabel.set_text(seconds_to_string(value / 60)) return True def _destroy(self, widget): """ Destroy self @param widget as Gtk.Button """ self.destroy() def _on_current_changed(self, player): """ Update View for current track - Cover - artist/title - reset progress bar - update time/total labels @param player as Player """ if player.current_track.id is not None: if Lp.player.current_track.id == Type.RADIOS: self._timelabel.hide() self._total_time_label.hide() self._progress.hide() surface = Lp.art.get_radio( player.current_track.artist, ArtSize.MONSTER * self.get_scale_factor()) else: self._timelabel.show() self._total_time_label.show() self._progress.show() surface = Lp.art.get_album( player.current_track.album_id, ArtSize.MONSTER * self.get_scale_factor()) self._cover.set_from_surface(surface) del surface album = player.current_track.album if player.current_track.year != '': album += " (%s)" % player.current_track.year self._title.set_text(player.current_track.title) self._artist.set_text(player.current_track.artist) self._album.set_text(album) self._progress.set_value(1.0) self._progress.set_range(0.0, player.current_track.duration * 60) self._total_time_label.set_text( seconds_to_string(player.current_track.duration)) self._timelabel.set_text("0:00") # Can add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 prev_artist = escape(player.prev_track.artist) prev_title = escape(player.prev_track.title) next_artist = escape(player.next_track.artist) next_title = escape(player.next_track.title) self._next_btn.set_tooltip_markup("<b>%s</b> - %s" % (next_artist, next_title)) self._prev_btn.set_tooltip_markup("<b>%s</b> - %s" % (prev_artist, prev_title)) # Do not show next popover non internal tracks as # tags will be readed on the fly if player.next_track.id >= 0: self._next_popover.update() self._next_popover.show() else: self._next_popover.hide() def _on_key_release_event(self, widget, event): """ Destroy window if Esc @param widget as Gtk.Widget @param event as Gdk.event """ if event.keyval == Gdk.KEY_Escape: self.destroy() def _on_prev_btn_clicked(self, widget): """ Go to prev track @param widget as Gtk.Button """ Lp.player.prev() def _on_play_btn_clicked(self, widget): """ Play/pause @param widget as Gtk.Button """ if Lp.player.is_playing(): Lp.player.pause() widget.set_image(self._play_image) else: Lp.player.play() widget.set_image(self._pause_image) def _on_next_btn_clicked(self, widget): """ Go to next track @param widget as Gtk.Button """ Lp.player.next() def _on_status_changed(self, obj): """ Update buttons and progress bar @param obj as unused """ is_playing = Lp.player.is_playing() if Lp.player.current_track.id != Type.RADIOS: self._progress.set_sensitive(is_playing) if is_playing and not self._timeout1: self._timeout1 = GLib.timeout_add(1000, self._update_position) self._change_play_btn_status(self._pause_image, _("Pause")) elif not is_playing and self._timeout1: GLib.source_remove(self._timeout1) self._timeout1 = None self._change_play_btn_status(self._play_image, _("Play")) def _on_progress_press_button(self, scale, data): """ On press, mark player as seeking @param unused """ self._seeking = True def _on_progress_release_button(self, scale, data): """ Callback for scale release button Seek player to scale value @param scale as Gtk.Scale, data as unused """ value = scale.get_value() self._seeking = False self._update_position(value) Lp.player.seek(value / 60)
class ToolbarEnd(Gtk.Bin): """ Toolbar end """ def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self._pop_next = NextPopover() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._shuffle_btn = builder.get_object('shuffle-button') self._shuffle_btn_image = builder.get_object('shuffle-button-image') Lp().settings.connect('changed::shuffle', self._shuffle_btn_aspect) self._party_btn = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) search_button = builder.get_object('search-button') self._search = SearchPopover(self) self._search.set_relative_to(search_button) searchAction = Gio.SimpleAction.new('search', None) searchAction.connect('activate', self._on_search_btn_clicked) app.add_action(searchAction) app.set_accels_for_action("app.search", ["<Control>f"]) self._queue_button = builder.get_object('queue-button') self._settings_button = builder.get_object('settings-button') Lp().player.connect('party-changed', self._on_party_changed) def setup_menu_btn(self, menu): """ Add an application menu to menu button @parma: menu as Gio.Menu """ self._settings_button.show() self._settings_button.set_menu_model(menu) def on_status_changed(self, player): """ Update buttons on status changed @param player as Player """ if player.is_playing(): # Party mode can be activated # via Fullscreen class, so check button state self._party_btn.set_active(player.is_party()) def do_realize(self): """ Show popover if needed """ Gtk.Bin.do_realize(self) self._set_shuffle_icon() def on_next_changed(self, player): """ Update buttons on current changed @param player as Player """ # Do not show next popover non internal tracks as # tags will be readed on the fly if player.next_track.id is not None and\ player.next_track.id >= 0 and\ player.is_playing() and\ (player.is_party() or Lp().settings.get_enum('shuffle') == Shuffle.TRACKS): self._pop_next.update() self._pop_next.set_relative_to(self) self._pop_next.show() else: self._pop_next.hide() ####################### # PRIVATE # ####################### def _set_shuffle_icon(self): """ Set shuffle icon """ shuffle = Lp().settings.get_enum('shuffle') if shuffle == Shuffle.NONE: self._shuffle_btn_image.get_style_context().remove_class( 'selected') self._shuffle_btn_image.set_from_icon_name( "media-playlist-consecutive-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self._shuffle_btn_image.set_from_icon_name( "media-playlist-shuffle-symbolic", Gtk.IconSize.SMALL_TOOLBAR) if shuffle in [Shuffle.TRACKS, Shuffle.TRACKS_ARTIST]: self._shuffle_btn_image.get_style_context().add_class( 'selected') else: self._shuffle_btn_image.get_style_context().remove_class( 'selected') if shuffle == Shuffle.TRACKS: if Lp().player.next_track.id is not None and\ not self._pop_next.is_visible(): self._pop_next.set_relative_to(self) self._pop_next.update() self._pop_next.show() elif Lp().player.is_playing(): self._pop_next.set_relative_to(None) self._pop_next.hide() def _shuffle_btn_aspect(self, settings, value): """ Mark shuffle button as active when shuffle active @param settings as Gio.Settings, value as str """ self._set_shuffle_icon() def _activate_party_button(self, action=None, param=None): """ Activate party button @param action as Gio.SimpleAction @param param as GLib.Variant """ self._party_btn.set_active(not self._party_btn.get_active()) def _on_search_btn_clicked(self, obj, param=None): """ Show search widget on search button clicked @param obj as Gtk.Button or Gtk.Action """ self._search.show() def _on_queue_btn_clicked(self, button): """ Show queue widget on queue button clicked @param button as Gtk.Button """ queue = QueueWidget() queue.set_relative_to(self._queue_button) queue.populate() queue.show() def _on_party_btn_toggled(self, button): """ Set party mode on if party button active @param obj as Gtk.button """ active = self._party_btn.get_active() self._shuffle_btn.set_sensitive(not active) if not Lp().settings.get_value('dark-ui'): settings = Gtk.Settings.get_default() settings.set_property("gtk-application-prefer-dark-theme", active) is_playing = Lp().player.is_playing() Lp().player.set_party(active) if not active: self._pop_next.set_relative_to(None) self._pop_next.hide() elif is_playing and not self._pop_next.is_visible(): self._pop_next.set_relative_to(self) self._pop_next.update() self._pop_next.show() def _on_party_changed(self, player, is_party): """ On party change, sync toolbar @param player as Player @param is party as bool """ if self._party_btn.get_active() != is_party: self._activate_party_button()
class ToolbarEnd(Gtk.Bin): """ Toolbar end """ def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self.connect('show', self._on_show) self.connect('hide', self._on_hide) self._pop_next = NextPopover() self._queue = None self._search = None self._timeout_id = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._grid_next = builder.get_object('grid-next') self._shuffle_button = builder.get_object('shuffle-button') self._shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self._activate_shuffle_button) app.add_action(shuffleAction) app.set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self._on_shuffle_changed) self._party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) self._search_button = builder.get_object('search-button') searchAction = Gio.SimpleAction.new('search', None) searchAction.connect('activate', self._on_search_button_clicked) app.add_action(searchAction) app.set_accels_for_action("app.search", ["<Control>f"]) self._queue_button = builder.get_object('queue-button') queueAction = Gio.SimpleAction.new('queue', None) queueAction.connect('activate', self._on_queue_button_clicked) app.add_action(queueAction) app.set_accels_for_action("app.queue", ["<Control>l"]) self._settings_button = builder.get_object('settings-button') Lp().player.connect('party-changed', self._on_party_changed) Lp().player.connect('queue-changed', self._on_queue_changed) def setup_menu(self, menu): """ Add an application menu to menu button @parma: menu as Gio.Menu """ self._settings_button.show() self._settings_button.set_menu_model(menu) def on_status_changed(self, player): """ Update buttons on status changed @param player as Player """ if player.is_playing(): # Party mode can be activated # via Fullscreen class, so check button state self._party_button.set_active(player.is_party()) def on_next_changed(self, player, force=False): """ Show next popover @param player as Player @param force to show the popover """ # Do not show popover is this menu is active # or if we are hidden if self._shuffle_button.get_active() or\ not self._grid_next.is_visible(): return self._timeout_id = None if self._pop_next.should_be_shown() or force: self._pop_next.update() if not self._pop_next.is_visible(): self._pop_next.set_relative_to(self._grid_next) self._pop_next.show() else: self._pop_next.hide() ####################### # PRIVATE # ####################### def _on_button_press(self, button, event): """ Show next popover on long press @param widget as Gtk.Widget @param event as Gdk.Event """ self._timeout_id = GLib.timeout_add(500, self.on_next_changed, Lp().player, True) def _on_button_release(self, button, event): """ If next popover shown, block event @param widget as Gtk.Widget @param event as Gdk.Event """ if self._timeout_id is None: return True else: GLib.source_remove(self._timeout_id) self._timeout_id = None def _set_shuffle_icon(self): """ Set shuffle icon """ shuffle = Lp().settings.get_enum('shuffle') if shuffle == Shuffle.NONE: self._shuffle_image.get_style_context().remove_class('selected') self._shuffle_image.set_from_icon_name( "media-playlist-consecutive-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self._shuffle_image.set_from_icon_name( "media-playlist-shuffle-symbolic", Gtk.IconSize.SMALL_TOOLBAR) if shuffle == Shuffle.TRACKS: self._shuffle_image.get_style_context().add_class('selected') else: self._shuffle_image.get_style_context().remove_class( 'selected') def _activate_party_button(self, action=None, param=None): """ Activate party button @param action as Gio.SimpleAction @param param as GLib.Variant """ self._party_button.set_active(not self._party_button.get_active()) Lp().window.responsive_design() def _activate_shuffle_button(self, action=None, param=None): """ Activate shuffle button @param action as Gio.SimpleAction @param param as GLib.Variant """ self._shuffle_button.set_active(not self._shuffle_button.get_active()) def _on_shuffle_changed(self, settings, value): """ Mark shuffle button as active when shuffle active @param settings as Gio.Settings, value as str """ self._set_shuffle_icon() self._pop_next.hide() def _on_search_button_clicked(self, obj, param=None): """ Show search widget on search button clicked @param obj as Gtk.Button or Gtk.Action """ if self._search is None: self._search = SearchPopover() self._search.set_relative_to(self._search_button) self._search.show() def _on_queue_button_clicked(self, button, param=None): """ Show queue widget on queue button clicked @param obj as Gtk.Button or Gtk.Action """ if self._queue is None: self._queue = QueuePopover() self._queue.set_relative_to(self._queue_button) self._queue.show() def _on_party_button_toggled(self, button): """ Set party mode on if party button active @param obj as Gtk.button """ active = self._party_button.get_active() self._shuffle_button.set_sensitive(not active) if not Lp().settings.get_value('dark-ui'): settings = Gtk.Settings.get_default() settings.set_property("gtk-application-prefer-dark-theme", active) Lp().player.set_party(active) self.on_next_changed(Lp().player) def _on_queue_changed(self, player): """ On queue changed, change buttno aspect @param player as Player @param is party as bool """ if player.get_queue(): self._queue_button.get_style_context().add_class('selected') else: self._queue_button.get_style_context().remove_class('selected') def _on_party_changed(self, player, is_party): """ On party change, sync toolbar @param player as Player @param is party as bool """ if self._party_button.get_active() != is_party: self._activate_party_button() def _on_show(self, widget): """ Show popover if needed @param widget as Gtk.Widget """ self._set_shuffle_icon() def _on_hide(self, widget): """ Hide popover @param widget as Gtk.Widget """ self._pop_next.hide()
class ToolbarEnd(Gtk.Bin): """ Toolbar end """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) self.connect('show', self.__on_show) self.connect('hide', self.__on_hide) self.set_hexpand(True) self.__next_popover = NextPopover() self.__search = None self.__next_was_inhibited = False self.__timeout_id = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self.__grid_next = builder.get_object('grid-next') self.__shuffle_button = builder.get_object('shuffle-button') self.__shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self.__activate_shuffle_button) Lp().add_action(shuffleAction) Lp().set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self.__on_playback_changed) Lp().settings.connect('changed::playback', self.__on_playback_changed) self.__party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self.__activate_party_button) Lp().add_action(party_action) Lp().set_accels_for_action("app.party", ["<Control>p"]) self.__search_button = builder.get_object('search-button') self.__helper = TouchHelper(self.__search_button, "search", "<Control>f") self.__helper.set_long_func(self.__on_search_long) self.__helper.set_short_func(self.__on_search_short) self.__settings_button = builder.get_object('settings-button') self.__list_button = builder.get_object('list-button') self.__list_button.set_property('has-tooltip', True) self.__list_button.connect('query-tooltip', self.__on_list_button_query_tooltip) self.__list_popover = None Lp().player.connect('party-changed', self.__on_party_changed) Lp().player.connect('lock-changed', self.__on_lock_changed) def setup_menu(self, menu): """ Add an application menu to menu button @parma: menu as Gio.Menu """ self.__settings_button.show() self.__settings_button.set_menu_model(menu) def on_status_changed(self, player): """ Update buttons on status changed @param player as Player """ if player.is_playing: # Party mode can be activated # via Fullscreen class, so check button state self.__party_button.set_active(player.is_party) def on_next_changed(self, player, force=False): """ Show next popover @param player as Player @param force to show the popover """ # Do not show popover is this menu is active # or if we are hidden if self.__shuffle_button.get_active() or\ not self.__grid_next.is_visible(): return if self.__next_popover.should_be_shown() or force: if self.__next_popover.is_visible(): self.__next_popover.update() else: self.__next_popover.set_relative_to(self.__grid_next) self.__next_popover.show() else: self.__next_popover.hide() def search(self, search): """ Search item @param search as str """ self.__on_search_short([]) self.__search.set_text(search) ####################### # PROTECTED # ####################### def _on_shuffle_button_toggled(self, button): """ Hide next popover @param button as Gtk.Button """ if button.get_active(): self.__next_popover.hide() self.__next_popover.inhibit(True) else: self.__next_popover.inhibit(False) if self.__next_popover.should_be_shown(): self.__next_popover.show() def _on_party_button_toggled(self, button): """ Set party mode on if party button active @param obj as Gtk.button """ active = self.__party_button.get_active() self.__shuffle_button.set_sensitive(not active) if not Lp().settings.get_value('dark-ui'): settings = Gtk.Settings.get_default() settings.set_property("gtk-application-prefer-dark-theme", active) Lp().player.set_party(active) self.on_next_changed(Lp().player) def _on_party_press_event(self, eventbox, event): """ Show party popover @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if event.button == 3: popover = PartyPopover() popover.set_relative_to(eventbox) self.__next_popover.hide() popover.connect('closed', self.__on_popover_closed) self.__next_popover.inhibit(True) popover.show() return True def _on_list_button_clicked(self, widget, unused=None): """ Show current playback context popover @param widget as Gtk.Widget """ if self.__list_popover is not None: return self.__next_was_inhibited = self.__next_popover.inhibited self.__next_popover.hide() self.__next_popover.inhibit(True) if Lp().player.current_track.id == Type.EXTERNALS: from lollypop.pop_externals import ExternalsPopover self.__list_popover = ExternalsPopover() self.__list_popover.set_relative_to(self.__list_button) self.__list_popover.populate() self.__list_popover.show() elif Lp().player.get_queue(): from lollypop.pop_queue import QueuePopover self.__list_popover = QueuePopover() self.__list_popover.set_relative_to(self.__list_button) self.__list_popover.show() elif Lp().player.get_user_playlist_ids(): from lollypop.pop_playlists import PlaylistsPopover self.__list_popover = PlaylistsPopover() self.__list_popover.set_relative_to(self.__list_button) self.__list_popover.show() else: from lollypop.pop_albums import AlbumsPopover self.__list_popover = AlbumsPopover() self.__list_popover.set_relative_to(self.__list_button) self.__list_popover.show() self.__list_popover.connect('closed', self.__on_list_popover_closed) return True ####################### # PRIVATE # ####################### def __on_search_long(self, args): """ Show current track menu @param args as [] """ if Lp().window.view is not None: Lp().window.view.set_search_mode() def __on_search_short(self, args): """ Show search popover @param args as [] """ self.__next_was_inhibited = self.__next_popover.inhibited self.__next_popover.hide() self.__next_popover.inhibit(True) if self.__search is None: from lollypop.pop_search import SearchPopover self.__search = SearchPopover() self.__search.connect('closed', self.__on_popover_closed) self.__search.set_relative_to(self.__search_button) self.__search.show() def __set_icon(self): """ Set shuffle icon """ shuffle = Lp().settings.get_enum('shuffle') repeat = Lp().settings.get_enum('playback') if repeat == NextContext.REPEAT_TRACK: self.__shuffle_image.get_style_context().remove_class('selected') self.__shuffle_image.set_from_icon_name( "media-playlist-repeat-song-symbolic", Gtk.IconSize.SMALL_TOOLBAR) elif shuffle == Shuffle.NONE: self.__shuffle_image.get_style_context().remove_class('selected') if repeat == NextContext.NONE: self.__shuffle_image.set_from_icon_name( "media-playlist-repeat-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self.__shuffle_image.set_from_icon_name( "media-playlist-consecutive-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self.__shuffle_image.set_from_icon_name( "media-playlist-shuffle-symbolic", Gtk.IconSize.SMALL_TOOLBAR) if shuffle == Shuffle.TRACKS: self.__shuffle_image.get_style_context().add_class('selected') else: self.__shuffle_image.get_style_context().remove_class( 'selected') def __activate_party_button(self, action=None, param=None): """ Activate party button @param action as Gio.SimpleAction @param param as GLib.Variant """ self.__party_button.set_active(not self.__party_button.get_active()) Lp().window.responsive_design() def __activate_shuffle_button(self, action=None, param=None): """ Activate shuffle button @param action as Gio.SimpleAction @param param as GLib.Variant """ self.__shuffle_button.set_active( not self.__shuffle_button.get_active()) def __on_lock_changed(self, player): """ Lock toolbar @param player as Player """ self.__party_button.set_sensitive(not player.locked) self.__list_button.set_sensitive(not player.locked) self.__shuffle_button.set_sensitive(not player.locked) def __on_playback_changed(self, settings, value): """ Update shuffle icon @param settings as Gio.Settings, value as str """ self.__set_icon() self.__next_popover.hide() def __on_party_changed(self, player, is_party): """ On party change, sync toolbar @param player as Player @param is party as bool """ if self.__party_button.get_active() != is_party: self.__activate_party_button() def __on_list_popover_closed(self, popover): """ Reset variable @param popover as Gtk.Popover """ self.__list_popover = None self.__on_popover_closed(popover) def __on_popover_closed(self, popover): """ Restore next popover if needed @param popover as Gtk.Popover """ if not self.__next_was_inhibited: self.__next_popover.inhibit(False) if self.__next_popover.should_be_shown(): self.__next_popover.set_relative_to(self.__grid_next) self.__next_popover.show() def __on_show(self, widget): """ Show popover if needed @param widget as Gtk.Widget """ self.__set_icon() def __on_hide(self, widget): """ Hide popover @param widget as Gtk.Widget """ self.__next_popover.hide() def __on_list_button_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ if Lp().player.current_track.id == Type.EXTERNALS: widget.set_tooltip_text(_("External tracks")) elif Lp().player.get_queue(): widget.set_tooltip_text(_("Queue")) elif Lp().player.get_user_playlist_ids(): widget.set_tooltip_text(_("Playing playlists")) else: widget.set_tooltip_text(_("Playing albums"))
class FullScreen(Gtk.Window, InfoController, PlaybackController, ProgressController): """ Show a fullscreen window showing current track context """ def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_title("Lollypop") PlaybackController.__init__(self) ProgressController.__init__(self) self.set_application(app) self.__timeout1 = None self.__timeout2 = None self.__signal1_id = None self.__signal2_id = None self.set_decorated(False) self.__parent = parent builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/FullScreen.ui") builder.connect_signals(self) # Calculate cover size screen = Gdk.Screen.get_default() monitor = screen.get_primary_monitor() geometry = screen.get_monitor_geometry(monitor) # We want 500 and 200 in full hd if geometry.width > geometry.height: artsize = int(ArtSize.FULLSCREEN * geometry.width / 1920) else: artsize = int(ArtSize.FULLSCREEN * geometry.height / 1920) InfoController.__init__(self, artsize) self._play_btn = builder.get_object("play_btn") self._next_btn = builder.get_object("next_btn") self._prev_btn = builder.get_object("prev_btn") self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object("play_image") self._pause_image = builder.get_object("pause_image") close_btn = builder.get_object("close_btn") close_btn.connect("clicked", self.__destroy) self._cover = builder.get_object("cover") self._title_label = builder.get_object("title") self._artist_label = builder.get_object("artist") self._album_label = builder.get_object("album") self._datetime = builder.get_object("datetime") self._progress = builder.get_object("progress_scale") self._timelabel = builder.get_object("playback") self._total_time_label = builder.get_object("duration") self.connect("key-release-event", self.__on_key_release_event) self.add(builder.get_object("widget")) def do_show(self): """ Init signals, set color and go party mode if nothing is playing """ self.__signal1_id = Lp().player.connect("current-changed", self.on_current_changed) self.__signal2_id = Lp().player.connect("status-changed", self.on_status_changed) if Lp().player.current_track.id is None: Lp().player.set_party(True) else: self.on_status_changed(Lp().player) self.on_current_changed(Lp().player) if self.__timeout1 is None: self.__timeout1 = GLib.timeout_add(1000, self._update_position) Gtk.Window.do_show(self) self.__parent.set_skip_pager_hint(True) self.__parent.set_skip_taskbar_hint(True) now = datetime.now() self._datetime.set_label(now.strftime("%a %d %b, %X")[:-3]) if self.__timeout2 is None: second = datetime.now().second if 60 - second > 0: GLib.timeout_add((60 - second) * 1000, self.__update_datetime) else: self.__timeout2 = GLib.timeout_add(60000, self.__update_datetime) self._update_position(Lp().player.position / 1000000) self.fullscreen() self._next_popover.set_relative_to(self._album_label) if Lp().player.next_track.id != Type.RADIOS: self._next_popover.show() # Disable idle self.__cookie = Lp().inhibit(Lp().window, Gtk.ApplicationInhibitFlags.IDLE, None) def do_hide(self): """ Remove signals and unset color """ self.__parent.set_skip_pager_hint(False) self.__parent.set_skip_taskbar_hint(False) Gtk.Window.do_hide(self) if self.__signal1_id is not None: Lp().player.disconnect(self.__signal1_id) self.__signal1_id = None if self.__signal2_id is not None: Lp().player.disconnect(self.__signal2_id) self.__signal2_id = None if self.__timeout1 is not None: GLib.source_remove(self.__timeout1) self.__timeout1 = None if self.__timeout2 is not None: GLib.source_remove(self.__timeout2) self._next_popover.set_relative_to(None) self._next_popover.hide() Lp().uninhibit(self.__cookie) def show_hide_volume_control(self): """ Show/Hide volume control """ self._show_volume_control = not self._show_volume_control self._update_state() def on_current_changed(self, player): """ Update infos and show/hide popover @param player as Player """ InfoController.on_current_changed(self, player) ProgressController.on_current_changed(self, player) if player.current_track.id is not None: album_name = player.current_track.album.name if player.current_track.year: album_name += " (%s)" % player.current_track.year self._album_label.set_text(album_name) # Do not show next popover non internal tracks as # tags will be readed on the fly if player.next_track.id is not None and player.next_track.id >= 0: self._next_popover.update() self._next_popover.show() else: self._next_popover.hide() ####################### # PRIVATE # ####################### def __update_datetime(self): """ Update datetime in headerbar """ now = datetime.now() self._datetime.set_label(now.strftime("%a %d %b, %X")[:-3]) if self.__timeout2 is None: self.__timeout2 = GLib.timeout_add(60000, self.__update_datetime) return False return True def __destroy(self, widget): """ Destroy self @param widget as Gtk.Button """ self.destroy() def __on_key_release_event(self, widget, event): """ Destroy window if Esc @param widget as Gtk.Widget @param event as Gdk.event """ if event.keyval == Gdk.KEY_Escape: self.destroy()
def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) self.set_hexpand(True) self.__search_popover = None self.__timeout_id = None builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/ToolbarEnd.ui") builder.connect_signals(self) self.__party_submenu = builder.get_object("party_submenu") self.add(builder.get_object("end")) # Map some settings to actions, can't use Gio.Settings.create_action() # because API does not support set_enabled() self.__shuffle_action = Gio.SimpleAction.new_stateful( "shuffle", GLib.VariantType.new("s"), GLib.Variant("s", "none")) self.__shuffle_action.set_state(App().settings.get_value("shuffle")) self.__shuffle_action.connect("change-state", self.__on_shuffle_change_state) self.__playback_action = Gio.SimpleAction.new_stateful( "playback", GLib.VariantType.new("s"), GLib.Variant("s", "none")) self.__playback_action.set_state(App().settings.get_value("playback")) self.__playback_action.connect("change-state", self.__on_playback_change_state) App().add_action(self.__shuffle_action) App().add_action(self.__playback_action) self.__shuffle_button = builder.get_object("shuffle-button") self.__shuffle_image = builder.get_object("shuffle-button-image") shuffle_button_action = Gio.SimpleAction.new("shuffle-button", None) shuffle_button_action.connect("activate", self.__on_shuffle_button_activate) App().add_action(shuffle_button_action) App().set_accels_for_action("app.shuffle-button", ["<Control>r"]) App().settings.connect("changed::shuffle", self.__on_playback_changed) App().settings.connect("changed::playback", self.__on_playback_changed) party_action = Gio.SimpleAction.new_stateful( "party", None, GLib.Variant.new_boolean(App().player.is_party)) party_action.connect("change-state", self.__on_party_mode_change_state) App().add_action(party_action) App().set_accels_for_action("app.party", ["<Control>p"]) scrobbling_disabled = App().settings.get_value("disable-scrobbling") scrobbling_action = Gio.SimpleAction.new_stateful( "scrobbling", None, GLib.Variant.new_boolean(not scrobbling_disabled)) scrobbling_action.connect("change-state", self.__on_scrobbling_mode_change_state) App().add_action(scrobbling_action) App().set_accels_for_action("app.scrobbling", ["<Control><Shift>s"]) self.__next_popover = NextPopover() self.__next_popover.set_relative_to(self.__shuffle_button) search_action = Gio.SimpleAction.new("search", None) self.__search_button = builder.get_object("search-button") search_action.connect("activate", self.__on_search_activate) App().add_action(search_action) App().set_accels_for_action("app.search", ["<Control>f"]) builder_menu = Gtk.Builder() builder_menu.add_from_resource("/org/gnome/Lollypop/Appmenu.ui") builder.get_object("settings-button").set_menu_model( builder_menu.get_object("app-menu")) self.__list_button = builder.get_object("list-button") self.__list_button.set_property("has-tooltip", True) self.__list_button.connect("query-tooltip", self.__on_list_button_query_tooltip) self.__list_popover = None App().player.connect("playlist-changed", self.__on_playlist_changed) self.__set_shuffle_icon()
class ToolbarEnd(Gtk.Bin): """ Toolbar end """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) self.set_hexpand(True) self.__search_popover = None self.__timeout_id = None builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/ToolbarEnd.ui") builder.connect_signals(self) self.__party_submenu = builder.get_object("party_submenu") self.add(builder.get_object("end")) # Map some settings to actions, can't use Gio.Settings.create_action() # because API does not support set_enabled() self.__shuffle_action = Gio.SimpleAction.new_stateful( "shuffle", GLib.VariantType.new("s"), GLib.Variant("s", "none")) self.__shuffle_action.set_state(App().settings.get_value("shuffle")) self.__shuffle_action.connect("change-state", self.__on_shuffle_change_state) self.__playback_action = Gio.SimpleAction.new_stateful( "playback", GLib.VariantType.new("s"), GLib.Variant("s", "none")) self.__playback_action.set_state(App().settings.get_value("playback")) self.__playback_action.connect("change-state", self.__on_playback_change_state) App().add_action(self.__shuffle_action) App().add_action(self.__playback_action) self.__shuffle_button = builder.get_object("shuffle-button") self.__shuffle_image = builder.get_object("shuffle-button-image") shuffle_button_action = Gio.SimpleAction.new("shuffle-button", None) shuffle_button_action.connect("activate", self.__on_shuffle_button_activate) App().add_action(shuffle_button_action) App().set_accels_for_action("app.shuffle-button", ["<Control>r"]) App().settings.connect("changed::shuffle", self.__on_playback_changed) App().settings.connect("changed::playback", self.__on_playback_changed) party_action = Gio.SimpleAction.new_stateful( "party", None, GLib.Variant.new_boolean(App().player.is_party)) party_action.connect("change-state", self.__on_party_mode_change_state) App().add_action(party_action) App().set_accels_for_action("app.party", ["<Control>p"]) scrobbling_disabled = App().settings.get_value("disable-scrobbling") scrobbling_action = Gio.SimpleAction.new_stateful( "scrobbling", None, GLib.Variant.new_boolean(not scrobbling_disabled)) scrobbling_action.connect("change-state", self.__on_scrobbling_mode_change_state) App().add_action(scrobbling_action) App().set_accels_for_action("app.scrobbling", ["<Control><Shift>s"]) self.__next_popover = NextPopover() self.__next_popover.set_relative_to(self.__shuffle_button) search_action = Gio.SimpleAction.new("search", None) self.__search_button = builder.get_object("search-button") search_action.connect("activate", self.__on_search_activate) App().add_action(search_action) App().set_accels_for_action("app.search", ["<Control>f"]) builder_menu = Gtk.Builder() builder_menu.add_from_resource("/org/gnome/Lollypop/Appmenu.ui") builder.get_object("settings-button").set_menu_model( builder_menu.get_object("app-menu")) self.__list_button = builder.get_object("list-button") self.__list_button.set_property("has-tooltip", True) self.__list_button.connect("query-tooltip", self.__on_list_button_query_tooltip) self.__list_popover = None App().player.connect("playlist-changed", self.__on_playlist_changed) self.__set_shuffle_icon() def on_next_changed(self, player): """ Show next popover @param player as Player """ if self.__next_popover.should_be_shown(): if self.__next_popover.is_visible(): self.__next_popover.update() else: self.__next_popover.popup() else: self.__next_popover.hide() def set_mini(self, mini): """ Set mini mode @param mini as bool """ if mini: self.__search_button.hide() self.__list_button.hide() else: self.__search_button.show() self.__list_button.show() def search(self, search): """ Search item @param search as str """ self.__on_search_button_cancelled() self.__search_popover.set_text(search) @property def next_popover(self): """ Get next popover @return popover """ return self.__next_popover ####################### # PROTECTED # ####################### def _on_list_button_toggled(self, button): """ Show current playback context popover @param button as Gtk.MenuButton """ if not button.get_active(): return self.__next_popover.hide() self.__next_popover.inhibit(True) if App().player.queue: from lollypop.pop_queue import QueuePopover popover = QueuePopover() elif App().player.playlist_ids: from lollypop.pop_playlists import PlaylistsPopover popover = PlaylistsPopover() else: from lollypop.pop_albums import AlbumsPopover popover = AlbumsPopover() popover.set_relative_to(button) popover.popup() popover.connect("closed", self.__on_popover_closed, button) return True def _on_search_button_toggled(self, button): """ Show search popover @param button as Gtk.Button """ if not button.get_active(): self.__search_popover.popdown() return self.__next_popover.hide() self.__next_popover.inhibit(True) if self.__search_popover is None: from lollypop.pop_search import SearchPopover self.__search_popover = SearchPopover() self.__search_popover.connect("closed", self.__on_popover_closed, button) self.__search_popover.set_relative_to(button) self.__search_popover.popup() return True def _on_shuffle_button_toggled(self, button): """ Create submenu @param button as Gtk.MenuButton """ if button.get_active(): # Create submenu "Configure party mode" self.__party_submenu.remove_all() self.__init_party_submenu() self.__next_popover.hide() self.__next_popover.inhibit(True) else: self.__next_popover.inhibit(False) if self.__next_popover.should_be_shown(): self.__next_popover.popup() def _on_settings_button_toggled(self, button): """ Create submenu @param button as Gtk.MenuButton """ if button.get_active(): self.__next_popover.hide() self.__next_popover.inhibit(True) else: self.__next_popover.inhibit(False) if self.__next_popover.should_be_shown(): self.__next_popover.popup() ####################### # PRIVATE # ####################### def __init_party_submenu(self): """ Init party submenu with current ids """ def on_change_state(action, value, genre_id): action.set_state(value) ids = list(App().settings.get_value("party-ids")) genre_ids = App().genres.get_ids() # Select all if genre_id is None: # Update others for genre_id in genre_ids: action = App().lookup_action("genre_%s" % genre_id) if action.get_state() != value: action.set_state(value) ids = [] # Party id added elif value: ids.append(genre_id) # Party id removed elif ids and len(party_ids) > 1: ids.remove(genre_id) # Initial value else: genre_ids.remove(genre_id) App().settings.set_value("party-ids", GLib.Variant("ai", ids)) App().player.set_party_ids() App().player.set_next() party_ids = App().settings.get_value("party-ids") all_ids = App().genres.get_ids() all_selected = len(set(all_ids) & set(party_ids)) == len(all_ids) or\ not party_ids action = Gio.SimpleAction.new_stateful( "all_party_ids", None, GLib.Variant.new_boolean(all_selected)) action.connect("change-state", on_change_state, None) App().add_action(action) item = Gio.MenuItem.new(_("All genres"), "app.all_party_ids") self.__party_submenu.append_item(item) i = 0 # Hack, hack, hack submenu_name = _("Next") menu = self.__party_submenu for (genre_id, name, sortname) in App().genres.get(): in_party_ids = not party_ids or genre_id in party_ids action_name = "genre_%s" % genre_id action = Gio.SimpleAction.new_stateful( action_name, None, GLib.Variant.new_boolean(in_party_ids)) action.connect("change-state", on_change_state, genre_id) App().add_action(action) item = Gio.MenuItem.new(name, "app.%s" % action_name) menu.append_item(item) if i > 10: submenu = Gio.Menu() item = Gio.MenuItem.new(submenu_name, None) submenu_name += " " item.set_submenu(submenu) menu.append_item(item) menu = submenu i = 0 i += 1 def __set_shuffle_icon(self): """ Set shuffle icon """ shuffle = App().settings.get_enum("shuffle") repeat = App().settings.get_enum("playback") if repeat == NextContext.REPEAT_TRACK: self.__shuffle_image.get_style_context().remove_class("selected") self.__shuffle_image.set_from_icon_name( "media-playlist-repeat-song-symbolic", Gtk.IconSize.SMALL_TOOLBAR) elif shuffle == Shuffle.NONE: self.__shuffle_image.get_style_context().remove_class("selected") if repeat == NextContext.NONE: self.__shuffle_image.set_from_icon_name( "media-playlist-repeat-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self.__shuffle_image.set_from_icon_name( "media-playlist-consecutive-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self.__shuffle_image.set_from_icon_name( "media-playlist-shuffle-symbolic", Gtk.IconSize.SMALL_TOOLBAR) if shuffle == Shuffle.TRACKS: self.__shuffle_image.get_style_context().add_class("selected") else: self.__shuffle_image.get_style_context().remove_class( "selected") def __on_party_mode_change_state(self, action, value): """ Activate party mode @param action as Gio.SimpleAction @param value as bool """ if not App().gtk_application_prefer_dark_theme and\ not App().settings.get_value("dark-ui"): settings = Gtk.Settings.get_default() settings.set_property("gtk-application-prefer-dark-theme", value) App().player.set_party(value.get_boolean()) action.set_state(value) self.__shuffle_action.set_enabled(not value) self.__playback_action.set_enabled(not value) self.on_next_changed(App().player) def __on_scrobbling_mode_change_state(self, action, value): """ Change scrobbling option @param action as Gio.SimpleAction @param value as bool """ action.set_state(value) App().settings.set_value("disable-scrobbling", GLib.Variant("b", not value)) def __on_shuffle_change_state(self, action, value): """ Update shuffle setting @param action as Gio.SimpleAction @param value as bool """ App().settings.set_value("shuffle", value) action.set_state(value) def __on_playback_change_state(self, action, value): """ Update playback setting @param action as Gio.SimpleAction @param value as bool """ App().settings.set_value("playback", value) action.set_state(value) def __on_shuffle_button_activate(self, action, param): """ Activate shuffle button @param action as Gio.SimpleAction @param param as GLib.Variant """ self.__shuffle_button.set_active( not self.__shuffle_button.get_active()) def __on_playback_changed(self, settings, value): """ Update shuffle icon @param settings as Gio.Settings, value as str """ self.__set_shuffle_icon() self.__next_popover.hide() def __on_popover_closed(self, popover, button): """ Restore next popover if needed @param popover as Popover @param button as Gtk.Button """ button.set_active(False) self.__next_popover.inhibit(False) if self.__next_popover.should_be_shown(): self.__next_popover.popup() def __on_search_activate(self, action, variant): """ @param action as Gio.SimpleAction @param variant as GLib.Variant """ if self.__search_button.get_visible(): self.__search_button.set_active( not self.__search_button.get_active()) else: App().window.container.show_view(Type.SEARCH) def __on_list_button_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ if App().player.queue: widget.set_tooltip_text(_("Queue")) elif App().player.playlist_ids: widget.set_tooltip_text(_("Playing playlists")) else: widget.set_tooltip_text(_("Playing albums")) def __on_playlist_changed(self, player): """ Update playback button status @param player as Player """ if player.albums or player.playlist_ids or player.queue: self.__list_button.set_sensitive(True) else: self.__list_button.set_sensitive(False)
def _on_party_changed(self, player, is_party): # GTK fail to change colors on popover, so destroy it self._pop_next.destroy() self._pop_next = NextPopover() if self._party_btn.get_active() != is_party: self._activate_party_button()
class FullScreen(Gtk.Window, InfoController, PlaybackController, ProgressController): """ Show a fullscreen window showing current track context """ def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_title("Lollypop") PlaybackController.__init__(self) ProgressController.__init__(self) self.set_application(app) self.__timeout1 = None self.__timeout2 = None self.__signal1_id = None self.__signal2_id = None self.set_decorated(False) self.__parent = parent builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/FullScreen.ui") builder.connect_signals(self) # Calculate cover size screen = Gdk.Screen.get_default() monitor = screen.get_monitor_at_window(parent.get_window()) geometry = screen.get_monitor_geometry(monitor) # We want 500 and 200 in full hd if geometry.width > geometry.height: artsize = int(ArtSize.FULLSCREEN * geometry.height / 1080) else: artsize = int(ArtSize.FULLSCREEN * geometry.width / 1920) InfoController.__init__(self, artsize) widget = builder.get_object("widget") self._play_btn = builder.get_object("play_btn") self._next_btn = builder.get_object("next_btn") self._prev_btn = builder.get_object("prev_btn") self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.RIGHT) self._play_image = builder.get_object("play_image") self._pause_image = builder.get_object("pause_image") close_btn = builder.get_object("close_btn") preferences = Gio.Settings.new("org.gnome.desktop.wm.preferences") layout = preferences.get_value("button-layout").get_string() if layout.split(":")[0] == "close": widget.attach(close_btn, 0, 0, 1, 1) close_btn.set_property("halign", Gtk.Align.START) else: widget.attach(close_btn, 2, 0, 1, 1) close_btn.set_property("halign", Gtk.Align.END) self._cover = builder.get_object("cover") self._title_label = builder.get_object("title") self._artist_label = builder.get_object("artist") self._album_label = builder.get_object("album") self._datetime = builder.get_object("datetime") self._progress = builder.get_object("progress_scale") self._timelabel = builder.get_object("playback") self._total_time_label = builder.get_object("duration") self.connect("key-release-event", self.__on_key_release_event) self.add(widget) def do_show(self): """ Init signals, set color and go party mode if nothing is playing """ self.__signal1_id = Lp().player.connect("current-changed", self.on_current_changed) self.__signal2_id = Lp().player.connect("status-changed", self.on_status_changed) if Lp().player.current_track.id is None: Lp().player.set_party(True) else: self.on_status_changed(Lp().player) self.on_current_changed(Lp().player) if self.__timeout1 is None: self.__timeout1 = GLib.timeout_add(1000, self._update_position) Gtk.Window.do_show(self) self.__parent.set_skip_pager_hint(True) self.__parent.set_skip_taskbar_hint(True) if self.__timeout2 is None: try: interface = Gio.Settings.new("org.gnome.desktop.interface") show_seconds = interface.get_value("clock-show-seconds") except: show_seconds = False self.__update_datetime(show_seconds) self.__timeout2 = GLib.timeout_add(1000, self.__update_datetime, show_seconds) self._update_position(Lp().player.position / Gst.SECOND) screen = Gdk.Screen.get_default() monitor = screen.get_monitor_at_window(self.__parent.get_window()) self.fullscreen_on_monitor(screen, monitor) self._next_popover.set_relative_to(self._next_btn) if Lp().player.next_track.id != Type.RADIOS: self._next_popover.show() # Disable screensaver (idle) Lp().inhibitor.manual_inhibit(suspend=False, idle=True) def do_hide(self): """ Remove signals and unset color """ self.__parent.set_skip_pager_hint(False) self.__parent.set_skip_taskbar_hint(False) Gtk.Window.do_hide(self) if self.__signal1_id is not None: Lp().player.disconnect(self.__signal1_id) self.__signal1_id = None if self.__signal2_id is not None: Lp().player.disconnect(self.__signal2_id) self.__signal2_id = None if self.__timeout1 is not None: GLib.source_remove(self.__timeout1) self.__timeout1 = None if self.__timeout2 is not None: GLib.source_remove(self.__timeout2) self._next_popover.set_relative_to(None) self._next_popover.hide() Lp().inhibitor.manual_uninhibit() def show_hide_volume_control(self): """ Show/Hide volume control """ self._show_volume_control = not self._show_volume_control self._update_state() def on_current_changed(self, player): """ Update infos and show/hide popover @param player as Player """ InfoController.on_current_changed(self, player) ProgressController.on_current_changed(self, player) if player.current_track.id is not None: album_name = player.current_track.album.name if player.current_track.year: album_name += " (%s)" % player.current_track.year self._album_label.set_text(album_name) # Do not show next popover non internal tracks as # tags will be readed on the fly if player.next_track.id is not None and player.next_track.id >= 0: self._next_popover.update() self._next_popover.show() else: self._next_popover.hide() ####################### # PROTECTED # ####################### def _on_close_button_clicked(self, widget): """ Destroy self @param widget as Gtk.Button """ self.destroy() ####################### # PRIVATE # ####################### def __update_datetime(self, show_seconds=False): """ Update datetime in headerbar @param show_seconds as bool """ now = datetime.now() if show_seconds: self._datetime.set_label(now.strftime("%a %d %b, %X")) else: self._datetime.set_label(now.strftime("%a %d %b, %X")[:-3]) if self.__timeout2 is None: self.__timeout2 = GLib.timeout_add(60000, self.__update_datetime) return False return True def __on_key_release_event(self, widget, event): """ Destroy window if Esc @param widget as Gtk.Widget @param event as Gdk.event """ if event.keyval == Gdk.KEY_Escape: self.destroy()
class FullScreen(Gtk.Window): """ Show a fullscreen window showing current track context """ def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) self.set_application(app) self._timeout1 = None self._timeout2 = None self._seeking = False self._signal1_id = None self._signal2_id = None self.set_decorated(False) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/FullScreen.ui') builder.connect_signals(self) # Calculate cover size screen = Gdk.Screen.get_default() monitor = screen.get_primary_monitor() geometry = screen.get_monitor_geometry(monitor) # We want 500 and 200 in full hd if geometry.width > geometry.height: self._artsize = int(ArtSize.MONSTER*geometry.width/1920) else: self._artsize = int(ArtSize.MONSTER*geometry.height/1920) self._play_btn = builder.get_object('play_btn') self._next_btn = builder.get_object('next_btn') self._prev_btn = builder.get_object('prev_btn') self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object('play_image') self._pause_image = builder.get_object('pause_image') close_btn = builder.get_object('close_btn') close_btn.connect('clicked', self._destroy) self._cover = builder.get_object('cover') self._title = builder.get_object('title') self._artist = builder.get_object('artist') self._album = builder.get_object('album') self._datetime = builder.get_object('datetime') self._progress = builder.get_object('progress_scale') self._timelabel = builder.get_object('playback') self._total_time_label = builder.get_object('duration') self.connect('key-release-event', self._on_key_release_event) self.add(builder.get_object('widget')) def do_show(self): """ Init signals, set color and go party mode if nothing is playing """ is_playing = Lp().player.is_playing() self._signal1_id = Lp().player.connect('current-changed', self._on_current_changed) self._signal2_id = Lp().player.connect('status-changed', self._on_status_changed) if Lp().player.current_track is None: Lp().player.set_party(True) else: if is_playing: self._change_play_btn_status(self._pause_image, _('Pause')) else: self._on_status_changed(Lp().player) self._on_current_changed(Lp().player) if self._timeout1 is None: self._timeout1 = GLib.timeout_add(1000, self._update_position) Gtk.Window.do_show(self) now = datetime.now() self._datetime.set_label(now.strftime('%a %d %b, %X')[:-3]) if self._timeout2 is None: second = datetime.now().second if 60 - second > 0: GLib.timeout_add((60-second)*1000, self._update_datetime) else: self._timeout2 = GLib.timeout_add(60000, self._update_datetime) self._update_position() self.fullscreen() self._next_popover.set_relative_to(self._album) if Lp().player.next_track.id != Type.RADIOS: self._next_popover.show() def do_hide(self): """ Remove signals and unset color """ Gtk.Window.do_hide(self) if self._signal1_id is not None: Lp().player.disconnect(self._signal1_id) self._signal1_id = None if self._signal2_id is not None: Lp().player.disconnect(self._signal2_id) self._signal2_id = None if self._timeout1 is not None: GLib.source_remove(self._timeout1) self._timeout1 = None if self._timeout2 is not None: GLib.source_remove(self._timeout2) self._next_popover.set_relative_to(None) self._next_popover.hide() ####################### # PRIVATE # ####################### def _update_datetime(self): """ Update datetime in headerbar """ now = datetime.now() self._datetime.set_label(now.strftime('%a %d %b, %X')[:-3]) if self._timeout2 is None: self._timeout2 = GLib.timeout_add(60000, self._update_datetime) return False return True def _change_play_btn_status(self, image, status): """ Update play button with image and status as tooltip @param image as Gtk.Image @param status as str """ self._play_btn.set_image(image) self._play_btn.set_tooltip_text(status) def _update_position(self, value=None): """ Update progress bar position @param value as int """ if not self._seeking and self._progress.is_visible(): if value is None and Lp().player.get_status() != Gst.State.PAUSED: value = Lp().player.get_position_in_track()/1000000 if value is not None: self._progress.set_value(value) self._timelabel.set_text(seconds_to_string(value/60)) return True def _destroy(self, widget): """ Destroy self @param widget as Gtk.Button """ self.destroy() def _on_current_changed(self, player): """ Update View for current track - Cover - artist/title - reset progress bar - update time/total labels @param player as Player """ if player.current_track.id is not None: if Lp().player.current_track.id == Type.RADIOS: self._timelabel.hide() self._total_time_label.hide() self._progress.hide() surface = Lp().art.get_radio_artwork( player.current_track.artist, self._artsize*self.get_scale_factor()) else: self._timelabel.show() self._total_time_label.show() self._progress.show() surface = Lp().art.get_album_artwork( player.current_track.album, self._artsize*self.get_scale_factor()) self._cover.set_from_surface(surface) del surface album_name = player.current_track.album.name if player.current_track.year != '': album_name += " (%s)" % player.current_track.year self._title.set_text(player.current_track.title) self._artist.set_text(player.current_track.artist) self._album.set_text(album_name) self._progress.set_value(0.0) self._progress.set_range(0.0, player.current_track.duration * 60) self._total_time_label.set_text( seconds_to_string(player.current_track.duration)) self._timelabel.set_text("0:00") # Can add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 prev_artist = escape(player.prev_track.artist) prev_title = escape(player.prev_track.title) next_artist = escape(player.next_track.artist) next_title = escape(player.next_track.title) self._next_btn.set_tooltip_markup("<b>%s</b> - %s" % (next_artist, next_title)) self._prev_btn.set_tooltip_markup("<b>%s</b> - %s" % (prev_artist, prev_title)) # Do not show next popover non internal tracks as # tags will be readed on the fly if player.next_track.id >= 0: self._next_popover.update() self._next_popover.show() else: self._next_popover.hide() def _on_key_release_event(self, widget, event): """ Destroy window if Esc @param widget as Gtk.Widget @param event as Gdk.event """ if event.keyval == Gdk.KEY_Escape: self.destroy() def _on_prev_btn_clicked(self, widget): """ Go to prev track @param widget as Gtk.Button """ Lp().player.prev() def _on_play_btn_clicked(self, widget): """ Play/pause @param widget as Gtk.Button """ if Lp().player.is_playing(): Lp().player.pause() widget.set_image(self._play_image) else: Lp().player.play() widget.set_image(self._pause_image) def _on_next_btn_clicked(self, widget): """ Go to next track @param widget as Gtk.Button """ Lp().player.next() def _on_status_changed(self, obj): """ Update buttons and progress bar @param obj as unused """ is_playing = Lp().player.is_playing() if Lp().player.current_track.id != Type.RADIOS: self._progress.set_sensitive(is_playing) if is_playing and not self._timeout1: self._timeout1 = GLib.timeout_add(1000, self._update_position) self._change_play_btn_status(self._pause_image, _("Pause")) elif not is_playing and self._timeout1: GLib.source_remove(self._timeout1) self._timeout1 = None self._change_play_btn_status(self._play_image, _("Play")) def _on_progress_press_btn(self, scale, data): """ On press, mark player as seeking @param unused """ self._seeking = True def _on_progress_release_btn(self, scale, data): """ Callback for scale release button Seek player to scale value @param scale as Gtk.Scale, data as unused """ value = scale.get_value() Lp().player.seek(value/60) self._seeking = False self._update_position(value)
class ToolbarEnd(Gtk.Bin): """ Toolbar end """ def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self.connect('show', self._on_show) self.connect('hide', self._on_hide) self._next_popover = NextPopover() self._search = None self._next_was_inhibited = False builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._grid_next = builder.get_object('grid-next') self._shuffle_button = builder.get_object('shuffle-button') self._shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self._activate_shuffle_button) app.add_action(shuffleAction) app.set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self._on_shuffle_changed) self._party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) self._search_button = builder.get_object('search-button') search_action = Gio.SimpleAction.new('search', None) search_action.connect('activate', self._on_search_button_clicked) app.add_action(search_action) app.set_accels_for_action("app.search", ["<Control>f"]) self._settings_button = builder.get_object('settings-button') self._list_button = builder.get_object('list-button') self._list_button.set_property('has-tooltip', True) self._list_button.connect('query-tooltip', self._on_list_button_query_tooltip) list_action = Gio.SimpleAction.new('list', None) list_action.connect('activate', self._on_list_button_clicked) app.add_action(list_action) app.set_accels_for_action("app.list", ["<Control>i"]) self._list_popover = None Lp().player.connect('party-changed', self._on_party_changed) Lp().player.connect('state-changed', self._on_lock_changed) def setup_menu(self, menu): """ Add an application menu to menu button @parma: menu as Gio.Menu """ self._settings_button.show() self._settings_button.set_menu_model(menu) def on_status_changed(self, player): """ Update buttons on status changed @param player as Player """ if player.is_playing(): # Party mode can be activated # via Fullscreen class, so check button state self._party_button.set_active(player.is_party()) def on_next_changed(self, player, force=False): """ Show next popover @param player as Player @param force to show the popover """ # Do not show popover is this menu is active # or if we are hidden if self._shuffle_button.get_active() or\ not self._grid_next.is_visible(): return if self._next_popover.should_be_shown() or force: if self._next_popover.is_visible(): self._next_popover.update() else: self._next_popover.set_relative_to(self._grid_next) self._next_popover.show() else: self._next_popover.hide() ####################### # PRIVATE # ####################### def _on_lock_changed(self, player): """ Lock toolbar @param player as Player """ self._party_button.set_sensitive(not player.locked) self._list_button.set_sensitive(not player.locked) self._shuffle_button.set_sensitive(not player.locked) def _on_shuffle_button_clicked(self, button): """ Hide next popover @param button as Gtk.Button """ self._next_popover.hide() def _set_shuffle_icon(self): """ Set shuffle icon """ shuffle = Lp().settings.get_enum('shuffle') if shuffle == Shuffle.NONE: self._shuffle_image.get_style_context().remove_class('selected') self._shuffle_image.set_from_icon_name( "media-playlist-consecutive-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self._shuffle_image.set_from_icon_name( "media-playlist-shuffle-symbolic", Gtk.IconSize.SMALL_TOOLBAR) if shuffle == Shuffle.TRACKS: self._shuffle_image.get_style_context().add_class('selected') else: self._shuffle_image.get_style_context().remove_class( 'selected') def _activate_party_button(self, action=None, param=None): """ Activate party button @param action as Gio.SimpleAction @param param as GLib.Variant """ self._party_button.set_active(not self._party_button.get_active()) Lp().window.responsive_design() def _activate_shuffle_button(self, action=None, param=None): """ Activate shuffle button @param action as Gio.SimpleAction @param param as GLib.Variant """ self._shuffle_button.set_active(not self._shuffle_button.get_active()) def _on_shuffle_changed(self, settings, value): """ Mark shuffle button as active when shuffle active @param settings as Gio.Settings, value as str """ self._set_shuffle_icon() self._next_popover.hide() def _on_search_button_clicked(self, obj, param=None): """ Show search widget on search button clicked @param obj as Gtk.Button or Gtk.Action """ self._next_was_inhibited = self._next_popover.inhibited self._next_popover.hide() self._next_popover.inhibit(True) if self._search is None: self._search = SearchPopover() self._search.connect('closed', self._on_popover_closed) self._search.set_relative_to(self._search_button) self._search.show() def _on_party_button_toggled(self, button): """ Set party mode on if party button active @param obj as Gtk.button """ active = self._party_button.get_active() self._shuffle_button.set_sensitive(not active) if not Lp().settings.get_value('dark-ui'): settings = Gtk.Settings.get_default() settings.set_property("gtk-application-prefer-dark-theme", active) Lp().player.set_party(active) self.on_next_changed(Lp().player) def _on_party_changed(self, player, is_party): """ On party change, sync toolbar @param player as Player @param is party as bool """ if self._party_button.get_active() != is_party: self._activate_party_button() def _on_list_button_clicked(self, widget, unused=None): """ Show current playback context popover @param widget as Gtk.Widget """ if self._list_popover is not None: return self._next_was_inhibited = self._next_popover.inhibited self._next_popover.hide() self._next_popover.inhibit(True) if Lp().player.current_track.id == Type.EXTERNALS: self._list_popover = ExternalsPopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.populate() self._list_popover.show() elif Lp().player.get_queue(): self._list_popover = QueuePopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.show() elif Lp().player.get_user_playlist_ids(): self._list_popover = PlaylistsPopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.show() else: self._list_popover = AlbumsPopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.show() self._list_popover.connect('closed', self._on_list_popover_closed) return True def _on_list_popover_closed(self, popover): """ Reset variable @param popover as Gtk.Popover """ self._list_popover = None self._on_popover_closed(popover) def _on_popover_closed(self, popover): """ Restore next popover if needed @param popover as Gtk.Popover """ if not self._next_was_inhibited: self._next_popover.inhibit(False) if self._next_popover.should_be_shown(): self._next_popover.show() def _on_show(self, widget): """ Show popover if needed @param widget as Gtk.Widget """ self._set_shuffle_icon() def _on_hide(self, widget): """ Hide popover @param widget as Gtk.Widget """ self._next_popover.hide() def _on_button_press_event(self, eventbox, event): """ Show party popover @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if event.button == 3: popover = PartyPopover() popover.set_relative_to(eventbox) self._next_popover.hide() popover.connect('closed', self._on_popover_closed) self._next_popover.inhibit(True) popover.show() return True def _on_list_button_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ if Lp().player.current_track.id == Type.EXTERNALS: widget.set_tooltip_text(_("Externals tracks playing")) elif Lp().player.get_queue(): widget.set_tooltip_text(_("Queue")) elif Lp().player.get_user_playlist_ids(): widget.set_tooltip_text(_("Playing playlists")) else: widget.set_tooltip_text(_("Playing albums"))
class FullScreen(Gtk.Window, InfosController, PlaybackController, ProgressController): """ Show a fullscreen window showing current track context """ def __init__(self, app, parent): """ Init window and set transient for parent @param app as Gio.Application @param parent as Gtk.window """ Gtk.Window.__init__(self) PlaybackController.__init__(self) ProgressController.__init__(self) self.set_application(app) self._timeout1 = None self._timeout2 = None self._seeking = False self._signal1_id = None self._signal2_id = None self.set_decorated(False) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/FullScreen.ui') builder.connect_signals(self) # Calculate cover size screen = Gdk.Screen.get_default() monitor = screen.get_primary_monitor() geometry = screen.get_monitor_geometry(monitor) # We want 500 and 200 in full hd if geometry.width > geometry.height: artsize = int(ArtSize.MONSTER*geometry.width/1920) else: artsize = int(ArtSize.MONSTER*geometry.height/1920) InfosController.__init__(self, artsize) self._play_btn = builder.get_object('play_btn') self._next_btn = builder.get_object('next_btn') self._prev_btn = builder.get_object('prev_btn') self._next_popover = NextPopover() self._next_popover.set_position(Gtk.PositionType.BOTTOM) self._play_image = builder.get_object('play_image') self._pause_image = builder.get_object('pause_image') close_btn = builder.get_object('close_btn') close_btn.connect('clicked', self._destroy) self._cover = builder.get_object('cover') self._cover_frame = builder.get_object('frame') self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._album_label = builder.get_object('album') self._datetime = builder.get_object('datetime') self._progress = builder.get_object('progress_scale') self._timelabel = builder.get_object('playback') self._total_time_label = builder.get_object('duration') self.connect('key-release-event', self._on_key_release_event) self.add(builder.get_object('widget')) def do_show(self): """ Init signals, set color and go party mode if nothing is playing """ self._signal1_id = Lp().player.connect('current-changed', self.on_current_changed) self._signal2_id = Lp().player.connect('status-changed', self.on_status_changed) if Lp().player.current_track.id is None: Lp().player.set_party(True) else: self.on_status_changed(Lp().player) self.on_current_changed(Lp().player) if self._timeout1 is None: self._timeout1 = GLib.timeout_add(1000, self._update_position) Gtk.Window.do_show(self) now = datetime.now() self._datetime.set_label(now.strftime('%a %d %b, %X')[:-3]) if self._timeout2 is None: second = datetime.now().second if 60 - second > 0: GLib.timeout_add((60-second)*1000, self._update_datetime) else: self._timeout2 = GLib.timeout_add(60000, self._update_datetime) self._update_position() self.fullscreen() self._next_popover.set_relative_to(self._album_label) if Lp().player.next_track.id != Type.RADIOS: self._next_popover.show() def do_hide(self): """ Remove signals and unset color """ Gtk.Window.do_hide(self) if self._signal1_id is not None: Lp().player.disconnect(self._signal1_id) self._signal1_id = None if self._signal2_id is not None: Lp().player.disconnect(self._signal2_id) self._signal2_id = None if self._timeout1 is not None: GLib.source_remove(self._timeout1) self._timeout1 = None if self._timeout2 is not None: GLib.source_remove(self._timeout2) self._next_popover.set_relative_to(None) self._next_popover.hide() def on_current_changed(self, player): """ Update infos and show/hide popover @param player as Player """ InfosController.on_current_changed(self, player) ProgressController.on_current_changed(self, player) if player.current_track.id is not None: album_name = player.current_track.album.name if player.current_track.year != '': album_name += " (%s)" % player.current_track.year self._album_label.set_text(album_name) # Do not show next popover non internal tracks as # tags will be readed on the fly if player.next_track.id >= 0: self._next_popover.update() self._next_popover.show() else: self._next_popover.hide() ####################### # PRIVATE # ####################### def _update_datetime(self): """ Update datetime in headerbar """ now = datetime.now() self._datetime.set_label(now.strftime('%a %d %b, %X')[:-3]) if self._timeout2 is None: self._timeout2 = GLib.timeout_add(60000, self._update_datetime) return False return True def _destroy(self, widget): """ Destroy self @param widget as Gtk.Button """ self.destroy() def _on_key_release_event(self, widget, event): """ Destroy window if Esc @param widget as Gtk.Widget @param event as Gdk.event """ if event.keyval == Gdk.KEY_Escape: self.destroy()
class ToolbarEnd(Gtk.Bin): """ Toolbar end """ def __init__(self, app): """ Init toolbar @param app as Gtk.Application """ Gtk.Bin.__init__(self) self.connect('show', self._on_show) self.connect('hide', self._on_hide) self._next_popover = NextPopover() self._search = None self._timeout_id = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarEnd.ui') builder.connect_signals(self) self.add(builder.get_object('end')) self._grid_next = builder.get_object('grid-next') self._shuffle_button = builder.get_object('shuffle-button') self._shuffle_image = builder.get_object('shuffle-button-image') shuffleAction = Gio.SimpleAction.new('shuffle-button', None) shuffleAction.connect('activate', self._activate_shuffle_button) app.add_action(shuffleAction) app.set_accels_for_action("app.shuffle-button", ["<Control>r"]) Lp().settings.connect('changed::shuffle', self._on_shuffle_changed) self._party_button = builder.get_object('party-button') party_action = Gio.SimpleAction.new('party', None) party_action.connect('activate', self._activate_party_button) app.add_action(party_action) app.set_accels_for_action("app.party", ["<Control>p"]) self._search_button = builder.get_object('search-button') search_action = Gio.SimpleAction.new('search', None) search_action.connect('activate', self._on_search_button_clicked) app.add_action(search_action) app.set_accels_for_action("app.search", ["<Control>f"]) self._settings_button = builder.get_object('settings-button') self._list_button = builder.get_object('list-button') self._list_button.set_property('has-tooltip', True) self._list_button.connect('query-tooltip', self._on_list_button_query_tooltip) list_action = Gio.SimpleAction.new('list', None) list_action.connect('activate', self._on_list_button_clicked) app.add_action(list_action) app.set_accels_for_action("app.list", ["<Control>l"]) self._list_popover = None Lp().player.connect('party-changed', self._on_party_changed) def setup_menu(self, menu): """ Add an application menu to menu button @parma: menu as Gio.Menu """ self._settings_button.show() self._settings_button.set_menu_model(menu) def on_status_changed(self, player): """ Update buttons on status changed @param player as Player """ if player.is_playing(): # Party mode can be activated # via Fullscreen class, so check button state self._party_button.set_active(player.is_party()) def on_next_changed(self, player, force=False): """ Show next popover @param player as Player @param force to show the popover """ # Do not show popover is this menu is active # or if we are hidden if self._shuffle_button.get_active() or\ not self._grid_next.is_visible(): return self._timeout_id = None if self._next_popover.should_be_shown() or force: self._next_popover.update() if not self._next_popover.is_visible(): self._next_popover.set_relative_to(self._grid_next) self._next_popover.show() else: self._next_popover.hide() ####################### # PRIVATE # ####################### def _on_shuffle_button_clicked(self, button): """ Hide next popover @param button as Gtk.Button """ self._next_popover.hide() def _on_button_press(self, button, event): """ Show next popover on long press @param button as Gtk.Button @param event as Gdk.Event """ self._timeout_id = GLib.timeout_add(500, self.on_next_changed, Lp().player, True) def _on_button_release(self, button, event): """ If next popover shown, block event @param button as Gtk.Button @param event as Gdk.Event """ if self._timeout_id is None: return True else: GLib.source_remove(self._timeout_id) self._timeout_id = None def _set_shuffle_icon(self): """ Set shuffle icon """ shuffle = Lp().settings.get_enum('shuffle') if shuffle == Shuffle.NONE: self._shuffle_image.get_style_context().remove_class('selected') self._shuffle_image.set_from_icon_name( "media-playlist-consecutive-symbolic", Gtk.IconSize.SMALL_TOOLBAR) else: self._shuffle_image.set_from_icon_name( "media-playlist-shuffle-symbolic", Gtk.IconSize.SMALL_TOOLBAR) if shuffle == Shuffle.TRACKS: self._shuffle_image.get_style_context().add_class('selected') else: self._shuffle_image.get_style_context().remove_class( 'selected') def _activate_party_button(self, action=None, param=None): """ Activate party button @param action as Gio.SimpleAction @param param as GLib.Variant """ self._party_button.set_active(not self._party_button.get_active()) Lp().window.responsive_design() def _activate_shuffle_button(self, action=None, param=None): """ Activate shuffle button @param action as Gio.SimpleAction @param param as GLib.Variant """ self._shuffle_button.set_active(not self._shuffle_button.get_active()) def _on_shuffle_changed(self, settings, value): """ Mark shuffle button as active when shuffle active @param settings as Gio.Settings, value as str """ self._set_shuffle_icon() self._next_popover.hide() def _on_search_button_clicked(self, obj, param=None): """ Show search widget on search button clicked @param obj as Gtk.Button or Gtk.Action """ if self._search is None: self._search = SearchPopover() self._search.connect('closed', self._on_popover_closed) self._search.set_relative_to(self._search_button) self._next_popover.hide() self._search.show() def _on_party_button_toggled(self, button): """ Set party mode on if party button active @param obj as Gtk.button """ active = self._party_button.get_active() self._shuffle_button.set_sensitive(not active) if not Lp().settings.get_value('dark-ui'): settings = Gtk.Settings.get_default() settings.set_property("gtk-application-prefer-dark-theme", active) Lp().player.set_party(active) self.on_next_changed(Lp().player) def _on_party_changed(self, player, is_party): """ On party change, sync toolbar @param player as Player @param is party as bool """ if self._party_button.get_active() != is_party: self._activate_party_button() def _on_list_button_clicked(self, widget, unused=None): """ Show current playback context popover @param widget as Gtk.Widget """ if self._list_popover is not None: return self._next_popover.hide() if Lp().player.current_track.id == Type.EXTERNALS: self._list_popover = ExternalsPopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.populate() self._list_popover.show() elif Lp().player.get_queue(): self._list_popover = QueuePopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.show() elif Lp().player.get_user_playlist_ids(): self._list_popover = PlaylistsPopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.show() else: self._list_popover = AlbumsPopover() self._list_popover.set_relative_to(self._list_button) self._list_popover.show() self._list_popover.connect('closed', self._on_list_popover_closed) return True def _on_list_popover_closed(self, popover): """ Reset variable @param popover as Gtk.Popover """ self._list_popover = None self._on_popover_closed(popover) def _on_popover_closed(self, popover): """ Restore next popover if needed @param popover as Gtk.Popover """ if self._next_popover.should_be_shown(): self._next_popover.show() def _on_show(self, widget): """ Show popover if needed @param widget as Gtk.Widget """ self._set_shuffle_icon() def _on_hide(self, widget): """ Hide popover @param widget as Gtk.Widget """ self._next_popover.hide() def _on_list_button_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ if Lp().player.current_track.id == Type.EXTERNALS: widget.set_tooltip_text(_("Externals tracks playing")) elif Lp().player.get_queue(): widget.set_tooltip_text(_("Queue")) elif Lp().player.get_user_playlist_ids(): widget.set_tooltip_text(_("Playing playlists")) else: widget.set_tooltip_text(_("Playing albums"))