Compare commits

..

No commits in common. "6f7141560a89dadd0545bb2adc8b9ed7294cf91a" and "c1f7e8e503560ce6e04d96e2ffd5b22fda88684e" have entirely different histories.

4 changed files with 27 additions and 81 deletions

View File

@ -1,10 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import gi import gi, os, json, shutil, hashlib, time, threading
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version("WebKit2", "4.1") gi.require_version("WebKit2", "4.1")
from gi.repository import Gtk, WebKit2 from gi.repository import GObject, Gtk, Gdk, WebKit2, GLib, Gio, GdkPixbuf
def create_same_window(origin, req): def create_same_window(origin, req):
@ -37,12 +37,6 @@ def create_custom_window(origin, options):
win.set_wmclass(wmclass, 'WebEngine') win.set_wmclass(wmclass, 'WebEngine')
win.set_title(options.get('title') or 'WebEngine') win.set_title(options.get('title') or 'WebEngine')
if options.get('icon_path'):
win.set_icon_from_file(options['icon_path'])
if options.get('icon'):
win.set_icon_name(options['icon'])
if options.get('frame') == False: if options.get('frame') == False:
win.set_decorated(False) win.set_decorated(False)
@ -56,7 +50,9 @@ def create_custom_window(origin, options):
win.set_resizable(False) win.set_resizable(False)
web = WebEngine(win, origin, options.get('uuid'))
web = WebEngine(win, origin)
web.set_root(origin.root, True) web.set_root(origin.root, True)
web.set_zoom_level(origin.get_zoom_level()) web.set_zoom_level(origin.get_zoom_level())
web.set_settings(origin.get_settings()) web.set_settings(origin.get_settings())

View File

@ -13,12 +13,13 @@ from gi.repository import WebKit2
class Inject: class Inject:
def __init__(self, webview, env = {}): def __init__(self, webview, env = {}):
self.webview = webview
self.manager = webview.get_user_content_manager() self.manager = webview.get_user_content_manager()
script_data = open(self.abspath('./inject.js'), 'r').read() script_data = open(self.abspath('./inject.js'), 'r').read()
frame = WebKit2.UserContentInjectedFrames.ALL_FRAMES frame = WebKit2.UserContentInjectedFrames.ALL_FRAMES
time = WebKit2.UserScriptInjectionTime.END time = WebKit2.UserScriptInjectionTime.END
script_data = script_data.replace("'{{env}}'", json.dumps(env)).replace("{{uuid}}", webview.uuid) script_data = script_data.replace("'{{env}}'", json.dumps(env))
script = WebKit2.UserScript(script_data, frame, time, None, None) script = WebKit2.UserScript(script_data, frame, time, None, None)
self.manager.add_script(script) self.manager.add_script(script)

View File

