frontengine 1.0.39__tar.gz → 1.0.41__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 (252) hide show
  1. {frontengine-1.0.39 → frontengine-1.0.41}/PKG-INFO +20 -1
  2. {frontengine-1.0.39 → frontengine-1.0.41}/README.md +19 -0
  3. frontengine-1.0.41/frontengine/show/gaming/crosshair_widget.py +160 -0
  4. frontengine-1.0.41/frontengine/show/teleprompter/teleprompter_widget.py +171 -0
  5. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/main_ui.py +8 -0
  6. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +1 -1
  7. frontengine-1.0.41/frontengine/ui/page/stream/stream_setting_ui.py +351 -0
  8. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/tools/tools_setting_ui.py +14 -8
  9. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/user_setting/user_setting_file.py +6 -0
  10. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/english.py +24 -0
  11. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/traditional_chinese.py +24 -0
  12. frontengine-1.0.41/frontengine/utils/obs/obs_client.py +148 -0
  13. frontengine-1.0.41/frontengine/utils/obs/obs_protocol.py +140 -0
  14. frontengine-1.0.41/frontengine/utils/obs/websocket_frames.py +147 -0
  15. frontengine-1.0.41/frontengine/utils/screen_text/screen_text_service.py +208 -0
  16. frontengine-1.0.41/frontengine/utils/soundboard/soundboard.py +172 -0
  17. frontengine-1.0.41/frontengine/utils/theme_schedule/__init__.py +0 -0
  18. frontengine-1.0.41/frontengine/utils/usage_tracking/__init__.py +0 -0
  19. frontengine-1.0.41/frontengine/utils/weather/__init__.py +0 -0
  20. frontengine-1.0.41/frontengine/utils/window_pin/__init__.py +0 -0
  21. frontengine-1.0.41/frontengine/utils/workshop/__init__.py +0 -0
  22. frontengine-1.0.41/frontengine/worker/__init__.py +0 -0
  23. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine.egg-info/PKG-INFO +20 -1
  24. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine.egg-info/SOURCES.txt +14 -0
  25. {frontengine-1.0.39 → frontengine-1.0.41}/pyproject.toml +1 -1
  26. {frontengine-1.0.39 → frontengine-1.0.41}/LICENSE +0 -0
  27. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/__init__.py +0 -0
  28. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/__main__.py +0 -0
  29. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/__init__.py +0 -0
  30. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/base_widget.py +0 -0
  31. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/camera/__init__.py +0 -0
  32. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/camera/camera_widget.py +0 -0
  33. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/capture/__init__.py +0 -0
  34. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/capture/region_capture.py +0 -0
  35. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/focus_shield/__init__.py +0 -0
  36. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
  37. {frontengine-1.0.39/frontengine/show/gif → frontengine-1.0.41/frontengine/show/gaming}/__init__.py +0 -0
  38. {frontengine-1.0.39/frontengine/show/image → frontengine-1.0.41/frontengine/show/gif}/__init__.py +0 -0
  39. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/gif/paint_gif.py +0 -0
  40. {frontengine-1.0.39/frontengine/show/load → frontengine-1.0.41/frontengine/show/image}/__init__.py +0 -0
  41. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/image/paint_image.py +0 -0
  42. {frontengine-1.0.39/frontengine/show/measure → frontengine-1.0.41/frontengine/show/load}/__init__.py +0 -0
  43. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/load/load_someone_make_ui.py +0 -0
  44. {frontengine-1.0.39/frontengine/show/monitor → frontengine-1.0.41/frontengine/show/measure}/__init__.py +0 -0
  45. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/measure/measure_widget.py +0 -0
  46. {frontengine-1.0.39/frontengine/show/notes → frontengine-1.0.41/frontengine/show/monitor}/__init__.py +0 -0
  47. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/monitor/monitor_widget.py +0 -0
  48. {frontengine-1.0.39/frontengine/show/particle → frontengine-1.0.41/frontengine/show/notes}/__init__.py +0 -0
  49. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/notes/sticky_note_widget.py +0 -0
  50. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/overlay_factory.py +0 -0
  51. {frontengine-1.0.39/frontengine/show/pet → frontengine-1.0.41/frontengine/show/particle}/__init__.py +0 -0
  52. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/particle/particle_ui.py +0 -0
  53. {frontengine-1.0.39/frontengine/show/presentation → frontengine-1.0.41/frontengine/show/pet}/__init__.py +0 -0
  54. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/pet/desktop_pet.py +0 -0
  55. {frontengine-1.0.39/frontengine/show/scene → frontengine-1.0.41/frontengine/show/presentation}/__init__.py +0 -0
  56. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/presentation/annotation_overlay.py +0 -0
  57. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/presentation/cursor_effects.py +0 -0
  58. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/presentation/keystroke_display.py +0 -0
  59. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/presentation/magnifier.py +0 -0
  60. {frontengine-1.0.39/frontengine/show/screen_care → frontengine-1.0.41/frontengine/show/scene}/__init__.py +0 -0
  61. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/scene/extend_graphic_scene.py +0 -0
  62. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/scene/extend_graphic_view.py +0 -0
  63. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/scene/scene.py +0 -0
  64. {frontengine-1.0.39/frontengine/show/sound_player → frontengine-1.0.41/frontengine/show/screen_care}/__init__.py +0 -0
  65. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/screen_care/color_vision_widget.py +0 -0
  66. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/screen_care/screen_filter.py +0 -0
  67. {frontengine-1.0.39/frontengine/show/spectrum → frontengine-1.0.41/frontengine/show/sound_player}/__init__.py +0 -0
  68. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/sound_player/sound_effect.py +0 -0
  69. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/sound_player/sound_player.py +0 -0
  70. {frontengine-1.0.39/frontengine/show/text → frontengine-1.0.41/frontengine/show/spectrum}/__init__.py +0 -0
  71. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/spectrum/spectrum_widget.py +0 -0
  72. {frontengine-1.0.39/frontengine/show/toast → frontengine-1.0.41/frontengine/show/teleprompter}/__init__.py +0 -0
  73. {frontengine-1.0.39/frontengine/show/video → frontengine-1.0.41/frontengine/show/text}/__init__.py +0 -0
  74. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/text/draw_text.py +0 -0
  75. {frontengine-1.0.39/frontengine/show/wallpaper → frontengine-1.0.41/frontengine/show/toast}/__init__.py +0 -0
  76. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/toast/toast_widget.py +0 -0
  77. {frontengine-1.0.39/frontengine/show/web → frontengine-1.0.41/frontengine/show/video}/__init__.py +0 -0
  78. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/video/video_player.py +0 -0
  79. {frontengine-1.0.39/frontengine/system_tray → frontengine-1.0.41/frontengine/show/wallpaper}/__init__.py +0 -0
  80. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
  81. {frontengine-1.0.39/frontengine/ui → frontengine-1.0.41/frontengine/show/web}/__init__.py +0 -0
  82. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/web/webview.py +0 -0
  83. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/show/window_helpers.py +0 -0
  84. {frontengine-1.0.39/frontengine/ui/color → frontengine-1.0.41/frontengine/system_tray}/__init__.py +0 -0
  85. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/system_tray/extend_system_tray.py +0 -0
  86. {frontengine-1.0.39/frontengine/ui/dialog → frontengine-1.0.41/frontengine/ui}/__init__.py +0 -0
  87. {frontengine-1.0.39/frontengine/ui/menu → frontengine-1.0.41/frontengine/ui/color}/__init__.py +0 -0
  88. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/color/global_color.py +0 -0
  89. {frontengine-1.0.39/frontengine/ui/page → frontengine-1.0.41/frontengine/ui/dialog}/__init__.py +0 -0
  90. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
  91. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
  92. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
  93. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
  94. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/reminder_dialog.py +0 -0
  95. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/save_file_dialog.py +0 -0
  96. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
  97. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
  98. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
  99. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
  100. {frontengine-1.0.39/frontengine/ui/page/control_center → frontengine-1.0.41/frontengine/ui/menu}/__init__.py +0 -0
  101. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/menu/help_menu.py +0 -0
  102. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/menu/how_to_menu.py +0 -0
  103. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/menu/language_menu.py +0 -0
  104. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/menu/preset_menu.py +0 -0
  105. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/menu/settings_menu.py +0 -0
  106. {frontengine-1.0.39/frontengine/ui/page/focus → frontengine-1.0.41/frontengine/ui/page}/__init__.py +0 -0
  107. {frontengine-1.0.39/frontengine/ui/page/gif → frontengine-1.0.41/frontengine/ui/page/control_center}/__init__.py +0 -0
  108. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
  109. {frontengine-1.0.39/frontengine/ui/page/image → frontengine-1.0.41/frontengine/ui/page/focus}/__init__.py +0 -0
  110. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
  111. {frontengine-1.0.39/frontengine/ui/page/particle → frontengine-1.0.41/frontengine/ui/page/gif}/__init__.py +0 -0
  112. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
  113. {frontengine-1.0.39/frontengine/ui/page/pet → frontengine-1.0.41/frontengine/ui/page/image}/__init__.py +0 -0
  114. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/image/image_setting_ui.py +0 -0
  115. {frontengine-1.0.39/frontengine/ui/page/presentation → frontengine-1.0.41/frontengine/ui/page/particle}/__init__.py +0 -0
  116. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
  117. {frontengine-1.0.39/frontengine/ui/page/scene_setting → frontengine-1.0.41/frontengine/ui/page/pet}/__init__.py +0 -0
  118. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
  119. {frontengine-1.0.39/frontengine/ui/page/scene_setting/scene_page → frontengine-1.0.41/frontengine/ui/page/presentation}/__init__.py +0 -0
  120. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
  121. {frontengine-1.0.39/frontengine/ui/page/screen_care → frontengine-1.0.41/frontengine/ui/page/scene_setting}/__init__.py +0 -0
  122. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
  123. {frontengine-1.0.39/frontengine/ui/page/sound_player → frontengine-1.0.41/frontengine/ui/page/scene_setting/scene_page}/__init__.py +0 -0
  124. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
  125. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
  126. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
  127. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
  128. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
  129. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
  130. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
  131. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
  132. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
  133. {frontengine-1.0.39/frontengine/ui/page/text → frontengine-1.0.41/frontengine/ui/page/screen_care}/__init__.py +0 -0
  134. {frontengine-1.0.39/frontengine/ui/page/tools → frontengine-1.0.41/frontengine/ui/page/sound_player}/__init__.py +0 -0
  135. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
  136. {frontengine-1.0.39/frontengine/ui/page/video → frontengine-1.0.41/frontengine/ui/page/stream}/__init__.py +0 -0
  137. {frontengine-1.0.39/frontengine/ui/page/wallpaper → frontengine-1.0.41/frontengine/ui/page/text}/__init__.py +0 -0
  138. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/text/text_setting_ui.py +0 -0
  139. {frontengine-1.0.39/frontengine/ui/page/web → frontengine-1.0.41/frontengine/ui/page/tools}/__init__.py +0 -0
  140. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/utils.py +0 -0
  141. {frontengine-1.0.39/frontengine/ui/page/widgets → frontengine-1.0.41/frontengine/ui/page/video}/__init__.py +0 -0
  142. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/video/video_setting_ui.py +0 -0
  143. {frontengine-1.0.39/frontengine/user_setting → frontengine-1.0.41/frontengine/ui/page/wallpaper}/__init__.py +0 -0
  144. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
  145. {frontengine-1.0.39/frontengine/utils → frontengine-1.0.41/frontengine/ui/page/web}/__init__.py +0 -0
  146. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/web/web_setting_ui.py +0 -0
  147. {frontengine-1.0.39/frontengine/utils/app_profile → frontengine-1.0.41/frontengine/ui/page/widgets}/__init__.py +0 -0
  148. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
  149. {frontengine-1.0.39/frontengine/utils/audio_meter → frontengine-1.0.41/frontengine/user_setting}/__init__.py +0 -0
  150. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/user_setting/preset_repository.py +0 -0
  151. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/user_setting/scene_setting.py +0 -0
  152. {frontengine-1.0.39/frontengine/utils/autostart → frontengine-1.0.41/frontengine/utils}/__init__.py +0 -0
  153. {frontengine-1.0.39/frontengine/utils/break_reminder → frontengine-1.0.41/frontengine/utils/app_profile}/__init__.py +0 -0
  154. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/app_profile/app_profile_service.py +0 -0
  155. {frontengine-1.0.39/frontengine/utils/browser → frontengine-1.0.41/frontengine/utils/audio_meter}/__init__.py +0 -0
  156. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
  157. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
  158. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/audio_meter/screen_audio.py +0 -0
  159. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
  160. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
  161. {frontengine-1.0.39/frontengine/utils/clipboard → frontengine-1.0.41/frontengine/utils/autostart}/__init__.py +0 -0
  162. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/autostart/autostart_service.py +0 -0
  163. {frontengine-1.0.39/frontengine/utils/color_vision → frontengine-1.0.41/frontengine/utils/break_reminder}/__init__.py +0 -0
  164. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/break_reminder/break_reminder.py +0 -0
  165. {frontengine-1.0.39/frontengine/utils/critical_exit → frontengine-1.0.41/frontengine/utils/browser}/__init__.py +0 -0
  166. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/browser/browser.py +0 -0
  167. {frontengine-1.0.39/frontengine/utils/file → frontengine-1.0.41/frontengine/utils/clipboard}/__init__.py +0 -0
  168. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/clipboard/clipboard_history.py +0 -0
  169. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
  170. {frontengine-1.0.39/frontengine/utils/focus_shield → frontengine-1.0.41/frontengine/utils/color_vision}/__init__.py +0 -0
  171. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/color_vision/color_vision.py +0 -0
  172. {frontengine-1.0.39/frontengine/utils/focus_timer → frontengine-1.0.41/frontengine/utils/critical_exit}/__init__.py +0 -0
  173. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
  174. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/critical_exit/critical_exit.py +0 -0
  175. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/critical_exit/win32_vk.py +0 -0
  176. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/exception/__init__.py +0 -0
  177. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/exception/exception_tags.py +0 -0
  178. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/exception/exceptions.py +0 -0
  179. {frontengine-1.0.39/frontengine/utils/hotkey → frontengine-1.0.41/frontengine/utils/file}/__init__.py +0 -0
  180. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/file/open/__init__.py +0 -0
  181. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/file/open/open_file.py +0 -0
  182. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/file/save/__init__.py +0 -0
  183. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/file/save/save_file.py +0 -0
  184. {frontengine-1.0.39/frontengine/utils/input_watch → frontengine-1.0.41/frontengine/utils/focus_shield}/__init__.py +0 -0
  185. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/focus_shield/focus_shield.py +0 -0
  186. {frontengine-1.0.39/frontengine/utils/json → frontengine-1.0.41/frontengine/utils/focus_timer}/__init__.py +0 -0
  187. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/focus_timer/focus_timer.py +0 -0
  188. {frontengine-1.0.39/frontengine/utils/logging → frontengine-1.0.41/frontengine/utils/hotkey}/__init__.py +0 -0
  189. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/hotkey/hotkey_service.py +0 -0
  190. {frontengine-1.0.39/frontengine/utils/measure → frontengine-1.0.41/frontengine/utils/input_watch}/__init__.py +0 -0
  191. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/input_watch/input_watch_service.py +0 -0
  192. {frontengine-1.0.39/frontengine/utils/multi_language → frontengine-1.0.41/frontengine/utils/json}/__init__.py +0 -0
  193. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/json/json_file.py +0 -0
  194. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/json/json_repository.py +0 -0
  195. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/json_format/__init__.py +0 -0
  196. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/json_format/json_process.py +0 -0
  197. {frontengine-1.0.39/frontengine/utils/now_playing → frontengine-1.0.41/frontengine/utils/logging}/__init__.py +0 -0
  198. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/logging/loggin_instance.py +0 -0
  199. {frontengine-1.0.39/frontengine/utils/pet_chat → frontengine-1.0.41/frontengine/utils/measure}/__init__.py +0 -0
  200. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/measure/measure.py +0 -0
  201. {frontengine-1.0.39/frontengine/utils/platform_info → frontengine-1.0.41/frontengine/utils/multi_language}/__init__.py +0 -0
  202. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/france.py +0 -0
  203. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/germany.py +0 -0
  204. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/italy.py +0 -0
  205. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/language_wrapper.py +0 -0
  206. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/russian.py +0 -0
  207. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/multi_language/simplified_chinese.py +0 -0
  208. {frontengine-1.0.39/frontengine/utils/playlist → frontengine-1.0.41/frontengine/utils/now_playing}/__init__.py +0 -0
  209. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/now_playing/now_playing.py +0 -0
  210. {frontengine-1.0.39/frontengine/utils/plugins → frontengine-1.0.41/frontengine/utils/obs}/__init__.py +0 -0
  211. {frontengine-1.0.39/frontengine/utils/power_mode → frontengine-1.0.41/frontengine/utils/pet_chat}/__init__.py +0 -0
  212. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
  213. {frontengine-1.0.39/frontengine/utils/preset_schedule → frontengine-1.0.41/frontengine/utils/platform_info}/__init__.py +0 -0
  214. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/platform_info/platform_info.py +0 -0
  215. {frontengine-1.0.39/frontengine/utils/recording → frontengine-1.0.41/frontengine/utils/playlist}/__init__.py +0 -0
  216. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/playlist/playlist.py +0 -0
  217. {frontengine-1.0.39/frontengine/utils/redirect_manager → frontengine-1.0.41/frontengine/utils/plugins}/__init__.py +0 -0
  218. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/plugins/plugin_loader.py +0 -0
  219. {frontengine-1.0.39/frontengine/utils/reminder → frontengine-1.0.41/frontengine/utils/power_mode}/__init__.py +0 -0
  220. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/power_mode/power_mode.py +0 -0
  221. {frontengine-1.0.39/frontengine/utils/smart_pause → frontengine-1.0.41/frontengine/utils/preset_schedule}/__init__.py +0 -0
  222. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
  223. {frontengine-1.0.39/frontengine/utils/system_stats → frontengine-1.0.41/frontengine/utils/recording}/__init__.py +0 -0
  224. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/recording/frame_recorder.py +0 -0
  225. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/recording/gif_writer.py +0 -0
  226. {frontengine-1.0.39/frontengine/utils/text_source → frontengine-1.0.41/frontengine/utils/redirect_manager}/__init__.py +0 -0
  227. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
  228. {frontengine-1.0.39/frontengine/utils/theme_schedule → frontengine-1.0.41/frontengine/utils/reminder}/__init__.py +0 -0
  229. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/reminder/reminder_service.py +0 -0
  230. {frontengine-1.0.39/frontengine/utils/usage_tracking → frontengine-1.0.41/frontengine/utils/screen_text}/__init__.py +0 -0
  231. {frontengine-1.0.39/frontengine/utils/weather → frontengine-1.0.41/frontengine/utils/smart_pause}/__init__.py +0 -0
  232. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/smart_pause/pause_rules.py +0 -0
  233. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
  234. {frontengine-1.0.39/frontengine/utils/window_pin → frontengine-1.0.41/frontengine/utils/soundboard}/__init__.py +0 -0
  235. {frontengine-1.0.39/frontengine/utils/workshop → frontengine-1.0.41/frontengine/utils/system_stats}/__init__.py +0 -0
  236. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/system_stats/system_stats.py +0 -0
  237. {frontengine-1.0.39/frontengine/worker → frontengine-1.0.41/frontengine/utils/text_source}/__init__.py +0 -0
  238. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/text_source/text_source.py +0 -0
  239. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
  240. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/usage_tracking/usage_service.py +0 -0
  241. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
  242. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/weather/weather_service.py +0 -0
  243. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/web_url.py +0 -0
  244. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/window_pin/window_layout.py +0 -0
  245. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/window_pin/window_pin.py +0 -0
  246. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/utils/workshop/workshop_content.py +0 -0
  247. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine/worker/qthread_worker.py +0 -0
  248. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine.egg-info/dependency_links.txt +0 -0
  249. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine.egg-info/entry_points.txt +0 -0
  250. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine.egg-info/requires.txt +0 -0
  251. {frontengine-1.0.39 → frontengine-1.0.41}/frontengine.egg-info/top_level.txt +0 -0
  252. {frontengine-1.0.39 → frontengine-1.0.41}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frontengine
