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.
Files changed (134) hide show
  1. {pywebwinui3-1.0.0b43/PyWebWinUI3.egg-info → pywebwinui3-1.0.0b45}/PKG-INFO +1 -1
  2. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45/PyWebWinUI3.egg-info}/PKG-INFO +1 -1
  3. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/SOURCES.txt +6 -6
  4. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/__init__.py +1 -1
  5. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/core.py +24 -2
  6. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/event.py +25 -22
  7. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/qt.py +261 -14
  8. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/util.py +13 -2
  9. 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
  10. pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/chunks/BprPI5v6.js +1 -0
  11. 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
  12. pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/entry/start.BDCzTRGD.js +1 -0
  13. 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
  14. pywebwinui3-1.0.0b45/pywebwinui3/web/_app/immutable/nodes/2.aKOPDyc3.js +88 -0
  15. pywebwinui3-1.0.0b45/pywebwinui3/web/_app/version.json +1 -0
  16. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/index.html +8 -8
  17. pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/chunks/TzBZ48H8.js +0 -1
  18. pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/entry/start.PpAqhk-4.js +0 -1
  19. pywebwinui3-1.0.0b43/pywebwinui3/web/_app/immutable/nodes/2.D8H1CTiv.js +0 -88
  20. pywebwinui3-1.0.0b43/pywebwinui3/web/_app/version.json +0 -1
  21. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/LICENSE +0 -0
  22. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/MANIFEST.in +0 -0
  23. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/NOTICE +0 -0
  24. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/dependency_links.txt +0 -0
  25. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/requires.txt +0 -0
  26. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/PyWebWinUI3.egg-info/top_level.txt +0 -0
  27. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/README.md +0 -0
  28. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/type.py +0 -0
  29. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.0.woff2 +0 -0
  30. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.1.woff2 +0 -0
  31. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.10.woff2 +0 -0
  32. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.11.woff2 +0 -0
  33. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.12.woff2 +0 -0
  34. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.13.woff2 +0 -0
  35. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.14.woff2 +0 -0
  36. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.15.woff2 +0 -0
  37. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.16.woff2 +0 -0
  38. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.17.woff2 +0 -0
  39. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.18.woff2 +0 -0
  40. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.19.woff2 +0 -0
  41. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.2.woff2 +0 -0
  42. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.20.woff2 +0 -0
  43. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.21.woff2 +0 -0
  44. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.22.woff2 +0 -0
  45. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.23.woff2 +0 -0
  46. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.24.woff2 +0 -0
  47. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.25.woff2 +0 -0
  48. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.26.woff2 +0 -0
  49. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.27.woff2 +0 -0
  50. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.28.woff2 +0 -0
  51. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.29.woff2 +0 -0
  52. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.3.woff2 +0 -0
  53. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.30.woff2 +0 -0
  54. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.31.woff2 +0 -0
  55. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.32.woff2 +0 -0
  56. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.33.woff2 +0 -0
  57. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.34.woff2 +0 -0
  58. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.35.woff2 +0 -0
  59. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.36.woff2 +0 -0
  60. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.37.woff2 +0 -0
  61. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.38.woff2 +0 -0
  62. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.39.woff2 +0 -0
  63. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.4.woff2 +0 -0
  64. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.40.woff2 +0 -0
  65. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.41.woff2 +0 -0
  66. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.42.woff2 +0 -0
  67. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.43.woff2 +0 -0
  68. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.44.woff2 +0 -0
  69. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.45.woff2 +0 -0
  70. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.46.woff2 +0 -0
  71. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.47.woff2 +0 -0
  72. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.48.woff2 +0 -0
  73. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.49.woff2 +0 -0
  74. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.5.woff2 +0 -0
  75. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.50.woff2 +0 -0
  76. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.51.woff2 +0 -0
  77. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.52.woff2 +0 -0
  78. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.53.woff2 +0 -0
  79. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.54.woff2 +0 -0
  80. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.55.woff2 +0 -0
  81. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.56.woff2 +0 -0
  82. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.57.woff2 +0 -0
  83. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.58.woff2 +0 -0
  84. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.59.woff2 +0 -0
  85. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.6.woff2 +0 -0
  86. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.60.woff2 +0 -0
  87. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.61.woff2 +0 -0
  88. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.62.woff2 +0 -0
  89. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.63.woff2 +0 -0
  90. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.64.woff2 +0 -0
  91. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.65.woff2 +0 -0
  92. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.66.woff2 +0 -0
  93. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.67.woff2 +0 -0
  94. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.68.woff2 +0 -0
  95. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.69.woff2 +0 -0
  96. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.7.woff2 +0 -0
  97. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.70.woff2 +0 -0
  98. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.71.woff2 +0 -0
  99. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.72.woff2 +0 -0
  100. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.73.woff2 +0 -0
  101. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.74.woff2 +0 -0
  102. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.75.woff2 +0 -0
  103. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.76.woff2 +0 -0
  104. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.77.woff2 +0 -0
  105. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.78.woff2 +0 -0
  106. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.79.woff2 +0 -0
  107. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.8.woff2 +0 -0
  108. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.80.woff2 +0 -0
  109. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.81.woff2 +0 -0
  110. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.82.woff2 +0 -0
  111. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.83.woff2 +0 -0
  112. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.84.woff2 +0 -0
  113. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.85.woff2 +0 -0
  114. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.86.woff2 +0 -0
  115. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.87.woff2 +0 -0
  116. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.88.woff2 +0 -0
  117. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.89.woff2 +0 -0
  118. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.9.woff2 +0 -0
  119. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.90.woff2 +0 -0
  120. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/PretendardVariable.subset.91.woff2 +0 -0
  121. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/Pretendard/Variable-dynamic-subset.min.css +0 -0
  122. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/SegoeFluentIcons.ttf +0 -0
  123. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/env.js +0 -0
  124. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/C5fctQ8t.js +0 -0
  125. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/DuPwkB53.js +0 -0
  126. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/KfJIcf5l.js +0 -0
  127. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/jnH-TXHS.js +0 -0
  128. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/ln01AzD2.js +0 -0
  129. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/oIQI43DZ.js +0 -0
  130. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/chunks/wKeSOqcH.js +0 -0
  131. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/pywebwinui3/web/_app/immutable/nodes/0.DYoNlsbe.js +0 -0
  132. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/requirements.txt +0 -0
  133. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/setup.cfg +0 -0
  134. {pywebwinui3-1.0.0b43 → pywebwinui3-1.0.0b45}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyWebWinUI3
