class SitesManagerChild(Gtk.ListBoxRow): """ Child showing snapshot, title and favicon """ __gsignals__ = { 'moved': (GObject.SignalFlags.RUN_FIRST, None, (str, bool)) } def __init__(self, netloc, window, ephemeral): """ Init child @param netloc as str @param window as Window @param ephemeral as bool """ Gtk.ListBoxRow.__init__(self) self.__window = window self.__netloc = netloc self.__minimal = None self.__ephemeral = ephemeral self.__views = [] self.__connected_ids = [] self.__scroll_timeout_id = None self.__pages_listbox = None self.set_margin_top(1) self.set_property("has-tooltip", True) self.connect("query-tooltip", self.__on_query_tooltip) builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Eolie/SitesManagerChild.ui") builder.connect_signals(self) widget = builder.get_object("widget") self.__indicator_label = LabelIndicator() self.__indicator_label.set_property("halign", Gtk.Align.CENTER) self.__indicator_label.show() self.__separator = builder.get_object("separator") self.__grid = builder.get_object("grid") self.__netloc_label = builder.get_object("netloc") self.__netloc_label.set_text(self.__netloc) self.__image = builder.get_object("image") self.__image.set_property("pixel-size", ArtSize.FAVICON) self.__set_artwork() self.add(widget) self.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.MOVE) self.drag_source_add_text_targets() self.connect("drag-begin", self.__on_drag_begin) self.connect("drag-data-get", self.__on_drag_data_get) self.drag_dest_set(Gtk.DestDefaults.DROP | Gtk.DestDefaults.MOTION, [], Gdk.DragAction.MOVE) self.drag_dest_add_text_targets() self.connect("drag-data-received", self.__on_drag_data_received) self.connect("drag-motion", self.__on_drag_motion) self.connect("drag-leave", self.__on_drag_leave) def add_view(self, view): """ Add view @param view as View @param uri as str """ if view not in self.__views: self.__views.append(view) view.webview.connect("shown", self.__on_webview_shown) view.webview.connect("favicon-changed", self.__on_webview_favicon_changed) self.update_label() self.__indicator_label.update_count(True) if not view.webview.shown: self.__indicator_label.mark_unshown(view.webview) if self.__pages_listbox is not None: child = PageChildRow(view, self.__window) child.show() self.__pages_listbox.add(child) def remove_view(self, view): """ Remove view and destroy self if no more view @param view as View """ if view in self.__views: self.__views.remove(view) view.webview.disconnect_by_func(self.__on_webview_shown) view.webview.disconnect_by_func(self.__on_webview_favicon_changed) self.update_label() self.__indicator_label.update_count(False) if not view.webview.shown: self.__indicator_label.mark_shown(view.webview) if self.__pages_listbox is not None: for child in self.__pages_listbox.get_children(): if child.view == view: self.__pages_listbox.remove(child) break def set_minimal(self, minimal): """ Make widget minimal @param minimal as bool """ if self.__minimal == minimal: return if minimal: self.__grid.remove(self.__netloc_label) if self.__pages_listbox is not None: self.__grid.remove(self.__pages_listbox) self.__pages_listbox.destroy() self.__pages_listbox = None self.__grid.attach(self.__indicator_label, 1, 0, 1, 1) self.__image.set_property("halign", Gtk.Align.CENTER) self.__image.set_hexpand(True) self.__separator.hide() else: self.__grid.remove(self.__indicator_label) self.__grid.attach(self.__netloc_label, 1, 0, 1, 1) self.__pages_listbox = Gtk.ListBox.new() self.__pages_listbox.show() self.__separator.show() self.__grid.attach(self.__pages_listbox, 0, 2, 2, 1) self.__image.set_hexpand(False) self.__image.set_property("halign", Gtk.Align.START) # Setup listbox current = self.__window.container.current for view in self.__views: child = PageChildRow(view, self.__window) child.show() self.__pages_listbox.add(child) if view == current: self.__pages_listbox.select_row(child) self.__minimal = minimal def reset(self, netloc): """ Reset widget to new netloc @param netloc as str """ if netloc != self.__netloc: self.__netloc = netloc self.__set_artwork() self.__netloc_label.set_text(self.__netloc) def update_label(self): """ Update label: if one view, use title else use netloc @param view as View """ self.__netloc_label.set_text(self.__netloc) def set_selected(self, selected): """ Mark self as selected @param selected as bool """ if selected: self.get_style_context().add_class("item-selected") else: self.get_style_context().remove_class("item-selected") if self.__pages_listbox is not None: if selected: current = self.__window.container.current for child in self.__pages_listbox.get_children(): if child.view == current: self.__pages_listbox.select_row(child) else: self.__pages_listbox.unselect_all() @property def empty(self): """ True if no view associated @return bool """ return len(self.__views) == 0 @property def views(self): """ Get views @return [view] """ return self.__views @property def ephemeral(self): """ Get ephemeral @return bool """ return self.__ephemeral @property def netloc(self): """ Get netloc @return str """ return self.__netloc ####################### # PROTECTED # ####################### def _on_scroll_event(self, eventbox, event): """ Switch between children @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if event.direction == Gdk.ScrollDirection.UP: self.__window.container.pages_manager.previous() elif event.direction == Gdk.ScrollDirection.DOWN: self.__window.container.pages_manager.next() self.__window.container.ctrl_released() def _on_button_press_event(self, eventbox, event): """ Hide popover or close view @param eventbox as Gtk.EventBox @param event as Gdk.Event """ try: if event.button == 2: for view in self.__views: self.__window.container.try_close_view(view) return True elif event.button == 3: from eolie.menu_sites import SitesMenu from eolie.menu_profiles import ProfilesMenu from eolie.menu_move_to import MoveToMenu sites_menu = SitesMenu(self.__views, self.__window) sites_menu.show() webview = self.__views[0].webview profiles_menu = ProfilesMenu(webview, self.__window) profiles_menu.show() moveto_menu = MoveToMenu(self.__views, self.__window) moveto_menu.show() popover = Gtk.PopoverMenu.new() popover.add(sites_menu) popover.add(profiles_menu) popover.add(moveto_menu) popover.child_set_property(profiles_menu, "submenu", "profiles") popover.child_set_property(moveto_menu, "submenu", "moveto") popover.set_relative_to(eventbox) popover.set_position(Gtk.PositionType.RIGHT) popover.forall(self.__update_popover_internals) popover.show() return True except Exception as e: print("SitesManagerChild::_on_button_press_event:", e) ####################### # PRIVATE # ####################### def __update_popover_internals(self, widget): """ Little hack to manage Gtk.ModelButton text @param widget as Gtk.Widget """ if isinstance(widget, Gtk.Label): widget.set_ellipsize(Pango.EllipsizeMode.END) widget.set_max_width_chars(30) widget.set_tooltip_text(widget.get_text()) elif hasattr(widget, "forall"): GLib.idle_add(widget.forall, self.__update_popover_internals) def __set_artwork(self): """ Set initial artwork on widget """ artwork = El().art.get_icon_theme_artwork(remove_www(self.__netloc), self.__ephemeral) if artwork is not None: self.__image.set_from_icon_name(artwork, Gtk.IconSize.INVALID) else: favicon_path = El().art.get_favicon_path(remove_www(self.__netloc)) if favicon_path is not None: self.__image.set_from_file(favicon_path) else: self.__image.set_from_icon_name("applications-internet", Gtk.IconSize.INVALID) def __on_webview_favicon_changed(self, webview, favicon, icon_theme_artwork): """ Set favicon @param webview as WebView @param favicon as cairo.Surface @param icon_theme_artwork as str """ if favicon is not None: self.__image.set_from_surface(favicon) elif icon_theme_artwork is not None: self.__image.set_from_icon_name(icon_theme_artwork, Gtk.IconSize.INVALID) 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 """ if self.__pages_listbox is not None: return tooltip = "<b>%s</b>" % GLib.markup_escape_text(self.__netloc) for view in self.__views: title = view.webview.get_title() if title is not None: tooltip += "\n%s" % GLib.markup_escape_text(title) widget.set_tooltip_markup(tooltip) def __on_drag_begin(self, widget, context): """ Set icon @param widget as Gtk.Widget @param context as Gdk.DragContext """ surface = self.__image.get_property("surface") if surface is None: return pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height()) widget.drag_source_set_icon_pixbuf(pixbuf) del pixbuf def __on_drag_data_get(self, widget, context, data, info, time): """ Send netloc @param widget as Gtk.Widget @param context as Gdk.DragContext @param data as Gtk.SelectionData @param info as int @param time as int """ data.set_text(self.__netloc, len(self.__netloc)) def __on_drag_data_received(self, widget, context, x, y, data, info, time): """ Move track @param widget as Gtk.Widget @param context as Gdk.DragContext @param x as int @param y as int @param data as Gtk.SelectionData @param info as int @param time as int """ height = self.get_allocated_height() if y > height / 2: up = False else: up = True try: netloc = data.get_text() self.emit("moved", netloc, up) except: pass def __on_drag_motion(self, widget, context, x, y, time): """ Add style @param widget as Gtk.Widget @param context as Gdk.DragContext @param x as int @param y as int @param time as int """ height = self.get_allocated_height() if y > height / 2: self.get_style_context().add_class("drag-up") self.get_style_context().remove_class("drag-down") else: self.get_style_context().remove_class("drag-up") self.get_style_context().add_class("drag-down") def __on_drag_leave(self, widget, context, time): """ Remove style @param widget as Gtk.Widget @param context as Gdk.DragContext @param time as int """ self.get_style_context().remove_class("drag-up") self.get_style_context().remove_class("drag-down") def __on_webview_shown(self, webview): """ Update indicataor """ self.__indicator_label.mark_shown(webview)
class PagesManagerChild(Gtk.FlowBoxChild): """ Child showing snapshot, title and favicon """ def __init__(self, view, window): """ Init child @param view as View @param window as Window """ Gtk.FlowBoxChild.__init__(self) self.__view = view self.__window = window self.__favicon = None self.__connected_ids = [] self.__scroll_timeout_id = None builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Eolie/PagesManagerChild.ui") builder.connect_signals(self) self.__indicator_label = LabelIndicator() self.__indicator_label.mark_unshown(view.webview) self.__indicator_label.set_hexpand(True) self.__indicator_label.set_margin_right(4) self.__indicator_label.set_property("halign", Gtk.Align.CENTER) self.__indicator_label.set_property("valign", Gtk.Align.CENTER) self.__indicator_label.set_ellipsize(Pango.EllipsizeMode.END) self.__indicator_label.show() if view.webview.title: self.__indicator_label.set_text(view.webview.title) builder.get_object("grid").attach(self.__indicator_label, 0, 0, 1, 1) self.__image = builder.get_object("image") self.__close_button = builder.get_object("close_button") self.__audio_indicator = builder.get_object("audio_indicator") if view.webview.is_playing_audio(): self.__audio_indicator.show() self.__close_button.get_image().set_from_icon_name( "window-close-symbolic", Gtk.IconSize.INVALID) self.__close_button.get_image().set_property("pixel-size", ArtSize.FAVICON) self.__spinner = builder.get_object("spinner") self.add(builder.get_object("widget")) self.get_style_context().add_class("sidebar-item") self.set_property("has-tooltip", True) self.set_property("halign", Gtk.Align.START) self.set_margin_start(20) self.set_margin_end(20) self.set_margin_top(20) self.set_margin_bottom(20) self.set_size_request(ArtSize.START_WIDTH + ArtSize.PREVIEW_WIDTH_MARGIN, ArtSize.START_HEIGHT + ArtSize.PREVIEW_WIDTH_MARGIN) self.connect("query-tooltip", self.__on_query_tooltip) view.connect("destroying", self.__on_view_destroying) self.__connected_signals = [] self.__connected_signals.append( self.__view.webview.connect("favicon-changed", self.__on_webview_favicon_changed)) self.__connected_signals.append( self.__view.webview.connect( "notify::is-playing-audio", self.__on_webview_notify_is_playing_audio)) self.__connected_signals.append( self.__view.webview.connect("uri-changed", self.__on_webview_uri_changed)) self.__connected_signals.append( self.__view.webview.connect("title-changed", self.__on_webview_title_changed)) self.__connected_signals.append( self.__view.webview.connect("scroll-event", self.__on_webview_scroll_event)) self.__connected_signals.append( self.__view.webview.connect("load-changed", self.__on_webview_load_changed)) self.__connected_signals.append( self.__view.webview.connect("shown", self.__on_webview_shown)) self.__set_favicon_artwork() if self.__view.webview.uri is not None: artwork_path = El().art.get_path(self.__view.webview.uri, "start") if artwork_path is not None and\ GLib.file_test(artwork_path, GLib.FileTest.IS_REGULAR): self.__image.set_from_file(artwork_path) def destroy(self): """ Disconnect signals and destroy self """ for signal_id in self.__connected_signals: self.__view.webview.disconnect(signal_id) Gtk.FlowBoxChild.destroy(self) @property def view(self): """ Get linked view @return View """ return self.__view ####################### # PROTECTED # ####################### def _on_button_press_event(self, eventbox, event): """ Hide popover or close view @param eventbox as Gtk.EventBox @param event as Gdk.Event """ if event.button == 2: self.__window.container.try_close_view(self.__view) return True elif event.button == 3: from eolie.menu_move_to import MoveToMenu moveto_menu = MoveToMenu([self.__view], self.__window, False) moveto_menu.show() popover = Gtk.PopoverMenu.new() popover.set_relative_to(eventbox) popover.set_position(Gtk.PositionType.BOTTOM) popover.add(moveto_menu) popover.forall(self.__update_popover_internals) popover.show() return True def _on_button_release_event(self, eventbox, event): """ @param eventbox as Gtk.EventBox @param event as Gdk.Event """ pass def _on_close_button_clicked(self, button): """ Destroy self @param button as Gtk.Button """ self.__window.container.try_close_view(self.__view) return True def _on_enter_notify_event(self, eventbox, event): """ Show close button @param eventbox as Gtk.EventBox @param event as Gdk.Event """ self.__close_button.get_image().set_from_icon_name( "window-close-symbolic", Gtk.IconSize.INVALID) def _on_leave_notify_event(self, eventbox, event): """ Show close button @param eventbox as Gtk.EventBox @param event as Gdk.Event """ allocation = eventbox.get_allocation() if event.x <= 0 or\ event.x >= allocation.width or\ event.y <= 0 or\ event.y >= allocation.height: self.__set_favicon_artwork() ####################### # PRIVATE # ####################### def __update_popover_internals(self, widget): """ Little hack to manage Gtk.ModelButton text @param widget as Gtk.Widget """ if isinstance(widget, Gtk.Label): widget.set_ellipsize(Pango.EllipsizeMode.END) widget.set_max_width_chars(40) widget.set_tooltip_text(widget.get_text()) elif hasattr(widget, "forall"): GLib.idle_add(widget.forall, self.__update_popover_internals) def __set_favicon_artwork(self): """ Set favicon artwork """ if self.__view.webview.ephemeral: return image = self.__close_button.get_image() if self.__favicon is not None: image.set_from_surface(self.__favicon) else: uri = self.__view.webview.uri artwork = El().art.get_icon_theme_artwork( uri, self.view.webview.ephemeral) if artwork is not None: image.set_from_icon_name(artwork, Gtk.IconSize.INVALID) else: favicon_path = El().art.get_favicon_path(uri) if favicon_path is not None: image.set_from_file(favicon_path) else: image.set_from_icon_name("applications-internet", Gtk.IconSize.INVALID) def __set_snapshot(self): """ Set webpage preview """ if self.__view.webview.ephemeral: self.__image.set_from_icon_name( "user-not-tracked-symbolic", Gtk.IconSize.DIALOG) else: self.__view.webview.get_snapshot( WebKit2.SnapshotRegion.VISIBLE, WebKit2.SnapshotOptions.NONE, None, get_snapshot, self.__on_snapshot) def __on_scroll_timeout(self): """ Update snapshot """ self.__scroll_timeout_id = None self.__set_snapshot() 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 = "" label = self.__indicator_label.get_text() uri = self.__view.webview.uri # GLib.markup_escape_text if uri is None: text = "<b>%s</b>" % GLib.markup_escape_text(label) else: text = "<b>%s</b>\n%s" % (GLib.markup_escape_text(label), GLib.markup_escape_text(uri)) widget.set_tooltip_markup(text) def __on_view_destroying(self, view): """ Destroy self @param view as View """ self.destroy() def __on_webview_favicon_changed(self, webview, favicon, icon_theme_artwork): """ Set favicon @param webview as WebView @param favicon as cairo.Surface @param icon_theme_artwork as str """ if favicon is not None: self.__favicon = favicon self.__close_button.get_image().set_from_surface(favicon) elif icon_theme_artwork is not None: self.__close_button.get_image().set_from_icon_name( icon_theme_artwork, Gtk.IconSize.INVALID) def __on_webview_notify_is_playing_audio(self, webview, playing): """ Update status @param webview as WebView @param playing as bool """ if not webview.is_loading() and webview.is_playing_audio(): self.__audio_indicator.show() else: self.__audio_indicator.hide() def __on_webview_scroll_event(self, webview, event): """ Update snapshot @param webview as WebView @param event as Gdk.EventScroll """ if self.__scroll_timeout_id is not None: GLib.source_remove(self.__scroll_timeout_id) self.__scroll_timeout_id = GLib.timeout_add(250, self.__on_scroll_timeout) def __on_snapshot(self, surface): """ Set snapshot @param surface as cairo.Surface """ self.__image.set_from_surface(surface) def __on_webview_uri_changed(self, webview, uri): """ Update uri @param webview as WebView @param uri as str """ # Js change, update snapshot if not webview.is_loading() and not webview.ephemeral: GLib.timeout_add(500, self.__set_snapshot) def __on_webview_title_changed(self, webview, title): """ Update title @param webview as WebView @param title as str """ self.__indicator_label.set_text(title) def __on_webview_load_changed(self, webview, event): """ Update widget content @param webview as WebView @param event as WebKit2.LoadEvent """ uri = webview.uri if event == WebKit2.LoadEvent.STARTED: self.__favicon = None self.__close_button.get_image().set_from_icon_name( "applications-internet", Gtk.IconSize.INVALID) self.__image.clear() self.__audio_indicator.hide() self.__spinner.start() self.__indicator_label.set_text(uri) elif event == WebKit2.LoadEvent.COMMITTED: self.__indicator_label.set_text(uri) elif event == WebKit2.LoadEvent.FINISHED: self.__spinner.stop() if webview.is_playing_audio(): self.__audio_indicator.show() GLib.timeout_add(500, self.__set_snapshot) def __on_webview_shown(self, webview): """ Remove indicator """ self.__indicator_label.mark_shown(webview)