frontengine 1.0.70__tar.gz → 1.0.72__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 (271) hide show
  1. {frontengine-1.0.70 → frontengine-1.0.72}/PKG-INFO +1 -1
  2. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/replica/replica_widget.py +22 -3
  3. frontengine-1.0.72/frontengine/ui/dialog/preset_schedule_dialog.py +150 -0
  4. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/window_replica_dialog.py +28 -7
  5. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/menu/settings_menu.py +9 -0
  6. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/english.py +21 -0
  7. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/france.py +21 -0
  8. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/germany.py +21 -0
  9. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/italy.py +21 -0
  10. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/russian.py +21 -0
  11. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/simplified_chinese.py +21 -0
  12. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/traditional_chinese.py +21 -0
  13. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/preset_schedule/preset_schedule_service.py +48 -1
  14. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/window_replica/dwm_thumbnail.py +45 -2
  15. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine.egg-info/PKG-INFO +1 -1
  16. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine.egg-info/SOURCES.txt +1 -0
  17. {frontengine-1.0.70 → frontengine-1.0.72}/pyproject.toml +1 -1
  18. {frontengine-1.0.70 → frontengine-1.0.72}/LICENSE +0 -0
  19. {frontengine-1.0.70 → frontengine-1.0.72}/README.md +0 -0
  20. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/__init__.py +0 -0
  21. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/__main__.py +0 -0
  22. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/__init__.py +0 -0
  23. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/base_widget.py +0 -0
  24. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/camera/__init__.py +0 -0
  25. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/camera/camera_widget.py +0 -0
  26. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/canvas/__init__.py +0 -0
  27. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/canvas/whiteboard_widget.py +0 -0
  28. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/capture/__init__.py +0 -0
  29. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/capture/region_capture.py +0 -0
  30. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/focus_shield/__init__.py +0 -0
  31. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
  32. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/gif/__init__.py +0 -0
  33. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/gif/paint_gif.py +0 -0
  34. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/image/__init__.py +0 -0
  35. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/image/paint_image.py +0 -0
  36. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/load/__init__.py +0 -0
  37. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/load/load_someone_make_ui.py +0 -0
  38. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/measure/__init__.py +0 -0
  39. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/measure/measure_widget.py +0 -0
  40. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/monitor/__init__.py +0 -0
  41. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/monitor/monitor_widget.py +0 -0
  42. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/notes/__init__.py +0 -0
  43. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/notes/sticky_note_widget.py +0 -0
  44. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/overlay_factory.py +0 -0
  45. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/particle/__init__.py +0 -0
  46. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/particle/particle_ui.py +0 -0
  47. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/pet/__init__.py +0 -0
  48. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/pet/desktop_pet.py +0 -0
  49. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/presentation/__init__.py +0 -0
  50. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/presentation/annotation_overlay.py +0 -0
  51. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/presentation/cursor_effects.py +0 -0
  52. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/presentation/keystroke_display.py +0 -0
  53. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/presentation/magnifier.py +0 -0
  54. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/replica/__init__.py +0 -0
  55. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/scene/__init__.py +0 -0
  56. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  57. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/scene/extend_graphic_view.py +0 -0
  58. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/scene/scene.py +0 -0
  59. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/screen_care/__init__.py +0 -0
  60. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/screen_care/color_vision_widget.py +0 -0
  61. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/screen_care/screen_filter.py +0 -0
  62. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/shortcuts/__init__.py +0 -0
  63. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/shortcuts/shortcut_sheet.py +0 -0
  64. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/sound_player/__init__.py +0 -0
  65. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/sound_player/sound_effect.py +0 -0
  66. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/sound_player/sound_player.py +0 -0
  67. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/spectrum/__init__.py +0 -0
  68. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  69. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/text/__init__.py +0 -0
  70. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/text/draw_text.py +0 -0
  71. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/toast/__init__.py +0 -0
  72. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/toast/toast_widget.py +0 -0
  73. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/video/__init__.py +0 -0
  74. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/video/video_player.py +0 -0
  75. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/wallpaper/__init__.py +0 -0
  76. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  77. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/web/__init__.py +0 -0
  78. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/web/webview.py +0 -0
  79. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/show/window_helpers.py +0 -0
  80. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/system_tray/__init__.py +0 -0
  81. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/system_tray/extend_system_tray.py +0 -0
  82. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/__init__.py +0 -0
  83. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/color/__init__.py +0 -0
  84. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/color/global_color.py +0 -0
  85. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/__init__.py +0 -0
  86. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  87. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  88. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
  89. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  90. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/how_to_use_dialog.py +0 -0
  91. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  92. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/remote_control_dialog.py +0 -0
  93. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/screen_privacy_dialog.py +0 -0
  94. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
  95. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/screensaver_dialog.py +0 -0
  96. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/signage_dialog.py +0 -0
  97. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  98. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
  99. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
  100. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/main_ui.py +0 -0
  101. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/menu/__init__.py +0 -0
  102. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/menu/help_menu.py +0 -0
  103. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/menu/how_to_menu.py +0 -0
  104. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/menu/language_menu.py +0 -0
  105. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/menu/preset_menu.py +0 -0
  106. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/nav/__init__.py +0 -0
  107. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/nav/sidebar.py +0 -0
  108. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/__init__.py +0 -0
  109. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/control_center/__init__.py +0 -0
  110. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  111. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/focus/__init__.py +0 -0
  112. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
  113. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/gif/__init__.py +0 -0
  114. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  115. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/image/__init__.py +0 -0
  116. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  117. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/layout_kit.py +0 -0
  118. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/particle/__init__.py +0 -0
  119. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  120. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/pet/__init__.py +0 -0
  121. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
  122. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/presentation/__init__.py +0 -0
  123. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
  124. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/__init__.py +0 -0
  125. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  126. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/__init__.py +0 -0
  127. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  128. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  129. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  130. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  131. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  132. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  133. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  134. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  135. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  136. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/screen_care/__init__.py +0 -0
  137. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +0 -0
  138. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/sound_player/__init__.py +0 -0
  139. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  140. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/text/__init__.py +0 -0
  141. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  142. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/tools/__init__.py +0 -0
  143. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/tools/tools_setting_ui.py +0 -0
  144. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/utils.py +0 -0
  145. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/video/__init__.py +0 -0
  146. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  147. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/wallpaper/__init__.py +0 -0
  148. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
  149. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/web/__init__.py +0 -0
  150. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  151. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/widgets/__init__.py +0 -0
  152. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
  153. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/style/__init__.py +0 -0
  154. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/ui/style/app_style.py +0 -0
  155. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/user_setting/__init__.py +0 -0
  156. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/user_setting/preset_repository.py +0 -0
  157. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/user_setting/scene_setting.py +0 -0
  158. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/user_setting/user_setting_file.py +0 -0
  159. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/__init__.py +0 -0
  160. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/app_profile/__init__.py +0 -0
  161. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  162. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/audio_meter/__init__.py +0 -0
  163. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  164. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
  165. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/audio_meter/microphone_meter.py +0 -0
  166. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  167. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  168. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
  169. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/autostart/__init__.py +0 -0
  170. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/autostart/autostart_service.py +0 -0
  171. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/break_reminder/__init__.py +0 -0
  172. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  173. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/browser/__init__.py +0 -0
  174. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/browser/browser.py +0 -0
  175. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/clipboard/__init__.py +0 -0
  176. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/clipboard/clipboard_history.py +0 -0
  177. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
  178. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/color_vision/__init__.py +0 -0
  179. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/color_vision/color_vision.py +0 -0
  180. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/critical_exit/__init__.py +0 -0
  181. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  182. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  183. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  184. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/exception/__init__.py +0 -0
  185. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/exception/exception_tags.py +0 -0
  186. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/exception/exceptions.py +0 -0
  187. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/focus_shield/__init__.py +0 -0
  188. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  189. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/focus_timer/__init__.py +0 -0
  190. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  191. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/hotkey/__init__.py +0 -0
  192. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  193. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/input_watch/__init__.py +0 -0
  194. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  195. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/input_watch/typing_watch.py +0 -0
  196. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/json/__init__.py +0 -0
  197. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/json/json_file.py +0 -0
  198. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/json/json_repository.py +0 -0
  199. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/keep_awake/__init__.py +0 -0
  200. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/keep_awake/keep_awake.py +0 -0
  201. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/logging/__init__.py +0 -0
  202. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/logging/loggin_instance.py +0 -0
  203. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/measure/__init__.py +0 -0
  204. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/measure/measure.py +0 -0
  205. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/midi/__init__.py +0 -0
  206. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/midi/midi_input.py +0 -0
  207. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/__init__.py +0 -0
  208. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  209. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/multi_language/retranslate.py +0 -0
  210. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/now_playing/__init__.py +0 -0
  211. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/now_playing/now_playing.py +0 -0
  212. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/pet_chat/__init__.py +0 -0
  213. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  214. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/platform_info/__init__.py +0 -0
  215. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/platform_info/platform_info.py +0 -0
  216. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/playlist/__init__.py +0 -0
  217. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/playlist/playlist.py +0 -0
  218. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/plugins/__init__.py +0 -0
  219. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/plugins/plugin_loader.py +0 -0
  220. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/power_mode/__init__.py +0 -0
  221. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/power_mode/power_mode.py +0 -0
  222. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/preset_schedule/__init__.py +0 -0
  223. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/recording/__init__.py +0 -0
  224. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/recording/frame_recorder.py +0 -0
  225. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/recording/gif_writer.py +0 -0
  226. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/redirect_manager/__init__.py +0 -0
  227. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  228. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/reminder/__init__.py +0 -0
  229. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/reminder/reminder_service.py +0 -0
  230. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/remote/__init__.py +0 -0
  231. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/remote/remote_server.py +0 -0
  232. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/screen_privacy/__init__.py +0 -0
  233. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/screen_privacy/capture_affinity.py +0 -0
  234. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/screen_privacy/share_watch.py +0 -0
  235. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/screen_text/__init__.py +0 -0
  236. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/screen_text/screen_text_service.py +0 -0
  237. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/screensaver/__init__.py +0 -0
  238. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/screensaver/screensaver_service.py +0 -0
  239. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/signage/__init__.py +0 -0
  240. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/signage/signage_service.py +0 -0
  241. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/smart_pause/__init__.py +0 -0
  242. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  243. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  244. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/steam/__init__.py +0 -0
  245. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/steam/steam_language.py +0 -0
  246. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/system_stats/__init__.py +0 -0
  247. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/system_stats/system_stats.py +0 -0
  248. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/text_source/__init__.py +0 -0
  249. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/text_source/text_source.py +0 -0
  250. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/theme_schedule/__init__.py +0 -0
  251. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  252. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/usage_tracking/__init__.py +0 -0
  253. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/usage_tracking/usage_service.py +0 -0
  254. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
  255. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/virtual_camera/__init__.py +0 -0
  256. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/virtual_camera/camera_feed.py +0 -0
  257. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/virtual_camera/virtual_camera.py +0 -0
  258. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/weather/__init__.py +0 -0
  259. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/weather/weather_service.py +0 -0
  260. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/web_url.py +0 -0
  261. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/window_pin/__init__.py +0 -0
  262. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/window_pin/window_layout.py +0 -0
  263. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/window_pin/window_pin.py +0 -0
  264. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/window_replica/__init__.py +0 -0
  265. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/workshop/__init__.py +0 -0
  266. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine/utils/workshop/workshop_content.py +0 -0
  267. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine.egg-info/dependency_links.txt +0 -0
  268. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine.egg-info/entry_points.txt +0 -0
  269. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine.egg-info/requires.txt +0 -0
  270. {frontengine-1.0.70 → frontengine-1.0.72}/frontengine.egg-info/top_level.txt +0 -0
  271. {frontengine-1.0.70 → frontengine-1.0.72}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.70
