PyWebWinUI3 1.0.0b43__tar.gz → 1.0.0b45__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pywebwinui3-1.0.0b43/PyWebWinUI3.egg-info → pywebwinui3-1.0.0b45}/PKG-INFO +1 -1
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45/PyWebWinUI3.egg-info}/PKG-INFO +1 -1
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/SOURCES.txt +6 -6
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/__init__.py +1 -1
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/core.py +24 -2
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/event.py +25 -22
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/qt.py +261 -14
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/util.py +13 -2
- pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/assets/2.BF6evWMx.css → pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/assets/2.QxDX12Lu.css +1 -1
- pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/chunks/BprPI5v6.js +1 -0
- pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/entry/app.UoyHjfbP.js → pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/entry/app.76e8hy19.js +2 -2
- pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/entry/start.BDCzTRGD.js +1 -0
- pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/nodes/1.DrRW4ELJ.js → pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/nodes/1.DJOepXLS.js +1 -1
- pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/nodes/2.aKOPDyc3.js +88 -0
- pywebwinui3-1.0.0b45/pywebwinui3/web/_app/version.json +1 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/index.html +8 -8
- pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/chunks/TzBZ48H8.js +0 -1
- pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/entry/start.PpAqhk-4.js +0 -1
- pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/nodes/2.D8H1CTiv.js +0 -88
- pywebwinui3-1.0.0b43/pywebwinui3/web/_app/version.json +0 -1
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/LICENSE +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/MANIFEST.in +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/NOTICE +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/dependency_links.txt +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/requires.txt +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/top_level.txt +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/README.md +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/type.py +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.0.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.1.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.10.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.11.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.12.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.13.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.14.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.15.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.16.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.17.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.18.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.19.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.2.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.20.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.21.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.22.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.23.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.24.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.25.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.26.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.27.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.28.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.29.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.3.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.30.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.31.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.32.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.33.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.34.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.35.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.36.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.37.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.38.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.39.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.4.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.40.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.41.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.42.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.43.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.44.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.45.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.46.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.47.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.48.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.49.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.5.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.50.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.51.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.52.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.53.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.54.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.55.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.56.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.57.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.58.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.59.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.6.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.60.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.61.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.62.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.63.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.64.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.65.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.66.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.67.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.68.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.69.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.7.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.70.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.71.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.72.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.73.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.74.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.75.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.76.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.77.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.78.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.79.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.8.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.80.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.81.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.82.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.83.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.84.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.85.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.86.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.87.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.88.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.89.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.9.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.90.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.91.woff2 +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/Variable-dynamic-subset.min.css +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/SegoeFluentIcons.ttf +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/env.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/C5fctQ8t.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/DuPwkB53.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/KfJIcf5l.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/jnH-TXHS.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/ln01AzD2.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/oIQI43DZ.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/wKeSOqcH.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/nodes/0.DYoNlsbe.js +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/requirements.txt +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/setup.cfg +0 -0
- {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/setup.py +0 -0
|
@@ -112,17 +112,17 @@ pywebwinui3/web/Pretendard/PretendardVariable.subset.91.woff2
|
|
|
112
112
|
pywebwinui3/web/Pretendard/Variable-dynamic-subset.min.css
|
|
113
113
|
pywebwinui3/web/_app/env.js
|
|
114
114
|
pywebwinui3/web/_app/version.json
|
|
115
|
-
pywebwinui3/web/_app/immutable/assets/2.
|
|
115
|
+
pywebwinui3/web/_app/immutable/assets/2.QxDX12Lu.css
|
|
116
|
+
pywebwinui3/web/_app/immutable/chunks/BprPI5v6.js
|
|
116
117
|
pywebwinui3/web/_app/immutable/chunks/C5fctQ8t.js
|
|
117
118
|
pywebwinui3/web/_app/immutable/chunks/DuPwkB53.js
|
|
118
119
|
pywebwinui3/web/_app/immutable/chunks/KfJIcf5l.js
|
|
119
|
-
pywebwinui3/web/_app/immutable/chunks/TzBZ48H8.js
|
|
120
120
|
pywebwinui3/web/_app/immutable/chunks/jnH-TXHS.js
|
|
121
121
|
pywebwinui3/web/_app/immutable/chunks/ln01AzD2.js
|
|
122
122
|
pywebwinui3/web/_app/immutable/chunks/oIQI43DZ.js
|
|
123
123
|
pywebwinui3/web/_app/immutable/chunks/wKeSOqcH.js
|
|
124
|
-
pywebwinui3/web/_app/immutable/entry/app.
|
|
125
|
-
pywebwinui3/web/_app/immutable/entry/start.
|
|
124
|
+
pywebwinui3/web/_app/immutable/entry/app.76e8hy19.js
|
|
125
|
+
pywebwinui3/web/_app/immutable/entry/start.BDCzTRGD.js
|
|
126
126
|
pywebwinui3/web/_app/immutable/nodes/0.DYoNlsbe.js
|
|
127
|
-
pywebwinui3/web/_app/immutable/nodes/1.
|
|
128
|
-
pywebwinui3/web/_app/immutable/nodes/2.
|
|
127
|
+
pywebwinui3/web/_app/immutable/nodes/1.DJOepXLS.js
|
|
128
|
+
pywebwinui3/web/_app/immutable/nodes/2.aKOPDyc3.js
|
|
@@ -68,6 +68,26 @@ class MainWindow:
|
|
|
68
68
|
self.events.accentColorChange += lambda palette: self.values.set("system_accent", palette)
|
|
69
69
|
self.events.themeChange += lambda theme: self.values.set("system_theme_resolved", theme)
|
|
70
70
|
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _normalize_notice_level(level) -> int:
|
|
73
|
+
try:
|
|
74
|
+
return int(level)
|
|
75
|
+
except (TypeError, ValueError):
|
|
76
|
+
name = str(getattr(level, "name", level)).split(".")[-1]
|
|
77
|
+
return int(getattr(Status, name, Status.Critical))
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def _normalize_notice_entry(cls, entry) -> list:
|
|
81
|
+
if not isinstance(entry, (list, tuple)):
|
|
82
|
+
return [int(Status.Critical), "", str(entry or ""), None]
|
|
83
|
+
|
|
84
|
+
level, title, description, item, *_ = [*entry, None, None, None, None]
|
|
85
|
+
return [cls._normalize_notice_level(level), title or "", description or "", item]
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def _normalize_notice_items(cls, items) -> list[list]:
|
|
89
|
+
return [cls._normalize_notice_entry(entry) for entry in items or []]
|
|
90
|
+
|
|
71
91
|
@property
|
|
72
92
|
def api(self) -> WebviewAPI:
|
|
73
93
|
if self._api is None:
|
|
@@ -118,11 +138,13 @@ class MainWindow:
|
|
|
118
138
|
|
|
119
139
|
def notice(self, level: Status, title: str, description: str, item: dict | None = None):
|
|
120
140
|
self.values["system_nofication"] = [
|
|
121
|
-
*self.values["system_nofication"],
|
|
122
|
-
[level, title, description, item],
|
|
141
|
+
*self._normalize_notice_items(self.values["system_nofication"]),
|
|
142
|
+
self._normalize_notice_entry([level, title, description, item]),
|
|
123
143
|
]
|
|
124
144
|
|
|
125
145
|
def init(self) -> dict:
|
|
146
|
+
if self.values.get("system_nofication"):
|
|
147
|
+
self.values.set("system_nofication", self._normalize_notice_items(self.values["system_nofication"]), False)
|
|
126
148
|
return dict(self.values)
|
|
127
149
|
|
|
128
150
|
def pin(self, state: bool):
|
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import atexit
|
|
4
3
|
import fnmatch
|
|
5
4
|
import logging
|
|
6
|
-
import os
|
|
7
5
|
import traceback
|
|
8
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
9
6
|
from typing import Any, Callable
|
|
10
7
|
|
|
11
8
|
logger = logging.getLogger("pywebwinui3.eventmanager")
|
|
12
9
|
|
|
13
|
-
_EVENT_WORKERS = max(4, min(32, (os.cpu_count() or 1) * 4))
|
|
14
|
-
_EVENT_EXECUTOR = ThreadPoolExecutor(
|
|
15
|
-
max_workers=_EVENT_WORKERS,
|
|
16
|
-
thread_name_prefix="pywebwinui3-event",
|
|
17
|
-
)
|
|
18
|
-
atexit.register(lambda: _EVENT_EXECUTOR.shutdown(wait=False))
|
|
19
|
-
|
|
20
10
|
|
|
21
11
|
def _call_listener(func: Callable[..., Any], args: tuple[Any, ...]):
|
|
22
12
|
try:
|
|
@@ -36,7 +26,7 @@ class Event:
|
|
|
36
26
|
|
|
37
27
|
for func in listeners:
|
|
38
28
|
try:
|
|
39
|
-
|
|
29
|
+
_call_listener(func, args)
|
|
40
30
|
except Exception:
|
|
41
31
|
logger.error(traceback.format_exc())
|
|
42
32
|
|
|
@@ -62,36 +52,49 @@ class Event:
|
|
|
62
52
|
|
|
63
53
|
class PathEvent:
|
|
64
54
|
def __init__(self) -> None:
|
|
65
|
-
self.
|
|
55
|
+
self.exact_items: dict[str, Event] = {}
|
|
56
|
+
self.pattern_items: dict[str, Event] = {}
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def _is_pattern(target: str) -> bool:
|
|
60
|
+
return any(char in target for char in "*?[]")
|
|
61
|
+
|
|
62
|
+
def _bucket(self, target: str) -> dict[str, Event]:
|
|
63
|
+
return self.pattern_items if self._is_pattern(target) else self.exact_items
|
|
66
64
|
|
|
67
65
|
def set(self, target: str, *args: Any):
|
|
68
|
-
if not self.
|
|
66
|
+
if not self.exact_items and not self.pattern_items:
|
|
69
67
|
return
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
exact_event = self.exact_items.get(target)
|
|
70
|
+
if exact_event is not None:
|
|
71
|
+
try:
|
|
72
|
+
exact_event.set(target, *args)
|
|
73
|
+
except Exception:
|
|
74
|
+
logger.error(traceback.format_exc())
|
|
74
75
|
|
|
76
|
+
for key, event in tuple(self.pattern_items.items()):
|
|
75
77
|
try:
|
|
76
|
-
|
|
78
|
+
if fnmatch.fnmatch(target, key):
|
|
79
|
+
event.set(target, *args)
|
|
77
80
|
except Exception:
|
|
78
81
|
logger.error(traceback.format_exc())
|
|
79
82
|
|
|
80
83
|
def __add__(self, item: list):
|
|
81
|
-
self.
|
|
84
|
+
self._bucket(item[0]).setdefault(item[0], Event()).__iadd__(item[1])
|
|
82
85
|
return self
|
|
83
86
|
|
|
84
87
|
def __sub__(self, item: list):
|
|
85
|
-
self.
|
|
88
|
+
self._bucket(item[0]).setdefault(item[0], Event()).__isub__(item[1])
|
|
86
89
|
return self
|
|
87
90
|
|
|
88
91
|
def __iadd__(self, item: list):
|
|
89
|
-
self.
|
|
92
|
+
self._bucket(item[0]).setdefault(item[0], Event()).__iadd__(item[1])
|
|
90
93
|
return self
|
|
91
94
|
|
|
92
95
|
def __isub__(self, item: list):
|
|
93
|
-
self.
|
|
96
|
+
self._bucket(item[0]).setdefault(item[0], Event()).__isub__(item[1])
|
|
94
97
|
return self
|
|
95
98
|
|
|
96
99
|
def __len__(self) -> int:
|
|
97
|
-
return len(self.
|
|
100
|
+
return len(self.exact_items) + len(self.pattern_items)
|
|
@@ -7,19 +7,110 @@ import os
|
|
|
7
7
|
import sys
|
|
8
8
|
import threading
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from ctypes import wintypes
|
|
10
11
|
|
|
11
12
|
from PySide6.QtCore import QEvent, QObject, QPoint, QRect, Qt, QTimer, QUrl, Signal, Slot
|
|
12
13
|
from PySide6.QtGui import QColor, QCursor, QDesktopServices, QIcon, QKeySequence, QShortcut
|
|
13
14
|
from PySide6.QtWebChannel import QWebChannel
|
|
14
|
-
from PySide6.QtWebEngineCore import QWebEnginePage, QWebEngineSettings
|
|
15
|
+
from PySide6.QtWebEngineCore import QWebEngineContextMenuRequest, QWebEnginePage, QWebEngineSettings
|
|
15
16
|
from PySide6.QtWebEngineWidgets import QWebEngineView
|
|
16
17
|
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget
|
|
17
18
|
import win32api
|
|
18
19
|
import win32con
|
|
19
20
|
import win32gui
|
|
21
|
+
import win32gui_struct
|
|
20
22
|
|
|
21
23
|
logger = logging.getLogger("pywebwinui3.qt")
|
|
22
24
|
|
|
25
|
+
_kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
26
|
+
_uxtheme = ctypes.WinDLL("uxtheme", use_last_error=True)
|
|
27
|
+
_get_proc_address = _kernel32.GetProcAddress
|
|
28
|
+
_get_proc_address.restype = ctypes.c_void_p
|
|
29
|
+
_get_proc_address.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_ordinal_proc(module, ordinal: int, restype, argtypes):
|
|
33
|
+
address = _get_proc_address(module._handle, ctypes.c_void_p(ordinal))
|
|
34
|
+
if not address:
|
|
35
|
+
return None
|
|
36
|
+
return ctypes.WINFUNCTYPE(restype, *argtypes)(address)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_set_preferred_app_mode = _get_ordinal_proc(_uxtheme, 135, ctypes.c_int, [ctypes.c_int])
|
|
40
|
+
_flush_menu_themes = _get_ordinal_proc(_uxtheme, 136, None, [])
|
|
41
|
+
_allow_dark_mode_for_window = _get_ordinal_proc(_uxtheme, 133, wintypes.BOOL, [wintypes.HWND, wintypes.BOOL])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _merge_chromium_flags(*flags: str):
|
|
45
|
+
current = os.environ.get("QTWEBENGINE_CHROMIUM_FLAGS", "").strip()
|
|
46
|
+
parts = [part for part in current.split() if part]
|
|
47
|
+
for flag in flags:
|
|
48
|
+
if flag not in parts:
|
|
49
|
+
parts.append(flag)
|
|
50
|
+
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = " ".join(parts)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _configure_webengine_gpu():
|
|
54
|
+
_merge_chromium_flags(
|
|
55
|
+
"--enable-gpu-rasterization",
|
|
56
|
+
"--enable-zero-copy",
|
|
57
|
+
"--enable-native-gpu-memory-buffers",
|
|
58
|
+
"--enable-oop-rasterization",
|
|
59
|
+
"--ignore-gpu-blocklist",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _apply_native_menu_theme(dark: bool, hwnd: int | None = None):
|
|
64
|
+
if _set_preferred_app_mode is not None:
|
|
65
|
+
_set_preferred_app_mode(2 if dark else 3)
|
|
66
|
+
if hwnd is not None and _allow_dark_mode_for_window is not None:
|
|
67
|
+
_allow_dark_mode_for_window(hwnd, bool(dark))
|
|
68
|
+
if _flush_menu_themes is not None:
|
|
69
|
+
_flush_menu_themes()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _open_external_url(url: str | QUrl) -> bool:
|
|
73
|
+
raw = url if isinstance(url, str) else url.toString()
|
|
74
|
+
if not raw:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
parsed = QUrl(raw)
|
|
78
|
+
if parsed.isValid() and QDesktopServices.openUrl(parsed):
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
if sys.platform.startswith("win"):
|
|
82
|
+
try:
|
|
83
|
+
os.startfile(raw)
|
|
84
|
+
return True
|
|
85
|
+
except OSError:
|
|
86
|
+
logger.debug("Fallback shell open failed for %s", raw, exc_info=True)
|
|
87
|
+
|
|
88
|
+
fallback = QUrl.fromUserInput(raw)
|
|
89
|
+
return fallback.isValid() and QDesktopServices.openUrl(fallback)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
_ANCHOR_INTERCEPT_SCRIPT = """
|
|
93
|
+
(() => {
|
|
94
|
+
if (window.__pywebwinui3_external_anchor_hook__) return;
|
|
95
|
+
window.__pywebwinui3_external_anchor_hook__ = true;
|
|
96
|
+
document.addEventListener('click', (event) => {
|
|
97
|
+
const anchor = event.target instanceof Element ? event.target.closest('a[href]') : null;
|
|
98
|
+
if (!anchor) return;
|
|
99
|
+
const href = anchor.getAttribute('href') || '';
|
|
100
|
+
if (!href || href.startsWith('#')) return;
|
|
101
|
+
if (/^(about|data|file|qrc):/i.test(href)) return;
|
|
102
|
+
if (!/^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(href)) return;
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
event.stopPropagation();
|
|
105
|
+
if (window.desktop?.api?.openExternal) {
|
|
106
|
+
window.desktop.api.openExternal(href);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
window.open(href, anchor.getAttribute('target') || '_blank');
|
|
110
|
+
}, true);
|
|
111
|
+
})();
|
|
112
|
+
"""
|
|
113
|
+
|
|
23
114
|
class MARGINS(ctypes.Structure):
|
|
24
115
|
_fields_ = [
|
|
25
116
|
("cxLeftWidth", ctypes.c_int),
|
|
@@ -42,7 +133,7 @@ class ExternalAwarePage(QWebEnginePage):
|
|
|
42
133
|
and navigation_type == QWebEnginePage.NavigationTypeLinkClicked
|
|
43
134
|
and url.scheme() not in {"about", "data", "file", "qrc"}
|
|
44
135
|
):
|
|
45
|
-
|
|
136
|
+
_open_external_url(url)
|
|
46
137
|
return False
|
|
47
138
|
return super().acceptNavigationRequest(url, navigation_type, is_main_frame)
|
|
48
139
|
|
|
@@ -50,7 +141,7 @@ class ExternalAwarePage(QWebEnginePage):
|
|
|
50
141
|
page = QWebEnginePage(self.profile(), self)
|
|
51
142
|
|
|
52
143
|
def open_and_cleanup(url):
|
|
53
|
-
|
|
144
|
+
_open_external_url(url)
|
|
54
145
|
page.deleteLater()
|
|
55
146
|
|
|
56
147
|
page.urlChanged.connect(open_and_cleanup)
|
|
@@ -60,6 +151,16 @@ class ExternalAwarePage(QWebEnginePage):
|
|
|
60
151
|
class FramelessWindow(QMainWindow):
|
|
61
152
|
closed = Signal()
|
|
62
153
|
page_loaded = Signal(bool)
|
|
154
|
+
WEB_MENU_BACK = 0x5001
|
|
155
|
+
WEB_MENU_FORWARD = 0x5002
|
|
156
|
+
WEB_MENU_RELOAD = 0x5003
|
|
157
|
+
WEB_MENU_UNDO = 0x5010
|
|
158
|
+
WEB_MENU_REDO = 0x5011
|
|
159
|
+
WEB_MENU_CUT = 0x5012
|
|
160
|
+
WEB_MENU_COPY = 0x5013
|
|
161
|
+
WEB_MENU_PASTE = 0x5014
|
|
162
|
+
WEB_MENU_SELECT_ALL = 0x5015
|
|
163
|
+
WINDOW_MENU_PIN = 0x5020
|
|
63
164
|
|
|
64
165
|
EDGE_MAP = {
|
|
65
166
|
"left": (Qt.LeftEdge, win32con.HTLEFT),
|
|
@@ -116,6 +217,12 @@ class FramelessWindow(QMainWindow):
|
|
|
116
217
|
settings.setAttribute(QWebEngineSettings.LocalContentCanAccessFileUrls, True)
|
|
117
218
|
settings.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
|
|
118
219
|
settings.setAttribute(QWebEngineSettings.JavascriptCanAccessClipboard, True)
|
|
220
|
+
for attribute_name in ("WebGLEnabled", "Accelerated2dCanvasEnabled"):
|
|
221
|
+
attribute = getattr(QWebEngineSettings, attribute_name, None)
|
|
222
|
+
if attribute is None and hasattr(QWebEngineSettings, "WebAttribute"):
|
|
223
|
+
attribute = getattr(QWebEngineSettings.WebAttribute, attribute_name, None)
|
|
224
|
+
if attribute is not None:
|
|
225
|
+
settings.setAttribute(attribute, True)
|
|
119
226
|
if debug:
|
|
120
227
|
developer_extras = getattr(QWebEngineSettings, "DeveloperExtrasEnabled", None)
|
|
121
228
|
if developer_extras is None and hasattr(QWebEngineSettings, "WebAttribute"):
|
|
@@ -130,6 +237,8 @@ class FramelessWindow(QMainWindow):
|
|
|
130
237
|
self.view.setPage(self.page)
|
|
131
238
|
self.setCentralWidget(self.view)
|
|
132
239
|
self.view.setMouseTracking(True)
|
|
240
|
+
self.view.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
241
|
+
self.view.customContextMenuRequested.connect(self._show_context_menu)
|
|
133
242
|
self.page.loadFinished.connect(self._handle_load_finished)
|
|
134
243
|
QApplication.instance().installEventFilter(self)
|
|
135
244
|
self._sync_view_geometry()
|
|
@@ -162,11 +271,27 @@ class FramelessWindow(QMainWindow):
|
|
|
162
271
|
if app is not None:
|
|
163
272
|
app.removeEventFilter(self)
|
|
164
273
|
self._set_resize_cursor(None)
|
|
274
|
+
self.hide()
|
|
165
275
|
if self._debug_tools_view is not None:
|
|
166
276
|
self._debug_tools_view.close()
|
|
167
|
-
self.closed.emit
|
|
277
|
+
QTimer.singleShot(0, self.closed.emit)
|
|
168
278
|
super().closeEvent(event)
|
|
169
279
|
|
|
280
|
+
def _maximize_native(self):
|
|
281
|
+
self._ensure_native_frame()
|
|
282
|
+
win32gui.ShowWindow(self._hwnd(), win32con.SW_MAXIMIZE)
|
|
283
|
+
|
|
284
|
+
def _restore_native(self):
|
|
285
|
+
self._ensure_native_frame()
|
|
286
|
+
win32gui.ShowWindow(self._hwnd(), win32con.SW_RESTORE)
|
|
287
|
+
|
|
288
|
+
def _start_native_move(self):
|
|
289
|
+
handle = self.windowHandle()
|
|
290
|
+
if handle is not None and hasattr(handle, "startSystemMove") and handle.startSystemMove():
|
|
291
|
+
return
|
|
292
|
+
win32gui.ReleaseCapture()
|
|
293
|
+
win32api.SendMessage(self._hwnd(), win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
|
|
294
|
+
|
|
170
295
|
def _hwnd(self) -> int:
|
|
171
296
|
return int(self.winId())
|
|
172
297
|
|
|
@@ -224,6 +349,7 @@ class FramelessWindow(QMainWindow):
|
|
|
224
349
|
| win32con.SWP_NOACTIVATE
|
|
225
350
|
| win32con.SWP_FRAMECHANGED,
|
|
226
351
|
)
|
|
352
|
+
_apply_native_menu_theme(self._resolved_theme() == "dark", hwnd)
|
|
227
353
|
self._native_frame_ready = True
|
|
228
354
|
|
|
229
355
|
def _refresh_resize_border(self):
|
|
@@ -240,6 +366,11 @@ class FramelessWindow(QMainWindow):
|
|
|
240
366
|
return QColor("#f3f3f3") if self._resolved_theme() == "light" else QColor("#202020")
|
|
241
367
|
|
|
242
368
|
def _apply_window_background(self):
|
|
369
|
+
dark = self._resolved_theme() == "dark"
|
|
370
|
+
if self._native_frame_ready:
|
|
371
|
+
_apply_native_menu_theme(dark, self._hwnd())
|
|
372
|
+
else:
|
|
373
|
+
_apply_native_menu_theme(dark)
|
|
243
374
|
color = self._background_color()
|
|
244
375
|
self.page.setBackgroundColor(color)
|
|
245
376
|
self.setStyleSheet(f"background-color: {color.name()};")
|
|
@@ -248,6 +379,7 @@ class FramelessWindow(QMainWindow):
|
|
|
248
379
|
|
|
249
380
|
def _handle_load_finished(self, _ok: bool):
|
|
250
381
|
self._apply_window_background()
|
|
382
|
+
self.page.runJavaScript(_ANCHOR_INTERCEPT_SCRIPT)
|
|
251
383
|
self.page_loaded.emit(_ok)
|
|
252
384
|
|
|
253
385
|
def _sync_view_geometry(self):
|
|
@@ -256,6 +388,76 @@ class FramelessWindow(QMainWindow):
|
|
|
256
388
|
self.view.setGeometry(rect)
|
|
257
389
|
self.view.updateGeometry()
|
|
258
390
|
|
|
391
|
+
def _show_context_menu(self, pos: QPoint):
|
|
392
|
+
request = self.view.lastContextMenuRequest()
|
|
393
|
+
if request is not None and request.isContentEditable():
|
|
394
|
+
edit_flags = request.editFlags()
|
|
395
|
+
self._exec_native_menu(
|
|
396
|
+
self.view.mapToGlobal(pos),
|
|
397
|
+
[
|
|
398
|
+
(self.WEB_MENU_UNDO, "Undo", bool(edit_flags & QWebEngineContextMenuRequest.EditFlag.CanUndo), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Undo)),
|
|
399
|
+
(self.WEB_MENU_REDO, "Redo", bool(edit_flags & QWebEngineContextMenuRequest.EditFlag.CanRedo), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Redo)),
|
|
400
|
+
None,
|
|
401
|
+
(self.WEB_MENU_CUT, "Cut", bool(edit_flags & QWebEngineContextMenuRequest.EditFlag.CanCut), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Cut)),
|
|
402
|
+
(self.WEB_MENU_COPY, "Copy", bool(edit_flags & QWebEngineContextMenuRequest.EditFlag.CanCopy), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Copy)),
|
|
403
|
+
(self.WEB_MENU_PASTE, "Paste", bool(edit_flags & QWebEngineContextMenuRequest.EditFlag.CanPaste), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Paste)),
|
|
404
|
+
None,
|
|
405
|
+
(self.WEB_MENU_SELECT_ALL, "Select All", bool(edit_flags & QWebEngineContextMenuRequest.EditFlag.CanSelectAll), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.SelectAll)),
|
|
406
|
+
],
|
|
407
|
+
)
|
|
408
|
+
return
|
|
409
|
+
|
|
410
|
+
self._exec_native_menu(
|
|
411
|
+
self.view.mapToGlobal(pos),
|
|
412
|
+
[
|
|
413
|
+
(self.WEB_MENU_BACK, "Back", self.page.action(QWebEnginePage.WebAction.Back).isEnabled(), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Back)),
|
|
414
|
+
(self.WEB_MENU_FORWARD, "Forward", self.page.action(QWebEnginePage.WebAction.Forward).isEnabled(), False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Forward)),
|
|
415
|
+
(self.WEB_MENU_RELOAD, "Reload", True, False, lambda: self.page.triggerAction(QWebEnginePage.WebAction.Reload)),
|
|
416
|
+
],
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
def _exec_native_menu(self, global_pos: QPoint, items):
|
|
420
|
+
menu = win32gui.CreatePopupMenu()
|
|
421
|
+
callbacks = {}
|
|
422
|
+
try:
|
|
423
|
+
win32gui.SetMenuInfo(menu, win32gui_struct.PackMENUINFO(dwStyle=win32con.MNS_CHECKORBMP))
|
|
424
|
+
for item in items:
|
|
425
|
+
if item is None:
|
|
426
|
+
win32gui.AppendMenu(menu, win32con.MF_SEPARATOR, 0, "")
|
|
427
|
+
continue
|
|
428
|
+
|
|
429
|
+
command_id, label, enabled, checked, callback, *extra = item
|
|
430
|
+
bitmap = extra[0] if extra else None
|
|
431
|
+
flags = win32con.MF_STRING
|
|
432
|
+
if not enabled:
|
|
433
|
+
flags |= win32con.MF_GRAYED
|
|
434
|
+
if checked:
|
|
435
|
+
flags |= win32con.MF_CHECKED
|
|
436
|
+
win32gui.AppendMenu(menu, flags, command_id, label)
|
|
437
|
+
if bitmap is not None:
|
|
438
|
+
packed, _extras = win32gui_struct.PackMENUITEMINFO(hbmpItem=bitmap)
|
|
439
|
+
win32gui.SetMenuItemInfo(menu, command_id, False, packed)
|
|
440
|
+
callbacks[command_id] = callback
|
|
441
|
+
|
|
442
|
+
hwnd = self._hwnd()
|
|
443
|
+
win32gui.SetForegroundWindow(hwnd)
|
|
444
|
+
command = win32gui.TrackPopupMenu(
|
|
445
|
+
menu,
|
|
446
|
+
win32con.TPM_RETURNCMD | win32con.TPM_NONOTIFY | win32con.TPM_RIGHTBUTTON,
|
|
447
|
+
global_pos.x(),
|
|
448
|
+
global_pos.y(),
|
|
449
|
+
0,
|
|
450
|
+
hwnd,
|
|
451
|
+
None,
|
|
452
|
+
)
|
|
453
|
+
win32gui.PostMessage(hwnd, win32con.WM_NULL, 0, 0)
|
|
454
|
+
|
|
455
|
+
callback = callbacks.get(command)
|
|
456
|
+
if command and callback is not None:
|
|
457
|
+
callback()
|
|
458
|
+
finally:
|
|
459
|
+
win32gui.DestroyMenu(menu)
|
|
460
|
+
|
|
259
461
|
def _open_devtools(self):
|
|
260
462
|
if self._debug_tools_view is None:
|
|
261
463
|
self._debug_tools_view = QWebEngineView()
|
|
@@ -284,8 +486,18 @@ class FramelessWindow(QMainWindow):
|
|
|
284
486
|
return watched.window() is self
|
|
285
487
|
return False
|
|
286
488
|
|
|
489
|
+
def _is_native_maximized(self) -> bool:
|
|
490
|
+
try:
|
|
491
|
+
return bool(win32gui.IsZoomed(self._hwnd()))
|
|
492
|
+
except Exception:
|
|
493
|
+
return self.isMaximized()
|
|
494
|
+
|
|
495
|
+
def _is_effectively_maximized(self) -> bool:
|
|
496
|
+
state = self.windowState()
|
|
497
|
+
return bool(state & Qt.WindowMaximized) or self._is_native_maximized()
|
|
498
|
+
|
|
287
499
|
def _edge_from_pos(self, pos: QPoint, size) -> str | None:
|
|
288
|
-
if self.
|
|
500
|
+
if self._is_effectively_maximized():
|
|
289
501
|
return None
|
|
290
502
|
|
|
291
503
|
border = self._resize_border
|
|
@@ -405,6 +617,7 @@ class FramelessWindow(QMainWindow):
|
|
|
405
617
|
return super().eventFilter(watched, event)
|
|
406
618
|
|
|
407
619
|
if self._resize_edge_name is None and not self._belongs_to_window(watched):
|
|
620
|
+
self._set_resize_cursor(None)
|
|
408
621
|
return super().eventFilter(watched, event)
|
|
409
622
|
|
|
410
623
|
global_pos = event.globalPosition().toPoint()
|
|
@@ -466,8 +679,7 @@ class FramelessWindow(QMainWindow):
|
|
|
466
679
|
@Slot()
|
|
467
680
|
def start_window_drag(self):
|
|
468
681
|
self._ensure_native_frame()
|
|
469
|
-
|
|
470
|
-
win32api.SendMessage(self._hwnd(), win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
|
|
682
|
+
self._start_native_move()
|
|
471
683
|
|
|
472
684
|
@Slot(str)
|
|
473
685
|
def start_window_resize(self, edge_name: str):
|
|
@@ -478,18 +690,40 @@ class FramelessWindow(QMainWindow):
|
|
|
478
690
|
|
|
479
691
|
@Slot()
|
|
480
692
|
def toggle_maximize(self):
|
|
481
|
-
if self.
|
|
482
|
-
self.
|
|
693
|
+
if self._is_effectively_maximized():
|
|
694
|
+
self._restore_native()
|
|
483
695
|
return
|
|
484
|
-
self.
|
|
696
|
+
self._maximize_native()
|
|
697
|
+
|
|
698
|
+
@Slot()
|
|
699
|
+
def show_window_menu(self):
|
|
700
|
+
pinned = bool(self.api._main.values.get("system_pin"))
|
|
701
|
+
maximized = self._is_effectively_maximized()
|
|
702
|
+
self._exec_native_menu(
|
|
703
|
+
QCursor.pos(),
|
|
704
|
+
[
|
|
705
|
+
(self.WINDOW_MENU_PIN, "Pin", True, pinned, lambda: self.api._main.pin(not pinned)),
|
|
706
|
+
(win32con.SC_MINIMIZE, "Minimize", True, False, self.minimize, win32con.HBMMENU_POPUP_MINIMIZE),
|
|
707
|
+
(
|
|
708
|
+
win32con.SC_RESTORE if maximized else win32con.SC_MAXIMIZE,
|
|
709
|
+
"Restore" if maximized else "Maximize",
|
|
710
|
+
True,
|
|
711
|
+
False,
|
|
712
|
+
self.toggle_maximize,
|
|
713
|
+
win32con.HBMMENU_POPUP_RESTORE if maximized else win32con.HBMMENU_POPUP_MAXIMIZE,
|
|
714
|
+
),
|
|
715
|
+
None,
|
|
716
|
+
(win32con.SC_CLOSE, "Close", True, False, self.close_window, win32con.HBMMENU_POPUP_CLOSE),
|
|
717
|
+
],
|
|
718
|
+
)
|
|
485
719
|
|
|
486
720
|
@Slot(int, int)
|
|
487
721
|
def apply_window_size(self, width: int, height: int):
|
|
488
722
|
width = max(self.minimumWidth(), int(width))
|
|
489
723
|
height = max(self.minimumHeight(), int(height))
|
|
490
724
|
|
|
491
|
-
if self.
|
|
492
|
-
self.
|
|
725
|
+
if self._is_effectively_maximized():
|
|
726
|
+
self._restore_native()
|
|
493
727
|
|
|
494
728
|
if self.width() == width and self.height() == height:
|
|
495
729
|
return
|
|
@@ -504,7 +738,7 @@ class FramelessWindow(QMainWindow):
|
|
|
504
738
|
return
|
|
505
739
|
|
|
506
740
|
self.setMinimumSize(width, height)
|
|
507
|
-
if not self.
|
|
741
|
+
if not self._is_effectively_maximized():
|
|
508
742
|
self.resize(max(self.width(), width), max(self.height(), height))
|
|
509
743
|
|
|
510
744
|
class WebviewAPI(QObject):
|
|
@@ -518,6 +752,7 @@ class WebviewAPI(QObject):
|
|
|
518
752
|
start_drag_requested = Signal()
|
|
519
753
|
start_resize_requested = Signal(str)
|
|
520
754
|
toggle_maximize_requested = Signal()
|
|
755
|
+
show_window_menu_requested = Signal()
|
|
521
756
|
|
|
522
757
|
def __init__(self, main_window, title: str, icon: str | None):
|
|
523
758
|
super().__init__()
|
|
@@ -544,13 +779,20 @@ class WebviewAPI(QObject):
|
|
|
544
779
|
return
|
|
545
780
|
|
|
546
781
|
self._debug_mode = debug
|
|
782
|
+
_configure_webengine_gpu()
|
|
547
783
|
app = QApplication.instance()
|
|
548
784
|
if app is None:
|
|
785
|
+
if hasattr(Qt, "AA_UseDesktopOpenGL"):
|
|
786
|
+
QApplication.setAttribute(Qt.AA_UseDesktopOpenGL, True)
|
|
549
787
|
QApplication.setAttribute(Qt.AA_ShareOpenGLContexts, True)
|
|
550
788
|
app = QApplication(sys.argv)
|
|
551
789
|
self._owns_app = True
|
|
552
790
|
|
|
553
791
|
self._app = app
|
|
792
|
+
resolved_theme = self._main.values.get("system_theme") or "system"
|
|
793
|
+
if resolved_theme == "system":
|
|
794
|
+
resolved_theme = self._main.values.get("system_theme_resolved") or "dark"
|
|
795
|
+
_apply_native_menu_theme(resolved_theme == "dark")
|
|
554
796
|
|
|
555
797
|
icon_path = self._main.resolve_path(self._icon)
|
|
556
798
|
page_override = os.environ.get("PYWEBWINUI3_PAGE_OVERRIDE")
|
|
@@ -567,6 +809,7 @@ class WebviewAPI(QObject):
|
|
|
567
809
|
self.start_drag_requested.connect(self._window.start_window_drag)
|
|
568
810
|
self.start_resize_requested.connect(self._window.start_window_resize)
|
|
569
811
|
self.toggle_maximize_requested.connect(self._window.toggle_maximize)
|
|
812
|
+
self.show_window_menu_requested.connect(self._window.show_window_menu)
|
|
570
813
|
|
|
571
814
|
self._main.sync_window_size(self._window.width(), self._window.height(), sync=False)
|
|
572
815
|
|
|
@@ -743,9 +986,13 @@ class WebviewAPI(QObject):
|
|
|
743
986
|
def toggleMaximize(self):
|
|
744
987
|
self.toggle_maximize_requested.emit()
|
|
745
988
|
|
|
989
|
+
@Slot()
|
|
990
|
+
def showWindowMenu(self):
|
|
991
|
+
self.show_window_menu_requested.emit()
|
|
992
|
+
|
|
746
993
|
@Slot(str)
|
|
747
994
|
def openExternal(self, url: str):
|
|
748
|
-
|
|
995
|
+
_open_external_url(url)
|
|
749
996
|
|
|
750
997
|
@Slot(str, result=str)
|
|
751
998
|
def resolveResource(self, value: str):
|
|
@@ -40,6 +40,15 @@ class SyncDict(dict):
|
|
|
40
40
|
self.event = event or PathEvent()
|
|
41
41
|
self.sync = sync
|
|
42
42
|
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _is_same_value(before:Any, after:Any):
|
|
45
|
+
if before is after:
|
|
46
|
+
return True
|
|
47
|
+
try:
|
|
48
|
+
return before == after
|
|
49
|
+
except Exception:
|
|
50
|
+
return False
|
|
51
|
+
|
|
43
52
|
def _sync(self, key, before, after, sync):
|
|
44
53
|
if sync and self.sync:
|
|
45
54
|
self.sync(key, after)
|
|
@@ -47,12 +56,14 @@ class SyncDict(dict):
|
|
|
47
56
|
|
|
48
57
|
def __setitem__(self, key:str, value:Any, sync=True):
|
|
49
58
|
before = self.get(key, None)
|
|
59
|
+
if self._is_same_value(before, value):
|
|
60
|
+
return value
|
|
50
61
|
super().__setitem__(key, value)
|
|
51
62
|
self._sync(key, before, value, sync)
|
|
63
|
+
return value
|
|
52
64
|
|
|
53
65
|
def set(self, key:str, value:Any, sync=True):
|
|
54
|
-
self.__setitem__(key, value, sync)
|
|
55
|
-
return value
|
|
66
|
+
return self.__setitem__(key, value, sync)
|
|
56
67
|
|
|
57
68
|
def append(self, key:str, value:Any, sync=True):
|
|
58
69
|
before = list(self.get(key,[]))
|