frontengine 1.0.69__tar.gz → 1.0.71__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 (270) hide show
  1. {frontengine-1.0.69 → frontengine-1.0.71}/PKG-INFO +1 -1
  2. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/replica/replica_widget.py +22 -3
  3. frontengine-1.0.71/frontengine/show/shortcuts/shortcut_sheet.py +152 -0
  4. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/hotkey_settings_dialog.py +15 -0
  5. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/window_replica_dialog.py +28 -7
  6. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/main_ui.py +30 -1
  7. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/menu/help_menu.py +12 -0
  8. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/user_setting/user_setting_file.py +1 -0
  9. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/english.py +10 -0
  10. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/france.py +10 -0
  11. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/germany.py +10 -0
  12. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/italy.py +10 -0
  13. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/russian.py +10 -0
  14. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/simplified_chinese.py +10 -0
  15. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/traditional_chinese.py +10 -0
  16. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/window_replica/dwm_thumbnail.py +45 -2
  17. frontengine-1.0.71/frontengine/utils/workshop/__init__.py +0 -0
  18. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine.egg-info/PKG-INFO +1 -1
  19. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine.egg-info/SOURCES.txt +2 -0
  20. {frontengine-1.0.69 → frontengine-1.0.71}/pyproject.toml +1 -1
  21. {frontengine-1.0.69 → frontengine-1.0.71}/LICENSE +0 -0
  22. {frontengine-1.0.69 → frontengine-1.0.71}/README.md +0 -0
  23. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/__init__.py +0 -0
  24. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/__main__.py +0 -0
  25. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/__init__.py +0 -0
  26. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/base_widget.py +0 -0
  27. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/camera/__init__.py +0 -0
  28. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/camera/camera_widget.py +0 -0
  29. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/canvas/__init__.py +0 -0
  30. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/canvas/whiteboard_widget.py +0 -0
  31. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/capture/__init__.py +0 -0
  32. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/capture/region_capture.py +0 -0
  33. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/focus_shield/__init__.py +0 -0
  34. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
  35. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/gif/__init__.py +0 -0
  36. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/gif/paint_gif.py +0 -0
  37. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/image/__init__.py +0 -0
  38. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/image/paint_image.py +0 -0
  39. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/load/__init__.py +0 -0
  40. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/load/load_someone_make_ui.py +0 -0
  41. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/measure/__init__.py +0 -0
  42. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/measure/measure_widget.py +0 -0
  43. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/monitor/__init__.py +0 -0
  44. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/monitor/monitor_widget.py +0 -0
  45. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/notes/__init__.py +0 -0
  46. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/notes/sticky_note_widget.py +0 -0
  47. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/overlay_factory.py +0 -0
  48. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/particle/__init__.py +0 -0
  49. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/particle/particle_ui.py +0 -0
  50. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/pet/__init__.py +0 -0
  51. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/pet/desktop_pet.py +0 -0
  52. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/presentation/__init__.py +0 -0
  53. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/presentation/annotation_overlay.py +0 -0
  54. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/presentation/cursor_effects.py +0 -0
  55. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/presentation/keystroke_display.py +0 -0
  56. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/presentation/magnifier.py +0 -0
  57. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/replica/__init__.py +0 -0
  58. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/scene/__init__.py +0 -0
  59. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  60. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/scene/extend_graphic_view.py +0 -0
  61. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/scene/scene.py +0 -0
  62. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/screen_care/__init__.py +0 -0
  63. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/screen_care/color_vision_widget.py +0 -0
  64. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/screen_care/screen_filter.py +0 -0
  65. {frontengine-1.0.69/frontengine/show/sound_player → frontengine-1.0.71/frontengine/show/shortcuts}/__init__.py +0 -0
  66. {frontengine-1.0.69/frontengine/show/spectrum → frontengine-1.0.71/frontengine/show/sound_player}/__init__.py +0 -0
  67. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/sound_player/sound_effect.py +0 -0
  68. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/sound_player/sound_player.py +0 -0
  69. {frontengine-1.0.69/frontengine/show/text → frontengine-1.0.71/frontengine/show/spectrum}/__init__.py +0 -0
  70. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  71. {frontengine-1.0.69/frontengine/show/toast → frontengine-1.0.71/frontengine/show/text}/__init__.py +0 -0
  72. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/text/draw_text.py +0 -0
  73. {frontengine-1.0.69/frontengine/show/video → frontengine-1.0.71/frontengine/show/toast}/__init__.py +0 -0
  74. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/toast/toast_widget.py +0 -0
  75. {frontengine-1.0.69/frontengine/show/wallpaper → frontengine-1.0.71/frontengine/show/video}/__init__.py +0 -0
  76. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/video/video_player.py +0 -0
  77. {frontengine-1.0.69/frontengine/show/web → frontengine-1.0.71/frontengine/show/wallpaper}/__init__.py +0 -0
  78. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  79. {frontengine-1.0.69/frontengine/system_tray → frontengine-1.0.71/frontengine/show/web}/__init__.py +0 -0
  80. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/web/webview.py +0 -0
  81. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/show/window_helpers.py +0 -0
  82. {frontengine-1.0.69/frontengine/ui → frontengine-1.0.71/frontengine/system_tray}/__init__.py +0 -0
  83. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/system_tray/extend_system_tray.py +0 -0
  84. {frontengine-1.0.69/frontengine/ui/color → frontengine-1.0.71/frontengine/ui}/__init__.py +0 -0
  85. {frontengine-1.0.69/frontengine/ui/dialog → frontengine-1.0.71/frontengine/ui/color}/__init__.py +0 -0
  86. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/color/global_color.py +0 -0
  87. {frontengine-1.0.69/frontengine/ui/menu → frontengine-1.0.71/frontengine/ui/dialog}/__init__.py +0 -0
  88. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  89. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  90. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
  91. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/how_to_use_dialog.py +0 -0
  92. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  93. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/remote_control_dialog.py +0 -0
  94. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/screen_privacy_dialog.py +0 -0
  95. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
  96. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/screensaver_dialog.py +0 -0
  97. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/signage_dialog.py +0 -0
  98. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  99. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
  100. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
  101. {frontengine-1.0.69/frontengine/ui/nav → frontengine-1.0.71/frontengine/ui/menu}/__init__.py +0 -0
  102. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/menu/how_to_menu.py +0 -0
  103. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/menu/language_menu.py +0 -0
  104. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/menu/preset_menu.py +0 -0
  105. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/menu/settings_menu.py +0 -0
  106. {frontengine-1.0.69/frontengine/ui/page → frontengine-1.0.71/frontengine/ui/nav}/__init__.py +0 -0
  107. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/nav/sidebar.py +0 -0
  108. {frontengine-1.0.69/frontengine/ui/page/control_center → frontengine-1.0.71/frontengine/ui/page}/__init__.py +0 -0
  109. {frontengine-1.0.69/frontengine/ui/page/focus → frontengine-1.0.71/frontengine/ui/page/control_center}/__init__.py +0 -0
  110. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  111. {frontengine-1.0.69/frontengine/ui/page/gif → frontengine-1.0.71/frontengine/ui/page/focus}/__init__.py +0 -0
  112. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
  113. {frontengine-1.0.69/frontengine/ui/page/image → frontengine-1.0.71/frontengine/ui/page/gif}/__init__.py +0 -0
  114. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  115. {frontengine-1.0.69/frontengine/ui/page/particle → frontengine-1.0.71/frontengine/ui/page/image}/__init__.py +0 -0
  116. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  117. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/layout_kit.py +0 -0
  118. {frontengine-1.0.69/frontengine/ui/page/pet → frontengine-1.0.71/frontengine/ui/page/particle}/__init__.py +0 -0
  119. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  120. {frontengine-1.0.69/frontengine/ui/page/presentation → frontengine-1.0.71/frontengine/ui/page/pet}/__init__.py +0 -0
  121. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
  122. {frontengine-1.0.69/frontengine/ui/page/scene_setting → frontengine-1.0.71/frontengine/ui/page/presentation}/__init__.py +0 -0
  123. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
  124. {frontengine-1.0.69/frontengine/ui/page/scene_setting/scene_page → frontengine-1.0.71/frontengine/ui/page/scene_setting}/__init__.py +0 -0
  125. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  126. {frontengine-1.0.69/frontengine/ui/page/screen_care → frontengine-1.0.71/frontengine/ui/page/scene_setting/scene_page}/__init__.py +0 -0
  127. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  128. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  129. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  130. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  131. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  132. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  133. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  134. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  135. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  136. {frontengine-1.0.69/frontengine/ui/page/sound_player → frontengine-1.0.71/frontengine/ui/page/screen_care}/__init__.py +0 -0
  137. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +0 -0
  138. {frontengine-1.0.69/frontengine/ui/page/text → frontengine-1.0.71/frontengine/ui/page/sound_player}/__init__.py +0 -0
  139. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  140. {frontengine-1.0.69/frontengine/ui/page/tools → frontengine-1.0.71/frontengine/ui/page/text}/__init__.py +0 -0
  141. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  142. {frontengine-1.0.69/frontengine/ui/page/video → frontengine-1.0.71/frontengine/ui/page/tools}/__init__.py +0 -0
  143. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/tools/tools_setting_ui.py +0 -0
  144. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/utils.py +0 -0
  145. {frontengine-1.0.69/frontengine/ui/page/wallpaper → frontengine-1.0.71/frontengine/ui/page/video}/__init__.py +0 -0
  146. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  147. {frontengine-1.0.69/frontengine/ui/page/web → frontengine-1.0.71/frontengine/ui/page/wallpaper}/__init__.py +0 -0
  148. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
  149. {frontengine-1.0.69/frontengine/ui/page/widgets → frontengine-1.0.71/frontengine/ui/page/web}/__init__.py +0 -0
  150. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  151. {frontengine-1.0.69/frontengine/ui/style → frontengine-1.0.71/frontengine/ui/page/widgets}/__init__.py +0 -0
  152. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
  153. {frontengine-1.0.69/frontengine/user_setting → frontengine-1.0.71/frontengine/ui/style}/__init__.py +0 -0
  154. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/ui/style/app_style.py +0 -0
  155. {frontengine-1.0.69/frontengine/utils → frontengine-1.0.71/frontengine/user_setting}/__init__.py +0 -0
  156. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/user_setting/preset_repository.py +0 -0
  157. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/user_setting/scene_setting.py +0 -0
  158. {frontengine-1.0.69/frontengine/utils/app_profile → frontengine-1.0.71/frontengine/utils}/__init__.py +0 -0
  159. {frontengine-1.0.69/frontengine/utils/audio_meter → frontengine-1.0.71/frontengine/utils/app_profile}/__init__.py +0 -0
  160. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  161. {frontengine-1.0.69/frontengine/utils/autostart → frontengine-1.0.71/frontengine/utils/audio_meter}/__init__.py +0 -0
  162. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  163. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
  164. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/audio_meter/microphone_meter.py +0 -0
  165. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  166. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  167. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
  168. {frontengine-1.0.69/frontengine/utils/break_reminder → frontengine-1.0.71/frontengine/utils/autostart}/__init__.py +0 -0
  169. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/autostart/autostart_service.py +0 -0
  170. {frontengine-1.0.69/frontengine/utils/browser → frontengine-1.0.71/frontengine/utils/break_reminder}/__init__.py +0 -0
  171. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  172. {frontengine-1.0.69/frontengine/utils/clipboard → frontengine-1.0.71/frontengine/utils/browser}/__init__.py +0 -0
  173. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/browser/browser.py +0 -0
  174. {frontengine-1.0.69/frontengine/utils/color_vision → frontengine-1.0.71/frontengine/utils/clipboard}/__init__.py +0 -0
  175. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/clipboard/clipboard_history.py +0 -0
  176. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
  177. {frontengine-1.0.69/frontengine/utils/critical_exit → frontengine-1.0.71/frontengine/utils/color_vision}/__init__.py +0 -0
  178. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/color_vision/color_vision.py +0 -0
  179. {frontengine-1.0.69/frontengine/utils/focus_shield → frontengine-1.0.71/frontengine/utils/critical_exit}/__init__.py +0 -0
  180. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  181. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  182. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  183. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/exception/__init__.py +0 -0
  184. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/exception/exception_tags.py +0 -0
  185. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/exception/exceptions.py +0 -0
  186. {frontengine-1.0.69/frontengine/utils/focus_timer → frontengine-1.0.71/frontengine/utils/focus_shield}/__init__.py +0 -0
  187. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  188. {frontengine-1.0.69/frontengine/utils/hotkey → frontengine-1.0.71/frontengine/utils/focus_timer}/__init__.py +0 -0
  189. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  190. {frontengine-1.0.69/frontengine/utils/input_watch → frontengine-1.0.71/frontengine/utils/hotkey}/__init__.py +0 -0
  191. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  192. {frontengine-1.0.69/frontengine/utils/json → frontengine-1.0.71/frontengine/utils/input_watch}/__init__.py +0 -0
  193. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  194. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/input_watch/typing_watch.py +0 -0
  195. {frontengine-1.0.69/frontengine/utils/keep_awake → frontengine-1.0.71/frontengine/utils/json}/__init__.py +0 -0
  196. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/json/json_file.py +0 -0
  197. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/json/json_repository.py +0 -0
  198. {frontengine-1.0.69/frontengine/utils/logging → frontengine-1.0.71/frontengine/utils/keep_awake}/__init__.py +0 -0
  199. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/keep_awake/keep_awake.py +0 -0
  200. {frontengine-1.0.69/frontengine/utils/measure → frontengine-1.0.71/frontengine/utils/logging}/__init__.py +0 -0
  201. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/logging/loggin_instance.py +0 -0
  202. {frontengine-1.0.69/frontengine/utils/midi → frontengine-1.0.71/frontengine/utils/measure}/__init__.py +0 -0
  203. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/measure/measure.py +0 -0
  204. {frontengine-1.0.69/frontengine/utils/multi_language → frontengine-1.0.71/frontengine/utils/midi}/__init__.py +0 -0
  205. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/midi/midi_input.py +0 -0
  206. {frontengine-1.0.69/frontengine/utils/now_playing → frontengine-1.0.71/frontengine/utils/multi_language}/__init__.py +0 -0
  207. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  208. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/multi_language/retranslate.py +0 -0
  209. {frontengine-1.0.69/frontengine/utils/pet_chat → frontengine-1.0.71/frontengine/utils/now_playing}/__init__.py +0 -0
  210. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/now_playing/now_playing.py +0 -0
  211. {frontengine-1.0.69/frontengine/utils/platform_info → frontengine-1.0.71/frontengine/utils/pet_chat}/__init__.py +0 -0
  212. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  213. {frontengine-1.0.69/frontengine/utils/playlist → frontengine-1.0.71/frontengine/utils/platform_info}/__init__.py +0 -0
  214. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/platform_info/platform_info.py +0 -0
  215. {frontengine-1.0.69/frontengine/utils/plugins → frontengine-1.0.71/frontengine/utils/playlist}/__init__.py +0 -0
  216. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/playlist/playlist.py +0 -0
  217. {frontengine-1.0.69/frontengine/utils/power_mode → frontengine-1.0.71/frontengine/utils/plugins}/__init__.py +0 -0
  218. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/plugins/plugin_loader.py +0 -0
  219. {frontengine-1.0.69/frontengine/utils/preset_schedule → frontengine-1.0.71/frontengine/utils/power_mode}/__init__.py +0 -0
  220. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/power_mode/power_mode.py +0 -0
  221. {frontengine-1.0.69/frontengine/utils/recording → frontengine-1.0.71/frontengine/utils/preset_schedule}/__init__.py +0 -0
  222. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  223. {frontengine-1.0.69/frontengine/utils/redirect_manager → frontengine-1.0.71/frontengine/utils/recording}/__init__.py +0 -0
  224. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/recording/frame_recorder.py +0 -0
  225. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/recording/gif_writer.py +0 -0
  226. {frontengine-1.0.69/frontengine/utils/reminder → frontengine-1.0.71/frontengine/utils/redirect_manager}/__init__.py +0 -0
  227. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  228. {frontengine-1.0.69/frontengine/utils/remote → frontengine-1.0.71/frontengine/utils/reminder}/__init__.py +0 -0
  229. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/reminder/reminder_service.py +0 -0
  230. {frontengine-1.0.69/frontengine/utils/screen_privacy → frontengine-1.0.71/frontengine/utils/remote}/__init__.py +0 -0
  231. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/remote/remote_server.py +0 -0
  232. {frontengine-1.0.69/frontengine/utils/screen_text → frontengine-1.0.71/frontengine/utils/screen_privacy}/__init__.py +0 -0
  233. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/screen_privacy/capture_affinity.py +0 -0
  234. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/screen_privacy/share_watch.py +0 -0
  235. {frontengine-1.0.69/frontengine/utils/screensaver → frontengine-1.0.71/frontengine/utils/screen_text}/__init__.py +0 -0
  236. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/screen_text/screen_text_service.py +0 -0
  237. {frontengine-1.0.69/frontengine/utils/signage → frontengine-1.0.71/frontengine/utils/screensaver}/__init__.py +0 -0
  238. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/screensaver/screensaver_service.py +0 -0
  239. {frontengine-1.0.69/frontengine/utils/smart_pause → frontengine-1.0.71/frontengine/utils/signage}/__init__.py +0 -0
  240. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/signage/signage_service.py +0 -0
  241. {frontengine-1.0.69/frontengine/utils/steam → frontengine-1.0.71/frontengine/utils/smart_pause}/__init__.py +0 -0
  242. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  243. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  244. {frontengine-1.0.69/frontengine/utils/system_stats → frontengine-1.0.71/frontengine/utils/steam}/__init__.py +0 -0
  245. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/steam/steam_language.py +0 -0
  246. {frontengine-1.0.69/frontengine/utils/text_source → frontengine-1.0.71/frontengine/utils/system_stats}/__init__.py +0 -0
  247. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/system_stats/system_stats.py +0 -0
  248. {frontengine-1.0.69/frontengine/utils/theme_schedule → frontengine-1.0.71/frontengine/utils/text_source}/__init__.py +0 -0
  249. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/text_source/text_source.py +0 -0
  250. {frontengine-1.0.69/frontengine/utils/usage_tracking → frontengine-1.0.71/frontengine/utils/theme_schedule}/__init__.py +0 -0
  251. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  252. {frontengine-1.0.69/frontengine/utils/virtual_camera → frontengine-1.0.71/frontengine/utils/usage_tracking}/__init__.py +0 -0
  253. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/usage_tracking/usage_service.py +0 -0
  254. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
  255. {frontengine-1.0.69/frontengine/utils/weather → frontengine-1.0.71/frontengine/utils/virtual_camera}/__init__.py +0 -0
  256. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/virtual_camera/camera_feed.py +0 -0
  257. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/virtual_camera/virtual_camera.py +0 -0
  258. {frontengine-1.0.69/frontengine/utils/window_pin → frontengine-1.0.71/frontengine/utils/weather}/__init__.py +0 -0
  259. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/weather/weather_service.py +0 -0
  260. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/web_url.py +0 -0
  261. {frontengine-1.0.69/frontengine/utils/window_replica → frontengine-1.0.71/frontengine/utils/window_pin}/__init__.py +0 -0
  262. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/window_pin/window_layout.py +0 -0
  263. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/window_pin/window_pin.py +0 -0
  264. {frontengine-1.0.69/frontengine/utils/workshop → frontengine-1.0.71/frontengine/utils/window_replica}/__init__.py +0 -0
  265. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine/utils/workshop/workshop_content.py +0 -0
  266. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine.egg-info/dependency_links.txt +0 -0
  267. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine.egg-info/entry_points.txt +0 -0
  268. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine.egg-info/requires.txt +0 -0
  269. {frontengine-1.0.69 → frontengine-1.0.71}/frontengine.egg-info/top_level.txt +0 -0
  270. {frontengine-1.0.69 → frontengine-1.0.71}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.69