3
- Version: 1.0.39
3
+ Version: 1.0.41
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
@@ -177,6 +177,25 @@ that caps every overlay's refresh rate and drops its render resolution.
177
177
 
178
178
  ---
179
179
 
180
+ ## Stream and gaming
181
+
182
+ Four things on the **Stream** tab, for playing and for going live:
183
+
184
+ - **Crosshair** — a cross, dot, circle or T shape in the middle of the screen,
185
+ in your colour and size. It is **only drawn on top**: nothing is injected,
186
+ no memory is read, no game file is touched. An exclusive-fullscreen game will
187
+ cover it, which is how the OS stacks windows.
188
+ - **Teleprompter** — scroll a script at an adjustable speed and size, with a
189
+ mirror option for prompter glass. It stops at the last line rather than
190
+ snapping back mid-read.
191
+ - **Soundboard** — up to twelve slots, each with a sound and an optional
192
+ hotkey. A file that has gone missing is dropped rather than kept as a dead
193
+ button.
194
+ - **OBS** — switch scenes and toggle recording or streaming through OBS's own
195
+ WebSocket (built in since OBS 28). The password stays in your local settings.
196
+
197
+ ---
198
+
180
199
  ## Screen time, clipboard and layouts
181
200
 
182
201
  Three things that remember, all of them **off until you switch them on** and
