Compare commits
14 Commits
Author | SHA1 | Date |
---|---|---|
yutent | 64dc70b594 | |
yutent | 3062354338 | |
yutent | ece744aafe | |
yutent | b398551deb | |
yutent | 5da22eed8e | |
yutent | f9c4007e3f | |
yutent | 2682af069b | |
yutent | 838d9d1134 | |
yutent | c17d171ad8 | |
yutent | b9e7144271 | |
yutent | 6e0beb6a66 | |
yutent | 89717b4019 | |
yutent | 6f7141560a | |
yutent | 4ef23a703d |
1
build.sh
1
build.sh
|
@ -22,6 +22,7 @@ sed -i "s/{{size}}/${_size}/" DEBIAN/control
|
||||||
sed -i "s/{{version}}/${version}/" DEBIAN/control
|
sed -i "s/{{version}}/${version}/" DEBIAN/control
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
sudo chown -R root:root unpack/
|
||||||
dpkg-deb -b unpack/ "python3-webengine-gtk3-${version}.deb"
|
dpkg-deb -b unpack/ "python3-webengine-gtk3-${version}.deb"
|
||||||
|
|
||||||
sudo rm -rf unpack
|
sudo rm -rf unpack
|
|
@ -4,7 +4,7 @@ Section: develop
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Yutent <yutent.io@gmail.com>
|
Maintainer: Yutent <yutent.io@gmail.com>
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: python3 (>=3.10), python3-gi, gir1.2-gtk-3.0, python3-pil, python3-gi-cairo, gir1.2-gdkpixbuf-2.0
|
Depends: python3 (>=3.10), python3-gi, gir1.2-webkit2-4.1, gir1.2-gtk-3.0, python3-pil, python3-gi-cairo, gir1.2-gdkpixbuf-2.0
|
||||||
Recommends: gir1.2-ayatanaappindicator3-0.1, gir1.2-notify-0.7, gir1.2-keybinder-3.0
|
Recommends: gir1.2-ayatanaappindicator3-0.1, gir1.2-notify-0.7, gir1.2-keybinder-3.0
|
||||||
Installed-Size: {{size}}
|
Installed-Size: {{size}}
|
||||||
Homepage: https://git.wkit.fun/appcat/python3-webengine-gtk3
|
Homepage: https://git.wkit.fun/appcat/python3-webengine-gtk3
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import gi, os, json, shutil, hashlib, time, threading
|
import gi
|
||||||
|
|
||||||
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 GObject, Gtk, Gdk, WebKit2, GLib, Gio, GdkPixbuf
|
from gi.repository import Gtk, WebKit2
|
||||||
|
|
||||||
|
|
||||||
def create_same_window(origin, req):
|
def create_same_window(origin, req):
|
||||||
|
@ -37,6 +37,12 @@ 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)
|
||||||
|
|
||||||
|
@ -50,9 +56,7 @@ 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())
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
home_dir = os.getenv('HOME')
|
||||||
|
|
||||||
|
env = {
|
||||||
|
"HOME_DIR": home_dir,
|
||||||
|
"CONFIG_DIR": os.path.join(home_dir, '.config'),
|
||||||
|
"CACHE_DIR": os.path.join(home_dir, '.cache'),
|
||||||
|
"LANG": os.environ['LANG'] or "en_US.UTF-8"
|
||||||
|
}
|
|
@ -13,14 +13,18 @@ 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()
|
code = open(self.abspath('./inject.js'), 'r').read()
|
||||||
frame = WebKit2.UserContentInjectedFrames.ALL_FRAMES
|
frame = WebKit2.UserContentInjectedFrames.ALL_FRAMES
|
||||||
time = WebKit2.UserScriptInjectionTime.END
|
time = WebKit2.UserScriptInjectionTime.START
|
||||||
script_data = script_data.replace("'{{env}}'", json.dumps(env))
|
|
||||||
script = WebKit2.UserScript(script_data, frame, time, None, None)
|
code = code.replace("'{{env}}'", json.dumps(env))
|
||||||
|
code = code.replace("{{uuid}}", webview.uuid)
|
||||||
|
code = code.replace("{{app_name}}", webview.app_name)
|
||||||
|
code = code.replace("{{app_version}}", webview.app_version)
|
||||||
|
|
||||||
|
script = WebKit2.UserScript(code, frame, time, None, None)
|
||||||
|
|
||||||
self.manager.add_script(script)
|
self.manager.add_script(script)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class Settings(WebKit2.Settings):
|
||||||
self.set_javascript_can_open_windows_automatically(True)
|
self.set_javascript_can_open_windows_automatically(True)
|
||||||
|
|
||||||
|
|
||||||
self.set_user_agent_with_application_details('WebEngine', version)
|
self.set_useragent_with_app('WebEngine', version)
|
||||||
|
|
||||||
|
|
||||||
# indexedDB 和 localStorage 和 离线缓存
|
# indexedDB 和 localStorage 和 离线缓存
|
||||||
|
@ -43,6 +43,9 @@ class Settings(WebKit2.Settings):
|
||||||
self.set_media_playback_allows_inline(True)
|
self.set_media_playback_allows_inline(True)
|
||||||
|
|
||||||
|
|
||||||
|
def set_useragent_with_app(self, name, ver):
|
||||||
|
self.set_user_agent_with_application_details(name, ver)
|
||||||
|
|
||||||
def set_useragent(self, str):
|
def set_useragent(self, str):
|
||||||
self.set_user_agent(str)
|
self.set_user_agent(str)
|
||||||
|
|
||||||
|
@ -75,6 +78,9 @@ def create_setting(options = None):
|
||||||
setting.enable_devtools()
|
setting.enable_devtools()
|
||||||
setting.mock_devices()
|
setting.mock_devices()
|
||||||
|
|
||||||
|
if options.get('app_name') and options.get('app_version'):
|
||||||
|
setting.set_useragent_with_app(options['app_name'], options['app_version'])
|
||||||
|
|
||||||
if options.get('useragent'):
|
if options.get('useragent'):
|
||||||
setting.set_useragent(options.get('useragent'))
|
setting.set_useragent(options.get('useragent'))
|
||||||
|
|
||||||
|
@ -86,6 +92,9 @@ def create_setting(options = None):
|
||||||
|
|
||||||
|
|
||||||
def wrapper(app, extra = None):
|
def wrapper(app, extra = None):
|
||||||
|
if options is not None:
|
||||||
|
app.app_name = options.get('app_name')
|
||||||
|
app.app_version = options.get('app_version')
|
||||||
app.set_settings(setting)
|
app.set_settings(setting)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
|
@ -3,9 +3,12 @@
|
||||||
# @author yutent<yutent.io@gmail.com>
|
# @author yutent<yutent.io@gmail.com>
|
||||||
# @date 2023/07/28 14:39:33
|
# @date 2023/07/28 14:39:33
|
||||||
|
|
||||||
import gi
|
import gi, threading
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import GdkPixbuf
|
from gi.repository import GdkPixbuf, GObject
|
||||||
|
|
||||||
|
def noop():
|
||||||
|
pass
|
||||||
|
|
||||||
def get_monitor_info(monitor):
|
def get_monitor_info(monitor):
|
||||||
return {
|
return {
|
||||||
|
@ -64,3 +67,33 @@ def dict_to_pixbuf(data):
|
||||||
image = None
|
image = None
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
# 定义一个异步修饰器, 用于在子线程中运行一些会阻塞主线程的任务
|
||||||
|
def run_async(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
thread = threading.Thread(target=func, args=args, kwargs=kwargs)
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
return thread
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
# 类型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
|
||||||
|
|
||||||
|
|
||||||
|
# 定义一个修饰器, 用于将当前方法转到主线程中运行 (子线程中调用方法时)
|
||||||
|
def idle(func):
|
||||||
|
def wrapper(*args):
|
||||||
|
GObject.idle_add(func, *args)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
build = (0, 5, 0)
|
build = (0, 7, 0)
|
||||||
version = '.'.join(map(str, build))
|
version = '.'.join(map(str, build))
|
|
@ -3,7 +3,8 @@
|
||||||
# @date 2023/08/08 14:07:26
|
# @date 2023/08/08 14:07:26
|
||||||
|
|
||||||
|
|
||||||
import gi, os, json, shutil, hashlib, time, threading
|
import gi, os, sys, json
|
||||||
|
import webbrowser, shutil, hashlib, random
|
||||||
|
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
gi.require_version("WebKit2", "4.1")
|
gi.require_version("WebKit2", "4.1")
|
||||||
|
@ -31,39 +32,13 @@ try:
|
||||||
except:
|
except:
|
||||||
Keybinder = None
|
Keybinder = None
|
||||||
|
|
||||||
|
from ._env import env
|
||||||
|
from ._version import version
|
||||||
|
|
||||||
|
|
||||||
from ._settings import create_setting
|
from ._settings import create_setting
|
||||||
from ._protocal import create_protocal
|
from ._protocal import create_protocal
|
||||||
from ._notify import create_notify
|
from ._notify import create_notify
|
||||||
from ._inject import Inject
|
from ._inject import Inject
|
||||||
from ._utils import get_monitor_info, pixbuf_to_dict, dict_to_pixbuf
|
from ._utils import noop, get_monitor_info, pixbuf_to_dict, dict_to_pixbuf, run_async, idle
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
env = {
|
|
||||||
"HOME_DIR": os.getenv('HOME'),
|
|
||||||
"CONFIG_DIR": os.path.join(os.getenv('HOME'), '.config'),
|
|
||||||
"CACHE_DIR": os.path.join(os.getenv('HOME'), '.cache')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def noop():
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,21 +47,32 @@ class WebEngine(WebKit2.WebView):
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'quit': (GObject.SignalFlags.RUN_FIRST, None, ())
|
'quit': (GObject.SignalFlags.RUN_FIRST, None, ())
|
||||||
}
|
}
|
||||||
|
app_name = 'WebEngine'
|
||||||
|
app_version = version
|
||||||
|
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):
|
def __init__(self, win, opener = None, uuid = 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(self.app_name)
|
||||||
|
|
||||||
if win.get_icon() is None and win.get_icon_name() is None:
|
if win.get_icon() is None and win.get_icon_name() is None:
|
||||||
win.set_icon_name('web-browser')
|
win.set_icon_name('web-browser')
|
||||||
|
@ -109,8 +95,25 @@ 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', lambda w: win.close())
|
self.connect('close', self.close_window)
|
||||||
|
|
||||||
|
# 通过外部关闭窗口时从父级中移除
|
||||||
|
win.connect("destroy", self.remove_from_opener)
|
||||||
|
win.connect('hide', lambda w: self.call_js('hide'))
|
||||||
|
win.connect('show', lambda w: self.call_js('show'))
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -152,14 +155,14 @@ class WebEngine(WebKit2.WebView):
|
||||||
raise ValueError('url must starts with "/" or "app://"')
|
raise ValueError('url must starts with "/" or "app://"')
|
||||||
|
|
||||||
|
|
||||||
|
@idle
|
||||||
def call_js(self, method, data = None, err = None):
|
def call_js(self, method, data = None, err = None):
|
||||||
if err is not None:
|
if err is not None:
|
||||||
err = str(err)
|
err = str(err)
|
||||||
scripts = 'native.$emit("' + method + '", ' + json.dumps(err) + ', ' + json.dumps(data) + ')'
|
scripts = 'native.$emit("' + method + '", ' + json.dumps(err) + ', ' + json.dumps(data) + ')'
|
||||||
self.evaluate_javascript(scripts, -1)
|
self.evaluate_javascript(scripts, -1)
|
||||||
|
|
||||||
|
@run_async
|
||||||
def called_by_js(self, webview, message):
|
def called_by_js(self, webview, message):
|
||||||
|
|
||||||
data = json.loads(message.get_js_value().to_json(0))
|
data = json.loads(message.get_js_value().to_json(0))
|
||||||
|
@ -172,21 +175,16 @@ class WebEngine(WebKit2.WebView):
|
||||||
|
|
||||||
|
|
||||||
match event:
|
match event:
|
||||||
case 'init':
|
|
||||||
output = env
|
case 'app':
|
||||||
|
_error, output = self._app_handler(params)
|
||||||
|
|
||||||
case 'fs':
|
case 'fs':
|
||||||
_error, output = self._fs_setting(params)
|
_error, output = self._fs_handler(params)
|
||||||
|
|
||||||
|
|
||||||
case 'clipboard':
|
case 'clipboard':
|
||||||
_error, output = self._clipboard_setting(params)
|
_error, output = self._clipboard_handler(params)
|
||||||
|
|
||||||
|
|
||||||
# 退出app
|
|
||||||
case 'quit':
|
|
||||||
self.window.close()
|
|
||||||
self.emit('quit')
|
|
||||||
|
|
||||||
# 读取图片, 返回图片像素数据
|
# 读取图片, 返回图片像素数据
|
||||||
case 'image':
|
case 'image':
|
||||||
|
@ -206,7 +204,7 @@ class WebEngine(WebKit2.WebView):
|
||||||
|
|
||||||
|
|
||||||
case 'keybinder':
|
case 'keybinder':
|
||||||
_error, output = self._keybinder_setting(params)
|
_error, output = self._keybinder_handler(params)
|
||||||
|
|
||||||
|
|
||||||
case 'tray':
|
case 'tray':
|
||||||
|
@ -219,15 +217,34 @@ class WebEngine(WebKit2.WebView):
|
||||||
case 'opener':
|
case 'opener':
|
||||||
callback = 'opener_message'
|
callback = 'opener_message'
|
||||||
output = params.get('data')
|
output = params.get('data')
|
||||||
scripts = 'native.$emit("opener_message", ' + json.dumps(output) + ')'
|
uuid = json.dumps(self.uuid if self.opener else None)
|
||||||
|
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)
|
||||||
|
break
|
||||||
|
return
|
||||||
|
|
||||||
case 'window':
|
case 'window':
|
||||||
_error, output = self._window_setting(params)
|
_error, output = self._window_handler(params)
|
||||||
|
|
||||||
|
|
||||||
case 'notify':
|
case 'notify':
|
||||||
|
@ -244,25 +261,61 @@ class WebEngine(WebKit2.WebView):
|
||||||
|
|
||||||
|
|
||||||
case 'proxy':
|
case 'proxy':
|
||||||
_error, output = self._proxy_setting(params)
|
_error, output = self._proxy_handler(params)
|
||||||
|
|
||||||
|
|
||||||
case 'md5':
|
case 'md5':
|
||||||
output = hashlib.md5(str(params.get('value'))).hexdigest()
|
output = hashlib.md5(str(params.get('value'))).hexdigest()
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
if self.custom_bridge is None:
|
if self.custom_bridge is not None:
|
||||||
pass
|
try:
|
||||||
else:
|
_error, output = self.custom_bridge(event, params) or (None, None)
|
||||||
_error, output = self.custom_bridge(event, params)
|
except Exception as err:
|
||||||
|
print(err)
|
||||||
|
_error = err
|
||||||
|
|
||||||
# 有回调则返回结果
|
# 有回调则返回结果
|
||||||
if callback:
|
if callback:
|
||||||
self.call_js(callback, output, _error)
|
self.call_js(callback, output, _error)
|
||||||
|
|
||||||
|
|
||||||
|
def _app_handler(self, params = {}):
|
||||||
|
_error = None
|
||||||
|
output = None
|
||||||
|
match(params.get('action')):
|
||||||
|
# 退出app
|
||||||
|
case 'quit':
|
||||||
|
self.close_window()
|
||||||
|
self.emit('quit')
|
||||||
|
|
||||||
def _fs_setting(self, params = {}):
|
case 'relaunch':
|
||||||
|
py = sys.executable
|
||||||
|
os.execl(py, py, *sys.argv)
|
||||||
|
|
||||||
|
return (_error, output)
|
||||||
|
|
||||||
|
def _shell_handler(self, params = {}):
|
||||||
|
_error = None
|
||||||
|
output = None
|
||||||
|
path = params.get('path')
|
||||||
|
|
||||||
|
match(params.get('action')):
|
||||||
|
case 'openExternal':
|
||||||
|
webbrowser.open(params.get('url'))
|
||||||
|
|
||||||
|
case 'showItemInFolder':
|
||||||
|
os.system(f"xdg-open '{path}'")
|
||||||
|
|
||||||
|
case 'openPath':
|
||||||
|
os.system(f"xdg-open '{path}'")
|
||||||
|
|
||||||
|
case 'trashItem':
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _fs_handler(self, params = {}):
|
||||||
_error = None
|
_error = None
|
||||||
output = None
|
output = None
|
||||||
filepath = params.get('filepath')
|
filepath = params.get('filepath')
|
||||||
|
@ -332,11 +385,14 @@ class WebEngine(WebKit2.WebView):
|
||||||
case 'isdir':
|
case 'isdir':
|
||||||
output = os.path.isdir(filepath)
|
output = os.path.isdir(filepath)
|
||||||
|
|
||||||
|
case 'mkdir':
|
||||||
|
output = os.makedirs(filepath)
|
||||||
|
|
||||||
return (_error, output)
|
return (_error, output)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _clipboard_setting(self, params = {}):
|
def _clipboard_handler(self, params = {}):
|
||||||
_error = None
|
_error = None
|
||||||
output = None
|
output = None
|
||||||
|
|
||||||
|
@ -373,7 +429,7 @@ class WebEngine(WebKit2.WebView):
|
||||||
return (_error, output)
|
return (_error, output)
|
||||||
|
|
||||||
|
|
||||||
def _keybinder_setting(self, params = {}):
|
def _keybinder_handler(self, params = {}):
|
||||||
_error = None
|
_error = None
|
||||||
output = None
|
output = None
|
||||||
keymap = params.get('value')
|
keymap = params.get('value')
|
||||||
|
@ -404,7 +460,7 @@ class WebEngine(WebKit2.WebView):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _window_setting(self, params = {}):
|
def _window_handler(self, params = {}):
|
||||||
_error = None
|
_error = None
|
||||||
output = None
|
output = None
|
||||||
|
|
||||||
|
@ -414,7 +470,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.window.close()
|
self.close_window()
|
||||||
|
|
||||||
case 'fullscreen':
|
case 'fullscreen':
|
||||||
self.window.fullscreen()
|
self.window.fullscreen()
|
||||||
|
@ -466,7 +522,7 @@ class WebEngine(WebKit2.WebView):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _proxy_setting(self, params = {}):
|
def _proxy_handler(self, params = {}):
|
||||||
dm = self.get_website_data_manager()
|
dm = self.get_website_data_manager()
|
||||||
output = True
|
output = True
|
||||||
_error = None
|
_error = None
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* @author yutent<yutent.io@gmail.com>
|
* @author yutent<yutent.io@gmail.com>
|
||||||
* @date 2023/07/21 17:38:11
|
* @date 2023/07/21 17:38:11
|
||||||
*/
|
*/
|
||||||
|
!(function () {
|
||||||
const MIME_TYPES = {
|
const MIME_TYPES = {
|
||||||
html: 'text/html',
|
html: 'text/html',
|
||||||
json: 'application/json',
|
json: 'application/json',
|
||||||
|
@ -48,6 +48,8 @@ const KEYS_MAP = {
|
||||||
const NO_CALLBACK = false
|
const NO_CALLBACK = false
|
||||||
const CALL_ONCE = true
|
const CALL_ONCE = true
|
||||||
|
|
||||||
|
const __events__ = Symbol('events')
|
||||||
|
|
||||||
function defer() {
|
function defer() {
|
||||||
let obj = {}
|
let obj = {}
|
||||||
obj.promise = new Promise((resolve, reject) => {
|
obj.promise = new Promise((resolve, reject) => {
|
||||||
|
@ -71,7 +73,7 @@ function handler(event, data = {}, need = CALL_ONCE) {
|
||||||
callback = rand()
|
callback = rand()
|
||||||
native.$once(callback, (err, res) => {
|
native.$once(callback, (err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
_.reject(err)
|
_.reject(new Error(err))
|
||||||
} else {
|
} else {
|
||||||
_.resolve(res)
|
_.resolve(res)
|
||||||
}
|
}
|
||||||
|
@ -90,6 +92,34 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
function readonly(obj, key, value) {
|
||||||
|
Object.defineProperty(obj, key, {
|
||||||
|
get() {
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
enumerable: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function _extend(origin, options = {}) {
|
||||||
|
for (let k in options) {
|
||||||
|
readonly(origin, k, options[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NativeImage {
|
class NativeImage {
|
||||||
#origin
|
#origin
|
||||||
|
|
||||||
|
@ -143,15 +173,15 @@ class NativeImage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EventEmitter {
|
class Native {
|
||||||
//
|
//
|
||||||
__events__ = Object.create(null)
|
[__events__] = Object.create(null)
|
||||||
|
|
||||||
$on(name, fn) {
|
$on(name, fn) {
|
||||||
if (this.__events__[name]) {
|
if (this[__events__][name]) {
|
||||||
this.__events__[name].push(fn)
|
this[__events__][name].push(fn)
|
||||||
} else {
|
} else {
|
||||||
this.__events__[name] = [fn]
|
this[__events__][name] = [fn]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,18 +191,20 @@ class EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
$off(name, fn) {
|
$off(name, fn) {
|
||||||
if (this.__events__[name]) {
|
if (this[__events__][name]) {
|
||||||
if (fn) {
|
if (fn) {
|
||||||
this.__events__[name] = this.__events__[name].filter(it => it !== fn)
|
this[__events__][name] = this[__events__][name].filter(
|
||||||
|
it => it !== fn
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
this.__events__[name] = []
|
this[__events__][name] = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$emit(name, ...args) {
|
$emit(name, ...args) {
|
||||||
if (this.__events__[name]) {
|
if (this[__events__][name]) {
|
||||||
for (let fn of this.__events__[name]) {
|
for (let fn of this[__events__][name]) {
|
||||||
try {
|
try {
|
||||||
fn.apply(this, args)
|
fn.apply(this, args)
|
||||||
if (fn.__once__) {
|
if (fn.__once__) {
|
||||||
|
@ -186,19 +218,34 @@ class EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
$destroy() {
|
$destroy() {
|
||||||
this.__events__ = Object.create(null)
|
this[__events__] = Object.create(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.native = new EventEmitter()
|
readonly(window, 'native', new Native())
|
||||||
|
|
||||||
native.$on('opener_message', data => window.postMessage(data))
|
native.$on('opener_message', (data, uuid) => _postMessage(data, uuid))
|
||||||
|
|
||||||
Object.assign(native, {
|
_extend(native, {
|
||||||
env: '{{env}}',
|
env: '{{env}}',
|
||||||
|
app: {
|
||||||
|
name: '{{app_name}}',
|
||||||
|
version: '{{app_version}}',
|
||||||
quit() {
|
quit() {
|
||||||
return handler('quit', {}, NO_CALLBACK)
|
return handler('app', { action: 'quit' }, NO_CALLBACK)
|
||||||
|
},
|
||||||
|
relaunch() {
|
||||||
|
return handler('app', { action: 'relaunch' }, NO_CALLBACK)
|
||||||
|
},
|
||||||
|
getLocale() {
|
||||||
|
return native.env.LANG
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shell: {
|
||||||
|
openExternal(url) {},
|
||||||
|
showItemInFolder(path) {},
|
||||||
|
openPath(path) {},
|
||||||
|
trashItem(path) {}
|
||||||
},
|
},
|
||||||
fs: {
|
fs: {
|
||||||
access(filepath, mode = 'r') {
|
access(filepath, mode = 'r') {
|
||||||
|
@ -246,6 +293,9 @@ Object.assign(native, {
|
||||||
},
|
},
|
||||||
copy(filepath, target) {
|
copy(filepath, target) {
|
||||||
return handler('fs', { action: 'copy', filepath, target })
|
return handler('fs', { action: 'copy', filepath, target })
|
||||||
|
},
|
||||||
|
mkdir(filepath) {
|
||||||
|
return handler('fs', { action: 'mkdir', filepath })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
image(filepath) {
|
image(filepath) {
|
||||||
|
@ -345,12 +395,21 @@ 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 })
|
||||||
},
|
},
|
||||||
|
@ -397,7 +456,11 @@ Object.assign(native, {
|
||||||
handler('window', { action: 'move', value: { x, y } }, NO_CALLBACK)
|
handler('window', { action: 'move', value: { x, y } }, NO_CALLBACK)
|
||||||
},
|
},
|
||||||
setOpacity(opacity = 1) {
|
setOpacity(opacity = 1) {
|
||||||
handler('window', { action: 'set_opacity', value: opacity }, NO_CALLBACK)
|
handler(
|
||||||
|
'window',
|
||||||
|
{ action: 'set_opacity', value: opacity },
|
||||||
|
NO_CALLBACK
|
||||||
|
)
|
||||||
},
|
},
|
||||||
alwayOnTop(setting = true) {
|
alwayOnTop(setting = true) {
|
||||||
handler(
|
handler(
|
||||||
|
@ -445,3 +508,4 @@ Object.assign(native, {
|
||||||
|
|
||||||
handler
|
handler
|
||||||
})
|
})
|
||||||
|
})()
|
||||||
|
|
Loading…
Reference in New Issue