3
+ Version: 1.0.71
4
4
  Summary: FrontEngine is use-define frontview or only use like screen saver
5
5
  Author-email: JE-Chen <jechenmailman@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/Intergration-Automation-Testing/FrontEngine
@@ -20,7 +20,7 @@ from PySide6.QtWidgets import QWidget
20
20
 
21
21
  from frontengine.utils.logging.loggin_instance import front_engine_logger
22
22
  from frontengine.utils.window_replica.dwm_thumbnail import (
23
- DEFAULT_REPLICA_WIDTH, MIN_REPLICA_SIZE, DwmThumbnail, available, fit_within,
23
+ DEFAULT_REPLICA_WIDTH, MIN_REPLICA_SIZE, DwmThumbnail, available, crop_rect, fit_within,
24
24
  )
25
25
 
26
26
 
@@ -28,7 +28,8 @@ class WindowReplicaWidget(QWidget):
28
28
  """顯示另一個視窗即時畫面的小視窗。"""
29
29
 
30
30
  def __init__(self, source_handle: int, title: str = "",
31
- opacity_percent: int = 100, parent: Optional[QWidget] = None) -> None:
31
+ opacity_percent: int = 100, parent: Optional[QWidget] = None,
32
+ crop: Optional[tuple] = None) -> None:
32
33
  front_engine_logger.info(f"[WindowReplicaWidget] Init | source={source_handle}")