@@ -153,6 +153,25 @@ that caps every overlay's refresh rate and drops its render resolution.
153
153
 
154
154
  ---
155
155
 
156
+ ## Stream and gaming
157
+
158
+ Four things on the **Stream** tab, for playing and for going live:
159
+
160
+ - **Crosshair** — a cross, dot, circle or T shape in the middle of the screen,
161
+ in your colour and size. It is **only drawn on top**: nothing is injected,
162
+ no memory is read, no game file is touched. An exclusive-fullscreen game will
163
+ cover it, which is how the OS stacks windows.
164
+ - **Teleprompter** — scroll a script at an adjustable speed and size, with a
165
+ mirror option for prompter glass. It stops at the last line rather than
166
+ snapping back mid-read.
167
+ - **Soundboard** — up to twelve slots, each with a sound and an optional
168
+ hotkey. A file that has gone missing is dropped rather than kept as a dead
169
+ button.
170
+ - **OBS** — switch scenes and toggle recording or streaming through OBS's own
171
+ WebSocket (built in since OBS 28). The password stays in your local settings.
172
+
173
+ ---
174
+
156
175
  ## Screen time, clipboard and layouts
157
176
 
158
177
  Three things that remember, all of them **off until you switch them on** and
@@ -0,0 +1,160 @@
1
+ """
2
+ 準星覆蓋層:在畫面正中央畫一個自訂準星,點擊穿透,不動到任何遊戲檔案。
3
+
4
+ 這是純粹的疊圖——不注入遊戲、不讀取記憶體、不改動遊戲的任何東西。全螢幕
5
+ 獨佔模式的遊戲會蓋在它上面,那是作業系統的堆疊規則,不是這裡能繞過的。
6
+
7
+ A crosshair overlay: draw a custom crosshair in the middle of the screen,
8
+ click-through, touching no game files.
9
+
10
+ It is only a layer on top - nothing is injected, no memory is read, no game is
11
+ modified. An exclusive-fullscreen game will cover it, which is how the OS
12
+ stacks windows and not something to work around.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from typing import Tuple
17
+
18
+ from PySide6.QtCore import QPointF, Qt
19
+ from PySide6.QtGui import QColor, QPainter, QPen
20
+
21
+ from frontengine.show.base_widget import BaseWidget
22
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
23
+
24
+ STYLE_CROSS = "cross"
25
+ STYLE_DOT = "dot"
26
+ STYLE_CIRCLE = "circle"
27
+ STYLE_T_SHAPE = "t"
28
+ STYLES = (STYLE_CROSS, STYLE_DOT, STYLE_CIRCLE, STYLE_T_SHAPE)
29
+
30
+ DEFAULT_COLOR = "#00ff88"
31
+ DEFAULT_SIZE = 24
32
+ DEFAULT_THICKNESS = 2
33
+ DEFAULT_GAP = 6
34
+ MIN_SIZE = 4
35
+ MAX_SIZE = 200
36
+
37
+
38
+ def normalize_style(style) -> str:
39
+ """把準星樣式正規化;不認得的一律當十字。"""
40
+ name = str(style or "").strip().lower()
41
+ return name if name in STYLES else STYLE_CROSS
42
+
43
+
44
+ def clamp_size(value, fallback: int = DEFAULT_SIZE) -> int:
45
+ """準星尺寸夾在看得見又不誇張的範圍。"""
46
+ try:
47
+ return max(MIN_SIZE, min(MAX_SIZE, int(value)))
48
+ except (TypeError, ValueError):
49
+ return fallback
50
+
51
+
52
+ def clamp_thickness(value, fallback: int = DEFAULT_THICKNESS) -> int:
53
+ """線寬夾在 1~12。"""
54
+ try:
55
+ return max(1, min(12, int(value)))
56
+ except (TypeError, ValueError):
57
+ return fallback
58
+
59
+
60
+ def clamp_gap(value, size: int = DEFAULT_SIZE, fallback: int = DEFAULT_GAP) -> int:
61
+ """
62
+ 中央空隙不能大到把準星吃光,所以上限跟著尺寸走。
63
+ The centre gap cannot swallow the crosshair, so its ceiling follows the size.
64
+ """
65
+ try:
66
+ gap = int(value)
67
+ except (TypeError, ValueError):
68
+ gap = fallback
69
+ return max(0, min(clamp_size(size) - 1, gap))
70
+
71
+
72
+ def arm_segments(size: int, gap: int) -> Tuple[Tuple[float, float], ...]:
73
+ """
74
+ 十字手臂的 (起點, 終點) 距離中心多遠。因為 clamp_gap 保證空隙一定小於
75
+ 尺寸,所以手臂至少會有一個像素長——準星不會被空隙吃到看不見。
76
+ How far each arm starts and ends from the centre. clamp_gap keeps the gap
77
+ strictly inside the size, so an arm is always at least a pixel long: the
78
+ crosshair can never be swallowed by its own gap.
79
+ """
80
+ length = clamp_size(size)
81
+ return ((clamp_gap(gap, length), length),)
82
+
83
+
84
+ class CrosshairWidget(BaseWidget):
85
+ """
86
+ 畫面中央的準星。位置固定在目標螢幕正中,不接受滑鼠(點擊會穿到遊戲)。
87
+ """
88
+
89
+ def __init__(self, style: str = STYLE_CROSS, color: str = DEFAULT_COLOR,
90
+ size: int = DEFAULT_SIZE, thickness: int = DEFAULT_THICKNESS,
91
+ gap: int = DEFAULT_GAP) -> None:
92
+ front_engine_logger.info(f"[CrosshairWidget] Init | style={style}")
93
+ super().__init__()
94
+ self.style = normalize_style(style)
95
+ candidate = QColor(color)
96
+ self.color = candidate if candidate.isValid() else QColor(DEFAULT_COLOR)
97
+ self.size = clamp_size(size)
98
+ self.thickness = clamp_thickness(thickness)
99
+ self.gap = clamp_gap(gap, self.size)
100
+ self.opacity = 1.0
101
+ self.resize(self.size * 2 + 8, self.size * 2 + 8)
102
+
103
+ def set_crosshair(self, style=None, color=None, size=None,
104
+ thickness=None, gap=None) -> None:
105
+ """一次改好幾項設定,改完立刻重畫。"""
106
+ if style is not None:
107
+ self.style = normalize_style(style)
108
+ if color is not None:
109
+ candidate = QColor(color)
110
+ if candidate.isValid():
111
+ self.color = candidate
112
+ if size is not None:
113
+ self.size = clamp_size(size)
114
+ if thickness is not None:
115
+ self.thickness = clamp_thickness(thickness)
116
+ if gap is not None or size is not None:
117
+ self.gap = clamp_gap(self.gap if gap is None else gap, self.size)
118
+ self.resize(self.size * 2 + 8, self.size * 2 + 8)
119
+ self.centre_on_screen()
120
+ self.update()
121
+
122
+ def centre_on_screen(self, screen=None) -> None:
123
+ """擺到螢幕正中央(準星要對準的是畫面中心)。"""
124
+ from PySide6.QtGui import QGuiApplication
125
+
126
+ target = screen or self.screen() or QGuiApplication.primaryScreen()
127
+ if target is None: # pragma: no cover - no screen at all
128
+ return
129
+ geometry = target.geometry()
130
+ self.move(geometry.x() + (geometry.width() - self.width()) // 2,
131
+ geometry.y() + (geometry.height() - self.height()) // 2)
132
+
133
+ def draw_content(self, painter: QPainter) -> None:
134
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
135
+ pen = QPen(self.color, self.thickness)
136
+ pen.setCapStyle(Qt.PenCapStyle.FlatCap)
137
+ painter.setPen(pen)
138
+ centre = QPointF(self.width() / 2.0, self.height() / 2.0)
139
+ if self.style == STYLE_DOT:
140
+ painter.setBrush(self.color)
141
+ painter.drawEllipse(centre, self.thickness, self.thickness)
142
+ return
143
+ if self.style == STYLE_CIRCLE:
144
+ painter.setBrush(Qt.BrushStyle.NoBrush)
145
+ painter.drawEllipse(centre, float(self.size), float(self.size))
146
+ return
147
+ self._draw_arms(painter, centre)
148
+
149
+ def _draw_arms(self, painter: QPainter, centre: QPointF) -> None:
150
+ """十字與 T 字共用的手臂繪製(T 字少了上面那一隻)。"""
151
+ for inner, outer in arm_segments(self.size, self.gap):
152
+ painter.drawLine(QPointF(centre.x() - outer, centre.y()),
153
+ QPointF(centre.x() - inner, centre.y()))
154
+ painter.drawLine(QPointF(centre.x() + inner, centre.y()),
155
+ QPointF(centre.x() + outer, centre.y()))
156
+ painter.drawLine(QPointF(centre.x(), centre.y() + inner),
157
+ QPointF(centre.x(), centre.y() + outer))
158
+ if self.style != STYLE_T_SHAPE:
159
+ painter.drawLine(QPointF(centre.x(), centre.y() - outer),
160
+ QPointF(centre.x(), centre.y() - inner))
@@ -0,0 +1,171 @@
1
+ """
2
+ 提詞機:把講稿以可調速度往上捲,字夠大、可以左右鏡像(配提詞玻璃用)。
3
+
4
+ 捲動用「每次前進幾個像素」而不是「每次一行」,這樣速度才會平順;到底之後
5
+ 會停在最後一行,不會自己跳回開頭把人嚇一跳。
6
+
7
+ A teleprompter: scroll a script upward at an adjustable speed, in large type,
8
+ optionally mirrored for prompter glass.
9
+
10
+ Scrolling advances by pixels rather than by lines so the motion stays smooth,
11
+ and it stops at the last line rather than snapping back to the top mid-read.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from typing import List, Optional
16
+
17
+ from PySide6.QtCore import Qt, QTimer
18
+ from PySide6.QtGui import QColor, QFont, QFontMetrics, QPainter, QTransform
19
+
20
+ from frontengine.show.base_widget import BaseWidget
21
+ from frontengine.utils.logging.loggin_instance import front_engine_logger
22
+ from frontengine.utils.power_mode.power_mode import tier_interval
23
+
24
+ DEFAULT_SPEED = 40 # 像素/秒 / pixels per second
25
+ MIN_SPEED = 5
26
+ MAX_SPEED = 400
27
+ DEFAULT_FONT_SIZE = 32
28
+ MIN_FONT_SIZE = 10
29
+ MAX_FONT_SIZE = 120
30
+ _TICK_MS = 33
31
+ _SIDE_MARGIN = 40
32
+
33
+
34
+ def clamp_speed(value, fallback: int = DEFAULT_SPEED) -> int:
35
+ """捲動速度夾在讀得完又跟得上的範圍。"""
36
+ try:
37
+ return max(MIN_SPEED, min(MAX_SPEED, int(value)))
38
+ except (TypeError, ValueError):
39
+ return fallback
40
+
41
+
42
+ def clamp_font_size(value, fallback: int = DEFAULT_FONT_SIZE) -> int:
43
+ """字級夾在看得見的範圍。"""
44
+ try:
45
+ return max(MIN_FONT_SIZE, min(MAX_FONT_SIZE, int(value)))
46
+ except (TypeError, ValueError):
47
+ return fallback
48
+
49
+
50
+ def wrap_lines(text: str, metrics: QFontMetrics, width: int) -> List[str]:
51
+ """
52
+ 依實際字寬把講稿折行;空行會保留(那是段落之間的呼吸)。
53
+ Wrap the script to the actual text width, keeping blank lines - they are the
54
+ pauses between paragraphs.
55
+ """
56
+ usable = max(40, int(width))
57
+ lines: List[str] = []
58
+ for paragraph in str(text or "").splitlines():
59
+ if not paragraph.strip():
60
+ lines.append("")
61
+ continue
62
+ current = ""
63
+ for word in paragraph.split():
64
+ candidate = f"{current} {word}".strip()
65
+ if current and metrics.horizontalAdvance(candidate) > usable:
66
+ lines.append(current)
67
+ current = word
68
+ else:
69
+ current = candidate
70
+ if current:
71
+ lines.append(current)
72
+ return lines
73
+
74
+
75
+ class TeleprompterWidget(BaseWidget):
76
+ """
77
+ 提詞機覆蓋層。捲動位置以像素計,速度、字級與鏡像都可以邊跑邊調。
78
+ """
79
+
80
+ def __init__(self, text: str = "", speed: int = DEFAULT_SPEED,
81
+ font_size: int = DEFAULT_FONT_SIZE, mirrored: bool = False) -> None:
82
+ front_engine_logger.info("[TeleprompterWidget] Init")
83
+ super().__init__()
84
+ self.script = str(text)
85
+ self.speed = clamp_speed(speed)
86
+ self.mirrored = bool(mirrored)
87
+ self.text_color = QColor("#ffffff")
88
+ self.opacity = 0.9
89
+ self.offset = 0.0
90
+ self._font = QFont()
91
+ self._font.setPointSize(clamp_font_size(font_size))
92
+ self._timer = QTimer(self)
93
+ self._timer.timeout.connect(self.advance)
94
+
95
+ @property
96
+ def font_size(self) -> int:
97
+ return self._font.pointSize()
98
+
99
+ def set_script(self, text: str) -> None:
100
+ """換一份講稿並回到開頭。"""
101
+ self.script = str(text)
102
+ self.offset = 0.0
103
+ self.update()
104
+
105
+ def set_speed(self, speed: int) -> None:
106
+ self.speed = clamp_speed(speed)
107
+
108
+ def set_font_size(self, size: int) -> None:
109
+ self._font.setPointSize(clamp_font_size(size))
110
+ self.update()
111
+
112
+ def set_mirrored(self, mirrored: bool) -> None:
113
+ """左右鏡像:透過提詞玻璃反射後才是正的。"""
114
+ self.mirrored = bool(mirrored)
115
+ self.update()
116
+
117
+ def lines(self) -> List[str]:
118
+ """目前折行後的講稿。"""
119
+ return wrap_lines(self.script, QFontMetrics(self._font),
120
+ max(40, self.width() - _SIDE_MARGIN * 2))
121
+
122
+ def content_height(self) -> int:
123
+ """整份講稿捲完需要多高。"""
124
+ return len(self.lines()) * QFontMetrics(self._font).lineSpacing()
125
+
126
+ def max_offset(self) -> float:
127
+ """捲到底的位置(不會捲過頭)。"""
128
+ return float(max(0, self.content_height() - self.height() // 2))
129
+
130
+ def start(self) -> None:
131
+ self._timer.start(tier_interval(_TICK_MS, self.quality_tier))
132
+
133
+ def stop(self) -> None:
134
+ self._timer.stop()
135
+
136
+ def apply_quality_tier(self) -> None:
137
+ if self._timer.isActive():
138
+ self._timer.start(tier_interval(_TICK_MS, self.quality_tier))
139
+
140
+ def rewind(self) -> None:
141
+ """回到講稿開頭。"""
142
+ self.offset = 0.0
143
+ self.update()
144
+
145
+ def advance(self, seconds: Optional[float] = None) -> float:
146
+ """
147
+ 前進一小段(預設一個 tick 的量),回傳新的捲動位置。捲到底就停住。
148
+ Advance one tick's worth and return the new offset, stopping at the end.
149
+ """
150
+ step = self.speed * (seconds if seconds is not None else _TICK_MS / 1000.0)
151
+ self.offset = min(self.max_offset(), self.offset + step)
152
+ self.update()
153
+ return self.offset
154
+
155
+ def draw_content(self, painter: QPainter) -> None:
156
+ painter.setFont(self._font)
157
+ painter.setPen(self.text_color)
158
+ if self.mirrored:
159
+ painter.setTransform(QTransform().translate(self.width(), 0).scale(-1, 1))
160
+ spacing = QFontMetrics(self._font).lineSpacing()
161
+ top = self.height() // 2 - int(self.offset)
162
+ for index, line in enumerate(self.lines()):
163
+ y = top + index * spacing
164
+ if y < -spacing or y > self.height():
165
+ continue
166
+ painter.drawText(_SIDE_MARGIN, y, self.width() - _SIDE_MARGIN * 2, spacing,
167
+ Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, line)
168
+
169
+ def closeEvent(self, event) -> None:
170
+ self.stop()
171
+ super().closeEvent(event)
@@ -29,6 +29,7 @@ from frontengine.ui.page.scene_setting.scene_setting_ui import SceneSettingUI
29
29
  from frontengine.ui.page.presentation.presentation_setting_ui import PresentationSettingUI
30
30
  from frontengine.ui.page.screen_care.screen_care_setting_ui import ScreenCareSettingUI
31
31
  from frontengine.ui.page.sound_player.sound_player_setting_ui import SoundPlayerSettingUI
32
+ from frontengine.ui.page.stream.stream_setting_ui import StreamSettingUI
32
33
  from frontengine.ui.page.text.text_setting_ui import TextSettingUI
33
34
  from frontengine.ui.page.tools.tools_setting_ui import ToolsSettingUI
34
35
  from frontengine.ui.page.video.video_setting_ui import VideoSettingUI
@@ -126,6 +127,7 @@ class FrontEngineMainUI(QMainWindow):
126
127
  self.focus_setting_ui = FocusSettingUI()
127
128
  self.widgets_setting_ui = WidgetsSettingUI()
128
129
  self.tools_setting_ui = ToolsSettingUI()
130
+ self.stream_setting_ui = StreamSettingUI()
129
131
 
130
132
  # 控制中心
131
133
  # Control Center
@@ -298,6 +300,9 @@ class FrontEngineMainUI(QMainWindow):
298
300
  for attribute in ("measure_widget_list", "capture_widget_list", "camera_widget_list"):
299
301
  self.control_center_ui.register_overlay_source(
300
302
  lambda attribute=attribute: getattr(self.tools_setting_ui, attribute, []))
303
+ for attribute in ("crosshair_widget_list", "teleprompter_widget_list"):
304
+ self.control_center_ui.register_overlay_source(
305
+ lambda attribute=attribute: getattr(self.stream_setting_ui, attribute, []))
301
306
 
302
307
  def _add_tabs(self) -> None:
303
308
  """加入所有內建與擴充的 Tab / Add all built-in and extended tabs"""
@@ -317,6 +322,7 @@ class FrontEngineMainUI(QMainWindow):
317
322
  (self.focus_setting_ui, "tab_focus_text"),
318
323
  (self.widgets_setting_ui, "tab_widgets_text"),
319
324
  (self.tools_setting_ui, "tab_tools_text"),
325
+ (self.stream_setting_ui, "tab_stream_text"),
320
326
  (self.control_center_ui, "tab_control_center_text"),
321
327
  ]
322
328
 
@@ -518,6 +524,8 @@ class FrontEngineMainUI(QMainWindow):
518
524
  if getattr(self, "tools_setting_ui", None) is not None:
519
525
  self.tools_setting_ui.release_pinned_windows()
520
526
  self.tools_setting_ui.stop_camera()
527
+ if getattr(self, "stream_setting_ui", None) is not None:
528
+ self.stream_setting_ui.soundboard.stop_all()
521
529
  if getattr(self, "reminder_service", None) is not None:
522
530
  self.reminder_service.stop()
523
531
  if getattr(self, "app_profile_service", None) is not None:
@@ -320,7 +320,7 @@ class ScreenCareSettingUI(QWidget):
320
320
  language_wrapper.language_word_dict.get("color_vision_start", "Simulate"))
321
321
 
322
322
  def _apply_color_vision_settings(self) -> None:
323
- for widget in list(self.color_vision_widget_list):
323
+ for widget in self.color_vision_widget_list[:]:
324
324
  try:
325
325
  widget.set_simulation(self.color_vision_combobox.currentData(),
326
326
  self.color_vision_slider.value() / 100.0)