3
+ Version: 1.0.72
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,150 @@
1
+ """
2
+ 預設集排程設定:星期幾、幾點幾分,套用哪一個預設集。
3
+
4
+ 這個排程本來就在跑,卻沒有任何介面——只能手動改 `user_setting.json`,連時間都
5
+ 不能在畫面上設。所以這裡不只是加上星期,是讓這個功能第一次真的到得了。
6
+
7
+ Preset schedule settings: which days, what time, which preset.
8
+
9
+ The schedule was already running with no interface at all - it could only be
10
+ configured by hand-editing `user_setting.json`, down to the time of day. So this
11
+ is not only about adding days: it is what makes the feature reachable at all.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, Dict, List, Optional
16
+
17
+ from PySide6.QtWidgets import (
18
+ QCheckBox, QComboBox, QDialog, QDialogButtonBox, QGridLayout, QLabel, QSpinBox, QWidget,
19
+ )
20
+
21
+ from frontengine.user_setting.preset_repository import PresetRepository
22
+ from frontengine.user_setting.user_setting_file import user_setting_dict, write_user_setting
23
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
24
+ from frontengine.utils.multi_language.language_wrapper import language_wrapper
25
+ from frontengine.utils.multi_language.retranslate import tr
26
+ from frontengine.utils.preset_schedule.preset_schedule_service import (
27
+ DEFAULT_PRESET_SCHEDULE, normalize_days,
28
+ )
29
+
30
+ SETTING_KEY = "preset_schedule"
31
+
32
+ # 0 = 週一,和 datetime.weekday() 一致
33
+ # 0 = Monday, matching datetime.weekday()
34
+ DAY_KEYS = (
35
+ ("weekday_monday", "Mon"),
36
+ ("weekday_tuesday", "Tue"),
37
+ ("weekday_wednesday", "Wed"),
38
+ ("weekday_thursday", "Thu"),
39
+ ("weekday_friday", "Fri"),
40
+ ("weekday_saturday", "Sat"),
41
+ ("weekday_sunday", "Sun"),
42
+ )
43
+
44
+
45
+ def _t(key: str, fallback: str) -> str:
46
+ return language_wrapper.language_word_dict.get(key, fallback)
47
+
48
+
49
+ def current_settings() -> Dict[str, Any]:
50
+ """目前的排程設定(缺項目補預設值)。"""
51
+ stored = user_setting_dict.get(SETTING_KEY)
52
+ settings = dict(DEFAULT_PRESET_SCHEDULE)
53
+ if isinstance(stored, dict):
54
+ settings.update({key: stored[key] for key in DEFAULT_PRESET_SCHEDULE if key in stored})
55
+ settings["days"] = normalize_days(settings.get("days"))
56
+ settings["enabled"] = bool(settings.get("enabled"))
57
+ return settings
58
+
59
+
60
+ class PresetScheduleDialog(QDialog):
61
+ """設定哪幾天、幾點,自動套用哪一個預設集。"""
62
+
63
+ def __init__(self, parent: Optional[QWidget] = None, repository=None) -> None:
64
+ front_engine_logger.info("[PresetScheduleDialog] Init")
65
+ super().__init__(parent)
66
+ self.setWindowTitle(_t("preset_schedule_title", "Scheduled preset"))
67
+ settings = current_settings()
68
+ self._repository = repository or PresetRepository()
69
+
70
+ self.enable_checkbox = tr(QCheckBox(), "preset_schedule_enable", "Apply a preset on a schedule")
71
+ self.enable_checkbox.setChecked(settings["enabled"])
72
+
73
+ self.preset_label = tr(QLabel(), "preset_schedule_preset", "Preset")
74
+ self.preset_combobox = QComboBox()
75
+ # 只列真的存在的預設集:讓人自己打名字的話,打錯了要等到排程觸發的那一刻
76
+ # 才會發現什麼都沒發生。
77
+ # Only presets that exist are listed: with a free-text name, a typo is
78
+ # discovered when the schedule fires and nothing happens.
79
+ for name in self._preset_names():
80
+ self.preset_combobox.addItem(name, name)
81
+ index = self.preset_combobox.findData(settings.get("preset"))
82
+ if index >= 0:
83
+ self.preset_combobox.setCurrentIndex(index)
84
+
85
+ self.time_label = tr(QLabel(), "preset_schedule_time", "At")
86
+ self.hour_spinbox = QSpinBox()
87
+ self.hour_spinbox.setRange(0, 23)
88
+ self.hour_spinbox.setValue(int(settings.get("hour", 9)) % 24)
89
+ self.minute_spinbox = QSpinBox()
90
+ self.minute_spinbox.setRange(0, 59)
91
+ self.minute_spinbox.setValue(int(settings.get("minute", 0)) % 60)
92
+
93
+ self.days_label = tr(QLabel(), "preset_schedule_days", "On")
94
+ self.day_checkboxes: List[QCheckBox] = []
95
+ days_holder = QWidget()
96
+ days_layout = QGridLayout(days_holder)
97
+ days_layout.setContentsMargins(0, 0, 0, 0)
98
+ for index, (key, fallback) in enumerate(DAY_KEYS):
99
+ checkbox = tr(QCheckBox(), key, fallback)
100
+ checkbox.setChecked(index in settings["days"])
101
+ days_layout.addWidget(checkbox, index // 4, index % 4)
102
+ self.day_checkboxes.append(checkbox)
103
+
104
+ self.hint_label = tr(
105
+ QLabel(), "preset_schedule_hint",
106
+ "With no day ticked it runs every day. It fires once as the time passes, "
107
+ "so starting FrontEngine after that time will not apply it late.")
108
+ self.hint_label.setWordWrap(True)
109
+
110
+ buttons = QDialogButtonBox(
111
+ QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
112
+ buttons.accepted.connect(self.accept)
113
+ buttons.rejected.connect(self.reject)
114
+
115
+ layout = QGridLayout(self)
116
+ layout.addWidget(self.enable_checkbox, 0, 0, 1, 3)
117
+ layout.addWidget(self.preset_label, 1, 0)
118
+ layout.addWidget(self.preset_combobox, 1, 1, 1, 2)
119
+ layout.addWidget(self.time_label, 2, 0)
120
+ layout.addWidget(self.hour_spinbox, 2, 1)
121
+ layout.addWidget(self.minute_spinbox, 2, 2)
122
+ layout.addWidget(self.days_label, 3, 0)
123
+ layout.addWidget(days_holder, 3, 1, 1, 2)
124
+ layout.addWidget(self.hint_label, 4, 0, 1, 3)
125
+ layout.addWidget(buttons, 5, 0, 1, 3)
126
+
127
+ def _preset_names(self) -> List[str]:
128
+ try:
129
+ return [name for name in self._repository.list_presets()
130
+ if not name.startswith("__")]
131
+ except OSError as error: # pragma: no cover - filesystem boundary
132
+ front_engine_logger.warning(f"[PresetScheduleDialog] list failed: {error!r}")
133
+ return []
134
+
135
+ def collect(self) -> Dict[str, Any]:
136
+ """把畫面上的選擇讀成設定。"""
137
+ return {
138
+ "enabled": self.enable_checkbox.isChecked(),
139
+ "preset": self.preset_combobox.currentData() or "",
140
+ "hour": self.hour_spinbox.value(),
141
+ "minute": self.minute_spinbox.value(),
142
+ "days": [index for index, box in enumerate(self.day_checkboxes) if box.isChecked()],
143
+ }
144
+
145
+ def accept(self) -> None:
146
+ user_setting_dict[SETTING_KEY] = self.collect()
147
+ write_user_setting()
148
+ front_engine_logger.info(
149
+ f"[PresetScheduleDialog] saved | {user_setting_dict[SETTING_KEY]}")
150
+ super().accept()
@@ -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()
@@ -12,6 +12,7 @@ from frontengine.ui.dialog.hotkey_settings_dialog import HotkeySettingsDialog
12
12
  from frontengine.ui.dialog.reminder_dialog import ReminderDialog
13
13
  from frontengine.ui.dialog.remote_control_dialog import RemoteControlDialog
14
14
  from frontengine.ui.dialog.screen_privacy_dialog import ScreenPrivacyDialog
15
+ from frontengine.ui.dialog.preset_schedule_dialog import PresetScheduleDialog
15
16
  from frontengine.ui.dialog.screensaver_dialog import ScreensaverDialog
16
17
  from frontengine.utils.keep_awake.keep_awake import available as keep_awake_available
17
18
  from frontengine.ui.dialog.signage_dialog import SignageDialog
@@ -124,6 +125,14 @@ def build_settings_menu(ui: "FrontEngineMainUI") -> None:
124
125
  menu.addAction(screensaver_action)
125
126
  ui.screensaver_action = screensaver_action
126
127
 
128
+ preset_schedule_action = QAction(
129
+ _t("settings_menu_preset_schedule", "Scheduled preset..."), menu)
130
+ retranslator.bind(preset_schedule_action, "settings_menu_preset_schedule",
131
+ "Scheduled preset...")
132
+ preset_schedule_action.triggered.connect(lambda: PresetScheduleDialog(ui).exec())
133
+ menu.addAction(preset_schedule_action)
134
+ ui.preset_schedule_action = preset_schedule_action
135
+
127
136
  signage_action = QAction(_t("settings_menu_signage", "Signage mode..."), menu)
128
137
  retranslator.bind(signage_action, "settings_menu_signage", "Signage mode...")
129
138
  signage_action.triggered.connect(lambda: _open_signage_dialog(ui))
@@ -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",
@@ -259,6 +266,20 @@ english_word_dict = {
259
266
  # Target monitor / control center
260
267
  "target_monitor_label": "Target monitor",
261
268
  "target_monitor_ask": "Ask",
269
+ "settings_menu_preset_schedule": "Scheduled preset...",
270
+ "preset_schedule_title": "Scheduled preset",
271
+ "preset_schedule_enable": "Apply a preset on a schedule",
272
+ "preset_schedule_preset": "Preset",
273
+ "preset_schedule_time": "At",
274
+ "preset_schedule_days": "On",
275
+ "preset_schedule_hint": "With no day ticked it runs every day. It fires once as the time passes, so starting FrontEngine after that time will not apply it late.",
276
+ "weekday_monday": "Mon",
277
+ "weekday_tuesday": "Tue",
278
+ "weekday_wednesday": "Wed",
279
+ "weekday_thursday": "Thu",
280
+ "weekday_friday": "Fri",
281
+ "weekday_saturday": "Sat",
282
+ "weekday_sunday": "Sun",
262
283
  "control_center_hide_all": "Hide all",
263
284
  "control_center_show_all": "Show all",
264
285
  "control_center_lock_all": "Lock overlays",
@@ -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",
@@ -258,6 +265,20 @@ french_word_dict = {
258
265
  "preset_no_presets": "Aucun préréglage enregistré pour l'instant.",
259
266
  "target_monitor_label": "Écran cible",
260
267
  "target_monitor_ask": "Demander",
268
+ "settings_menu_preset_schedule": "Préréglage programmé...",
269
+ "preset_schedule_title": "Préréglage programmé",
270
+ "preset_schedule_enable": "Appliquer un préréglage selon un horaire",
271
+ "preset_schedule_preset": "Préréglage",
272
+ "preset_schedule_time": "À",
273
+ "preset_schedule_days": "Le",
274
+ "preset_schedule_hint": "Sans jour coché, cela s'applique tous les jours. Le déclenchement a lieu au passage de l'heure : démarrer FrontEngine après ne le rattrape pas.",
275
+ "weekday_monday": "Lun",
276
+ "weekday_tuesday": "Mar",
277
+ "weekday_wednesday": "Mer",
278
+ "weekday_thursday": "Jeu",
279
+ "weekday_friday": "Ven",
280
+ "weekday_saturday": "Sam",
281
+ "weekday_sunday": "Dim",
261
282
  "control_center_hide_all": "Tout masquer",
262
283
  "control_center_show_all": "Tout afficher",
263
284
  "control_center_lock_all": "Verrouiller les calques",
@@ -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",
@@ -258,6 +265,20 @@ germany_word_dict = {
258
265
  "preset_no_presets": "Noch keine Voreinstellungen gespeichert.",
259
266
  "target_monitor_label": "Zielbildschirm",
260
267
  "target_monitor_ask": "Nachfragen",
268
+ "settings_menu_preset_schedule": "Geplante Voreinstellung...",
269
+ "preset_schedule_title": "Geplante Voreinstellung",
270
+ "preset_schedule_enable": "Voreinstellung nach Zeitplan anwenden",
271
+ "preset_schedule_preset": "Voreinstellung",
272
+ "preset_schedule_time": "Um",
273
+ "preset_schedule_days": "An",
274
+ "preset_schedule_hint": "Ohne angehakten Tag läuft es täglich. Es löst beim Überschreiten der Uhrzeit einmal aus; ein späterer Start holt es nicht nach.",
275
+ "weekday_monday": "Mo",
276
+ "weekday_tuesday": "Di",
277
+ "weekday_wednesday": "Mi",
278
+ "weekday_thursday": "Do",
279
+ "weekday_friday": "Fr",
280
+ "weekday_saturday": "Sa",
281
+ "weekday_sunday": "So",
261
282
  "control_center_hide_all": "Alle ausblenden",
262
283
  "control_center_show_all": "Alle anzeigen",
263
284
  "control_center_lock_all": "Overlays sperren",
@@ -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",
@@ -258,6 +265,20 @@ italian_word_dict = {
258
265
  "preset_no_presets": "Nessuna preimpostazione salvata finora.",
259
266
  "target_monitor_label": "Schermo di destinazione",
260
267
  "target_monitor_ask": "Chiedi",
268
+ "settings_menu_preset_schedule": "Preimpostazione pianificata...",
269
+ "preset_schedule_title": "Preimpostazione pianificata",
270
+ "preset_schedule_enable": "Applica una preimpostazione a orario",
271
+ "preset_schedule_preset": "Preimpostazione",
272
+ "preset_schedule_time": "Alle",
273
+ "preset_schedule_days": "Nei giorni",
274
+ "preset_schedule_hint": "Senza giorni selezionati vale tutti i giorni. Scatta nel momento in cui l'ora viene superata: avviare FrontEngine dopo non la recupera.",
275
+ "weekday_monday": "Lun",
276
+ "weekday_tuesday": "Mar",
277
+ "weekday_wednesday": "Mer",
278
+ "weekday_thursday": "Gio",
279
+ "weekday_friday": "Ven",
280
+ "weekday_saturday": "Sab",
281
+ "weekday_sunday": "Dom",
261
282
  "control_center_hide_all": "Nascondi tutto",
262
283
  "control_center_show_all": "Mostra tutto",
263
284
  "control_center_lock_all": "Blocca gli overlay",
@@ -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": "Показать копию",
@@ -258,6 +265,20 @@ russian_word_dict = {
258
265
  "preset_no_presets": "Пресеты пока не сохранены.",
259
266
  "target_monitor_label": "Целевой экран",
260
267
  "target_monitor_ask": "Спрашивать",
268
+ "settings_menu_preset_schedule": "Пресет по расписанию...",
269
+ "preset_schedule_title": "Пресет по расписанию",
270
+ "preset_schedule_enable": "Применять пресет по расписанию",
271
+ "preset_schedule_preset": "Пресет",
272
+ "preset_schedule_time": "В",
273
+ "preset_schedule_days": "По дням",
274
+ "preset_schedule_hint": "Если ни один день не отмечен, срабатывает каждый день. Срабатывает в момент наступления времени: запуск FrontEngine позже не применит его задним числом.",
275
+ "weekday_monday": "Пн",
276
+ "weekday_tuesday": "Вт",
277
+ "weekday_wednesday": "Ср",
278
+ "weekday_thursday": "Чт",
279
+ "weekday_friday": "Пт",
280
+ "weekday_saturday": "Сб",
281
+ "weekday_sunday": "Вс",
261
282
  "control_center_hide_all": "Скрыть всё",
262
283
  "control_center_show_all": "Показать всё",
263
284
  "control_center_lock_all": "Заблокировать слои",
@@ -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": "显示副本",
@@ -258,6 +265,20 @@ simplified_chinese_word_dict = {
258
265
  "preset_no_presets": "还没有储存任何预设。",
259
266
  "target_monitor_label": "目标萤幕",
260
267
  "target_monitor_ask": "每次询问",
268
+ "settings_menu_preset_schedule": "排程预设⋯",
269
+ "preset_schedule_title": "排程预设",
270
+ "preset_schedule_enable": "按计划自动套用预设",
271
+ "preset_schedule_preset": "预设",
272
+ "preset_schedule_time": "时间",
273
+ "preset_schedule_days": "星期",
274
+ "preset_schedule_hint": "没有勾任何一天就是每天都跑。它是在时间点跨过去的那一刻触发一次,所以在那之后才打开 FrontEngine 不会补套用。",
275
+ "weekday_monday": "一",
276
+ "weekday_tuesday": "二",
277
+ "weekday_wednesday": "三",
278
+ "weekday_thursday": "四",
279
+ "weekday_friday": "五",
280
+ "weekday_saturday": "六",
281
+ "weekday_sunday": "日",
261
282
  "control_center_hide_all": "全部隐藏",
262
283
  "control_center_show_all": "全部显示",
263
284
  "control_center_lock_all": "锁定覆盖层",
@@ -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": "顯示複本",
@@ -259,6 +266,20 @@ traditional_chinese_word_dict = {
259
266
  # Target monitor / control center
260
267
  "target_monitor_label": "目標螢幕",
261
268
  "target_monitor_ask": "詢問",
269
+ "settings_menu_preset_schedule": "排程預設集⋯",
270
+ "preset_schedule_title": "排程預設集",
271
+ "preset_schedule_enable": "依排程自動套用預設集",
272
+ "preset_schedule_preset": "預設集",
273
+ "preset_schedule_time": "時間",
274
+ "preset_schedule_days": "星期",
275
+ "preset_schedule_hint": "沒有勾任何一天就是每天都跑。它是在時間點跨過去的那一刻觸發一次,所以在那之後才開啟 FrontEngine 不會補套用。",
276
+ "weekday_monday": "一",
277
+ "weekday_tuesday": "二",
278
+ "weekday_wednesday": "三",
279
+ "weekday_thursday": "四",
280
+ "weekday_friday": "五",
281
+ "weekday_saturday": "六",
282
+ "weekday_sunday": "日",
262
283
  "control_center_hide_all": "全部隱藏",
263
284
  "control_center_show_all": "全部顯示",
264
285
  "control_center_lock_all": "鎖定覆蓋層",
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from datetime import datetime
4
- from typing import Any, Callable, Dict, Optional
4
+ from typing import Any, Callable, Dict, List, Optional
5
5
 
6
6
  from PySide6.QtCore import QObject, QTimer, Signal
7
7
 
@@ -9,14 +9,53 @@ from frontengine.utils.logging.loggin_instance import front_engine_logger
9
9
 
10
10
  # 預設的排程設定(預設停用)
11
11
  # Default preset-schedule config (disabled by default).
12
+ # 星期用 0=週一 … 6=週日(和 datetime.weekday() 一致,省得多一層換算)。
13
+ # 空清單代表「每天」——沒有勾任何一天卻期待它每天觸發,比什麼都不做合理;
14
+ # 真的不想要就把整個排程關掉。
15
+ # Days are 0=Monday … 6=Sunday, matching datetime.weekday() so nothing has to be
16
+ # converted. An empty list means every day: someone who ticked no days but left
17
+ # the schedule on meant "every day" far more likely than "never" - and "never"
18
+ # is what switching the schedule off is for.
19
+ ALL_DAYS = (0, 1, 2, 3, 4, 5, 6)
20
+
12
21
  DEFAULT_PRESET_SCHEDULE: Dict[str, Any] = {
13
22
  "enabled": False,
14
23
  "preset": "",
15
24
  "hour": 9,
16
25
  "minute": 0,
26
+ "days": [],
17
27
  }
18
28
 
19
29
 
30
+ def normalize_days(value: Any) -> List[int]:
31
+ """
32
+ 把設定裡的星期整理成排序過、不重複、0..6 的清單。手動編過的設定檔可能塞進
33
+ 字串、負數或 99,這裡一律丟掉而不是讓排程在半夜炸掉。
34
+ Tidy the configured days into a sorted, unique 0..6 list. A hand-edited
35
+ settings file may hold strings, negatives or 99; those are dropped rather
36
+ than left to break the schedule in the middle of the night.
37
+ """
38
+ if value is None:
39
+ return []
40
+ if isinstance(value, (str, bytes)) or not isinstance(value, (list, tuple, set)):
41
+ return []
42
+ days = set()
43
+ for item in value:
44
+ try:
45
+ day = int(item)
46
+ except (TypeError, ValueError):
47
+ continue
48
+ if 0 <= day <= 6:
49
+ days.add(day)
50
+ return sorted(days)
51
+
52
+
53
+ def runs_today(config: Any, weekday: int) -> bool:
54
+ """今天是不是該跑。沒有指定星期就是每天都跑。"""
55
+ days = normalize_days((config or {}).get("days") if isinstance(config, dict) else None)
56
+ return not days or weekday in days
57
+
58
+
20
59
  def _coerce_int(value: Any, default: int, modulo: int) -> int:
21
60
  try:
22
61
  return int(value) % modulo
@@ -86,6 +125,14 @@ class PresetScheduleService(QObject):
86
125
  now = self._now_provider()
87
126
  target = _coerce_int(config.get("hour"), 9, 24) * 60 + _coerce_int(config.get("minute"), 0, 60)
88
127
  current = now.hour * 60 + now.minute
128
+ # 不該跑的日子仍要記下目前時間,否則隔天第一次輪詢會把「上次」當成
129
+ # 未知而錯過當天的觸發點。
130
+ # Days that do not run still record the current minute: otherwise the
131
+ # first poll of the next day has no previous value and misses the
132
+ # crossing entirely.
133
+ if not runs_today(config, now.weekday()):
134
+ self._prev_minutes = current
135
+ return
89
136
  except Exception as error: # pragma: no cover - defensive boundary
90
137
  front_engine_logger.warning(f"[PresetSchedule] poll error: {error!r}")
91
138
  return