Improve side-by-side experience

Includes multiple improvements to scroll syncing, preview re-render,
layout separation, etc
github/fork/yochananmarqos/patch-1
Gonçalo Silva 2019-04-25 15:57:06 +01:00
parent 5e770510ee
commit 2cb161307c
11 changed files with 393 additions and 252 deletions

View File

@ -59,7 +59,7 @@ body {
word-wrap: break-word;
max-width: 980px;
margin: auto;
padding: 2em;
padding: 4em;
}
a {

47
data/ui/Preview.ui 100644
View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkImage" id="pan-down">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">pan-down-symbolic</property>
<property name="icon_size">2</property>
</object>
<object class="GtkBox" id="preview">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkRevealer" id="preview_mode_revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_type">crossfade</property>
<property name="transition_duration">750</property>
<property name="reveal_child">True</property>
<child>
<object class="GtkButton" id="preview_mode_button">
<property name="label" translatable="yes">Full-Width</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Switch Preview Mode</property>
<property name="halign">end</property>
<property name="image">pan-down</property>
<property name="image_position">right</property>
<property name="always_show_image">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</interface>

View File

@ -28,9 +28,11 @@ from urllib.parse import unquote
import gi
from uberwriter.text_view_markup_handler import MarkupHandler
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf, GObject
from uberwriter import latex_to_PNG, text_view_markup_handler
from gi.repository import Gtk, Gdk, GdkPixbuf
from uberwriter import latex_to_PNG
from uberwriter.settings import Settings
from uberwriter.fix_table import FixTable
@ -360,8 +362,8 @@ class InlinePreview:
text = self.text_buffer.get_text(start_iter, end_iter, False)
math = text_view_markup_handler.regex["MATH"]
link = text_view_markup_handler.regex["LINK"]
math = MarkupHandler.regex["MATH"]
link = MarkupHandler.regex["LINK"]
footnote = re.compile(r'\[\^([^\s]+?)\]')
image = re.compile(r"!\[(.*?)\]\((.+?)\)")

View File

@ -0,0 +1,49 @@
from queue import Queue
from threading import Thread
from gi.repository import GLib
from uberwriter import helpers
from uberwriter.theme import Theme
class PreviewConverter:
"""Converts markdown to html using a background thread."""
def __init__(self):
super().__init__()
self.queue = Queue()
worker = Thread(target=self.__do_convert, name="preview-converter")
worker.daemon = True
worker.start()
def convert(self, text, callback, *user_data):
"""Converts text to html, calling callback when done.
The callback argument contains the result."""
self.queue.put((text, callback, user_data))
def stop(self):
"""Stops the background worker. PreviewConverter shouldn't be used after this."""
self.queue.put((None, None))
def __do_convert(self):
while True:
while True:
(text, callback, user_data) = self.queue.get()
if text is None and callback is None:
return
if self.queue.empty():
break
args = ['--standalone',
'--mathjax',
'--css=' + Theme.get_current().web_css_path,
'--lua-filter=' + helpers.get_script_path('relative_to_absolute.lua'),
'--lua-filter=' + helpers.get_script_path('task-list.lua')]
text = helpers.pandoc_convert(text, to="html5", args=args)
GLib.idle_add(callback, text, *user_data)

View File

@ -0,0 +1,147 @@
import math
import webbrowser
from enum import auto, IntEnum
import gi
from uberwriter.helpers import get_builder
gi.require_version('WebKit2', '4.0')
from gi.repository import WebKit2
from uberwriter.preview_converter import PreviewConverter
from uberwriter.settings import Settings
from uberwriter.web_view import WebView
class Step(IntEnum):
CONVERT_HTML = auto()
LOAD_WEBVIEW = auto()
RENDER = auto()
class Previewer:
def __init__(self, content, editor, text_view):
self.content = content
self.editor = editor
self.text_view = text_view
self.web_view = None
self.web_view_pending_html = None
builder = get_builder("Preview")
self.preview = builder.get_object("preview")
self.preview_mode_button = builder.get_object("preview_mode_button")
self.preview_mode_button.get_style_context().add_class('toggle-button')
self.preview_converter = PreviewConverter()
self.web_scroll_handler_id = None
self.text_scroll_handler_id = None
self.text_changed_handler_id = None
self.settings = Settings.new()
self.loading = False
self.showing = False
def show(self):
self.__show()
def reload(self):
if self.showing:
self.show()
def __show(self, html=None, step=Step.CONVERT_HTML):
if step == Step.CONVERT_HTML:
# First step: convert text to HTML.
buf = self.text_view.get_buffer()
self.preview_converter.convert(
buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False),
self.__show, Step.LOAD_WEBVIEW)
elif step == Step.LOAD_WEBVIEW:
# Second step: load HTML.
self.loading = True
if not self.web_view:
self.web_view = WebView()
self.web_view.get_settings().set_allow_universal_access_from_file_urls(True)
# Show preview once the load is finished
self.web_view.connect("load-changed", self.on_load_changed)
# All links will be opened in default browser, but local files are opened in apps.
self.web_view.connect("decide-policy", self.on_click_link)
if self.web_view.is_loading():
self.web_view_pending_html = html
else:
self.web_view.load_html(html, 'file://localhost/')
elif step == Step.RENDER:
# Last and one-time step: show the web view.
if self.showing:
return
self.showing = True
if self.settings.get_boolean("preview-side-by-side"):
self.content.set_size_request(self.text_view.get_min_width() * 2, -1)
self.web_view.set_scroll_scale(self.text_view.get_scroll_scale())
self.web_scroll_handler_id = \
self.web_view.connect("scroll-scale-changed", self.on_web_view_scrolled)
self.text_scroll_handler_id = \
self.text_view.connect("scroll-scale-changed", self.on_text_view_scrolled)
self.text_changed_handler_id = \
self.text_view.get_buffer().connect("changed", self.__show)
else:
self.content.remove(self.editor)
self.preview.pack_start(self.web_view, True, True, 0)
self.content.add(self.preview)
self.web_view.show()
def hide(self):
if self.showing:
self.showing = False
if self.settings.get_boolean("preview-side-by-side"):
self.content.set_size_request(-1, -1)
self.web_view.disconnect(self.web_scroll_handler_id)
self.text_view.disconnect(self.text_scroll_handler_id)
self.text_view.get_buffer().disconnect(self.text_changed_handler_id)
else:
self.content.add(self.editor)
self.content.remove(self.preview)
self.preview.remove(self.web_view)
if self.loading:
self.loading = False
self.web_view.destroy()
self.web_view = None
def on_load_changed(self, _web_view, event):
if event == WebKit2.LoadEvent.FINISHED:
self.loading = False
if self.web_view_pending_html:
self.__show(html=self.web_view_pending_html, step=Step.LOAD_WEBVIEW)
self.web_view_pending_html = None
else:
self.__show(step=Step.RENDER)
def on_text_view_scrolled(self, _text_view, scale):
if not math.isclose(scale, self.web_view.get_scroll_scale(), rel_tol=1e-5):
self.web_view.set_scroll_scale(scale)
def on_web_view_scrolled(self, _web_view, scale):
if not math.isclose(scale, self.text_view.get_scroll_scale(), rel_tol=1e-5):
self.text_view.set_scroll_scale(scale)
@staticmethod
def on_click_link(web_view, decision, _decision_type):
if web_view.get_uri().startswith(("http://", "https://", "www.")):
webbrowser.open(web_view.get_uri())
decision.ignore()
return True

