Compare commits

..

25 Commits

Author SHA1 Message Date
WarmUpTill
4d2c3e0f80 Copy settings of selected transition for show / hide transition 2022-06-13 11:40:12 -07:00
WarmUpTill
1665841589 Fix freeze on setting hide/show transition
A "private" copy of the transition source has to be used for show and
hide transitions as otherwise this can lead to an OBS freeze the next
time the show / hide transition is used
2022-06-13 11:40:12 -07:00
WarmUpTill
e89343b03e Fix crash when deleting last macro
A crash could occur if the last macro was deleted and the highlighting
of macro segments was enabled due to the macro segments already being
deleted while the widgets representing these segments were not
2022-06-12 06:45:11 -07:00
WarmUpTill
a9c79b02ff Fix UI elements shifting slightly
Content margins of scene item selection widget would cause Transition
action to shift slightly whenever the widget's visibility changed
2022-06-12 04:50:13 -07:00
WarmUpTill
c6541825f8 Add option to select before and after for time of simple date check 2022-06-12 03:46:18 -07:00
WarmUpTill
003a1aa696 Remove delay before saving replay buffer 2022-06-12 03:46:04 -07:00
WarmUpTill
20767630c0 Add option to set show, hide, and override transitions (#471) 2022-06-12 12:45:45 +02:00
WarmUpTill
70fb972f84 Add option to ignore time component in simple check 2022-05-18 04:55:55 -07:00
WarmUpTill
4d65956f25 Fix MacOS memory leak 2022-05-18 04:55:28 -07:00
WarmUpTill
a2e4580970 Fix media and audio condition unexpectedly returning true after unpause 2022-05-12 11:42:39 -07:00
WarmUpTill
8da1e3ad40 Rework time restriction to support "within" duration modifier
Also renamed duration constraint to duration modifier
2022-05-12 11:42:39 -07:00
WarmUpTill
92b6a057f6 Restructure files
- Move condition and action related parts into separate files
 - Move duration constraint to macro condition
 - Move duration constraint widget to macro condition edit
 - Adjust includes
2022-05-12 11:42:39 -07:00
WarmUpTill
70a3ae0245 Fix waiting for transition to complete with fixed duration transitions 2022-05-12 11:42:39 -07:00
WarmUpTill
8cc7b65436 Do not allow duplicates in condtion and action type selection 2022-05-12 11:42:39 -07:00
WarmUpTill
fd18e64e19 Add studio mode action (previously scene swap and preview scene action)
The option to enable and disable studio mode is new
2022-05-12 11:42:39 -07:00
WarmUpTill
e4526c4e81 Enable option to check previous scene during active transition 2022-05-07 16:55:36 -07:00
WarmUpTill
d6ef69a3d4 Add spinbox control to threshold slider 2022-05-06 11:37:43 -07:00
WarmUpTill
b5eb0b3f3e Add option to disable registering of pause control hotkeys 2022-05-06 11:37:28 -07:00
WarmUpTill
22a361f977 Fix scene group not being random 2022-05-04 06:20:10 -07:00
WarmUpTill
a0db023aae cleanup es-ES.ini 2022-05-04 02:28:10 -07:00
EliasDIPA
dc8a60a5e5 Traduccion al Español añadida 2022-05-04 02:18:32 -07:00
WarmUpTill
d47b878b56 Display next match time if repeat is selected 2022-04-24 09:45:22 -07:00
WarmUpTill
9105087036 Show update on repeat option only if repeat is selected 2022-04-24 09:45:22 -07:00
WarmUpTill
e26f41a1a8 Add option to preserve original date in combination with repeat option 2022-04-24 09:45:22 -07:00
WarmUpTill
ff513d8664 Fix cursor condition frame blocking mouse inputs 2022-04-24 06:28:28 -07:00
52 changed files with 2270 additions and 922 deletions

View File

@@ -107,6 +107,7 @@ else()
include_directories("${LIBCURL_INCLUDE_DIRS}")
add_definitions(-DVCAM_SUPPORTED)
add_definitions(-DREPLAYBUFFER_SUPPORTED)
add_definitions(-DVISIBILITY_TRANSITIONS_SUPPORTED)
endif()
# Platform specific settings
@@ -190,6 +191,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/switch-sequence.hpp
src/headers/switch-video.hpp
src/headers/switch-generic.hpp
src/headers/macro-action.hpp
src/headers/macro-action-edit.hpp
src/headers/macro-action-audio.hpp
src/headers/macro-action-file.hpp
@@ -198,7 +200,6 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-action-macro.hpp
src/headers/macro-action-media.hpp
src/headers/macro-action-plugin-state.hpp
src/headers/macro-action-preview-scene.hpp
src/headers/macro-action-profile.hpp
src/headers/macro-action-random.hpp
src/headers/macro-action-recording.hpp
@@ -206,7 +207,6 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-action-run.hpp
src/headers/macro-action-scene-collection.hpp
src/headers/macro-action-scene-order.hpp
src/headers/macro-action-scene-swap.hpp
src/headers/macro-action-scene-switch.hpp
src/headers/macro-action-scene-transform.hpp
src/headers/macro-action-scene-visibility.hpp
@@ -214,11 +214,13 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-action-sequence.hpp
src/headers/macro-action-source.hpp
src/headers/macro-action-streaming.hpp
src/headers/macro-action-studio-mode.hpp
src/headers/macro-action-systray.hpp
src/headers/macro-action-timer.hpp
src/headers/macro-action-transition.hpp
src/headers/macro-action-virtual-cam.hpp
src/headers/macro-action-wait.hpp
src/headers/macro-condition.hpp
src/headers/macro-condition-edit.hpp
src/headers/macro-condition-audio.hpp
src/headers/macro-condition-cursor.hpp
@@ -246,6 +248,7 @@ 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-ref.hpp
src/headers/macro-list-entry-widget.hpp
src/headers/macro-properties.hpp
src/headers/macro-segment.hpp
@@ -294,6 +297,7 @@ set(advanced-scene-switcher_SOURCES
src/switch-audio.cpp
src/switch-video.cpp
src/switch-generic.cpp
src/macro-action.cpp
src/macro-action-edit.cpp
src/macro-action-audio.cpp
src/macro-action-file.cpp
@@ -302,7 +306,6 @@ set(advanced-scene-switcher_SOURCES
src/macro-action-macro.cpp
src/macro-action-media.cpp
src/macro-action-plugin-state.cpp
src/macro-action-preview-scene.cpp
src/macro-action-profile.cpp
src/macro-action-random.cpp
src/macro-action-recording.cpp
@@ -310,7 +313,6 @@ set(advanced-scene-switcher_SOURCES
src/macro-action-run.cpp
src/macro-action-scene-collection.cpp
src/macro-action-scene-order.cpp
src/macro-action-scene-swap.cpp
src/macro-action-scene-switch.cpp
src/macro-action-scene-transform.cpp
src/macro-action-scene-visibility.cpp
@@ -318,11 +320,13 @@ set(advanced-scene-switcher_SOURCES
src/macro-action-sequence.cpp
src/macro-action-source.cpp
src/macro-action-streaming.cpp
src/macro-action-studio-mode.cpp
src/macro-action-systray.cpp
src/macro-action-timer.cpp
src/macro-action-transition.cpp
src/macro-action-virtual-cam.cpp
src/macro-action-wait.cpp
src/macro-condition.cpp
src/macro-condition-edit.cpp
src/macro-condition-audio.cpp
src/macro-condition-cursor.cpp
@@ -350,6 +354,7 @@ set(advanced-scene-switcher_SOURCES
src/macro-condition-virtual-cam.cpp
src/macro-condition-window.cpp
src/macro.cpp
src/macro-ref.cpp
src/macro-list-entry-widget.cpp
src/macro-properties.cpp
src/macro-segment.cpp
@@ -374,10 +379,11 @@ set(advanced-scene-switcher_SOURCES
if(DEFINED LibObs_VERSION_MAJOR)
if(LibObs_VERSION_MAJOR GREATER_EQUAL 27)
add_definitions(-DVCAM_SUPPORTED)
add_definitions(-DVISIBILITY_TRANSITIONS_SUPPORTED)
else()
message(
WARNING
"OBS version ${LibObs_VERSION_MAJOR} found - disabling virtual camera functionality"
"OBS version ${LibObs_VERSION_MAJOR} found - disabling virtual camera and hide/show transition functionality"
)
endif()
if(LibObs_VERSION_MAJOR GREATER_EQUAL 26)

View File

@@ -82,6 +82,7 @@ AdvSceneSwitcher.macroTab.minimize="Minimize"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
AdvSceneSwitcher.macroTab.disableHotkeys="Register hotkeys to control pause state of selected macro"
; Macro Logic
AdvSceneSwitcher.logic.none="Ignore entry"
@@ -115,6 +116,7 @@ AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
AdvSceneSwitcher.condition.scene.type.changed="Scene changed"
AdvSceneSwitcher.condition.scene.type.notChanged="Scene has not changed"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="During transition check for transition target scene"
AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="During transition check for transition source scene"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Window"
@@ -127,7 +129,7 @@ AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDat
AdvSceneSwitcher.condition.media="Media"
AdvSceneSwitcher.condition.media.anyOnScene="Any media source on"
AdvSceneSwitcher.condition.media.allOnScene="All media sources on"
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use time constraints instead)"
AdvSceneSwitcher.condition.media.matchOnChange="Only match on change (Note: This option will be removed in a future version - please use duration modifiers instead)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Unfortunately not all media source types behave the same (e.g. Media Source vs. VLC Video Source \"Stopped\" state).\nSo please experiment what works for your setup!"
AdvSceneSwitcher.condition.media.entry="{{mediaSources}}{{scenes}} state is {{states}} and {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Video"
@@ -267,9 +269,11 @@ AdvSceneSwitcher.condition.date.ignoreDate="If unchecked the date component will
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.simple="On {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{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.date.entry.nextMatchDate="Next match at: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} On repeat update selected date to repeat date"
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
@@ -348,6 +352,7 @@ AdvSceneSwitcher.action.recording.type.unpause="Unpause recording"
AdvSceneSwitcher.action.recording.pause.hint="Note that depending on your recording settings you might not be able to pause recording"
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
AdvSceneSwitcher.action.replay="Replay buffer"
AdvSceneSwitcher.action.replay.saveWarn="Warning: Saving too frequently might result in the replay buffer not actually being saved!"
AdvSceneSwitcher.action.replay.type.stop="Stop replay buffer"
AdvSceneSwitcher.action.replay.type.start="Start replay buffer"
AdvSceneSwitcher.action.replay.type.save="Save replay buffer"
@@ -432,13 +437,20 @@ AdvSceneSwitcher.action.file="File"
AdvSceneSwitcher.action.file.type.write="Write"
AdvSceneSwitcher.action.file.type.append="Append"
AdvSceneSwitcher.action.file.entry="{{actions}} to {{filePath}}:"
AdvSceneSwitcher.action.previewScene="Switch preview scene"
AdvSceneSwitcher.action.previewScene.entry="Switch preview scene to {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="Swap scene (Studio mode)"
AdvSceneSwitcher.action.SceneSwap.entry="Swap preview and program scene in studio mode"
AdvSceneSwitcher.action.studioMode="Studio mode"
AdvSceneSwitcher.action.studioMode.type.swap="Swap preview and program scene"
AdvSceneSwitcher.action.studioMode.type.setScene="Set preview scene to"
AdvSceneSwitcher.action.studioMode.type.enable="Enable studio mode"
AdvSceneSwitcher.action.studioMode.type.disable="Disable studio mode"
AdvSceneSwitcher.action.studioMode.entry="{{actions}}{{scenes}}"
AdvSceneSwitcher.action.transition="Transition"
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}Set transition type to {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}Set transition duration to {{duration}}seconds"
AdvSceneSwitcher.action.transition.type.scene="scene transition"
AdvSceneSwitcher.action.transition.type.sceneOverride="scene transition override"
AdvSceneSwitcher.action.transition.type.sourceShow="source show transition"
AdvSceneSwitcher.action.transition.type.sourceHide="source hide transition"
AdvSceneSwitcher.action.transition.entry.line1="Modify {{type}}{{scenes}}{{sources}}"
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}Set transition type to {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}Set transition duration to {{duration}}seconds"
AdvSceneSwitcher.action.timer="Timer"
AdvSceneSwitcher.action.timer.type.pause="Pause"
AdvSceneSwitcher.action.timer.type.continue="Continue"
@@ -757,7 +769,8 @@ AdvSceneSwitcher.unit.milliseconds="milliseconds"
AdvSceneSwitcher.unit.secends="seconds"
AdvSceneSwitcher.unit.minutes="minutes"
AdvSceneSwitcher.unit.hours="hours"
AdvSceneSwitcher.duration.condition.none="No time constraint"
AdvSceneSwitcher.duration.condition.none="No duration modifier"
AdvSceneSwitcher.duration.condition.more="For at least"
AdvSceneSwitcher.duration.condition.equal="For exactly"
AdvSceneSwitcher.duration.condition.less="For at most"
AdvSceneSwitcher.duration.condition.within="Within the last"

766
data/locale/es-ES.ini Normal file
View File

@@ -0,0 +1,766 @@
AdvSceneSwitcher.pluginName="Advanced Scene Switcher"
AdvSceneSwitcher.windowTitle="Advanced Scene Switcher"
; General Tab
AdvSceneSwitcher.generalTab.title="General"
AdvSceneSwitcher.generalTab.status="Estado"
AdvSceneSwitcher.generalTab.status.hotkeytips="Los atajos pueden ser definidos en los ajustes de OBS"
AdvSceneSwitcher.generalTab.status.currentStatus="Advanced Scene Switcher está:"
AdvSceneSwitcher.generalTab.status.onStartup="Al iniciar OBS:"
AdvSceneSwitcher.generalTab.status.onStartup.asLastRun="Empezar el selector de escenas si no estaba iniciado"
AdvSceneSwitcher.generalTab.status.onStartup.alwaysStart="Siempre iniciar el selector de escenas"
AdvSceneSwitcher.generalTab.status.onStartup.doNotStart="No iniciar el selector de escenas"
AdvSceneSwitcher.generalTab.status.start="Iniciar"
AdvSceneSwitcher.generalTab.status.stop="Detener"
AdvSceneSwitcher.generalTab.status.autoStart="Iniciar automáticamente el selector de escenas cuando:"
AdvSceneSwitcher.generalTab.status.autoStart.never="Nunca"
AdvSceneSwitcher.generalTab.status.autoStart.recording="Grabando"
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Emitiendo"
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Grabando o emitiendo"
AdvSceneSwitcher.generalTab.status.checkInterval="Comprobar las condiciones de cambio cada"
AdvSceneSwitcher.generalTab.generalBehavior="Comportamiento general"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="Si no se cumple ninguna condición por"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Solo será tan preciso como el intervalo de comprobación configurado."
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.dontSwitch="No cambiar"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchToRandom="Cambiar a una escena aleatoria"
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet.switchTo="Cambiar a:"
AdvSceneSwitcher.generalTab.generalBehavior.cooldown="Después de una coincidencia, no cambiar de escena durante"
AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint="¡Durante este tiempo, se ignorarán las posibles coincidencias!"
AdvSceneSwitcher.generalTab.generalBehavior.verboseLogging="Habilitar el registro detallado"
AdvSceneSwitcher.generalTab.generalBehavior.saveWindowGeo="Guardar la posición y el tamaño de la ventana"
AdvSceneSwitcher.generalTab.generalBehavior.showTrayNotifications="Mostrar notificaciones del sistema"
AdvSceneSwitcher.generalTab.generalBehavior.disableUIHints="Deshabilitar sugerencias de interfaz de usuario"
AdvSceneSwitcher.generalTab.generalBehavior.hideLegacyTabs="Ocultar pestañas que se pueden representar a través de macros"
AdvSceneSwitcher.generalTab.priority="Prioridad"
AdvSceneSwitcher.generalTab.priority.description="Prioridad de métodos de conmutación (la prioridad más alta está en la parte superior)"
AdvSceneSwitcher.generalTab.priority.threadPriority="Usar prioridad de subproceso"
AdvSceneSwitcher.generalTab.priority.threadPriorityNotice="(No se recomienda elevar la prioridad por encima de \"Normal\")"
AdvSceneSwitcher.generalTab.saveOrLoadsettings="Guardar / Cargar ajustes"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.export="Exportar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.import="Importar"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.exportWindowTitle="Exportar los ajustes para Advanced Scene Switcher ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.importWindowTitle="Importar los ajustes para Advanced Scene Switcher ..."
AdvSceneSwitcher.generalTab.saveOrLoadsettings.textType="Archivos de texto (*.txt)"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadFail="La importacion la configuracion de escenas fallo"
AdvSceneSwitcher.generalTab.saveOrLoadsettings.loadSuccess="Configuración de escenas importada correctamente"
AdvSceneSwitcher.generalTab.priority.fileContent="Contenido del archivo"
AdvSceneSwitcher.generalTab.priority.sceneSequence="Secuencia de escenas"
AdvSceneSwitcher.generalTab.priority.idleDetection="Detección de inactivos"
AdvSceneSwitcher.generalTab.priority.executable="Ejecutable"
AdvSceneSwitcher.generalTab.priority.screenRegion="Región de pantalla"
AdvSceneSwitcher.generalTab.priority.windowTitle="Título de la ventana"
AdvSceneSwitcher.generalTab.priority.media="Medios"
AdvSceneSwitcher.generalTab.priority.time="Tiempo"
AdvSceneSwitcher.generalTab.priority.audio="Audio"
AdvSceneSwitcher.generalTab.priority.video="Video"
AdvSceneSwitcher.generalTab.priority.macro="Macro"
; Macro Tab
AdvSceneSwitcher.macroTab.title="Macro"
AdvSceneSwitcher.macroTab.macros="Macros"
AdvSceneSwitcher.macroTab.priorityWarning="Nota: Se recomienda configurar las macros para que sean la funcionalidad de máxima prioridad.\nEsta configuración se puede cambiar en la ficha General."
AdvSceneSwitcher.macroTab.help="Las macros le permiten ejecutar una cadena de acciones en función de varias condiciones.\n\nHaga clic en el símbolo más resaltado para agregar una nueva macro."
AdvSceneSwitcher.macroTab.editConditionHelp="Esta sección le permite definir las condiciones de la macro.\n\nSeleccione una macro existente o agregue una nueva a la izquierda.\nLuego haga clic en el botón más a continuación para agregar una nueva condición."
AdvSceneSwitcher.macroTab.editActionHelp="Esta sección le permite definir acciones de la macro.\n\nSeleccione una macro existente o agregue una nueva a la izquierda.\nLuego haga clic en el botón más a continuación para agregar una nueva acción"
AdvSceneSwitcher.macroTab.edit="Editar macro"
AdvSceneSwitcher.macroTab.edit.logic="Tipo de lógica:"
AdvSceneSwitcher.macroTab.edit.condition="Tipo de condición:"
AdvSceneSwitcher.macroTab.edit.action="Tipo de acción:"
AdvSceneSwitcher.macroTab.add="Agregar nueva macro"
AdvSceneSwitcher.macroTab.name="Nombre:"
AdvSceneSwitcher.macroTab.run="Ejecutar macro"
AdvSceneSwitcher.macroTab.runFail="Error al ejecutar \"%1\" si se ha producido un error en una de las acciones o si la macro ya se está ejecutando."
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
AdvSceneSwitcher.macroTab.exists="El nombre de la macro ya existe"
AdvSceneSwitcher.macroTab.copy="Crear copia"
AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
AdvSceneSwitcher.macroTab.collapseAll="Contraer todo"
AdvSceneSwitcher.macroTab.maximize="Maximizar"
AdvSceneSwitcher.macroTab.minimize="Minimizar"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Resaltar macros ejecutadas recientemente"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Resaltar condiciones de la macro seleccionada, que se evaluaron como verdaderas recientemente"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Resaltar acciones realizadas recientemente de la macro seleccionada actualmente"
; Lógica de macros
AdvSceneSwitcher.logic.none="Omitir entrada"
AdvSceneSwitcher.logic.and="Y"
AdvSceneSwitcher.logic.or="O"
AdvSceneSwitcher.logic.andNot="Y no"
AdvSceneSwitcher.logic.orNot="O no"
AdvSceneSwitcher.logic.rootNone="Si"
AdvSceneSwitcher.logic.not="Si no"
; Macro Conditions
AdvSceneSwitcher.condition.audio="Audio"
AdvSceneSwitcher.condition.audio.state.below="abajo"
AdvSceneSwitcher.condition.audio.state.exact="exactamente"
AdvSceneSwitcher.condition.audio.state.above="arriba"
AdvSceneSwitcher.condition.audio.state.mute="silenciado"
AdvSceneSwitcher.condition.audio.state.unmute="no silenciado"
AdvSceneSwitcher.condition.audio.type.output="Volumen de salida"
AdvSceneSwitcher.condition.audio.type.volume="Nivel de volumen configurado"
AdvSceneSwitcher.condition.audio.entry="{{checkType}} de {{audioSources}} es {{condition}} {{volume}}"
AdvSceneSwitcher.condition.cursor="Cursor"
AdvSceneSwitcher.condition.cursor.type.region="está en la región"
AdvSceneSwitcher.condition.cursor.type.moving="se está moviendo"
AdvSceneSwitcher.condition.cursor.showFrame="Mostrar cuadro"
AdvSceneSwitcher.condition.cursor.hideFrame="Ocultar cuadro"
AdvSceneSwitcher.condition.cursor.entry.line1="El cursor es {{conditions}} {{minX}} {{minY}} {{maxX}} {{maxY}} - {{toggleFrameButton}}"
AdvSceneSwitcher.condition.cursor.entry.line2="El cursor se encuentra actualmente en {{xPos}} x {{yPos}}"
AdvSceneSwitcher.condition.scene="Escena"
AdvSceneSwitcher.condition.scene.type.current="La escena actual es"
AdvSceneSwitcher.condition.scene.type.previous="La escena anterior es"
AdvSceneSwitcher.condition.scene.type.changed="Escena cambiada"
AdvSceneSwitcher.condition.scene.type.notChanged="La escena no ha cambiado"
AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour="Durante la transición, verifique la escena de destino de la transición"
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}} {{scenes}}"
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Ventana"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} existen y ..."
AdvSceneSwitcher.condition.window.entry.line2="... es {{fullscreen}} pantalla completa {{maximized}} maximizada {{focused}} enfocada {{windowFocusChanged}} ventana de primer plano cambiada"
AdvSceneSwitcher.condition.file="Archivo"
AdvSceneSwitcher.condition.file.entry.line1="Contenido de {{fileType}} {{filePath}} coincidencias:"
AdvSceneSwitcher.condition.file.entry.line2="{{matchText}}"
AdvSceneSwitcher.condition.file.entry.line3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.condition.media="Medios"
AdvSceneSwitcher.condition.media.anyOnScene="Cualquier fuente multimedia activada"
AdvSceneSwitcher.condition.media.allOnScene="Todas las fuentes de medios activadas"
AdvSceneSwitcher.condition.media.matchOnChange="Solo coincidir con el cambio (Nota: esta opción se eliminará en una versión futura; utilice las restricciones de tiempo en su lugar)"
AdvSceneSwitcher.condition.media.inconsistencyInfo="Desafortunadamente, no todos los tipos de fuentes de medios se comportan de la misma manera (p. ej., fuente de medios frente a estado \"Detenido\" de fuente de video VLC).\n¡Así que experimente lo que funciona para su configuración!"
AdvSceneSwitcher.condition.media.entry="El estado de {{mediaSources}}{{scenes}} es {{states}} y {{timeRestrictions}} {{time}}"
AdvSceneSwitcher.condition.video="Video"
AdvSceneSwitcher.condition.video.condition.match="coincide exactamente"
AdvSceneSwitcher.condition.video.condition.differ="no coincide"
AdvSceneSwitcher.condition.video.condition.hasChanged="ha cambiado"
AdvSceneSwitcher.condition.video.condition.hasNotChanged="no ha cambiado"
AdvSceneSwitcher.condition.video.condition.noImage="no tiene salida"
AdvSceneSwitcher.condition.video.condition.pattern="coincide con el patrón"
AdvSceneSwitcher.condition.video.condition.object="contiene objeto"
AdvSceneSwitcher.condition.video.askFileAction="¿Desea utilizar un archivo existente o crear una captura de pantalla de la fuente seleccionada actualmente?"
AdvSceneSwitcher.condition.video.askFileAction.file="Usar archivo existente"
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Crear captura de pantalla"
AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Usar coincidencia de patrones"
AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="Esto le permitirá controlar cuánto debe cambiar la imagen para que la condición sea verdadera".
AdvSceneSwitcher.condition.video.patternThreshold="Umbral: "
AdvSceneSwitcher.condition.video.patternThresholdDescription="Un valor de umbral más alto significa que el patrón debe coincidir más estrechamente con la fuente de video".
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Usar canal alfa como máscara para el patrón."
AdvSceneSwitcher.condition.video.objectScaleThreshold="Factor de escala: "
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="Un factor de escala más bajo generará más coincidencias pero una mayor carga de CPU".
AdvSceneSwitcher.condition.video.minNeighborDescription="Un valor de vecinos mínimo más alto dará como resultado menos coincidencias pero de mayor calidad".
AdvSceneSwitcher.condition.video.showMatch="Mostrar coincidencia"
AdvSceneSwitcher.condition.video.showMatch.loading="Buscando coincidencias"
AdvSceneSwitcher.condition.video.screenshotFail="¡Error al obtener la captura de pantalla de la fuente!"
AdvSceneSwitcher.condition.video.screenshotEmpty="La captura de pantalla está vacía. ¿Está visible la fuente?"
AdvSceneSwitcher.condition.video.patternMatchFail="¡No se encontró el patrón!"
AdvSceneSwitcher.condition.video.patternMatchSuccess="El patrón está resaltado en rojo"
AdvSceneSwitcher.condition.video.objectMatchFail="¡No se encontró el objeto!"
AdvSceneSwitcher.condition.video.objectMatchSuccess="El objeto está resaltado en rojo"
AdvSceneSwitcher.condition.video.modelLoadFail="¡No se pudieron cargar los datos del modelo!"
AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePath}}"
AdvSceneSwitcher.condition.video.entry.modelPath="Datos del modelo (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Mínimo de vecinos: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduzca la carga de la CPU realizando una comprobación solo cada {{throttleCount}} milisegundos"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Realizar comprobación solo en el área {{checkArea}} {{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="Tamaño mínimo:"
AdvSceneSwitcher.condition.video.maxSize="Tamaño máximo:"
AdvSceneSwitcher.condition.video.selectArea="Seleccionar área"
AdvSceneSwitcher.condition.video.selectArea.status="Solo se comprobará el área resaltada"
AdvSceneSwitcher.condition.video.width="Ancho"
AdvSceneSwitcher.condition.video.height="Altura"
AdvSceneSwitcher.condition.stream="Transmisión"
AdvSceneSwitcher.condition.stream.state.start="Transmisión en ejecución"
AdvSceneSwitcher.condition.stream.state.stop="Transmisión detenida"
AdvSceneSwitcher.condition.stream.state.starting="Inicio de transmisión"
AdvSceneSwitcher.condition.stream.state.stopping="Detener transmisión"
AdvSceneSwitcher.condition.stream.entry="{{streamState}}"
AdvSceneSwitcher.condition.record="Grabación"
AdvSceneSwitcher.condition.record.state.start="Grabación en ejecución"
AdvSceneSwitcher.condition.record.state.pause="Grabación en pausa"
AdvSceneSwitcher.condition.record.state.stop="Grabación detenida"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Proceso"
AdvSceneSwitcher.condition.process.entry="{{processes}} se está ejecutando {{focused}} y está enfocado"
AdvSceneSwitcher.condition.idle="Inactivo"
AdvSceneSwitcher.condition.idle.entry="No hay entradas de teclado o ratón durante {{duration}}"
AdvSceneSwitcher.condition.pluginState="Estado del complemento"
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Se activó un cambio de escena automático en este intervalo"
AdvSceneSwitcher.condition.pluginState.state.running="El conmutador de escenas avanzado se está ejecutando"
AdvSceneSwitcher.condition.pluginState.state.shutdown="OBS se está cerrando"
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
AdvSceneSwitcher.condition.timer="Temporizador"
AdvSceneSwitcher.condition.timer.type.fixed="Fijar"
AdvSceneSwitcher.condition.timer.type.random="Aleatorio"
AdvSceneSwitcher.condition.timer.pause="Pausa"
AdvSceneSwitcher.condition.timer.continue="Continuar"
AdvSceneSwitcher.condition.timer.entry.line1.fixed="{{type}} la duración de {{duration}} ha pasado"
AdvSceneSwitcher.condition.timer.entry.line1.random="La duración de {{type}} de {{duration}} a {{duration2}} ha pasado"
AdvSceneSwitcher.condition.timer.entry.line2="Tiempo restante: {{remaining}} segundos"
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Ahorra tiempo restante {{autoReset}} Restablecer el temporizador después de alcanzar la duración"
AdvSceneSwitcher.condition.timer.reset="Reiniciar"
AdvSceneSwitcher.condition.macro="Macro"
AdvSceneSwitcher.condition.macro.type.count="Recuento"
AdvSceneSwitcher.condition.macro.type.state="Estado"
AdvSceneSwitcher.condition.macro.type.selection="Tipo de condición: {{types}}"
AdvSceneSwitcher.condition.macro.count.type.below="Menor que"
AdvSceneSwitcher.condition.macro.count.type.above="Más que"
AdvSceneSwitcher.condition.macro.count.type.equal="Exactamente"
AdvSceneSwitcher.condition.macro.count.reset="Reiniciar"
AdvSceneSwitcher.condition.macro.pausedWarning="¡La macro seleccionada está actualmente en pausa!"
AdvSceneSwitcher.condition.macro.state.entry="Las condiciones de {{macros}} son verdaderas"
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} se ejecutó {{conditions}} {{count}} veces"
AdvSceneSwitcher.condition.macro.count.entry.line2="Recuento actual: {{currentCount}} {{resetCount}}"
AdvSceneSwitcher.condition.source="Fuente"
AdvSceneSwitcher.condition.source.type.active="Está activo"
AdvSceneSwitcher.condition.source.type.showing="Se muestra"
AdvSceneSwitcher.condition.source.type.settings="Coincidencia de configuración"
AdvSceneSwitcher.condition.source.regex="Usar expresiones regulares"
AdvSceneSwitcher.condition.source.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.condition.source.entry.line1="{{sources}} {{conditions}}"
AdvSceneSwitcher.condition.source.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.source.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.virtualCamera="Cámara virtual"
AdvSceneSwitcher.condition.virtualCamera.state.start="Cámara virtual iniciada"
AdvSceneSwitcher.condition.virtualCamera.state.stop="Cámara virtual detenida"
AdvSceneSwitcher.condition.virtualCamera.entry="{{states}}"
AdvSceneSwitcher.condition.filter="Filtro"
AdvSceneSwitcher.condition.filter.type.active="Está habilitado"
AdvSceneSwitcher.condition.filter.type.showing="Está deshabilitado"
AdvSceneSwitcher.condition.filter.type.settings="Coincidencia de configuración"
AdvSceneSwitcher.condition.filter.regex="Usar expresiones regulares"
AdvSceneSwitcher.condition.filter.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.condition.filter.entry.line1="En {{sources}} {{filters}} {{conditions}}"
AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.sceneOrder="Orden de elementos de escena"
AdvSceneSwitcher.condition.sceneOrder.type.above="Está arriba"
AdvSceneSwitcher.condition.sceneOrder.type.below="Está debajo"
AdvSceneSwitcher.condition.sceneOrder.type.position="Está en la posición"
AdvSceneSwitcher.condition.sceneOrder.positionInfo="El valor de posición comienza en la parte inferior con 0 y aumenta en uno para cada elemento de la escena, incluidos los de los grupos de escenas"
AdvSceneSwitcher.condition.sceneOrder.entry="En{{scenes}}{{sources}}{{conditions}}{{sources2}}{{position}}"
AdvSceneSwitcher.condition.hotkey="Tecla de acceso rápido"
AdvSceneSwitcher.condition.hotkey.name="Tecla de acceso directo de activación de macro"
AdvSceneSwitcher.condition.hotkey.tip="Nota: puede configurar las combinaciones de teclas para esta tecla de acceso rápido en la ventana de configuración de OBS"
AdvSceneSwitcher.condition.hotkey.entry.line1="Se presiona la tecla de acceso rápido"
AdvSceneSwitcher.condition.hotkey.entry.line2="Nombre: {{name}}"
AdvSceneSwitcher.condition.replay="Búfer de reproducción"
AdvSceneSwitcher.condition.replay.state.stopped="Búfer de reproducción detenido"
AdvSceneSwitcher.condition.replay.state.started="Búfer de reproducción iniciado"
AdvSceneSwitcher.condition.replay.state.saved="Búfer de reproducción guardado"
AdvSceneSwitcher.condition.replay.entry="{{state}}"
AdvSceneSwitcher.condition.date="Fecha"
AdvSceneSwitcher.condition.date.anyDay="Cualquier día"
AdvSceneSwitcher.condition.date.monday="Lunes"
AdvSceneSwitcher.condition.date.tuesday="Martes"
AdvSceneSwitcher.condition.date.wednesday="Miércoles"
AdvSceneSwitcher.condition.date.thursday="Jueves"
AdvSceneSwitcher.condition.date.friday="Viernes"
AdvSceneSwitcher.condition.date.saturday="Sábado"
AdvSceneSwitcher.condition.date.sunday="Domingo"
AdvSceneSwitcher.condition.date.state.at="A las"
AdvSceneSwitcher.condition.date.state.after="Después"
AdvSceneSwitcher.condition.date.state.before="Antes"
AdvSceneSwitcher.condition.date.state.between="Entre"
AdvSceneSwitcher.condition.date.separator="y"
AdvSceneSwitcher.condition.date.ignoreDate="Si no se marca, se ignorará el componente de fecha"
AdvSceneSwitcher.condition.date.ignoreTime="Si no se marca, se ignorará el componente de tiempo"
AdvSceneSwitcher.condition.date.showAdvancedSettings="Mostrar configuración avanzada"
AdvSceneSwitcher.condition.date.showSimpleSettings="Mostrar configuración simple"
AdvSceneSwitcher.condition.date.entry.simple="El {{dayOfWeek}} {{weekCondition}} {{ignoreWeekTime}}{{weekTime}}"
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} Repetir cada {{duration}} en la coincidencia de fechas"
AdvSceneSwitcher.condition.date.entry.nextMatchDate="Próxima coincidencia en: %1"
AdvSceneSwitcher.condition.date.entry.updateOnRepeat="{{updateOnRepeat}} Al repetir actualizar la fecha seleccionada para repetir la fecha"
AdvSceneSwitcher.condition.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.condition.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.condition.sceneTransform.regex="Usar expresiones regulares"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="En{{scenes}}{{sources}}coincide con la transformación"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Transición"
AdvSceneSwitcher.condition.transition.type.current="El tipo de transición actual es"
AdvSceneSwitcher.condition.transition.type.duration="La duración de la transición actual es"
AdvSceneSwitcher.condition.transition.type.started="Transición iniciada"
AdvSceneSwitcher.condition.transition.type.ended="Transición finalizada"
AdvSceneSwitcher.condition.transition.type.transitionSource="Transición desde"
AdvSceneSwitcher.condition.transition.type.transitionTarget="Transición a"
AdvSceneSwitcher.condition.transition.durationSuffix="segundos"
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{scenes}}{{duration}}{{durationSuffix}}"
AdvSceneSwitcher.condition.sceneVisibility="Visibilidad del elemento de escena"
AdvSceneSwitcher.condition.sceneVisibility.type.shown="Mostrado"
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="Oculto"
AdvSceneSwitcher.condition.sceneVisibility.entry="En{{scenes}}{{sources}}es{{conditions}}"
AdvSceneSwitcher.condition.studioMode="Modo de estudio"
AdvSceneSwitcher.condition.studioMode.state.active="El modo de estudio está activo"
AdvSceneSwitcher.condition.studioMode.state.notActive="El modo de estudio no está activo"
AdvSceneSwitcher.condition.studioMode.state.previewScene="La vista previa de la escena es"
AdvSceneSwitcher.condition.studioMode.entry="{{conditions}}{{scenes}}"
AdvSceneSwitcher.condition.openvr="OpenVR"
AdvSceneSwitcher.condition.errorStatus="Error de OpenVR: "
AdvSceneSwitcher.condition.openvr.entry.line1="HMD está en..."
AdvSceneSwitcher.condition.openvr.entry.line2="{{controls}}"
AdvSceneSwitcher.condition.openvr.entry.line3="HMD se encuentra actualmente en {{xPos}} x {{yPos}} x {{zPos}}"
AdvSceneSwitcher.condition.stats="Estadísticas OBS"
AdvSceneSwitcher.condition.stats.type.fps="FPS"
AdvSceneSwitcher.condition.stats.type.CPUUsage="Uso de CPU"
AdvSceneSwitcher.condition.stats.type.HDDSpaceAvailable="Espacio en disco disponible"
AdvSceneSwitcher.condition.stats.type.memoryUsage="Uso de memoria"
AdvSceneSwitcher.condition.stats.type.averageTimeToRender="Tiempo medio para renderizar fotograma"
AdvSceneSwitcher.condition.stats.type.skippedFrames="Fotogramas omitidos debido a un retraso en la codificación"
AdvSceneSwitcher.condition.stats.type.missedFrames="Frames perdidos debido al retraso en el procesamiento"
AdvSceneSwitcher.condition.stats.type.droppedFrames.stream="Transmitir fotogramas perdidos"
AdvSceneSwitcher.condition.stats.type.megabytesSent.stream="Transmisión total de salida de datos"
AdvSceneSwitcher.condition.stats.type.bitrate.stream="Tasa de bits de transmisión"
AdvSceneSwitcher.condition.stats.type.droppedFrames.recording="Grabando fotogramas caídos"
AdvSceneSwitcher.condition.stats.type.megabytesSent.recording="Grabando salida de datos total"
AdvSceneSwitcher.condition.stats.type.bitrate.recording="Tasa de bits de grabación"
AdvSceneSwitcher.condition.stats.condition.above="arriba de"
AdvSceneSwitcher.condition.stats.condition.equals="igual a"
AdvSceneSwitcher.condition.stats.condition.below="abajo de"
AdvSceneSwitcher.condition.stats.dockHint="Puede abrir el panel de \"Estadísticas\" para ver el estado actual"
AdvSceneSwitcher.condition.stats.entry="{{stats}} esta {{condition}} {{value}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Cambiar escena"
AdvSceneSwitcher.action.scene.entry="Cambiar a la escena {{scenes}} usando {{transitions}} con una duración de {{duration}} segundos"
AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Espere hasta que se complete la transición a la escena de destino"
AdvSceneSwitcher.action.wait="Esperar"
AdvSceneSwitcher.action.wait.type.fixed="fijo"
AdvSceneSwitcher.action.wait.type.random="aleatorio"
AdvSceneSwitcher.action.wait.entry.fixed="Espere {{waitType}} de {{duration}}"
AdvSceneSwitcher.action.wait.entry.random="Espere {{waitType}} de {{duration}} a {{duration2}}"
AdvSceneSwitcher.action.audio="Audio"
AdvSceneSwitcher.action.audio.type.mute="Silencio"
AdvSceneSwitcher.action.audio.type.unmute="Activar silencio"
AdvSceneSwitcher.action.audio.type.sourceVolume="Establecer volumen de origen"
AdvSceneSwitcher.action.audio.type.masterVolume="Establecer volumen maestro"
AdvSceneSwitcher.action.audio.fade.type.duration="durante una duración de"
AdvSceneSwitcher.action.audio.fade.type.rate="a una velocidad de"
AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade {{fadeTypes}} {{duration}} segundos."
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade {{fadeTypes}} {{rate}}por segundo."
AdvSceneSwitcher.action.audio.fade.wait="Espere a que se complete el desvanecimiento".
AdvSceneSwitcher.action.audio.fade.abort="Cancelar atenuación ya activa."
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
AdvSceneSwitcher.action.recording="Grabando"
AdvSceneSwitcher.action.recording.type.stop="Detener grabación"
AdvSceneSwitcher.action.recording.type.start="Iniciar grabación"
AdvSceneSwitcher.action.recording.type.pause="Pausar grabación"
AdvSceneSwitcher.action.recording.type.unpause="Reanudar grabación"
AdvSceneSwitcher.action.recording.pause.hint="Tenga en cuenta que, dependiendo de la configuración de grabación, es posible que no pueda pausar la grabación"
AdvSceneSwitcher.action.recording.entry="{{actions}}{{pauseHint}}"
AdvSceneSwitcher.action.replay="Búfer de reproducción"
AdvSceneSwitcher.action.replay.type.stop="Detener el búfer de reproducción"
AdvSceneSwitcher.action.replay.type.start="Iniciar búfer de reproducción"
AdvSceneSwitcher.action.replay.type.save="Guardar búfer de reproducción"
AdvSceneSwitcher.action.replay.entry="{{actions}}"
AdvSceneSwitcher.action.streaming="Transmisión"
AdvSceneSwitcher.action.streaming.type.stop="Detener transmisión"
AdvSceneSwitcher.action.streaming.type.start="Iniciar transmisión"
AdvSceneSwitcher.action.streaming.entry="{{actions}}"
AdvSceneSwitcher.action.run="Ejecutar"
AdvSceneSwitcher.action.run.arguments="Argumentos:"
AdvSceneSwitcher.action.run.addArgument="Agregar argumento"
AdvSceneSwitcher.action.run.addArgumentDescription="Añadir nuevo argumento:"
AdvSceneSwitcher.action.run.entry="Ejecutar {{filePath}}"
AdvSceneSwitcher.action.sceneVisibility="Visibilidad del elemento de escena"
AdvSceneSwitcher.action.sceneVisibility.type.show="Mostrar"
AdvSceneSwitcher.action.sceneVisibility.type.hide="Ocultar"
AdvSceneSwitcher.action.sceneVisibility.type.source="Fuente"
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Cualquiera"
AdvSceneSwitcher.action.sceneVisibility.entry="En{{scenes}}{{actions}}{{sourceTypes}}{{sources}}{{sourceGroups}}"
AdvSceneSwitcher.action.filter="Filtro"
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
AdvSceneSwitcher.action.filter.type.settings="Establecer configuración"
AdvSceneSwitcher.action.filter.entry="En {{sources}} {{actions}} {{filters}}"
AdvSceneSwitcher.action.filter.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.action.source="Fuente"
AdvSceneSwitcher.action.source.type.enable="Habilitar"
AdvSceneSwitcher.action.source.type.disable="Deshabilitar"
AdvSceneSwitcher.action.source.type.settings="Establecer configuración"
AdvSceneSwitcher.action.source.entry="{{actions}} {{sources}}"
AdvSceneSwitcher.action.source.warning="Advertencia: la IU de OBS no puede controlar la habilitación y deshabilitación global de fuentes"
AdvSceneSwitcher.action.source.getSettings="Obtener la configuración actual"
AdvSceneSwitcher.action.media="Medios"
AdvSceneSwitcher.action.media.type.play="Reproducir"
AdvSceneSwitcher.action.media.type.pause="Pausa"
AdvSceneSwitcher.action.media.type.stop="Detener"
AdvSceneSwitcher.action.media.type.restart="Reiniciar"
AdvSceneSwitcher.action.media.type.next="Siguiente"
AdvSceneSwitcher.action.media.type.previous="Anterior"
AdvSceneSwitcher.action.media.type.seek="Buscar"
AdvSceneSwitcher.action.media.entry="{{actions}}{{duration}}{{mediaSources}}"
AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pausa"
AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
AdvSceneSwitcher.action.macro.type.stop="Detener"
AdvSceneSwitcher.action.macro.entry="{{actions}} {{macros}}"
AdvSceneSwitcher.action.pluginState="Estado del complemento"
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"
AdvSceneSwitcher.action.pluginState.type.import="Importar configuración desde"
AdvSceneSwitcher.action.pluginState.importWarning="Nota: la acción se ignorará mientras se abra la ventana de configuración."
AdvSceneSwitcher.action.pluginState.entry="{{actions}}{{values}}{{scenes}}{{settings}}{{settingsWarning}}"
AdvSceneSwitcher.action.virtualCamera="Cámara virtual"
AdvSceneSwitcher.action.virtualCamera.type.stop="Detener cámara virtual"
AdvSceneSwitcher.action.virtualCamera.type.start="Iniciar cámara virtual"
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
AdvSceneSwitcher.action.hotkey="Tecla de acceso rápido"
AdvSceneSwitcher.action.hotkey.leftShift="Mayús a la izquierda"
AdvSceneSwitcher.action.hotkey.rightShift="Mayús a la derecha"
AdvSceneSwitcher.action.hotkey.leftCtrl="Ctrl izquierdo"
AdvSceneSwitcher.action.hotkey.rightCtrl="Ctrl derecho"
AdvSceneSwitcher.action.hotkey.leftAlt="Alt izquierdo"
AdvSceneSwitcher.action.hotkey.rightAlt="Alt derecho"
AdvSceneSwitcher.action.hotkey.leftMeta="Meta izquierda"
AdvSceneSwitcher.action.hotkey.rightMeta="Meta derecho"
AdvSceneSwitcher.action.hotkey.onlyOBS="Enviar pulsación de tecla solo a OBS"
AdvSceneSwitcher.action.hotkey.disabled="No se puede simular la pulsación de teclas: ¡funcionalidad desactivada!"
AdvSceneSwitcher.action.hotkey.entry="Presione {{keys}} durante {{duration}} milisegundos"
AdvSceneSwitcher.action.sceneOrder="Orden de elementos de escena"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Mover hacia arriba"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Mover hacia abajo"
AdvSceneSwitcher.action.sceneOrder.type.moveTop="Mover al principio"
AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Mover al final"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}transformar{{sources}}"
AdvSceneSwitcher.action.file="Archivo"
AdvSceneSwitcher.action.file.type.write="Escribir"
AdvSceneSwitcher.action.file.type.append="Agregar"
AdvSceneSwitcher.action.file.entry="{{actions}} a {{filePath}}:"
AdvSceneSwitcher.action.previewScene="Cambiar escena de vista previa"
AdvSceneSwitcher.action.previewScene.entry="Cambiar escena de vista previa a {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="Cambiar escena (modo Estudio)"
AdvSceneSwitcher.action.SceneSwap.entry="Intercambiar Vista Previa y Programa en Modo Estudio"
AdvSceneSwitcher.action.transition="Transición"
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}Establecer el tipo de transición en {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}Establecer la duración de la transición en {{duration}}segundos"
AdvSceneSwitcher.action.timer="Temporizador"
AdvSceneSwitcher.action.timer.type.pause="Pausa"
AdvSceneSwitcher.action.timer.type.continue="Continuar"
AdvSceneSwitcher.action.timer.type.reset="Reiniciar"
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Establecer tiempo restante de"
AdvSceneSwitcher.action.timer.entry="{{timerAction}} temporizadores en {{macros}} {{duration}}"
AdvSceneSwitcher.action.random="Aleatorio"
AdvSceneSwitcher.action.random.entry="Ejecute aleatoriamente cualquiera de las siguientes macros (las macros en pausa se ignoran)"
AdvSceneSwitcher.action.systray="Notificación de la bandeja del sistema"
AdvSceneSwitcher.action.systray.entry="Mostrar notificación: {{message}}"
AdvSceneSwitcher.action.screenshot="Captura de pantalla"
AdvSceneSwitcher.action.screenshot.mainOutput="Salida principal de OBS"
AdvSceneSwitcher.action.screenshot.entry="Captura de pantalla {{sources}}"
AdvSceneSwitcher.action.profile="Perfil"
AdvSceneSwitcher.action.profile.entry="Cambiar perfil activo a {{profiles}}"
AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
AdvSceneSwitcher.action.sceneCollection.entry="Cambiar la colección de escenas activa a {{sceneCollections}}"
AdvSceneSwitcher.action.sceneCollection.warning="Nota: Cualquier acción posterior a esta no se ejecutará, ya que la colección de escenas cambiante también volverá a cargar la configuración del conmutador de escenas.\nLa acción de la colección de escenas se ignorará mientras se abra la ventana de configuración".
AdvSceneSwitcher.action.sequence="Secuencia"
AdvSceneSwitcher.action.sequence.entry="Cada vez que se realiza esta acción, ejecute la siguiente macro de la lista (las macros en pausa se ignoran)"
AdvSceneSwitcher.action.sequence.status="Última macro ejecutada: %1 - Siguiente macro a ejecutar: %2"
AdvSceneSwitcher.action.sequence.status.none="ninguno"
AdvSceneSwitcher.action.sequence.restart="Reiniciar desde el principio una vez que se alcance el final de la lista"
AdvSceneSwitcher.action.sequence.continueFrom="Continuar con el elemento seleccionado"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transición"
AdvSceneSwitcher.transitionTab.setTransitionBy="Al cambiar las transiciones:"
AdvSceneSwitcher.transitionTab.transitionOverride="Establecer anulaciones de transición"
AdvSceneSwitcher.transitionTab.adjustActiveTransitionType="Cambiar tipo de transición activa"
AdvSceneSwitcher.transitionTab.transitionBehaviorSelectionError="Al menos una opción debe estar habilitada:\n\n - Usar anulaciones de transición\n\n - Cambiar el tipo de transición activo"
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliza la transición para el cambio de escena automatizado de la escena A a la escena B"
AdvSceneSwitcher.transitionTab.transitionsHelp="<html><head/><body> <p> Estos ajustes <span style =\"font-style: italic; \"> solo </span> surgió a las transiciones causadas por el selector de escenas - Echa un vistazo a <a href =\"https://obsproject.com/forum/resources/transition-table.1174/\"> <span style=\" texto-decoración: subrayado; color: # 268bd2; \"> Tabla de transición </span> </a> si desea configurar esto para cambios de escena manuales. <br/> Los definiciones definidos aquí tienen prioridad sobre los ajustes de transición configurados en cualquier otro lugar del selector de escenas. < br/> <br/> Haz clic en el símbolo más a continuación para agregar una nueva entrada. </p></body></html>"
AdvSceneSwitcher.transitionTab.defaultTransition="Cambiar transición si la escena está activa"
AdvSceneSwitcher.transitionTab.entry="Cambiar de {{scenes}} a {{scenes2}} usando {{transitions}} con una duración de {{duration}}"
AdvSceneSwitcher.transitionTab.defaultTransitionEntry="Cuando la escena {{scenes}} está activa, cambie la transición de escena predeterminada a {{transitions}}"
AdvSceneSwitcher.transitionTab.defaultTransitionsHelp="Haga clic en el símbolo más para añadir una entrada."
AdvSceneSwitcher.transitionTab.defaultTransition.delay="Cambiar transición {{defTransitionDelay}} después del cambio de escena."
AdvSceneSwitcher.transitionTab.defaultTransition.delay.help="El retraso se usa para evitar cambios de escena cancelados, lo que puede suceder si se cambia el tipo de transición mientras una transición aún está en curso".
; Pause Scenes Tab
AdvSceneSwitcher.pauseTab.title="Pausar"
AdvSceneSwitcher.pauseTab.pauseOnScene="Pausar el selector de escenas en la escena"
AdvSceneSwitcher.pauseTab.pauseInFocus1="Pausar el selector de escena cuando "
AdvSceneSwitcher.pauseTab.pauseInFocus2="esté en foco"
AdvSceneSwitcher.pauseTab.pauseTypeScene="la escena esté activa"
AdvSceneSwitcher.pauseTab.pauseTypeWindow="la ventana esté en foco"
AdvSceneSwitcher.pauseTab.pauseTargetAll="Todas"
AdvSceneSwitcher.pauseTab.pauseEntry="Pausar {{pauseTargets}} cuando {{pauseTypes}} {{scenes}} {{windows}}"
AdvSceneSwitcher.pauseTab.help="En esta pestaña puedes configurar la pausa de métodos de cambio individuales si una escena está activa o una ventana está enfocada.\n\nHaz clic en el símbolo más resaltado para continuar."
; Window Title Tab
AdvSceneSwitcher.windowTitleTab.title="Titulo"
AdvSceneSwitcher.windowTitleTab.regexrDescription="<html><head/><body><p>Introduce el título directo de una ventana o un regex válido. Puedes verificar la sintaxis y las coincidencias para las expresiones regulares usando <a href=\"https://regexr.com\"><span style=\" text-decoration: underline; color:#268bd2;\">RegExr</span></a></p></body></html>"
AdvSceneSwitcher.windowTitleTab.stayInFocus1="Ignorar nombre de la ventana"
AdvSceneSwitcher.windowTitleTab.stayInFocus2=" "
AdvSceneSwitcher.windowTitleTab.fullscreen="si está en pantalla completa"
AdvSceneSwitcher.windowTitleTab.maximized="si está maximizada"
AdvSceneSwitcher.windowTitleTab.focused="si está en foco"
AdvSceneSwitcher.windowTitleTab.entry="{{windows}} {{scenes}} {{transitions}} {{fullscreen}} {{maximized}} {{focused}}"
AdvSceneSwitcher.windowTitleTab.windowsHelp="Cambia de escena según el título de la ventana de las aplicaciones en ejecución.\nSe pueden seleccionar las siguientes condiciones adicionales:\nLa ventana está en Pantalla completa\nLa ventana está maximizada\nLa ventana está enfocada\n\nHaz clic en el símbolo más resaltado para continuar."
AdvSceneSwitcher.windowTitleTab.ignoreWindowsHelp="Si se ignora el título de una ventana, el selector de escena actuará como si la ventana seleccionada anteriormente todavía estuviera enfocada.\nEsto te permitirá evitar cambios de escena, si cambias con frecuencia a una ventana diferente, lo que no provocará un cambio de escena.\n\nElige una ventana o introduce un título de ventana arriba y haz clic en el símbolo más a continuación para agregarlo a la lista."
; Executable Tab
AdvSceneSwitcher.executableTab.title="Ejecutable"
AdvSceneSwitcher.executableTab.implemented="Implementado por dasOven"
AdvSceneSwitcher.executableTab.requiresFocus="sólo si está en foco"
AdvSceneSwitcher.executableTab.entry="Cuando {{processes}} está ejectuándose cambiar a {{scenes}} usando {{transitions}} {{requiresFocus}}"
AdvSceneSwitcher.executableTab.help="Esta pestaña te permitirá cambiar de escena automáticamente si se está ejecutando un proceso.\nEsto puede ser útil en situaciones en las que el nombre de la ventana puede cambiar o no se conoce.\n\nHaz clic en el símbolo más resaltado para continuar."
; Screen Region Tab
AdvSceneSwitcher.screenRegionTab.title="Región"
AdvSceneSwitcher.screenRegionTab.currentPosition="El cursor está actualmente en:"
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Mostrar marcos de guía"
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Ocultar guide frames"
AdvSceneSwitcher.screenRegionTab.excludeScenes.None="Sin selección"
AdvSceneSwitcher.screenRegionTab.entry="Si el cursor está en {{minX}} {{minY}} x {{maxX}} {{maxY}} cambiar a {{scenes}} usando {{transitions}} a menos que esté en {{excludeScenes}}"
AdvSceneSwitcher.screenRegionTab.help="Esta pestaña te permitirá cambiar escenas automáticamente según la posición actual del cursor del ratón.\n\nHaz clic en el símbolo más resaltado para continuar."
; Media Tab
AdvSceneSwitcher.mediaTab.title="Medios"
AdvSceneSwitcher.mediaTab.implemented="Implementado por Exeldro"
AdvSceneSwitcher.mediaTab.states.none="Ninguno"
AdvSceneSwitcher.mediaTab.states.playing="Reproduciéndose"
AdvSceneSwitcher.mediaTab.states.opening="Abriendo"
AdvSceneSwitcher.mediaTab.states.buffering="Almacenando en búfer"
AdvSceneSwitcher.mediaTab.states.Paused="Pausado"
AdvSceneSwitcher.mediaTab.states.stopped="Detenido"
AdvSceneSwitcher.mediaTab.states.ended="Finalizado"
AdvSceneSwitcher.mediaTab.states.error="Error"
AdvSceneSwitcher.mediaTab.states.playlistEnd="Finalizado(lista de reproducción)"
AdvSceneSwitcher.mediaTab.states.any="Cualquiera"
AdvSceneSwitcher.mediaTab.timeRestriction.none="Ninguno"
AdvSceneSwitcher.mediaTab.timeRestriction.shorter="Tiempo es más corto"
AdvSceneSwitcher.mediaTab.timeRestriction.longer="Tiempo es más largo"
AdvSceneSwitcher.mediaTab.timeRestriction.remainShorter="Tiempo que quede es más corto"
AdvSceneSwitcher.mediaTab.timeRestriction.remainLonger="Tiempo que quede es más largo"
AdvSceneSwitcher.mediaTab.entry="Cuando el estado de {{mediaSources}} es {{states}} y el {{timeRestrictions}} que {{time}} cambiar a {{scenes}} usando {{transitions}}"
AdvSceneSwitcher.mediaTab.help="Esta pestaña te permitirá cambiar de escena según los estados de las fuentes de medios.\nPor ejemplo, puede volver automáticamente a la escena anterior una vez que la fuente de medios seleccionada finalizó su reproducción.\n\nHaz clic en el símbolo más resaltado para continuar."
; File Tab
AdvSceneSwitcher.fileTab.title="Archivo"
AdvSceneSwitcher.fileTab.readWriteSceneFile="Leer / escribir escena desde / a un archivo"
AdvSceneSwitcher.fileTab.currentSceneOutputFile="Escribe el nombre de la escena actual en este archivo:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFile="Habilitar el cambio de escenas según la entrada de archivos"
AdvSceneSwitcher.fileTab.switchSceneNameInputFile="Leer el nombre de la escena a la que se cambiará desde este archivo:"
AdvSceneSwitcher.fileTab.switchSceneBaseOnFileContent="Cambiar de escena según el contenido del archivo"
AdvSceneSwitcher.fileTab.remoteFileWarning="Ten en cuenta que si eliges la opción remota, el selector de escenas intentará acceder a la ubicación remota cada x ms como se especifica en la pestaña General."
AdvSceneSwitcher.fileTab.remoteFileWarning1="Ten en cuenta que el selector de escenas intentará acceder a la ubicación remota cada "
AdvSceneSwitcher.fileTab.remoteFileWarning2="ms"
AdvSceneSwitcher.fileTab.libcurlWarning="¡Error al cargar libcurl! ¡No será posible acceder a archivos remotos!"
AdvSceneSwitcher.fileTab.selectWrite="Selecciona un archivo para escribir ..."
AdvSceneSwitcher.fileTab.selectRead="Seleccione un archivo para leer ..."
AdvSceneSwitcher.fileTab.textFileType="Archivos de texto (*.txt)"
AdvSceneSwitcher.fileTab.anyFileType="Cualquier archivo (*.*)"
AdvSceneSwitcher.fileTab.remote="archivo remoto"
AdvSceneSwitcher.fileTab.local="archivo local"
AdvSceneSwitcher.fileTab.useRegExp="usar expresiones regulares (coincidencia de patrones)"
AdvSceneSwitcher.fileTab.checkfileContentTime="si la fecha de modificación cambió"
AdvSceneSwitcher.fileTab.checkfileContent="si el contenido cambia"
AdvSceneSwitcher.fileTab.entry="Cambiar a {{scenes}} usando {{transitions}} si el contenido de {{fileType}} {{filePath}} {{browseButton}} coincide con:"
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
AdvSceneSwitcher.fileTab.help="Esta pestaña te permitirá cambiar escenas automáticamente según el contenido de archivos remotos o locales.\n\nHaz clic en el símbolo más resaltado para continuar."
; Random Tab
AdvSceneSwitcher.randomTab.title="Aleatorio"
AdvSceneSwitcher.randomTab.randomDisabledWarning="Funcionalidad deshabilitada - para activarla, selecciona \"Si no se cumple ninguna condición de cambio, cambiar a cualquier escena en la pestaña Aleatorio\" en la pestaña General"
AdvSceneSwitcher.randomTab.entry="Si no se cumple ninguna condición de cambio, cambiar a {{scenes}} usando {{transitions}} después de {{delay}}"
AdvSceneSwitcher.randomTab.help="El selector de escenas elegirá aleatoriamente una entrada en esta pestaña para cambiar durante el tiempo configurado.\nTen en cuenta que la misma entrada no se elegirá dos veces seguidas.\n\nHaz clic en el símbolo más resaltado para continuar."
; Time Tab
AdvSceneSwitcher.timeTab.title="Tiempo"
AdvSceneSwitcher.timeTab.anyDay="En cualquier día"
AdvSceneSwitcher.timeTab.mondays="Lunes"
AdvSceneSwitcher.timeTab.tuesdays="Martes"
AdvSceneSwitcher.timeTab.wednesdays="Miércoles"
AdvSceneSwitcher.timeTab.thursdays="Jueves"
AdvSceneSwitcher.timeTab.fridays="Viernes"
AdvSceneSwitcher.timeTab.saturdays="Sábados"
AdvSceneSwitcher.timeTab.sundays="Domingos"
AdvSceneSwitcher.timeTab.afterstart="Empezar al iniciar emisión/grabación"
AdvSceneSwitcher.timeTab.afterstart.tip="Se utilizará el tiempo relativo al inicio de la transmisión / grabación"
AdvSceneSwitcher.timeTab.entry="{{triggers}} a las {{time}} cambiar a {{scenes}} usando {{transitions}}"
AdvSceneSwitcher.timeTab.help="Esta pestaña te permitirá cambiar automáticamente a una escena diferente en función de la hora local actual.\n\nTen en cuenta que el selector de escenas solo cambiará de escena a la hora exacta que especificaste.\nAsegúrate de haber configurado los ajustes de prioridad en la pestaña General a tu gusto para que no se pierda el punto de tiempo seleccionado debido a que otros métodos de cambio tengan una prioridad más alta.\n\nHaz clic en el símbolo más resaltado para continuar."
; Idle Tab
AdvSceneSwitcher.idleTab.title="Inactividad"
AdvSceneSwitcher.idleTab.enable="Habilitar la detección de inactividad"
AdvSceneSwitcher.idleTab.idleswitch="Después de {{duration}} sin entradas de teclado o ratón, cambiar a la escena {{scenes}} usando la {{transitions}}"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus1="No cambiar si"
AdvSceneSwitcher.idleTab.dontSwitchIfFocus2="está en foco"
; Scene Sequence Tab
AdvSceneSwitcher.sceneSequenceTab.title="Secuencia"
AdvSceneSwitcher.sceneSequenceTab.description="Se puede cancelar una secuencia de cambios de escena automática pausando / deteniendo el selector de escenas o cambiando manualmente a una escena diferente"
AdvSceneSwitcher.sceneSequenceTab.save="Guardar secuencias de escenas en un archivo"
AdvSceneSwitcher.sceneSequenceTab.load="Cargar secuencias de escenas desde un archivo"
AdvSceneSwitcher.sceneSequenceTab.saveTitle="Guardar la secuencia de escenas en un archivo ..."
AdvSceneSwitcher.sceneSequenceTab.loadTitle="Seleccionar un archivo para leer la secuencia de escenas ..."
AdvSceneSwitcher.sceneSequenceTab.loadFail="¡Advanced Scene Switcher no ha podido cargar los ajustes!"
AdvSceneSwitcher.sceneSequenceTab.loadSuccess="¡Advanced Scene Switcher ha importando los ajustes correctamente!"
AdvSceneSwitcher.sceneSequenceTab.fileType="Archivos de texto (*.txt)"
AdvSceneSwitcher.sceneSequenceTab.interruptible="interrumpible"
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Se permiten otros métodos de cambio para interrumpir esta secuencia de escenas."
AdvSceneSwitcher.sceneSequenceTab.entry="Cuando {{startScenes}} esté activo, cambiar a {{scenes}} después de {{delay}} usando {{transitions}} {{interruptible}}"
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extender secuencia"
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Después de {{delay}} cambiar a {{scenes}} usando {{transitions}}"
AdvSceneSwitcher.sceneSequenceTab.help="Esta pestaña te permitirá cambiar automáticamente a una escena diferente si una escena estuvo activa durante un período de tiempo configurado.\nPor ejemplo, podrías alternar automáticamente entre dos escenas automáticamente.\n\nHaz clic en el símbolo más resaltado para seguir."
; Audio Tab
AdvSceneSwitcher.audioTab.title="Audio"
AdvSceneSwitcher.audioTab.condition.above="por encima del"
AdvSceneSwitcher.audioTab.condition.below="por debajo del"
AdvSceneSwitcher.audioTab.ignoreInactiveSource="a menos que la fuente esté inactiva"
AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Si múltiples entradas coinciden ..."
AdvSceneSwitcher.audioTab.multiMatchfallback="... durante {{duration}} cambiar a {{scenes}} usando {{transitions}}"
AdvSceneSwitcher.audioTab.help="Esta pestaña te permitirá cambiar de escena según el volumen de las fuentes.\nPor ejemplo, podrías cambiar automáticamente a una escena diferente si el volumen de su micrófono alcanza un cierto umbral.\n\nHaz clic en el símbolo más resaltado para continuar."
; Video Tab
AdvSceneSwitcher.videoTab.title="Vídeo"
AdvSceneSwitcher.videoTab.getScreenshot="Obtener captura de pantalla para la entrada seleccionada"
AdvSceneSwitcher.videoTab.getScreenshotHelp="Obtén una captura de pantalla de la fuente de vídeo de la entrada actualmente seleccionada y configúrala automáticamente como la imagen de destino"
AdvSceneSwitcher.videoTab.condition.match="coincide exactamente"
AdvSceneSwitcher.videoTab.condition.match.tooltip="Una coincidencia exacta requiere que el objetivo y la imagen de origen tengan la misma resolución.\nAdicionalmente, todos los píxeles deben coincidir, por lo que no se recomienda el uso de formatos de imagen que utilizan compresión (por ejemplo, .JPG)."
AdvSceneSwitcher.videoTab.condition.differ="no coincide"
AdvSceneSwitcher.videoTab.condition.hasNotChanged="no ha cambiado"
AdvSceneSwitcher.videoTab.condition.hasChanged="ha cambiado"
AdvSceneSwitcher.videoTab.ignoreInactiveSource="a menos que la fuente esté inactiva"
AdvSceneSwitcher.videoTab.entry="Cuando {{videoSources}} {{condition}} {{filePath}} {{browseButton}} durante {{duration}} cambiar a {{scenes}} usando {{transitions}} {{ignoreInactiveSource}}"
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Esta pestaña te permitirá cambiar escenas según la salida de vídeo actual de las fuentes seleccionadas.<br/>Asegúrate de revisar <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> para una implementación aún mejor de esta funcionalidad.<br/><br/> Haz clic en el símbolo más resaltado para continuar.</p></body></html>"
; Network Tab
AdvSceneSwitcher.networkTab.title="Red"
AdvSceneSwitcher.networkTab.description="Esta pestaña le permitirá controlar de forma remota la escena activa de otra instancia de OBS.\nTenga en cuenta que los nombres de las escenas deben coincidir exactamente en todas las instancias de OBS".
AdvSceneSwitcher.networkTab.warning="Ejecutar el servidor fuera de una red local permitirá que terceros lean la escena activa".
AdvSceneSwitcher.networkTab.server="Iniciar servidor (envía mensajes de cambio de escena a todos los clientes conectados)"
AdvSceneSwitcher.networkTab.server.port="Puerto"
AdvSceneSwitcher.networkTab.server.lockToIPv4="Bloquear servidor para usar solo IPv4"
AdvSceneSwitcher.networkTab.server.sendSceneChange="Enviar mensajes para cambios de escena"
AdvSceneSwitcher.networkTab.server.restrictSendToAutomatedSwitches="Solo enviar mensajes para cambios de escena automatizados"
AdvSceneSwitcher.networkTab.server.sendPreview="Enviar mensajes para obtener una vista previa del cambio de escena cuando se ejecuta en Modo Estudio"
AdvSceneSwitcher.networkTab.startFailed.message="El servidor WebSockets no pudo iniciarse, tal vez porque:\n - El puerto TCP %1 puede estar actualmente en uso en otro lugar de este sistema, posiblemente por otra aplicación. Intente configurar un puerto TCP diferente en el WebSocket configuración del servidor o detenga cualquier aplicación que pueda estar usando este puerto.\n - Mensaje de error: %2"
AdvSceneSwitcher.networkTab.server.status.currentStatus="Estado actual"
AdvSceneSwitcher.networkTab.server.status.notRunning="Desconectado"
AdvSceneSwitcher.networkTab.server.status.starting="Iniciando"
AdvSceneSwitcher.networkTab.server.status.running="En ejecución"
AdvSceneSwitcher.networkTab.server.restart="Reiniciar servidor"
AdvSceneSwitcher.networkTab.client="Iniciar cliente (Recibe mensajes de cambios de escena)"
AdvSceneSwitcher.networkTab.client.address="Nombre de host o dirección IP"
AdvSceneSwitcher.networkTab.client.port="Puerto"
AdvSceneSwitcher.networkTab.client.status.currentStatus="Estado actual"
AdvSceneSwitcher.networkTab.client.status.disconnected="Desconectado"
AdvSceneSwitcher.networkTab.client.status.connecting="Conectando"
AdvSceneSwitcher.networkTab.client.status.connected="Conectado"
AdvSceneSwitcher.networkTab.client.reconnect="Forzar reconexión"
; Scene Group Tab
AdvSceneSwitcher.sceneGroupTab.title="Grupo de escenas"
AdvSceneSwitcher.sceneGroupTab.list="Grupos de escenas"
AdvSceneSwitcher.sceneGroupTab.edit="Editar grupos de escenas"
AdvSceneSwitcher.sceneGroupTab.edit.name="Nombre:"
AdvSceneSwitcher.sceneGroupTab.edit.type="Tipo: {{type}}"
AdvSceneSwitcher.sceneGroupTab.type.count="Contar"
AdvSceneSwitcher.sceneGroupTab.type.time="Tiempo"
AdvSceneSwitcher.sceneGroupTab.type.random="Al azar"
AdvSceneSwitcher.sceneGroupTab.edit.count="Avanzar a la siguiente escena en la lista después de {{count}} coincidencias"
AdvSceneSwitcher.sceneGroupTab.edit.time="Avanzar a la siguiente escena de la lista después de que haya pasado {{time}}"
AdvSceneSwitcher.sceneGroupTab.edit.random="Elige la siguiente escena en la lista al azar"
AdvSceneSwitcher.sceneGroupTab.edit.repeat="Empezar desde el principio si se llega al final de la lista de escenas"
AdvSceneSwitcher.sceneGroupTab.edit.addScene="Agregar escena"
AdvSceneSwitcher.sceneGroupTab.add="Agregar grupo de escenas"
AdvSceneSwitcher.sceneGroupTab.defaultname="Grupo de escenas %1"
AdvSceneSwitcher.sceneGroupTab.exists="El grupo de escenas o el nombre de la escena ya existe"
AdvSceneSwitcher.sceneGroupTab.help="Los grupos de escenas se pueden seleccionar como un objetivo al igual que una escena normal.\n\nComo sugiere el nombre, un grupo de escenas es una colección de varias escenas.\nEl grupo de escenas avanzará a través de la lista de sus escenas asignadas según los ajustes configurados, que se puede encontrar en el lado derecho.\n\nPuedes configurar el grupo de escenas para avanzar a la siguiente escena en la lista:\nDespués de varias veces, el grupo de escenas se selecciona como objetivo.\nDespués de que un cierto período de tiempo haya pasado.\nO al azar.\n\nPor ejemplo, un grupo de escenas que contiene las escenas ... \nEscena 1 \nEscena 2 \nEscena 3 \n ... activará la \"Escena 1 \" la primera vez que se seleccione como objetivo. \nLa segunda vez se activará \"Escena 2 \". \nLas veces restantes \"Escena 3 \" se activarán. \n\nHaz clic en el símbolo más resaltado a continuación para agregar un nuevo grupo de escenas."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Selecciona el grupo de escenas que deseas modificar a la izquierda. \n\nSelecciona una escena para agregar a este grupo de escenas seleccionando la escena de arriba y haciendo clic en el símbolo más a continuación. \n\nSe puede agregar una escena varias veces al mismo grupo de escenas."
; Scene Trigger Tab
AdvSceneSwitcher.sceneTriggerTab.title="Activadores de escena"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--selecciona el activador--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="esté activo"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="no esté activo"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="cambie a otra escena"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--Selecciona la acción--"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="empezar a grabar"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="pausar la grabación"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="reanudar grabación"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="detener grabación"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="detener transmisión"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="iniciar transmisión"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="iniciar búfer de repetición"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="detener búfer de repetición"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="silenciar fuente"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="dejar de silenciar fuente"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="iniciar el selector de escenas"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="detener el selector de escenas"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startVirtualCamera="Iniciar Camara Virtual"
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopVirtualCamera="Detener Camara Virtual"
AdvSceneSwitcher.sceneTriggerTab.entry="Cuando {{scenes}} {{triggers}} {{actions}} {{audioSources}} después de {{duration}}"
AdvSceneSwitcher.sceneTriggerTab.help="Esta pestaña te permite activar acciones sobre cambios de escena, como detener la grabación o la transmisión."
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Iniciar Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Detener Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.startStopToggleSwitcherHotkey="Alternar inicio / detención para Advanced Scene Switcher"
AdvSceneSwitcher.hotkey.macro.pause="Pausar macro %1"
AdvSceneSwitcher.hotkey.macro.unpause="Despausar macro %1"
AdvSceneSwitcher.hotkey.macro.togglePause="Alternar pausa de macro %1"
AdvSceneSwitcher.hotkey.upMacroSegmentHotkey="Mover selección de segmento de macro hacia arriba"
AdvSceneSwitcher.hotkey.downMacroSegmentHotkey="Mover selección de segmento de macro hacia abajo"
AdvSceneSwitcher.hotkey.removeMacroSegmentHotkey="Eliminar segmento de macro seleccionado"
AdvSceneSwitcher.askBackup="Se detectó una nueva versión de Advanced Scene Switcher.\n¿Crear una copia de seguridad de la configuración anterior?"
AdvSceneSwitcher.askForMacro="Select macro {{macroSelection}}"
AdvSceneSwitcher.close="Cerrar"
AdvSceneSwitcher.browse="Explorar"
AdvSceneSwitcher.selectScene="--seleccionar escena--"
AdvSceneSwitcher.selectPreviousScene="Escena anterior"
AdvSceneSwitcher.selectCurrentScene="Escena actual"
AdvSceneSwitcher.selectAnyScene="Cualquier escena"
AdvSceneSwitcher.currentTransition="Transición actual"
AdvSceneSwitcher.anyTransition="Cualquier transición"
AdvSceneSwitcher.selectTransition="--seleccionar transición--"
AdvSceneSwitcher.selectWindow="--seleccionar ventana--"
AdvSceneSwitcher.selectSource="--seleccionar fuente--"
AdvSceneSwitcher.selectAudioSource="--seleccione la fuente de audio--"
AdvSceneSwitcher.selectVideoSource="--seleccione la fuente de video--"
AdvSceneSwitcher.OBSVideoOutput="Salida de vídeo OBS"
AdvSceneSwitcher.selectMediaSource="--seleccione la fuente de medios--"
AdvSceneSwitcher.selectProcess="--seleccionar proceso--"
AdvSceneSwitcher.selectFilter="--seleccionar filtro--"
AdvSceneSwitcher.selectMacro="--seleccionar macro--"
AdvSceneSwitcher.selectItem="--seleccionar elemento--"
AdvSceneSwitcher.selectProfile="--seleccionar perfil--"
AdvSceneSwitcher.selectSceneCollection="--seleccionar colección de escenas--"
AdvSceneSwitcher.enterPath="--ingrese la ruta--"
AdvSceneSwitcher.enterText="--ingresar texto--"
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="las entradas no válidas no se guardarán"
AdvSceneSwitcher.selectWindowTip="Usa \"OBS \" para especificar la ventana de OBS\nUsa \"Cambiar de tarea \" para especificar ALT + TAB"
AdvSceneSwitcher.sceneItemSelection.all="Todos"
AdvSceneSwitcher.sceneItemSelection.any="Cualquiera"
AdvSceneSwitcher.status.active="Activo"
AdvSceneSwitcher.status.inactive="Inactivo"
AdvSceneSwitcher.running="Iniciar complemento"
AdvSceneSwitcher.stopped="Complemento de detención"
AdvSceneSwitcher.firstBootMessage="<html><head/><body><p>Esta parece ser la primera vez que se inicia el conmutador de escena avanzado.<br>Por favor, eche un vistazo a <a href=\"https:/ /github.com/WarmUpTill/SceneSwitcher/wiki\"><span style=\" text-decoration: underline; color:#268bd2;\">Wiki</span></a> para obtener una lista de guías y ejemplos.<br>No dude en hacer preguntas en el complemento <a href=\"https://obsproject.com /forum/threads/advanced-scene-switcher.48264\"><span style=\" text-decoration: underline; color:#268bd2;\">hilo</span></a> en los foros de OBS!</p></body></html>"
AdvSceneSwitcher.deprecatedTabWarning="¡Se detuvo el desarrollo de esta pestaña!\nConsidere cambiar a macros en su lugar.\nEsta sugerencia se puede desactivar en la pestaña General".
AdvSceneSwitcher.unit.milliseconds="milisegundos"
AdvSceneSwitcher.unit.secends="segundos"
AdvSceneSwitcher.unit.minutes="minutos"
AdvSceneSwitcher.unit.hours="horas"
AdvSceneSwitcher.duration.condition.none="Sin límite de tiempo"
AdvSceneSwitcher.duration.condition.more="Al menos durante"
AdvSceneSwitcher.duration.condition.equal="Para exactamente"
AdvSceneSwitcher.duration.condition.less="Para como máximo"
;Traducido por: @EliasDipa

