pages_manager_child.py 11.9 KB
Newer Older
Cédric Bellegarde's avatar
Cédric Bellegarde committed
1
# Copyright (c) 2017-2019 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
2 3 4 5 6 7 8 9 10 11 12
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

13
from gi.repository import Gtk, GLib, WebKit2, Pango
14

15 16
from gettext import gettext as _

Cédric Bellegarde's avatar
Cédric Bellegarde committed
17
from eolie.label_indicator import LabelIndicator
18
from eolie.define import App, ArtSize
19
from eolie.utils import resize_favicon
20 21


22
class PagesManagerChild(Gtk.FlowBoxChild):
23
    """
24
        Child showing snapshot, title and favicon
25 26
    """

Cédric Bellegarde's avatar
Cédric Bellegarde committed
27
    def __init__(self, view, window):
28 29
        """
            Init child
30 31
            @param view as View
            @param window as Window
32
        """
33 34 35
        Gtk.FlowBoxChild.__init__(self)
        self.__view = view
        self.__window = window
36
        self.__connected_ids = []
37
        self.__scroll_timeout_id = None
38
        builder = Gtk.Builder()
39
        builder.add_from_resource("/org/gnome/Eolie/PagesManagerChild.ui")
40
        builder.connect_signals(self)
41
        self.__indicator_label = LabelIndicator(False)
42
        self.__indicator_label.mark_unshown(view.webview)
43 44 45 46 47 48
        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()
49 50
        if view.webview.title:
            self.__indicator_label.set_text(view.webview.title)
51
        builder.get_object("grid").attach(self.__indicator_label, 1, 0, 1, 1)
52
        self.__image = builder.get_object("image")
53 54
        self.__pin_button = builder.get_object("pin_button")
        self.__pin_image = builder.get_object("pin_image")
55
        self.__close_button = builder.get_object("close_button")
56 57
        self.__close_button_image = self.__close_button.get_image()
        self.__close_button_image.set_from_icon_name(
Cédric Bellegarde's avatar
Cédric Bellegarde committed
58 59
            "window-close-symbolic",
            Gtk.IconSize.INVALID)
60 61
        self.__close_button_image.set_property("pixel-size",
                                               ArtSize.FAVICON)
62
        self.__spinner = builder.get_object("spinner")
Cédric Bellegarde's avatar
Cédric Bellegarde committed
63
        self.add(builder.get_object("widget"))
64 65 66 67

        self.get_style_context().add_class("sidebar-item")

        self.set_property("has-tooltip", True)
68 69 70 71 72
        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)
73 74 75 76
        self.set_size_request(ArtSize.START_WIDTH +
                              ArtSize.PREVIEW_WIDTH_MARGIN,
                              ArtSize.START_HEIGHT +
                              ArtSize.PREVIEW_WIDTH_MARGIN)
77
        self.connect("query-tooltip", self.__on_query_tooltip)
78
        view.connect("destroying", self.__on_view_destroying)
79 80
        self.__view.webview.connect("snapshot-changed",
                                    self.__on_webview_snapshot_changed)
81
        self.__view.webview.connect("notify::favicon",
82 83 84 85 86 87 88 89 90
                                    self.__on_webview_favicon_changed)
        self.__view.webview.connect("notify::is-playing-audio",
                                    self.__on_webview_notify_is_playing_audio)
        self.__view.webview.connect("title-changed",
                                    self.__on_webview_title_changed)
        self.__view.webview.connect("load-changed",
                                    self.__on_webview_load_changed)
        self.__view.webview.connect("shown",
                                    self.__on_webview_shown)
91
        self.__on_webview_favicon_changed(self.__view.webview)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
92

93 94 95 96 97 98
    @property
    def view(self):
        """
            Get linked view
            @return View
        """
99
        return self.__view
100 101 102 103

#######################
# PROTECTED           #
#######################
Cédric Bellegarde's avatar
Cédric Bellegarde committed
104
    def _on_button_press_event(self, eventbox, event):
105 106 107 108 109 110
        """
            Hide popover or close view
            @param eventbox as Gtk.EventBox
            @param event as Gdk.Event
        """
        if event.button == 2:
111
            self.__window.container.try_close_view(self.__view)
Cédric Bellegarde's avatar
Cédric Bellegarde committed
112
            return True
113 114
        elif event.button == 3:
            from eolie.menu_move_to import MoveToMenu
115 116 117 118 119 120
            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)
121 122 123
            popover.forall(self.__update_popover_internals)
            popover.show()
            return True
124

Cédric Bellegarde's avatar
Cédric Bellegarde committed
125 126 127 128 129 130 131
    def _on_button_release_event(self, eventbox, event):
        """
            @param eventbox as Gtk.EventBox
            @param event as Gdk.Event
        """
        pass

132 133 134 135 136 137 138 139 140 141 142 143 144
    def _on_pin_button_clicked(self, button):
        """
            Pin/Unpin page
            @param button as Gtk.Button
        """
        if self.view.webview.uri in App().pinned:
            self.__pin_image.set_opacity(0.5)
            App().remove_from_pinned(self.view.webview.uri)
        else:
            self.__pin_image.set_opacity(1)
            App().add_to_pinned(self.view.webview.uri)
        return True