View File

@ -33,32 +33,32 @@ class SearchAndReplace:
uberwriter
"""
def __init__(self, parentwindow, textview):
def __init__(self, parentwindow, textview, builder):
self.parentwindow = parentwindow
self.textview = textview
self.textbuffer = textview.get_buffer()
self.box = parentwindow.builder.get_object("searchbar_placeholder")
self.box = builder.get_object("searchbar_placeholder")
self.box.set_reveal_child(False)
self.searchbar = parentwindow.builder.get_object("searchbar")
self.searchentry = parentwindow.builder.get_object("searchentrybox")
self.searchbar = builder.get_object("searchbar")
self.searchentry = builder.get_object("searchentrybox")
self.searchentry.connect('changed', self.search)
self.searchentry.connect('activate', self.scrolltonext)
self.searchentry.connect('key-press-event', self.key_pressed)
self.open_replace_button = parentwindow.builder.get_object("replace")
self.open_replace_button = builder.get_object("replace")
self.open_replace_button.connect("toggled", self.toggle_replace)
self.nextbutton = parentwindow.builder.get_object("next_result")
self.prevbutton = parentwindow.builder.get_object("previous_result")
self.regexbutton = parentwindow.builder.get_object("regex")
self.casesensitivebutton = parentwindow.builder.get_object("case_sensitive")
self.nextbutton = builder.get_object("next_result")
self.prevbutton = builder.get_object("previous_result")
self.regexbutton = builder.get_object("regex")
self.casesensitivebutton = builder.get_object("case_sensitive")
self.replacebox = parentwindow.builder.get_object("replace_placeholder")
self.replacebox = builder.get_object("replace_placeholder")
self.replacebox.set_reveal_child(False)
self.replace_one_button = parentwindow.builder.get_object("replace_one")
self.replace_all_button = parentwindow.builder.get_object("replace_all")
self.replaceentry = parentwindow.builder.get_object("replaceentrybox")
self.replace_one_button = builder.get_object("replace_one")
self.replace_all_button = builder.get_object("replace_all")
self.replaceentry = builder.get_object("replaceentrybox")
self.replace_all_button.connect('clicked', self.replace_all)
self.replace_one_button.connect('clicked', self.replace_clicked)

View File

@ -1,13 +1,7 @@
import math
import re
from gettext import gettext as _
from queue import Queue
from threading import Thread
from gi.repository import GLib, Gio, Gtk
from uberwriter import helpers
from uberwriter.helpers import get_builder
from uberwriter.settings import Settings
from uberwriter.stats_counter import StatsCounter

View File

@ -37,7 +37,8 @@ class TextView(Gtk.TextView):
'insert-header': (GObject.SignalFlags.ACTION, None, ()),
'insert-strikethrough': (GObject.SignalFlags.ACTION, None, ()),
'undo': (GObject.SignalFlags.ACTION, None, ()),
'redo': (GObject.SignalFlags.ACTION, None, ())
'redo': (GObject.SignalFlags.ACTION, None, ()),
'scroll-scale-changed': (GObject.SIGNAL_RUN_LAST, None, (float,)),
}
font_sizes = [18, 17, 16, 15, 14] # Must match CSS selectors in gtk/base.css
@ -136,6 +137,8 @@ class TextView(Gtk.TextView):
if parent:
parent.set_size_request(self.get_min_width(), 500)
self.scroller = TextViewScroller(self, parent)
parent.get_vadjustment().connect("changed", self.on_scroll_scale_changed)
parent.get_vadjustment().connect("value-changed", self.on_scroll_scale_changed)
else:
self.scroller = None
@ -152,6 +155,9 @@ class TextView(Gtk.TextView):
self.markup.apply()
return False
def on_scroll_scale_changed(self, *_):
self.emit("scroll-scale-changed", self.get_scroll_scale())
def set_focus_mode(self, focus_mode):
"""Toggle focus mode.