View File

@@ -369,8 +369,8 @@ AdvSceneSwitcher.action.previewScene.entry="Önizleme sahnesini şu şekilde de
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.transition.entry.line2="{{setTransition}}Geçiş türünü ayarla {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line3="{{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"

View File

@@ -251,7 +251,6 @@ AdvSceneSwitcher.condition.date.ignoreDate="如果未选中,日期组件将被
AdvSceneSwitcher.condition.date.ignoreTime="如果未选中,时间组件将被忽略"
AdvSceneSwitcher.condition.date.showAdvancedSettings="显示高级设置"
AdvSceneSwitcher.condition.date.showSimpleSettings="显示简单设置"
AdvSceneSwitcher.condition.date.entry.simple="在 {{dayOfWeek}} 的 {{weekTime}}"
AdvSceneSwitcher.condition.date.entry.advanced="{{condition}} {{ignoreDate}}{{date}} {{ignoreTime}}{{time}} {{separator}} {{date2}} {{time2}}"
AdvSceneSwitcher.condition.date.entry.repeat="{{repeat}} 在匹配到日期时间后,每隔 {{duration}} 重复一次"
AdvSceneSwitcher.condition.sceneTransform="场景项目被改变"
@@ -412,8 +411,8 @@ AdvSceneSwitcher.action.previewScene.entry="将预览场景切换到 {{scenes}}"
AdvSceneSwitcher.action.SceneSwap="交换场景 (Studio mode)"
AdvSceneSwitcher.action.SceneSwap.entry="在studio模式下交换预览和编程场景"
AdvSceneSwitcher.action.transition="过场特效"
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}将过场特效类型设置为 {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}将过场特效持续时间设置为 {{duration}}seconds"
AdvSceneSwitcher.action.transition.entry.line2="{{setTransition}}将过场特效类型设置为 {{transitions}}"
AdvSceneSwitcher.action.transition.entry.line3="{{setDuration}}将过场特效持续时间设置为 {{duration}}seconds"
AdvSceneSwitcher.action.timer="计时器"
AdvSceneSwitcher.action.timer.type.pause="暂停"
AdvSceneSwitcher.action.timer.type.continue="继续"

View File

@@ -460,6 +460,19 @@ bool SwitcherData::sceneChangedDuringWait()
return (waitScene && currentSource != waitScene);
}
// Relies on the fact that switcher->currentScene will only be updated on event
// OBS_FRONTEND_EVENT_SCENE_CHANGED but obs_frontend_get_current_scene() will
// already return the scene to be transitioned to.
bool SwitcherData::anySceneTransitionStarted()
{
auto currentSceneSrouce = obs_frontend_get_current_scene();
auto currentScene = obs_source_get_weak_source(currentSceneSrouce);
bool ret = switcher->currentScene != currentScene;
obs_weak_source_release(currentScene);
obs_source_release(currentSceneSrouce);
return ret;
}
/******************************************************************************
* OBS module setup
******************************************************************************/

