Compare commits

..

63 Commits

Author SHA1 Message Date
WarmUpTill
da28fea2e0 Fix potential freeze or crash with "Show match" dialog
This is a workaround to handle random segfaults triggered when using ...
QMetaObject::invokeMethod(this, "RedrawImage", -Qt::QueuedConnection,
		   Q_ARG(QImage, image));
... from within CheckForMatchLoop().
Even using BlockingQueuedConnection causes deadlocks.
2022-02-26 16:08:55 -08:00
WarmUpTill
b6b0531dbc Fix size ofi new collapsed segments containing ResizingPlainTextEdit 2022-02-26 16:08:55 -08:00
WarmUpTill
2de0a94756 Fix highlight animations never stopping 2022-02-26 16:08:55 -08:00
WarmUpTill
d018740442 Fix potential deadlock in ShowMatchDialog 2022-02-26 16:08:55 -08:00
WarmUpTill
d89f8a7972 Clear pixmap if screenshot creation failed 2022-02-26 16:08:55 -08:00
WarmUpTill
f7a2aa09cb Fix potential crash 2022-02-26 16:08:55 -08:00
WarmUpTill
04825e4b88 Fix warning 2022-02-26 08:23:10 -08:00
WarmUpTill
1cfd981245 Reduce text edit areas size of file, filter, source, transform segments 2022-02-26 08:23:10 -08:00
WarmUpTill
6fb8297275 Use QPropertyAnimation::DeleteWhenStopped PulseWidget() when once is set
Also fixes potential crash when waiting for single shot timer when
looping and widget and animations are deleted in the meantime
2022-02-26 08:23:10 -08:00
WarmUpTill
4253c99cf2 Fade out segment controls which will likely not be needed
This was done to give some hints which controls will likely need to be
used to avoid mistakenly clicking on the action controls when editing
conditions.
2022-02-26 08:23:10 -08:00
WarmUpTill
ae13a68cae Double click on list item in run and sequence action to edit 2022-02-26 08:23:10 -08:00
WarmUpTill
4e5aae7e5f Add 'OBS stats' condition type (#414)
It supports various different types of stats:
* fps
* cpu
* ram
* frame time
* skipped frames
* missed frames
* dropped frames
* data size sent
* bitrate
2022-02-26 11:37:54 +01:00
WarmUpTill
88dff37776 Allow drag and drop reordering of macros 2022-02-24 14:15:23 -08:00
WarmUpTill
83df1ab658 Skip collapse animation if section is already in the desired state 2022-02-24 14:15:23 -08:00
WarmUpTill
5298b14803 Use name of macro to be copied in addNewMacro() dialog 2022-02-24 14:15:23 -08:00
WarmUpTill
eeedf11c6a Add hotkey to delete macro segments 2022-02-24 14:15:23 -08:00
WarmUpTill
89efb6d1c8 Allow selecting either an action or condition but not both 2022-02-24 14:15:23 -08:00
WarmUpTill
1054bce4a7 Remove slight movement of macro segment when (de-)selecting them 2022-02-24 14:15:23 -08:00
WarmUpTill
d3cbddf0a4 Allow selecting scene groups in preview scene change action 2022-02-24 14:15:05 -08:00
WarmUpTill
a91fed1583 Simplify waitForTransitionChange()
Will now also work if the target scene is the already currently active
scene.
2022-02-24 14:15:05 -08:00
WarmUpTill
c94a791d19 Allow switching to already active scene if in studio mode
Preview and program scene can differ in regards to the active scene
items.
Thus it can make sense to switch to the already active scene.
2022-02-24 14:15:05 -08:00
WarmUpTill
02e380a9f2 Allow selecting current and previous scene in preview scene action 2022-02-24 14:15:05 -08:00
WarmUpTill
4349a98614 CI: Switch to Visual Studio 17 2022 2022-02-22 11:40:33 -08:00
WarmUpTill
f294283306 CI: Bump OBS version 2022-02-21 12:09:23 -08:00
WarmUpTill
cc9d5897da Add deprecation warning for legacy tabs 2022-02-20 04:50:12 -08:00
WarmUpTill
8e3eb8519b Fix potential crashes when adding or removing macro segments
I wrongly assumed that std::deque would guarantee that pointers to
elements in deque would not be invalidated by insert() or erase() but
this is not the case it seems.

""
An erase in the middle of the deque invalidates all the iterators and
references to elements of the deque. An erase at either end of the deque
invalidates only the iterators and the references to the erased
elements.
""

I guess I got lucky noone ran into these sorts of crashes for now?
2022-02-20 04:50:12 -08:00
WarmUpTill
354ef56070 Do not select any item if pressing add without any active selection 2022-02-20 04:50:12 -08:00
WarmUpTill
352dac5e32 Add MacroSegmentList to allow deselecting items
Previously once a macro segement was selected you could only deselect it
by clicking on different macro segment, thus never allowing the user to
deselect all macro segments
2022-02-20 04:50:12 -08:00
WarmUpTill
584fe4b3b6 Copy logic type when adding conditions 2022-02-20 04:50:12 -08:00
WarmUpTill
bdd489aea5 Fix crashes related to PulseWidget()
* Fix crash if widget was deleted during animation
* Fix crash if multiple animations were started at the same time
2022-02-20 04:50:12 -08:00
WarmUpTill
0b2e725471 Do not run collapse animation when calling SetContent() 2022-02-20 04:50:12 -08:00
WarmUpTill
45e8378021 Remove old macro segment controls 2022-02-20 04:50:12 -08:00
WarmUpTill
955f308c59 Add list-like controls to macro actions and conditions 2022-02-20 04:50:12 -08:00
WarmUpTill
2c25bb4dd4 Make slider background transparent
Sliders, e.g. in the video condition, would otherwise block parts of the
highlight frame around macro segments.
2022-02-18 13:40:33 -08:00
WarmUpTill
d8dcd91ed9 Split video condition into multiple files 2022-02-18 13:40:33 -08:00
WarmUpTill
03900e936b Rename class AdvSSScreenshotObj to ScreenshotHelper 2022-02-18 13:40:33 -08:00
WarmUpTill
6d2360eb87 Clean up AdvSSScreenshotObj usage in video condition 2022-02-18 13:40:33 -08:00
WarmUpTill
ab4a70a758 Refresh contents of "Show match" dialog every second 2022-02-18 13:40:33 -08:00
WarmUpTill
b48276bd5c Add option to check for OBS shutdown to MacroConditionPluginState 2022-02-18 13:35:15 -08:00
WarmUpTill
34167366db Add day of week based check to date condition 2022-02-12 17:57:35 -08:00
WarmUpTill
bfa8f4d9d5 Visually clean up date condition
- add "and" separator if "between" check is chosen
- split dateTimeEdit into a dateEdit and a timeEdit to make editing
  easier
- move ignore* checkboxes in front of respective edit widgets to make
  their intention more clear and add tooltips to further clarify their
  use
2022-02-12 17:57:35 -08:00
WarmUpTill
f8e3a3e275 CI: Fix MacOS build
Packages version bump
2022-02-10 12:19:56 -08:00
WarmUpTill
1328896801 Reopen last opened tab in current session
Will reset to General tab on startup / scene collection change
2022-02-10 11:29:44 -08:00
WarmUpTill
0effbd8ace Add context menu entry for expanding and collapsing edit areas 2022-02-04 11:38:42 -08:00
WarmUpTill
08a8157f3a Reduce spacing between macro segments 2022-02-04 11:38:42 -08:00
WarmUpTill
262bb3f1ee Keep size of individual macro sgements consistent
Previously the segments would expand if there was still room in the
condition and action area.
2022-02-04 11:38:42 -08:00
WarmUpTill
415f63c46c Fix warnings generated about missing icons 2022-02-04 11:38:42 -08:00
WarmUpTill
0771f939a6 Restore original color after PulseWidget() completes 2022-02-04 11:38:42 -08:00
WarmUpTill
a81ee605d6 Rename function 2022-02-04 11:38:42 -08:00
WarmUpTill
1e7726bcfc Add option to highlight recently executed macros on the Macro tab 2022-02-04 11:38:42 -08:00
WarmUpTill
dc698e426d Add MacroListEntryWidget
To be used for macro tab's macro list as regular QListWidgetItem are too
limited
2022-02-04 11:38:42 -08:00
WarmUpTill
a4f66dbbcd Fix warnings 2022-02-04 11:38:42 -08:00
WarmUpTill
1516b9a939 Add option to hide legacy tabs 2022-02-04 11:38:42 -08:00
WarmUpTill
e13f20c860 Adding frame highlighting individual macro segments 2022-02-04 11:38:42 -08:00
WarmUpTill
727992f1d0 Clean up stylesheet usage 2022-02-04 11:38:42 -08:00
WarmUpTill
4e75a15118 Clean up unnecessary variable 2022-02-04 11:38:42 -08:00
WarmUpTill
48368f4bd6 Move vertical / horizontal control handling to macro segment 2022-02-04 11:38:42 -08:00
WarmUpTill
61d9031933 Add option to toggle between vertical and horizontal macro controls 2022-02-04 11:38:42 -08:00
WarmUpTill
487c735e13 Delay show and hide of macro controls 2022-02-04 11:38:42 -08:00
WarmUpTill
7a34c334bd Highlight macro segments new position when moving it up or down 2022-02-04 11:38:42 -08:00
WarmUpTill
4988683e22 Resize conditions and actions areas on expand / collapse all 2022-02-04 11:38:42 -08:00
WarmUpTill
cb681643c8 Fix scene switch action blocking unexpectedly
If "wait for transition" was checked and no valid scene was selected the
waitForTransitionChange() function will wait for its maximum allowed
duration as switcher->currentScene will never be null.
2022-02-02 09:55:35 -08:00
WarmUpTill
d79e10ec97 Add tr-TR.ini locale (Provided by dargat) 2022-01-30 19:15:49 +01:00
78 changed files with 4127 additions and 1131 deletions

View File

@@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch]
env:
PLUGIN_NAME: SceneSwitcher
LIB_NAME: advanced-scene-switcher
OBS_TAG: 27.1.3
OBS_TAG: 27.2.0
jobs:
macos64:
@@ -85,7 +85,7 @@ jobs:
run: |
curl -L -O http://s.sudre.free.fr/Software/files/Packages.dmg
sudo hdiutil attach ./Packages.dmg
sudo installer -pkg /Volumes/Packages\ 1.2.9/Install\ Packages.pkg -target /
sudo installer -pkg /Volumes/Packages\ 1.2.10/Install\ Packages.pkg -target /
- name: Package
if: success()
shell: bash
@@ -184,7 +184,8 @@ jobs:
libx11-xcb-dev \
libxcb-xfixes0-dev \
libopencv-dev \
libprocps-dev
libprocps-dev \
libpci-dev
- name: "Configure"
shell: bash
run: |
@@ -223,7 +224,7 @@ jobs:
arch: [32, 64]
env:
QT_VERSION: 5.15.2
CMAKE_GENERATOR: "Visual Studio 16 2019"
CMAKE_GENERATOR: "Visual Studio 17 2022"
CMAKE_SYSTEM_VERSION: "10.0.18363.657"
WINDOWS_DEPS_VERSION: "2019"
steps:

View File