View File

@ -0,0 +1,104 @@
import gi
gi.require_version('WebKit2', '4.0')
from gi.repository import WebKit2, GLib, GObject
class WebView(WebKit2.WebView):
"""A WebView that provides read/write access to scroll.
It does so using JavaScript, by continuously monitoring it while loaded.
The alternative is using a WebExtension and C-bindings (see reference), but that is more
complicated implementation-wise, as well as build-wise until we start building with Meson.
Reference: https://github.com/aperezdc/webkit2gtk-python-webextension-example
"""
GET_SCROLL_SCALE_JS = """
e = document.documentElement;
e.scrollHeight > e.clientHeight ? e.scrollTop / (e.scrollHeight - e.clientHeight) : 0;
"""
SET_SCROLL_SCALE_JS = """
scale = {:.16f};
e = document.documentElement;
e.scrollTop = (e.scrollHeight - e.clientHeight) * scale;
"""
__gsignals__ = {
"scroll-scale-changed": (GObject.SIGNAL_RUN_LAST, None, (float,)),
}
def __init__(self):
super().__init__()
self.connect("load-changed", self.on_load_changed)
self.connect("load-failed", self.on_load_failed)
self.connect("destroy", self.on_destroy)
self.scroll_scale = 0.0
self.pending_scroll_scale = None
self.state_loaded = False
self.state_load_failed = False
self.state_waiting = False
self.timeout_id = None
def get_scroll_scale(self):
return self.scroll_scale
def set_scroll_scale(self, scale):
self.pending_scroll_scale = scale
self.state_loop()
def on_load_changed(self, _web_view, event):
self.state_loaded = event >= WebKit2.LoadEvent.COMMITTED and not self.state_load_failed
self.state_load_failed = False
self.pending_scroll_scale = self.scroll_scale
self.state_loop()
def on_load_failed(self, _web_view, _event):
self.state_loaded = False
self.state_load_failed = True
self.state_loop()
def on_destroy(self, _widget):
self.state_loaded = False
self.state_loop()
def read_scroll_scale(self):
self.state_waiting = True
self.run_javascript(
self.GET_SCROLL_SCALE_JS, None, self.sync_scroll_scale)
def write_scroll_scale(self, scroll_scale):
self.run_javascript(
self.SET_SCROLL_SCALE_JS.format(scroll_scale), None, None)
def sync_scroll_scale(self, _web_view, result):
self.state_waiting = False
result = self.run_javascript_finish(result)
self.state_loop(result.get_js_value().to_double())
def state_loop(self, scroll_scale=None, delay=16): # 16ms ~ 60hz
# Remove any pending callbacks
if self.timeout_id:
GLib.source_remove(self.timeout_id)
self.timeout_id = None
# Set scroll scale if specified, and the state is not dirty
if scroll_scale not in (None, self.scroll_scale):
self.scroll_scale = scroll_scale
self.emit("scroll-scale-changed", self.scroll_scale)
# Handle the current state
if not self.state_loaded or self.state_load_failed or self.state_waiting:
return
if self.pending_scroll_scale:
self.write_scroll_scale(self.pending_scroll_scale)
self.pending_scroll_scale = None
if delay > 0:
self.timeout_id = GLib.timeout_add(delay, self.state_loop, None, 0)
else:
self.read_scroll_scale()