View File

@@ -23,7 +23,7 @@ void Duration::Load(obs_data_t *obj, const char *secondsName,
bool Duration::DurationReached()
{
if (_startTime.time_since_epoch().count() == 0) {
if (IsReset()) {
_startTime = std::chrono::high_resolution_clock::now();
}
@@ -32,9 +32,14 @@ bool Duration::DurationReached()
return runTime.count() >= seconds * 1000;
}
bool Duration::IsReset()
{
return _startTime.time_since_epoch().count() == 0;
}
double Duration::TimeRemaining()
{
if (_startTime.time_since_epoch().count() == 0) {
if (IsReset()) {
return seconds;
}
auto runTime = std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -161,131 +166,3 @@ void DurationSelection::_UnitChanged(int idx)
emit UnitChanged(unit);
}
void DurationConstraint::Save(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
obs_data_set_int(obj, condName, static_cast<int>(_type));
_dur.Save(obj, secondsName, unitName);
}
void DurationConstraint::Load(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
// For backwards compatability check if duration value exist without
// time constraint condition - if so assume DurationCondition::MORE
if (!obs_data_has_user_value(obj, condName) &&
obs_data_has_user_value(obj, secondsName)) {
obs_data_set_int(obj, condName,
static_cast<int>(DurationCondition::MORE));
}
_type = static_cast<DurationCondition>(obs_data_get_int(obj, condName));
_dur.Load(obj, secondsName, unitName);
}
bool DurationConstraint::DurationReached()
{
switch (_type) {
case DurationCondition::NONE:
return true;
break;
case DurationCondition::MORE:
return _dur.DurationReached();
break;
case DurationCondition::EQUAL:
if (_dur.DurationReached() && !_timeReached) {
_timeReached = true;
return true;
}
break;
case DurationCondition::LESS:
return !_dur.DurationReached();
break;
default:
break;
}
return false;
}
void DurationConstraint::Reset()
{
_timeReached = false;
_dur.Reset();
}
static void populateConditions(QComboBox *list)
{
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.none"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.more"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.equal"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.less"));
}
DurationConstraintEdit::DurationConstraintEdit(QWidget *parent)
{
_condition = new QComboBox(parent);
_duration = new DurationSelection(parent);
_toggle = new QPushButton(parent);
_toggle->setMaximumSize(22, 22);
_toggle->setIcon(
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
populateConditions(_condition);
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(_ConditionChanged(int)));
QObject::connect(_duration, &DurationSelection::DurationChanged, this,
&DurationConstraintEdit::DurationChanged);
QObject::connect(_duration, &DurationSelection::UnitChanged, this,
&DurationConstraintEdit::UnitChanged);
QWidget::connect(_toggle, SIGNAL(clicked()), this,
SLOT(ToggleClicked()));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_toggle);
layout->addWidget(_condition);
layout->addWidget(_duration);
setLayout(layout);
Collapse(true);
}
void DurationConstraintEdit::SetValue(DurationConstraint &value)
{
_duration->SetDuration(value.GetDuration());
_condition->setCurrentIndex(static_cast<int>(value.GetCondition()));
_duration->setVisible(value.GetCondition() != DurationCondition::NONE);
}
void DurationConstraintEdit::SetUnit(DurationUnit u)
{
_duration->SetUnit(u);
}
void DurationConstraintEdit::SetDuration(const Duration &d)
{
_duration->SetDuration(d);
}
void DurationConstraintEdit::_ConditionChanged(int value)
{
auto cond = static_cast<DurationCondition>(value);
Collapse(cond == DurationCondition::NONE);
emit ConditionChanged(cond);
}
void DurationConstraintEdit::ToggleClicked()
{
Collapse(false);
}
void DurationConstraintEdit::Collapse(bool collapse)
{
_toggle->setVisible(collapse);
_duration->setVisible(!collapse);
_condition->setVisible(!collapse);
}