33
34
  super().__init__(parent)
34
35
  self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
@@ -42,6 +43,10 @@ class WindowReplicaWidget(QWidget):
42
43
  self.source_handle = int(source_handle)
43
44
  self.source_title = title
44
45
  self.opacity_percent = max(1, min(100, int(opacity_percent)))
46
+ # 裁切用比例存:來源視窗被調整大小時,框到的還是同一塊。
47
+ # The crop is kept as fractions so it still points at the same part of
48
+ # the source after that window is resized.
49
+ self.crop = crop
45
50
  self.thumbnail = DwmThumbnail()
46
51
  self._drag_origin: Optional[QPoint] = None
47
52
 
@@ -63,6 +68,14 @@ class WindowReplicaWidget(QWidget):
63
68
  if not self.thumbnail.register(int(self.winId()), self.source_handle):
64
69
  return False
65
70
  source_size = self.thumbnail.source_size()
71
+ # 有裁切的話,長寬比要照框出來的那一塊算,不是照整個視窗算——否則框一條
72
+ # 細長的聊天欄會得到一個又寬又空的複本。
73
+ # With a crop the aspect ratio comes from the cropped part, not the whole
74
+ # window: cropping a narrow chat column would otherwise give a wide,
75
+ # mostly empty replica.
76
+ cropped = crop_rect(source_size, self.crop)
77
+ if cropped is not None:
78
+ source_size = (cropped[2] - cropped[0], cropped[3] - cropped[1])
66
79
  width, height = fit_within(source_size, (DEFAULT_REPLICA_WIDTH,
67
80
  int(DEFAULT_REPLICA_WIDTH * 9 / 16)))