145
    def _on_close_button_clicked(self, button):
146 147
        """
            Destroy self
148
            @param button as Gtk.Button
149
        """
150
        self.__window.container.try_close_view(self.__view)
151
        return True
152 153 154 155 156 157 158

    def _on_enter_notify_event(self, eventbox, event):
        """
            Show close button
            @param eventbox as Gtk.EventBox
            @param event as Gdk.Event
        """
159 160
        if self.view.webview.uri in App().pinned:
            self.__pin_image.set_opacity(1)
161
            self.__pin_button.set_tooltip_text(_("Unpin this page"))
162 163
        else:
            self.__pin_image.set_opacity(0.5)
164
            self.__pin_button.set_tooltip_text(_("Pin this page"))
165
        self.__pin_button.set_opacity(1)
166
        self.__close_button_image.set_from_icon_name(
Cédric Bellegarde's avatar
Cédric Bellegarde committed
167 168
            "window-close-symbolic",
            Gtk.IconSize.INVALID)
169 170 171 172 173 174 175 176 177 178 179 180

    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:
181
            self.__pin_button.set_opacity(0)
182
            self.__on_webview_favicon_changed(self.__view.webview)
183 184 185 186

#######################
# PRIVATE             #
#######################
187 188 189 190 191 192 193 194 195 196 197 198
    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)

199 200 201 202 203 204 205 206 207 208
    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 = ""
209
        label = self.__indicator_label.get_text()
210
        uri = self.__view.webview.uri
211 212 213 214 215 216 217
        # 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)
218

219
    def __on_view_destroying(self, view):
220
        """
221
            Destroy self
222
            @param view as View
223
        """
224 225 226 227 228 229 230 231 232 233 234 235
        self.__view.webview.disconnect_by_func(
                                    self.__on_webview_snapshot_changed)
        self.__view.webview.disconnect_by_func(
                                    self.__on_webview_favicon_changed)
        self.__view.webview.disconnect_by_func(
                                    self.__on_webview_notify_is_playing_audio)
        self.__view.webview.disconnect_by_func(
                                    self.__on_webview_title_changed)
        self.__view.webview.disconnect_by_func(
                                    self.__on_webview_load_changed)
        self.__view.webview.disconnect_by_func(
                                    self.__on_webview_shown)
236
        self.destroy()
237

238
    def __on_webview_notify_is_playing_audio(self, webview, playing):
239
        """
240
            Update favicon
241
            @param webview as WebView
242 243 244
            @param playing as bool
        """
        self.__on_webview_favicon_changed(webview)
245

246
    def __on_webview_favicon_changed(self, webview, *ignore):
247
        """
248
            Set favicon
249 250
            @param webview as WebView
        """
251 252
        if webview.current_event == WebKit2.LoadEvent.STARTED:
            return
253 254 255 256 257 258 259 260 261
        image = self.__close_button.get_image()
        if webview.is_playing_audio():
            image.set_from_icon_name("audio-speakers-symbolic",
                                     Gtk.IconSize.INVALID)
            return
        artwork = App().art.get_icon_theme_artwork(webview.uri,
                                                   webview.ephemeral)
        if artwork is not None:
            image.set_from_icon_name(artwork, Gtk.IconSize.INVALID)
262 263 264 265 266 267 268
        else:
            surface = webview.get_favicon()
            if surface is not None:
                image.set_from_surface(resize_favicon(surface))
            else:
                image.set_from_icon_name("applications-internet",
                                         Gtk.IconSize.INVALID)
269

270
    def __on_webview_title_changed(self, webview, title):
271 272 273 274 275
        """
            Update title
            @param webview as WebView
            @param title as str
        """
276
        self.__indicator_label.set_text(title)
277

278
    def __on_webview_load_changed(self, webview, event):
279 280 281 282 283
        """
            Update widget content
            @param webview as WebView
            @param event as WebKit2.LoadEvent
        """
284
        uri = webview.uri
285
        if event == WebKit2.LoadEvent.STARTED:
286 287 288
            image = self.__close_button.get_image()
            image.set_from_icon_name("content-loading-symbolic",
                                     Gtk.IconSize.INVALID)
289
            self.__spinner.start()
290
            self.__indicator_label.set_text(uri)
291
        elif event == WebKit2.LoadEvent.COMMITTED:
292
            self.__indicator_label.set_text(uri)
293
        elif event == WebKit2.LoadEvent.FINISHED:
294
            self.__on_webview_favicon_changed(webview)
295
            self.__spinner.stop()
296 297 298 299 300 301 302 303 304 305 306 307 308

    def __on_webview_snapshot_changed(self, webview, surface):
        """
            Update preview with surface
            @param webview as WebView
            @param surface as cairo.surface
        """
        if self.__view.webview.ephemeral:
            self.__image.set_from_icon_name(
                "user-not-tracked-symbolic",
                Gtk.IconSize.DIALOG)
        else:
            self.__image.set_from_surface(surface)
309 310 311 312 313

    def __on_webview_shown(self, webview):
        """
            Remove indicator
        """
314
        self.__indicator_label.mark_shown(webview)