View File

@@ -3,22 +3,26 @@
ThresholdSlider::ThresholdSlider(double min, double max, const QString &label,
const QString &description, QWidget *parent)
: QWidget(parent)
: QWidget(parent),
_spinBox(new QDoubleSpinBox()),
_slider(new QSlider())
{
_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);
_spinBox->setMinimum(min);
_spinBox->setMaximum(max);
_spinBox->setDecimals(5);
connect(_slider, SIGNAL(valueChanged(int)), this,
SLOT(NotifyValueChanged(int)));
SLOT(SliderValueChanged(int)));
connect(_spinBox, SIGNAL(valueChanged(double)), this,
SLOT(SpinBoxValueChanged(double)));
QVBoxLayout *mainLayout = new QVBoxLayout();
QHBoxLayout *sliderLayout = new QHBoxLayout();
sliderLayout->addWidget(_value);
if (!label.isEmpty()) {
sliderLayout->addWidget(new QLabel(label));
}
sliderLayout->addWidget(_spinBox);
sliderLayout->addWidget(_slider);
mainLayout->addLayout(sliderLayout);
if (!description.isEmpty()) {
@@ -30,21 +34,21 @@ ThresholdSlider::ThresholdSlider(double min, double max, const QString &label,
void ThresholdSlider::SetDoubleValue(double value)
{
const QSignalBlocker b1(_slider);
const QSignalBlocker b2(_spinBox);
_slider->setValue(value * _scale);
SetDoubleValueText(value);
_spinBox->setValue(value);
}
void ThresholdSlider::NotifyValueChanged(int value)
void ThresholdSlider::SpinBoxValueChanged(double value)
{
int sliderPos = value * _scale;
_slider->setValue(sliderPos);
emit DoubleValueChanged(value);
}
void ThresholdSlider::SliderValueChanged(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);
_spinBox->setValue(doubleValue);
}

View File

@@ -2,6 +2,7 @@
#include <QWidget>
#include <QSlider>
#include <QLabel>
#include <QDoubleSpinBox>
class ThresholdSlider : public QWidget {
Q_OBJECT
@@ -12,14 +13,13 @@ public:
const QString &description = "", QWidget *parent = 0);
void SetDoubleValue(double);
public slots:
void NotifyValueChanged(int value);
void SliderValueChanged(int value);
void SpinBoxValueChanged(double value);
signals:
void DoubleValueChanged(double value);
private:
void SetDoubleValueText(double);
QLabel *_value;
QDoubleSpinBox *_spinBox;
QSlider *_slider;
double _scale = 100.0;
int _precision = 2;
};