68
81
  self.resize(width, height)
@@ -71,7 +84,13 @@ class WindowReplicaWidget(QWidget):
71
84
 
72
85
  def _refresh(self) -> None:
73
86
  self.thumbnail.update(self.width(), self.height(),
74
- opacity=int(self.opacity_percent * 255 / 100))
87
+ opacity=int(self.opacity_percent * 255 / 100),
88
+ source_rect=crop_rect(self.thumbnail.source_size(), self.crop))
89
+
90
+ def set_crop(self, crop: Optional[tuple]) -> None:
91
+ """換一塊要顯示的區域(None 表示整個視窗)。"""
92
+ self.crop = crop
93
+ self._refresh()
75
94
 
76
95
  def set_opacity_percent(self, percent: int) -> None:
77
96
  self.opacity_percent = max(1, min(100, int(percent)))
@@ -0,0 +1,152 @@
1
+ """
2
+ 快速鍵速查表:把目前綁定的全域快速鍵列在畫面上。
3
+
4
+ 快速鍵改得掉,所以印在說明文件裡的那份遲早會過時;這一份是直接從實際生效的綁定
5
+ 讀出來的,改過就跟著改。
6
+
7
+ 排版與文字是純函式(`sheet_rows`),所以「顯示的內容對不對」不必開視窗就能驗。
8
+
9
+ A shortcut cheat sheet: the global shortcuts as they are actually bound.
10
+
11
+ Shortcuts can be rebound, so any list written into the documentation goes stale;
12
+ this one is read from the bindings in force and follows them.
13
+
14
+ The rows are produced by a pure function (`sheet_rows`), so what it says can be
15
+ checked without opening a window.
16
+ """
17
+ from __future__ import annotations
18
+
19
+ from typing import Dict, List, Optional, Tuple
20
+
21
+ from PySide6.QtCore import Qt
22
+ from PySide6.QtGui import QColor, QFont, QPainter
23
+ from PySide6.QtWidgets import QApplication
24
+
25
+ from frontengine.show.base_widget import BaseWidget
26
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
27
+
28
+ # pynput 的寫法轉成看得懂的樣子
29
+ # pynput spellings turned into something readable
30
+ _PRETTY = {
31
+ "ctrl": "Ctrl", "alt": "Alt", "shift": "Shift", "cmd": "Cmd",
32
+ "up": "Up", "down": "Down", "left": "Left", "right": "Right",
33
+ "space": "Space", "enter": "Enter", "esc": "Esc", "tab": "Tab",
34
+ }
35
+
36
+
37
+ def pretty_combo(combo: str) -> str:
38
+ """
39
+ 把 `<ctrl>+<shift>+<f12>` 變成 `Ctrl + Shift + F12`。
40
+
41
+ 使用者是在對照畫面上按下去的手指,不是在讀設定檔,所以尖括號和小寫都要去掉。
42
+ Turn `<ctrl>+<shift>+<f12>` into `Ctrl + Shift + F12`. The reader is matching
43
+ this against their fingers, not against a settings file, so the angle
44
+ brackets and the lower case go.
45
+ """
46
+ parts = []
47
+ for raw in str(combo or "").split("+"):
48
+ name = raw.strip().strip("<>").strip()
49
+ if not name:
50
+ continue
51
+ if name in _PRETTY:
52
+ parts.append(_PRETTY[name])
53
+ elif len(name) > 1 and name[0] == "f" and name[1:].isdigit():
54
+ parts.append(name.upper())
55
+ else:
56
+ parts.append(name.upper() if len(name) == 1 else name.capitalize())
57
+ return " + ".join(parts)
58
+
59
+
60
+ def sheet_rows(bindings: Dict[str, str], label_for) -> List[Tuple[str, str]]:
61
+ """
62
+ 產生 (快速鍵, 動作說明) 的清單,依動作說明排序。
63
+
64
+ `bindings` 是「組合字串 -> 動作名稱」,和 HotkeyService 用的同一份,所以這裡
65
+ 列出來的一定就是實際會生效的。沒有綁定時回傳空清單,呼叫端才能說「沒有」而
66
+ 不是開一個空白視窗。
67
+ Rows of (shortcut, what it does), sorted by the description.
68
+
69
+ `bindings` is the same combo-to-action mapping HotkeyService uses, so what
70
+ is listed is what will actually fire. With nothing bound this returns an
71
+ empty list, so the caller can say so rather than opening a blank window.
72
+ """
73
+ rows = []
74
+ for combo, action in (bindings or {}).items():
75
+ if not combo or not action:
76
+ continue
77
+ rows.append((pretty_combo(combo), label_for(action)))
78
+ return sorted(rows, key=lambda row: row[1].lower())
79
+
80
+
81
+ class ShortcutSheetWidget(BaseWidget):
82
+ """把速查表畫在畫面中央,點擊穿透。"""
83
+
84
+ PADDING = 28
85
+ LINE_SPACING = 10
86
+
87
+ def __init__(self, rows: List[Tuple[str, str]], title: str = "Shortcuts",
88
+ font_size: int = 20, opacity: float = 0.9) -> None:
89
+ front_engine_logger.info(f"[ShortcutSheetWidget] Init | rows={len(rows)}")
90
+ super().__init__()
91
+ self.opacity = max(0.1, min(1.0, float(opacity)))
92
+ self.rows = list(rows)
93
+ self.title = title
94
+ self.font_size = max(10, int(font_size))
95
+ self.text_color = QColor("#ffffff")
96
+ self.key_color = QColor("#ffd740")
97
+ self.background_color = QColor(0, 0, 0, 210)
98
+
99
+ def sizeHint(self): # pragma: no cover - Qt geometry
100
+ screen = QApplication.primaryScreen()
101
+ available = screen.availableGeometry() if screen is not None else None
102
+ if available is None:
103
+ return super().sizeHint()
104
+ return available.size() / 2
105
+
106
+ def draw_content(self, painter: QPainter) -> None:
107
+ painter.fillRect(self.rect(), self.background_color)
108
+
109
+ title_font = QFont()
110
+ title_font.setPixelSize(int(self.font_size * 1.3))
111
+ title_font.setBold(True)
112
+ painter.setFont(title_font)
113
+ painter.setPen(self.text_color)
114
+ y = self.PADDING + int(self.font_size * 1.3)
115
+ painter.drawText(self.PADDING, y, self.title)
116
+
117
+ row_font = QFont()
118
+ row_font.setPixelSize(self.font_size)
119
+ painter.setFont(row_font)
120
+ y += int(self.font_size * 1.4)
121
+
122
+ # 左欄放快速鍵、右欄放說明。欄寬固定,兩欄才會各自對齊成一直線。
123
+ # Shortcut on the left, description on the right, at a fixed column so
124
+ # both line up rather than drifting with the text.
125
+ key_column = self.PADDING
126
+ text_column = self.PADDING + int(self.font_size * 11)
127
+ for combo, description in self.rows:
128
+ y += self.font_size + self.LINE_SPACING
129
+ painter.setPen(self.key_color)
130
+ painter.drawText(key_column, y, combo)
131
+ painter.setPen(self.text_color)
132
+ painter.drawText(text_column, y, description)
133
+
134
+ def keyPressEvent(self, event) -> None: # pragma: no cover - Qt event
135
+ if event.key() in (Qt.Key.Key_Escape,):
136
+ self.close()
137
+ return
138
+ super().keyPressEvent(event)
139
+
140
+
141
+ def build_sheet(bindings: Dict[str, str], label_for, title: str = "Shortcuts",
142
+ font_size: int = 20) -> Optional[ShortcutSheetWidget]:
143
+ """
144
+ 建立速查表;沒有任何綁定時回傳 None。
145
+ Build the sheet, or None when nothing is bound - an empty black rectangle
146
+ explains nothing.
147
+ """
148
+ rows = sheet_rows(bindings, label_for)
149
+ if not rows:
150
+ front_engine_logger.info("[ShortcutSheetWidget] nothing bound, not showing")
151
+ return None
152
+ return ShortcutSheetWidget(rows, title=title, font_size=font_size)
@@ -32,6 +32,7 @@ _ACTION_LABELS: Dict[str, Tuple[str, str]] = {
32
32
  "opacity_down": ("hotkey_opacity_down", "Opacity down"),
33
33
  "dashboard_next": ("web_dashboard_next", "Next page"),
34
34
  "toggle_lock": ("hotkey_toggle_lock", "Lock / unlock overlays"),
35
+ "show_shortcuts": ("hotkey_show_shortcuts", "Show this shortcut list"),
35
36
  }
