Compare commits

...

193 Commits

Author SHA1 Message Date
WarmUpTill
bf7fe71ae3 Fix system tray warning not being translated
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-07-17 17:49:44 +02:00
炭酸コーラ
12a6b26d9a Updated Japanese translation to the latest version 2025-07-17 17:45:44 +02:00
WarmUpTill
373e74c080 Switch to different port for aquiring Twitch tokens
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-07-16 21:15:36 +02:00
WarmUpTill
f3e7eaf212 Rework to support frequently resizing widgets
Some checks failed
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Check locale / ubuntu64 (push) Has been cancelled
2025-07-13 18:17:46 +02:00
WarmUpTill
b036736547 Refactor MacroSegmentList rework to support nested macros 2025-07-13 18:17:46 +02:00
WarmUpTill
817de13e9d Add nested macro support 2025-07-13 18:17:46 +02:00
WarmUpTill
0b774c171d Cleanup
* Use GetSettingsWindow() instead of window() to avoid connecting to the
  wrong widget
* Fix typos
2025-07-13 18:17:46 +02:00
WarmUpTill
be8f7bd70f Add ResizableWidget
When manual resizing is enabled, the vertical widget size can be changed
by dragging the widget in the lower right corner.
2025-07-13 18:17:46 +02:00
WarmUpTill
5f963b5b7d Refactor macro edit area into separate class
This will enable support for nested macros
2025-07-13 18:17:46 +02:00
WarmUpTill
c6155c9fea Add default settings support 2025-07-13 18:17:46 +02:00
WarmUpTill
be809dbfab Fix macro dock being being destroyed when opening macro settings
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-07-09 15:22:04 +02:00
WarmUpTill
7478c149b3 Add option to toggle mute state of audio source
Some checks failed
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Check locale / ubuntu64 (push) Has been cancelled
2025-07-08 17:51:21 +02:00
WarmUpTill
f4eaa9785d Fix crash on macro load 2025-07-08 17:49:33 +02:00
WarmUpTill
49c0de3f1b Add context menu option to expand / collapse all macro groups 2025-07-08 17:49:33 +02:00
WarmUpTill
101ef4e973 Fix crash when adding new macro to group 2025-07-08 17:49:33 +02:00
WarmUpTill
82568b23e8 Fix crash when initially setting up tab ordering
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The tabOrder vector was initialized with the size of tabNames.
Then new tabs were registered with AddSetupTabCallback() causing
tabNames to grow but tabOrder to remain the same.

Usually this is not a problem as LoadTabOrder() will be called which
ensures that their sizes are in sync.

However, when using a fresh OBS install or one with a corrupted scene
configuration file, the on_save() callbacks are called before any
loading callbacks.
With the sizes of tabNames and tabOrder not being in sync this would
lead to a crash.