View File

@@ -21,6 +21,7 @@ public:
const char *unitName = "displayUnit");
bool DurationReached();
bool IsReset();
double TimeRemaining();
void SetTimeRemaining(double);
void Reset();
@@ -57,57 +58,3 @@ private:
double _unitMultiplier;
};
enum class DurationCondition {
NONE,
MORE,
EQUAL,
LESS,
};
class DurationConstraint {
public:
void Save(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void Load(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void SetCondition(DurationCondition cond) { _type = cond; }
void SetDuration(const Duration &dur) { _dur = dur; }
void SetValue(double value) { _dur.seconds = value; }
void SetUnit(DurationUnit u) { _dur.displayUnit = u; }
DurationCondition GetCondition() { return _type; }
Duration GetDuration() { return _dur; }
bool DurationReached();
void Reset();
private:
DurationCondition _type = DurationCondition::NONE;
Duration _dur;
bool _timeReached = false;
};
class DurationConstraintEdit : public QWidget {
Q_OBJECT
public:
DurationConstraintEdit(QWidget *parent = nullptr);
void SetValue(DurationConstraint &value);
void SetUnit(DurationUnit u);
void SetDuration(const Duration &d);
private slots:
void _ConditionChanged(int value);
void ToggleClicked();
signals:
void DurationChanged(double value);
void UnitChanged(DurationUnit u);
void ConditionChanged(DurationCondition value);
private:
void Collapse(bool collapse);
DurationSelection *_duration;
QComboBox *_condition;
QPushButton *_toggle;
};

View File

@@ -1,5 +1,8 @@
#pragma once
#include "macro.hpp"
#include "macro-action.hpp"
#include <QComboBox>
#include <memory>
struct MacroActionInfo {
using TCreateMethod = std::shared_ptr<MacroAction> (*)(Macro *m);

View File

@@ -2,7 +2,9 @@
#include "macro-action-edit.hpp"
#include "resizing-text-edit.hpp"
#include <QComboBox>
#include <QSpinBox>
#include <QPushButton>
enum class FilterAction {
ENABLE,

View File

@@ -2,6 +2,7 @@
#include "macro-action-edit.hpp"
#include "hotkey.hpp"
#include <QComboBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QHBoxLayout>

View File

@@ -1,6 +1,8 @@
#pragma once
#include "macro-action-edit.hpp"
#include <QComboBox>
class MacroActionProfile : public MacroAction {
public:
MacroActionProfile(Macro *m) : MacroAction(m) {}

View File

@@ -28,14 +28,6 @@ public:
ReplayBufferAction _action = ReplayBufferAction::STOP;
private:
// Add artifical delay before trying to save the replay buffer again.
//
// Continiously calling obs_frontend_replay_buffer_save() does not
// result in any output actually being written.
// OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED also seems to be sent before
// any data is written.
Duration _duration;
static bool _registered;
static const std::string id;
};
@@ -62,10 +54,10 @@ private slots:
protected:
QComboBox *_actions;
QLabel *_saveWarning;
std::shared_ptr<MacroActionReplayBuffer> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -1,6 +1,8 @@
#pragma once
#include "macro-action-edit.hpp"
#include <QComboBox>
class MacroActionSceneCollection : public MacroAction {
public:
MacroActionSceneCollection(Macro *m) : MacroAction(m) {}

View File

@@ -1,39 +0,0 @@
#pragma once
#include "macro-action-edit.hpp"
class MacroActionSceneSwap : public MacroAction {
public:
MacroActionSceneSwap(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionSceneSwap>(m);
}
private:
static bool _registered;
static const std::string id;
};
class MacroActionSceneSwapEdit : public QWidget {
Q_OBJECT
public:
MacroActionSceneSwapEdit(
QWidget *parent,
std::shared_ptr<MacroActionSceneSwap> entryData = nullptr);
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionSceneSwapEdit(
parent, std::dynamic_pointer_cast<MacroActionSceneSwap>(
action));
}
protected:
std::shared_ptr<MacroActionSceneSwap> _entryData;
};

View File

@@ -1,6 +1,8 @@
#pragma once
#include "macro-action-edit.hpp"
#include <QComboBox>
class MacroActionScreenshot : public MacroAction {
public:
MacroActionScreenshot(Macro *m) : MacroAction(m) {}

View File

@@ -5,6 +5,7 @@
#include <QSpinBox>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
enum class SourceAction {
ENABLE,

View File

@@ -2,9 +2,16 @@
#include "macro-action-edit.hpp"
#include "scene-selection.hpp"
class MacroActionPreviewScene : public MacroAction {
enum class StudioModeAction {
SWAP_SCENE,
SET_SCENE,
ENABLE_STUDIO_MODE,
DISABLE_STUDIO_MODE,
};
class MacroActionSudioMode : public MacroAction {
public:
MacroActionPreviewScene(Macro *m) : MacroAction(m) {}
MacroActionSudioMode(Macro *m) : MacroAction(m) {}
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
@@ -13,9 +20,10 @@ public:
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionPreviewScene>(m);
return std::make_shared<MacroActionSudioMode>(m);
}
StudioModeAction _action = StudioModeAction::SWAP_SCENE;
SceneSelection _scene;
private:
@@ -23,33 +31,33 @@ private:
static const std::string id;
};
class MacroActionPreviewSceneEdit : public QWidget {
class MacroActionSudioModeEdit : public QWidget {
Q_OBJECT
public:
MacroActionPreviewSceneEdit(
MacroActionSudioModeEdit(
QWidget *parent,
std::shared_ptr<MacroActionPreviewScene> entryData = nullptr);
std::shared_ptr<MacroActionSudioMode> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionPreviewSceneEdit(
parent,
std::dynamic_pointer_cast<MacroActionPreviewScene>(
action));
return new MacroActionSudioModeEdit(
parent, std::dynamic_pointer_cast<MacroActionSudioMode>(
action));
}
private slots:
void ActionChanged(int value);
void SceneChanged(const SceneSelection &);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_actions;
SceneSelectionWidget *_scenes;
std::shared_ptr<MacroActionPreviewScene> _entryData;
std::shared_ptr<MacroActionSudioMode> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -2,6 +2,8 @@
#include "macro-action-edit.hpp"
#include "duration-control.hpp"
#include "transition-selection.hpp"
#include "scene-selection.hpp"
#include "scene-item-selection.hpp"
#include <QCheckBox>
#include <QHBoxLayout>
@@ -20,12 +22,26 @@ public:
return std::make_shared<MacroActionTransition>(m);
}
enum class Type {
SCENE,
SCENE_OVERRIDE,
SOURCE_SHOW,
SOURCE_HIDE,
};
Type _type = Type::SCENE;
SceneItemSelection _source;
SceneSelection _scene;
bool _setDuration = true;
bool _setType = true;
bool _setTransitionType = true;
TransitionSelection _transition;
Duration _duration;
private:
void SetSceneTransition();
void SetTransitionOverride();
void SetSourceTransition(bool);
static bool _registered;
static const std::string id;
};
@@ -48,7 +64,10 @@ public:
}
private slots:
void SetTypeChanged(int state);
void ActionChanged(int value);
void SourceChanged(const SceneItemSelection &);
void SceneChanged(const SceneSelection &);
void SetTransitionChanged(int state);
void SetDurationChanged(int state);
void TransitionChanged(const TransitionSelection &);
void DurationChanged(double seconds);
@@ -56,14 +75,19 @@ signals:
void HeaderInfoChanged(const QString &);
protected:
QCheckBox *_setType;
QComboBox *_actions;
SceneItemSelectionWidget *_sources;
SceneSelectionWidget *_scenes;
QCheckBox *_setTransition;
QCheckBox *_setDuration;
TransitionSelectionWidget *_transitions;
DurationSelection *_duration;
QHBoxLayout *_typeLayout;
QHBoxLayout *_transitionLayout;
QHBoxLayout *_durationLayout;
std::shared_ptr<MacroActionTransition> _entryData;
private:
void SetWidgetVisibility();
bool _loading = true;
};

View File

@@ -0,0 +1,26 @@
#pragma once
#include "macro-segment.hpp"
#include "macro-ref.hpp"
class MacroAction : public MacroSegment {
public:
MacroAction(Macro *m) : MacroSegment(m) {}
virtual bool PerformAction() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual void LogAction();
};
class MacroRefAction : public MacroAction {
public:
MacroRefAction(Macro *m) : MacroAction(m) {}
void ResolveMacroRef();
MacroRef _macro;
};
class MultiMacroRefAction : public MacroAction {
public:
MultiMacroRefAction(Macro *m) : MacroAction(m) {}
void ResolveMacroRef();
std::vector<MacroRef> _macros;
};

View File

@@ -5,6 +5,7 @@
#include <QCheckBox>
#include <QDateTimeEdit>
#include <QComboBox>
#include <QTimer>
enum class DateCondition {
AT,
@@ -37,12 +38,19 @@ public:
return std::make_shared<MacroConditionDate>(m);
}
void SetDate1(const QDate &date);
void SetDate2(const QDate &date);
void SetTime1(const QTime &time);
void SetTime2(const QTime &time);
QDateTime GetDateTime1();
QDateTime GetDateTime2();
QDateTime GetNextMatchDateTime();
DayOfWeekSelection _dayOfWeek = DayOfWeekSelection::ANY;
QDateTime _dateTime = QDateTime::currentDateTime();
QDateTime _dateTime2 = QDateTime::currentDateTime();
bool _ignoreDate = false;
bool _ignoreTime = false;
bool _repeat = false;
bool _updateOnRepeat = true;
Duration _duration;
DateCondition _condition = DateCondition::AT;
bool _dayOfWeekCheck = false;
@@ -51,6 +59,12 @@ private:
bool CheckDayOfWeek(int64_t);
bool CheckRegularDate(int64_t);
QDateTime _dateTime = QDateTime::currentDateTime();
QDateTime _dateTime2 = QDateTime::currentDateTime();
// Used depending whether or not _updateOnRepeat is set
QDateTime _origDateTime = QDateTime::currentDateTime();
QDateTime _origDateTime2 = QDateTime::currentDateTime();
static bool _registered;
static const std::string id;
};
@@ -81,14 +95,18 @@ private slots:
void IgnoreDateChanged(int state);
void IgnoreTimeChanged(int state);
void RepeatChanged(int state);
void UpdateOnRepeatChanged(int state);
void DurationChanged(double seconds);
void DurationUnitChanged(DurationUnit unit);
void AdvancedSettingsToggleClicked();
void ShowNextMatch();
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_weekCondition;
QComboBox *_dayOfWeek;
QCheckBox *_ignoreWeekTime;
QTimeEdit *_weekTime;
QComboBox *_condition;
@@ -100,17 +118,21 @@ protected:
QCheckBox *_ignoreDate;
QCheckBox *_ignoreTime;
QCheckBox *_repeat;
QLabel *_nextMatchDate;
QCheckBox *_updateOnRepeat;
DurationSelection *_duration;
QPushButton *_advancedSettingsTooggle;
QHBoxLayout *_simpleLayout;
QHBoxLayout *_advancedLayout;
QHBoxLayout *_repeatLayout;
QVBoxLayout *_repeatLayout;
QHBoxLayout *_repeatUpdateLayout;
std::shared_ptr<MacroConditionDate> _entryData;
private:
void SetWidgetStatus();
void ShowSecondDateSelection(bool visible);
QTimer _timer;
bool _loading = true;
};

View File

@@ -1,5 +1,7 @@
#pragma once
#include "macro.hpp"
#include "macro-condition.hpp"
#include <memory>
struct MacroConditionInfo {
using TCreateMethod = std::shared_ptr<MacroCondition> (*)(Macro *m);
@@ -8,7 +10,7 @@ struct MacroConditionInfo {
TCreateMethod _createFunc = nullptr;
TCreateWidgetMethod _createWidgetFunc = nullptr;
std::string _name;
bool _useDurationConstraint = true;
bool _useDurationModifier = true;
};
class MacroConditionFactory {
@@ -22,12 +24,36 @@ public:
static auto GetConditionTypes() { return _methods; }
static std::string GetConditionName(const std::string &);
static std::string GetIdByName(const QString &name);
static bool UsesDurationConstraint(const std::string &id);
static bool UsesDurationModifier(const std::string &id);
private:
static std::map<std::string, MacroConditionInfo> _methods;
};
class DurationModifierEdit : public QWidget {
Q_OBJECT
public:
DurationModifierEdit(QWidget *parent = nullptr);
void SetValue(DurationModifier &value);
void SetUnit(DurationUnit u);
void SetDuration(const Duration &d);
private slots:
void _ModifierChanged(int value);
void ToggleClicked();
signals:
void DurationChanged(double value);
void UnitChanged(DurationUnit u);
void ModifierChanged(DurationModifier::Type value);
private:
void Collapse(bool collapse);
DurationSelection *_duration;
QComboBox *_condition;
QPushButton *_toggle;
};
class MacroConditionEdit : public MacroSegmentEdit {
Q_OBJECT
@@ -44,7 +70,7 @@ private slots:
void LogicSelectionChanged(int idx);
void ConditionSelectionChanged(const QString &text);
void DurationChanged(double seconds);
void DurationConditionChanged(DurationCondition cond);
void DurationModifierChanged(DurationModifier::Type m);
void DurationUnitChanged(DurationUnit unit);
private:
@@ -53,7 +79,7 @@ private:
QComboBox *_logicSelection;
QComboBox *_conditionSelection;
DurationConstraintEdit *_dur;
DurationModifierEdit *_dur;
std::shared_ptr<MacroCondition> *_entryData;
bool _isRoot = true;

View File

@@ -0,0 +1,88 @@
#pragma once
#include "macro-segment.hpp"
#include "macro-ref.hpp"
#include "duration-control.hpp"
constexpr auto logic_root_offset = 100;
enum class LogicType {
ROOT_NONE = 0,
ROOT_NOT,
ROOT_LAST,
// leave some space for potential expansion
NONE = 100,
AND,
OR,
AND_NOT,
OR_NOT,
LAST,
};
static inline bool isRootLogicType(LogicType l)
{
return static_cast<int>(l) < logic_root_offset;
}
struct LogicTypeInfo {
std::string _name;
};
class DurationModifier {
public:
enum class Type {
NONE,
MORE,
EQUAL,
LESS,
WITHIN, // Condition will remain true for a set a amount of time
// regardless of current state
};
void Save(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void Load(obs_data_t *obj, const char *condName = "time_constraint",
const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void SetModifier(Type cond) { _type = cond; }
void SetTimeRemaining(const double &val) { _dur.SetTimeRemaining(val); }
void SetValue(double value) { _dur.seconds = value; }
void SetUnit(DurationUnit u) { _dur.displayUnit = u; }
Type GetType() { return _type; }
Duration GetDuration() { return _dur; }
bool DurationReached();
void Reset();
private:
Type _type = Type::NONE;
Duration _dur;
bool _timeReached = false;
};
class MacroCondition : public MacroSegment {
public:
MacroCondition(Macro *m) : MacroSegment(m) {}
virtual bool CheckCondition() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
LogicType GetLogicType() { return _logic; }
void SetLogicType(LogicType logic) { _logic = logic; }
static const std::map<LogicType, LogicTypeInfo> logicTypes;
void ResetDuration();
void CheckDurationModifier(bool &val);
DurationModifier GetDurationModifier() { return _duration; }
void SetDurationModifier(DurationModifier::Type m);
void SetDurationUnit(DurationUnit u);
void SetDuration(double seconds);
private:
LogicType _logic = LogicType::ROOT_NONE;
DurationModifier _duration;
};
class MacroRefCondition : public MacroCondition {
public:
MacroRefCondition(Macro *m) : MacroCondition(m) {}
void ResolveMacroRef();
MacroRef _macro;
};

View File

@@ -1,10 +1,12 @@
#pragma once
#include "macro.hpp"
#include <QWidget>
#include <QDialog>
#include <QCheckBox>
#include <obs-data.h>
// Global macro properties
class MacroProperties {
public:
void Save(obs_data_t *obj);
@@ -15,15 +17,19 @@ public:
bool _highlightActions = false;
};
// Dialog for configuring global and macro specific settings
class MacroPropertiesDialog : public QDialog {
Q_OBJECT
public:
MacroPropertiesDialog(QWidget *parent, const MacroProperties &);
static bool AskForSettings(QWidget *parent, MacroProperties &userInput);
MacroPropertiesDialog(QWidget *parent, const MacroProperties &,
Macro *macro);
static bool AskForSettings(QWidget *parent, MacroProperties &userInput,
Macro *macro);
private:
QCheckBox *_executed;
QCheckBox *_conditions;
QCheckBox *_actions;
QCheckBox *_hotkeys;
};

23
src/headers/macro-ref.hpp Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <QString>
#include <obs-module.h>
class Macro;
class MacroRef {
public:
MacroRef(){};
MacroRef(std::string name);
void UpdateRef();
void UpdateRef(std::string name);
void UpdateRef(QString name);
void Save(obs_data_t *obj);
void Load(obs_data_t *obj);
Macro *get();
Macro *operator->();
private:
std::string _name = "";
Macro *_ref = nullptr;
};

View File

@@ -1,6 +1,7 @@
#pragma once
#include "macro-segment.hpp"
#include "duration-control.hpp"
#include "macro-action.hpp"
#include "macro-condition.hpp"
#include "macro-ref.hpp"
#include <QString>
#include <string>
@@ -13,62 +14,6 @@
constexpr auto macro_func = 10;
constexpr auto logic_root_offset = 100;
enum class LogicType {
ROOT_NONE = 0,
ROOT_NOT,
ROOT_LAST,
// leave some space for potential expansion
NONE = 100,
AND,
OR,
AND_NOT,
OR_NOT,
LAST,
};
static inline bool isRootLogicType(LogicType l)
{
return static_cast<int>(l) < logic_root_offset;
}
struct LogicTypeInfo {
std::string _name;
};
class MacroCondition : public MacroSegment {
public:
MacroCondition(Macro *m) : MacroSegment(m) {}
virtual bool CheckCondition() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
LogicType GetLogicType() { return _logic; }
void SetLogicType(LogicType logic) { _logic = logic; }
static const std::map<LogicType, LogicTypeInfo> logicTypes;
bool DurationReached() { return _duration.DurationReached(); }
void ResetDuration() { _duration.Reset(); }
DurationConstraint GetDurationConstraint() { return _duration; }
void SetDurationConstraint(const DurationConstraint &dur);
void SetDurationCondition(DurationCondition cond);
void SetDurationUnit(DurationUnit u);
void SetDuration(double seconds);
private:
LogicType _logic = LogicType::ROOT_NONE;
DurationConstraint _duration;
};
class MacroAction : public MacroSegment {
public:
MacroAction(Macro *m) : MacroSegment(m) {}
virtual bool PerformAction() = 0;
virtual bool Save(obs_data_t *obj) = 0;
virtual bool Load(obs_data_t *obj) = 0;
virtual void LogAction();
};
class Macro {
public:
Macro(const std::string &name = "");
@@ -114,6 +59,9 @@ public:
bool OnChangePreventedActionsRecently();
void ResetUIHelpers();
void EnablePauseHotkeys(bool);
bool PauseHotkeysEnabled();
private:
void SetupHotkeys();
void ClearHotkeys();
@@ -132,6 +80,7 @@ private:
bool _matchOnChange = false;
bool _paused = false;
int _count = 0;
bool _registerHotkeys = true;
obs_hotkey_id _pauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID;
obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID;
@@ -151,41 +100,3 @@ private:
Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
class MacroRef {
public:
MacroRef(){};
MacroRef(std::string name);
void UpdateRef();
void UpdateRef(std::string name);
void UpdateRef(QString name);
void Save(obs_data_t *obj);
void Load(obs_data_t *obj);
Macro *get();
Macro *operator->();
private:
std::string _name = "";
Macro *_ref = nullptr;
};
class MacroRefCondition : public MacroCondition {
public:
MacroRefCondition(Macro *m) : MacroCondition(m) {}
void ResolveMacroRef();
MacroRef _macro;
};
class MacroRefAction : public MacroAction {
public:
MacroRefAction(Macro *m) : MacroAction(m) {}
void ResolveMacroRef();
MacroRef _macro;
};
class MultiMacroRefAction : public MacroAction {
public:
MultiMacroRefAction(Macro *m) : MacroAction(m) {}
void ResolveMacroRef();
std::vector<MacroRef> _macros;
};

View File

@@ -235,6 +235,7 @@ struct SwitcherData {
void setWaitScene();
bool sceneChangedDuringWait();
bool anySceneTransitionStarted();
bool prioFuncsValid();

View File

@@ -54,8 +54,15 @@ std::string MacroActionFactory::GetIdByName(const QString &name)
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : MacroActionFactory::GetActionTypes()) {
list->addItem(obs_module_text(entry.second._name.c_str()));
for (auto &[_, action] : MacroActionFactory::GetActionTypes()) {
QString entry(obs_module_text(action._name.c_str()));
if (list->findText(entry) == -1) {
list->addItem(entry);
} else {
blog(LOG_WARNING,
"did not insert duplicate action entry with name \"%s\"",
entry.toStdString().c_str());
}
}
list->model()->sort(0);
}
@@ -64,10 +71,9 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData,
const std::string &id)
: MacroSegmentEdit(switcher->macroProperties._highlightActions, parent),
_actionSelection(new QComboBox()),
_entryData(entryData)
{
_actionSelection = new QComboBox();
QWidget::connect(_actionSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ActionSelectionChanged(const QString &)));
@@ -214,11 +220,11 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
{
std::lock_guard<std::mutex> lock(switcher->m);
actionsList->Remove(idx);
macro->Actions().erase(macro->Actions().begin() + idx);
switcher->abortMacroWait = true;
switcher->macroWaitCv.notify_all();
macro->UpdateActionIndices();
actionsList->Remove(idx);
SetActionData(*macro);
}
MacroActionSelectionChanged(-1);

View File

@@ -1,87 +0,0 @@
#include "headers/macro-action-preview-scene.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionPreviewScene::id = "preview_scene";
bool MacroActionPreviewScene::_registered = MacroActionFactory::Register(
MacroActionPreviewScene::id,
{MacroActionPreviewScene::Create, MacroActionPreviewSceneEdit::Create,
"AdvSceneSwitcher.action.previewScene"});
bool MacroActionPreviewScene::PerformAction()
{
auto s = obs_weak_source_get_source(_scene.GetScene());
obs_frontend_set_current_preview_scene(s);
obs_source_release(s);
return true;
}
void MacroActionPreviewScene::LogAction()
{
vblog(LOG_INFO, "set preview scene to \"%s\"",
_scene.ToString().c_str());
}
bool MacroActionPreviewScene::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
_scene.Save(obj);
return true;
}
bool MacroActionPreviewScene::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_scene.Load(obj);
return true;
}
std::string MacroActionPreviewScene::GetShortDesc()
{
return _scene.ToString();
}
MacroActionPreviewSceneEdit::MacroActionPreviewSceneEdit(
QWidget *parent, std::shared_ptr<MacroActionPreviewScene> entryData)
: QWidget(parent)
{
_scenes = new SceneSelectionWidget(window(), true, true, true);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
};
placeWidgets(
obs_module_text("AdvSceneSwitcher.action.previewScene.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionPreviewSceneEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_scenes->SetScene(_entryData->_scene);
}
void MacroActionPreviewSceneEdit::SceneChanged(const SceneSelection &s)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = s;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}

View File

@@ -32,13 +32,8 @@ bool MacroActionReplayBuffer::PerformAction()
}
break;
case ReplayBufferAction::SAVE:
if (obs_frontend_replay_buffer_active() &&
_duration.DurationReached()) {
if (obs_frontend_replay_buffer_active()) {
obs_frontend_replay_buffer_save();
// Default buffer size is 20s so waiting for 10s before
// trying to save again seems reasonable
_duration.seconds = 10;
_duration.Reset();
}
break;
default:
@@ -82,21 +77,25 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionReplayBufferEdit::MacroActionReplayBufferEdit(
QWidget *parent, std::shared_ptr<MacroActionReplayBuffer> entryData)
: QWidget(parent)
: QWidget(parent),
_actions(new QComboBox()),
_saveWarning(new QLabel(
obs_module_text("AdvSceneSwitcher.action.replay.saveWarn")))
{
_actions = new QComboBox();
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QHBoxLayout *mainLayout = new QHBoxLayout;
QHBoxLayout *layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.replay.entry"),
mainLayout, widgetPlaceholders);
layout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(layout);
mainLayout->addWidget(_saveWarning);
setLayout(mainLayout);
_entryData = entryData;
@@ -110,6 +109,8 @@ void MacroActionReplayBufferEdit::UpdateEntryData()
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_saveWarning->setVisible(_entryData->_action ==
ReplayBufferAction::SAVE);
}
void MacroActionReplayBufferEdit::ActionChanged(int value)
@@ -120,6 +121,9 @@ void MacroActionReplayBufferEdit::ActionChanged(int value)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<ReplayBufferAction>(value);
_saveWarning->setVisible(_entryData->_action ==
ReplayBufferAction::SAVE);
adjustSize();
}
#endif

View File

@@ -1,46 +0,0 @@
#include "headers/macro-action-scene-swap.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSceneSwap::id = "scene_swap";
bool MacroActionSceneSwap::_registered = MacroActionFactory::Register(
MacroActionSceneSwap::id,
{MacroActionSceneSwap::Create, MacroActionSceneSwapEdit::Create,
"AdvSceneSwitcher.action.sceneSwap"});
bool MacroActionSceneSwap::PerformAction()
{
obs_frontend_preview_program_trigger_transition();
return true;
}
void MacroActionSceneSwap::LogAction()
{
vblog(LOG_INFO, "trigger preview to active scene transition");
}
bool MacroActionSceneSwap::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
return true;
}
bool MacroActionSceneSwap::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
return true;
}
MacroActionSceneSwapEdit::MacroActionSceneSwapEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneSwap> entryData)
: QWidget(parent)
{
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.SceneSwap.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
}

