frontengine 1.0.65__tar.gz → 1.0.66__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.
- {frontengine-1.0.65 → frontengine-1.0.66}/PKG-INFO +1 -1
- frontengine-1.0.66/frontengine/show/replica/replica_widget.py +118 -0
- frontengine-1.0.66/frontengine/ui/dialog/window_replica_dialog.py +124 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/main_ui.py +4 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/tools/tools_setting_ui.py +22 -1
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/english.py +7 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/france.py +7 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/germany.py +7 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/italy.py +7 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/russian.py +7 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/simplified_chinese.py +7 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/traditional_chinese.py +7 -0
- frontengine-1.0.66/frontengine/utils/window_replica/__init__.py +0 -0
- frontengine-1.0.66/frontengine/utils/window_replica/dwm_thumbnail.py +189 -0
- frontengine-1.0.66/frontengine/utils/workshop/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine.egg-info/PKG-INFO +1 -1
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine.egg-info/SOURCES.txt +5 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/pyproject.toml +1 -1
- {frontengine-1.0.65 → frontengine-1.0.66}/LICENSE +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/README.md +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/__main__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/base_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/camera/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/camera/camera_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/canvas/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/canvas/whiteboard_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/capture/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/capture/region_capture.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/focus_shield/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/focus_shield/focus_shield_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/gif/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/gif/paint_gif.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/image/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/image/paint_image.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/load/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/load/load_someone_make_ui.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/measure/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/measure/measure_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/monitor/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/monitor/monitor_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/notes/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/notes/sticky_note_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/overlay_factory.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/particle/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/particle/particle_ui.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/pet/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/pet/desktop_pet.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/presentation/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/presentation/annotation_overlay.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/presentation/cursor_effects.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/presentation/keystroke_display.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/presentation/magnifier.py +0 -0
- {frontengine-1.0.65/frontengine/show/scene → frontengine-1.0.66/frontengine/show/replica}/__init__.py +0 -0
- {frontengine-1.0.65/frontengine/show/screen_care → frontengine-1.0.66/frontengine/show/scene}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/scene/extend_graphic_scene.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/scene/extend_graphic_view.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/scene/scene.py +0 -0
- {frontengine-1.0.65/frontengine/show/sound_player → frontengine-1.0.66/frontengine/show/screen_care}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/screen_care/color_vision_widget.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/screen_care/screen_filter.py +0 -0
- {frontengine-1.0.65/frontengine/show/spectrum → frontengine-1.0.66/frontengine/show/sound_player}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/sound_player/sound_effect.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/sound_player/sound_player.py +0 -0
- {frontengine-1.0.65/frontengine/show/text → frontengine-1.0.66/frontengine/show/spectrum}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/spectrum/spectrum_widget.py +0 -0
- {frontengine-1.0.65/frontengine/show/toast → frontengine-1.0.66/frontengine/show/text}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/text/draw_text.py +0 -0
- {frontengine-1.0.65/frontengine/show/video → frontengine-1.0.66/frontengine/show/toast}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/toast/toast_widget.py +0 -0
- {frontengine-1.0.65/frontengine/show/wallpaper → frontengine-1.0.66/frontengine/show/video}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/video/video_player.py +0 -0
- {frontengine-1.0.65/frontengine/show/web → frontengine-1.0.66/frontengine/show/wallpaper}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/wallpaper/wallpaper_widget.py +0 -0
- {frontengine-1.0.65/frontengine/system_tray → frontengine-1.0.66/frontengine/show/web}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/web/webview.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/show/window_helpers.py +0 -0
- {frontengine-1.0.65/frontengine/ui → frontengine-1.0.66/frontengine/system_tray}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/system_tray/extend_system_tray.py +0 -0
- {frontengine-1.0.65/frontengine/ui/color → frontengine-1.0.66/frontengine/ui}/__init__.py +0 -0
- {frontengine-1.0.65/frontengine/ui/dialog → frontengine-1.0.66/frontengine/ui/color}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/color/global_color.py +0 -0
- {frontengine-1.0.65/frontengine/ui/menu → frontengine-1.0.66/frontengine/ui/dialog}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/app_profile_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/choose_file_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/clipboard_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/hotkey_settings_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/how_to_use_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/reminder_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/remote_control_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/screen_privacy_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/screen_text_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/screensaver_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/signage_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/smart_pause_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/usage_report_dialog.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/dialog/window_pin_dialog.py +0 -0
- {frontengine-1.0.65/frontengine/ui/nav → frontengine-1.0.66/frontengine/ui/menu}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/menu/help_menu.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/menu/how_to_menu.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/menu/language_menu.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/menu/preset_menu.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/menu/settings_menu.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page → frontengine-1.0.66/frontengine/ui/nav}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/nav/sidebar.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/control_center → frontengine-1.0.66/frontengine/ui/page}/__init__.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/focus → frontengine-1.0.66/frontengine/ui/page/control_center}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/control_center/control_center_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/gif → frontengine-1.0.66/frontengine/ui/page/focus}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/focus/focus_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/image → frontengine-1.0.66/frontengine/ui/page/gif}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/gif/gif_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/particle → frontengine-1.0.66/frontengine/ui/page/image}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/image/image_setting_ui.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/layout_kit.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/pet → frontengine-1.0.66/frontengine/ui/page/particle}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/particle/particle_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/presentation → frontengine-1.0.66/frontengine/ui/page/pet}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/pet/pet_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/scene_setting → frontengine-1.0.66/frontengine/ui/page/presentation}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/presentation/presentation_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/scene_setting/scene_page → frontengine-1.0.66/frontengine/ui/page/scene_setting}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_manager.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/screen_care → frontengine-1.0.66/frontengine/ui/page/scene_setting/scene_page}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/base_scene_page.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/gif.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/image.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/registry.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/sound.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/text.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/video.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_page/web.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/scene_setting/scene_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/sound_player → frontengine-1.0.66/frontengine/ui/page/screen_care}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/screen_care/screen_care_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/text → frontengine-1.0.66/frontengine/ui/page/sound_player}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/sound_player/sound_player_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/tools → frontengine-1.0.66/frontengine/ui/page/text}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/text/text_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/video → frontengine-1.0.66/frontengine/ui/page/tools}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/utils.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/wallpaper → frontengine-1.0.66/frontengine/ui/page/video}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/video/video_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/web → frontengine-1.0.66/frontengine/ui/page/wallpaper}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/wallpaper/wallpaper_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/page/widgets → frontengine-1.0.66/frontengine/ui/page/web}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/web/web_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/ui/style → frontengine-1.0.66/frontengine/ui/page/widgets}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/page/widgets/widgets_setting_ui.py +0 -0
- {frontengine-1.0.65/frontengine/user_setting → frontengine-1.0.66/frontengine/ui/style}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/ui/style/app_style.py +0 -0
- {frontengine-1.0.65/frontengine/utils → frontengine-1.0.66/frontengine/user_setting}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/user_setting/preset_repository.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/user_setting/scene_setting.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/user_setting/user_setting_file.py +0 -0
- {frontengine-1.0.65/frontengine/utils/app_profile → frontengine-1.0.66/frontengine/utils}/__init__.py +0 -0
- {frontengine-1.0.65/frontengine/utils/audio_meter → frontengine-1.0.66/frontengine/utils/app_profile}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/app_profile/app_profile_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/autostart → frontengine-1.0.66/frontengine/utils/audio_meter}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/audio_meter/audio_envelope.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/audio_meter/loopback_capture.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/audio_meter/microphone_meter.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/audio_meter/screen_audio.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/audio_meter/spectrum_analyzer.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/audio_meter/system_audio_meter.py +0 -0
- {frontengine-1.0.65/frontengine/utils/break_reminder → frontengine-1.0.66/frontengine/utils/autostart}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/autostart/autostart_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/browser → frontengine-1.0.66/frontengine/utils/break_reminder}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/break_reminder/break_reminder.py +0 -0
- {frontengine-1.0.65/frontengine/utils/clipboard → frontengine-1.0.66/frontengine/utils/browser}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/browser/browser.py +0 -0
- {frontengine-1.0.65/frontengine/utils/color_vision → frontengine-1.0.66/frontengine/utils/clipboard}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/clipboard/clipboard_history.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/clipboard/clipboard_watcher.py +0 -0
- {frontengine-1.0.65/frontengine/utils/critical_exit → frontengine-1.0.66/frontengine/utils/color_vision}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/color_vision/color_vision.py +0 -0
- {frontengine-1.0.65/frontengine/utils/focus_shield → frontengine-1.0.66/frontengine/utils/critical_exit}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/critical_exit/check_key_is_press.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/critical_exit/critical_exit.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/critical_exit/win32_vk.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/exception/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/exception/exception_tags.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/exception/exceptions.py +0 -0
- {frontengine-1.0.65/frontengine/utils/focus_timer → frontengine-1.0.66/frontengine/utils/focus_shield}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/focus_shield/focus_shield.py +0 -0
- {frontengine-1.0.65/frontengine/utils/hotkey → frontengine-1.0.66/frontengine/utils/focus_timer}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/focus_timer/focus_timer.py +0 -0
- {frontengine-1.0.65/frontengine/utils/input_watch → frontengine-1.0.66/frontengine/utils/hotkey}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/hotkey/hotkey_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/json → frontengine-1.0.66/frontengine/utils/input_watch}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/input_watch/input_watch_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/logging → frontengine-1.0.66/frontengine/utils/json}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/json/json_file.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/json/json_repository.py +0 -0
- {frontengine-1.0.65/frontengine/utils/measure → frontengine-1.0.66/frontengine/utils/logging}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/logging/loggin_instance.py +0 -0
- {frontengine-1.0.65/frontengine/utils/midi → frontengine-1.0.66/frontengine/utils/measure}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/measure/measure.py +0 -0
- {frontengine-1.0.65/frontengine/utils/multi_language → frontengine-1.0.66/frontengine/utils/midi}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/midi/midi_input.py +0 -0
- {frontengine-1.0.65/frontengine/utils/now_playing → frontengine-1.0.66/frontengine/utils/multi_language}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/language_wrapper.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/retranslate.py +0 -0
- {frontengine-1.0.65/frontengine/utils/pet_chat → frontengine-1.0.66/frontengine/utils/now_playing}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/now_playing/now_playing.py +0 -0
- {frontengine-1.0.65/frontengine/utils/platform_info → frontengine-1.0.66/frontengine/utils/pet_chat}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/pet_chat/pet_chat_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/playlist → frontengine-1.0.66/frontengine/utils/platform_info}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/platform_info/platform_info.py +0 -0
- {frontengine-1.0.65/frontengine/utils/plugins → frontengine-1.0.66/frontengine/utils/playlist}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/playlist/playlist.py +0 -0
- {frontengine-1.0.65/frontengine/utils/power_mode → frontengine-1.0.66/frontengine/utils/plugins}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/plugins/plugin_loader.py +0 -0
- {frontengine-1.0.65/frontengine/utils/preset_schedule → frontengine-1.0.66/frontengine/utils/power_mode}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/power_mode/power_mode.py +0 -0
- {frontengine-1.0.65/frontengine/utils/recording → frontengine-1.0.66/frontengine/utils/preset_schedule}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/preset_schedule/preset_schedule_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/redirect_manager → frontengine-1.0.66/frontengine/utils/recording}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/recording/frame_recorder.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/recording/gif_writer.py +0 -0
- {frontengine-1.0.65/frontengine/utils/reminder → frontengine-1.0.66/frontengine/utils/redirect_manager}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/redirect_manager/redirect_manager_class.py +0 -0
- {frontengine-1.0.65/frontengine/utils/remote → frontengine-1.0.66/frontengine/utils/reminder}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/reminder/reminder_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/screen_privacy → frontengine-1.0.66/frontengine/utils/remote}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/remote/remote_server.py +0 -0
- {frontengine-1.0.65/frontengine/utils/screen_text → frontengine-1.0.66/frontengine/utils/screen_privacy}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/screen_privacy/capture_affinity.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/screen_privacy/share_watch.py +0 -0
- {frontengine-1.0.65/frontengine/utils/screensaver → frontengine-1.0.66/frontengine/utils/screen_text}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/screen_text/screen_text_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/signage → frontengine-1.0.66/frontengine/utils/screensaver}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/screensaver/screensaver_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/smart_pause → frontengine-1.0.66/frontengine/utils/signage}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/signage/signage_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/steam → frontengine-1.0.66/frontengine/utils/smart_pause}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/smart_pause/pause_rules.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/smart_pause/smart_pause_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/system_stats → frontengine-1.0.66/frontengine/utils/steam}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/steam/steam_language.py +0 -0
- {frontengine-1.0.65/frontengine/utils/text_source → frontengine-1.0.66/frontengine/utils/system_stats}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/system_stats/system_stats.py +0 -0
- {frontengine-1.0.65/frontengine/utils/theme_schedule → frontengine-1.0.66/frontengine/utils/text_source}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/text_source/text_source.py +0 -0
- {frontengine-1.0.65/frontengine/utils/usage_tracking → frontengine-1.0.66/frontengine/utils/theme_schedule}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/theme_schedule/theme_schedule_service.py +0 -0
- {frontengine-1.0.65/frontengine/utils/virtual_camera → frontengine-1.0.66/frontengine/utils/usage_tracking}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/usage_tracking/usage_service.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/usage_tracking/usage_tracker.py +0 -0
- {frontengine-1.0.65/frontengine/utils/weather → frontengine-1.0.66/frontengine/utils/virtual_camera}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/virtual_camera/camera_feed.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/virtual_camera/virtual_camera.py +0 -0
- {frontengine-1.0.65/frontengine/utils/window_pin → frontengine-1.0.66/frontengine/utils/weather}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/weather/weather_service.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/web_url.py +0 -0
- {frontengine-1.0.65/frontengine/utils/workshop → frontengine-1.0.66/frontengine/utils/window_pin}/__init__.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/window_pin/window_layout.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/window_pin/window_pin.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/workshop/workshop_content.py +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine.egg-info/dependency_links.txt +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine.egg-info/entry_points.txt +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine.egg-info/requires.txt +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/frontengine.egg-info/top_level.txt +0 -0
- {frontengine-1.0.65 → frontengine-1.0.66}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontengine
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.66
|
|
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,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
視窗複本覆蓋層:一個小視窗,裡面是另一個視窗的即時畫面。
|
|
3
|
+
|
|
4
|
+
和其他覆蓋層不同,這一個**不能點擊穿透**:使用者要能把它拖到順手的位置、拉大縮小、
|
|
5
|
+
關掉。所以它是一個可以拖曳的無邊框視窗,而不是一層蓋在畫面上的東西。
|
|
6
|
+
|
|
7
|
+
A window replica overlay: a small window showing another window, live.
|
|
8
|
+
|
|
9
|
+
Unlike the other overlays this one must **not** pass clicks through: the user has
|
|
10
|
+
to be able to drag it somewhere convenient, resize it and close it. So it is a
|
|
11
|
+
draggable frameless window rather than a sheet laid over the screen.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
from PySide6.QtCore import QPoint, Qt
|
|
18
|
+
from PySide6.QtGui import QColor, QPainter
|
|
19
|
+
from PySide6.QtWidgets import QWidget
|
|
20
|
+
|
|
21
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
22
|
+
from frontengine.utils.window_replica.dwm_thumbnail import (
|
|
23
|
+
DEFAULT_REPLICA_WIDTH, MIN_REPLICA_SIZE, DwmThumbnail, available, fit_within,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class WindowReplicaWidget(QWidget):
|
|
28
|
+
"""顯示另一個視窗即時畫面的小視窗。"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, source_handle: int, title: str = "",
|
|
31
|
+
opacity_percent: int = 100, parent: Optional[QWidget] = None) -> None:
|
|
32
|
+
front_engine_logger.info(f"[WindowReplicaWidget] Init | source={source_handle}")
|
|
33
|
+
super().__init__(parent)
|
|
34
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
35
|
+
self.setWindowFlags(
|
|
36
|
+
Qt.WindowType.FramelessWindowHint
|
|
37
|
+
| Qt.WindowType.WindowStaysOnTopHint
|
|
38
|
+
| Qt.WindowType.Tool)
|
|
39
|
+
self.setWindowTitle(title or "FrontEngine")
|
|
40
|
+
self.setMinimumSize(MIN_REPLICA_SIZE, MIN_REPLICA_SIZE)
|
|
41
|
+
|
|
42
|
+
self.source_handle = int(source_handle)
|
|
43
|
+
self.source_title = title
|
|
44
|
+
self.opacity_percent = max(1, min(100, int(opacity_percent)))
|
|
45
|
+
self.thumbnail = DwmThumbnail()
|
|
46
|
+
self._drag_origin: Optional[QPoint] = None
|
|
47
|
+
|
|
48
|
+
self.resize(DEFAULT_REPLICA_WIDTH, int(DEFAULT_REPLICA_WIDTH * 9 / 16))
|
|
49
|
+
|
|
50
|
+
def start(self) -> bool:
|
|
51
|
+
"""
|
|
52
|
+
接上來源視窗並依它的長寬比調整大小。接不上就回 False,呼叫端不要把一個
|
|
53
|
+
空視窗留在畫面上。
|
|
54
|
+
Attach to the source and take its aspect ratio. False when it could not
|
|
55
|
+
attach, so the caller does not leave an empty window on screen.
|
|
56
|
+
"""
|
|
57
|
+
if not available():
|
|
58
|
+
front_engine_logger.info("[WindowReplicaWidget] not supported on this platform")
|
|
59
|
+
return False
|
|
60
|
+
# 縮圖要接在「這個視窗的 handle」上,所以必須先讓它有 handle。
|
|
61
|
+
# The thumbnail attaches to this window's handle, so it needs one first.
|
|
62
|
+
self.show()
|
|
63
|
+
if not self.thumbnail.register(int(self.winId()), self.source_handle):
|
|
64
|
+
return False
|
|
65
|
+
source_size = self.thumbnail.source_size()
|
|
66
|
+
width, height = fit_within(source_size, (DEFAULT_REPLICA_WIDTH,
|
|
67
|
+
int(DEFAULT_REPLICA_WIDTH * 9 / 16)))
|
|
68
|
+
self.resize(width, height)
|
|
69
|
+
self._refresh()
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
def _refresh(self) -> None:
|
|
73
|
+
self.thumbnail.update(self.width(), self.height(),
|
|
74
|
+
opacity=int(self.opacity_percent * 255 / 100))
|
|
75
|
+
|
|
76
|
+
def set_opacity_percent(self, percent: int) -> None:
|
|
77
|
+
self.opacity_percent = max(1, min(100, int(percent)))
|
|
78
|
+
self._refresh()
|
|
79
|
+
|
|
80
|
+
def resizeEvent(self, event) -> None:
|
|
81
|
+
super().resizeEvent(event)
|
|
82
|
+
self._refresh()
|
|
83
|
+
|
|
84
|
+
def paintEvent(self, event) -> None:
|
|
85
|
+
# DWM 把縮圖合成上來之前,先鋪一層不透明底色。少了這一步,來源還沒畫上來的
|
|
86
|
+
# 那一瞬間會透出後面的東西,看起來像破圖。
|
|
87
|
+
# An opaque backing before DWM composites the thumbnail. Without it the
|
|
88
|
+
# moment before the source arrives shows whatever is behind, which reads
|
|
89
|
+
# as a rendering fault.
|
|
90
|
+
painter = QPainter(self)
|
|
91
|
+
painter.fillRect(self.rect(), QColor(0, 0, 0))
|
|
92
|
+
|
|
93
|
+
def mousePressEvent(self, event) -> None:
|
|
94
|
+
if event.button() == Qt.MouseButton.LeftButton:
|
|
95
|
+
self._drag_origin = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
|
96
|
+
event.accept()
|
|
97
|
+
|
|
98
|
+
def mouseMoveEvent(self, event) -> None:
|
|
99
|
+
if self._drag_origin is not None and event.buttons() & Qt.MouseButton.LeftButton:
|
|
100
|
+
self.move(event.globalPosition().toPoint() - self._drag_origin)
|
|
101
|
+
event.accept()
|
|
102
|
+
|
|
103
|
+
def mouseReleaseEvent(self, event) -> None:
|
|
104
|
+
self._drag_origin = None
|
|
105
|
+
event.accept()
|
|
106
|
+
|
|
107
|
+
def mouseDoubleClickEvent(self, event) -> None:
|
|
108
|
+
"""雙擊關掉——沒有標題列,總得有一個看得懂的出口。"""
|
|
109
|
+
self.close()
|
|
110
|
+
event.accept()
|
|
111
|
+
|
|
112
|
+
def closeEvent(self, event) -> None:
|
|
113
|
+
# 縮圖是系統資源,視窗沒了也不會自己消失。從標題列以外的任何路徑關閉時
|
|
114
|
+
# closeEvent 是唯一會被呼叫到的地方,所以收尾放這裡。
|
|
115
|
+
# The thumbnail is a system resource and does not go away with the
|
|
116
|
+
# window. closeEvent is the one place every close path reaches.
|
|
117
|
+
self.thumbnail.unregister()
|
|
118
|
+
super().closeEvent(event)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
挑一個視窗,開一個顯示它即時畫面的小視窗。
|
|
3
|
+
|
|
4
|
+
和「釘選視窗」的差別在於原視窗動不動:釘選會把它搬到最上層,複本讓它留在原地。
|
|
5
|
+
Pick a window and open a small one showing it live. The difference from pinning
|
|
6
|
+
is whether the original moves: pinning brings it to the top, a replica leaves it
|
|
7
|
+
where it is.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import List, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
from PySide6.QtCore import Qt
|
|
14
|
+
from PySide6.QtWidgets import (
|
|
15
|
+
QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel, QListWidget,
|
|
16
|
+
QListWidgetItem, QPushButton, QSlider, QWidget,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from frontengine.show.replica.replica_widget import WindowReplicaWidget
|
|
20
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
21
|
+
from frontengine.utils.multi_language.language_wrapper import language_wrapper
|
|
22
|
+
from frontengine.utils.multi_language.retranslate import retranslator, tr
|
|
23
|
+
from frontengine.utils.window_pin import window_pin
|
|
24
|
+
from frontengine.utils.window_replica.dwm_thumbnail import available
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _t(key: str, fallback: str) -> str:
|
|
28
|
+
return language_wrapper.language_word_dict.get(key, fallback)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class WindowReplicaDialog(QDialog):
|
|
32
|
+
"""列出目前的視窗,讓使用者挑一個做成即時複本。"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, parent: Optional[QWidget] = None, lister=None,
|
|
35
|
+
replica_factory=WindowReplicaWidget) -> None:
|
|
36
|
+
front_engine_logger.info("[WindowReplicaDialog] Init")
|
|
37
|
+
super().__init__(parent)
|
|
38
|
+
self.setWindowTitle(_t("window_replica_title", "Replicate a window"))
|
|
39
|
+
self._lister = lister or window_pin.list_windows
|
|
40
|
+
self._replica_factory = replica_factory
|
|
41
|
+
self.replica_widget_list: List[WindowReplicaWidget] = []
|
|
42
|
+
|
|
43
|
+
self.window_list = QListWidget()
|
|
44
|
+
self.window_list.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
|
45
|
+
self.refresh_button = tr(QPushButton(), "window_replica_refresh", "Refresh")
|
|
46
|
+
self.refresh_button.clicked.connect(self.reload_windows)
|
|
47
|
+
self.show_button = tr(QPushButton(), "window_replica_show", "Show replica")
|
|
48
|
+
self.show_button.clicked.connect(self.start_replica)
|
|
49
|
+
|
|
50
|
+
self.opacity_label = tr(QLabel(), "window_replica_opacity", "Opacity")
|
|
51
|
+
self.opacity_slider = QSlider(Qt.Orientation.Horizontal)
|
|
52
|
+
self.opacity_slider.setRange(10, 100)
|
|
53
|
+
self.opacity_slider.setValue(100)
|
|
54
|
+
|
|
55
|
+
hint_key, hint_fallback = (
|
|
56
|
+
("window_replica_hint",
|
|
57
|
+
"A second window showing the chosen one live; the original stays where it "
|
|
58
|
+
"is. Drag the replica to move it, double-click it to close it. Windows only.")
|
|
59
|
+
if available() else
|
|
60
|
+
("window_replica_unsupported", "Window replicas are Windows only."))
|
|
61
|
+
self.hint_label = QLabel(_t(hint_key, hint_fallback))
|
|
62
|
+
retranslator.bind(self.hint_label, hint_key, hint_fallback)
|
|
63
|
+
self.hint_label.setWordWrap(True)
|
|
64
|
+
|
|
65
|
+
self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
|
|
66
|
+
self.button_box.rejected.connect(self.reject)
|
|
67
|
+
|
|
68
|
+
layout = QGridLayout(self)
|
|
69
|
+
layout.addWidget(self.window_list, 0, 0, 1, 2)
|
|
70
|
+
layout.addWidget(self.refresh_button, 1, 0)
|
|
71
|
+
layout.addWidget(self.show_button, 1, 1)
|
|
72
|
+
layout.addWidget(self.opacity_label, 2, 0)
|
|
73
|
+
layout.addWidget(self.opacity_slider, 2, 1)
|
|
74
|
+
layout.addWidget(self.hint_label, 3, 0, 1, 2)
|
|
75
|
+
layout.addWidget(self.button_box, 4, 0, 1, 2)
|
|
76
|
+
|
|
77
|
+
self.show_button.setEnabled(available())
|
|
78
|
+
self.reload_windows()
|
|
79
|
+
|
|
80
|
+
def reload_windows(self) -> List[Tuple[int, str]]:
|
|
81
|
+
"""重新抓一次視窗清單。"""
|
|
82
|
+
self.window_list.clear()
|
|
83
|
+
windows = self._lister() or []
|
|
84
|
+
for handle, title in windows:
|
|
85
|
+
item = QListWidgetItem(title)
|
|
86
|
+
item.setData(Qt.ItemDataRole.UserRole, int(handle))
|
|
87
|
+
self.window_list.addItem(item)
|
|
88
|
+
return windows
|
|
89
|
+
|
|
90
|
+
def selected(self) -> Optional[Tuple[int, str]]:
|
|
91
|
+
"""目前選到的 (handle, 標題);沒選就是 None。"""
|
|
92
|
+
item = self.window_list.currentItem()
|
|
93
|
+
if item is None:
|
|
94
|
+
return None
|
|
95
|
+
return int(item.data(Qt.ItemDataRole.UserRole)), item.text()
|
|
96
|
+
|
|
97
|
+
def start_replica(self) -> Optional[WindowReplicaWidget]:
|
|
98
|
+
"""
|
|
99
|
+
對選到的視窗開一個複本。接不上就把它收掉再回 None——留一個永遠黑著的
|
|
100
|
+
小視窗在畫面上,比什麼都不做更糟。
|
|
101
|
+
Open a replica of the selected window. If it cannot attach, the widget is
|
|
102
|
+
closed and this returns None: leaving a permanently black window on
|
|
103
|
+
screen is worse than doing nothing.
|
|
104
|
+
"""
|
|
105
|
+
chosen = self.selected()
|
|
106
|
+
if chosen is None:
|
|
107
|
+
return None
|
|
108
|
+
handle, title = chosen
|
|
109
|
+
replica = self._replica_factory(handle, title, self.opacity_slider.value(), None)
|
|
110
|
+
if not replica.start():
|
|
111
|
+
front_engine_logger.info(f"[WindowReplicaDialog] could not replicate {title!r}")
|
|
112
|
+
replica.close()
|
|
113
|
+
return None
|
|
114
|
+
self.replica_widget_list.append(replica)
|
|
115
|
+
return replica
|
|
116
|
+
|
|
117
|
+
def close_all(self) -> None:
|
|
118
|
+
"""關掉這個對話框開過的所有複本。"""
|
|
119
|
+
for replica in list(self.replica_widget_list):
|
|
120
|
+
try:
|
|
121
|
+
replica.close()
|
|
122
|
+
except RuntimeError: # pragma: no cover - 底層物件已消失
|
|
123
|
+
pass
|
|
124
|
+
self.replica_widget_list.clear()
|
|
@@ -888,6 +888,10 @@ class FrontEngineMainUI(QMainWindow):
|
|
|
888
888
|
self.widgets_setting_ui.stop_spectrum()
|
|
889
889
|
if getattr(self, "tools_setting_ui", None) is not None:
|
|
890
890
|
self.tools_setting_ui.release_pinned_windows()
|
|
891
|
+
# 複本註冊的是 DWM 縮圖,那是系統資源:視窗沒了也不會自己消失。
|
|
892
|
+
# A replica holds a DWM thumbnail, a system resource that does not
|
|
893
|
+
# go away just because the process is closing.
|
|
894
|
+
self.tools_setting_ui.close_replicas()
|
|
891
895
|
self.tools_setting_ui.stop_camera()
|
|
892
896
|
self.tools_setting_ui.stop_virtual_camera()
|
|
893
897
|
|
|
@@ -25,6 +25,7 @@ from frontengine.ui.dialog.screen_text_dialog import (
|
|
|
25
25
|
ScreenTextDialog, ask_for_consent, has_consent,
|
|
26
26
|
)
|
|
27
27
|
from frontengine.ui.dialog.window_pin_dialog import WindowPinDialog
|
|
28
|
+
from frontengine.ui.dialog.window_replica_dialog import WindowReplicaDialog
|
|
28
29
|
from frontengine.user_setting.user_setting_file import user_setting_dict, write_user_setting
|
|
29
30
|
from frontengine.ui.page.utils import coerce_int
|
|
30
31
|
from frontengine.ui.page.layout_kit import SettingPage
|
|
@@ -85,6 +86,7 @@ class ToolsSettingUI(SettingPage):
|
|
|
85
86
|
# raises RuntimeError on touch and "copy last" was a silent no-op.
|
|
86
87
|
self.last_capture: Optional[QPixmap] = None
|
|
87
88
|
self.pin_dialog: Optional[WindowPinDialog] = None
|
|
89
|
+
self.replica_dialog: Optional[WindowReplicaDialog] = None
|
|
88
90
|
|
|
89
91
|
self._build_measure_row()
|
|
90
92
|
self._build_capture_row()
|
|
@@ -95,6 +97,11 @@ class ToolsSettingUI(SettingPage):
|
|
|
95
97
|
self.pin_button = tr(QPushButton(), "tools_pin_window", "Pin a window...")
|
|
96
98
|
self.pin_button.clicked.connect(self.open_pin_dialog)
|
|
97
99
|
|
|
100
|
+
# 視窗複本:原視窗留在原地,另外開一個小視窗顯示它的即時畫面
|
|
101
|
+
# Window replica: the original stays put, a small window shows it live
|
|
102
|
+
self.replica_button = tr(QPushButton(), "tools_replicate_window", "Replicate a window...")
|
|
103
|
+
self.replica_button.clicked.connect(self.open_replica_dialog)
|
|
104
|
+
|
|
98
105
|
# 視窗版面:記下每個視窗的位置,之後一鍵擺回去
|
|
99
106
|
# Window layouts: remember where the windows are and put them back later.
|
|
100
107
|
self.layout_label = tr(QLabel(), "tools_layout_label", "Window layout")
|
|
@@ -154,7 +161,7 @@ class ToolsSettingUI(SettingPage):
|
|
|
154
161
|
windows.add_row("tools_name", self.layout_name_edit, "Name")
|
|
155
162
|
windows.add_inline(self.layout_save_button)
|
|
156
163
|
windows.add_row("tools_saved", self.layout_combobox, "Saved")
|
|
157
|
-
windows.add_inline(self.layout_restore_button, self.pin_button)
|
|
164
|
+
windows.add_inline(self.layout_restore_button, self.pin_button, self.replica_button)
|
|
158
165
|
|
|
159
166
|
self.add_body_widget(self.hint_label)
|
|
160
167
|
self.finish_body()
|
|
@@ -366,6 +373,20 @@ class ToolsSettingUI(SettingPage):
|
|
|
366
373
|
self.pin_dialog.exec()
|
|
367
374
|
return self.pin_dialog
|
|
368
375
|
|
|
376
|
+
def open_replica_dialog(self) -> WindowReplicaDialog:
|
|
377
|
+
"""開視窗複本對話框(關掉主程式時會把開過的複本收乾淨)。"""
|
|
378
|
+
self.replica_dialog = WindowReplicaDialog(self)
|
|
379
|
+
self.replica_dialog.exec()
|
|
380
|
+
return self.replica_dialog
|
|
381
|
+
|
|
382
|
+
def close_replicas(self) -> None:
|
|
383
|
+
"""關掉開過的視窗複本(主程式關閉時呼叫)。"""
|
|
384
|
+
if self.replica_dialog is not None:
|
|
385
|
+
try:
|
|
386
|
+
self.replica_dialog.close_all()
|
|
387
|
+
except RuntimeError:
|
|
388
|
+
self.replica_dialog = None
|
|
389
|
+
|
|
369
390
|
def release_pinned_windows(self) -> None:
|
|
370
391
|
"""把釘過的別人視窗放開(主程式關閉時呼叫)。"""
|
|
371
392
|
if self.pin_dialog is not None:
|
|
@@ -46,6 +46,13 @@ english_word_dict = {
|
|
|
46
46
|
"tools_shape": "Shape",
|
|
47
47
|
"tools_name": "Name",
|
|
48
48
|
"tools_saved": "Saved",
|
|
49
|
+
"tools_replicate_window": "Replicate a window...",
|
|
50
|
+
"window_replica_title": "Replicate a window",
|
|
51
|
+
"window_replica_refresh": "Refresh",
|
|
52
|
+
"window_replica_show": "Show replica",
|
|
53
|
+
"window_replica_opacity": "Opacity",
|
|
54
|
+
"window_replica_hint": "A second window showing the chosen one live; the original stays where it is. Drag the replica to move it, double-click it to close it. Windows only.",
|
|
55
|
+
"window_replica_unsupported": "Window replicas are Windows only.",
|
|
49
56
|
"settings_menu_screensaver": "Screensaver...",
|
|
50
57
|
"screensaver_title": "Screensaver",
|
|
51
58
|
"screensaver_enable": "Show something when idle",
|
|
@@ -47,6 +47,13 @@ french_word_dict = {
|
|
|
47
47
|
"tools_shape": "Forme",
|
|
48
48
|
"tools_name": "Nom",
|
|
49
49
|
"tools_saved": "Enregistrés",
|
|
50
|
+
"tools_replicate_window": "Dupliquer une fenêtre...",
|
|
51
|
+
"window_replica_title": "Dupliquer une fenêtre",
|
|
52
|
+
"window_replica_refresh": "Actualiser",
|
|
53
|
+
"window_replica_show": "Afficher le duplicata",
|
|
54
|
+
"window_replica_opacity": "Opacité",
|
|
55
|
+
"window_replica_hint": "Une seconde fenêtre affiche la fenêtre choisie en direct ; l'originale reste à sa place. Faites glisser pour la déplacer, double-cliquez pour la fermer. Windows uniquement.",
|
|
56
|
+
"window_replica_unsupported": "Les duplicatas de fenêtre sont réservés à Windows.",
|
|
50
57
|
"settings_menu_screensaver": "Économiseur d'écran...",
|
|
51
58
|
"screensaver_title": "Économiseur d'écran",
|
|
52
59
|
"screensaver_enable": "Afficher quelque chose en cas d'inactivité",
|
|
@@ -47,6 +47,13 @@ germany_word_dict = {
|
|
|
47
47
|
"tools_shape": "Form",
|
|
48
48
|
"tools_name": "Name",
|
|
49
49
|
"tools_saved": "Gespeichert",
|
|
50
|
+
"tools_replicate_window": "Fenster duplizieren...",
|
|
51
|
+
"window_replica_title": "Fenster duplizieren",
|
|
52
|
+
"window_replica_refresh": "Aktualisieren",
|
|
53
|
+
"window_replica_show": "Duplikat anzeigen",
|
|
54
|
+
"window_replica_opacity": "Deckkraft",
|
|
55
|
+
"window_replica_hint": "Ein zweites Fenster zeigt das gewählte live; das Original bleibt, wo es ist. Ziehen zum Verschieben, Doppelklick zum Schließen. Nur Windows.",
|
|
56
|
+
"window_replica_unsupported": "Fensterduplikate gibt es nur unter Windows.",
|
|
50
57
|
"settings_menu_screensaver": "Bildschirmschoner...",
|
|
51
58
|
"screensaver_title": "Bildschirmschoner",
|
|
52
59
|
"screensaver_enable": "Bei Untätigkeit etwas anzeigen",
|
|
@@ -47,6 +47,13 @@ italian_word_dict = {
|
|
|
47
47
|
"tools_shape": "Forma",
|
|
48
48
|
"tools_name": "Nome",
|
|
49
49
|
"tools_saved": "Salvati",
|
|
50
|
+
"tools_replicate_window": "Duplica una finestra...",
|
|
51
|
+
"window_replica_title": "Duplica una finestra",
|
|
52
|
+
"window_replica_refresh": "Aggiorna",
|
|
53
|
+
"window_replica_show": "Mostra il duplicato",
|
|
54
|
+
"window_replica_opacity": "Opacità",
|
|
55
|
+
"window_replica_hint": "Una seconda finestra mostra quella scelta in diretta; l'originale resta dov'è. Trascina per spostarla, doppio clic per chiuderla. Solo Windows.",
|
|
56
|
+
"window_replica_unsupported": "I duplicati di finestra sono solo per Windows.",
|
|
50
57
|
"settings_menu_screensaver": "Salvaschermo...",
|
|
51
58
|
"screensaver_title": "Salvaschermo",
|
|
52
59
|
"screensaver_enable": "Mostra qualcosa quando sei inattivo",
|
|
@@ -47,6 +47,13 @@ russian_word_dict = {
|
|
|
47
47
|
"tools_shape": "Форма",
|
|
48
48
|
"tools_name": "Имя",
|
|
49
49
|
"tools_saved": "Сохранённые",
|
|
50
|
+
"tools_replicate_window": "Копия окна...",
|
|
51
|
+
"window_replica_title": "Копия окна",
|
|
52
|
+
"window_replica_refresh": "Обновить",
|
|
53
|
+
"window_replica_show": "Показать копию",
|
|
54
|
+
"window_replica_opacity": "Непрозрачность",
|
|
55
|
+
"window_replica_hint": "Второе окно показывает выбранное вживую, оригинал остаётся на месте. Перетащите, чтобы переместить, двойной щелчок закрывает. Только Windows.",
|
|
56
|
+
"window_replica_unsupported": "Копии окон работают только в Windows.",
|
|
50
57
|
"settings_menu_screensaver": "Заставка...",
|
|
51
58
|
"screensaver_title": "Заставка",
|
|
52
59
|
"screensaver_enable": "Показывать что-нибудь при простое",
|
{frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/simplified_chinese.py
RENAMED
|
@@ -47,6 +47,13 @@ simplified_chinese_word_dict = {
|
|
|
47
47
|
"tools_shape": "形状",
|
|
48
48
|
"tools_name": "名称",
|
|
49
49
|
"tools_saved": "已保存",
|
|
50
|
+
"tools_replicate_window": "窗口副本⋯",
|
|
51
|
+
"window_replica_title": "窗口副本",
|
|
52
|
+
"window_replica_refresh": "刷新",
|
|
53
|
+
"window_replica_show": "显示副本",
|
|
54
|
+
"window_replica_opacity": "透明度",
|
|
55
|
+
"window_replica_hint": "另外开一个小窗口显示选定窗口的实时画面,原窗口留在原地不动。拖动可以移动它,双击关闭。仅限 Windows。",
|
|
56
|
+
"window_replica_unsupported": "窗口副本仅限 Windows。",
|
|
50
57
|
"settings_menu_screensaver": "屏幕保护⋯",
|
|
51
58
|
"screensaver_title": "屏幕保护",
|
|
52
59
|
"screensaver_enable": "空闲时显示东西",
|
{frontengine-1.0.65 → frontengine-1.0.66}/frontengine/utils/multi_language/traditional_chinese.py
RENAMED
|
@@ -47,6 +47,13 @@ traditional_chinese_word_dict = {
|
|
|
47
47
|
"tools_shape": "形狀",
|
|
48
48
|
"tools_name": "名稱",
|
|
49
49
|
"tools_saved": "已儲存",
|
|
50
|
+
"tools_replicate_window": "視窗複本⋯",
|
|
51
|
+
"window_replica_title": "視窗複本",
|
|
52
|
+
"window_replica_refresh": "重新整理",
|
|
53
|
+
"window_replica_show": "顯示複本",
|
|
54
|
+
"window_replica_opacity": "透明度",
|
|
55
|
+
"window_replica_hint": "另外開一個小視窗顯示選定視窗的即時畫面,原視窗留在原地不動。拖曳可以搬動它,連點兩下關閉。僅限 Windows。",
|
|
56
|
+
"window_replica_unsupported": "視窗複本僅限 Windows。",
|
|
50
57
|
"settings_menu_screensaver": "螢幕保護⋯",
|
|
51
58
|
"screensaver_title": "螢幕保護",
|
|
52
59
|
"screensaver_enable": "閒置時顯示東西",
|
|
File without changes
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
用 DWM 縮圖做出另一個視窗的即時複本。
|
|
3
|
+
|
|
4
|
+
這和「釘選視窗」是兩件事:釘選是把**原視窗**搬到最上層,原本在哪就不在哪了;
|
|
5
|
+
複本是另外開一個小視窗顯示它的即時畫面,原視窗留在原地不動。想邊工作邊看影片、
|
|
6
|
+
或盯著背景的編譯進度,要的是後者。
|
|
7
|
+
|
|
8
|
+
畫面由 DWM 直接合成,不需要抓圖也不需要輪詢,所以更新是即時的而且幾乎不耗 CPU。
|
|
9
|
+
Windows 專屬:其他平台一律回報「做不到」,不假裝成功。
|
|
10
|
+
|
|
11
|
+
A live replica of another window, via DWM thumbnails.
|
|
12
|
+
|
|
13
|
+
This is not window pinning. Pinning moves the *real* window to the top, so it is
|
|
14
|
+
no longer where it was; a replica is a second small window showing its live
|
|
15
|
+
picture while the original stays put. Watching a video or a build log while
|
|
16
|
+
working is the second thing.
|
|
17
|
+
|
|
18
|
+
DWM composites the picture itself - no screen grabbing, no polling - so it is
|
|
19
|
+
live and costs almost nothing. Windows only: every other platform reports "not
|
|
20
|
+
possible" rather than pretending.
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import sys
|
|
25
|
+
from typing import Optional, Tuple
|
|
26
|
+
|
|
27
|
+
from frontengine.utils.logging.loggin_instance import front_engine_logger
|
|
28
|
+
|
|
29
|
+
# DWM_THUMBNAIL_PROPERTIES 的旗標 / Flags for DWM_THUMBNAIL_PROPERTIES
|
|
30
|
+
DWM_TNP_RECTDESTINATION = 0x00000001
|
|
31
|
+
DWM_TNP_RECTSOURCE = 0x00000002
|
|
32
|
+
DWM_TNP_OPACITY = 0x00000004
|
|
33
|
+
DWM_TNP_VISIBLE = 0x00000008
|
|
34
|
+
DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010
|
|
35
|
+
|
|
36
|
+
MIN_REPLICA_SIZE = 80
|
|
37
|
+
DEFAULT_REPLICA_WIDTH = 480
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def available() -> bool:
|
|
41
|
+
"""這個平台能不能做視窗複本。"""
|
|
42
|
+
return sys.platform == "win32"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def fit_within(source: Tuple[int, int], bounds: Tuple[int, int]) -> Tuple[int, int]:
|
|
46
|
+
"""
|
|
47
|
+
把來源尺寸等比縮到不超過 bounds,並保持長寬比。
|
|
48
|
+
|
|
49
|
+
純算術,所以縮放這件事不必開一個真的視窗才能驗證。來源尺寸拿不到時會是 0,
|
|
50
|
+
這裡直接回傳 bounds 而不是除以零。
|
|
51
|
+
Scale the source into `bounds` keeping its aspect ratio. Pure arithmetic, so
|
|
52
|
+
the scaling can be checked without opening a window. A source size can come
|
|
53
|
+
back as 0, in which case this returns the bounds rather than dividing by it.
|
|
54
|
+
"""
|
|
55
|
+
source_width, source_height = source
|
|
56
|
+
max_width, max_height = bounds
|
|
57
|
+
if source_width <= 0 or source_height <= 0:
|
|
58
|
+
return max(MIN_REPLICA_SIZE, max_width), max(MIN_REPLICA_SIZE, max_height)
|
|
59
|
+
scale = min(max_width / source_width, max_height / source_height)
|
|
60
|
+
width = max(MIN_REPLICA_SIZE, int(source_width * scale))
|
|
61
|
+
height = max(MIN_REPLICA_SIZE, int(source_height * scale))
|
|
62
|
+
return width, height
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class DwmThumbnail:
|
|
66
|
+
"""
|
|
67
|
+
一個註冊好的 DWM 縮圖。register() 開始、update() 調整、unregister() 收掉。
|
|
68
|
+
|
|
69
|
+
縮圖是系統資源,忘了 unregister 的話 DWM 會一直替一個已經看不到的目的地合成
|
|
70
|
+
畫面。釋放一律由呼叫端明確做,**沒有** __del__ 兜底:直譯器關閉時的解構順序
|
|
71
|
+
不保證,那時 ctypes 載入的 dwmapi 可能已經不在,靠它反而是靠一個不會發生的
|
|
72
|
+
保險。實際的三條路徑是覆蓋層的 closeEvent、對話框的 close_all()、以及主程式
|
|
73
|
+
關閉時的收尾。
|
|
74
|
+
|
|
75
|
+
A registered DWM thumbnail. It is a system resource: forgetting to
|
|
76
|
+
unregister leaves DWM compositing for a destination nobody can see.
|
|
77
|
+
|
|
78
|
+
Releasing is always explicit, with **no** __del__ fallback: destruction order
|
|
79
|
+
at interpreter shutdown is not guaranteed and the ctypes-loaded dwmapi may
|
|
80
|
+
already be gone, so relying on it means relying on something that may never
|
|
81
|
+
run. The three real paths are the overlay's closeEvent, the dialog's
|
|
82
|
+
close_all(), and the application's own shutdown.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self) -> None:
|
|
86
|
+
self._handle: Optional[int] = None
|
|
87
|
+
self._dwmapi = None
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def registered(self) -> bool:
|
|
91
|
+
return self._handle is not None
|
|
92
|
+
|
|
93
|
+
def register(self, destination: int, source: int) -> bool:
|
|
94
|
+
"""把 source 視窗的畫面接到 destination 視窗上。"""
|
|
95
|
+
if not available():
|
|
96
|
+
return False
|
|
97
|
+
self.unregister()
|
|
98
|
+
try:
|
|
99
|
+
import ctypes
|
|
100
|
+
from ctypes import wintypes
|
|
101
|
+
|
|
102
|
+
dwmapi = ctypes.windll.dwmapi
|
|
103
|
+
handle = wintypes.HANDLE()
|
|
104
|
+
result = dwmapi.DwmRegisterThumbnail(
|
|
105
|
+
wintypes.HWND(destination), wintypes.HWND(source), ctypes.byref(handle))
|
|
106
|
+
if result != 0 or not handle.value:
|
|
107
|
+
front_engine_logger.warning(
|
|
108
|
+
f"[DwmThumbnail] register failed | hresult={result}")
|
|
109
|
+
return False
|
|
110
|
+
self._dwmapi = dwmapi
|
|
111
|
+
self._handle = handle.value
|
|
112
|
+
return True
|
|
113
|
+
except Exception as error: # pragma: no cover - Win32 boundary
|
|
114
|
+
front_engine_logger.warning(f"[DwmThumbnail] register error: {error!r}")
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
def source_size(self) -> Tuple[int, int]:
|
|
118
|
+
"""來源視窗的原始尺寸;問不到就回 (0, 0)。"""
|
|
119
|
+
if self._handle is None or self._dwmapi is None:
|
|
120
|
+
return (0, 0)
|
|
121
|
+
try:
|
|
122
|
+
import ctypes
|
|
123
|
+
from ctypes import wintypes
|
|
124
|
+
|
|
125
|
+
class SIZE(ctypes.Structure):
|
|
126
|
+
_fields_ = [("cx", wintypes.LONG), ("cy", wintypes.LONG)]
|
|
127
|
+
|
|
128
|
+
size = SIZE()
|
|
129
|
+
if self._dwmapi.DwmQueryThumbnailSourceSize(
|
|
130
|
+
wintypes.HANDLE(self._handle), ctypes.byref(size)) != 0:
|
|
131
|
+
return (0, 0)
|
|
132
|
+
return (int(size.cx), int(size.cy))
|
|
133
|
+
except Exception as error: # pragma: no cover - Win32 boundary
|
|
134
|
+
front_engine_logger.warning(f"[DwmThumbnail] source size error: {error!r}")
|
|
135
|
+
return (0, 0)
|
|
136
|
+
|
|
137
|
+
def update(self, width: int, height: int, opacity: int = 255,
|
|
138
|
+
client_area_only: bool = True) -> bool:
|
|
139
|
+
"""把縮圖鋪滿目的地視窗的這個大小。"""
|
|
140
|
+
if self._handle is None or self._dwmapi is None:
|
|
141
|
+
return False
|
|
142
|
+
try:
|
|
143
|
+
import ctypes
|
|
144
|
+
from ctypes import wintypes
|
|
145
|
+
|
|
146
|
+
class RECT(ctypes.Structure):
|
|
147
|
+
_fields_ = [("left", wintypes.LONG), ("top", wintypes.LONG),
|
|
148
|
+
("right", wintypes.LONG), ("bottom", wintypes.LONG)]
|
|
149
|
+
|
|
150
|
+
class DWM_THUMBNAIL_PROPERTIES(ctypes.Structure):
|
|
151
|
+
_fields_ = [
|
|
152
|
+
("dwFlags", wintypes.DWORD),
|
|
153
|
+
("rcDestination", RECT),
|
|
154
|
+
("rcSource", RECT),
|
|
155
|
+
("opacity", ctypes.c_ubyte),
|
|
156
|
+
("fVisible", wintypes.BOOL),
|
|
157
|
+
("fSourceClientAreaOnly", wintypes.BOOL),
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
properties = DWM_THUMBNAIL_PROPERTIES()
|
|
161
|
+
properties.dwFlags = (DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY
|
|
162
|
+
| DWM_TNP_VISIBLE | DWM_TNP_SOURCECLIENTAREAONLY)
|
|
163
|
+
properties.rcDestination = RECT(0, 0, max(1, int(width)), max(1, int(height)))
|
|
164
|
+
properties.opacity = max(0, min(255, int(opacity)))
|
|
165
|
+
properties.fVisible = True
|
|
166
|
+
# 只取客戶區:不然複本裡會有一圈原視窗的標題列與邊框,看起來像截圖失誤。
|
|
167
|
+
# Client area only: otherwise the replica carries the source's title
|
|
168
|
+
# bar and border, which reads as a botched screenshot.
|
|
169
|
+
properties.fSourceClientAreaOnly = bool(client_area_only)
|
|
170
|
+
result = self._dwmapi.DwmUpdateThumbnailProperties(
|
|
171
|
+
wintypes.HANDLE(self._handle), ctypes.byref(properties))
|
|
172
|
+
return result == 0
|
|
173
|
+
except Exception as error: # pragma: no cover - Win32 boundary
|
|
174
|
+
front_engine_logger.warning(f"[DwmThumbnail] update error: {error!r}")
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
def unregister(self) -> None:
|
|
178
|
+
"""收掉縮圖。重複呼叫是安全的。"""
|
|
179
|
+
if self._handle is None or self._dwmapi is None:
|
|
180
|
+
self._handle = None
|
|
181
|
+
return
|
|
182
|
+
try:
|
|
183
|
+
from ctypes import wintypes
|
|
184
|
+
|
|
185
|
+
self._dwmapi.DwmUnregisterThumbnail(wintypes.HANDLE(self._handle))
|
|
186
|
+
except Exception as error: # pragma: no cover - Win32 boundary
|
|
187
|
+
front_engine_logger.warning(f"[DwmThumbnail] unregister error: {error!r}")
|
|
188
|
+
finally:
|
|
189
|
+
self._handle = None
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frontengine
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.66
|
|
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
|
|
@@ -42,6 +42,8 @@ frontengine/show/presentation/annotation_overlay.py
|
|
|
42
42
|
frontengine/show/presentation/cursor_effects.py
|
|
43
43
|
frontengine/show/presentation/keystroke_display.py
|
|
44
44
|
frontengine/show/presentation/magnifier.py
|
|
45
|
+
frontengine/show/replica/__init__.py
|
|
46
|
+
frontengine/show/replica/replica_widget.py
|
|
45
47
|
frontengine/show/scene/__init__.py
|
|
46
48
|
frontengine/show/scene/extend_graphic_scene.py
|
|
47
49
|
frontengine/show/scene/extend_graphic_view.py
|
|
@@ -85,6 +87,7 @@ frontengine/ui/dialog/signage_dialog.py
|
|
|
85
87
|
frontengine/ui/dialog/smart_pause_dialog.py
|
|
86
88
|
frontengine/ui/dialog/usage_report_dialog.py
|
|
87
89
|
frontengine/ui/dialog/window_pin_dialog.py
|
|
90
|
+
frontengine/ui/dialog/window_replica_dialog.py
|
|
88
91
|
frontengine/ui/menu/__init__.py
|
|
89
92
|
frontengine/ui/menu/help_menu.py
|
|
90
93
|
frontengine/ui/menu/how_to_menu.py
|
|
@@ -254,5 +257,7 @@ frontengine/utils/weather/weather_service.py
|
|
|
254
257
|
frontengine/utils/window_pin/__init__.py
|
|
255
258
|
frontengine/utils/window_pin/window_layout.py
|
|
256
259
|
frontengine/utils/window_pin/window_pin.py
|
|
260
|
+
frontengine/utils/window_replica/__init__.py
|
|
261
|
+
frontengine/utils/window_replica/dwm_thumbnail.py
|
|
257
262
|
frontengine/utils/workshop/__init__.py
|
|
258
263
|
frontengine/utils/workshop/workshop_content.py
|