36
37
 
37
38
 
@@ -39,6 +40,20 @@ def _t(key: str, fallback: str) -> str:
39
40
  return language_wrapper.language_word_dict.get(key, fallback)
40
41
 
41
42
 
43
+ def action_label(action: str) -> str:
44
+ """
45
+ 動作名稱的顯示文字,翻不到就退回動作名稱本身。
46
+
47
+ 速查表和這個對話框共用同一張表:兩邊各記一份的話,改了快速鍵的說明只會改到
48
+ 其中一邊,而使用者看到的是兩個不同的名字。
49
+ The display text for an action, falling back to the action name. The cheat
50
+ sheet and this dialog share one table: with a copy each, renaming a shortcut
51
+ updates only one of them and the user sees two different names.
52
+ """
53
+ key, fallback = _ACTION_LABELS.get(action, ("", str(action)))
54
+ return _t(key, fallback) if key else fallback
55
+
56
+
42
57
  class HotkeySettingsDialog(QDialog):
43
58
  """
44
59
  讓使用者以 pynput 組合鍵語法(例如 <ctrl>+<shift>+<f12>)重新綁定全域
@@ -12,8 +12,8 @@ from typing import List, Optional, Tuple
12
12
 
13
13
  from PySide6.QtCore import Qt
14
14
  from PySide6.QtWidgets import (
15
- QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel, QListWidget,
16
- QListWidgetItem, QPushButton, QSlider, QWidget,
15
+ QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QGridLayout, QLabel,
16
+ QListWidget, QListWidgetItem, QPushButton, QSlider, QWidget,
17
17
  )
18
18
 
19
19
  from frontengine.show.replica.replica_widget import WindowReplicaWidget
@@ -23,6 +23,19 @@ from frontengine.utils.multi_language.retranslate import retranslator, tr
23
23
  from frontengine.utils.window_pin import window_pin
24
24
  from frontengine.utils.window_replica.dwm_thumbnail import available
25
25
 
26
+ # 要顯示來源的哪一塊,用比例(左, 上, 右, 下)表示。
27
+ # 用比例而不是像素:來源視窗被調整大小之後,框到的還是同一塊。
28
+ # Which part of the source to show, as (left, top, right, bottom) fractions.
29
+ # Fractions rather than pixels, so a resize of the source keeps the same part.
30
+ CROP_REGIONS = (
31
+ ("window_replica_whole", "Whole window", None),
32
+ ("window_replica_top_half", "Top half", (0.0, 0.0, 1.0, 0.5)),
33
+ ("window_replica_bottom_half", "Bottom half", (0.0, 0.5, 1.0, 1.0)),
34
+ ("window_replica_left_half", "Left half", (0.0, 0.0, 0.5, 1.0)),
35
+ ("window_replica_right_half", "Right half", (0.5, 0.0, 1.0, 1.0)),
36
+ ("window_replica_centre", "Centre", (0.25, 0.25, 0.75, 0.75)),
37
+ )
38
+
26
39
 
27
40
  def _t(key: str, fallback: str) -> str:
28
41
  return language_wrapper.language_word_dict.get(key, fallback)
@@ -47,6 +60,11 @@ class WindowReplicaDialog(QDialog):
47
60
  self.show_button = tr(QPushButton(), "window_replica_show", "Show replica")
48
61
  self.show_button.clicked.connect(self.start_replica)
49
62
 
63
+ self.crop_label = tr(QLabel(), "window_replica_region", "Show")
64
+ self.crop_combobox = QComboBox()
65
+ for key, fallback, region in CROP_REGIONS:
66
+ self.crop_combobox.addItem(_t(key, fallback), region)
67
+
50
68
  self.opacity_label = tr(QLabel(), "window_replica_opacity", "Opacity")
51
69
  self.opacity_slider = QSlider(Qt.Orientation.Horizontal)
52
70
  self.opacity_slider.setRange(10, 100)
@@ -69,10 +87,12 @@ class WindowReplicaDialog(QDialog):
69
87
  layout.addWidget(self.window_list, 0, 0, 1, 2)
70
88
  layout.addWidget(self.refresh_button, 1, 0)
71
89
  layout.addWidget(self.show_button, 1, 1)
72
- layout.addWidget(self.opacity_label, 2, 0)
73
- layout.addWidget(self.opacity_slider, 2, 1)
74
- layout.addWidget(self.hint_label, 3, 0, 1, 2)
75
- layout.addWidget(self.button_box, 4, 0, 1, 2)
90
+ layout.addWidget(self.crop_label, 2, 0)
91
+ layout.addWidget(self.crop_combobox, 2, 1)
92
+ layout.addWidget(self.opacity_label, 3, 0)
93
+ layout.addWidget(self.opacity_slider, 3, 1)
94
+ layout.addWidget(self.hint_label, 4, 0, 1, 2)
95
+ layout.addWidget(self.button_box, 5, 0, 1, 2)
76
96
 
77
97
  self.show_button.setEnabled(available())
78
98
  self.reload_windows()
@@ -106,7 +126,8 @@ class WindowReplicaDialog(QDialog):
106
126
  if chosen is None:
107
127
  return None
108
128
  handle, title = chosen
109
- replica = self._replica_factory(handle, title, self.opacity_slider.value(), None)
129
+ replica = self._replica_factory(handle, title, self.opacity_slider.value(), None,
130
+ self.crop_combobox.currentData())
110
131
  if not replica.start():
111
132
  front_engine_logger.info(f"[WindowReplicaDialog] could not replicate {title!r}")
112
133
  replica.close()
@@ -15,6 +15,7 @@ from qt_material import apply_stylesheet
15
15
  from frontengine.ui.nav.sidebar import NavigationSidebar
16
16
  from frontengine.ui.style.app_style import apply_app_style
17
17
 
18
+ from frontengine.show.shortcuts.shortcut_sheet import build_sheet
18
19
  from frontengine.show.toast.toast_widget import show_toast
19
20
  from frontengine.system_tray.extend_system_tray import ExtendSystemTray
20
21
  from frontengine.ui.menu.help_menu import build_help_menu
@@ -55,7 +56,7 @@ from frontengine.utils.hotkey.hotkey_service import HotkeyService
55
56
  from frontengine.utils.keep_awake.keep_awake import KeepAwake
56
57
  from frontengine.utils.logging.loggin_instance import front_engine_logger
57
58
  from frontengine.utils.multi_language.language_wrapper import language_wrapper
58
- from frontengine.utils.multi_language.retranslate import retranslator
59
+ from frontengine.utils.multi_language.retranslate import retranslator, translate
59
60
  from frontengine.utils.plugins.plugin_loader import load_plugins
60
61
  from frontengine.utils.preset_schedule.preset_schedule_service import PresetScheduleService
61
62
  from frontengine.ui.dialog.remote_control_dialog import (
@@ -66,6 +67,7 @@ from frontengine.ui.dialog.signage_dialog import current_settings as current_sig
66
67
  from frontengine.utils.signage.signage_service import SignageService
67
68
  from frontengine.utils.midi.midi_input import MidiInput, binding_key
68
69
  from frontengine.utils.remote.remote_server import RemoteServer
70
+ from frontengine.ui.dialog.hotkey_settings_dialog import action_label as hotkey_action_label
69
71
  from frontengine.ui.dialog.smart_pause_dialog import current_rules
70
72
  from frontengine.utils.screen_privacy.share_watch import ShareWatchService
71
73
  from frontengine.utils.screensaver.screensaver_service import ScreensaverService
@@ -689,6 +691,33 @@ class FrontEngineMainUI(QMainWindow):
689
691
  self.web_setting_ui.show_next_dashboard_page()
690
692
  elif action == "toggle_lock":
691
693
  self.control_center_ui.toggle_lock_all()
694
+ elif action == "show_shortcuts":
695
+ self.toggle_shortcut_sheet()
696
+
697
+ def toggle_shortcut_sheet(self) -> None:
698
+ """
699
+ 開關快速鍵速查表。已經開著就收起來,所以同一個快速鍵按第二次是關閉——
700
+ 不然使用者得去找別的方法把它關掉。
701
+ Toggle the shortcut sheet: pressing the same shortcut again closes it,
702
+ rather than leaving the user hunting for another way to dismiss it.
703
+ """
704
+ existing = getattr(self, "shortcut_sheet", None)
705
+ if existing is not None:
706
+ try:
707
+ existing.close()
708
+ except RuntimeError: # pragma: no cover - 底層物件已消失
709
+ pass
710
+ self.shortcut_sheet = None
711
+ return
712
+ sheet = build_sheet(
713
+ get_hotkey_bindings(),
714
+ lambda action: hotkey_action_label(action),
715
+ title=translate("shortcut_sheet_title", "Shortcuts"))
716
+ if sheet is None:
717
+ return
718
+ sheet.set_ui_window_flag()
719
+ sheet.showFullScreen()
720
+ self.shortcut_sheet = sheet
692
721
 
693
722
  def _on_smart_pause_changed(self, paused: bool, reason: str) -> None:
694
723
  """