View File

@@ -2,6 +2,8 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
using namespace std::chrono_literals;
const std::string MacroActionSwitchScene::id = "scene_switch";
bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
@@ -9,7 +11,21 @@ bool MacroActionSwitchScene::_registered = MacroActionFactory::Register(
{MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create,
"AdvSceneSwitcher.action.switchScene"});
void waitForTransitionChange(int duration)
void waitForTransitionChange(OBSWeakSource &transition)
{
const auto time = 100ms;
obs_source_t *source = obs_weak_source_get_source(transition);
std::unique_lock<std::mutex> lock(switcher->m);
bool stillTransitioning = true;
while (stillTransitioning && !switcher->abortMacroWait) {
switcher->macroTransitionCv.wait_for(lock, time);
float t = obs_transition_get_time(source);
stillTransitioning = t < 1.0f && t > 0.0f;
}
obs_source_release(source);
}
void waitForTransitionChangeFixedDuration(int duration)
{
duration += 200; // It seems to be necessary to add a small buffer
auto time = std::chrono::high_resolution_clock::now() +
@@ -25,7 +41,7 @@ void waitForTransitionChange(int duration)
}
}
int getTransitionOverrideDuration(OBSWeakSource scene)
int getTransitionOverrideDuration(OBSWeakSource &scene)
{
int duration = 0;
obs_source_t *source = obs_weak_source_get_source(scene);
@@ -39,11 +55,41 @@ int getTransitionOverrideDuration(OBSWeakSource scene)
return duration;
}
int getExpectedTransitionDuration(OBSWeakSource scene, double duration)
bool isUsingFixedLengthTransition(OBSWeakSource &transition)
{
int overrideDuration = getTransitionOverrideDuration(scene);
if (overrideDuration && !switcher->transitionOverrideOverride) {
return overrideDuration;
obs_source_t *source = obs_weak_source_get_source(transition);
bool ret = obs_transition_fixed(source);
obs_source_release(source);
return ret;
}
OBSWeakSource getOverrideTransition(OBSWeakSource &scene)
{
OBSWeakSource transition;
obs_source_t *source = obs_weak_source_get_source(scene);
obs_data_t *data = obs_source_get_private_settings(source);
transition = GetWeakTransitionByName(
obs_data_get_string(data, "transition"));
obs_data_release(data);
obs_source_release(source);
return transition;
}
int getExpectedTransitionDuration(OBSWeakSource &scene, OBSWeakSource &t,
double duration)
{
OBSWeakSource transition = t;
if (!switcher->transitionOverrideOverride) {
auto overrideTransition = getOverrideTransition(scene);
if (overrideTransition) {
transition = overrideTransition;
if (!isUsingFixedLengthTransition(transition)) {
return getTransitionOverrideDuration(scene);
}
}
}
if (isUsingFixedLengthTransition(transition)) {
return -1; // no API is available to access the fixed duration
}
if (duration != 0) {
return duration * 1000;
@@ -54,12 +100,19 @@ int getExpectedTransitionDuration(OBSWeakSource scene, double duration)
bool MacroActionSwitchScene::PerformAction()
{
auto scene = _scene.GetScene();
switchScene({scene, _transition.GetTransition(),
(int)(_duration.seconds * 1000)},
auto transition = _transition.GetTransition();
switchScene({scene, transition, (int)(_duration.seconds * 1000)},
obs_frontend_preview_program_mode_active());
if (_blockUntilTransitionDone && scene) {
waitForTransitionChange(getExpectedTransitionDuration(
scene, _duration.seconds));
const int expectedTransitionDuration =
getExpectedTransitionDuration(scene, transition,
_duration.seconds);
if (expectedTransitionDuration < 0) {
waitForTransitionChange(transition);
} else {
waitForTransitionChangeFixedDuration(
expectedTransitionDuration);
}
return !switcher->abortMacroWait;
}
return true;

View File

@@ -0,0 +1,183 @@
#include "headers/macro-action-studio-mode.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionSudioMode::id = "studio_mode";
bool MacroActionSudioMode::_registered = MacroActionFactory::Register(
MacroActionSudioMode::id,
{MacroActionSudioMode::Create, MacroActionSudioModeEdit::Create,
"AdvSceneSwitcher.action.studioMode"});
// TODO: Remove in future version (backwards compatibility)
static const std::string idOld1 = "scene_swap";
static const std::string idOld2 = "preview_scene";
static bool oldRegisterd1 = MacroActionFactory::Register(
idOld1, {MacroActionSudioMode::Create, MacroActionSudioModeEdit::Create,
"AdvSceneSwitcher.action.studioMode"});
static bool oldRegisterd2 = MacroActionFactory::Register(
idOld2, {MacroActionSudioMode::Create, MacroActionSudioModeEdit::Create,
"AdvSceneSwitcher.action.studioMode"});
const static std::map<StudioModeAction, std::string> actionTypes = {
{StudioModeAction::SWAP_SCENE,
"AdvSceneSwitcher.action.studioMode.type.swap"},
{StudioModeAction::SET_SCENE,
"AdvSceneSwitcher.action.studioMode.type.setScene"},
{StudioModeAction::ENABLE_STUDIO_MODE,
"AdvSceneSwitcher.action.studioMode.type.enable"},
{StudioModeAction::DISABLE_STUDIO_MODE,
"AdvSceneSwitcher.action.studioMode.type.disable"},
};
// Calling obs_frontend_set_preview_program_mode() directly from a thread that
// is not the main OBS UI thread will lead to undefined behaviour, so we have
// to use this helper function instead - copied from obs-websocket plugin
static void enableStudioMode(bool enable)
{
if (obs_frontend_preview_program_mode_active() != enable) {
obs_queue_task(
OBS_TASK_UI,
[](void *param) {
auto studioModeEnabled = (bool *)param;
obs_frontend_set_preview_program_mode(
*studioModeEnabled);
},
&enable, true);
}
}
bool MacroActionSudioMode::PerformAction()
{
switch (_action) {
case StudioModeAction::SWAP_SCENE:
obs_frontend_preview_program_trigger_transition();
break;
case StudioModeAction::SET_SCENE: {
auto s = obs_weak_source_get_source(_scene.GetScene());
obs_frontend_set_current_preview_scene(s);
obs_source_release(s);
break;
}
case StudioModeAction::ENABLE_STUDIO_MODE:
enableStudioMode(true);
break;
case StudioModeAction::DISABLE_STUDIO_MODE:
enableStudioMode(false);
break;
default:
break;
}
return true;
}
void MacroActionSudioMode::LogAction()
{
auto it = actionTypes.find(_action);
if (it != actionTypes.end()) {
vblog(LOG_INFO, "performed action \"%s\" with scene \"%s\"",
it->second.c_str(), _scene.ToString().c_str());
} else {
blog(LOG_WARNING, "ignored unknown studio mode action %d",
static_cast<int>(_action));
}
}
bool MacroActionSudioMode::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_scene.Save(obj);
return true;
}
bool MacroActionSudioMode::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_action =
static_cast<StudioModeAction>(obs_data_get_int(obj, "action"));
_scene.Load(obj);
// TODO: Remove in future version (backwards compatibility)
if (obs_data_get_string(obj, "id") == idOld2) {
_action = StudioModeAction::SET_SCENE;
}
return true;
}
std::string MacroActionSudioMode::GetShortDesc()
{
if (_action == StudioModeAction::SET_SCENE) {
return _scene.ToString();
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionSudioModeEdit::MacroActionSudioModeEdit(
QWidget *parent, std::shared_ptr<MacroActionSudioMode> entryData)
: QWidget(parent),
_actions(new QComboBox()),
_scenes(new SceneSelectionWidget(window(), true, true, true))
{
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{actions}}", _actions},
{"{{scenes}}", _scenes},
};
QHBoxLayout *mainLayout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.action.studioMode.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionSudioModeEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_scenes->SetScene(_entryData->_scene);
_scenes->setVisible(_entryData->_action == StudioModeAction::SET_SCENE);
}
void MacroActionSudioModeEdit::SceneChanged(const SceneSelection &s)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = s;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionSudioModeEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_action = static_cast<StudioModeAction>(value);
_scenes->setVisible(_entryData->_action == StudioModeAction::SET_SCENE);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}

View File

@@ -9,9 +9,20 @@ bool MacroActionTransition::_registered = MacroActionFactory::Register(
{MacroActionTransition::Create, MacroActionTransitionEdit::Create,
"AdvSceneSwitcher.action.transition"});
bool MacroActionTransition::PerformAction()
const static std::map<MacroActionTransition::Type, std::string> actionTypes = {
{MacroActionTransition::Type::SCENE,
"AdvSceneSwitcher.action.transition.type.scene"},
{MacroActionTransition::Type::SCENE_OVERRIDE,
"AdvSceneSwitcher.action.transition.type.sceneOverride"},
{MacroActionTransition::Type::SOURCE_SHOW,
"AdvSceneSwitcher.action.transition.type.sourceShow"},
{MacroActionTransition::Type::SOURCE_HIDE,
"AdvSceneSwitcher.action.transition.type.sourceHide"},
};
void MacroActionTransition::SetSceneTransition()
{
if (_setType) {
if (_setTransitionType) {
auto t =
obs_weak_source_get_source(_transition.GetTransition());
obs_frontend_set_current_transition(t);
@@ -20,17 +31,106 @@ bool MacroActionTransition::PerformAction()
if (_setDuration) {
obs_frontend_set_transition_duration(_duration.seconds * 1000);
}
}
void MacroActionTransition::SetTransitionOverride()
{
obs_source_t *scene = obs_weak_source_get_source(_scene.GetScene());
obs_data_t *data = obs_source_get_private_settings(scene);
if (_setTransitionType) {
obs_data_set_string(data, "transition",
_transition.ToString().c_str());
}
if (_setDuration) {
obs_data_set_int(data, "transition_duration",
_duration.seconds * 1000);
}
obs_data_release(data);
obs_source_release(scene);
}
void MacroActionTransition::SetSourceTransition(bool show)
{
#ifdef VISIBILITY_TRANSITIONS_SUPPORTED
const auto setTransitionFunc = show ? obs_sceneitem_set_show_transition
: obs_sceneitem_set_hide_transition;
const auto setDurationFunc =
show ? obs_sceneitem_set_show_transition_duration
: obs_sceneitem_set_hide_transition_duration;
auto transition =
obs_weak_source_get_source(_transition.GetTransition());
obs_data_t *settings = obs_source_get_settings(transition);
obs_source_t *t = obs_source_create_private(
obs_source_get_id(transition), obs_source_get_name(transition),
settings);
obs_data_release(settings);
obs_source_release(transition);
const auto items = _source.GetSceneItems(_scene);
for (auto &item : items) {
if (_setTransitionType) {
setTransitionFunc(item, t);
}
if (_setDuration) {
setDurationFunc(item, _duration.seconds * 1000);
}
obs_sceneitem_release(item);
}
obs_source_release(t);
#else
blog(LOG_WARNING, "Setting hide / show transition not supported!");
#endif
}
bool MacroActionTransition::PerformAction()
{
switch (_type) {
case Type::SCENE:
SetSceneTransition();
break;
case Type::SCENE_OVERRIDE:
SetTransitionOverride();
break;
case Type::SOURCE_SHOW:
SetSourceTransition(true);
break;
case Type::SOURCE_HIDE:
SetSourceTransition(false);
break;
}
return true;
}
void MacroActionTransition::LogAction()
{
std::string msgBegin;
switch (_type) {
case Type::SCENE:
msgBegin += "set scene transition";
break;
case Type::SCENE_OVERRIDE:
msgBegin +=
"set scene override transition of " + _scene.ToString();
break;
case Type::SOURCE_SHOW:
msgBegin += "set source show transition of " +
_source.ToString() + " on scene " +
_scene.ToString();
break;
case Type::SOURCE_HIDE:
msgBegin += "set source hide transition of " +
_source.ToString() + " on scene " +
_scene.ToString();
break;
}
if (_setDuration) {
vblog(LOG_INFO, "set transition duration to %s",
vblog(LOG_INFO, "%s duration to %s", msgBegin.c_str(),
_duration.ToString().c_str());
}
if (_setType) {
vblog(LOG_INFO, "set transition type to \"%s\"",
if (_setTransitionType) {
vblog(LOG_INFO, "%s type to \"%s\"", msgBegin.c_str(),
_transition.ToString().c_str());
}
}
@@ -38,64 +138,111 @@ void MacroActionTransition::LogAction()
bool MacroActionTransition::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "actionType", static_cast<int>(_type));
_source.Save(obj);
_scene.Save(obj);
_duration.Save(obj);
_transition.Save(obj);
obs_data_set_bool(obj, "setDuration", _setDuration);
obs_data_set_bool(obj, "setType", _setType);
obs_data_set_bool(obj, "setType", _setTransitionType);
return true;
}
bool MacroActionTransition::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_type = static_cast<Type>(obs_data_get_int(obj, "actionType"));
_source.Load(obj);
_scene.Load(obj);
_duration.Load(obj);
_transition.Load(obj);
_setDuration = obs_data_get_bool(obj, "setDuration");
_setType = obs_data_get_bool(obj, "setType");
_setTransitionType = obs_data_get_bool(obj, "setType");
return true;
}
std::string MacroActionTransition::GetShortDesc()
{
return _transition.ToString();
std::string msgBegin;
switch (_type) {
case Type::SCENE:
return _transition.ToString();
case Type::SCENE_OVERRIDE:
return _scene.ToString() + " - " + _transition.ToString();
case Type::SOURCE_SHOW:
return _scene.ToString() + " - " + _source.ToString() + " - " +
_transition.ToString();
case Type::SOURCE_HIDE:
return _scene.ToString() + " - " + _source.ToString() + " - " +
_transition.ToString();
}
return "";
}
static inline void populateActionSelection(QComboBox *list)
{
for (auto entry : actionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroActionTransitionEdit::MacroActionTransitionEdit(
QWidget *parent, std::shared_ptr<MacroActionTransition> entryData)
: QWidget(parent)
: QWidget(parent),
_actions(new QComboBox),
_sources(new SceneItemSelectionWidget(parent, false)),
_scenes(new SceneSelectionWidget(this, false, false, true)),
_setTransition(new QCheckBox),
_setDuration(new QCheckBox),
_transitions(new TransitionSelectionWidget(this, false)),
_duration(new DurationSelection(this, false)),
_transitionLayout(new QHBoxLayout),
_durationLayout(new QHBoxLayout)
{
_transitions = new TransitionSelectionWidget(this, false);
_duration = new DurationSelection(this, false);
_setType = new QCheckBox();
_setDuration = new QCheckBox();
populateActionSelection(_actions);
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
_sources, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
QWidget::connect(_transitions,
SIGNAL(TransitionChanged(const TransitionSelection &)),
this,
SLOT(TransitionChanged(const TransitionSelection &)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_setType, SIGNAL(stateChanged(int)), this,
SLOT(SetTypeChanged(int)));
QWidget::connect(_setTransition, SIGNAL(stateChanged(int)), this,
SLOT(SetTransitionChanged(int)));
QWidget::connect(_setDuration, SIGNAL(stateChanged(int)), this,
SLOT(SetDurationChanged(int)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{type}}", _actions},
{"{{sources}}", _sources},
{"{{scenes}}", _scenes},
{"{{transitions}}", _transitions},
{"{{duration}}", _duration},
{"{{setType}}", _setType},
{"{{setTransition}}", _setTransition},
{"{{setDuration}}", _setDuration},
};
_typeLayout = new QHBoxLayout;
QHBoxLayout *typeLayout = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.action.transition.entry.line1"),
_typeLayout, widgetPlaceholders);
_durationLayout = new QHBoxLayout;
typeLayout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.action.transition.entry.line2"),
_transitionLayout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.action.transition.entry.line3"),
_durationLayout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(_typeLayout);
mainLayout->addLayout(typeLayout);
mainLayout->addLayout(_transitionLayout);
mainLayout->addLayout(_durationLayout);
setLayout(mainLayout);
@@ -110,12 +257,53 @@ void MacroActionTransitionEdit::UpdateEntryData()
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_type));
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem((_entryData->_source));
_setDuration->setChecked(_entryData->_setDuration);
_duration->SetDuration(_entryData->_duration);
_setType->setChecked(_entryData->_setType);
_setTransition->setChecked(_entryData->_setTransitionType);
_transitions->SetTransition(_entryData->_transition);
_transitions->setEnabled(_entryData->_setType);
_transitions->setEnabled(_entryData->_setTransitionType);
_duration->setEnabled(_entryData->_setDuration);
SetWidgetVisibility();
}
void MacroActionTransitionEdit::SourceChanged(const SceneItemSelection &item)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_source = item;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionTransitionEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_type = static_cast<MacroActionTransition::Type>(value);
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionTransitionEdit::SceneChanged(const SceneSelection &s)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_scene = s;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionTransitionEdit::TransitionChanged(const TransitionSelection &t)
@@ -140,14 +328,24 @@ void MacroActionTransitionEdit::DurationChanged(double seconds)
_entryData->_duration.seconds = seconds;
}
void MacroActionTransitionEdit::SetTypeChanged(int state)
void MacroActionTransitionEdit::SetWidgetVisibility()
{
_sources->setVisible(
_entryData->_type == MacroActionTransition::Type::SOURCE_HIDE ||
_entryData->_type == MacroActionTransition::Type::SOURCE_SHOW);
_scenes->setVisible(_entryData->_type !=
MacroActionTransition::Type::SCENE);
adjustSize();
}
void MacroActionTransitionEdit::SetTransitionChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_setType = state;
_entryData->_setTransitionType = state;
_transitions->setEnabled(state);
if (state) {
emit HeaderInfoChanged(

32
src/macro-action.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include "headers/macro-action.hpp"
#include "headers/advanced-scene-switcher.hpp"
bool MacroAction::Save(obs_data_t *obj)
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
return true;
}
bool MacroAction::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
return true;
}
void MacroAction::LogAction()
{
vblog(LOG_INFO, "performed action %s", GetId().c_str());
}
void MacroRefAction::ResolveMacroRef()
{
_macro.UpdateRef();
}
void MultiMacroRefAction::ResolveMacroRef()
{
for (auto &m : _macros) {
m.UpdateRef();
}
}

View File

@@ -212,6 +212,10 @@ void MacroConditionAudio::SetVolumeLevel(void *data, const float *,
const float *)
{
MacroConditionAudio *c = static_cast<MacroConditionAudio *>(data);
const auto macro = c->GetMacro();
if (macro && macro->Paused()) {
return;
}
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
if (peak[i] > c->_peak) {

View File

@@ -64,17 +64,18 @@ static inline void populateConditionSelection(QComboBox *list)
MacroConditionCursorEdit::MacroConditionCursorEdit(
QWidget *parent, std::shared_ptr<MacroConditionCursor> entryData)
: QWidget(parent)
: QWidget(parent),
_minX(new QSpinBox()),
_minY(new QSpinBox()),
_maxX(new QSpinBox()),
_maxY(new QSpinBox()),
_conditions(new QComboBox()),
_frameToggle(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.cursor.showFrame"))),
_xPos(new QLabel("-")),
_yPos(new QLabel("-"))
{
_conditions = new QComboBox();
_minX = new QSpinBox();
_minY = new QSpinBox();
_maxX = new QSpinBox();
_maxY = new QSpinBox();
_frameToggle = new QPushButton(
obs_module_text("AdvSceneSwitcher.condition.cursor.showFrame"));
_xPos = new QLabel("-");
_yPos = new QLabel("-");
_frame.setAttribute(Qt::WA_TransparentForMouseEvents);
populateConditionSelection(_conditions);

View File

@@ -20,6 +20,12 @@ static std::map<DateCondition, std::string> dateConditionTypes = {
"AdvSceneSwitcher.condition.date.state.between"},
};
static std::map<DateCondition, std::string> weekDateConditionTypes = {
{DateCondition::AT, "AdvSceneSwitcher.condition.date.state.at"},
{DateCondition::AFTER, "AdvSceneSwitcher.condition.date.state.after"},
{DateCondition::BEFORE, "AdvSceneSwitcher.condition.date.state.before"},
};
static std::map<DayOfWeekSelection, std::string> dayOfWeekNames = {
{DayOfWeekSelection::ANY, "AdvSceneSwitcher.condition.date.anyDay"},
{DayOfWeekSelection::MONDAY, "AdvSceneSwitcher.condition.date.monday"},
@@ -42,8 +48,23 @@ bool MacroConditionDate::CheckDayOfWeek(int64_t msSinceLastCheck)
cur.date().dayOfWeek() != static_cast<int>(_dayOfWeek)) {
return false;
}
if (_ignoreTime) {
return true;
}
_dateTime.setDate(cur.date());
return _dateTime <= cur && _dateTime >= cur.addMSecs(-msSinceLastCheck);
switch (_condition) {
case DateCondition::AT:
return _dateTime <= cur &&
_dateTime >= cur.addMSecs(-msSinceLastCheck);
case DateCondition::AFTER:
return cur >= _dateTime;
case DateCondition::BEFORE:
return cur <= _dateTime;
default:
break;
}
return false;
}
bool MacroConditionDate::CheckRegularDate(int64_t msSinceLastCheck)
@@ -107,13 +128,16 @@ 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));
const auto &dateToSave = _updateOnRepeat ? _dateTime : _origDateTime;
const auto &dateToSave2 = _updateOnRepeat ? _dateTime2 : _origDateTime2;
obs_data_set_string(obj, "dateTime",
_dateTime.toString().toStdString().c_str());
dateToSave.toString().toStdString().c_str());
obs_data_set_string(obj, "dateTime2",
_dateTime2.toString().toStdString().c_str());
dateToSave2.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);
obs_data_set_bool(obj, "updateOnRepeat", _updateOnRepeat);
_duration.Save(obj);
obs_data_set_bool(obj, "dayOfWeekCheck", _dayOfWeekCheck);
return true;
@@ -128,11 +152,19 @@ bool MacroConditionDate::Load(obs_data_t *obj)
static_cast<DateCondition>(obs_data_get_int(obj, "condition"));
_dateTime = QDateTime::fromString(
QString::fromStdString(obs_data_get_string(obj, "dateTime")));
_origDateTime = _dateTime;
_dateTime2 = QDateTime::fromString(
QString::fromStdString(obs_data_get_string(obj, "dateTime2")));
_origDateTime2 = _dateTime2;
_ignoreDate = obs_data_get_bool(obj, "ignoreDate");
_ignoreTime = obs_data_get_bool(obj, "ignoreTime");
_repeat = obs_data_get_bool(obj, "repeat");
// TODO: Remove this fallback in future version
if (!obs_data_has_user_value(obj, "updateOnRepeat")) {
_updateOnRepeat = true;
} else {
_updateOnRepeat = obs_data_get_bool(obj, "updateOnRepeat");
}
_duration.Load(obj);
// TODO: Remove this fallback in future version
@@ -141,6 +173,11 @@ bool MacroConditionDate::Load(obs_data_t *obj)
} else {
_dayOfWeekCheck = obs_data_get_bool(obj, "dayOfWeekCheck");
}
// The following code is used to avoid issues with old save files in
// which the simple date check did not support setting _condition
if (_dayOfWeekCheck && _condition == DateCondition::BETWEEN) {
_condition = DateCondition::AT;
}
return true;
}
@@ -152,20 +189,62 @@ std::string MacroConditionDate::GetShortDesc()
return "";
}
std::string dayName = obs_module_text(it->second.c_str());
if (_ignoreTime) {
return dayName;
}
return dayName + " " +
_dateTime.time().toString().toStdString();
} else {
if (!_ignoreDate && !_ignoreTime) {
return _dateTime.toString().toStdString();
return GetDateTime1().toString().toStdString();
} else if (_ignoreDate && !_ignoreTime) {
return _dateTime.time().toString().toStdString();
return GetDateTime1().time().toString().toStdString();
} else if (!_ignoreDate && _ignoreTime) {
return _dateTime.date().toString().toStdString();
return GetDateTime1().date().toString().toStdString();
}
}
return "";
}
void MacroConditionDate::SetDate1(const QDate &date)
{
_dateTime.setDate(date);
_origDateTime.setDate(date);
}
void MacroConditionDate::SetDate2(const QDate &date)
{
_dateTime2.setDate(date);
_origDateTime2.setDate(date);
}
void MacroConditionDate::SetTime1(const QTime &time)
{
_dateTime.setTime(time);
_origDateTime.setTime(time);
}
void MacroConditionDate::SetTime2(const QTime &time)
{
_dateTime2.setTime(time);
_origDateTime2.setTime(time);
}
QDateTime MacroConditionDate::GetDateTime1()
{
return _updateOnRepeat ? _dateTime : _origDateTime;
}
QDateTime MacroConditionDate::GetDateTime2()
{
return _updateOnRepeat ? _dateTime2 : _origDateTime2;
}
QDateTime MacroConditionDate::GetNextMatchDateTime()
{
return _dateTime;
}
static inline void populateDaySelection(QComboBox *list)
{
for (auto entry : dayOfWeekNames) {
@@ -180,45 +259,64 @@ static inline void populateConditionSelection(QComboBox *list)
}
}
static inline void populateWeekConditionSelection(QComboBox *list)
{
for (auto entry : weekDateConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionDateEdit::MacroConditionDateEdit(
QWidget *parent, std::shared_ptr<MacroConditionDate> entryData)
: QWidget(parent)
: QWidget(parent),
_weekCondition(new QComboBox()),
_dayOfWeek(new QComboBox()),
_ignoreWeekTime(new QCheckBox()),
_weekTime(new QTimeEdit()),
_condition(new QComboBox()),
_date(new QDateEdit()),
_time(new QTimeEdit()),
_separator(new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.date.separator"))),
_date2(new QDateEdit()),
_time2(new QTimeEdit()),
_ignoreDate(new QCheckBox()),
_ignoreTime(new QCheckBox()),
_repeat(new QCheckBox()),
_nextMatchDate(new QLabel()),
_updateOnRepeat(new QCheckBox()),
_duration(new DurationSelection()),
_advancedSettingsTooggle(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"))),
_simpleLayout(new QHBoxLayout()),
_advancedLayout(new QHBoxLayout()),
_repeatLayout(new QVBoxLayout()),
_repeatUpdateLayout(new QHBoxLayout())
{
_dayOfWeek = new QComboBox();
_weekTime = new QTimeEdit();
_ignoreWeekTime->setToolTip(
obs_module_text("AdvSceneSwitcher.condition.date.ignoreTime"));
_weekTime->setDisplayFormat("hh:mm:ss");
_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();
_advancedSettingsTooggle = new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"));
QWidget::connect(_weekCondition, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_dayOfWeek, SIGNAL(currentIndexChanged(int)), this,
SLOT(DayOfWeekChanged(int)));
QWidget::connect(_ignoreWeekTime, SIGNAL(stateChanged(int)), this,
SLOT(IgnoreTimeChanged(int)));
QWidget::connect(_weekTime, SIGNAL(timeChanged(const QTime &)), this,
SLOT(TimeChanged(const QTime &)));
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
@@ -237,6 +335,8 @@ MacroConditionDateEdit::MacroConditionDateEdit(
SLOT(IgnoreTimeChanged(int)));
QWidget::connect(_repeat, SIGNAL(stateChanged(int)), this,
SLOT(RepeatChanged(int)));
QWidget::connect(_updateOnRepeat, SIGNAL(stateChanged(int)), this,
SLOT(UpdateOnRepeatChanged(int)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
@@ -246,9 +346,12 @@ MacroConditionDateEdit::MacroConditionDateEdit(
populateDaySelection(_dayOfWeek);
populateConditionSelection(_condition);
populateWeekConditionSelection(_weekCondition);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{weekCondition}}", _weekCondition},
{"{{dayOfWeek}}", _dayOfWeek},
{"{{ignoreWeekTime}}", _ignoreWeekTime},
{"{{weekTime}}", _weekTime},
{"{{condition}}", _condition},
{"{{date}}", _date},
@@ -259,20 +362,26 @@ MacroConditionDateEdit::MacroConditionDateEdit(
{"{{ignoreDate}}", _ignoreDate},
{"{{ignoreTime}}", _ignoreTime},
{"{{repeat}}", _repeat},
{"{{updateOnRepeat}}", _updateOnRepeat},
{"{{duration}}", _duration},
};
_simpleLayout = new QHBoxLayout;
placeWidgets(
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;
auto repeatLayout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.date.entry.repeat"),
_repeatLayout, widgetPlaceholders);
repeatLayout, widgetPlaceholders);
placeWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.date.entry.updateOnRepeat"),
_repeatUpdateLayout, widgetPlaceholders);
_repeatLayout->addLayout(repeatLayout);
_repeatLayout->addWidget(_nextMatchDate);
_repeatLayout->addLayout(_repeatUpdateLayout);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(_simpleLayout);
@@ -284,6 +393,11 @@ MacroConditionDateEdit::MacroConditionDateEdit(
mainLayout->addLayout(_advancedToggleLayout);
setLayout(mainLayout);
_timer.setInterval(3000);
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(ShowNextMatch()));
_timer.start();
_entryData = entryData;
UpdateEntryData();
_loading = false;
@@ -320,7 +434,7 @@ void MacroConditionDateEdit::DateChanged(const QDate &date)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime.setDate(date);
_entryData->SetDate1(date);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -333,7 +447,7 @@ void MacroConditionDateEdit::TimeChanged(const QTime &time)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime.setTime(time);
_entryData->SetTime1(time);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
@@ -346,7 +460,7 @@ void MacroConditionDateEdit::Date2Changed(const QDate &date)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime2.setDate(date);
_entryData->SetDate2(date);
}
void MacroConditionDateEdit::Time2Changed(const QTime &time)
@@ -356,7 +470,7 @@ void MacroConditionDateEdit::Time2Changed(const QTime &time)
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dateTime2.setTime(time);
_entryData->SetTime2(time);
}
void MacroConditionDateEdit::IgnoreDateChanged(int state)
@@ -390,6 +504,17 @@ void MacroConditionDateEdit::RepeatChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_repeat = state;
_duration->setDisabled(!state);
SetWidgetStatus();
}
void MacroConditionDateEdit::UpdateOnRepeatChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_updateOnRepeat = state;
}
void MacroConditionDateEdit::DurationChanged(double seconds)
@@ -417,33 +542,53 @@ void MacroConditionDateEdit::AdvancedSettingsToggleClicked()
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dayOfWeekCheck = !_entryData->_dayOfWeekCheck;
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_dayOfWeekCheck = !_entryData->_dayOfWeekCheck;
_entryData->_condition = DateCondition::AT;
}
_condition->setCurrentIndex(0);
_weekCondition->setCurrentIndex(0);
SetWidgetStatus();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionDateEdit::ShowNextMatch()
{
if (!_entryData || _entryData->_dayOfWeekCheck ||
!_entryData->_repeat) {
return;
}
QString format(obs_module_text(
"AdvSceneSwitcher.condition.date.entry.nextMatchDate"));
_nextMatchDate->setText(
format.arg(_entryData->GetNextMatchDateTime().toString()));
}
void MacroConditionDateEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_weekCondition->setCurrentIndex(
static_cast<int>(_entryData->_condition));
_dayOfWeek->setCurrentIndex(
static_cast<int>(static_cast<int>(_entryData->_dayOfWeek)));
_weekTime->setTime(_entryData->_dateTime.time());
_ignoreWeekTime->setChecked(!_entryData->_ignoreTime);
_weekTime->setDateTime(_entryData->GetDateTime1());
_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());
_date->setDateTime(_entryData->GetDateTime1());
_time->setDateTime(_entryData->GetDateTime1());
_date2->setDateTime(_entryData->GetDateTime2());
_time2->setDateTime(_entryData->GetDateTime2());
_ignoreDate->setChecked(!_entryData->_ignoreDate);
_ignoreTime->setChecked(!_entryData->_ignoreTime);
_repeat->setChecked(_entryData->_repeat);
_updateOnRepeat->setChecked(_entryData->_updateOnRepeat);
_duration->SetDuration(_entryData->_duration);
_duration->setDisabled(!_entryData->_repeat);
ShowNextMatch();
SetWidgetStatus();
}
@@ -456,9 +601,15 @@ void MacroConditionDateEdit::SetWidgetStatus()
setLayoutVisible(_simpleLayout, _entryData->_dayOfWeekCheck);
setLayoutVisible(_advancedLayout, !_entryData->_dayOfWeekCheck);
setLayoutVisible(_repeatLayout, !_entryData->_dayOfWeekCheck);
_nextMatchDate->setVisible(!_entryData->_dayOfWeekCheck &&
_entryData->_repeat);
setLayoutVisible(_repeatUpdateLayout,
!_entryData->_dayOfWeekCheck && _entryData->_repeat);
if (_entryData->_dayOfWeekCheck) {
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showAdvancedSettings"));
_weekTime->setDisabled(_entryData->_ignoreTime);
_weekCondition->setDisabled(_entryData->_ignoreTime);
} else {
_advancedSettingsTooggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.date.showSimpleSettings"));
@@ -470,10 +621,8 @@ void MacroConditionDateEdit::SetWidgetStatus()
DateCondition::BETWEEN);
}
const QSignalBlocker b1(_weekTime);
const QSignalBlocker b2(_weekTime);
_weekTime->setTime(_entryData->_dateTime.time());
_weekTime->setTime(_entryData->_dateTime.time());
const QSignalBlocker b(_weekTime);
_weekTime->setTime(_entryData->GetDateTime1().time());
adjustSize();
}