View File

@ -1,134 +0,0 @@
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('WebKit2', '4.0')
from gi.repository import WebKit2, GLib
class WebViewScroller:
"""Provides read/write scrolling functionality to a WebView.
It does so using JavaScript, by continuously monitoring it while loaded and focused.
The alternative is using a WebExtension and C-bindings (see reference), but that is more
complicated implementation-wise, and build-wise, at least we start building with Meson.
Reference: https://github.com/aperezdc/webkit2gtk-python-webextension-example
"""
SETUP_SROLL_SCALE_JS = """
const e = document.documentElement;
function get_scroll_scale() {
if (document.readyState !== "complete" || e.clientHeight === 0) {
return null;
} else if (e.scrollHeight <= e.clientHeight) {
return 0;
} else {
return e.scrollTop / (e.scrollHeight - e.clientHeight);
}
}
function set_scroll_scale(scale) {
if (document.readyState !== "complete") {
window.addEventListener("load", function() { set_scroll_scale(scale); });
} else if (e.clientHeight === 0) {
window.addEventListener("resize", function() { set_scroll_scale(scale); });
} else if (e.scrollHeight > e.clientHeight) {
e.scrollTop = (e.scrollHeight - e.clientHeight) * scale;
}
return get_scroll_scale();
}
get_scroll_scale();
""".strip()
GET_SCROLL_SCALE_JS = "get_scroll_scale();"
SET_SCROLL_SCALE_JS = "set_scroll_scale({:.16f});"
def __init__(self, webview):
super().__init__()
self.webview = webview
self.webview.connect("focus-in-event", self.on_focus_changed)
self.webview.connect("focus-out-event", self.on_focus_changed)
self.webview.connect("load-changed", self.on_load_changed)
self.webview.connect("destroy", self.on_destroy)
self.scroll_scale = 0
self.state_loaded = False
self.state_setup = False
self.state_focused = True
self.state_dirty = False
self.state_waiting = False
self.timeout_id = None
def get_scroll_scale(self):
return self.scroll_scale
def set_scroll_scale(self, scale):
self.scroll_scale = scale
self.state_dirty = True
self.state_loop()
def on_focus_changed(self, _webview, event):
self.state_focused = event.in_
self.state_loop()
def on_load_changed(self, _webview, event):
self.state_loaded = event == WebKit2.LoadEvent.FINISHED
self.state_loop()
def on_destroy(self, _widget):
self.state_loaded = False
self.state_focused = False
self.state_loop()
self.webview = None
def setup_scroll_state(self):
self.state_waiting = True
self.state_setup = True
self.webview.run_javascript(
self.SETUP_SROLL_SCALE_JS, None, self.sync_scroll_scale)
def read_scroll_scale(self):
self.state_waiting = True
self.webview.run_javascript(
self.GET_SCROLL_SCALE_JS, None, self.sync_scroll_scale)
def write_scroll_scale(self):
self.state_waiting = True
self.state_dirty = False
self.webview.run_javascript(
self.SET_SCROLL_SCALE_JS.format(self.scroll_scale), None, self.sync_scroll_scale)
def sync_scroll_scale(self, _webview, result):
self.state_waiting = False
result = self.webview.run_javascript_finish(result)
self.state_loop(result.get_js_value().to_double())
def state_loop(self, scroll_scale=None, read_delay=500):
# Remove any pending callbacks
if self.timeout_id:
GLib.source_remove(self.timeout_id)
self.timeout_id = None
# Set scroll scale if specified, and the state is not dirty
if scroll_scale is not None and not self.state_dirty:
self.scroll_scale = scroll_scale
# Handle the current state
if self.state_waiting:
return
elif not self.state_loaded:
return
elif not self.state_setup:
self.setup_scroll_state()
elif self.state_dirty:
self.write_scroll_scale()
elif self.state_focused:
if read_delay > 0:
self.timeout_id = GLib.timeout_add(read_delay, self.state_loop, None, 0)
else:
self.read_scroll_scale()