@@ -36,6 +36,18 @@ def build_help_menu(ui_we_want_to_set: FrontEngineMainUI) -> None:
36
36
  "How to use..."
37
37
  )
38
38
 
39
+ # 也要能從選單開。只能用快速鍵叫出來的「快速鍵速查表」是個笑話:不知道快速鍵
40
+ # 的人正是最需要它的人。
41
+ # It has to be reachable from the menu too. A shortcut sheet you can only open
42
+ # with a shortcut is a joke: the person who does not know the shortcuts is
43
+ # exactly who needs it.
44
+ _add_action(
45
+ help_menu,
46
+ "shortcut_sheet_action",
47
+ lambda: ui_we_want_to_set.toggle_shortcut_sheet(),
48
+ "Shortcut list..."
49
+ )
50
+
39
51
  _add_action(
40
52
  help_menu,
41
53
  "help_menu_open_issue",
@@ -17,6 +17,7 @@ default_hotkeys: Dict[str, str] = {
17
17
  "opacity_down": "<ctrl>+<shift>+<down>",
18
18
  "dashboard_next": "<ctrl>+<shift>+<right>",
19
19
  "toggle_lock": "<ctrl>+<shift>+l",
20
+ "show_shortcuts": "<ctrl>+<shift>+<f8>",
20
21
  }
21
22
 
22
23
  user_setting_dict: Dict[str, Any] = {
@@ -49,6 +49,13 @@ english_word_dict = {
49
49
  "pet_settle_typing": "Settle while typing",
50
50
  "target_monitor_span": "Span all screens",
51
51
  "tools_replicate_window": "Replicate a window...",
52
+ "window_replica_region": "Show",
53
+ "window_replica_whole": "Whole window",
54
+ "window_replica_top_half": "Top half",
55
+ "window_replica_bottom_half": "Bottom half",
56
+ "window_replica_left_half": "Left half",
57
+ "window_replica_right_half": "Right half",
58
+ "window_replica_centre": "Centre",
52
59
  "window_replica_title": "Replicate a window",
53
60
  "window_replica_refresh": "Refresh",
54
61
  "window_replica_show": "Show replica",
@@ -569,6 +576,9 @@ english_word_dict = {
569
576
  "clipboard_clear": 'Clear (keep pinned)',
570
577
  "clipboard_persist": 'Keep this history between sessions',
571
578
  "clipboard_hint": 'History stays on this machine and is never sent anywhere. Unless you tick the box above it is kept in memory only, because clipboards often hold passwords.',
579
+ "shortcut_sheet_title": "Shortcuts",
580
+ "shortcut_sheet_action": "Shortcut list...",
581
+ "hotkey_show_shortcuts": "Show this shortcut list",
572
582
  "tools_layout_label": 'Window layout',
573
583
  "tools_layout_name": 'Layout name',
574
584
  "tools_layout_save": 'Save layout',
@@ -50,6 +50,13 @@ french_word_dict = {
50
50
  "pet_settle_typing": "Rester tranquille pendant la frappe",
51
51
  "target_monitor_span": "Sur tous les écrans",
52
52
  "tools_replicate_window": "Dupliquer une fenêtre...",
53
+ "window_replica_region": "Afficher",
54
+ "window_replica_whole": "Fenêtre entière",
55
+ "window_replica_top_half": "Moitié supérieure",
56
+ "window_replica_bottom_half": "Moitié inférieure",
57
+ "window_replica_left_half": "Moitié gauche",
58
+ "window_replica_right_half": "Moitié droite",
59
+ "window_replica_centre": "Centre",
53
60
  "window_replica_title": "Dupliquer une fenêtre",
54
61
  "window_replica_refresh": "Actualiser",
55
62
  "window_replica_show": "Afficher le duplicata",
@@ -559,6 +566,9 @@ french_word_dict = {
559
566
  "clipboard_clear": "Effacer (garder les épinglés)",
560
567
  "clipboard_persist": "Conserver cet historique d'une session à l'autre",
561
568
  "clipboard_hint": "L'historique reste sur cette machine et n'est jamais envoyé ailleurs. Sauf si vous cochez la case ci-dessus, il n'est gardé qu'en mémoire, car les presse-papiers contiennent souvent des mots de passe.",
569
+ "shortcut_sheet_title": "Raccourcis",
570
+ "shortcut_sheet_action": "Liste des raccourcis...",
571
+ "hotkey_show_shortcuts": "Afficher cette liste de raccourcis",
562
572
  "tools_layout_label": "Disposition des fenêtres",
563
573
  "tools_layout_name": "Nom de la disposition",
564
574
  "tools_layout_save": "Enregistrer la disposition",
@@ -50,6 +50,13 @@ germany_word_dict = {
50
50
  "pet_settle_typing": "Beim Tippen still halten",
51
51
  "target_monitor_span": "Über alle Bildschirme",
52
52
  "tools_replicate_window": "Fenster duplizieren...",
53
+ "window_replica_region": "Anzeigen",
54
+ "window_replica_whole": "Ganzes Fenster",
55
+ "window_replica_top_half": "Obere Hälfte",
56
+ "window_replica_bottom_half": "Untere Hälfte",
57
+ "window_replica_left_half": "Linke Hälfte",
58
+ "window_replica_right_half": "Rechte Hälfte",
59
+ "window_replica_centre": "Mitte",
53
60
  "window_replica_title": "Fenster duplizieren",
54
61
  "window_replica_refresh": "Aktualisieren",
55
62
  "window_replica_show": "Duplikat anzeigen",
@@ -562,6 +569,9 @@ germany_word_dict = {
562
569
  "clipboard_clear": "Löschen (Angeheftetes behalten)",
563
570
  "clipboard_persist": "Diesen Verlauf über Sitzungen hinweg behalten",
564
571
  "clipboard_hint": "Der Verlauf bleibt auf diesem Rechner und wird niemals irgendwohin gesendet. Solange Sie das Kästchen oben nicht ankreuzen, bleibt er nur im Arbeitsspeicher – denn in Zwischenablagen stehen oft Passwörter.",
572
+ "shortcut_sheet_title": "Tastenkürzel",
573
+ "shortcut_sheet_action": "Tastenkürzel-Liste...",
574
+ "hotkey_show_shortcuts": "Diese Tastenkürzel-Liste anzeigen",
565
575
  "tools_layout_label": "Fensteranordnung",
566
576
  "tools_layout_name": "Name der Anordnung",
567
577
  "tools_layout_save": "Anordnung speichern",
@@ -50,6 +50,13 @@ italian_word_dict = {
50
50
  "pet_settle_typing": "Fermo mentre scrivi",
51
51
  "target_monitor_span": "Su tutti gli schermi",
52
52
  "tools_replicate_window": "Duplica una finestra...",
53
+ "window_replica_region": "Mostra",
54
+ "window_replica_whole": "Tutta la finestra",
55
+ "window_replica_top_half": "Metà superiore",
56
+ "window_replica_bottom_half": "Metà inferiore",
57
+ "window_replica_left_half": "Metà sinistra",
58
+ "window_replica_right_half": "Metà destra",
59
+ "window_replica_centre": "Centro",
53
60
  "window_replica_title": "Duplica una finestra",
54
61
  "window_replica_refresh": "Aggiorna",
55
62
  "window_replica_show": "Mostra il duplicato",
@@ -559,6 +566,9 @@ italian_word_dict = {
559
566
  "clipboard_clear": "Cancella (mantieni i fissati)",
560
567
  "clipboard_persist": "Conserva questa cronologia tra una sessione e l'altra",
561
568
  "clipboard_hint": "La cronologia resta su questa macchina e non viene mai inviata altrove. Se non spunti la casella qui sopra viene tenuta solo in memoria, perché negli appunti finiscono spesso delle password.",
569
+ "shortcut_sheet_title": "Scorciatoie",
570
+ "shortcut_sheet_action": "Elenco scorciatoie...",
571
+ "hotkey_show_shortcuts": "Mostra questo elenco di scorciatoie",
562
572
  "tools_layout_label": "Disposizione delle finestre",
563
573
  "tools_layout_name": "Nome della disposizione",
564
574
  "tools_layout_save": "Salva la disposizione",
@@ -50,6 +50,13 @@ russian_word_dict = {
50
50
  "pet_settle_typing": "Не двигаться при наборе текста",
51
51
  "target_monitor_span": "На все экраны",
52
52
  "tools_replicate_window": "Копия окна...",
53
+ "window_replica_region": "Показывать",
54
+ "window_replica_whole": "Всё окно",
55
+ "window_replica_top_half": "Верхняя половина",
56
+ "window_replica_bottom_half": "Нижняя половина",
57
+ "window_replica_left_half": "Левая половина",
58
+ "window_replica_right_half": "Правая половина",
59
+ "window_replica_centre": "Центр",
53
60
  "window_replica_title": "Копия окна",
54
61
  "window_replica_refresh": "Обновить",
55
62
  "window_replica_show": "Показать копию",
@@ -559,6 +566,9 @@ russian_word_dict = {
559
566
  "clipboard_clear": "Очистить (оставить закреплённые)",
560
567
  "clipboard_persist": "Сохранять эту историю между сеансами",
561
568
  "clipboard_hint": "История остаётся на этом компьютере и никуда не отправляется. Пока флажок выше не установлен, она хранится только в памяти — ведь в буфере обмена часто оказываются пароли.",
569
+ "shortcut_sheet_title": "Горячие клавиши",
570
+ "shortcut_sheet_action": "Список горячих клавиш...",
571
+ "hotkey_show_shortcuts": "Показать этот список",
562
572
  "tools_layout_label": "Расположение окон",
563
573
  "tools_layout_name": "Название расположения",
564
574
  "tools_layout_save": "Сохранить расположение",
@@ -50,6 +50,13 @@ simplified_chinese_word_dict = {
50
50
  "pet_settle_typing": "打字时安分",
51
51
  "target_monitor_span": "横跨所有屏幕",
52
52
  "tools_replicate_window": "窗口副本⋯",
53
+ "window_replica_region": "显示范围",
54
+ "window_replica_whole": "整个窗口",
55
+ "window_replica_top_half": "上半部",
56
+ "window_replica_bottom_half": "下半部",
57
+ "window_replica_left_half": "左半部",
58
+ "window_replica_right_half": "右半部",
59
+ "window_replica_centre": "中央",
53
60
  "window_replica_title": "窗口副本",
54
61
  "window_replica_refresh": "刷新",
55
62
  "window_replica_show": "显示副本",
@@ -561,6 +568,9 @@ simplified_chinese_word_dict = {
561
568
  "clipboard_clear": "清除(保留钉选)",
562
569
  "clipboard_persist": "跨工作阶段保留这份历史",
563
570
  "clipboard_hint": "历史只留在这台机器上,不会送到任何地方。除非你勾选上面那个选项,否则只保存在记忆体里 — 因为剪贴板常常有密码。",
571
+ "shortcut_sheet_title": "快捷键",
572
+ "shortcut_sheet_action": "快捷键一览⋯",
573
+ "hotkey_show_shortcuts": "显示快捷键一览",
564
574
  "tools_layout_label": "视窗版面",
565
575
  "tools_layout_name": "版面名称",
566
576
  "tools_layout_save": "储存版面",
@@ -50,6 +50,13 @@ traditional_chinese_word_dict = {
50
50
  "pet_settle_typing": "打字時安分",
51
51
  "target_monitor_span": "橫跨所有螢幕",
52
52
  "tools_replicate_window": "視窗複本⋯",
53
+ "window_replica_region": "顯示範圍",
54
+ "window_replica_whole": "整個視窗",
55
+ "window_replica_top_half": "上半部",
56
+ "window_replica_bottom_half": "下半部",
57
+ "window_replica_left_half": "左半部",
58
+ "window_replica_right_half": "右半部",
59
+ "window_replica_centre": "中央",
53
60
  "window_replica_title": "視窗複本",
54
61
  "window_replica_refresh": "重新整理",
55
62
  "window_replica_show": "顯示複本",
@@ -568,6 +575,9 @@ traditional_chinese_word_dict = {
568
575
  "clipboard_clear": '清空(保留釘選)',
569
576
  "clipboard_persist": '跨工作階段保存這份歷史',
570
577
  "clipboard_hint": '歷史只留在本機、不會傳到任何地方。沒有勾選上面那項時只存在記憶體,因為剪貼簿裡常常有密碼。',
578
+ "shortcut_sheet_title": "快速鍵",
579
+ "shortcut_sheet_action": "快速鍵一覽⋯",
580
+ "hotkey_show_shortcuts": "顯示快速鍵一覽",
571
581
  "tools_layout_label": '視窗版面',
572
582
  "tools_layout_name": '版面名稱',
573
583
  "tools_layout_save": '儲存版面',
@@ -42,6 +42,40 @@ def available() -> bool:
42
42
  return sys.platform == "win32"
43
43
 
44
44
 
45
+ def crop_rect(source: Tuple[int, int], crop: Optional[Tuple[float, float, float, float]]
46
+ ) -> Optional[Tuple[int, int, int, int]]:
47
+ """
48
+ 把「左、上、右、下」四個 0..1 的比例換算成來源視窗上的像素矩形。
49
+
50
+ 用比例而不是像素,是因為來源視窗會被使用者調大調小;記像素的話,視窗一改大小
51
+ 框到的就不再是原來那一塊了。
52
+
53
+ 框不出面積時回傳 None——寬或高為零的來源矩形會讓 DWM 直接不畫,畫面上只剩一
54
+ 塊黑,看起來像壞掉而不是「你框錯了」。
55
+ Turn left/top/right/bottom fractions into a pixel rectangle on the source.
56
+
57
+ Fractions rather than pixels because the source window gets resized: pixels
58
+ would stop pointing at the same part of it the moment that happened.
59
+
60
+ A rectangle with no area returns None. A zero-width or zero-height source
61
+ rectangle makes DWM draw nothing at all, leaving a black panel that reads as
62
+ broken rather than as a bad selection.
63
+ """
64
+ if crop is None:
65
+ return None
66
+ source_width, source_height = source
67
+ if source_width <= 0 or source_height <= 0:
68
+ return None
69
+ left, top, right, bottom = (max(0.0, min(1.0, float(value))) for value in crop)
70
+ if right <= left or bottom <= top:
71
+ return None
72
+ rect = (int(left * source_width), int(top * source_height),
73
+ int(right * source_width), int(bottom * source_height))
74
+ if rect[2] - rect[0] < 1 or rect[3] - rect[1] < 1:
75
+ return None
76
+ return rect
77
+
78
+
45
79
  def fit_within(source: Tuple[int, int], bounds: Tuple[int, int]) -> Tuple[int, int]:
46
80
  """
47
81
  把來源尺寸等比縮到不超過 bounds,並保持長寬比。
@@ -135,8 +169,13 @@ class DwmThumbnail:
135
169
  return (0, 0)
136
170
 
137
171
  def update(self, width: int, height: int, opacity: int = 255,
138
- client_area_only: bool = True) -> bool:
139
- """把縮圖鋪滿目的地視窗的這個大小。"""
172
+ client_area_only: bool = True,
173
+ source_rect: Optional[Tuple[int, int, int, int]] = None) -> bool:
174
+ """
175
+ 把縮圖鋪滿目的地視窗的這個大小。給了 source_rect 就只顯示來源的那一塊。
176
+ Fill the destination at this size. With a source_rect, only that part of
177
+ the source is shown.
178
+ """
140
179
  if self._handle is None or self._dwmapi is None:
141
180
  return False
142
181
  try:
@@ -161,6 +200,10 @@ class DwmThumbnail:
161
200
  properties.dwFlags = (DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY
162
201
  | DWM_TNP_VISIBLE | DWM_TNP_SOURCECLIENTAREAONLY)
163
202
  properties.rcDestination = RECT(0, 0, max(1, int(width)), max(1, int(height)))
203
+ if source_rect is not None:
204
+ left, top, right, bottom = source_rect
205
+ properties.rcSource = RECT(int(left), int(top), int(right), int(bottom))
206
+ properties.dwFlags |= DWM_TNP_RECTSOURCE
164
207
  properties.opacity = max(0, min(255, int(opacity)))
165
208
  properties.fVisible = True
166
209
  # 只取客戶區:不然複本裡會有一圈原視窗的標題列與邊框,看起來像截圖失誤。