frontengine 1.0.72__tar.gz → 1.0.74__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 (276) hide show
  1. {frontengine-1.0.72 → frontengine-1.0.74}/PKG-INFO +1 -1
  2. frontengine-1.0.74/frontengine/show/draggable_window.py +65 -0
  3. frontengine-1.0.74/frontengine/show/pinned/pinned_image.py +103 -0
  4. frontengine-1.0.74/frontengine/show/reference/reference_board.py +155 -0
  5. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/replica/replica_widget.py +2 -26
  6. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/main_ui.py +7 -1
  7. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/image/image_setting_ui.py +52 -0
  8. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/tools/tools_setting_ui.py +36 -1
  9. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/english.py +5 -0
  10. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/france.py +5 -0
  11. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/germany.py +5 -0
  12. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/italy.py +5 -0
  13. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/russian.py +5 -0
  14. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/simplified_chinese.py +5 -0
  15. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/traditional_chinese.py +5 -0
  16. frontengine-1.0.74/frontengine/utils/window_replica/__init__.py +0 -0
  17. frontengine-1.0.74/frontengine/utils/workshop/__init__.py +0 -0
  18. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine.egg-info/PKG-INFO +1 -1
  19. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine.egg-info/SOURCES.txt +5 -0
  20. {frontengine-1.0.72 → frontengine-1.0.74}/pyproject.toml +1 -1
  21. {frontengine-1.0.72 → frontengine-1.0.74}/LICENSE +0 -0
  22. {frontengine-1.0.72 → frontengine-1.0.74}/README.md +0 -0
  23. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/__init__.py +0 -0
  24. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/__main__.py +0 -0
  25. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/__init__.py +0 -0
  26. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/base_widget.py +0 -0
  27. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/camera/__init__.py +0 -0
  28. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/camera/camera_widget.py +0 -0
  29. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/canvas/__init__.py +0 -0
  30. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/canvas/whiteboard_widget.py +0 -0
  31. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/capture/__init__.py +0 -0
  32. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/capture/region_capture.py +0 -0
  33. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/focus_shield/__init__.py +0 -0
  34. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
  35. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/gif/__init__.py +0 -0
  36. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/gif/paint_gif.py +0 -0
  37. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/image/__init__.py +0 -0
  38. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/image/paint_image.py +0 -0
  39. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/load/__init__.py +0 -0
  40. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/load/load_someone_make_ui.py +0 -0
  41. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/measure/__init__.py +0 -0
  42. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/measure/measure_widget.py +0 -0
  43. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/monitor/__init__.py +0 -0
  44. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/monitor/monitor_widget.py +0 -0
  45. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/notes/__init__.py +0 -0
  46. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/notes/sticky_note_widget.py +0 -0
  47. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/overlay_factory.py +0 -0
  48. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/particle/__init__.py +0 -0
  49. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/particle/particle_ui.py +0 -0
  50. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/pet/__init__.py +0 -0
  51. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/pet/desktop_pet.py +0 -0
  52. {frontengine-1.0.72/frontengine/show/presentation → frontengine-1.0.74/frontengine/show/pinned}/__init__.py +0 -0
  53. {frontengine-1.0.72/frontengine/show/replica → frontengine-1.0.74/frontengine/show/presentation}/__init__.py +0 -0
  54. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/presentation/annotation_overlay.py +0 -0
  55. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/presentation/cursor_effects.py +0 -0
  56. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/presentation/keystroke_display.py +0 -0
  57. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/presentation/magnifier.py +0 -0
  58. {frontengine-1.0.72/frontengine/show/scene → frontengine-1.0.74/frontengine/show/reference}/__init__.py +0 -0
  59. {frontengine-1.0.72/frontengine/show/screen_care → frontengine-1.0.74/frontengine/show/replica}/__init__.py +0 -0
  60. {frontengine-1.0.72/frontengine/show/shortcuts → frontengine-1.0.74/frontengine/show/scene}/__init__.py +0 -0
  61. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  62. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/scene/extend_graphic_view.py +0 -0
  63. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/scene/scene.py +0 -0
  64. {frontengine-1.0.72/frontengine/show/sound_player → frontengine-1.0.74/frontengine/show/screen_care}/__init__.py +0 -0
  65. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/screen_care/color_vision_widget.py +0 -0
  66. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/screen_care/screen_filter.py +0 -0
  67. {frontengine-1.0.72/frontengine/show/spectrum → frontengine-1.0.74/frontengine/show/shortcuts}/__init__.py +0 -0
  68. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/shortcuts/shortcut_sheet.py +0 -0
  69. {frontengine-1.0.72/frontengine/show/text → frontengine-1.0.74/frontengine/show/sound_player}/__init__.py +0 -0
  70. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/sound_player/sound_effect.py +0 -0
  71. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/sound_player/sound_player.py +0 -0
  72. {frontengine-1.0.72/frontengine/show/toast → frontengine-1.0.74/frontengine/show/spectrum}/__init__.py +0 -0
  73. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  74. {frontengine-1.0.72/frontengine/show/video → frontengine-1.0.74/frontengine/show/text}/__init__.py +0 -0
  75. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/text/draw_text.py +0 -0
  76. {frontengine-1.0.72/frontengine/show/wallpaper → frontengine-1.0.74/frontengine/show/toast}/__init__.py +0 -0
  77. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/toast/toast_widget.py +0 -0
  78. {frontengine-1.0.72/frontengine/show/web → frontengine-1.0.74/frontengine/show/video}/__init__.py +0 -0
  79. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/video/video_player.py +0 -0
  80. {frontengine-1.0.72/frontengine/system_tray → frontengine-1.0.74/frontengine/show/wallpaper}/__init__.py +0 -0
  81. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  82. {frontengine-1.0.72/frontengine/ui → frontengine-1.0.74/frontengine/show/web}/__init__.py +0 -0
  83. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/web/webview.py +0 -0
  84. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/show/window_helpers.py +0 -0
  85. {frontengine-1.0.72/frontengine/ui/color → frontengine-1.0.74/frontengine/system_tray}/__init__.py +0 -0
  86. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/system_tray/extend_system_tray.py +0 -0
  87. {frontengine-1.0.72/frontengine/ui/dialog → frontengine-1.0.74/frontengine/ui}/__init__.py +0 -0
  88. {frontengine-1.0.72/frontengine/ui/menu → frontengine-1.0.74/frontengine/ui/color}/__init__.py +0 -0
  89. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/color/global_color.py +0 -0
  90. {frontengine-1.0.72/frontengine/ui/nav → frontengine-1.0.74/frontengine/ui/dialog}/__init__.py +0 -0
  91. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  92. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  93. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
  94. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  95. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/how_to_use_dialog.py +0 -0
  96. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/preset_schedule_dialog.py +0 -0
  97. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  98. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/remote_control_dialog.py +0 -0
  99. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/screen_privacy_dialog.py +0 -0
  100. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
  101. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/screensaver_dialog.py +0 -0
  102. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/signage_dialog.py +0 -0
  103. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  104. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
  105. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
  106. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/dialog/window_replica_dialog.py +0 -0
  107. {frontengine-1.0.72/frontengine/ui/page → frontengine-1.0.74/frontengine/ui/menu}/__init__.py +0 -0
  108. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/menu/help_menu.py +0 -0
  109. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/menu/how_to_menu.py +0 -0
  110. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/menu/language_menu.py +0 -0
  111. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/menu/preset_menu.py +0 -0
  112. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/menu/settings_menu.py +0 -0
  113. {frontengine-1.0.72/frontengine/ui/page/control_center → frontengine-1.0.74/frontengine/ui/nav}/__init__.py +0 -0
  114. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/nav/sidebar.py +0 -0
  115. {frontengine-1.0.72/frontengine/ui/page/focus → frontengine-1.0.74/frontengine/ui/page}/__init__.py +0 -0
  116. {frontengine-1.0.72/frontengine/ui/page/gif → frontengine-1.0.74/frontengine/ui/page/control_center}/__init__.py +0 -0
  117. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  118. {frontengine-1.0.72/frontengine/ui/page/image → frontengine-1.0.74/frontengine/ui/page/focus}/__init__.py +0 -0
  119. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
  120. {frontengine-1.0.72/frontengine/ui/page/particle → frontengine-1.0.74/frontengine/ui/page/gif}/__init__.py +0 -0
  121. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  122. {frontengine-1.0.72/frontengine/ui/page/pet → frontengine-1.0.74/frontengine/ui/page/image}/__init__.py +0 -0
  123. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/layout_kit.py +0 -0
  124. {frontengine-1.0.72/frontengine/ui/page/presentation → frontengine-1.0.74/frontengine/ui/page/particle}/__init__.py +0 -0
  125. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  126. {frontengine-1.0.72/frontengine/ui/page/scene_setting → frontengine-1.0.74/frontengine/ui/page/pet}/__init__.py +0 -0
  127. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
  128. {frontengine-1.0.72/frontengine/ui/page/scene_setting/scene_page → frontengine-1.0.74/frontengine/ui/page/presentation}/__init__.py +0 -0
  129. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
  130. {frontengine-1.0.72/frontengine/ui/page/screen_care → frontengine-1.0.74/frontengine/ui/page/scene_setting}/__init__.py +0 -0
  131. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  132. {frontengine-1.0.72/frontengine/ui/page/sound_player → frontengine-1.0.74/frontengine/ui/page/scene_setting/scene_page}/__init__.py +0 -0
  133. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  134. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  135. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  136. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  137. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  138. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  139. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  140. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  141. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  142. {frontengine-1.0.72/frontengine/ui/page/text → frontengine-1.0.74/frontengine/ui/page/screen_care}/__init__.py +0 -0
  143. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +0 -0
  144. {frontengine-1.0.72/frontengine/ui/page/tools → frontengine-1.0.74/frontengine/ui/page/sound_player}/__init__.py +0 -0
  145. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  146. {frontengine-1.0.72/frontengine/ui/page/video → frontengine-1.0.74/frontengine/ui/page/text}/__init__.py +0 -0
  147. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  148. {frontengine-1.0.72/frontengine/ui/page/wallpaper → frontengine-1.0.74/frontengine/ui/page/tools}/__init__.py +0 -0
  149. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/utils.py +0 -0
  150. {frontengine-1.0.72/frontengine/ui/page/web → frontengine-1.0.74/frontengine/ui/page/video}/__init__.py +0 -0
  151. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  152. {frontengine-1.0.72/frontengine/ui/page/widgets → frontengine-1.0.74/frontengine/ui/page/wallpaper}/__init__.py +0 -0
  153. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
  154. {frontengine-1.0.72/frontengine/ui/style → frontengine-1.0.74/frontengine/ui/page/web}/__init__.py +0 -0
  155. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  156. {frontengine-1.0.72/frontengine/user_setting → frontengine-1.0.74/frontengine/ui/page/widgets}/__init__.py +0 -0
  157. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
  158. {frontengine-1.0.72/frontengine/utils → frontengine-1.0.74/frontengine/ui/style}/__init__.py +0 -0
  159. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/ui/style/app_style.py +0 -0
  160. {frontengine-1.0.72/frontengine/utils/app_profile → frontengine-1.0.74/frontengine/user_setting}/__init__.py +0 -0
  161. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/user_setting/preset_repository.py +0 -0
  162. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/user_setting/scene_setting.py +0 -0
  163. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/user_setting/user_setting_file.py +0 -0
  164. {frontengine-1.0.72/frontengine/utils/audio_meter → frontengine-1.0.74/frontengine/utils}/__init__.py +0 -0
  165. {frontengine-1.0.72/frontengine/utils/autostart → frontengine-1.0.74/frontengine/utils/app_profile}/__init__.py +0 -0
  166. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  167. {frontengine-1.0.72/frontengine/utils/break_reminder → frontengine-1.0.74/frontengine/utils/audio_meter}/__init__.py +0 -0
  168. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  169. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
  170. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/audio_meter/microphone_meter.py +0 -0
  171. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  172. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  173. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
  174. {frontengine-1.0.72/frontengine/utils/browser → frontengine-1.0.74/frontengine/utils/autostart}/__init__.py +0 -0
  175. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/autostart/autostart_service.py +0 -0
  176. {frontengine-1.0.72/frontengine/utils/clipboard → frontengine-1.0.74/frontengine/utils/break_reminder}/__init__.py +0 -0
  177. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  178. {frontengine-1.0.72/frontengine/utils/color_vision → frontengine-1.0.74/frontengine/utils/browser}/__init__.py +0 -0
  179. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/browser/browser.py +0 -0
  180. {frontengine-1.0.72/frontengine/utils/critical_exit → frontengine-1.0.74/frontengine/utils/clipboard}/__init__.py +0 -0
  181. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/clipboard/clipboard_history.py +0 -0
  182. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
  183. {frontengine-1.0.72/frontengine/utils/focus_shield → frontengine-1.0.74/frontengine/utils/color_vision}/__init__.py +0 -0
  184. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/color_vision/color_vision.py +0 -0
  185. {frontengine-1.0.72/frontengine/utils/focus_timer → frontengine-1.0.74/frontengine/utils/critical_exit}/__init__.py +0 -0
  186. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  187. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  188. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  189. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/exception/__init__.py +0 -0
  190. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/exception/exception_tags.py +0 -0
  191. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/exception/exceptions.py +0 -0
  192. {frontengine-1.0.72/frontengine/utils/hotkey → frontengine-1.0.74/frontengine/utils/focus_shield}/__init__.py +0 -0
  193. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  194. {frontengine-1.0.72/frontengine/utils/input_watch → frontengine-1.0.74/frontengine/utils/focus_timer}/__init__.py +0 -0
  195. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  196. {frontengine-1.0.72/frontengine/utils/json → frontengine-1.0.74/frontengine/utils/hotkey}/__init__.py +0 -0
  197. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  198. {frontengine-1.0.72/frontengine/utils/keep_awake → frontengine-1.0.74/frontengine/utils/input_watch}/__init__.py +0 -0
  199. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  200. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/input_watch/typing_watch.py +0 -0
  201. {frontengine-1.0.72/frontengine/utils/logging → frontengine-1.0.74/frontengine/utils/json}/__init__.py +0 -0
  202. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/json/json_file.py +0 -0
  203. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/json/json_repository.py +0 -0
  204. {frontengine-1.0.72/frontengine/utils/measure → frontengine-1.0.74/frontengine/utils/keep_awake}/__init__.py +0 -0
  205. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/keep_awake/keep_awake.py +0 -0
  206. {frontengine-1.0.72/frontengine/utils/midi → frontengine-1.0.74/frontengine/utils/logging}/__init__.py +0 -0
  207. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/logging/loggin_instance.py +0 -0
  208. {frontengine-1.0.72/frontengine/utils/multi_language → frontengine-1.0.74/frontengine/utils/measure}/__init__.py +0 -0
  209. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/measure/measure.py +0 -0
  210. {frontengine-1.0.72/frontengine/utils/now_playing → frontengine-1.0.74/frontengine/utils/midi}/__init__.py +0 -0
  211. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/midi/midi_input.py +0 -0
  212. {frontengine-1.0.72/frontengine/utils/pet_chat → frontengine-1.0.74/frontengine/utils/multi_language}/__init__.py +0 -0
  213. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  214. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/multi_language/retranslate.py +0 -0
  215. {frontengine-1.0.72/frontengine/utils/platform_info → frontengine-1.0.74/frontengine/utils/now_playing}/__init__.py +0 -0
  216. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/now_playing/now_playing.py +0 -0
  217. {frontengine-1.0.72/frontengine/utils/playlist → frontengine-1.0.74/frontengine/utils/pet_chat}/__init__.py +0 -0
  218. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  219. {frontengine-1.0.72/frontengine/utils/plugins → frontengine-1.0.74/frontengine/utils/platform_info}/__init__.py +0 -0
  220. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/platform_info/platform_info.py +0 -0
  221. {frontengine-1.0.72/frontengine/utils/power_mode → frontengine-1.0.74/frontengine/utils/playlist}/__init__.py +0 -0
  222. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/playlist/playlist.py +0 -0
  223. {frontengine-1.0.72/frontengine/utils/preset_schedule → frontengine-1.0.74/frontengine/utils/plugins}/__init__.py +0 -0
  224. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/plugins/plugin_loader.py +0 -0
  225. {frontengine-1.0.72/frontengine/utils/recording → frontengine-1.0.74/frontengine/utils/power_mode}/__init__.py +0 -0
  226. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/power_mode/power_mode.py +0 -0
  227. {frontengine-1.0.72/frontengine/utils/redirect_manager → frontengine-1.0.74/frontengine/utils/preset_schedule}/__init__.py +0 -0
  228. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  229. {frontengine-1.0.72/frontengine/utils/reminder → frontengine-1.0.74/frontengine/utils/recording}/__init__.py +0 -0
  230. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/recording/frame_recorder.py +0 -0
  231. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/recording/gif_writer.py +0 -0
  232. {frontengine-1.0.72/frontengine/utils/remote → frontengine-1.0.74/frontengine/utils/redirect_manager}/__init__.py +0 -0
  233. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  234. {frontengine-1.0.72/frontengine/utils/screen_privacy → frontengine-1.0.74/frontengine/utils/reminder}/__init__.py +0 -0
  235. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/reminder/reminder_service.py +0 -0
  236. {frontengine-1.0.72/frontengine/utils/screen_text → frontengine-1.0.74/frontengine/utils/remote}/__init__.py +0 -0
  237. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/remote/remote_server.py +0 -0
  238. {frontengine-1.0.72/frontengine/utils/screensaver → frontengine-1.0.74/frontengine/utils/screen_privacy}/__init__.py +0 -0
  239. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/screen_privacy/capture_affinity.py +0 -0
  240. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/screen_privacy/share_watch.py +0 -0
  241. {frontengine-1.0.72/frontengine/utils/signage → frontengine-1.0.74/frontengine/utils/screen_text}/__init__.py +0 -0
  242. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/screen_text/screen_text_service.py +0 -0
  243. {frontengine-1.0.72/frontengine/utils/smart_pause → frontengine-1.0.74/frontengine/utils/screensaver}/__init__.py +0 -0
  244. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/screensaver/screensaver_service.py +0 -0
  245. {frontengine-1.0.72/frontengine/utils/steam → frontengine-1.0.74/frontengine/utils/signage}/__init__.py +0 -0
  246. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/signage/signage_service.py +0 -0
  247. {frontengine-1.0.72/frontengine/utils/system_stats → frontengine-1.0.74/frontengine/utils/smart_pause}/__init__.py +0 -0
  248. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  249. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  250. {frontengine-1.0.72/frontengine/utils/text_source → frontengine-1.0.74/frontengine/utils/steam}/__init__.py +0 -0
  251. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/steam/steam_language.py +0 -0
  252. {frontengine-1.0.72/frontengine/utils/theme_schedule → frontengine-1.0.74/frontengine/utils/system_stats}/__init__.py +0 -0
  253. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/system_stats/system_stats.py +0 -0
  254. {frontengine-1.0.72/frontengine/utils/usage_tracking → frontengine-1.0.74/frontengine/utils/text_source}/__init__.py +0 -0
  255. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/text_source/text_source.py +0 -0
  256. {frontengine-1.0.72/frontengine/utils/virtual_camera → frontengine-1.0.74/frontengine/utils/theme_schedule}/__init__.py +0 -0
  257. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  258. {frontengine-1.0.72/frontengine/utils/weather → frontengine-1.0.74/frontengine/utils/usage_tracking}/__init__.py +0 -0
  259. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/usage_tracking/usage_service.py +0 -0
  260. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
  261. {frontengine-1.0.72/frontengine/utils/window_pin → frontengine-1.0.74/frontengine/utils/virtual_camera}/__init__.py +0 -0
  262. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/virtual_camera/camera_feed.py +0 -0
  263. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/virtual_camera/virtual_camera.py +0 -0
  264. {frontengine-1.0.72/frontengine/utils/window_replica → frontengine-1.0.74/frontengine/utils/weather}/__init__.py +0 -0
  265. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/weather/weather_service.py +0 -0
  266. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/web_url.py +0 -0
  267. {frontengine-1.0.72/frontengine/utils/workshop → frontengine-1.0.74/frontengine/utils/window_pin}/__init__.py +0 -0
  268. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/window_pin/window_layout.py +0 -0
  269. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/window_pin/window_pin.py +0 -0
  270. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/window_replica/dwm_thumbnail.py +0 -0
  271. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine/utils/workshop/workshop_content.py +0 -0
  272. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine.egg-info/dependency_links.txt +0 -0
  273. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine.egg-info/entry_points.txt +0 -0
  274. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine.egg-info/requires.txt +0 -0
  275. {frontengine-1.0.72 → frontengine-1.0.74}/frontengine.egg-info/top_level.txt +0 -0
  276. {frontengine-1.0.72 → frontengine-1.0.74}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.72