View File

@ -19,20 +19,17 @@ import locale
import logging
import os
import urllib
import webbrowser
from gettext import gettext as _
import gi
from uberwriter.export_dialog import Export
from uberwriter.previewer import Previewer
from uberwriter.stats_handler import StatsHandler
from uberwriter.text_view import TextView
from uberwriter.web_view_scroller import WebViewScroller
gi.require_version('Gtk', '3.0')
gi.require_version('WebKit2', '4.0') # pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, GObject, GLib, Gio
from gi.repository import WebKit2 as WebKit
import cairo
@ -74,8 +71,8 @@ class Window(Gtk.ApplicationWindow):
title="Uberwriter")
# Set UI
self.builder = get_builder('Window')
root = self.builder.get_object("FullscreenOverlay")
builder = get_builder('Window')
root = builder.get_object("FullscreenOverlay")
root.connect('style-updated', self.apply_current_theme)
self.connect("delete-event", self.on_delete_called)
self.add(root)
@ -88,7 +85,7 @@ class Window(Gtk.ApplicationWindow):
# Headerbars
self.headerbar = headerbars.MainHeaderbar(app)
self.set_titlebar(self.headerbar.hb_container)
self.fs_headerbar = headerbars.FullscreenHeaderbar(self.builder, app)
self.fs_headerbar = headerbars.FullscreenHeaderbar(builder, app)
self.title_end = " UberWriter"
self.set_headerbar_title("New File" + self.title_end)
@ -101,9 +98,7 @@ class Window(Gtk.ApplicationWindow):
self.accel_group = Gtk.AccelGroup()
self.add_accel_group(self.accel_group)
self.content = self.builder.get_object('content')
self.scrolled_window = self.builder.get_object('editor_scrolledwindow')
self.scrolled_window = builder.get_object('editor_scrolledwindow')
self.scrolled_window.get_style_context().add_class('uberwriter-scrolled-window')
# Setup text editor
@ -114,15 +109,16 @@ class Window(Gtk.ApplicationWindow):
self.text_view.grab_focus()
self.scrolled_window.add(self.text_view)
# Stats stats counter
self.stats_revealer = self.builder.get_object('editor_stats_revealer')
self.stats_button = self.builder.get_object('editor_stats_button')
self.stats_button.get_style_context().add_class('stats-button')
# Setup stats counter
self.stats_revealer = builder.get_object('editor_stats_revealer')
self.stats_button = builder.get_object('editor_stats_button')
self.stats_button.get_style_context().add_class('toggle-button')
self.stats_handler = StatsHandler(self.stats_button, self.text_view)
# Setup preview webview
self.web_view = None
self.web_view_scroller = None
# Setup preview
content = builder.get_object('content')
editor = builder.get_object('editor')
self.previewer = Previewer(content, editor, self.text_view)
# Setup header/stats bar hide after 3 seconds
self.top_bottom_bars_visible = True
@ -145,8 +141,8 @@ class Window(Gtk.ApplicationWindow):
###
# Sidebar initialization test
###
self.paned_window = self.builder.get_object("main_paned")
self.sidebar_box = self.builder.get_object("sidebar_box")
self.paned_window = builder.get_object("main_paned")
self.sidebar_box = builder.get_object("sidebar_box")
self.sidebar = Sidebar(self)
self.sidebar_box.hide()
@ -154,7 +150,7 @@ class Window(Gtk.ApplicationWindow):
# Search and replace initialization
# Same interface as Sidebar ;)
###
self.searchreplace = SearchAndReplace(self, self.text_view)
self.searchreplace = SearchAndReplace(self, self.text_view, builder)
# Set current theme
self.apply_current_theme()
@ -459,70 +455,14 @@ class Window(Gtk.ApplicationWindow):
"""
if state.get_boolean():
self.show_web_view()
self.previewer.show()
else:
self.show_text_editor()
self.previewer.hide()
return True
def show_text_editor(self):
# Remove web view
if self.settings.get_boolean("preview-side-by-side"):
self.set_size_request(-1, -1)
self.content.remove(self.web_view)
else:
self.scrolled_window.remove(self.scrolled_window.get_child())
self.scrolled_window.add(self.text_view)
self.text_view.show()
self.queue_draw()
# Sync scroll between web view and text view
self.text_view.set_scroll_scale(self.web_view_scroller.get_scroll_scale())
# Destroy web view to clean up resources
self.web_view.destroy()
self.web_view = None
self.web_view_scroller = None
def show_web_view(self, loaded=False):
if loaded:
# Sync scroll between text view and web view
self.web_view_scroller.set_scroll_scale(self.text_view.get_scroll_scale())
# Show web view
if self.settings.get_boolean("preview-side-by-side"):
self.content.add(self.web_view)
self.set_size_request(self.text_view.get_min_width() * 2, -1)
else:
self.scrolled_window.remove(self.scrolled_window.get_child())
self.scrolled_window.add(self.web_view)
self.web_view.show()
self.queue_draw()
else:
args = ['--standalone',
'--mathjax',
'--css=' + Theme.get_current().web_css_path,
'--lua-filter=' + helpers.get_script_path('relative_to_absolute.lua'),
'--lua-filter=' + helpers.get_script_path('task-list.lua')]
output = helpers.pandoc_convert(self.text_view.get_text(), to="html5", args=args)
if self.web_view is None:
self.web_view = WebKit.WebView()
self.web_view.get_settings().set_allow_universal_access_from_file_urls(True)
self.web_view_scroller = WebViewScroller(self.web_view)
# Show preview once the load is finished
self.web_view.connect("load-changed", self.on_preview_load_change)
# This saying that all links will be opened in default browser, \
# but local files are opened in appropriate apps:
self.web_view.connect("decide-policy", self.on_click_link)
self.web_view.load_html(output, 'file://localhost/')
def reload_preview(self):
if self.web_view:
self.show_web_view()
self.previewer.reload()
def load_file(self, filename=None):
"""Open File from command line or open / open recent etc."""
@ -701,17 +641,3 @@ class Window(Gtk.ApplicationWindow):
self.filename = None
base_path = "/"
self.settings.set_value("open-file-path", GLib.Variant("s", base_path))
def on_preview_load_change(self, _web_view, event):
"""swaps text editor with preview once the load is complete
"""
if event == WebKit.LoadEvent.FINISHED:
self.show_web_view(loaded=True)
def on_click_link(self, web_view, decision, _decision_type):
"""provide ability for self.webview to open links in default browser
"""
if web_view.get_uri().startswith(("http://", "https://", "www.")):
webbrowser.open(web_view.get_uri())
decision.ignore()
return True # Don't let the event "bubble up"