Skip to content

Commit

Permalink
update to webkit2
Browse files Browse the repository at this point in the history
support for offline docs
optimize code
  • Loading branch information
jonian committed Apr 22, 2017
1 parent 55f685c commit 43e4601
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 91 deletions.
10 changes: 2 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,9 @@ DevDocs desktop application created with GTK3 and Python.
To launch the application from the terminal use `devdocs-desktop [STRING]`.
If a string is provided, the application will open the first available result page.

## Requirements (for development)
## Requirements

* python (>= 3)
* python-gobject
* webkitgtk (engine for GTK+ 3)

## To Do

Add support for offline documentation.
python, python-gobject, webkitgtk

## Installation

Expand Down
162 changes: 80 additions & 82 deletions devdocs_desktop.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#! /usr/bin/python3
#! /usr/bin/python

import os
import gi
Expand All @@ -9,9 +9,9 @@
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version('GLib', '2.0')
gi.require_version('WebKit', '3.0')
gi.require_version('WebKit2', '4.0')

from gi.repository import Gtk, Gdk, GLib, WebKit, Soup
from gi.repository import Gtk, Gdk, GLib, WebKit2


class DevdocsDesktop:
Expand All @@ -23,31 +23,34 @@ def __init__(self):
self.args = argparse.ArgumentParser(prog='devdocs-desktop')
self.args.add_argument('s', metavar='STR', help='the string to search', nargs='?', default='')

self.app_url = 'https://devdocs.io'
self.do_link = False
self.search = self.args.parse_args().s
self.session = WebKit.get_default_session()
self.app_url = 'https://devdocs.io'
self.search = self.args.parse_args().s
self.open_link = False

self.main = Gtk.Builder()
self.main.add_from_file(self.file_path('ui/main.ui'))
self.main.connect_signals(self)

self.webview = WebKit.WebView()
self.cookies = WebKit2.WebContext.get_default().get_cookie_manager()
self.manager = WebKit2.UserContentManager()
self.webview = WebKit2.WebView.new_with_user_content_manager(self.manager)
self.webview.load_uri(self.url_with_search())

self.webview.connect('navigation-requested', self.on_webview_nav_requested)
self.webview.connect('load-committed', self.on_webview_load_commited)
self.webview.connect('load-finished', self.on_webview_load_finished)
self.webview.connect('title-changed', self.on_webview_title_changed)
self.history = self.webview.get_back_forward_list()
self.history.connect('changed', self.on_history_changed)

self.webview.connect('notify::uri', self.on_webview_uri_changed)
self.webview.connect('notify::title', self.on_webview_title_changed)
self.webview.connect('decide-policy', self.on_webview_decide_policy)
self.webview.connect('context-menu', self.on_webview_context_menu)

self.scrolled = self.main.get_object('scrolled_main')
self.scrolled.add(self.webview)

self.header_back = self.main.get_object('header_button_back')
self.header_back = self.main.get_object('header_button_back')
self.header_forward = self.main.get_object('header_button_forward')
self.header_title = self.main.get_object('header_label_title')
self.header_save = self.main.get_object('header_button_save')
self.header_title = self.main.get_object('header_label_title')
self.header_save = self.main.get_object('header_button_save')

self.header_search = self.main.get_object('header_search_entry')
self.header_search.get_style_context().remove_class('search')
Expand All @@ -57,7 +60,7 @@ def __init__(self):
self.window.show_all()

self.create_settings_path()
self.set_webview_settings()
self.inject_custom_styles()
self.enable_persistent_cookies()

def run(self):
Expand All @@ -67,48 +70,36 @@ def quit(self):
Gtk.main_quit()

def url_with_search(self):
url = self.app_url

if self.search != '':
url = url + '#q=' + self.search

url = "%s#q=%s" % (self.app_url, self.search)
return url

def create_settings_path(self):
directory = self.settings_path()

if not os.path.exists(directory):
os.makedirs(directory)
if not os.path.exists(self.settings_path()):
os.makedirs(self.settings_path())

def settings_path(self, filepath=''):
root = os.path.expanduser('~') + '/.devdocs-desktop'
root = "%s/.devdocs-desktop" % os.path.expanduser('~')
return os.path.join(root, filepath)

def file_path(self, filepath):
root = os.path.dirname(os.path.realpath(__file__))
return os.path.join(root, filepath)

def set_webview_settings(self):
userstyle = 'file://' + self.file_path('styles/user.css')
settings = self.webview.get_settings()
def inject_custom_styles(self):
style = open(self.file_path('styles/user.css'), 'r').read()
frame = WebKit2.UserContentInjectedFrames.ALL_FRAMES
level = WebKit2.UserStyleLevel.USER
style = WebKit2.UserStyleSheet(style, frame, level, None, None)

settings.set_property('enable-webaudio', True)
settings.set_property('enable-media-stream', True)
settings.set_property('user-stylesheet-uri', userstyle)
settings.set_property('javascript-can-access-clipboard', True)
self.manager.add_style_sheet(style)

def enable_persistent_cookies(self):
cookiefile = self.settings_path('cookies.txt')
cookiejar = Soup.CookieJarText.new(cookiefile, False)
cookiejar.set_accept_policy(Soup.CookieJarAcceptPolicy.ALWAYS)
self.session.add_feature(cookiejar)