View File

@@ -53,10 +53,10 @@ std::string MacroConditionFactory::GetIdByName(const QString &name)
return "";
}
bool MacroConditionFactory::UsesDurationConstraint(const std::string &id)
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
{
if (auto it = _methods.find(id); it != _methods.end()) {
return it->second._useDurationConstraint;
return it->second._useDurationModifier;
}
return false;
}
@@ -83,24 +83,109 @@ static inline void populateLogicSelection(QComboBox *list, bool root = false)
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : MacroConditionFactory::GetConditionTypes()) {
list->addItem(obs_module_text(entry.second._name.c_str()));
for (auto &[_, condition] :
MacroConditionFactory::GetConditionTypes()) {
QString entry(obs_module_text(condition._name.c_str()));
if (list->findText(entry) == -1) {
list->addItem(entry);
} else {
blog(LOG_WARNING,
"did not insert duplicate condition entry with name \"%s\"",
entry.toStdString().c_str());
}
}
list->model()->sort(0);
}
static void populateDurationModifierTypes(QComboBox *list)
{
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.none"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.more"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.equal"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.less"));
list->addItem(
obs_module_text("AdvSceneSwitcher.duration.condition.within"));
}
DurationModifierEdit::DurationModifierEdit(QWidget *parent)
{
_condition = new QComboBox(parent);
_duration = new DurationSelection(parent);
_toggle = new QPushButton(parent);
_toggle->setMaximumSize(22, 22);
_toggle->setIcon(
QIcon(QString::fromStdString(getDataFilePath("res/time.svg"))));
populateDurationModifierTypes(_condition);
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this,
SLOT(_ModifierChanged(int)));
QObject::connect(_duration, &DurationSelection::DurationChanged, this,
&DurationModifierEdit::DurationChanged);
QObject::connect(_duration, &DurationSelection::UnitChanged, this,
&DurationModifierEdit::UnitChanged);
QWidget::connect(_toggle, SIGNAL(clicked()), this,
SLOT(ToggleClicked()));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_toggle);
layout->addWidget(_condition);
layout->addWidget(_duration);
setLayout(layout);
Collapse(true);
}
void DurationModifierEdit::SetValue(DurationModifier &value)
{
_duration->SetDuration(value.GetDuration());
_condition->setCurrentIndex(static_cast<int>(value.GetType()));
_duration->setVisible(value.GetType() != DurationModifier::Type::NONE);
}
void DurationModifierEdit::SetUnit(DurationUnit u)
{
_duration->SetUnit(u);
}
void DurationModifierEdit::SetDuration(const Duration &d)
{
_duration->SetDuration(d);
}
void DurationModifierEdit::_ModifierChanged(int value)
{
auto m = static_cast<DurationModifier::Type>(value);
Collapse(m == DurationModifier::Type::NONE);
emit ModifierChanged(m);
}
void DurationModifierEdit::ToggleClicked()
{
Collapse(false);
}
void DurationModifierEdit::Collapse(bool collapse)
{
_toggle->setVisible(collapse);
_duration->setVisible(!collapse);
_condition->setVisible(!collapse);
}
MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root)
: MacroSegmentEdit(switcher->macroProperties._highlightConditions,
parent),
_logicSelection(new QComboBox()),
_conditionSelection(new QComboBox()),
_dur(new DurationModifierEdit()),
_entryData(entryData),
_isRoot(root)
{
_logicSelection = new QComboBox();
_conditionSelection = new QComboBox();
_dur = new DurationConstraintEdit();
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
this, SLOT(LogicSelectionChanged(int)));
QWidget::connect(_conditionSelection,
@@ -110,9 +195,9 @@ MacroConditionEdit::MacroConditionEdit(
SLOT(DurationChanged(double)));
QWidget::connect(_dur, SIGNAL(UnitChanged(DurationUnit)), this,
SLOT(DurationUnitChanged(DurationUnit)));
QWidget::connect(_dur, SIGNAL(ConditionChanged(DurationCondition)),
QWidget::connect(_dur, SIGNAL(ModifierChanged(DurationModifier::Type)),
this,
SLOT(DurationConditionChanged(DurationCondition)));
SLOT(DurationModifierChanged(DurationModifier::Type)));
QWidget::connect(window(), SIGNAL(HighlightConditionsChanged(bool)),
this, SLOT(EnableHighlight(bool)));
@@ -194,9 +279,9 @@ void MacroConditionEdit::UpdateEntryData(const std::string &id)
SetLogicSelection();
_section->SetContent(widget, (*_entryData)->GetCollapsed());
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
auto constraint = (*_entryData)->GetDurationConstraint();
_dur->SetValue(constraint);
_dur->setVisible(MacroConditionFactory::UsesDurationModifier(id));
auto modifier = (*_entryData)->GetDurationModifier();
_dur->SetValue(modifier);
SetFocusPolicyOfWidgets();
}
@@ -215,7 +300,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
auto macro = _entryData->get()->GetMacro();
std::string id = MacroConditionFactory::GetIdByName(text);
auto temp = DurationConstraint();
auto temp = DurationModifier();
_dur->SetValue(temp);
HeaderInfoChanged("");
{
@@ -231,7 +316,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
this, SLOT(HeaderInfoChanged(const QString &)));
_section->SetContent(widget);
_dur->setVisible(MacroConditionFactory::UsesDurationConstraint(id));
_dur->setVisible(MacroConditionFactory::UsesDurationModifier(id));
SetFocusPolicyOfWidgets();
}
@@ -245,14 +330,14 @@ void MacroConditionEdit::DurationChanged(double seconds)
(*_entryData)->SetDuration(seconds);
}
void MacroConditionEdit::DurationConditionChanged(DurationCondition cond)
void MacroConditionEdit::DurationModifierChanged(DurationModifier::Type m)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
(*_entryData)->SetDurationCondition(cond);
(*_entryData)->SetDurationModifier(m);
}
void MacroConditionEdit::DurationUnitChanged(DurationUnit unit)
@@ -352,13 +437,13 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
{
std::lock_guard<std::mutex> lock(switcher->m);
conditionsList->Remove(idx);
macro->Conditions().erase(macro->Conditions().begin() + idx);
macro->UpdateConditionIndices();
if (idx == 0 && macro->Conditions().size() > 0) {
auto newRoot = macro->Conditions().at(0);
newRoot->SetLogicType(LogicType::ROOT_NONE);
}
conditionsList->Remove(idx);
SetConditionData(*macro);
}
MacroConditionSelectionChanged(-1);