3
- Version: 1.0.0b43
3
+ Version: 1.0.0b45
4
4
  Summary: Create modern WinUI3-style desktop UIs in Python effortlessly using PySide6 and QtWebEngine.
5
5
  Home-page: https://github.com/Haruna5718/PyWebWinUI3
6
6
  Author: Haruna5718
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyWebWinUI3
3
- Version: 1.0.0b43
3
+ Version: 1.0.0b45
4
4
  Summary: Create modern WinUI3-style desktop UIs in Python effortlessly using PySide6 and QtWebEngine.
5
5
  Home-page: https://github.com/Haruna5718/PyWebWinUI3
6
6
  Author: Haruna5718
@@ -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.BF6evWMx.css
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.UoyHjfbP.js
125
- pywebwinui3/web/_app/immutable/entry/start.PpAqhk-4.js
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.DrRW4ELJ.js
128
- pywebwinui3/web/_app/immutable/nodes/2.D8H1CTiv.js
127
+ pywebwinui3/web/_app/immutable/nodes/1.DJOepXLS.js
128
+ pywebwinui3/web/_app/immutable/nodes/2.aKOPDyc3.js
@@ -1,3 +1,3 @@
1
1
  __all__ = ["core", "event", "util", "type"]
2
2
 
3
- __version__ = '1.0.0-beta.43'
3
+ __version__ = '1.0.0-beta.45'
@@ -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
- _EVENT_EXECUTOR.submit(_call_listener, func, args)
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.items: dict[str, Event] = {}
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.items:
66
+ if not self.exact_items and not self.pattern_items:
69
67
  return
70
68
 
71
- for key, event in tuple(self.items.items()):
72
- if not fnmatch.fnmatch(target, key):
73
- continue
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
- event.set(target, *args)
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.items.setdefault(item[0], Event()).__iadd__(item[1])
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.items.setdefault(item[0], Event()).__isub__(item[1])
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.items.setdefault(item[0], Event()).__iadd__(item[1])
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.items.setdefault(item[0], Event()).__isub__(item[1])
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.items)
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
- QDesktopServices.openUrl(url)
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
- QDesktopServices.openUrl(url)
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.isMaximized():
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
- win32gui.ReleaseCapture()
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.isMaximized():
482
- self.showNormal()
693
+ if self._is_effectively_maximized():
694
+ self._restore_native()
483
695
  return
484
- self.showMaximized()
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.isMaximized():
492
- self.showNormal()
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.isMaximized():
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
- QDesktopServices.openUrl(QUrl(url))
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,[]))