class Row(Gtk.ListBoxRow): """ A row """ def __init__(self, rowid, num): """ Init row widgets @param rowid as int @param num as int @param show loved as bool """ # We do not use Gtk.Builder for speed reasons Gtk.ListBoxRow.__init__(self) self._artists_label = None self._track = Track(rowid) self.__number = num self.__preview_timeout_id = None self.__context_timeout_id = None self.__context = None self._indicator = IndicatorWidget(self._track.id) self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) self._row_widget = Gtk.EventBox() self._row_widget.connect("button-press-event", self.__on_button_press) self._row_widget.connect("enter-notify-event", self.__on_enter_notify) self._row_widget.connect("leave-notify-event", self.__on_leave_notify) self._grid = Gtk.Grid() self._grid.set_column_spacing(5) self._row_widget.add(self._grid) self._title_label = Gtk.Label.new(self._track.name) self._title_label.set_property("has-tooltip", True) self._title_label.connect("query-tooltip", self.__on_query_tooltip) self._title_label.set_property("hexpand", True) self._title_label.set_property("halign", Gtk.Align.START) self._title_label.set_ellipsize(Pango.EllipsizeMode.END) if self._track.non_album_artists: self._artists_label = Gtk.Label.new( GLib.markup_escape_text(", ".join( self._track.non_album_artists))) self._artists_label.set_use_markup(True) self._artists_label.set_property("has-tooltip", True) self._artists_label.connect("query-tooltip", self.__on_query_tooltip) self._artists_label.set_property("hexpand", True) self._artists_label.set_property("halign", Gtk.Align.END) self._artists_label.set_ellipsize(Pango.EllipsizeMode.END) self._artists_label.set_opacity(0.3) self._artists_label.set_margin_end(5) self._artists_label.show() self._duration_label = Gtk.Label.new( seconds_to_string(self._track.duration)) self._duration_label.get_style_context().add_class("dim-label") self._num_label = Gtk.Label() self._num_label.set_ellipsize(Pango.EllipsizeMode.END) self._num_label.set_property("valign", Gtk.Align.CENTER) self._num_label.set_width_chars(4) self._num_label.get_style_context().add_class("dim-label") self.update_num_label() self.__menu_button = Gtk.Button.new() # Here a hack to make old Gtk version support min-height css attribute # min-height = 24px, borders = 2px, we set directly on stack # min-width = 24px, borders = 2px, padding = 8px self.__menu_button.set_size_request(34, 26) self.__menu_button.set_relief(Gtk.ReliefStyle.NONE) self.__menu_button.get_style_context().add_class("menu-button") self.__menu_button.get_style_context().add_class("track-menu-button") self._grid.add(self._num_label) self._grid.add(self._title_label) if self._artists_label is not None: self._grid.add(self._artists_label) self._grid.add(self._duration_label) self._grid.add(self.__menu_button) self.add(self._row_widget) self.get_style_context().add_class("trackrow") def show_spinner(self): """ Show spinner """ self._indicator.show_spinner() def set_indicator(self, playing, loved): """ Show indicator @param widget name as str @param playing as bool @param loved as bool """ self._indicator.clear() if playing: self.get_style_context().remove_class("trackrow") self.get_style_context().add_class("trackrowplaying") if loved: self._indicator.play_loved() else: self._indicator.play() else: self.get_style_context().remove_class("trackrowplaying") self.get_style_context().add_class("trackrow") if loved and self.__context is None: self._indicator.loved() else: self._indicator.empty() def set_number(self, num): """ Set number @param number as int """ self.__number = num def update_duration(self): """ Update duration for row """ # Get a new track to get new duration (cache) track = Track(self._track.id) self._duration_label.set_text(seconds_to_string(track.duration)) def update_num_label(self): """ Update position label for row """ if Lp().player.track_in_queue(self._track): self._num_label.get_style_context().add_class("queued") pos = Lp().player.get_track_position(self._track.id) self._num_label.set_text(str(pos)) elif self.__number > 0: self._num_label.get_style_context().remove_class("queued") self._num_label.set_text(str(self.__number)) else: self._num_label.get_style_context().remove_class("queued") self._num_label.set_text("") @property def id(self): """ Get object id @return Current id as int """ return self._track.id ####################### # PRIVATE # ####################### def __play_preview(self): """ Play track @param widget as Gtk.Widget """ Lp().player.preview.set_property("uri", self._track.uri) Lp().player.preview.set_state(Gst.State.PLAYING) self.set_indicator(True, False) self.__preview_timeout_id = None def __on_map(self, widget): """ Fix for Gtk < 3.18, if we are in a popover, do not show menu button """ widget = self.get_parent() while widget is not None: if isinstance(widget, Gtk.Popover): break widget = widget.get_parent() if widget is None: self._grid.add(self.__menu_button) self.__menu_button.show() def __on_artist_button_press(self, eventbox, event): """ Go to artist page @param eventbox as Gtk.EventBox @param event as Gdk.EventButton """ Lp().window.show_artists_albums(self._album.artist_ids) return True def __on_enter_notify(self, widget, event): """ Set image on buttons now, speed reason @param widget as Gtk.Widget @param event as Gdk.Event """ if self.__context_timeout_id is not None: GLib.source_remove(self.__context_timeout_id) self.__context_timeout_id = None if Lp().settings.get_value("preview-output").get_string() != "": self.__preview_timeout_id = GLib.timeout_add( 500, self.__play_preview) if self.__menu_button.get_image() is None: image = Gtk.Image.new_from_icon_name("go-previous-symbolic", Gtk.IconSize.MENU) image.set_opacity(0.2) self.__menu_button.set_image(image) self.__menu_button.connect("clicked", self.__on_button_clicked) self._indicator.update_button() def __on_leave_notify(self, widget, event): """ Stop preview @param widget as Gtk.Widget @param event as Gdk.Event """ allocation = widget.get_allocation() if event.x <= 0 or\ event.x >= allocation.width or\ event.y <= 0 or\ event.y >= allocation.height: if self.__context is not None and\ self.__context_timeout_id is None: self.__context_timeout_id = GLib.timeout_add( 1000, self.__on_button_clicked, self.__menu_button) if Lp().settings.get_value("preview-output").get_string() != "": if self.__preview_timeout_id is not None: GLib.source_remove(self.__preview_timeout_id) self.__preview_timeout_id = None self.set_indicator( Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) Lp().player.preview.set_state(Gst.State.NULL) def __on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if self.__context is not None: self.__on_button_clicked(self.__menu_button) if event.button == 3: if GLib.getenv("WAYLAND_DISPLAY") != "" and\ self.get_ancestor(Gtk.Popover) is not None: print("https://bugzilla.gnome.org/show_bug.cgi?id=774148") window = widget.get_window() if window == event.window: self.__popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self.__on_button_clicked(self.__menu_button) return True elif event.button == 2: if self._track.id in Lp().player.queue: Lp().player.del_from_queue(self._track.id) else: Lp().player.append_to_queue(self._track.id) def __on_button_clicked(self, button): """ Popup menu for track relative to button @param button as Gtk.Button """ self.__context_timeout_id = None image = self.__menu_button.get_image() if self.__context is None: image.set_from_icon_name("go-next-symbolic", Gtk.IconSize.MENU) self.__context = ContextWidget(self._track, button) self.__context.set_property("halign", Gtk.Align.END) self.__context.show() self._duration_label.hide() self._grid.insert_next_to(button, Gtk.PositionType.LEFT) self._grid.attach_next_to(self.__context, button, Gtk.PositionType.LEFT, 1, 1) self.set_indicator(Lp().player.current_track.id == self._track.id, False) else: image.set_from_icon_name("go-previous-symbolic", Gtk.IconSize.MENU) self.__context.destroy() self._duration_label.show() self.__context = None self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) def __popup_menu(self, widget, xcoordinate=None, ycoordinate=None): """ Popup menu for track @param widget as Gtk.Button @param xcoordinate as int (or None) @param ycoordinate as int (or None) """ popover = TrackMenuPopover(self._track, TrackMenu(self._track)) if xcoordinate is not None and ycoordinate is not None: rect = widget.get_allocation() rect.x = xcoordinate rect.y = ycoordinate rect.width = rect.height = 1 popover.set_relative_to(widget) popover.set_pointing_to(rect) popover.connect("closed", self.__on_closed) self.get_style_context().add_class("track-menu-selected") popover.show() def __on_closed(self, widget): """ Remove selected style @param widget as Gtk.Popover """ self.get_style_context().remove_class("track-menu-selected") 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 """ text = "" layout = widget.get_layout() label = widget.get_text() if layout.is_ellipsized(): text = "%s" % (GLib.markup_escape_text(label)) widget.set_tooltip_markup(text)
class Row(Gtk.ListBoxRow): """ A row """ def __init__(self, show_loved): """ Init row widgets @param show loved as bool """ # We do not use Gtk.Builder for speed reasons Gtk.ListBoxRow.__init__(self) self._indicator = IndicatorWidget() self._show_loved = show_loved self._object_id = None self._number = 0 self._row_widget = Gtk.EventBox() self._grid = Gtk.Grid() self._grid.set_column_spacing(5) self._row_widget.add(self._grid) self._title_label = Gtk.Label() self._title_label.set_property('has-tooltip', True) self._title_label.set_property('hexpand', True) self._title_label.set_property('halign', Gtk.Align.START) self._title_label.set_ellipsize(Pango.EllipsizeMode.END) self._duration_label = Gtk.Label() self._duration_label.get_style_context().add_class('dim-label') self._num_label = Gtk.Label() self._num_label.set_ellipsize(Pango.EllipsizeMode.END) self._num_label.set_width_chars(4) self._num_label.get_style_context().add_class('dim-label') self._grid.add(self._num_label) self._grid.add(self._title_label) self._grid.add(self._duration_label) self.add(self._row_widget) self.get_style_context().add_class('trackrow') def show_cover(self, show): """ Show cover @param show as bool """ pass def show_menu(self, show): """ Show menu """ pass def show_indicator(self, playing, loved): """ Show indicator @param widget name as str @param playing as bool @param loved as bool """ self._indicator.clear() if playing: self.get_style_context().remove_class('trackrow') self.get_style_context().add_class('trackrowplaying') if self._show_loved and loved: self._indicator.play_loved() else: self._indicator.play() else: self.get_style_context().remove_class('trackrowplaying') self.get_style_context().add_class('trackrow') if self._show_loved and loved: self._indicator.loved() else: self._indicator.empty() def set_num_label(self, label): """ Set num label @param label as string """ self._num_label.set_markup(label) def set_title_label(self, label): """ Set title label @param label as string """ self._title_label.set_markup(label) def set_duration_label(self, label): """ Set duration label @param label as string """ self._duration_label.set_text(label) def set_object_id(self, object_id): """ Store current object id @param object id as int """ self._object_id = object_id def get_object_id(self): """ Get object id @return Current object id as int """ return self._object_id def set_number(self, num): """ Set track number @param num as int """ self._number = num def get_number(self): """ Get track number @return num as int """ return self._number def set_cover(self, pixbuf, tooltip): """ Set cover @param cover as Gdk.Pixbuf @param tooltip as str """ pass ####################### # PRIVATE # ####################### def _on_title_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 = self._title_label.get_layout() if layout.is_ellipsized(): label = self._title_label.get_label() self._title_label.set_tooltip_markup(label) else: self._title_label.set_tooltip_text('')
class Row(Gtk.ListBoxRow): """ A row """ def __init__(self, rowid, num): """ Init row widgets @param rowid as int @param num as int @param show loved as bool """ # We do not use Gtk.Builder for speed reasons Gtk.ListBoxRow.__init__(self) self._track = Track(rowid) self._number = num self._indicator = IndicatorWidget(self._track.id) self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) self._row_widget = Gtk.EventBox() self._row_widget.connect('button-press-event', self._on_button_press) self._row_widget.connect('enter-notify-event', self._on_enter_notify) self._grid = Gtk.Grid() self._grid.set_column_spacing(5) self._row_widget.add(self._grid) self._title_label = Gtk.Label.new(self._track.formated_name()) self._title_label.set_use_markup(True) self._title_label.set_property('has-tooltip', True) self._title_label.connect('query-tooltip', self._on_title_query_tooltip) self._title_label.set_property('hexpand', True) self._title_label.set_property('halign', Gtk.Align.START) self._title_label.set_ellipsize(Pango.EllipsizeMode.END) self._duration_label = Gtk.Label.new( seconds_to_string(self._track.duration)) self._duration_label.get_style_context().add_class('dim-label') self._num_label = Gtk.Label() self._num_label.set_ellipsize(Pango.EllipsizeMode.END) self._num_label.set_property('valign', Gtk.Align.CENTER) self._num_label.set_width_chars(4) self._num_label.get_style_context().add_class('dim-label') self.update_num_label() self._menu_button = Gtk.Button.new() # Here a hack to make old Gtk version support min-height css attribute # min-height = 24px, borders = 2px, we set directly on stack # min-width = 24px, borders = 2px, padding = 8px self._menu_button.set_size_request(34, 26) self._menu_button.set_relief(Gtk.ReliefStyle.NONE) self._menu_button.get_style_context().add_class('menu-button') self._menu_button.get_style_context().add_class('track-menu-button') self._grid.add(self._num_label) self._grid.add(self._title_label) self._grid.add(self._duration_label) # TODO Remove this later if Gtk.get_minor_version() > 16: self._grid.add(self._menu_button) else: self.connect('map', self._on_map) self.add(self._row_widget) self.get_style_context().add_class('trackrow') def set_indicator(self, playing, loved): """ Show indicator @param widget name as str @param playing as bool @param loved as bool """ self._indicator.clear() if playing: self.get_style_context().remove_class('trackrow') self.get_style_context().add_class('trackrowplaying') if loved: self._indicator.play_loved() else: self._indicator.play() else: self.get_style_context().remove_class('trackrowplaying') self.get_style_context().add_class('trackrow') if loved: self._indicator.loved() else: self._indicator.empty() def set_number(self, num): """ Set number @param number as int """ self._number = num def update_num_label(self): """ Update position label for row """ if Lp().player.is_in_queue(self._track.id): self._num_label.get_style_context().add_class('queued') pos = Lp().player.get_track_position(self._track.id) self._num_label.set_text(str(pos)) elif self._number > 0: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text(str(self._number)) else: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text('') def get_id(self): """ Get object id @return Current id as int """ return self._track.id ####################### # PRIVATE # ####################### def _on_map(self, widget): """ Fix for Gtk < 3.18, if we are in a popover, do not show menu button """ widget = self.get_parent() while widget is not None: if isinstance(widget, Gtk.Popover): break widget = widget.get_parent() if widget is None: self._grid.add(self._menu_button) def _on_enter_notify(self, widget, event): """ Set image on buttons now, speed reason @param widget as Gtk.Widget @param event as Gdk.Event """ if self._menu_button.get_image() is None: image = Gtk.Image.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.MENU) image.set_opacity(0.2) self._menu_button.set_image(image) self._menu_button.connect('clicked', self._on_button_clicked) self._indicator.update_button() def _on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if event.button == 3 and Gtk.get_minor_version() > 16: window = widget.get_window() if window == event.window: self._popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self._popup_menu(self._menu_button) return True elif event.button == 2: if self._track.id in Lp().player.get_queue(): Lp().player.del_from_queue(self._track.id) else: Lp().player.append_to_queue(self._track.id) def _on_button_clicked(self, widget): """ Popup menu for track relative to button @param widget as Gtk.Button """ self._popup_menu(widget) def _popup_menu(self, widget, xcoordinate=None, ycoordinate=None): """ Popup menu for track @param widget as Gtk.Button @param xcoordinate as int (or None) @param ycoordinate as int (or None) """ popover = TrackMenuPopover(self._track.id, TrackMenu(self._track.id)) popover.set_relative_to(widget) if xcoordinate is not None and ycoordinate is not None: rect = widget.get_allocation() rect.x = xcoordinate rect.y = ycoordinate rect.width = rect.height = 1 popover.set_pointing_to(rect) popover.connect('closed', self._on_closed) self.get_style_context().add_class('track-menu-selected') popover.show() def _on_closed(self, widget): """ Remove selected style @param widget as Gtk.Popover """ self.get_style_context().remove_class('track-menu-selected') def _on_title_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 = self._title_label.get_layout() if layout.is_ellipsized(): self._title_label.set_tooltip_markup(self._track.formated_name()) elif len(self._track.artists) > 1: self._title_label.set_tooltip_text( ", ".join(self._track.artists[1:])) else: self._title_label.set_tooltip_text('')
class Row(Gtk.ListBoxRow): """ A row """ def __init__(self, rowid, num): """ Init row widgets @param rowid as int @param num as int @param show loved as bool """ # We do not use Gtk.Builder for speed reasons Gtk.ListBoxRow.__init__(self) self._artists_label = None self._track = Track(rowid) self.__number = num self.__timeout_id = None self._indicator = IndicatorWidget(self._track.id) self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) self._row_widget = Gtk.EventBox() self._row_widget.connect('button-press-event', self.__on_button_press) self._row_widget.connect('enter-notify-event', self.__on_enter_notify) self._grid = Gtk.Grid() self._grid.set_column_spacing(5) self._row_widget.add(self._grid) self._title_label = Gtk.Label.new(self._track.name) self._title_label.set_property('has-tooltip', True) self._title_label.connect('query-tooltip', self.__on_query_tooltip) self._title_label.set_property('hexpand', True) self._title_label.set_property('halign', Gtk.Align.START) self._title_label.set_ellipsize(Pango.EllipsizeMode.END) if self._track.non_album_artists: self._artists_label = Gtk.Label.new( GLib.markup_escape_text(", ".join( self._track.non_album_artists))) self._artists_label.set_use_markup(True) self._artists_label.set_property('has-tooltip', True) self._artists_label.connect('query-tooltip', self.__on_query_tooltip) self._artists_label.set_property('hexpand', True) self._artists_label.set_property('halign', Gtk.Align.END) self._artists_label.set_ellipsize(Pango.EllipsizeMode.END) self._artists_label.set_opacity(0.3) self._artists_label.set_margin_end(5) self._artists_label.show() self._duration_label = Gtk.Label.new( seconds_to_string(self._track.duration)) self._duration_label.get_style_context().add_class('dim-label') self._num_label = Gtk.Label() self._num_label.set_ellipsize(Pango.EllipsizeMode.END) self._num_label.set_property('valign', Gtk.Align.CENTER) self._num_label.set_width_chars(4) self._num_label.get_style_context().add_class('dim-label') self.update_num_label() self.__menu_button = Gtk.Button.new() # Here a hack to make old Gtk version support min-height css attribute # min-height = 24px, borders = 2px, we set directly on stack # min-width = 24px, borders = 2px, padding = 8px self.__menu_button.set_size_request(34, 26) self.__menu_button.set_relief(Gtk.ReliefStyle.NONE) self.__menu_button.get_style_context().add_class('menu-button') self.__menu_button.get_style_context().add_class('track-menu-button') self._grid.add(self._num_label) self._grid.add(self._title_label) if self._artists_label is not None: self._grid.add(self._artists_label) self._grid.add(self._duration_label) # TODO Remove this later if Gtk.get_minor_version() > 16: self._grid.add(self.__menu_button) else: self.connect('map', self.__on_map) self.add(self._row_widget) self.get_style_context().add_class('trackrow') def show_spinner(self): """ Show spinner """ self._indicator.show_spinner() def set_indicator(self, playing, loved): """ Show indicator @param widget name as str @param playing as bool @param loved as bool """ self._indicator.clear() if playing: self.get_style_context().remove_class('trackrow') self.get_style_context().add_class('trackrowplaying') if loved: self._indicator.play_loved() else: self._indicator.play() else: self.get_style_context().remove_class('trackrowplaying') self.get_style_context().add_class('trackrow') if loved: self._indicator.loved() else: self._indicator.empty() def set_number(self, num): """ Set number @param number as int """ self.__number = num def update_duration(self): """ Update duration for row """ # Get a new track to get new duration (cache) track = Track(self._track.id) self._duration_label.set_text(seconds_to_string(track.duration)) def update_num_label(self): """ Update position label for row """ if Lp().player.is_in_queue(self._track.id): self._num_label.get_style_context().add_class('queued') pos = Lp().player.get_track_position(self._track.id) self._num_label.set_text(str(pos)) elif self.__number > 0: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text(str(self.__number)) else: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text('') @property def id(self): """ Get object id @return Current id as int """ return self._track.id ####################### # PRIVATE # ####################### def __play_preview(self): """ Play track @param widget as Gtk.Widget """ Lp().player.preview.set_property('uri', self._track.uri) Lp().player.preview.set_state(Gst.State.PLAYING) self.set_indicator(True, False) self.__timeout_id = None def __on_map(self, widget): """ Fix for Gtk < 3.18, if we are in a popover, do not show menu button """ widget = self.get_parent() while widget is not None: if isinstance(widget, Gtk.Popover): break widget = widget.get_parent() if widget is None: self._grid.add(self.__menu_button) self.__menu_button.show() def __on_enter_notify(self, widget, event): """ Set image on buttons now, speed reason @param widget as Gtk.Widget @param event as Gdk.Event """ if Lp().settings.get_value('preview-output').get_string() != '': widget.connect('leave-notify-event', self.__on_leave_notify) self.__timeout_id = GLib.timeout_add(500, self.__play_preview) if self.__menu_button.get_image() is None: image = Gtk.Image.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.MENU) image.set_opacity(0.2) self.__menu_button.set_image(image) self.__menu_button.connect('clicked', self.__on_button_clicked) self._indicator.update_button() def __on_leave_notify(self, widget, event): """ Stop preview @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 self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) Lp().player.preview.set_state(Gst.State.NULL) widget.disconnect_by_func(self.__on_leave_notify) def __on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if event.button == 3 and Gtk.get_minor_version() > 16: window = widget.get_window() if window == event.window: self.__popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self.__popup_menu(self.__menu_button) return True elif event.button == 2: if self._track.id in Lp().player.get_queue(): Lp().player.del_from_queue(self._track.id) else: Lp().player.append_to_queue(self._track.id) def __on_button_clicked(self, widget): """ Popup menu for track relative to button @param widget as Gtk.Button """ self.__popup_menu(widget) def __popup_menu(self, widget, xcoordinate=None, ycoordinate=None): """ Popup menu for track @param widget as Gtk.Button @param xcoordinate as int (or None) @param ycoordinate as int (or None) """ popover = TrackMenuPopover(self._track, TrackMenu(self._track)) popover.set_relative_to(widget) if xcoordinate is not None and ycoordinate is not None: rect = widget.get_allocation() rect.x = xcoordinate rect.y = ycoordinate rect.width = rect.height = 1 popover.set_pointing_to(rect) popover.connect('closed', self.__on_closed) self.get_style_context().add_class('track-menu-selected') popover.show() def __on_closed(self, widget): """ Remove selected style @param widget as Gtk.Popover """ self.get_style_context().remove_class('track-menu-selected') 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 """ text = '' layout = widget.get_layout() label = widget.get_text() if layout.is_ellipsized(): text = "%s" % (GLib.markup_escape_text(label)) widget.set_tooltip_markup(text)
class Row(Gtk.ListBoxRow): """ A row """ def __init__(self, show_loved): """ Init row widgets @param show loved as bool """ # We do not use Gtk.Builder for speed reasons Gtk.ListBoxRow.__init__(self) self._indicator = IndicatorWidget() self._show_loved = show_loved self._id = None self._number = 0 self._row_widget = Gtk.EventBox() self._grid = Gtk.Grid() self._grid.set_column_spacing(5) self._row_widget.add(self._grid) self._title_label = Gtk.Label() self._title_label.set_property('has-tooltip', True) self._title_label.set_property('hexpand', True) self._title_label.set_property('halign', Gtk.Align.START) self._title_label.set_ellipsize(Pango.EllipsizeMode.END) self._duration_label = Gtk.Label() self._duration_label.get_style_context().add_class('dim-label') self._num_label = Gtk.Label() self._num_label.set_ellipsize(Pango.EllipsizeMode.END) self._num_label.set_property('valign', Gtk.Align.CENTER) self._num_label.set_width_chars(4) self._num_label.get_style_context().add_class('dim-label') self._menu_button = Gtk.Button.new_from_icon_name('open-menu-symbolic', Gtk.IconSize.MENU) self._menu_button.set_relief(Gtk.ReliefStyle.NONE) self._menu_button.get_style_context().add_class('menu-button') self._menu_button.get_style_context().add_class('track-menu-button') self._menu_button.get_image().set_opacity(0.2) self._menu_button.show() self._row_widget.connect('button-press-event', self._on_button_press) self._menu_button.connect('clicked', self._on_button_clicked) self._grid.add(self._num_label) self._grid.add(self._title_label) self._grid.add(self._duration_label) # TODO Remove this later if Gtk.get_minor_version() > 16: self._grid.add(self._menu_button) else: self.connect('map', self._on_map) self.add(self._row_widget) self.get_style_context().add_class('trackrow') def show_indicator(self, playing, loved): """ Show indicator @param widget name as str @param playing as bool @param loved as bool """ self._indicator.clear() if playing: self.get_style_context().remove_class('trackrow') self.get_style_context().add_class('trackrowplaying') if self._show_loved and loved: self._indicator.play_loved() else: self._indicator.play() else: self.get_style_context().remove_class('trackrowplaying') self.get_style_context().add_class('trackrow') if self._show_loved and loved: self._indicator.loved() else: self._indicator.empty() def set_num_label(self, label, queued=False): """ Set num label @param label as string """ if queued: self._num_label.get_style_context().add_class('queued') else: self._num_label.get_style_context().remove_class('queued') self._num_label.set_markup(label) def set_title_label(self, label): """ Set title label @param label as string """ self._title_label.set_markup(label) def set_duration_label(self, label): """ Set duration label @param label as string """ self._duration_label.set_text(label) def set_id(self, id): """ Store current object id @param id as int """ self._id = id self._indicator.set_id(id) def get_id(self): """ Get object id @return Current id as int """ return self._id def set_number(self, num): """ Set track number @param num as int """ self._number = num def get_number(self): """ Get track number @return num as int """ return self._number def set_cover(self, pixbuf, tooltip): """ Set cover @param cover as Gdk.Pixbuf @param tooltip as str """ pass ####################### # PRIVATE # ####################### def _on_map(self, widget): """ Fix for Gtk < 3.18, if we are in a popover, do not show menu button """ widget = self.get_parent() while widget is not None: if isinstance(widget, Gtk.Popover): break widget = widget.get_parent() if widget is None: self._grid.add(self._menu_button) def _on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if event.button == 3: window = widget.get_window() if window == event.window: self._popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self._popup_menu(self._menu_button) return True elif event.button == 2: if self._id in Lp().player.get_queue(): Lp().player.del_from_queue(self._id) else: Lp().player.append_to_queue(self._id) def _on_button_clicked(self, widget): """ Popup menu for track relative to button @param widget as Gtk.Button """ self._popup_menu(widget) def _popup_menu(self, widget, xcoordinate=None, ycoordinate=None): """ Popup menu for track @param widget as Gtk.Button @param xcoordinate as int (or None) @param ycoordinate as int (or None) """ popover = TrackMenuPopover(self._id, TrackMenu(self._id)) popover.set_relative_to(widget) if xcoordinate is not None and ycoordinate is not None: rect = widget.get_allocation() rect.x = xcoordinate rect.y = ycoordinate rect.width = rect.height = 1 popover.set_pointing_to(rect) popover.connect('closed', self._on_closed) self.get_style_context().add_class('track-menu-selected') popover.show() def _on_closed(self, widget): """ Remove selected style @param widget as Gtk.Popover """ self.get_style_context().remove_class('track-menu-selected') def _on_title_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 = self._title_label.get_layout() if layout.is_ellipsized(): label = self._title_label.get_label() self._title_label.set_tooltip_markup(label) else: self._title_label.set_tooltip_text('')
class Row(Gtk.ListBoxRow): """ A row """ def __init__(self, rowid, num): """ Init row widgets @param rowid as int @param num as int @param show loved as bool """ # We do not use Gtk.Builder for speed reasons Gtk.ListBoxRow.__init__(self) self._artists_label = None self._track = Track(rowid) self.__number = num self.__preview_timeout_id = None self.__context_timeout_id = None self.__context = None self._indicator = IndicatorWidget(self._track.id) self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) self._row_widget = Gtk.EventBox() self._row_widget.connect('button-press-event', self.__on_button_press) self._row_widget.connect('enter-notify-event', self.__on_enter_notify) self._row_widget.connect('leave-notify-event', self.__on_leave_notify) self._grid = Gtk.Grid() self._grid.set_column_spacing(5) self._row_widget.add(self._grid) self._title_label = Gtk.Label.new(self._track.name) self._title_label.set_property('has-tooltip', True) self._title_label.connect('query-tooltip', self.__on_query_tooltip) self._title_label.set_property('hexpand', True) self._title_label.set_property('halign', Gtk.Align.START) self._title_label.set_ellipsize(Pango.EllipsizeMode.END) if self._track.non_album_artists: self._artists_label = Gtk.Label.new(GLib.markup_escape_text( ", ".join(self._track.non_album_artists))) self._artists_label.set_use_markup(True) self._artists_label.set_property('has-tooltip', True) self._artists_label.connect('query-tooltip', self.__on_query_tooltip) self._artists_label.set_property('hexpand', True) self._artists_label.set_property('halign', Gtk.Align.END) self._artists_label.set_ellipsize(Pango.EllipsizeMode.END) self._artists_label.set_opacity(0.3) self._artists_label.set_margin_end(5) self._artists_label.show() self._duration_label = Gtk.Label.new( seconds_to_string(self._track.duration)) self._duration_label.get_style_context().add_class('dim-label') self._num_label = Gtk.Label() self._num_label.set_ellipsize(Pango.EllipsizeMode.END) self._num_label.set_property('valign', Gtk.Align.CENTER) self._num_label.set_width_chars(4) self._num_label.get_style_context().add_class('dim-label') self.update_num_label() self.__menu_button = Gtk.Button.new() # Here a hack to make old Gtk version support min-height css attribute # min-height = 24px, borders = 2px, we set directly on stack # min-width = 24px, borders = 2px, padding = 8px self.__menu_button.set_size_request(34, 26) self.__menu_button.set_relief(Gtk.ReliefStyle.NONE) self.__menu_button.get_style_context().add_class('menu-button') self.__menu_button.get_style_context().add_class('track-menu-button') self._grid.add(self._num_label) self._grid.add(self._title_label) if self._artists_label is not None: self._grid.add(self._artists_label) self._grid.add(self._duration_label) # TODO Remove this later if Gtk.get_minor_version() > 16: self._grid.add(self.__menu_button) else: self.connect('map', self.__on_map) self.add(self._row_widget) self.get_style_context().add_class('trackrow') def show_spinner(self): """ Show spinner """ self._indicator.show_spinner() def set_indicator(self, playing, loved): """ Show indicator @param widget name as str @param playing as bool @param loved as bool """ self._indicator.clear() if playing: self.get_style_context().remove_class('trackrow') self.get_style_context().add_class('trackrowplaying') if loved: self._indicator.play_loved() else: self._indicator.play() else: self.get_style_context().remove_class('trackrowplaying') self.get_style_context().add_class('trackrow') if loved and self.__context is None: self._indicator.loved() else: self._indicator.empty() def set_number(self, num): """ Set number @param number as int """ self.__number = num def update_duration(self): """ Update duration for row """ # Get a new track to get new duration (cache) track = Track(self._track.id) self._duration_label.set_text(seconds_to_string(track.duration)) def update_num_label(self): """ Update position label for row """ if Lp().player.track_in_queue(self._track): self._num_label.get_style_context().add_class('queued') pos = Lp().player.get_track_position(self._track.id) self._num_label.set_text(str(pos)) elif self.__number > 0: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text(str(self.__number)) else: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text('') @property def id(self): """ Get object id @return Current id as int """ return self._track.id ####################### # PRIVATE # ####################### def __play_preview(self): """ Play track @param widget as Gtk.Widget """ Lp().player.preview.set_property('uri', self._track.uri) Lp().player.preview.set_state(Gst.State.PLAYING) self.set_indicator(True, False) self.__preview_timeout_id = None def __on_map(self, widget): """ Fix for Gtk < 3.18, if we are in a popover, do not show menu button """ widget = self.get_parent() while widget is not None: if isinstance(widget, Gtk.Popover): break widget = widget.get_parent() if widget is None: self._grid.add(self.__menu_button) self.__menu_button.show() def __on_enter_notify(self, widget, event): """ Set image on buttons now, speed reason @param widget as Gtk.Widget @param event as Gdk.Event """ if self.__context_timeout_id is not None: GLib.source_remove(self.__context_timeout_id) self.__context_timeout_id = None if Lp().settings.get_value('preview-output').get_string() != '': self.__preview_timeout_id = GLib.timeout_add(500, self.__play_preview) if self.__menu_button.get_image() is None: image = Gtk.Image.new_from_icon_name('go-previous-symbolic', Gtk.IconSize.MENU) image.set_opacity(0.2) self.__menu_button.set_image(image) self.__menu_button.connect('clicked', self.__on_button_clicked) self._indicator.update_button() def __on_leave_notify(self, widget, event): """ Stop preview @param widget as Gtk.Widget @param event as Gdk.Event """ allocation = widget.get_allocation() if event.x <= 0 or\ event.x >= allocation.width or\ event.y <= 0 or\ event.y >= allocation.height: if self.__context is not None and\ self.__context_timeout_id is None: self.__context_timeout_id = GLib.timeout_add( 1000, self.__on_button_clicked, self.__menu_button) if Lp().settings.get_value('preview-output').get_string() != '': if self.__preview_timeout_id is not None: GLib.source_remove(self.__preview_timeout_id) self.__preview_timeout_id = None self.set_indicator( Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) Lp().player.preview.set_state(Gst.State.NULL) def __on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if self.__context is not None: self.__on_button_clicked(self.__menu_button) if event.button == 3: if GLib.getenv("WAYLAND_DISPLAY") != "" and\ self.get_ancestor(Gtk.Popover) is not None: print("https://bugzilla.gnome.org/show_bug.cgi?id=774148") window = widget.get_window() if window == event.window: self.__popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self.__on_button_clicked(self.__menu_button) return True elif event.button == 2: if self._track.id in Lp().player.get_queue(): Lp().player.del_from_queue(self._track.id) else: Lp().player.append_to_queue(self._track.id) def __on_button_clicked(self, button): """ Popup menu for track relative to button @param button as Gtk.Button """ self.__context_timeout_id = None image = self.__menu_button.get_image() if self.__context is None: image.set_from_icon_name('go-next-symbolic', Gtk.IconSize.MENU) self.__context = ContextWidget(self._track, button) self.__context.set_property('halign', Gtk.Align.END) self.__context.show() self._duration_label.hide() self._grid.insert_next_to(button, Gtk.PositionType.LEFT) self._grid.attach_next_to(self.__context, button, Gtk.PositionType.LEFT, 1, 1) self.set_indicator(Lp().player.current_track.id == self._track.id, False) else: image.set_from_icon_name('go-previous-symbolic', Gtk.IconSize.MENU) self.__context.destroy() self._duration_label.show() self.__context = None self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) def __popup_menu(self, widget, xcoordinate=None, ycoordinate=None): """ Popup menu for track @param widget as Gtk.Button @param xcoordinate as int (or None) @param ycoordinate as int (or None) """ popover = TrackMenuPopover(self._track, TrackMenu(self._track)) if xcoordinate is not None and ycoordinate is not None: rect = widget.get_allocation() rect.x = xcoordinate rect.y = ycoordinate rect.width = rect.height = 1 popover.set_relative_to(widget) popover.set_pointing_to(rect) popover.connect('closed', self.__on_closed) self.get_style_context().add_class('track-menu-selected') popover.show() def __on_closed(self, widget): """ Remove selected style @param widget as Gtk.Popover """ self.get_style_context().remove_class('track-menu-selected') 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 """ text = '' layout = widget.get_layout() label = widget.get_text() if layout.is_ellipsized(): text = "%s" % (GLib.markup_escape_text(label)) widget.set_tooltip_markup(text)
class Row(Gtk.ListBoxRow): """ A row """ def __init__(self, rowid, num): """ Init row widgets @param rowid as int @param num as int @param show loved as bool """ # We do not use Gtk.Builder for speed reasons Gtk.ListBoxRow.__init__(self) self.set_sensitive(False) self._track = Track(rowid) self._number = num self._indicator = IndicatorWidget(self._track.id) self.set_indicator(Lp().player.current_track.id == self._track.id, utils.is_loved(self._track.id)) self.set_sensitive(True) self._row_widget = Gtk.EventBox() self._row_widget.connect('button-press-event', self._on_button_press) self._row_widget.connect('enter-notify-event', self._on_enter_notify) self._grid = Gtk.Grid() self._grid.set_column_spacing(5) self._row_widget.add(self._grid) self._title_label = Gtk.Label.new(self._track.formated_name()) self._title_label.set_use_markup(True) self._title_label.set_property('has-tooltip', True) self._title_label.connect('query-tooltip', self._on_title_query_tooltip) self._title_label.set_property('hexpand', True) self._title_label.set_property('halign', Gtk.Align.START) self._title_label.set_ellipsize(Pango.EllipsizeMode.END) self._duration_label = Gtk.Label.new( seconds_to_string(self._track.duration)) self._duration_label.get_style_context().add_class('dim-label') self._num_label = Gtk.Label() self._num_label.set_ellipsize(Pango.EllipsizeMode.END) self._num_label.set_property('valign', Gtk.Align.CENTER) self._num_label.set_width_chars(4) self._num_label.get_style_context().add_class('dim-label') self.update_num_label() self._menu_image = Gtk.Image.new() self._menu_image.set_opacity(0.2) self._menu_button = Gtk.Button.new() self._menu_button.set_relief(Gtk.ReliefStyle.NONE) self._menu_button.get_style_context().add_class('menu-button') self._menu_button.get_style_context().add_class('track-menu-button') self._menu_button.set_image(self._menu_image) self._menu_button.connect('clicked', self._on_button_clicked) self._grid.add(self._num_label) self._grid.add(self._title_label) self._grid.add(self._duration_label) # TODO Remove this later if Gtk.get_minor_version() > 16: self._grid.add(self._menu_button) else: self.connect('map', self._on_map) self.add(self._row_widget) self.get_style_context().add_class('trackrow') def set_indicator(self, playing, loved): """ Show indicator @param widget name as str @param playing as bool @param loved as bool """ self._indicator.clear() if playing: self.get_style_context().remove_class('trackrow') self.get_style_context().add_class('trackrowplaying') if loved: self._indicator.play_loved() else: self._indicator.play() else: self.get_style_context().remove_class('trackrowplaying') self.get_style_context().add_class('trackrow') if loved: self._indicator.loved() else: self._indicator.empty() def set_number(self, num): """ Set number @param number as int """ self._number = num def update_num_label(self): """ Update position label for row """ if Lp().player.is_in_queue(self._track.id): self._num_label.get_style_context().add_class('queued') pos = Lp().player.get_track_position(self._track.id) self._num_label.set_text(str(pos)) elif self._number > 0: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text(str(self._number)) else: self._num_label.get_style_context().remove_class('queued') self._num_label.set_text('') def get_id(self): """ Get object id @return Current id as int """ return self._track.id ####################### # PRIVATE # ####################### def _on_map(self, widget): """ Fix for Gtk < 3.18, if we are in a popover, do not show menu button """ widget = self.get_parent() while widget is not None: if isinstance(widget, Gtk.Popover): break widget = widget.get_parent() if widget is None: self._grid.add(self._menu_button) def _on_enter_notify(self, widget, event): """ Set image on buttons now, speed reason @param widget as Gtk.Widget @param event as Gdk.Event """ if self._menu_image.get_pixbuf() is None: self._menu_image.set_from_icon_name('open-menu-symbolic', Gtk.IconSize.MENU) self._indicator.update_button() def _on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if event.button == 3: window = widget.get_window() if window == event.window: self._popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self._popup_menu(self._menu_button) return True elif event.button == 2: if self._track.id in Lp().player.get_queue(): Lp().player.del_from_queue(self._track.id) else: Lp().player.append_to_queue(self._track.id) def _on_button_clicked(self, widget): """ Popup menu for track relative to button @param widget as Gtk.Button """ self._popup_menu(widget) def _popup_menu(self, widget, xcoordinate=None, ycoordinate=None): """ Popup menu for track @param widget as Gtk.Button @param xcoordinate as int (or None) @param ycoordinate as int (or None) """ popover = TrackMenuPopover(self._track.id, TrackMenu(self._track.id)) popover.set_relative_to(widget) if xcoordinate is not None and ycoordinate is not None: rect = widget.get_allocation() rect.x = xcoordinate rect.y = ycoordinate rect.width = rect.height = 1 popover.set_pointing_to(rect) popover.connect('closed', self._on_closed) self.get_style_context().add_class('track-menu-selected') popover.show() def _on_closed(self, widget): """ Remove selected style @param widget as Gtk.Popover """ self.get_style_context().remove_class('track-menu-selected') def _on_title_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 = self._title_label.get_layout() if layout.is_ellipsized(): label = self._title_label.get_label() self._title_label.set_tooltip_markup(label) else: self._title_label.set_tooltip_text('')