3
+ Version: 1.0.74
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
@@ -0,0 +1,65 @@
1
+ """
2
+ 可拖曳的置頂小視窗:釘選截圖與視窗複本共用的底座。
3
+
4
+ 這兩者都是「拿來對照的東西」而不是背景,所以都需要同一套行為:沒有邊框、永遠在
5
+ 最上層、可以拖到順手的位置、雙擊或 Escape 關掉。兩邊各寫一份的話,改了其中一個
6
+ 的關閉方式,另一個就會變成另一種操作邏輯——而使用者不會知道為什麼。
7
+
8
+ A draggable always-on-top window: the base shared by the pinned capture and the
9
+ window replica.
10
+
11
+ Both are things you refer to rather than backdrops, so both need the same
12
+ behaviour: no frame, always on top, draggable somewhere convenient, closed with a
13
+ double-click or Escape. Written twice, changing how one of them closes would
14
+ quietly leave the other behaving differently, with nothing to explain why.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ from typing import Optional
19
+
20
+ from PySide6.QtCore import QPoint, Qt
21
+ from PySide6.QtWidgets import QWidget
22
+
23
+
24
+ class DraggableTopWindow(QWidget):
25
+ """無邊框、置頂、可拖曳、雙擊或 Escape 關閉。"""
26
+
27
+ def __init__(self, parent: Optional[QWidget] = None) -> None:
28
+ super().__init__(parent)
29
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
30
+ self.setWindowFlags(
31
+ Qt.WindowType.FramelessWindowHint
32
+ | Qt.WindowType.WindowStaysOnTopHint
33
+ | Qt.WindowType.Tool)
34
+ self.setWindowTitle("FrontEngine")
35
+ self._drag_origin: Optional[QPoint] = None
36
+
37
+ def mousePressEvent(self, event) -> None:
38
+ if event.button() == Qt.MouseButton.LeftButton:
39
+ self._drag_origin = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
40
+ event.accept()
41
+
42
+ def mouseMoveEvent(self, event) -> None:
43
+ if self._drag_origin is not None and event.buttons() & Qt.MouseButton.LeftButton:
44
+ self.move(event.globalPosition().toPoint() - self._drag_origin)
45
+ event.accept()
46
+
47
+ def mouseReleaseEvent(self, event) -> None:
48
+ self._drag_origin = None
49
+ event.accept()
50
+
51
+ def mouseDoubleClickEvent(self, event) -> None:
52
+ """
53
+ 雙擊關掉。沒有標題列就沒有右上角的叉,總得留一個看得懂的出口,否則使用者
54
+ 只能去工作管理員。
55
+ Double-click closes. With no title bar there is no cross in the corner, and
56
+ something covering the screen with no way out leaves only the task manager.
57
+ """
58
+ self.close()
59
+ event.accept()
60
+
61
+ def keyPressEvent(self, event) -> None:
62
+ if event.key() == Qt.Key.Key_Escape:
63
+ self.close()
64
+ return
65
+ super().keyPressEvent(event)
@@ -0,0 +1,103 @@
1
+ """
2
+ 把截下來的一塊畫面釘在螢幕上。
3
+
4
+ 區域截圖原本只能送到剪貼簿或存成檔案——想「一邊看著這段規格一邊改程式」的話,
5
+ 得自己找一個看圖程式打開再想辦法讓它保持在最上層。釘選就是把中間那幾步拿掉。
6
+
7
+ 和圖片覆蓋層的差別在來源與互動:圖片覆蓋層吃**檔案路徑**、點擊穿透;這個吃記憶體
8
+ 裡的 `QPixmap`,而且要能拖、能縮放、能關掉——截下來的東西是拿來對照的,不是背景。
9
+
10
+ Pin a captured piece of screen on top.
11
+
12
+ Region capture could only reach the clipboard or a file: wanting to read a
13
+ specification while editing code meant opening an image viewer and finding a way
14
+ to keep it on top. Pinning removes the steps in between.
15
+
16
+ It differs from the image overlay in both source and behaviour: that one takes a
17
+ **file path** and passes clicks through, while this takes a `QPixmap` already in
18
+ memory and has to be draggable, resizable and closable - a capture is something
19
+ you refer to, not a backdrop.
20
+ """
21
+ from __future__ import annotations
22
+
23
+ from typing import Optional, Tuple
24
+
25
+ from PySide6.QtGui import QPainter, QPixmap
26
+ from PySide6.QtWidgets import QWidget
27
+
28
+ from frontengine.show.draggable_window import DraggableTopWindow
29
+
30
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
31
+
32
+ MIN_PINNED_SIZE = 48
33
+ MAX_ZOOM = 4.0
34
+ MIN_ZOOM = 0.1
35
+ ZOOM_STEP = 1.1
36
+
37
+
38
+ def zoom_size(base: Tuple[int, int], zoom: float) -> Tuple[int, int]:
39
+ """
40
+ 縮放後的大小,維持長寬比並夾在可用範圍內。
41
+
42
+ 純算術,所以縮放不必開視窗就能驗。下限存在的理由和複本一樣:縮到剩幾像素的
43
+ 圖片既拖不動也關不掉,等於卡在畫面上。
44
+ The scaled size, keeping the aspect ratio and clamped to a usable range.
45
+
46
+ Pure arithmetic, so zooming can be checked without a window. The floor is
47
+ there for the same reason as the replica's: an image a few pixels across can
48
+ be neither dragged nor closed, so it is simply stuck on screen.
49
+ """
50
+ width, height = base
51
+ if width <= 0 or height <= 0:
52
+ return (MIN_PINNED_SIZE, MIN_PINNED_SIZE)
53
+ factor = max(MIN_ZOOM, min(MAX_ZOOM, float(zoom)))
54
+ return (max(MIN_PINNED_SIZE, int(width * factor)),
55
+ max(MIN_PINNED_SIZE, int(height * factor)))
56
+
57
+
58
+ def clamp_zoom(zoom: float) -> float:
59
+ """把縮放倍率夾在可用範圍內。"""
60
+ try:
61
+ return max(MIN_ZOOM, min(MAX_ZOOM, float(zoom)))
62
+ except (TypeError, ValueError):
63
+ return 1.0
64
+
65
+
66
+ class PinnedImageWidget(DraggableTopWindow):
67
+ """釘在最上層的一張圖:可拖曳、可滾輪縮放、雙擊或 Escape 關閉。"""
68
+
69
+ def __init__(self, pixmap: QPixmap, opacity_percent: int = 100,
70
+ parent: Optional[QWidget] = None) -> None:
71
+ super().__init__(parent)
72
+ self.pixmap = pixmap if pixmap is not None else QPixmap()
73
+ self.zoom = 1.0
74
+ self.setWindowOpacity(max(0.1, min(1.0, opacity_percent / 100.0)))
75
+
76
+ base = (self.pixmap.width(), self.pixmap.height())
77
+ front_engine_logger.info(f"[PinnedImageWidget] Init | size={base}")
78
+ self.resize(*zoom_size(base, 1.0))
79
+
80
+ def base_size(self) -> Tuple[int, int]:
81
+ return (self.pixmap.width(), self.pixmap.height())
82
+
83
+ def apply_zoom(self, zoom: float) -> None:
84
+ """套用縮放倍率並跟著改變視窗大小。"""
85
+ self.zoom = clamp_zoom(zoom)
86
+ self.resize(*zoom_size(self.base_size(), self.zoom))
87
+
88
+ def paintEvent(self, event) -> None:
89
+ painter = QPainter(self)
90
+ if self.pixmap.isNull():
91
+ return
92
+ # 縮放時用平滑轉換:釘起來的多半是文字或介面截圖,鋸齒會直接讓它讀不了。
93
+ # Smooth transformation: what gets pinned is usually text or an interface,
94
+ # and aliasing makes exactly that unreadable.
95
+ painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True)
96
+ painter.drawPixmap(self.rect(), self.pixmap)
97
+
98
+ def wheelEvent(self, event) -> None:
99
+ steps = event.angleDelta().y() / 120.0
100
+ if steps:
101
+ self.apply_zoom(self.zoom * (ZOOM_STEP ** steps))
102
+ event.accept()
103
+
@@ -0,0 +1,155 @@
1
+ """
2
+ 參考圖板:一塊畫布上放好幾張圖,各自可以搬動,整塊可以縮放平移。
3
+
4
+ 和「圖片覆蓋層」的差別是數量與擺法:覆蓋層是把一張圖固定在某個位置當背景,圖板
5
+ 是把一疊參考資料攤開來排在手邊——畫圖的、做設計的、對著規格寫程式的都在做這件事。
6
+
7
+ 視圖行為(無邊框、置頂、透明、滾輪縮放、拖曳平移)沿用場景功能的
8
+ `ExtendGraphicView`,這裡只負責「放什麼進去、一開始怎麼排」。
9
+
10
+ A reference board: several images on one canvas, each movable, the whole thing
11
+ zoomable and pannable.
12
+
13
+ It differs from the image overlay in number and arrangement: the overlay fixes one
14
+ picture in place as a backdrop, while a board spreads a pile of references out
15
+ within reach - what illustrators, designers and anyone coding against a
16
+ specification actually do.
17
+
18
+ The view behaviour - frameless, on top, transparent, wheel zoom, drag to pan -
19
+ comes from the scene feature's `ExtendGraphicView`; this only decides what goes on
20
+ it and how it is laid out to begin with.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ from pathlib import Path
25
+ from typing import List, Sequence, Tuple
26
+
27
+ from PySide6.QtCore import Qt
28
+ from PySide6.QtGui import QPixmap
29
+ from PySide6.QtWidgets import QGraphicsPixmapItem, QGraphicsScene
30
+
31
+ from frontengine.show.scene.extend_graphic_view import ExtendGraphicView
32
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
33
+
34
+ BOARD_EXTENSIONS = (".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif")
35
+ DEFAULT_COLUMNS = 3
36
+ DEFAULT_ITEM_SIZE = 320
37
+ ITEM_GAP = 24
38
+
39
+
40
+ def columns_for(count: int, preferred: int = DEFAULT_COLUMNS) -> int:
41
+ """
42
+ 幾張圖排幾欄。張數比預設欄數少時就照張數排,免得三張圖被排成一欄兩列、
43
+ 右邊空一大塊。
44
+ How many columns for this many images. Fewer images than the preferred column
45
+ count simply use their own number, so three pictures are not laid out as a
46
+ row of two with a gap beside them.
47
+ """
48
+ count = max(0, int(count))
49
+ if count == 0:
50
+ return 0
51
+ return max(1, min(int(preferred), count))
52
+
53
+
54
+ def grid_positions(count: int, item_size: int = DEFAULT_ITEM_SIZE,
55
+ columns: int = DEFAULT_COLUMNS,
56
+ gap: int = ITEM_GAP) -> List[Tuple[int, int]]:
57
+ """
58
+ 一開始把圖排成網格的座標。純算術,所以排版不必開視窗就能驗。
59
+
60
+ 先排好再讓使用者自己搬:全部疊在原點的話,開起來看到的是一張圖,其他幾張都
61
+ 藏在下面,使用者會以為只載入了一張。
62
+ The initial grid coordinates. Pure arithmetic, so the layout can be checked
63
+ without a window.
64
+
65
+ They are arranged before the user rearranges them: stacked at the origin, the
66
+ board would open showing one picture with the rest hidden underneath, and it
67
+ would look as though only one had loaded.
68
+ """
69
+ count = max(0, int(count))
70
+ if count == 0:
71
+ return []
72
+ column_count = columns_for(count, columns)
73
+ step = max(1, int(item_size)) + max(0, int(gap))
74
+ return [((index % column_count) * step, (index // column_count) * step)
75
+ for index in range(count)]
76
+
77
+
78
+ def readable_images(paths: Sequence[str]) -> List[str]:
79
+ """
80
+ 留下副檔名認得、而且真的讀得進來的圖。
81
+
82
+ 壞掉或不是圖片的檔案要在這裡就擋下來:讓 QPixmap 靜靜產生一張空圖的話,圖板
83
+ 上會出現一塊看不見的空白,使用者只會覺得「有一張圖不見了」。
84
+ Keep the files whose suffix is known and that actually load.
85
+
86
+ A broken or non-image file is dropped here: letting QPixmap quietly produce a
87
+ null pixmap puts an invisible blank on the board, and all the user sees is
88
+ that one of their pictures is missing.
89
+ """
90
+ kept = []
91
+ for path in paths or ():
92
+ try:
93
+ candidate = Path(str(path))
94
+ if candidate.suffix.lower() not in BOARD_EXTENSIONS:
95
+ continue
96
+ if not candidate.is_file():
97
+ continue
98
+ except OSError: # pragma: no cover - filesystem boundary
99
+ continue
100
+ if QPixmap(str(candidate)).isNull():
101
+ front_engine_logger.info(f"[ReferenceBoard] skipped unreadable image: {candidate}")
102
+ continue
103
+ kept.append(str(candidate))
104
+ return kept
105
+
106
+
107
+ class ReferenceBoardWidget(ExtendGraphicView):
108
+ """好幾張參考圖放在同一塊畫布上,各自可以搬動。"""
109
+
110
+ def __init__(self, item_size: int = DEFAULT_ITEM_SIZE, columns: int = DEFAULT_COLUMNS):
111
+ front_engine_logger.info("[ReferenceBoardWidget] Init")
112
+ self.board_scene = QGraphicsScene()
113
+ super().__init__(self.board_scene)
114
+ self.item_size = max(64, int(item_size))
115
+ self.columns = max(1, int(columns))
116
+ self.items: List[QGraphicsPixmapItem] = []
117
+
118
+ def add_images(self, paths: Sequence[str]) -> int:
119
+ """把圖加進圖板並排好,回傳實際加進去的張數。"""
120
+ usable = readable_images(paths)
121
+ positions = grid_positions(len(usable), self.item_size, self.columns)
122
+ for path, (x, y) in zip(usable, positions):
123
+ pixmap = QPixmap(path).scaled(
124
+ self.item_size, self.item_size,
125
+ Qt.AspectRatioMode.KeepAspectRatio,
126
+ Qt.TransformationMode.SmoothTransformation)
127
+ item = QGraphicsPixmapItem(pixmap)
128
+ # 每張圖各自可以搬:圖板的用途就是把它們排成自己看得順的位置。
129
+ # Each picture moves on its own: arranging them is the whole point.
130
+ item.setFlag(QGraphicsPixmapItem.GraphicsItemFlag.ItemIsMovable, True)
131
+ item.setFlag(QGraphicsPixmapItem.GraphicsItemFlag.ItemIsSelectable, True)
132
+ item.setPos(x, y)
133
+ self.board_scene.addItem(item)
134
+ self.items.append(item)
135
+ front_engine_logger.info(f"[ReferenceBoardWidget] added {len(usable)} of {len(paths or ())}")
136
+ return len(usable)
137
+
138
+ def remove_selected(self) -> int:
139
+ """把選起來的圖從圖板上拿掉,回傳拿掉幾張。"""
140
+ removed = 0
141
+ for item in list(self.items):
142
+ if item.isSelected():
143
+ self.board_scene.removeItem(item)
144
+ self.items.remove(item)
145
+ removed += 1
146
+ return removed
147
+
148
+ def keyPressEvent(self, event) -> None:
149
+ if event.key() == Qt.Key.Key_Escape:
150
+ self.close()
151
+ return
152
+ if event.key() in (Qt.Key.Key_Delete, Qt.Key.Key_Backspace):
153
+ self.remove_selected()
154
+ return
155
+ super().keyPressEvent(event)
@@ -14,7 +14,7 @@ from __future__ import annotations
14
14
 
15
15
  from typing import Optional
16
16
 
17
- from PySide6.QtCore import QPoint, Qt
17
+ from frontengine.show.draggable_window import DraggableTopWindow
18
18
  from PySide6.QtGui import QColor, QPainter
19
19
  from PySide6.QtWidgets import QWidget
20
20
 
@@ -24,7 +24,7 @@ from frontengine.utils.window_replica.dwm_thumbnail import (
24
24
  )
25
25
 
26
26
 
27
- class WindowReplicaWidget(QWidget):
27
+ class WindowReplicaWidget(DraggableTopWindow):
28
28
  """顯示另一個視窗即時畫面的小視窗。"""
29
29
 
30
30
  def __init__(self, source_handle: int, title: str = "",
@@ -32,11 +32,6 @@ class WindowReplicaWidget(QWidget):
32
32
  crop: Optional[tuple] = None) -> None:
33
33
  front_engine_logger.info(f"[WindowReplicaWidget] Init | source={source_handle}")
34
34
  super().__init__(parent)
35
- self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
36
- self.setWindowFlags(
37
- Qt.WindowType.FramelessWindowHint
38
- | Qt.WindowType.WindowStaysOnTopHint
39
- | Qt.WindowType.Tool)
40
35
  self.setWindowTitle(title or "FrontEngine")
41
36
  self.setMinimumSize(MIN_REPLICA_SIZE, MIN_REPLICA_SIZE)
42
37
 
@@ -48,7 +43,6 @@ class WindowReplicaWidget(QWidget):
48
43
  # the source after that window is resized.
49
44
  self.crop = crop
50
45
  self.thumbnail = DwmThumbnail()
51
- self._drag_origin: Optional[QPoint] = None
52
46
 
53
47
  self.resize(DEFAULT_REPLICA_WIDTH, int(DEFAULT_REPLICA_WIDTH * 9 / 16))
54
48
 
@@ -109,24 +103,6 @@ class WindowReplicaWidget(QWidget):
109
103
  painter = QPainter(self)
110
104
  painter.fillRect(self.rect(), QColor(0, 0, 0))
111
105
 
112
- def mousePressEvent(self, event) -> None:
113
- if event.button() == Qt.MouseButton.LeftButton:
114
- self._drag_origin = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
115
- event.accept()
116
-
117
- def mouseMoveEvent(self, event) -> None:
118
- if self._drag_origin is not None and event.buttons() & Qt.MouseButton.LeftButton:
119
- self.move(event.globalPosition().toPoint() - self._drag_origin)
120
- event.accept()
121
-
122
- def mouseReleaseEvent(self, event) -> None:
123
- self._drag_origin = None
124
- event.accept()
125
-
126
- def mouseDoubleClickEvent(self, event) -> None:
127
- """雙擊關掉——沒有標題列,總得有一個看得懂的出口。"""
128
- self.close()
129
- event.accept()
130
106
 
131
107
  def closeEvent(self, event) -> None:
132
108
  # 縮圖是系統資源,視窗沒了也不會自己消失。從標題列以外的任何路徑關閉時
@@ -398,9 +398,14 @@ class FrontEngineMainUI(QMainWindow):
398
398
  "now_playing_widget_list", "note_widget_list"):
399
399
  self.control_center_ui.register_overlay_source(
400
400
  lambda attribute=attribute: getattr(self.widgets_setting_ui, attribute, []))
401
- for attribute in ("measure_widget_list", "capture_widget_list", "camera_widget_list"):
401
+ for attribute in ("measure_widget_list", "capture_widget_list", "camera_widget_list",
402
+ "pinned_widget_list"):
402
403
  self.control_center_ui.register_overlay_source(
403
404
  lambda attribute=attribute: getattr(self.tools_setting_ui, attribute, []))
405
+ # 參考圖板也要進控制中心,否則「全部關閉」關不掉它
406
+ # The reference boards belong here too, or "close all" cannot reach them
407
+ self.control_center_ui.register_overlay_source(
408
+ lambda: getattr(self.image_setting_ui, "board_widget_list", []))
404
409
 
405
410
  # 螢幕保護能顯示哪些分頁:來源名稱 -> (頁面屬性, 啟動方法, 覆蓋層清單屬性)
406
411
  # What the screensaver can show: source -> (page attribute, start method,
@@ -929,6 +934,7 @@ class FrontEngineMainUI(QMainWindow):
929
934
  self.widgets_setting_ui.stop_spectrum()
930
935
  if getattr(self, "tools_setting_ui", None) is not None:
931
936
  self.tools_setting_ui.release_pinned_windows()
937
+ self.tools_setting_ui.close_pinned()
932
938
  # 複本註冊的是 DWM 縮圖,那是系統資源:視窗沒了也不會自己消失。
933
939
  # A replica holds a DWM thumbnail, a system resource that does not
934
940
  # go away just because the process is closing.
@@ -6,6 +6,7 @@ from PySide6.QtWidgets import (
6
6
  )
7
7
 
8
8
  from frontengine.show.image.paint_image import ImageWidget
9
+ from frontengine.show.reference.reference_board import ReferenceBoardWidget
9
10
  from frontengine.ui.dialog.choose_file_dialog import choose_image
10
11
  from frontengine.ui.page.layout_kit import SettingPage
11
12
  from frontengine.ui.page.utils import (
@@ -97,6 +98,21 @@ class ImageSettingUI(SettingPage):
97
98
  self.slideshow_interval_combobox.addItems(["3", "5", "10", "15", "30", "60"])
98
99
  self.slideshow_interval_combobox.setCurrentText("5")
99
100
  self.slideshow_shuffle_checkbox = tr(QCheckBox(), "slideshow_shuffle", "Shuffle")
101
+ # 參考圖板:好幾張圖攤在同一塊畫布上,各自可以搬動
102
+ # Reference board: several pictures on one canvas, each movable
103
+ self.board_widget_list: list = []
104
+ self.board_choose_button = tr(QPushButton(), "image_board_choose",
105
+ "Open reference board...")
106
+ self.board_choose_button.clicked.connect(self.open_reference_board)
107
+ self.board_close_button = tr(QPushButton(), "image_board_close", "Close boards")
108
+ self.board_close_button.clicked.connect(self.close_reference_boards)
109
+ self.board_hint_label = tr(
110
+ QLabel(), "image_board_hint",
111
+ "Pick several pictures at once. Drag each one to arrange it, drag the "
112
+ "background to pan, scroll to zoom, Delete removes the selected one and "
113
+ "Escape closes the board.")
114
+ self.board_hint_label.setWordWrap(True)
115
+
100
116
  self.slideshow_recursive_checkbox = tr(QCheckBox(), "slideshow_recursive",
101
117
  "Include subfolders")
102
118
 
@@ -117,6 +133,10 @@ class ImageSettingUI(SettingPage):
117
133
  slideshow.add_row(self.slideshow_interval_label, self.slideshow_interval_combobox)
118
134
  slideshow.add_inline(self.slideshow_shuffle_checkbox, self.slideshow_recursive_checkbox)
119
135
 
136
+ board = self.add_section("image_board_label", "Reference board")
137
+ board.add_inline(self.board_choose_button, self.board_close_button)
138
+ board.add_widget(self.board_hint_label)
139
+
120
140
  where = self.add_section("section_where", "Where")
121
141
  where.add_row(self.target_monitor_label, self.target_monitor_combobox)
122
142
  where.add_inline(self.show_on_all_screen_checkbox, self.show_on_bottom_checkbox,
@@ -125,6 +145,38 @@ class ImageSettingUI(SettingPage):
125
145
  self.finish_body()
126
146
  self.set_footer(primary=self.start_button, status=self.ready_label)
127
147
 
148
+ def open_reference_board(self):
149
+ """
150
+ 挑幾張圖開一塊參考圖板。一張都沒挑就不要開空白畫布——那看起來像壞掉。
151
+ Open a board from several chosen pictures. Nothing chosen opens nothing:
152
+ an empty canvas reads as a fault.
153
+ """
154
+ paths, _ = QFileDialog.getOpenFileNames(
155
+ self,
156
+ language_wrapper.language_word_dict.get("image_board_choose", "Open reference board..."),
157
+ "",
158
+ "Images (*.png *.jpg *.jpeg *.webp *.bmp *.gif)")
159
+ if not paths:
160
+ return None
161
+ board = ReferenceBoardWidget()
162
+ if board.add_images(paths) == 0:
163
+ # 全部讀不進來就不要留一塊空畫布在畫面上。
164
+ # With nothing readable, do not leave an empty canvas on screen.
165
+ board.close()
166
+ return None
167
+ board.showMaximized()
168
+ self.board_widget_list.append(board)
169
+ return board
170
+
171
+ def close_reference_boards(self) -> None:
172
+ """關掉開過的參考圖板。"""
173
+ for board in list(self.board_widget_list):
174
+ try:
175
+ board.close()
176
+ except RuntimeError: # pragma: no cover - 底層物件已消失
177
+ pass
178
+ self.board_widget_list.clear()
179
+
128
180
  def set_show_all_screen(self) -> None:
129
181
  front_engine_logger.info("[ImageSettingUI] set_show_all_screen")
130
182
  self.show_all_screen = self.show_on_all_screen_checkbox.isChecked()
@@ -18,6 +18,7 @@ from frontengine.show.camera.camera_widget import (
18
18
  SHAPE_CIRCLE, SHAPE_RECTANGLE, SHAPE_ROUNDED, CameraWidget, list_cameras,
19
19
  )
20
20
  from frontengine.show.capture.region_capture import RegionCaptureWidget, is_usable
21
+ from frontengine.show.pinned.pinned_image import PinnedImageWidget
21
22
  from frontengine.show.measure.measure_widget import (
22
23
  MODE_ANGLE, MODE_COLOR, MODE_RULER, MeasureWidget,
23
24
  )
@@ -77,6 +78,9 @@ class ToolsSettingUI(SettingPage):
77
78
  self.last_screen_text: Optional[str] = None
78
79
  self.last_recording: Optional[str] = None
79
80
  self.capture_widget_list: List[RegionCaptureWidget] = []
81
+ # 釘住的截圖:使用者自己關掉之外,主程式結束時也要收乾淨
82
+ # Pinned captures: closed by the user, and swept up on shutdown too
83
+ self.pinned_widget_list: List[PinnedImageWidget] = []
80
84
  self.camera_widget_list: List[CameraWidget] = []
81
85
  # 存畫面本身,不要存那個覆蓋層:框選完它就自己 close() 了,而
82
86
  # WA_DeleteOnClose 會把底層物件銷毀,留下來的參考碰一下就 RuntimeError,
@@ -131,7 +135,8 @@ class ToolsSettingUI(SettingPage):
131
135
  measure.add_inline(self.measure_button)
132
136
 
133
137
  capture = self.add_section(self.capture_label)
134
- capture.add_inline(self.capture_button, self.capture_copy_button)
138
+ capture.add_inline(self.capture_button, self.capture_copy_button,
139
+ self.capture_pin_button)
135
140
 
136
141
  screen_text = self.add_section(self.screen_text_label)
137
142
  screen_text.add_row("tools_action", self.screen_text_combobox, "Action")
@@ -189,6 +194,8 @@ class ToolsSettingUI(SettingPage):
189
194
  self.capture_button = tr(QPushButton(), "tools_capture_start", "Capture area")
190
195
  self.capture_button.clicked.connect(self.start_capture)
191
196
  self.capture_copy_button = tr(QPushButton(), "tools_capture_copy", "Copy last")
197
+ self.capture_pin_button = tr(QPushButton(), "tools_capture_pin", "Pin last")
198
+ self.capture_pin_button.clicked.connect(self.pin_last_capture)
192
199
  self.capture_copy_button.clicked.connect(self.copy_last_capture)
193
200
 
194
201
  def _build_screen_text_row(self) -> None:
@@ -311,6 +318,34 @@ class ToolsSettingUI(SettingPage):
311
318
  if widget in self.capture_widget_list:
312
319
  self.capture_widget_list.remove(widget)
313
320
 
321
+ def pin_last_capture(self) -> Optional[PinnedImageWidget]:
322
+ """
323
+ 把最近一次的截圖釘在畫面上。沒有截過就什麼都不做——開一個空白視窗只會讓
324
+ 使用者以為截圖壞了。
325
+ Pin the most recent capture. With nothing captured this does nothing: an
326
+ empty window would read as the capture having failed.
327
+ """
328
+ if self.last_capture is None or self.last_capture.isNull():
329
+ front_engine_logger.info("[ToolsSettingUI] nothing captured to pin")
330
+ return None
331
+ pinned = PinnedImageWidget(self.last_capture)
332
+ pinned.show()
333
+ self.pinned_widget_list.append(pinned)
334
+ return pinned
335
+
336
+ def close_pinned(self) -> None:
337
+ """關掉所有釘住的截圖(主程式關閉時呼叫)。"""
338
+ # 用切片而不是 list():邊走邊關的時候原清單有可能被動到,而 Sonar 對
339
+ # list() 會抱怨、對切片不會。
340
+ # A slice rather than list(): the original can still be touched while
341
+ # closing, and Sonar objects to list() but not to slicing.
342
+ for pinned in self.pinned_widget_list[:]:
343
+ try:
344
+ pinned.close()
345
+ except RuntimeError: # pragma: no cover - 底層物件已消失
346
+ pass
347
+ self.pinned_widget_list.clear()
348
+
314
349
  def copy_last_capture(self) -> bool:
315
350
  """把最近一次的截圖再放進剪貼簿一次。"""
316
351
  if self.last_capture is None or self.last_capture.isNull():
@@ -171,6 +171,10 @@ english_word_dict = {
171
171
  "web_setting_open_enable_input": "Enable input",
172
172
  # Scene UI
173
173
  "tab_scene_text": "Scene",
174
+ "image_board_label": "REFERENCE BOARD",
175
+ "image_board_choose": "Open reference board...",
176
+ "image_board_close": "Close boards",
177
+ "image_board_hint": "Pick several pictures at once. Drag each one to arrange it, drag the background to pan, scroll to zoom, Delete removes the selected one and Escape closes the board.",
174
178
  "scene_start": "Start scene",
175
179
  "scene_table_view_type": "Type",
176
180
  "scene_file_path": "File path",
@@ -542,6 +546,7 @@ english_word_dict = {
542
546
  "tools_measure_start": 'Start measuring',
543
547
  "tools_measure_stop": 'Stop measuring',
544
548
  "tools_color_format": 'Copy colour as',
549
+ "tools_capture_pin": "Pin last",
545
550
  "tools_capture_label": 'Region capture',
546
551
  "tools_capture_start": 'Capture area',
547
552
  "tools_capture_copy": 'Copy last',
@@ -171,6 +171,10 @@ french_word_dict = {
171
171
  "web_setting_open_enable_input": "Activer l'entrée",
172
172
  # Scene UI
173
173
  "tab_scene_text": "Scène",
174
+ "image_board_label": "PLANCHE DE RÉFÉRENCE",
175
+ "image_board_choose": "Ouvrir une planche...",
176
+ "image_board_close": "Fermer les planches",
177
+ "image_board_hint": "Choisissez plusieurs images à la fois. Faites glisser chacune pour l'arranger, le fond pour vous déplacer, la molette pour zoomer ; Suppr retire l'image sélectionnée et Échap ferme la planche.",
174
178
  "scene_start": "Commencer la scène",
175
179
  "scene_table_view_type": "Type",
176
180
  "scene_file_path": "Chemin du fichier",
@@ -532,6 +536,7 @@ french_word_dict = {
532
536
  "tools_measure_start": "Commencer à mesurer",
533
537
  "tools_measure_stop": "Arrêter de mesurer",
534
538
  "tools_color_format": "Copier la couleur en",
539
+ "tools_capture_pin": "Épingler la dernière",
535
540
  "tools_capture_label": "Capture de zone",
536
541
  "tools_capture_start": "Capturer une zone",
537
542
  "tools_capture_copy": "Copier la dernière",
@@ -171,6 +171,10 @@ germany_word_dict = {
171
171
  "web_setting_open_enable_input": "Eingabe aktivieren",
172
172
  # Szenen-UI
173
173
  "tab_scene_text": "Szene",
174
+ "image_board_label": "REFERENZTAFEL",
175
+ "image_board_choose": "Referenztafel öffnen...",
176
+ "image_board_close": "Tafeln schließen",
177
+ "image_board_hint": "Mehrere Bilder auf einmal wählen. Einzelne ziehen zum Anordnen, den Hintergrund ziehen zum Verschieben, Mausrad zum Zoomen, Entf entfernt das gewählte Bild, Escape schließt die Tafel.",
174
178
  "scene_start": "Szene starten",
175
179
  "scene_table_view_type": "Typ",
176
180
  "scene_file_path": "Dateipfad",
@@ -535,6 +539,7 @@ germany_word_dict = {
535
539
  "tools_measure_start": "Messen starten",
536
540
  "tools_measure_stop": "Messen beenden",
537
541
  "tools_color_format": "Farbe kopieren als",
542
+ "tools_capture_pin": "Letzten anheften",
538
543
  "tools_capture_label": "Bereichsaufnahme",
539
544
  "tools_capture_start": "Bereich aufnehmen",
540
545
  "tools_capture_copy": "Letzte kopieren",
@@ -171,6 +171,10 @@ italian_word_dict = {
171
171
  "web_setting_open_enable_input": "Abilita input",
172
172
  # Scene UI
173
173
  "tab_scene_text": "Scena",
174
+ "image_board_label": "TAVOLA DI RIFERIMENTO",
175
+ "image_board_choose": "Apri tavola di riferimento...",
176
+ "image_board_close": "Chiudi le tavole",
177
+ "image_board_hint": "Scegli più immagini insieme. Trascina ciascuna per disporla, trascina lo sfondo per spostarti, rotellina per lo zoom, Canc toglie quella selezionata ed Esc chiude la tavola.",
174
178
  "scene_start": "Inizia la scena",
175
179
  "scene_table_view_type": "Tipo",
176
180
  "scene_file_path": "Percorso del file",
@@ -532,6 +536,7 @@ italian_word_dict = {
532
536
  "tools_measure_start": "Inizia a misurare",
533
537
  "tools_measure_stop": "Smetti di misurare",
534
538
  "tools_color_format": "Copia il colore come",
539
+ "tools_capture_pin": "Fissa l'ultima",
535
540
  "tools_capture_label": "Cattura di un'area",
536
541
  "tools_capture_start": "Cattura un'area",
537
542
  "tools_capture_copy": "Copia l'ultima",