@@ -229,6 +229,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-condition-idle.hpp
src/headers/macro-condition-macro.hpp
src/headers/macro-condition-media.hpp
src/headers/macro-condition-obs-stats.hpp
src/headers/macro-condition-plugin-state.hpp
src/headers/macro-condition-process.hpp
src/headers/macro-condition-recording.hpp
@@ -245,8 +246,9 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-condition-virtual-cam.hpp
src/headers/macro-condition-window.hpp
src/headers/macro.hpp
src/headers/macro-controls.hpp
src/headers/macro-list-entry-widget.hpp
src/headers/macro-segment.hpp
src/headers/macro-segment-list.hpp
src/headers/macro-selection.hpp
src/headers/curl-helper.hpp
src/headers/hotkey.hpp
@@ -260,6 +262,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/section.hpp
src/headers/status-control.hpp
src/headers/platform-funcs.hpp
src/headers/resizing-text-edit.hpp
src/headers/utility.hpp
src/headers/volume-control.hpp
src/headers/version.h)
@@ -329,6 +332,7 @@ set(advanced-scene-switcher_SOURCES
src/macro-condition-idle.cpp
src/macro-condition-macro.cpp
src/macro-condition-media.cpp
src/macro-condition-obs-stats.cpp
src/macro-condition-plugin-state.cpp
src/macro-condition-process.cpp
src/macro-condition-recording.cpp
@@ -345,8 +349,9 @@ set(advanced-scene-switcher_SOURCES
src/macro-condition-virtual-cam.cpp
src/macro-condition-window.cpp
src/macro.cpp
src/macro-controls.cpp
src/macro-list-entry-widget.cpp
src/macro-segment.cpp
src/macro-segment-list.cpp
src/macro-selection.cpp
src/macro-tab.cpp
src/curl-helper.cpp
@@ -355,6 +360,7 @@ set(advanced-scene-switcher_SOURCES
src/screenshot-helper.cpp
src/transition-selection.cpp
src/name-dialog.cpp
src/resizing-text-edit.cpp
src/duration-control.cpp
src/status-control.cpp
src/section.cpp

View File

@@ -30,6 +30,8 @@ AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Enable verbose loggi
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Save window position and size"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Show system tray notifications"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Disable UI hints"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Hide tabs which can be represented via macros"
AdvSceneSwitcher.generalTab.generalBehavior.highlightExecutedMacros="Highlight recently executed macros"
AdvSceneSwitcher.generalTab.priority="Priority"
AdvSceneSwitcher.generalTab.priority.description="Switching methods priority (Highest priority is at the top)"
AdvSceneSwitcher.generalTab.priority.threadPriority="Use thread priority"
@@ -76,6 +78,8 @@ AdvSceneSwitcher.macroTab.exists="Macro name exists already"
AdvSceneSwitcher.macroTab.copy="Create copy"
AdvSceneSwitcher.macroTab.expandAll="Expand all"
AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
AdvSceneSwitcher.macroTab.maximize="Maximize"
AdvSceneSwitcher.macroTab.minimize="Minimize"
; Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry"
@@ -143,9 +147,13 @@ AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
AdvSceneSwitcher.condition.video.showMatch="Show match"
AdvSceneSwitcher.condition.video.showMatch.loading="Checking for match"
AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of source!"
AdvSceneSwitcher.condition.video.screenshotEmpty="Screenshot is empty - Is the source visible?"
AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!"
AdvSceneSwitcher.condition.video.patternMatchSuccess="Pattern is highlighted in red"
AdvSceneSwitcher.condition.video.objectMatchFail="Object was not found!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="Object is highlighted in red"
AdvSceneSwitcher.condition.video.modelLoadFail="Model data could not be loaded!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
@@ -171,6 +179,7 @@ AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duratio
AdvSceneSwitcher.condition.pluginState="Plugin state"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
AdvSceneSwitcher.condition.pluginState.state.running="Advanced scene switcher is running"
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS is shutting down"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="Timer"
AdvSceneSwitcher.condition.timer.type.fixed="Fixed"
@@ -233,12 +242,26 @@ AdvSceneSwitcher.condition.replay.state.started="Replay buffer started"
AdvSceneSwitcher.condition.replay.state.saved="Replay buffer saved"
AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Date"
AdvSceneSwitcher.condition.date.anyDay="Any day"
AdvSceneSwitcher.condition.date.monday="Monday"
AdvSceneSwitcher.condition.date.tuesday="Tuesday"
AdvSceneSwitcher.condition.date.wednesday="Wednesday"
AdvSceneSwitcher.condition.date.thursday="Thursday"
AdvSceneSwitcher.condition.date.friday="Friday"
AdvSceneSwitcher.condition.date.saturday="Saturday"
AdvSceneSwitcher.condition.date.sunday="Sunday"
AdvSceneSwitcher.condition.date.state.at="At"
AdvSceneSwitcher.condition.date.state.after="After"
AdvSceneSwitcher.condition.date.state.before="Before"
AdvSceneSwitcher.condition.date.state.between="Between"
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} Ignore date component {{ignoreTime}} Ignore time component"
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Repeat every {{duration}} on date match"
AdvSceneSwitcher.condition.date.separator="and"
AdvSceneSwitcher.condition.date.ignoreDate="If unchecked the date component will be ignored"
AdvSceneSwitcher.condition.date.ignoreTime="If unchecked the time component will be ignored"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Show advanced settings"
AdvSceneSwitcher.condition.date.showSimpleSettings="Show simple settings"
AdvSceneSwitcher.condition.date.entry.simple="On {{dayOfWeek}} at {{weekTime}}"
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Repeat every {{duration}} on date match"
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
@@ -268,6 +291,25 @@ AdvSceneSwitcher.condition.errorStatus="OpenVR error: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD is in ..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD is currently at {{xPos}} x {{yPos}} x {{zPos}}"
AdvSceneSwitcher.condition.stats="OBS stats"
AdvSceneSwitcher.condition.stats.type.fps="FPS"
AdvSceneSwitcher.condition.stats.type.CPUUsage="CPU Usage"
AdvSceneSwitcher.condition.stats.type.HDDSpaceAvailable="Disk space available"
AdvSceneSwitcher.condition.stats.type.memoryUsage="Memory Usage"
AdvSceneSwitcher.condition.stats.type.averageTimeToRender="Average time to render frame"
AdvSceneSwitcher.condition.stats.type.skippedFrames="Skipped frames due to encoding lag"
AdvSceneSwitcher.condition.stats.type.missedFrames="Frames missed due to rendering lag"
AdvSceneSwitcher.condition.stats.type.droppedFrames.stream="Stream dropped frames"
AdvSceneSwitcher.condition.stats.type.megabytesSent.stream="Stream total data output"
AdvSceneSwitcher.condition.stats.type.bitrate.stream="Stream bitrate"
AdvSceneSwitcher.condition.stats.type.droppedFrames.recording="Recording dropped frames"
AdvSceneSwitcher.condition.stats.type.megabytesSent.recording="Recording total data output"
AdvSceneSwitcher.condition.stats.type.bitrate.recording="Recording bitrate"
AdvSceneSwitcher.condition.stats.condition.above="above"
AdvSceneSwitcher.condition.stats.condition.equals="equal to"
AdvSceneSwitcher.condition.stats.condition.below="below"
AdvSceneSwitcher.condition.stats.dockHint="You can open the \"Stats\" dock to view the current status"
AdvSceneSwitcher.condition.stats.entry="{{stats}} is {{condition}} {{value}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Switch scene"
@@ -653,6 +695,7 @@ 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.removeMacroSegmentHotkey="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}}"
@@ -692,6 +735,8 @@ AdvSceneSwitcher.stopped="Plugin stopped"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>This seems to be the first time the Advanced Scene Switcher was started.<br>Please have a look at the <a href=\"https://github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> for a list of guides and examples.<br>Do not hesitate to ask questions in the plugin's <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">thread</span></a> on the OBS forums!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="Development for this tab stopped!\nPlease consider transitioning to using Macros instead.\nThis hint can be disabled on the General tab."
AdvSceneSwitcher.unit.milliseconds="milliseconds"
AdvSceneSwitcher.unit.secends="seconds"
AdvSceneSwitcher.unit.minutes="minutes"

691
data/locale/tr-TR.ini Normal file
View File

@@ -0,0 +1,691 @@
AdvSceneSwitcher.pluginName="Gelismis Otomatik Sahne Degistirici"
AdvSceneSwitcher.windowTitle="Gelismis Otomatik Sahne Degistirici"
; General Tab
AdvSceneSwitcher.generalTab.title="Genel"
AdvSceneSwitcher.generalTab.status="Durum"
AdvSceneSwitcher.generalTab.status.hotkeytips="OBS ayarlarinda yapılandırılabilir Kısayol tusları"
AdvSceneSwitcher.generalTab.status.currentStatus="Gelismis Otomatik Sahne Degistirici:"
AdvSceneSwitcher.generalTab.status.onStartup="OBS'yi baslatirken:"
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Etkinse, sahne değiştiriciyi etkinleştirin"
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Her zaman sahne değiştiriciyi etkinleştirin"
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="Sahne değiştiriciyi başlatma"
AdvSceneSwitcher.generalTab.status.start="Başlat"
AdvSceneSwitcher.generalTab.status.stop="Durdur"
AdvSceneSwitcher.generalTab.status.autoStart="Aşağıdaki durumlarda sahne değiştiriciyi otomatik olarak başlatın:"
AdvSceneSwitcher.generalTab.status.autoStart.never="Asla"
AdvSceneSwitcher.generalTab.status.autoStart.recording="Kayıt"
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Yayın"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Kayıt veya Yayın"
AdvSceneSwitcher.generalTab.status.checkInterval="Anahtar koşullarını her seferinde kontrol edin"
AdvSceneSwitcher.generalTab.generalBehavior="Genel davranış"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="...için herhangi bir işlem yapılmazsa"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Yalnızca yapılandırılmış kontrol aralığı kadar doğru olacaktır."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="Geçiş Yapma"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Rastgele sekmesinde herhangi bir sahneye geçin"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.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"
AdvSceneSwitcher.generalTab.priority="Öncelik"
AdvSceneSwitcher.generalTab.priority.description="Geçiş yöntemleri önceliği (En yüksek öncelik en üsttedir)"
AdvSceneSwitcher.generalTab.priority.threadPriority="Konu önceliğini kullan"
AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(Önceliği \"Normal\" üzerine çıkarmak önerilmez)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings="Kayıt / Yükle Ayarları"
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.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"
AdvSceneSwitcher.generalTab.priority.sceneSequence="Sahne Sıralama"
AdvSceneSwitcher.generalTab.priority.idleDetection="Boşta Algılama"
AdvSceneSwitcher.generalTab.priority.executable="Yürütülebilir"
AdvSceneSwitcher.generalTab.priority.screenRegion="Ekran Alanı"
AdvSceneSwitcher.generalTab.priority.windowTitle="Pencere Başlığı"
AdvSceneSwitcher.generalTab.priority.media="Medya"
AdvSceneSwitcher.generalTab.priority.time="Zaman"
AdvSceneSwitcher.generalTab.priority.audio="Ses"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Makro"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Makro"
AdvSceneSwitcher.macroTab.macros="Makrolar"
AdvSceneSwitcher.macroTab.priorityWarning="Not: Makroları en yüksek öncelikli işlev olacak şekilde yapılandırmanız önerilir.\n Bu ayar, Genel sekmesinde değiştirilebilir."
AdvSceneSwitcher.macroTab.help="Makrolar, birden çok koşula bağlı olarak bir dizi eylemi yürütmenize izin verir.\n\n Yeni bir Makro eklemek için vurgulanan artı sembolüne tıklayın."
AdvSceneSwitcher.macroTab.editConditionHelp="Bu bölüm, Makro koşullarını tanımlamanızı sağlar.\n\n Sol taraftan mevcut bir Makro seçin veya yeni bir Makro ekleyin.\n Ardından yeni bir koşul eklemek için aşağıdaki artı düğmesini tıklayın."
AdvSceneSwitcher.macroTab.editActionHelp="Bu bölüm, Makro eylemlerini tanımlamanızı sağlar.\n\n Soldan mevcut bir Makro seçin veya yeni bir Makro ekleyin.\n Ardından yeni bir eylem eklemek için aşağıdaki artı düğmesini tıklayın."
AdvSceneSwitcher.macroTab.edit="Makro Düzenleme"
AdvSceneSwitcher.macroTab.edit.logic="Mantık türü:"
AdvSceneSwitcher.macroTab.edit.condition="Durum türü:"
AdvSceneSwitcher.macroTab.edit.action="Eylem türü:"
AdvSceneSwitcher.macroTab.add="Yeni Makro ekle"
AdvSceneSwitcher.macroTab.name="İsim:"
AdvSceneSwitcher.macroTab.run="Makro Çalıştırma"
AdvSceneSwitcher.macroTab.runFail="\"%1\" çalıştırılamadı!\n Eylemlerden biri başarısız oldu veya makro zaten çalışıyor."
AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın"
AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
AdvSceneSwitcher.macroTab.exists="Macro adı zaten mevcut"
AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
; Macro Logic
AdvSceneSwitcher.logic.none="Girişi yoksay"
AdvSceneSwitcher.logic.and="Ve"
AdvSceneSwitcher.logic.or="Ya da"
AdvSceneSwitcher.logic.andNot="ve değil"
AdvSceneSwitcher.logic.orNot="ya da değil"
AdvSceneSwitcher.logic.rootNone="Eğer"
AdvSceneSwitcher.logic.not="Eğer değilse"
; Macro Conditions
AdvSceneSwitcher.condition.audio="Ses"
AdvSceneSwitcher.condition.audio.state.below="altında"
AdvSceneSwitcher.condition.audio.state.above="üstünde"
AdvSceneSwitcher.condition.audio.state.mute="sessiz"
AdvSceneSwitcher.condition.audio.state.unmute="ses açıldı"
AdvSceneSwitcher.condition.audio.entry="Hacim{{audioSources}} gibi {{condition}} {{volume}}"
AdvSceneSwitcher.condition.cursor="İmleç"
AdvSceneSwitcher.condition.cursor.type.region="alanında"
AdvSceneSwitcher.condition.cursor.type.moving="hareketlidir"
AdvSceneSwitcher.condition.cursor.showFrame="Çerçeveyi Göster"
AdvSceneSwitcher.condition.cursor.hideFrame="Çerçeveyi Gizle"
AdvSceneSwitcher.condition.cursor.entry.line1="İmleç {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="İmleç şuanda {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="Sahne"
AdvSceneSwitcher.condition.scene.type.current="Mevcut Sahne"
AdvSceneSwitcher.condition.scene.type.previous="Önceki Sahne"
AdvSceneSwitcher.condition.scene.type.changed="Sahne değişti"
AdvSceneSwitcher.condition.scene.type.notChanged="Sahne değişmedi"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Geçiş hedefi sahnesi için geçiş kontrolü sırasında"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Pencere"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} Varolan ve ..."
AdvSceneSwitcher.condition.window.entry.line2="... {{fullscreen}} Tam ekran {{maximized}} büyütüldü {{focused}} odaklanıldı {{windowFocusChanged}} ön plan penceresi değişikliği"
AdvSceneSwitcher.condition.file="Dosya"
AdvSceneSwitcher.condition.file.entry.line1="İçerik {{fileType}} {{filePath}} eşleştirmeler:"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medya"
AdvSceneSwitcher.condition.media.anyOnScene="Herhangi bir medya kaynağı"
AdvSceneSwitcher.condition.media.allOnScene="Tüm medya kaynakları "
AdvSceneSwitcher.condition.media.matchOnChange="Yalnızca değişiklikle eşleştirin (Not: Bu seçenek gelecekteki bir sürümde kaldırılacaktır - lütfen bunun yerine zaman kısıtlamalarını kullanın)"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} durumu {{states}} ve {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="Tam olarak eşleşir"
AdvSceneSwitcher.condition.video.condition.differ="Eşleştirme"
AdvSceneSwitcher.condition.video.condition.hasChanged="Değişti"
AdvSceneSwitcher.condition.video.condition.hasNotChanged="Değişmedi"
AdvSceneSwitcher.condition.video.condition.noImage=ıktı yok"
AdvSceneSwitcher.condition.video.condition.pattern="Modelle eşleşir."
AdvSceneSwitcher.condition.video.condition.object="nesne içerir"
AdvSceneSwitcher.condition.video.askFileAction="Mevcut bir dosyayı kullanmak mı yoksa seçili olan kaynağın ekran görüntüsünü mü oluşturmak istiyorsunuz?"
AdvSceneSwitcher.condition.video.askFileAction.file="Mevcut dosyayı kullan"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Ekran görüntüsü oluştur"
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Model eşleştirmeyi kullan"
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="Bu, koşulun doğru olması için görüntünün ne kadar değişmesi gerektiğini kontrol etmenizi sağlar."
AdvSceneSwitcher.condition.video.patternThreshold="Eşik: "
AdvSceneSwitcher.condition.video.patternThresholdDescription="Daha yüksek bir eşik değeri, modelin video kaynağıyla daha yakından eşleşmesi gerektiği anlamına gelir."
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Desen için maske olarak alfa kanalını kullanın."
AdvSceneSwitcher.condition.video.objectScaleThreshold="Ölçek faktörü: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="Daha düşük bir ölçek faktörü, daha fazla eşleşmeye, ancak daha yüksek CPU yüküne yol açacaktır."
AdvSceneSwitcher.condition.video.minNeighborDescription="Daha yüksek bir minimum komşu değeri, daha az ancak daha kaliteli eşleşmelerle sonuçlanacaktır."
AdvSceneSwitcher.condition.video.showMatch="Eşleşmeyi Göster"
AdvSceneSwitcher.condition.video.screenshotFail="Kaynağın ekran görüntüsü alınamadı!"
AdvSceneSwitcher.condition.video.patternMatchFail="Desen bulunamadı!"
AdvSceneSwitcher.condition.video.objectMatchFail="Nesne bulunamadı!"
AdvSceneSwitcher.condition.video.modelLoadFail="Model verileri yüklenemedi!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{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.minSize="Minimum boyut: {{minSizeX}} x {{minSizeY}}"
AdvSceneSwitcher.condition.video.entry.maxSize="Maximum boyut: {{maxSizeX}} x {{maxSizeY}}"
AdvSceneSwitcher.condition.video.entry.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}}"
AdvSceneSwitcher.condition.record="Kayıt"
AdvSceneSwitcher.condition.record.state.start="Kayıt Çalışıyor"
AdvSceneSwitcher.condition.record.state.pause="Kayıt durakladı"
AdvSceneSwitcher.condition.record.state.stop="Kayıt durdu"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="İşlem"
AdvSceneSwitcher.condition.process.entry="{{processes}} çalışıyor {{focused}} ve odaklandı"
AdvSceneSwitcher.condition.idle="Boşta"
AdvSceneSwitcher.condition.idle.entry="...için klavye veya fare girişi yok {{duration}}"
AdvSceneSwitcher.condition.pluginState="Eklenti durumu"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Bu aralıkta otomatik sahne değişikliği tetiklendi"
AdvSceneSwitcher.condition.pluginState.state.running="Gelişmiş sahne değiştirici çalışıyor"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="Zamanlayıcı"
AdvSceneSwitcher.condition.timer.type.fixed="Sabit"
AdvSceneSwitcher.condition.timer.type.random="Rastgele"
AdvSceneSwitcher.condition.timer.pause="Duraklat"
AdvSceneSwitcher.condition.timer.continue="Devam etmek"
AdvSceneSwitcher.condition.timer.entry.line1.fixed="{{type}} süresi {{duration}} geçti"
AdvSceneSwitcher.condition.timer.entry.line1.random="{{type}} itibaren süre {{duration}} to {{duration2}} geçti"
AdvSceneSwitcher.condition.timer.entry.line2="Kalan Süre: {{remaining}} saniye"
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Kalan zamanı kayıt {{autoReset}} Süreye ulaşıldıktan sonra zamanlayıcıyı otomatik olarak sıfırla"
AdvSceneSwitcher.condition.timer.reset="Sıfırlama"
AdvSceneSwitcher.condition.macro="Makro"
AdvSceneSwitcher.condition.macro.type.count="Saymak"
AdvSceneSwitcher.condition.macro.type.state="Belirtmek"
AdvSceneSwitcher.condition.macro.type.selection="Koşul türü: {{types}}"
AdvSceneSwitcher.condition.macro.count.type.below="Daha az"
AdvSceneSwitcher.condition.macro.count.type.above="Daha fazla"
AdvSceneSwitcher.condition.macro.count.type.equal="Aynen"
AdvSceneSwitcher.condition.macro.count.reset="Sıfırlama"
AdvSceneSwitcher.condition.macro.pausedWarning="Seçili makro şu anda duraklatıldı!"
AdvSceneSwitcher.condition.macro.state.entry="Koşulları {{macros}} doğru"
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} yürütüldü {{conditions}} {{count}} zaman"
AdvSceneSwitcher.condition.macro.count.entry.line2="Mevcut sayı: {{currentCount}} {{resetCount}}"
AdvSceneSwitcher.condition.source="Kaynak"
AdvSceneSwitcher.condition.source.type.active="Aktiftir"
AdvSceneSwitcher.condition.source.type.showing="gösteriyor"
AdvSceneSwitcher.condition.source.type.settings="Ayarlar eşleştirildi"
AdvSceneSwitcher.condition.source.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.source.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.virtualCamera="Sanal kamera"
AdvSceneSwitcher.condition.virtualCamera.state.start="Sanal kamera başladı"
AdvSceneSwitcher.condition.virtualCamera.state.stop="Sanal kamera durdu"
AdvSceneSwitcher.condition.virtualCamera.entry="{{states}}"
AdvSceneSwitcher.condition.filter="Filtrele"
AdvSceneSwitcher.condition.filter.type.active="Etkinleştirildi"
AdvSceneSwitcher.condition.filter.type.showing="Etkisizleştirildi"
AdvSceneSwitcher.condition.filter.type.settings="Ayarlar eşleşti"
AdvSceneSwitcher.condition.filter.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.filter.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.condition.filter.entry.line1="Açık {{sources}} {{filters}} {{conditions}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.sceneOrder="Sahne öğesi sırası"
AdvSceneSwitcher.condition.sceneOrder.type.above="Üstünde"
AdvSceneSwitcher.condition.sceneOrder.type.below="Altında"
AdvSceneSwitcher.condition.sceneOrder.type.position="konumunda"
AdvSceneSwitcher.condition.sceneOrder.positionInfo="Konum değeri en altta 0 ile başlar ve sahne gruplarındakiler dahil her sahne öğesi için bir artar."
AdvSceneSwitcher.condition.sceneOrder.entry="Açık{{scenes}}{{sources}}{{conditions}}{{sources2}}{{position}}"
AdvSceneSwitcher.condition.hotkey="Kısayol tuşu"
AdvSceneSwitcher.condition.hotkey.name="Makro tetik kısayol tuşu"
AdvSceneSwitcher.condition.hotkey.tip="Not: Bu kısayol tuşu için tuş atamalarını OBS ayarları penceresinde yapılandırabilirsiniz."
AdvSceneSwitcher.condition.hotkey.entry="İsim: {{name}}"
AdvSceneSwitcher.condition.replay="Tekrar oynatma arabelleği"
AdvSceneSwitcher.condition.replay.state.stopped="Tekrar oynatma arabelleği durdu"
AdvSceneSwitcher.condition.replay.state.started="Tekrar oynatma arabelleği başladı"
AdvSceneSwitcher.condition.replay.state.saved="Tekrar oynatma arabelleği kaydedildi"
AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Tarih"
AdvSceneSwitcher.condition.date.state.at="de"
AdvSceneSwitcher.condition.date.state.after="Sonra"
AdvSceneSwitcher.condition.date.state.before="Önce"
AdvSceneSwitcher.condition.date.state.between="Arasında"
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} Tarih bileşenini yoksay {{ignoreTime}} Zaman bileşenini yoksay"
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Tekrar her {{duration}} tarih eşleşmesinde"
AdvSceneSwitcher.condition.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.condition.sceneTransform.regex="Normal ifadeler kullanın"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="Açık{{scenes}}{{sources}} dönüşümle eşleşir"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Geçiş"
AdvSceneSwitcher.condition.transition.type.current="Geçerli geçiş türü"
AdvSceneSwitcher.condition.transition.type.duration="Mevcut geçiş süresi"
AdvSceneSwitcher.condition.transition.type.started="Geçiş başladı"
AdvSceneSwitcher.condition.transition.type.ended="Geçiş sona erdi"
AdvSceneSwitcher.condition.transition.type.transitionSource="İtibaren geçiş"
AdvSceneSwitcher.condition.transition.type.transitionTarget="Geçiş "
AdvSceneSwitcher.condition.transition.durationSuffix="saniyesi"
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
AdvSceneSwitcher.condition.sceneVisibility="Sahne öğesi görünürlüğü"
AdvSceneSwitcher.condition.sceneVisibility.type.shown="Gösterilen"
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="Gizli"
AdvSceneSwitcher.condition.sceneVisibility.entry="Açık {{scenes}}{{sources}}is{{conditions}}"
AdvSceneSwitcher.condition.studioMode="Stüdyo ingilizce"
AdvSceneSwitcher.condition.studioMode.state.active="Studyo modu Etkinleştirildi"
AdvSceneSwitcher.condition.studioMode.state.notActive="Studyo modu aktif değil"
AdvSceneSwitcher.condition.studioMode.state.previewScene="Önizleme sahnesi"
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
AdvSceneSwitcher.condition.openvr="Aç VR"
AdvSceneSwitcher.condition.errorStatus="Aç VR hatası: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD içinde ..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD mevcut {{xPos}} x {{yPos}} x {{zPos}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Sahne Degistirici"
AdvSceneSwitcher.action.scene.entry="Sahneyi {{scenes}} kullanarak {{transitions}} süresi olan {{duration}} saniye"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Hedef sahneye geçiş tamamlanana kadar bekleyin"
AdvSceneSwitcher.action.wait="Bekle"
AdvSceneSwitcher.action.wait.type.fixed="sabit"
AdvSceneSwitcher.action.wait.type.random="rastgele"
AdvSceneSwitcher.action.wait.entry.fixed="Şunu bekle {{waitType}} süre {{duration}}"
AdvSceneSwitcher.action.wait.entry.random="Şunu bekle {{waitType}} bu aralıktan {{duration}} buna {{duration2}}"
AdvSceneSwitcher.action.audio="Ses"
AdvSceneSwitcher.action.audio.type.mute="Sessiz"
AdvSceneSwitcher.action.audio.type.unmute="Ses açmak"
AdvSceneSwitcher.action.audio.type.sourceVolume="Kaynak ses seviyesini ayarla"
AdvSceneSwitcher.action.audio.type.masterVolume="Ana ses seviyesini ayarla"
AdvSceneSwitcher.action.audio.fade="{{fade}}karartmak {{duration}} saniye"
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="Kayıt"
AdvSceneSwitcher.action.recording.type.stop="Kayıt Durdur"
AdvSceneSwitcher.action.recording.type.start="Kayıt Başlat"
AdvSceneSwitcher.action.recording.type.pause="Kayıt Duraklat"
AdvSceneSwitcher.action.recording.type.unpause="Kayıt Duraklatma"
AdvSceneSwitcher.action.recording.pause.hint="Kayıt ayarlarınıza bağlı olarak kaydı duraklatamayabileceğinizi unutmayın."
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
AdvSceneSwitcher.action.replay="Tekrar arabelleği"
AdvSceneSwitcher.action.replay.type.stop="Tekrar arabelleğini durdur"
AdvSceneSwitcher.action.replay.type.start="Tekrar arabelleğini başlat"
AdvSceneSwitcher.action.replay.type.save="Tekrar arabelleğini kaydet"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Yayın"
AdvSceneSwitcher.action.streaming.type.stop="Yayın durdur"
AdvSceneSwitcher.action.streaming.type.start="Yayın başlat"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Çalıştır"
AdvSceneSwitcher.action.run.arguments="Argümanlar:"
AdvSceneSwitcher.action.run.addArgument="Argüman ekle"
AdvSceneSwitcher.action.run.addArgumentDescription="Yeni Argüman ekle:"
AdvSceneSwitcher.action.run.entry="Çalıştır {{filePath}}"
AdvSceneSwitcher.action.sceneVisibility="Sahne öğesi görünürlüğü"
AdvSceneSwitcher.action.sceneVisibility.type.show="Göster"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Gizle"
AdvSceneSwitcher.action.sceneVisibility.type.source="Kayıt"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Herhangi"
AdvSceneSwitcher.action.sceneVisibility.entry="Açık{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.filter="Filtrele"
AdvSceneSwitcher.action.filter.type.enable="Etkin"
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
AdvSceneSwitcher.action.filter.type.settings="Ayarları yap"
AdvSceneSwitcher.action.filter.entry="Açık {{sources}} {{actions}} {{filters}}"
AdvSceneSwitcher.action.filter.getSettings="Mevcut ayarları al"
AdvSceneSwitcher.action.source="Kaynak"
AdvSceneSwitcher.action.source.type.enable="Etkin"
AdvSceneSwitcher.action.source.type.disable="Etkisiz"
AdvSceneSwitcher.action.source.type.settings="Ayarları yap"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.warning="Uyarı: Kaynakların global olarak etkinleştirilmesi ve devre dışı bırakılması, OBS UI tarafından kontrol edilemez"
AdvSceneSwitcher.action.source.getSettings="Mevcut Ayarları yap"
AdvSceneSwitcher.action.media="Medya"
AdvSceneSwitcher.action.media.type.play="Oynat"
AdvSceneSwitcher.action.media.type.pause="Duraklat"
AdvSceneSwitcher.action.media.type.stop="Dur"
AdvSceneSwitcher.action.media.type.restart="Yeniden Başlat"
AdvSceneSwitcher.action.media.type.next="Sonraki"
AdvSceneSwitcher.action.media.type.previous="Önceki"
AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}"
AdvSceneSwitcher.action.macro="Makro"
AdvSceneSwitcher.action.macro.type.pause="Duraklat"
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
AdvSceneSwitcher.action.pluginState.type.import="Ayarları şuradan içe aktar:"
AdvSceneSwitcher.action.pluginState.importWarning="Not: Ayarlar penceresi açılırken eylem göz ardı edilecektir."
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
AdvSceneSwitcher.action.virtualCamera="Sanal Kamera"
AdvSceneSwitcher.action.virtualCamera.type.stop="Sanal Kamerayı Durdur"
AdvSceneSwitcher.action.virtualCamera.type.start="Sanal Kamerayı Başlat"
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
AdvSceneSwitcher.action.hotkey="Kısayol tuşu"
AdvSceneSwitcher.action.hotkey.leftShift="Sol shift"
AdvSceneSwitcher.action.hotkey.rightShift="Sağ Shift"
AdvSceneSwitcher.action.hotkey.leftCtrl="Sol Ctrl"
AdvSceneSwitcher.action.hotkey.rightCtrl="Sağ Ctrl"
AdvSceneSwitcher.action.hotkey.leftAlt="Sol Alt"
AdvSceneSwitcher.action.hotkey.rightAlt="Sağ Alt"
AdvSceneSwitcher.action.hotkey.leftMeta="Sol Meta"
AdvSceneSwitcher.action.hotkey.rightMeta="Sağ Meta"
AdvSceneSwitcher.action.hotkey.onlyOBS="Tuşa basımı yalnızca OBS'ye gönder"
AdvSceneSwitcher.action.hotkey.disabled="Tuşa basma simülasyonu yapılamıyor - işlevsellik devre dışı!"
AdvSceneSwitcher.action.hotkey.entry="Bas {{keys}} uygun {{duration}} millisaniye"
AdvSceneSwitcher.action.sceneOrder="Sahne öğesi sırası"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Yukarı taşı"
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.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}transform{{sources}}"
AdvSceneSwitcher.action.file="Dosya"
AdvSceneSwitcher.action.file.type.write="Yaz"
AdvSceneSwitcher.action.file.type.append="Ekle"
AdvSceneSwitcher.action.file.entry="{{actions}} {{filePath}}:"
AdvSceneSwitcher.action.previewScene="Önizleme sahnesini değiştir"
AdvSceneSwitcher.action.previewScene.entry="Önizleme sahnesini şu şekilde değiştir: {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="Sahneyi değiştir (Studyo modu)"
AdvSceneSwitcher.action.SceneSwap.entry="Stüdyo modunda önizleme ve program sahnesini değiştir"
AdvSceneSwitcher.action.transition="Geçiş"
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}Geçiş türünü ayarla {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}Geçiş süresini şuna ayarla: {{duration}}saniyeler"
AdvSceneSwitcher.action.timer="Zamanlayıcı"
AdvSceneSwitcher.action.timer.type.pause="Duraklat"
AdvSceneSwitcher.action.timer.type.continue="Devam et"
AdvSceneSwitcher.action.timer.type.reset="Sıfırla"
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Kalan süreyi ayarla"
AdvSceneSwitcher.action.timer.entry="{{timerAction}} zamanlayıcılar açık {{macros}} {{duration}}"
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.systray.entry="Bildirimleri göster: {{message}}"
AdvSceneSwitcher.action.screenshot="Ekran görüntüsü"
AdvSceneSwitcher.action.screenshot.mainOutput="OBS'nin ana çıkışı"
AdvSceneSwitcher.action.screenshot.entry="Ekran görüntüsü {{sources}}"
AdvSceneSwitcher.action.profile="Profil"
AdvSceneSwitcher.action.profile.entry="Aktif profili şununla değiştir: {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Sahne koleksiyonu"
AdvSceneSwitcher.action.sceneCollection.entry="Aktif sahne koleksiyonunu şu şekilde değiştir: {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="Not: Değişen sahne koleksiyonu sahne değiştirici ayarlarını da yeniden yükleyeceğinden, bundan sonraki eylemler yürütülmeyecektir.\n Ayarlar penceresi açılırken sahne toplama eylemi yok sayılacaktır."
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Geçiş"
AdvSceneSwitcher.transitionTab.setTransitionBy="Geçişleri değiştirirken:"
AdvSceneSwitcher.transitionTab.transitionOverride="Geçiş geçersiz kılmaları ayarla"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="En az bir seçenek etkinleştirilmelidir: \n\n - Geçiş geçersiz kılmalarını kullan \n\n - Etkin geçiş türünü değiştir"
AdvSceneSwitcher.transitionTab.transitionForAToB="Sahne A'dan sahne B'ye otomatik sahne geçişi için geçişi kullanın"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body><p>Bu ayarlar <span style=\"font-style:italic;\">yalnızca</span>, sahne değiştiricinin neden olduğu geçişleri etkiler - <a href=\ Göz atın "https://obsproject.com/forum/resources/transition-table.1174/\"><span style=\" text-decoration: underline; color:#268bd2;\">Geçiş Tablosu</span></ a> bunu manuel sahne değişiklikleri için yapılandırmak istiyorsanız.<br/>Burada tanımlanan ayarlar, sahne değiştiricide başka bir yerde yapılandırılmış geçiş ayarlarına göre önceliklidir.<br/><br/>Yeni bir giriş eklemek için aşağıdaki artı simgesini tıklayın. .</p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Sahne aktifse geçişi değiştir"
AdvSceneSwitcher.transitionTab.entry="Şuradan geçiş yap {{scenes}} buna {{scenes2}} kullanarak {{transitions}} süresinde {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Ne zaman sahne {{scenes}} aktifse, varsayılan sahne geçişini değiştir{{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Bir giriş eklemek için artı sembolüne tıklayın."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Geçişi değiştirmek {{defTransitionDelay}} sahne değişikliğinden sonra."
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="Gecikme, bir geçiş devam ederken geçiş türü değiştirilirse meydana gelebilecek iptal edilen sahne geçişlerini önlemek için kullanılır."
; Pause Scenes Tab
AdvSceneSwitcher.pauseTab.title="Duraklat"
AdvSceneSwitcher.pauseTab.pauseOnScene="Sahne Değiştiriciyi sahnede duraklatın"
AdvSceneSwitcher.pauseTab.pauseInFocus1="Sahne Değiştiriciyi duraklat şu durumda: "
AdvSceneSwitcher.pauseTab.pauseInFocus2="odakta"
AdvSceneSwitcher.pauseTab.pauseTypeScene="sahne aktif"
AdvSceneSwitcher.pauseTab.pauseTypeWindow="pencere odakta"
AdvSceneSwitcher.pauseTab.pauseTargetAll="tümü"
AdvSceneSwitcher.pauseTab.pauseEntry="Duraklat {{pauseTargets}} kontrol edildiğinde {{pauseTypes}} {{scenes}} {{windows}}"
AdvSceneSwitcher.pauseTab.help="Bu sekmede, bir sahne etkinse veya pencere odaktaysa bireysel geçiş yöntemlerini duraklatmak için yapılandırabilirsiniz.\ve Devam etmek için vurgulanan artı sembolüne tıklayın."
; Window Title Tab
AdvSceneSwitcher.windowTitleTab.title="Başlık"
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Doğrudan pencere başlıklarını veya geçerli normal ifadeyi girin. <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span'i kullanarak normal ifadeler için söz dizimini ve eşleşmeleri kontrol edebilirsiniz. ></a></p></body></html>"
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Bu pencere adını yoksay"
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
AdvSceneSwitcher.windowTitleTab.fullscreen="tam ekran ise"
AdvSceneSwitcher.windowTitleTab.maximized="maksimize edilirse"
AdvSceneSwitcher.windowTitleTab.focused="eğer odaklanmışsa"
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
AdvSceneSwitcher.windowTitleTab.windowsHelp="Çalışan uygulamaların pencere başlığına göre sahneler arasında geçiş yapın.\nAşağıdaki ek koşullar seçilebilir:\nPencere Tam Ekrandır\nPencere büyütülür\nPencere odaklanılır\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="Bir pencere başlığı yoksayılırsa, sahne değiştirici, önceden seçilen pencere hala odaktaymış gibi davranacaktır.\nBu, sık sık farklı bir pencereye geçerseniz, sahne değişikliğini tetiklemeyecek olan sahne geçişlerinden kaçınmanıza olanak tanır.\ n\nBir pencere seçin veya yukarıya bir pencere başlığı girin ve listeye eklemek için aşağıdaki artı sembolüne tıklayın."
; Executable Tab
AdvSceneSwitcher.executableTab.title="yürütülebilir"
AdvSceneSwitcher.executableTab.implemented="dasOven tarafından uygulandı"
AdvSceneSwitcher.executableTab.requiresFocus="sadece odaklanmışsa"
AdvSceneSwitcher.executableTab.entry="Bu {{processes}} çalışıyorsa değiştir {{scenes}} kullanılır {{transitions}} {{requiresFocus}}"
AdvSceneSwitcher.executableTab.help="Bu sekme, bir işlem çalışıyorsa sahneleri otomatik olarak değiştirmenize olanak tanır.\nBu, pencere adının değişebileceği veya bilinmediği durumlarda faydalı olabilir.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Screen Region Tab
AdvSceneSwitcher.screenRegionTab.title="Bölge"
AdvSceneSwitcher.screenRegionTab.currentPosition="İmleç şu anda şu konumda:"
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Kılavuz çerçeveleri göster"
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Kılavuz çerçeveleri gizle"
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="Seçim yok"
AdvSceneSwitcher.screenRegionTab.entry="Eğer imleç şuanda böyleyse {{minX}} {{minY}} x {{maxX}} {{maxY}} şuna çevir {{scenes}} kullanarak {{transitions}} sürece {{excludeScenes}}"
AdvSceneSwitcher.screenRegionTab.help="Bu sekme, fare imlecinizin geçerli konumuna göre sahneleri otomatik olarak değiştirmenize olanak tanır.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Media Tab
AdvSceneSwitcher.mediaTab.title="Medya"
AdvSceneSwitcher.mediaTab.implemented="Exelero tarafından uygulandı"
AdvSceneSwitcher.mediaTab.states.none="Yok"
AdvSceneSwitcher.mediaTab.states.playing="Oynamak"
AdvSceneSwitcher.mediaTab.states.opening="Açmak"
AdvSceneSwitcher.mediaTab.states.buffering="Arabelleğe alma"
AdvSceneSwitcher.mediaTab.states.Paused="Duraklatıldı"
AdvSceneSwitcher.mediaTab.states.stopped="Durduruldu"
AdvSceneSwitcher.mediaTab.states.ended="Bitti"
AdvSceneSwitcher.mediaTab.states.error="Hata"
AdvSceneSwitcher.mediaTab.states.playedToEnd="Sonuna kadar oynandı"
AdvSceneSwitcher.mediaTab.states.any="Herhangi"
AdvSceneSwitcher.mediaTab.timeRestriction.none="Yok"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Zaman daha kısa"
AdvSceneSwitcher.mediaTab.timeRestriction.longer="Zaman daha uzun"
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Kalan süre daha kısa"
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Kalan süre daha uzun"
AdvSceneSwitcher.mediaTab.entry="Kaynak {{mediaSources}} durumu {{states}} ve {{timeRestrictions}} {{time}} şuna çevir {{scenes}} kullanarak {{transitions}}"
AdvSceneSwitcher.mediaTab.help="Bu sekme, medya kaynaklarının durumuna göre sahneler arasında geçiş yapmanızı sağlar.\nÖrneğin, seçilen medya kaynaklı oynatmayı bitirdiğinde otomatik olarak önceki sahneye dönebilirsiniz.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; File Tab
AdvSceneSwitcher.fileTab.title="Dosya"
AdvSceneSwitcher.fileTab.readWriteSceneFile="Oku / Sahneden yazdır / Dosyaya"
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Mevcut sahnenin adını bu dosyaya yazın:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFile="Dosya girişine göre sahnelerin değiştirilmesini etkinleştirin"
AdvSceneSwitcher.fileTab.switchSceneNameInputFile="Bu dosyadan geçiş yapılacak sahne adını okuyun:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFileContent="Dosya içeriğine göre sahneyi değiştir"
AdvSceneSwitcher.fileTab.remoteFileWarning="Uzak seçeneğini seçerseniz, sahne değiştiricinin Genel sekmesinde belirtildiği gibi her x ms'de bir uzak konuma erişmeye çalışacağını lütfen unutmayın!"
AdvSceneSwitcher.fileTab.remoteFileWarning1="Sahne değiştiricinin her seferinde uzak konuma erişmeye çalışacağını unutmayın."
AdvSceneSwitcher.fileTab.remoteFileWarning2="ms"
AdvSceneSwitcher.fileTab.libcurlWarning="libcurl yüklenemedi! Uzak dosyalara erişim mümkün olmayacak!"
AdvSceneSwitcher.fileTab.selectWrite="Yazmak için bir dosya seçin..."
AdvSceneSwitcher.fileTab.selectRead="Okumak için bir dosya seçin..."
AdvSceneSwitcher.fileTab.textFileType="Text dosyası (*.txt)"
AdvSceneSwitcher.fileTab.anyFileType="Herhangi bir dosya türü (*.*)"
AdvSceneSwitcher.fileTab.remote="uzak dosya"
AdvSceneSwitcher.fileTab.local="yerel dosya"
AdvSceneSwitcher.fileTab.useRegExp="normal ifadeler kullan (desen eşleştirme)"
AdvSceneSwitcher.fileTab.checkfileContentTime="değişiklik tarihi değiştiyse"
AdvSceneSwitcher.fileTab.checkfileContent="İçerik değiştiyse"
AdvSceneSwitcher.fileTab.entry="Suna dönüştür {{scenes}} kullanıp {{transitions}} içerik şunlarla {{fileType}} {{filePath}} {{browseButton}} eşleşirse:"
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.fileTab.help="Bu sekme, uzak veya yerel dosyaların içeriğine göre sahneleri otomatik olarak değiştirmenize olanak tanır.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Random Tab
AdvSceneSwitcher.randomTab.title="Rastgele"
AdvSceneSwitcher.randomTab.randomDisabledWarning="İşlevsellik devre dışı - Etkinleştirmek için Genel sekmesinde \"Herhangi bir geçiş koşulu karşılanmazsa Rastgele sekmesinde herhangi bir sahneye geç\"i seçin"
AdvSceneSwitcher.randomTab.entry="Herhangi bir geçiş koşulu karşılanmıyorsa, geçiş yapın {{scenes}} kullan {{transitions}} için {{delay}}"
AdvSceneSwitcher.randomTab.help="Sahne değiştirici, yapılandırılan süre için geçiş yapmak için bu sekmede rastgele bir giriş seçecektir.\nAynı girişin arka arkaya iki kez seçilmeyeceğini unutmayın.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Time Tab
AdvSceneSwitcher.timeTab.title="Zaman"
AdvSceneSwitcher.timeTab.anyDay="Herhangi bir günde"
AdvSceneSwitcher.timeTab.mondays="Pazartesi günleri"
AdvSceneSwitcher.timeTab.tuesdays="Salı günleri"
AdvSceneSwitcher.timeTab.wednesdays="Çarşamba günleri"
AdvSceneSwitcher.timeTab.thursdays="Perşembe günleri"
AdvSceneSwitcher.timeTab.fridays="Cuma günleri"
AdvSceneSwitcher.timeTab.saturdays="Cumartesi günleri"
AdvSceneSwitcher.timeTab.sundays="Pazar günleri"
AdvSceneSwitcher.timeTab.afterstart="Akış/kayıt başladıktan sonra"
AdvSceneSwitcher.timeTab.afterstart.tip="Akışın / kaydın başlangıcına göre zaman kullanılacaktır"
AdvSceneSwitcher.timeTab.entry="{{triggers}} da/de {{time}} Değiştir {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.timeTab.help="Bu sekme, geçerli yerel saate göre otomatik olarak farklı bir sahneye geçmenizi sağlar.\n\nSahne değiştiricinin sahneleri yalnızca tam olarak belirttiğiniz saatte değiştireceğini unutmayın.\nGenel'de öncelik ayarlarını yapılandırdığınızdan emin olun. sekmesini beğeninize göre seçin, böylece daha yüksek önceliğe sahip diğer geçiş yöntemleri nedeniyle seçilen zaman noktası kaçırılmaz.\n\nDevam etmek için vurgulanan artı sembolüne tıklayın."
; Idle Tab
AdvSceneSwitcher.idleTab.title="Boşta"
AdvSceneSwitcher.idleTab.enable="Boşta Algılamayı Etkinleştir"
AdvSceneSwitcher.idleTab.idleswitch="Sonra {{duration}} klavye veya fare girişi olmadan sahneye geçiş {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="eğer geçiş yapmayın"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="odakta"
; Scene Sequence Tab
AdvSceneSwitcher.sceneSequenceTab.title="Sıra"
AdvSceneSwitcher.sceneSequenceTab.description="Otomatik sahne geçişleri dizisi, sahne değiştiriciyi duraklatarak/durdurarak veya manuel olarak farklı bir sahneye geçerek iptal edilebilir."
AdvSceneSwitcher.sceneSequenceTab.save="Sahne dizilerini dosyaya kaydet"
AdvSceneSwitcher.sceneSequenceTab.load="Dosyadan sahne dizilerini yükle"
AdvSceneSwitcher.sceneSequenceTab.saveTitle="Sahne Sırasını dosyaya kaydet..."
AdvSceneSwitcher.sceneSequenceTab.loadTitle="Sahne Sırasını okumak için bir dosya seçin ..."
AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcher ayarları içe aktaramadı!"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Gelişmiş Sahne Değiştirici ayarları başarıyla içe aktarıldı!"
AdvSceneSwitcher.sceneSequenceTab.fileType="Text dosyaları (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="Kesilebilir"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Diğer anahtarlama yöntemlerinin bu sahne dizisini kesmesine izin verilir"
AdvSceneSwitcher.sceneSequenceTab.entry=" {{startScenes}} aktif olduğunda değiştir {{scenes}} sonra {{delay}} kullan {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Sırayı Uzat"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Sonra {{delay}} çevirmek {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="Bu sekme, bir sahne yapılandırılmış bir süre boyunca etkinse otomatik olarak farklı bir sahneye geçmenizi sağlar.\nÖrneğin, iki sahne arasında otomatik olarak ileri ve geri geçiş yapabilirsiniz.\n\nVurgulanan artı sembolüne tıklayarak devam et."
; Audio Tab
AdvSceneSwitcher.audioTab.title="Ses"
AdvSceneSwitcher.audioTab.condition.above="üstünde"
AdvSceneSwitcher.audioTab.condition.below="altında"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="kaynak etkin olmadığı sürece"
AdvSceneSwitcher.audioTab.entry="Ses Hacmi{{audioSources}} durumu {{condition}} {{volumeWidget}} için {{duration}} saniye geçiş {{scenes}} kullanarak {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Birden çok giriş eşleşirse..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... için {{duration}} saniye geçiş {{scenes}} kullan {{transitions}}"
AdvSceneSwitcher.audioTab.help="Bu sekme, kaynakların ses düzeyine göre sahneler arasında geçiş yapmanızı sağlar.\nÖrneğin, mikrofonunuzun ses düzeyi belirli bir eşiğe ulaşırsa otomatik olarak farklı bir sahneye geçebilirsiniz.\n\nDevam etmek için vurgulanan artı simgesini tıklayın."
; Video Tab
AdvSceneSwitcher.videoTab.title="Video"
AdvSceneSwitcher.videoTab.getScreenshot="Seçilen giriş için ekran görüntüsü al"
AdvSceneSwitcher.videoTab.getScreenshotHelp="Seçili olan girişin video kaynağının Ekran Görüntüsünü alın ve otomatik olarak hedef görüntü olarak ayarlayın"
AdvSceneSwitcher.videoTab.condition.match="tam olarak eşleşir"
AdvSceneSwitcher.videoTab.condition.match.tooltip="Tam eşleşme, hedef ve kaynak görüntünün aynı çözünürlükte olmasını gerektirir.\nAyrıca her bir pikselin eşleşmesi gerekir, bu nedenle sıkıştırma kullanan görüntü biçimlerinin (ör. .JPG) kullanılması önerilmez!"
AdvSceneSwitcher.videoTab.condition.differ="eşleşmiyor"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="değişmedi"
AdvSceneSwitcher.videoTab.condition.hasChanged="değişti"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="Kaynak etkin olmadığı sürece"
AdvSceneSwitcher.videoTab.entry="Zaman {{videoSources}} {{condition}} {{filePath}} {{browseButton}} için {{duration}} şuna dönüştür {{scenes}} kullan {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Bu sekme, seçilen kaynakların mevcut video çıkışına göre sahneler arasında geçiş yapmanızı sağlar.<br/><a href=\"https:// adresini kontrol ettiğinizden emin olun. obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> bu işlevin daha da iyi bir şekilde uygulanması için.<br/><br/> Devam etmek için vurgulanan artı simgesini tıklayın.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Ağ"
AdvSceneSwitcher.networkTab.description="Bu sekme, başka bir OBS örneğinin etkin sahnesini uzaktan kontrol etmenizi sağlar.\nSahne adlarının tüm OBS örneklerinde tam olarak eşleşmesi gerektiğini lütfen unutmayın."
AdvSceneSwitcher.networkTab.warning="Sunucuyu yerel bir ağın dışında çalıştırmak, üçüncü tarafların aktif sahneyi okumasına izin verecektir."
AdvSceneSwitcher.networkTab.DisabledWarning="Bu işlevsellik, obs-websocket eklentisini paralel olarak çalıştırırken kitaplık uyumsuzlukları nedeniyle ne yazık ki macOS'ta devre dışı bırakılmak zorunda kaldı."
AdvSceneSwitcher.networkTab.server="Sunucuyu başlat (Bağlı tüm istemcilere sahne değiştirme mesajları gönderir)"
AdvSceneSwitcher.networkTab.server.port="Port"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Sunucuyu yalnızca IPv4 kullanacak şekilde kilitleyin"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Sahne değişiklikleri için mesaj gönder"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Yalnızca otomatik sahne anahtarları için mesaj gönder"
AdvSceneSwitcher.networkTab.server.sendPreview="Stüdyo modunda çalışırken önizleme sahnesi değişikliği için mesajlar gönderin"
AdvSceneSwitcher.networkTab.startFailed.message="WebSockets sunucusu başlatılamadı, bunun nedeni şunlar olabilir:\n - %1 TCP bağlantı noktası şu anda bu sistemde başka bir yerde, muhtemelen başka bir uygulama tarafından kullanılıyor olabilir. WebSocket sunucu ayarlarında farklı bir TCP bağlantı noktası ayarlamayı deneyin veya bu bağlantı noktasını kullanabilecek herhangi bir uygulamayı durdurun.\n - Hata mesajı: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Şu anki durum"
AdvSceneSwitcher.networkTab.server.status.notRunning="Çalışmıyor"
AdvSceneSwitcher.networkTab.server.status.starting="Başlangıç"
AdvSceneSwitcher.networkTab.server.status.running="Çalışıyor"
AdvSceneSwitcher.networkTab.server.restart="Sunucuyu yeniden başlat"
AdvSceneSwitcher.networkTab.client="İstemciyi başlat (Sahne değiştirme mesajlarını alır)"
AdvSceneSwitcher.networkTab.client.address="Ana makine adı veya IP adresi"
AdvSceneSwitcher.networkTab.client.port="Port"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Şu anki durum"
AdvSceneSwitcher.networkTab.client.status.disconnected="Bağlantı kesildi"
AdvSceneSwitcher.networkTab.client.status.connecting="Bağlanıyor"
AdvSceneSwitcher.networkTab.client.status.connected="Bağlandı"
AdvSceneSwitcher.networkTab.client.reconnect="Yeniden bağlanmaya zorla"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Sahne Grubu"
AdvSceneSwitcher.sceneGroupTab.list="Sahne Grupları"
AdvSceneSwitcher.sceneGroupTab.edit="Sahne Gruplarını Düzenle"
AdvSceneSwitcher.sceneGroupTab.edit.name="İsim:"
AdvSceneSwitcher.sceneGroupTab.edit.type="Tip: {{type}}"
AdvSceneSwitcher.sceneGroupTab.type.count="Saymak"
AdvSceneSwitcher.sceneGroupTab.type.time="Zaman"
AdvSceneSwitcher.sceneGroupTab.type.random="Rastgele"
AdvSceneSwitcher.sceneGroupTab.edit.count="Sonra listedeki bir sonraki sahneye ilerleyin {{count}} karşılama"
AdvSceneSwitcher.sceneGroupTab.edit.time="Sonra listedeki bir sonraki sahneye ilerleyin {{time}} geçtiyse"
AdvSceneSwitcher.sceneGroupTab.edit.random="Listeden rastgele bir sonraki sahneyi seçin"
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Sahne listesinin sonuna ulaşılırsa baştan başlayın"
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Sahne ekle"
AdvSceneSwitcher.sceneGroupTab.add="Sahne Grubu Ekle"
AdvSceneSwitcher.sceneGroupTab.defaultname="Sahne Grubu %1"
AdvSceneSwitcher.sceneGroupTab.exists="Sahne Grubu veya Sahne adı zaten var"
AdvSceneSwitcher.sceneGroupTab.help="Sahne Grupları, normal bir sahne gibi bir hedef olarak seçilebilir.\n\nAdından da anlaşılacağı gibi, bir sahne grubu birden fazla sahneden oluşan bir koleksiyondur.\nSahne grubu, yapılandırılmış ayarlara bağlı olarak kendisine atanan sahneler listesinde ilerleyecektir. sağ tarafta bulunabilir.\n\nSahne grubunu listedeki bir sonraki sahneye geçecek şekilde yapılandırabilirsiniz:\nSahne grubu birkaç kez hedef olarak seçildikten sonra.\nBelirli bir süre sonra geçti.\nVeya rastgele.\n\nÖrneğin, sahneleri içeren bir sahne grubu ...\nSahne 1\nSahne 2\nSahne 3 \n... ilk seçildiğinde \"Sahne 1\"i etkinleştirecek bir hedef olarak.\nİkinci kez \"Sahne 2\"yi etkinleştirecek.\nKalan zamanlar \"Scene 3\" etkinleştirilecek.\n\nYeni bir sahne grubu eklemek için aşağıdaki vurgulanan artı sembolüne tıklayın."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Soldan değiştirmek istediğiniz sahne grubunu seçin.\n\nYukarıdaki sahneyi seçip aşağıdaki artı simgesini tıklayarak bu sahne grubuna eklemek için bir sahne seçin.\n\nAynı sahneye birden fazla sahne eklenebilir grup."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Sahne Tetikleyicileri"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--tetikleyiciyi seçin--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="aktif"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="aktif değil"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="uzaklaştı"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--eylemi seç--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="Kayda başla"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="Kaydı duraklat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="kaydı duraklatma"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="Kaydetmeyi bırak"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Akışı durdur"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Akışı başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Yeniden oynatma arabelleğini başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Tekrar arabelleğini durdur"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Kaynağı sessize al"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Kaynağı sesi aç"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Sahne değiştiriciyi başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Sahne değiştiriciyi durdur"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="Sanal kamerayı başlat"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="Sanal kamerayı durdur"
AdvSceneSwitcher.sceneTriggerTab.entry="Şundan {{scenes}} {{triggers}} {{actions}} {{audioSources}} sonra {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Bu sekme, kaydı veya akışı durdurma gibi sahne değişikliklerinde eylemleri tetiklemenize olanak tanır."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Advanced Scene Switcher'ı başlatın"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Gelişmiş Sahne Değiştiriciyi Durdurun"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Gelişmiş Sahne Değiştirici için Başlat/Durdur arasında geçiş yapın"
AdvSceneSwitcher.hotkey.macro.pause="%1 makrosunu duraklat"
AdvSceneSwitcher.hotkey.macro.unpause="%1 makrosunu duraklatma"
AdvSceneSwitcher.hotkey.macro.togglePause="%1 makrosunun duraklatmasını aç/kapat"
AdvSceneSwitcher.askBackup="Advanced Scene Switcher'ın yeni bir sürümü algılandı.\nEski ayarların bir yedeği oluşturulsun mu?"
AdvSceneSwitcher.askForMacro="Makro seç {{macroSelection}}"
AdvSceneSwitcher.close="Kapat"
AdvSceneSwitcher.browse="Araştır"
AdvSceneSwitcher.selectScene="--sahne seç--"
AdvSceneSwitcher.selectPreviousScene="Önceki Sahne"
AdvSceneSwitcher.selectCurrentScene="Mevcut Sahne"
AdvSceneSwitcher.selectAnyScene="Herhangi bir Sahne"
AdvSceneSwitcher.currentTransition="Mevcut Geçiş"
AdvSceneSwitcher.anyTransition="Herhangi Bir Geçiş"
AdvSceneSwitcher.selectTransition="--geçişi seç--"
AdvSceneSwitcher.selectWindow="--pencereyi seç--"
AdvSceneSwitcher.selectSource="--kaynak Seç--"
AdvSceneSwitcher.selectAudioSource="--ses kaynağını seçin--"
AdvSceneSwitcher.selectVideoSource="--video kaynağını seçin--"
AdvSceneSwitcher.selectMediaSource="--medya kaynağını seçin--"
AdvSceneSwitcher.selectProcess="--işlemi seç--"
AdvSceneSwitcher.selectFilter="--filtre seç--"
AdvSceneSwitcher.selectMacro="--makroyu seçin--"
AdvSceneSwitcher.selectItem="--Öğeyi seçin--"
AdvSceneSwitcher.selectProfile="--profil seç--"
AdvSceneSwitcher.selectSceneCollection="--sahne koleksiyonunu seç--"
AdvSceneSwitcher.enterPath="--yolu girin--"
AdvSceneSwitcher.enterText="--metni girin--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="geçersiz girişler kaydedilmeyecek"
AdvSceneSwitcher.selectWindowTip="\"OBS\" OBS penceresini belirlemek için \nKullan \"Görev değiştirme\"için ALT + TAB"
AdvSceneSwitcher.sceneItemSelection.all="Hepsi"
AdvSceneSwitcher.sceneItemSelection.any="Herhangi"
AdvSceneSwitcher.status.active="Aktif"
AdvSceneSwitcher.status.inactive="İnaktif"
AdvSceneSwitcher.running="Eklenti çalışıyor"
AdvSceneSwitcher.stopped="Eklenti durdu"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Gelişmiş Sahne Değiştirici ilk kez başlatılıyor gibi görünüyor.<br>Lütfen <a href=\"https://github.com/ adresine bir göz atın. WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> için kılavuzlar ve örnekler listesi.<br>Yapmayın. eklentinin <a href=\"https://obsproject.com/forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:# sayfasında soru sormaktan çekinmeyin OBS forumlarında 268bd2;\">konu</span></a>!</p></body></html>"
AdvSceneSwitcher.unit.milliseconds="millisaniye"
AdvSceneSwitcher.unit.secends="saniye"
AdvSceneSwitcher.unit.minutes="dakika"
AdvSceneSwitcher.unit.hours="saat"
AdvSceneSwitcher.duration.condition.none="Zaman kısıtlaması yok"
AdvSceneSwitcher.duration.condition.more="En azından"
AdvSceneSwitcher.duration.condition.equal="Tam olarak"
AdvSceneSwitcher.duration.condition.less="En çoğu"

View File

@@ -325,6 +325,54 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_26">
<item>
<widget class="QCheckBox" name="highlightExecutedMacros">
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.highlightExecutedMacros</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_19">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_24">
<item>
<widget class="QCheckBox" name="hideLegacyTabs">
<property name="text">
<string>AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_16">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@@ -536,6 +584,15 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
@@ -643,10 +700,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -666,10 +719,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -701,7 +750,7 @@
<property name="title">
<string>AdvSceneSwitcher.macroTab.edit</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_34">
<layout class="QVBoxLayout" name="verticalLayout_38">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="0,0,0,0,0,0">
<item>
@@ -751,204 +800,290 @@
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter">
<widget class="QSplitter" name="macroSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QScrollArea" name="macroConditions">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>792</width>
<height>218</height>
</rect>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QVBoxLayout" name="macroConditionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_35" stretch="10,1">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_27">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="macroEditConditionHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.editConditionHelp</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="macroEditConditionLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QPushButton" name="conditionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="conditionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QPushButton" name="conditionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="conditionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_135">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_48">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_134">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="conditionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="conditionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QScrollArea" name="macroActions">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>792</width>
<height>218</height>
</rect>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="macroActionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33" stretch="10,1">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_25">
<item row="0" column="0">
<widget class="QLabel" name="macroEditActionHelp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>AdvSceneSwitcher.macroTab.editActionHelp</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="macroEditActionLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QPushButton" name="actionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</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>
</layout>
</item>
</layout>
</widget>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QPushButton" name="actionAdd">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">addIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionRemove">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">removeIconSmall</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_133">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_47">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_132">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="actionUp">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">upArrowIconSmall</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="actionDown">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">downArrowIconSmall</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>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
@@ -1123,10 +1258,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1146,10 +1277,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1299,10 +1426,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1322,10 +1445,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1476,10 +1595,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1499,10 +1614,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1666,10 +1777,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1689,10 +1796,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1976,10 +2079,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -1999,10 +2098,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2225,10 +2320,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2248,10 +2339,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2393,10 +2480,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2416,10 +2499,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2675,10 +2754,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2698,10 +2773,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2939,10 +3010,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -2962,10 +3029,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -3237,10 +3300,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -3260,10 +3319,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -3459,10 +3514,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -3482,10 +3533,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -3651,10 +3698,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -3674,10 +3717,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -4031,10 +4070,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -4054,10 +4089,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -4251,10 +4282,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -4274,10 +4301,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -4425,10 +4448,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
@@ -4448,10 +4467,6 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>

View File

@@ -20,6 +20,8 @@ SwitcherData *GetSwitcher()
return switcher;
}
AdvSceneSwitcher *AdvSceneSwitcher::window = nullptr;
/******************************************************************************
* Create the Advanced Scene Switcher settings window
******************************************************************************/
@@ -39,6 +41,7 @@ AdvSceneSwitcher::~AdvSceneSwitcher()
{
if (switcher) {
switcher->settingsWindowOpened = false;
switcher->lastOpenedTab = ui->tabWidget->currentIndex();
}
}
@@ -85,7 +88,9 @@ void AdvSceneSwitcher::loadUI()
setupTriggerTab();
setupMacroTab();
setDeprecationWarnings();
setTabOrder();
setCurrentTab();
restoreWindowGeo();
checkFirstTimeSetup();
@@ -340,7 +345,7 @@ bool SwitcherData::checkForMatch(OBSWeakSource &scene,
return match;
}
void switchScene(const sceneSwitchInfo &sceneSwitch)
void switchScene(const sceneSwitchInfo &sceneSwitch, bool force)
{
if (!sceneSwitch.scene && switcher->verbose) {
blog(LOG_INFO, "nothing to switch to");
@@ -350,7 +355,7 @@ void switchScene(const sceneSwitchInfo &sceneSwitch)
obs_source_t *source = obs_weak_source_get_source(sceneSwitch.scene);
obs_source_t *currentSource = obs_frontend_get_current_scene();
if (source && source != currentSource) {
if (source && (source != currentSource || force)) {
transitionData currentTransitionData;
setNextTransition(sceneSwitch, currentSource,
currentTransitionData);
@@ -574,6 +579,18 @@ void setStreamStopping()
std::chrono::high_resolution_clock::now();
}
void handleExit()
{
switcher->obsIsShuttingDown = true;
if (switcher && switcher->obsIsShuttingDown &&
switcher->shutdownConditionCount) {
switcher->Stop();
switcher->checkMacros();
switcher->runMacros();
}
FreeSceneSwitcher();
}
// Note to future self:
// be careful using switcher->m here as there is potential for deadlocks when using
// frontend functions such as obs_frontend_set_current_scene()
@@ -585,7 +602,7 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
switch (event) {
case OBS_FRONTEND_EVENT_EXIT:
FreeSceneSwitcher();
handleExit();
break;
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
handleSceneChange();
@@ -652,8 +669,6 @@ void LoadPlugins()
}
}
AdvSceneSwitcher *ssWindow;
extern "C" void InitSceneSwitcher()
{
blog(LOG_INFO, "version: %s", g_GIT_TAG);
@@ -671,15 +686,16 @@ extern "C" void InitSceneSwitcher()
auto cb = []() {
if (switcher->settingsWindowOpened) {
ssWindow->show();
ssWindow->raise();
ssWindow->activateWindow();
AdvSceneSwitcher::window->show();
AdvSceneSwitcher::window->raise();
AdvSceneSwitcher::window->activateWindow();
} else {
ssWindow =
AdvSceneSwitcher::window =
new AdvSceneSwitcher(static_cast<QMainWindow *>(
obs_frontend_get_main_window()));
ssWindow->setAttribute(Qt::WA_DeleteOnClose);
ssWindow->show();
AdvSceneSwitcher::window->setAttribute(
Qt::WA_DeleteOnClose);
AdvSceneSwitcher::window->show();
}
};

View File

@@ -16,7 +16,15 @@ else()
endif()
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../headers")
set(module_SOURCES macro-condition-video.cpp macro-condition-video.hpp)
set(module_SOURCES
macro-condition-video.cpp
macro-condition-video.hpp
video-match-dialog.cpp
video-match-dialog.hpp
threshold-slider.cpp
threshold-slider.hpp
opencv-helpers.cpp
opencv-helpers.hpp)
add_library(advanced-scene-switcher-opencv MODULE ${module_SOURCES})
if(BUILD_OUT_OF_TREE)

View File

@@ -78,19 +78,21 @@ bool MacroConditionVideo::CheckCondition()
return _lastMatchResult;
}
if (_screenshotData && _screenshotData->done) {
if (_screenshotData.done) {
match = Compare();
_lastMatchResult = match;
if (!requiresFileInput(_condition)) {
_matchImage = std::move(_screenshotData->image);
_matchImage = std::move(_screenshotData.image);
}
_screenshotData.reset(nullptr);
_getNextScreenshot = true;
} else {
match = _lastMatchResult;
}
GetScreenshot();
if (_getNextScreenshot) {
GetScreenshot();
}
return match;
}
@@ -178,46 +180,10 @@ std::string MacroConditionVideo::GetShortDesc()
void MacroConditionVideo::GetScreenshot()
{
auto source = obs_weak_source_get_source(_videoSource);
_screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
_screenshotData.~ScreenshotHelper();
new (&_screenshotData) ScreenshotHelper(source);
obs_source_release(source);
}
// Assumption is that QImage uses Format_RGBA8888.
// Conversion from: https://github.com/dbzhang800/QtOpenCV
cv::Mat QImageToMat(const QImage &img)
{
if (img.isNull()) {
return cv::Mat();
}
return cv::Mat(img.height(), img.width(), CV_8UC(img.depth() / 8),
(uchar *)img.bits(), img.bytesPerLine());
}
QImage MatToQImage(const cv::Mat &mat)
{
if (mat.empty()) {
return QImage();
}
return QImage(mat.data, mat.cols, mat.rows, mat.step,
QImage::Format::Format_RGBA8888);
}
PatternMatchData createPatternData(QImage &pattern)
{
PatternMatchData data;
if (pattern.isNull()) {
return data;
}
data.rgbaPattern = QImageToMat(pattern);
std::vector<cv::Mat1b> rgbaChannelsPattern;
cv::split(data.rgbaPattern, rgbaChannelsPattern);
std::vector<cv::Mat1b> rgbChanlesPattern(
rgbaChannelsPattern.begin(), rgbaChannelsPattern.begin() + 3);
cv::merge(rgbChanlesPattern, data.rgbPattern);
cv::threshold(rgbaChannelsPattern[3], data.mask, 0, 255,
cv::THRESH_BINARY);
return data;
_getNextScreenshot = false;
}
bool MacroConditionVideo::LoadImageFromFile()
@@ -241,51 +207,10 @@ bool MacroConditionVideo::LoadModelData(std::string &path)
return !_objectCascade.empty();
}
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
cv::Mat &result, bool useAlphaAsMask = true)
{
if (img.isNull() || patternData.rgbaPattern.empty()) {
return;
}
if (img.height() < patternData.rgbaPattern.rows ||
img.width() < patternData.rgbaPattern.cols) {
return;
}
auto i = QImageToMat(img);
if (useAlphaAsMask) {
std::vector<cv::Mat1b> rgbaChannelsImage;
cv::split(i, rgbaChannelsImage);
std::vector<cv::Mat1b> rgbChanlesImage(
rgbaChannelsImage.begin(),
rgbaChannelsImage.begin() + 3);
cv::Mat3b rgbImage;
cv::merge(rgbChanlesImage, rgbImage);
cv::matchTemplate(rgbImage, patternData.rgbPattern, result,
cv::TM_CCORR_NORMED, patternData.mask);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
} else {
cv::matchTemplate(i, patternData.rgbaPattern, result,
cv::TM_CCOEFF_NORMED);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
}
}
void matchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask)
{
auto data = createPatternData(pattern);
matchPattern(img, data, threshold, result, useAlphaAsMask);
}
bool MacroConditionVideo::ScreenshotContainsPattern()
{
cv::Mat result;
matchPattern(_screenshotData->image, _patternData, _patternThreshold,
matchPattern(_screenshotData.image, _patternData, _patternThreshold,
result, _useAlphaAsMask);
return countNonZero(result) > 0;
}
@@ -295,34 +220,16 @@ bool MacroConditionVideo::OutputChanged()
if (_usePatternForChangedCheck) {
cv::Mat result;
_patternData = createPatternData(_matchImage);
matchPattern(_screenshotData->image, _patternData,
matchPattern(_screenshotData.image, _patternData,
_patternThreshold, result, _useAlphaAsMask);
return countNonZero(result) == 0;
}
return _screenshotData->image != _matchImage;
}
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize)
{
if (img.isNull() || cascade.empty()) {
return {};
}
auto i = QImageToMat(img);
cv::Mat frameGray;
cv::cvtColor(i, frameGray, cv::COLOR_BGR2GRAY);
equalizeHist(frameGray, frameGray);
std::vector<cv::Rect> objects;
cascade.detectMultiScale(frameGray, objects, scaleFactor, minNeighbors,
0, minSize, maxSize);
return objects;
return _screenshotData.image != _matchImage;
}
bool MacroConditionVideo::ScreenshotContainsObject()
{
auto objects = matchObject(_screenshotData->image, _objectCascade,
auto objects = matchObject(_screenshotData.image, _objectCascade,
_scaleFactor, _minNeighbors,
{_minSizeX, _minSizeY},
{_maxSizeX, _maxSizeY});
@@ -333,15 +240,15 @@ bool MacroConditionVideo::Compare()
{
switch (_condition) {
case VideoCondition::MATCH:
return _screenshotData->image == _matchImage;
return _screenshotData.image == _matchImage;
case VideoCondition::DIFFER:
return _screenshotData->image != _matchImage;
return _screenshotData.image != _matchImage;
case VideoCondition::HAS_CHANGED:
return OutputChanged();
case VideoCondition::HAS_NOT_CHANGED:
return !OutputChanged();
case VideoCondition::NO_IMAGE:
return _screenshotData->image.isNull();
return _screenshotData.image.isNull();
case VideoCondition::PATTERN:
return ScreenshotContainsPattern();
case VideoCondition::OBJECT:
@@ -359,57 +266,10 @@ static inline void populateConditionSelection(QComboBox *list)
}
}
ThresholdSlider::ThresholdSlider(double min, double max, const QString &label,
const QString &description, QWidget *parent)
: QWidget(parent)
{
_slider = new QSlider();
_slider->setOrientation(Qt::Horizontal);
_slider->setRange(min * _scale, max * _scale);
_value = new QLabel();
QString labelText = label + QString("0.");
for (int i = 0; i < _precision; i++) {
labelText.append(QString("0"));
}
_value->setText(labelText);
connect(_slider, SIGNAL(valueChanged(int)), this,
SLOT(NotifyValueChanged(int)));
QVBoxLayout *mainLayout = new QVBoxLayout();
QHBoxLayout *sliderLayout = new QHBoxLayout();
sliderLayout->addWidget(_value);
sliderLayout->addWidget(_slider);
mainLayout->addLayout(sliderLayout);
if (!description.isEmpty()) {
mainLayout->addWidget(new QLabel(description));
}
mainLayout->setContentsMargins(0, 0, 0, 0);
setLayout(mainLayout);
}
void ThresholdSlider::SetDoubleValue(double value)
{
_slider->setValue(value * _scale);
SetDoubleValueText(value);
}
void ThresholdSlider::NotifyValueChanged(int value)
{
double doubleValue = value / _scale;
SetDoubleValueText(doubleValue);
emit DoubleValueChanged(doubleValue);
}
void ThresholdSlider::SetDoubleValueText(double value)
{
QString labelText = _value->text();
labelText.chop(_precision + 2); // 2 for the part left of the "."
labelText.append(QString::number(value, 'f', _precision));
_value->setText(labelText);
}
MacroConditionVideoEdit::MacroConditionVideoEdit(
QWidget *parent, std::shared_ptr<MacroConditionVideo> entryData)
: QWidget(parent)
: QWidget(parent),
_matchDialog(this, entryData.get(), &GetSwitcher()->m)
{
_videoSelection = new QComboBox();
_condition = new QComboBox();
@@ -682,7 +542,7 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
} else {
auto source =
obs_weak_source_get_source(_entryData->_videoSource);
auto screenshot = std::make_unique<AdvSSScreenshotObj>(source);
ScreenshotHelper screenshot(source);
obs_source_release(source);
path = QFileDialog::getSaveFileName(this);
@@ -693,16 +553,15 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
if (!screenshot->done) { // Screenshot usually completed by now
if (!screenshot.done) { // Screenshot usually completed by now
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (!screenshot->done) {
if (!screenshot.done) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
return;
}
screenshot->image.save(path);
screenshot.image.save(path);
}
_imagePath->SetPath(path);
ImagePathChanged(path);
@@ -822,83 +681,12 @@ void MacroConditionVideoEdit::ThrottleCountChanged(int value)
_entryData->_throttleCount = value / GetSwitcher()->interval;
}
QImage markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
{
auto matchImg = QImageToMat(image);
for (int row = 0; row < matchResult.rows - 1; row++) {
for (int col = 0; col < matchResult.cols - 1; col++) {
if (matchResult.at<float>(row, col) != 0.0) {
rectangle(matchImg, {col, row},
cv::Point(col + pattern.width(),
row + pattern.height()),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
}
}
return MatToQImage(matchImg);
}
QImage markObjects(QImage &image, std::vector<cv::Rect> &objects)
{
auto frame = QImageToMat(image);
for (size_t i = 0; i < objects.size(); i++) {
rectangle(frame, cv::Point(objects[i].x, objects[i].y),
cv::Point(objects[i].x + objects[i].width,
objects[i].y + objects[i].height),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
return MatToQImage(frame);
}
void MacroConditionVideoEdit::ShowMatchClicked()
{
auto source = obs_weak_source_get_source(_entryData->_videoSource);
auto screenshot = std::make_unique<AdvSSScreenshotObj>(source);
obs_source_release(source);
if (!screenshot->done) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (!screenshot->done) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
return;
}
QImage markedIamge;
if (_entryData->_condition == VideoCondition::PATTERN) {
cv::Mat result;
QImage pattern = _entryData->GetMatchImage();
matchPattern(screenshot->image, pattern,
_entryData->_patternThreshold, result,
_entryData->_useAlphaAsMask);
if (countNonZero(result) == 0) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.patternMatchFail"));
return;
}
markedIamge = markPatterns(result, screenshot->image, pattern);
} else if (_entryData->_condition == VideoCondition::OBJECT) {
auto objects = matchObject(
screenshot->image, _entryData->_objectCascade,
_entryData->_scaleFactor, _entryData->_minNeighbors,
{_entryData->_minSizeX, _entryData->_minSizeY},
{_entryData->_maxSizeX, _entryData->_maxSizeY});
if (objects.empty()) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.objectMatchFail"));
return;
}
markedIamge = markObjects(screenshot->image, objects);
}
QLabel *label = new QLabel;
label->setPixmap(QPixmap::fromImage(markedIamge));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(label);
QDialog dialog;
dialog.setLayout(layout);
dialog.setWindowTitle("Advanced Scene Switcher");
dialog.exec();
_matchDialog.show();
_matchDialog.raise();
_matchDialog.activateWindow();
_matchDialog.ShowMatch();
}
void MacroConditionVideoEdit::ModelPathChanged(const QString &text)

View File

@@ -1,15 +1,16 @@
#pragma once
#include <macro.hpp>
#include <screenshot-helper.hpp>
#include <opencv-helpers.hpp>
#include <file-selection.hpp>
#include <threshold-slider.hpp>
#include <video-match-dialog.hpp>
#include <QWidget>
#include <QComboBox>
#include <QCheckBox>
#include <QHBoxLayout>
#include <chrono>
#undef NO // MacOS macro that can conflict with OpenCV
#include <opencv2/opencv.hpp>
#include <QLabel>
enum class VideoCondition {
MATCH,
@@ -21,15 +22,6 @@ enum class VideoCondition {
OBJECT,
};
struct PatternMatchData {
cv::Mat4b rgbaPattern;
cv::Mat3b rgbPattern;
cv::Mat1b mask;
};
constexpr int minMinNeighbors = 3;
constexpr int maxMinNeighbors = 6;
class MacroConditionVideo : public MacroCondition {
public:
MacroConditionVideo(Macro *m) : MacroCondition(m) {}
@@ -74,7 +66,8 @@ private:
bool Compare();
bool CheckShouldBeSkipped();
std::unique_ptr<AdvSSScreenshotObj> _screenshotData = nullptr;
bool _getNextScreenshot = true;
ScreenshotHelper _screenshotData;
QImage _matchImage;
std::string _modelDataPath =
obs_get_module_data_path(obs_current_module()) +
@@ -87,27 +80,6 @@ private:
static const std::string id;
};
class ThresholdSlider : public QWidget {
Q_OBJECT
public:
ThresholdSlider(double min = 0., double max = 1.,
const QString &label = "threshold",
const QString &description = "", QWidget *parent = 0);
void SetDoubleValue(double);
public slots:
void NotifyValueChanged(int value);
signals:
void DoubleValueChanged(double value);
private:
void SetDoubleValueText(double);
QLabel *_value;
QSlider *_slider;
double _scale = 100.0;
int _precision = 2;
};
class MacroConditionVideoEdit : public QWidget {
Q_OBJECT
@@ -175,6 +147,7 @@ protected:
QCheckBox *_throttleEnable;
QSpinBox *_throttleCount;
QPushButton *_showMatch;
ShowMatchDialog _matchDialog;
std::shared_ptr<MacroConditionVideo> _entryData;

View File

@@ -0,0 +1,98 @@
#include "opencv-helpers.hpp"
PatternMatchData createPatternData(QImage &pattern)
{
PatternMatchData data;
if (pattern.isNull()) {
return data;
}
data.rgbaPattern = QImageToMat(pattern);
std::vector<cv::Mat1b> rgbaChannelsPattern;
cv::split(data.rgbaPattern, rgbaChannelsPattern);
std::vector<cv::Mat1b> rgbChanlesPattern(
rgbaChannelsPattern.begin(), rgbaChannelsPattern.begin() + 3);
cv::merge(rgbChanlesPattern, data.rgbPattern);
cv::threshold(rgbaChannelsPattern[3], data.mask, 0, 255,
cv::THRESH_BINARY);
return data;
}
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
cv::Mat &result, bool useAlphaAsMask)
{
if (img.isNull() || patternData.rgbaPattern.empty()) {
return;
}
if (img.height() < patternData.rgbaPattern.rows ||
img.width() < patternData.rgbaPattern.cols) {
return;
}
auto i = QImageToMat(img);
if (useAlphaAsMask) {
std::vector<cv::Mat1b> rgbaChannelsImage;
cv::split(i, rgbaChannelsImage);
std::vector<cv::Mat1b> rgbChanlesImage(
rgbaChannelsImage.begin(),
rgbaChannelsImage.begin() + 3);
cv::Mat3b rgbImage;
cv::merge(rgbChanlesImage, rgbImage);
cv::matchTemplate(rgbImage, patternData.rgbPattern, result,
cv::TM_CCORR_NORMED, patternData.mask);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
} else {
cv::matchTemplate(i, patternData.rgbaPattern, result,
cv::TM_CCOEFF_NORMED);
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
}
}
void matchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask)
{
auto data = createPatternData(pattern);
matchPattern(img, data, threshold, result, useAlphaAsMask);
}
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize)
{
if (img.isNull() || cascade.empty()) {
return {};
}
auto i = QImageToMat(img);
cv::Mat frameGray;
cv::cvtColor(i, frameGray, cv::COLOR_BGR2GRAY);
equalizeHist(frameGray, frameGray);
std::vector<cv::Rect> objects;
cascade.detectMultiScale(frameGray, objects, scaleFactor, minNeighbors,
0, minSize, maxSize);
return objects;
}
// Assumption is that QImage uses Format_RGBA8888.
// Conversion from: https://github.com/dbzhang800/QtOpenCV
cv::Mat QImageToMat(const QImage &img)
{
if (img.isNull()) {
return cv::Mat();
}
return cv::Mat(img.height(), img.width(), CV_8UC(img.depth() / 8),
(uchar *)img.bits(), img.bytesPerLine());
}
QImage MatToQImage(const cv::Mat &mat)
{
if (mat.empty()) {
return QImage();
}
return QImage(mat.data, mat.cols, mat.rows,
QImage::Format::Format_RGBA8888);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <QImage>
#undef NO // MacOS macro that can conflict with OpenCV
#include <opencv2/opencv.hpp>
constexpr int minMinNeighbors = 3;
constexpr int maxMinNeighbors = 6;
struct PatternMatchData {
cv::Mat4b rgbaPattern;
cv::Mat3b rgbPattern;
cv::Mat1b mask;
};
PatternMatchData createPatternData(QImage &pattern);
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
cv::Mat &result, bool useAlphaAsMask = true);
void matchPattern(QImage &img, QImage &pattern, double threshold,
cv::Mat &result, bool useAlphaAsMask);
std::vector<cv::Rect> matchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors,
cv::Size minSize, cv::Size maxSize);
cv::Mat QImageToMat(const QImage &img);
QImage MatToQImage(const cv::Mat &mat);

View File

@@ -0,0 +1,50 @@
#include "threshold-slider.hpp"
#include <QHBoxLayout>
ThresholdSlider::ThresholdSlider(double min, double max, const QString &label,
const QString &description, QWidget *parent)
: QWidget(parent)
{
_slider = new QSlider();
_slider->setOrientation(Qt::Horizontal);
_slider->setRange(min * _scale, max * _scale);
_value = new QLabel();
QString labelText = label + QString("0.");
for (int i = 0; i < _precision; i++) {
labelText.append(QString("0"));
}
_value->setText(labelText);
connect(_slider, SIGNAL(valueChanged(int)), this,
SLOT(NotifyValueChanged(int)));
QVBoxLayout *mainLayout = new QVBoxLayout();
QHBoxLayout *sliderLayout = new QHBoxLayout();
sliderLayout->addWidget(_value);
sliderLayout->addWidget(_slider);
mainLayout->addLayout(sliderLayout);
if (!description.isEmpty()) {
mainLayout->addWidget(new QLabel(description));
}
mainLayout->setContentsMargins(0, 0, 0, 0);
setLayout(mainLayout);
}
void ThresholdSlider::SetDoubleValue(double value)
{
_slider->setValue(value * _scale);
SetDoubleValueText(value);
}
void ThresholdSlider::NotifyValueChanged(int value)
{
double doubleValue = value / _scale;
SetDoubleValueText(doubleValue);
emit DoubleValueChanged(doubleValue);
}
void ThresholdSlider::SetDoubleValueText(double value)
{
QString labelText = _value->text();
labelText.chop(_precision + 2); // 2 for the part left of the "."
labelText.append(QString::number(value, 'f', _precision));
_value->setText(labelText);
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <QWidget>
#include <QSlider>
#include <QLabel>
class ThresholdSlider : public QWidget {
Q_OBJECT
public:
ThresholdSlider(double min = 0., double max = 1.,
const QString &label = "threshold",
const QString &description = "", QWidget *parent = 0);
void SetDoubleValue(double);
public slots:
void NotifyValueChanged(int value);
signals:
void DoubleValueChanged(double value);
private:
void SetDoubleValueText(double);
QLabel *_value;
QSlider *_slider;
double _scale = 100.0;
int _precision = 2;
};

View File

@@ -0,0 +1,160 @@
#include "video-match-dialog.hpp"
#include "macro-condition-video.hpp"
#include "opencv-helpers.hpp"
#include "utility.hpp"
#include <condition_variable>
ShowMatchDialog::ShowMatchDialog(QWidget *parent,
MacroConditionVideo *conditionData,
std::mutex *mutex)
: QDialog(parent),
_conditionData(conditionData),
_imageLabel(new QLabel),
_scrollArea(new QScrollArea),
_mtx(mutex)
{
setWindowTitle("Advanced Scene Switcher");
_statusLabel = new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.showMatch.loading"));
_scrollArea->setBackgroundRole(QPalette::Dark);
_scrollArea->setWidget(_imageLabel);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(_statusLabel);
layout->addWidget(_scrollArea);
setLayout(layout);
// This is a workaround to handle random segfaults triggered when using:
// QMetaObject::invokeMethod(this, "RedrawImage", -Qt::QueuedConnection,
// Q_ARG(QImage, image));
// from within CheckForMatchLoop().
// Even using BlockingQueuedConnection causes deadlocks
_timer.setInterval(500);
QWidget::connect(&_timer, &QTimer::timeout, this,
&ShowMatchDialog::Resize);
_timer.start();
}
ShowMatchDialog::~ShowMatchDialog()
{
_stop = true;
if (_thread.joinable()) {
_thread.join();
}
}
void ShowMatchDialog::ShowMatch()
{
if (_thread.joinable()) {
return;
}
if (!_conditionData) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
return;
}
_thread = std::thread(&ShowMatchDialog::CheckForMatchLoop, this);
}
void ShowMatchDialog::Resize()
{
_imageLabel->adjustSize();
}
void ShowMatchDialog::CheckForMatchLoop()
{
std::condition_variable cv;
while (!_stop) {
std::unique_lock<std::mutex> lock(*_mtx);
auto source = obs_weak_source_get_source(
_conditionData->_videoSource);
ScreenshotHelper screenshot(source);
obs_source_release(source);
cv.wait_for(lock, std::chrono::seconds(1));
if (_stop) {
return;
}
if (!screenshot.done) {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
_imageLabel->setPixmap(QPixmap());
continue;
}
if (screenshot.image.width() == 0 ||
screenshot.image.height() == 0) {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotEmpty"));
_imageLabel->setPixmap(QPixmap());
continue;
}
auto image = MarkMatch(screenshot.image);
_imageLabel->setPixmap(QPixmap::fromImage(screenshot.image));
}
}
QImage markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
{
auto matchImg = QImageToMat(image);
for (int row = 0; row < matchResult.rows - 1; row++) {
for (int col = 0; col < matchResult.cols - 1; col++) {
if (matchResult.at<float>(row, col) != 0.0) {
rectangle(matchImg, {col, row},
cv::Point(col + pattern.width(),
row + pattern.height()),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
}
}
return MatToQImage(matchImg);
}
QImage markObjects(QImage &image, std::vector<cv::Rect> &objects)
{
auto frame = QImageToMat(image);
for (size_t i = 0; i < objects.size(); i++) {
rectangle(frame, cv::Point(objects[i].x, objects[i].y),
cv::Point(objects[i].x + objects[i].width,
objects[i].y + objects[i].height),
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
return MatToQImage(frame);
}
QImage ShowMatchDialog::MarkMatch(QImage &screenshot)
{
QImage resultIamge;
if (_conditionData->_condition == VideoCondition::PATTERN) {
cv::Mat result;
QImage pattern = _conditionData->GetMatchImage();
matchPattern(screenshot, pattern,
_conditionData->_patternThreshold, result,
_conditionData->_useAlphaAsMask);
if (countNonZero(result) == 0) {
resultIamge = screenshot;
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.patternMatchFail"));
} else {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.patternMatchSuccess"));
resultIamge = markPatterns(result, screenshot, pattern);
}
} else if (_conditionData->_condition == VideoCondition::OBJECT) {
auto objects = matchObject(
screenshot, _conditionData->_objectCascade,
_conditionData->_scaleFactor,
_conditionData->_minNeighbors,
{_conditionData->_minSizeX, _conditionData->_minSizeY},
{_conditionData->_maxSizeX, _conditionData->_maxSizeY});
if (objects.empty()) {
resultIamge = screenshot;
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.objectMatchFail"));
} else {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.objectMatchSuccess"));
resultIamge = markObjects(screenshot, objects);
}
}
return resultIamge;
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QScrollArea>
#include <QTimer>
#include <thread>
#include <mutex>
class MacroConditionVideo;
class ShowMatchDialog : public QDialog {
Q_OBJECT
public:
ShowMatchDialog(QWidget *parent, MacroConditionVideo *_conditionData,
std::mutex *mutex);
virtual ~ShowMatchDialog();
void ShowMatch();
private slots:
void Resize();
private:
void CheckForMatchLoop();
QImage MarkMatch(QImage &screenshot);
MacroConditionVideo *_conditionData;
QScrollArea *_scrollArea;
QLabel *_statusLabel;
QLabel *_imageLabel;
QTimer _timer;
std::mutex *_mtx;
std::thread _thread;
std::atomic_bool _stop = {false};
};

View File

@@ -192,6 +192,71 @@ void AdvSceneSwitcher::on_uiHintsDisable_stateChanged(int state)
switcher->disableHints = state;
}
void AdvSceneSwitcher::on_highlightExecutedMacros_stateChanged(int state)
{
if (loading) {
return;
}
switcher->highlightExecutedMacros = state;
}
bool isLegacyTab(const QString &name)
{
return name == obs_module_text(
"AdvSceneSwitcher.transitionTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.windowTitleTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.executableTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.screenRegionTab.title") ||
name == obs_module_text("AdvSceneSwitcher.mediaTab.title") ||
name == obs_module_text("AdvSceneSwitcher.fileTab.title") ||
name == obs_module_text("AdvSceneSwitcher.randomTab.title") ||
name == obs_module_text("AdvSceneSwitcher.timeTab.title") ||
name == obs_module_text("AdvSceneSwitcher.idleTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.sceneSequenceTab.title") ||
name == obs_module_text("AdvSceneSwitcher.audioTab.title") ||
name == obs_module_text("AdvSceneSwitcher.videoTab.title") ||
name == obs_module_text("AdvSceneSwitcher.pauseTab.title") ||
name == obs_module_text(
"AdvSceneSwitcher.sceneTriggerTab.title");
}
void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
{
switcher->hideLegacyTabs = state;
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
#if defined(_WIN32) || defined(__APPLE__)
ui->tabWidget->setTabVisible(idx, !state);
#else
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
ui->tabWidget->setTabEnabled(idx, !state);
ui->tabWidget->setStyleSheet(
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
#endif
}
}
}
void AdvSceneSwitcher::setDeprecationWarnings()
{
QString toolTip =
switcher->disableHints
? ""
: obs_module_text(
"AdvSceneSwitcher.deprecatedTabWarning");
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
ui->tabWidget->widget(idx)->setToolTip(toolTip);
}
}
}
QString getDefaultSaveLocation()
{
QString desktopPath = QStandardPaths::writableLocation(
@@ -399,6 +464,13 @@ void AdvSceneSwitcher::setTabOrder()
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
}
void AdvSceneSwitcher::setCurrentTab()
{
if (switcher->lastOpenedTab >= 0) {
ui->tabWidget->setCurrentIndex(switcher->lastOpenedTab);
}
}
void AdvSceneSwitcher::restoreWindowGeo()
{
if (switcher->saveWindowGeo && windowPosValid(switcher->windowPos)) {
@@ -456,6 +528,9 @@ void SwitcherData::loadSettings(obs_data_t *obj)
loadSceneTriggers(obj);
loadGeneralSettings(obj);
loadHotkeys(obj);
// Reset on startup and scene collection change
switcher->lastOpenedTab = -1;
}
void SwitcherData::saveSettings(obs_data_t *obj)
@@ -511,6 +586,9 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
obs_data_set_bool(obj, "showSystemTrayNotifications",
showSystemTrayNotifications);
obs_data_set_bool(obj, "disableHints", disableHints);
obs_data_set_bool(obj, "highlightExecutedMacros",
highlightExecutedMacros);
obs_data_set_bool(obj, "hideLegacyTabs", hideLegacyTabs);
obs_data_set_int(obj, "priority0", functionNamesByPriority[0]);
obs_data_set_int(obj, "priority1", functionNamesByPriority[1]);
@@ -584,6 +662,9 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
showSystemTrayNotifications =
obs_data_get_bool(obj, "showSystemTrayNotifications");
disableHints = obs_data_get_bool(obj, "disableHints");
highlightExecutedMacros =
obs_data_get_bool(obj, "highlightExecutedMacros");
hideLegacyTabs = obs_data_get_bool(obj, "hideLegacyTabs");
obs_data_set_default_int(obj, "priority0", default_priority_0);
obs_data_set_default_int(obj, "priority1", default_priority_1);
@@ -797,6 +878,9 @@ void AdvSceneSwitcher::setupGeneralTab()
ui->showTrayNotifications->setChecked(
switcher->showSystemTrayNotifications);
ui->uiHintsDisable->setChecked(switcher->disableHints);
ui->highlightExecutedMacros->setChecked(
switcher->highlightExecutedMacros);
ui->hideLegacyTabs->setChecked(switcher->hideLegacyTabs);
for (int p : switcher->functionNamesByPriority) {
std::string s = "";

View File

@@ -6,6 +6,7 @@
#else
#include "ui_advanced-scene-switcher.h"
#endif
#include "macro-segment-list.hpp"
#include "switcher-data-structs.hpp"
#include "platform-funcs.hpp"
@@ -32,6 +33,8 @@ public:
AdvSceneSwitcher(QWidget *parent);
~AdvSceneSwitcher();
static AdvSceneSwitcher *window;
void reject() override;
void closeEvent(QCloseEvent *event) override;
@@ -46,13 +49,15 @@ public:
void OpenSequenceExtendEdit(SequenceWidget *sw);
void SetEditSceneGroup(SceneGroup &sg);
bool addNewMacro(std::string &name);
bool addNewMacro(std::string &name, std::string format = "");
Macro *getSelectedMacro();
void SetEditMacro(Macro &m);
void HighlightAction(int idx);
void HighlightCondition(int idx);
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
void SetActionData(Macro &m);
void SetConditionData(Macro &m);
void ConnectControlSignals(MacroActionEdit *);
void ConnectControlSignals(MacroConditionEdit *);
void SwapActions(Macro *m, int pos1, int pos2);
@@ -77,7 +82,9 @@ public:
void setupVideoTab();
void setupNetworkTab();
void setupMacroTab();
void setDeprecationWarnings();
void setTabOrder();
void setCurrentTab();
void restoreWindowGeo();
void checkFirstTimeSetup();
@@ -117,11 +124,15 @@ public slots:
void on_runMacroInParallel_stateChanged(int value);
void on_runMacroOnChange_stateChanged(int value);
void on_macros_currentRowChanged(int idx);
void on_macros_itemChanged(QListWidgetItem *);
void on_conditionAdd_clicked();
void on_conditionRemove_clicked();
void on_conditionUp_clicked();
void on_conditionDown_clicked();
void on_actionAdd_clicked();
void on_actionRemove_clicked();
void on_actionUp_clicked();
void on_actionDown_clicked();
void DeleteMacroSegementHotkey();
void ShowMacroContextMenu(const QPoint &);
void ShowMacroActionsContextMenu(const QPoint &);
void ShowMacroConditionsContextMenu(const QPoint &);
@@ -130,14 +141,25 @@ public slots:
void ExpandAllConditions();
void CollapseAllActions();
void CollapseAllConditions();
void MinimizeActions();
void MinimizeConditions();
void MacroActionSelectionChanged(int idx);
void AddMacroAction(int idx);
void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx);
void MacroConditionSelectionChanged(int idx);
void AddMacroCondition(int idx);
void RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx);
void FadeOutActionControls();
void FadeOutConditionControls();
void ResetOpacityActionControls();
void ResetOpacityConditionControls();
void HighlightControls();
void HighlightMatchedMacros();
void MacroDragDropReorder(QModelIndex, int, int, QModelIndex, int);
void on_screenRegionSwitches_currentRowChanged(int idx);
void on_showFrame_clicked();
@@ -168,6 +190,8 @@ public slots:
void on_saveWindowGeo_stateChanged(int state);
void on_showTrayNotifications_stateChanged(int state);
void on_uiHintsDisable_stateChanged(int state);
void on_highlightExecutedMacros_stateChanged(int state);
void on_hideLegacyTabs_stateChanged(int state);
void on_exportSettings_clicked();
void on_importSettings_clicked();
@@ -271,6 +295,13 @@ public slots:
void on_close_clicked();
private:
void SetSelection(MacroSegmentList *, int);
MacroSegmentList *conditionsList = nullptr;
MacroSegmentList *actionsList = nullptr;
int currentConditionIdx = -1;
int currentActionIdx = -1;
};
/******************************************************************************
@@ -282,7 +313,7 @@ void setNextTransition(const sceneSwitchInfo &ssi, obs_source_t *currentSource,
void overwriteTransitionOverride(const sceneSwitchInfo &ssi,
transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, const transitionData &td);
void switchScene(const sceneSwitchInfo &ssi);
void switchScene(const sceneSwitchInfo &ssi, bool force = false);
void switchPreviewScene(const OBSWeakSource &ws);
/******************************************************************************

View File

@@ -34,6 +34,7 @@ public:
std::shared_ptr<MacroAction> * = nullptr,
const std::string &id = "scene_switch");
void UpdateEntryData(const std::string &id);
void SetEntryData(std::shared_ptr<MacroAction> *);
private slots:
void ActionSelectionChanged(const QString &text);

View File

@@ -1,8 +1,9 @@
#pragma once
#include <QSpinBox>
#include <QPlainTextEdit>
#include "macro-action-edit.hpp"
#include "file-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox>
enum class FileAction {
WRITE,
@@ -57,7 +58,7 @@ signals:
protected:
FileSelection *_filePath;
QPlainTextEdit *_text;
ResizingPlainTextEdit *_text;
QComboBox *_actions;
std::shared_ptr<MacroActionFile> _entryData;

View File

@@ -1,7 +1,8 @@
#pragma once
#include <QSpinBox>
#include <QPlainTextEdit>
#include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox>
enum class FilterAction {
ENABLE,
@@ -63,7 +64,7 @@ protected:
QComboBox *_filters;
QComboBox *_actions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
ResizingPlainTextEdit *_settings;
std::shared_ptr<MacroActionFilter> _entryData;
private:

View File

@@ -50,6 +50,7 @@ private slots:
void RemoveArg();
void ArgUp();
void ArgDown();
void ArgItemClicked(QListWidgetItem *);
signals:
void HeaderInfoChanged(const QString &);

View File

@@ -2,9 +2,9 @@
#include "macro-action-edit.hpp"
#include "scene-selection.hpp"
#include "scene-item-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox>
#include <QPlainTextEdit>
class MacroActionSceneTransform : public MacroAction {
public:
@@ -61,7 +61,7 @@ protected:
SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
ResizingPlainTextEdit *_settings;
std::shared_ptr<MacroActionSceneTransform> _entryData;
private:

View File

@@ -53,6 +53,7 @@ private slots:
void Remove();
void Up();
void Down();
void MacroItemClicked(QListWidgetItem *);
void ContinueFromClicked();
void RestartChanged(int state);
void UpdateStatusLine();

View File

@@ -1,10 +1,10 @@
#pragma once
#include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox>
#include <QLabel>
#include <QPushButton>
#include <QPlainTextEdit>
enum class SourceAction {
ENABLE,
@@ -63,7 +63,7 @@ protected:
QComboBox *_sources;
QComboBox *_actions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
ResizingPlainTextEdit *_settings;
QLabel *_warning;
std::shared_ptr<MacroActionSource> _entryData;

View File

@@ -13,6 +13,17 @@ enum class DateCondition {
BETWEEN,
};
enum class DayOfWeekSelection {
ANY = 0,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY,
};
class MacroConditionDate : public MacroCondition {
public:
MacroConditionDate(Macro *m) : MacroCondition(m) {}
@@ -26,15 +37,20 @@ public:
return std::make_shared<MacroConditionDate>(m);
}
QDateTime _dateTime;
QDateTime _dateTime2;
DayOfWeekSelection _dayOfWeek = DayOfWeekSelection::ANY;
QDateTime _dateTime = QDateTime::currentDateTime();
QDateTime _dateTime2 = QDateTime::currentDateTime();
bool _ignoreDate = false;
bool _ignoreTime = false;
bool _repeat = false;
Duration _duration;
DateCondition _condition = DateCondition::AT;
bool _dayOfWeekCheck = false;
private:
bool checkDayOfWeek();
bool checkRegularDate();
static bool _registered;
static const std::string id;
};
@@ -56,28 +72,45 @@ public:
}
private slots:
void DayOfWeekChanged(int day);
void ConditionChanged(int cond);
void DateTimeChanged(const QDateTime &datetime);
void DateTime2Changed(const QDateTime &datetime);
void DateChanged(const QDate &date);
void TimeChanged(const QTime &time);
void Date2Changed(const QDate &date);
void Time2Changed(const QTime &time);
void IgnoreDateChanged(int state);
void IgnoreTimeChanged(int state);
void RepeatChanged(int state);
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
void AdvancedSettingsToggleClicked();
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_dayOfWeek;
QTimeEdit *_weekTime;
QComboBox *_condition;
QDateTimeEdit *_dateTime;
QDateTimeEdit *_dateTime2;
QDateEdit *_date;
QTimeEdit *_time;
QLabel *_separator;
QDateEdit *_date2;
QTimeEdit *_time2;
QCheckBox *_ignoreDate;
QCheckBox *_ignoreTime;
QCheckBox *_repeat;
DurationSelection *_duration;
QPushButton *_advancedSettingsTooggle;
QHBoxLayout *_simpleLayout;
QHBoxLayout *_advancedLayout;
QHBoxLayout *_repeatLayout;
std::shared_ptr<MacroConditionDate> _entryData;
private:
void SetWidgetStatus();
void ShowSecondDateSelection(bool visible);
bool _loading = true;
};

View File

@@ -38,6 +38,7 @@ public:
bool IsRootNode();
void SetRootNode(bool);
void UpdateEntryData(const std::string &id);
void SetEntryData(std::shared_ptr<MacroCondition> *);
private slots:
void LogicSelectionChanged(int idx);

View File

@@ -1,13 +1,13 @@
#pragma once
#include "macro.hpp"
#include "file-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QWidget>
#include <QComboBox>
#include <QDateTime>
#include <QLineEdit>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox>
enum class FileType {
@@ -75,7 +75,7 @@ signals:
protected:
QComboBox *_fileType;
FileSelection *_filePath;
QPlainTextEdit *_matchText;
ResizingPlainTextEdit *_matchText;
QCheckBox *_useRegex;
QCheckBox *_checkModificationDate;
QCheckBox *_checkFileContent;

View File

@@ -1,9 +1,9 @@
#pragma once
#include "macro.hpp"
#include "resizing-text-edit.hpp"
#include <QComboBox>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox>
enum class FilterCondition {
@@ -67,7 +67,7 @@ protected:
QComboBox *_filters;
QComboBox *_conditions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
ResizingPlainTextEdit *_settings;
QCheckBox *_regex;
std::shared_ptr<MacroConditionFilter> _entryData;

View File

@@ -0,0 +1,118 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <util/platform.h>
enum class StatsType {
FPS,
CPU_USAGE,
DISK_USAGE, // not implemented
MEM_USAGE,
AVG_FRAMETIME,
RENDER_LAG,
ENCODE_LAG,
STREAM_DROPPED_FRAMES,
STREAM_BITRATE,
STREAM_MB_SENT,
RECORDING_DROPPED_FRAMES, // not sure if this makes sense
RECORDING_BITRATE,
RECORDING_MB_SENT,
};
enum class StatsCondition {
ABOVE,
EQUALS,
BELOW,
};
class MacroConditionStats : public MacroCondition {
public:
MacroConditionStats(Macro *m);
~MacroConditionStats();
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionStats>(m);
}
double _value = 0;
StatsType _type = StatsType::FPS;
StatsCondition _condition = StatsCondition::ABOVE;
private:
bool CheckFPS();
bool CheckCPU();
bool CheckMemory();
bool CheckAvgFrametime();
bool CheckRenderLag();
bool CheckEncodeLag();
bool CheckStreamDroppedFrames();
bool CheckStreamBitrate();
bool CheckStreamMBSent();
bool CheckRecordingDroppedFrames();
bool CheckRecordingBitrate();
bool CheckRecordingMBSent();
os_cpu_usage_info_t *_cpu_info = nullptr;
uint32_t _first_encoded = 0xFFFFFFFF;
uint32_t _first_skipped = 0xFFFFFFFF;
uint32_t _first_rendered = 0xFFFFFFFF;
uint32_t _first_lagged = 0xFFFFFFFF;
struct OutputInfo {
void Update(obs_output_t *output);
uint64_t lastBytesSent = 0;
uint64_t lastBytesSentTime = 0;
int first_total = 0;
int first_dropped = 0;
double dropped_relative = 0.0;
long double kbps = 0.0l;
};
OutputInfo _streamInfo;
OutputInfo _recordingInfo;
static bool _registered;
static const std::string id;
};
class MacroConditionStatsEdit : public QWidget {
Q_OBJECT
public:
MacroConditionStatsEdit(
QWidget *parent,
std::shared_ptr<MacroConditionStats> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionStatsEdit(
parent,
std::dynamic_pointer_cast<MacroConditionStats>(cond));
}
private slots:
void ValueChanged(double value);
void StatsTypeChanged(int type);
void ConditionChanged(int cond);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_stats;
QComboBox *_condition;
QDoubleSpinBox *_value;
std::shared_ptr<MacroConditionStats> _entryData;
private:
void SetWidgetVisibility();
bool _loading = true;
};

View File

@@ -4,13 +4,15 @@
#include <QComboBox>
enum class PluginStateCondition {
SCENESWITCHED,
SCENE_SWITCHED,
RUNNING,
SHUTDOWN,
};
class MacroConditionPluginState : public MacroCondition {
public:
MacroConditionPluginState(Macro *m) : MacroCondition(m) {}
~MacroConditionPluginState();
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
@@ -20,7 +22,7 @@ public:
return std::make_shared<MacroConditionPluginState>(m);
}
PluginStateCondition _condition = PluginStateCondition::SCENESWITCHED;
PluginStateCondition _condition = PluginStateCondition::SCENE_SWITCHED;
private:
static bool _registered;

View File

@@ -2,9 +2,9 @@
#include "macro.hpp"
#include "scene-selection.hpp"
#include "scene-item-selection.hpp"
#include "resizing-text-edit.hpp"
#include <QSpinBox>
#include <QPlainTextEdit>
#include <QCheckBox>
class MacroConditionSceneTransform : public MacroCondition {
@@ -61,7 +61,7 @@ protected:
SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
ResizingPlainTextEdit *_settings;
QCheckBox *_regex;
std::shared_ptr<MacroConditionSceneTransform> _entryData;

View File

@@ -1,9 +1,9 @@
#pragma once
#include "macro.hpp"
#include "resizing-text-edit.hpp"
#include <QComboBox>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QCheckBox>
enum class SourceCondition {
@@ -64,7 +64,7 @@ protected:
QComboBox *_sources;
QComboBox *_conditions;
QPushButton *_getSettings;
QPlainTextEdit *_settings;
ResizingPlainTextEdit *_settings;
QCheckBox *_regex;
std::shared_ptr<MacroConditionSource> _entryData;

View File

@@ -1,31 +0,0 @@
#pragma once
#include <QFrame>
#include <QPropertyAnimation>
#include <QScrollArea>
#include <QPushButton>
#include <QLayout>
#include <QWidget>
class MacroEntryControls : public QWidget {
Q_OBJECT
public:
explicit MacroEntryControls(const int animationDuration = 300,
QWidget *parent = 0);
void Show(bool visible = true);
signals:
void Add();
void Remove();
void Up();
void Down();
private:
QPushButton *_add;
QPushButton *_remove;
QPushButton *_up;
QPushButton *_down;
QPropertyAnimation *_animation = nullptr;
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include <QLabel>
#include <QCheckBox>
#include <memory>
class Macro;
class MacroListEntryWidget : public QWidget {
Q_OBJECT
public:
MacroListEntryWidget(std::shared_ptr<Macro>, QWidget *parent);
void SetName(const QString &);
void SetMacro(std::shared_ptr<Macro> &);
private slots:
void PauseChanged(int);
private:
QLabel *_name;
QCheckBox *_running;
std::shared_ptr<Macro> _macro;
};

View File

@@ -0,0 +1,25 @@
#pragma once
#include <QWidget>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QLabel>
class MacroSegmentList : public QScrollArea {
Q_OBJECT
public:
MacroSegmentList(QWidget *parent = nullptr);
void SetHelpMsg(const QString &msg);
void SetHelpMsgVisible(bool visible);
QVBoxLayout *ContentLayout() { return _contentLayout; }
signals:
void SelectionChagned(int idx);
protected:
void mousePressEvent(QMouseEvent *event);
private:
QVBoxLayout *_contentLayout;
QLabel *_helpMsg;
};

View File

@@ -1,5 +1,7 @@
#pragma once
#include <QWidget>
#include <QFrame>
#include <QVBoxLayout>
#include <obs.hpp>
class Macro;
@@ -27,7 +29,6 @@ private:
class Section;
class QLabel;
class MacroEntryControls;
class MacroSegmentEdit : public QWidget {
Q_OBJECT
@@ -38,13 +39,10 @@ public:
// list of actions and conditions
void SetFocusPolicyOfWidgets();
void SetCollapsed(bool collapsed);
void SetSelected(bool);
protected slots:
void HeaderInfoChanged(const QString &);
void Add();
void Remove();
void Up();
void Down();
void Collapsed(bool);
signals:
void MacroAdded(const QString &name);
@@ -53,18 +51,15 @@ signals:
void SceneGroupAdded(const QString &name);
void SceneGroupRemoved(const QString &name);
void SceneGroupRenamed(const QString &oldName, const QString newName);
void AddAt(int idx);
void RemoveAt(int idx);
void UpAt(int idx);
void DownAt(int idx);
void SelectionChagned(int idx);
protected:
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mousePressEvent(QMouseEvent *event);
Section *_section;
QLabel *_headerInfo;
MacroEntryControls *_controls;
QFrame *_frame;
QVBoxLayout *_highLightFrameLayout;
private:
virtual MacroSegment *Data() = 0;

View File

@@ -75,8 +75,8 @@ public:
virtual ~Macro();
bool CeckMatch();
bool PerformAction(bool forceParallel = false,
bool ignorePause = false);
bool PerformActions(bool forceParallel = false,
bool ignorePause = false);
bool Matched() { return _matched; }
std::string Name() { return _name; }
void SetName(const std::string &name);
@@ -106,6 +106,9 @@ public:
// Helper function for plugin state condition regarding scene change
bool SwitchesScene();
// UI helpers for the macro tab
bool WasExecutedRecently();
private:
void SetupHotkeys();
void ClearHotkeys();
@@ -127,6 +130,8 @@ private:
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
bool _wasExecutedRecently = false;
bool _die = false;
bool _stop = false;
bool _done = true;

View File

@@ -0,0 +1,17 @@
#pragma once
#include <QPlainTextEdit>
class ResizingPlainTextEdit : public QPlainTextEdit {
Q_OBJECT
public:
ResizingPlainTextEdit(QWidget *parent, const int scrollAt = 10,
const int minLines = 3,
const int paddingLines = 2);
private slots:
void ResizeTexteditArea();
private:
const int _scrollAt;
const int _minLines;
const int _paddingLines;
};

View File

@@ -4,10 +4,13 @@
#include <QImage>
#include <chrono>
class AdvSSScreenshotObj {
class ScreenshotHelper {
public:
AdvSSScreenshotObj(obs_source_t *source);
~AdvSSScreenshotObj();
ScreenshotHelper() = default;
ScreenshotHelper(obs_source_t *source);
ScreenshotHelper &operator=(const ScreenshotHelper &) = delete;
ScreenshotHelper(const ScreenshotHelper &) = delete;
~ScreenshotHelper();
void Screenshot();
void Download();
@@ -17,7 +20,6 @@ public:
gs_texrender_t *texrender = nullptr;
gs_stagesurf_t *stagesurf = nullptr;
OBSWeakSource weakSource;
std::string path;
QImage image;
uint32_t cx = 0;
uint32_t cy = 0;
@@ -26,4 +28,7 @@ public:
bool done = false;
std::chrono::high_resolution_clock::time_point time;
private:
bool _initDone = false;
};

View File

@@ -14,7 +14,8 @@ public:
explicit Section(const int animationDuration = 300,
QWidget *parent = 0);
void SetContent(QWidget *w, bool collapsed = true);
void SetContent(QWidget *w);
void SetContent(QWidget *w, bool collapsed);
void AddHeaderWidget(QWidget *);
void SetCollapsed(bool);

View File

@@ -29,7 +29,7 @@ struct VideoSwitch : virtual SceneSwitcherEntry {
double duration = 0;
bool ignoreInactiveSource = false;
std::unique_ptr<AdvSSScreenshotObj> screenshotData = nullptr;
std::unique_ptr<ScreenshotHelper> screenshotData = nullptr;
std::chrono::high_resolution_clock::time_point previousTime{};
QImage matchImage;

View File

@@ -77,6 +77,7 @@ struct SwitcherData {
SwitcherThread *th = nullptr;
bool settingsWindowOpened = false;
int lastOpenedTab = -1;
bool firstBoot = true;
std::condition_variable cv;
@@ -86,6 +87,8 @@ struct SwitcherData {
bool stop = false;
bool verbose = false;
bool disableHints = false;
bool hideLegacyTabs = false;
bool highlightExecutedMacros = false;
bool showSystemTrayNotifications = false;
bool showFrame = false;
bool transitionOverrideOverride = false;
@@ -121,6 +124,8 @@ struct SwitcherData {
std::condition_variable macroTransitionCv;
bool macroSceneSwitched = false;
bool replayBufferSaved = false;
bool obsIsShuttingDown = false;
int shutdownConditionCount = 0;
std::deque<WindowSwitch> windowSwitches;
std::vector<std::string> ignoreIdleWindows;
@@ -210,6 +215,7 @@ struct SwitcherData {
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 removeMacroSegment = OBS_INVALID_HOTKEY_ID;
bool saveWindowGeo = false;
QPoint windowPos = {};

View File

@@ -47,8 +47,7 @@ void deleteLayoutItem(QLayoutItem *item);
void clearLayout(QLayout *layout, int afterIdx = 0);
void setLayoutVisible(QLayout *layout, bool visible);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
QColor = QColor(0, 0, 0, 0),
QString specifier = "QLabel ",
QColor endColor = QColor(0, 0, 0, 0),
bool once = false);
void listAddClicked(QListWidget *list, QWidget *newWidget,
QPushButton *addButton = nullptr,

View File

@@ -34,6 +34,17 @@ void startStopToggleHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
}
}
void removeMacroSegmentHotkeyFunc(void *, obs_hotkey_id, obs_hotkey_t *,
bool pressed)
{
if (pressed && switcher->settingsWindowOpened &&
AdvSceneSwitcher::window) {
QMetaObject::invokeMethod(AdvSceneSwitcher::window,
"DeleteMacroSegementHotkey",
Qt::QueuedConnection);
}
}
void registerHotkeys()
{
switcher->startHotkey = obs_hotkey_register_frontend(
@@ -49,6 +60,11 @@ void registerHotkeys()
obs_module_text(
"AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey"),
startStopToggleHotkeyFunc, NULL);
switcher->removeMacroSegment = obs_hotkey_register_frontend(
"removeMacroSegmentSwitcherHotkey",
obs_module_text(
"AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey"),
removeMacroSegmentHotkeyFunc, NULL);
switcher->hotkeysRegistered = true;
}
@@ -67,6 +83,12 @@ void SwitcherData::saveHotkeys(obs_data_t *obj)
obs_data_array_t *toggleHotkeyArrray = obs_hotkey_save(toggleHotkey);
obs_data_set_array(obj, "toggleHotkey", toggleHotkeyArrray);
obs_data_array_release(toggleHotkeyArrray);
obs_data_array_t *removeSegmentArrray =
obs_hotkey_save(removeMacroSegment);
obs_data_set_array(obj, "removeMacroSegmentHotkey",
removeSegmentArrray);
obs_data_array_release(removeSegmentArrray);
}
void SwitcherData::loadHotkeys(obs_data_t *obj)
@@ -89,4 +111,9 @@ void SwitcherData::loadHotkeys(obs_data_t *obj)
obs_data_get_array(obj, "toggleHotkey");
obs_hotkey_load(toggleHotkey, toggleHotkeyArrray);
obs_data_array_release(toggleHotkeyArrray);
obs_data_array_t *removeSegmentArrray =
obs_data_get_array(obj, "removeMacroSegmentHotkey");
obs_hotkey_load(removeMacroSegment, removeSegmentArrray);
obs_data_array_release(removeSegmentArrray);
}

View File

@@ -2,7 +2,6 @@
#include "headers/macro-action-edit.hpp"
#include "headers/macro-action-scene-switch.hpp"
#include "headers/section.hpp"
#include "headers/macro-controls.hpp"
#include "headers/utility.hpp"
std::map<std::string, MacroActionInfo> MacroActionFactory::_methods;
@@ -77,9 +76,15 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
_section->AddHeaderWidget(_actionSelection);
_section->AddHeaderWidget(_headerInfo);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_section);
mainLayout->addWidget(_controls);
QVBoxLayout *actionLayout = new QVBoxLayout;
actionLayout->setContentsMargins(0, 0, 0, 0);
actionLayout->setSpacing(0);
actionLayout->addWidget(_frame);
_highLightFrameLayout->addWidget(_section);
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addLayout(actionLayout);
setLayout(mainLayout);
_entryData = entryData;
@@ -106,7 +111,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, false);
_section->SetContent(widget);
SetFocusPolicyOfWidgets();
}
@@ -123,6 +128,11 @@ void MacroActionEdit::UpdateEntryData(const std::string &id)
SetFocusPolicyOfWidgets();
}
void MacroActionEdit::SetEntryData(std::shared_ptr<MacroAction> *data)
{
_entryData = data;
}
MacroSegment *MacroActionEdit::Data()
{
return _entryData->get();
@@ -146,20 +156,23 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
MacroActionSwitchScene temp(nullptr);
id = temp.GetId();
}
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().emplace(macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
macro->Actions().at(idx)->Load(data);
obs_data_release(data);
{
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().emplace(macro->Actions().begin() + idx,
MacroActionFactory::Create(id, macro));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
macro->Actions().at(idx)->Load(data);
obs_data_release(data);
}
macro->UpdateActionIndices();
}
macro->UpdateActionIndices();
clearLayout(ui->macroEditActionLayout, idx);
clearLayout(actionsList->ContentLayout(), idx);
PopulateMacroActions(*macro, idx);
HighlightAction(idx);
SetActionData(*macro);
}
void AdvSceneSwitcher::on_actionAdd_clicked()
@@ -169,8 +182,19 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
return;
}
AddMacroAction((int)macro->Actions().size());
ui->macroEditActionHelp->setVisible(false);
if (currentActionIdx == -1) {
auto macro = getSelectedMacro();
if (!macro) {
return;
}
AddMacroAction((int)macro->Actions().size());
} else {
AddMacroAction(currentActionIdx + 1);
}
if (currentActionIdx != -1) {
MacroActionSelectionChanged(currentActionIdx + 1);
}
actionsList->SetHelpMsgVisible(false);
}
void AdvSceneSwitcher::RemoveMacroAction(int idx)
@@ -184,23 +208,48 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().erase(macro->Actions().begin() + idx);
switcher->abortMacroWait = true;
switcher->macroWaitCv.notify_all();
macro->UpdateActionIndices();
{
std::lock_guard<std::mutex> lock(switcher->m);
macro->Actions().erase(macro->Actions().begin() + idx);
switcher->abortMacroWait = true;
switcher->macroWaitCv.notify_all();
macro->UpdateActionIndices();
}
clearLayout(ui->macroEditActionLayout, idx);
clearLayout(actionsList->ContentLayout(), idx);
PopulateMacroActions(*macro, idx);
SetActionData(*macro);
}
void AdvSceneSwitcher::on_actionRemove_clicked()
{
auto macro = getSelectedMacro();
if (!macro) {
if (currentActionIdx == -1) {
auto macro = getSelectedMacro();
if (!macro) {
return;
}
RemoveMacroAction((int)macro->Actions().size() - 1);
} else {
RemoveMacroAction(currentActionIdx);
}
MacroActionSelectionChanged(-1);
}
void AdvSceneSwitcher::on_actionUp_clicked()
{
if (currentActionIdx == -1) {
return;
}
RemoveMacroAction((int)macro->Actions().size() - 1);
MoveMacroActionUp(currentActionIdx);
MacroActionSelectionChanged(currentActionIdx - 1);
}
void AdvSceneSwitcher::on_actionDown_clicked()
{
if (currentActionIdx == -1) {
return;
}
MoveMacroActionDown(currentActionIdx);
MacroActionSelectionChanged(currentActionIdx + 1);
}
void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
@@ -219,16 +268,16 @@ void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2)
auto a1 = m->Actions().begin() + pos1;
auto a2 = m->Actions().begin() + pos2;
auto item1 = ui->macroEditActionLayout->takeAt(pos1);
auto item2 = ui->macroEditActionLayout->takeAt(pos2 - 1);
auto item1 = actionsList->ContentLayout()->takeAt(pos1);
auto item2 = actionsList->ContentLayout()->takeAt(pos2 - 1);
deleteLayoutItem(item1);
deleteLayoutItem(item2);
auto widget1 = new MacroActionEdit(this, &(*a1), (*a1)->GetId());
auto widget2 = new MacroActionEdit(this, &(*a2), (*a2)->GetId());
ConnectControlSignals(widget1);
ConnectControlSignals(widget2);
ui->macroEditActionLayout->insertWidget(pos1, widget1);
ui->macroEditActionLayout->insertWidget(pos2, widget2);
actionsList->ContentLayout()->insertWidget(pos1, widget1);
actionsList->ContentLayout()->insertWidget(pos2, widget2);
}
void AdvSceneSwitcher::MoveMacroActionUp(int idx)
@@ -243,6 +292,7 @@ void AdvSceneSwitcher::MoveMacroActionUp(int idx)
}
SwapActions(macro, idx, idx - 1);
HighlightAction(idx - 1);
}
void AdvSceneSwitcher::MoveMacroActionDown(int idx)
@@ -257,4 +307,24 @@ void AdvSceneSwitcher::MoveMacroActionDown(int idx)
}
SwapActions(macro, idx, idx + 1);
HighlightAction(idx + 1);
}
void AdvSceneSwitcher::MacroActionSelectionChanged(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
SetSelection(actionsList, idx);
SetSelection(conditionsList, -1);
if (idx < 0 || (unsigned)idx >= macro->Actions().size()) {
currentActionIdx = -1;
} else {
currentActionIdx = idx;
}
currentConditionIdx = -1;
HighlightControls();
}

View File

@@ -87,7 +87,7 @@ MacroActionFileEdit::MacroActionFileEdit(
: QWidget(parent)
{
_filePath = new FileSelection(FileSelection::Type::WRITE);
_text = new QPlainTextEdit();
_text = new ResizingPlainTextEdit(this);
_actions = new QComboBox();
populateActionSelection(_actions);
@@ -128,6 +128,9 @@ void MacroActionFileEdit::UpdateEntryData()
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_filePath->SetPath(QString::fromStdString(_entryData->_file));
_text->setPlainText(QString::fromStdString(_entryData->_text));
adjustSize();
updateGeometry();
}
void MacroActionFileEdit::PathChanged(const QString &text)
@@ -150,6 +153,9 @@ void MacroActionFileEdit::TextChanged()
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_text = _text->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionFileEdit::ActionChanged(int value)

View File

@@ -98,7 +98,7 @@ MacroActionFilterEdit::MacroActionFilterEdit(
_actions = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.filter.getSettings"));
_settings = new QPlainTextEdit();
_settings = new ResizingPlainTextEdit(this);
populateActionSelection(_actions);
populateSourcesWithFilterSelection(_sources);
@@ -153,6 +153,9 @@ void MacroActionFilterEdit::UpdateEntryData()
GetWeakSourceName(_entryData->_filter).c_str());
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS);
adjustSize();
updateGeometry();
}
void MacroActionFilterEdit::SourceChanged(const QString &text)
@@ -211,6 +214,9 @@ void MacroActionFilterEdit::SettingsChanged()
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionFilterEdit::SetWidgetVisibility(bool showSettings)

View File

@@ -36,7 +36,7 @@ bool MacroActionMacro::PerformAction()
_macro->ResetCount();
break;
case PerformMacroAction::RUN:
_macro->PerformAction();
_macro->PerformActions();
break;
case PerformMacroAction::STOP:
_macro->Stop();

View File

@@ -46,7 +46,7 @@ MacroActionPreviewSceneEdit::MacroActionPreviewSceneEdit(
QWidget *parent, std::shared_ptr<MacroActionPreviewScene> entryData)
: QWidget(parent)
{
_scenes = new SceneSelectionWidget(window(), false, false, false);
_scenes = new SceneSelectionWidget(window(), true, true, true);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));

View File

@@ -44,12 +44,12 @@ bool MacroActionRandom::PerformAction()
}
if (macros.size() == 1) {
lastRandomMacro = macros[0];
return macros[0]->PerformAction();
return macros[0]->PerformActions();
}
size_t idx = std::rand() % (macros.size());
lastRandomMacro = macros[idx];
return macros[idx]->PerformAction();
return macros[idx]->PerformActions();
}
void MacroActionRandom::LogAction()
@@ -107,8 +107,6 @@ MacroActionRandomEdit::MacroActionRandomEdit(
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(AddMacro()));
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(RemoveMacro()));
QWidget::connect(_macroList, SIGNAL(currentRowChanged(int)), this,
SLOT(MacroSelectionChanged(int)));
QWidget::connect(window(),
SIGNAL(MacroRenamed(const QString &, const QString &)),
this,

View File

@@ -102,6 +102,8 @@ MacroActionRunEdit::MacroActionRunEdit(
SLOT(RemoveArg()));
QWidget::connect(_argUp, SIGNAL(clicked()), this, SLOT(ArgUp()));
QWidget::connect(_argDown, SIGNAL(clicked()), this, SLOT(ArgDown()));
connect(_argList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this,
SLOT(ArgItemClicked(QListWidgetItem *)));
auto *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
@@ -235,6 +237,33 @@ void MacroActionRunEdit::ArgDown()
}
}
void MacroActionRunEdit::ArgItemClicked(QListWidgetItem *item)
{
if (_loading || !_entryData) {
return;
}
std::string name;
bool accepted = AdvSSNameDialog::AskForName(
this,
obs_module_text("AdvSceneSwitcher.action.run.addArgument"),
obs_module_text(
"AdvSceneSwitcher.action.run.addArgumentDescription"),
name, item->text(), 170, false);
if (!accepted || name.empty()) {
return;
}
auto arg = QString::fromStdString(name);
QVariant v = QVariant::fromValue(arg);
item->setText(arg);
item->setData(Qt::UserRole, arg);
int idx = _argList->currentRow();
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_args[idx] = arg;
}
void MacroActionRunEdit::SetArgListSize()
{
setHeightToContentHeight(_argList);

View File

@@ -9,39 +9,57 @@ bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
{MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create,
"AdvSceneSwitcher.action.switchScene"});
void waitForTransitionChange(OBSWeakSource &target)
void waitForTransitionChange(int duration)
{
const int maxNrChecks = 100;
int curCheckNr = 0;
bool transitionEnded = false;
duration += 200; // It seems to be necessary to add a small buffer
auto time = std::chrono::high_resolution_clock::now() +
std::chrono::milliseconds(duration);
auto sceneCheckStartedOn = obs_frontend_get_current_scene();
obs_source_release(sceneCheckStartedOn);
switcher->abortMacroWait = false;
std::unique_lock<std::mutex> lock(switcher->m);
while (!transitionEnded && curCheckNr < maxNrChecks &&
!switcher->abortMacroWait) {
switcher->abortMacroWait = false;
switcher->macroTransitionCv.wait_for(
lock,
std::chrono::milliseconds(
(long long)switcher->interval),
[] { return switcher->abortMacroWait.load(); });
auto curScene = obs_frontend_get_current_scene();
obs_source_release(curScene);
transitionEnded = curScene != sceneCheckStartedOn ||
switcher->currentScene == target;
++curCheckNr;
while (!switcher->abortMacroWait) {
if (switcher->macroTransitionCv.wait_until(lock, time) ==
std::cv_status::timeout) {
break;
}
}
}
int getTransitionOverrideDuration(OBSWeakSource scene)
{
int duration = 0;
obs_source_t *source = obs_weak_source_get_source(scene);
obs_data_t *data = obs_source_get_private_settings(source);
auto name = obs_data_get_string(data, "transition");
if (strlen(name) != 0) {
duration = obs_data_get_int(data, "transition_duration");
}
obs_data_release(data);
obs_source_release(source);
return duration;
}
int getExpectedTransitionDuration(OBSWeakSource scene, double duration)
{
int overrideDuration = getTransitionOverrideDuration(scene);
if (overrideDuration && !switcher->transitionOverrideOverride) {
return overrideDuration;
}
if (duration != 0) {
return duration * 1000;
}
return obs_frontend_get_transition_duration();
}
bool MacroActionSwitchScene::PerformAction()
{
auto scene = _scene.GetScene();
switchScene({scene, _transition.GetTransition(),
(int)(_duration.seconds * 1000)});
if (_blockUntilTransitionDone) {
waitForTransitionChange(scene);
(int)(_duration.seconds * 1000)},
obs_frontend_preview_program_mode_active());
if (_blockUntilTransitionDone && scene) {
waitForTransitionChange(getExpectedTransitionDuration(
scene, _duration.seconds));
return !switcher->abortMacroWait;
}
return true;

View File

@@ -111,7 +111,7 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
_sources = new SceneItemSelectionWidget(parent);
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
_settings = new QPlainTextEdit();
_settings = new ResizingPlainTextEdit(this);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
@@ -161,6 +161,9 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem(_entryData->_source);
_settings->setPlainText(formatJsonString(_entryData->GetSettings()));
adjustSize();
updateGeometry();
}
void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
@@ -212,4 +215,7 @@ void MacroActionSceneTransformEdit::SettingsChanged()
std::lock_guard<std::mutex> lock(switcher->m);
auto json = _settings->toPlainText().toStdString();
_entryData->SetSettings(json);
adjustSize();
updateGeometry();
}

View File

@@ -11,8 +11,8 @@ bool MacroActionSequence::_registered = MacroActionFactory::Register(
int getNextUnpausedMacroIdx(std::vector<MacroRef> &macros, int startIdx)
{
for (; macros.size() > startIdx; ++startIdx) {
if (!macros[startIdx]->Paused()) {
for (; (int)macros.size() > startIdx; ++startIdx) {
if (macros[startIdx].get() && !macros[startIdx]->Paused()) {
return startIdx;
}
}
@@ -63,7 +63,7 @@ bool MacroActionSequence::PerformAction()
return true;
}
return macro->PerformAction();
return macro->PerformActions();
}
void MacroActionSequence::LogAction()
@@ -141,8 +141,10 @@ MacroActionSequenceEdit::MacroActionSequenceEdit(
QWidget::connect(_down, SIGNAL(clicked()), this, SLOT(Down()));
QWidget::connect(_continueFrom, SIGNAL(clicked()), this,
SLOT(ContinueFromClicked()));
QWidget::connect(_macroList, SIGNAL(currentRowChanged(int)), this,
SLOT(MacroSelectionChanged(int)));
QWidget::connect(_macroList,
SIGNAL(itemDoubleClicked(QListWidgetItem *)), this,
SLOT(MacroItemClicked(QListWidgetItem *)));
QWidget::connect(window(),
SIGNAL(MacroRenamed(const QString &, const QString &)),
this,
@@ -312,6 +314,32 @@ void MacroActionSequenceEdit::Down()
}
}
void MacroActionSequenceEdit::MacroItemClicked(QListWidgetItem *item)
{
if (_loading || !_entryData) {
return;
}
std::string macroName;
bool accepted = MacroSelectionDialog::AskForMacro(this, macroName);
if (!accepted || macroName.empty()) {
return;
}
MacroRef macro(macroName);
if (!macro.get()) {
return;
}
item->setText(QString::fromStdString(macroName));
int idx = _macroList->currentRow();
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_macros[idx] = macro;
SetMacroListSize();
}
void MacroActionSequenceEdit::ContinueFromClicked()
{
if (_loading || !_entryData) {

View File

@@ -90,7 +90,7 @@ MacroActionSourceEdit::MacroActionSourceEdit(
_actions = new QComboBox();
_getSettings = new QPushButton(
obs_module_text("AdvSceneSwitcher.action.source.getSettings"));
_settings = new QPlainTextEdit();
_settings = new ResizingPlainTextEdit(this);
_warning = new QLabel(
obs_module_text("AdvSceneSwitcher.action.source.warning"));
@@ -141,6 +141,9 @@ void MacroActionSourceEdit::UpdateEntryData()
GetWeakSourceName(_entryData->_source).c_str());
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
SetWidgetVisibility(_entryData->_action == SourceAction::SETTINGS);
adjustSize();
updateGeometry();
}
void MacroActionSourceEdit::SourceChanged(const QString &text)
@@ -184,6 +187,9 @@ void MacroActionSourceEdit::SettingsChanged()
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroActionSourceEdit::SetWidgetVisibility(bool showSettings)

View File

@@ -3,6 +3,8 @@
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <QCalendarWidget>
const std::string MacroConditionDate::id = "date";
bool MacroConditionDate::_registered = MacroConditionFactory::Register(
@@ -18,7 +20,34 @@ static std::map<DateCondition, std::string> dateConditionTypes = {
"AdvSceneSwitcher.condition.date.state.between"},
};
bool MacroConditionDate::CheckCondition()
static std::map<DayOfWeekSelection, std::string> dayOfWeekNames = {
{DayOfWeekSelection::ANY, "AdvSceneSwitcher.condition.date.anyDay"},
{DayOfWeekSelection::MONDAY, "AdvSceneSwitcher.condition.date.monday"},
{DayOfWeekSelection::TUESDAY,
"AdvSceneSwitcher.condition.date.tuesday"},
{DayOfWeekSelection::WEDNESDAY,
"AdvSceneSwitcher.condition.date.wednesday"},
{DayOfWeekSelection::THURSDAY,
"AdvSceneSwitcher.condition.date.thursday"},
{DayOfWeekSelection::FRIDAY, "AdvSceneSwitcher.condition.date.friday"},
{DayOfWeekSelection::SATURDAY,
"AdvSceneSwitcher.condition.date.saturday"},
{DayOfWeekSelection::SUNDAY, "AdvSceneSwitcher.condition.date.sunday"},
};
bool MacroConditionDate::checkDayOfWeek()
{
QDateTime cur = QDateTime::currentDateTime();
if (_dayOfWeek != DayOfWeekSelection::ANY &&
cur.date().dayOfWeek() != static_cast<int>(_dayOfWeek)) {
return false;
}
_dateTime.setDate(cur.date());
return _dateTime >= cur &&
_dateTime <= cur.addMSecs(switcher->interval);
}
bool MacroConditionDate::checkRegularDate()
{
bool match = false;
QDateTime cur = QDateTime::currentDateTime();
@@ -61,9 +90,18 @@ bool MacroConditionDate::CheckCondition()
return match;
}
bool MacroConditionDate::CheckCondition()
{
if (_dayOfWeekCheck) {
return checkDayOfWeek();
}
return checkRegularDate();
}
bool MacroConditionDate::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "dayOfWeek", static_cast<int>(_dayOfWeek));
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_string(obj, "dateTime",
_dateTime.toString().toStdString().c_str());
@@ -73,13 +111,15 @@ bool MacroConditionDate::Save(obs_data_t *obj)
obs_data_set_bool(obj, "ignoreTime", _ignoreTime);
obs_data_set_bool(obj, "repeat", _repeat);
_duration.Save(obj);
obs_data_set_bool(obj, "dayOfWeekCheck", _dayOfWeekCheck);
return true;
}
bool MacroConditionDate::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_dayOfWeek = static_cast<DayOfWeekSelection>(
obs_data_get_int(obj, "dayOfWeek"));
_condition =
static_cast<DateCondition>(obs_data_get_int(obj, "condition"));
_dateTime = QDateTime::fromString(
@@ -90,12 +130,43 @@ bool MacroConditionDate::Load(obs_data_t *obj)
_ignoreTime = obs_data_get_bool(obj, "ignoreTime");
_repeat = obs_data_get_bool(obj, "repeat");
_duration.Load(obj);
// TODO: Remove this fallback in future version
if (!obs_data_has_user_value(obj, "dayOfWeekCheck")) {
_dayOfWeekCheck = false;
} else {
_dayOfWeekCheck = obs_data_get_bool(obj, "dayOfWeekCheck");
}
return true;
}
std::string MacroConditionDate::GetShortDesc()
{
return _dateTime.toString().toStdString();
if (_dayOfWeekCheck) {
auto it = dayOfWeekNames.find(_dayOfWeek);
if (it == dayOfWeekNames.end()) {
return "";
}
std::string dayName = obs_module_text(it->second.c_str());
return dayName + " " +
_dateTime.time().toString().toStdString();
} else {
if (!_ignoreDate && !_ignoreTime) {
return _dateTime.toString().toStdString();
} else if (_ignoreDate && !_ignoreTime) {
return _dateTime.time().toString().toStdString();
} else if (!_ignoreDate && _ignoreTime) {
return _dateTime.date().toString().toStdString();
}
}
return "";
}
static inline void populateDaySelection(QComboBox *list)
{
for (auto entry : dayOfWeekNames) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
static inline void populateConditionSelection(QComboBox *list)
@@ -109,24 +180,53 @@ MacroConditionDateEdit::MacroConditionDateEdit(
QWidget *parent, std::shared_ptr<MacroConditionDate> entryData)
: QWidget(parent)
{
_dayOfWeek = new QComboBox();
_weekTime = new QTimeEdit();
_weekTime->setDisplayFormat("hh:mm:ss");
_condition = new QComboBox();
_dateTime = new QDateTimeEdit();
_dateTime->setCalendarPopup(true);
_dateTime->setDisplayFormat("yyyy.MM.dd hh:mm:ss");
_dateTime2 = new QDateTimeEdit();
_dateTime2->setCalendarPopup(true);
_dateTime2->setDisplayFormat("yyyy.MM.dd hh:mm:ss");
_date = new QDateEdit();
_date->setDisplayFormat("yyyy.MM.dd ");
_date->setCalendarPopup(true);
auto cal = _date->calendarWidget();
cal->showSelectedDate();
_time = new QTimeEdit();
_time->setDisplayFormat("hh:mm:ss");
_separator = new QLabel(
obs_module_text("AdvSceneSwitcher.condition.date.separator"));
_date2 = new QDateEdit();
_date2->setDisplayFormat("yyyy.MM.dd ");
_date2->setCalendarPopup(true);
cal = _date2->calendarWidget();
cal->showSelectedDate();
_time2 = new QTimeEdit();
_time2->setDisplayFormat("hh:mm:ss");
_ignoreDate = new QCheckBox();
_ignoreDate->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.date.ignoreDate"));
_ignoreTime = new QCheckBox();
_ignoreTime->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.date.ignoreTime"));
_repeat = new QCheckBox();
_duration = new DurationSelection();
_advancedSettingsTooggle = new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"));
QWidget::connect(_dayOfWeek, SIGNAL(currentIndexChanged(int)), this,
SLOT(DayOfWeekChanged(int)));
QWidget::connect(_weekTime, SIGNAL(timeChanged(const QTime &)), this,
SLOT(TimeChanged(const QTime &)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_dateTime, SIGNAL(dateTimeChanged(const QDateTime &)),
this, SLOT(DateTimeChanged(const QDateTime &)));
QWidget::connect(_dateTime2, SIGNAL(dateTimeChanged(const QDateTime &)),
this, SLOT(DateTime2Changed(const QDateTime &)));
QWidget::connect(_date, SIGNAL(dateChanged(const QDate &)), this,
SLOT(DateChanged(const QDate &)));
QWidget::connect(_date2, SIGNAL(dateChanged(const QDate &)), this,
SLOT(Date2Changed(const QDate &)));
QWidget::connect(_time, SIGNAL(timeChanged(const QTime &)), this,
SLOT(TimeChanged(const QTime &)));
QWidget::connect(_time2, SIGNAL(timeChanged(const QTime &)), this,
SLOT(Time2Changed(const QTime &)));
QWidget::connect(_ignoreDate, SIGNAL(stateChanged(int)), this,
SLOT(IgnoreDateChanged(int)));
QWidget::connect(_ignoreTime, SIGNAL(stateChanged(int)), this,
@@ -137,30 +237,47 @@ MacroConditionDateEdit::MacroConditionDateEdit(
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
QWidget::connect(_advancedSettingsTooggle, SIGNAL(clicked()), this,
SLOT(AdvancedSettingsToggleClicked()));
populateDaySelection(_dayOfWeek);
populateConditionSelection(_condition);
auto line1Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{dayOfWeek}}", _dayOfWeek},
{"{{weekTime}}", _weekTime},
{"{{condition}}", _condition},
{"{{dateTime}}", _dateTime},
{"{{dateTime2}}", _dateTime2},
{"{{date}}", _date},
{"{{time}}", _time},
{"{{separator}}", _separator},
{"{{date2}}", _date2},
{"{{time2}}", _time2},
{"{{ignoreDate}}", _ignoreDate},
{"{{ignoreTime}}", _ignoreTime},
{"{{repeat}}", _repeat},
{"{{duration}}", _duration},
};
_simpleLayout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.line1"),
line1Layout, widgetPlaceholders);
auto line2Layout = new QHBoxLayout;
obs_module_text("AdvSceneSwitcher.condition.date.entry.simple"),
_simpleLayout, widgetPlaceholders);
_advancedLayout = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.date.entry.advanced"),
_advancedLayout, widgetPlaceholders);
_repeatLayout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.line2"),
line2Layout, widgetPlaceholders);
obs_module_text("AdvSceneSwitcher.condition.date.entry.repeat"),
_repeatLayout, widgetPlaceholders);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
mainLayout->addLayout(_simpleLayout);
mainLayout->addLayout(_advancedLayout);
mainLayout->addLayout(_repeatLayout);
auto *_advancedToggleLayout = new QHBoxLayout;
_advancedToggleLayout->addWidget(_advancedSettingsTooggle);
_advancedToggleLayout->addStretch();
mainLayout->addLayout(_advancedToggleLayout);
setLayout(mainLayout);
_entryData = entryData;
@@ -168,6 +285,18 @@ MacroConditionDateEdit::MacroConditionDateEdit(
_loading = false;
}
void MacroConditionDateEdit::DayOfWeekChanged(int day)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dayOfWeek = static_cast<DayOfWeekSelection>(day);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::ConditionChanged(int cond)
{
if (_loading || !_entryData) {
@@ -180,27 +309,50 @@ void MacroConditionDateEdit::ConditionChanged(int cond)
DateCondition::BETWEEN);
}
void MacroConditionDateEdit::DateTimeChanged(const QDateTime &datetime)
void MacroConditionDateEdit::DateChanged(const QDate &date)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime = datetime;
_entryData->_dateTime.setDate(date);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::DateTime2Changed(const QDateTime &datetime)
void MacroConditionDateEdit::TimeChanged(const QTime &time)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime2 = datetime;
_entryData->_dateTime.setTime(time);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::Date2Changed(const QDate &date)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime2.setDate(date);
}
void MacroConditionDateEdit::Time2Changed(const QTime &time)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime2.setTime(time);
}
void MacroConditionDateEdit::IgnoreDateChanged(int state)
@@ -210,7 +362,8 @@ void MacroConditionDateEdit::IgnoreDateChanged(int state)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_ignoreDate = state;
_entryData->_ignoreDate = !state;
SetWidgetStatus();
}
void MacroConditionDateEdit::IgnoreTimeChanged(int state)
@@ -220,7 +373,8 @@ void MacroConditionDateEdit::IgnoreTimeChanged(int state)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_ignoreTime = state;
_entryData->_ignoreTime = !state;
SetWidgetStatus();
}
void MacroConditionDateEdit::RepeatChanged(int state)
@@ -254,26 +408,74 @@ void MacroConditionDateEdit::DurationUnitChanged(DurationUnit unit)
_entryData->_duration.displayUnit = unit;
}
void MacroConditionDateEdit::AdvancedSettingsToggleClicked()
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dayOfWeekCheck = !_entryData->_dayOfWeekCheck;
SetWidgetStatus();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_dayOfWeek->setCurrentIndex(
static_cast<int>(static_cast<int>(_entryData->_dayOfWeek)));
_weekTime->setTime(_entryData->_dateTime.time());
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_dateTime->setDateTime(_entryData->_dateTime);
_dateTime2->setDateTime(_entryData->_dateTime2);
_ignoreDate->setChecked(_entryData->_ignoreDate);
_ignoreTime->setChecked(_entryData->_ignoreTime);
_date->setDate(_entryData->_dateTime.date());
_time->setTime(_entryData->_dateTime.time());
_date2->setDate(_entryData->_dateTime2.date());
_time2->setTime(_entryData->_dateTime2.time());
_ignoreDate->setChecked(!_entryData->_ignoreDate);
_ignoreTime->setChecked(!_entryData->_ignoreTime);
_repeat->setChecked(_entryData->_repeat);
_duration->SetDuration(_entryData->_duration);
_duration->setDisabled(!_entryData->_repeat);
ShowSecondDateSelection(_entryData->_condition ==
DateCondition::BETWEEN);
SetWidgetStatus();
}
void MacroConditionDateEdit::SetWidgetStatus()
{
if (!_entryData) {
return;
}
setLayoutVisible(_simpleLayout, _entryData->_dayOfWeekCheck);
setLayoutVisible(_advancedLayout, !_entryData->_dayOfWeekCheck);
setLayoutVisible(_repeatLayout, !_entryData->_dayOfWeekCheck);
if (_entryData->_dayOfWeekCheck) {
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"));
} else {
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showSimpleSettings"));
_date->setDisabled(_entryData->_ignoreDate);
_date2->setDisabled(_entryData->_ignoreDate);
_time->setDisabled(_entryData->_ignoreTime);
_time2->setDisabled(_entryData->_ignoreTime);
ShowSecondDateSelection(_entryData->_condition ==
DateCondition::BETWEEN);
}
const QSignalBlocker b1(_weekTime);
const QSignalBlocker b2(_weekTime);
_weekTime->setTime(_entryData->_dateTime.time());
_weekTime->setTime(_entryData->_dateTime.time());
adjustSize();
}
void MacroConditionDateEdit::ShowSecondDateSelection(bool visible)
{
_dateTime2->setVisible(visible);
adjustSize();
_separator->setVisible(visible);
_date2->setVisible(visible);
_time2->setVisible(visible);
}

View File

@@ -2,7 +2,6 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-scene.hpp"
#include "headers/section.hpp"
#include "headers/macro-controls.hpp"
#include "headers/utility.hpp"
std::map<std::string, MacroConditionInfo> MacroConditionFactory::_methods;
@@ -120,9 +119,15 @@ MacroConditionEdit::MacroConditionEdit(
_section->AddHeaderWidget(_headerInfo);
_section->AddHeaderWidget(_dur);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_section);
mainLayout->addWidget(_controls);
QVBoxLayout *conditionLayout = new QVBoxLayout;
conditionLayout->setContentsMargins(0, 0, 0, 0);
conditionLayout->setSpacing(0);
conditionLayout->addWidget(_frame);
_highLightFrameLayout->addWidget(_section);
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addLayout(conditionLayout);
setLayout(mainLayout);
UpdateEntryData(id);
@@ -183,6 +188,11 @@ void MacroConditionEdit::UpdateEntryData(const std::string &id)
SetFocusPolicyOfWidgets();
}
void MacroConditionEdit::SetEntryData(std::shared_ptr<MacroCondition> *data)
{
_entryData = data;
}
void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
{
if (_loading || !_entryData) {
@@ -207,7 +217,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
MacroConditionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget, false);
_section->SetContent(widget);
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
SetFocusPolicyOfWidgets();
}
@@ -259,30 +269,38 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
}
std::string id;
if (idx - 1 >= 0) {
LogicType logic;
if (idx >= 1) {
id = macro->Conditions().at(idx - 1)->GetId();
if (idx == 1) {
logic = LogicType::NONE;
} else {
logic = macro->Conditions().at(idx - 1)->GetLogicType();
}
} else {
MacroConditionScene temp(macro);
id = temp.GetId();
logic = LogicType::ROOT_NONE;
}
std::lock_guard<std::mutex> lock(switcher->m);
bool root = idx == 0;
auto cond = macro->Conditions().emplace(
macro->Conditions().begin() + idx,
MacroConditionFactory::Create(id, macro));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Conditions().at(idx - 1)->Save(data);
macro->Conditions().at(idx)->Load(data);
obs_data_release(data);
{
std::lock_guard<std::mutex> lock(switcher->m);
auto cond = macro->Conditions().emplace(
macro->Conditions().begin() + idx,
MacroConditionFactory::Create(id, macro));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Conditions().at(idx - 1)->Save(data);
macro->Conditions().at(idx)->Load(data);
obs_data_release(data);
}
(*cond)->SetLogicType(logic);
macro->UpdateConditionIndices();
}
auto logic = root ? LogicType::ROOT_NONE : LogicType::NONE;
(*cond)->SetLogicType(logic);
macro->UpdateConditionIndices();
clearLayout(ui->macroEditConditionLayout, idx);
clearLayout(conditionsList->ContentLayout(), idx);
PopulateMacroConditions(*macro, idx);
HighlightCondition(idx);
SetConditionData(*macro);
}
void AdvSceneSwitcher::on_conditionAdd_clicked()
@@ -292,8 +310,19 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
return;
}
AddMacroCondition((int)macro->Conditions().size());
ui->macroEditConditionHelp->setVisible(false);
if (currentConditionIdx == -1) {
auto macro = getSelectedMacro();
if (!macro) {
return;
}
AddMacroCondition((int)macro->Conditions().size());
} else {
AddMacroCondition(currentConditionIdx + 1);
}
if (currentConditionIdx != -1) {
MacroConditionSelectionChanged(currentConditionIdx + 1);
}
conditionsList->SetHelpMsgVisible(false);
}
void AdvSceneSwitcher::RemoveMacroCondition(int idx)
@@ -307,26 +336,52 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
macro->Conditions().erase(macro->Conditions().begin() + idx);
macro->UpdateConditionIndices();
{
std::lock_guard<std::mutex> lock(switcher->m);
macro->Conditions().erase(macro->Conditions().begin() + idx);
macro->UpdateConditionIndices();
if (idx == 0 && macro->Conditions().size() > 0) {
auto newRoot = macro->Conditions().at(0);
newRoot->SetLogicType(LogicType::ROOT_NONE);
if (idx == 0 && macro->Conditions().size() > 0) {
auto newRoot = macro->Conditions().at(0);
newRoot->SetLogicType(LogicType::ROOT_NONE);
}
}
clearLayout(ui->macroEditConditionLayout, idx);
clearLayout(conditionsList->ContentLayout(), idx);
PopulateMacroConditions(*macro, idx);
SetConditionData(*macro);
}
void AdvSceneSwitcher::on_conditionRemove_clicked()
{
auto macro = getSelectedMacro();
if (!macro) {
if (currentConditionIdx == -1) {
auto macro = getSelectedMacro();
if (!macro) {
return;
}
RemoveMacroCondition((int)macro->Conditions().size() - 1);
} else {
RemoveMacroCondition(currentConditionIdx);
}
MacroConditionSelectionChanged(-1);
}
void AdvSceneSwitcher::on_conditionUp_clicked()
{
if (currentConditionIdx == -1) {
return;
}
RemoveMacroCondition((int)macro->Conditions().size() - 1);
MoveMacroConditionUp(currentConditionIdx);
MacroConditionSelectionChanged(currentConditionIdx - 1);
}
void AdvSceneSwitcher::on_conditionDown_clicked()
{
if (currentConditionIdx == -1) {
return;
}
MoveMacroConditionDown(currentConditionIdx);
MacroConditionSelectionChanged(currentConditionIdx + 1);
}
void AdvSceneSwitcher::SwapConditions(Macro *m, int pos1, int pos2)
@@ -353,8 +408,8 @@ void AdvSceneSwitcher::SwapConditions(Macro *m, int pos1, int pos2)
(*c2)->SetLogicType(logic1);
}
auto item1 = ui->macroEditConditionLayout->takeAt(pos1);
auto item2 = ui->macroEditConditionLayout->takeAt(pos2 - 1);
auto item1 = conditionsList->ContentLayout()->takeAt(pos1);
auto item2 = conditionsList->ContentLayout()->takeAt(pos2 - 1);
deleteLayoutItem(item1);
deleteLayoutItem(item2);
auto widget1 =
@@ -363,8 +418,8 @@ void AdvSceneSwitcher::SwapConditions(Macro *m, int pos1, int pos2)
new MacroConditionEdit(this, &(*c2), (*c2)->GetId(), false);
ConnectControlSignals(widget1);
ConnectControlSignals(widget2);
ui->macroEditConditionLayout->insertWidget(pos1, widget1);
ui->macroEditConditionLayout->insertWidget(pos2, widget2);
conditionsList->ContentLayout()->insertWidget(pos1, widget1);
conditionsList->ContentLayout()->insertWidget(pos2, widget2);
}
void AdvSceneSwitcher::MoveMacroConditionUp(int idx)
@@ -379,6 +434,7 @@ void AdvSceneSwitcher::MoveMacroConditionUp(int idx)
}
SwapConditions(macro, idx, idx - 1);
HighlightCondition(idx - 1);
}
void AdvSceneSwitcher::MoveMacroConditionDown(int idx)
@@ -393,4 +449,24 @@ void AdvSceneSwitcher::MoveMacroConditionDown(int idx)
}
SwapConditions(macro, idx, idx + 1);
HighlightCondition(idx + 1);
}
void AdvSceneSwitcher::MacroConditionSelectionChanged(int idx)
{
auto macro = getSelectedMacro();
if (!macro) {
return;
}
SetSelection(conditionsList, idx);
SetSelection(actionsList, -1);
if (idx < 0 || (unsigned)idx >= macro->Conditions().size()) {
currentConditionIdx = -1;
} else {
currentConditionIdx = idx;
}
currentActionIdx = -1;
HighlightControls();
}

View File

@@ -138,7 +138,7 @@ MacroConditionFileEdit::MacroConditionFileEdit(
{
_fileType = new QComboBox();
_filePath = new FileSelection();
_matchText = new QPlainTextEdit();
_matchText = new ResizingPlainTextEdit(this);
_useRegex = new QCheckBox(
obs_module_text("AdvSceneSwitcher.fileTab.useRegExp"));
_checkModificationDate = new QCheckBox(obs_module_text(
@@ -208,6 +208,9 @@ void MacroConditionFileEdit::UpdateEntryData()
_useRegex->setChecked(_entryData->_useRegex);
_checkModificationDate->setChecked(_entryData->_useTime);
_checkFileContent->setChecked(_entryData->_onlyMatchIfChanged);
adjustSize();
updateGeometry();
}
void MacroConditionFileEdit::FileTypeChanged(int index)
@@ -250,6 +253,9 @@ void MacroConditionFileEdit::MatchTextChanged()
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_text = _matchText->toPlainText().toUtf8().constData();
adjustSize();
updateGeometry();
}
void MacroConditionFileEdit::UseRegexChanged(int state)

View File

@@ -100,7 +100,7 @@ MacroConditionFilterEdit::MacroConditionFilterEdit(
_conditions = new QComboBox();
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.filter.getSettings"));
_settings = new QPlainTextEdit();
_settings = new ResizingPlainTextEdit(this);
_regex = new QCheckBox(
obs_module_text("AdvSceneSwitcher.condition.filter.regex"));
@@ -208,6 +208,9 @@ void MacroConditionFilterEdit::SettingsChanged()
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroConditionFilterEdit::RegexChanged(int state)
@@ -244,4 +247,7 @@ void MacroConditionFilterEdit::UpdateEntryData()
_regex->setChecked(_entryData->_regex);
SetSettingsSelectionVisible(_entryData->_condition ==
FilterCondition::SETTINGS);
adjustSize();
updateGeometry();
}

View File

@@ -0,0 +1,584 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-obs-stats.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionStats::id = "obs_stats";
bool MacroConditionStats::_registered = MacroConditionFactory::Register(
MacroConditionStats::id,
{MacroConditionStats::Create, MacroConditionStatsEdit::Create,
"AdvSceneSwitcher.condition.stats"});
static std::map<StatsType, std::string> statsTypes = {
{StatsType::FPS, "AdvSceneSwitcher.condition.stats.type.fps"},
{StatsType::CPU_USAGE,
"AdvSceneSwitcher.condition.stats.type.CPUUsage"},
{StatsType::DISK_USAGE,
"AdvSceneSwitcher.condition.stats.type.HDDSpaceAvailable"},
{StatsType::MEM_USAGE,
"AdvSceneSwitcher.condition.stats.type.memoryUsage"},
{StatsType::AVG_FRAMETIME,
"AdvSceneSwitcher.condition.stats.type.averageTimeToRender"},
{StatsType::RENDER_LAG,
"AdvSceneSwitcher.condition.stats.type.missedFrames"},
{StatsType::ENCODE_LAG,
"AdvSceneSwitcher.condition.stats.type.skippedFrames"},
{StatsType::STREAM_DROPPED_FRAMES,
"AdvSceneSwitcher.condition.stats.type.droppedFrames.stream"},
{StatsType::STREAM_BITRATE,
"AdvSceneSwitcher.condition.stats.type.bitrate.stream"},
{StatsType::STREAM_MB_SENT,
"AdvSceneSwitcher.condition.stats.type.megabytesSent.stream"},
{StatsType::RECORDING_DROPPED_FRAMES,
"AdvSceneSwitcher.condition.stats.type.droppedFrames.recording"},
{StatsType::RECORDING_BITRATE,
"AdvSceneSwitcher.condition.stats.type.bitrate.recording"},
{StatsType::RECORDING_MB_SENT,
"AdvSceneSwitcher.condition.stats.type.megabytesSent.recording"},
};
static std::map<StatsCondition, std::string> statsConditionTypes = {
{StatsCondition::ABOVE,
"AdvSceneSwitcher.condition.stats.condition.above"},
{StatsCondition::EQUALS,
"AdvSceneSwitcher.condition.stats.condition.equals"},
{StatsCondition::BELOW,
"AdvSceneSwitcher.condition.stats.condition.below"},
};
MacroConditionStats::MacroConditionStats(Macro *m)
: MacroCondition(m), _cpu_info(os_cpu_usage_info_start())
{
}
MacroConditionStats::~MacroConditionStats()
{
os_cpu_usage_info_destroy(_cpu_info);
}
bool doubleEquals(double left, double right, double epsilon)
{
return (fabs(left - right) < epsilon);
}
bool MacroConditionStats::CheckFPS()
{
switch (_condition) {
case StatsCondition::ABOVE:
return obs_get_active_fps() > _value;
case StatsCondition::EQUALS:
return doubleEquals(obs_get_active_fps(), _value, 0.01);
case StatsCondition::BELOW:
return obs_get_active_fps() < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckCPU()
{
double usage = os_cpu_usage_info_query(_cpu_info);
switch (_condition) {
case StatsCondition::ABOVE:
return usage > _value;
case StatsCondition::EQUALS:
return doubleEquals(usage, _value, 0.1);
case StatsCondition::BELOW:
return usage < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckMemory()
{
auto rss =
(long double)os_get_proc_resident_size() / (1024.0l * 1024.0l);
switch (_condition) {
case StatsCondition::ABOVE:
return rss > _value;
case StatsCondition::EQUALS:
return doubleEquals(rss, _value, 0.1);
case StatsCondition::BELOW:
return rss < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckAvgFrametime()
{
auto num = (long double)obs_get_average_frame_time_ns() / 1000000.0l;
switch (_condition) {
case StatsCondition::ABOVE:
return num > _value;
case StatsCondition::EQUALS:
return doubleEquals(num, _value, 0.1);
case StatsCondition::BELOW:
return num < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckRenderLag()
{
uint32_t total_rendered = obs_get_total_frames();
uint32_t total_lagged = obs_get_lagged_frames();
if (total_rendered < _first_rendered || total_lagged < _first_lagged) {
_first_rendered = total_rendered;
_first_lagged = total_lagged;
}
total_rendered -= _first_rendered;
total_lagged -= _first_lagged;
auto num = total_rendered ? (long double)total_lagged /
(long double)total_rendered
: 0.0l;
num *= 100.0l;
switch (_condition) {
case StatsCondition::ABOVE:
return num > _value;
case StatsCondition::EQUALS:
return doubleEquals(num, _value, 0.1);
case StatsCondition::BELOW:
return num < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckEncodeLag()
{
video_t *video = obs_get_video();
uint32_t total_encoded = video_output_get_total_frames(video);
uint32_t total_skipped = video_output_get_skipped_frames(video);
if (total_encoded < _first_encoded || total_skipped < _first_encoded) {
_first_encoded = total_encoded;
_first_skipped = total_skipped;
}
total_encoded -= _first_encoded;
total_skipped -= _first_skipped;
auto num = total_encoded ? (long double)total_skipped /
(long double)total_encoded
: 0.0l;
num *= 100.0l;
switch (_condition) {
case StatsCondition::ABOVE:
return num > _value;
case StatsCondition::EQUALS:
return doubleEquals(num, _value, 0.1);
case StatsCondition::BELOW:
return num < _value;
default:
break;
}
return false;
}
void MacroConditionStats::OutputInfo::Update(obs_output_t *output)
{
uint64_t totalBytes = output ? obs_output_get_total_bytes(output) : 0;
uint64_t curTime = os_gettime_ns();
uint64_t bytesSent = totalBytes;
if (bytesSent < lastBytesSent)
bytesSent = 0;
if (bytesSent == 0)
lastBytesSent = 0;
uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;
long double timePassed =
(long double)(curTime - lastBytesSentTime) / 1000000000.0l;
kbps = (long double)bitsBetween / timePassed / 1000.0l;
if (timePassed < 0.01l)
kbps = 0.0l;
int total = output ? obs_output_get_total_frames(output) : 0;
int dropped = output ? obs_output_get_frames_dropped(output) : 0;
if (total < first_total || dropped < first_dropped) {
first_total = 0;
first_dropped = 0;
}
total -= first_total;
dropped -= first_dropped;
dropped_relative =
total ? (long double)dropped / (long double)total * 100.0l
: 0.0l;
lastBytesSent = bytesSent;
lastBytesSentTime = curTime;
}
bool MacroConditionStats::CheckStreamDroppedFrames()
{
auto output = obs_frontend_get_streaming_output();
_streamInfo.Update(output);
obs_output_release(output);
switch (_condition) {
case StatsCondition::ABOVE:
return _streamInfo.dropped_relative > _value;
case StatsCondition::EQUALS:
return doubleEquals(_streamInfo.dropped_relative, _value, 0.1);
case StatsCondition::BELOW:
return _streamInfo.dropped_relative < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckStreamBitrate()
{
auto output = obs_frontend_get_streaming_output();
_streamInfo.Update(output);
obs_output_release(output);
switch (_condition) {
case StatsCondition::ABOVE:
return _streamInfo.kbps > _value;
case StatsCondition::EQUALS:
return doubleEquals(_streamInfo.kbps, _value, 1.0);
case StatsCondition::BELOW:
return _streamInfo.kbps < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckStreamMBSent()
{
auto output = obs_frontend_get_streaming_output();
uint64_t totalBytes = output ? obs_output_get_total_bytes(output) : 0;
obs_output_release(output);
long double num = (long double)totalBytes / (1024.0l * 1024.0l);
switch (_condition) {
case StatsCondition::ABOVE:
return num > _value;
case StatsCondition::EQUALS:
return doubleEquals(num, _value, 0.1);
case StatsCondition::BELOW:
return num < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckRecordingDroppedFrames()
{
auto output = obs_frontend_get_recording_output();
_recordingInfo.Update(output);
obs_output_release(output);
switch (_condition) {
case StatsCondition::ABOVE:
return _recordingInfo.dropped_relative > _value;
case StatsCondition::EQUALS:
return doubleEquals(_recordingInfo.dropped_relative, _value,
0.1);
case StatsCondition::BELOW:
return _recordingInfo.dropped_relative < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckRecordingBitrate()
{
auto output = obs_frontend_get_recording_output();
_recordingInfo.Update(output);
obs_output_release(output);
switch (_condition) {
case StatsCondition::ABOVE:
return _recordingInfo.kbps > _value;
case StatsCondition::EQUALS:
return doubleEquals(_recordingInfo.kbps, _value, 1.0);
case StatsCondition::BELOW:
return _recordingInfo.kbps < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckRecordingMBSent()
{
auto output = obs_frontend_get_recording_output();
uint64_t totalBytes = output ? obs_output_get_total_bytes(output) : 0;
obs_output_release(output);
long double num = (long double)totalBytes / (1024.0l * 1024.0l);
switch (_condition) {
case StatsCondition::ABOVE:
return num > _value;
case StatsCondition::EQUALS:
return doubleEquals(num, _value, 0.1);
case StatsCondition::BELOW:
return num < _value;
default:
break;
}
return false;
}
bool MacroConditionStats::CheckCondition()
{
switch (_type) {
case StatsType::FPS:
return CheckFPS();
case StatsType::CPU_USAGE:
return CheckCPU();
case StatsType::DISK_USAGE:
// TODO: not implemented
break;
case StatsType::MEM_USAGE:
return CheckMemory();
case StatsType::AVG_FRAMETIME:
return CheckAvgFrametime();
case StatsType::RENDER_LAG:
return CheckRenderLag();
case StatsType::ENCODE_LAG:
return CheckEncodeLag();
case StatsType::STREAM_DROPPED_FRAMES:
return CheckStreamDroppedFrames();
case StatsType::STREAM_BITRATE:
return CheckStreamBitrate();
case StatsType::STREAM_MB_SENT:
return CheckStreamMBSent();
case StatsType::RECORDING_DROPPED_FRAMES:
return CheckRecordingDroppedFrames();
case StatsType::RECORDING_BITRATE:
return CheckRecordingBitrate();
case StatsType::RECORDING_MB_SENT:
return CheckRecordingMBSent();
default:
break;
}
return false;
}
bool MacroConditionStats::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_double(obj, "value", _value);
obs_data_set_int(obj, "type", static_cast<int>(_type));
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
return true;
}
bool MacroConditionStats::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_value = obs_data_get_double(obj, "value");
_type = static_cast<StatsType>(obs_data_get_int(obj, "type"));
_condition =
static_cast<StatsCondition>(obs_data_get_int(obj, "condition"));
return true;
}
std::string MacroConditionStats::GetShortDesc()
{
auto it = statsTypes.find(_type);
if (it != statsTypes.end()) {
return obs_module_text(it->second.c_str());
}
return "";
}
static inline void populateStatsTypes(QComboBox *list)
{
list->clear();
for (auto entry : statsTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
// TODO: not implemented
if (entry.first == StatsType::DISK_USAGE) {
qobject_cast<QListView *>(list->view())
->setRowHidden(list->count() - 1, true);
}
}
}
static inline void populateConditionSelection(QComboBox *list)
{
list->clear();
for (auto entry : statsConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionStatsEdit::MacroConditionStatsEdit(
QWidget *parent, std::shared_ptr<MacroConditionStats> entryData)
: QWidget(parent),
_stats(new QComboBox),
_condition(new QComboBox),
_value(new QDoubleSpinBox)
{
_value->setMaximum(999999999999);
populateStatsTypes(_stats);
populateConditionSelection(_condition);
setToolTip(
obs_module_text("AdvSceneSwitcher.condition.stats.dockHint"));
QWidget::connect(_value, SIGNAL(valueChanged(double)), this,
SLOT(ValueChanged(double)));
QWidget::connect(_stats, SIGNAL(currentIndexChanged(int)), this,
SLOT(StatsTypeChanged(int)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QHBoxLayout *layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{value}}", _value},
{"{{stats}}", _stats},
{"{{condition}}", _condition},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.stats.entry"),
layout, widgetPlaceholders);
setLayout(layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionStatsEdit::ValueChanged(double value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_value = value;
}
void MacroConditionStatsEdit::StatsTypeChanged(int type)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_type = static_cast<StatsType>(type);
SetWidgetVisibility();
}
_value->setValue(0);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionStatsEdit::ConditionChanged(int cond)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<StatsCondition>(cond);
}
void MacroConditionStatsEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_value->setValue(_entryData->_value);
_stats->setCurrentIndex(static_cast<int>(_entryData->_type));
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
SetWidgetVisibility();
}
void MacroConditionStatsEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
switch (_entryData->_type) {
case StatsType::FPS:
_value->setMaximum(1000);
_value->setSuffix("");
break;
case StatsType::CPU_USAGE:
_value->setMaximum(100);
_value->setSuffix("%");
break;
case StatsType::DISK_USAGE:
_value->setMaximum(999999999999);
_value->setSuffix("MB");
break;
case StatsType::MEM_USAGE:
_value->setMaximum(999999999999);
_value->setSuffix("MB");
break;
case StatsType::AVG_FRAMETIME:
_value->setMaximum(999999999999);
_value->setSuffix("ms");
break;
case StatsType::RENDER_LAG:
_value->setMaximum(100);
_value->setSuffix("%");
break;
case StatsType::ENCODE_LAG:
_value->setMaximum(100);
_value->setSuffix("%");
break;
case StatsType::STREAM_DROPPED_FRAMES:
_value->setMaximum(100);
_value->setSuffix("%");
break;
case StatsType::STREAM_BITRATE:
_value->setMaximum(999999999999);
_value->setSuffix("kb/s");
break;
case StatsType::STREAM_MB_SENT:
_value->setMaximum(999999999999);
_value->setSuffix("MB");
break;
case StatsType::RECORDING_DROPPED_FRAMES:
_value->setMaximum(100);
_value->setSuffix("%");
break;
case StatsType::RECORDING_BITRATE:
_value->setMaximum(999999999999);
_value->setSuffix("kb/s");
break;
case StatsType::RECORDING_MB_SENT:
_value->setMaximum(999999999999);
_value->setSuffix("MB");
break;
default:
break;
}
adjustSize();
}

View File

@@ -12,19 +12,30 @@ bool MacroConditionPluginState::_registered = MacroConditionFactory::Register(
"AdvSceneSwitcher.condition.pluginState"});
static std::map<PluginStateCondition, std::string> pluginStateConditionTypes = {
{PluginStateCondition::SCENESWITCHED,
{PluginStateCondition::SCENE_SWITCHED,
"AdvSceneSwitcher.condition.pluginState.state.sceneSwitched"},
{PluginStateCondition::RUNNING,
"AdvSceneSwitcher.condition.pluginState.state.running"},
{PluginStateCondition::SHUTDOWN,
"AdvSceneSwitcher.condition.pluginState.state.shutdown"},
};
MacroConditionPluginState::~MacroConditionPluginState()
{
if (_condition == PluginStateCondition::SHUTDOWN) {
switcher->shutdownConditionCount--;
}
}
bool MacroConditionPluginState::CheckCondition()
{
switch (_condition) {
case PluginStateCondition::SCENESWITCHED:
case PluginStateCondition::SCENE_SWITCHED:
return switcher->macroSceneSwitched;
case PluginStateCondition::RUNNING:
return true;
case PluginStateCondition::SHUTDOWN:
return switcher->obsIsShuttingDown;
default:
break;
}
@@ -43,6 +54,9 @@ bool MacroConditionPluginState::Load(obs_data_t *obj)
MacroCondition::Load(obj);
_condition = static_cast<PluginStateCondition>(
obs_data_get_int(obj, "condition"));
if (_condition == PluginStateCondition::SHUTDOWN) {
switcher->shutdownConditionCount++;
}
return true;
}
@@ -89,7 +103,13 @@ void MacroConditionPluginStateEdit::ConditionChanged(int cond)
}
std::lock_guard<std::mutex> lock(switcher->m);
if (_entryData->_condition == PluginStateCondition::SHUTDOWN) {
switcher->shutdownConditionCount--;
}
_entryData->_condition = static_cast<PluginStateCondition>(cond);
if (_entryData->_condition == PluginStateCondition::SHUTDOWN) {
switcher->shutdownConditionCount++;
}
}
void MacroConditionPluginStateEdit::UpdateEntryData()

View File

@@ -73,7 +73,7 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
parent, true, SceneItemSelectionWidget::AllSelectionType::ANY);
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.sceneTransform.getTransform"));
_settings = new QPlainTextEdit();
_settings = new ResizingPlainTextEdit(this);
_regex = new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.sceneTransform.regex"));
@@ -134,6 +134,9 @@ void MacroConditionSceneTransformEdit::UpdateEntryData()
_sources->SetSceneItem(_entryData->_source);
_regex->setChecked(_entryData->_regex);
_settings->setPlainText(QString::fromStdString(_entryData->_settings));
adjustSize();
updateGeometry();
}
void MacroConditionSceneTransformEdit::SceneChanged(const SceneSelection &s)
@@ -190,6 +193,9 @@ void MacroConditionSceneTransformEdit::SettingsChanged()
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroConditionSceneTransformEdit::RegexChanged(int state)

View File

@@ -94,7 +94,7 @@ MacroConditionSourceEdit::MacroConditionSourceEdit(
_conditions = new QComboBox();
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.source.getSettings"));
_settings = new QPlainTextEdit();
_settings = new ResizingPlainTextEdit(this);
_regex = new QCheckBox(
obs_module_text("AdvSceneSwitcher.condition.source.regex"));
@@ -185,6 +185,9 @@ void MacroConditionSourceEdit::SettingsChanged()
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_settings = _settings->toPlainText().toStdString();
adjustSize();
updateGeometry();
}
void MacroConditionSourceEdit::RegexChanged(int state)
@@ -218,4 +221,7 @@ void MacroConditionSourceEdit::UpdateEntryData()
_regex->setChecked(_entryData->_regex);
SetSettingsSelectionVisible(_entryData->_condition ==
SourceCondition::SETTINGS);
adjustSize();
updateGeometry();
}

View File

@@ -0,0 +1,344 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-date.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionDate::id = "date";
bool MacroConditionDate::_registered = MacroConditionFactory::Register(
MacroConditionDate::id,
{MacroConditionDate::Create, MacroConditionDateEdit::Create,
"AdvSceneSwitcher.condition.date", false});
static std::map<DateCondition, std::string> dateConditionTypes = {
{DateCondition::AT, "AdvSceneSwitcher.condition.date.state.at"},
{DateCondition::AFTER, "AdvSceneSwitcher.condition.date.state.after"},
{DateCondition::BEFORE, "AdvSceneSwitcher.condition.date.state.before"},
{DateCondition::BETWEEN,
"AdvSceneSwitcher.condition.date.state.between"},
};
bool MacroConditionDate::CheckCondition()
{
bool match = false;
QDateTime cur = QDateTime::currentDateTime();
if (_ignoreDate) {
_dateTime.setDate(cur.date());
_dateTime2.setDate(cur.date());
}
if (_ignoreTime) {
_dateTime.setTime(cur.time());
_dateTime2.setTime(cur.time());
}
switch (_condition) {
case DateCondition::AT:
match = _dateTime >= cur &&
_dateTime <= cur.addMSecs(switcher->interval);
break;
case DateCondition::AFTER:
match = cur >= _dateTime;
break;
case DateCondition::BEFORE:
match = cur <= _dateTime;
break;
case DateCondition::BETWEEN:
if (_dateTime2 > _dateTime) {
match = cur >= _dateTime && cur <= _dateTime2;
} else {
match = cur >= _dateTime2 && cur <= _dateTime;
}
break;
default:
break;
}
if (match && _repeat) {
_dateTime = _dateTime.addSecs(_duration.seconds);
_dateTime2 = _dateTime2.addSecs(_duration.seconds);
}
return match;
}
bool MacroConditionDate::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_string(obj, "dateTime",
_dateTime.toString().toStdString().c_str());
obs_data_set_string(obj, "dateTime2",
_dateTime2.toString().toStdString().c_str());
obs_data_set_bool(obj, "ignoreDate", _ignoreDate);
obs_data_set_bool(obj, "ignoreTime", _ignoreTime);
obs_data_set_bool(obj, "repeat", _repeat);
_duration.Save(obj);
return true;
}
bool MacroConditionDate::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_condition =
static_cast<DateCondition>(obs_data_get_int(obj, "condition"));
_dateTime = QDateTime::fromString(
QString::fromStdString(obs_data_get_string(obj, "dateTime")));
_dateTime2 = QDateTime::fromString(
QString::fromStdString(obs_data_get_string(obj, "dateTime2")));
_ignoreDate = obs_data_get_bool(obj, "ignoreDate");
_ignoreTime = obs_data_get_bool(obj, "ignoreTime");
_repeat = obs_data_get_bool(obj, "repeat");
_duration.Load(obj);
return true;
}
std::string MacroConditionDate::GetShortDesc()
{
return _dateTime.toString().toStdString();
}
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : dateConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
#include <QCalendarWidget>
MacroConditionDateEdit::MacroConditionDateEdit(
QWidget *parent, std::shared_ptr<MacroConditionDate> entryData)
: QWidget(parent)
{
_condition = new QComboBox();
_date = new QDateEdit();
_date->setDisplayFormat("yyyy.MM.dd ");
_date->setCalendarPopup(true);
auto cal = _date->calendarWidget();
cal->showSelectedDate();
_time = new QTimeEdit();
_time->setDisplayFormat("hh:mm:ss");
_separator = new QLabel(
obs_module_text("AdvSceneSwitcher.condition.date.separator"));
_date2 = new QDateEdit();
_date2->setDisplayFormat("yyyy.MM.dd ");
_date2->setCalendarPopup(true);
cal = _date2->calendarWidget();
cal->showSelectedDate();
_time2 = new QTimeEdit();
_time2->setDisplayFormat("hh:mm:ss");
_ignoreDate = new QCheckBox();
_ignoreDate->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.date.ignoreDate"));
_ignoreTime = new QCheckBox();
_ignoreTime->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.date.ignoreTime"));
_repeat = new QCheckBox();
_duration = new DurationSelection();
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_date, SIGNAL(dateChanged(const QDate &)), this,
SLOT(DateChanged(const QDate &)));
QWidget::connect(_date2, SIGNAL(dateChanged(const QDate &)), this,
SLOT(Date2Changed(const QDate &)));
QWidget::connect(_time, SIGNAL(timeChanged(const QTime &)), this,
SLOT(TimeChanged(const QTime &)));
QWidget::connect(_time2, SIGNAL(timeChanged(const QTime &)), this,
SLOT(Time2Changed(const QTime &)));
QWidget::connect(_ignoreDate, SIGNAL(stateChanged(int)), this,
SLOT(IgnoreDateChanged(int)));
QWidget::connect(_ignoreTime, SIGNAL(stateChanged(int)), this,
SLOT(IgnoreTimeChanged(int)));
QWidget::connect(_repeat, SIGNAL(stateChanged(int)), this,
SLOT(RepeatChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
populateConditionSelection(_condition);
auto line1Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{condition}}", _condition},
{"{{date}}", _date},
{"{{time}}", _time},
{"{{separator}}", _separator},
{"{{date2}}", _date2},
{"{{time2}}", _time2},
{"{{ignoreDate}}", _ignoreDate},
{"{{ignoreTime}}", _ignoreTime},
{"{{repeat}}", _repeat},
{"{{duration}}", _duration},
};
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.line1"),
line1Layout, widgetPlaceholders);
auto line2Layout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.line2"),
line2Layout, widgetPlaceholders);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionDateEdit::ConditionChanged(int cond)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition = static_cast<DateCondition>(cond);
ShowSecondDateSelection(_entryData->_condition ==
DateCondition::BETWEEN);
}
void MacroConditionDateEdit::DateChanged(const QDate &date)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime.setDate(date);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::TimeChanged(const QTime &time)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime.setTime(time);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::Date2Changed(const QDate &date)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime2.setDate(date);
}
void MacroConditionDateEdit::Time2Changed(const QTime &time)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime2.setTime(time);
}
void MacroConditionDateEdit::IgnoreDateChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_ignoreDate = !state;
SetWidgetStatus();
}
void MacroConditionDateEdit::IgnoreTimeChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_ignoreTime = !state;
SetWidgetStatus();
}
void MacroConditionDateEdit::RepeatChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_repeat = state;
_duration->setDisabled(!state);
}
void MacroConditionDateEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionDateEdit::DurationUnitChanged(DurationUnit unit)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.displayUnit = unit;
}
void MacroConditionDateEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
_date->setDate(_entryData->_dateTime.date());
_time->setTime(_entryData->_dateTime.time());
_date2->setDate(_entryData->_dateTime2.date());
_time2->setTime(_entryData->_dateTime2.time());
_ignoreDate->setChecked(!_entryData->_ignoreDate);
_ignoreTime->setChecked(!_entryData->_ignoreTime);
_repeat->setChecked(_entryData->_repeat);
_duration->SetDuration(_entryData->_duration);
_duration->setDisabled(!_entryData->_repeat);
SetWidgetStatus();
}
void MacroConditionDateEdit::SetWidgetStatus()
{
if (!_entryData) {
return;
}
_date->setDisabled(_entryData->_ignoreDate);
_date2->setDisabled(_entryData->_ignoreDate);
_time->setDisabled(_entryData->_ignoreTime);
_time2->setDisabled(_entryData->_ignoreTime);
ShowSecondDateSelection(_entryData->_condition ==
DateCondition::BETWEEN);
}
void MacroConditionDateEdit::ShowSecondDateSelection(bool visible)
{
_separator->setVisible(visible);
_date2->setVisible(visible);
_time2->setVisible(visible);
adjustSize();
}

View File

@@ -1,70 +0,0 @@
#include "headers/macro-controls.hpp"
MacroEntryControls::MacroEntryControls(const int animationDuration,
QWidget *parent)
: QWidget(parent)
{
_add = new QPushButton(this);
_add->setMaximumSize(QSize(22, 22));
_add->setProperty("themeID",
QVariant(QString::fromUtf8("addIconSmall")));
_add->setFlat(true);
_remove = new QPushButton(this);
_remove->setMaximumSize(QSize(22, 22));
_remove->setProperty("themeID",
QVariant(QString::fromUtf8("removeIconSmall")));
_remove->setFlat(true);
_up = new QPushButton(this);
_up->setMaximumSize(QSize(22, 22));
_up->setProperty("themeID",
QVariant(QString::fromUtf8("upArrowIconSmall")));
_up->setFlat(true);
_down = new QPushButton(this);
_down->setMaximumSize(QSize(22, 22));
_down->setProperty("themeID",
QVariant(QString::fromUtf8("downArrowIconSmall")));
_down->setFlat(true);
auto line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
auto mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(_add);
mainLayout->addWidget(_remove);
auto *separator = new QFrame(this);
separator->setFrameShape(QFrame::VLine);
separator->setFrameShadow(QFrame::Sunken);
mainLayout->addWidget(separator);
mainLayout->addWidget(_up);
mainLayout->addWidget(_down);
mainLayout->addWidget(line);
mainLayout->setContentsMargins(0, 0, 0, 0);
_animation = new QPropertyAnimation(this, "maximumHeight");
_animation->setDuration(animationDuration);
_animation->setStartValue(0);
_animation->setEndValue(mainLayout->sizeHint().height());
setMaximumHeight(0);
setLayout(mainLayout);
connect(_add, &QPushButton::clicked, this, &MacroEntryControls::Add);
connect(_remove, &QPushButton::clicked, this,
&MacroEntryControls::Remove);
connect(_up, &QPushButton::clicked, this, &MacroEntryControls::Up);
connect(_down, &QPushButton::clicked, this, &MacroEntryControls::Down);
}
void MacroEntryControls::Show(bool visible)
{
_animation->setDirection(visible ? QAbstractAnimation::Forward
: QAbstractAnimation::Backward);
_animation->start();
}

View File

@@ -0,0 +1,39 @@
#include "headers/macro-list-entry-widget.hpp"
#include "headers/macro.hpp"
MacroListEntryWidget::MacroListEntryWidget(std::shared_ptr<Macro> macro,
QWidget *parent)
: QWidget(parent), _macro(macro)
{
_name = new QLabel(QString::fromStdString(macro->Name()));
_running = new QCheckBox();
_running->setChecked(!macro->Paused());
connect(_running, SIGNAL(stateChanged(int)), this,
SLOT(PauseChanged(int)));
setStyleSheet("\
QCheckBox { background-color: rgba(0,0,0,0); }\
QLabel { background-color: rgba(0,0,0,0); }");
auto layout = new QHBoxLayout;
layout->setContentsMargins(3, 3, 3, 3);
layout->addWidget(_running);
layout->addWidget(_name);
layout->addStretch();
setLayout(layout);
}
void MacroListEntryWidget::PauseChanged(int state)
{
_macro->SetPaused(!state);
}
void MacroListEntryWidget::SetName(const QString &name)
{
_name->setText(name);
}
void MacroListEntryWidget::SetMacro(std::shared_ptr<Macro> &m)
{
_macro = m;
}

View File

@@ -0,0 +1,44 @@
#include "headers/macro-segment-list.hpp"
#include <QGridLayout>
#include <QSpacerItem>
#include <QMouseEvent>
MacroSegmentList::MacroSegmentList(QWidget *parent) : QScrollArea(parent)
{
_contentLayout = new QVBoxLayout;
_helpMsg = new QLabel;
_helpMsg->setWordWrap(true);
_helpMsg->setAlignment(Qt::AlignCenter);
auto helperLayout = new QGridLayout();
helperLayout->addWidget(_helpMsg, 0, 0,
Qt::AlignHCenter | Qt::AlignVCenter);
helperLayout->addLayout(_contentLayout, 0, 0);
helperLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
auto layout = new QVBoxLayout;
layout->addLayout(helperLayout, 10);
layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
QSizePolicy::Expanding));
auto wrapper = new QWidget;
wrapper->setLayout(layout);
setWidget(wrapper);
setWidgetResizable(true);
}
void MacroSegmentList::SetHelpMsg(const QString &msg)
{
_helpMsg->setText(msg);
}
void MacroSegmentList::SetHelpMsgVisible(bool visible)
{
_helpMsg->setVisible(visible);
}
void MacroSegmentList::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
emit SelectionChagned(-1);
}
}

View File

@@ -1,9 +1,9 @@
#include "headers/macro-segment.hpp"
#include "headers/macro-controls.hpp"
#include "headers/section.hpp"
#include <obs.hpp>
#include <QEvent>
#include <QMouseEvent>
#include <QLabel>
#include <QScrollBar>
@@ -44,7 +44,20 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent) : QWidget(parent)
{
_section = new Section(300);
_headerInfo = new QLabel();
_controls = new MacroEntryControls();
_frame = new QFrame;
_frame->setObjectName("segmentFrame");
_highLightFrameLayout = new QVBoxLayout;
SetSelected(false);
_frame->setLayout(_highLightFrameLayout);
// Set background transparent to avoid blocking highlight frame
setStyleSheet("QCheckBox { background-color: rgba(0,0,0,0); }"
"QLabel { background-color: rgba(0,0,0,0); }"
"QSlider { background-color: rgba(0,0,0,0); }");
// Keep the size of macro segments consistent, even if there is room in
// the edit areas
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QWidget::connect(_section, &Section::Collapsed, this,
&MacroSegmentEdit::Collapsed);
@@ -68,16 +81,6 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent) : QWidget(parent)
parent,
SIGNAL(SceneGroupRenamed(const QString &, const QString)), this,
SIGNAL(SceneGroupRenamed(const QString &, const QString)));
// Control signals
QWidget::connect(_controls, &MacroEntryControls::Add, this,
&MacroSegmentEdit::Add);
QWidget::connect(_controls, &MacroEntryControls::Remove, this,
&MacroSegmentEdit::Remove);
QWidget::connect(_controls, &MacroEntryControls::Up, this,
&MacroSegmentEdit::Up);
QWidget::connect(_controls, &MacroEntryControls::Down, this,
&MacroSegmentEdit::Down);
}
void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
@@ -86,35 +89,6 @@ void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
_headerInfo->setText(text);
}
void MacroSegmentEdit::Add()
{
if (Data()) {
// Insert after current entry
emit AddAt(Data()->GetIndex() + 1);
}
}
void MacroSegmentEdit::Remove()
{
if (Data()) {
emit RemoveAt(Data()->GetIndex());
}
}
void MacroSegmentEdit::Up()
{
if (Data()) {
emit UpAt(Data()->GetIndex());
}
}
void MacroSegmentEdit::Down()
{
if (Data()) {
emit DownAt(Data()->GetIndex());
}
}
void MacroSegmentEdit::Collapsed(bool collapsed)
{
if (Data()) {
@@ -122,14 +96,11 @@ void MacroSegmentEdit::Collapsed(bool collapsed)
}
}
void MacroSegmentEdit::enterEvent(QEvent *)
void MacroSegmentEdit::mousePressEvent(QMouseEvent *event)
{
_controls->Show(true);
}
void MacroSegmentEdit::leaveEvent(QEvent *)
{
_controls->Show(false);
if (event->button() == Qt::LeftButton && Data()) {
emit SelectionChagned(Data()->GetIndex());
}
}
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
@@ -150,3 +121,24 @@ void MacroSegmentEdit::SetCollapsed(bool collapsed)
{
_section->SetCollapsed(collapsed);
}
void MacroSegmentEdit::SetSelected(bool selected)
{
if (selected) {
_frame->setStyleSheet("#segmentFrame {"
"border-color: rgba(0, 0, 0, 255);"
"border-width: 2px;"
"border-style: dashed;"
"border-radius: 4px;"
"background-color: rgba(0,0,0,100);"
"}");
} else {
_frame->setStyleSheet("#segmentFrame {"
"border-color: rgba(0, 0, 0, 0);"
"border-width: 2px;"
"border-style: dashed;"
"border-radius: 4px;"
"background-color: rgba(0,0,0,50);"
"}");
}
}

View File

@@ -1,4 +1,5 @@
#include "headers/macro.hpp"
#include "headers/macro-list-entry-widget.hpp"
#include "headers/macro-action-edit.hpp"
#include "headers/macro-condition-edit.hpp"
#include "headers/advanced-scene-switcher.hpp"
@@ -7,7 +8,10 @@
#include <QColor>
#include <QMenu>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
static QTimer highlightMatchTimer;
static QMetaObject::Connection addPulse;
bool macroNameExists(std::string name)
@@ -15,15 +19,21 @@ bool macroNameExists(std::string name)
return !!GetMacroByName(name.c_str());
}
bool AdvSceneSwitcher::addNewMacro(std::string &name)
bool AdvSceneSwitcher::addNewMacro(std::string &name, std::string format)
{
QString format{
obs_module_text("AdvSceneSwitcher.macroTab.defaultname")};
QString fmt;
int i = 1;
QString placeHolderText = format.arg(i);
if (format.empty()) {
fmt = {obs_module_text(
"AdvSceneSwitcher.macroTab.defaultname")};
} else {
fmt = QString::fromStdString(format);
i = 2;
}
QString placeHolderText = fmt.arg(i);
while ((macroNameExists(placeHolderText.toUtf8().constData()))) {
placeHolderText = format.arg(++i);
placeHolderText = fmt.arg(++i);
}
bool accepted = AdvSSNameDialog::AskForName(
@@ -52,6 +62,17 @@ bool AdvSceneSwitcher::addNewMacro(std::string &name)
return true;
}
QListWidgetItem *AddNewMacroListEntry(QListWidget *list,
std::shared_ptr<Macro> &macro)
{
QListWidgetItem *item = new QListWidgetItem(list);
item->setData(Qt::UserRole, QString::fromStdString(macro->Name()));
auto listEntry = new MacroListEntryWidget(macro, list);
item->setSizeHint(listEntry->minimumSizeHint());
list->setItemWidget(item, listEntry);
return item;
}
void AdvSceneSwitcher::on_macroAdd_clicked()
{
std::string name;
@@ -60,10 +81,7 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
}
QString text = QString::fromStdString(name);
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked);
auto item = AddNewMacroListEntry(ui->macros, switcher->macros.back());
ui->macros->setCurrentItem(item);
ui->macroAdd->disconnect(addPulse);
@@ -102,34 +120,44 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
void AdvSceneSwitcher::on_macroUp_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
int index = ui->macros->currentRow();
if (index != -1 && index != 0) {
ui->macros->insertItem(index - 1, ui->macros->takeItem(index));
ui->macros->setCurrentRow(index - 1);
if (!listMoveUp(ui->macros)) {
return;
}
iter_swap(switcher->macros.begin() + index,
switcher->macros.begin() + index - 1);
int index = ui->macros->currentRow() + 1;
auto *entry1 = static_cast<MacroListEntryWidget *>(
ui->macros->itemWidget(ui->macros->item(index)));
auto *entry2 = static_cast<MacroListEntryWidget *>(
ui->macros->itemWidget(ui->macros->item(index - 1)));
entry1->SetMacro(*(switcher->macros.begin() + index - 1));
entry2->SetMacro(*(switcher->macros.begin() + index));
iter_swap(switcher->macros.begin() + index,
switcher->macros.begin() + index - 1);
for (auto &m : switcher->macros) {
m->ResolveMacroRef();
}
for (auto &m : switcher->macros) {
m->ResolveMacroRef();
}
}
void AdvSceneSwitcher::on_macroDown_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
int index = ui->macros->currentRow();
if (index != -1 && index != ui->macros->count() - 1) {
ui->macros->insertItem(index + 1, ui->macros->takeItem(index));
ui->macros->setCurrentRow(index + 1);
if (!listMoveDown(ui->macros)) {
return;
}
iter_swap(switcher->macros.begin() + index,
switcher->macros.begin() + index + 1);
int index = ui->macros->currentRow() - 1;
auto *entry1 = static_cast<MacroListEntryWidget *>(
ui->macros->itemWidget(ui->macros->item(index)));
auto *entry2 = static_cast<MacroListEntryWidget *>(
ui->macros->itemWidget(ui->macros->item(index + 1)));
entry1->SetMacro(*(switcher->macros.begin() + index + 1));
entry2->SetMacro(*(switcher->macros.begin() + index));
iter_swap(switcher->macros.begin() + index,
switcher->macros.begin() + index + 1);
for (auto &m : switcher->macros) {
m->ResolveMacroRef();
}
for (auto &m : switcher->macros) {
m->ResolveMacroRef();
}
}
@@ -160,11 +188,10 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
if (nameValid) {
macro->SetName(newName.toUtf8().constData());
QListWidgetItem *item = ui->macros->currentItem();
// Don't trigger itemChanged()
// pause state remains as is
ui->macros->blockSignals(true);
item->setText(newName);
ui->macros->blockSignals(false);
item->setData(Qt::UserRole, newName);
auto listEntry = static_cast<MacroListEntryWidget *>(
ui->macros->itemWidget(item));
listEntry->SetName(newName);
} else {
ui->macroName->setText(oldName);
}
@@ -180,7 +207,7 @@ void AdvSceneSwitcher::on_runMacro_clicked()
return;
}
bool ret = macro->PerformAction(true, true);
bool ret = macro->PerformActions(true, true);
if (!ret) {
QString err =
obs_module_text("AdvSceneSwitcher.macroTab.runFail");
@@ -215,10 +242,9 @@ void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx)
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
actions[afterIdx]->GetId());
ConnectControlSignals(newEntry);
ui->macroEditActionLayout->addWidget(newEntry);
ui->macroEditActionHelp->setVisible(false);
actionsList->ContentLayout()->addWidget(newEntry);
}
ui->macroEditActionHelp->setVisible(actions.size() == 0);
actionsList->SetHelpMsgVisible(actions.size() == 0);
}
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
@@ -230,11 +256,43 @@ void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
this, &conditions[afterIdx],
conditions[afterIdx]->GetId(), root);
ConnectControlSignals(newEntry);
ui->macroEditConditionLayout->addWidget(newEntry);
ui->macroEditConditionHelp->setVisible(false);
conditionsList->ContentLayout()->addWidget(newEntry);
root = false;
}
ui->macroEditConditionHelp->setVisible(conditions.size() == 0);
conditionsList->SetHelpMsgVisible(conditions.size() == 0);
}
void AdvSceneSwitcher::SetActionData(Macro &m)
{
auto &actions = m.Actions();
for (int idx = 0; idx < actionsList->ContentLayout()->count(); idx++) {
auto item = actionsList->ContentLayout()->itemAt(idx);
if (!item) {
continue;
}
auto widget = static_cast<MacroActionEdit *>(item->widget());
if (!widget) {
continue;
}
widget->SetEntryData(&*(actions.begin() + idx));
}
}
void AdvSceneSwitcher::SetConditionData(Macro &m)
{
auto &conditions = m.Conditions();
for (int idx = 0; idx < conditionsList->ContentLayout()->count();
idx++) {
auto item = conditionsList->ContentLayout()->itemAt(idx);
if (!item) {
continue;
}
auto widget = static_cast<MacroConditionEdit *>(item->widget());
if (!widget) {
continue;
}
widget->SetEntryData(&*(conditions.begin() + idx));
}
}
void AdvSceneSwitcher::SetEditMacro(Macro &m)
@@ -247,17 +305,21 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m)
ui->runMacroInParallel->setChecked(m.RunInParallel());
ui->runMacroOnChange->setChecked(m.MatchOnChange());
}
clearLayout(ui->macroEditConditionLayout);
clearLayout(ui->macroEditActionLayout);
clearLayout(conditionsList->ContentLayout());
clearLayout(actionsList->ContentLayout());
PopulateMacroConditions(m);
PopulateMacroActions(m);
ui->macroEdit->setDisabled(false);
currentActionIdx = -1;
currentConditionIdx = -1;
HighlightControls();
}
void AdvSceneSwitcher::HighlightAction(int idx)
{
auto item = ui->macroEditActionLayout->itemAt(idx);
auto item = actionsList->ContentLayout()->itemAt(idx);
if (!item) {
return;
}
@@ -265,13 +327,12 @@ void AdvSceneSwitcher::HighlightAction(int idx)
if (!widget) {
return;
}
PulseWidget(widget, QColor(Qt::green), QColor(0, 0, 0, 0), "QLabel ",
true);
PulseWidget(widget, QColor(Qt::green), QColor(0, 0, 0, 0), true);
}
void AdvSceneSwitcher::HighlightCondition(int idx)
{
auto item = ui->macroEditConditionLayout->itemAt(idx);
auto item = conditionsList->ContentLayout()->itemAt(idx);
if (!item) {
return;
}
@@ -279,32 +340,19 @@ void AdvSceneSwitcher::HighlightCondition(int idx)
if (!widget) {
return;
}
PulseWidget(widget, QColor(Qt::green), QColor(0, 0, 0, 0), "QLabel ",
true);
PulseWidget(widget, QColor(Qt::green), QColor(0, 0, 0, 0), true);
}
void AdvSceneSwitcher::ConnectControlSignals(MacroActionEdit *c)
void AdvSceneSwitcher::ConnectControlSignals(MacroActionEdit *a)
{
connect(c, &MacroActionEdit::AddAt, this,
&AdvSceneSwitcher::AddMacroAction);
connect(c, &MacroActionEdit::RemoveAt, this,
&AdvSceneSwitcher::RemoveMacroAction);
connect(c, &MacroActionEdit::UpAt, this,
&AdvSceneSwitcher::MoveMacroActionUp);
connect(c, &MacroActionEdit::DownAt, this,
&AdvSceneSwitcher::MoveMacroActionDown);
connect(a, &MacroActionEdit::SelectionChagned, this,
&AdvSceneSwitcher::MacroActionSelectionChanged);
}
void AdvSceneSwitcher::ConnectControlSignals(MacroConditionEdit *c)
{
connect(c, &MacroConditionEdit::AddAt, this,
&AdvSceneSwitcher::AddMacroCondition);
connect(c, &MacroConditionEdit::RemoveAt, this,
&AdvSceneSwitcher::RemoveMacroCondition);
connect(c, &MacroConditionEdit::UpAt, this,
&AdvSceneSwitcher::MoveMacroConditionUp);
connect(c, &MacroConditionEdit::DownAt, this,
&AdvSceneSwitcher::MoveMacroConditionDown);
connect(c, &MacroActionEdit::SelectionChagned, this,
&AdvSceneSwitcher::MacroConditionSelectionChanged);
}
Macro *AdvSceneSwitcher::getSelectedMacro()
@@ -315,7 +363,7 @@ Macro *AdvSceneSwitcher::getSelectedMacro()
return nullptr;
}
QString name = item->text();
QString name = item->data(Qt::UserRole).toString();
return GetMacroByQString(name);
}
@@ -331,7 +379,7 @@ void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
}
QListWidgetItem *item = ui->macros->item(idx);
QString macroName = item->text();
QString macroName = item->data(Qt::UserRole).toString();
auto macro = GetMacroByQString(macroName);
if (macro) {
@@ -339,34 +387,31 @@ void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
}
}
void AdvSceneSwitcher::on_macros_itemChanged(QListWidgetItem *item)
void AdvSceneSwitcher::MacroDragDropReorder(QModelIndex, int from, int,
QModelIndex, int to)
{
if (loading) {
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (from > to) {
std::rotate(switcher->macros.rend() - from - 1,
switcher->macros.rend() - from,
switcher->macros.rend() - to);
} else {
std::rotate(switcher->macros.begin() + from,
switcher->macros.begin() + from + 1,
switcher->macros.begin() + to);
}
std::lock_guard<std::mutex> lock(switcher->m);
QString name = item->text();
auto m = GetMacroByQString(name);
if (m) {
m->SetPaused(item->checkState() != Qt::Checked);
for (auto &m : switcher->macros) {
m->ResolveMacroRef();
}
}
void AdvSceneSwitcher::setupMacroTab()
{
const QSignalBlocker signalBlocker(ui->macros);
ui->macros->clear();
for (auto &m : switcher->macros) {
QString text = QString::fromStdString(m->Name());
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
if (m->Paused()) {
item->setCheckState(Qt::Unchecked);
} else {
item->setCheckState(Qt::Checked);
}
AddNewMacroListEntry(ui->macros, m);
}
if (switcher->macros.size() == 0) {
@@ -378,20 +423,47 @@ void AdvSceneSwitcher::setupMacroTab()
ui->macroHelp->setVisible(false);
}
connect(ui->macros->model(),
SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)),
this,
SLOT(MacroDragDropReorder(QModelIndex, int, int, QModelIndex,
int)));
delete conditionsList;
conditionsList = new MacroSegmentList(this);
conditionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editConditionHelp"));
connect(conditionsList, &MacroSegmentList::SelectionChagned, this,
&AdvSceneSwitcher::MacroConditionSelectionChanged);
ui->macroConditionsLayout->insertWidget(0, conditionsList);
delete actionsList;
actionsList = new MacroSegmentList(this);
actionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editActionHelp"));
connect(actionsList, &MacroSegmentList::SelectionChagned, this,
&AdvSceneSwitcher::MacroActionSelectionChanged);
ui->macroActionsLayout->insertWidget(0, actionsList);
ui->macros->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->macros, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroContextMenu);
ui->macroActions->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->macroActions, &QWidget::customContextMenuRequested, this,
actionsList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(actionsList, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroActionsContextMenu);
ui->macroConditions->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->macroConditions, &QWidget::customContextMenuRequested, this,
conditionsList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(conditionsList, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroConditionsContextMenu);
ui->macroEdit->setDisabled(true);
ui->macroPriorityWarning->setVisible(
switcher->functionNamesByPriority[0] != macro_func);
highlightMatchTimer.setInterval(1000);
connect(&highlightMatchTimer, &QTimer::timeout, this,
&AdvSceneSwitcher::HighlightMatchedMacros);
highlightMatchTimer.start();
}
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
@@ -405,48 +477,56 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
{
QPoint globalPos = ui->macroActions->mapToGlobal(pos);
QPoint globalPos = actionsList->mapToGlobal(pos);
QMenu myMenu;
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
this, &AdvSceneSwitcher::ExpandAllActions);
myMenu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), this,
&AdvSceneSwitcher::CollapseAllActions);
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
this, &AdvSceneSwitcher::MinimizeConditions);
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
this, &AdvSceneSwitcher::MinimizeActions);
myMenu.exec(globalPos);
}
void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos)
{
QPoint globalPos = ui->macroConditions->mapToGlobal(pos);
QPoint globalPos = conditionsList->mapToGlobal(pos);
QMenu myMenu;
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
this, &AdvSceneSwitcher::ExpandAllConditions);
myMenu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), this,
&AdvSceneSwitcher::CollapseAllConditions);
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
this, &AdvSceneSwitcher::MinimizeActions);
myMenu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
this, &AdvSceneSwitcher::MinimizeConditions);
myMenu.exec(globalPos);
}
void AdvSceneSwitcher::CopyMacro()
{
obs_data_t *data = obs_data_create();
getSelectedMacro()->Save(data);
std::string name;
if (!addNewMacro(name)) {
obs_data_release(data);
const auto macro = getSelectedMacro();
if (!macro) {
return;
}
std::string format = macro->Name() + " %1";
std::string name;
if (!addNewMacro(name, format)) {
return;
}
obs_data_t *data = obs_data_create();
macro->Save(data);
switcher->macros.back()->Load(data);
switcher->macros.back()->SetName(name);
obs_data_release(data);
QString text = QString::fromStdString(name);
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
item->setData(Qt::UserRole, text);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Checked);
auto item = AddNewMacroListEntry(ui->macros, switcher->macros.back());
ui->macros->setCurrentItem(item);
}
@@ -468,7 +548,7 @@ void AdvSceneSwitcher::ExpandAllActions()
if (!m) {
return;
}
setCollapsedStateOfSegmentsIn(ui->macroEditActionLayout, false);
setCollapsedStateOfSegmentsIn(actionsList->ContentLayout(), false);
}
void AdvSceneSwitcher::ExpandAllConditions()
@@ -477,7 +557,7 @@ void AdvSceneSwitcher::ExpandAllConditions()
if (!m) {
return;
}
setCollapsedStateOfSegmentsIn(ui->macroEditConditionLayout, false);
setCollapsedStateOfSegmentsIn(conditionsList->ContentLayout(), false);
}
void AdvSceneSwitcher::CollapseAllActions()
@@ -486,7 +566,7 @@ void AdvSceneSwitcher::CollapseAllActions()
if (!m) {
return;
}
setCollapsedStateOfSegmentsIn(ui->macroEditActionLayout, true);
setCollapsedStateOfSegmentsIn(actionsList->ContentLayout(), true);
}
void AdvSceneSwitcher::CollapseAllConditions()
@@ -495,5 +575,139 @@ void AdvSceneSwitcher::CollapseAllConditions()
if (!m) {
return;
}
setCollapsedStateOfSegmentsIn(ui->macroEditConditionLayout, true);
setCollapsedStateOfSegmentsIn(conditionsList->ContentLayout(), true);
}
void AdvSceneSwitcher::MinimizeActions()
{
QList<int> sizes = ui->macroSplitter->sizes();
int sum = sizes[0] + sizes[1];
int actionsHeight = sum / 10;
sizes[1] = actionsHeight;
sizes[0] = sum - actionsHeight;
ui->macroSplitter->setSizes(sizes);
}
void AdvSceneSwitcher::MinimizeConditions()
{
QList<int> sizes = ui->macroSplitter->sizes();
int sum = sizes[0] + sizes[1];
int conditionsHeight = sum / 10;
sizes[0] = conditionsHeight;
sizes[1] = sum - conditionsHeight;
ui->macroSplitter->setSizes(sizes);
}
void AdvSceneSwitcher::HighlightMatchedMacros()
{
if (loading || !(switcher && switcher->highlightExecutedMacros)) {
return;
}
for (int idx = 0; idx < (int)switcher->macros.size(); idx++) {
if (switcher->macros[idx]->WasExecutedRecently()) {
auto item = ui->macros->item(idx);
if (!item) {
continue;
}
auto widget = ui->macros->itemWidget(item);
if (!widget) {
continue;
}
PulseWidget(widget, Qt::green, QColor(0, 0, 0, 0),
true);
}
}
}
void AdvSceneSwitcher::SetSelection(MacroSegmentList *list, int idx)
{
for (int i = 0; i < list->ContentLayout()->count(); ++i) {
auto widget = static_cast<MacroSegmentEdit *>(
list->ContentLayout()->itemAt(i)->widget());
if (widget) {
widget->SetSelected(i == idx);
}
}
}
void AdvSceneSwitcher::DeleteMacroSegementHotkey()
{
if (currentActionIdx != -1) {
RemoveMacroAction(currentActionIdx);
} else if (currentConditionIdx != -1) {
RemoveMacroCondition(currentConditionIdx);
}
}
void fade(QWidget *widget, bool fadeOut)
{
auto curEffect = widget->graphicsEffect();
if (curEffect) {
auto curOpacity =
dynamic_cast<QGraphicsOpacityEffect *>(curEffect);
if (curOpacity && ((fadeOut && curOpacity->opacity() == 0.3) ||
(!fadeOut && curOpacity->opacity() == 1))) {
return;
}
} else if (!fadeOut) {
return;
}
QGraphicsOpacityEffect *opacityEffect = new QGraphicsOpacityEffect();
widget->setGraphicsEffect(opacityEffect);
QPropertyAnimation *animation =
new QPropertyAnimation(opacityEffect, "opacity");
animation->setDuration(350);
animation->setStartValue(fadeOut ? 1 : 0.3);
animation->setEndValue(fadeOut ? .3 : 1);
animation->setEasingCurve(QEasingCurve::OutQuint);
animation->start(QPropertyAnimation::DeleteWhenStopped);
}
void AdvSceneSwitcher::FadeOutActionControls()
{
fade(ui->actionAdd, true);
fade(ui->actionRemove, true);
fade(ui->actionUp, true);
fade(ui->actionDown, true);
}
void AdvSceneSwitcher::FadeOutConditionControls()
{
fade(ui->conditionAdd, true);
fade(ui->conditionRemove, true);
fade(ui->conditionUp, true);
fade(ui->conditionDown, true);
}
void AdvSceneSwitcher::ResetOpacityActionControls()
{
fade(ui->actionAdd, false);
fade(ui->actionRemove, false);
fade(ui->actionUp, false);
fade(ui->actionDown, false);
}
void AdvSceneSwitcher::ResetOpacityConditionControls()
{
fade(ui->conditionAdd, false);
fade(ui->conditionRemove, false);
fade(ui->conditionUp, false);
fade(ui->conditionDown, false);
}
void AdvSceneSwitcher::HighlightControls()
{
if ((currentActionIdx == -1 && currentConditionIdx == -1) ||
(currentActionIdx != -1 && currentConditionIdx != -1)) {
ResetOpacityActionControls();
ResetOpacityConditionControls();
} else if (currentActionIdx != -1) {
FadeOutConditionControls();
ResetOpacityActionControls();
} else {
FadeOutActionControls();
ResetOpacityConditionControls();
}
}

View File

@@ -115,7 +115,7 @@ bool Macro::CeckMatch()
return _matched;
}
bool Macro::PerformAction(bool forceParallel, bool ignorePause)
bool Macro::PerformActions(bool forceParallel, bool ignorePause)
{
if (!_done) {
vblog(LOG_INFO, "macro %s already running", _name.c_str());
@@ -133,6 +133,7 @@ bool Macro::PerformAction(bool forceParallel, bool ignorePause)
} else {
RunActions(ret, ignorePause);
}
_wasExecutedRecently = true;
return ret;
}
@@ -382,6 +383,15 @@ bool Macro::SwitchesScene()
return false;
}
bool Macro::WasExecutedRecently()
{
if (_wasExecutedRecently) {
_wasExecutedRecently = false;
return true;
}
return false;
}
static void pauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
{
if (pressed) {
@@ -582,7 +592,7 @@ bool SwitcherData::runMacros()
for (auto m : macros) {
if (m->Matched()) {
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
if (!m->PerformAction()) {
if (!m->PerformActions()) {
blog(LOG_WARNING, "abort macro: %s",
m->Name().c_str());
}

View File

@@ -0,0 +1,31 @@
#include "headers/resizing-text-edit.hpp"
ResizingPlainTextEdit::ResizingPlainTextEdit(QWidget *parent,
const int scrollAt,
const int minLines,
const int paddingLines)
: QPlainTextEdit(parent),
_scrollAt(scrollAt),
_minLines(minLines),
_paddingLines(paddingLines)
{
QWidget::connect(this, SIGNAL(textChanged()), this,
SLOT(ResizeTexteditArea()));
}
void ResizingPlainTextEdit::ResizeTexteditArea()
{
QFontMetrics f(font());
int rowHeight = f.lineSpacing();
int numLines = document()->blockCount();
if (numLines + _paddingLines < _minLines) {
setFixedHeight(_minLines * rowHeight);
} else if (numLines + _paddingLines < _scrollAt) {
setFixedHeight((numLines + _paddingLines) * rowHeight);
} else {
setFixedHeight(_scrollAt * rowHeight);
}
adjustSize();
updateGeometry();
}

View File

@@ -89,7 +89,7 @@ SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
// As this might be confusing reverse that order internally
int idx = count - 1 - _idx;
if (idx >= 0 && idx < items.size()) {
if (idx >= 0 && idx < (int)items.size()) {
obs_sceneitem_addref(items[idx]);
ret.emplace_back(items[idx]);
}

View File

@@ -3,23 +3,26 @@
static void ScreenshotTick(void *param, float);
AdvSSScreenshotObj::AdvSSScreenshotObj(obs_source_t *source)
ScreenshotHelper::ScreenshotHelper(obs_source_t *source)
: weakSource(OBSGetWeakRef(source))
{
_initDone = true;
obs_add_tick_callback(ScreenshotTick, this);
}
AdvSSScreenshotObj::~AdvSSScreenshotObj()
ScreenshotHelper::~ScreenshotHelper()
{
obs_enter_graphics();
gs_stagesurface_destroy(stagesurf);
gs_texrender_destroy(texrender);
obs_leave_graphics();
if (_initDone) {
obs_enter_graphics();
gs_stagesurface_destroy(stagesurf);
gs_texrender_destroy(texrender);
obs_leave_graphics();
obs_remove_tick_callback(ScreenshotTick, this);
obs_remove_tick_callback(ScreenshotTick, this);
}
}
void AdvSSScreenshotObj::Screenshot()
void ScreenshotHelper::Screenshot()
{
OBSSource source = OBSGetStrongRef(weakSource);
@@ -69,12 +72,12 @@ void AdvSSScreenshotObj::Screenshot()
}
}
void AdvSSScreenshotObj::Download()
void ScreenshotHelper::Download()
{
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
}
void AdvSSScreenshotObj::Copy()
void ScreenshotHelper::Copy()
{
uint8_t *videoData = nullptr;
uint32_t videoLinesize = 0;
@@ -91,7 +94,7 @@ void AdvSSScreenshotObj::Copy()
}
}
void AdvSSScreenshotObj::MarkDone()
void ScreenshotHelper::MarkDone()
{
time = std::chrono::high_resolution_clock::now();
done = true;
@@ -104,8 +107,7 @@ void AdvSSScreenshotObj::MarkDone()
static void ScreenshotTick(void *param, float)
{
AdvSSScreenshotObj *data =
reinterpret_cast<AdvSSScreenshotObj *>(param);
ScreenshotHelper *data = reinterpret_cast<ScreenshotHelper *>(param);
if (data->stage == STAGE_FINISH) {
return;

View File

@@ -12,7 +12,8 @@ Section::Section(const int animationDuration, QWidget *parent)
_mainLayout = new QGridLayout(this);
_headerWidgetLayout = new QHBoxLayout();
_toggleButton->setStyleSheet("QToolButton {border: none;}");
_toggleButton->setStyleSheet(
"QToolButton {border: none; background-color: rgba(0,0,0,0);}");
_toggleButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
_toggleButton->setArrowType(Qt::ArrowType::RightArrow);
_toggleButton->setCheckable(true);
@@ -31,10 +32,8 @@ Section::Section(const int animationDuration, QWidget *parent)
_headerWidgetLayout->setSpacing(11);
_headerWidgetLayout->addWidget(_toggleButton);
int row = 0;
_mainLayout->addLayout(_headerWidgetLayout, row, 0, 1, 1,
Qt::AlignLeft);
_mainLayout->addWidget(_headerLine, row++, 2, 1, 1);
_mainLayout->addLayout(_headerWidgetLayout, 0, 0, 1, 1, Qt::AlignLeft);
_mainLayout->addWidget(_headerLine, 0, 2, 1, 1);
setLayout(_mainLayout);
connect(_toggleButton, &QToolButton::toggled, this, &Section::Collapse);
@@ -43,17 +42,21 @@ Section::Section(const int animationDuration, QWidget *parent)
void Section::Collapse(bool collapse)
{
_toggleButton->setChecked(collapse);
_toggleButton->setArrowType(!collapse ? Qt::ArrowType::DownArrow
: Qt::ArrowType::RightArrow);
_toggleAnimation->setDirection(!collapse
? QAbstractAnimation::Forward
: QAbstractAnimation::Backward);
_toggleButton->setArrowType(collapse ? Qt::ArrowType::RightArrow
: Qt::ArrowType::DownArrow);
_toggleAnimation->setDirection(collapse ? QAbstractAnimation::Backward
: QAbstractAnimation::Forward);
_transitioning = true;
_collapsed = collapse;
_toggleAnimation->start();
emit Collapsed(collapse);
}
void Section::SetContent(QWidget *w)
{
SetContent(w, _collapsed);
}
void Section::SetContent(QWidget *w, bool collapsed)
{
CleanUpPreviousContent();
@@ -61,13 +64,15 @@ void Section::SetContent(QWidget *w, bool collapsed)
// Setup contentArea
_contentArea = new QScrollArea(this);
_contentArea->setObjectName("macroSegmentContent");
_contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
_contentArea->setStyleSheet("QScrollArea { border: none; }");
_contentArea->setStyleSheet(
"#macroSegmentContent { border: none; background-color: rgba(0,0,0,0); }");
_contentArea->setMaximumHeight(0);
_contentArea->setMinimumHeight(0);
w->installEventFilter(this);
_content = w;
_content->installEventFilter(this);
auto newLayout = new QVBoxLayout();
newLayout->setContentsMargins(0, 0, 0, 0);
newLayout->addWidget(w);
@@ -75,7 +80,9 @@ void Section::SetContent(QWidget *w, bool collapsed)
_mainLayout->addWidget(_contentArea, 1, 0, 1, 3);
_headerHeight = sizeHint().height() - _contentArea->maximumHeight();
_contentHeight = newLayout->sizeHint().height();
_contentHeight = _content->sizeHint().height();
SetupAnimations();
if (collapsed) {
this->setMinimumHeight(_headerHeight);
@@ -84,8 +91,11 @@ void Section::SetContent(QWidget *w, bool collapsed)
this->setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
}
SetupAnimations();
Collapse(collapsed);
const QSignalBlocker b(_toggleButton);
_toggleButton->setChecked(collapsed);
_toggleButton->setArrowType(collapsed ? Qt::ArrowType::RightArrow
: Qt::ArrowType::DownArrow);
_collapsed = collapsed;
}
void Section::AddHeaderWidget(QWidget *w)
@@ -95,17 +105,24 @@ void Section::AddHeaderWidget(QWidget *w)
void Section::SetCollapsed(bool collapsed)
{
if (_collapsed == collapsed) {
return;
}
Collapse(collapsed);
}
bool Section::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Resize && !_transitioning && !_collapsed) {
_contentHeight = _content->sizeHint().height();
setMaximumHeight(_headerHeight + _contentHeight);
setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
SetupAnimations();
if (_contentHeight != _content->sizeHint().height()) {
_contentHeight = _content->sizeHint().height();
setMaximumHeight(_headerHeight + _contentHeight);
setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
// Note: Calling this too frequently inside this event
// filter will cause a segfault for some reason
SetupAnimations();
}
}
return QObject::eventFilter(obj, event);
}

View File

@@ -13,6 +13,12 @@ StatusControl::StatusControl(QWidget *parent, bool noLayout) : QWidget(parent)
{
_button = new QPushButton("-", this);
_status = new QLabel("-", this);
_status->setStyleSheet("QLabel{ \
border-style: outset; \
border-width: 2px; \
border-radius: 7px; \
border-color: rgb(0,0,0,0) \
}");
_statusPrefix = new QLabel(
obs_module_text(
"AdvSceneSwitcher.generalTab.status.currentStatus"),
@@ -87,7 +93,7 @@ void StatusControl::SetStopped()
_status->setText(obs_module_text("AdvSceneSwitcher.status.inactive"));
if (!switcher->disableHints) {
_pulse = PulseWidget(_status, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
QColor(0, 0, 0, 0));
}
_setToStopped = true;
}

View File

@@ -134,10 +134,16 @@ void AdvSceneSwitcher::setupRandomTab()
ui->randomHelp->setVisible(false);
}
ui->randomDisabledWarning->setStyleSheet("QLabel{ \
border-style: outset; \
border-width: 2px; \
border-radius: 7px; \
border-color: rgb(0,0,0,0) \
}");
if (switcher->switchIfNotMatching != RANDOM_SWITCH) {
if (!switcher->disableHints) {
PulseWidget(ui->randomDisabledWarning, QColor(Qt::red),
QColor(0, 0, 0, 0), "QLabel ");
QColor(0, 0, 0, 0));
}
} else {
ui->randomDisabledWarning->setVisible(false);

View File

@@ -98,7 +98,7 @@ void AdvSceneSwitcher::on_getScreenshot_clicked()
}
auto source = obs_weak_source_get_source(s->videoSource);
auto screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
auto screenshotData = std::make_unique<ScreenshotHelper>(source);
obs_source_release(source);
QString filePath = QFileDialog::getSaveFileName(this);
@@ -254,7 +254,7 @@ void VideoSwitch::load(obs_data_t *obj)
void VideoSwitch::getScreenshot()
{
auto source = obs_weak_source_get_source(videoSource);
screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
screenshotData = std::make_unique<ScreenshotHelper>(source);
obs_source_release(source);
}

View File

@@ -881,37 +881,42 @@ bool windowPosValid(QPoint pos)
}
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
QColor endColor, QString specifier,
bool once)
QColor endColor, bool once)
{
widget->setStyleSheet(specifier + "{ \
border-style: outset; \
border-width: 2px; \
border-radius: 10px; \
border-color: rgb(0,0,0,0) \
}");
QGraphicsColorizeEffect *eEffect = new QGraphicsColorizeEffect(widget);
widget->setGraphicsEffect(eEffect);
QPropertyAnimation *paAnimation =
new QPropertyAnimation(eEffect, "color");
paAnimation->setStartValue(startColor);
paAnimation->setEndValue(endColor);
paAnimation->setDuration(1000);
QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect(widget);
widget->setGraphicsEffect(effect);
QPropertyAnimation *animation =
new QPropertyAnimation(effect, "color", widget);
animation->setStartValue(startColor);
animation->setEndValue(endColor);
animation->setDuration(1000);
QMetaObject::Connection con;
if (!once) {
if (once) {
auto widgetPtr = widget;
con = QWidget::connect(
paAnimation, &QPropertyAnimation::finished,
[paAnimation]() {
QTimer::singleShot(1000, [paAnimation] {
paAnimation->start();
});
animation, &QPropertyAnimation::finished,
[widgetPtr]() {
if (widgetPtr) {
widgetPtr->setGraphicsEffect(nullptr);
}
});
animation->start(QPropertyAnimation::DeleteWhenStopped);
} else {
auto widgetPtr = widget;
con = QWidget::connect(
animation, &QPropertyAnimation::finished,
[animation, widgetPtr]() {
QTimer *timer = new QTimer(widgetPtr);
QWidget::connect(timer, &QTimer::timeout,
[animation] {
animation->start();
});
timer->setSingleShot(true);
timer->start(1000);
});
animation->start();
}
paAnimation->start();
return con;
}