View File

@@ -329,18 +329,30 @@ void MacroConditionMedia::ResetSignalHandler()
void MacroConditionMedia::MediaStopped(void *data, calldata_t *)
{
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
const auto macro = media->GetMacro();
if (macro && macro->Paused()) {
return;
}
media->_stopped = true;
}
void MacroConditionMedia::MediaEnded(void *data, calldata_t *)
{
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
const auto macro = media->GetMacro();
if (macro && macro->Paused()) {
return;
}
media->_ended = true;
}
void MacroConditionMedia::MediaNext(void *data, calldata_t *)
{
MacroConditionMedia *media = static_cast<MacroConditionMedia *>(data);
const auto macro = media->GetMacro();
if (macro && macro->Paused()) {
return;
}
media->_next = true;
}

View File

@@ -28,19 +28,20 @@ bool MacroConditionScene::CheckCondition()
switch (_type) {
case SceneType::CURRENT:
if (!_useTransitionTargetScene) {
return switcher->currentScene == _scene.GetScene(false);
} else {
bool match = false;
if (_useTransitionTargetScene) {
auto current = obs_frontend_get_current_scene();
auto weak = obs_source_get_weak_source(current);
match = weak == _scene.GetScene(false);
bool match = weak == _scene.GetScene(false);
obs_weak_source_release(weak);
obs_source_release(current);
return match;
}
return switcher->currentScene == _scene.GetScene(false);
case SceneType::PREVIOUS:
if (switcher->anySceneTransitionStarted() &&
_useTransitionTargetScene) {
return switcher->currentScene == _scene.GetScene(false);
}
return switcher->previousScene == _scene.GetScene(false);
case SceneType::CHANGED:
return sceneChanged;
@@ -168,8 +169,18 @@ void MacroConditionSceneEdit::SetWidgetVisibility()
{
_scenes->setVisible(_entryData->_type == SceneType::CURRENT ||
_entryData->_type == SceneType::PREVIOUS);
_useTransitionTargetScene->setVisible(_entryData->_type ==
SceneType::CURRENT);
_useTransitionTargetScene->setVisible(
_entryData->_type == SceneType::CURRENT ||
_entryData->_type == SceneType::PREVIOUS);
if (_entryData->_type == SceneType::PREVIOUS) {
_useTransitionTargetScene->setText(obs_module_text(
"AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour"));
}
if (_entryData->_type == SceneType::CURRENT) {
_useTransitionTargetScene->setText(obs_module_text(
"AdvSceneSwitcher.condition.scene.currentSceneTransitionBehaviour"));
}
adjustSize();
}

View File

@@ -36,20 +36,6 @@ bool isCurrentTransition(OBSWeakSource &t)
return match;
}
// Relies on the fact that switcher->currentScene will only be updated on event
// OBS_FRONTEND_EVENT_SCENE_CHANGED but obs_frontend_get_current_scene() will
// already return the scene to be transitioned to.
bool anySceneTransitionStarted()
{
bool ret;
auto currentSceneSrouce = obs_frontend_get_current_scene();
auto currentScene = obs_source_get_weak_source(currentSceneSrouce);
ret = switcher->currentScene != currentScene;
obs_weak_source_release(currentScene);
obs_source_release(currentSceneSrouce);
return ret;
}
bool isTargetScene(OBSWeakSource &target)
{
auto source = obs_frontend_get_current_scene();
@@ -64,7 +50,7 @@ bool MacroConditionTransition::CheckCondition()
{
bool anyTransitionEnded = _lastTransitionEndTime !=
switcher->lastTransitionEndTime;
bool anyTransitionStarted = anySceneTransitionStarted();
bool anyTransitionStarted = switcher->anySceneTransitionStarted();
bool transitionStarted = false;
bool transitionEnded = false;

141
src/macro-condition.cpp Normal file
View File

@@ -0,0 +1,141 @@
#include "headers/macro-condition.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
{LogicType::AND, {"AdvSceneSwitcher.logic.and"}},
{LogicType::OR, {"AdvSceneSwitcher.logic.or"}},
{LogicType::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
{LogicType::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
{LogicType::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
};
void DurationModifier::Save(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
obs_data_set_int(obj, condName, static_cast<int>(_type));
_dur.Save(obj, secondsName, unitName);
}
void DurationModifier::Load(obs_data_t *obj, const char *condName,
const char *secondsName, const char *unitName)
{
// For backwards compatability check if duration value exist without
// time constraint condition - if so assume DurationCondition::MORE
if (!obs_data_has_user_value(obj, condName) &&
obs_data_has_user_value(obj, secondsName)) {
obs_data_set_int(obj, condName, static_cast<int>(Type::MORE));
}
_type = static_cast<Type>(obs_data_get_int(obj, condName));
_dur.Load(obj, secondsName, unitName);
}
bool DurationModifier::DurationReached()
{
switch (_type) {
case DurationModifier::Type::NONE:
return true;
break;
case DurationModifier::Type::MORE:
return _dur.DurationReached();
break;
case DurationModifier::Type::EQUAL:
if (_dur.DurationReached() && !_timeReached) {
_timeReached = true;
return true;
}
break;
case DurationModifier::Type::LESS:
return !_dur.DurationReached();
break;
case DurationModifier::Type::WITHIN:
if (_dur.IsReset()) {
return false;
}
return !_dur.DurationReached();
break;
default:
break;
}
return false;
}
void DurationModifier::Reset()
{
_timeReached = false;
_dur.Reset();
}
bool MacroCondition::Save(obs_data_t *obj)
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
_duration.Save(obj);
return true;
}
bool MacroCondition::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
_duration.Load(obj);
return true;
}
void MacroCondition::ResetDuration()
{
_duration.Reset();
}
void MacroCondition::CheckDurationModifier(bool &val)
{
if (_duration.GetType() != DurationModifier::Type::WITHIN && !val) {
_duration.Reset();
}
if (_duration.GetType() == DurationModifier::Type::WITHIN && val) {
_duration.Reset();
}
switch (_duration.GetType()) {
case DurationModifier::Type::NONE:
case DurationModifier::Type::MORE:
case DurationModifier::Type::EQUAL:
case DurationModifier::Type::LESS:
if (!val) {
_duration.Reset();
}
val = val && _duration.DurationReached();
return;
case DurationModifier::Type::WITHIN:
if (val) {
_duration.SetTimeRemaining(
_duration.GetDuration().seconds);
}
val = val || _duration.DurationReached();
break;
default:
break;
}
}
void MacroCondition::SetDurationModifier(DurationModifier::Type m)
{
_duration.SetModifier(m);
}
void MacroCondition::SetDurationUnit(DurationUnit u)
{
_duration.SetUnit(u);
}
void MacroCondition::SetDuration(double seconds)
{
_duration.SetValue(seconds);
}
void MacroRefCondition::ResolveMacroRef()
{
_macro.UpdateRef();
}

View File

@@ -31,14 +31,17 @@ void MacroProperties::Load(obs_data_t *obj)
}
MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
const MacroProperties &prop)
const MacroProperties &prop,
Macro *macro)
: QDialog(parent),
_executed(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightExecutedMacros"))),
_conditions(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
_actions(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.highlightPerformedActions")))
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
_hotkeys(new QCheckBox(
obs_module_text("AdvSceneSwitcher.macroTab.disableHotkeys")))
{
setModal(true);
setWindowModality(Qt::WindowModality::WindowModal);
@@ -49,11 +52,17 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
_executed->setChecked(prop._highlightExecuted);
_conditions->setChecked(prop._highlightConditions);
_actions->setChecked(prop._highlightActions);
if (macro) {
_hotkeys->setChecked(macro->PauseHotkeysEnabled());
} else {
_hotkeys->hide();
}
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(_executed);
layout->addWidget(_conditions);
layout->addWidget(_actions);
layout->addWidget(_hotkeys);
setLayout(layout);
QDialogButtonBox *buttonbox = new QDialogButtonBox(
@@ -65,9 +74,10 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
}
bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
MacroProperties &userInput)
MacroProperties &userInput,
Macro *macro)
{
MacroPropertiesDialog dialog(parent, userInput);
MacroPropertiesDialog dialog(parent, userInput, macro);
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
if (dialog.exec() != DialogCode::Accepted) {
return false;
@@ -75,5 +85,8 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
userInput._highlightExecuted = dialog._executed->isChecked();
userInput._highlightConditions = dialog._conditions->isChecked();
userInput._highlightActions = dialog._actions->isChecked();
if (macro) {
macro->EnablePauseHotkeys(dialog._hotkeys->isChecked());
}
return true;
}

42
src/macro-ref.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "headers/macro-ref.hpp"
#include "headers/macro.hpp"
MacroRef::MacroRef(std::string name) : _name(name)
{
UpdateRef();
}
void MacroRef::UpdateRef()
{
_ref = GetMacroByName(_name.c_str());
}
void MacroRef::UpdateRef(std::string newName)
{
_name = newName;
UpdateRef();
}
void MacroRef::UpdateRef(QString newName)
{
_name = newName.toStdString();
UpdateRef();
}
void MacroRef::Save(obs_data_t *obj)
{
if (_ref) {
obs_data_set_string(obj, "macro", _ref->Name().c_str());
}
}
void MacroRef::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "macro");
UpdateRef();
}
Macro *MacroRef::get()
{
return _ref;
}
Macro *MacroRef::operator->()
{
return _ref;
}

View File

@@ -98,12 +98,13 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
if (!item) {
return;
}
int idx = ui->macros->currentRow();
delete item;
QString name;
{
std::lock_guard<std::mutex> lock(switcher->m);
switcher->abortMacroWait = true;
switcher->macroWaitCv.notify_all();
int idx = ui->macros->currentRow();
QString::fromStdString(switcher->macros[idx]->Name());
switcher->macros.erase(switcher->macros.begin() + idx);
for (auto &m : switcher->macros) {
@@ -111,8 +112,6 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
}
}
delete item;
if (ui->macros->count() == 0) {
ui->macroHelp->setVisible(true);
}
@@ -360,6 +359,10 @@ void AdvSceneSwitcher::on_macros_currentRowChanged(int idx)
if (idx == -1) {
SetMacroEditAreaDisabled(true);
conditionsList->Clear();
actionsList->Clear();
conditionsList->SetHelpMsgVisible(true);
actionsList->SetHelpMsgVisible(true);
return;
}
@@ -408,7 +411,8 @@ void AdvSceneSwitcher::HighlightOnChange()
void AdvSceneSwitcher::on_macroProperties_clicked()
{
MacroProperties prop = switcher->macroProperties;
bool accepted = MacroPropertiesDialog::AskForSettings(this, prop);
bool accepted = MacroPropertiesDialog::AskForSettings(
this, prop, getSelectedMacro());
if (!accepted) {
return;
}

View File

@@ -11,19 +11,8 @@
constexpr int perfLogThreshold = 300;
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
{LogicType::AND, {"AdvSceneSwitcher.logic.and"}},
{LogicType::OR, {"AdvSceneSwitcher.logic.or"}},
{LogicType::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
{LogicType::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
{LogicType::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
};
Macro::Macro(const std::string &name)
{
SetupHotkeys();
SetName(name);
}
@@ -54,10 +43,7 @@ bool Macro::CeckMatch()
ms.count(), c->GetId().c_str(), Name().c_str());
}
if (!cond) {
c->ResetDuration();
}
cond = cond && c->DurationReached();
c->CheckDurationModifier(cond);
switch (c->GetLogicType()) {
case LogicType::NONE:
@@ -262,6 +248,7 @@ bool Macro::Save(obs_data_t *obj)
obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "onChange", _matchOnChange);
obs_data_set_bool(obj, "registerHotkeys", _registerHotkeys);
obs_data_array_t *pauseHotkey = obs_hotkey_save(_pauseHotkey);
obs_data_set_array(obj, "pauseHotkey", pauseHotkey);
obs_data_array_release(pauseHotkey);
@@ -341,6 +328,11 @@ bool Macro::Load(obs_data_t *obj)
_runInParallel = obs_data_get_bool(obj, "parallel");
_matchOnChange = obs_data_get_bool(obj, "onChange");
obs_data_set_default_bool(obj, "registerHotkeys", true);
_registerHotkeys = obs_data_get_bool(obj, "registerHotkeys");
if (_registerHotkeys) {
SetupHotkeys();
}
obs_data_array_t *pauseHotkey = obs_data_get_array(obj, "pauseHotkey");
obs_hotkey_load(_pauseHotkey, pauseHotkey);
obs_data_array_release(pauseHotkey);
@@ -352,7 +344,6 @@ bool Macro::Load(obs_data_t *obj)
obs_data_get_array(obj, "togglePauseHotkey");
obs_hotkey_load(_togglePauseHotkey, togglePauseHotkey);
obs_data_array_release(togglePauseHotkey);
SetHotkeysDesc();
bool root = true;
@@ -470,6 +461,25 @@ void Macro::ResetUIHelpers()
}
}
void Macro::EnablePauseHotkeys(bool value)
{
if (_registerHotkeys == value) {
return;
}
if (_registerHotkeys) {
ClearHotkeys();
} else {
SetupHotkeys();
}
_registerHotkeys = value;
}
bool Macro::PauseHotkeysEnabled()
{
return _registerHotkeys;
}
static void pauseCB(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
{
if (pressed) {
@@ -556,61 +566,6 @@ void Macro::SetHotkeysDesc()
_name, _togglePauseHotkey);
}
bool MacroCondition::Save(obs_data_t *obj)
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
_duration.Save(obj);
return true;
}
bool MacroCondition::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
_duration.Load(obj);
return true;
}
void MacroCondition::SetDurationConstraint(const DurationConstraint &dur)
{
_duration = dur;
}
void MacroCondition::SetDurationCondition(DurationCondition cond)
{
_duration.SetCondition(cond);
}
void MacroCondition::SetDurationUnit(DurationUnit u)
{
_duration.SetUnit(u);
}
void MacroCondition::SetDuration(double seconds)
{
_duration.SetValue(seconds);
}
bool MacroAction::Save(obs_data_t *obj)
{
MacroSegment::Save(obj);
obs_data_set_string(obj, "id", GetId().c_str());
return true;
}
bool MacroAction::Load(obs_data_t *obj)
{
MacroSegment::Load(obj);
return true;
}
void MacroAction::LogAction()
{
vblog(LOG_INFO, "performed action %s", GetId().c_str());
}
void SwitcherData::saveMacros(obs_data_t *obj)
{
switcher->macroProperties.Save(obj);
@@ -697,60 +652,3 @@ Macro *GetMacroByQString(const QString &name)
{
return GetMacroByName(name.toUtf8().constData());
}
MacroRef::MacroRef(std::string name) : _name(name)
{
UpdateRef();
}
void MacroRef::UpdateRef()
{
_ref = GetMacroByName(_name.c_str());
}
void MacroRef::UpdateRef(std::string newName)
{
_name = newName;
UpdateRef();
}
void MacroRef::UpdateRef(QString newName)
{
_name = newName.toStdString();
UpdateRef();
}
void MacroRef::Save(obs_data_t *obj)
{
if (_ref) {
obs_data_set_string(obj, "macro", _ref->Name().c_str());
}
}
void MacroRef::Load(obs_data_t *obj)
{
_name = obs_data_get_string(obj, "macro");
UpdateRef();
}
Macro *MacroRef::get()
{
return _ref;
}
Macro *MacroRef::operator->()
{
return _ref;
}
void MacroRefCondition::ResolveMacroRef()
{
_macro.UpdateRef();
}
void MacroRefAction::ResolveMacroRef()
{
_macro.UpdateRef();
}
void MultiMacroRefAction::ResolveMacroRef()
{
for (auto &m : _macros) {
m.UpdateRef();
}
}

View File

@@ -19,9 +19,9 @@ void GetWindowList(std::vector<std::string> &windows)
windows.resize(0);
@autoreleasepool {
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSMutableArray *apps = (__bridge NSMutableArray *)cfApps;
for (NSDictionary *app in apps) {
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
@@ -37,14 +37,17 @@ void GetWindowList(std::vector<std::string> &windows)
if (!name.empty() &&
find(windows.begin(), windows.end(), name) ==
windows.end())
windows.end()) {
windows.emplace_back(name);
}
if (!owner.empty() &&
find(windows.begin(), windows.end(), owner) ==
windows.end())
windows.end()) {
windows.emplace_back(owner);
}
}
apps = nil;
CFRelease(cfApps);
}
}
@@ -61,12 +64,10 @@ void GetWindowList(QStringList &windows)
void GetCurrentWindowTitle(std::string &title)
{
title.resize(0);
@autoreleasepool {
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID);
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSMutableArray *apps = (__bridge NSMutableArray *)cfApps;
for (NSDictionary *app in apps) {
int layer =
[[app objectForKey:@"kCGWindowLayer"] intValue];
@@ -85,14 +86,16 @@ void GetCurrentWindowTitle(std::string &title)
lengthOfBytesUsingEncoding:
NSUTF8StringEncoding]);
if (!name.empty())
if (!name.empty()) {
title = name;
else if (!owner.empty())
} else if (!owner.empty()) {
title = owner;
}
break;
}
}
apps = nil;
CFRelease(cfApps);
}
}
@@ -165,9 +168,9 @@ bool isMaximized(const std::string &title)
{
@autoreleasepool {
NSArray *screens = [NSScreen screens];
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSMutableArray *apps = (__bridge NSMutableArray *)cfApps;
for (NSDictionary *app in apps) {
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
@@ -190,11 +193,15 @@ bool isMaximized(const std::string &title)
screen) &&
isWindowMaximizedOnScreen(app,
screen)) {
apps = nil;
CFRelease(cfApps);
return true;
}
}
}
}
apps = nil;
CFRelease(cfApps);
}
return false;
}
@@ -214,9 +221,9 @@ bool isFullscreen(const std::string &title)
{
@autoreleasepool {
NSArray *screens = [NSScreen screens];
NSMutableArray *apps =
(__bridge NSMutableArray *)CGWindowListCopyWindowInfo(
kCGWindowListOptionAll, kCGNullWindowID);
CFArrayRef cfApps = CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSMutableArray *apps = (__bridge NSMutableArray *)cfApps;
for (NSDictionary *app in apps) {
std::string name([[app objectForKey:@"kCGWindowName"]
UTF8String],
@@ -237,12 +244,17 @@ bool isFullscreen(const std::string &title)
for (NSScreen *screen in screens) {
if (isWindowOriginOnScreen(app, screen,
true) &&
isWindowFullscreenOnScreen(app,
screen))
isWindowFullscreenOnScreen(
app, screen)) {
apps = nil;
CFRelease(cfApps);
return true;
}
}
}
}
apps = nil;
CFRelease(cfApps);
}
return false;
}
@@ -264,12 +276,14 @@ void GetProcessList(QStringList &list)
NSArray *array = [ws runningApplications];
for (NSRunningApplication *app in array) {
NSString *name = app.localizedName;
if (!name)
if (!name) {
continue;
}
const char *str = name.UTF8String;
if (str && *str)
if (str && *str) {
list << (str);
}
}
}
}
@@ -412,89 +426,6 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
// on MacOS
canSimulateKeyPresses = false;
return;
// Check premissions
NSDictionary *options =
@{(__bridge id)kAXTrustedCheckOptionPrompt: @NO};
if (!AXIsProcessTrustedWithOptions((CFDictionaryRef)options)) {
NSString *urlString =
@"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
[[NSWorkspace sharedWorkspace]
openURL:[NSURL URLWithString:urlString]];
canSimulateKeyPresses = false;
return;
}
long modifierFlags = 0;
auto source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
if (!source) {
canSimulateKeyPresses = false;
return;
}
// Press keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
CGKeyCode inputKeyCode = it->second;
CGEventRef keyPress =
CGEventCreateKeyboardEvent(source, inputKeyCode, true);
if (!keyPress) {
canSimulateKeyPresses = false;
CFRelease(source);
return;
}
CGEventSetFlags(keyPress, modifierFlags);
CGEventPost(kCGAnnotatedSessionEventTap, keyPress);
CFRelease(keyPress);
switch (it->first) {
case HotkeyType::Key_Shift_L:
case HotkeyType::Key_Shift_R:
modifierFlags |= NX_SHIFTMASK;
break;
case HotkeyType::Key_Control_L:
case HotkeyType::Key_Control_R:
modifierFlags |= NX_CONTROLMASK;
break;
case HotkeyType::Key_Alt_L:
case HotkeyType::Key_Alt_R:
modifierFlags |= NX_ALTERNATEMASK;
break;
case HotkeyType::Key_Win_L:
case HotkeyType::Key_Win_R:
modifierFlags |= NX_COMMANDMASK;
break;
default:
break;
}
}
// When instantly releasing the key presses OBS might miss them
std::this_thread::sleep_for(std::chrono::milliseconds(300));
// Release keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
CGKeyCode inputKeyCode = it->second;
CGEventRef keyRelease =
CGEventCreateKeyboardEvent(source, inputKeyCode, false);
if (!keyRelease) {
canSimulateKeyPresses = false;
CFRelease(source);
return;
}
CGEventPost(kCGAnnotatedSessionEventTap, keyRelease);
CFRelease(keyRelease);
}
CFRelease(source);
}
void PlatformCleanup() {}

View File

@@ -58,6 +58,7 @@ OBSWeakSource SceneGroup::getNextSceneRandom()
}
int rIdx = lastRandomScene;
std::srand(std::time(0));
do {
rIdx = rand() % scenes.size();
} while (rIdx == lastRandomScene);

View File

@@ -125,6 +125,7 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this,
SLOT(IdxChanged(int)));
auto layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(_idx);
layout->addWidget(_sceneItems);
setLayout(layout);