def update_history_buttons(self):
back = self.webview.can_go_back()
self.header_back.set_sensitive(back)
filepath = self.settings_path('cookies.txt')
storage = WebKit2.CookiePersistentStorage.TEXT
policy = WebKit2.CookieAcceptPolicy.ALWAYS

forward = self.webview.can_go_forward()
self.header_forward.set_sensitive(forward)
self.cookies.set_accept_policy(policy)
self.cookies.set_persistent_storage(filepath, storage)

def toggle_save_button(self, visible):
self.header_save.set_visible(visible)
Expand All @@ -118,8 +109,8 @@ def on_window_main_destroy(self, _event):
self.quit()

def on_window_main_key_release_event(self, _widget, event):
kname = Gdk.keyval_name(event.keyval)
text = self.header_search.get_text()
kname = Gdk.keyval_name(event.keyval)
text = self.header_search.get_text()
visible = self.header_search.get_visible()

if kname == 'Escape' and visible:
Expand Down Expand Up @@ -160,55 +151,60 @@ def on_menu_main_link_clicked(self, widget):
link = '' if link == 'home' else link

self.header_search.set_text('')
self.js_click_element('a[href="/' + link + '"]')
self.js_open_link(link)

def on_header_button_save_clicked(self, _widget):
self.toggle_save_button(False)
self.js_click_element('._sidebar-footer ._settings-btn')
self.header_title.set_label('Downloading...')

def on_webview_nav_requested(self, _widget, _frame, request):
uri = request.get_uri()
def on_webview_decide_policy(self, _widget, decision, dtype):
types = WebKit2.PolicyDecisionType

if self.do_link:
if self.app_url in uri:
link = uri.split(self.app_url)[-1]
self.js_click_element('a[href="' + link + '"]')
else:
webbrowser.open(uri)
if self.open_link and dtype == types.NAVIGATION_ACTION:
self.open_link = False
uri = decision.get_request().get_uri()

return True
if not self.app_url in uri:
decision.ignore()
webbrowser.open(uri)

self.do_link = False
return False
def on_webview_title_changed(self, _widget, _title):
title = self.webview.get_title()
self.header_title.set_label(title)

def on_webview_load_commited(self, _widget, frame):
self.do_link = False
self.update_history_buttons()
self.toggle_save_button(frame.get_uri().endswith('settings'))
def on_webview_uri_changed(self, _widget, _uri):
save = self.webview.get_uri().endswith('settings')
self.toggle_save_button(save)

def on_webview_load_finished(self, _widget, frame):
self.update_history_buttons()
self.toggle_save_button(frame.get_uri().endswith('settings'))
def on_history_changed(self, _list, _added, _removed):
back = self.webview.can_go_back()
self.header_back.set_sensitive(back)

def on_webview_title_changed(self, _widget, _frame, title):
self.header_title.set_label(title)
forward = self.webview.can_go_forward()
self.header_forward.set_sensitive(forward)

def on_webview_open_link(self, _widget):
self.do_link = True
def on_webview_open_link(self, action):
self.open_link = True

def on_webview_context_menu(self, _widget, menu, _coords, _keyboard):
for item in menu.get_children():
label = item.get_label()
lnk_open = '_Open' in label
new_open = '_Window' in label
download = '_Download' in label
actions = WebKit2.ContextMenuAction
include = [
actions.GO_BACK, actions.GO_FORWARD, actions.STOP, actions.RELOAD,
actions.COPY, actions.CUT, actions.PASTE, actions.DELETE, actions.SELECT_ALL,
actions.OPEN_LINK, actions.COPY_LINK_TO_CLIPBOARD,
actions.COPY_IMAGE_TO_CLIPBOARD, actions.COPY_IMAGE_URL_TO_CLIPBOARD,
actions.COPY_VIDEO_LINK_TO_CLIPBOARD, actions.COPY_AUDIO_LINK_TO_CLIPBOARD
]

for item in menu.get_items():
action = item.get_stock_action()

if new_open or download:
item.destroy()
if not action in include:
menu.remove(item)

if lnk_open:
item.connect('select', self.on_webview_open_link)
if action == actions.OPEN_LINK:
item.get_action().connect('activate', self.on_webview_open_link)

def js_form_input(self, text):
script = """
Expand All @@ -218,15 +214,17 @@ def js_form_input(self, text):
if (fi) { fi.value = '%s' };
if (fe) { fe.dispatchEvent(ev); }
"""
script = script % (text)

self.webview.execute_script(script)
script = script % text
self.webview.run_javascript(script)

def js_click_element(self, selector):
script = "var sl = $('%s'); if (sl) { sl.click(); }"
script = script % (selector)
script = "var sl = $('%s'); if (sl) { sl.click(); }" % selector
self.webview.run_javascript(script)

self.webview.execute_script(script)
def js_open_link(self, link):
link = """a[href="/%s"]""" % link.split(self.app_url)[-1]
self.js_click_element(link)


if __name__ == '__main__':
Expand Down
Binary file modified screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion ui/main.ui
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
<child>
<object class="GtkModelButton" id="menu_main_offline">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="text" translatable="yes">Offline Data</property>
Expand Down Expand Up @@ -289,6 +288,7 @@
<child>
<object class="GtkButton" id="header_button_back">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_header_button_back_clicked" swapped="no"/>
Expand All @@ -309,6 +309,7 @@
<child>
<object class="GtkButton" id="header_button_forward">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_header_button_forward_clicked" swapped="no"/>
Expand Down

0 comments on commit 43e4601

Please sign in to comment.