Fixed by adding a tabWidgetOrderValid() check within SaveTabOrder()
before iterating over both vectors.
The tabWidgetOrderValid() was expanded to check the sizes of both
vectors.
Additionally the tabOrder initialization was moved from global static
initialization to function local static initialization to ensure that
potential preceeding calls to AddSetupTabCallback() will be taken into
account when setting up the tabOrder vector.
2025-07-06 10:19:20 +02:00
WarmUpTill
0cd7004f6a Setup MIDI device observers in parallel to avoid blocking OBS startup
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-06-25 13:56:40 +02:00
WarmUpTill
40c62ba700 CI: Add libremidi dependencies 2025-06-25 13:56:40 +02:00
Arimil
f2c7b532d9 Add support for kwin (wayland) (#1393)
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-06-21 19:51:25 +02:00
WarmUpTill
456a9c04c7 Add icon for windows installer
Some checks failed
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Check locale / ubuntu64 (push) Has been cancelled
2025-06-20 22:29:35 +02:00
WarmUpTill
3e1fdbde45 Add option to set and check list source settings by name
This makes it easier to select the intended settings value as the
underlying value often has no direct connection to the user facing name.

It also makes it possible to select list entries whos underlying value
changes frequently, but the user facing value does not.
(E.g. device IDs based on the input port compared to the device name)
2025-06-20 22:29:35 +02:00
WarmUpTill
874b9b86e2 Refactor locking of macro segments
This should avoid crashes when actions or conditions are performed in
parallel to the main macro loop and will improve the UI responsiveness
in some scenarios
2025-06-20 22:29:35 +02:00
WarmUpTill
98d1f83acc Cleanup 2025-06-20 22:29:35 +02:00
WarmUpTill
7e91f81957 Ease development with dirty dependency directory 2025-06-20 22:29:35 +02:00
WarmUpTill
20488afdd1 Fix tempvars being reset across macros for cached widgets
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-06-12 21:29:07 +02:00
WarmUpTill
d9d387ad47 Fix crash when deleting cached macro widgets 2025-06-12 21:29:07 +02:00
WarmUpTill
0736d673e1 Add option to check if chat was cleared / message was removed 2025-06-12 21:29:07 +02:00
WarmUpTill
34151e4bc6 Fix "Get user info" only allowing small values for user ids 2025-06-12 21:29:07 +02:00
WarmUpTill
1346c19bec Fix crash when invalid token is used to connect to Twitch chat 2025-06-12 21:29:07 +02:00
WarmUpTill
be6bc48231 Fix macro selection resetting when any macro was removed 2025-06-12 18:33:24 +02:00
WarmUpTill
79a8ad57af Rework HTTP action URL input
The URL to be accessed can now be passed as a single URL instead of it
being split into a host and path component
2025-06-12 18:33:24 +02:00
WarmUpTill
daeb9275a3 Add support for inline scripts
* Script can be defined in the macro segment or loaded from a file
* Supports both LUA and Python
* Can be used for actions and conditions
* obs_script_create and obs_script_destroy are resolved at runtime
  (Let's hope the API remains stable)
2025-06-09 14:20:44 +02:00
WarmUpTill
73b542a4db Move scripting support to seperate project 2025-06-09 14:20:44 +02:00
WarmUpTill
e3471066e9 Export symbols 2025-06-09 14:20:44 +02:00
WarmUpTill
ec41c06b4d Update feature request description 2025-06-09 14:20:44 +02:00
WarmUpTill
201e45d058 Fix macro tree not resizing properly with OBS 31.1.0 2025-06-07 18:56:42 +02:00
WarmUpTill
b17aa30432 Fix attempting to cache widgets of about to be deleted macros 2025-06-06 19:14:46 +02:00
WarmUpTill
93703c80bc Hide option to verify timestamps when functionality is disabled 2025-06-01 11:57:26 +02:00
WarmUpTill
d7951a7179 Cleanup constness 2025-06-01 11:57:26 +02:00
WarmUpTill
d42a3b584a Rework resource tab hotkey handling
Added hotkey to add new entry
2025-06-01 11:57:26 +02:00
WarmUpTill
becd1bd02a Set default hotkey for new macro to CTRL + N 2025-06-01 11:57:26 +02:00
WarmUpTill
30422aecf3 Fix crash when deleting macro 2025-06-01 11:57:26 +02:00
WarmUpTill
c567e6ef7f Allow import of json files
Automatic backups are stored as json files
2025-06-01 11:57:26 +02:00
WarmUpTill
1ca61f3ed4 Add option to not link date-tz even if the source code exists 2025-05-28 22:11:07 +02:00
WarmUpTill
1affe9dce3 Fix macro segment list entries not resizing properly 2025-05-27 22:37:46 +02:00
WarmUpTill
406e3c1855 Cleanup
* Add variable support to process condition
* Display unresolved variable value in Window action
* Add tooltips to indicate that variables are supported
2025-05-27 22:37:46 +02:00
WarmUpTill
c05dd40c4c Add option to change various OBS video settings 2025-05-27 22:37:46 +02:00
WarmUpTill
9a86ecac42 Add option to disable widget caching 2025-05-27 22:37:46 +02:00
WarmUpTill
721a786e79 Rework preview dialog to improve performance
Object detection and OCR models were constantly being re-initialized
each frame due to copy in the signal / slot handling
2025-05-27 22:37:46 +02:00
WarmUpTill
32d29875ed Move responsibility of cascade init to ObjDetectParameters 2025-05-27 22:37:46 +02:00
warmuptill
0e5f56b562 Add tesseract config file support 2025-05-27 22:37:46 +02:00
WarmUpTill
5490fabf92 Add hotkey to create new macro 2025-05-27 22:37:46 +02:00
WarmUpTill
5e3ab19940 Add ProfileSelectionWidget
Refreshes list of profiles when widget becomes visible to support widget
caching.
2025-05-27 22:37:46 +02:00
WarmUpTill
1c94a1ab44 Add X and Y position tempvars to cursor condition 2025-05-27 22:37:46 +02:00
WarmUpTill
34baa56134 Update process list on visibility change
This is done to support widget caching
2025-05-27 22:37:46 +02:00
WarmUpTill
5ce4171773 Add MonitorSelectionWidget
Refreshes list of monitors when widget becomes visible to support widget
caching
2025-05-27 22:37:46 +02:00
WarmUpTill
c281c6db83 Add WindowSelectionWidget
Refreshes list of windows when widget becomes visible to support widget
caching
2025-05-27 22:37:46 +02:00
WarmUpTill
c43439ee64 Add option to keep selection when FilterComboBox entry doesn't exist 2025-05-27 22:37:46 +02:00
WarmUpTill
a84731b8fe Rework TransitionSelectionWidget to support widget caching 2025-05-27 22:37:46 +02:00
WarmUpTill
61fbff5821 Fix SetSourceSetting() not changing settings if the default value is set
This won't help in scenarios in which even a default value does not
exist, e.g. "text" for the text sources, as here it is unclear which
value type to use
2025-05-27 22:37:46 +02:00
WarmUpTill
1381654fed Add Add support for widget caching to FilterSelectionWidget 2025-05-27 22:37:46 +02:00
WarmUpTill
9805601c07 Add support for widget caching to SceneItemSelectionWidget 2025-05-27 22:37:46 +02:00
WarmUpTill
fcf57ee031 Add support for widget caching to SceneSelectionWidget 2025-05-27 22:37:46 +02:00
WarmUpTill
ba3e87a761 Rework SourceSelectionWidget to support widget caching 2025-05-27 22:37:46 +02:00
WarmUpTill
3dd3f576c3 Add hint that parallel condition check feature is experimental 2025-05-27 22:37:46 +02:00
WarmUpTill
7403a18e96 Cleanup 2025-05-27 22:37:46 +02:00
warmuptill
56494480ba Cache macro segment widgets for each macro 2025-05-27 22:37:46 +02:00
warmuptill
cdc5d16e95 Allow changing Tesseract model directory
This will prevent custom models being deleted when installing a new
version of the plugin, as the plugin's data directory might get wiped on
some operating systems.
2025-05-27 22:37:46 +02:00
warmuptill
27aed79305 Enable building with OCR support in Ubuntu environment 2025-05-27 22:37:46 +02:00
WarmUpTill
7ec95e33eb Hide outdated video condition options
The "throttle" and "reduce matching latency" optiohns were introduced
before "short circuit evaluation" was available and are now outdated.

Both don't behave as expected with this option enabled.
The throttling effect can be better achieved with an additional "Timer"
condition.
2025-05-27 22:37:46 +02:00
WarmUpTill
98260b25a1 Add option to toggle the pause state of a macro 2025-05-27 22:37:46 +02:00
WarmUpTill
45b37de9f7 Add MQTT connection tab 2025-05-27 20:23:07 +02:00
WarmUpTill
027a3e9074 Add MQTT condition and action 2025-05-27 20:23:07 +02:00
WarmUpTill
cab50e0922 Add MQTT helpers 2025-05-27 20:23:07 +02:00
WarmUpTill
88514e209d CI: build paho.mqtt.cpp libs 2025-05-27 20:23:07 +02:00
WarmUpTill
4ed7f13b72 Add paho.mqtt.cpp dependency 2025-05-27 20:23:07 +02:00
WarmUpTill
0fb11ac274 Add copy / move to MessageDispatcher 2025-05-27 20:23:07 +02:00
WarmUpTill
942933290a Remove unnecessary include 2025-05-27 20:23:07 +02:00
WarmUpTill
f13beaead2 Reduce log spam in case of invalid token
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The caching mechanism for the token validity checks did not have the
desired effect, and the Twitch channel to user id mapping function was
attempting to resolve names even with an invalid token.
2025-05-08 21:10:13 +02:00
WarmUpTill
13dba6527d Exclude jsoncons files from release folder 2025-05-08 21:10:13 +02:00
WarmUpTill
b78a6510a9 Start the plugin on first boot 2025-05-08 21:10:13 +02:00
WarmUpTill
6c22f438b6 Disable macro add button highlight when importing macros 2025-05-08 21:10:13 +02:00
WarmUpTill
347abe6c84 Enable macro highlighting by default 2025-05-08 21:10:13 +02:00
WarmUpTill
230863adda Lazy initialize macro segment widgets
This should make it less time consuming to accidentally switch between
macros with a large amount of macro segments
2025-05-08 21:10:13 +02:00
WarmUpTill
f59478fac7 Cleanup 2025-05-08 21:10:13 +02:00
WarmUpTill
8adac79cd8 Rework option to run macro actions in parallel to other macros 2025-05-08 20:44:56 +02:00
WarmUpTill
ea93c44db7 Add option to check macro conditions in parallel to other macros 2025-05-08 20:44:56 +02:00
WarmUpTill
9e20b341d8 Rework Macro constructors to support passing GlobalMacroSettings 2025-05-08 20:44:56 +02:00
WarmUpTill
232cbb06f6 Disable Twitch event timestamp verification by default
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
It brakes too often to be worth it as an option to be enabled by
default.
2025-05-07 18:33:04 +02:00
WarmUpTill
e9d57a0f4a Refactor log helpers and add option to disable logging
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-05-01 19:55:28 +02:00
WarmUpTill
9633a61a65 Add option to disable Twitch event timestamp verification
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-05-01 12:46:20 +02:00
WarmUpTill
0643b250e1 Fix typos 2025-05-01 12:46:20 +02:00
WarmUpTill
b52738881f Fix "HTTP" action failing to send data on MacOS
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-04-30 21:25:44 +02:00
WarmUpTill
470d5ba3d7 Log HTTP errors when verbose logging is enabled 2025-04-30 21:25:44 +02:00
WarmUpTill
8326e72047 Add link to installation wiki page
Some checks failed
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-23 11:48:17 +02:00
WarmUpTill
12ab4d8cf9 Add option to query JSON strings and access JSON array elements
Some checks failed
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
2025-04-22 15:35:45 +02:00
WarmUpTill
295ec9eb81 Add more JSON tests 2025-04-22 15:35:45 +02:00
WarmUpTill
4c5dbd4b7c Add more JSON helpers 2025-04-22 15:35:45 +02:00
WarmUpTill
5568f92ad0 Move JSON helpers to lib 2025-04-22 15:35:45 +02:00
WarmUpTill
7c4c0056ce Remove libremidi patch 2025-04-22 15:35:45 +02:00
WarmUpTill
d2b4b1cc07 Add jsoncons 2025-04-22 15:35:45 +02:00
WarmUpTill
a3ca22d238 Cleanup CMakeLists.txt 2025-04-22 15:35:45 +02:00
WarmUpTill
c73542a3cc Fix variables not being re-resolved on Load()
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-17 19:22:52 +02:00
WarmUpTill
70bbc7cdac Implement proper timestamp validation for Twitch messages
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-11 19:02:37 +02:00
WarmUpTill
d892298995 Add missing "[adv-ss]" log tag 2025-04-11 19:02:37 +02:00
WarmUpTill
0fe31432be Add "previous scene" to the "scene has (not) changed" checks 2025-04-11 18:57:58 +02:00
WarmUpTill
aaa0113ccb Ignore Xerrors 2025-04-11 18:57:25 +02:00
WarmUpTill
b908954b46 CI: Add cmake setup step to Linux build
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-04-02 13:48:23 +02:00
WarmUpTill
b0eede8a85 Add "disable" effect to macro conditions using "ignore" logic selection 2025-04-02 13:48:23 +02:00
WarmUpTill
aa87911b71 Use cpp-httplib based HTTP action type
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The goal is to remove the older, more limited version of the HTTP action
at some point in the future.
2025-03-30 14:06:19 +02:00
WarmUpTill
1b05019acc Add option to hide entries from action selection
Intended to be used to hide legacy version of actions types (e.g. HTTP)
2025-03-30 14:01:41 +02:00
WarmUpTill
78a5a2629d Hide the "remote" file check option
This option will be removed at some point in the future.
The http action should be used instead.
2025-03-30 14:01:41 +02:00
WarmUpTill
634270a978 Cleanup includes 2025-03-30 14:01:41 +02:00
WarmUpTill
78ba22e1e4 Hide "get settings" button when setting macro property value 2025-03-30 14:01:41 +02:00
WarmUpTill
aba5737a60 Update and clean up locale (qwe1154323937) 2025-03-30 14:01:41 +02:00
WarmUpTill
4315f7f621 Exclude unwanted files from sources archive 2025-03-30 14:01:41 +02:00
WarmUpTill
53c535962f Use tab key to switch to dialog controls and set default focus to input 2025-03-30 14:01:41 +02:00
WarmUpTill
1e718b78c7 Fall back to project version if git tag cannot be queried 2025-03-30 14:01:41 +02:00
WarmUpTill
b1d2156228 Update libremidi to v4.3.0 2025-03-30 14:01:41 +02:00
WarmUpTill
9c3c953c6b Ignore deprecation warnings for Qt 6.7 and above
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-22 18:09:51 +01:00
WarmUpTill
ae74f68db7 Add option to close projector windows 2025-03-22 18:09:51 +01:00
WarmUpTill
213f1bba36 Add "Any" entry of the reward selection only for Twitch condition 2025-03-22 18:09:51 +01:00
WarmUpTill
5a2cb0bd68 Add more scripting signals / procedures 2025-03-22 18:09:51 +01:00
WarmUpTill
23b461828b Add start start / stop callbacks 2025-03-22 18:09:51 +01:00
WarmUpTill
9944a1b03b Move interval reset handling 2025-03-22 18:09:51 +01:00
WarmUpTill
eaad4d1bbd Fix Twitch helper caches misbehaving
API calls with different sets of arguments could map to the same key
value pair, which resulted in unexpected behavior for functions using
those caches
2025-03-22 18:09:51 +01:00
WarmUpTill
17d9b73b9a Fix thread flood when Twitch event sub connection is lost
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-15 12:31:13 +01:00
WarmUpTill
d6185a6099 Add handling for Twitch API throttling 2025-03-15 12:31:13 +01:00
WarmUpTill
41c06b9edb Refactor Twitch condition event handling 2025-03-15 12:31:13 +01:00
WarmUpTill
d8807077da Add option to query reward information 2025-03-15 12:31:13 +01:00
WarmUpTill
34e125f82d Rework chache handling
* No longer provide multiple HTTP helper functions with the same name
* Default to cache not being used
* Explicitly use the cache for certain Twitch actions and conditions
* Clear caches on shutdown to avoid leaks being reported
2025-03-15 12:31:13 +01:00
WarmUpTill
d670b6d07e Move JSON temp var heper functions 2025-03-15 12:31:13 +01:00
WarmUpTill
7b0f985f18 Add macro properties for 'reward' object in redemption events 2025-03-15 12:31:13 +01:00
WarmUpTill
212be923f6 Add option to query user information 2025-03-15 12:31:13 +01:00
WarmUpTill
88fcb57e9f Cleanup 2025-03-15 12:31:13 +01:00
WarmUpTill
9b609c118f Fix disabling chat emote only mode attempting to send chat messages 2025-03-15 12:31:13 +01:00
WarmUpTill
37c398d37a Indicate that "Set to condition/action value" should be avoided 2025-03-15 12:31:13 +01:00
WarmUpTill
a51b7f6b13 Fix UI theming issues
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-03-11 22:12:05 +01:00
WarmUpTill
808fd84b83 Remove Qt5 support 2025-03-11 22:12:05 +01:00
WarmUpTill
57bcea15f5 cmake: Replace find_qt with find_package 2025-03-10 19:48:01 +01:00
WarmUpTill
4e51b56b9b Add game capture condition 2025-03-10 18:49:07 +01:00
WarmUpTill
ef5cf41d34 Add OBS 31 support 2025-03-10 18:49:07 +01:00
WarmUpTill
911e79ea6a Fix OCR controls showing in place of color controls
This happened when OCR support was disabled at build time
2025-03-10 18:33:13 +01:00
WarmUpTill
d586177de0 Add screenshot condition
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2025-03-09 20:57:51 +01:00
WarmUpTill
202c36646c Fix UI issues when opening ItemSettingsDialogs
Some widgets were displayed incorrectly if the parent of the dialog
window is not the main settings window
2025-03-09 20:05:07 +01:00
WarmUpTill
ab5102f5ca Fix typo 2025-03-09 20:05:07 +01:00
WarmUpTill
e612fb99f6 Cleanup
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
* Reorder action type selection
* Refactor
2025-03-06 21:30:03 +01:00
WarmUpTill
691e77a69a Add option to generate random number 2025-03-06 21:30:03 +01:00
Kaito Udagawa
930118c61f Call Setup() after Load()
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Tesseract API depends on the languageCode and this must be reinitialized after Load
2025-03-06 08:43:54 +01:00
Kaito Udagawa
1e05f2f2ce Fix languageCode
LanguageCode is not respected by OCRParameters and settings properly.
2025-03-06 08:43:54 +01:00
WarmUpTill
ae25b4d023 Improve setting selection handling
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
Will now try to keep the current selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
af055a12a1 Cleanup 2025-03-05 14:16:04 +01:00
WarmUpTill
7de28eedd3 Improve settings button selection handling
Will now try to keep the current button selection if possible.
This can be useful when switching between source of the same time.
2025-03-05 14:16:04 +01:00
WarmUpTill
8dfe81f522 Display failure if Twitch token account name cannot be fetched
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-27 19:52:41 +01:00
WarmUpTill
a1d8ae291d Add option to hide empty name warning in ItemSettingsDialog 2025-02-27 19:52:41 +01:00
WarmUpTill
eb6989527d Add support for libproc2 version 4.0.5
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-02-10 18:12:08 +01:00
WarmUpTill
2986a5dd96 Fix crash when actions were copied into a action queue
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The steps added via AddPostLoadStep() from the copy were never executed.
Thus they might be invalid when RunPostLoadSteps() is next called.

This could result in a crash when a new action or condition is added
after e.g. a "Filter" action's TempVariableRef was copied, which adds a
PostLoadStep to resolve the macro segment reference.
2025-02-04 08:26:04 +01:00
WarmUpTill
816ee9b244 Fix crash when changing Twitch condition type 2025-02-04 08:26:04 +01:00
WarmUpTill
51b53bf948 Fix script actions / conditions not working after stopping macro
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-30 09:39:50 +01:00
WarmUpTill
2c5121ee94 Prevent crash when macro contains invalid action or condition 2025-01-30 09:39:50 +01:00
WarmUpTill
6a58684854 Fix import of incomplete settings not resetting duration to zero
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-23 19:00:51 +01:00
WarmUpTill
0b622fdbed Fix crash when importing settings 2025-01-23 19:00:51 +01:00
WarmUpTill
3bce8e075f CI: Switch to and adapt to Ubuntu 24 runner
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2025-01-15 21:36:54 +01:00
WarmUpTill
f598d84ea1 Fix freeze due to ResizingPlainTextEdit constantly resizing incorrectly 2025-01-15 21:36:35 +01:00
WarmUpTill
d7a244e80e Fix transition condition not considering transition overrides
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-31 12:47:20 +01:00
WarmUpTill
408002e96a Add support for OBS 31 in-tree builds
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-12-12 21:41:21 +01:00
WarmUpTill
50da3d3210 Fix "Media" condition showing the incorrect macro property selection
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-30 12:11:14 +01:00
WarmUpTill
9e13067769 Fix typo
Some checks failed
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-11-23 17:11:05 +01:00
WarmUpTill
821e768e76 Reduce screenshot selection area to area to be checked
Some checks failed
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
debian-build / build (push) Has been cancelled
2024-11-22 20:52:15 +01:00
WarmUpTill
4d2179448b Rename files 2024-11-22 20:52:15 +01:00
WarmUpTill
40d9002470 Update scripting examples
* Add more comments
* Add example showcasing how to register and set temp vars
2024-11-22 20:36:36 +01:00
WarmUpTill
7f1e310ca6 Add support to register, deregister, and set temp vars from scripts 2024-11-22 20:36:36 +01:00
WarmUpTill
66d02f4683 Fix typos 2024-11-22 20:36:36 +01:00
Arash Partow
0aae1b9978 Update ExprTk to 0.0.3
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-11-22 19:56:02 +01:00
WarmUpTill
db50822b05 Prepare to fix deprecation warning when using OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
The following log message will be present in OBS 31 when
CheckIfTrayIsDisabled() is called:

DEPRECATION: obs_frontend_get_global_config is deprecated. Read from
global or user configuration explicitly instead.

It will be resolved when the minimum OBS version is bumped to OBS 31.
2024-11-02 08:28:52 +01:00
WarmUpTill
63a545b293 Fix "Date" condition returning true unexpectedly
This could happen if the plugin was stopped on the General tab and
restarted or the first time the plugin was started.
2024-11-02 08:28:52 +01:00
WarmUpTill
bc0497d2c9 Add MacroWasCheckedSinceLastStart() 2024-11-02 08:28:52 +01:00
WarmUpTill
37226a3a4c Fix macro group icon not being displayed correctly in OBS 31
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-31 19:39:38 +01:00
WarmUpTill
9f15fbe47c Recursively search for source button in property groups
Some checks are pending
debian-build / build (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-30 18:29:23 +01:00
WarmUpTill
0ee6feb529 Add option to check the current stream key
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-26 23:42:59 +02:00
WarmUpTill
3b82b55090 Fix Queue condition not displaying the correct size selection 2024-10-26 23:42:59 +02:00
WarmUpTill
071c3309c2 Add option to check current streaming service
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
2024-10-26 11:09:06 +02:00
WarmUpTill
ee7ee7a846 Move AutoUpdateTooltipLabel definition 2024-10-26 11:09:06 +02:00
WarmUpTill
d10300bd4b Fix typo 2024-10-26 11:09:06 +02:00
WarmUpTill
e262672876 Add "Last empty" row to Queue tab 2024-10-26 11:08:51 +02:00
WarmUpTill
392e775c7b Add temp var support for size and running state 2024-10-26 11:08:51 +02:00
WarmUpTill
f641c20564 Add SetTempVarValue() overload to support boolean values 2024-10-26 11:08:51 +02:00
WarmUpTill
39327be88e Add option to move scene item above / below specified scene item
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-21 20:46:27 +02:00
WarmUpTill
ed0963799c Fix "Screenshot" action variable selection not being loaded
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled
2024-10-18 18:30:49 +02:00
WarmUpTill
f9e73a2b8f Fix crash when clearing Twitch chat buffer 2024-10-18 18:29:46 +02:00
Przemek Pawlas
dfaa704b71 Remove some unused includes 2024-10-12 17:09:33 +02:00
Przemek Pawlas
d54edddde4 Enable use of variables as input for "Variable" action 2024-10-12 17:09:33 +02:00
Przemek Pawlas
37cc289204 Rename function 2024-10-12 17:09:33 +02:00
352 changed files with 27335 additions and 11613 deletions

View File

@@ -6,15 +6,6 @@
"autosort": true
},
"additional_commands": {
"find_qt": {
"flags": [],
"kwargs": {
"COMPONENTS": "+",
"COMPONENTS_WIN": "+",
"COMPONENTS_MACOS": "+",
"COMPONENTS_LINUX": "+"
}
},
"set_target_properties_obs": {
"pargs": 1,
"flags": [],

View File

@@ -20,6 +20,7 @@ body:
- macOS 13
- macOS 12
- macOS 11
- Ubuntu 24.04
- Ubuntu 23.10
- Ubuntu 22.04
- Other

View File

@@ -2,7 +2,7 @@
# https://github.com/obsproject/.github/tree/master/.github/ISSUE_TEMPLATE
name: Feature request
description: Suggest an idea for this projectReport a bug or crash
description: Suggest an idea for this project
body:
- type: markdown
id: md_welcome

View File

@@ -9,5 +9,10 @@ package 'libxtst-dev'
package 'libxss-dev'
package 'libopencv-dev'
package 'libtesseract-dev'
package 'libprocps-dev'
package 'libusb-1.0-0-dev'
package 'libproc2-dev'
package 'libusb-1.0-0-dev'
package 'libpaho-mqttpp-dev'
package 'libpaho-mqtt-dev'
package 'libpaho-mqtt-dev'
package 'libasound2-dev'
package 'libpipewire-0.3-dev'

View File

@@ -478,14 +478,36 @@ Usage: %B${functrace[1]%:*}%b <option> [<options>]
unset MACOSX_DEPLOYMENT_TARGET
popd
local mqtt_dir="${project_root}/deps/paho.mqtt.cpp"
local mqtt_build_dir="${mqtt_dir}/build_${target##*-}"
local -a mqtt_cmake_args=(
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_OSX_ARCHITECTURES=${${target##*-}//universal/x86_64;arm64}
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET:-10.15}
-DCMAKE_PREFIX_PATH="${advss_dep_path};${_plugin_deps}"
-DCMAKE_INSTALL_PREFIX="${advss_dep_path}"
-DPAHO_BUILD_SHARED=OFF
-DPAHO_BUILD_STATIC=ON
-DPAHO_WITH_MQTT_C=ON
-DOPENSSL_ROOT_DIR="${advss_dep_path}"
-DPAHO_WITH_SSL=OFF # TODO: figure out linking issues with openssl
)
pushd ${mqtt_dir}
log_info "Configure paho.mqtt.cpp ..."
cmake -S . -B ${mqtt_build_dir} ${mqtt_cmake_args}
log_info "Building paho.mqtt.cpp ..."
cmake --build ${mqtt_build_dir} --config Release
log_info "Installing paho.mqtt.cpp ..."
cmake --install ${mqtt_build_dir} --prefix "${advss_dep_path}" --config Release
popd
;;
linux)
# Hacky workaround to support libproc2 with Ubuntu 22 build environment
local lsb_version=$(lsb_release -r | cut -f 2 || true)
if [[ "${lsb_version}" == '22.04' ]] {
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-0_4.0.2-3_amd64.deb
sudo apt install ${project_root}/build-aux/CI/linux/ubuntu22/libproc2-dev_4.0.2-3_amd64.deb
}
# Nothing to do for now
;;
}
}

View File

@@ -219,6 +219,37 @@ function Build {
} else {
Log-Information "Failed to locate msbuild.exe - skipping libusb build"
}
Push-Location -Stack BuildMqttTemp
Ensure-Location $ProjectRoot
$MqttPath = "${ProjectRoot}/deps/paho.mqtt.cpp"
$MqttBuildPath = "${MqttPath}/build"
# Explicitly disable PkgConfig and tiff as it will lead build errors
$MqttCmakeArgs = @(
"-DCMAKE_BUILD_TYPE=${Configuration}"
"-DCMAKE_PREFIX_PATH:PATH=${OBSDepPath}"
"-DCMAKE_INSTALL_PREFIX:PATH=${ADVSSDepPath}"
"-DPAHO_WITH_MQTT_C=ON"
)
Log-Information "Configuring paho.mqtt.cpp..."
Invoke-External cmake -S ${MqttPath} -B ${MqttBuildPath} @MqttCmakeArgs
$MqttCmakeArgs = @(
'--config', "${Configuration}"
)
if ( $VerbosePreference -eq 'Continue' ) {
$MqttCmakeArgs += ('--verbose')
}
Log-Information "Building paho.mqtt.cpp..."
Invoke-External cmake --build "${MqttBuildPath}" @MqttCmakeArgs
Log-Information "Install paho.mqtt.cpp..."
Invoke-External cmake --install "${MqttBuildPath}" --prefix "${ADVSSDepPath}" @MqttCmakeArgs
}
Build

0
.github/test vendored
View File

View File

@@ -43,7 +43,7 @@ jobs:
# devscripts and libobs-dev are needed but they were already installed
# from check_libobs_revision and install_frontend_header sections.
sudo apt update
sudo apt install cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qtbase5-dev libopencv-dev libprocps-dev
sudo apt install build-essential cmake debhelper libcurl4-openssl-dev libxss-dev libxtst-dev qt6-base-dev libopencv-dev libproc2-dev
- name: build
run: |
debuild --no-lintian --no-sign

View File

@@ -10,7 +10,7 @@ env:
jobs:
check-event:
name: Check GitHub Event Data 🔎
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
defaults:
run:
shell: bash
@@ -177,7 +177,7 @@ jobs:
ubuntu-build:
name: Build for Ubuntu 🐧
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: check-event
defaults:
run:
@@ -210,6 +210,11 @@ jobs:
restore-keys: |
${{ runner.os }}-ccache-x86_64-
- name: Set up CMake 🏗️
uses: jwlawson/actions-setup-cmake@v1.13
with:
cmake-version: '3.24.x'
- name: Set up Homebrew 🍺
uses: Homebrew/actions/setup-homebrew@master
@@ -241,14 +246,14 @@ jobs:
- name: Upload Artifacts 📡
uses: actions/upload-artifact@v4
with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*.*
- name: Upload debug symbol artifacts 🪲
uses: actions/upload-artifact@v4
if: ${{ fromJSON(needs.check-event.outputs.package) }}
with:
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-ubuntu-24.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-x86_64*-dbgsym.ddeb
windows-build:

View File

@@ -3,7 +3,7 @@ on:
workflow_call:
jobs:
clang-format:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
@@ -15,7 +15,7 @@ jobs:
failCondition: error
cmake-format:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:

View File

@@ -31,7 +31,7 @@ jobs:
create-release:
name: Create Release 🛫
if: github.ref_type == 'tag'
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build-project
defaults:
run:
@@ -77,7 +77,7 @@ jobs:
variants=(
'windows-x64;zip|exe'
'macos-universal;tar.xz|pkg'
'ubuntu-22.04-x86_64;tar.xz|deb|ddeb'
'ubuntu-24.04-x86_64;tar.xz|deb|ddeb'
'sources;tar.xz'
)

9
.gitmodules vendored
View File

@@ -28,3 +28,12 @@
[submodule "deps/libusb"]
path = deps/libusb
url = https://github.com/libusb/libusb.git
[submodule "deps/date"]
path = deps/date
url = https://github.com/HowardHinnant/date.git
[submodule "deps/jsoncons"]
path = deps/jsoncons
url = https://github.com/danielaparker/jsoncons.git
[submodule "deps/paho.mqtt.cpp"]
path = deps/paho.mqtt.cpp
url = https://github.com/eclipse-paho/paho.mqtt.cpp.git

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.16...3.26)
cmake_minimum_required(VERSION 3.21...3.26)
project(advanced-scene-switcher VERSION 1.0.0)
@@ -20,6 +20,12 @@ if(BUILD_OUT_OF_TREE)
include(helpers)
endif()
# OBS 31 no longer defines find_qt so check if we need to include it for in-tree
# builds
if(NOT COMMAND find_qt)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/helpers_common.cmake")
endif()
set(LIB_NAME "${PROJECT_NAME}-lib")
add_library(${PROJECT_NAME} MODULE)
add_library(${LIB_NAME} SHARED)
@@ -27,6 +33,11 @@ add_library(${LIB_NAME} SHARED)
include(cmake/common/get_git_revision_description.cmake)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
git_describe(GIT_TAG)
if(${GIT_TAG} STREQUAL "GIT-NOTFOUND")
set(GIT_TAG ${PROJECT_VERSION})
endif()
message(STATUS "${PROJECT_NAME} version: ${GIT_TAG}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/version.cpp.in"
"${CMAKE_CURRENT_BINARY_DIR}/lib/version.cpp" @ONLY)
@@ -92,8 +103,6 @@ target_sources(
lib/macro/macro-action-macro.hpp
lib/macro/macro-action-queue.cpp
lib/macro/macro-action-queue.hpp
lib/macro/macro-action-script.cpp
lib/macro/macro-action-script.hpp
lib/macro/macro-action-variable.cpp
lib/macro/macro-action-variable.hpp
lib/macro/macro-action.cpp
@@ -106,8 +115,6 @@ target_sources(
lib/macro/macro-condition-macro.hpp
lib/macro/macro-condition-queue.cpp
lib/macro/macro-condition-queue.hpp
lib/macro/macro-condition-script.cpp
lib/macro/macro-condition-script.hpp
lib/macro/macro-condition-tempvar.cpp
lib/macro/macro-condition-tempvar.hpp
lib/macro/macro-condition-variable.cpp
@@ -116,6 +123,8 @@ target_sources(
lib/macro/macro-condition.hpp
lib/macro/macro-dock.cpp
lib/macro/macro-dock.hpp
lib/macro/macro-edit.cpp
lib/macro/macro-edit.hpp
lib/macro/macro-export-import-dialog.cpp
lib/macro/macro-export-import-dialog.hpp
lib/macro/macro-helpers.cpp
@@ -128,14 +137,10 @@ target_sources(
lib/macro/macro-ref.hpp
lib/macro/macro-run-button.cpp
lib/macro/macro-run-button.hpp
lib/macro/macro-script-handler.cpp
lib/macro/macro-script-handler.hpp
lib/macro/macro-segment-copy-paste.cpp
lib/macro/macro-segment-copy-paste.hpp
lib/macro/macro-segment-list.cpp
lib/macro/macro-segment-list.hpp
lib/macro/macro-segment-script.cpp
lib/macro/macro-segment-script.hpp
lib/macro/macro-segment-selection.cpp
lib/macro/macro-segment-selection.hpp
lib/macro/macro-segment.cpp
@@ -157,6 +162,8 @@ target_sources(
lib/queue/action-queue.hpp
lib/queue/action-queue-tab.cpp
lib/queue/action-queue-tab.hpp
lib/utils/auto-update-tooltip-label.cpp
lib/utils/auto-update-tooltip-label.hpp
lib/utils/backup.cpp
lib/utils/backup.hpp
lib/utils/condition-logic.cpp
@@ -182,6 +189,8 @@ target_sources(
lib/utils/help-icon.cpp
lib/utils/item-selection-helpers.cpp
lib/utils/item-selection-helpers.hpp
lib/utils/json-helpers.cpp
lib/utils/json-helpers.hpp
lib/utils/layout-helpers.cpp
lib/utils/layout-helpers.hpp
lib/utils/list-controls.cpp
@@ -208,17 +217,14 @@ target_sources(
lib/utils/plugin-state-helpers.hpp
lib/utils/priority-helper.cpp
lib/utils/priority-helper.hpp
lib/utils/properties-view.cpp
lib/utils/properties-view.hpp
lib/utils/properties-view.moc.hpp
lib/utils/regex-config.cpp
lib/utils/regex-config.hpp
lib/utils/resizable-widget.cpp
lib/utils/resizable-widget.hpp
lib/utils/resizing-text-edit.cpp
lib/utils/resizing-text-edit.hpp
lib/utils/resource-table.cpp
lib/utils/resource-table.hpp
lib/utils/resource-table-hotkey-handler.cpp
lib/utils/resource-table-hotkey-handler.hpp
lib/utils/scene-selection.cpp
lib/utils/scene-selection.hpp
lib/utils/scene-switch-helpers.cpp
@@ -287,9 +293,16 @@ include(cmake/common/advss_helpers.cmake)
setup_obs_lib_dependency(${LIB_NAME})
setup_obs_lib_dependency(${PROJECT_NAME})
find_qt(COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt::Core Qt::Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core Qt6::Widgets)
target_link_libraries(${LIB_NAME} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported Qt
# version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.0.0")
target_compile_definitions(${LIB_NAME} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
target_compile_options(
${PROJECT_NAME}
PRIVATE
@@ -338,6 +351,21 @@ else()
endif()
target_link_libraries(${LIB_NAME} PUBLIC nlohmann_json::nlohmann_json)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/deps/jsoncons/CMakeLists.txt")
# Don't build jsoncons unit tests as they are causing compilation issues and
# won't be executed either way
if(OS_MACOS)
cmake_policy(SET CMP0077 NEW)
endif()
set(JSONCONS_BUILD_TESTS
OFF
CACHE BOOL "" FORCE)
add_subdirectory(deps/jsoncons EXCLUDE_FROM_ALL)
target_link_libraries(${LIB_NAME} PRIVATE jsoncons)
target_compile_definitions(${LIB_NAME} PRIVATE JSONPATH_SUPPORT=1)
endif()
find_package(CURL QUIET)
find_package(Libcurl QUIET)
if(CURL_FOUND)
@@ -391,6 +419,7 @@ else()
set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
set_target_properties(${LIB_NAME} PROPERTIES SOVERSION 1)
find_package(Qt6 REQUIRED COMPONENTS DBus)
find_package(X11 REQUIRED COMPONENTS Xss)
target_include_directories(${LIB_NAME} PRIVATE "${X11_INCLUDE_DIR}"
"${X11_Xss_INCLUDE_PATH}")
@@ -405,7 +434,12 @@ else()
set(PROCESS_CONDITION_SUPPORTED 1)
endif()
if(PROCPS2_INCLUDE_DIR)
message(STATUS "${PROJECT_NAME} using libproc2")
find_package(PkgConfig REQUIRED)
pkg_check_modules(libproc2 IMPORTED_TARGET libproc2<4.0.5 QUIET)
if(libproc2_FOUND)
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_USE_INFO)
endif()
pkg_check_modules(libproc2 REQUIRED IMPORTED_TARGET libproc2)
set(PROC_INCLUDE_DIR "${PROCPS2_INCLUDE_DIR}")
target_compile_definitions(${LIB_NAME} PRIVATE PROCPS2_AVAILABLE)
set(PROCESS_CONDITION_SUPPORTED 1)
@@ -417,7 +451,11 @@ else()
)
endif()
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp
lib/linux/kwin-helpers.cpp)
# Don't include irrelevant folders into sources archive
list(APPEND CPACK_SOURCE_IGNORE_FILES "\\.deps/.*")
endif()
if(NOT OS_WINDOWS)

View File

@@ -19,6 +19,8 @@ The **Snap** package manager offers an OBS Studio installation which is bundled
sudo snap install obs-studio
```
More information can be found [here](https://github.com/WarmUpTill/SceneSwitcher/wiki/Installation).
## Contributing
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.

View File

@@ -7,7 +7,7 @@ Build-Depends: cmake,
libcurl4-openssl-dev,
libobs-dev,
libxtst-dev,
qtbase5-dev,
qt6-base-dev,
libxss-dev,
libopencv-dev
Standards-Version: 4.6.0

View File

@@ -3,7 +3,7 @@
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export QT_SELECT = qt5
export QT_SELECT = qt6
%:
dh $@

View File

@@ -283,8 +283,14 @@ endfunction()
function(setup_advss_plugin target)
setup_obs_lib_dependency(${target})
find_qt(COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt::Core Qt::Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
target_link_libraries(${target} PRIVATE Qt6::Core Qt6::Widgets)
# Ignore QCheckBox::stateChanged deprecation warning until minimum supported
# Qt version is at least Qt 6.7, which introduces QCheckBox::checkStateChanged
if(Qt6_VERSION VERSION_GREATER "6.7.0")
target_compile_definitions(${target} PRIVATE QT_NO_DEPRECATED_WARNINGS)
endif()
set_target_properties(
${target}

View File

@@ -17,85 +17,6 @@ if(NOT QT_VERSION)
set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6)
endif()
# find_qt: Macro to find best possible Qt version for use with the project:
macro(find_qt)
set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX)
cmake_parse_arguments(find_qt "" "${oneValueArgs}" "${multiValueArgs}"
${ARGN})
# Do not use versionless targets in the first step to avoid Qt::Core being
# clobbered by later opportunistic find_package runs
set(QT_NO_CREATE_VERSIONLESS_TARGETS TRUE)
message(DEBUG "Start Qt version discovery...")
# Loop until _QT_VERSION is set or FATAL_ERROR aborts script execution early
while(NOT _QT_VERSION)
message(DEBUG "QT_VERSION set to ${QT_VERSION}")
if(QT_VERSION STREQUAL AUTO AND NOT qt_test_version)
set(qt_test_version 6)
elseif(NOT QT_VERSION STREQUAL AUTO)
set(qt_test_version ${QT_VERSION})
endif()
message(DEBUG "Attempting to find Qt${qt_test_version}")
find_package(
Qt${qt_test_version}
COMPONENTS Core
QUIET)
if(TARGET Qt${qt_test_version}::Core)
set(_QT_VERSION
${qt_test_version}
CACHE INTERNAL "")
message(STATUS "Qt version found: ${_QT_VERSION}")
unset(qt_test_version)
break()
elseif(QT_VERSION STREQUAL AUTO)
if(qt_test_version EQUAL 6)
message(WARNING "Qt6 was not found, falling back to Qt5")
set(qt_test_version 5)
continue()
endif()
endif()
message(FATAL_ERROR "Neither Qt6 nor Qt5 found.")
endwhile()
# Enable versionless targets for the remaining Qt components
set(QT_NO_CREATE_VERSIONLESS_TARGETS FALSE)
set(qt_components ${find_qt_COMPONENTS})
if(OS_WINDOWS)
list(APPEND qt_components ${find_qt_COMPONENTS_WIN})
elseif(OS_MACOS)
list(APPEND qt_components ${find_qt_COMPONENTS_MAC})
else()
list(APPEND qt_components ${find_qt_COMPONENTS_LINUX})
endif()
message(DEBUG "Trying to find Qt components ${qt_components}...")
find_package(Qt${_QT_VERSION} REQUIRED ${qt_components})
list(APPEND qt_components Core)
if("Gui" IN_LIST find_qt_COMPONENTS_LINUX)
list(APPEND qt_components "GuiPrivate")
endif()
# Check for versionless targets of each requested component and create if
# necessary
foreach(component IN LISTS qt_components)
message(DEBUG "Checking for target Qt::${component}")
if(NOT TARGET Qt::${component} AND TARGET Qt${_QT_VERSION}::${component})
add_library(Qt::${component} INTERFACE IMPORTED)
set_target_properties(
Qt::${component} PROPERTIES INTERFACE_LINK_LIBRARIES
Qt${_QT_VERSION}::${component})
endif()
set_property(TARGET Qt::${component} PROPERTY INTERFACE_COMPILE_FEATURES "")
endforeach()
endmacro()
# check_uuid: Helper function to check for valid UUID
function(check_uuid uuid_string return_value)
set(valid_uuid TRUE)

View File

@@ -80,6 +80,8 @@ function(set_target_properties_plugin target)
configure_file(cmake/windows/resources/installer-Windows.iss.in
"${CMAKE_CURRENT_BINARY_DIR}/installer-Windows.generated.iss")
configure_file(data/res/images/logo.ico
"${CMAKE_CURRENT_BINARY_DIR}/installer.ico" COPYONLY)
configure_file(cmake/windows/resources/resource.rc.in
"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.rc")

View File

@@ -20,6 +20,8 @@ OutputBaseFilename={#MyAppName}-{#MyAppVersion}-Windows-Installer
Compression=lzma
SolidCompression=yes
DirExistsWarning=no
SetupIconFile=installer.ico
UninstallDisplayIcon={app}\data\obs-plugins\advanced-scene-switcher\res\images\logo.ico
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

View File

@@ -26,7 +26,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Wechsle zu
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Wechsle zu:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Nach Ausführen von Aktionen überspringe das Ausführen von Aktionen für"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="In diesem Zeitraum werden potentielle erfüllte Bedingungen ignoriert!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ausführliches Logging"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Fensterposition und -größe speichern"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Benachrichtigungen im System-Tray anzeigen"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deaktiviere UI Tipps"
@@ -41,7 +40,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exportieren"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importieren"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportiere Erweiterter Automatischer Szenenwechsler Einstellungen zu Datei ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Exportiere Erweiterter Automatischer Szenenwechsler Einstellungen von Datei ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text Dateien (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text Dateien (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Importieren der Einstellungen ist fehlgeschlagen"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Einstellungen wurden erfolgreich importiert"
AdvSceneSwitcher.generalTab.priority.fileContent="Datei Inhalt"
@@ -195,12 +194,12 @@ AdvSceneSwitcher.condition.video.modelLoadFail="Modelldaten konnten nicht gelade
AdvSceneSwitcher.condition.video.type.main="OBS's Haupt-Ausgabe"
AdvSceneSwitcher.condition.video.type.source="Quelle"
AdvSceneSwitcher.condition.video.type.scene="Szene"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Modelldaten (Haar-Kaskaden-Klassifikator): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimale Anzahl von Nachbarn: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzieren Sie die CPU-Belastung, indem Sie die Prüfung nur alle {{throttleCount}} Millisekunden"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Kontrolle nur im Bereich durchführen"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Modelldaten (Haar-Kaskaden-Klassifikator): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Minimale Anzahl von Nachbarn: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Reduzieren Sie die CPU-Belastung, indem Sie die Prüfung nur alle {{throttleCount}} Millisekunden"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Kontrolle nur im Bereich durchführen"
AdvSceneSwitcher.condition.video.layout.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="Minimale Größe:"
AdvSceneSwitcher.condition.video.maxSize="Maximale Größe:"
AdvSceneSwitcher.condition.video.selectArea="Bereich auswählen"
@@ -212,7 +211,6 @@ AdvSceneSwitcher.condition.stream.state.start="Stream läuft"
AdvSceneSwitcher.condition.stream.state.stop="Stream gestoppt"
AdvSceneSwitcher.condition.stream.state.starting="Stream wird gestartet"
AdvSceneSwitcher.condition.stream.state.stopping="Stream wird gestoppt"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Aufnahme"
AdvSceneSwitcher.condition.record.state.start="Aufnahme läuft"
AdvSceneSwitcher.condition.record.state.pause="Aufnahme pausiert"
@@ -490,7 +488,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Nach unten verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="An erste Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="An letzte Stelle verschieben"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -527,7 +525,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Standard"
AdvSceneSwitcher.action.screenshot.save.custom="Benutzerdefiniert"
AdvSceneSwitcher.action.screenshot.type.source="Quelle"
AdvSceneSwitcher.action.screenshot.type.scene="Szene"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's Haupt-Ausgabe"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Quellen oder Szenen, die nicht immer gerendert werden, können dazu führen, dass einige Teile der Screenshots leer bleiben."
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}und speichere in{{saveType}}{{variables}}Pfad"
AdvSceneSwitcher.action.profile="Profil"
@@ -755,9 +752,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Starte/Stoppe den Erweite
AdvSceneSwitcher.hotkey.macro.pause="Pausiere Makro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Makro %1 nicht mehr pausieren"
AdvSceneSwitcher.hotkey.macro.togglePause="Pause von Makro %1 togglen"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Makro-Segmentauswahl nach oben verschieben"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Makro-Segmentauswahl nach unten verschieben"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Ausgewähltes Makro-Segment entfernen"
AdvSceneSwitcher.hotkey.macro.segment.up="Makro-Segmentauswahl nach oben verschieben"
AdvSceneSwitcher.hotkey.macro.segment.down="Makro-Segmentauswahl nach unten verschieben"
AdvSceneSwitcher.hotkey.macro.segment.remove="Ausgewähltes Makro-Segment entfernen"
AdvSceneSwitcher.askBackup="Neue Version des Erweiterten Automatischen Szenenwechslers wurde erkannt.\nSoll ein Backup der alten Einstellungen angelegt werden?"
AdvSceneSwitcher.askForMacro="Makro auswählen {{macroSelection}}"

View File

@@ -29,11 +29,11 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Switch to:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="After performing actions skip performing actions for"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="During this time potential matches will be ignored!"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel="Log level:"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.disable="Disable logging"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Default"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logMacro="Log executed macros"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="Log performed actions"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Verbose logging"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose logging"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
@@ -42,6 +42,7 @@ AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailure="Display warni
AdvSceneSwitcher.generalTab.generalBehavior.warnPluginLoadFailureMessage="<html><body>Loading of the following plugin libraries was unsuccessful, which could result in some Advanced Scene Switcher functions not being available:%1Check the OBS logs for details.<br>This message can be disabled on the General tab.</body></html>"
AdvSceneSwitcher.generalTab.generalBehavior.warnCorruptedInstallMessage="The plugin installation seems to be corrupted and might crash!\nPlease make sure the plugin was installed correctly!"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
AdvSceneSwitcher.generalTab.generalBehavior.disableMacroWidgetCache="Disable macro widget caching"
AdvSceneSwitcher.generalTab.matchBehavior="Match behavior"
AdvSceneSwitcher.generalTab.priority="Priority"
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
@@ -52,7 +53,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Export"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Import"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Export Advanced Scene Switcher settings to file ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Import Advanced Scene Switcher settings from file ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text files (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text files (*.txt);;JSON files (*.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Advanced Scene Switcher failed to import settings"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Advanced Scene Switcher settings imported successfully"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportSensitiveDataWarning="Warning:\nThe exported data might contain sensitive information!"
@@ -97,6 +98,7 @@ AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="Add new action queue"
AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip="Remove selected action queues"
AdvSceneSwitcher.actionQueueTab.name.header="Name"
AdvSceneSwitcher.actionQueueTab.size.header="Size"
AdvSceneSwitcher.actionQueueTab.lastEmpty.header="Last empty"
AdvSceneSwitcher.actionQueueTab.isRunning.header="Is running"
AdvSceneSwitcher.actionQueueTab.runOnStartup.header="Run on startup"
AdvSceneSwitcher.actionQueueTab.yes="Yes"
@@ -131,14 +133,26 @@ AdvSceneSwitcher.twitchConnectionTab.no="No"
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 Twitch connections?"
# MQTT Connections Tab
AdvSceneSwitcher.mqttConnectionTab.title="MQTT Connections"
AdvSceneSwitcher.mqttConnectionTab.help="MQTT connections can be used to communicate with other devices.\n\nClick on the highlighted plus symbol to add a new connection."
AdvSceneSwitcher.mqttConnectionTab.mqttonnectionAddButton.tooltip="Add new MQTT connection"
AdvSceneSwitcher.mqttConnectionTab.mqttConnectionRemoveButton.tooltip="Remove selected MQTT connections"
AdvSceneSwitcher.mqttConnectionTab.name.header="Name"
AdvSceneSwitcher.mqttConnectionTab.address.header="Address"
AdvSceneSwitcher.mqttConnectionTab.topics.header="Topic subscription count"
AdvSceneSwitcher.mqttConnectionTab.status.header="Status"
AdvSceneSwitcher.mqttConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
AdvSceneSwitcher.mqttConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?"
# Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab."
AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro."
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define Macro conditions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new condition."
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define Macro actions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define Macro actions, which are executed if the conditions are *not* met.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define macro conditions.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new condition."
AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action."
AdvSceneSwitcher.macroTab.edit="Edit macro"
AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
@@ -163,6 +177,8 @@ AdvSceneSwitcher.macroTab.contextMenuAdd="Add"
AdvSceneSwitcher.macroTab.copy="Duplicate Macro"
AdvSceneSwitcher.macroTab.group="Group Selected Macros"
AdvSceneSwitcher.macroTab.ungroup="Ungroup Selected Groups"
AdvSceneSwitcher.macroTab.expandAllGroups="Expand all Groups"
AdvSceneSwitcher.macroTab.collapseAllGroups="Collapse all Groups"
AdvSceneSwitcher.macroTab.rename="Rename"
AdvSceneSwitcher.macroTab.remove="Remove"
AdvSceneSwitcher.macroTab.export="Export"
@@ -198,8 +214,11 @@ AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of curre
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for new macros"
AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange="Save settings when selecting a new macro"
AdvSceneSwitcher.macroTab.newMacroCheckInParallel="Evaluate conditions of new macros in parallel to other macros"
AdvSceneSwitcher.macroTab.checkInParallel.tooltip="If this option is not enabled, this macro's conditions will be evaluated after the preceeding macro's condition check has completed.\nSo, if there are three macros - Macro A, Macro B, and Macro C - in the macro list the order the conditions in are evaluated is:\n • Macro A's conditions\n • Macro B's conditions\n • Macro C's conditions\nIf this option is enabled, the order might differ.\nEnabling this option can be useful for scenarios in which you have multiple long running condition checks, which you don't want to result in \"blocking\" other macros."
AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange="Save settings when switching between macros"
AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange.tooltip="Saving the settings can be an expensive operation if you are working with a very large scene collection.\nIn this case, it might make sense to disable this option."
AdvSceneSwitcher.macroTab.currentCheckInParallel="Evaluate conditions of selected macro in parallel to other macros (Experimental)"
AdvSceneSwitcher.macroTab.currentRegisterHotkeys="Register hotkeys to control the pause state of selected macro"
AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for currently selected macro"
AdvSceneSwitcher.macroTab.shortCircuit.tooltip="Enabling short circuit evaluation might improve the performance, as some condition checks are skipped, if the overall macro cannot be evaluated to \"true\" anymore.\nHowever, please note that condition checks, which are skipped over, will also not update their duration modifier checks."
@@ -307,7 +326,7 @@ AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="During transi
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}{{regex}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.window="{{checkTitle}}Window title matches{{windowRegex}}{{windows}}"
AdvSceneSwitcher.condition.window.entry.window="{{checkTitle}}Window title matches{{windows}}{{windowRegex}}"
AdvSceneSwitcher.condition.window.entry.fullscreen="{{fullscreen}}Window is fullscreen"
AdvSceneSwitcher.condition.window.entry.maximized="{{maximized}}Window is maximized"
AdvSceneSwitcher.condition.window.entry.focused="{{focused}}Window is focused"
@@ -376,6 +395,13 @@ AdvSceneSwitcher.condition.video.patternMatchSuccessFullImage="Full image matche
AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
AdvSceneSwitcher.condition.video.ocrMatchSuccess="Detected text:\n\n%1"
AdvSceneSwitcher.condition.video.ocrMatchFail="Could not detect any text!"
AdvSceneSwitcher.condition.video.ocrUseConfigFile="Use custom tesseract config file"
AdvSceneSwitcher.condition.video.ocrOpenConfigFile="Open"
AdvSceneSwitcher.condition.video.ocrOpenConfig.createFailed="Could not create the config file!"
AdvSceneSwitcher.condition.video.ocrOpenConfig.openFailed="Could not open the config file!"
AdvSceneSwitcher.condition.video.ocrConfigReload="Reload configuration file"
AdvSceneSwitcher.condition.video.ocrConfigHint="Tesseract config files consist of lines with parameter-value pairs (space separated).\nFor example:\n\ntessedit_char_blacklist\t\t\t\t\"abc\"\nlanguage_model_penalty_non_dict_word\t0"
AdvSceneSwitcher.condition.video.modelLoadFail="Model data could not be loaded!"
AdvSceneSwitcher.condition.video.selectColor="Select Color"
AdvSceneSwitcher.condition.video.ocrMode.singleColumn="Single column of text of variable sizes"
@@ -395,16 +421,18 @@ AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="How similar
AdvSceneSwitcher.condition.video.type.main="OBS's main output"
AdvSceneSwitcher.condition.video.type.source="Source"
AdvSceneSwitcher.condition.video.type.scene="Scene"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every{{throttleCount}}milliseconds"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Perform check only in area"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.entry.orcColorPick="Check for text color:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.entry.orcTextType="Check for text type:{{textType}}"
AdvSceneSwitcher.condition.video.entry.orcLanguage="Check for language:{{languageCode}}"
AdvSceneSwitcher.condition.video.entry.color="Check for color:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Model data (haar cascade classifier):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Minimum neighbors:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Reduce CPU load by performing check only every{{throttleCount}}milliseconds"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Perform check only in area"
AdvSceneSwitcher.condition.video.layout.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.layout.ocrColorPick="Check for text color:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.ocrTextType="Check for text type:{{textType}}"
AdvSceneSwitcher.condition.video.layout.ocrBaseDir="Tesseract base directory:{{tesseractBaseDir}}"
AdvSceneSwitcher.condition.video.layout.ocrLanguage="Check for language:{{languageCode}}"
AdvSceneSwitcher.condition.video.layout.ocrConfig="Config file:{{configFile}}{{openConfigFile}}{{reloadConfig}}{{configFileHint}}"
AdvSceneSwitcher.condition.video.layout.color="Check for color:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.minSize="Minimum size:"
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
AdvSceneSwitcher.condition.video.selectArea="Select area"
@@ -418,7 +446,10 @@ AdvSceneSwitcher.condition.stream.state.stop="Stream stopped"
AdvSceneSwitcher.condition.stream.state.starting="Stream starting"
AdvSceneSwitcher.condition.stream.state.stopping="Stream stopping"
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Keyframe interval equals"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.stream.state.streamKey="Stream key is"
AdvSceneSwitcher.condition.stream.state.service="Service name matches"
AdvSceneSwitcher.condition.stream.service.tooltip="Current service name: %1"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}{{streamKey}}{{serviceName}}{{regex}}{{currentService}}"
AdvSceneSwitcher.condition.record="Recording"
AdvSceneSwitcher.condition.record.state.start="Recording running"
AdvSceneSwitcher.condition.record.state.pause="Recording paused"
@@ -478,6 +509,7 @@ AdvSceneSwitcher.condition.source.type.showing="Is showing"
AdvSceneSwitcher.condition.source.type.settingsMatch="Settings match"
AdvSceneSwitcher.condition.source.type.settingsChanged="Settings changed"
AdvSceneSwitcher.condition.source.type.individualSettingMatches="Setting value matches"
AdvSceneSwitcher.condition.source.type.individualSettingListSelectionMatches="Settings value list selection matches"
AdvSceneSwitcher.condition.source.type.individualSettingChanged="Setting value changed"
AdvSceneSwitcher.condition.source.type.width="Width"
AdvSceneSwitcher.condition.source.type.height="Height"
@@ -486,6 +518,7 @@ AdvSceneSwitcher.condition.source.sizeCompare.equal="Equals"
AdvSceneSwitcher.condition.source.sizeCompare.more="Bigger than"
AdvSceneSwitcher.condition.source.refresh="Refresh"
AdvSceneSwitcher.condition.source.refresh.tooltip="Repopulate the source settings selection with the settings of the source which's name matches the variable value."
AdvSceneSwitcher.condition.source.includeDefaults="Include default settings values"
AdvSceneSwitcher.condition.source.sceneVisibilityHint="Scene specific visibility can be checked using the \"Scene item visibility\" condition"
AdvSceneSwitcher.condition.source.getSettings="Get current settings"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}}{{conditions}}{{settingSelection}}{{refresh}}{{sizeCompareMethods}}{{size}}"
@@ -501,9 +534,11 @@ AdvSceneSwitcher.condition.filter.type.showing="Is disabled"
AdvSceneSwitcher.condition.filter.type.settingsMatch="Settings match"
AdvSceneSwitcher.condition.filter.type.settingsChanged="Settings changed"
AdvSceneSwitcher.condition.filter.type.individualSettingMatches="Settings value matches"
AdvSceneSwitcher.condition.filter.type.individualSettingListSelectionMatches="Settings value list selection matches"
AdvSceneSwitcher.condition.filter.type.individualSettingChanged="Settings value changed"
AdvSceneSwitcher.condition.filter.refresh="Refresh"
AdvSceneSwitcher.condition.filter.refresh.tooltip="Repopulate the filter settings selection with the settings of the filter which's name matches the variable value."
AdvSceneSwitcher.condition.filter.includeDefaults="Include default settings values"
AdvSceneSwitcher.condition.filter.getSettings="Get current settings"
AdvSceneSwitcher.condition.filter.entry.line1="On{{sources}}{{filters}}{{conditions}}{{settingSelection}}{{refresh}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
@@ -701,6 +736,8 @@ AdvSceneSwitcher.condition.twitch.type.polling.channel.live="Stream is currently
AdvSceneSwitcher.condition.twitch.type.polling.channel.title="Current title matches"
AdvSceneSwitcher.condition.twitch.type.polling.channel.category="Current category is"
AdvSceneSwitcher.condition.twitch.type.chat.message="Chat message received"
AdvSceneSwitcher.condition.twitch.type.chat.messageRemoved="Single chat message removed"
AdvSceneSwitcher.condition.twitch.type.chat.cleared="All messages of user or channel cleared"
AdvSceneSwitcher.condition.twitch.type.chat.properties="Chat message properties:"
AdvSceneSwitcher.condition.twitch.type.chat.properties.select="Select chat message property:"
AdvSceneSwitcher.condition.twitch.type.chat.properties.firstMessage="Is first message"
@@ -765,6 +802,14 @@ AdvSceneSwitcher.condition.streamDeck.checkData="Check data field"
AdvSceneSwitcher.condition.streamDeck.startListen="Start listening"
AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening"
AdvSceneSwitcher.condition.streamDeck.pluginDownload="<html><head/><body><p>The Stream Deck plugin can be found <a href=\"https://github.com/WarmUpTill/advanced-scene-switcher-streamdeck-plugin/releases\"><span style=\" text-decoration: underline; color:#268bd2;\">here on GitHub</span></a>.</p></body></html>"
AdvSceneSwitcher.condition.gameCapture="Game capture"
AdvSceneSwitcher.condition.gameCapture.entry="{{sources}}hooked a game."
AdvSceneSwitcher.condition.screenshot="Screenshot"
AdvSceneSwitcher.condition.screenshot.entry="A screenshot was taken"
AdvSceneSwitcher.condition.mqtt="MQTT"
AdvSceneSwitcher.condition.mqtt.layout.match="Message was received from{{connection}} which matches{{regex}}:"
AdvSceneSwitcher.condition.mqtt.layout.listen="Set message selection to incoming message:{{listenButton}}"
AdvSceneSwitcher.condition.script="Script"
# Macro Actions
AdvSceneSwitcher.action.unknown="Unknown action"
@@ -783,6 +828,7 @@ AdvSceneSwitcher.action.wait.entry.random="Wait for{{waitType}}duration from{{du
AdvSceneSwitcher.action.audio="Audio"
AdvSceneSwitcher.action.audio.type.mute="Mute"
AdvSceneSwitcher.action.audio.type.unmute="Unmute"
AdvSceneSwitcher.action.audio.type.toggleMute="Toggle mute"
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset"
@@ -848,6 +894,7 @@ AdvSceneSwitcher.action.filter.entry="On{{sources}}{{actions}}{{filters}}{{refre
AdvSceneSwitcher.action.filter.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
AdvSceneSwitcher.action.filter.getSettings="Get current settings"
AdvSceneSwitcher.action.filter.inputMethod.individualManual="Set to fixed value"
AdvSceneSwitcher.action.filter.inputMethod.individualListEntryManual="Set to list entry"
AdvSceneSwitcher.action.filter.inputMethod.individualTempvar="Set to macro property"
AdvSceneSwitcher.action.filter.inputMethod.json="Set setting JSON string"
AdvSceneSwitcher.action.source="Source"
@@ -878,6 +925,7 @@ AdvSceneSwitcher.action.source.deinterlaceMode.yadif2x="Yadif 2x"
AdvSceneSwitcher.action.source.deinterlaceOrder.topFieldFirst="Top Field First"
AdvSceneSwitcher.action.source.deinterlaceOrder.bottomFieldFirst="Bottom Field First"
AdvSceneSwitcher.action.source.inputMethod.individualManual="Set to fixed value"
AdvSceneSwitcher.action.source.inputMethod.individualListEntryManual="Set to list entry"
AdvSceneSwitcher.action.source.inputMethod.individualTempvar="Set to macro property"
AdvSceneSwitcher.action.source.inputMethod.json="Set setting JSON string"
AdvSceneSwitcher.action.source.refresh="Refresh"
@@ -899,6 +947,7 @@ AdvSceneSwitcher.action.media.seek.percentage.label="Percentage of total duratio
AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pause"
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
AdvSceneSwitcher.action.macro.type.togglePause="Toggle pause"
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
AdvSceneSwitcher.action.macro.type.run="Run macro"
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="Do not consider condition state"
@@ -915,6 +964,10 @@ AdvSceneSwitcher.action.macro.type.stop="Stop actions"
AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
AdvSceneSwitcher.action.macro.type.nestedMacro="Nested macro"
AdvSceneSwitcher.action.macro.type.nestedMacro.conditionHelp="This section allows you to define macro conditions.\n\nClick the plus button below to add a new condition."
AdvSceneSwitcher.action.macro.type.nestedMacro.actionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nClick the plus button below to add a new action."
AdvSceneSwitcher.action.macro.type.nestedMacro.elseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nClick the plus button below to add a new action."
AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}of{{macros}}"
AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}"
@@ -956,7 +1009,9 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Move to top"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.type.above="Move above"
AdvSceneSwitcher.action.sceneOrder.type.below="Move below"
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}{{sources2}}"
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
AdvSceneSwitcher.action.sceneTransform.type.manual="Set transform string"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Set transform setting"
@@ -1011,7 +1066,6 @@ AdvSceneSwitcher.action.screenshot.save.custom="Custom file path"
AdvSceneSwitcher.action.screenshot.save.variable="Variable (base64 encoded PNG)"
AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="Scene"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS's main output"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Sources or scenes, which are not always rendered, may result in some parts of screenshots to remain blank."
AdvSceneSwitcher.action.screenshot.entry="Screenshot{{targetType}}{{sources}}{{scenes}}and save to{{saveType}}{{variables}}"
AdvSceneSwitcher.action.profile="Profile"
@@ -1040,21 +1094,35 @@ AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.request="Send{{api}}of typ
AdvSceneSwitcher.action.websocket.entry.sceneSwitcher.event="Send{{api}}of type{{type}}to connected clients"
AdvSceneSwitcher.action.websocket.entry.generic="Send{{api}}via{{connection}}"
AdvSceneSwitcher.action.http="HTTP"
AdvSceneSwitcher.action.http.legacy="HTTP (legacy)"
AdvSceneSwitcher.action.http.setHeaders="Set headers"
AdvSceneSwitcher.action.http.headers="Headers:"
AdvSceneSwitcher.action.http.addHeader="Add header"
AdvSceneSwitcher.action.http.addHeader.name="Header name"
AdvSceneSwitcher.action.http.addHeader.value="Header value"
AdvSceneSwitcher.action.http.setParams="Set parameters"
AdvSceneSwitcher.action.http.params="Parameters:"
AdvSceneSwitcher.action.http.addParam.name="Parameter name"
AdvSceneSwitcher.action.http.addParam.value="Parameter value"
AdvSceneSwitcher.action.http.body="Message body:"
AdvSceneSwitcher.action.http.type.get="GET"
AdvSceneSwitcher.action.http.type.post="POST"
AdvSceneSwitcher.action.http.type.put="PUT"
AdvSceneSwitcher.action.http.type.patch="PATCH"
AdvSceneSwitcher.action.http.type.delete="DELETE"
AdvSceneSwitcher.action.http.layout.method="Send{{method}}to URL{{url}}"
AdvSceneSwitcher.action.http.layout.contentType="Content type:{{contentType}}"
AdvSceneSwitcher.action.http.layout.timeout="Timeout:{{timeout}}seconds"
AdvSceneSwitcher.action.http.entry.line1="Send{{method}}to{{url}}"
AdvSceneSwitcher.action.http.entry.line2="Timeout:{{timeout}}seconds"
AdvSceneSwitcher.action.variable="Variable"
AdvSceneSwitcher.action.variable.type.set="Set to fixed value"
AdvSceneSwitcher.action.variable.type.set="Set to value"
AdvSceneSwitcher.action.variable.type.append="Append"
AdvSceneSwitcher.action.variable.type.appendVar="Append variable"
AdvSceneSwitcher.action.variable.type.increment="Increment"
AdvSceneSwitcher.action.variable.type.decrement="Decrement"
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value"
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value"
AdvSceneSwitcher.action.variable.type.setActionValue="Set to action value (legacy)"
AdvSceneSwitcher.action.variable.type.setConditionValue="Set to condition value (legacy)"
AdvSceneSwitcher.action.variable.type.roundToInt="Round to nearest integer"
AdvSceneSwitcher.action.variable.type.subString="Set to substring of current value"
AdvSceneSwitcher.action.variable.type.findAndReplace="Find and replace in current value"
@@ -1063,7 +1131,10 @@ AdvSceneSwitcher.action.variable.type.askForValue="Get user input"
AdvSceneSwitcher.action.variable.type.environmentVariable="Set to environment variable value"
AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of scene"
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
AdvSceneSwitcher.action.variable.type.extractJsonField="Extract JSON field with name"
AdvSceneSwitcher.action.variable.type.queryJson="Query JSON"
AdvSceneSwitcher.action.variable.type.queryJson.info="You can use \"JSONPath\" (RFC 9535) syntax here.\nSo, for example:\n\n • $['books'][0]['category']\n • $.books[0].category\n\nIf the query or the JSON should be invalid, the variable value will not be changed.\nIf the input is valid, the result of the query will always be a JSON array."
AdvSceneSwitcher.action.variable.type.accessJsonArray="Access JSON array at index"
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
AdvSceneSwitcher.action.variable.type.setToTempvar.help="This action type will allow you to extract values out of segments of the current macro and assign those values to the selected variable.\nFor example, you can get the current scene name from a scene condition and assign this name to a variable."
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
@@ -1072,6 +1143,7 @@ AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
AdvSceneSwitcher.action.variable.type.swapValues="Swap variable values"
AdvSceneSwitcher.action.variable.type.trim="Trim whitespace"
AdvSceneSwitcher.action.variable.type.changeCase="Change case"
AdvSceneSwitcher.action.variable.type.randomNumber="Generate random number"
AdvSceneSwitcher.action.variable.case.type.lowerCase="lowercase"
AdvSceneSwitcher.action.variable.case.type.upperCase="UPPERCASE"
AdvSceneSwitcher.action.variable.case.type.capitalized="Capitalized"
@@ -1086,19 +1158,23 @@ AdvSceneSwitcher.action.variable.findAndReplace.replace="Text to replace with"
AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip="Using \1, \2, etc. to represent first, second, etc. capture groups is supported."
AdvSceneSwitcher.action.variable.subString.begin="Beginning"
AdvSceneSwitcher.action.variable.subString.all="All"
AdvSceneSwitcher.action.variable.generateInteger="Only generate integers"
AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}"
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Use custom prompt{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Fill with placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.layout.randomNumber="Generate random number in range from{{randomNumberStart}}to{{randomNumberEnd}}"
AdvSceneSwitcher.action.projector="Projector"
AdvSceneSwitcher.action.projector.action.open="Open"
AdvSceneSwitcher.action.projector.action.close="Close"
AdvSceneSwitcher.action.projector.type.source="Source"
AdvSceneSwitcher.action.projector.type.scene="Scene"
AdvSceneSwitcher.action.projector.type.preview="Preview"
@@ -1107,8 +1183,9 @@ AdvSceneSwitcher.action.projector.type.multiview="Multiview"
AdvSceneSwitcher.action.projector.display="Display"
AdvSceneSwitcher.action.projector.windowed="Windowed"
AdvSceneSwitcher.action.projector.fullscreen="Fullscreen"
AdvSceneSwitcher.action.projector.entry="Open{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="on{{monitors}}"
AdvSceneSwitcher.action.projector.entry.close="{{actions}}projector with name{{projectorWindowName}}{{regex}}"
AdvSceneSwitcher.action.projector.entry.open.windowed="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.open.fullscreen="{{actions}}{{windowTypes}}projector of{{types}}{{scenes}}{{sources}}on{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Send message to{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Set MIDI message selection to messages incoming on{{listenDevices}}:{{listenButton}}"
@@ -1128,10 +1205,15 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Create stream clip"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Send chat announcement"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Enable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Disable chat's emote-only mode"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Send chat message"
AdvSceneSwitcher.action.twitch.type.user.getInfo="Get user information"
AdvSceneSwitcher.action.twitch.type.reward.getInfo="Get channel points reward information"
AdvSceneSwitcher.action.twitch.reward.toggleControl="Toggle reward name / variable selection control"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}"
AdvSceneSwitcher.action.twitch.entry.default="On{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Using account{{account}}{{actions}}on{{channel}}"
AdvSceneSwitcher.action.twitch.entry.user.getInfo="Using account{{account}}{{actions}}for{{userInfoQueryType}}{{userLogin}}{{userId}}"
AdvSceneSwitcher.action.twitch.entry.reward.getInfo="Using account{{account}}{{actions}}for channel{{channel}}{{pointsReward}}{{rewardVariable}}{{toggleRewardSelection}}"
AdvSceneSwitcher.action.twitch.title.title="Enter title"
AdvSceneSwitcher.action.twitch.marker.description="Describe marker"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Add a slight delay before capturing the clip"
@@ -1141,6 +1223,8 @@ AdvSceneSwitcher.action.twitch.announcement.blue="Blue"
AdvSceneSwitcher.action.twitch.announcement.green="Green"
AdvSceneSwitcher.action.twitch.announcement.orange="Orange"
AdvSceneSwitcher.action.twitch.announcement.purple="Purple"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.id="User Id"
AdvSceneSwitcher.action.twitch.user.getInfo.queryType.login="User login"
AdvSceneSwitcher.action.clipboard="Clipboard"
AdvSceneSwitcher.action.clipboard.type.copy.text="Copy text"
AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image"
@@ -1165,6 +1249,24 @@ AdvSceneSwitcher.action.window.entry="{{actions}}{{windows}}{{regex}}"
AdvSceneSwitcher.action.log="Log"
AdvSceneSwitcher.action.log.placeholder="My log message!"
AdvSceneSwitcher.action.log.entry="Write to OBS log:{{logMessage}}"
AdvSceneSwitcher.action.mqtt="MQTT"
AdvSceneSwitcher.action.mqtt.topic="Topic:"
AdvSceneSwitcher.action.mqtt.qos="QoS:"
AdvSceneSwitcher.action.mqtt.retain="Retained message:"
AdvSceneSwitcher.action.mqtt.layout="Send message to{{connection}}"
AdvSceneSwitcher.action.obsSetting="OBS setting"
AdvSceneSwitcher.action.obsSetting.action.setFPSType="Set FPS selection type to"
AdvSceneSwitcher.action.obsSetting.action.setFPSCommonValue="Set FPS common selection to"
AdvSceneSwitcher.action.obsSetting.action.setFPSIntegerValue="Set FPS integer selection to"
AdvSceneSwitcher.action.obsSetting.action.setFPSFractionNumeratorValue="Set fractional FPS numerator selection to"
AdvSceneSwitcher.action.obsSetting.action.setFPSFractionDenominatorValue="Set fractional FPS denominator selection to"
AdvSceneSwitcher.action.obsSetting.action.setBaseCanvasX="Set base resolution X value"
AdvSceneSwitcher.action.obsSetting.action.setBaseCanvasY="Set base resolution Y value"
AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasX="Set output resolution X value"
AdvSceneSwitcher.action.obsSetting.action.setOutputCanvasY="Set output resolution Y value"
AdvSceneSwitcher.action.obsSetting.getCurrentValue="Get current value"
AdvSceneSwitcher.action.obsSettings.layout="{{actions}}{{fpsType}}{{fpsIntValue}}{{fpsStringValue}}{{canvasSizeValue}}{{getCurrentValue}}"
AdvSceneSwitcher.action.script="Script"
# Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
@@ -1173,9 +1275,10 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Toggle Start/Stop for the
AdvSceneSwitcher.hotkey.macro.pause="Pause macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Unpause macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Toggle pause of macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Move macro segment selection up"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Move macro segment selection down"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Remove selected macro segment"
AdvSceneSwitcher.hotkey.macro.new="Add new macro"
AdvSceneSwitcher.hotkey.macro.segment.up="Move macro segment selection up"
AdvSceneSwitcher.hotkey.macro.segment.down="Move macro segment selection down"
AdvSceneSwitcher.hotkey.macro.segment.remove="Remove selected macro segment"
AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?"
AdvSceneSwitcher.askForMacro="Select macro{{macroSelection}}"
@@ -1205,7 +1308,7 @@ AdvSceneSwitcher.validation.text.maxLength="Only %1 characters are allowed in th
AdvSceneSwitcher.variable.select="--select variable--"
AdvSceneSwitcher.variable.add="Add new variable"
AdvSceneSwitcher.variable.configure="Configure variable settings"
AdvSceneSwitcher.variable.invalid="Invalid varialbe selection"
AdvSceneSwitcher.variable.invalid="Invalid variable selection"
AdvSceneSwitcher.variable.name="Name:"
AdvSceneSwitcher.variable.value="Current value:"
AdvSceneSwitcher.variable.save="Save / load behavior"
@@ -1321,6 +1424,35 @@ AdvSceneSwitcher.osc.message.type.false="False"
AdvSceneSwitcher.osc.message.type.infinity="Infinitum"
AdvSceneSwitcher.osc.message.type.null="Nil"
AdvSceneSwitcher.mqttConnection.startListen="Start listening"
AdvSceneSwitcher.mqttConnection.stopListen="Stop listening"
AdvSceneSwitcher.mqttConnection.select="--select connection--"
AdvSceneSwitcher.mqttConnection.add="Add new connection"
AdvSceneSwitcher.mqttConnection.configure="Configure connection settings"
AdvSceneSwitcher.mqttConnection.invalid="Invalid connection selection"
AdvSceneSwitcher.mqttConnection.name="Name:"
AdvSceneSwitcher.mqttConnection.address="Address:"
AdvSceneSwitcher.mqttConnection.username="Username:"
AdvSceneSwitcher.mqttConnection.password="Password:"
AdvSceneSwitcher.mqttConnection.reconnect="Reconnect automatically:"
AdvSceneSwitcher.mqttConnection.reconnectDelay="Automatically reconnect after:"
AdvSceneSwitcher.mqttConnection.connectOnStart="Connect on startup:"
AdvSceneSwitcher.mqttConnection.test="Test connection"
AdvSceneSwitcher.mqttConnection.status.connected="Connected!"
AdvSceneSwitcher.mqttConnection.status.connecting="Connecting ..."
AdvSceneSwitcher.mqttConnection.status.disconnected="Could not connect!"
AdvSceneSwitcher.mqttConnection.topic="Topic"
AdvSceneSwitcher.mqttConnection.qos="QoS"
AdvSceneSwitcher.mqttConnection.topics="Topic subscriptions:"
AdvSceneSwitcher.mqttConnection.inputWarning.emptyTopic="Topic cannot be empty."
AdvSceneSwitcher.mqttConnection.inputWarning.emptyTopic.title="Input Error"
AdvSceneSwitcher.mqttConnection.inputWarning.duplicateTopic="This topic already exists."
AdvSceneSwitcher.mqttConnection.inputWarning.duplicateTopic.title="Duplicate Topic"
AdvSceneSwitcher.mqttConnection.inputWarning.qosRange="QoS must be 0, 1, or 2."
AdvSceneSwitcher.mqttConnection.inputWarning.qosRange.title="Invalid QoS"
AdvSceneSwitcher.mqttConnection.add.layout="Add topic subscription{{topic}}with QoS{{QoS}}"
AdvSceneSwitcher.mqttConnection.add.title="Add MQTT Topic Subscription"
AdvSceneSwitcher.twitchToken.name="Account name:"
AdvSceneSwitcher.twitchToken.nameNotAvailable="Account already in use"
AdvSceneSwitcher.twitchToken.select="--select Twitch connection--"
@@ -1372,6 +1504,7 @@ AdvSceneSwitcher.twitchToken.moderator.chat.settings.manage="Manage channel's ch
AdvSceneSwitcher.twitchToken.user.whispers.manage="Manage user's whispers."
AdvSceneSwitcher.twitchToken.chat.read="View live stream chat messages."
AdvSceneSwitcher.twitchToken.chat.edit="Send live stream chat messages."
AdvSceneSwitcher.twitchToken.validateTimestamps="Validate timestamps of received Twitch event messages. (Recommended)"
AdvSceneSwitcher.twitch.selection.channel.open="Open channel"
AdvSceneSwitcher.twitch.selection.channel.open.tooltip.details="Open channel in external application handling the HTTPS protocol."
@@ -1398,6 +1531,17 @@ AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noPermission="Can't sele
AdvSceneSwitcher.twitch.selection.points.reward.tooltip.noChannel="Can't select points reward without entering a channel first!"
AdvSceneSwitcher.twitch.selection.points.reward.tooltip.error="Can't select points reward because Twitch responded with an error! Check OBS logs for more details."
AdvSceneSwitcher.script.type.inline="Inline"
AdvSceneSwitcher.script.type.file="File"
AdvSceneSwitcher.script.type.layout="Script type:{{scriptType}}"
AdvSceneSwitcher.script.language.python="Python"
AdvSceneSwitcher.script.language.lua="LUA"
AdvSceneSwitcher.script.language.select="--select language--"
AdvSceneSwitcher.script.language.layout="Script language:{{language}}"
AdvSceneSwitcher.script.file.open="Open"
AdvSceneSwitcher.script.file.open.failed="Could not open script file!"
AdvSceneSwitcher.script.file.layout="Script file:{{path}}{{open}}"
AdvSceneSwitcher.tempVar.select="--select value--"
AdvSceneSwitcher.tempVar.selectionInfo.lastValues="Last values:"
@@ -1584,12 +1728,21 @@ AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived="Shoutout time"
AdvSceneSwitcher.tempVar.twitch.started_at.shoutoutReceived.description="The UTC timestamp (in RFC3339 format) of when the moderator sent the Shoutout."
AdvSceneSwitcher.tempVar.twitch.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.id.redemption="Redemption ID"
AdvSceneSwitcher.tempVar.twitch.user_id.reward="User ID redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_login.reward="User login redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_name.reward="User name redeeming the reward"
AdvSceneSwitcher.tempVar.twitch.user_input.reward="User input for reward"
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed, at the time it was redeemed."
AdvSceneSwitcher.tempVar.twitch.reward.reward="Reward Information (JSON)"
AdvSceneSwitcher.tempVar.twitch.reward.reward.description="Basic information about the reward that was redeemed in JSON form, at the time it was redeemed."
AdvSceneSwitcher.tempVar.twitch.reward.id.reward="Reward ID"
AdvSceneSwitcher.tempVar.twitch.reward.id.reward.description="The ID that uniquely identifies the reward."
AdvSceneSwitcher.tempVar.twitch.reward.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.reward.title.reward.description="The rewards title."
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward="Reward prompt"
AdvSceneSwitcher.tempVar.twitch.reward.prompt.reward.description="The prompt displayed to the viewer if user input is required."
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.reward.cost.reward.description="The rewards cost, in Channel Points."
AdvSceneSwitcher.tempVar.twitch.status.reward="Reward status"
AdvSceneSwitcher.tempVar.twitch.status.reward.description="Defaults to unfulfilled. Possible values are unknown, unfulfilled, fulfilled, and canceled."
AdvSceneSwitcher.tempVar.twitch.redeemed_at.reward="Redemption time"
@@ -1604,22 +1757,37 @@ AdvSceneSwitcher.tempVar.twitch.title.reward="Reward title"
AdvSceneSwitcher.tempVar.twitch.cost.reward="Reward cost"
AdvSceneSwitcher.tempVar.twitch.prompt.reward="Reward description"
AdvSceneSwitcher.tempVar.twitch.is_user_input_required.reward="Reward requires user input"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Reward maximum per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream"
AdvSceneSwitcher.tempVar.twitch.background_color.reward="Reward background color"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image"
AdvSceneSwitcher.tempVar.twitch.image.reward="Reward image (JSON)"
AdvSceneSwitcher.tempVar.twitch.image.reward.description="Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward="Reward image URL"
AdvSceneSwitcher.tempVar.twitch.image.url_4x.reward.description="The URL to the custom image for the reward. This field is null if the broadcaster didnt upload images."
AdvSceneSwitcher.tempVar.twitch.default_image.reward="Reward default image (JSON)"
AdvSceneSwitcher.tempVar.twitch.default_image.reward.description="Set of default images of 1x, 2x and 4x sizes for the reward."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Reward cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward="Reward default image URL"
AdvSceneSwitcher.tempVar.twitch.default_image.url_4x.reward.description="The URL to the default image for the reward."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward="Redemption count"
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.redemptions_redeemed_current_stream.reward.description="The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isnt live or max_per_stream isnt enabled."
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward="Redemptions skip request queue"
AdvSceneSwitcher.tempVar.twitch.should_redemptions_skip_request_queue.reward.description="Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.reward="Maximum redemptions per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward="Maximum redemptions per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward="Maximum redemptions per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_stream.max_per_stream.reward.description="The maximum number of redemptions allowed per live stream."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.reward="Reward maximum per user per stream (JSON)"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward="Maximum redemptions per user per stream enabled"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.is_enabled.reward.description="A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per user per live stream. Is true if the reward applies a limit."
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward="Maximum redemptions per user per stream"
AdvSceneSwitcher.tempVar.twitch.max_per_user_per_stream.max_per_user_per_stream.reward.description="The settings used to determine whether to apply a maximum to the number of redemptions allowed per user per live stream."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward="Global cooldown (JSON)"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.reward.description="Whether a cooldown is enabled and what the cooldown is in seconds."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward="Global cooldown enabled"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.is_enabled.reward.description="A Boolean value that determines whether to apply a cooldown period. Is true if a cooldown period is enabled."
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward="Global cooldown"
AdvSceneSwitcher.tempVar.twitch.global_cooldown.global_cooldown_seconds.reward.description="The cooldown period, in seconds."
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward="Reward cooldown expiration"
AdvSceneSwitcher.tempVar.twitch.cooldown_expires_at.reward.description="Timestamp of the cooldown expiration. null if the reward isnt on cooldown."
AdvSceneSwitcher.tempVar.twitch.charity_name="Charity name"
AdvSceneSwitcher.tempVar.twitch.charity_description="Charity description"
@@ -1767,6 +1935,24 @@ AdvSceneSwitcher.tempVar.twitch.is_turbo.chatReceive.description="'true' if the
AdvSceneSwitcher.tempVar.twitch.is_vip.chatReceive="Is chatter a VIP"
AdvSceneSwitcher.tempVar.twitch.is_vip.chatReceive.description="'true' if the chatter is a VIP in the channel, 'false' otherwise."
AdvSceneSwitcher.tempVar.twitch.login.chatRemove="User login"
AdvSceneSwitcher.tempVar.twitch.login.chatRemove.description="The name of the user who sent the message."
AdvSceneSwitcher.tempVar.twitch.message.chatRemove="Chat message"
AdvSceneSwitcher.tempVar.twitch.message_id.chatRemove.description="The chat message that was removed."
AdvSceneSwitcher.tempVar.twitch.message_id.chatRemove="Chat message ID"
AdvSceneSwitcher.tempVar.twitch.message_id.chatRemove.description="The ID of the message in UUID format."
AdvSceneSwitcher.tempVar.twitch.timestamp.chatRemove="Chat message timestamp"
AdvSceneSwitcher.tempVar.twitch.timestamp.chatRemove.description="The UNIX timestamp."
AdvSceneSwitcher.tempVar.twitch.login.chatClear="User login"
AdvSceneSwitcher.tempVar.twitch.login.chatClear.description="The login name of the user whose messages were removed from the chat room because they were banned or put in a timeout."
AdvSceneSwitcher.tempVar.twitch.user_id.chatClear="User ID"
AdvSceneSwitcher.tempVar.twitch.user_id.chatClear.description="Optional.\nThe User ID of the user that was banned or put in a timeout.\nEmpty in case the complete chat was cleared."
AdvSceneSwitcher.tempVar.twitch.ban_duration.chatClear="Ban duration"
AdvSceneSwitcher.tempVar.twitch.ban_duration.chatClear.description="Set to zero if the complete chat was cleared or a user was banned indefinitely.\nIs set to non-zero value, if a user was put in a timeout.\nWill contain the duration of the timeout in seconds."
AdvSceneSwitcher.tempVar.twitch.timestamp.chatClear="Timestamp"
AdvSceneSwitcher.tempVar.twitch.timestamp.chatClear.description="The UNIX timestamp."
AdvSceneSwitcher.tempVar.twitch.user_login.chatJoin="User login"
AdvSceneSwitcher.tempVar.twitch.user_login.chatJoin.description="The user login of the person who joined the chat room."
@@ -1788,6 +1974,24 @@ AdvSceneSwitcher.tempVar.twitch.to_broadcaster_user_name.raid.description="The b
AdvSceneSwitcher.tempVar.twitch.viewers.raid="Raid participants"
AdvSceneSwitcher.tempVar.twitch.viewers.raid.description="The number of viewers in the raid."
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo="User ID"
AdvSceneSwitcher.tempVar.twitch.id.user.getInfo.description="The user ID of the queried account."
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo="User login"
AdvSceneSwitcher.tempVar.twitch.login.user.getInfo.description="The user login of the queried account."
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo="User name"
AdvSceneSwitcher.tempVar.twitch.display_name.user.getInfo.description="The display name of the queried account."
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo="User type"
AdvSceneSwitcher.tempVar.twitch.type.user.getInfo.description="The type of user. Possible values are:\n admin — Twitch administrator\n global_mod\n staff — Twitch staff\n \"\" — Normal user"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo="Broadcaster type"
AdvSceneSwitcher.tempVar.twitch.broadcaster_type.user.getInfo.description="The type of broadcaster. Possible values are:\n affiliate — An affiliate broadcaster\n partner — A partner broadcaster\n \"\" — A normal broadcaster"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo="Description"
AdvSceneSwitcher.tempVar.twitch.description.user.getInfo.description="The user's description of their channel."
AdvSceneSwitcher.tempVar.twitch.profile_image_url.user.getInfo="Profile image URL"
AdvSceneSwitcher.tempVar.twitch.offline_image_url.user.getInfo="Offline image URL"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo="Account creation date"
AdvSceneSwitcher.tempVar.twitch.created_at.user.getInfo.description="The UTC date and time that the user's account was created. The timestamp is in RFC3339 format."
AdvSceneSwitcher.tempVar.audio.output_volume="Output volume"
AdvSceneSwitcher.tempVar.audio.output_volume.description="The volume the audio source is outputting."
AdvSceneSwitcher.tempVar.audio.configured_volume="Configured volume"
@@ -1877,8 +2081,11 @@ AdvSceneSwitcher.tempVar.display.height.description="The height of the display w
AdvSceneSwitcher.tempVar.streaming.keyframeInterval="Stream keyframe interval"
AdvSceneSwitcher.tempVar.streaming.keyframeInterval.description="Stream keyframe interval configured in the OBS settings."
AdvSceneSwitcher.tempVar.streaming.streamKey="Stream key"
AdvSceneSwitcher.tempVar.streaming.durationSeconds="Stream duration"
AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed since the stream was started.\nThis value will be zero if the stream is stopped."
AdvSceneSwitcher.tempVar.streaming.serviceName="Service"
AdvSceneSwitcher.tempVar.streaming.serviceName.description="The name of the streaming service being streamed to (e.g. Twitch)"
AdvSceneSwitcher.tempVar.clipboard.text="Clipboard text"
AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the clipboard.\nWill be empty if the clipboard does not contain text."
@@ -1958,6 +2165,30 @@ AdvSceneSwitcher.tempVar.streamDeck.column="Key column"
AdvSceneSwitcher.tempVar.streamDeck.data="Data"
AdvSceneSwitcher.tempVar.streamDeck.data.description="The value of the \"Data\" field configured in the action on the Stream Deck side."
AdvSceneSwitcher.tempVar.queue.size="Size"
AdvSceneSwitcher.tempVar.queue.running="Is running"
AdvSceneSwitcher.tempVar.queue.running.description="Returns \"true\" if the queue is started and \"false\" if it is stopped."
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath="Last screenshot path"
AdvSceneSwitcher.tempVar.screenshot.lastScreenshotPath.description="The filesystem path to the last screenshot taken."
AdvSceneSwitcher.tempVar.gameCapture.title="Title"
AdvSceneSwitcher.tempVar.gameCapture.title.description="Window title of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.class="Class"
AdvSceneSwitcher.tempVar.gameCapture.class.description="Window class of the application captured by the source."
AdvSceneSwitcher.tempVar.gameCapture.executable="Executable"
AdvSceneSwitcher.tempVar.gameCapture.executable.description="Executable name of the application captured by the source."
AdvSceneSwitcher.tempVar.http.status="Status code"
AdvSceneSwitcher.tempVar.http.body="Message body"
AdvSceneSwitcher.tempVar.http.error="Error"
AdvSceneSwitcher.tempVar.http.error.description="Empty when no error occurred.\nOther possible values:\n\n * Could not establish connection\n * Failed to bind IP address\n * Failed to read connection\n * Failed to write connection\n * Maximum redirect count exceeded\n * Connection handling canceled\n * SSL connection failed\n * SSL certificate loading failed\n * SSL server verification failed\n * Unsupported HTTP multipart boundary characters\n * Compression failed\n * Connection timed out\n * Proxy connection failed\n * Unknown"
AdvSceneSwitcher.tempVar.mqtt.message="Message"
AdvSceneSwitcher.tempVar.cursor.x="Cursor position (X)"
AdvSceneSwitcher.tempVar.cursor.y="Cursor position (Y)"
AdvSceneSwitcher.selectScene="--select scene--"
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
AdvSceneSwitcher.selectCurrentScene="Current Scene"
@@ -1996,7 +2227,6 @@ AdvSceneSwitcher.settings.suffix.type.list=" (List)"
AdvSceneSwitcher.settings.suffix.type.color=" (Color)"
AdvSceneSwitcher.settings.suffix.type.button=" (Button)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)"
AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
@@ -2078,13 +2308,12 @@ AdvSceneSwitcher.script.timeout="Script timeout:{{timeout}}"
# This secion is copied from the OBS locale files
# commonly shared locale
# OBS commonly shared locale
Browse="Browse"
Show="Show"
Hide="Hide"
# properties window
# OBS properties window
Basic.PropertiesWindow="Properties for '%1'"
Basic.PropertiesWindow.AutoSelectFormat="%1 (autoselect: %2)"
Basic.PropertiesWindow.SelectColor="Select color"
@@ -2101,7 +2330,7 @@ Basic.PropertiesWindow.AddEditableListFiles="Add files to '%1'"
Basic.PropertiesWindow.AddEditableListEntry="Add entry to '%1'"
Basic.PropertiesWindow.EditEditableListEntry="Edit entry from '%1'"
# properties view
# OBS properties view
Basic.PropertiesView.FPS.Simple="Simple FPS Values"
Basic.PropertiesView.FPS.Rational="Rational FPS Values"
Basic.PropertiesView.FPS.ValidFPSRanges="Valid FPS Ranges:"
@@ -2109,6 +2338,11 @@ Basic.PropertiesView.UrlButton.Text="Open this link in your default web browser?
Basic.PropertiesView.UrlButton.Text.Url="URL: %1"
Basic.PropertiesView.UrlButton.OpenUrl="Open URL"
# OBS settings
Basic.Settings.Video.FPSCommon="Common FPS Values"
Basic.Settings.Video.FPSInteger="Integer FPS Value"
Basic.Settings.Video.FPSFraction="Fractional FPS Value"
# Legacy tabs below - please don't waste your time adding translations for these :)
# Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"

View File

@@ -25,7 +25,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="No cambiar"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Cambiar a una escena aleatoria"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Cambiar a:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="¡Durante este tiempo, se ignorarán las posibles coincidencias!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Habilitar el registro detallado"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Guardar la posición y el tamaño de la ventana"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificaciones del sistema"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deshabilitar sugerencias de interfaz de usuario"
@@ -39,7 +38,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exportar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportar los ajustes para Advanced Scene Switcher ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importar los ajustes para Advanced Scene Switcher ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Archivos de texto (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Archivos de texto (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="La importacion la configuracion de escenas fallo"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Configuración de escenas importada correctamente"
AdvSceneSwitcher.generalTab.priority.fileContent="Contenido del archivo"
@@ -155,12 +154,12 @@ AdvSceneSwitcher.condition.video.patternMatchSuccess="El patrón está resaltado
AdvSceneSwitcher.condition.video.objectMatchFail="¡No se encontró el objeto!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="El objeto está resaltado en rojo"
AdvSceneSwitcher.condition.video.modelLoadFail="¡No se pudieron cargar los datos del modelo!"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Realizar comprobación solo en el área"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Realizar comprobación solo en el área {{checkArea}} {{selectArea}}"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Realizar comprobación solo en el área"
AdvSceneSwitcher.condition.video.layout.checkArea="{{checkAreaEnable}}Realizar comprobación solo en el área {{checkArea}} {{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="Tamaño mínimo:"
AdvSceneSwitcher.condition.video.maxSize="Tamaño máximo:"
AdvSceneSwitcher.condition.video.selectArea="Seleccionar área"
@@ -172,7 +171,6 @@ AdvSceneSwitcher.condition.stream.state.start="Transmisión en ejecución"
AdvSceneSwitcher.condition.stream.state.stop="Transmisión detenida"
AdvSceneSwitcher.condition.stream.state.starting="Inicio de transmisión"
AdvSceneSwitcher.condition.stream.state.stopping="Detener transmisión"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Grabación"
AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
@@ -407,7 +405,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover al principio"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover al final"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -439,7 +437,6 @@ AdvSceneSwitcher.action.random="Aleatorio"
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
AdvSceneSwitcher.action.profile="Perfil"
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
@@ -643,9 +640,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Alternar inicio / detenci
AdvSceneSwitcher.hotkey.macro.pause="Pausar macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Despausar macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Alternar pausa de macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Mover selección de segmento de macro hacia arriba"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Mover selección de segmento de macro hacia abajo"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Eliminar segmento de macro seleccionado"
AdvSceneSwitcher.hotkey.macro.segment.up="Mover selección de segmento de macro hacia arriba"
AdvSceneSwitcher.hotkey.macro.segment.down="Mover selección de segmento de macro hacia abajo"
AdvSceneSwitcher.hotkey.macro.segment.remove="Eliminar segmento de macro seleccionado"
AdvSceneSwitcher.askBackup="Se detectó una nueva versión de Advanced Scene Switcher.\n¿Crear una copia de seguridad de la configuración anterior?"
AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}"

View File

@@ -26,7 +26,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Basculer v
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Basculer vers :"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Après avoir effectué des actions, sauter l'exécution d'actions pendant"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Pendant cette période, les correspondances potentielles seront ignorées !"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Activer les journaux détaillés"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Enregistrer la position et la taille de la fenêtre"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Afficher les notifications dans la zone de notification système"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Désactiver les indications de l'interface utilisateur"
@@ -44,7 +43,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exporter"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importer"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exporter les paramètres d'Advanced Scene Switcher vers un fichier ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importer les paramètres d'Advanced Scene Switcher depuis un fichier ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Fichiers texte (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Fichiers texte (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="L'importation des paramètres d'Advanced Scene Switcher a échoué"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Les paramètres d'Advanced Scene Switcher ont été importés avec succès"
AdvSceneSwitcher.generalTab.priority.fileContent="Contenu du fichier"
@@ -258,14 +257,14 @@ AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="À quel poi
AdvSceneSwitcher.condition.video.type.main="Sortie principale d'OBS"
AdvSceneSwitcher.condition.video.type.source="Source"
AdvSceneSwitcher.condition.video.type.scene="Scène"
AdvSceneSwitcher.condition.video.entry.modelPath="Données du modèle (classificateur de cascade de Haar) :{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Nombre minimum de voisins :{{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Réduire la charge CPU en effectuant la vérification uniquement toutes les{{throttleCount}}millisecondes"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Effectuer la vérification uniquement dans la zone"
AdvSceneSwitcher.condition.video.entry.orcColorPick="Vérifier la couleur du texte :{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.entry.orcTextType="Vérifier le type de texte :{{textType}}"
AdvSceneSwitcher.condition.video.entry.orcLanguage="Vérifier la langue :{{languageCode}}"
AdvSceneSwitcher.condition.video.entry.color="Vérifier la couleur :{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Données du modèle (classificateur de cascade de Haar) :{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Nombre minimum de voisins :{{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Réduire la charge CPU en effectuant la vérification uniquement toutes les{{throttleCount}}millisecondes"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Effectuer la vérification uniquement dans la zone"
AdvSceneSwitcher.condition.video.layout.ocrColorPick="Vérifier la couleur du texte :{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.ocrTextType="Vérifier le type de texte :{{textType}}"
AdvSceneSwitcher.condition.video.layout.ocrLanguage="Vérifier la langue :{{languageCode}}"
AdvSceneSwitcher.condition.video.layout.color="Vérifier la couleur :{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.minSize="Taille minimale :"
AdvSceneSwitcher.condition.video.maxSize="Taille maximale :"
AdvSceneSwitcher.condition.video.selectArea="Sélectionner la zone"
@@ -597,7 +596,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Déplacer vers le bas"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Déplacer en haut"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Déplacer en bas"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Déplacer à la position"
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Sur{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformation de l'élément de la scène"
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
AdvSceneSwitcher.action.sceneTransform.type.reset="Réinitialiser la transformation"
@@ -647,7 +646,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Par défaut"
AdvSceneSwitcher.action.screenshot.save.custom="Personnalisé"
AdvSceneSwitcher.action.screenshot.type.source="Source"
AdvSceneSwitcher.action.screenshot.type.scene="Scène"
AdvSceneSwitcher.action.screenshot.mainOutput="Sortie principale d'OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Les sources ou les scènes qui ne sont pas toujours rendues peuvent entraîner des parties de captures d'écran vides."
AdvSceneSwitcher.action.screenshot.entry="Capturer{{targetType}}{{sources}}{{scenes}}et enregistrer à l'emplacement{{saveType}}{{variables}}"
AdvSceneSwitcher.action.profile="Profil"
@@ -705,10 +703,10 @@ AdvSceneSwitcher.action.variable.invalidSelection="Sélection invalide !"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="La récupération de valeurs de variables à partir d'actions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="La récupération de valeurs de variables à partir de conditions %1 n'est pas prise en charge !"
AdvSceneSwitcher.action.variable.currentSegmentValue="Valeur actuelle :"
AdvSceneSwitcher.action.variable.entry.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Début de la sous-chaîne :{{subStringStart}}Taille de la sous-chaîne :{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Attribuer la valeur du match{{regexMatchIdx}}en utilisant une expression régulière :"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Utiliser un message personnalisé{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Remplir avec un indicateur de position{{inputPlaceholder}}"
AdvSceneSwitcher.action.projector="Projecteur"
AdvSceneSwitcher.action.projector.type.source="Source"
AdvSceneSwitcher.action.projector.type.scene="Scène"
@@ -718,8 +716,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivue"
AdvSceneSwitcher.action.projector.display="Affichage"
AdvSceneSwitcher.action.projector.windowed="Fenêtré"
AdvSceneSwitcher.action.projector.fullscreen="Plein écran"
AdvSceneSwitcher.action.projector.entry="Ouvrir le projecteur{{windowTypes}}de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="sur{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Envoyer un message à{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Définir la sélection de messages MIDI sur les messages entrants de{{listenDevices}}:{{listenButton}}"
@@ -924,9 +920,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Basculer le démarrage/ar
AdvSceneSwitcher.hotkey.macro.pause="Mettre en pause la macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Reprendre la macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Basculer la pause de la macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Déplacer la sélection du segment de macro vers le haut"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Déplacer la sélection du segment de macro vers le bas"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Supprimer le segment de macro sélectionné"
AdvSceneSwitcher.hotkey.macro.segment.up="Déplacer la sélection du segment de macro vers le haut"
AdvSceneSwitcher.hotkey.macro.segment.down="Déplacer la sélection du segment de macro vers le bas"
AdvSceneSwitcher.hotkey.macro.segment.remove="Supprimer le segment de macro sélectionné"
AdvSceneSwitcher.askBackup="Une nouvelle version du commutateur de scènes avancé a été détectée.\nSouhaitez-vous créer une sauvegarde des anciens paramètres ?"
AdvSceneSwitcher.askForMacro="Sélectionnez la macro{{macroSelection}}"

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.logLevel="Nível de log:"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default="Padrão"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction="Registrar ações executadas"
AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose="Registro detalhado"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ativar registro detalhado"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Salvar posição e tamanho da janela"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificações na área de notificação"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Desativar dicas de interface"
@@ -49,7 +48,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exportar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportar configurações do Advanced Scene Switcher para arquivo ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importar configurações do Advanced Scene Switcher de arquivo ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Arquivos de texto (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Arquivos de texto (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="O Advanced Scene Switcher falhou ao importar configurações"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Configurações do Advanced Scene Switcher importadas com sucesso"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportSensitiveDataWarning="Aviso:\nOs dados exportados podem conter informações sensíveis!"
@@ -373,16 +372,16 @@ AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription="Quão semel
AdvSceneSwitcher.condition.video.type.main="Saída principal do OBS"
AdvSceneSwitcher.condition.video.type.source="Fonte"
AdvSceneSwitcher.condition.video.type.scene="Cena"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Dados do modelo (classificador em cascata haar):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Vizinhos mínimos:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzir carga de CPU realizando a verificação apenas a cada {{throttleCount}} milissegundos"
AdvSceneSwitcher.condition.video.entry.checkAreaEnable="Realizar verificação apenas na área"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.entry.orcColorPick="Verificar cor do texto:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.entry.orcTextType="Verificar tipo de texto:{{textType}}"
AdvSceneSwitcher.condition.video.entry.orcLanguage="Verificar idioma:{{languageCode}}"
AdvSceneSwitcher.condition.video.entry.color="Verificar cor:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Dados do modelo (classificador em cascata haar):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Vizinhos mínimos:{{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}}Reduzir carga de CPU realizando a verificação apenas a cada {{throttleCount}} milissegundos"
AdvSceneSwitcher.condition.video.layout.checkAreaEnable="Realizar verificação apenas na área"
AdvSceneSwitcher.condition.video.layout.checkArea="{{checkAreaEnable}}{{checkArea}}{{selectArea}}"
AdvSceneSwitcher.condition.video.layout.ocrColorPick="Verificar cor do texto:{{textColor}}{{selectColor}}"
AdvSceneSwitcher.condition.video.layout.ocrTextType="Verificar tipo de texto:{{textType}}"
AdvSceneSwitcher.condition.video.layout.ocrLanguage="Verificar idioma:{{languageCode}}"
AdvSceneSwitcher.condition.video.layout.color="Verificar cor:{{color}}{{selectColor}}"
AdvSceneSwitcher.condition.video.minSize="Tamanho mínimo:"
AdvSceneSwitcher.condition.video.maxSize="Tamanho máximo:"
AdvSceneSwitcher.condition.video.selectArea="Selecionar área"
@@ -395,7 +394,6 @@ AdvSceneSwitcher.condition.stream.state.stop="Streaming parado"
AdvSceneSwitcher.condition.stream.state.starting="Iniciando o streaming"
AdvSceneSwitcher.condition.stream.state.stopping="Parando o streaming"
AdvSceneSwitcher.condition.stream.state.keyFrameInterval="Intervalo de keyframe igual a"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Gravação"
AdvSceneSwitcher.condition.record.state.start="Gravação em execução"
AdvSceneSwitcher.condition.record.state.pause="Gravação pausada"
@@ -907,7 +905,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover para baixo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover para o topo"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover para o fundo"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover para posição"
AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Em{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformação de item de cena"
AdvSceneSwitcher.action.sceneTransform.type.manual="Definir string de transformação"
AdvSceneSwitcher.action.sceneTransform.type.setSingleSetting="Definir configuração de transformação"
@@ -961,7 +959,6 @@ AdvSceneSwitcher.action.screenshot.save.default="Padrão"
AdvSceneSwitcher.action.screenshot.save.custom="Personalizado"
AdvSceneSwitcher.action.screenshot.type.source="Fonte"
AdvSceneSwitcher.action.screenshot.type.scene="Cena"
AdvSceneSwitcher.action.screenshot.mainOutput="Saída principal do OBS"
AdvSceneSwitcher.action.screenshot.blackscreenNote="Fontes ou cenas, que nem sempre são renderizadas, podem resultar em algumas partes das capturas de tela permanecendo em branco."
AdvSceneSwitcher.action.screenshot.entry="Captura de tela{{targetType}}{{sources}}{{scenes}}e salvar em{{saveType}}{{variables}}localização"
AdvSceneSwitcher.action.profile="Perfil"
@@ -1014,7 +1011,7 @@ AdvSceneSwitcher.action.variable.type.askForValue="Obter entrada do usuário"
AdvSceneSwitcher.action.variable.type.environmentVariable="Definir valor da variável de ambiente"
AdvSceneSwitcher.action.variable.type.sceneItemCount="Definir para contagem de itens de cena"
AdvSceneSwitcher.action.variable.type.stringLength="Definir comprimento da string"
AdvSceneSwitcher.action.variable.type.extractJson="Extrair campo json com nome"
AdvSceneSwitcher.action.variable.type.extractJsonField="Extrair campo json com nome"
AdvSceneSwitcher.action.variable.type.setToTempvar="Definir para propriedade da macro"
AdvSceneSwitcher.action.variable.type.setToTempvar.help="Este tipo de ação permite extrair valores de segmentos da macro atual e atribuir esses valores à variável selecionada.\nPor exemplo, você pode obter o nome da cena atual de uma condição de cena e atribuir esse nome a uma variável."
AdvSceneSwitcher.action.variable.type.sceneItemName="Definir para nome do item de cena no índice"
@@ -1034,13 +1031,13 @@ AdvSceneSwitcher.action.variable.invalidSelection="Seleção inválida!"
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Obter valores de variáveis das ações %1 não é suportado!"
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Obter valores de variáveis das condições %1 não é suportado!"
AdvSceneSwitcher.action.variable.currentSegmentValue="Valor atual:"
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
AdvSceneSwitcher.action.variable.entry.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
AdvSceneSwitcher.action.variable.entry.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.entry.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
AdvSceneSwitcher.action.variable.entry.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.variable.layout.pad="{{actions}}de{{variables}}para comprimento{{stringLength}}adicionando{{paddingCharSelection}}ao{{direction}}"
AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}de{{variables}}para comprimento{{stringLength}}removendo caracteres da{{direction}}"
AdvSceneSwitcher.action.variable.layout.substringIndex="Início da substring:{{subStringStart}}Tamanho da substring:{{subStringSize}}"
AdvSceneSwitcher.action.variable.layout.substringRegex="Atribuir valor do{{regexMatchIdx}}correspondência usando expressão regular:"
AdvSceneSwitcher.action.variable.layout.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
AdvSceneSwitcher.action.variable.layout.userInput.customPrompt="{{useCustomPrompt}}Usar prompt personalizado{{inputPrompt}}"
AdvSceneSwitcher.action.variable.layout.userInput.placeholder="{{useInputPlaceholder}}Preencher com placeholder{{inputPlaceholder}}"
AdvSceneSwitcher.action.projector="Projetor"
AdvSceneSwitcher.action.projector.type.source="Fonte"
AdvSceneSwitcher.action.projector.type.scene="Cena"
@@ -1050,8 +1047,6 @@ AdvSceneSwitcher.action.projector.type.multiview="Multivisão"
AdvSceneSwitcher.action.projector.display="Exibição"
AdvSceneSwitcher.action.projector.windowed="Janela"
AdvSceneSwitcher.action.projector.fullscreen="Tela cheia"
AdvSceneSwitcher.action.projector.entry="Abrir{{windowTypes}}projetor de{{types}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.projector.entry.monitor="em{{monitors}}"
AdvSceneSwitcher.action.midi="MIDI"
AdvSceneSwitcher.action.midi.entry="Enviar mensagem para{{device}}:"
AdvSceneSwitcher.action.midi.entry.listen="Definir seleção de mensagem MIDI para mensagens recebidas em{{listenDevices}}:{{listenButton}}"
@@ -1071,10 +1066,10 @@ AdvSceneSwitcher.action.twitch.type.clip.create="Criar clipe da transmissão"
AdvSceneSwitcher.action.twitch.type.chat.announcement.send="Enviar anúncio de chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.enable="Habilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.chat.emoteOnly.disable="Desabilitar modo apenas emotes no chat"
AdvSceneSwitcher.action.twitch.type.sendChatMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.type.chat.sendMessage="Enviar mensagem de chat"
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Não é possível selecionar categoria sem selecionar uma conta Twitch primeiro!"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}"
AdvSceneSwitcher.action.twitch.entry.default="Em{{account}}{{actions}}{{streamTitle}}{{category}}{{markerDescription}}{{clipHasDelay}}{{duration}}{{announcementColor}}{{channel}}{{pointsReward}}"
AdvSceneSwitcher.action.twitch.entry.chat="Usando conta{{account}}{{actions}}em{{channel}}"
AdvSceneSwitcher.action.twitch.title.title="Digite o título"
AdvSceneSwitcher.action.twitch.marker.description="Descreva o marcador"
AdvSceneSwitcher.action.twitch.clip.hasDelay="Adicionar um leve atraso antes de capturar o clipe"
@@ -1116,9 +1111,9 @@ AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Alternar Iniciar/Parar pa
AdvSceneSwitcher.hotkey.macro.pause="Pausar macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Retomar macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Alternar pausa da macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Mover seleção de segmento de macro para cima"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Mover seleção de segmento de macro para baixo"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Remover segmento de macro selecionado"
AdvSceneSwitcher.hotkey.macro.segment.up="Mover seleção de segmento de macro para cima"
AdvSceneSwitcher.hotkey.macro.segment.down="Mover seleção de segmento de macro para baixo"
AdvSceneSwitcher.hotkey.macro.segment.remove="Remover segmento de macro selecionado"
AdvSceneSwitcher.askBackup="Detectada uma nova versão do Advanced Scene Switcher.\nDeve ser criado um backup das configurações antigas?"
AdvSceneSwitcher.askForMacro="Selecionar macro{{macroSelection}}"

View File

@@ -25,7 +25,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.dontSwitch="Не пере
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Переключиться на любую сцену на вкладке Random"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Переключиться на:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="В течение этого времени потенциальные совпадения будут игнорироваться!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Включить ведение подробного журнала"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Сохранять положение и размер окна"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Отключить подсказки пользовательского интерфейса"
AdvSceneSwitcher.generalTab.priority="Приоритет"
@@ -37,7 +36,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Экспорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Импорт"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Экспортировать настройки расширенного переключателя сцен в файл ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Импортировать настройки Advanced Scene Switcher из файла ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Текстовые файлы (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Текстовые файлы (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Advanced Scene Switcher не удалось импортировать настройки"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Настройки Advanced Scene Switcher импортированы успешно"
AdvSceneSwitcher.generalTab.priority.fileContent="Содержание файла"

View File

@@ -26,7 +26,6 @@ AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchToRandom="Rastgele s
AdvSceneSwitcher.generalTab.generalBehavior.onNoMatch.switchTo="Değiştirmek:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Bir eşleşmeden sonra aşağıdakiler için işlem yapmayın:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="Bu süre zarfında olası eşleşmeler göz ardı edilecektir!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Ayrıntılı günlük kaydını etkinleştir"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Pencere konumunu ve boyutunu kaydet"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Sistem tepsisi bildirimlerini göster"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="UI ipuçlarını devre dışı bırak"
@@ -39,7 +38,7 @@ AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Dışa Aktar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="İçe Aktar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Gelişmiş Sahne Değiştirici ayarlarını dosyaya aktar ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Gelişmiş Sahne Değiştirici ayarlarını dosyadan içe aktar ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text dosyası (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Text dosyası (*.txt *.json)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="Gelişmiş Sahne Değiştirici ayarları dosyadan içe aktarmakta başarısız oldu"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Gelişmiş Sahne Değiştirici ayarları başarılı bir şekilde dosyadan içe aktarıldı"
AdvSceneSwitcher.generalTab.priority.fileContent="Dosya İçeriği"
@@ -140,16 +139,15 @@ AdvSceneSwitcher.condition.video.screenshotFail="Kaynağın ekran görüntüsü
AdvSceneSwitcher.condition.video.patternMatchFail="Desen bulunamadı!"
AdvSceneSwitcher.condition.video.objectMatchFail="Nesne bulunamadı!"
AdvSceneSwitcher.condition.video.modelLoadFail="Model verileri yüklenemedi!"
AdvSceneSwitcher.condition.video.entry="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Model verileri (haar kademeli sınıflandırıcı):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum komşular: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}} Yalnızca her seferinde kontrol gerçekleştirerek CPU yükünü azaltın {{throttleCount}} millisaniyeler"
AdvSceneSwitcher.condition.video.layout="{{videoInputTypes}}{{sources}}{{scenes}}{{condition}}{{imagePath}}"
AdvSceneSwitcher.condition.video.layout.modelPath="Model verileri (haar kademeli sınıflandırıcı):{{modelDataPath}}"
AdvSceneSwitcher.condition.video.layout.minNeighbor="Minimum komşular: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.layout.throttle="{{throttleEnable}} Yalnızca her seferinde kontrol gerçekleştirerek CPU yükünü azaltın {{throttleCount}} millisaniyeler"
AdvSceneSwitcher.condition.stream="Yayınlama"
AdvSceneSwitcher.condition.stream.state.start="Yayın çalışıyor"
AdvSceneSwitcher.condition.stream.state.stop="Yayın durdu"
AdvSceneSwitcher.condition.stream.state.starting="Yayın başlıyor"
AdvSceneSwitcher.condition.stream.state.stopping="Yayın duruyor"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}{{keyFrameInterval}}"
AdvSceneSwitcher.condition.record="Kayıt"
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
@@ -336,7 +334,7 @@ AdvSceneSwitcher.action.sceneOrder.type.moveDown="Aşağı indir"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="En üste taşı"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Aşağıya taşı"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{sources2}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}{{settingSelection}}{{singleSettingValue}}"
@@ -357,7 +355,6 @@ AdvSceneSwitcher.action.random="Rastgele"
AdvSceneSwitcher.action.random.entry="Aşağıdaki makrolardan herhangi birini rastgele çalıştırın (duraklatılmış makrolar yoksayılır)"
AdvSceneSwitcher.action.systray="Sistem tepsisi bildirimi"
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"

File diff suppressed because it is too large Load Diff

BIN
data/res/images/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

1
deps/date vendored Submodule

Submodule deps/date added at 5bdb7e6f31

13156
deps/exprtk/exprtk.hpp vendored

File diff suppressed because it is too large Load Diff

1
deps/jsoncons vendored Submodule

Submodule deps/jsoncons added at 64b9da1e9f

2
deps/libremidi vendored

1
deps/paho.mqtt.cpp vendored Submodule

Submodule deps/paho.mqtt.cpp added at 165476b1dc

View File

@@ -181,28 +181,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="logLevel">
<item>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.default</string>
</property>
</item>
<item>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logMacro</string>
</property>
</item>
<item>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.logAction</string>
</property>
</item>
<item>
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.logLevel.verbose</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="logLevel"/>
</item>
<item>
<spacer name="horizontalSpacer_49">
@@ -315,6 +294,17 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QCheckBox" name="disableMacroWidgetCache">
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.disableMacroWidgetCache</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_26">
<item>
@@ -800,7 +790,7 @@
</item>
</layout>
</widget>
<widget class="QGroupBox" name="macroEdit">
<widget class="QGroupBox" name="macroEditGroup">
<property name="title">
<string>AdvSceneSwitcher.macroTab.edit</string>
</property>
@@ -906,339 +896,7 @@
</layout>
</item>
<item>
<widget class="QSplitter" name="macroActionConditionSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="macroConditions" native="true">
<layout class="QVBoxLayout" name="macroConditionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="conditionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="conditionControlsLayout">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QToolButton" name="conditionAdd">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionAddButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-plus</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionRemove">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionRemoveButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-trash</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionTop">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionTopButton</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionUp">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionUpButton</string>
</property>
<property name="text">
<string/>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-up</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionDown">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionDownButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-down</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionBottom">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionBottomButton</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QSplitter" name="macroElseActionSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="macroActions" native="true">
<layout class="QVBoxLayout" name="macroActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="actionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="actionControlsLayout">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QToolButton" name="actionAdd">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionAddButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-plus</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionRemove">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionRemoveButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-trash</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionTop">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionTopButton</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionUp">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionUpButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-up</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionDown">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionDownButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-down</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionBottom">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionBottomButton</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="toggleElseActions">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.toggleShowElseSection</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="macroElseActions" native="true">
<layout class="QVBoxLayout" name="macroElseActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="elseActionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="elseActionControlsLayout">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QToolButton" name="elseActionAdd">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionAddButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-plus</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionRemove">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionRemoveButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-trash</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionTop">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionTopButton</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionUp">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionUpButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-up</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionDown">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-down</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionBottom">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionBottomButton</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</widget>
<widget class="advss::MacroEdit" name="macroEdit" native="true"/>
</item>
</layout>
</widget>
@@ -4450,12 +4108,6 @@
<extends>QListView</extends>
<header>macro-tree.hpp</header>
</customwidget>
<customwidget>
<class>advss::MacroSegmentList</class>
<extends>QScrollArea</extends>
<header>macro-segment-list.hpp</header>
<container>1</container>
</customwidget>
<customwidget>
<class>advss::MacroRunButton</class>
<extends>QPushButton</extends>
@@ -4466,6 +4118,12 @@
<extends>QWidget</extends>
<header>duration-control.hpp</header>
</customwidget>
<customwidget>
<class>advss::MacroEdit</class>
<extends>QWidget</extends>
<header>macro-edit.hpp</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
@@ -4502,28 +4160,7 @@
<tabstop>runMacroInParallel</tabstop>
<tabstop>runMacroOnChange</tabstop>
<tabstop>macroSettings</tabstop>
<tabstop>conditionsList</tabstop>
<tabstop>conditionAdd</tabstop>
<tabstop>conditionRemove</tabstop>
<tabstop>conditionTop</tabstop>
<tabstop>conditionUp</tabstop>
<tabstop>conditionDown</tabstop>
<tabstop>conditionBottom</tabstop>
<tabstop>actionsList</tabstop>
<tabstop>actionAdd</tabstop>
<tabstop>actionRemove</tabstop>
<tabstop>actionTop</tabstop>
<tabstop>actionUp</tabstop>
<tabstop>actionDown</tabstop>
<tabstop>actionBottom</tabstop>
<tabstop>toggleElseActions</tabstop>
<tabstop>elseActionsList</tabstop>
<tabstop>elseActionAdd</tabstop>
<tabstop>elseActionRemove</tabstop>
<tabstop>elseActionTop</tabstop>
<tabstop>elseActionUp</tabstop>
<tabstop>elseActionDown</tabstop>
<tabstop>elseActionBottom</tabstop>
<tabstop>macroEdit</tabstop>
<tabstop>sceneGroups</tabstop>
<tabstop>sceneGroupAdd</tabstop>
<tabstop>sceneGroupRemove</tabstop>

388
forms/macro-edit.ui Normal file
View File

@@ -0,0 +1,388 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MacroEdit</class>
<widget class="QWidget" name="MacroEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="macroActionConditionSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="macroConditions" native="true">
<layout class="QVBoxLayout" name="macroConditionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="conditionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="conditionControlsLayout">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QToolButton" name="conditionAdd">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionAddButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-plus</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionRemove">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionRemoveButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-trash</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionTop">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionTopButton</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionUp">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionUpButton</string>
</property>
<property name="text">
<string/>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-up</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionDown">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionDownButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-down</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="conditionBottom">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.conditionBottomButton</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QSplitter" name="macroElseActionSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="macroActions" native="true">
<layout class="QVBoxLayout" name="macroActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="actionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="actionControlsLayout">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QToolButton" name="actionAdd">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionAddButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-plus</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionRemove">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionRemoveButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-trash</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionTop">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionTopButton</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionUp">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionUpButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-up</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionDown">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionDownButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-down</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="actionBottom">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.actionBottomButton</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="toggleElseActions">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.toggleShowElseSection</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="macroElseActions" native="true">
<layout class="QVBoxLayout" name="macroElseActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="advss::MacroSegmentList" name="elseActionsList">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="elseActionControlsLayout">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QToolButton" name="elseActionAdd">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionAddButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-plus</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionRemove">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionRemoveButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-trash</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionTop">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionTopButton</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionUp">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionUpButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-up</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionDown">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionDownButton</string>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
<property name="class" stdset="0">
<string notr="true">icon-down</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="elseActionBottom">
<property name="toolTip">
<string>AdvSceneSwitcher.macroTab.tooltip.elseActionBottomButton</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>advss::MacroSegmentList</class>
<extends>QScrollArea</extends>
<header>macro-segment-list.hpp</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>conditionsList</tabstop>
<tabstop>conditionAdd</tabstop>
<tabstop>conditionRemove</tabstop>
<tabstop>conditionTop</tabstop>
<tabstop>conditionUp</tabstop>
<tabstop>conditionDown</tabstop>
<tabstop>conditionBottom</tabstop>
<tabstop>actionsList</tabstop>
<tabstop>actionAdd</tabstop>
<tabstop>actionRemove</tabstop>
<tabstop>actionTop</tabstop>
<tabstop>actionUp</tabstop>
<tabstop>actionDown</tabstop>
<tabstop>actionBottom</tabstop>
<tabstop>toggleElseActions</tabstop>
<tabstop>elseActionsList</tabstop>
<tabstop>elseActionAdd</tabstop>
<tabstop>elseActionRemove</tabstop>
<tabstop>elseActionTop</tabstop>
<tabstop>elseActionUp</tabstop>
<tabstop>elseActionDown</tabstop>
<tabstop>elseActionBottom</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -1,6 +1,5 @@
#include "advanced-scene-switcher.hpp"
#include "backup.hpp"
#include "curl-helper.hpp"
#include "log-helper.hpp"
#include "macro-helpers.hpp"
#include "obs-module-helper.hpp"
@@ -19,10 +18,15 @@
#include <obs-frontend-api.h>
#include <QAction>
#include <QDirIterator>
#include <QLibrary>
#include <QMainWindow>
#include <QTextStream>
#include <regex>
#ifdef _WIN32
#include <Windows.h>
#endif
namespace advss {
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
@@ -142,16 +146,7 @@ void AdvSceneSwitcher::LoadUI()
bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
{
auto eventType = event->type();
if (obj == ui->macroElseActions && eventType == QEvent::Resize) {
QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
if (resizeEvent->size().height() == 0) {
SetElseActionsStateToHidden();
return QDialog::eventFilter(obj, event);
}
SetElseActionsStateToVisible();
} else if (eventType == QEvent::KeyPress) {
if (eventType == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
auto pressedKey = keyEvent->key();
@@ -307,7 +302,7 @@ void SwitcherData::Thread()
}
}
ResetForNextInterval();
RunIntervalResetSteps();
if (match) {
if (macroMatch) {
@@ -357,14 +352,6 @@ void SwitcherData::SetPreconditions()
InvalidateMacroTempVarValues();
}
void SwitcherData::ResetForNextInterval()
{
// Plugin reset functions
for (const auto &func : resetIntervalSteps) {
func();
}
}
bool SwitcherData::CheckForMatch(OBSWeakSource &scene,
OBSWeakSource &transition, int &linger,
bool &setPrevSceneAfterLinger,
@@ -445,7 +432,7 @@ void AutoStartActionQueues();
void SwitcherData::Start()
{
if (!(th && th->isRunning())) {
ResetForNextInterval();
RunIntervalResetSteps();
ResetMacros();
AutoStartActionQueues();
@@ -453,10 +440,7 @@ void SwitcherData::Start()
th = new SwitcherThread();
th->start((QThread::Priority)threadPriority);
// Will be overwritten quickly but might be useful
writeToStatusFile("Advanced Scene Switcher running");
SendWebsocketVendorEvent("AdvancedSceneSwitcherStarted",
nullptr);
RunStartSteps();
}
if (showSystemTrayNotifications) {
@@ -483,11 +467,7 @@ void SwitcherData::Stop()
th->wait();
delete th;
th = nullptr;
writeToStatusFile("Advanced Scene Switcher stopped");
if (!obsIsShuttingDown) {
SendWebsocketVendorEvent("AdvancedSceneSwitcherStopped",
nullptr);
}
RunStopSteps();
}
if (showSystemTrayNotifications) {
@@ -619,7 +599,7 @@ static void handleSceneCollectionChanging()
AdvSceneSwitcher::window->close();
}
if (!switcher->stop) {
switcher->sceneColletionStop = true;
switcher->sceneCollectionStop = true;
switcher->Stop();
}
}
@@ -735,7 +715,7 @@ void OpenSettingsWindow()
}
}
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
void AdvSceneSwitcher::HighlightMacroSettingsButton(bool enable)
{
static QObject *highlight = nullptr;
if ((highlight && enable) || (!highlight && !enable)) {
@@ -755,13 +735,28 @@ void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
highlight = HighlightWidget(ui->macroSettings, Qt::green);
}
void HighligthMacroSettingsButton(bool enable)
void AdvSceneSwitcher::HighlightAction(int idx, QColor color) const
{
ui->macroEdit->HighlightAction(idx, color);
}
void AdvSceneSwitcher::HighlightElseAction(int idx, QColor color) const
{
ui->macroEdit->HighlightElseAction(idx, color);
}
void AdvSceneSwitcher::HighlightCondition(int idx, QColor color) const
{
ui->macroEdit->HighlightCondition(idx, color);
}
void HighlightMacroSettingsButton(bool enable)
{
auto window = GetSettingsWindow();
if (!window) {
return;
}
static_cast<AdvSceneSwitcher *>(window)->HighligthMacroSettingsButton(
static_cast<AdvSceneSwitcher *>(window)->HighlightMacroSettingsButton(
enable);
}

View File

@@ -11,6 +11,7 @@ namespace advss {
class MacroActionEdit;
class MacroConditionEdit;
class MacroSegment;
class Duration;
class SequenceWidget;
struct SceneGroup;
@@ -66,6 +67,7 @@ public slots:
void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
void on_disableComboBoxFilter_stateChanged(int state);
void on_disableMacroWidgetCache_stateChanged(int state);
void on_warnPluginLoadFailure_stateChanged(int state);
void on_hideLegacyTabs_stateChanged(int state);
void on_priorityUp_clicked();
@@ -77,119 +79,53 @@ public slots:
/* --- Begin of macro tab section --- */
public:
void SetupMacroTab();
bool MacroTabIsInFocus();
bool AddNewMacro(std::shared_ptr<Macro> &res, std::string &name,
std::string format = "");
void RemoveMacro(std::shared_ptr<Macro> &);
void RemoveSelectedMacros();
void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
std::shared_ptr<Macro> GetSelectedMacro();
std::vector<std::shared_ptr<Macro>> GetSelectedMacros();
void SetEditMacro(Macro &m);
void SetMacroEditAreaDisabled(bool);
void HighlightAction(int idx, QColor color = QColor(Qt::green));
void HighlightElseAction(int idx, QColor color = QColor(Qt::green));
void HighlightCondition(int idx, QColor color = QColor(Qt::green));
std::shared_ptr<Macro> GetSelectedMacro() const;
std::vector<std::shared_ptr<Macro>> GetSelectedMacros() const;
void SetMacroEditAreaDisabled(bool) const;
void HighlightAction(int idx, QColor color = QColor(Qt::green)) const;
void HighlightElseAction(int idx,
QColor color = QColor(Qt::green)) const;
void HighlightCondition(int idx,
QColor color = QColor(Qt::green)) const;
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void SetActionData(Macro &m);
void SetElseActionData(Macro &m);
void SetConditionData(Macro &m);
void SetActionData(Macro &m) const;
void SetElseActionData(Macro &m) const;
void SetConditionData(Macro &m) const;
void SwapActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2);
void HighligthMacroSettingsButton(bool enable = true);
void HighlightMacroSettingsButton(bool enable = true);
public slots:
void on_macroAdd_clicked();
void on_macroRemove_clicked();
void on_macroUp_clicked();
void on_macroDown_clicked();
void on_macroUp_clicked() const;
void on_macroDown_clicked() const;
void on_macroName_editingFinished();
void on_runMacroInParallel_stateChanged(int value);
void on_runMacroOnChange_stateChanged(int value);
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_conditionTop_clicked();
void on_conditionUp_clicked();
void on_conditionDown_clicked();
void on_conditionBottom_clicked();
void on_actionAdd_clicked();
void on_actionRemove_clicked();
void on_actionTop_clicked();
void on_actionUp_clicked();
void on_actionDown_clicked();
void on_actionBottom_clicked();
void on_toggleElseActions_clicked();
void on_elseActionAdd_clicked();
void on_elseActionRemove_clicked();
void on_elseActionTop_clicked();
void on_elseActionUp_clicked();
void on_elseActionDown_clicked();
void on_elseActionBottom_clicked();
void MacroSelectionAboutToChange();
void on_runMacroInParallel_stateChanged(int value) const;
void on_runMacroOnChange_stateChanged(int value) const;
void MacroSelectionChanged();
void UpMacroSegementHotkey();
void DownMacroSegementHotkey();
void DeleteMacroSegementHotkey();
void ShowMacroContextMenu(const QPoint &);
void ShowMacroActionsContextMenu(const QPoint &);
void ShowMacroElseActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &);
void CopyMacro();
void RenameSelectedMacro();
void ExportMacros();
void ExportMacros() const;
void ImportMacros();
void ExpandAllActions();
void ExpandAllElseActions();
void ExpandAllConditions();
void CollapseAllActions();
void CollapseAllElseActions();
void CollapseAllConditions();
void MinimizeActions();
void MaximizeActions();
void MinimizeElseActions();
void MaximizeElseActions();
void MinimizeConditions();
void MaximizeConditions();
void SetElseActionsStateToHidden();
void SetElseActionsStateToVisible();
void MacroActionSelectionChanged(int idx);
void MacroActionReorder(int to, int target);
void AddMacroAction(Macro *macro, int idx, const std::string &id,
obs_data_t *data);
void AddMacroAction(int idx);
void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx);
void MacroElseActionSelectionChanged(int idx);
void MacroElseActionReorder(int to, int target);
void AddMacroElseAction(Macro *macro, int idx, const std::string &id,
obs_data_t *data);
void AddMacroElseAction(int idx);
void RemoveMacroElseAction(int idx);
void SwapElseActions(Macro *m, int pos1, int pos2);
void MoveMacroElseActionUp(int idx);
void MoveMacroElseActionDown(int idx);
void MacroConditionSelectionChanged(int idx);
void MacroConditionReorder(int to, int target);
void AddMacroCondition(int idx);
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
obs_data_t *data, Logic::Type logic);
void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx);
void HighlightControls();
void HighlightOnChange();
void HighlightOnChange() const;
void on_macroSettings_clicked();
void CopyMacroSegment();
void PasteMacroSegment();
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString &newName);
void MacroSegmentOrderChanged();
void SegmentTempVarsChanged();
void SegmentTempVarsChanged(MacroSegment *);
void HighlightMacrosChanged(bool value);
void ConnectionAdded(const QString &);
@@ -197,16 +133,7 @@ signals:
void ConnectionRemoved(const QString &);
private:
enum class MacroSection { CONDITIONS, ACTIONS, ELSE_ACTIONS };
void SetupMacroSegmentSelection(MacroSection type, int idx);
bool ResolveMacroImportNameConflict(std::shared_ptr<Macro> &);
bool MacroTabIsInFocus();
MacroSection lastInteracted = MacroSection::CONDITIONS;
int currentConditionIdx = -1;
int currentActionIdx = -1;
int currentElseActionIdx = -1;
/* --- End of macro tab section --- */
@@ -386,6 +313,6 @@ private:
};
void OpenSettingsWindow();
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
} // namespace advss

View File

@@ -113,12 +113,12 @@ void AdvSceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
static_cast<SwitcherData::StartupBehavior>(index);
}
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int value)
void AdvSceneSwitcher::on_logLevel_currentIndexChanged(int idx)
{
if (loading) {
return;
}
switcher->logLevel = static_cast<SwitcherData::LogLevel>(value);
SetLogLevel(static_cast<LogLevel>(ui->logLevel->itemData(idx).toInt()));
}
void AdvSceneSwitcher::on_autoStartEvent_currentIndexChanged(int index)
@@ -163,7 +163,7 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *)
switcher->windowSize = this->size();
switcher->macroListMacroEditSplitterPosition =
ui->macroListMacroEditSplitter->sizes();
MacroSelectionAboutToChange(); // Trigger saving of splitter states
ui->macroEdit->SetMacro(nullptr); // Trigger saving of splitter states
obs_frontend_save();
}
@@ -205,6 +205,16 @@ void AdvSceneSwitcher::on_disableComboBoxFilter_stateChanged(int state)
FilterComboBox::SetFilterBehaviourEnabled(!state);
}
void AdvSceneSwitcher::on_disableMacroWidgetCache_stateChanged(int state)
{
if (loading) {
return;
}
switcher->disableMacroWidgetCache = state;
MacroSegmentList::SetCachingEnabled(!state);
}
void AdvSceneSwitcher::on_warnPluginLoadFailure_stateChanged(int state)
{
if (loading) {
@@ -275,12 +285,15 @@ static bool containsSensitiveData(obs_data_t *data)
obs_data_get_array(data, "twitchConnections");
OBSDataArrayAutoRelease websocketConnections =
obs_data_get_array(data, "websocketConnections");
OBSDataArrayAutoRelease mqttConnections =
obs_data_get_array(data, "mqttConnections");
auto isNotEmpty = [](obs_data_array *array) {
return obs_data_array_count(array) > 0;
};
return isNotEmpty(twitchTokens) || isNotEmpty(websocketConnections);
return isNotEmpty(twitchTokens) || isNotEmpty(websocketConnections) ||
isNotEmpty(mqttConnections);
}
void AdvSceneSwitcher::on_exportSettings_clicked()
@@ -344,6 +357,10 @@ void AdvSceneSwitcher::on_importSettings_clicked()
return;
}
// We have to make sure to that no macro is currently being edited while
// the new settings are loaded
ui->macros->clearSelection();
std::lock_guard<std::mutex> lock(switcher->m);
switcher->LoadSettings(obj);
switcher->lastImportPath = path.toStdString();
@@ -379,6 +396,7 @@ void AdvSceneSwitcher::CheckFirstTimeSetup()
switcher->firstBoot = false;
DisplayMessage(
obs_module_text("AdvSceneSwitcher.firstBootMessage"));
switcher->Start();
}
}
@@ -517,21 +535,23 @@ void SwitcherData::SaveGeneralSettings(obs_data_t *obj)
cooldown.Save(obj, "cooldown");
obs_data_set_bool(obj, "enableCooldown", enableCooldown);
obs_data_set_bool(obj, "active", sceneColletionStop ? true : !stop);
sceneColletionStop = false;
obs_data_set_bool(obj, "active", sceneCollectionStop ? true : !stop);
sceneCollectionStop = false;
obs_data_set_int(obj, "startup_behavior",
static_cast<int>(startupBehavior));
obs_data_set_int(obj, "autoStartEvent",
static_cast<int>(autoStartEvent));
obs_data_set_int(obj, "logLevel", static_cast<int>(logLevel));
obs_data_set_int(obj, "logLevelVersion", 1);
SaveLogLevel(obj);
obs_data_set_bool(obj, "showSystemTrayNotifications",
showSystemTrayNotifications);
obs_data_set_bool(obj, "disableHints", disableHints);
obs_data_set_bool(obj, "disableFilterComboboxFilter",
disableFilterComboboxFilter);
obs_data_set_bool(obj, "disableMacroWidgetCache",
disableMacroWidgetCache);
obs_data_set_bool(obj, "warnPluginLoadFailure", warnPluginLoadFailure);
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
@@ -582,31 +602,15 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
autoStartEvent =
static_cast<AutoStart>(obs_data_get_int(obj, "autoStartEvent"));
logLevel = static_cast<LogLevel>(obs_data_get_int(obj, "logLevel"));
if (obs_data_get_int(obj, "logLevelVersion") < 1) {
enum OldLogLevel { DEFAULT, LOG_ACTION, VERBOSE };
OldLogLevel oldLogLevel = static_cast<OldLogLevel>(
obs_data_get_int(obj, "logLevel"));
switch (oldLogLevel) {
case DEFAULT:
logLevel = LogLevel::DEFAULT;
break;
case LOG_ACTION:
logLevel = LogLevel::LOG_ACTION;
break;
case VERBOSE:
logLevel = LogLevel::VERBOSE;
break;
default:
break;
}
}
LoadLogLevel(obj);
showSystemTrayNotifications =
obs_data_get_bool(obj, "showSystemTrayNotifications");
disableHints = obs_data_get_bool(obj, "disableHints");
disableFilterComboboxFilter =
obs_data_get_bool(obj, "disableFilterComboboxFilter");
disableMacroWidgetCache =
obs_data_get_bool(obj, "disableMacroWidgetCache");
obs_data_set_default_bool(obj, "warnPluginLoadFailure", true);
warnPluginLoadFailure = obs_data_get_bool(obj, "warnPluginLoadFailure");
obs_data_set_default_bool(obj, "hideLegacyTabs", true);
@@ -890,7 +894,9 @@ void AdvSceneSwitcher::SetupGeneralTab()
SIGNAL(DurationChanged(const Duration &)), this,
SLOT(CooldownDurationChanged(const Duration &)));
ui->logLevel->setCurrentIndex(static_cast<int>(switcher->logLevel));
PopulateLogLevelSelection(ui->logLevel);
ui->logLevel->setCurrentIndex(
ui->logLevel->findData(static_cast<int>(GetLogLevel())));
ui->saveWindowGeo->setChecked(switcher->saveWindowGeo);
ui->showTrayNotifications->setChecked(
@@ -900,6 +906,9 @@ void AdvSceneSwitcher::SetupGeneralTab()
switcher->disableFilterComboboxFilter);
FilterComboBox::SetFilterBehaviourEnabled(
!switcher->disableFilterComboboxFilter);
ui->disableMacroWidgetCache->setChecked(
switcher->disableMacroWidgetCache);
MacroSegmentList::SetCachingEnabled(!switcher->disableMacroWidgetCache);
ui->warnPluginLoadFailure->setChecked(switcher->warnPluginLoadFailure);
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);

View File

@@ -259,7 +259,8 @@ void AudioSwitch::setVolumeLevel(void *data, const float *,
}
}
obs_volmeter_t *AddVolmeterToSource(AudioSwitch *entry, obs_weak_source *source)
static obs_volmeter_t *addVolmeterToSource(AudioSwitch *entry,
obs_weak_source *source)
{
obs_volmeter_t *volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, AudioSwitch::setVolumeLevel, entry);
@@ -279,7 +280,7 @@ void AudioSwitch::resetVolmeter()
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
volmeter = AddVolmeterToSource(this, audioSource);
volmeter = addVolmeterToSource(this, audioSource);
}
bool AudioSwitch::initialized()
@@ -318,7 +319,7 @@ void AudioSwitch::load(obs_data_t *obj)
duration.Load(obj, "duration");
ignoreInactiveSource = obs_data_get_bool(obj, "ignoreInactiveSource");
volmeter = AddVolmeterToSource(this, audioSource);
volmeter = addVolmeterToSource(this, audioSource);
}
void AudioSwitchFallback::save(obs_data_t *obj)
@@ -349,7 +350,7 @@ AudioSwitch::AudioSwitch(const AudioSwitch &other)
condition(other.condition),
duration(other.duration)
{
volmeter = AddVolmeterToSource(this, other.audioSource);
volmeter = addVolmeterToSource(this, other.audioSource);
}
AudioSwitch::AudioSwitch(AudioSwitch &&other) noexcept

View File

@@ -21,6 +21,29 @@ bool FileSwitch::pause = false;
static QObject *addPulse = nullptr;
static std::hash<std::string> strHash;
static void writeToStatusFile(const QString &msg)
{
if (!GetSwitcher() || !GetSwitcher()->fileIO.writeEnabled ||
GetSwitcher()->fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(GetSwitcher()->fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
stream << msg << Qt::endl;
}
file.close();
}
static bool _ = []() {
AddStartStep(
[]() { writeToStatusFile("Advanced Scene Switcher running"); });
AddStopStep(
[]() { writeToStatusFile("Advanced Scene Switcher stopped"); });
return true;
}();
void AdvSceneSwitcher::on_browseButton_clicked()
{
QString path = QFileDialog::getOpenFileName(
@@ -113,24 +136,6 @@ void SwitcherData::writeSceneInfoToFile()
}
}
void SwitcherData::writeToStatusFile(const QString &msg)
{
if (!fileIO.writeEnabled || fileIO.writePath.empty()) {
return;
}
QFile file(QString::fromStdString(fileIO.writePath));
if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream << msg << "\n";
#else
stream << msg << Qt::endl;
#endif
}
file.close();
}
bool SwitcherData::checkSwitchInfoFromFile(OBSWeakSource &scene,
OBSWeakSource &transition)
{

View File

@@ -32,6 +32,7 @@
#endif
#include <fstream>
#include <sstream>
#include "kwin-helpers.h"
namespace advss {
@@ -44,6 +45,10 @@ static XScreenSaverAllocInfoFunc allocSSFunc = nullptr;
static XScreenSaverQueryInfoFunc querySSFunc = nullptr;
bool canGetIdleTime = false;
static bool KWin = false;
static FocusNotifier notifier;
static QString KWinScriptObjectPath;
static QLibrary *libprocps = nullptr;
#ifdef PROCPS_AVAILABLE
typedef PROCTAB *(*openproc_func)(int flags);
@@ -275,6 +280,11 @@ int getActiveWindow(Window *&window)
void GetCurrentWindowTitle(std::string &title)
{
if (KWin) {
title = FocusNotifier::getActiveWindowTitle();
return;
}
Window *data = 0;
if (getActiveWindow(data) != Success || !data) {
return;
@@ -286,6 +296,7 @@ void GetCurrentWindowTitle(std::string &title)
auto name = getWindowName(data[0]);
XFree(data);
if (name.empty()) {
return;
}
@@ -384,7 +395,11 @@ static void getProcessListProcps2(QStringList &processes)
return;
}
while ((stack = procps_pids_get_(info, PIDS_FETCH_TASKS_ONLY))) {
#ifdef PROCPS2_USE_INFO
auto cmd = PIDS_VAL(0, str, stack, info);
#else
auto cmd = PIDS_VAL(0, str, stack);
#endif
QString procName(cmd);
if (!procName.isEmpty() && !processes.contains(procName)) {
processes << procName;
@@ -408,6 +423,10 @@ void GetProcessList(QStringList &processes)
long getForegroundProcessPid()
{
if (KWin) {
return FocusNotifier::getActiveWindowPID();
}
Window *window;
if (getActiveWindow(window) != Success || !window || !*window) {
return -1;
@@ -433,6 +452,7 @@ long getForegroundProcessPid()
pid = *((long *)prop);
XFree(prop);
return pid;
}
@@ -546,6 +566,11 @@ static void initProc2()
#endif
}
int ignoreXerror(Display *d, XErrorEvent *e)
{
return 0;
}
void PlatformInit()
{
auto display = disp();
@@ -553,9 +578,21 @@ void PlatformInit()
return;
}
KWin = isKWinAvailable();
if (!(KWin && startKWinScript(KWinScriptObjectPath) &&
registerKWinDBusListener(&notifier))) {
// something bad happened while trying to initialize
// the KWin script/dbus so disable it
KWin = false;
blog(LOG_INFO, "not using KWin compat");
} else {
blog(LOG_INFO, "using KWin compat");
}
initXss();
initProcps();
initProc2();
XSetErrorHandler(ignoreXerror);
}
static void cleanupHelper(QLibrary *lib)
@@ -572,6 +609,9 @@ void PlatformCleanup()
cleanupHelper(libprocps);
cleanupHelper(libproc2);
cleanupDisplay();
XSetErrorHandler(NULL);
if (KWin && !KWinScriptObjectPath.isEmpty())
stopKWinScript(KWinScriptObjectPath);
}
} // namespace advss

166
lib/linux/kwin-helpers.cpp Normal file
View File

@@ -0,0 +1,166 @@
#include "kwin-helpers.h"
#include "log-helper.hpp"
#include <QDir>
#include <QFile>
#include <QFileDevice>
#include <QTextStream>
#include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusReply>
namespace advss {
int FocusNotifier::activePID = -1;
std::string FocusNotifier::activeTitle = {};
int FocusNotifier::getActiveWindowPID()
{
return activePID;
}
std::string FocusNotifier::getActiveWindowTitle()
{
return activeTitle;
}
void FocusNotifier::focusChanged(const int pid)
{
activePID = pid;
}
void FocusNotifier::focusTitle(const QString &title)
{
activeTitle = title.toStdString();
}
bool isKWinAvailable()
{
const QDBusConnectionInterface *interface =
QDBusConnection::sessionBus().interface();
if (!interface)
return false;
const QStringList services =
interface->registeredServiceNames().value();
return services.contains("org.kde.KWin");
}
bool startKWinScript(QString &scriptObjectPath)
{
const QString scriptPath =
"/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js";
const QString script =
R"(workspace.windowActivated.connect(function(client) {
if (!client) return;
if (!client.pid) return;
if (!client.caption) return;
callDBus(
"com.github.AdvancedSceneSwitcher",
"/com/github/AdvancedSceneSwitcher",
"com.github.AdvancedSceneSwitcher",
"focusChanged",
client.pid
);
callDBus(
"com.github.AdvancedSceneSwitcher",
"/com/github/AdvancedSceneSwitcher",
"com.github.AdvancedSceneSwitcher",
"focusTitle",
client.caption
);
}))";
if (const QDir dir; !dir.mkpath(QFileInfo(scriptPath).absolutePath())) {
blog(LOG_ERROR, "error creating /tmp/AdvancedSceneSwitcher");
return false;
}
QFile scriptFile(scriptPath);
if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
blog(LOG_ERROR,
"error opening KWinFocusNotifier.js for writing");
return false;
}
scriptFile.setPermissions(QFileDevice::ReadOwner |
QFileDevice::WriteOwner);
QTextStream outputStream(&scriptFile);
outputStream << script;
scriptFile.close();
const QDBusConnection bus = QDBusConnection::sessionBus();
QDBusInterface scriptingIface("org.kde.KWin", "/Scripting",
"org.kde.kwin.Scripting", bus);
if (!scriptingIface.isValid()) {
return false;
}
const QDBusReply<bool> scriptRunningReply =
scriptingIface.call("isScriptLoaded", scriptPath);
if (scriptRunningReply.isValid() && scriptRunningReply.value()) {
// script already registered and running, don't do it again
// this will leave the script running since we do not have
// a valid script id anymore, but at the very least this prevents
// it from running multiple times
return true;
}
const QDBusReply<int> scriptIdReply =
scriptingIface.call("loadScript", scriptPath);
if (!scriptIdReply.isValid()) {
return false;
}
const int scriptId = scriptIdReply.value();
scriptObjectPath =
QString("/Scripting/Script%1").arg(QString::number(scriptId));
QDBusInterface scriptRunner("org.kde.KWin", scriptObjectPath,
"org.kde.kwin.Script", bus);
if (!scriptRunner.isValid()) {
return false;
}
const QDBusReply<void> runReply = scriptRunner.call("run");
return runReply.isValid();
}
bool stopKWinScript(const QString &scriptObjectPath)
{
QDBusInterface scriptRunner("org.kde.KWin", scriptObjectPath,
"org.kde.kwin.Script",
QDBusConnection::sessionBus());
if (!scriptRunner.isValid()) {
return false;
}
const QDBusReply<void> stopReply = scriptRunner.call("stop");
return stopReply.isValid();
}
bool registerKWinDBusListener(FocusNotifier *notifier)
{
static const QString serviceName = "com.github.AdvancedSceneSwitcher";
static const QString objectPath = "/com/github/AdvancedSceneSwitcher";
auto bus = QDBusConnection::sessionBus();
if (bus.objectRegisteredAt(objectPath)) {
// already registered?
return true;
}
if (!bus.registerService(serviceName)) {
return false;
}
if (!bus.registerObject(objectPath, notifier,
QDBusConnection::ExportAllSlots)) {
return false;
}
return true;
}
} // namespace advss

33
lib/linux/kwin-helpers.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <QObject>
#include <QString>
#include <string>
namespace advss {
class FocusNotifier final : public QObject {
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher")
static int activePID;
static std::string activeTitle;
public:
using QObject::QObject;
static int getActiveWindowPID();
static std::string getActiveWindowTitle();
public slots:
void focusChanged(const int pid);
void focusTitle(const QString &title);
};
bool isKWinAvailable();
bool startKWinScript(QString &scriptObjectPath);
bool stopKWinScript(const QString &scriptObjectPath);
bool registerKWinDBusListener(FocusNotifier *notifier);
void printDBusError();
} // namespace advss

View File

@@ -7,8 +7,6 @@
#include "section.hpp"
#include "switch-button.hpp"
#include <QGraphicsOpacityEffect>
namespace advss {
static inline void populateActionSelection(QComboBox *list)
@@ -17,6 +15,9 @@ static inline void populateActionSelection(QComboBox *list)
QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) {
list->addItem(entry);
qobject_cast<QListView *>(list->view())
->setRowHidden(list->count() - 1,
action._hidden);
} else {
blog(LOG_WARNING,
"did not insert duplicate action entry with name \"%s\"",
@@ -34,12 +35,14 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
_enable(new SwitchButton()),
_entryData(entryData)
{
auto actionStateTimer = new QTimer(this);
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
SLOT(ActionEnableChanged(bool)));
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
QWidget::connect(actionStateTimer, SIGNAL(timeout()), this,
SLOT(UpdateActionState()));
populateActionSelection(_actionSelection);
@@ -60,12 +63,40 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData(id);
SetupWidgets(true);
_actionStateTimer.start(300);
actionStateTimer->start(300);
_loading = false;
}
void MacroActionEdit::SetupWidgets(bool basicSetup)
{
if (_allWidgetsAreSetup) {
return;
}
const auto id = (*_entryData)->GetId();
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
const bool enabled = (*_entryData)->Enabled();
_enable->setChecked(enabled);
SetDisableEffect(!enabled);
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
if (basicSetup) {
return;
}
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, (*_entryData)->GetCollapsed());
SetFocusPolicyOfWidgets();
_allWidgetsAreSetup = true;
}
void MacroActionEdit::ActionSelectionChanged(const QString &text)
{
if (_loading || !_entryData) {
@@ -95,38 +126,11 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
SetFocusPolicyOfWidgets();
}
void MacroActionEdit::UpdateEntryData(const std::string &id)
{
_actionSelection->setCurrentText(
obs_module_text(MacroActionFactory::GetActionName(id).c_str()));
const bool enabled = (*_entryData)->Enabled();
_enable->setChecked(enabled);
SetDisableEffect(!enabled);
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
_section->SetContent(widget, (*_entryData)->GetCollapsed());
SetFocusPolicyOfWidgets();
}
void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
{
_entryData = data;
}
void MacroActionEdit::SetDisableEffect(bool value)
{
if (value) {
auto effect = new QGraphicsOpacityEffect(this);
effect->setOpacity(0.5);
_section->setGraphicsEffect(effect);
} else {
_section->setGraphicsEffect(nullptr);
}
}
void MacroActionEdit::ActionEnableChanged(bool value)
{
if (_loading || !_entryData) {
@@ -144,13 +148,9 @@ void MacroActionEdit::UpdateActionState()
return;
}
SetEnableAppearance((*_entryData)->Enabled());
}
void MacroActionEdit::SetEnableAppearance(bool value)
{
_enable->setChecked(value);
SetDisableEffect(!value);
const bool enabled = (*_entryData)->Enabled();
SetEnableAppearance(enabled);
_enable->setChecked(enabled);
}
std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
@@ -158,473 +158,4 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
return *_entryData;
}
void AdvSceneSwitcher::AddMacroAction(Macro *macro, int idx,
const std::string &id, obs_data_t *data)
{
if (idx < 0 || idx > (int)macro->Actions().size()) {
assert(false);
return;
}
{
auto lock = LockContext();
macro->Actions().emplace(macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro));
if (data) {
macro->Actions().at(idx)->Load(data);
}
macro->Actions().at(idx)->PostLoad();
RunPostLoadSteps();
macro->UpdateActionIndices();
ui->actionsList->Insert(
idx,
new MacroActionEdit(this, &macro->Actions()[idx], id));
SetActionData(*macro);
}
HighlightAction(idx);
ui->actionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::AddMacroAction(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx > (int)macro->Actions().size()) {
assert(false);
return;
}
std::string id;
if (idx - 1 >= 0) {
id = macro->Actions().at(idx - 1)->GetId();
} else {
id = MacroAction::GetDefaultID();
}
OBSDataAutoRelease data;
if (idx - 1 >= 0) {
data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
}
AddMacroAction(macro.get(), idx, id, data);
}
void AdvSceneSwitcher::on_actionAdd_clicked()
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (currentActionIdx == -1) {
AddMacroAction((int)macro->Actions().size());
} else {
AddMacroAction(currentActionIdx + 1);
}
if (currentActionIdx != -1) {
MacroActionSelectionChanged(currentActionIdx + 1);
}
}
void AdvSceneSwitcher::RemoveMacroAction(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Actions().size()) {
return;
}
{
auto lock = LockContext();
ui->actionsList->Remove(idx);
macro->Actions().erase(macro->Actions().begin() + idx);
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
macro->UpdateActionIndices();
SetActionData(*macro);
}
MacroActionSelectionChanged(-1);
lastInteracted = MacroSection::ACTIONS;
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::on_actionRemove_clicked()
{
if (currentActionIdx == -1) {
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
RemoveMacroAction((int)macro->Actions().size() - 1);
} else {
RemoveMacroAction(currentActionIdx);
}
MacroActionSelectionChanged(-1);
}
void AdvSceneSwitcher::on_actionTop_clicked()
{
if (currentActionIdx == -1) {
return;
}
MacroActionReorder(0, currentActionIdx);
MacroActionSelectionChanged(0);
}
void AdvSceneSwitcher::on_actionUp_clicked()
{
if (currentActionIdx == -1 || currentActionIdx == 0) {
return;
}
MoveMacroActionUp(currentActionIdx);
MacroActionSelectionChanged(currentActionIdx - 1);
}
void AdvSceneSwitcher::on_actionDown_clicked()
{
if (currentActionIdx == -1 ||
currentActionIdx == ui->actionsList->ContentLayout()->count() - 1) {
return;
}
MoveMacroActionDown(currentActionIdx);
MacroActionSelectionChanged(currentActionIdx + 1);
}
void AdvSceneSwitcher::on_actionBottom_clicked()
{
if (currentActionIdx == -1) {
return;
}
const int newIdx = ui->actionsList->ContentLayout()->count() - 1;
MacroActionReorder(newIdx, currentActionIdx);
MacroActionSelectionChanged(newIdx);
}
void AdvSceneSwitcher::on_elseActionAdd_clicked()
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (currentElseActionIdx == -1) {
AddMacroElseAction((int)macro->ElseActions().size());
} else {
AddMacroElseAction(currentElseActionIdx + 1);
}
if (currentElseActionIdx != -1) {
MacroElseActionSelectionChanged(currentElseActionIdx + 1);
}
}
void AdvSceneSwitcher::on_elseActionRemove_clicked()
{
if (currentElseActionIdx == -1) {
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
RemoveMacroElseAction((int)macro->ElseActions().size() - 1);
} else {
RemoveMacroElseAction(currentElseActionIdx);
}
MacroElseActionSelectionChanged(-1);
}
void AdvSceneSwitcher::on_elseActionTop_clicked()
{
if (currentElseActionIdx == -1) {
return;
}
MacroElseActionReorder(0, currentElseActionIdx);
MacroElseActionSelectionChanged(0);
}
void AdvSceneSwitcher::on_elseActionUp_clicked()
{
if (currentElseActionIdx == -1 || currentElseActionIdx == 0) {
return;
}
MoveMacroElseActionUp(currentElseActionIdx);
MacroElseActionSelectionChanged(currentElseActionIdx - 1);
}
void AdvSceneSwitcher::on_elseActionDown_clicked()
{
if (currentElseActionIdx == -1 ||
currentElseActionIdx ==
ui->elseActionsList->ContentLayout()->count() - 1) {
return;
}
MoveMacroElseActionDown(currentElseActionIdx);
MacroElseActionSelectionChanged(currentElseActionIdx + 1);
}
void AdvSceneSwitcher::on_elseActionBottom_clicked()
{
if (currentElseActionIdx == -1) {
return;
}
const int newIdx = ui->elseActionsList->ContentLayout()->count() - 1;
MacroElseActionReorder(newIdx, currentElseActionIdx);
MacroElseActionSelectionChanged(newIdx);
}
void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
{
if (pos1 == pos2) {
return;
}
if (pos1 > pos2) {
std::swap(pos1, pos2);
}
auto lock = LockContext();
iter_swap(m->Actions().begin() + pos1, m->Actions().begin() + pos2);
m->UpdateActionIndices();
auto widget1 = static_cast<MacroActionEdit *>(
ui->actionsList->ContentLayout()->takeAt(pos1)->widget());
auto widget2 = static_cast<MacroActionEdit *>(
ui->actionsList->ContentLayout()->takeAt(pos2 - 1)->widget());
ui->actionsList->Insert(pos1, widget2);
ui->actionsList->Insert(pos2, widget1);
SetActionData(*m);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::MoveMacroActionUp(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 1 || idx >= (int)macro->Actions().size()) {
return;
}
SwapActions(macro.get(), idx, idx - 1);
HighlightAction(idx - 1);
}
void AdvSceneSwitcher::MoveMacroActionDown(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Actions().size() - 1) {
return;
}
SwapActions(macro.get(), idx, idx + 1);
HighlightAction(idx + 1);
}
void AdvSceneSwitcher::MacroElseActionSelectionChanged(int idx)
{
SetupMacroSegmentSelection(MacroSection::ELSE_ACTIONS, idx);
}
void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (to == from || from < 0 || from > (int)macro->ElseActions().size() ||
to < 0 || to > (int)macro->ElseActions().size()) {
return;
}
{
auto lock = LockContext();
auto action = macro->ElseActions().at(from);
macro->ElseActions().erase(macro->ElseActions().begin() + from);
macro->ElseActions().insert(macro->ElseActions().begin() + to,
action);
macro->UpdateElseActionIndices();
ui->elseActionsList->ContentLayout()->insertItem(
to, ui->elseActionsList->ContentLayout()->takeAt(from));
SetElseActionData(*macro);
}
HighlightElseAction(to);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::AddMacroElseAction(Macro *macro, int idx,
const std::string &id,
obs_data_t *data)
{
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
assert(false);
return;
}
{
auto lock = LockContext();
macro->ElseActions().emplace(macro->ElseActions().begin() + idx,
MacroActionFactory::Create(id,
macro));
if (data) {
macro->ElseActions().at(idx)->Load(data);
}
macro->ElseActions().at(idx)->PostLoad();
RunPostLoadSteps();
macro->UpdateElseActionIndices();
ui->elseActionsList->Insert(
idx, new MacroActionEdit(
this, &macro->ElseActions()[idx], id));
SetElseActionData(*macro);
}
HighlightElseAction(idx);
ui->elseActionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::AddMacroElseAction(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
assert(false);
return;
}
std::string id;
if (idx - 1 >= 0) {
id = macro->ElseActions().at(idx - 1)->GetId();
} else {
id = MacroAction::GetDefaultID();
}
OBSDataAutoRelease data;
if (idx - 1 >= 0) {
data = obs_data_create();
macro->ElseActions().at(idx - 1)->Save(data);
}
AddMacroElseAction(macro.get(), idx, id, data);
}
void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->ElseActions().size()) {
return;
}
{
auto lock = LockContext();
ui->elseActionsList->Remove(idx);
macro->ElseActions().erase(macro->ElseActions().begin() + idx);
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
macro->UpdateElseActionIndices();
SetElseActionData(*macro);
}
MacroElseActionSelectionChanged(-1);
lastInteracted = MacroSection::ELSE_ACTIONS;
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::SwapElseActions(Macro *m, int pos1, int pos2)
{
if (pos1 == pos2) {
return;
}
if (pos1 > pos2) {
std::swap(pos1, pos2);
}
auto lock = LockContext();
iter_swap(m->ElseActions().begin() + pos1,
m->ElseActions().begin() + pos2);
m->UpdateElseActionIndices();
auto widget1 = static_cast<MacroActionEdit *>(
ui->elseActionsList->ContentLayout()->takeAt(pos1)->widget());
auto widget2 = static_cast<MacroActionEdit *>(
ui->elseActionsList->ContentLayout()
->takeAt(pos2 - 1)
->widget());
ui->elseActionsList->Insert(pos1, widget2);
ui->elseActionsList->Insert(pos2, widget1);
SetElseActionData(*m);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::MoveMacroElseActionUp(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 1 || idx >= (int)macro->ElseActions().size()) {
return;
}
SwapElseActions(macro.get(), idx, idx - 1);
HighlightElseAction(idx - 1);
}
void AdvSceneSwitcher::MoveMacroElseActionDown(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->ElseActions().size() - 1) {
return;
}
SwapElseActions(macro.get(), idx, idx + 1);
HighlightElseAction(idx + 1);
}
void AdvSceneSwitcher::MacroActionSelectionChanged(int idx)
{
SetupMacroSegmentSelection(MacroSection::ACTIONS, idx);
}
void AdvSceneSwitcher::MacroActionReorder(int to, int from)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (to == from || from < 0 || from > (int)macro->Actions().size() ||
to < 0 || to > (int)macro->Actions().size()) {
return;
}
{
auto lock = LockContext();
auto action = macro->Actions().at(from);
macro->Actions().erase(macro->Actions().begin() + from);
macro->Actions().insert(macro->Actions().begin() + to, action);
macro->UpdateActionIndices();
ui->actionsList->ContentLayout()->insertItem(
to, ui->actionsList->ContentLayout()->takeAt(from));
SetActionData(*macro);
}
HighlightAction(to);
emit(MacroSegmentOrderChanged());
}
} // namespace advss

View File

@@ -17,7 +17,7 @@ public:
QWidget *parent = nullptr,
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = MacroAction::GetDefaultID().data());
void UpdateEntryData(const std::string &id);
void SetupWidgets(bool basicSetup = false);
void SetEntryData(std::shared_ptr<MacroAction> *);
private slots:
@@ -27,14 +27,11 @@ private slots:
private:
std::shared_ptr<MacroSegment> Data() const;
void SetDisableEffect(bool);
void SetEnableAppearance(bool);
FilterComboBox *_actionSelection;
SwitchButton *_enable;
std::shared_ptr<MacroAction> *_entryData;
QTimer _actionStateTimer;
bool _loading = true;
};

View File

@@ -12,6 +12,7 @@ struct MacroActionInfo {
std::function<std::shared_ptr<MacroAction>(Macro *m)> _create = nullptr;
CreateActionWidget _createWidget = nullptr;
std::string _name;
bool _hidden = false;
};
class MacroActionFactory {
@@ -19,7 +20,7 @@ public:
MacroActionFactory() = delete;
EXPORT static bool Register(const std::string &id, MacroActionInfo);
static bool Deregister(const std::string &id);
EXPORT static bool Deregister(const std::string &id);
static std::shared_ptr<MacroAction> Create(const std::string &id,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,

View File

@@ -12,27 +12,13 @@ bool MacroActionMacro::_registered = MacroActionFactory::Register(
{MacroActionMacro::Create, MacroActionMacroEdit::Create,
"AdvSceneSwitcher.action.macro"});
const static std::map<MacroActionMacro::Action, std::string> actionTypes = {
{MacroActionMacro::Action::PAUSE,
"AdvSceneSwitcher.action.macro.type.pause"},
{MacroActionMacro::Action::UNPAUSE,
"AdvSceneSwitcher.action.macro.type.unpause"},
{MacroActionMacro::Action::RESET_COUNTER,
"AdvSceneSwitcher.action.macro.type.resetCounter"},
{MacroActionMacro::Action::RUN,
"AdvSceneSwitcher.action.macro.type.run"},
{MacroActionMacro::Action::STOP,
"AdvSceneSwitcher.action.macro.type.stop"},
{MacroActionMacro::Action::DISABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.disableAction"},
{MacroActionMacro::Action::ENABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.enableAction"},
{MacroActionMacro::Action::TOGGLE_ACTION,
"AdvSceneSwitcher.action.macro.type.toggleAction"},
};
bool MacroActionMacro::PerformAction()
{
if (_action == Action::NESTED_MACRO) {
const bool conditionsMatched = _nestedMacro->CheckConditions();
return _nestedMacro->PerformActions(conditionsMatched);
}
auto macro = _macro.GetMacro();
if (!macro) {
return true;
@@ -45,10 +31,13 @@ bool MacroActionMacro::PerformAction()
case Action::UNPAUSE:
macro->SetPaused(false);
break;
case Action::TOGGLE_PAUSE:
macro->SetPaused(!macro->Paused());
break;
case Action::RESET_COUNTER:
macro->ResetRunCount();
break;
case Action::RUN:
case Action::RUN_ACTIONS:
RunActions(macro.get());
break;
case Action::STOP:
@@ -96,7 +85,7 @@ void MacroActionMacro::LogAction() const
ablog(LOG_INFO, "reset counter for \"%s\"",
macro->Name().c_str());
break;
case Action::RUN:
case Action::RUN_ACTIONS:
ablog(LOG_INFO, "run nested macro \"%s\"",
macro->Name().c_str());
break;
@@ -115,6 +104,9 @@ void MacroActionMacro::LogAction() const
ablog(LOG_INFO, "toggled action %d of macro \"%s\"",
_actionIndex.GetValue(), macro->Name().c_str());
break;
case Action::NESTED_MACRO:
ablog(LOG_INFO, "run nested macro");
break;
default:
break;
}
@@ -127,6 +119,10 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
_actionIndex.Save(obj, "actionIndex");
obs_data_set_int(obj, "action", static_cast<int>(_action));
_runOptions.Save(obj);
OBSDataAutoRelease nestedMacroData = obs_data_create();
_nestedMacro->Save(nestedMacroData);
obs_data_set_obj(obj, "nestedMacro", nestedMacroData);
obs_data_set_int(obj, "customWidgetHeight", _customWidgetHeight);
return true;
}
@@ -138,6 +134,16 @@ bool MacroActionMacro::Load(obs_data_t *obj)
_action = static_cast<MacroActionMacro::Action>(
obs_data_get_int(obj, "action"));
_runOptions.Load(obj);
if (obs_data_has_user_value(obj, "nestedMacro")) {
OBSDataAutoRelease nestedMacroData =
obs_data_get_obj(obj, "nestedMacro");
_nestedMacro->Load(nestedMacroData);
_nestedMacro->PostLoad();
}
_customWidgetHeight = obs_data_get_int(obj, "customWidgetHeight");
return true;
}
@@ -210,8 +216,33 @@ void MacroActionMacro::RunActions(Macro *actionMacro) const
static void populateActionSelection(QComboBox *list)
{
for (const auto &[_, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
static const std::vector<std::pair<MacroActionMacro::Action, std::string>>
actions = {
{MacroActionMacro::Action::PAUSE,
"AdvSceneSwitcher.action.macro.type.pause"},
{MacroActionMacro::Action::UNPAUSE,
"AdvSceneSwitcher.action.macro.type.unpause"},
{MacroActionMacro::Action::TOGGLE_PAUSE,
"AdvSceneSwitcher.action.macro.type.togglePause"},
{MacroActionMacro::Action::RESET_COUNTER,
"AdvSceneSwitcher.action.macro.type.resetCounter"},
{MacroActionMacro::Action::NESTED_MACRO,
"AdvSceneSwitcher.action.macro.type.nestedMacro"},
{MacroActionMacro::Action::RUN_ACTIONS,
"AdvSceneSwitcher.action.macro.type.run"},
{MacroActionMacro::Action::STOP,
"AdvSceneSwitcher.action.macro.type.stop"},
{MacroActionMacro::Action::DISABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.disableAction"},
{MacroActionMacro::Action::ENABLE_ACTION,
"AdvSceneSwitcher.action.macro.type.enableAction"},
{MacroActionMacro::Action::TOGGLE_ACTION,
"AdvSceneSwitcher.action.macro.type.toggleAction"},
};
for (const auto &[value, name] : actions) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
@@ -235,7 +266,7 @@ static void populateActionTypeSelection(QComboBox *list)
MacroActionMacroEdit::MacroActionMacroEdit(
QWidget *parent, std::shared_ptr<MacroActionMacro> entryData)
: QWidget(parent),
: ResizableWidget(parent),
_macros(new MacroSelection(parent)),
_actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION)),
@@ -254,7 +285,13 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_entryLayout(new QHBoxLayout()),
_conditionLayout(new QHBoxLayout()),
_reevaluateConditionStateLayout(new QHBoxLayout()),
_setInputsLayout(new QHBoxLayout())
_setInputsLayout(new QHBoxLayout()),
_nestedMacro(new MacroEdit(
this,
QStringList()
<< "AdvSceneSwitcher.action.macro.type.nestedMacro.conditionHelp"
<< "AdvSceneSwitcher.action.macro.type.nestedMacro.actionHelp"
<< "AdvSceneSwitcher.action.macro.type.nestedMacro.elseActionHelp"))
{
populateActionSelection(_actions);
populateConditionBehaviorSelection(_conditionBehaviors);
@@ -303,17 +340,24 @@ MacroActionMacroEdit::MacroActionMacroEdit(
layout->addLayout(_setInputsLayout);
layout->addWidget(_inputs);
layout->addWidget(_skipWhenPaused);
layout->addWidget(_nestedMacro);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
MacroActionMacroEdit::~MacroActionMacroEdit()
{
HighligthMacroSettingsButton(false);
HighlightMacroSettingsButton(false);
if (!_entryData) {
return;
}
_entryData->_customWidgetHeight = GetCustomHeight();
_nestedMacro->SetMacro({}); // Save splitter states
}
void MacroActionMacroEdit::UpdateEntryData()
@@ -321,7 +365,8 @@ void MacroActionMacroEdit::UpdateEntryData()
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_actions->setCurrentIndex(
_actions->findData(static_cast<int>(_entryData->_action)));
_actionIndex->SetValue(_entryData->_actionIndex);
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
_macros->SetCurrentMacro(_entryData->_macro);
@@ -335,9 +380,20 @@ void MacroActionMacroEdit::UpdateEntryData()
_skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused);
_setInputs->setChecked(_entryData->_runOptions.setInputs);
SetupMacroInput(_entryData->_macro.GetMacro().get());
const auto &macro = _entryData->_nestedMacro;
_nestedMacro->SetMacro(macro);
SetWidgetVisibility();
}
QWidget *MacroActionMacroEdit::Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionMacroEdit(
parent, std::dynamic_pointer_cast<MacroActionMacro>(action));
}
void MacroActionMacroEdit::MacroChanged(const QString &text)
{
GUARD_LOADING_AND_LOCK();
@@ -350,10 +406,11 @@ void MacroActionMacroEdit::MacroChanged(const QString &text)
SetWidgetVisibility();
}
void MacroActionMacroEdit::ActionChanged(int value)
void MacroActionMacroEdit::ActionChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionMacro::Action>(value);
_entryData->_action = static_cast<MacroActionMacro::Action>(
_actions->itemData(idx).toInt());
SetWidgetVisibility();
}
@@ -435,7 +492,8 @@ void MacroActionMacroEdit::SetWidgetVisibility()
PlaceWidgets(
obs_module_text(
_entryData->_action == MacroActionMacro::Action::RUN
_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS
? "AdvSceneSwitcher.action.macro.entry.run"
: "AdvSceneSwitcher.action.macro.entry.other"),
_entryLayout, placeholders);
@@ -451,7 +509,7 @@ void MacroActionMacroEdit::SetWidgetVisibility()
_conditionLayout, placeholders);
}
if (_entryData->_action == MacroActionMacro::Action::RUN ||
if (_entryData->_action == MacroActionMacro::Action::RUN_ACTIONS ||
_entryData->_action == MacroActionMacro::Action::STOP) {
_macros->HideSelectedMacro();
} else {
@@ -467,27 +525,43 @@ void MacroActionMacroEdit::SetWidgetVisibility()
_actionIndex->setVisible(isModifyingActionState);
SetLayoutVisible(_conditionLayout,
_entryData->_action == MacroActionMacro::Action::RUN);
_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS);
const bool needsAdditionalConditionWidgets =
_entryData->_action == MacroActionMacro::Action::RUN &&
_entryData->_action == MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_runOptions.logic !=
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS;
_conditionMacros->setVisible(needsAdditionalConditionWidgets);
SetLayoutVisible(_reevaluateConditionStateLayout,
needsAdditionalConditionWidgets);
SetLayoutVisible(_setInputsLayout,
_entryData->_action == MacroActionMacro::Action::RUN);
_entryData->_action ==
MacroActionMacro::Action::RUN_ACTIONS);
_inputs->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN &&
MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_runOptions.setInputs);
HighligthMacroSettingsButton(_entryData->_action ==
MacroActionMacro::Action::RUN &&
_entryData->_runOptions.setInputs &&
!_inputs->HasInputsToSet());
HighlightMacroSettingsButton(
_entryData->_action == MacroActionMacro::Action::RUN_ACTIONS &&
_entryData->_runOptions.setInputs &&
!_inputs->HasInputsToSet());
_actionTypes->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN);
MacroActionMacro::Action::RUN_ACTIONS);
_skipWhenPaused->setVisible(_entryData->_action ==
MacroActionMacro::Action::RUN);
MacroActionMacro::Action::RUN_ACTIONS);
_nestedMacro->setVisible(_entryData->_action ==
MacroActionMacro::Action::NESTED_MACRO);
_macros->setVisible(_entryData->_action !=
MacroActionMacro::Action::NESTED_MACRO);
SetResizingEnabled(_entryData->_action ==
MacroActionMacro::Action::NESTED_MACRO);
if (_nestedMacro->IsEmpty()) {
_nestedMacro->ShowAllMacroSections();
// TODO: find a better solution than setting a fixed height
_entryData->_customWidgetHeight = 600;
}
SetCustomHeight(_entryData->_customWidgetHeight);
adjustSize();
updateGeometry();

View File

@@ -1,8 +1,11 @@
#pragma once
#include "macro-action-edit.hpp"
#include "macro.hpp"
#include "macro-edit.hpp"
#include "macro-input.hpp"
#include "macro-selection.hpp"
#include "macro-segment-selection.hpp"
#include "resizable-widget.hpp"
#include <QCheckBox>
#include <QHBoxLayout>
@@ -44,15 +47,19 @@ public:
PAUSE,
UNPAUSE,
RESET_COUNTER,
RUN,
RUN_ACTIONS,
STOP,
DISABLE_ACTION,
ENABLE_ACTION,
TOGGLE_ACTION,
TOGGLE_PAUSE,
NESTED_MACRO,
};
Action _action = Action::RUN;
Action _action = Action::NESTED_MACRO;
IntVariable _actionIndex = 1;
RunOptions _runOptions = {};
std::shared_ptr<Macro> _nestedMacro = std::make_shared<Macro>();
int _customWidgetHeight = 0;
private:
void RunActions(Macro *actionMacro) const;
@@ -61,7 +68,7 @@ private:
static const std::string id;
};
class MacroActionMacroEdit final : public QWidget {
class MacroActionMacroEdit final : public ResizableWidget {
Q_OBJECT
public:
@@ -70,13 +77,7 @@ public:
std::shared_ptr<MacroActionMacro> entryData = nullptr);
~MacroActionMacroEdit();
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionMacroEdit(
parent,
std::dynamic_pointer_cast<MacroActionMacro>(action));
}
static QWidget *Create(QWidget *, std::shared_ptr<MacroAction>);
private slots:
void MacroChanged(const QString &text);
@@ -111,6 +112,7 @@ private:
QHBoxLayout *_conditionLayout;
QHBoxLayout *_reevaluateConditionStateLayout;
QHBoxLayout *_setInputsLayout;
MacroEdit *_nestedMacro;
std::shared_ptr<MacroActionMacro> _entryData;
bool _loading = true;

View File

@@ -170,11 +170,7 @@ void MacroActionQueueEdit::UpdateEntryData()
void MacroActionQueueEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_macro = text;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -182,11 +178,7 @@ void MacroActionQueueEdit::MacroChanged(const QString &text)
void MacroActionQueueEdit::QueueChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_queue = GetWeakActionQueueByQString(text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -194,11 +186,7 @@ void MacroActionQueueEdit::QueueChanged(const QString &text)
void MacroActionQueueEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_action = static_cast<MacroActionQueue::Action>(value);
SetWidgetVisibility();
emit HeaderInfoChanged(

File diff suppressed because it is too large Load Diff

View File

@@ -28,8 +28,8 @@ public:
int GetSegmentIndexValue() const;
void ResolveVariablesToFixedValues();
enum class Type {
SET_FIXED_VALUE,
enum class Action {
SET_VALUE,
APPEND,
APPEND_VAR,
INCREMENT,
@@ -52,18 +52,21 @@ public:
SWAP_VALUES,
TRIM,
CHANGE_CASE,
RANDOM_NUMBER,
QUERY_JSON,
ARRAY_JSON,
};
Type _type = Type::SET_FIXED_VALUE;
Action _action = Action::SET_VALUE;
std::weak_ptr<Variable> _variable;
std::weak_ptr<Variable> _variable2;
StringVariable _strValue = "";
double _numValue = 0;
int _subStringStart = 0;
int _subStringSize = 0;
DoubleVariable _numValue = 0;
IntVariable _subStringStart = 0;
IntVariable _subStringSize = 0;
RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig();
std::string _regexPattern = ".*";
int _regexMatchIdx = 0;
IntVariable _regexMatchIdx = 0;
RegexConfig _findRegex;
StringVariable _findStr = obs_module_text(
"AdvSceneSwitcher.action.variable.findAndReplace.find");
@@ -99,6 +102,12 @@ public:
};
CaseType _caseType = CaseType::LOWER_CASE;
DoubleVariable _randomNumberStart = 0;
DoubleVariable _randomNumberEnd = 100;
bool _generateInteger = true;
StringVariable _jsonQuery = "$.some.nested.value";
IntVariable _jsonIndex = 0;
private:
void DecrementCurrentSegmentVariableRef();
@@ -108,6 +117,7 @@ private:
void HandleMathExpression(Variable *);
void HandleCaseChange(Variable *);
void SetToSceneItemName(Variable *);
void GenerateRandomNumber(Variable *);
std::weak_ptr<MacroSegment> _macroSegment;
int _segmentIdxLoadValue = -1;
@@ -136,15 +146,15 @@ private slots:
void Variable2Changed(const QString &);
void ActionChanged(int);
void StrValueChanged();
void NumValueChanged(double);
void NumValueChanged(const NumberVariable<double> &value);
void SegmentIndexChanged(const IntVariable &);
void UpdateSegmentVariableValue();
void MacroSegmentOrderChanged();
void SubStringStartChanged(int val);
void SubStringSizeChanged(int val);
void SubStringStartChanged(const NumberVariable<int> &start);
void SubStringSizeChanged(const NumberVariable<int> &size);
void SubStringRegexChanged(const RegexConfig &conf);
void RegexPatternChanged();
void RegexMatchIdxChanged(int val);
void RegexMatchIdxChanged(const NumberVariable<int> &index);
void FindStrValueChanged();
void FindRegexChanged(const RegexConfig &conf);
void ReplaceStrValueChanged();
@@ -161,6 +171,11 @@ private slots:
void StringLengthChanged(const NumberVariable<int> &);
void CharSelectionChanged(const QString &);
void CaseTypeChanged(int index);
void RandomNumberStartChanged(const NumberVariable<double> &);
void RandomNumberEndChanged(const NumberVariable<double> &);
void GenerateIntegerChanged(int);
void JsonQueryChanged();
void JsonIndexChanged(const NumberVariable<int> &);
signals:
void HeaderInfoChanged(const QString &);
@@ -173,18 +188,18 @@ private:
VariableSelection *_variables2;
FilterComboBox *_actions;
VariableTextEdit *_strValue;
QDoubleSpinBox *_numValue;
VariableDoubleSpinBox *_numValue;
MacroSegmentSelection *_segmentIdx;
QLabel *_segmentValueStatus;
ResizingPlainTextEdit *_segmentValue;
QVBoxLayout *_substringLayout;
QHBoxLayout *_subStringIndexEntryLayout;
QHBoxLayout *_subStringRegexEntryLayout;
QSpinBox *_subStringStart;
QSpinBox *_subStringSize;
VariableSpinBox *_subStringStart;
VariableSpinBox *_subStringSize;
RegexConfigWidget *_substringRegex;
ResizingPlainTextEdit *_regexPattern;
QSpinBox *_regexMatchIdx;
VariableSpinBox *_regexMatchIdx;
QHBoxLayout *_findReplaceLayout;
RegexConfigWidget *_findRegex;
VariableTextEdit *_findStr;
@@ -206,6 +221,13 @@ private:
VariableSpinBox *_stringLength;
SingleCharSelection *_paddingCharSelection;
FilterComboBox *_caseType;
VariableDoubleSpinBox *_randomNumberStart;
VariableDoubleSpinBox *_randomNumberEnd;
QCheckBox *_generateInteger;
QVBoxLayout *_randomLayout;
VariableLineEdit *_jsonQuery;
QLabel *_jsonQueryHelp;
VariableSpinBox *_jsonIndex;
QHBoxLayout *_entryLayout;
std::shared_ptr<MacroActionVariable> _entryData;

View File

@@ -11,15 +11,12 @@ bool MacroAction::Save(obs_data_t *obj) const
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_bool(obj, "enabled", _enabled);
return true;
}
bool MacroAction::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
obs_data_set_default_bool(obj, "enabled", true);
_enabled = obs_data_get_bool(obj, "enabled");
return true;
}
@@ -28,16 +25,6 @@ void MacroAction::LogAction() const
ablog(LOG_INFO, "performed action %s", GetId().c_str());
}
void MacroAction::SetEnabled(bool value)
{
_enabled = value;
}
bool MacroAction::Enabled() const
{
return _enabled;
}
void MacroAction::ResolveVariablesToFixedValues() {}
std::string_view MacroAction::GetDefaultID()

View File

@@ -19,13 +19,9 @@ public:
// Used to resolve variables before actions are added to action queues
virtual void ResolveVariablesToFixedValues();
void SetEnabled(bool);
bool Enabled() const;
static std::string_view GetDefaultID();
private:
bool _enabled = true;
};
class EXPORT MacroRefAction : virtual public MacroAction {

View File

@@ -138,7 +138,7 @@ MacroConditionEdit::MacroConditionEdit(
mainLayout->addWidget(_frame);
setLayout(mainLayout);
UpdateEntryData(id);
SetupWidgets(true);
_loading = false;
}
@@ -152,9 +152,11 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
const auto logic = static_cast<Logic::Type>(
_logicSelection->itemData(idx).toInt());
(*_entryData)->SetLogicType(logic);
SetEnableAppearance(logic != Logic::Type::NONE);
}
bool MacroConditionEdit::IsRootNode()
bool MacroConditionEdit::IsRootNode() const
{
return _isRoot;
}
@@ -164,6 +166,7 @@ void MacroConditionEdit::SetLogicSelection()
const auto logic = (*_entryData)->GetLogicType();
_logicSelection->setCurrentIndex(
_logicSelection->findData(static_cast<int>(logic)));
SetEnableAppearance(logic != Logic::Type::NONE);
}
void MacroConditionEdit::SetRootNode(bool root)
@@ -175,23 +178,36 @@ void MacroConditionEdit::SetRootNode(bool root)
SetLogicSelection();
}
void MacroConditionEdit::UpdateEntryData(const std::string &id)
void MacroConditionEdit::SetupWidgets(bool basicSetup)
{
if (_allWidgetsAreSetup) {
return;
}
const auto id = (*_entryData)->GetId();
_conditionSelection->setCurrentText(obs_module_text(
MacroConditionFactory::GetConditionName(id).c_str()));
auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
HeaderInfoChanged(
QString::fromStdString((*_entryData)->GetShortDesc()));
SetLogicSelection();
_section->SetContent(widget, (*_entryData)->GetCollapsed());
_dur->setVisible(MacroConditionFactory::UsesDurationModifier(id));
auto modifier = (*_entryData)->GetDurationModifier();
_dur->SetValue(modifier);
if (basicSetup) {
return;
}
auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, (*_entryData)->GetCollapsed());
SetFocusPolicyOfWidgets();
_allWidgetsAreSetup = true;
}
void MacroConditionEdit::SetEntryData(std::shared_ptr<MacroCondition> *data)
@@ -259,289 +275,4 @@ std::shared_ptr<MacroSegment> MacroConditionEdit::Data() const
return *_entryData;
}
void AdvSceneSwitcher::AddMacroCondition(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx > (int)macro->Conditions().size()) {
assert(false);
return;
}
std::string id;
Logic::Type logic;
if (idx >= 1) {
id = macro->Conditions().at(idx - 1)->GetId();
if (idx == 1) {
logic = Logic::Type::OR;
} else {
logic = macro->Conditions().at(idx - 1)->GetLogicType();
}
} else {
id = MacroCondition::GetDefaultID();
logic = Logic::Type::ROOT_NONE;
}
OBSDataAutoRelease data;
if (idx - 1 >= 0) {
data = obs_data_create();
macro->Conditions().at(idx - 1)->Save(data);
}
AddMacroCondition(macro.get(), idx, id, data.Get(), logic);
}
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
const std::string &id,
obs_data_t *data, Logic::Type logic)
{
if (idx < 0 || idx > (int)macro->Conditions().size()) {
assert(false);
return;
}
{
auto lock = LockContext();
auto cond = macro->Conditions().emplace(
macro->Conditions().begin() + idx,
MacroConditionFactory::Create(id, macro));
if (data) {
macro->Conditions().at(idx)->Load(data);
}
macro->Conditions().at(idx)->PostLoad();
RunPostLoadSteps();
(*cond)->SetLogicType(logic);
macro->UpdateConditionIndices();
ui->conditionsList->Insert(
idx,
new MacroConditionEdit(this, &macro->Conditions()[idx],
id, idx == 0));
SetConditionData(*macro);
}
HighlightCondition(idx);
ui->conditionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::on_conditionAdd_clicked()
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (currentConditionIdx == -1) {
AddMacroCondition((int)macro->Conditions().size());
} else {
AddMacroCondition(currentConditionIdx + 1);
}
if (currentConditionIdx != -1) {
MacroConditionSelectionChanged(currentConditionIdx + 1);
}
}
void AdvSceneSwitcher::RemoveMacroCondition(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Conditions().size()) {
return;
}
{
auto lock = LockContext();
ui->conditionsList->Remove(idx);
macro->Conditions().erase(macro->Conditions().begin() + idx);
macro->UpdateConditionIndices();
if (idx == 0 && macro->Conditions().size() > 0) {
auto newRoot = macro->Conditions().at(0);
newRoot->SetLogicType(Logic::Type::ROOT_NONE);
static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(0))
->SetRootNode(true);
}
SetConditionData(*macro);
}
MacroConditionSelectionChanged(-1);
lastInteracted = MacroSection::CONDITIONS;
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::on_conditionRemove_clicked()
{
if (currentConditionIdx == -1) {
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
RemoveMacroCondition((int)macro->Conditions().size() - 1);
} else {
RemoveMacroCondition(currentConditionIdx);
}
MacroConditionSelectionChanged(-1);
}
void AdvSceneSwitcher::on_conditionTop_clicked()
{
if (currentConditionIdx == -1) {
return;
}
MacroConditionReorder(0, currentConditionIdx);
MacroConditionSelectionChanged(0);
}
void AdvSceneSwitcher::on_conditionUp_clicked()
{
if (currentConditionIdx == -1 || currentConditionIdx == 0) {
return;
}
MoveMacroConditionUp(currentConditionIdx);
MacroConditionSelectionChanged(currentConditionIdx - 1);
}
void AdvSceneSwitcher::on_conditionDown_clicked()
{
if (currentConditionIdx == -1 ||
currentConditionIdx ==
ui->conditionsList->ContentLayout()->count() - 1) {
return;
}
MoveMacroConditionDown(currentConditionIdx);
MacroConditionSelectionChanged(currentConditionIdx + 1);
}
void AdvSceneSwitcher::on_conditionBottom_clicked()
{
if (currentConditionIdx == -1) {
return;
}
const int newIdx = ui->conditionsList->ContentLayout()->count() - 1;
MacroConditionReorder(newIdx, currentConditionIdx);
MacroConditionSelectionChanged(newIdx);
}
void AdvSceneSwitcher::SwapConditions(Macro *m, int pos1, int pos2)
{
if (pos1 == pos2) {
return;
}
if (pos1 > pos2) {
std::swap(pos1, pos2);
}
bool root = pos1 == 0;
auto lock = LockContext();
iter_swap(m->Conditions().begin() + pos1,
m->Conditions().begin() + pos2);
m->UpdateConditionIndices();
auto c1 = m->Conditions().begin() + pos1;
auto c2 = m->Conditions().begin() + pos2;
if (root) {
auto logic1 = (*c1)->GetLogicType();
auto logic2 = (*c2)->GetLogicType();
(*c1)->SetLogicType(logic2);
(*c2)->SetLogicType(logic1);
}
auto widget1 = static_cast<MacroConditionEdit *>(
ui->conditionsList->ContentLayout()->takeAt(pos1)->widget());
auto widget2 = static_cast<MacroConditionEdit *>(
ui->conditionsList->ContentLayout()->takeAt(pos2 - 1)->widget());
ui->conditionsList->Insert(pos1, widget2);
ui->conditionsList->Insert(pos2, widget1);
SetConditionData(*m);
widget2->SetRootNode(root);
widget1->SetRootNode(false);
emit(MacroSegmentOrderChanged());
}
void AdvSceneSwitcher::MoveMacroConditionUp(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 1 || idx >= (int)macro->Conditions().size()) {
return;
}
SwapConditions(macro.get(), idx, idx - 1);
HighlightCondition(idx - 1);
}
void AdvSceneSwitcher::MoveMacroConditionDown(int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (idx < 0 || idx >= (int)macro->Conditions().size() - 1) {
return;
}
SwapConditions(macro.get(), idx, idx + 1);
HighlightCondition(idx + 1);
}
void AdvSceneSwitcher::MacroConditionSelectionChanged(int idx)
{
SetupMacroSegmentSelection(MacroSection::CONDITIONS, idx);
}
void AdvSceneSwitcher::MacroConditionReorder(int to, int from)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (to == from || from < 0 || from > (int)macro->Conditions().size() ||
to < 0 || to > (int)macro->Conditions().size()) {
return;
}
{
auto lock = LockContext();
auto condition = macro->Conditions().at(from);
if (to == 0) {
condition->SetLogicType(Logic::Type::ROOT_NONE);
static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(from))
->SetRootNode(true);
macro->Conditions().at(0)->SetLogicType(
Logic::Type::AND);
static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(0))
->SetRootNode(false);
}
if (from == 0) {
condition->SetLogicType(Logic::Type::AND);
static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(from))
->SetRootNode(false);
macro->Conditions().at(1)->SetLogicType(
Logic::Type::ROOT_NONE);
static_cast<MacroConditionEdit *>(
ui->conditionsList->WidgetAt(1))
->SetRootNode(true);
}
macro->Conditions().erase(macro->Conditions().begin() + from);
macro->Conditions().insert(macro->Conditions().begin() + to,
condition);
macro->UpdateConditionIndices();
ui->conditionsList->ContentLayout()->insertItem(
to, ui->conditionsList->ContentLayout()->takeAt(from));
SetConditionData(*macro);
}
HighlightCondition(to);
emit(MacroSegmentOrderChanged());
}
} // namespace advss

View File

@@ -39,9 +39,9 @@ public:
std::shared_ptr<MacroCondition> * = nullptr,
const std::string &id = MacroCondition::GetDefaultID().data(),
bool root = true);
bool IsRootNode();
bool IsRootNode() const;
void SetRootNode(bool);
void UpdateEntryData(const std::string &id);
void SetupWidgets(bool basicSetup = false);
void SetEntryData(std::shared_ptr<MacroCondition> *);
private slots:

View File

@@ -20,7 +20,7 @@ class MacroConditionFactory {
public:
MacroConditionFactory() = delete;
EXPORT static bool Register(const std::string &, MacroConditionInfo);
static bool Deregister(const std::string &);
EXPORT static bool Deregister(const std::string &);
static std::shared_ptr<MacroCondition> Create(const std::string &,
Macro *m);
static QWidget *CreateWidget(const std::string &id, QWidget *parent,

View File

@@ -538,11 +538,7 @@ void MacroConditionMacroEdit::UpdateEntryData()
void MacroConditionMacroEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_macro = text;
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
emit HeaderInfoChanged(
@@ -551,21 +547,13 @@ void MacroConditionMacroEdit::MacroChanged(const QString &text)
void MacroConditionMacroEdit::CountChanged(const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_count = value;
}
void MacroConditionMacroEdit::CountConditionChanged(int cond)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_counterCondition =
static_cast<MacroConditionMacro::CounterCondition>(cond);
}
@@ -590,11 +578,7 @@ void MacroConditionMacroEdit::MacroRemove(const QString &)
void MacroConditionMacroEdit::TypeChanged(int type)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->SetType(static_cast<MacroConditionMacro::Type>(type));
SetupWidgets();
}
@@ -640,11 +624,7 @@ void MacroConditionMacroEdit::UpdatePaused()
void MacroConditionMacroEdit::MultiStateConditionChanged(int cond)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_multiSateCondition =
static_cast<MacroConditionMacro::MultiStateCondition>(cond);
}
@@ -652,21 +632,13 @@ void MacroConditionMacroEdit::MultiStateConditionChanged(int cond)
void MacroConditionMacroEdit::MultiStateCountChanged(
const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_multiSateCount = value;
}
void MacroConditionMacroEdit::Add(const std::string &name)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
MacroRef macro(name);
_entryData->_macros.push_back(macro);
adjustSize();
@@ -675,11 +647,7 @@ void MacroConditionMacroEdit::Add(const std::string &name)
void MacroConditionMacroEdit::Remove(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_macros.erase(std::next(_entryData->_macros.begin(), idx));
adjustSize();
updateGeometry();
@@ -687,12 +655,8 @@ void MacroConditionMacroEdit::Remove(int idx)
void MacroConditionMacroEdit::Replace(int idx, const std::string &name)
{
if (_loading || !_entryData) {
return;
}
GUARD_LOADING_AND_LOCK();
MacroRef macro(name);
auto lock = LockContext();
_entryData->_macros[idx] = macro;
adjustSize();
updateGeometry();
@@ -700,11 +664,7 @@ void MacroConditionMacroEdit::Replace(int idx, const std::string &name)
void MacroConditionMacroEdit::ActionIndexChanged(const IntVariable &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_actionIndex = value;
}

View File

@@ -9,7 +9,6 @@
#include <QPushButton>
#include <QHBoxLayout>
#include <QTimer>
#include <QListWidget>
namespace advss {

View File

@@ -27,6 +27,9 @@ bool MacroConditionQueue::CheckCondition()
return false;
}
SetTempVarValue("size", std::to_string(queue->Size()));
SetTempVarValue("running", queue->IsRunning());
switch (_condition) {
case Condition::STARTED:
return queue->IsRunning();
@@ -63,6 +66,18 @@ std::string MacroConditionQueue::GetShortDesc() const
return GetActionQueueName(_queue);
}
void MacroConditionQueue::SetupTempVars()
{
MacroCondition::SetupTempVars();
AddTempvar("size",
obs_module_text("AdvSceneSwitcher.tempVar.queue.size"));
AddTempvar(
"running",
obs_module_text("AdvSceneSwitcher.tempVar.queue.running"),
obs_module_text(
"AdvSceneSwitcher.tempVar.queue.running.description"));
}
static inline void populateQueueTypeSelection(QComboBox *list)
{
for (const auto &[_, name] : conditionTypes) {
@@ -100,11 +115,7 @@ MacroConditionQueueEdit::MacroConditionQueueEdit(
void MacroConditionQueueEdit::ConditionChanged(int condition)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_condition =
static_cast<MacroConditionQueue::Condition>(condition);
emit HeaderInfoChanged(
@@ -114,11 +125,7 @@ void MacroConditionQueueEdit::ConditionChanged(int condition)
void MacroConditionQueueEdit::QueueChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_queue = GetWeakActionQueueByQString(text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -126,11 +133,7 @@ void MacroConditionQueueEdit::QueueChanged(const QString &text)
void MacroConditionQueueEdit::SizeChanged(const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_size = value;
}
@@ -141,6 +144,7 @@ void MacroConditionQueueEdit::UpdateEntryData()
}
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_queues->SetActionQueue(_entryData->_queue);
_size->SetValue(_entryData->_size);
SetWidgetVisibility();
}

View File

@@ -32,6 +32,8 @@ public:
IntVariable _size = 1;
private:
void SetupTempVars();
static bool _registered;
static const std::string id;
};

View File

@@ -264,32 +264,19 @@ void MacroConditionTempVarEdit::UpdateEntryData()
void MacroConditionTempVarEdit::VariableChanged(const TempVariableRef &var)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_tempVar = var;
}
void MacroConditionTempVarEdit::Variable2Changed(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_variable2 = GetWeakVariableByQString(text);
}
void MacroConditionTempVarEdit::ConditionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_type =
static_cast<MacroConditionTempVar::Condition>(value);
SetWidgetVisibility();
@@ -297,11 +284,7 @@ void MacroConditionTempVarEdit::ConditionChanged(int value)
void MacroConditionTempVarEdit::StrValueChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_strValue = _strValue->toPlainText().toStdString();
adjustSize();
updateGeometry();
@@ -309,21 +292,13 @@ void MacroConditionTempVarEdit::StrValueChanged()
void MacroConditionTempVarEdit::NumValueChanged(double val)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_numValue = val;
}
void MacroConditionTempVarEdit::RegexChanged(const RegexConfig &conf)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_regex = conf;
adjustSize();
updateGeometry();

View File

@@ -236,11 +236,7 @@ void MacroConditionVariableEdit::UpdateEntryData()
void MacroConditionVariableEdit::VariableChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_variable = GetWeakVariableByQString(text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -248,22 +244,13 @@ void MacroConditionVariableEdit::VariableChanged(const QString &text)
void MacroConditionVariableEdit::Variable2Changed(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_variable2 = GetWeakVariableByQString(text);
}
void MacroConditionVariableEdit::ConditionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_type =
static_cast<MacroConditionVariable::Condition>(value);
SetWidgetVisibility();
@@ -271,11 +258,7 @@ void MacroConditionVariableEdit::ConditionChanged(int value)
void MacroConditionVariableEdit::StrValueChanged()
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_strValue = _strValue->toPlainText().toStdString();
adjustSize();
updateGeometry();
@@ -283,21 +266,13 @@ void MacroConditionVariableEdit::StrValueChanged()
void MacroConditionVariableEdit::NumValueChanged(double val)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_numValue = val;
}
void MacroConditionVariableEdit::RegexChanged(const RegexConfig &conf)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_regex = conf;
adjustSize();
updateGeometry();

1824
lib/macro/macro-edit.cpp Normal file

File diff suppressed because it is too large Load Diff

144
lib/macro/macro-edit.hpp Normal file
View File

@@ -0,0 +1,144 @@
#pragma once
#include "condition-logic.hpp"
#include <ui_macro-edit.h>
namespace advss {
class Macro;
class MacroSegment;
/*******************************************************************************
* Advanced Scene Switcher window
*******************************************************************************/
class MacroEdit : public QWidget {
Q_OBJECT
public:
MacroEdit(QWidget *parent, QStringList helpMsg = {});
void SetMacro(const std::shared_ptr<Macro> &);
void ClearSegmentWidgetCacheFor(Macro *) const;
void SetControlsDisabled(bool disable) const;
void HighlightAction(int idx, QColor color = QColor(Qt::green)) const;
void HighlightElseAction(int idx,
QColor color = QColor(Qt::green)) const;
void HighlightCondition(int idx,
QColor color = QColor(Qt::green)) const;
void ResetConditionHighlights();
void ResetActionHighlights();
void SetActionData(Macro &m) const;
void SetElseActionData(Macro &m) const;
void SetConditionData(Macro &m) const;
void SwapActions(Macro *m, int pos1, int pos2);
void SwapElseActions(Macro *m, int pos1, int pos2);
void SwapConditions(Macro *m, int pos1, int pos2);
void CopyMacroSegment();
void PasteMacroSegment();
bool IsEmpty() const;
void ShowAllMacroSections();
private slots:
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_conditionTop_clicked();
void on_conditionUp_clicked();
void on_conditionDown_clicked();
void on_conditionBottom_clicked();
void on_actionAdd_clicked();
void on_actionRemove_clicked();
void on_actionTop_clicked();
void on_actionUp_clicked();
void on_actionDown_clicked();
void on_actionBottom_clicked();
void on_toggleElseActions_clicked() const;
void on_elseActionAdd_clicked();
void on_elseActionRemove_clicked();
void on_elseActionTop_clicked();
void on_elseActionUp_clicked();
void on_elseActionDown_clicked();
void on_elseActionBottom_clicked();
void UpMacroSegmentHotkey();
void DownMacroSegmentHotkey();
void DeleteMacroSegmentHotkey();
void ShowMacroActionsContextMenu(const QPoint &);
void ShowMacroElseActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &);
void ExpandAllActions() const;
void ExpandAllElseActions() const;
void ExpandAllConditions() const;
void CollapseAllActions() const;
void CollapseAllElseActions() const;
void CollapseAllConditions() const;
void MinimizeActions() const;
void MaximizeActions() const;
void MinimizeElseActions() const;
void MaximizeElseActions() const;
void MinimizeConditions() const;
void MaximizeConditions() const;
void SetElseActionsStateToHidden() const;
void SetElseActionsStateToVisible() const;
void MacroActionSelectionChanged(int idx);
void MacroActionReorder(int to, int target);
void AddMacroAction(Macro *macro, int idx, const std::string &id,
obs_data_t *data);
void AddMacroAction(int idx);
void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx);
void MacroElseActionSelectionChanged(int idx);
void MacroElseActionReorder(int to, int target);
void AddMacroElseAction(Macro *macro, int idx, const std::string &id,
obs_data_t *data);
void AddMacroElseAction(int idx);
void RemoveMacroElseAction(int idx);
void MoveMacroElseActionUp(int idx);
void MoveMacroElseActionDown(int idx);
void MacroConditionSelectionChanged(int idx);
void MacroConditionReorder(int to, int target);
void AddMacroCondition(int idx);
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
obs_data_t *data, Logic::Type logic);
void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx);
void HighlightControls() const;
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString &newName);
void MacroSegmentOrderChanged();
void SegmentTempVarsChanged(MacroSegment *);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private:
enum class MacroSection { CONDITIONS, ACTIONS, ELSE_ACTIONS };
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void SetupMacroSegmentSelection(MacroSection type, int idx);
void
SetupContextMenu(const QPoint &pos,
const std::function<void(MacroEdit *, int)> &remove,
const std::function<void(MacroEdit *)> &expand,
const std::function<void(MacroEdit *)> &collapse,
const std::function<void(MacroEdit *)> &maximize,
const std::function<void(MacroEdit *)> &minimize,
MacroSegmentList *list);
void RunSegmentHighlightChecks();
bool ElseSectionIsVisible() const;
MacroSection lastInteracted = MacroSection::CONDITIONS;
int currentConditionIdx = -1;
int currentActionIdx = -1;
int currentElseActionIdx = -1;
std::shared_ptr<Macro> _currentMacro;
std::unique_ptr<Ui_MacroEdit> ui;
};
} // namespace advss

View File

@@ -67,9 +67,7 @@ void SetMacroSwitchedScene(bool value)
{
static bool setupDone = false;
if (!setupDone) {
// Will always be called with switcher lock already held
AddIntervalResetStep([]() { macroSceneSwitched = false; },
false);
AddIntervalResetStep([]() { macroSceneSwitched = false; });
setupDone = true;
}
macroSceneSwitched = value;
@@ -80,35 +78,43 @@ bool MacroSwitchedScene()
return macroSceneSwitched;
}
std::string GetMacroName(Macro *macro)
std::string GetMacroName(const Macro *macro)
{
return macro ? macro->Name() : "";
}
std::chrono::high_resolution_clock::time_point
LastMacroConditionCheckTime(Macro *macro)
LastMacroConditionCheckTime(const Macro *macro)
{
return macro ? macro->LastConditionCheckTime()
: std::chrono::high_resolution_clock::time_point{};
}
bool MacroIsStopped(Macro *macro)
bool MacroIsStopped(const Macro *macro)
{
return macro ? macro->GetStop() : true;
}
bool MacroIsPaused(Macro *macro)
bool MacroIsPaused(const Macro *macro)
{
return macro ? macro->Paused() : true;
}
bool MacroWasPausedSince(
Macro *macro,
const Macro *macro,
const std::chrono::high_resolution_clock::time_point &time)
{
return macro ? macro->WasPausedSince(time) : false;
}
bool MacroWasCheckedSinceLastStart(const Macro *macro)
{
if (!macro) {
return false;
}
return macro->LastConditionCheckTime().time_since_epoch().count() != 0;
}
void AddMacroHelperThread(Macro *macro, std::thread &&newThread)
{
if (!macro) {
@@ -138,7 +144,7 @@ void ResetMacroRunCount(Macro *macro)
macro->ResetRunCount();
}
bool IsValidMacroSegmentIndex(Macro *m, const int idx, bool isCondition)
bool IsValidMacroSegmentIndex(const Macro *m, const int idx, bool isCondition)
{
if (!m || idx < 0) {
return false;

View File

@@ -41,16 +41,17 @@ EXPORT std::atomic_int &GetShutdownConditionCount();
EXPORT void SetMacroSwitchedScene(bool value);
EXPORT bool MacroSwitchedScene();
EXPORT std::string GetMacroName(Macro *);
EXPORT std::string GetMacroName(const Macro *);
EXPORT std::chrono::high_resolution_clock::time_point
LastMacroConditionCheckTime(Macro *);
LastMacroConditionCheckTime(const Macro *);
EXPORT bool MacroIsStopped(Macro *);
EXPORT bool MacroIsPaused(Macro *);
EXPORT bool MacroIsStopped(const Macro *);
EXPORT bool MacroIsPaused(const Macro *);
EXPORT bool
MacroWasPausedSince(Macro *,
MacroWasPausedSince(const Macro *,
const std::chrono::high_resolution_clock::time_point &);
EXPORT bool MacroWasCheckedSinceLastStart(const Macro *);
EXPORT void AddMacroHelperThread(Macro *, std::thread &&);
@@ -67,6 +68,6 @@ EXPORT void InvalidateMacroTempVarValues();
EXPORT void ResetMacroConditionTimers(Macro *);
EXPORT void ResetMacroRunCount(Macro *);
bool IsValidMacroSegmentIndex(Macro *m, const int idx, bool isCondition);
bool IsValidMacroSegmentIndex(const Macro *m, const int idx, bool isCondition);
} // namespace advss

View File

@@ -181,15 +181,7 @@ void MacroInputEdit::SetInputVariablesAndValues(
_variables = inputs;
_values = values;
if ((size_t)_values.size() < _variables._inputVariables.size()) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
for (int i = 0;
_variables._inputVariables.size() - (size_t)_values.size();
i++) {
_values.push_back({});
}
#else
_values.resize(_variables._inputVariables.size());
#endif
}
SetupWidgets();
}
@@ -199,7 +191,7 @@ bool MacroInputEdit::HasInputsToSet() const
return !_variables._inputVariables.empty();
}
void HighligthMacroSettingsButton(bool enable);
void HighlightMacroSettingsButton(bool enable);
void MacroInputEdit::SetupWidgets()
{

View File

@@ -10,11 +10,12 @@ MacroList::MacroList(QWidget *parent, bool allowDuplicates, bool reorder)
: ListEditor(parent, reorder),
_allowDuplicates(allowDuplicates)
{
QWidget::connect(window(),
QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroRenamed(const QString &, const QString &)),
this,
SLOT(MacroRename(const QString &, const QString &)));
QWidget::connect(window(), SIGNAL(MacroRemoved(const QString &)), this,
QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
UpdateListSize();
}

View File

@@ -11,10 +11,14 @@ namespace advss {
MacroRunButton::MacroRunButton(QWidget *parent) : QPushButton(parent)
{
installEventFilter(this);
auto parentWindow = window();
if (parentWindow) {
parentWindow->installEventFilter(this);
}
// GetSettingsWindow() might still return null while this constructor is called
QTimer::singleShot(0, this, [this]() {
auto parentWindow = GetSettingsWindow();
if (parentWindow) {
parentWindow->installEventFilter(this);
}
});
setToolTip(obs_module_text("AdvSceneSwitcher.macroTab.run.tooltip"));

View File

@@ -13,7 +13,7 @@ struct MacroSegmentCopyInfo {
};
static MacroSegmentCopyInfo copyInfo;
void AdvSceneSwitcher::CopyMacroSegment()
void MacroEdit::CopyMacroSegment()
{
copyInfo.segment.reset();
copyInfo.type = MacroSegmentCopyInfo::Type::NONE;
@@ -23,7 +23,7 @@ void AdvSceneSwitcher::CopyMacroSegment()
return;
}
auto macro = GetSelectedMacro();
auto macro = _currentMacro;
if (!macro) {
return;
}
@@ -43,13 +43,13 @@ void AdvSceneSwitcher::CopyMacroSegment()
}
}
void AdvSceneSwitcher::PasteMacroSegment()
void MacroEdit::PasteMacroSegment()
{
if (copyInfo.type == MacroSegmentCopyInfo::Type::NONE) {
return;
}
auto macro = GetSelectedMacro();
auto macro = _currentMacro;
if (!macro || !copyInfo.segment) {
return;
}
@@ -113,14 +113,14 @@ void SetCopySegmentTargetActionType(bool setToElseAction)
}
}
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
void SetupSegmentCopyPasteShortcutHandlers(MacroEdit *edit)
{
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);
QWidget::connect(copyShortcut, &QShortcut::activated, window,
&AdvSceneSwitcher::CopyMacroSegment);
auto pasteShortcut = new QShortcut(QKeySequence("Ctrl+V"), window);
QWidget::connect(pasteShortcut, &QShortcut::activated, window,
&AdvSceneSwitcher::PasteMacroSegment);
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), edit);
QWidget::connect(copyShortcut, &QShortcut::activated, edit,
&MacroEdit::CopyMacroSegment);
auto pasteShortcut = new QShortcut(QKeySequence("Ctrl+V"), edit);
QWidget::connect(pasteShortcut, &QShortcut::activated, edit,
&MacroEdit::PasteMacroSegment);
}
} // namespace advss

View File

@@ -1,10 +1,10 @@
namespace advss {
class AdvSceneSwitcher;
class MacroEdit;
bool MacroSegmentIsInClipboard();
bool MacroActionIsInClipboard();
void SetCopySegmentTargetActionType(bool setToElseAction);
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
void SetupSegmentCopyPasteShortcutHandlers(MacroEdit *edit);
} // namespace advss

View File

@@ -13,36 +13,62 @@
namespace advss {
bool MacroSegmentList::_useCache = true;
MacroSegmentList::MacroSegmentList(QWidget *parent)
: QScrollArea(parent),
_stackedWidget(new QStackedWidget(this)),
_layout(new QVBoxLayout),
_contentLayout(new QVBoxLayout),
_helpMsg(new QLabel)
_helpMsg(new QLabel(this))
{
_helpMsg->setWordWrap(true);
_helpMsg->setAlignment(Qt::AlignCenter);
auto helpWidget = new QWidget(this);
auto helpLayout = new QVBoxLayout(helpWidget);
helpLayout->addWidget(_helpMsg);
helpLayout->setAlignment(Qt::AlignCenter);
auto contentWidget = new QWidget(this);
contentWidget->setLayout(_contentLayout);
_contentLayout->setSpacing(0);
auto helperLayout = new QGridLayout();
helperLayout->addWidget(_helpMsg, 0, 0,
Qt::AlignHCenter | Qt::AlignVCenter);
helperLayout->addLayout(_contentLayout, 0, 0);
helperLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
_layout->addLayout(helperLayout, 10);
_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
QSizePolicy::Expanding));
auto wrapper = new QWidget;
wrapper->setLayout(_layout);
setWidget(wrapper);
auto contentWrapper = new QWidget(this);
auto wrapperLayout = new QVBoxLayout(contentWrapper);
wrapperLayout->setContentsMargins(0, 0, 0, 0);
wrapperLayout->addWidget(contentWidget);
// Move macro segments to the top of the list
wrapperLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
QSizePolicy::Expanding));
_stackedWidget->addWidget(helpWidget);
_stackedWidget->addWidget(contentWrapper);
setWidget(_stackedWidget);
setWidgetResizable(true);
setAcceptDrops(true);
SetHelpMsgVisible(true);
connect(verticalScrollBar(), &QScrollBar::valueChanged, this,
[this]() { SetupVisibleMacroSegmentWidgets(); });
}
static void clearWidgetVector(const std::vector<QWidget *> &widgets)
{
for (auto widget : widgets) {
widget->deleteLater();
}
};
MacroSegmentList::~MacroSegmentList()
{
if (_autoScrollThread.joinable()) {
_autoScroll = false;
_autoScrollThread.join();
}
ClearWidgetCache();
}
static bool posIsInScrollbar(const QScrollBar *scrollbar, const QPoint &pos)
@@ -53,7 +79,7 @@ static bool posIsInScrollbar(const QScrollBar *scrollbar, const QPoint &pos)
if (!scrollbar->isVisible()) {
return false;
}
const auto geo = scrollbar->geometry();
const auto &geo = scrollbar->geometry();
const auto globalGeo = QRect(scrollbar->mapToGlobal(geo.topLeft()),
scrollbar->mapToGlobal(geo.bottomRight()));
return globalGeo.contains(pos);
@@ -93,31 +119,102 @@ void MacroSegmentList::SetHelpMsg(const QString &msg) const
_helpMsg->setText(msg);
}
void MacroSegmentList::SetHelpMsgVisible(bool visible) const
void MacroSegmentList::SetHelpMsgVisible(bool visible)
{
_helpMsg->setVisible(visible);
_stackedWidget->setCurrentIndex(visible ? 0 : 1);
adjustSize();
updateGeometry();
}
void MacroSegmentList::Insert(int idx, MacroSegmentEdit *widget)
void MacroSegmentList::Insert(int idx, QWidget *widget)
{
widget->installEventFilter(this);
_contentLayout->insertWidget(idx, widget);
SetupVisibleMacroSegmentWidgets();
SetHelpMsgVisible(false);
adjustSize();
updateGeometry();
}
void MacroSegmentList::Add(QWidget *widget)
{
widget->installEventFilter(this);
_contentLayout->addWidget(widget);
Insert(_contentLayout->count(), widget);
}
void MacroSegmentList::Remove(int idx) const
void MacroSegmentList::Remove(int idx)
{
DeleteLayoutItemWidget(_contentLayout->takeAt(idx));
adjustSize();
updateGeometry();
if (IsEmpty()) {
SetHelpMsgVisible(true);
}
}
void MacroSegmentList::Clear(int idx) const
void MacroSegmentList::Clear(int idx)
{
ClearLayout(_contentLayout, idx);
adjustSize();
updateGeometry();
SetHelpMsgVisible(true);
}
void MacroSegmentList::SetCachingEnabled(bool enable)
{
_useCache = enable;
}
void MacroSegmentList::CacheCurrentWidgetsFor(const Macro *macro)
{
if (!_useCache) {
return;
}
std::vector<QWidget *> result;
int idx = 0;
QLayoutItem *item;
while ((item = _contentLayout->takeAt(idx))) {
if (!item || !item->widget()) {
continue;
}
auto widget = item->widget();
widget->hide();
result.emplace_back(widget);
}
_widgetCache[macro] = result;
}
bool MacroSegmentList::PopulateWidgetsFromCache(const Macro *macro)
{
if (!_useCache) {
return false;
}
auto it = _widgetCache.find(macro);
if (it == _widgetCache.end()) {
return false;
}
for (auto widget : it->second) {
_contentLayout->addWidget(widget);
widget->show();
}
adjustSize();
updateGeometry();
return true;
}
void MacroSegmentList::ClearWidgetsFromCacheFor(const Macro *macro)
{
auto it = _widgetCache.find(macro);
if (it == _widgetCache.end()) {
return;
}
clearWidgetVector(it->second);
_widgetCache.erase(it);
}
void MacroSegmentList::Highlight(int idx, QColor color)
@@ -178,11 +275,7 @@ void MacroSegmentList::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton ||
event->button() == Qt::RightButton) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragPosition = GetDragIndex(event->globalPos());
#else
_dragPosition = GetDragIndex(event->globalPosition().toPoint());
#endif
emit SelectionChanged(_dragPosition);
} else {
_dragPosition = -1;
@@ -269,6 +362,13 @@ void MacroSegmentList::HideLastDropLine()
_dropLineIdx = -1;
}
void MacroSegmentList::ClearWidgetCache()
{
for (const auto &[_, widgets] : _widgetCache) {
clearWidgetVector(widgets);
}
}
static bool isInUpperHalfOf(const QPoint &pos, const QRect &rect)
{
return QRect(rect.topLeft(),
@@ -300,11 +400,7 @@ void MacroSegmentList::dragMoveEvent(QDragMoveEvent *event)
return;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragCursorPos = (mapToGlobal(event->pos()));
#else
_dragCursorPos = (mapToGlobal(event->position().toPoint()));
#endif
CheckDropLine(_dragCursorPos);
}
@@ -330,6 +426,55 @@ QRect MacroSegmentList::GetContentItemRectWithPadding(int idx) const
return rect;
}
void MacroSegmentList::SetVisibilityCheckEnable(bool enable)
{
_checkVisibility = enable;
if (_checkVisibility) {
QTimer::singleShot(0, this, [this]() {
SetupVisibleMacroSegmentWidgets();
});
}
}
bool MacroSegmentList::IsEmpty() const
{
return _contentLayout->count() == 0;
}
QSize MacroSegmentList::minimumSizeHint() const
{
return _stackedWidget->currentWidget()->minimumSizeHint();
}
QSize MacroSegmentList::sizeHint() const
{
const auto contentSize = _stackedWidget->currentWidget()->sizeHint();
const auto hint =
QSize(width(), contentSize.height() + 2 * frameWidth());
return hint;
}
void MacroSegmentList::SetupVisibleMacroSegmentWidgets()
{
if (!_checkVisibility) {
return;
}
const auto viewportRect = viewport()->rect();
for (auto segment : widget()->findChildren<MacroSegmentEdit *>()) {
const auto pos = segment->mapTo(viewport(), QPoint(0, 0));
const QRect rect(pos, segment->size());
if (!viewportRect.intersects(rect)) {
continue;
}
segment->SetupWidgets();
}
updateGeometry();
}
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos) const
{
int idx = -1;
@@ -450,16 +595,6 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
{
HideLastDropLine();
auto widget = qobject_cast<QWidget *>(event->source());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (widget && !widget->geometry().contains(event->pos()) &&
widgetIsInLayout(widget, _contentLayout)) {
int dropPosition = GetDropIndex(mapToGlobal(event->pos()));
if (dropPosition == -1) {
return;
}
emit Reorder(dropPosition, _dragPosition);
}
#else
if (widget &&
!widget->geometry().contains(event->position().toPoint()) &&
widgetIsInLayout(widget, _contentLayout)) {
@@ -470,8 +605,13 @@ void MacroSegmentList::dropEvent(QDropEvent *event)
}
emit Reorder(dropPosition, _dragPosition);
}
#endif
_dragPosition = -1;
}
void MacroSegmentList::resizeEvent(QResizeEvent *event)
{
QScrollArea::resizeEvent(event);
SetupVisibleMacroSegmentWidgets();
}
} // namespace advss

View File

@@ -1,56 +1,80 @@
#pragma once
#include "macro-segment.hpp"
#include <QWidget>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QLabel>
#include <thread>
#include <QLabel>
#include <QScrollArea>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QWidget>
namespace advss {
class Macro;
class MacroSegmentList : public QScrollArea {
Q_OBJECT
public:
MacroSegmentList(QWidget *parent = nullptr);
virtual ~MacroSegmentList();
~MacroSegmentList();
void SetHelpMsg(const QString &msg) const;
void SetHelpMsgVisible(bool visible) const;
void SetHelpMsgVisible(bool visible);
MacroSegmentEdit *WidgetAt(int idx) const;
MacroSegmentEdit *WidgetAt(const QPoint &) const;
int IndexAt(const QPoint &) const;
void Insert(int idx, MacroSegmentEdit *widget);
void Insert(int idx, QWidget *widget);
void Add(QWidget *widget);
void Remove(int idx) const;
void Clear(int idx = 0) const; // Clear all elements >= idx
void Remove(int idx);
void Clear(int idx = 0); // Clear all elements >= idx
bool IsEmpty() const;
static void SetCachingEnabled(bool enable);
void CacheCurrentWidgetsFor(const Macro *);
bool PopulateWidgetsFromCache(const Macro *);
void ClearWidgetsFromCacheFor(const Macro *);
void SetVisibilityCheckEnable(bool enable);
void Highlight(int idx, QColor color = QColor(Qt::green));
void SetCollapsed(bool) const;
void SetSelection(int idx) const;
QVBoxLayout *ContentLayout() const { return _contentLayout; }
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
signals:
void SelectionChanged(int idx);
void Reorder(int source, int target);
protected:
bool eventFilter(QObject *object, QEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
bool eventFilter(QObject *object, QEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private:
void SetupVisibleMacroSegmentWidgets();
void ClearWidgetCache();
int GetSegmentIndexFromPos(const QPoint &) const;
bool IsInListArea(const QPoint &) const;
QRect GetContentItemRectWithPadding(int idx) const;
int GetDragIndex(const QPoint &) const;
int GetDropIndex(const QPoint &) const;
void CheckScroll() const;
void CheckDropLine(const QPoint &);
bool IsInListArea(const QPoint &) const;
QRect GetContentItemRectWithPadding(int idx) const;
void HideLastDropLine();
int _dragPosition = -1;
@@ -59,9 +83,15 @@ private:
std::thread _autoScrollThread;
std::atomic_bool _autoScroll{false};
QStackedWidget *_stackedWidget;
QVBoxLayout *_layout;
QVBoxLayout *_contentLayout;
QLabel *_helpMsg;
bool _checkVisibility = true;
static bool _useCache;
std::unordered_map<const Macro *, std::vector<QWidget *>> _widgetCache;
};
} // namespace advss

View File

@@ -21,7 +21,7 @@ MacroSegmentSelection::MacroSegmentSelection(QWidget *parent, Type type,
{
_index->setMinimum(0);
_index->setMaximum(99);
_index->specialValueText("-");
_index->setSpecialValueText("-");
if (!allowVariables) {
_index->DisableVariableSelection();
}
@@ -31,7 +31,8 @@ MacroSegmentSelection::MacroSegmentSelection(QWidget *parent, Type type,
_index,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(IndexChanged(const NumberVariable<int> &)));
QWidget::connect(window(), SIGNAL(MacroSegmentOrderChanged()), this,
QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroSegmentOrderChanged()), this,
SLOT(MacroSegmentOrderChanged()));
auto layout = new QHBoxLayout;

View File

@@ -6,6 +6,7 @@
#include <QApplication>
#include <QEvent>
#include <QGraphicsOpacityEffect>
#include <QLabel>
#include <QMouseEvent>
#include <QScrollBar>
@@ -24,24 +25,24 @@ bool MacroSegment::Save(obs_data_t *obj) const
obs_data_set_bool(data, "collapsed", _collapsed);
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
obs_data_set_string(data, "customLabel", _customLabel.c_str());
obs_data_set_bool(data, "enabled", _enabled);
obs_data_set_int(data, "version", 1);
obs_data_set_obj(obj, "segmentSettings", data);
return true;
}
bool MacroSegment::Load(obs_data_t *obj)
{
OBSDataAutoRelease data = obs_data_get_obj(obj, "segmentSettings");
_collapsed = obs_data_get_bool(data, "collapsed");
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
_customLabel = obs_data_get_string(data, "customLabel");
obs_data_set_default_bool(data, "enabled", true);
_enabled = obs_data_get_bool(data, "enabled");
// TODO: remove this fallback at some point
if (obs_data_has_user_value(obj, "segmentSettings")) {
OBSDataAutoRelease data =
obs_data_get_obj(obj, "segmentSettings");
_collapsed = obs_data_get_bool(data, "collapsed");
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
_customLabel = obs_data_get_string(data, "customLabel");
} else {
_collapsed = obs_data_get_bool(obj, "collapsed");
_useCustomLabel = false;
_customLabel = obs_module_text(
"AdvSceneSwitcher.macroTab.segment.defaultCustomLabel");
if (!obs_data_has_user_value(data, "version")) {
_enabled = obs_data_get_bool(obj, "enabled");
}
ClearAvailableTempvars();
@@ -73,6 +74,16 @@ bool MacroSegment::GetHighlightAndReset()
return false;
}
void MacroSegment::SetEnabled(bool value)
{
_enabled = value;
}
bool MacroSegment::Enabled() const
{
return _enabled;
}
std::string MacroSegment::GetVariableValue() const
{
if (_supportsVariableValue) {
@@ -96,7 +107,7 @@ void MacroSegment::SetupTempVars()
void MacroSegment::ClearAvailableTempvars()
{
_tempVariables.clear();
NotifyUIAboutTempVarChange();
NotifyUIAboutTempVarChange(this);
}
static std::shared_ptr<MacroSegment>
@@ -138,7 +149,7 @@ void MacroSegment::AddTempvar(const std::string &id, const std::string &name,
TempVariable var(id, name, description, sharedSegment);
_tempVariables.emplace_back(std::move(var));
NotifyUIAboutTempVarChange();
NotifyUIAboutTempVarChange(this);
}
void MacroSegment::SetTempVarValue(const std::string &id,
@@ -246,20 +257,25 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
QWidget::connect(_section, &Section::Collapsed, this,
&MacroSegmentEdit::Collapsed);
// Macro signals
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroAdded(const QString &)), this,
SIGNAL(MacroAdded(const QString &)));
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
QWidget::connect(GetSettingsWindow(),
SIGNAL(MacroRemoved(const QString &)), this,
SIGNAL(MacroRemoved(const QString &)));
QWidget::connect(
parent, SIGNAL(MacroRenamed(const QString &, const QString &)),
this, SIGNAL(MacroRenamed(const QString &, const QString &)));
GetSettingsWindow(),
SIGNAL(MacroRenamed(const QString &, const QString &)), this,
SIGNAL(MacroRenamed(const QString &, const QString &)));
// Scene group signals
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
QWidget::connect(GetSettingsWindow(),
SIGNAL(SceneGroupAdded(const QString &)), this,
SIGNAL(SceneGroupAdded(const QString &)));
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
this, SIGNAL(SceneGroupRemoved(const QString &)));
QWidget::connect(GetSettingsWindow(),
SIGNAL(SceneGroupRemoved(const QString &)), this,
SIGNAL(SceneGroupRemoved(const QString &)));
QWidget::connect(
parent,
GetSettingsWindow(),
SIGNAL(SceneGroupRenamed(const QString &, const QString)), this,
SIGNAL(SceneGroupRenamed(const QString &, const QString)));
@@ -294,13 +310,8 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
QMouseEvent *newEvent = new QMouseEvent(
mouseEvent->type(),
_headerInfo->mapTo(this, mouseEvent->pos()),
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
mouseEvent->globalPos(),
#else
mouseEvent->globalPosition(),
#endif
mouseEvent->button(), mouseEvent->buttons(),
mouseEvent->modifiers());
mouseEvent->globalPosition(), mouseEvent->button(),
mouseEvent->buttons(), mouseEvent->modifiers());
QApplication::sendEvent(parentWidget(), newEvent);
}
return QWidget::eventFilter(obj, ev);
@@ -318,13 +329,29 @@ void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
_headerInfo->setText(text);
}
void MacroSegmentEdit::Collapsed(bool collapsed)
void MacroSegmentEdit::Collapsed(bool collapsed) const
{
if (Data()) {
Data()->SetCollapsed(collapsed);
}
}
void MacroSegmentEdit::SetDisableEffect(bool value)
{
if (value) {
auto effect = new QGraphicsOpacityEffect(this);
effect->setOpacity(0.5);
_section->setGraphicsEffect(effect);
} else {
_section->setGraphicsEffect(nullptr);
}
}
void MacroSegmentEdit::SetEnableAppearance(bool value)
{
SetDisableEffect(!value);
}
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
{
QList<QWidget *> widgets = this->findChildren<QWidget *>();

View File

@@ -19,7 +19,7 @@ namespace advss {
class Macro;
class EXPORT MacroSegment {
class EXPORT MacroSegment : public Lockable {
public:
MacroSegment(Macro *m, bool supportsVariableValue);
virtual ~MacroSegment() = default;
@@ -39,6 +39,8 @@ public:
virtual std::string GetId() const = 0;
void EnableHighlight();
bool GetHighlightAndReset();
void SetEnabled(bool);
bool Enabled() const;
virtual std::string GetVariableValue() const;
protected:
@@ -46,13 +48,22 @@ protected:
friend void IncrementVariableRef(MacroSegment *);
friend void DecrementVariableRef(MacroSegment *);
void SetVariableValue(const std::string &value);
bool IsReferencedInVars() { return _variableRefs != 0; }
bool IsReferencedInVars() const { return _variableRefs != 0; }
virtual void SetupTempVars();
void AddTempvar(const std::string &id, const std::string &name,
const std::string &description = "");
void SetTempVarValue(const std::string &id, const std::string &value);
template<typename T, typename = std::enable_if_t<
std::is_same<std::decay_t<T>, bool>::value>>
void SetTempVarValue(const std::string &id, T value)
{
SetTempVarValue(id, value ? std::string("true")
: std::string("false"));
}
private:
void ClearAvailableTempvars();
std::optional<const TempVariable>
@@ -66,6 +77,7 @@ private:
// UI helper
bool _highlight = false;
bool _collapsed = false;
bool _enabled = true;
// Custom header labels
bool _useCustomLabel = false;
@@ -94,12 +106,13 @@ public:
void SetCollapsed(bool collapsed);
void SetSelected(bool);
virtual std::shared_ptr<MacroSegment> Data() const = 0;
virtual void SetupWidgets(bool basicSetup = false) = 0;
public slots:
void HeaderInfoChanged(const QString &);
protected slots:
void Collapsed(bool);
void Collapsed(bool) const;
signals:
void MacroAdded(const QString &name);
void MacroRemoved(const QString &name);
@@ -109,12 +122,16 @@ signals:
void SceneGroupRenamed(const QString &oldName, const QString newName);
protected:
void SetDisableEffect(bool);
void SetEnableAppearance(bool);
bool eventFilter(QObject *obj, QEvent *ev) override;
Section *_section;
QLabel *_headerInfo;
QWidget *_frame;
QVBoxLayout *_contentLayout;
bool _allWidgetsAreSetup = false;
private:
enum class DropLineState {

View File

@@ -49,7 +49,7 @@ void MacroSelection::SetCurrentMacro(const MacroRef &macro)
void MacroSelection::HideSelectedMacro()
{
auto ssWindow = static_cast<AdvSceneSwitcher *>(window());
auto ssWindow = static_cast<AdvSceneSwitcher *>(GetSettingsWindow());
if (!ssWindow) {
return;
}
@@ -76,12 +76,16 @@ void MacroSelection::ShowAllMacros()
void MacroSelection::MacroRemove(const QString &name)
{
const bool currentSelectionWasRemoved = name == currentText();
int idx = findText(name);
if (idx == -1) {
return;
}
removeItem(idx);
setCurrentIndex(-1);
if (currentSelectionWasRemoved) {
setCurrentIndex(-1);
}
}
void MacroSelection::MacroRename(const QString &oldName, const QString &newName)

View File

@@ -19,6 +19,8 @@ void GlobalMacroSettings::Save(obs_data_t *obj) const
obs_data_set_bool(data, "highlightExecuted", _highlightExecuted);
obs_data_set_bool(data, "highlightConditions", _highlightConditions);
obs_data_set_bool(data, "highlightActions", _highlightActions);
obs_data_set_bool(data, "newMacroCheckInParallel",
_newMacroCheckInParallel);
obs_data_set_bool(data, "newMacroRegisterHotkey",
_newMacroRegisterHotkeys);
obs_data_set_bool(data, "newMacroUseShortCircuitEvaluation",
@@ -41,6 +43,8 @@ void GlobalMacroSettings::Load(obs_data_t *obj)
_highlightExecuted = obs_data_get_bool(data, "highlightExecuted");
_highlightConditions = obs_data_get_bool(data, "highlightConditions");
_highlightActions = obs_data_get_bool(data, "highlightActions");
_newMacroCheckInParallel =
obs_data_get_bool(data, "newMacroCheckInParallel");
_newMacroRegisterHotkeys =
obs_data_get_bool(data, "newMacroRegisterHotkey");
_newMacroUseShortCircuitEvaluation =
@@ -62,12 +66,16 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
_highlightActions(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
_newMacroCheckInParallel(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroCheckInParallel"))),
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
_newMacroUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation"))),
_saveSettingsOnMacroChange(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.saveSettingsOnMacroChange"))),
_currentCheckInParallel(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentCheckInParallel"))),
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentRegisterHotkeys"))),
_currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
@@ -107,6 +115,11 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
setWindowModality(Qt::WindowModality::WindowModal);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
_newMacroCheckInParallel->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.checkInParallel.tooltip"));
_currentCheckInParallel->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.checkInParallel.tooltip"));
_newMacroUseShortCircuitEvaluation->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
_saveSettingsOnMacroChange->setToolTip(obs_module_text(
@@ -146,6 +159,8 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
auto generalLayout = new QVBoxLayout;
generalLayout->addWidget(_saveSettingsOnMacroChange);
generalLayout->addWidget(_currentCheckInParallel);
generalLayout->addWidget(_newMacroCheckInParallel);
generalLayout->addWidget(_currentSkipOnStartup);
generalLayout->addWidget(_currentStopActionsIfNotDone);
generalLayout->addWidget(_currentUseShortCircuitEvaluation);
@@ -273,6 +288,7 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
_highlightExecutedMacros->setChecked(settings._highlightExecuted);
_highlightConditions->setChecked(settings._highlightConditions);
_highlightActions->setChecked(settings._highlightActions);
_newMacroCheckInParallel->setChecked(settings._newMacroCheckInParallel);
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
_newMacroUseShortCircuitEvaluation->setChecked(
settings._newMacroUseShortCircuitEvaluation);
@@ -280,20 +296,23 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
settings._saveSettingsOnMacroChange);
if (!macro || macro->IsGroup()) {
hotkeyOptions->hide();
// General group
_currentSkipOnStartup->hide();
_currentStopActionsIfNotDone->hide();
_currentUseShortCircuitEvaluation->hide();
_currentCheckInParallel->hide();
SetLayoutVisible(customConditionIntervalLayout, false);
SetLayoutVisible(pauseStateSaveBehavorLayout, false);
// Hotkey group
_currentMacroRegisterHotkeys->hide();
inputOptions->hide();
_dockOptions->hide();
return;
}
_currentCheckInParallel->setChecked(macro->CheckInParallel());
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
_currentUseShortCircuitEvaluation->setChecked(
macro->ShortCircuitEvaluationEnabled());
@@ -431,6 +450,8 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
userInput._highlightConditions =
dialog._highlightConditions->isChecked();
userInput._highlightActions = dialog._highlightActions->isChecked();
userInput._newMacroCheckInParallel =
dialog._newMacroCheckInParallel->isChecked();
userInput._newMacroRegisterHotkeys =
dialog._newMacroRegisterHotkeys->isChecked();
userInput._newMacroUseShortCircuitEvaluation =
@@ -441,6 +462,7 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
return true;
}
macro->SetCheckInParallel(dialog._currentCheckInParallel->isChecked());
macro->EnablePauseHotkeys(
dialog._currentMacroRegisterHotkeys->isChecked());
macro->SetShortCircuitEvaluation(
@@ -456,7 +478,14 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
macro->SetStopActionsIfNotDone(
dialog._currentStopActionsIfNotDone->isChecked());
macro->EnableDock(dialog._currentMacroRegisterDock->isChecked());
// Only apply "on change" to avoid recreation of the dock widget
const bool enableDock = dialog._currentMacroRegisterDock->isChecked();
if (macro->DockEnabled() != enableDock) {
macro->EnableDock(
dialog._currentMacroRegisterDock->isChecked());
}
macro->SetDockHasRunButton(
dialog._currentMacroDockAddRunButton->isChecked());
macro->SetDockHasPauseButton(

View File

@@ -20,9 +20,10 @@ public:
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
bool _highlightExecuted = false;
bool _highlightConditions = false;
bool _highlightActions = false;
bool _highlightExecuted = true;
bool _highlightConditions = true;
bool _highlightActions = true;
bool _newMacroCheckInParallel = false;
bool _newMacroRegisterHotkeys = true;
bool _newMacroUseShortCircuitEvaluation = false;
bool _saveSettingsOnMacroChange = true;
@@ -52,10 +53,12 @@ private:
QCheckBox *_highlightExecutedMacros;
QCheckBox *_highlightConditions;
QCheckBox *_highlightActions;
QCheckBox *_newMacroCheckInParallel;
QCheckBox *_newMacroRegisterHotkeys;
QCheckBox *_newMacroUseShortCircuitEvaluation;
QCheckBox *_saveSettingsOnMacroChange;
// Current macro specific settings
QCheckBox *_currentCheckInParallel;
QCheckBox *_currentMacroRegisterHotkeys;
QCheckBox *_currentUseShortCircuitEvaluation;
QCheckBox *_currentUseCustomConditionCheckInterval;

File diff suppressed because it is too large Load Diff

View File

@@ -178,6 +178,7 @@ void MacroTreeItem::Update(bool force)
QSizePolicy::Maximum);
_expand->setMaximumSize(10, 16);
_expand->setMinimumSize(10, 0);
_expand->setProperty("class", "checkbox-icon indicator-expand");
#ifdef __APPLE__
_expand->setAttribute(Qt::WA_LayoutUsesWidgetRect);
#endif
@@ -413,6 +414,20 @@ void MacroTreeModel::Add(std::shared_ptr<Macro> item)
QItemSelectionModel::Select);
}
void MacroTreeModel::MoveToBeginningOfGroup(std::shared_ptr<Macro> item,
std::shared_ptr<Macro> group)
{
auto it = std::find(_macros.begin(), _macros.end(), item);
assert(it != _macros.end());
std::shared_ptr<Macro> tmp = *it;
_macros.erase(it);
it = std::find(_macros.begin(), _macros.end(), group);
assert(it != _macros.end());
_macros.insert(std::next(it), tmp);
Reset(_macros);
}
void MacroTreeModel::Remove(std::shared_ptr<Macro> item)
{
auto lock = LockContext();
@@ -788,6 +803,13 @@ void MacroTree::Add(std::shared_ptr<Macro> item,
assert(GetModel()->IsInValidState());
}
void MacroTree::AddToGroup(std::shared_ptr<Macro> item,
std::shared_ptr<Macro> group) const
{
GetModel()->Add(item);
GetModel()->MoveToBeginningOfGroup(item, group);
}
std::shared_ptr<Macro> MacroTree::GetCurrentMacro() const
{
return GetModel()->GetCurrentMacro();
@@ -888,14 +910,7 @@ void MacroTree::dropEvent(QDropEvent *event)
QModelIndexList indices = selectedIndexes();
DropIndicatorPosition indicator = dropIndicatorPosition();
int row = indexAt(
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
event->position().toPoint()
#else
event->pos()
#endif
)
.row();
int row = indexAt(event->position().toPoint()).row();
bool emptyDrop = row == -1;
if (emptyDrop) {
@@ -1166,6 +1181,28 @@ bool MacroTree::SelectionEmpty() const
return selectedIndexes().empty();
}
bool MacroTree::GroupsExist() const
{
for (const auto &macro : GetMacros()) {
if (macro->IsGroup()) {
return true;
}
}
return false;
}
void MacroTree::ExpandGroup(std::shared_ptr<Macro> item) const
{
auto *mtm = GetModel();
mtm->ExpandGroup(item);
}
void MacroTree::CollapseGroup(std::shared_ptr<Macro> item) const
{
auto *mtm = GetModel();
mtm->CollapseGroup(item);
}
void MacroTree::MoveItemBefore(const std::shared_ptr<Macro> &item,
const std::shared_ptr<Macro> &after) const
{
@@ -1266,6 +1303,26 @@ void MacroTree::UngroupSelectedGroups()
assert(GetModel()->IsInValidState());
}
void MacroTree::ExpandAll()
{
for (const auto &macro : GetMacros()) {
if (!macro->IsGroup()) {
continue;
}
ExpandGroup(macro);
}
}
void MacroTree::CollapseAll()
{
for (const auto &macro : GetMacros()) {
if (!macro->IsGroup()) {
continue;
}
CollapseGroup(macro);
}
}
void MacroTree::SelectionChangedHelper(const QItemSelection &,
const QItemSelection &)
{
@@ -1307,10 +1364,10 @@ QSize MacroTreeDelegate::sizeHint(const QStyleOptionViewItem &option,
QWidget *item = tree->indexWidget(index);
if (!item) {
return (QSize(0, 0));
return QStyledItemDelegate::sizeHint(option, index);
}
return (QSize(option.widget->minimumWidth(), item->height()));
return QSize(item->sizeHint());
}
} // namespace advss

View File

@@ -91,6 +91,8 @@ private:
void MoveItemAfter(const std::shared_ptr<Macro> &item,
const std::shared_ptr<Macro> &after);
void Add(std::shared_ptr<Macro> item);
void MoveToBeginningOfGroup(std::shared_ptr<Macro> item,
std::shared_ptr<Macro>);
void Remove(std::shared_ptr<Macro> item);
std::shared_ptr<Macro> Neighbor(const std::shared_ptr<Macro> &m,
bool above) const;
@@ -126,6 +128,8 @@ public:
void Reset(std::deque<std::shared_ptr<Macro>> &, bool highlight);
void Add(std::shared_ptr<Macro> item,
std::shared_ptr<Macro> after = {}) const;
void AddToGroup(std::shared_ptr<Macro> item,
std::shared_ptr<Macro> group) const;
void Remove(std::shared_ptr<Macro> item) const;
void Up(std::shared_ptr<Macro> item) const;
void Down(std::shared_ptr<Macro> item) const;
@@ -135,10 +139,15 @@ public:
bool GroupedItemsSelected() const;
bool SingleItemSelected() const;
bool SelectionEmpty() const;
bool GroupsExist() const;
void ExpandGroup(std::shared_ptr<Macro> item) const;
void CollapseGroup(std::shared_ptr<Macro> item) const;
public slots:
void GroupSelectedItems();
void UngroupSelectedGroups();
void ExpandAll();
void CollapseAll();
void SelectionChangedHelper(const QItemSelection &,
const QItemSelection &);

View File

@@ -3,6 +3,7 @@
#include "macro-condition-factory.hpp"
#include "macro-dock.hpp"
#include "macro-helpers.hpp"
#include "macro-settings.hpp"
#include "plugin-state-helpers.hpp"
#include "splitter-helpers.hpp"
#include "sync-helpers.hpp"
@@ -76,15 +77,21 @@ namespace advss {
static std::deque<std::shared_ptr<Macro>> macros;
Macro::Macro(const std::string &name, const bool addHotkey,
const bool shortCircuitEvaluation)
Macro::Macro(const std::string &name)
{
SetName(name);
if (addHotkey) {
}
Macro::Macro(const std::string &name, const GlobalMacroSettings &settings)
: Macro(name)
{
if (settings._newMacroRegisterHotkeys) {
SetupHotkeys();
}
_registerHotkeys = addHotkey;
_useShortCircuitEvaluation = shortCircuitEvaluation;
_registerHotkeys = settings._newMacroRegisterHotkeys;
_checkInParallel = settings._newMacroCheckInParallel;
_useShortCircuitEvaluation =
settings._newMacroUseShortCircuitEvaluation;
}
Macro::~Macro()
@@ -104,7 +111,8 @@ std::shared_ptr<Macro>
Macro::CreateGroup(const std::string &name,
std::vector<std::shared_ptr<Macro>> &children)
{
auto group = std::make_shared<Macro>(name, false);
auto group = std::make_shared<Macro>(name);
group->_registerHotkeys = false;
for (auto &c : children) {
c->SetParent(group);
}
@@ -165,7 +173,10 @@ static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
static constexpr auto perfLogThreshold = 300ms;
const auto startTime = std::chrono::high_resolution_clock::now();
const bool conditionMatched = condition->CheckCondition();
bool conditionMatched = false;
condition->WithLock([&condition, &conditionMatched]() {
conditionMatched = condition->CheckCondition();
});
const auto endTime = std::chrono::high_resolution_clock::now();
const auto timeSpent = endTime - startTime;
@@ -244,14 +255,52 @@ bool Macro::CheckConditions(bool ignorePause)
return false;
}
_matched = false;
for (auto &condition : _conditions) {
if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
const auto checkConditionsTask =
[this,
ignorePause](const std::deque<std::shared_ptr<MacroCondition>>
&conditions) {
for (auto &condition : conditions) {
if (!condition) {
continue;
}
if (_paused && !ignorePause) {
vblog(LOG_INFO, "Macro %s is paused",
_name.c_str());
return false;
}
_matched = CheckConditionHelper(condition);
}
return _matched;
};
if (CheckInParallel()) {
if (!_conditionCheckFuture.valid()) {
_stop = false;
_matched = false;
_conditionCheckFuture = std::async(
std::launch::async,
[this, checkConditionsTask]() {
// Copy to avoid settings modifications
// causing issues
const auto conditionsCopy = _conditions;
checkConditionsTask(conditionsCopy);
});
return false;
}
_matched = CheckConditionHelper(condition);
if (_conditionCheckFuture.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready) {
vblog(LOG_INFO,
"Macro %s still waiting for condition check result",
_name.c_str());
return false;
}
_conditionCheckFuture.get();
} else {
_stop = false;
_matched = false;
_matched = checkConditionsTask(_conditions);
}
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
@@ -268,7 +317,9 @@ bool Macro::CheckConditions(bool ignorePause)
bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
{
if (!_done) {
if (_actionRunFuture.valid() &&
_actionRunFuture.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready) {
vblog(LOG_INFO, "Macro %s already running", _name.c_str());
if (!_stopActionsIfNotDone) {
@@ -286,14 +337,15 @@ bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
: std::bind(&Macro::RunElseActions, this,
std::placeholders::_1);
_stop = false;
_done = false;
bool ret = true;
if (_runInParallel || forceParallel) {
if (_backgroundThread.joinable()) {
_backgroundThread.join();
if (_actionRunFuture.valid()) {
_actionRunFuture.get();
}
_backgroundThread = std::thread(
[this, runFunc, ignorePause] { runFunc(ignorePause); });
_actionRunFuture = std::async(std::launch::async,
[this, runFunc, ignorePause] {
runFunc(ignorePause);
});
} else {
ret = runFunc(ignorePause);
}
@@ -331,6 +383,13 @@ bool Macro::ConditionsShouldBeChecked() const
bool Macro::ShouldRunActions() const
{
if (CheckInParallel() && _conditionCheckFuture.valid()) {
vblog(LOG_INFO,
"%s not ready to perform actions as condition check is still running",
_name.c_str());
return false;
}
const bool hasActionsToExecute =
!_paused && (_matched || _elseActions.size() > 0) &&
(!_performActionsOnChange || _conditionSateChanged);
@@ -383,11 +442,17 @@ bool Macro::RunActionsHelper(
bool actionsExecutedSuccessfully = true;
for (auto &action : actions) {
if (!action) {
continue;
}
if (action->Enabled()) {
action->LogAction();
bool actionResult = false;
action->WithLock([&action, &actionResult]() {
actionResult = action->PerformAction();
});
actionsExecutedSuccessfully =
actionsExecutedSuccessfully &&
action->PerformAction();
actionsExecutedSuccessfully && actionResult;
} else {
vblog(LOG_INFO, "skipping disabled action %s",
action->GetId().c_str());
@@ -400,7 +465,6 @@ bool Macro::RunActionsHelper(
action->EnableHighlight();
}
}
_done = true;
return actionsExecutedSuccessfully;
}
@@ -500,9 +564,35 @@ void Macro::Stop()
t.join();
}
}
if (_backgroundThread.joinable()) {
_backgroundThread.join();
if (_actionRunFuture.valid()) {
_actionRunFuture.get();
}
if (_conditionCheckFuture.valid()) {
_conditionCheckFuture.get();
}
}
void Macro::SetCheckInParallel(bool parallel)
{
_checkInParallel = parallel;
_conditionCheckFuture = {};
}
bool Macro::ParallelTasksCompleted() const
{
// A parallel action run might be triggered by RunInParallel() or the
// "Run Macro" button, so checking just for RunInParallel() will not
// suffice
if (!CheckInParallel() && !_actionRunFuture.valid()) {
return true;
}
if (_actionRunFuture.valid()) {
return false;
}
if (CheckInParallel() && _conditionCheckFuture.valid()) {
return false;
}
return true;
}
MacroInputVariables Macro::GetInputVariables() const
@@ -640,16 +730,31 @@ std::deque<std::shared_ptr<MacroCondition>> &Macro::Conditions()
return _conditions;
}
const std::deque<std::shared_ptr<MacroCondition>> &Macro::Conditions() const
{
return _conditions;
}
std::deque<std::shared_ptr<MacroAction>> &Macro::Actions()
{
return _actions;
}
const std::deque<std::shared_ptr<MacroAction>> &Macro::Actions() const
{
return _actions;
}
std::deque<std::shared_ptr<MacroAction>> &Macro::ElseActions()
{
return _elseActions;
}
const std::deque<std::shared_ptr<MacroAction>> &Macro::ElseActions() const
{
return _elseActions;
}
static void updateIndicesHelper(std::deque<std::shared_ptr<MacroSegment>> &list)
{
int idx = 0;
@@ -704,6 +809,7 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
static_cast<int>(_pauseSaveBehavior));
obs_data_set_bool(obj, "pause", _paused);
obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "checkConditionsInParallel", _checkInParallel);
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
@@ -789,6 +895,7 @@ bool Macro::Load(obs_data_t *obj)
break;
}
_runInParallel = obs_data_get_bool(obj, "parallel");
_checkInParallel = obs_data_get_bool(obj, "checkConditionsInParallel");
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
@@ -832,9 +939,12 @@ bool Macro::Load(obs_data_t *obj)
auto newEntry = MacroConditionFactory::Create(id, this);
if (newEntry) {
_conditions.emplace_back(newEntry);
auto c = _conditions.back().get();
c->Load(arrayObj);
c->ValidateLogicSelection(root, Name().c_str());
auto condition = _conditions.back().get();
condition->WithLock([&]() {
condition->Load(arrayObj);
condition->ValidateLogicSelection(
root, Name().c_str());
});
} else {
blog(LOG_WARNING,
"discarding condition entry with unknown id (%s) for macro %s",
@@ -847,12 +957,15 @@ bool Macro::Load(obs_data_t *obj)
OBSDataArrayAutoRelease actions = obs_data_get_array(obj, "actions");
count = obs_data_array_count(actions);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease array_obj = obs_data_array_item(actions, i);
std::string id = obs_data_get_string(array_obj, "id");
OBSDataAutoRelease arrayObj = obs_data_array_item(actions, i);
std::string id = obs_data_get_string(arrayObj, "id");
auto newEntry = MacroActionFactory::Create(id, this);
if (newEntry) {
_actions.emplace_back(newEntry);
_actions.back()->Load(array_obj);
auto action = _actions.back().get();
action->WithLock([action, &arrayObj]() {
action->Load(arrayObj);
});
} else {
blog(LOG_WARNING,
"discarding action entry with unknown id (%s) for macro %s",
@@ -865,13 +978,16 @@ bool Macro::Load(obs_data_t *obj)
obs_data_get_array(obj, "elseActions");
count = obs_data_array_count(elseActions);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease array_obj =
OBSDataAutoRelease arrayObj =
obs_data_array_item(elseActions, i);
std::string id = obs_data_get_string(array_obj, "id");
std::string id = obs_data_get_string(arrayObj, "id");
auto newEntry = MacroActionFactory::Create(id, this);
if (newEntry) {
_elseActions.emplace_back(newEntry);
_elseActions.back()->Load(array_obj);
auto action = _elseActions.back().get();
action->WithLock([action, &arrayObj]() {
action->Load(arrayObj);
});
} else {
blog(LOG_WARNING,
"discarding elseAction entry with unknown id (%s) for macro %s",
@@ -888,13 +1004,13 @@ bool Macro::Load(obs_data_t *obj)
bool Macro::PostLoad()
{
for (auto &c : _conditions) {
c->PostLoad();
c->WithLock([c]() { c->PostLoad(); });
}
for (auto &a : _actions) {
a->PostLoad();
a->WithLock([a]() { a->PostLoad(); });
}
for (auto &a : _elseActions) {
a->PostLoad();
a->WithLock([a]() { a->PostLoad(); });
}
return true;
}
@@ -1460,6 +1576,14 @@ std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
void InvalidateMacroTempVarValues()
{
for (const auto &m : macros) {
// Do not invalidate the temp vars set during condition checks
// or action executions running in parallel to the "main" macro
// loop, as otherwise access to the information stored in those
// variables might get lost while those checks or actions are
// still ongoing.
if (!m->ParallelTasksCompleted()) {
continue;
}
m->InvalidateTempVarValues();
}
}

View File

@@ -7,6 +7,7 @@
#include "variable-string.hpp"
#include "temp-variable.hpp"
#include <future>
#include <QString>
#include <QByteArray>
#include <string>
@@ -20,6 +21,7 @@
namespace advss {
class MacroDock;
class GlobalMacroSettings;
class Macro {
using TimePoint = std::chrono::high_resolution_clock::time_point;
@@ -27,8 +29,8 @@ class Macro {
public:
enum class PauseStateSaveBehavior { PERSIST, PAUSE, UNPAUSE };
Macro(const std::string &name = "", const bool addHotkey = false,
const bool shortCircuitEvaluation = false);
Macro(const std::string &name = "");
Macro(const std::string &name, const GlobalMacroSettings &settings);
~Macro();
std::string Name() const { return _name; }
@@ -77,6 +79,9 @@ public:
void AddHelperThread(std::thread &&);
void SetRunInParallel(bool parallel) { _runInParallel = parallel; }
bool RunInParallel() const { return _runInParallel; }
bool CheckInParallel() const { return _checkInParallel; }
void SetCheckInParallel(bool parallel);
bool ParallelTasksCompleted() const;
// Input variables
MacroInputVariables GetInputVariables() const;
@@ -90,8 +95,11 @@ public:
// Macro segments
std::deque<std::shared_ptr<MacroCondition>> &Conditions();
const std::deque<std::shared_ptr<MacroCondition>> &Conditions() const;
std::deque<std::shared_ptr<MacroAction>> &Actions();
const std::deque<std::shared_ptr<MacroAction>> &Actions() const;
std::deque<std::shared_ptr<MacroAction>> &ElseActions();
const std::deque<std::shared_ptr<MacroAction>> &ElseActions() const;
void UpdateActionIndices();
void UpdateElseActionIndices();
void UpdateConditionIndices();
@@ -182,11 +190,10 @@ private:
std::string _name = "";
bool _die = false;
bool _stop = false;
bool _done = true;
std::future<void> _actionRunFuture;
TimePoint _lastCheckTime{};
TimePoint _lastUnpauseTime{};
TimePoint _lastExecutionTime{};
std::thread _backgroundThread;
std::vector<std::thread> _helperThreads;
std::deque<std::shared_ptr<MacroCondition>> _conditions;
@@ -204,7 +211,9 @@ private:
bool _conditionSateChanged = false;
bool _runInParallel = false;
bool _checkInParallel = false;
bool _matched = false;
std::future<void> _conditionCheckFuture;
bool _lastMatched = false;
bool _performActionsOnChange = true;
bool _skipExecOnStart = false;

View File

@@ -5,6 +5,7 @@
#include "plugin-state-helpers.hpp"
#include "sync-helpers.hpp"
#include "tab-helpers.hpp"
#include "time-helpers.hpp"
#include "ui-helpers.hpp"
#include <QTimer>
@@ -39,8 +40,8 @@ ActionQueueTable *ActionQueueTable::Create()
void ActionQueueTable::Add()
{
auto newQueue = std::make_shared<ActionQueue>();
auto accepted =
ActionQueueSettingsDialog::AskForSettings(this, *newQueue);
auto accepted = ActionQueueSettingsDialog::AskForSettings(
GetSettingsWindow(), *newQueue);
if (!accepted) {
return;
}
@@ -55,6 +56,13 @@ void ActionQueueTable::Add()
QString::fromStdString(newQueue->Name()));
}
static double getSecondsSinceLastEmpty(ActionQueue *queue)
{
const auto time = std::chrono::high_resolution_clock::now() -
queue->GetLastEmptyTime();
return std::chrono::duration_cast<std::chrono::seconds>(time).count();
}
static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
{
assert(queue);
@@ -64,6 +72,7 @@ static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
result << QString::fromStdString(queue->Name());
}
result << QString::number(queue->Size())
<< FormatRelativeTime(getSecondsSinceLastEmpty(queue))
<< QString::fromStdString(obs_module_text(
queue->IsRunning()
? "AdvSceneSwitcher.actionQueueTab.yes"
@@ -115,7 +124,7 @@ static void openSettingsDialog()
auto oldName = queue->Name();
bool accepted = ActionQueueSettingsDialog::AskForSettings(
tabWidget->Table(), *queue.get());
GetSettingsWindow(), *queue.get());
if (accepted && oldName != queue->Name()) {
ActionQueueSignalManager::Instance()->Rename(
QString::fromStdString(oldName),
@@ -199,6 +208,8 @@ ActionQueueTable::ActionQueueTable(QTabWidget *parent)
"AdvSceneSwitcher.actionQueueTab.name.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.size.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.lastEmpty.header")
<< obs_module_text(
"AdvSceneSwitcher.actionQueueTab.isRunning.header")
<< obs_module_text(

View File

@@ -26,7 +26,10 @@ void SetupActionQueues()
done = true;
}
ActionQueue::ActionQueue() : Item() {}
ActionQueue::ActionQueue() : Item()
{
_lastEmpty = std::chrono::high_resolution_clock::now();
}
ActionQueue::~ActionQueue()
{
@@ -110,6 +113,7 @@ void ActionQueue::Add(const std::shared_ptr<MacroAction> &action)
action->Save(data);
copy->Load(data);
copy->PostLoad();
RunPostLoadSteps();
copy->ResolveVariablesToFixedValues();
_actions.emplace_back(copy);
} else {
@@ -124,6 +128,14 @@ bool ActionQueue::IsEmpty()
return _actions.empty();
}
ActionQueue::TimePoint ActionQueue::GetLastEmptyTime()
{
if (IsEmpty()) {
_lastEmpty = std::chrono::high_resolution_clock::now();
}
return _lastEmpty;
}
size_t ActionQueue::Size()
{
std::lock_guard<std::mutex> lock(_mutex);
@@ -137,6 +149,8 @@ void ActionQueue::RunActions()
{ // Grab next action to run
std::unique_lock<std::mutex> lock(_mutex);
while (_actions.empty() && !_stop) {
_lastEmpty =
std::chrono::high_resolution_clock::now();
_cv.wait(lock);
}
@@ -166,7 +180,7 @@ ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
"AdvSceneSwitcher.actionQueues.select",
"AdvSceneSwitcher.actionQueues.add",
"AdvSceneSwitcher.actionQueues.nameNotAvailable",
parent),
true, parent),
_queueRunStatus(new QLabel()),
_startStopToggle(new QPushButton()),
_queueSize(new QLabel()),

View File

@@ -2,11 +2,12 @@
#include "item-selection-helpers.hpp"
#include "macro-action.hpp"
#include <chrono>
#include <condition_variable>
#include <deque>
#include <obs-data.h>
#include <QCheckBox>
#include <thread>
#include <condition_variable>
namespace advss {
@@ -14,6 +15,8 @@ class ActionQueueSelection;
class ActionQueueSettingsDialog;
class ActionQueue : public Item {
using TimePoint = std::chrono::high_resolution_clock::time_point;
public:
ActionQueue();
~ActionQueue();
@@ -30,6 +33,7 @@ public:
void Clear();
bool IsEmpty();
TimePoint GetLastEmptyTime();
void Add(const std::shared_ptr<MacroAction> &);
size_t Size();
@@ -44,6 +48,7 @@ private:
std::condition_variable _cv;
std::thread _thread;
std::deque<std::shared_ptr<MacroAction>> _actions;
TimePoint _lastEmpty;
friend ActionQueueSelection;
friend ActionQueueSettingsDialog;

View File

@@ -168,17 +168,6 @@ void SwitcherData::SaveVersion(obs_data_t *obj,
obs_data_set_string(obj, "version", currentVersion.c_str());
}
void SwitcherData::AddIntervalResetStep(std::function<void()> function,
bool tryLock)
{
if (!tryLock) {
resetIntervalSteps.emplace_back(function);
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
resetIntervalSteps.emplace_back(function);
}
void SwitcherData::RunPostLoadSteps()
{
for (const auto &func : postLoadSteps) {
@@ -266,6 +255,16 @@ static void removeMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
}
}
static void addNewMacroHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
bool pressed)
{
if (pressed && SettingsWindowIsOpened()) {
QMetaObject::invokeMethod(GetSettingsWindow(),
"on_macroAdd_clicked",
Qt::QueuedConnection);
}
}
static void registerHotkeys()
{
switcher->startHotkey = obs_hotkey_register_frontend(
@@ -281,19 +280,21 @@ static void registerHotkeys()
obs_module_text(
"AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey"),
startStopToggleHotkeyFunc, NULL);
switcher->newMacroHotkey = obs_hotkey_register_frontend(
"newMacroSwitcherHotkey",
obs_module_text("AdvSceneSwitcher.hotkey.macro.new"),
addNewMacroHotkeyFunc, NULL);
switcher->upMacroSegment = obs_hotkey_register_frontend(
"upMacroSegmentSwitcherHotkey",
obs_module_text("AdvSceneSwitcher.hotkey.upMacroSegmentHotkey"),
obs_module_text("AdvSceneSwitcher.hotkey.macro.segment.up"),
upMacroSegmentHotkeyFunc, NULL);
switcher->downMacroSegment = obs_hotkey_register_frontend(
"downMacroSegmentSwitcherHotkey",
obs_module_text(
"AdvSceneSwitcher.hotkey.downMacroSegmentHotkey"),
obs_module_text("AdvSceneSwitcher.hotkey.macro.segment.down"),
downMacroSegmentHotkeyFunc, NULL);
switcher->removeMacroSegment = obs_hotkey_register_frontend(
"removeMacroSegmentSwitcherHotkey",
obs_module_text(
"AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey"),
obs_module_text("AdvSceneSwitcher.hotkey.macro.segment.remove"),
removeMacroSegmentHotkeyFunc, NULL);
switcher->hotkeysRegistered = true;
@@ -311,6 +312,7 @@ void SwitcherData::SaveHotkeys(obs_data_t *obj)
saveHotkey(obj, startHotkey, "startHotkey");
saveHotkey(obj, stopHotkey, "stopHotkey");
saveHotkey(obj, toggleHotkey, "toggleHotkey");
saveHotkey(obj, newMacroHotkey, "newMacroHotkey");
saveHotkey(obj, upMacroSegment, "upMacroSegmentHotkey");
saveHotkey(obj, downMacroSegment, "downMacroSegmentHotkey");
saveHotkey(obj, removeMacroSegment, "removeMacroSegmentHotkey");
@@ -323,14 +325,31 @@ static void loadHotkey(obs_data_t *obj, obs_hotkey_id id, const char *name)
obs_data_array_release(a);
}
static void setDefaultHotkeyBinding(obs_data_t *data, const char *name,
const char *value)
{
OBSDataAutoRelease hotkeyData = obs_data_create_from_json(value);
OBSDataArrayAutoRelease hotkeyArray = obs_data_array_create();
obs_data_array_push_back(hotkeyArray, hotkeyData);
obs_data_set_default_array(data, name, hotkeyArray);
}
void SwitcherData::LoadHotkeys(obs_data_t *obj)
{
if (!hotkeysRegistered) {
registerHotkeys();
}
setDefaultHotkeyBinding(obj, "newMacroHotkey",
"{"
"\"control\": true,"
"\"key\": \"OBS_KEY_N\""
"}");
loadHotkey(obj, startHotkey, "startHotkey");
loadHotkey(obj, stopHotkey, "stopHotkey");
loadHotkey(obj, toggleHotkey, "toggleHotkey");
loadHotkey(obj, newMacroHotkey, "newMacroHotkey");
loadHotkey(obj, upMacroSegment, "upMacroSegmentHotkey");
loadHotkey(obj, downMacroSegment, "downMacroSegmentHotkey");
loadHotkey(obj, removeMacroSegment, "removeMacroSegmentHotkey");

View File

@@ -57,11 +57,9 @@ public:
bool AnySceneTransitionStarted();
void SetPreconditions();
void ResetForNextInterval();
void AddSaveStep(std::function<void(obs_data_t *)>);
void AddLoadStep(std::function<void(obs_data_t *)>);
void AddPostLoadStep(std::function<void()>);
void AddIntervalResetStep(std::function<void()>, bool lock = true);
void RunPostLoadSteps();
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
int &linger, bool &setPreviousSceneAsMatch,
@@ -100,11 +98,10 @@ public:
std::vector<std::function<void(obs_data_t *)>> saveSteps;
std::vector<std::function<void(obs_data_t *)>> loadSteps;
std::vector<std::function<void()>> postLoadSteps;
std::vector<std::function<void()>> resetIntervalSteps;
bool firstBoot = true;
bool transitionActive = false;
bool sceneColletionStop = false;
bool sceneCollectionStop = false;
bool obsIsShuttingDown = false;
bool firstInterval = true;
bool firstIntervalAfterStop = true;
@@ -135,9 +132,6 @@ public:
bool transitionOverrideOverride = false;
bool adjustActiveTransitionType = true;
enum class LogLevel { DEFAULT, LOG_MACRO, LOG_ACTION, VERBOSE };
LogLevel logLevel = LogLevel::DEFAULT;
/* --- End of General tab section --- */
std::string lastTitle;
@@ -155,6 +149,7 @@ public:
obs_hotkey_id startHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id stopHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id toggleHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id newMacroHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id upMacroSegment = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id downMacroSegment = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id removeMacroSegment = OBS_INVALID_HOTKEY_ID;
@@ -169,6 +164,7 @@ public:
bool warnPluginLoadFailure = true;
bool disableHints = false;
bool disableFilterComboboxFilter = false;
bool disableMacroWidgetCache = false;
bool hideLegacyTabs = true;
bool saveWindowGeo = false;
QPoint windowPos = {};
@@ -233,7 +229,6 @@ public:
bool checkPause();
void checkDefaultSceneTransitions();
void writeSceneInfoToFile();
void writeToStatusFile(const QString &msg);
void checkSwitchCooldown(bool &match);
std::deque<WindowSwitch> windowSwitches;

View File

@@ -0,0 +1,34 @@
#include "auto-update-tooltip-label.hpp"
namespace advss {
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
QWidget *parent, const std::function<QString()> &updateTooltipCallback,
int updateIntervalMs)
: QLabel(parent),
_callback(updateTooltipCallback),
_timer(new QTimer(this)),
_updateIntervalMs(updateIntervalMs)
{
connect(_timer, &QTimer::timeout, this,
&AutoUpdateTooltipLabel::UpdateTooltip);
}
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
{
_timer->start(_updateIntervalMs);
QLabel::enterEvent(event);
}
void AutoUpdateTooltipLabel::leaveEvent(QEvent *event)
{
_timer->stop();
QLabel::leaveEvent(event);
}
void AutoUpdateTooltipLabel::UpdateTooltip()
{
setToolTip(_callback());
}
} // namespace advss

View File

@@ -0,0 +1,32 @@
#pragma once
#include "export-symbol-helper.hpp"
#include <functional>
#include <QLabel>
#include <QTimer>
namespace advss {
class ADVSS_EXPORT AutoUpdateTooltipLabel : public QLabel {
Q_OBJECT
public:
AutoUpdateTooltipLabel(
QWidget *parent,
const std::function<QString()> &updateTooltipCallback,
int updateIntervalMs = 300);
protected:
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
private slots:
void UpdateTooltip();
private:
const std::function<QString()> _callback;
QTimer *_timer;
const int _updateIntervalMs;
};
} // namespace advss

View File

@@ -7,7 +7,7 @@
namespace advss {
class SliderIgnoreScroll : public QSlider {
class ADVSS_EXPORT SliderIgnoreScroll : public QSlider {
Q_OBJECT
public:

View File

@@ -9,15 +9,14 @@
namespace advss {
class DurationSelection : public QWidget {
class ADVSS_EXPORT DurationSelection : public QWidget {
Q_OBJECT
public:
EXPORT DurationSelection(QWidget *parent = nullptr,
bool showUnitSelection = true,
double minValue = 0.0);
EXPORT void SetDuration(const Duration &);
DurationSelection(QWidget *parent = nullptr,
bool showUnitSelection = true, double minValue = 0.0);
void SetDuration(const Duration &);
Duration GetDuration() const { return _current; }
EXPORT QDoubleSpinBox *SpinBox() { return _duration->SpinBox(); }
QDoubleSpinBox *SpinBox() { return _duration->SpinBox(); }
private slots:
void _DurationChanged(const NumberVariable<double> &value);

View File

@@ -29,6 +29,7 @@ void Duration::Load(obs_data_t *obj, const char *name)
// TODO: remove this fallback
if (!data || !obs_data_has_user_value(data, "version") ||
obs_data_get_int(data, "version") != 1) {
_value = 0.;
bool usingDefaultArgs = strcmp("duration", name) == 0;
if (usingDefaultArgs) {
_value = obs_data_get_double(obj, "seconds");

View File

@@ -62,6 +62,11 @@ void FilterComboBox::SetFilterBehaviourEnabled(bool value)
FilterComboBox::_filteringEnabled = value;
}
void FilterComboBox::SetAllowUnmatchedSelection(bool allow)
{
_allowUnmatchedSelection = allow;
}
void FilterComboBox::setCurrentText(const QString &text)
{
if (_filteringEnabled) {
@@ -82,7 +87,7 @@ void FilterComboBox::setItemText(int index, const QString &text)
void FilterComboBox::focusOutEvent(QFocusEvent *event)
{
// Reset on invalid selection
if (findText(currentText()) == -1) {
if (!_allowUnmatchedSelection && findText(currentText()) == -1) {
setCurrentIndex(-1);
Emit(-1, "");
}

View File

@@ -13,6 +13,7 @@ public:
FilterComboBox(QWidget *parent = nullptr,
const QString &placehodler = "");
static void SetFilterBehaviourEnabled(bool);
void SetAllowUnmatchedSelection(bool allow);
void setCurrentText(const QString &text);
void setItemText(int index, const QString &text);
@@ -27,6 +28,7 @@ private slots:
private:
void Emit(int index, const QString &text);
bool _allowUnmatchedSelection = false;
int _lastEmittedIndex = -1;
QString _lastEmittedText;

Some files were not shown because too many files have changed in this diff Show More