class ToolbarInfos(Gtk.Bin): """ Init infos toolbar """ def __init__(self): Gtk.Bin.__init__(self) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfos.ui') builder.connect_signals(self) infobox = builder.get_object('infos') infobox.set_property('has-tooltip', True) self.add(infobox) self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover = builder.get_object('cover') self._pop_albums = AlbumsPopover() self._pop_albums.set_relative_to(infobox) self._pop_tunein = TuneinPopover(RadiosManager()) self._pop_tunein.set_relative_to(infobox) Lp.art.connect('cover-changed', self._update_cover) Lp.art.connect('logo-changed', self._update_logo) """ Update toolbar on current changed @param player as Player """ def on_current_changed(self, player): art = None self._artist_label.set_text(player.current_track.artist) self._title_label.set_text(player.current_track.title) if player.current_track.id == Type.RADIOS: art = Lp.art.get_radio(player.current_track.artist, ArtSize.SMALL) elif player.current_track.id == Type.EXTERNALS: art = Lp.art.get_cover_for_uri(player.current_track.uri, ArtSize.SMALL, False) else: art = Lp.art.get_album(player.current_track.album_id, ArtSize.SMALL) if art is not None: self._cover.set_from_pixbuf(art) del art self._cover.set_tooltip_text(player.current_track.album) self._cover.show() else: self._cover.hide() """ Update infos on status changed @param player as Player """ def on_status_changed(self, player): pass ####################### # PRIVATE # ####################### """ Update cover for album_id @param art as Art @param album id as int """ def _update_cover(self, art, album_id): if Lp.player.current_track.album_id == album_id: pixbuf = Lp.art.get_album(album_id, ArtSize.SMALL) self._cover.set_from_pixbuf(pixbuf) del pixbuf """ Update logo for name @param art as Art @param name as str """ def _update_logo(self, art, name): if Lp.player.current_track.artist == name: pixbuf = Lp.art.get_radio(name, ArtSize.SMALL) self._cover.set_from_pixbuf(pixbuf) del pixbuf """ Pop albums from current artistleft click Show playlist menu on right @param eventbox as Gtk.EventBox @param event as Gdk.Event """ def _on_infobox_clicked(self, eventbox, event): if Lp.player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(eventbox) expopover.populate() expopover.show() elif Lp.player.current_track.id is not None: if event.button == 1: if Lp.player.current_track.id == Type.RADIOS: self._pop_tunein.populate() self._pop_tunein.show() else: self._pop_albums.populate() self._pop_albums.show() elif Lp.player.current_track.id >= 0: menu = PopToolbarMenu(Lp.player.current_track.id, None) popover = Gtk.Popover.new_from_model(eventbox, menu) popover.show() return True """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 return False layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b>\n%s" % (artist, title)) else: return False return True """ Show hand cursor over """ def _on_eventbox_realize(self, eventbox): eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))
class ToolbarInfo(Gtk.Bin, InfosController): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) InfosController.__init__(self, ArtSize.SMALL) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfo.ui') builder.connect_signals(self) self._pop_tunein = None self._pop_info = None self._timeout_id = None self._width = 0 self._infobox = builder.get_object('info') self.add(self._infobox) self._labels = builder.get_object('nowplaying_labels') self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover_frame = builder.get_object('frame') self._cover = builder.get_object('cover') self._cover.set_property('has-tooltip', True) # Since GTK 3.20, we can set cover full height if Gtk.get_minor_version() > 18: self._cover.get_style_context().add_class('toolbar-cover-frame') else: self._cover.get_style_context().add_class('small-cover-frame') self.connect('realize', self._on_realize) Lp().art.connect('album-artwork-changed', self._update_cover) Lp().art.connect('radio-artwork-changed', self._update_logo) def do_get_preferred_width(self): """ We force preferred width @return (int, int) """ return (self._width, self._width) def get_preferred_height(self): """ Return preferred height @return (int, int) """ return self._labels.get_preferred_height() def set_width(self, width): """ Set widget width @param width as int """ self._width = width self.set_property('width-request', width) ####################### # PRIVATE # ####################### def _update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp().player.current_track.album.id == album_id: surface = Lp().art.get_album_artwork( Lp().player.current_track.album, ArtSize.SMALL) self._cover.set_from_surface(surface) del surface def _update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp().player.current_track.album_artist == name: pixbuf = Lp().art.get_radio_artwork(name, ArtSize.SMALL) self._cover.set_from_surface(pixbuf) del pixbuf def _show_track_menu(self): """ Show current track menu """ self._timeout_id = None if Lp().player.current_track.id >= 0: popover = TrackMenuPopover( Lp().player.current_track.id, PopToolbarMenu(Lp().player.current_track.id)) popover.set_relative_to(self._infobox) popover.show() def _on_title_press_event(self, widget, event): """ On long press: show current track menu @param widget as Gtk.Widget @param event as Gdk.Event """ self._timeout_id = GLib.timeout_add(500, self._show_track_menu) return True def _on_title_release_event(self, widget, event): """ Show track information popover On long press/right click: show current track menu @param widget as Gtk.Widget @param event as Gdk.Event """ if self._timeout_id is not None: GLib.source_remove(self._timeout_id) self._timeout_id = None if Lp().player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(widget) expopover.populate() expopover.show() elif Lp().player.current_track.id is not None: if event.button == 1: if Lp().player.current_track.id == Type.RADIOS: if self._pop_tunein is None: self._pop_tunein = TuneinPopover() self._pop_tunein.populate() self._pop_tunein.set_relative_to(widget) self._pop_tunein.show() else: if self._pop_info is None: self._pop_info = InfoPopover() self._pop_info.set_relative_to(widget) self._pop_info.show() else: self._show_track_menu() return True def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ # Can't add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True def _on_realize(self, toolbar): """ @param toolbar as ToolbarInfos """ style = self.get_style_context() padding = style.get_padding(style.get_state()) self._artsize = self.get_allocated_height()\ - padding.top - padding.bottom # Since GTK 3.20, we can set cover full height if Gtk.get_minor_version() < 20: self._artsize -= 2 def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))
class RadiosView(View): """ Show radios in a grid """ def __init__(self): """ Init view """ View.__init__(self) self._signal = Lp().art.connect('radio-artwork-changed', self._on_logo_changed) self._radios_manager = Radios() self._radios_manager.connect('radios-changed', self._on_radios_changed) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/RadiosView.ui') builder.connect_signals(self) widget = builder.get_object('widget') self._empty = builder.get_object('empty') self._pop_tunein = TuneinPopover(self._radios_manager) self._pop_tunein.set_relative_to(builder.get_object('search_btn')) self._sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.BOTH) self._radiobox = Gtk.FlowBox() self._radiobox.set_selection_mode(Gtk.SelectionMode.NONE) self._radiobox.connect("child-activated", self._on_album_activated) self._radiobox.set_property('column-spacing', 5) self._radiobox.set_property('row-spacing', 5) self._radiobox.set_homogeneous(True) self._radiobox.set_max_children_per_line(1000) self._radiobox.show() self._stack = Gtk.Stack() self._stack.set_transition_duration(500) self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self._stack.add(self._scrolled) self._stack.add(self._empty) self._stack.show() self._viewport.set_property("valign", Gtk.Align.START) self._viewport.set_property('margin', 5) self._viewport.add(self._radiobox) self._scrolled.set_property('expand', True) self.add(widget) self.add(self._stack) def populate(self): """ Populate view with tracks from playlist Thread safe """ Lp().player.set_radios(self._radios_manager.get()) if Lp().player.current_track.id == Type.RADIOS: Lp().player.set_next() # We force next update Lp().player.set_prev() # We force prev update t = Thread(target=self._populate) t.daemon = True t.start() ####################### # PRIVATE # ####################### def _populate(self): """ Populate view with tracks from playlist Thread safe """ radios = [] # Get radios name for (name, url) in self._radios_manager.get(): radios.append(name) GLib.idle_add(self._show_stack, radios) def _get_children(self): """ Return view children @return [RadioWidget] """ children = [] for child in self._radiobox.get_children(): widget = child.get_child() children.append(widget) return children def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ if self._signal is not None: Lp().art.disconnect(self._signal) def _on_new_clicked(self, widget): """ Show popover for adding a new radio @param widget as Gtk.Widget """ popover = RadioPopover('', self._radios_manager) popover.set_relative_to(widget) popover.show() def _on_search_clicked(self, widget): """ Show popover for searching radios @param widget as Gtk.Widget """ self._pop_tunein.populate() self._pop_tunein.show() def _on_radios_changed(self, manager): """ Update radios @param manager as PlaylistManager """ radios_name = [] currents = [] new_name = None old_widget = None old_child = None # Get radios name for (name, url) in manager.get(): radios_name.append(name) # Get currents widget less removed for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() not in radios_name: old_widget = widget old_child = child else: currents.append(widget.get_name()) # Add the new radio for name in radios_name: if name not in currents: new_name = name break # Rename widget if new_name is not None: if old_widget is not None: old_widget.set_name(new_name) else: radios = [new_name] self._show_stack(radios) # Delete widget elif old_widget is not None: self._radiobox.remove(old_child) old_widget.destroy() if not self._radiobox.get_children(): self._show_stack([]) # Update player state based on current view radios = [] for child in self._radiobox.get_children(): widget = child.get_child() name = widget.get_name() url = manager.get_url(name) radios.append((name, url)) Lp().player.set_radios(radios) if Lp().player.current_track.id == Type.RADIOS: Lp().player.set_next() # We force next update Lp().player.set_prev() # We force prev update def _on_logo_changed(self, player, name): """ Update radio logo @param player as Plyaer @param name as string """ for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() == name: widget.update_cover() def _show_stack(self, radios): """ Switch empty/radios view based on radios @param [radio names as string] """ if radios: self._stack.set_visible_child(self._scrolled) self._add_radios(radios, True) else: self._stack.set_visible_child(self._empty) def _add_radios(self, radios, first=False): """ Pop a radio and add it to the view, repeat operation until radio list is empty @param [radio names as string] @param first as bool """ if radios and not self._stop: radio = radios.pop(0) widget = RadioWidget(radio, self._radios_manager) widget.show() self._sizegroup.add_widget(widget) if first: self._radiobox.insert(widget, 0) else: self._radiobox.insert(widget, -1) GLib.idle_add(self._add_radios, radios) else: self._stop = False return None def _on_album_activated(self, flowbox, child): """ Play album @param flowbox as Gtk.Flowbox @child as Gtk.FlowboxChild """ name = child.get_child().get_name() url = self._radios_manager.get_url(name) if url: track = Track() track.set_radio(name, url) Lp().player.load(track)
class ToolbarInfo(Gtk.Bin, InfosController): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) InfosController.__init__(self, ArtSize.SMALL) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfo.ui') builder.connect_signals(self) self._pop_tunein = None self._pop_info = None self._timeout_id = None self._width = 0 self._infobox = builder.get_object('info') self.add(self._infobox) self._labels = builder.get_object('nowplaying_labels') self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover_frame = builder.get_object('frame') self._cover = builder.get_object('cover') self._cover.set_property('has-tooltip', True) self.connect('realize', self._on_realize) Lp().art.connect('album-artwork-changed', self._update_cover) Lp().art.connect('radio-artwork-changed', self._update_logo) def do_get_preferred_width(self): """ We force preferred width @return (int, int) """ return (self._width, self._width) def get_preferred_height(self): """ Return preferred height @return (int, int) """ return self._labels.get_preferred_height() def set_width(self, width): """ Set widget width @param width as int """ self._width = width self.set_property('width-request', width) ####################### # PRIVATE # ####################### def _update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp().player.current_track.album.id == album_id: surface = Lp().art.get_album_artwork( Lp().player.current_track.album, ArtSize.SMALL) self._cover.set_from_surface(surface) del surface def _update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp().player.current_track.album_artist == name: pixbuf = Lp().art.get_radio_artwork(name, ArtSize.SMALL) self._cover.set_from_surface(pixbuf) del pixbuf def _show_track_menu(self): """ Show current track menu """ self._timeout_id = None if Lp().player.current_track.id >= 0: popover = TrackMenuPopover( Lp().player.current_track.id, PopToolbarMenu(Lp().player.current_track.id)) popover.set_relative_to(self._infobox) popover.show() def _on_title_press_event(self, widget, event): """ On long press: show current track menu @param widget as Gtk.Widget @param event as Gdk.Event """ self._timeout_id = GLib.timeout_add(500, self._show_track_menu) return True def _on_title_release_event(self, widget, event): """ Show track information popover On long press/right click: show current track menu @param widget as Gtk.Widget @param event as Gdk.Event """ if self._timeout_id is not None: GLib.source_remove(self._timeout_id) self._timeout_id = None if Lp().player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(widget) expopover.populate() expopover.show() elif Lp().player.current_track.id is not None: if event.button == 1: if Lp().player.current_track.id == Type.RADIOS: if self._pop_tunein is None: self._pop_tunein = TuneinPopover() self._pop_tunein.populate() self._pop_tunein.set_relative_to(widget) self._pop_tunein.show() else: if self._pop_info is None: self._pop_info = InfoPopover() self._pop_info.set_relative_to(widget) self._pop_info.show() else: self._show_track_menu() return True def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ # Can't add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True def _on_realize(self, toolbar): """ @param toolbar as ToolbarInfos """ style = self.get_style_context() padding = style.get_padding(style.get_state()) self._artsize = self.get_allocated_height()\ - padding.top - padding.bottom - 2 def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))
class RadiosView(View): """ Show radios in a grid """ def __init__(self): """ Init view """ View.__init__(self) self._signal = Lp().art.connect('radio-artwork-changed', self._on_logo_changed) self._radios_manager = Radios() self._radios_manager.connect('radios-changed', self._on_radios_changed) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/RadiosView.ui') builder.connect_signals(self) widget = builder.get_object('widget') self._empty = builder.get_object('empty') self._pop_tunein = TuneinPopover(self._radios_manager) self._pop_tunein.set_relative_to(builder.get_object('search_btn')) self._sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.BOTH) self._radiobox = Gtk.FlowBox() self._radiobox.set_selection_mode(Gtk.SelectionMode.NONE) self._radiobox.connect("child-activated", self._on_album_activated) self._radiobox.set_property('column-spacing', 5) self._radiobox.set_property('row-spacing', 5) self._radiobox.set_homogeneous(True) self._radiobox.set_max_children_per_line(1000) self._radiobox.show() self._stack = Gtk.Stack() self._stack.set_transition_duration(500) self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self._stack.add(self._scrolledWindow) self._stack.add(self._empty) self._stack.show() self._viewport.set_property("valign", Gtk.Align.START) self._viewport.set_property('margin', 5) self._viewport.add(self._radiobox) self._scrolledWindow.set_property('expand', True) self.add(widget) self.add(self._stack) def populate(self): """ Populate view with tracks from playlist Thread safe """ t = Thread(target=self._populate) t.daemon = True t.start() ####################### # PRIVATE # ####################### def _populate(self): """ Populate view with tracks from playlist Thread safe """ radios = [] # Get radios name for (name, url) in self._radios_manager.get(): radios.append(name) GLib.idle_add(self._show_stack, radios) def _get_children(self): """ Return view children @return [RadioWidget] """ children = [] for child in self._radiobox.get_children(): widget = child.get_child() children.append(widget) return children def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ if self._signal is not None: Lp().art.disconnect(self._signal) def _on_new_clicked(self, widget): """ Show popover for adding a new radio @param widget as Gtk.Widget """ popover = RadioPopover('', self._radios_manager) popover.set_relative_to(widget) popover.show() def _on_search_clicked(self, widget): """ Show popover for searching radios @param widget as Gtk.Widget """ self._pop_tunein.populate() self._pop_tunein.show() def _on_radios_changed(self, manager): """ Update radios @param manager as PlaylistManager """ radios_name = [] currents = [] new_name = None old_widget = None old_child = None # Get radios name for (name, url) in manager.get(): radios_name.append(name) # Get currents widget less removed for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() not in radios_name: old_widget = widget old_child = child else: currents.append(widget.get_name()) # Add the new radio for name in radios_name: if name not in currents: new_name = name break # Rename widget if new_name is not None: if old_widget is not None: old_widget.set_name(new_name) else: radios = [new_name] self._show_stack(radios) # Delete widget elif old_widget is not None: self._radiobox.remove(old_child) old_widget.destroy() if not self._radiobox.get_children(): self._show_stack([]) def _on_logo_changed(self, player, name): """ Update radio logo @param player as Plyaer @param name as string """ for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() == name: widget.update_cover() def _show_stack(self, radios): """ Switch empty/radios view based on radios @param [radio names as string] """ if radios: self._stack.set_visible_child(self._scrolledWindow) self._add_radios(radios, True) else: self._stack.set_visible_child(self._empty) def _add_radios(self, radios, first=False): """ Pop a radio and add it to the view, repeat operation until radio list is empty @param [radio names as string] @param first as bool """ if radios and not self._stop: radio = radios.pop(0) widget = RadioWidget(radio, self._radios_manager) widget.show() self._sizegroup.add_widget(widget) if first: self._radiobox.insert(widget, 0) else: self._radiobox.insert(widget, -1) GLib.idle_add(self._add_radios, radios) else: self._stop = False return None def _on_album_activated(self, flowbox, child): """ Play album @param flowbox as Gtk.Flowbox @child as Gtk.FlowboxChild """ name = child.get_child().get_name() url = self._radios_manager.get_url(name) if url: track = Track() track.set_radio(name, url) Lp().player.load(track)
class ToolbarInfos(Gtk.Bin): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfos.ui') builder.connect_signals(self) self._pop_tunein = None self._pop_infos = None self._infobox = builder.get_object('infos') self._infobox.set_property('has-tooltip', True) self.add(self._infobox) self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover = builder.get_object('cover') Lp.art.connect('cover-changed', self._update_cover) Lp.art.connect('logo-changed', self._update_logo) def on_current_changed(self, player): """ Update toolbar on current changed @param player as Player """ art = None if player.current_track.artist == '': self._artist_label.hide() else: self._artist_label.show() self._artist_label.set_text(player.current_track.artist) if player.current_track.title == '': self._title_label.hide() else: self._title_label.show() self._title_label.set_text(player.current_track.title) if player.current_track.id == Type.RADIOS: art = Lp.art.get_radio(player.current_track.artist, ArtSize.SMALL*self.get_scale_factor()) elif player.current_track.id == Type.EXTERNALS: art = Lp.art.get_cover_for_uri( player.current_track.uri, ArtSize.SMALL*self.get_scale_factor(), False) else: art = Lp.art.get_album(player.current_track.album, ArtSize.SMALL*self.get_scale_factor()) if art is not None: self._cover.set_from_surface(art) del art self._cover.set_tooltip_text(player.current_track.album.name) self._cover.show() else: self._cover.hide() def on_status_changed(self, player): """ Update infos on status changed @param player as Player """ pass ####################### # PRIVATE # ####################### def _update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp.player.current_track.album.id == album_id: pixbuf = Lp.art.get_album(Lp.player.current_track.album, ArtSize.SMALL) self._cover.set_from_surface(pixbuf) del pixbuf def _update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp.player.current_track.artist == name: pixbuf = Lp.art.get_radio(name, ArtSize.SMALL) self._cover.set_from_surface(pixbuf) del pixbuf def _on_infobox_clicked(self, eventbox, event): """ Pop albums from current artistleft click Show playlist menu on right @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if Lp.player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(eventbox) expopover.populate() expopover.show() elif Lp.player.current_track.id is not None: if event.button == 1: if Lp.player.current_track.id == Type.RADIOS: if self._pop_tunein is None: self._pop_tunein = TuneinPopover() self._pop_tunein.populate() self._pop_tunein.set_relative_to(self._infobox) self._pop_tunein.show() else: if self._pop_infos is None: self._pop_infos = InfosPopover() self._pop_infos.set_relative_to(self._infobox) self._pop_infos.show() elif Lp.player.current_track.id >= 0: menu = PopToolbarMenu(Lp.player.current_track.id, None) popover = Gtk.Popover.new_from_model(eventbox, menu) rating = RatingWidget(Lp.player.current_track) rating.set_margin_top(5) rating.set_margin_bottom(5) rating.set_property('halign', Gtk.Align.START) rating.set_property('hexpand', True) rating.show() loved = LovedWidget(Lp.player.current_track.id) loved.set_margin_end(5) loved.set_margin_top(5) loved.set_margin_bottom(5) loved.set_property('halign', Gtk.Align.END) loved.set_property('hexpand', True) loved.show() # Hack to add two widgets in popover # Use a Gtk.PopoverMenu later # (GTK>3.16 available on Debian stable) stack = Gtk.Stack() grid = Gtk.Grid() grid.set_orientation(Gtk.Orientation.VERTICAL) stack.add_named(grid, 'main') stack.show_all() menu_widget = popover.get_child() menu_widget.reparent(grid) separator = Gtk.Separator() separator.show() grid.add(separator) hgrid = Gtk.Grid() hgrid.add(rating) hgrid.add(loved) hgrid.show() grid.add(hgrid) popover.add(stack) popover.show() return True def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ # Can't add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))
class RadiosView(FlowBoxView, ViewController): """ Show radios flow box """ def __init__(self): """ Init view """ FlowBoxView.__init__(self) ViewController.__init__(self, ViewControllerType.RADIO) self._widget_class = RadioWidget self.__radios = Radios() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/RadiosView.ui") builder.connect_signals(self) self.insert_row(0) self.attach(builder.get_object("widget"), 0, 0, 1, 1) self.__pop_tunein = TuneinPopover(self.__radios) self.__pop_tunein.set_relative_to(builder.get_object("search_btn")) def populate(self, radio_ids): """ Add radio widgets @param radio_ids as [int] """ if radio_ids: FlowBoxView.populate(self, radio_ids) else: self._scrolled.hide() view = MessageView(_("No favorite radios")) view.show() self.add(view) ####################### # PROTECTED # ####################### def _add_items(self, radio_ids): """ Add radios to the view Start lazy loading @param radio ids as [int] """ widget = FlowBoxView._add_items(self, radio_ids, self.__radios) if widget is not None: widget.connect("overlayed", self.on_overlayed) def _on_new_clicked(self, widget): """ Show popover for adding a new radio @param widget as Gtk.Widget """ from lollypop.pop_radio import RadioPopover popover = RadioPopover(None, self.__radios) popover.set_relative_to(widget) popover.popup() def _on_search_clicked(self, widget): """ Show popover for searching radios @param widget as Gtk.Widget """ self.__pop_tunein.populate() self.__pop_tunein.show() def _on_artwork_changed(self, artwork, name): """ Update children artwork if matching name @param artwork as Artwork @param name as str """ for child in self._box.get_children(): if name == child.name: child.set_artwork() def _on_map(self, widget): """ Set active ids """ self.__signal_id = self.__radios.connect("radio-changed", self.__on_radio_changed) App().settings.set_value("state-one-ids", GLib.Variant("ai", [Type.RADIOS])) App().settings.set_value("state-two-ids", GLib.Variant("ai", [])) def _on_unmap(self, widget): """ Destroy popover @param widget as Gtk.Widget """ if self.__signal_id is not None: self.__radios.disconnect(self.__signal_id) self.__signal_id = None self.__pop_tunein.destroy() ####################### # PRIVATE # ####################### def __add_radio(self, radio_id): """ Add radio @param radio_id as int """ widget = RadioWidget(radio_id, self.__radios) self._box.insert(widget, 0) widget.populate() widget.show() def __on_radio_changed(self, radios, radio_id): """ Update view based on radio_id status @param radios as Radios @param radio_id as int """ exists = radios.exists(radio_id) if exists: item = None for child in self._box.get_children(): if child.id == radio_id: item = child break if item is None: self.__add_radio(radio_id) else: name = self.__radios.get_name(radio_id) item.rename(name) else: for child in self._box.get_children(): if child.id == radio_id: child.destroy()
class ToolbarInfos(Gtk.Bin): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfos.ui') builder.connect_signals(self) infobox = builder.get_object('infos') infobox.set_property('has-tooltip', True) self.add(infobox) self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover = builder.get_object('cover') self._pop_albums = AlbumsPopover() self._pop_albums.set_relative_to(infobox) self._pop_tunein = TuneinPopover(RadiosManager()) self._pop_tunein.set_relative_to(infobox) Lp.art.connect('cover-changed', self._update_cover) Lp.art.connect('logo-changed', self._update_logo) def on_current_changed(self, player): """ Update toolbar on current changed @param player as Player """ art = None if player.current_track.artist == '': self._artist_label.hide() else: self._artist_label.show() self._artist_label.set_text(player.current_track.artist) if player.current_track.title == '': self._title_label.hide() else: self._title_label.show() self._title_label.set_text(player.current_track.title) if player.current_track.id == Type.RADIOS: art = Lp.art.get_radio(player.current_track.artist, ArtSize.SMALL) elif player.current_track.id == Type.EXTERNALS: art = Lp.art.get_cover_for_uri(player.current_track.uri, ArtSize.SMALL, False) else: art = Lp.art.get_album(player.current_track.album_id, ArtSize.SMALL) if art is not None: self._cover.set_from_surface(art) del art self._cover.set_tooltip_text(player.current_track.album.name) self._cover.show() else: self._cover.hide() def on_status_changed(self, player): """ Update infos on status changed @param player as Player """ pass ####################### # PRIVATE # ####################### def _update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp.player.current_track.album_id == album_id: pixbuf = Lp.art.get_album(album_id, ArtSize.SMALL) self._cover.set_from_surface(pixbuf) del pixbuf def _update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp.player.current_track.artist == name: pixbuf = Lp.art.get_radio(name, ArtSize.SMALL) self._cover.set_from_surface(pixbuf) del pixbuf def _on_infobox_clicked(self, eventbox, event): """ Pop albums from current artistleft click Show playlist menu on right @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if Lp.player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(eventbox) expopover.populate() expopover.show() elif Lp.player.current_track.id is not None: if event.button == 1: if Lp.player.current_track.id == Type.RADIOS: self._pop_tunein.populate() self._pop_tunein.show() else: self._pop_albums.populate() self._pop_albums.show() elif Lp.player.current_track.id >= 0: menu = PopToolbarMenu(Lp.player.current_track.id, None) popover = Gtk.Popover.new_from_model(eventbox, menu) popover.show() return True def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ # Can't add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))
class ToolbarInfos(Gtk.Bin): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfos.ui') builder.connect_signals(self) self._pop_tunein = None self._pop_infos = None self._cover_size = ArtSize.SMALL self._infobox = builder.get_object('infos') self._infobox.set_property('has-tooltip', True) self.add(self._infobox) self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover_frame = builder.get_object('frame') self._cover = builder.get_object('cover') Lp().art.connect('album-artwork-changed', self._update_cover) Lp().art.connect('radio-artwork-changed', self._update_logo) def on_current_changed(self, player): """ Update toolbar on current changed @param player as Player """ art = None if player.current_track.artist == '': self._artist_label.hide() else: self._artist_label.show() self._artist_label.set_text(player.current_track.artist) if player.current_track.title == '': self._title_label.hide() else: self._title_label.show() self._title_label.set_text(player.current_track.title) if player.current_track.id == Type.RADIOS: art = Lp().art.get_radio_artwork( player.current_track.artist, self._cover_size*self.get_scale_factor()) elif player.current_track.id == Type.EXTERNALS: art = Lp().art.get_album_artwork2( player.current_track.uri, self._cover_size*self.get_scale_factor()) elif player.current_track.id is not None: art = Lp().art.get_album_artwork( player.current_track.album, self._cover_size*self.get_scale_factor()) if art is not None: self._cover.set_from_surface(art) del art self._cover.set_tooltip_text(player.current_track.album.name) self._cover_frame.show() else: self._cover_frame.hide() def on_status_changed(self, player): """ Update infos on status changed @param player as Player """ pass ####################### # PRIVATE # ####################### def _update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp().player.current_track.album.id == album_id: surface = Lp().art.get_album_artwork( Lp().player.current_track.album, self._cover_size) self._cover.set_from_surface(surface) del surface def _update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp().player.current_track.artist == name: pixbuf = Lp().art.get_radio_artwork(name, self._cover_size) self._cover.set_from_surface(pixbuf) del pixbuf def _on_infobox_clicked(self, eventbox, event): """ Pop albums from current artistleft click Show playlist menu on right @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if Lp().player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(eventbox) expopover.populate() expopover.show() elif Lp().player.current_track.id is not None: if event.button == 1: if Lp().player.current_track.id == Type.RADIOS: if self._pop_tunein is None: self._pop_tunein = TuneinPopover() self._pop_tunein.populate() self._pop_tunein.set_relative_to(self._infobox) self._pop_tunein.show() else: if self._pop_infos is None: self._pop_infos = InfosPopover() self._pop_infos.set_relative_to(self._infobox) self._pop_infos.show() elif Lp().player.current_track.id >= 0: menu = PopToolbarMenu(Lp().player.current_track.id) popover = TrackMenuPopover( Lp().player.current_track.id, PopToolbarMenu(Lp().player.current_track.id)) popover.set_relative_to(self._infobox) popover.show() return True def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ # Can't add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ style = self.get_style_context() padding = style.get_padding(style.get_state()) self._cover_size = self.get_allocated_height()\ - padding.top - padding.bottom eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))
class RadiosView(View): """ Init radios ui with a scrolled grid of radios widgets """ def __init__(self): View.__init__(self) self._signal = None self._radios_manager = RadiosManager() self._radios_manager.connect('playlists-changed', self._on_radios_changed) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/RadiosView.ui') builder.connect_signals(self) widget = builder.get_object('widget') self._pop_tunein = TuneinPopover(self._radios_manager) self._pop_tunein.set_relative_to(builder.get_object('search_btn')) self._sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.BOTH) self._radiobox = Gtk.FlowBox() self._radiobox.set_sort_func(self._sort_radios) self._radiobox.set_selection_mode(Gtk.SelectionMode.NONE) self._radiobox.connect("child-activated", self._on_album_activated) self._radiobox.set_property('column-spacing', 5) self._radiobox.set_property('row-spacing', 5) self._radiobox.set_homogeneous(True) self._radiobox.set_max_children_per_line(1000) self._radiobox.show() self._viewport.set_property("valign", Gtk.Align.START) self._viewport.set_property('margin', 5) self._viewport.add(self._radiobox) self._scrolledWindow.set_property('expand', True) self.add(widget) self.add(self._scrolledWindow) """ Populate view with tracks from playlist Thread safe """ def populate(self): radios_name = [] # Get radios name for (i, name) in self._radios_manager.get(): radios_name.append(name) GLib.idle_add(self._add_radios, radios_name) """ Connect player signal """ def do_show(self): View.do_show(self) self._signal = Lp.art.connect('logo-changed', self._on_logo_changed) """ Disconnect player signal """ def do_hide(self): View.do_hide(self) if self._signal is not None: Lp.art.disconnect(self._signal) ####################### # PRIVATE # ####################### """ Return view children @return [RadioWidget] """ def _get_children(self): children = [] for child in self._radiobox.get_children(): widget = child.get_child() children.append(widget) return children """ Sort radios @param a as Gtk.FlowBoxChild @param b as Gtk.FlowBoxChild """ def _sort_radios(self, a, b): child1 = a.get_children()[0] child2 = b.get_children()[0] return child1.get_name().lower() > child2.get_name().lower() """ Show popover for adding a new radio @param widget as Gtk.Widget """ def _on_new_clicked(self, widget): popover = RadioPopover('', self._radios_manager) popover.set_relative_to(widget) popover.show() """ Show popover for searching radios @param widget as Gtk.Widget """ def _on_search_clicked(self, widget): self._pop_tunein.populate() self._pop_tunein.show() """ Update radios @param manager as PlaylistManager """ def _on_radios_changed(self, manager): radios_name = [] currents = [] new_name = None old_widget = None old_child = None # Get radios name for (i, name) in manager.get(): radios_name.append(name) # Get currents widget less removed for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() not in radios_name: old_widget = widget old_child = child else: currents.append(widget.get_name()) # Add the new radio for name in radios_name: if name not in currents: new_name = name break # Rename widget if new_name is not None: if old_widget is not None: old_widget.set_name(new_name) else: self._add_radios([new_name]) # Delete widget elif old_widget is not None: self._radiobox.remove(old_child) old_widget.destroy() """ Update radio logo @param player as Plyaer @param name as string """ def _on_logo_changed(self, player, name): for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() == name: widget.update_cover() """ Pop a radio and add it to the view, repeat operation until radio list is empty @param [radio names as string] """ def _add_radios(self, radios): if radios and not self._stop: radio = radios.pop(0) widget = RadioWidget(radio, self._radios_manager) widget.show() self._sizegroup.add_widget(widget) self._radiobox.insert(widget, -1) GLib.idle_add(self._add_radios, radios) else: self._stop = False return None """ Play album @param flowbox as Gtk.Flowbox @child as Gtk.FlowboxChild """ def _on_album_activated(self, flowbox, child): name = child.get_child().get_name() uris = self._radios_manager.get_tracks(name) if len(uris) > 0: track = Track() track.set_radio(name, uris[0]) Lp.player.load(track)
class ToolbarInfo(Gtk.Bin, InfosController): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) InfosController.__init__(self, ArtSize.SMALL) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfo.ui') builder.connect_signals(self) self.__pop_tunein = None self.__pop_info = None self.__timeout_id = None self.__width = 0 self._infobox = builder.get_object('info') self.add(self._infobox) helper = TouchHelper(self._infobox, None, None) helper.set_long_func(self.__on_info_long) helper.set_short_func(self.__on_info_short) self._spinner = builder.get_object('spinner') self.__labels = builder.get_object('nowplaying_labels') self.__labels.connect('query-tooltip', self.__on_query_tooltip) self.__labels.set_property('has-tooltip', True) self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover = builder.get_object('cover') self._cover.set_property('has-tooltip', True) # Since GTK 3.20, we can set cover full height if Gtk.get_minor_version() > 18: self._cover.get_style_context().add_class('toolbar-cover-frame') else: self._cover.get_style_context().add_class('small-cover-frame') self.connect('realize', self.__on_realize) Lp().player.connect('loading-changed', self.__on_loading_changed) Lp().art.connect('album-artwork-changed', self.__update_cover) Lp().art.connect('radio-artwork-changed', self.__update_logo) def do_get_preferred_width(self): """ We force preferred width @return (int, int) """ return (self.__width, self.__width) def get_preferred_height(self): """ Return preferred height @return (int, int) """ return self.__labels.get_preferred_height() def set_width(self, width): """ Set widget width @param width as int """ self.__width = width self.set_property('width-request', width) ####################### # PROTECTED # ####################### def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) ####################### # PRIVATE # ####################### def __update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp().player.current_track.album.id == album_id: surface = Lp().art.get_album_artwork( Lp().player.current_track.album, self._artsize, self._cover.get_scale_factor()) self._cover.set_from_surface(surface) del surface def __update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp().player.current_track.album_artist == name: pixbuf = Lp().art.get_radio_artwork(name, self._artsize) self._cover.set_from_surface(pixbuf) del pixbuf def __on_info_long(self, args): """ Show current track menu @param args as [] """ if Lp().player.current_track.id >= 0: from lollypop.pop_menu import PlaylistsMenu from lollypop.pop_menu import TrackMenuPopover popover = TrackMenuPopover( Lp().player.current_track, PlaylistsMenu(Lp().player.current_track)) popover.set_relative_to(self._infobox) popover.show() def __on_info_short(self, args): """ Show track information popover @param args as [] """ if Lp().player.current_track.id == Type.EXTERNALS: from lollypop.pop_externals import ExternalsPopover expopover = ExternalsPopover() expopover.set_relative_to(self._infobox) expopover.populate() expopover.show() elif Lp().player.current_track.id is not None: if Lp().player.current_track.id == Type.RADIOS: if self.__pop_tunein is None: from lollypop.pop_tunein import TuneinPopover self.__pop_tunein = TuneinPopover() self.__pop_tunein.populate() self.__pop_tunein.set_relative_to(self._infobox) self.__pop_tunein.show() else: if self.__pop_info is None: from lollypop.pop_info import InfoPopover self.__pop_info = InfoPopover() self.__pop_info.set_relative_to(self._infobox) self.__pop_info.show() def __on_loading_changed(self, player, show): """ Show spinner based on loading status @param player as player @param show as bool """ if show: self._title_label.hide() self._artist_label.hide() self._cover.hide() self._spinner.show() self._spinner.start() self._infobox.show() else: self._spinner.hide() self._spinner.stop() def __on_realize(self, toolbar): """ Calculate art size @param toolbar as ToolbarInfos """ style = self.get_style_context() padding = style.get_padding(style.get_state()) self._artsize = self.get_allocated_height()\ - padding.top - padding.bottom # Since GTK 3.20, we can set cover full height if Gtk.get_minor_version() < 20: self._artsize -= 2 def __on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = GLib.markup_escape_text(self._artist_label.get_text()) title = GLib.markup_escape_text(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True
class RadiosView(LazyLoadingView): """ Show radios in a grid """ def __init__(self): """ Init view """ LazyLoadingView.__init__(self, True) self.__signal = Lp().art.connect("radio-artwork-changed", self.__on_logo_changed) self.__radios_manager = Radios() self.__radios_manager.connect("radios-changed", self.__on_radios_changed) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/RadiosView.ui") builder.connect_signals(self) widget = builder.get_object("widget") self.__empty = builder.get_object("empty") self.__pop_tunein = TuneinPopover(self.__radios_manager) self.__pop_tunein.set_relative_to(builder.get_object("search_btn")) self._box = Gtk.FlowBox() self._box.set_selection_mode(Gtk.SelectionMode.NONE) # Allow lazy loading to not jump up and down self._box.set_homogeneous(True) self._box.set_max_children_per_line(1000) self._box.set_filter_func(self._filter_func) self._box.show() self.__stack = Gtk.Stack() self.__stack.set_transition_duration(500) self.__stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self.__stack.add(self._scrolled) self.__stack.add(self.__empty) self.__stack.show() self._viewport.set_property("valign", Gtk.Align.START) self._viewport.set_property("margin", 5) self._scrolled.set_property("expand", True) self.add(widget) self.add(self.__stack) def populate(self): """ Populate view with tracks from playlist """ Lp().player.set_radios(self.__radios_manager.get()) if Lp().player.current_track.id == Type.RADIOS: Lp().player.set_next() # We force next update Lp().player.set_prev() # We force prev update helper = TaskHelper() helper.run(self.__get_radios, callback=(self.__on_get_radios, )) ####################### # PROTECTED # ####################### def _get_children(self): """ Return view children @return [RadioWidget] """ children = [] for child in self._box.get_children(): children.append(child) return children def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ LazyLoadingView._on_destroy(self, widget) if self.__signal is not None: Lp().art.disconnect(self.__signal) def _on_new_clicked(self, widget): """ Show popover for adding a new radio @param widget as Gtk.Widget """ popover = RadioPopover("", self.__radios_manager) popover.set_relative_to(widget) popover.show() def _on_search_clicked(self, widget): """ Show popover for searching radios @param widget as Gtk.Widget """ self.__pop_tunein.populate() self.__pop_tunein.show() ####################### # PRIVATE # ####################### def __get_radios(self): """ Get radios @return [name] """ radios = [] # Get radios name for (name, url) in self.__radios_manager.get(): radios.append(name) return radios def __on_radios_changed(self, manager): """ Update radios @param manager as PlaylistManager """ radios_name = [] currents = [] new_name = None old_child = None # Get radios name for (name, url) in manager.get(): radios_name.append(name) # Get currents widget less removed for child in self._box.get_children(): if child.title not in radios_name: old_child = child else: currents.append(child.title) # Add the new radio for name in radios_name: if name not in currents: new_name = name break # Rename widget if new_name is not None: if old_child is not None: old_child.set_name(new_name) else: radios = [new_name] self.__show_stack(radios) # Delete widget elif old_child is not None: old_child.destroy() if not self._box.get_children(): self.__show_stack([]) # Update player state based on current view radios = [] for child in self._box.get_children(): name = child.title url = manager.get_url(name) radios.append((name, url)) Lp().player.set_radios(radios) if Lp().player.current_track.id == Type.RADIOS: Lp().player.set_next() # We force next update Lp().player.set_prev() # We force prev update def __show_stack(self, radios): """ Switch empty/radios view based on radios @param [radio names as string] """ if radios: self.__stack.set_visible_child(self._scrolled) self.__add_radios(radios, True) else: self.__stack.set_visible_child(self.__empty) def __add_radios(self, radios, first=False): """ Pop a radio and add it to the view, repeat operation until radio list is empty @param [radio names as string] @param first as bool """ if self._stop: self._stop = False return if radios: radio = radios.pop(0) widget = RadioWidget(radio, self.__radios_manager) widget.connect("overlayed", self._on_overlayed) widget.show() self._lazy_queue.append(widget) if first: self._box.insert(widget, 0) else: self._box.insert(widget, -1) GLib.idle_add(self.__add_radios, radios) else: GLib.idle_add(self.lazy_loading) if self._viewport.get_child() is None: self._viewport.add(self._box) def __on_logo_changed(self, player, name): """ Update radio logo @param player as Plyaer @param name as string """ for child in self._box.get_children(): if child.title == name: child.update_cover() def __on_get_radios(self, radios): """ Switch empty/radios view based on radios @param [radio names as string] """ if radios: self.__stack.set_visible_child(self._scrolled) self.__add_radios(radios, True) else: self.__stack.set_visible_child(self.__empty)
class ToolbarInfos(Gtk.Bin): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfos.ui') builder.connect_signals(self) self._pop_tunein = None self._pop_infos = None self._cover_size = ArtSize.SMALL self._infobox = builder.get_object('infos') self._infobox.set_property('has-tooltip', True) self.add(self._infobox) self._labels = builder.get_object('nowplaying_labels') self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover_frame = builder.get_object('frame') self._cover = builder.get_object('cover') Lp().art.connect('album-artwork-changed', self._update_cover) Lp().art.connect('radio-artwork-changed', self._update_logo) def get_preferred_height(self): """ Return preferred height @return (int, int) """ return self._labels.get_preferred_height() def on_current_changed(self, player): """ Update toolbar on current changed @param player as Player """ art = None if player.current_track.artist == '': self._artist_label.hide() else: self._artist_label.show() self._artist_label.set_text(player.current_track.artist) if player.current_track.title == '': self._title_label.hide() else: self._title_label.show() self._title_label.set_text(player.current_track.title) if player.current_track.id == Type.RADIOS: art = Lp().art.get_radio_artwork( player.current_track.artist, self._cover_size * self.get_scale_factor()) elif player.current_track.id == Type.EXTERNALS: art = Lp().art.get_album_artwork2( player.current_track.uri, self._cover_size * self.get_scale_factor()) elif player.current_track.id is not None: art = Lp().art.get_album_artwork( player.current_track.album, self._cover_size * self.get_scale_factor()) if art is not None: self._cover.set_from_surface(art) del art self._cover.set_tooltip_text(player.current_track.album.name) self._cover_frame.show() else: self._cover_frame.hide() def on_status_changed(self, player): """ Update infos on status changed @param player as Player """ pass ####################### # PRIVATE # ####################### def _update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp().player.current_track.album.id == album_id: surface = Lp().art.get_album_artwork( Lp().player.current_track.album, self._cover_size) self._cover.set_from_surface(surface) del surface def _update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp().player.current_track.artist == name: pixbuf = Lp().art.get_radio_artwork(name, self._cover_size) self._cover.set_from_surface(pixbuf) del pixbuf def _on_infobox_clicked(self, eventbox, event): """ Pop albums from current artistleft click Show playlist menu on right @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if Lp().player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(eventbox) expopover.populate() expopover.show() elif Lp().player.current_track.id is not None: if event.button == 1: if Lp().player.current_track.id == Type.RADIOS: if self._pop_tunein is None: self._pop_tunein = TuneinPopover() self._pop_tunein.populate() self._pop_tunein.set_relative_to(self._infobox) self._pop_tunein.show() else: if self._pop_infos is None: self._pop_infos = InfosPopover() self._pop_infos.set_relative_to(self._infobox) self._pop_infos.show() elif Lp().player.current_track.id >= 0: popover = TrackMenuPopover( Lp().player.current_track.id, PopToolbarMenu(Lp().player.current_track.id)) popover.set_relative_to(self._infobox) popover.show() return True def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ # Can't add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ style = self.get_style_context() padding = style.get_padding(style.get_state()) self._cover_size = self.get_allocated_height()\ - padding.top - padding.bottom - 2 eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))
class ToolbarInfos(Gtk.Bin): """ Informations toolbar """ def __init__(self): """ Init toolbar """ Gtk.Bin.__init__(self) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ToolbarInfos.ui') builder.connect_signals(self) self._pop_tunein = None self._pop_infos = None self._cover_size = ArtSize.SMALL self._infobox = builder.get_object('infos') self._infobox.set_property('has-tooltip', True) self.add(self._infobox) self._title_label = builder.get_object('title') self._artist_label = builder.get_object('artist') self._cover_frame = builder.get_object('frame') self._cover = builder.get_object('cover') Lp().art.connect('album-artwork-changed', self._update_cover) Lp().art.connect('radio-artwork-changed', self._update_logo) def on_current_changed(self, player): """ Update toolbar on current changed @param player as Player """ art = None if player.current_track.artist == '': self._artist_label.hide() else: self._artist_label.show() self._artist_label.set_text(player.current_track.artist) if player.current_track.title == '': self._title_label.hide() else: self._title_label.show() self._title_label.set_text(player.current_track.title) if player.current_track.id == Type.RADIOS: art = Lp().art.get_radio_artwork( player.current_track.artist, self._cover_size * self.get_scale_factor()) elif player.current_track.id == Type.EXTERNALS: art = Lp().art.get_album_artwork2( player.current_track.uri, self._cover_size * self.get_scale_factor()) elif player.current_track.id is not None: art = Lp().art.get_album_artwork( player.current_track.album, self._cover_size * self.get_scale_factor()) if art is not None: self._cover.set_from_surface(art) del art self._cover.set_tooltip_text(player.current_track.album.name) self._cover_frame.show() else: self._cover_frame.hide() def on_status_changed(self, player): """ Update infos on status changed @param player as Player """ pass ####################### # PRIVATE # ####################### def _update_cover(self, art, album_id): """ Update cover for album_id @param art as Art @param album id as int """ if Lp().player.current_track.album.id == album_id: surface = Lp().art.get_album_artwork( Lp().player.current_track.album, self._cover_size) self._cover.set_from_surface(surface) del surface def _update_logo(self, art, name): """ Update logo for name @param art as Art @param name as str """ if Lp().player.current_track.artist == name: pixbuf = Lp().art.get_radio_artwork(name, self._cover_size) self._cover.set_from_surface(pixbuf) del pixbuf def _on_infobox_clicked(self, eventbox, event): """ Pop albums from current artistleft click Show playlist menu on right @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if Lp().player.current_track.id == Type.EXTERNALS: expopover = ExternalsPopover() expopover.set_relative_to(eventbox) expopover.populate() expopover.show() elif Lp().player.current_track.id is not None: if event.button == 1: if Lp().player.current_track.id == Type.RADIOS: if self._pop_tunein is None: self._pop_tunein = TuneinPopover() self._pop_tunein.populate() self._pop_tunein.set_relative_to(self._infobox) self._pop_tunein.show() else: if self._pop_infos is None: self._pop_infos = InfosPopover() self._pop_infos.set_relative_to(self._infobox) self._pop_infos.show() elif Lp().player.current_track.id >= 0: menu = PopToolbarMenu(Lp().player.current_track.id, None) popover = Gtk.Popover.new_from_model(eventbox, menu) rating = RatingWidget(Lp().player.current_track) rating.set_margin_top(5) rating.set_margin_bottom(5) rating.set_property('halign', Gtk.Align.START) rating.set_property('hexpand', True) rating.show() loved = LovedWidget(Lp().player.current_track.id) loved.set_margin_end(5) loved.set_margin_top(5) loved.set_margin_bottom(5) loved.set_property('halign', Gtk.Align.END) loved.set_property('hexpand', True) loved.show() # Hack to add two widgets in popover # Use a Gtk.PopoverMenu later # (GTK>3.16 available on Debian stable) stack = Gtk.Stack() grid = Gtk.Grid() grid.set_orientation(Gtk.Orientation.VERTICAL) stack.add_named(grid, 'main') stack.show_all() menu_widget = popover.get_child() menu_widget.reparent(grid) separator = Gtk.Separator() separator.show() grid.add(separator) hgrid = Gtk.Grid() hgrid.add(rating) hgrid.add(loved) hgrid.show() grid.add(hgrid) popover.add(stack) popover.show() return True def _on_query_tooltip(self, widget, x, y, keyboard, tooltip): """ Show tooltip if needed @param widget as Gtk.Widget @param x as int @param y as int @param keyboard as bool @param tooltip as Gtk.Tooltip """ # Can't add a \n in markup # GTK bug => https://bugzilla.gnome.org/show_bug.cgi?id=749965 layout_title = self._title_label.get_layout() layout_artist = self._artist_label.get_layout() if layout_title.is_ellipsized() or layout_artist.is_ellipsized(): artist = escape(self._artist_label.get_text()) title = escape(self._title_label.get_text()) tooltip.set_markup("<b>%s</b> - %s" % (artist, title)) else: return False return True def _on_eventbox_realize(self, eventbox): """ Show hand cursor over """ style = self.get_style_context() padding = style.get_padding(style.get_state()) self._cover_size = self.get_allocated_height()\ - padding.top - padding.bottom eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1))