@ -3,7 +3,7 @@
# @date 2023/08/08 14:07:26 # @date 2023/08/08 14:07:26
import gi, os, json, shutil, hashlib, random import gi, os, json, shutil, hashlib, time, threading
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version("WebKit2", "4.1") gi.require_version("WebKit2", "4.1")
@ -54,6 +54,18 @@ def noop():
pass pass
# 类型js的settimeout的修饰器
def set_timeout(timeout = 0.5):
def decorator(callback):
def wrapper(*args):
t = threading.Timer(timeout, callback, args=args)
t.start()
return t
return wrapper
return decorator
class WebEngine(WebKit2.WebView): class WebEngine(WebKit2.WebView):
@ -61,28 +73,18 @@ class WebEngine(WebKit2.WebView):
'quit': (GObject.SignalFlags.RUN_FIRST, None, ()) 'quit': (GObject.SignalFlags.RUN_FIRST, None, ())
} }
uuid = None
root = None root = None
window = None window = None
opener = None opener = None
children = set()
custom_bridge = None custom_bridge = None
def __init__(self, win, opener = None, uuid = None): def __init__(self, win, opener = None):
WebKit2.WebView.__init__(self) WebKit2.WebView.__init__(self)
if uuid is None:
self.uuid = random.randbytes(8).hex()
else:
self.uuid = uuid
self.opener = opener self.opener = opener
self.window = win self.window = win
if opener is not None:
opener.children.add(self)
if win.get_title() is None: if win.get_title() is None:
win.set_title('WebEngine') win.set_title('WebEngine')
@ -107,22 +109,9 @@ class WebEngine(WebKit2.WebView):
self.connect('create', self.create_new_window) self.connect('create', self.create_new_window)
# 允许前端 widnow.close() 关闭窗口 # 允许前端 widnow.close() 关闭窗口
self.connect('close', self.close_window) self.connect('close', lambda w: win.close())
win.connect("destroy", self.remove_from_opener) # 通过外部关闭窗口时从父级中移除
def remove_from_opener(self, win = None):
if self.opener is not None:
try:
self.opener.children.remove(self)
except:
pass
def close_window(self, wv = None):
self.remove_from_opener()
self.window.close()
# 响应原生js的 window.open # 响应原生js的 window.open
def create_new_window(self, webview, nav): def create_new_window(self, webview, nav):
@ -196,7 +185,7 @@ class WebEngine(WebKit2.WebView):
# 退出app # 退出app
case 'quit': case 'quit':
self.close_window() self.window.close()
self.emit('quit') self.emit('quit')
# 读取图片, 返回图片像素数据 # 读取图片, 返回图片像素数据
@ -230,30 +219,12 @@ class WebEngine(WebKit2.WebView):
case 'opener': case 'opener':
callback = 'opener_message' callback = 'opener_message'
output = params.get('data') output = params.get('data')
uuid = json.dumps(self.uuid if self.opener else None) scripts = 'native.$emit("opener_message", ' + json.dumps(output) + ')'
scripts = f"native.$emit('opener_message', {json.dumps(output)}, {uuid})"
if self.opener is None: if self.opener is None:
self.evaluate_javascript(scripts, -1) self.evaluate_javascript(scripts, -1)
else: else:
self.opener.evaluate_javascript(scripts, -1) self.opener.evaluate_javascript(scripts, -1)
return return
case 'children':
callback = 'opener_message'
output = params.get('data')
uuid = params.get('uuid')
scripts = f"native.$emit('opener_message', {json.dumps(output)})"
if len(self.children) == 0:
self.evaluate_javascript(scripts, -1)
else:
if uuid is None:
for child in self.children:
child.evaluate_javascript(scripts, -1)
else:
for child in self.children:
if child.uuid == uuid:
child.evaluate_javascript(scripts, -1)
return
case 'window': case 'window':
_error, output = self._window_setting(params) _error, output = self._window_setting(params)
@ -443,7 +414,7 @@ class WebEngine(WebKit2.WebView):
self.new_window_by_custom(params.get('options')) self.new_window_by_custom(params.get('options'))
case 'close': case 'close':
self.close_window() self.window.close()
case 'fullscreen': case 'fullscreen':
self.window.fullscreen() self.window.fullscreen()

View File

@ -90,19 +90,6 @@ function base64(str = '') {
return btoa(str).replace(/[+=\/]/g, '') return btoa(str).replace(/[+=\/]/g, '')
} }
function _postMessage(data = {}, uuid = null) {
let ev = new Event('message')
Object.assign(ev, {
data,
source: {
postMessage(msg) {
native.children.postMessage(msg, uuid)
}
}
})
window.dispatchEvent(ev)
}
class NativeImage { class NativeImage {
#origin #origin
@ -205,7 +192,7 @@ class EventEmitter {
window.native = new EventEmitter() window.native = new EventEmitter()
native.$on('opener_message', (data, uuid) => _postMessage(data, uuid)) native.$on('opener_message', data => window.postMessage(data))
Object.assign(native, { Object.assign(native, {
env: '{{env}}', env: '{{env}}',
@ -358,21 +345,12 @@ Object.assign(native, {
} }
}, },
opener: { opener: {
//
postMessage(data = {}) { postMessage(data = {}) {
return handler('opener', { action: 'postmessage', data }, NO_CALLBACK) return handler('opener', { action: 'postmessage', data }, NO_CALLBACK)
} }
}, },
children: {
postMessage(data = {}, uuid = null) {
return handler(
'children',
{ action: 'postmessage', data, uuid },
NO_CALLBACK
)
}
},
window: { window: {
uuid: '{{uuid}}',
create(options = {}) { create(options = {}) {
return handler('window', { action: 'create', options }) return handler('window', { action: 'create', options })
}, },