mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7afdd817b | ||
|
|
efaf9a2ef3 | ||
|
|
edb2952fd6 | ||
|
|
088828a351 | ||
|
|
e359b7fa00 | ||
|
|
44fc5177d6 | ||
|
|
604a27141f | ||
|
|
fd65a84d7d | ||
|
|
966b389807 | ||
|
|
a6bcce5a63 | ||
|
|
530fbdd282 | ||
|
|
54f1051456 | ||
|
|
d136bf5561 | ||
|
|
5cba948e02 | ||
|
|
ab922f6735 | ||
|
|
d71c87535f | ||
|
|
ce501bd972 | ||
|
|
ec5944f53c | ||
|
|
5e64e0bbaa | ||
|
|
e16f1d732d | ||
|
|
cdacfc946c | ||
|
|
26e854f0a3 | ||
|
|
2bc89364b2 | ||
|
|
e080d2de9b | ||
|
|
ca1262aeb7 | ||
|
|
cbc95e2095 | ||
|
|
9c5051cbf8 | ||
|
|
e6ca3390a2 | ||
|
|
e74b531905 |
@@ -145,8 +145,10 @@ target_sources(
|
||||
# Utility function sources
|
||||
target_sources(
|
||||
${LIB_NAME}
|
||||
PRIVATE lib/utils/action-queue.cpp
|
||||
lib/utils/action-queue.hpp
|
||||
PRIVATE lib/queue/action-queue.cpp
|
||||
lib/queue/action-queue.hpp
|
||||
lib/queue/action-queue-tab.cpp
|
||||
lib/queue/action-queue-tab.hpp
|
||||
lib/utils/backup.cpp
|
||||
lib/utils/backup.hpp
|
||||
lib/utils/curl-helper.cpp
|
||||
@@ -191,6 +193,10 @@ target_sources(
|
||||
lib/utils/regex-config.hpp
|
||||
lib/utils/resizing-text-edit.cpp
|
||||
lib/utils/resizing-text-edit.hpp
|
||||
lib/utils/resource-table.cpp
|
||||
lib/utils/resource-table.hpp
|
||||
lib/utils/resource-table-hotkey-handler.cpp
|
||||
lib/utils/resource-table-hotkey-handler.hpp
|
||||
lib/utils/scene-selection.cpp
|
||||
lib/utils/scene-selection.hpp
|
||||
lib/utils/scene-switch-helpers.cpp
|
||||
@@ -217,6 +223,8 @@ target_sources(
|
||||
lib/utils/switch-button.hpp
|
||||
lib/utils/sync-helpers.cpp
|
||||
lib/utils/sync-helpers.hpp
|
||||
lib/utils/tab-helpers.cpp
|
||||
lib/utils/tab-helpers.hpp
|
||||
lib/utils/temp-variable.cpp
|
||||
lib/utils/temp-variable.hpp
|
||||
lib/utils/ui-helpers.cpp
|
||||
@@ -234,6 +242,7 @@ target_sources(
|
||||
lib/variables/variable-string.cpp
|
||||
lib/variables/variable-string.hpp
|
||||
lib/variables/variable-tab.cpp
|
||||
lib/variables/variable-tab.hpp
|
||||
lib/variables/variable-text-edit.cpp
|
||||
lib/variables/variable-text-edit.hpp
|
||||
lib/variables/variable.cpp
|
||||
@@ -268,6 +277,7 @@ target_include_directories(
|
||||
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/lib"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/legacy"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/macro"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/queue"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/utils"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/variables"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/forms")
|
||||
|
||||
@@ -12,8 +12,14 @@ if(NOT DEFINED PLUGIN_BUILD_NUMBER AND EXISTS "${_BUILD_NUMBER_CACHE}")
|
||||
file(READ "${_BUILD_NUMBER_CACHE}" PLUGIN_BUILD_NUMBER)
|
||||
math(EXPR PLUGIN_BUILD_NUMBER "${PLUGIN_BUILD_NUMBER}+1")
|
||||
elseif(NOT DEFINED PLUGIN_BUILD_NUMBER)
|
||||
if($ENV{CI} AND $ENV{GITHUB_RUN_ID})
|
||||
set(PLUGIN_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}")
|
||||
if($ENV{CI})
|
||||
if($ENV{GITHUB_RUN_ID})
|
||||
set(PLUGIN_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}")
|
||||
elseif($ENV{GITLAB_RUN_ID})
|
||||
set(PLUGIN_BUILD_NUMBER "$ENV{GITLAB_RUN_ID}")
|
||||
else()
|
||||
set(PLUGIN_BUILD_NUMBER "1")
|
||||
endif()
|
||||
else()
|
||||
set(PLUGIN_BUILD_NUMBER "1")
|
||||
endif()
|
||||
|
||||
@@ -68,9 +68,14 @@ else()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
# Disable warning for https://github.com/WarmUpTill/SceneSwitcher/issues/1091
|
||||
add_compile_options(-Wno-error=psabi)
|
||||
|
||||
# Disable false-positive warning in GCC 12.1.0 and later
|
||||
add_compile_options(-Wno-error=maybe-uninitialized)
|
||||
add_compile_options(-Wno-error=stringop-overflow)
|
||||
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)
|
||||
add_compile_options(-Wno-error=maybe-uninitialized)
|
||||
add_compile_options(-Wno-error=stringop-overflow)
|
||||
endif()
|
||||
|
||||
# Add warning for infinite recursion (added in GCC 12)
|
||||
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
|
||||
|
||||
@@ -287,8 +287,8 @@ AdvSceneSwitcher.condition.sceneOrder.entry="Auf{{scenes}}{{sources}}{{condition
|
||||
AdvSceneSwitcher.condition.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Makro-Trigger-Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Hinweis: Die Tastenkombinationen für diesen Hotkey können in den OBS-Einstellungen konfiguriert werden"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="Hotkey ist gedrückt"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="Name: {{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.keyState="Hotkey ist{{keyState}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="Name:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="Replay Buffer"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="Replay Buffer gestoppt"
|
||||
AdvSceneSwitcher.condition.replay.state.started="Replay Buffer gestartet"
|
||||
|
||||
@@ -88,6 +88,47 @@ AdvSceneSwitcher.variableTab.lastChanged.text="%1 seconds ago"
|
||||
AdvSceneSwitcher.variableTab.lastChanged.text.none="No change since launch"
|
||||
AdvSceneSwitcher.variableTab.lastChanged.tooltip="Times changed: %1\n\nPrevious value: %2"
|
||||
|
||||
; Action Queue Tab
|
||||
AdvSceneSwitcher.actionQueueTab.title="Action Queues"
|
||||
AdvSceneSwitcher.actionQueueTab.help="Action queues are executed sequentially but in parallel to the reset of the macro system.\nThe first action added to the queue will be the first one to be processed.\n\nClick on the highlighted plus symbol to add a new queue."
|
||||
AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip="Add new action queue"
|
||||
AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip="Remove selected action queues"
|
||||
AdvSceneSwitcher.actionQueueTab.name.header="Name"
|
||||
AdvSceneSwitcher.actionQueueTab.size.header="Size"
|
||||
AdvSceneSwitcher.actionQueueTab.isRunning.header="Is running"
|
||||
AdvSceneSwitcher.actionQueueTab.runOnStartup.header="Run on startup"
|
||||
AdvSceneSwitcher.actionQueueTab.yes="Yes"
|
||||
AdvSceneSwitcher.actionQueueTab.no="No"
|
||||
AdvSceneSwitcher.actionQueueTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
|
||||
AdvSceneSwitcher.actionQueueTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 action queues?"
|
||||
|
||||
; Websocket Connections Tab
|
||||
AdvSceneSwitcher.websocketConnectionTab.title="Websocket Connections"
|
||||
AdvSceneSwitcher.websocketConnectionTab.help="Websocket connections can be used to communicate with other OBS instances or programs.\n\nClick on the highlighted plus symbol to add a new connection."
|
||||
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip="Add new webscoket connection"
|
||||
AdvSceneSwitcher.websocketConnectionTab.websocketConnectionRemoveButton.tooltip="Remove selected websocket connections"
|
||||
AdvSceneSwitcher.websocketConnectionTab.name.header="Name"
|
||||
AdvSceneSwitcher.websocketConnectionTab.address.header="Address"
|
||||
AdvSceneSwitcher.websocketConnectionTab.port.header="Port"
|
||||
AdvSceneSwitcher.websocketConnectionTab.protocol.header="Is using OBS protocol?"
|
||||
AdvSceneSwitcher.websocketConnectionTab.protocol.yes="Yes"
|
||||
AdvSceneSwitcher.websocketConnectionTab.protocol.no="No"
|
||||
AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
|
||||
AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 connections?"
|
||||
|
||||
; Twitch Connections Tab
|
||||
AdvSceneSwitcher.twitchConnectionTab.title="Twitch Connections"
|
||||
AdvSceneSwitcher.twitchConnectionTab.help="Twitch connections can be used to use Twitch events as triggers to execute actions or perform actions on the linked Twitch account.\n\nClick on the highlighted plus symbol to add a new connection."
|
||||
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip.tooltip="Add new Twitch connection"
|
||||
AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip="Remove selected Twitch connections"
|
||||
AdvSceneSwitcher.twitchConnectionTab.name.header="Name"
|
||||
AdvSceneSwitcher.twitchConnectionTab.isValid.header="Is valid?"
|
||||
AdvSceneSwitcher.twitchConnectionTab.permissionCount.header="Number of permissions"
|
||||
AdvSceneSwitcher.twitchConnectionTab.yes="Yes"
|
||||
AdvSceneSwitcher.twitchConnectionTab.no="No"
|
||||
AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text="Are you sure you want to remove \"%1\"?"
|
||||
AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text="Are you sure you want to remove %1 Twitch connections?"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
AdvSceneSwitcher.macroTab.macros="Macros"
|
||||
@@ -124,6 +165,7 @@ AdvSceneSwitcher.macroTab.rename="Rename"
|
||||
AdvSceneSwitcher.macroTab.remove="Remove"
|
||||
AdvSceneSwitcher.macroTab.export="Export"
|
||||
AdvSceneSwitcher.macroTab.export.info="Paste the string below into the import dialog to import the selected macros:"
|
||||
AdvSceneSwitcher.macroTab.export.usePlainText="Use plain text"
|
||||
AdvSceneSwitcher.macroTab.import="Import"
|
||||
AdvSceneSwitcher.macroTab.import.info="Paste the export string into the below text box to import macros:"
|
||||
AdvSceneSwitcher.macroTab.import.invalid="Invalid import data provided!"
|
||||
@@ -132,6 +174,9 @@ AdvSceneSwitcher.macroTab.expandAll="Expand all"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Collapse all"
|
||||
AdvSceneSwitcher.macroTab.maximize="Maximize"
|
||||
AdvSceneSwitcher.macroTab.minimize="Minimize"
|
||||
AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label"
|
||||
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label"
|
||||
AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..."
|
||||
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
|
||||
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
|
||||
AdvSceneSwitcher.macroTab.generalSettings="General settings"
|
||||
@@ -437,10 +482,12 @@ AdvSceneSwitcher.condition.sceneOrder.type.position="Is at position"
|
||||
AdvSceneSwitcher.condition.sceneOrder.positionInfo="The position value starts at the bottom with 0 and increases by one for each scene item including the ones in scene groups"
|
||||
AdvSceneSwitcher.condition.sceneOrder.entry="On{{scenes}}{{sources}}{{conditions}}{{sources2}}{{position}}"
|
||||
AdvSceneSwitcher.condition.hotkey="Hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.pressed="pressed"
|
||||
AdvSceneSwitcher.condition.hotkey.released="released"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Macro trigger hotkey"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Note: You can configure the keybindings for this hotkey in the OBS settings window"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="Hotkey is pressed"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="Name:{{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.keyState="Hotkey is{{keyState}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="Name:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="Replay buffer"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="Replay buffer stopped"
|
||||
AdvSceneSwitcher.condition.replay.state.started="Replay buffer started"
|
||||
@@ -545,7 +592,15 @@ AdvSceneSwitcher.condition.run="Run"
|
||||
AdvSceneSwitcher.condition.run.entry="Process exits before timeout of{{timeout}}seconds"
|
||||
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Check for exit code{{exitCode}}"
|
||||
AdvSceneSwitcher.condition.midi="MIDI"
|
||||
AdvSceneSwitcher.condition.midi.entry="Mesasge was received from{{device}}which matches:"
|
||||
<<<<<<< HEAD
|
||||
AdvSceneSwitcher.condition.midi.entry="Message was received from{{device}}which matches:"
|
||||
=======
|
||||
AdvSceneSwitcher.condition.midi.condition.message="MIDI message was received"
|
||||
AdvSceneSwitcher.condition.midi.condition.deviceName="MIDI device is connected"
|
||||
AdvSceneSwitcher.condition.midi.condition.deviceName.info="On some platforms with certain APIs dynamic MIDI device detection might not function.\nSo this type of check might not be 100% reliable in all circumstances."
|
||||
AdvSceneSwitcher.condition.midi.entry.message="{{conditions}}from{{device}}which matches:"
|
||||
AdvSceneSwitcher.condition.midi.entry.deviceName="{{conditions}}{{deviceNames}}{{regex}}{{deviceListInfo}}"
|
||||
>>>>>>> c4e35d1a (Add MIDI device name check)
|
||||
AdvSceneSwitcher.condition.midi.entry.listen="Set MIDI message selection to messages incoming on selected device:{{listenButton}}"
|
||||
AdvSceneSwitcher.condition.display="Display"
|
||||
AdvSceneSwitcher.condition.display.type.displayName="Name of connected displays matches"
|
||||
@@ -1672,6 +1727,9 @@ AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed s
|
||||
AdvSceneSwitcher.tempVar.clipboard.text="Clipboard text"
|
||||
AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the clipboard.\nWill be empty if the clipboard does not contain text."
|
||||
|
||||
AdvSceneSwitcher.tempVar.file.content="File content"
|
||||
AdvSceneSwitcher.tempVar.file.date="File modification date"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
|
||||
@@ -238,8 +238,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="En{{scenes}}{{sources}}{{conditions
|
||||
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.hotkey.entry.name="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"
|
||||
|
||||
@@ -351,8 +351,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="Sur{{scenes}}{{sources}}{{condition
|
||||
AdvSceneSwitcher.condition.hotkey="Raccourci clavier"
|
||||
AdvSceneSwitcher.condition.hotkey.name="Raccourci clavier de déclenchement de macro"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="Remarque : Vous pouvez configurer les raccourcis clavier pour ce raccourci dans la fenêtre de paramètres d'OBS"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="Raccourci clavier est enfoncé"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="Nom :{{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="Nom:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="Tampon de répétition"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="Tampon de répétition arrêté"
|
||||
AdvSceneSwitcher.condition.replay.state.started="Tampon de répétition démarré"
|
||||
@@ -447,7 +446,6 @@ AdvSceneSwitcher.condition.run="Exécution"
|
||||
AdvSceneSwitcher.condition.run.entry="Le processus se termine avant le délai de{{timeout}}secondes"
|
||||
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Vérifier le code de sortie{{exitCode}}
|
||||
AdvSceneSwitcher.condition.midi="MIDI"
|
||||
AdvSceneSwitcher.condition.midi.entry="Message reçu depuis{{device}}qui correspond à :"
|
||||
AdvSceneSwitcher.condition.midi.entry.listen="Définir la sélection des messages MIDI pour les messages entrants sur l'appareil sélectionné :{{listenButton}}"
|
||||
AdvSceneSwitcher.condition.display="Affichage"
|
||||
AdvSceneSwitcher.condition.display.type.displayName="Le nom des écrans connectés correspond à"
|
||||
|
||||
@@ -332,8 +332,7 @@ AdvSceneSwitcher.condition.sceneOrder.entry="在 {{scenes}} {{sources}} {{condit
|
||||
AdvSceneSwitcher.condition.hotkey="热键"
|
||||
AdvSceneSwitcher.condition.hotkey.name="宏触发热键"
|
||||
AdvSceneSwitcher.condition.hotkey.tip="注意:您可以在OBS设置窗口中为此热键配置按键绑定"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line1="热键被按下"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.line2="名称: {{name}}"
|
||||
AdvSceneSwitcher.condition.hotkey.entry.name="名称:{{name}}"
|
||||
AdvSceneSwitcher.condition.replay="回放缓存"
|
||||
AdvSceneSwitcher.condition.replay.state.stopped="回放缓存已停止"
|
||||
AdvSceneSwitcher.condition.replay.state.started="回放缓存已启动"
|
||||
@@ -435,7 +434,6 @@ AdvSceneSwitcher.condition.run="运行"
|
||||
AdvSceneSwitcher.condition.run.entry="进程在超时 {{timeout}} 秒之前退出"
|
||||
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}检查退出代码{{exitCode}}"
|
||||
AdvSceneSwitcher.condition.midi="MIDI"
|
||||
AdvSceneSwitcher.condition.midi.entry="从{{device}} 收到的信息,符合:(从不咕咕的阿坤:这个好像是声卡工作是录音相关的功能,英文太抽象了,我就硬译过来了,如有相关设备,音乐好的,可以根据英文翻译好发给我!)"
|
||||
AdvSceneSwitcher.condition.midi.entry.listen="将MIDI信息选择设置为从选定设备上接收的信息: {{listenButton}}"
|
||||
AdvSceneSwitcher.condition.display="显示器"
|
||||
AdvSceneSwitcher.condition.display.type.displayName="连接的显示器的名称匹配"
|
||||
|
||||
@@ -1602,104 +1602,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="variableTab">
|
||||
<attribute name="title">
|
||||
<string>AdvSceneSwitcher.variableTab.title</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_33">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_25">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="variables">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="variablesHelp">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.variableTab.help</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_30">
|
||||
<item>
|
||||
<widget class="QPushButton" name="variableAdd">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.variableTab.variableAddButton.tooltip</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">addIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="variableRemove">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="themeID" stdset="0">
|
||||
<string notr="true">removeIconSmall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_211">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="networkTab">
|
||||
<attribute name="title">
|
||||
<string>AdvSceneSwitcher.networkTab.title</string>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "status-control.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "tab-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "version.h"
|
||||
|
||||
@@ -44,7 +45,7 @@ AdvSceneSwitcher::~AdvSceneSwitcher()
|
||||
{
|
||||
if (switcher) {
|
||||
switcher->settingsWindowOpened = false;
|
||||
switcher->lastOpenedTab = ui->tabWidget->currentIndex();
|
||||
SaveLastOpenedTab(ui->tabWidget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,11 +113,11 @@ void AdvSceneSwitcher::LoadUI()
|
||||
SetupSceneGroupTab();
|
||||
SetupTriggerTab();
|
||||
SetupMacroTab();
|
||||
SetupVariableTab();
|
||||
SetupOtherTabs(ui->tabWidget);
|
||||
|
||||
SetDeprecationWarnings();
|
||||
SetTabOrder();
|
||||
SetCurrentTab();
|
||||
SetTabOrder(ui->tabWidget);
|
||||
SetCurrentTab(ui->tabWidget);
|
||||
RestoreWindowGeo();
|
||||
CheckFirstTimeSetup();
|
||||
|
||||
@@ -145,12 +146,6 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
|
||||
} else if (pressedKey == Qt::Key_Delete) {
|
||||
RemoveSelectedMacros();
|
||||
}
|
||||
} else if (obj == ui->variables && ui->variables->isVisible()) {
|
||||
if (pressedKey == Qt::Key_F2) {
|
||||
OpenSettingsForSelectedVariable();
|
||||
} else if (pressedKey == Qt::Key_Delete) {
|
||||
RemoveSelectedVariables();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,9 +522,7 @@ bool SwitcherData::AnySceneTransitionStarted()
|
||||
extern "C" EXPORT void FreeSceneSwitcher()
|
||||
{
|
||||
PlatformCleanup();
|
||||
for (const auto &cleanupStep : switcher->pluginCleanupSteps) {
|
||||
cleanupStep();
|
||||
}
|
||||
RunPluginCleanupSteps();
|
||||
|
||||
delete switcher;
|
||||
switcher = nullptr;
|
||||
@@ -759,13 +752,6 @@ QWidget *GetSettingsWindow()
|
||||
|
||||
void SetupActionQueues();
|
||||
|
||||
extern "C" EXPORT void RunPluginPostLoadSteps()
|
||||
{
|
||||
for (const auto &postLoadStep : switcher->pluginPostLoadSteps) {
|
||||
postLoadStep();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
|
||||
translateFunc translate)
|
||||
{
|
||||
@@ -779,9 +765,7 @@ extern "C" EXPORT void InitSceneSwitcher(obs_module_t *module,
|
||||
SetupDock();
|
||||
SetupActionQueues();
|
||||
|
||||
for (const auto &initStep : switcher->pluginInitSteps) {
|
||||
initStep();
|
||||
}
|
||||
RunPluginInitSteps();
|
||||
|
||||
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
|
||||
obs_frontend_add_event_callback(OBSEvent, switcher);
|
||||
|
||||
@@ -34,8 +34,6 @@ public:
|
||||
|
||||
void LoadUI();
|
||||
|
||||
void SetTabOrder();
|
||||
void SetCurrentTab();
|
||||
void RestoreWindowGeo();
|
||||
void CheckFirstTimeSetup();
|
||||
|
||||
@@ -59,7 +57,6 @@ public slots:
|
||||
void on_autoStartEvent_currentIndexChanged(int index);
|
||||
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
|
||||
void on_checkInterval_valueChanged(int value);
|
||||
void on_tabMoved(int from, int to);
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
void on_exportSettings_clicked();
|
||||
void on_importSettings_clicked();
|
||||
@@ -209,19 +206,6 @@ private:
|
||||
|
||||
/* --- End of macro tab section --- */
|
||||
|
||||
/* --- Begin of variable tab section --- */
|
||||
public:
|
||||
void SetupVariableTab();
|
||||
|
||||
public slots:
|
||||
void on_variableAdd_clicked();
|
||||
void on_variableRemove_clicked();
|
||||
|
||||
void OpenSettingsForSelectedVariable();
|
||||
void RemoveSelectedVariables();
|
||||
|
||||
/* --- End of variable tab section --- */
|
||||
|
||||
/* --- Begin of legacy tab section --- */
|
||||
public:
|
||||
void ClearFrames(QListWidget *list);
|
||||
|
||||
120
lib/general.cpp
120
lib/general.cpp
@@ -9,6 +9,7 @@
|
||||
#include "splitter-helpers.hpp"
|
||||
#include "status-control.hpp"
|
||||
#include "switcher-data.hpp"
|
||||
#include "tab-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "variable.hpp"
|
||||
@@ -19,17 +20,6 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static constexpr std::array<const char *, 19> tabNames = {
|
||||
"generalTab", "macroTab", "variableTab",
|
||||
"windowTitleTab", "executableTab", "screenRegionTab",
|
||||
"mediaTab", "fileTab", "randomTab",
|
||||
"timeTab", "idleTab", "sceneSequenceTab",
|
||||
"audioTab", "videoTab", "networkTab",
|
||||
"sceneGroupTab", "transitionsTab", "pauseTab",
|
||||
"sceneTriggerTab"};
|
||||
|
||||
static std::vector<int> tabOrder = std::vector<int>(tabNames.size());
|
||||
|
||||
void AdvSceneSwitcher::reject()
|
||||
{
|
||||
close();
|
||||
@@ -244,14 +234,7 @@ void AdvSceneSwitcher::on_hideLegacyTabs_stateChanged(int state)
|
||||
|
||||
for (int idx = 0; idx < ui->tabWidget->count(); idx++) {
|
||||
if (isLegacyTab(ui->tabWidget->tabText(idx))) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
|
||||
ui->tabWidget->setTabEnabled(idx, !state);
|
||||
ui->tabWidget->setStyleSheet(
|
||||
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
|
||||
#else
|
||||
ui->tabWidget->setTabVisible(idx, !state);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,67 +346,6 @@ void AdvSceneSwitcher::on_importSettings_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
static int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
{
|
||||
int at = -1;
|
||||
QString tabName = tabNames.at(pos);
|
||||
QWidget *page = tabWidget->findChild<QWidget *>(tabName);
|
||||
if (page) {
|
||||
at = tabWidget->indexOf(page);
|
||||
}
|
||||
if (at == -1) {
|
||||
blog(LOG_INFO, "failed to find tab %s",
|
||||
tabName.toUtf8().constData());
|
||||
}
|
||||
|
||||
return at;
|
||||
}
|
||||
|
||||
static bool tabWidgetOrderValid()
|
||||
{
|
||||
auto tmp = std::vector<int>(tabNames.size());
|
||||
std::iota(tmp.begin(), tmp.end(), 0);
|
||||
|
||||
for (auto &p : tmp) {
|
||||
auto it = std::find(tabOrder.begin(), tabOrder.end(), p);
|
||||
if (it == tabOrder.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void resetTabWidgetOrder()
|
||||
{
|
||||
tabOrder = std::vector<int>(tabNames.size());
|
||||
std::iota(tabOrder.begin(), tabOrder.end(), 0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetTabOrder()
|
||||
{
|
||||
if (!tabWidgetOrderValid()) {
|
||||
resetTabWidgetOrder();
|
||||
}
|
||||
|
||||
QTabBar *bar = ui->tabWidget->tabBar();
|
||||
for (int i = 0; i < bar->count(); ++i) {
|
||||
int curPos = findTabIndex(ui->tabWidget, tabOrder[i]);
|
||||
|
||||
if (i != curPos && curPos != -1) {
|
||||
bar->moveTab(curPos, i);
|
||||
}
|
||||
}
|
||||
|
||||
connect(bar, &QTabBar::tabMoved, this, &AdvSceneSwitcher::on_tabMoved);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetCurrentTab()
|
||||
{
|
||||
if (switcher->lastOpenedTab >= 0) {
|
||||
ui->tabWidget->setCurrentIndex(switcher->lastOpenedTab);
|
||||
}
|
||||
}
|
||||
|
||||
static bool windowPosValid(QPoint pos)
|
||||
{
|
||||
return !!QGuiApplication::screenAt(pos);
|
||||
@@ -446,17 +368,12 @@ void AdvSceneSwitcher::CheckFirstTimeSetup()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_tabMoved(int from, int to)
|
||||
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::swap(tabOrder[from], tabOrder[to]);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
switcher->showFrame = false;
|
||||
ClearFrames(ui->screenRegionSwitches);
|
||||
SetShowFrames();
|
||||
@@ -537,7 +454,7 @@ void SwitcherData::LoadSettings(obs_data_t *obj)
|
||||
RunPostLoadSteps();
|
||||
|
||||
// Reset on startup and scene collection change
|
||||
switcher->lastOpenedTab = -1;
|
||||
ResetLastOpenedTab();
|
||||
startupLoadDone = true;
|
||||
}
|
||||
|
||||
@@ -685,13 +602,7 @@ void SwitcherData::LoadGeneralSettings(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::SaveUISettings(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease tabWidgetOrder = obs_data_array_create();
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry = obs_data_create();
|
||||
obs_data_set_int(entry, tabNames[i], tabOrder[i]);
|
||||
obs_data_array_push_back(tabWidgetOrder, entry);
|
||||
}
|
||||
obs_data_set_array(obj, "tabWidgetOrder", tabWidgetOrder);
|
||||
SaveTabOrder(obj);
|
||||
|
||||
obs_data_set_bool(obj, "saveWindowGeo", saveWindowGeo);
|
||||
obs_data_set_int(obj, "windowPosX", windowPos.x());
|
||||
@@ -705,28 +616,7 @@ void SwitcherData::SaveUISettings(obs_data_t *obj)
|
||||
|
||||
void SwitcherData::LoadUISettings(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease defaultTabWidgetOrder = obs_data_array_create();
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry = obs_data_create();
|
||||
obs_data_set_default_int(entry, tabNames[i], i);
|
||||
obs_data_array_push_back(defaultTabWidgetOrder, entry);
|
||||
}
|
||||
obs_data_set_default_array(obj, "tabWidgetOrder",
|
||||
defaultTabWidgetOrder);
|
||||
|
||||
tabOrder.clear();
|
||||
OBSDataArrayAutoRelease tabWidgetOrder =
|
||||
obs_data_get_array(obj, "tabWidgetOrder");
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry =
|
||||
obs_data_array_item(tabWidgetOrder, i);
|
||||
tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(entry, tabNames[i])));
|
||||
}
|
||||
|
||||
if (!tabWidgetOrderValid()) {
|
||||
resetTabWidgetOrder();
|
||||
}
|
||||
LoadTabOrder(obj);
|
||||
|
||||
saveWindowGeo = obs_data_get_bool(obj, "saveWindowGeo");
|
||||
windowPos = {(int)obs_data_get_int(obj, "windowPosX"),
|
||||
|
||||
@@ -141,7 +141,7 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
||||
placeHolderText = format.arg(++i);
|
||||
}
|
||||
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.sceneGroupTab.add"), name,
|
||||
placeHolderText);
|
||||
|
||||
@@ -8,9 +8,13 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool usePlainText = false;
|
||||
|
||||
MacroExportImportDialog::MacroExportImportDialog(Type type)
|
||||
: QDialog(nullptr),
|
||||
_importExportString(new QPlainTextEdit(this))
|
||||
_importExportString(new QPlainTextEdit(this)),
|
||||
_usePlainText(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.export.usePlainText")))
|
||||
{
|
||||
_importExportString->setReadOnly(type == Type::EXPORT_MACRO);
|
||||
auto label = new QLabel(obs_module_text(
|
||||
@@ -30,9 +34,15 @@ MacroExportImportDialog::MacroExportImportDialog(Type type)
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
buttons->setCenterButtons(true);
|
||||
|
||||
_usePlainText->setChecked(usePlainText);
|
||||
_usePlainText->setVisible(type == Type::EXPORT_MACRO);
|
||||
connect(_usePlainText, &QCheckBox::stateChanged, this,
|
||||
&MacroExportImportDialog::UsePlainTextChanged);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(_importExportString);
|
||||
layout->addWidget(_usePlainText);
|
||||
layout->addWidget(buttons);
|
||||
setLayout(layout);
|
||||
|
||||
@@ -65,6 +75,21 @@ void MacroExportImportDialog::ExportMacros(const QString &json)
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void MacroExportImportDialog::UsePlainTextChanged(int value)
|
||||
{
|
||||
if (usePlainText == (!!value)) {
|
||||
return;
|
||||
}
|
||||
const auto current = _importExportString->toPlainText();
|
||||
if (usePlainText) {
|
||||
_importExportString->setPlainText(compressMacroString(current));
|
||||
} else {
|
||||
_importExportString->setPlainText(
|
||||
decompressMacroString(current));
|
||||
}
|
||||
usePlainText = value;
|
||||
}
|
||||
|
||||
static bool isValidData(const QString &json)
|
||||
{
|
||||
OBSDataAutoRelease data =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
@@ -13,8 +14,12 @@ public:
|
||||
static void ExportMacros(const QString &json);
|
||||
static bool ImportMacros(QString &json);
|
||||
|
||||
private slots:
|
||||
void UsePlainTextChanged(int);
|
||||
|
||||
private:
|
||||
QPlainTextEdit *_importExportString;
|
||||
QCheckBox *_usePlainText;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -246,6 +246,11 @@ MacroSegmentEdit *MacroSegmentList::WidgetAt(int idx)
|
||||
return static_cast<MacroSegmentEdit *>(item->widget());
|
||||
}
|
||||
|
||||
MacroSegmentEdit *MacroSegmentList::WidgetAt(const QPoint &pos)
|
||||
{
|
||||
return WidgetAt(GetSegmentIndexFromPos(mapToGlobal(pos)));
|
||||
}
|
||||
|
||||
void MacroSegmentList::HideLastDropLine()
|
||||
{
|
||||
if (_dropLineIdx >= 0 && _dropLineIdx < _contentLayout->count()) {
|
||||
@@ -319,7 +324,7 @@ QRect MacroSegmentList::GetContentItemRectWithPadding(int idx)
|
||||
return rect;
|
||||
}
|
||||
|
||||
int MacroSegmentList::GetWidgetIdx(const QPoint &pos)
|
||||
int MacroSegmentList::GetSegmentIndexFromPos(const QPoint &pos)
|
||||
{
|
||||
int idx = -1;
|
||||
for (int i = 0; i < _contentLayout->count(); ++i) {
|
||||
@@ -359,7 +364,7 @@ void MacroSegmentList::CheckScroll()
|
||||
|
||||
void MacroSegmentList::CheckDropLine(const QPoint &pos)
|
||||
{
|
||||
int idx = GetWidgetIdx(pos);
|
||||
int idx = GetSegmentIndexFromPos(pos);
|
||||
if (idx == _dragPosition) {
|
||||
return;
|
||||
}
|
||||
@@ -410,7 +415,7 @@ bool MacroSegmentList::IsInListArea(const QPoint &pos)
|
||||
|
||||
int MacroSegmentList::GetDropIndex(const QPoint &pos)
|
||||
{
|
||||
int idx = GetWidgetIdx(pos);
|
||||
int idx = GetSegmentIndexFromPos(pos);
|
||||
if (idx == _dragPosition) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
void SetHelpMsg(const QString &msg);
|
||||
void SetHelpMsgVisible(bool visible);
|
||||
MacroSegmentEdit *WidgetAt(int idx);
|
||||
MacroSegmentEdit *WidgetAt(const QPoint &);
|
||||
void Insert(int idx, MacroSegmentEdit *widget);
|
||||
void Add(QWidget *widget);
|
||||
void Remove(int idx);
|
||||
@@ -44,7 +45,7 @@ protected:
|
||||
private:
|
||||
int GetDragIndex(const QPoint &);
|
||||
int GetDropIndex(const QPoint &);
|
||||
int GetWidgetIdx(const QPoint &);
|
||||
int GetSegmentIndexFromPos(const QPoint &);
|
||||
void CheckScroll();
|
||||
void CheckDropLine(const QPoint &);
|
||||
bool IsInListArea(const QPoint &);
|
||||
|
||||
@@ -20,13 +20,30 @@ MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue)
|
||||
|
||||
bool MacroSegment::Save(obs_data_t *obj) const
|
||||
{
|
||||
obs_data_set_bool(obj, "collapsed", static_cast<int>(_collapsed));
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_bool(data, "collapsed", _collapsed);
|
||||
obs_data_set_bool(data, "useCustomLabel", _useCustomLabel);
|
||||
obs_data_set_string(data, "customLabel", _customLabel.c_str());
|
||||
obs_data_set_obj(obj, "segmentSettings", data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroSegment::Load(obs_data_t *obj)
|
||||
{
|
||||
_collapsed = obs_data_get_bool(obj, "collapsed");
|
||||
// TODO: remove this fallback at some point
|
||||
if (obs_data_has_user_value(obj, "segmentSettings")) {
|
||||
OBSDataAutoRelease data =
|
||||
obs_data_get_obj(obj, "segmentSettings");
|
||||
_collapsed = obs_data_get_bool(data, "collapsed");
|
||||
_useCustomLabel = obs_data_get_bool(data, "useCustomLabel");
|
||||
_customLabel = obs_data_get_string(data, "customLabel");
|
||||
} else {
|
||||
_collapsed = obs_data_get_bool(obj, "collapsed");
|
||||
_useCustomLabel = false;
|
||||
_customLabel = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.defaultCustomLabel");
|
||||
}
|
||||
|
||||
ClearAvailableTempvars();
|
||||
return true;
|
||||
}
|
||||
@@ -296,6 +313,12 @@ bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
|
||||
|
||||
void MacroSegmentEdit::HeaderInfoChanged(const QString &text)
|
||||
{
|
||||
if (Data() && Data()->GetUseCustomLabel()) {
|
||||
_headerInfo->show();
|
||||
_headerInfo->setText(
|
||||
QString::fromStdString(Data()->GetCustomLabel()));
|
||||
return;
|
||||
}
|
||||
_headerInfo->setVisible(!text.isEmpty());
|
||||
_headerInfo->setText(text);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ public:
|
||||
int GetIndex() const { return _idx; }
|
||||
void SetCollapsed(bool collapsed) { _collapsed = collapsed; }
|
||||
bool GetCollapsed() const { return _collapsed; }
|
||||
void SetUseCustomLabel(bool enable) { _useCustomLabel = enable; }
|
||||
bool GetUseCustomLabel() const { return _useCustomLabel; }
|
||||
void SetCustomLabel(const std::string &label) { _customLabel = label; }
|
||||
std::string GetCustomLabel() const { return _customLabel; }
|
||||
virtual bool Save(obs_data_t *obj) const = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
virtual bool PostLoad();
|
||||
@@ -63,6 +67,11 @@ private:
|
||||
bool _highlight = false;
|
||||
bool _collapsed = false;
|
||||
|
||||
// Custom header labels
|
||||
bool _useCustomLabel = false;
|
||||
std::string _customLabel = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.defaultCustomLabel");
|
||||
|
||||
// Variable helpers
|
||||
const bool _supportsVariableValue = false;
|
||||
int _variableRefs = 0;
|
||||
@@ -86,8 +95,10 @@ public:
|
||||
void SetSelected(bool);
|
||||
virtual std::shared_ptr<MacroSegment> Data() const = 0;
|
||||
|
||||
protected slots:
|
||||
public slots:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected slots:
|
||||
void Collapsed(bool);
|
||||
void Highlight();
|
||||
void EnableHighlight(bool);
|
||||
|
||||
@@ -95,7 +95,9 @@ void MacroSelection::MacroRename(const QString &oldName, const QString &newName)
|
||||
}
|
||||
}
|
||||
|
||||
MacroSelectionDialog::MacroSelectionDialog(QWidget *)
|
||||
MacroSelectionDialog::MacroSelectionDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
_macroSelection(new MacroSelection(this))
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::ApplicationModal);
|
||||
@@ -103,21 +105,20 @@ MacroSelectionDialog::MacroSelectionDialog(QWidget *)
|
||||
setMinimumWidth(350);
|
||||
setMinimumHeight(70);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel);
|
||||
|
||||
buttonbox->setCenterButtons(true);
|
||||
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
_macroSelection = new MacroSelection(window());
|
||||
auto *selectionLayout = new QHBoxLayout;
|
||||
auto selectionLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{macroSelection}}", _macroSelection},
|
||||
};
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.askForMacro"),
|
||||
selectionLayout, widgetPlaceholders);
|
||||
auto *layout = new QVBoxLayout();
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addLayout(selectionLayout);
|
||||
layout->addWidget(buttonbox);
|
||||
setLayout(layout);
|
||||
@@ -125,7 +126,7 @@ MacroSelectionDialog::MacroSelectionDialog(QWidget *)
|
||||
|
||||
bool MacroSelectionDialog::AskForMacro(QWidget *parent, std::string ¯oName)
|
||||
{
|
||||
MacroSelectionDialog dialog(parent);
|
||||
MacroSelectionDialog dialog(GetSettingsWindow());
|
||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
|
||||
@@ -67,7 +67,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
||||
placeHolderText = fmt.arg(++i);
|
||||
}
|
||||
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.name"), name,
|
||||
placeHolderText);
|
||||
@@ -199,7 +199,7 @@ void AdvSceneSwitcher::RenameSelectedMacro()
|
||||
|
||||
std::string oldName = macro->Name();
|
||||
std::string name;
|
||||
if (!AdvSSNameDialog::AskForName(
|
||||
if (!NameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.windowTitle"),
|
||||
obs_module_text("AdvSceneSwitcher.item.newName"), name,
|
||||
QString::fromStdString(oldName))) {
|
||||
@@ -370,7 +370,7 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
|
||||
}
|
||||
|
||||
std::string newName;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this,
|
||||
obs_module_text(macro->IsGroup()
|
||||
? "AdvSceneSwitcher.macroTab.addGroup"
|
||||
@@ -943,11 +943,39 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
||||
menu.exec(globalPos);
|
||||
}
|
||||
|
||||
static void handleCustomLabelChange(MacroSegmentEdit *segmentEdit,
|
||||
QAction *contextMenuOption)
|
||||
{
|
||||
bool enable = contextMenuOption->isChecked();
|
||||
auto segment = segmentEdit->Data();
|
||||
segment->SetUseCustomLabel(enable);
|
||||
if (!enable) {
|
||||
segmentEdit->HeaderInfoChanged(
|
||||
QString::fromStdString(segment->GetShortDesc()));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string label;
|
||||
bool accepted = NameDialog::AskForName(
|
||||
GetSettingsWindow(),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
|
||||
"", label, QString::fromStdString(segment->GetCustomLabel()));
|
||||
if (!accepted) {
|
||||
segment->SetUseCustomLabel(false);
|
||||
return;
|
||||
}
|
||||
|
||||
segment->SetCustomLabel(label);
|
||||
segmentEdit->HeaderInfoChanged("");
|
||||
}
|
||||
|
||||
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
||||
std::function<void(AdvSceneSwitcher *)> expand,
|
||||
std::function<void(AdvSceneSwitcher *)> collapse,
|
||||
std::function<void(AdvSceneSwitcher *)> maximize,
|
||||
std::function<void(AdvSceneSwitcher *)> minimize)
|
||||
std::function<void(AdvSceneSwitcher *)> minimize,
|
||||
MacroSegmentList *list)
|
||||
{
|
||||
QMenu menu;
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
|
||||
@@ -958,34 +986,46 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
||||
ss, [ss, maximize]() { maximize(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
|
||||
ss, [ss, minimize]() { minimize(ss); });
|
||||
menu.exec(pos);
|
||||
|
||||
auto segmentEdit = list->WidgetAt(pos);
|
||||
if (segmentEdit) {
|
||||
auto customLabel = menu.addAction(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
|
||||
customLabel->setCheckable(true);
|
||||
auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
|
||||
customLabel->setChecked(segment &&
|
||||
segment->GetUseCustomLabel());
|
||||
QWidget::connect(customLabel, &QAction::triggered,
|
||||
std::bind(handleCustomLabelChange, segmentEdit,
|
||||
customLabel));
|
||||
}
|
||||
menu.exec(list->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
|
||||
{
|
||||
setupConextMenu(this, ui->actionsList->mapToGlobal(pos),
|
||||
&AdvSceneSwitcher::ExpandAllActions,
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllActions,
|
||||
&AdvSceneSwitcher::CollapseAllActions,
|
||||
&AdvSceneSwitcher::MaximizeActions,
|
||||
&AdvSceneSwitcher::MinimizeActions);
|
||||
&AdvSceneSwitcher::MinimizeActions, ui->actionsList);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos)
|
||||
{
|
||||
setupConextMenu(this, ui->elseActionsList->mapToGlobal(pos),
|
||||
&AdvSceneSwitcher::ExpandAllElseActions,
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllElseActions,
|
||||
&AdvSceneSwitcher::CollapseAllElseActions,
|
||||
&AdvSceneSwitcher::MaximizeElseActions,
|
||||
&AdvSceneSwitcher::MinimizeElseActions);
|
||||
&AdvSceneSwitcher::MinimizeElseActions,
|
||||
ui->elseActionsList);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos)
|
||||
{
|
||||
setupConextMenu(this, ui->conditionsList->mapToGlobal(pos),
|
||||
&AdvSceneSwitcher::ExpandAllConditions,
|
||||
setupConextMenu(this, pos, &AdvSceneSwitcher::ExpandAllConditions,
|
||||
&AdvSceneSwitcher::CollapseAllConditions,
|
||||
&AdvSceneSwitcher::MaximizeConditions,
|
||||
&AdvSceneSwitcher::MinimizeConditions);
|
||||
&AdvSceneSwitcher::MinimizeConditions,
|
||||
ui->conditionsList);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::CopyMacro()
|
||||
|
||||
260
lib/queue/action-queue-tab.cpp
Normal file
260
lib/queue/action-queue-tab.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#include "action-queue-tab.hpp"
|
||||
#include "action-queue.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
#include "tab-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool registerTab();
|
||||
static void setupTab(QTabWidget *);
|
||||
static bool registerTabDone = registerTab();
|
||||
|
||||
static ActionQueueTable *tabWidget = nullptr;
|
||||
|
||||
static bool registerTab()
|
||||
{
|
||||
AddPluginInitStep([]() {
|
||||
AddSetupTabCallback("actionQueueTab", ActionQueueTable::Create,
|
||||
setupTab);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
{
|
||||
SetTabVisibleByName(
|
||||
tabWidget, visible,
|
||||
obs_module_text("AdvSceneSwitcher.actionQueueTab.title"));
|
||||
}
|
||||
|
||||
ActionQueueTable *ActionQueueTable::Create()
|
||||
{
|
||||
tabWidget = new ActionQueueTable();
|
||||
return tabWidget;
|
||||
}
|
||||
|
||||
void ActionQueueTable::Add()
|
||||
{
|
||||
auto newQueue = std::make_shared<ActionQueue>();
|
||||
auto accepted =
|
||||
ActionQueueSettingsDialog::AskForSettings(this, *newQueue);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto &queues = GetActionQueues();
|
||||
queues.emplace_back(newQueue);
|
||||
}
|
||||
|
||||
ActionQueueSignalManager::Instance()->Add(
|
||||
QString::fromStdString(newQueue->Name()));
|
||||
}
|
||||
|
||||
static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
|
||||
{
|
||||
assert(queue);
|
||||
|
||||
auto result = QStringList();
|
||||
if (addName) {
|
||||
result << QString::fromStdString(queue->Name());
|
||||
}
|
||||
result << QString::number(queue->Size())
|
||||
<< QString::fromStdString(obs_module_text(
|
||||
queue->IsRunning()
|
||||
? "AdvSceneSwitcher.actionQueueTab.yes"
|
||||
: "AdvSceneSwitcher.actionQueueTab.yes"))
|
||||
<< QString::fromStdString(obs_module_text(
|
||||
queue->RunsOnStartup()
|
||||
? "AdvSceneSwitcher.actionQueueTab.yes"
|
||||
: "AdvSceneSwitcher.actionQueueTab.yes"));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void updateQueueStatus(QTableWidget *table)
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); row++) {
|
||||
auto item = table->item(row, 0);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto weakQueue = GetWeakActionQueueByQString(item->text());
|
||||
auto queue = weakQueue.lock();
|
||||
if (!queue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdateItemTableRow(table, row,
|
||||
getCellLabels(queue.get(), false));
|
||||
}
|
||||
}
|
||||
|
||||
static void openSettingsDialog()
|
||||
{
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto weakQueue = GetWeakActionQueueByQString(cell->text());
|
||||
auto queue = weakQueue.lock();
|
||||
if (!queue) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldName = queue->Name();
|
||||
bool accepted = ActionQueueSettingsDialog::AskForSettings(
|
||||
tabWidget->Table(), *queue.get());
|
||||
if (accepted && oldName != queue->Name()) {
|
||||
ActionQueueSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
QString::fromStdString(queue->Name()));
|
||||
}
|
||||
}
|
||||
|
||||
static void removeQueuesWithNames(const QStringList &queueNames)
|
||||
{
|
||||
for (const auto &name : queueNames) {
|
||||
auto queue = GetWeakActionQueueByQString(name).lock();
|
||||
if (!queue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &queues = GetActionQueues();
|
||||
queues.erase(
|
||||
std::remove_if(
|
||||
queues.begin(), queues.end(),
|
||||
[queue](const std::shared_ptr<Item> &item) {
|
||||
return item == queue;
|
||||
}),
|
||||
queues.end());
|
||||
}
|
||||
}
|
||||
|
||||
void ActionQueueTable::Remove()
|
||||
{
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList queueNames;
|
||||
for (const auto &row : selectedRows) {
|
||||
auto cell = tabWidget->Table()->item(row.row(), 0);
|
||||
if (!cell) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queueNames << cell->text();
|
||||
}
|
||||
|
||||
int queueNameCount = queueNames.size();
|
||||
if (queueNameCount == 1) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.removeSingleQueuePopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(queueNames.at(0)),
|
||||
true)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.removeMultipleQueuesPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(queueNameCount), true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
RemoveItemsByName(GetActionQueues(), queueNames);
|
||||
}
|
||||
|
||||
for (const auto &name : queueNames) {
|
||||
ActionQueueSignalManager::Instance()->Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
ActionQueueTable::ActionQueueTable(QTabWidget *parent)
|
||||
: ResourceTable(
|
||||
parent,
|
||||
obs_module_text("AdvSceneSwitcher.actionQueueTab.help"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.queueAddButton.tooltip"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.queueRemoveButton.tooltip"),
|
||||
QStringList()
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.name.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.size.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.isRunning.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.actionQueueTab.runOnStartup.header"),
|
||||
openSettingsDialog)
|
||||
{
|
||||
for (const auto &queue : GetActionQueues()) {
|
||||
auto q = std::static_pointer_cast<ActionQueue>(queue);
|
||||
AddItemTableRow(Table(), getCellLabels(q.get()));
|
||||
}
|
||||
|
||||
SetHelpVisible(GetActionQueues().empty());
|
||||
}
|
||||
|
||||
static void setupTab(QTabWidget *tab)
|
||||
{
|
||||
if (GetActionQueues().empty()) {
|
||||
setTabVisible(tab, false);
|
||||
}
|
||||
|
||||
QWidget::connect(ActionQueueSignalManager::Instance(),
|
||||
&ActionQueueSignalManager::Rename,
|
||||
[](const QString &oldName, const QString &newName) {
|
||||
RenameItemTableRow(tabWidget->Table(), oldName,
|
||||
newName);
|
||||
});
|
||||
QWidget::connect(
|
||||
ActionQueueSignalManager::Instance(),
|
||||
&ActionQueueSignalManager::Add, [tab](const QString &name) {
|
||||
AddItemTableRow(
|
||||
tabWidget->Table(),
|
||||
getCellLabels(GetWeakActionQueueByQString(name)
|
||||
.lock()
|
||||
.get()));
|
||||
tabWidget->SetHelpVisible(false);
|
||||
tabWidget->HighlightAddButton(false);
|
||||
setTabVisible(tab, true);
|
||||
});
|
||||
QWidget::connect(ActionQueueSignalManager::Instance(),
|
||||
&ActionQueueSignalManager::Remove,
|
||||
[](const QString &name) {
|
||||
RemoveItemTableRow(tabWidget->Table(), name);
|
||||
if (tabWidget->Table()->rowCount() == 0) {
|
||||
tabWidget->SetHelpVisible(true);
|
||||
tabWidget->HighlightAddButton(true);
|
||||
}
|
||||
});
|
||||
|
||||
auto timer = new QTimer(tabWidget);
|
||||
timer->setInterval(1000);
|
||||
QWidget::connect(timer, &QTimer::timeout,
|
||||
[]() { updateQueueStatus(tabWidget->Table()); });
|
||||
timer->start();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
20
lib/queue/action-queue-tab.hpp
Normal file
20
lib/queue/action-queue-tab.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "resource-table.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ActionQueueTable final : public ResourceTable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static ActionQueueTable *Create();
|
||||
|
||||
private slots:
|
||||
void Add();
|
||||
void Remove();
|
||||
|
||||
private:
|
||||
ActionQueueTable(QTabWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -6,6 +6,11 @@ namespace advss {
|
||||
|
||||
static std::deque<std::shared_ptr<Item>> queues;
|
||||
|
||||
std::deque<std::shared_ptr<Item>> &GetActionQueues()
|
||||
{
|
||||
return queues;
|
||||
}
|
||||
|
||||
void SetupActionQueues()
|
||||
{
|
||||
static bool done = false;
|
||||
@@ -95,6 +95,7 @@ signals:
|
||||
void Remove(const QString &);
|
||||
};
|
||||
|
||||
std::deque<std::shared_ptr<Item>> &GetActionQueues();
|
||||
void SetupActionQueues();
|
||||
void SaveActionQueues(obs_data_t *);
|
||||
void LoadActionQueues(obs_data_t *);
|
||||
@@ -187,24 +187,6 @@ void SwitcherData::RunPostLoadSteps()
|
||||
postLoadSteps.clear();
|
||||
}
|
||||
|
||||
void SwitcherData::AddPluginInitStep(std::function<void()> function)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
pluginInitSteps.emplace_back(function);
|
||||
}
|
||||
|
||||
void SwitcherData::AddPluginPostLoadStep(std::function<void()> function)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
pluginPostLoadSteps.emplace_back(function);
|
||||
}
|
||||
|
||||
void SwitcherData::AddPluginCleanupStep(std::function<void()> function)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
pluginCleanupSteps.emplace_back(function);
|
||||
}
|
||||
|
||||
void SwitcherData::AddSaveStep(std::function<void(obs_data_t *)> function)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
@@ -65,9 +65,6 @@ public:
|
||||
void AddPostLoadStep(std::function<void()>);
|
||||
void AddIntervalResetStep(std::function<void()>, bool lock = true);
|
||||
void RunPostLoadSteps();
|
||||
void AddPluginInitStep(std::function<void()>);
|
||||
void AddPluginPostLoadStep(std::function<void()>);
|
||||
void AddPluginCleanupStep(std::function<void()>);
|
||||
bool CheckForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger, bool &setPreviousSceneAsMatch,
|
||||
bool ¯oMatch);
|
||||
@@ -106,9 +103,6 @@ public:
|
||||
std::vector<std::function<void(obs_data_t *)>> loadSteps;
|
||||
std::vector<std::function<void()>> postLoadSteps;
|
||||
std::vector<std::function<void()>> resetIntervalSteps;
|
||||
std::vector<std::function<void()>> pluginInitSteps;
|
||||
std::vector<std::function<void()>> pluginPostLoadSteps;
|
||||
std::vector<std::function<void()>> pluginCleanupSteps;
|
||||
|
||||
bool firstBoot = true;
|
||||
bool transitionActive = false;
|
||||
@@ -171,7 +165,6 @@ public:
|
||||
/* --- Start of UI section --- */
|
||||
|
||||
bool settingsWindowOpened = false;
|
||||
int lastOpenedTab = -1;
|
||||
std::string lastImportPath;
|
||||
QStringList loadFailureLibs;
|
||||
bool warnPluginLoadFailure = true;
|
||||
|
||||
@@ -177,7 +177,7 @@ void ItemSelection::RenameItem()
|
||||
Item *item = variant.value<Item *>();
|
||||
|
||||
std::string name;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.windowTitle"),
|
||||
obs_module_text("AdvSceneSwitcher.item.newName"), name,
|
||||
QString::fromStdString(item->Name()));
|
||||
@@ -347,4 +347,18 @@ void Item::Save(obs_data_t *obj) const
|
||||
obs_data_set_string(obj, "name", _name.c_str());
|
||||
}
|
||||
|
||||
void RemoveItemsByName(std::deque<std::shared_ptr<Item>> &items,
|
||||
const QStringList &names)
|
||||
{
|
||||
for (const auto &name : names) {
|
||||
items.erase(std::remove_if(
|
||||
items.begin(), items.end(),
|
||||
[name](const std::shared_ptr<Item> &item) {
|
||||
return item->Name() ==
|
||||
name.toStdString();
|
||||
}),
|
||||
items.end());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -32,6 +32,9 @@ protected:
|
||||
friend ItemSettingsDialog;
|
||||
};
|
||||
|
||||
void EXPORT RemoveItemsByName(std::deque<std::shared_ptr<Item>> &items,
|
||||
const QStringList &names);
|
||||
|
||||
class ItemSettingsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@@ -5,22 +5,22 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
AdvSSNameDialog::AdvSSNameDialog(QWidget *parent) : QDialog(parent)
|
||||
NameDialog::NameDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
_label(new QLabel(this)),
|
||||
_userText(new QLineEdit(this))
|
||||
|
||||
{
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setFixedWidth(555);
|
||||
setMinimumHeight(100);
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
auto layout = new QVBoxLayout;
|
||||
setLayout(layout);
|
||||
|
||||
label = new QLabel(this);
|
||||
layout->addWidget(label);
|
||||
label->setText("Set Text");
|
||||
|
||||
userText = new QLineEdit(this);
|
||||
layout->addWidget(userText);
|
||||
layout->addWidget(_label);
|
||||
layout->addWidget(_userText);
|
||||
|
||||
QDialogButtonBox *buttonbox = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
@@ -30,43 +30,44 @@ AdvSSNameDialog::AdvSSNameDialog(QWidget *parent) : QDialog(parent)
|
||||
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
static bool IsWhitespace(char ch)
|
||||
static bool isWhitespace(char ch)
|
||||
{
|
||||
return ch == ' ' || ch == '\t';
|
||||
}
|
||||
|
||||
static void CleanWhitespace(std::string &str)
|
||||
static void cleanWhitespace(std::string &str)
|
||||
{
|
||||
while (str.size() && IsWhitespace(str.back()))
|
||||
while (str.size() && isWhitespace(str.back())) {
|
||||
str.erase(str.end() - 1);
|
||||
while (str.size() && IsWhitespace(str.front()))
|
||||
}
|
||||
while (str.size() && isWhitespace(str.front())) {
|
||||
str.erase(str.begin());
|
||||
}
|
||||
}
|
||||
|
||||
bool AdvSSNameDialog::AskForName(QWidget *parent, const QString &title,
|
||||
const QString &text,
|
||||
std::string &userTextInput,
|
||||
const QString &placeHolder, int maxSize,
|
||||
bool clean)
|
||||
bool NameDialog::AskForName(QWidget *parent, const QString &title,
|
||||
const QString &prompt, std::string &userTextInput,
|
||||
const QString &placeHolder, int maxSize, bool clean)
|
||||
{
|
||||
if (maxSize <= 0 || maxSize > 32767) {
|
||||
maxSize = 170;
|
||||
}
|
||||
|
||||
AdvSSNameDialog dialog(parent);
|
||||
NameDialog dialog(parent);
|
||||
dialog.setWindowTitle(title);
|
||||
|
||||
dialog.label->setText(text);
|
||||
dialog.userText->setMaxLength(maxSize);
|
||||
dialog.userText->setText(placeHolder);
|
||||
dialog.userText->selectAll();
|
||||
dialog._label->setVisible(!prompt.isEmpty());
|
||||
dialog._label->setText(prompt);
|
||||
dialog._userText->setMaxLength(maxSize);
|
||||
dialog._userText->setText(placeHolder);
|
||||
dialog._userText->selectAll();
|
||||
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
userTextInput = dialog.userText->text().toUtf8().constData();
|
||||
userTextInput = dialog._userText->text().toUtf8().constData();
|
||||
if (clean) {
|
||||
CleanWhitespace(userTextInput);
|
||||
cleanWhitespace(userTextInput);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,23 +8,23 @@
|
||||
namespace advss {
|
||||
|
||||
// Based on OBS's NameDialog
|
||||
class AdvSSNameDialog : public QDialog {
|
||||
class NameDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AdvSSNameDialog(QWidget *parent);
|
||||
NameDialog(QWidget *parent);
|
||||
|
||||
// Returns true if user clicks OK, false otherwise
|
||||
// userTextInput returns string that user typed into dialog
|
||||
EXPORT static bool AskForName(QWidget *parent, const QString &title,
|
||||
const QString &text,
|
||||
const QString &prompt,
|
||||
std::string &userTextInput,
|
||||
const QString &placeHolder = QString(""),
|
||||
int maxSize = 170, bool clean = true);
|
||||
|
||||
private:
|
||||
QLabel *label;
|
||||
QLineEdit *userText;
|
||||
QLabel *_label;
|
||||
QLineEdit *_userText;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -9,7 +9,8 @@ namespace advss {
|
||||
const char *obs_module_text(const char *text)
|
||||
{
|
||||
if (!advss::switcher) {
|
||||
return "";
|
||||
assert(false);
|
||||
return "obs_module_text called too early";
|
||||
}
|
||||
return advss::switcher->Translate(text);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static std::vector<std::function<void()>> &getPluginInitSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getPluginPostLoadSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::vector<std::function<void()>> &getPluginCleanupSteps()
|
||||
{
|
||||
static std::vector<std::function<void()>> steps;
|
||||
return steps;
|
||||
}
|
||||
|
||||
static std::mutex mutex;
|
||||
|
||||
void SavePluginSettings(obs_data_t *obj)
|
||||
{
|
||||
GetSwitcher()->SaveSettings(obj);
|
||||
@@ -40,17 +60,44 @@ void RunPostLoadSteps()
|
||||
|
||||
void AddPluginInitStep(std::function<void()> step)
|
||||
{
|
||||
GetSwitcher()->AddPluginInitStep(step);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getPluginInitSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void AddPluginPostLoadStep(std::function<void()> step)
|
||||
{
|
||||
GetSwitcher()->AddPluginPostLoadStep(step);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getPluginPostLoadSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void AddPluginCleanupStep(std::function<void()> step)
|
||||
{
|
||||
GetSwitcher()->AddPluginCleanupStep(step);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
getPluginCleanupSteps().emplace_back(step);
|
||||
}
|
||||
|
||||
void RunPluginInitSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getPluginInitSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void RunPluginPostLoadSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getPluginPostLoadSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void RunPluginCleanupSteps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &step : getPluginCleanupSteps()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
void StopPlugin()
|
||||
|
||||
@@ -17,6 +17,9 @@ EXPORT void RunPostLoadSteps();
|
||||
EXPORT void AddPluginInitStep(std::function<void()>);
|
||||
EXPORT void AddPluginPostLoadStep(std::function<void()>);
|
||||
EXPORT void AddPluginCleanupStep(std::function<void()>);
|
||||
void RunPluginInitSteps();
|
||||
extern "C" EXPORT void RunPluginPostLoadSteps();
|
||||
void RunPluginCleanupSteps();
|
||||
|
||||
EXPORT void StopPlugin();
|
||||
EXPORT void StartPlugin();
|
||||
|
||||
70
lib/utils/resource-table-hotkey-handler.cpp
Normal file
70
lib/utils/resource-table-hotkey-handler.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "resource-table-hotkey-handler.hpp"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QWidget>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace advss {
|
||||
|
||||
void RegisterHotkeyFunction(QWidget *widget, Qt::Key key,
|
||||
std::function<void()> func)
|
||||
{
|
||||
ResourceTabHotkeyHandler::Instance()->RegisterHandler(widget, key,
|
||||
func);
|
||||
}
|
||||
|
||||
void DeregisterHotkeyFunctions(QWidget *widget)
|
||||
{
|
||||
ResourceTabHotkeyHandler::Instance()->Deregister(widget);
|
||||
}
|
||||
|
||||
ResourceTabHotkeyHandler *ResourceTabHotkeyHandler::Instance()
|
||||
{
|
||||
static ResourceTabHotkeyHandler handler;
|
||||
return &handler;
|
||||
}
|
||||
|
||||
void ResourceTabHotkeyHandler::RegisterHandler(QWidget *widget, Qt::Key key,
|
||||
std::function<void()> func)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_callbacks.emplace(widget, CallbackData{key, func});
|
||||
widget->installEventFilter(this);
|
||||
}
|
||||
|
||||
void ResourceTabHotkeyHandler::Deregister(QWidget *widget)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_callbacks.erase(widget);
|
||||
}
|
||||
|
||||
bool ResourceTabHotkeyHandler::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() != QEvent::KeyPress) {
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
auto pressedKey = keyEvent->key();
|
||||
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
auto it = _callbacks.find(obj);
|
||||
if (it == _callbacks.end()) {
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
auto current = it;
|
||||
while (current != _callbacks.end() && current->first == it->first) {
|
||||
auto &[_, cbData] = *current;
|
||||
if (pressedKey != cbData.key) {
|
||||
++current;
|
||||
continue;
|
||||
}
|
||||
cbData.func();
|
||||
++current;
|
||||
continue;
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
37
lib/utils/resource-table-hotkey-handler.hpp
Normal file
37
lib/utils/resource-table-hotkey-handler.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <QEvent>
|
||||
#include <QObject>
|
||||
|
||||
namespace advss {
|
||||
|
||||
EXPORT void RegisterHotkeyFunction(QWidget *, Qt::Key,
|
||||
std::function<void()> func);
|
||||
EXPORT void DeregisterHotkeyFunctions(QWidget *);
|
||||
|
||||
class ResourceTabHotkeyHandler : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static ResourceTabHotkeyHandler *Instance();
|
||||
void RegisterHandler(QWidget *, Qt::Key, std::function<void()> func);
|
||||
void Deregister(QWidget *);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private:
|
||||
struct CallbackData {
|
||||
Qt::Key key;
|
||||
std::function<void()> func;
|
||||
};
|
||||
|
||||
ResourceTabHotkeyHandler() : QObject(){};
|
||||
|
||||
std::multimap<QObject *, CallbackData> _callbacks;
|
||||
std::mutex _mutex;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
157
lib/utils/resource-table.cpp
Normal file
157
lib/utils/resource-table.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "resource-table.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "resource-table-hotkey-handler.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QHeaderView>
|
||||
|
||||
namespace advss {
|
||||
|
||||
ResourceTable::ResourceTable(QTabWidget *parent, const QString &help,
|
||||
const QString &addToolTip,
|
||||
const QString &removeToolTip,
|
||||
const QStringList &headers,
|
||||
const std::function<void()> &openSettings)
|
||||
: QWidget(parent),
|
||||
_table(new QTableWidget()),
|
||||
_add(new QPushButton()),
|
||||
_remove(new QPushButton()),
|
||||
_help(new QLabel(help))
|
||||
{
|
||||
_add->setMaximumWidth(22);
|
||||
_add->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("addIconSmall")));
|
||||
_add->setFlat(true);
|
||||
_add->setToolTip(addToolTip);
|
||||
|
||||
_remove->setMaximumWidth(22);
|
||||
_remove->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||
_remove->setFlat(true);
|
||||
_remove->setToolTip(removeToolTip);
|
||||
|
||||
_help->setWordWrap(true);
|
||||
_help->setAlignment(Qt::AlignCenter);
|
||||
|
||||
_table->setColumnCount(headers.size());
|
||||
_table->horizontalHeader()->setSectionResizeMode(
|
||||
QHeaderView::ResizeMode::Interactive);
|
||||
_table->setHorizontalHeaderLabels(headers);
|
||||
_table->verticalHeader()->hide();
|
||||
_table->setCornerButtonEnabled(false);
|
||||
_table->setShowGrid(false);
|
||||
_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
|
||||
auto helpAndTableLayout = new QGridLayout();
|
||||
helpAndTableLayout->setContentsMargins(0, 0, 0, 0);
|
||||
helpAndTableLayout->addWidget(_table, 0, 0);
|
||||
helpAndTableLayout->addWidget(_help, 0, 0, Qt::AlignCenter);
|
||||
|
||||
auto controlLayout = new QHBoxLayout;
|
||||
controlLayout->setContentsMargins(0, 0, 0, 0);
|
||||
controlLayout->addWidget(_add);
|
||||
controlLayout->addWidget(_remove);
|
||||
controlLayout->addStretch();
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addLayout(helpAndTableLayout);
|
||||
layout->addLayout(controlLayout);
|
||||
setLayout(layout);
|
||||
|
||||
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(Add()));
|
||||
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(Remove()));
|
||||
QWidget::connect(_table, &QTableWidget::cellDoubleClicked,
|
||||
[openSettings]() { openSettings(); });
|
||||
|
||||
RegisterHotkeyFunction(this, Qt::Key_F2, openSettings);
|
||||
RegisterHotkeyFunction(this, Qt::Key_Delete, [this]() { Remove(); });
|
||||
}
|
||||
|
||||
ResourceTable::~ResourceTable()
|
||||
{
|
||||
DeregisterHotkeyFunctions(this);
|
||||
}
|
||||
|
||||
void ResourceTable::SetHelpVisible(bool visible) const
|
||||
{
|
||||
_help->setVisible(visible);
|
||||
}
|
||||
|
||||
void ResourceTable::HighlightAddButton(bool enable)
|
||||
{
|
||||
_add->disconnect(_highlightConnection);
|
||||
if (enable && HighlightUIElementsEnabled()) {
|
||||
_highlightConnection = PulseWidget(_add, QColor(Qt::green));
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceTable::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
const auto columnCount = _table->columnCount();
|
||||
const auto columnSize = (_table->width() - 1) / columnCount;
|
||||
for (int i = 0; i < columnCount; ++i) {
|
||||
_table->horizontalHeader()->resizeSection(i, columnSize);
|
||||
}
|
||||
}
|
||||
|
||||
void AddItemTableRow(QTableWidget *table, const QStringList &cellLabels)
|
||||
{
|
||||
int row = table->rowCount();
|
||||
table->setRowCount(row + 1);
|
||||
|
||||
int col = 0;
|
||||
for (const auto &cellLabel : cellLabels) {
|
||||
auto *item = new QTableWidgetItem(cellLabel);
|
||||
item->setToolTip(cellLabel);
|
||||
table->setItem(row, col, item);
|
||||
col++;
|
||||
}
|
||||
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
void UpdateItemTableRow(QTableWidget *table, int row,
|
||||
const QStringList &cellLabels)
|
||||
{
|
||||
int col = 1; // Skip the name cell
|
||||
for (const auto &cellLabel : cellLabels) {
|
||||
auto item = table->item(row, col);
|
||||
item->setText(cellLabel);
|
||||
item->setToolTip(cellLabel);
|
||||
col++;
|
||||
}
|
||||
}
|
||||
|
||||
void RenameItemTableRow(QTableWidget *table, const QString &oldName,
|
||||
const QString &newName)
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); row++) {
|
||||
auto item = table->item(row, 0);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->text() == oldName) {
|
||||
item->setText(newName);
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void RemoveItemTableRow(QTableWidget *table, const QString &name)
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); ++row) {
|
||||
auto item = table->item(row, 0);
|
||||
if (item && item->text() == name) {
|
||||
table->removeRow(row);
|
||||
return;
|
||||
}
|
||||
}
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
50
lib/utils/resource-table.hpp
Normal file
50
lib/utils/resource-table.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QString>
|
||||
#include <QTableWidget>
|
||||
|
||||
class QResizeEvent;
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ADVSS_EXPORT ResourceTable : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ResourceTable(QTabWidget *parent, const QString &help,
|
||||
const QString &addToolTip, const QString &removeToolTip,
|
||||
const QStringList &headers,
|
||||
const std::function<void()> &openSettings);
|
||||
virtual ~ResourceTable();
|
||||
|
||||
QTableWidget *Table() const { return _table; }
|
||||
void SetHelpVisible(bool) const;
|
||||
void HighlightAddButton(bool);
|
||||
|
||||
protected slots:
|
||||
virtual void Add() {}
|
||||
virtual void Remove() {}
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
private:
|
||||
QTableWidget *_table;
|
||||
QPushButton *_add;
|
||||
QPushButton *_remove;
|
||||
QLabel *_help;
|
||||
|
||||
QMetaObject::Connection _highlightConnection;
|
||||
};
|
||||
|
||||
EXPORT void AddItemTableRow(QTableWidget *table, const QStringList &cells);
|
||||
EXPORT void UpdateItemTableRow(QTableWidget *table, int row,
|
||||
const QStringList &cells);
|
||||
EXPORT void RenameItemTableRow(QTableWidget *table, const QString &oldName,
|
||||
const QString &newName);
|
||||
EXPORT void RemoveItemTableRow(QTableWidget *table, const QString &name);
|
||||
|
||||
} // namespace advss
|
||||
182
lib/utils/tab-helpers.cpp
Normal file
182
lib/utils/tab-helpers.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "tab-helpers.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QTabBar>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
static std::vector<std::string> tabNames = {
|
||||
"generalTab", "macroTab", "windowTitleTab",
|
||||
"executableTab", "screenRegionTab", "mediaTab",
|
||||
"fileTab", "randomTab", "timeTab",
|
||||
"idleTab", "sceneSequenceTab", "audioTab",
|
||||
"videoTab", "networkTab", "sceneGroupTab",
|
||||
"transitionsTab", "pauseTab", "sceneTriggerTab"};
|
||||
|
||||
static std::vector<int> tabOrder = std::vector<int>(tabNames.size());
|
||||
|
||||
namespace {
|
||||
struct TabCallbacks {
|
||||
std::function<QWidget *()> createWidget;
|
||||
std::function<void(QTabWidget *)> setupTab;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static std::unordered_map<const char *, TabCallbacks> createTabCallbacks;
|
||||
|
||||
static int lastOpenedTab = -1;
|
||||
|
||||
void SetTabVisibleByName(QTabWidget *tabWidget, bool visible,
|
||||
const QString &name)
|
||||
{
|
||||
for (int idx = 0; idx < tabWidget->count(); idx++) {
|
||||
if (tabWidget->tabText(idx) != name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabWidget->setTabVisible(idx, visible);
|
||||
}
|
||||
}
|
||||
|
||||
void AddSetupTabCallback(const char *tabName,
|
||||
std::function<QWidget *()> createWidget,
|
||||
std::function<void(QTabWidget *)> setupTab)
|
||||
{
|
||||
if (std::find(tabNames.begin(), tabNames.end(), tabName) !=
|
||||
tabNames.end()) {
|
||||
return;
|
||||
}
|
||||
tabNames.emplace_back(tabName);
|
||||
TabCallbacks callbacks = {createWidget, setupTab};
|
||||
createTabCallbacks.emplace(tabName, callbacks);
|
||||
}
|
||||
|
||||
void SaveLastOpenedTab(QTabWidget *tabWidget)
|
||||
{
|
||||
lastOpenedTab = tabWidget->currentIndex();
|
||||
}
|
||||
|
||||
void ResetLastOpenedTab()
|
||||
{
|
||||
lastOpenedTab = -1;
|
||||
}
|
||||
|
||||
void SaveTabOrder(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease tabWidgetOrder = obs_data_array_create();
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry = obs_data_create();
|
||||
obs_data_set_int(entry, tabNames[i].c_str(), tabOrder[i]);
|
||||
obs_data_array_push_back(tabWidgetOrder, entry);
|
||||
}
|
||||
obs_data_set_array(obj, "tabWidgetOrder", tabWidgetOrder);
|
||||
}
|
||||
|
||||
static bool tabWidgetOrderValid()
|
||||
{
|
||||
auto tmp = std::vector<int>(tabNames.size());
|
||||
std::iota(tmp.begin(), tmp.end(), 0);
|
||||
|
||||
for (auto &p : tmp) {
|
||||
auto it = std::find(tabOrder.begin(), tabOrder.end(), p);
|
||||
if (it == tabOrder.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void resetTabWidgetOrder()
|
||||
{
|
||||
tabOrder = std::vector<int>(tabNames.size());
|
||||
std::iota(tabOrder.begin(), tabOrder.end(), 0);
|
||||
}
|
||||
|
||||
void LoadTabOrder(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease defaultTabWidgetOrder = obs_data_array_create();
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry = obs_data_create();
|
||||
obs_data_set_default_int(entry, tabNames[i].c_str(), i);
|
||||
obs_data_array_push_back(defaultTabWidgetOrder, entry);
|
||||
}
|
||||
obs_data_set_default_array(obj, "tabWidgetOrder",
|
||||
defaultTabWidgetOrder);
|
||||
|
||||
tabOrder.clear();
|
||||
OBSDataArrayAutoRelease tabWidgetOrder =
|
||||
obs_data_get_array(obj, "tabWidgetOrder");
|
||||
for (size_t i = 0; i < tabNames.size(); i++) {
|
||||
OBSDataAutoRelease entry =
|
||||
obs_data_array_item(tabWidgetOrder, i);
|
||||
tabOrder.emplace_back(
|
||||
(int)(obs_data_get_int(entry, tabNames[i].c_str())));
|
||||
}
|
||||
|
||||
if (!tabWidgetOrderValid()) {
|
||||
resetTabWidgetOrder();
|
||||
}
|
||||
}
|
||||
|
||||
static int findTabIndex(QTabWidget *tabWidget, int pos)
|
||||
{
|
||||
int at = -1;
|
||||
QString tabName = QString::fromStdString(tabNames.at(pos));
|
||||
QWidget *page = tabWidget->findChild<QWidget *>(tabName);
|
||||
if (page) {
|
||||
at = tabWidget->indexOf(page);
|
||||
}
|
||||
if (at == -1) {
|
||||
blog(LOG_INFO, "failed to find tab %s",
|
||||
tabName.toUtf8().constData());
|
||||
}
|
||||
|
||||
return at;
|
||||
}
|
||||
|
||||
void SetTabOrder(QTabWidget *tabWidget)
|
||||
{
|
||||
if (!tabWidgetOrderValid()) {
|
||||
resetTabWidgetOrder();
|
||||
}
|
||||
|
||||
auto bar = tabWidget->tabBar();
|
||||
for (int i = 0; i < bar->count(); ++i) {
|
||||
int curPos = findTabIndex(tabWidget, tabOrder[i]);
|
||||
|
||||
if (i != curPos && curPos != -1) {
|
||||
bar->moveTab(curPos, i);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget::connect(bar, &QTabBar::tabMoved, [](int from, int to) {
|
||||
std::swap(tabOrder[from], tabOrder[to]);
|
||||
});
|
||||
}
|
||||
|
||||
void SetCurrentTab(QTabWidget *tabWidget)
|
||||
{
|
||||
if (lastOpenedTab >= 0) {
|
||||
tabWidget->setCurrentIndex(lastOpenedTab);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOtherTabs(QTabWidget *tabWidget)
|
||||
{
|
||||
for (const auto &[name, callbacks] : createTabCallbacks) {
|
||||
auto widget = callbacks.createWidget();
|
||||
widget->setObjectName(name);
|
||||
auto tabText = obs_module_text(
|
||||
(std::string("AdvSceneSwitcher.") + name + ".title")
|
||||
.c_str());
|
||||
tabWidget->insertTab(0, widget, tabText);
|
||||
callbacks.setupTab(tabWidget);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
22
lib/utils/tab-helpers.hpp
Normal file
22
lib/utils/tab-helpers.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <obs-data.h>
|
||||
#include <QTabWidget>
|
||||
|
||||
namespace advss {
|
||||
|
||||
EXPORT void AddSetupTabCallback(const char *tabName,
|
||||
std::function<QWidget *()> createWidget,
|
||||
std::function<void(QTabWidget *)> setupTab);
|
||||
EXPORT void SetTabVisibleByName(QTabWidget *tabWidget, bool visible,
|
||||
const QString &name);
|
||||
void SaveLastOpenedTab(QTabWidget *tabWidget);
|
||||
void ResetLastOpenedTab();
|
||||
void SetTabOrder(QTabWidget *tabWidget);
|
||||
void SetCurrentTab(QTabWidget *tabWidget);
|
||||
void SetupOtherTabs(QTabWidget *tabWidget);
|
||||
void SaveTabOrder(obs_data_t *obj);
|
||||
void LoadTabOrder(obs_data_t *obj);
|
||||
|
||||
} // namespace advss
|
||||
@@ -250,6 +250,8 @@ TempVariableSelection::TempVariableSelection(QWidget *parent)
|
||||
_info->setPixmap(pixmap);
|
||||
_info->hide();
|
||||
|
||||
_selection->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_selection->setMaximumWidth(350);
|
||||
_selection->setDuplicatesEnabled(true);
|
||||
PopulateSelection();
|
||||
|
||||
|
||||
@@ -1,28 +1,61 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "variable-tab.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "plugin-state-helpers.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
#include "tab-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <QTableWidgetItem>
|
||||
#include <QTimer>
|
||||
|
||||
namespace advss {
|
||||
|
||||
static void setVariableTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
{
|
||||
for (int idx = 0; idx < tabWidget->count(); idx++) {
|
||||
if (tabWidget->tabText(idx) !=
|
||||
obs_module_text("AdvSceneSwitcher.variableTab.title")) {
|
||||
continue;
|
||||
}
|
||||
static bool registerTab();
|
||||
static void setupTab(QTabWidget *);
|
||||
static bool registerTabDone = registerTab();
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
// TODO: Switch to setTabVisible() once QT 5.15 is more wide spread
|
||||
tabWidget->setTabEnabled(idx, visible);
|
||||
tabWidget->setStyleSheet(
|
||||
"QTabBar::tab::disabled {width: 0; height: 0; margin: 0; padding: 0; border: none;} ");
|
||||
#else
|
||||
tabWidget->setTabVisible(idx, visible);
|
||||
#endif
|
||||
static VariableTable *tabWidget = nullptr;
|
||||
|
||||
static bool registerTab()
|
||||
{
|
||||
AddPluginInitStep([]() {
|
||||
AddSetupTabCallback("variableTab", VariableTable::Create,
|
||||
setupTab);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
{
|
||||
SetTabVisibleByName(
|
||||
tabWidget, visible,
|
||||
obs_module_text("AdvSceneSwitcher.variableTab.title"));
|
||||
}
|
||||
|
||||
VariableTable *VariableTable::Create()
|
||||
{
|
||||
tabWidget = new VariableTable();
|
||||
return tabWidget;
|
||||
}
|
||||
|
||||
void VariableTable::Add()
|
||||
{
|
||||
auto newVariable = std::make_shared<Variable>();
|
||||
auto accepted =
|
||||
VariableSettingsDialog::AskForSettings(this, *newVariable);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto &variables = GetVariables();
|
||||
variables.emplace_back(newVariable);
|
||||
}
|
||||
|
||||
VariableSignalManager::Instance()->Add(
|
||||
QString::fromStdString(newVariable->Name()));
|
||||
}
|
||||
|
||||
static QString formatSaveActionText(Variable *variable)
|
||||
@@ -91,46 +124,18 @@ static QString formatLastChangedTooltip(Variable *variable)
|
||||
.arg(QString::fromStdString(variable->GetPreviousValue()));
|
||||
}
|
||||
|
||||
static void addVariableRow(QTableWidget *table, Variable *variable)
|
||||
static QStringList getCellLabels(Variable *variable, bool addName = true)
|
||||
{
|
||||
if (!variable) {
|
||||
blog(LOG_INFO, "%s called with nullptr", __func__);
|
||||
assert(false);
|
||||
return;
|
||||
assert(variable);
|
||||
|
||||
auto result = QStringList();
|
||||
if (addName) {
|
||||
result << QString::fromStdString(variable->Name());
|
||||
}
|
||||
|
||||
int row = table->rowCount();
|
||||
table->setRowCount(row + 1);
|
||||
|
||||
int col = 0;
|
||||
auto *item =
|
||||
new QTableWidgetItem(QString::fromStdString(variable->Name()));
|
||||
table->setItem(row, col, item);
|
||||
auto varValue = QString::fromStdString(variable->Value(false));
|
||||
item = new QTableWidgetItem(varValue);
|
||||
item->setToolTip(varValue);
|
||||
table->setItem(row, ++col, item);
|
||||
item = new QTableWidgetItem(formatSaveActionText(variable));
|
||||
table->setItem(row, ++col, item);
|
||||
item = new QTableWidgetItem(formatLastUsedText(variable));
|
||||
table->setItem(row, ++col, item);
|
||||
item = new QTableWidgetItem(formatLastChangedText(variable));
|
||||
item->setToolTip(formatLastChangedTooltip(variable));
|
||||
table->setItem(row, ++col, item);
|
||||
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
static void removeVariableRow(QTableWidget *table, const QString &name)
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); ++row) {
|
||||
auto item = table->item(row, 0);
|
||||
if (item && item->text() == name) {
|
||||
table->removeRow(row);
|
||||
return;
|
||||
}
|
||||
}
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
result << QString::fromStdString(variable->Value(false))
|
||||
<< formatSaveActionText(variable) << formatLastUsedText(variable)
|
||||
<< formatLastChangedText(variable);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void updateVariableStatus(QTableWidget *table)
|
||||
@@ -147,116 +152,20 @@ static void updateVariableStatus(QTableWidget *table)
|
||||
continue;
|
||||
}
|
||||
|
||||
item = table->item(row, 1);
|
||||
auto varValue = QString::fromStdString(variable->Value(false));
|
||||
item->setText(varValue);
|
||||
item->setToolTip(varValue);
|
||||
item = table->item(row, 2);
|
||||
item->setText(formatSaveActionText(variable.get()));
|
||||
item = table->item(row, 3);
|
||||
item->setText(formatLastUsedText(variable.get()));
|
||||
item = table->item(row, 4);
|
||||
item->setText(formatLastChangedText(variable.get()));
|
||||
item->setToolTip(formatLastChangedTooltip(variable.get()));
|
||||
UpdateItemTableRow(table, row,
|
||||
getCellLabels(variable.get(), false));
|
||||
}
|
||||
}
|
||||
|
||||
static void renameVariable(QTableWidget *table, const QString &oldName,
|
||||
const QString &newName)
|
||||
static void openSettingsDialog()
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); row++) {
|
||||
auto item = table->item(row, 0);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->text() == oldName) {
|
||||
item->setText(newName);
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
blog(LOG_INFO, "%s called but entry \"%s\" not found", __func__,
|
||||
oldName.toStdString().c_str());
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetupVariableTab()
|
||||
{
|
||||
ui->variables->installEventFilter(this);
|
||||
|
||||
if (GetVariables().empty()) {
|
||||
setVariableTabVisible(ui->tabWidget, false);
|
||||
} else {
|
||||
ui->variablesHelp->hide();
|
||||
}
|
||||
|
||||
static const QStringList horizontalHeaders =
|
||||
QStringList()
|
||||
<< obs_module_text("AdvSceneSwitcher.variableTab.name.header")
|
||||
<< obs_module_text("AdvSceneSwitcher.variableTab.value.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.saveLoadBehavior.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastUsed.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastChanged.header");
|
||||
|
||||
auto &variables = GetVariables();
|
||||
|
||||
ui->variables->setColumnCount(horizontalHeaders.size());
|
||||
ui->variables->horizontalHeader()->setSectionResizeMode(
|
||||
QHeaderView::ResizeMode::Stretch);
|
||||
ui->variables->setHorizontalHeaderLabels(horizontalHeaders);
|
||||
|
||||
for (const auto &var : variables) {
|
||||
auto variable = std::static_pointer_cast<Variable>(var);
|
||||
addVariableRow(ui->variables, variable.get());
|
||||
}
|
||||
|
||||
ui->variables->resizeColumnsToContents();
|
||||
ui->variables->resizeRowsToContents();
|
||||
|
||||
QWidget::connect(
|
||||
VariableSignalManager::Instance(),
|
||||
&VariableSignalManager::Rename,
|
||||
[this](const QString &oldName, const QString &newName) {
|
||||
renameVariable(ui->variables, oldName, newName);
|
||||
});
|
||||
QWidget::connect(VariableSignalManager::Instance(),
|
||||
&VariableSignalManager::Add, this,
|
||||
[this](const QString &name) {
|
||||
addVariableRow(ui->variables,
|
||||
GetVariableByQString(name));
|
||||
ui->variablesHelp->hide();
|
||||
setVariableTabVisible(ui->tabWidget, true);
|
||||
});
|
||||
QWidget::connect(VariableSignalManager::Instance(),
|
||||
&VariableSignalManager::Remove, this,
|
||||
[this](const QString &name) {
|
||||
removeVariableRow(ui->variables, name);
|
||||
if (ui->variables->rowCount() == 0) {
|
||||
ui->variablesHelp->show();
|
||||
}
|
||||
});
|
||||
QWidget::connect(ui->variables, &QTableWidget::cellDoubleClicked, this,
|
||||
&AdvSceneSwitcher::OpenSettingsForSelectedVariable);
|
||||
|
||||
auto timer = new QTimer(this);
|
||||
timer->setInterval(1000);
|
||||
QWidget::connect(timer, &QTimer::timeout,
|
||||
[this]() { updateVariableStatus(ui->variables); });
|
||||
timer->start();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::OpenSettingsForSelectedVariable()
|
||||
{
|
||||
auto selectedRows = ui->variables->selectionModel()->selectedRows();
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cell = ui->variables->item(selectedRows.last().row(), 0);
|
||||
auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
@@ -268,8 +177,8 @@ void AdvSceneSwitcher::OpenSettingsForSelectedVariable()
|
||||
}
|
||||
|
||||
auto oldName = variable->Name();
|
||||
bool accepted = VariableSettingsDialog::AskForSettings(ui->variables,
|
||||
*variable.get());
|
||||
bool accepted = VariableSettingsDialog::AskForSettings(
|
||||
tabWidget->Table(), *variable.get());
|
||||
if (accepted && oldName != variable->Name()) {
|
||||
VariableSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
@@ -277,35 +186,17 @@ void AdvSceneSwitcher::OpenSettingsForSelectedVariable()
|
||||
}
|
||||
}
|
||||
|
||||
static void removeVariablesWithNames(const QStringList &varNames)
|
||||
void VariableTable::Remove()
|
||||
{
|
||||
for (const auto &name : varNames) {
|
||||
auto variable = GetVariableByQString(name);
|
||||
if (!variable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &variables = GetVariables();
|
||||
variables.erase(
|
||||
std::remove_if(
|
||||
variables.begin(), variables.end(),
|
||||
[variable](const std::shared_ptr<Item> &item) {
|
||||
return item.get() == variable;
|
||||
}),
|
||||
variables.end());
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveSelectedVariables()
|
||||
{
|
||||
auto selectedRows = ui->variables->selectionModel()->selectedRows();
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList varNames;
|
||||
for (auto row : selectedRows) {
|
||||
auto cell = ui->variables->item(row.row(), 0);
|
||||
for (const auto &row : selectedRows) {
|
||||
auto cell = tabWidget->Table()->item(row.row(), 0);
|
||||
if (!cell) {
|
||||
continue;
|
||||
}
|
||||
@@ -330,7 +221,7 @@ void AdvSceneSwitcher::RemoveSelectedVariables()
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
removeVariablesWithNames(varNames);
|
||||
RemoveItemsByName(GetVariables(), varNames);
|
||||
}
|
||||
|
||||
for (const auto &name : varNames) {
|
||||
@@ -338,28 +229,71 @@ void AdvSceneSwitcher::RemoveSelectedVariables()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_variableAdd_clicked()
|
||||
VariableTable::VariableTable(QTabWidget *parent)
|
||||
: ResourceTable(
|
||||
parent, obs_module_text("AdvSceneSwitcher.variableTab.help"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.variableAddButton.tooltip"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip"),
|
||||
QStringList()
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.name.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.value.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.saveLoadBehavior.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastUsed.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastChanged.header"),
|
||||
openSettingsDialog)
|
||||
{
|
||||
auto newVariable = std::make_shared<Variable>();
|
||||
auto accepted =
|
||||
VariableSettingsDialog::AskForSettings(this, *newVariable);
|
||||
if (!accepted) {
|
||||
return;
|
||||
for (const auto &variable : GetVariables()) {
|
||||
auto v = std::static_pointer_cast<Variable>(variable);
|
||||
AddItemTableRow(Table(), getCellLabels(v.get()));
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto &variables = GetVariables();
|
||||
variables.emplace_back(newVariable);
|
||||
}
|
||||
|
||||
VariableSignalManager::Instance()->Add(
|
||||
QString::fromStdString(newVariable->Name()));
|
||||
SetHelpVisible(GetVariables().empty());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_variableRemove_clicked()
|
||||
static void setupTab(QTabWidget *tab)
|
||||
{
|
||||
RemoveSelectedVariables();
|
||||
if (GetVariables().empty()) {
|
||||
setTabVisible(tab, false);
|
||||
}
|
||||
|
||||
QWidget::connect(VariableSignalManager::Instance(),
|
||||
&VariableSignalManager::Rename,
|
||||
[](const QString &oldName, const QString &newName) {
|
||||
RenameItemTableRow(tabWidget->Table(), oldName,
|
||||
newName);
|
||||
});
|
||||
QWidget::connect(
|
||||
VariableSignalManager::Instance(), &VariableSignalManager::Add,
|
||||
[tab](const QString &name) {
|
||||
AddItemTableRow(
|
||||
tabWidget->Table(),
|
||||
getCellLabels(GetVariableByQString(name)));
|
||||
tabWidget->SetHelpVisible(false);
|
||||
tabWidget->HighlightAddButton(false);
|
||||
setTabVisible(tab, true);
|
||||
});
|
||||
QWidget::connect(VariableSignalManager::Instance(),
|
||||
&VariableSignalManager::Remove,
|
||||
[](const QString &name) {
|
||||
RemoveItemTableRow(tabWidget->Table(), name);
|
||||
if (tabWidget->Table()->rowCount() == 0) {
|
||||
tabWidget->SetHelpVisible(true);
|
||||
tabWidget->HighlightAddButton(true);
|
||||
}
|
||||
});
|
||||
|
||||
auto timer = new QTimer(tabWidget);
|
||||
timer->setInterval(1000);
|
||||
QWidget::connect(timer, &QTimer::timeout,
|
||||
[]() { updateVariableStatus(tabWidget->Table()); });
|
||||
timer->start();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
20
lib/variables/variable-tab.hpp
Normal file
20
lib/variables/variable-tab.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "resource-table.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class VariableTable final : public ResourceTable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static VariableTable *Create();
|
||||
|
||||
private slots:
|
||||
void Add();
|
||||
void Remove();
|
||||
|
||||
private:
|
||||
VariableTable(QTabWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -175,7 +175,9 @@ target_sources(
|
||||
utils/transition-selection.cpp
|
||||
utils/transition-selection.hpp
|
||||
utils/websocket-helpers.cpp
|
||||
utils/websocket-helpers.hpp)
|
||||
utils/websocket-helpers.hpp
|
||||
utils/websocket-tab.cpp
|
||||
utils/websocket-tab.hpp)
|
||||
|
||||
if(OS_WINDOWS)
|
||||
target_sources(${PROJECT_NAME} PRIVATE utils/windows/windows.cpp)
|
||||
|
||||
@@ -94,15 +94,15 @@ MacroActionSystrayEdit::MacroActionSystrayEdit(
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.systray.title")),
|
||||
row, 0);
|
||||
layout->addWidget(_title, ++row, 1);
|
||||
layout->addWidget(_title, row++, 1);
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.systray.message")),
|
||||
row, 0);
|
||||
layout->addWidget(_message, ++row, 1);
|
||||
layout->addWidget(_message, row++, 1);
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.systray.icon")),
|
||||
row, 0);
|
||||
layout->addWidget(_iconPath, ++row, 1);
|
||||
layout->addWidget(_iconPath, row++, 1);
|
||||
|
||||
auto mainlayout = new QVBoxLayout();
|
||||
mainlayout->addLayout(layout);
|
||||
|
||||
@@ -151,7 +151,7 @@ MacroActionWebsocketEdit::MacroActionWebsocketEdit(
|
||||
_apiType(new QComboBox(this)),
|
||||
_messageType(new QComboBox(this)),
|
||||
_message(new VariableTextEdit(this)),
|
||||
_connection(new ConnectionSelection(this)),
|
||||
_connection(new WSConnectionSelection(this)),
|
||||
_editLayout(new QHBoxLayout()),
|
||||
_settingsConflict(new QLabel())
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
API _api = API::SCENE_SWITCHER;
|
||||
MessageType _type = MessageType::REQUEST;
|
||||
StringVariable _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
std::weak_ptr<Connection> _connection;
|
||||
std::weak_ptr<WSConnection> _connection;
|
||||
|
||||
private:
|
||||
void SendRequest(const std::string &msg);
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
QComboBox *_apiType;
|
||||
QComboBox *_messageType;
|
||||
VariableTextEdit *_message;
|
||||
ConnectionSelection *_connection;
|
||||
WSConnectionSelection *_connection;
|
||||
QHBoxLayout *_editLayout;
|
||||
QLabel *_settingsConflict;
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ static std::string getRemoteData(std::string &url)
|
||||
return readBuffer;
|
||||
}
|
||||
|
||||
void MacroConditionFile::SetCondition(Condition condition)
|
||||
{
|
||||
_condition = condition;
|
||||
SetupTempVars();
|
||||
}
|
||||
|
||||
bool MacroConditionFile::MatchFileContent(QString &filedata)
|
||||
{
|
||||
if (_onlyMatchIfChanged) {
|
||||
@@ -65,6 +71,7 @@ bool MacroConditionFile::CheckRemoteFileContent()
|
||||
std::string path = _file;
|
||||
std::string data = getRemoteData(path);
|
||||
SetVariableValue(data);
|
||||
SetTempVarValue("content", data);
|
||||
QString qdata = QString::fromStdString(data);
|
||||
return MatchFileContent(qdata);
|
||||
}
|
||||
@@ -86,6 +93,7 @@ bool MacroConditionFile::CheckLocalFileContent()
|
||||
|
||||
QString filedata = QTextStream(&file).readAll();
|
||||
SetVariableValue(filedata.toStdString());
|
||||
SetTempVarValue("content", filedata.toStdString());
|
||||
bool match = MatchFileContent(filedata);
|
||||
|
||||
file.close();
|
||||
@@ -114,6 +122,7 @@ bool MacroConditionFile::CheckChangeContent()
|
||||
break;
|
||||
}
|
||||
|
||||
SetTempVarValue("content", filedata.toStdString());
|
||||
size_t newHash = strHash(filedata.toUtf8().constData());
|
||||
const bool contentChanged = newHash != _lastHash;
|
||||
_lastHash = newHash;
|
||||
@@ -131,24 +140,39 @@ bool MacroConditionFile::CheckChangeDate()
|
||||
SetVariableValue(newLastMod.toString().toStdString());
|
||||
const bool dateChanged = _lastMod != newLastMod;
|
||||
_lastMod = newLastMod;
|
||||
SetTempVarValue("date", newLastMod.toString(Qt::ISODate).toStdString());
|
||||
return dateChanged;
|
||||
}
|
||||
|
||||
void MacroConditionFile::SetupTempVars()
|
||||
{
|
||||
MacroCondition::SetupTempVars();
|
||||
if (_condition == Condition::DATE_CHANGE) {
|
||||
AddTempvar(
|
||||
"date",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.file.date"));
|
||||
} else {
|
||||
AddTempvar("content",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.file.content"));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroConditionFile::CheckCondition()
|
||||
{
|
||||
bool ret = false;
|
||||
switch (_condition) {
|
||||
case MacroConditionFile::ConditionType::MATCH:
|
||||
case Condition::MATCH:
|
||||
if (_fileType == FileType::REMOTE) {
|
||||
ret = CheckRemoteFileContent();
|
||||
break;
|
||||
}
|
||||
ret = CheckLocalFileContent();
|
||||
break;
|
||||
case MacroConditionFile::ConditionType::CONTENT_CHANGE:
|
||||
case Condition::CONTENT_CHANGE:
|
||||
ret = CheckChangeContent();
|
||||
break;
|
||||
case MacroConditionFile::ConditionType::DATE_CHANGE:
|
||||
case Condition::DATE_CHANGE:
|
||||
ret = CheckChangeDate();
|
||||
break;
|
||||
default:
|
||||
@@ -158,7 +182,6 @@ bool MacroConditionFile::CheckCondition()
|
||||
if (GetVariableValue().empty()) {
|
||||
SetVariableValue(ret ? "true" : "false");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -187,8 +210,8 @@ bool MacroConditionFile::Load(obs_data_t *obj)
|
||||
_file.Load(obj, "file");
|
||||
_text.Load(obj, "text");
|
||||
_fileType = static_cast<FileType>(obs_data_get_int(obj, "fileType"));
|
||||
_condition =
|
||||
static_cast<ConditionType>(obs_data_get_int(obj, "condition"));
|
||||
SetCondition(
|
||||
static_cast<Condition>(obs_data_get_int(obj, "condition")));
|
||||
_useTime = obs_data_get_bool(obj, "useTime");
|
||||
_onlyMatchIfChanged = obs_data_get_bool(obj, "onlyMatchIfChanged");
|
||||
return true;
|
||||
@@ -292,7 +315,8 @@ void MacroConditionFileEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_fileTypes->setCurrentIndex(static_cast<int>(_entryData->_fileType));
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_conditions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->GetCondition()));
|
||||
_filePath->SetPath(_entryData->_file);
|
||||
_matchText->setPlainText(_entryData->_text);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
@@ -338,8 +362,8 @@ void MacroConditionFileEdit::ConditionChanged(int index)
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_condition =
|
||||
static_cast<MacroConditionFile::ConditionType>(index);
|
||||
_entryData->SetCondition(
|
||||
static_cast<MacroConditionFile::Condition>(index));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -406,18 +430,18 @@ void MacroConditionFileEdit::SetWidgetVisibility()
|
||||
return;
|
||||
}
|
||||
|
||||
_matchText->setVisible(_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_regex->setVisible(_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_matchText->setVisible(_entryData->GetCondition() ==
|
||||
MacroConditionFile::Condition::MATCH);
|
||||
_regex->setVisible(_entryData->GetCondition() ==
|
||||
MacroConditionFile::Condition::MATCH);
|
||||
_checkModificationDate->setVisible(
|
||||
_entryData->_useTime &&
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_entryData->GetCondition() ==
|
||||
MacroConditionFile::Condition::MATCH);
|
||||
_checkFileContent->setVisible(
|
||||
_entryData->_onlyMatchIfChanged &&
|
||||
_entryData->_condition ==
|
||||
MacroConditionFile::ConditionType::MATCH);
|
||||
_entryData->GetCondition() ==
|
||||
MacroConditionFile::Condition::MATCH);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -31,16 +31,17 @@ public:
|
||||
REMOTE,
|
||||
};
|
||||
|
||||
enum class ConditionType {
|
||||
enum class Condition {
|
||||
MATCH,
|
||||
CONTENT_CHANGE,
|
||||
DATE_CHANGE,
|
||||
};
|
||||
void SetCondition(Condition condition);
|
||||
Condition GetCondition() const { return _condition; }
|
||||
|
||||
StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
StringVariable _text = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
FileType _fileType = FileType::LOCAL;
|
||||
ConditionType _condition = ConditionType::MATCH;
|
||||
RegexConfig _regex;
|
||||
|
||||
// TODO: Remove in future version
|
||||
@@ -53,7 +54,9 @@ private:
|
||||
bool CheckLocalFileContent();
|
||||
bool CheckChangeContent();
|
||||
bool CheckChangeDate();
|
||||
void SetupTempVars();
|
||||
|
||||
Condition _condition = Condition::MATCH;
|
||||
QDateTime _lastMod;
|
||||
size_t _lastHash = 0;
|
||||
static bool _registered;
|
||||
|
||||
@@ -23,13 +23,17 @@ MacroConditionHotkey::MacroConditionHotkey(Macro *m) : MacroCondition(m)
|
||||
|
||||
bool MacroConditionHotkey::CheckCondition()
|
||||
{
|
||||
const bool hotkeyIsCurrentlyPressed = _hotkey->GetPressed();
|
||||
const bool hotkeyWasPressedSinceLastCheck = _hotkey->GetLastPressed() >
|
||||
_lastCheck;
|
||||
const bool keyStateCurrentlyMatches =
|
||||
_checkPressed ? _hotkey->GetPressed() : !_hotkey->GetPressed();
|
||||
const auto lastKeyStateMatch = _checkPressed
|
||||
? _hotkey->GetLastPressed()
|
||||
: _hotkey->GetLastReleased();
|
||||
const bool hotkeySateChangedSinceLastCheck = lastKeyStateMatch >
|
||||
_lastCheck;
|
||||
const bool macroWasPausedSinceLastCheck =
|
||||
MacroWasPausedSince(GetMacro(), _lastCheck);
|
||||
bool ret = hotkeyIsCurrentlyPressed ||
|
||||
(hotkeyWasPressedSinceLastCheck &&
|
||||
bool ret = keyStateCurrentlyMatches ||
|
||||
(hotkeySateChangedSinceLastCheck &&
|
||||
!macroWasPausedSinceLastCheck);
|
||||
_lastCheck = std::chrono::high_resolution_clock::now();
|
||||
return ret;
|
||||
@@ -39,6 +43,7 @@ bool MacroConditionHotkey::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_hotkey->Save(obj);
|
||||
obs_data_set_bool(obj, "checkPressed", _checkPressed);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -52,34 +57,47 @@ bool MacroConditionHotkey::Load(obs_data_t *obj)
|
||||
"hotkey name conflict for \"%s\" - using previous key bind",
|
||||
description);
|
||||
}
|
||||
if (obs_data_has_user_value(obj, "checkPressed")) {
|
||||
_checkPressed = obs_data_get_bool(obj, "checkPressed");
|
||||
} else { // TODO: Remove fallback at some point in the future
|
||||
_checkPressed = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroConditionHotkeyEdit::MacroConditionHotkeyEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionHotkey> entryData)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),
|
||||
_name(new QLineEdit()),
|
||||
_keyState(new QComboBox())
|
||||
{
|
||||
_name = new QLineEdit();
|
||||
QLabel *line1 = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.hotkey.entry.line1"));
|
||||
QLabel *hint = new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.condition.hotkey.tip"));
|
||||
_keyState->addItems(
|
||||
QStringList()
|
||||
<< obs_module_text("AdvSceneSwitcher.condition.hotkey.pressed")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.condition.hotkey.released"));
|
||||
|
||||
QWidget::connect(_name, SIGNAL(editingFinished()), this,
|
||||
SLOT(NameChanged()));
|
||||
QWidget::connect(_keyState, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(KeyStateChanged(int)));
|
||||
|
||||
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{name}}", _name},
|
||||
};
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.hotkey.entry.line2"),
|
||||
switchLayout, widgetPlaceholders);
|
||||
auto keyStateLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.hotkey.entry.keyState"),
|
||||
keyStateLayout, {{"{{keyState}}", _keyState}});
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(line1);
|
||||
mainLayout->addLayout(switchLayout);
|
||||
mainLayout->addWidget(hint);
|
||||
auto nameLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.hotkey.entry.name"),
|
||||
nameLayout, {{"{{name}}", _name}});
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(keyStateLayout);
|
||||
mainLayout->addLayout(nameLayout);
|
||||
mainLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.condition.hotkey.tip")));
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -116,6 +134,17 @@ void MacroConditionHotkeyEdit::UpdateEntryData()
|
||||
|
||||
_name->setText(
|
||||
QString::fromStdString(_entryData->_hotkey->GetDescription()));
|
||||
_keyState->setCurrentIndex(_entryData->_checkPressed ? 0 : 1);
|
||||
}
|
||||
|
||||
void MacroConditionHotkeyEdit::KeyStateChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_checkPressed = index == 0;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
}
|
||||
|
||||
std::shared_ptr<Hotkey> _hotkey;
|
||||
bool _checkPressed = true;
|
||||
|
||||
private:
|
||||
std::chrono::high_resolution_clock::time_point _lastCheck{};
|
||||
@@ -46,12 +47,12 @@ public:
|
||||
|
||||
private slots:
|
||||
void NameChanged();
|
||||
|
||||
protected:
|
||||
QLineEdit *_name;
|
||||
std::shared_ptr<MacroConditionHotkey> _entryData;
|
||||
void KeyStateChanged(int);
|
||||
|
||||
private:
|
||||
QLineEdit *_name;
|
||||
QComboBox *_keyState;
|
||||
std::shared_ptr<MacroConditionHotkey> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ void MacroConditionWebsocket::SetConnection(const std::string &connectionName)
|
||||
_messageBuffer = connection->RegisterForEvents();
|
||||
}
|
||||
|
||||
std::weak_ptr<Connection> MacroConditionWebsocket::GetConnection() const
|
||||
std::weak_ptr<WSConnection> MacroConditionWebsocket::GetConnection() const
|
||||
{
|
||||
return _connection;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ MacroConditionWebsocketEdit::MacroConditionWebsocketEdit(
|
||||
_conditions(new QComboBox(this)),
|
||||
_message(new VariableTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(parent)),
|
||||
_connection(new ConnectionSelection(this)),
|
||||
_connection(new WSConnectionSelection(this)),
|
||||
_editLayout(new QHBoxLayout())
|
||||
{
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
void SetType(Type);
|
||||
Type GetType() const { return _type; }
|
||||
void SetConnection(const std::string &);
|
||||
std::weak_ptr<Connection> GetConnection() const;
|
||||
std::weak_ptr<WSConnection> GetConnection() const;
|
||||
StringVariable _message = obs_module_text("AdvSceneSwitcher.enterText");
|
||||
RegexConfig _regex;
|
||||
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
void SetupTempVars();
|
||||
|
||||
Type _type = Type::REQUEST;
|
||||
std::weak_ptr<Connection> _connection;
|
||||
std::weak_ptr<WSConnection> _connection;
|
||||
|
||||
WebsocketMessageBuffer _messageBuffer;
|
||||
std::chrono::high_resolution_clock::time_point _lastCheck{};
|
||||
@@ -82,7 +82,7 @@ private:
|
||||
QComboBox *_conditions;
|
||||
VariableTextEdit *_message;
|
||||
RegexConfigWidget *_regex;
|
||||
ConnectionSelection *_connection;
|
||||
WSConnectionSelection *_connection;
|
||||
QHBoxLayout *_editLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
||||
Q_DECLARE_METATYPE(advss::Connection *);
|
||||
Q_DECLARE_METATYPE(advss::WSConnection *);
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -55,7 +55,7 @@ static void loadConnections(obs_data_t *obj)
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(connectionArray, i);
|
||||
auto con = Connection::Create();
|
||||
auto con = WSConnection::Create();
|
||||
connections.emplace_back(con);
|
||||
connections.back()->Load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
@@ -63,10 +63,11 @@ static void loadConnections(obs_data_t *obj)
|
||||
obs_data_array_release(connectionArray);
|
||||
}
|
||||
|
||||
Connection::Connection(bool useCustomURI, std::string customURI,
|
||||
std::string name, std::string address, uint64_t port,
|
||||
std::string pass, bool connectOnStart, bool reconnect,
|
||||
int reconnectDelay, bool useOBSWebsocketProtocol)
|
||||
WSConnection::WSConnection(bool useCustomURI, std::string customURI,
|
||||
std::string name, std::string address, uint64_t port,
|
||||
std::string pass, bool connectOnStart,
|
||||
bool reconnect, int reconnectDelay,
|
||||
bool useOBSWebsocketProtocol)
|
||||
: Item(name),
|
||||
_useCustomURI(useCustomURI),
|
||||
_customURI(customURI),
|
||||
@@ -81,7 +82,7 @@ Connection::Connection(bool useCustomURI, std::string customURI,
|
||||
{
|
||||
}
|
||||
|
||||
Connection::Connection(const Connection &other) : Item(other)
|
||||
WSConnection::WSConnection(const WSConnection &other) : Item(other)
|
||||
{
|
||||
_useCustomURI = other._useCustomURI;
|
||||
_customURI = other._customURI;
|
||||
@@ -96,7 +97,7 @@ Connection::Connection(const Connection &other) : Item(other)
|
||||
_client.UseOBSWebsocketProtocol(_useOBSWSProtocol);
|
||||
}
|
||||
|
||||
Connection &Connection::operator=(const Connection &other)
|
||||
WSConnection &WSConnection::operator=(const WSConnection &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
_useCustomURI = other._useCustomURI;
|
||||
@@ -115,7 +116,7 @@ Connection &Connection::operator=(const Connection &other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
WSConnection::~WSConnection()
|
||||
{
|
||||
_client.Disconnect();
|
||||
}
|
||||
@@ -125,7 +126,7 @@ static std::string constructUri(std::string addr, int port)
|
||||
return "ws://" + addr + ":" + std::to_string(port);
|
||||
}
|
||||
|
||||
std::string Connection::GetURI()
|
||||
std::string WSConnection::GetURI() const
|
||||
{
|
||||
if (_useCustomURI) {
|
||||
return _customURI;
|
||||
@@ -133,16 +134,16 @@ std::string Connection::GetURI()
|
||||
return constructUri(_address, _port);
|
||||
}
|
||||
|
||||
void Connection::Reconnect()
|
||||
void WSConnection::Reconnect()
|
||||
{
|
||||
_client.Disconnect();
|
||||
_client.Connect(GetURI(), _password, _reconnect, _reconnectDelay);
|
||||
}
|
||||
|
||||
void Connection::SendMsg(const std::string &msg)
|
||||
void WSConnection::SendMsg(const std::string &msg)
|
||||
{
|
||||
const auto status = _client.GetStatus();
|
||||
if (status == WSConnection::Status::DISCONNECTED) {
|
||||
if (status == WSClientConnection::Status::DISCONNECTED) {
|
||||
_client.Connect(GetURI(), _password, _reconnect,
|
||||
_reconnectDelay);
|
||||
blog(LOG_WARNING,
|
||||
@@ -151,12 +152,12 @@ void Connection::SendMsg(const std::string &msg)
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == WSConnection::Status::AUTHENTICATED) {
|
||||
if (status == WSClientConnection::Status::AUTHENTICATED) {
|
||||
_client.SendRequest(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Load(obs_data_t *obj)
|
||||
void WSConnection::Load(obs_data_t *obj)
|
||||
{
|
||||
Item::Load(obj);
|
||||
|
||||
@@ -184,7 +185,7 @@ void Connection::Load(obs_data_t *obj)
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Save(obs_data_t *obj) const
|
||||
void WSConnection::Save(obs_data_t *obj) const
|
||||
{
|
||||
Item::Save(obj);
|
||||
obs_data_set_bool(obj, "useCustomURI", _useCustomURI);
|
||||
@@ -199,50 +200,50 @@ void Connection::Save(obs_data_t *obj) const
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
}
|
||||
|
||||
WebsocketMessageBuffer Connection::RegisterForEvents()
|
||||
WebsocketMessageBuffer WSConnection::RegisterForEvents()
|
||||
{
|
||||
return _client.RegisterForEvents();
|
||||
}
|
||||
|
||||
void Connection::UseOBSWebsocketProtocol(bool useOBSWSProtocol)
|
||||
void WSConnection::UseOBSWebsocketProtocol(bool useOBSWSProtocol)
|
||||
{
|
||||
_useOBSWSProtocol = useOBSWSProtocol;
|
||||
_client.UseOBSWebsocketProtocol(useOBSWSProtocol);
|
||||
}
|
||||
|
||||
Connection *GetConnectionByName(const QString &name)
|
||||
WSConnection *GetConnectionByName(const QString &name)
|
||||
{
|
||||
return GetConnectionByName(name.toStdString());
|
||||
}
|
||||
|
||||
Connection *GetConnectionByName(const std::string &name)
|
||||
WSConnection *GetConnectionByName(const std::string &name)
|
||||
{
|
||||
for (auto &con : connections) {
|
||||
if (con->Name() == name) {
|
||||
return dynamic_cast<Connection *>(con.get());
|
||||
return dynamic_cast<WSConnection *>(con.get());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::weak_ptr<Connection> GetWeakConnectionByName(const std::string &name)
|
||||
std::weak_ptr<WSConnection> GetWeakConnectionByName(const std::string &name)
|
||||
{
|
||||
for (const auto &c : connections) {
|
||||
if (c->Name() == name) {
|
||||
std::weak_ptr<Connection> wp =
|
||||
std::dynamic_pointer_cast<Connection>(c);
|
||||
std::weak_ptr<WSConnection> wp =
|
||||
std::dynamic_pointer_cast<WSConnection>(c);
|
||||
return wp;
|
||||
}
|
||||
}
|
||||
return std::weak_ptr<Connection>();
|
||||
return std::weak_ptr<WSConnection>();
|
||||
}
|
||||
|
||||
std::weak_ptr<Connection> GetWeakConnectionByQString(const QString &name)
|
||||
std::weak_ptr<WSConnection> GetWeakConnectionByQString(const QString &name)
|
||||
{
|
||||
return GetWeakConnectionByName(name.toStdString());
|
||||
}
|
||||
|
||||
std::string GetWeakConnectionName(std::weak_ptr<Connection> connection)
|
||||
std::string GetWeakConnectionName(std::weak_ptr<WSConnection> connection)
|
||||
{
|
||||
auto con = connection.lock();
|
||||
if (!con) {
|
||||
@@ -268,40 +269,45 @@ static bool ConnectionNameAvailable(const std::string &name)
|
||||
|
||||
static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
|
||||
{
|
||||
Connection &ConnectionSettings = dynamic_cast<Connection &>(settings);
|
||||
return ConnectionSettingsDialog::AskForSettings(parent,
|
||||
ConnectionSettings);
|
||||
WSConnection &ConnectionSettings =
|
||||
dynamic_cast<WSConnection &>(settings);
|
||||
return WSConnectionSettingsDialog::AskForSettings(parent,
|
||||
ConnectionSettings);
|
||||
}
|
||||
|
||||
ConnectionSelection::ConnectionSelection(QWidget *parent)
|
||||
: ItemSelection(connections, Connection::Create, AskForSettingsWrapper,
|
||||
WSConnectionSelection::WSConnectionSelection(QWidget *parent)
|
||||
: ItemSelection(connections, WSConnection::Create,
|
||||
AskForSettingsWrapper,
|
||||
"AdvSceneSwitcher.connection.select",
|
||||
"AdvSceneSwitcher.connection.add",
|
||||
"AdvSceneSwitcher.item.nameNotAvailable",
|
||||
"AdvSceneSwitcher.connection.configure", parent)
|
||||
{
|
||||
// Connect to slots
|
||||
QWidget::connect(
|
||||
window(),
|
||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)),
|
||||
this, SLOT(RenameItem(const QString &, const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(ConnectionAdded(const QString &)),
|
||||
this, SLOT(AddItem(const QString &)));
|
||||
QWidget::connect(window(), SIGNAL(ConnectionRemoved(const QString &)),
|
||||
this, SLOT(RemoveItem(const QString &)));
|
||||
QWidget::connect(ConnectionSelectionSignalManager::Instance(),
|
||||
SIGNAL(Rename(const QString &, const QString &)), this,
|
||||
SLOT(RenameItem(const QString &, const QString &)));
|
||||
QWidget::connect(ConnectionSelectionSignalManager::Instance(),
|
||||
SIGNAL(Add(const QString &)), this,
|
||||
SLOT(AddItem(const QString &)));
|
||||
QWidget::connect(ConnectionSelectionSignalManager::Instance(),
|
||||
SIGNAL(Remove(const QString &)), this,
|
||||
SLOT(RemoveItem(const QString &)));
|
||||
|
||||
// Forward signals
|
||||
QWidget::connect(
|
||||
this, SIGNAL(ItemRenamed(const QString &, const QString &)),
|
||||
window(),
|
||||
SIGNAL(ConnectionRenamed(const QString &, const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ItemAdded(const QString &)), window(),
|
||||
SIGNAL(ConnectionAdded(const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ItemRemoved(const QString &)), window(),
|
||||
SIGNAL(ConnectionRemoved(const QString &)));
|
||||
QWidget::connect(this,
|
||||
SIGNAL(ItemRenamed(const QString &, const QString &)),
|
||||
ConnectionSelectionSignalManager::Instance(),
|
||||
SIGNAL(Rename(const QString &, const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ItemAdded(const QString &)),
|
||||
ConnectionSelectionSignalManager::Instance(),
|
||||
SIGNAL(Add(const QString &)));
|
||||
QWidget::connect(this, SIGNAL(ItemRemoved(const QString &)),
|
||||
ConnectionSelectionSignalManager::Instance(),
|
||||
SIGNAL(Remove(const QString &)));
|
||||
}
|
||||
|
||||
void ConnectionSelection::SetConnection(const std::string &con)
|
||||
void WSConnectionSelection::SetConnection(const std::string &con)
|
||||
{
|
||||
if (!!GetConnectionByName(con)) {
|
||||
SetItem(con);
|
||||
@@ -310,8 +316,8 @@ void ConnectionSelection::SetConnection(const std::string &con)
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSelection::SetConnection(
|
||||
const std::weak_ptr<Connection> &connection_)
|
||||
void WSConnectionSelection::SetConnection(
|
||||
const std::weak_ptr<WSConnection> &connection_)
|
||||
{
|
||||
auto connection = connection_.lock();
|
||||
if (connection) {
|
||||
@@ -321,8 +327,8 @@ void ConnectionSelection::SetConnection(
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||
const Connection &settings)
|
||||
WSConnectionSettingsDialog::WSConnectionSettingsDialog(
|
||||
QWidget *parent, const WSConnection &settings)
|
||||
: ItemSettingsDialog(settings, connections,
|
||||
"AdvSceneSwitcher.connection.select",
|
||||
"AdvSceneSwitcher.connection.add",
|
||||
@@ -449,7 +455,7 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
|
||||
UseCustomURIChanged(settings._useCustomURI);
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::UseCustomURIChanged(int state)
|
||||
void WSConnectionSettingsDialog::UseCustomURIChanged(int state)
|
||||
{
|
||||
SetGridLayoutRowVisible(_layout, _addressRow, !state);
|
||||
SetGridLayoutRowVisible(_layout, _portRow, !state);
|
||||
@@ -459,33 +465,33 @@ void ConnectionSettingsDialog::UseCustomURIChanged(int state)
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::ProtocolChanged(int state)
|
||||
void WSConnectionSettingsDialog::ProtocolChanged(int state)
|
||||
{
|
||||
_password->setEnabled(state);
|
||||
_showPassword->setEnabled(state);
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::ReconnectChanged(int state)
|
||||
void WSConnectionSettingsDialog::ReconnectChanged(int state)
|
||||
{
|
||||
_reconnectDelay->setEnabled(state);
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::SetStatus()
|
||||
void WSConnectionSettingsDialog::SetStatus()
|
||||
{
|
||||
switch (_testConnection.GetStatus()) {
|
||||
case WSConnection::Status::DISCONNECTED:
|
||||
case WSClientConnection::Status::DISCONNECTED:
|
||||
_status->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.status.disconnected"));
|
||||
break;
|
||||
case WSConnection::Status::CONNECTING:
|
||||
case WSClientConnection::Status::CONNECTING:
|
||||
_status->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.status.connecting"));
|
||||
break;
|
||||
case WSConnection::Status::CONNECTED:
|
||||
case WSClientConnection::Status::CONNECTED:
|
||||
_status->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.status.connected"));
|
||||
break;
|
||||
case WSConnection::Status::AUTHENTICATED:
|
||||
case WSClientConnection::Status::AUTHENTICATED:
|
||||
_status->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.connection.status.authenticated"));
|
||||
break;
|
||||
@@ -494,19 +500,19 @@ void ConnectionSettingsDialog::SetStatus()
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::ShowPassword()
|
||||
void WSConnectionSettingsDialog::ShowPassword()
|
||||
{
|
||||
SetButtonIcon(_showPassword, ":res/images/visible.svg");
|
||||
_password->setEchoMode(QLineEdit::Normal);
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::HidePassword()
|
||||
void WSConnectionSettingsDialog::HidePassword()
|
||||
{
|
||||
SetButtonIcon(_showPassword, ":res/images/invisible.svg");
|
||||
_password->setEchoMode(QLineEdit::PasswordEchoOnEdit);
|
||||
}
|
||||
|
||||
void ConnectionSettingsDialog::TestConnection()
|
||||
void WSConnectionSettingsDialog::TestConnection()
|
||||
{
|
||||
_testConnection.UseOBSWebsocketProtocol(_useOBSWSProtocol->isChecked());
|
||||
_testConnection.Disconnect();
|
||||
@@ -517,14 +523,14 @@ void ConnectionSettingsDialog::TestConnection()
|
||||
_testConnection.Connect(uri, _password->text().toStdString(), false);
|
||||
_statusTimer.setInterval(1000);
|
||||
QWidget::connect(&_statusTimer, &QTimer::timeout, this,
|
||||
&ConnectionSettingsDialog::SetStatus);
|
||||
&WSConnectionSettingsDialog::SetStatus);
|
||||
_statusTimer.start();
|
||||
}
|
||||
|
||||
bool ConnectionSettingsDialog::AskForSettings(QWidget *parent,
|
||||
Connection &settings)
|
||||
bool WSConnectionSettingsDialog::AskForSettings(QWidget *parent,
|
||||
WSConnection &settings)
|
||||
{
|
||||
ConnectionSettingsDialog dialog(parent, settings);
|
||||
WSConnectionSettingsDialog dialog(parent, settings);
|
||||
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
@@ -544,4 +550,16 @@ bool ConnectionSettingsDialog::AskForSettings(QWidget *parent,
|
||||
return true;
|
||||
}
|
||||
|
||||
ConnectionSelectionSignalManager::ConnectionSelectionSignalManager(
|
||||
QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ConnectionSelectionSignalManager *ConnectionSelectionSignalManager::Instance()
|
||||
{
|
||||
static ConnectionSelectionSignalManager manager;
|
||||
return &manager;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -18,22 +18,22 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ConnectionSelection;
|
||||
class ConnectionSettingsDialog;
|
||||
class WSConnectionSelection;
|
||||
class WSConnectionSettingsDialog;
|
||||
|
||||
class Connection : public Item {
|
||||
class WSConnection : public Item {
|
||||
public:
|
||||
Connection(bool useCustomURI, std::string customURI, std::string name,
|
||||
std::string address, uint64_t port, std::string pass,
|
||||
bool connectOnStart, bool reconnect, int reconnectDelay,
|
||||
bool useOBSWebsocketProtocol);
|
||||
Connection() = default;
|
||||
Connection(const Connection &);
|
||||
Connection &operator=(const Connection &);
|
||||
~Connection();
|
||||
WSConnection(bool useCustomURI, std::string customURI, std::string name,
|
||||
std::string address, uint64_t port, std::string pass,
|
||||
bool connectOnStart, bool reconnect, int reconnectDelay,
|
||||
bool useOBSWebsocketProtocol);
|
||||
WSConnection() = default;
|
||||
WSConnection(const WSConnection &);
|
||||
WSConnection &operator=(const WSConnection &);
|
||||
~WSConnection();
|
||||
static std::shared_ptr<Item> Create()
|
||||
{
|
||||
return std::make_shared<Connection>();
|
||||
return std::make_shared<WSConnection>();
|
||||
}
|
||||
|
||||
void Reconnect();
|
||||
@@ -43,10 +43,11 @@ public:
|
||||
std::string GetName() const { return _name; }
|
||||
WebsocketMessageBuffer RegisterForEvents();
|
||||
bool IsUsingOBSProtocol() const { return _useOBSWSProtocol; }
|
||||
std::string GetURI() const;
|
||||
uint64_t GetPort() const { return _port; }
|
||||
|
||||
private:
|
||||
void UseOBSWebsocketProtocol(bool);
|
||||
std::string GetURI();
|
||||
|
||||
bool _useCustomURI = false;
|
||||
std::string _customURI = "ws://localhost:4455";
|
||||
@@ -58,25 +59,18 @@ private:
|
||||
int _reconnectDelay = 3;
|
||||
bool _useOBSWSProtocol = true;
|
||||
|
||||
WSConnection _client;
|
||||
WSClientConnection _client;
|
||||
|
||||
friend ConnectionSelection;
|
||||
friend ConnectionSettingsDialog;
|
||||
friend WSConnectionSelection;
|
||||
friend WSConnectionSettingsDialog;
|
||||
};
|
||||
|
||||
Connection *GetConnectionByName(const QString &);
|
||||
Connection *GetConnectionByName(const std::string &);
|
||||
std::weak_ptr<Connection> GetWeakConnectionByName(const std::string &name);
|
||||
std::weak_ptr<Connection> GetWeakConnectionByQString(const QString &name);
|
||||
std::string GetWeakConnectionName(std::weak_ptr<Connection>);
|
||||
std::deque<std::shared_ptr<Item>> &GetConnections();
|
||||
|
||||
class ConnectionSettingsDialog : public ItemSettingsDialog {
|
||||
class WSConnectionSettingsDialog : public ItemSettingsDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionSettingsDialog(QWidget *parent, const Connection &);
|
||||
static bool AskForSettings(QWidget *parent, Connection &settings);
|
||||
WSConnectionSettingsDialog(QWidget *parent, const WSConnection &);
|
||||
static bool AskForSettings(QWidget *parent, WSConnection &settings);
|
||||
|
||||
private slots:
|
||||
void UseCustomURIChanged(int);
|
||||
@@ -103,20 +97,39 @@ private:
|
||||
QGridLayout *_layout;
|
||||
|
||||
QTimer _statusTimer;
|
||||
WSConnection _testConnection;
|
||||
WSClientConnection _testConnection;
|
||||
|
||||
int _customURIRow = -1;
|
||||
int _addressRow = -1;
|
||||
int _portRow = -1;
|
||||
};
|
||||
|
||||
class ConnectionSelection : public ItemSelection {
|
||||
class WSConnectionSelection : public ItemSelection {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionSelection(QWidget *parent = 0);
|
||||
WSConnectionSelection(QWidget *parent = 0);
|
||||
void SetConnection(const std::string &);
|
||||
void SetConnection(const std::weak_ptr<Connection> &);
|
||||
void SetConnection(const std::weak_ptr<WSConnection> &);
|
||||
};
|
||||
|
||||
class ConnectionSelectionSignalManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConnectionSelectionSignalManager(QObject *parent = nullptr);
|
||||
static ConnectionSelectionSignalManager *Instance();
|
||||
|
||||
signals:
|
||||
void Rename(const QString &, const QString &);
|
||||
void Add(const QString &);
|
||||
void Remove(const QString &);
|
||||
};
|
||||
|
||||
WSConnection *GetConnectionByName(const QString &);
|
||||
WSConnection *GetConnectionByName(const std::string &);
|
||||
std::weak_ptr<WSConnection> GetWeakConnectionByName(const std::string &name);
|
||||
std::weak_ptr<WSConnection> GetWeakConnectionByQString(const QString &name);
|
||||
std::string GetWeakConnectionName(std::weak_ptr<WSConnection>);
|
||||
std::deque<std::shared_ptr<Item>> &GetConnections();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -116,6 +116,9 @@ void Hotkey::Callback(void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed)
|
||||
if (pressed) {
|
||||
hotkey->_lastPressed =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
} else {
|
||||
hotkey->_lastReleased =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
hotkey->_pressed = pressed;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
|
||||
bool GetPressed() const { return _pressed; }
|
||||
auto GetLastPressed() const { return _lastPressed; }
|
||||
auto GetLastReleased() const { return _lastReleased; }
|
||||
std::string GetDescription() const { return _description; }
|
||||
bool UpdateDescription(const std::string &);
|
||||
|
||||
@@ -43,6 +44,7 @@ private:
|
||||
obs_hotkey_id _hotkeyID = OBS_INVALID_HOTKEY_ID;
|
||||
bool _pressed = false;
|
||||
std::chrono::high_resolution_clock::time_point _lastPressed{};
|
||||
std::chrono::high_resolution_clock::time_point _lastReleased{};
|
||||
// When set will not attempt to share settings with existing hotkey
|
||||
bool _ignoreExistingHotkeys = false;
|
||||
};
|
||||
|
||||
@@ -232,7 +232,7 @@ void SceneItemSelection::Save(obs_data_t *obj, const char *name) const
|
||||
break;
|
||||
case SceneItemSelection::Type::INDEX_RANGE:
|
||||
_index.Save(data, indexSaveName.data());
|
||||
_index.Save(data, indexEndSaveName.data());
|
||||
_indexEnd.Save(data, indexEndSaveName.data());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -648,8 +648,10 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
|
||||
"AdvSceneSwitcher.sceneItemSelection.configure"));
|
||||
|
||||
_index->setMinimum(1);
|
||||
_index->setMaximum(999);
|
||||
_index->setSuffix(".");
|
||||
_indexEnd->setMinimum(1);
|
||||
_indexEnd->setMaximum(999);
|
||||
_indexEnd->setSuffix(".");
|
||||
|
||||
populateSourceGroupSelection(_sourceGroups);
|
||||
|
||||
@@ -243,6 +243,9 @@ SourceSettingSelection::SourceSettingSelection(QWidget *parent)
|
||||
_tooltip->setPixmap(pixmap);
|
||||
_tooltip->hide();
|
||||
|
||||
_settings->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_settings->setMaximumWidth(350);
|
||||
|
||||
QWidget::connect(_settings, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(SelectionIdxChanged(int)));
|
||||
|
||||
|
||||
@@ -137,9 +137,9 @@ void StringListEdit::showEvent(QShowEvent *e)
|
||||
void StringListEdit::Add()
|
||||
{
|
||||
std::string name;
|
||||
bool accepted = AdvSSNameDialog::AskForName(this, _addString,
|
||||
_addStringDescription, name,
|
||||
"", _maxStringSize, false);
|
||||
bool accepted = NameDialog::AskForName(this, _addString,
|
||||
_addStringDescription, name, "",
|
||||
_maxStringSize, false);
|
||||
|
||||
if (!accepted || (!_allowEmpty && name.empty())) {
|
||||
return;
|
||||
@@ -205,10 +205,10 @@ void StringListEdit::Down()
|
||||
void StringListEdit::Clicked(QListWidgetItem *item)
|
||||
{
|
||||
std::string name;
|
||||
bool accepted = AdvSSNameDialog::AskForName(this, _addString,
|
||||
_addStringDescription, name,
|
||||
item->text(),
|
||||
_maxStringSize, false);
|
||||
bool accepted = NameDialog::AskForName(this, _addString,
|
||||
_addStringDescription, name,
|
||||
item->text(), _maxStringSize,
|
||||
false);
|
||||
|
||||
if (!accepted || (!_allowEmpty && name.empty())) {
|
||||
return;
|
||||
|
||||
@@ -86,7 +86,7 @@ static void registerWebsocketVendor()
|
||||
}
|
||||
}
|
||||
|
||||
WSConnection::WSConnection(bool useOBSProtocol) : QObject(nullptr)
|
||||
WSClientConnection::WSClientConnection(bool useOBSProtocol) : QObject(nullptr)
|
||||
{
|
||||
_client.get_alog().clear_channels(
|
||||
websocketpp::log::alevel::frame_header |
|
||||
@@ -98,15 +98,15 @@ WSConnection::WSConnection(bool useOBSProtocol) : QObject(nullptr)
|
||||
#endif
|
||||
|
||||
UseOBSWebsocketProtocol(useOBSProtocol);
|
||||
_client.set_close_handler(bind(&WSConnection::OnClose, this, _1));
|
||||
_client.set_close_handler(bind(&WSClientConnection::OnClose, this, _1));
|
||||
}
|
||||
|
||||
WSConnection::~WSConnection()
|
||||
WSClientConnection::~WSClientConnection()
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
void WSConnection::ConnectThread()
|
||||
void WSClientConnection::ConnectThread()
|
||||
{
|
||||
do {
|
||||
std::unique_lock<std::mutex> lck(_waitMtx);
|
||||
@@ -144,8 +144,9 @@ void WSConnection::ConnectThread()
|
||||
_status = Status::DISCONNECTED;
|
||||
}
|
||||
|
||||
void WSConnection::Connect(const std::string &uri, const std::string &pass,
|
||||
bool reconnect, int reconnectDelay)
|
||||
void WSClientConnection::Connect(const std::string &uri,
|
||||
const std::string &pass, bool reconnect,
|
||||
int reconnectDelay)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_connectMtx);
|
||||
if (_status != Status::DISCONNECTED) {
|
||||
@@ -161,11 +162,11 @@ void WSConnection::Connect(const std::string &uri, const std::string &pass,
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
_thread = std::thread(&WSConnection::ConnectThread, this);
|
||||
_thread = std::thread(&WSClientConnection::ConnectThread, this);
|
||||
blog(LOG_INFO, "connect to '%s' started", uri.c_str());
|
||||
}
|
||||
|
||||
void WSConnection::Disconnect()
|
||||
void WSClientConnection::Disconnect()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_connectMtx);
|
||||
_disconnect = true;
|
||||
@@ -218,46 +219,46 @@ std::string ConstructVendorRequestMessage(const std::string &message)
|
||||
return result;
|
||||
}
|
||||
|
||||
void WSConnection::SendRequest(const std::string &msg)
|
||||
void WSClientConnection::SendRequest(const std::string &msg)
|
||||
{
|
||||
Send(msg);
|
||||
}
|
||||
|
||||
WebsocketMessageBuffer WSConnection::RegisterForEvents()
|
||||
WebsocketMessageBuffer WSClientConnection::RegisterForEvents()
|
||||
{
|
||||
return _dispatcher.RegisterClient();
|
||||
}
|
||||
|
||||
WSConnection::Status WSConnection::GetStatus() const
|
||||
WSClientConnection::Status WSClientConnection::GetStatus() const
|
||||
{
|
||||
return _status;
|
||||
}
|
||||
|
||||
void WSConnection::UseOBSWebsocketProtocol(bool useOBSProtocol)
|
||||
void WSClientConnection::UseOBSWebsocketProtocol(bool useOBSProtocol)
|
||||
{
|
||||
_client.set_open_handler(bind(useOBSProtocol
|
||||
? &WSConnection::OnOBSOpen
|
||||
: &WSConnection::OnGenericOpen,
|
||||
this, _1));
|
||||
_client.set_open_handler(
|
||||
bind(useOBSProtocol ? &WSClientConnection::OnOBSOpen
|
||||
: &WSClientConnection::OnGenericOpen,
|
||||
this, _1));
|
||||
_client.set_message_handler(
|
||||
bind(useOBSProtocol ? &WSConnection::OnOBSMessage
|
||||
: &WSConnection::OnGenericMessage,
|
||||
bind(useOBSProtocol ? &WSClientConnection::OnOBSMessage
|
||||
: &WSClientConnection::OnGenericMessage,
|
||||
this, _1, _2));
|
||||
}
|
||||
|
||||
void WSConnection::OnGenericOpen(connection_hdl)
|
||||
void WSClientConnection::OnGenericOpen(connection_hdl)
|
||||
{
|
||||
blog(LOG_INFO, "connection to %s opened", _uri.c_str());
|
||||
_status = Status::AUTHENTICATED;
|
||||
}
|
||||
|
||||
void WSConnection::OnOBSOpen(connection_hdl)
|
||||
void WSClientConnection::OnOBSOpen(connection_hdl)
|
||||
{
|
||||
blog(LOG_INFO, "connection to %s opened", _uri.c_str());
|
||||
_status = Status::CONNECTING;
|
||||
}
|
||||
|
||||
void WSConnection::HandleHello(obs_data_t *helloMsg)
|
||||
void WSClientConnection::HandleHello(obs_data_t *helloMsg)
|
||||
{
|
||||
_status = Status::CONNECTED;
|
||||
|
||||
@@ -293,7 +294,7 @@ void WSConnection::HandleHello(obs_data_t *helloMsg)
|
||||
Send(response);
|
||||
}
|
||||
|
||||
void WSConnection::HandleEvent(obs_data_t *msg)
|
||||
void WSClientConnection::HandleEvent(obs_data_t *msg)
|
||||
{
|
||||
auto d = obs_data_get_obj(msg, "d");
|
||||
auto eventData = obs_data_get_obj(d, "eventData");
|
||||
@@ -319,7 +320,7 @@ void WSConnection::HandleEvent(obs_data_t *msg)
|
||||
obs_data_release(d);
|
||||
}
|
||||
|
||||
void WSConnection::HandleResponse(obs_data_t *response)
|
||||
void WSClientConnection::HandleResponse(obs_data_t *response)
|
||||
{
|
||||
auto data = obs_data_get_obj(response, "d");
|
||||
auto id = obs_data_get_string(data, "requestId");
|
||||
@@ -333,7 +334,8 @@ void WSConnection::HandleResponse(obs_data_t *response)
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
void WSConnection::OnGenericMessage(connection_hdl, client::message_ptr message)
|
||||
void WSClientConnection::OnGenericMessage(connection_hdl,
|
||||
client::message_ptr message)
|
||||
{
|
||||
if (!message) {
|
||||
return;
|
||||
@@ -347,7 +349,8 @@ void WSConnection::OnGenericMessage(connection_hdl, client::message_ptr message)
|
||||
vblog(LOG_INFO, "received event msg \"%s\"", payload.c_str());
|
||||
}
|
||||
|
||||
void WSConnection::OnOBSMessage(connection_hdl, client::message_ptr message)
|
||||
void WSClientConnection::OnOBSMessage(connection_hdl,
|
||||
client::message_ptr message)
|
||||
{
|
||||
if (!message) {
|
||||
return;
|
||||
@@ -392,7 +395,7 @@ void WSConnection::OnOBSMessage(connection_hdl, client::message_ptr message)
|
||||
obs_data_release(json);
|
||||
}
|
||||
|
||||
void WSConnection::Send(const std::string &msg)
|
||||
void WSClientConnection::Send(const std::string &msg)
|
||||
{
|
||||
if (_connection.expired()) {
|
||||
return;
|
||||
@@ -408,7 +411,7 @@ void WSConnection::Send(const std::string &msg)
|
||||
vblog(LOG_INFO, "sent message to '%s':\n%s", _uri.c_str(), msg.c_str());
|
||||
}
|
||||
|
||||
void WSConnection::OnClose(connection_hdl)
|
||||
void WSClientConnection::OnClose(connection_hdl)
|
||||
{
|
||||
blog(LOG_INFO, "client-connection to %s closed.", _uri.c_str());
|
||||
_status = Status::DISCONNECTED;
|
||||
|
||||
@@ -30,13 +30,13 @@ void SendWebsocketEvent(const std::string &);
|
||||
std::string ConstructVendorRequestMessage(const std::string &message);
|
||||
[[nodiscard]] WebsocketMessageBuffer RegisterForWebsocketMessages();
|
||||
|
||||
class WSConnection : public QObject {
|
||||
class WSClientConnection : public QObject {
|
||||
using server = websocketpp::server<websocketpp::config::asio>;
|
||||
using client = websocketpp::client<websocketpp::config::asio_client>;
|
||||
|
||||
public:
|
||||
explicit WSConnection(bool useOBSProtocol = true);
|
||||
virtual ~WSConnection();
|
||||
explicit WSClientConnection(bool useOBSProtocol = true);
|
||||
virtual ~WSClientConnection();
|
||||
|
||||
void Connect(const std::string &uri, const std::string &pass,
|
||||
bool _reconnect, int reconnectDelay = 10);
|
||||
|
||||
243
plugins/base/utils/websocket-tab.cpp
Normal file
243
plugins/base/utils/websocket-tab.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
#include "websocket-tab.hpp"
|
||||
#include "connection-manager.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
#include "tab-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QTabWidget>
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool registerTab();
|
||||
static void setupTab(QTabWidget *);
|
||||
static bool registerTabDone = registerTab();
|
||||
|
||||
static WSConnectionsTable *tabWidget = nullptr;
|
||||
|
||||
static bool registerTab()
|
||||
{
|
||||
AddSetupTabCallback("websocketConnectionTab",
|
||||
WSConnectionsTable::Create, setupTab);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
{
|
||||
SetTabVisibleByName(
|
||||
tabWidget, visible,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.title"));
|
||||
}
|
||||
|
||||
WSConnectionsTable *WSConnectionsTable::Create()
|
||||
{
|
||||
tabWidget = new WSConnectionsTable();
|
||||
return tabWidget;
|
||||
}
|
||||
|
||||
void WSConnectionsTable::Add()
|
||||
{
|
||||
auto newConnection = std::make_shared<WSConnection>();
|
||||
auto accepted = WSConnectionSettingsDialog::AskForSettings(
|
||||
this, *newConnection);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto &connections = GetConnections();
|
||||
connections.emplace_back(newConnection);
|
||||
}
|
||||
|
||||
ConnectionSelectionSignalManager::Instance()->Add(
|
||||
QString::fromStdString(newConnection->Name()));
|
||||
}
|
||||
|
||||
void WSConnectionsTable::Remove()
|
||||
{
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList connectionNames;
|
||||
for (auto row : selectedRows) {
|
||||
auto cell = tabWidget->Table()->item(row.row(), 0);
|
||||
if (!cell) {
|
||||
continue;
|
||||
}
|
||||
|
||||
connectionNames << cell->text();
|
||||
}
|
||||
|
||||
int connectionNameCount = connectionNames.size();
|
||||
if (connectionNameCount == 1) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.removeSingleConnectionPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(connectionNames.at(0)),
|
||||
true)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.removeMultipleConnectionsPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(connectionNameCount),
|
||||
true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
RemoveItemsByName(GetConnections(), connectionNames);
|
||||
}
|
||||
|
||||
for (const auto &name : connectionNames) {
|
||||
ConnectionSelectionSignalManager::Instance()->Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
static QString formatProtocolUsageText(WSConnection *connection)
|
||||
{
|
||||
return obs_module_text(
|
||||
connection->IsUsingOBSProtocol()
|
||||
? "AdvSceneSwitcher.websocketConnectionTab.protocol.yes"
|
||||
: "AdvSceneSwitcher.websocketConnectionTab.protocol.no");
|
||||
}
|
||||
|
||||
static QStringList getCellLabels(WSConnection *connection, bool addName = true)
|
||||
{
|
||||
assert(connection);
|
||||
|
||||
auto result = QStringList();
|
||||
if (addName) {
|
||||
result << QString::fromStdString(connection->Name());
|
||||
}
|
||||
result << QString::fromStdString(connection->GetURI())
|
||||
<< QString::number(connection->GetPort())
|
||||
<< formatProtocolUsageText(connection);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void openSettingsDialog()
|
||||
{
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto weakConnection = GetWeakConnectionByQString(cell->text());
|
||||
auto connection = weakConnection.lock();
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldName = connection->Name();
|
||||
bool accepted = WSConnectionSettingsDialog::AskForSettings(
|
||||
tabWidget->Table(), *connection.get());
|
||||
if (accepted && oldName != connection->Name()) {
|
||||
ConnectionSelectionSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
QString::fromStdString(connection->Name()));
|
||||
}
|
||||
}
|
||||
|
||||
static const QStringList headers =
|
||||
QStringList()
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.name.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.address.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.port.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.protocol.header");
|
||||
|
||||
WSConnectionsTable::WSConnectionsTable(QTabWidget *parent)
|
||||
: ResourceTable(
|
||||
parent,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.help"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.websocketConnectionAddButton.tooltip"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.websocketConnectionTab.websocketConnectionRemoveButton.tooltip"),
|
||||
headers, openSettingsDialog)
|
||||
{
|
||||
for (const auto &connection : GetConnections()) {
|
||||
auto c = std::static_pointer_cast<WSConnection>(connection);
|
||||
AddItemTableRow(Table(), getCellLabels(c.get()));
|
||||
}
|
||||
|
||||
SetHelpVisible(GetConnections().empty());
|
||||
}
|
||||
|
||||
static void updateConnectionStatus(QTableWidget *table)
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); row++) {
|
||||
auto item = table->item(row, 0);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto weakConnection = GetWeakConnectionByQString(item->text());
|
||||
auto connection = weakConnection.lock();
|
||||
if (!connection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdateItemTableRow(table, row,
|
||||
getCellLabels(connection.get(), false));
|
||||
}
|
||||
}
|
||||
|
||||
static void setupTab(QTabWidget *tab)
|
||||
{
|
||||
if (GetConnections().empty()) {
|
||||
setTabVisible(tab, false);
|
||||
}
|
||||
|
||||
QWidget::connect(ConnectionSelectionSignalManager::Instance(),
|
||||
&ConnectionSelectionSignalManager::Rename,
|
||||
[](const QString &oldName, const QString &newName) {
|
||||
RenameItemTableRow(tabWidget->Table(), oldName,
|
||||
newName);
|
||||
});
|
||||
QWidget::connect(
|
||||
ConnectionSelectionSignalManager::Instance(),
|
||||
&ConnectionSelectionSignalManager::Add,
|
||||
[tab](const QString &name) {
|
||||
AddItemTableRow(
|
||||
tabWidget->Table(),
|
||||
getCellLabels(GetConnectionByName(name)));
|
||||
tabWidget->SetHelpVisible(false);
|
||||
tabWidget->HighlightAddButton(false);
|
||||
setTabVisible(tab, true);
|
||||
});
|
||||
QWidget::connect(ConnectionSelectionSignalManager::Instance(),
|
||||
&ConnectionSelectionSignalManager::Remove,
|
||||
[](const QString &name) {
|
||||
RemoveItemTableRow(tabWidget->Table(), name);
|
||||
if (tabWidget->Table()->rowCount() == 0) {
|
||||
tabWidget->SetHelpVisible(true);
|
||||
tabWidget->HighlightAddButton(true);
|
||||
}
|
||||
});
|
||||
|
||||
auto timer = new QTimer(tabWidget);
|
||||
timer->setInterval(1000);
|
||||
QWidget::connect(timer, &QTimer::timeout,
|
||||
[]() { updateConnectionStatus(tabWidget->Table()); });
|
||||
timer->start();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
20
plugins/base/utils/websocket-tab.hpp
Normal file
20
plugins/base/utils/websocket-tab.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "resource-table.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class WSConnectionsTable final : public ResourceTable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static WSConnectionsTable *Create();
|
||||
|
||||
private slots:
|
||||
void Add();
|
||||
void Remove();
|
||||
|
||||
private:
|
||||
WSConnectionsTable(QTabWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -12,7 +12,73 @@ bool MacroConditionMidi::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionMidi::Create, MacroConditionMidiEdit::Create,
|
||||
"AdvSceneSwitcher.condition.midi"});
|
||||
|
||||
static const std::map<MacroConditionMidi::Condition, std::string>
|
||||
conditionTypes = {
|
||||
{MacroConditionMidi::Condition::MIDI_MESSAGE,
|
||||
"AdvSceneSwitcher.condition.midi.condition.message"},
|
||||
{MacroConditionMidi::Condition::MIDI_CONNECTED_DEVICE_NAMES,
|
||||
"AdvSceneSwitcher.condition.midi.condition.deviceName"},
|
||||
};
|
||||
|
||||
bool MacroConditionMidi::CheckCondition()
|
||||
{
|
||||
switch (_condition) {
|
||||
case Condition::MIDI_MESSAGE:
|
||||
return CheckMessage();
|
||||
case Condition::MIDI_CONNECTED_DEVICE_NAMES:
|
||||
return CheckConnectedDevcieNames();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionMidi::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_message.Save(obj);
|
||||
_device.Save(obj);
|
||||
_deviceName.Save(obj, "deviceName");
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionMidi::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_message.Load(obj);
|
||||
_device.Load(obj);
|
||||
_deviceName.Load(obj, "deviceName");
|
||||
_regex.Load(obj);
|
||||
SetCondition(
|
||||
static_cast<Condition>(obs_data_get_int(obj, "condition")));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionMidi::GetShortDesc() const
|
||||
{
|
||||
return _condition == MacroConditionMidi::Condition::MIDI_MESSAGE
|
||||
? _device.Name()
|
||||
: "";
|
||||
}
|
||||
|
||||
void MacroConditionMidi::SetDevice(const MidiDevice &dev)
|
||||
{
|
||||
_device = dev;
|
||||
_messageBuffer = dev.RegisterForMidiMessages();
|
||||
}
|
||||
|
||||
void MacroConditionMidi::SetCondition(Condition condition)
|
||||
{
|
||||
_condition = condition;
|
||||
if (_condition == Condition::MIDI_MESSAGE) {
|
||||
_messageBuffer = _device.RegisterForMidiMessages();
|
||||
}
|
||||
SetupTempVars();
|
||||
}
|
||||
|
||||
bool MacroConditionMidi::CheckMessage()
|
||||
{
|
||||
if (!_messageBuffer) {
|
||||
return false;
|
||||
@@ -40,37 +106,27 @@ bool MacroConditionMidi::CheckCondition()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacroConditionMidi::Save(obs_data_t *obj) const
|
||||
bool MacroConditionMidi::CheckConnectedDevcieNames()
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_message.Save(obj);
|
||||
_device.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionMidi::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_message.Load(obj);
|
||||
_device.Load(obj);
|
||||
_messageBuffer = _device.RegisterForMidiMessages();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionMidi::GetShortDesc() const
|
||||
{
|
||||
return _device.Name();
|
||||
}
|
||||
|
||||
void MacroConditionMidi::SetDevice(const MidiDevice &dev)
|
||||
{
|
||||
_device = dev;
|
||||
_messageBuffer = dev.RegisterForMidiMessages();
|
||||
auto deviceNames = GetDeviceNames();
|
||||
if (!_regex.Enabled()) {
|
||||
return std::find(deviceNames.begin(), deviceNames.end(),
|
||||
std::string(_deviceName)) != deviceNames.end();
|
||||
}
|
||||
for (const auto &deviceName : deviceNames) {
|
||||
if (_regex.Matches(deviceName, _deviceName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacroConditionMidi::SetupTempVars()
|
||||
{
|
||||
MacroCondition::SetupTempVars();
|
||||
if (_condition == Condition::MIDI_CONNECTED_DEVICE_NAMES) {
|
||||
return;
|
||||
}
|
||||
AddTempvar("type",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.midi.type"));
|
||||
AddTempvar("channel",
|
||||
@@ -98,16 +154,45 @@ void MacroConditionMidi::SetVariableValues(const MidiMessage &m)
|
||||
SetTempVarValue("value2", std::to_string(m.Value()));
|
||||
}
|
||||
|
||||
static void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : conditionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionMidiEdit::MacroConditionMidiEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionMidi> entryData)
|
||||
: QWidget(parent),
|
||||
_conditions(new QComboBox()),
|
||||
_devices(new MidiDeviceSelection(this, MidiDeviceType::INPUT)),
|
||||
_message(new MidiMessageSelection(this)),
|
||||
_resetMidiDevices(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.midi.resetDevices"))),
|
||||
_listen(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.midi.startListen")))
|
||||
obs_module_text("AdvSceneSwitcher.midi.startListen"))),
|
||||
_deviceNames(new QComboBox()),
|
||||
_regex(new RegexConfigWidget()),
|
||||
_deviceListInfo(new QLabel()),
|
||||
_listenLayout(new QHBoxLayout()),
|
||||
_entryLayout(new QHBoxLayout())
|
||||
{
|
||||
_deviceNames->addItems(GetDeviceNamesAsQStringList());
|
||||
_deviceNames->setEditable(true);
|
||||
|
||||
QString path = GetThemeTypeName() == "Light"
|
||||
? ":/res/images/help.svg"
|
||||
: ":/res/images/help_light.svg";
|
||||
QIcon icon(path);
|
||||
QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||
_deviceListInfo->setPixmap(pixmap);
|
||||
_deviceListInfo->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.midi.condition.deviceName.info"));
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_devices,
|
||||
SIGNAL(DeviceSelectionChanged(const MidiDevice &)),
|
||||
this,
|
||||
@@ -121,19 +206,21 @@ MacroConditionMidiEdit::MacroConditionMidiEdit(
|
||||
SLOT(ToggleListen()));
|
||||
QWidget::connect(&_listenTimer, SIGNAL(timeout()), this,
|
||||
SLOT(SetMessageSelectionToLastReceived()));
|
||||
QWidget::connect(_deviceNames,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(DeviceNameChanged(const QString &)));
|
||||
QWidget::connect(_regex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
|
||||
auto entryLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.midi.entry"),
|
||||
entryLayout, {{"{{device}}", _devices}});
|
||||
auto listenLayout = new QHBoxLayout;
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.midi.entry.listen"),
|
||||
listenLayout, {{"{{listenButton}}", _listen}});
|
||||
_listenLayout, {{"{{listenButton}}", _listen}});
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addLayout(_entryLayout);
|
||||
mainLayout->addWidget(_message);
|
||||
mainLayout->addLayout(listenLayout);
|
||||
mainLayout->addLayout(_listenLayout);
|
||||
mainLayout->addWidget(_resetMidiDevices);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -155,11 +242,14 @@ void MacroConditionMidiEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_conditions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->GetCondition()));
|
||||
_message->SetMessage(_entryData->_message);
|
||||
_devices->SetDevice(_entryData->GetDevice());
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
_deviceNames->setCurrentText(QString::fromStdString(
|
||||
_entryData->_deviceName.UnresolvedValue()));
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device)
|
||||
@@ -211,6 +301,46 @@ void MacroConditionMidiEdit::EnableListening(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::SetWidgetVisibility()
|
||||
{
|
||||
_entryLayout->removeWidget(_conditions);
|
||||
_entryLayout->removeWidget(_devices);
|
||||
_entryLayout->removeWidget(_deviceNames);
|
||||
_entryLayout->removeWidget(_regex);
|
||||
|
||||
ClearLayout(_entryLayout);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{conditions}}", _conditions},
|
||||
{"{{device}}", _devices},
|
||||
{"{{deviceNames}}", _deviceNames},
|
||||
{"{{regex}}", _regex},
|
||||
{"{{deviceListInfo}}", _deviceListInfo},
|
||||
};
|
||||
|
||||
const bool isMessageCheck = _entryData->GetCondition() ==
|
||||
MacroConditionMidi::Condition::MIDI_MESSAGE;
|
||||
|
||||
auto layoutString =
|
||||
isMessageCheck
|
||||
? "AdvSceneSwitcher.condition.midi.entry.message"
|
||||
: "AdvSceneSwitcher.condition.midi.entry.deviceName";
|
||||
|
||||
PlaceWidgets(obs_module_text(layoutString), _entryLayout,
|
||||
widgetPlaceholders);
|
||||
|
||||
SetLayoutVisible(_listenLayout, isMessageCheck);
|
||||
_devices->setVisible(isMessageCheck);
|
||||
_message->setVisible(isMessageCheck);
|
||||
_resetMidiDevices->setVisible(isMessageCheck);
|
||||
_deviceNames->setVisible(!isMessageCheck);
|
||||
_regex->setVisible(!isMessageCheck);
|
||||
_deviceListInfo->setVisible(!isMessageCheck);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::ToggleListen()
|
||||
{
|
||||
if (!_entryData) {
|
||||
@@ -249,4 +379,41 @@ void MacroConditionMidiEdit::SetMessageSelectionToLastReceived()
|
||||
_entryData->_message = *message;
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::ConditionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetCondition(
|
||||
static_cast<MacroConditionMidi::Condition>(value));
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::DeviceNameChanged(const QString &deviceName)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_deviceName = deviceName.toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionMidiEdit::RegexChanged(const RegexConfig &conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_regex = conf;
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "midi-helpers.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
@@ -23,11 +25,23 @@ public:
|
||||
void SetDevice(const MidiDevice &dev);
|
||||
const MidiDevice &GetDevice() const { return _device; }
|
||||
MidiMessage _message;
|
||||
StringVariable _deviceName;
|
||||
RegexConfig _regex;
|
||||
|
||||
enum class Condition {
|
||||
MIDI_MESSAGE,
|
||||
MIDI_CONNECTED_DEVICE_NAMES,
|
||||
};
|
||||
void SetCondition(Condition);
|
||||
Condition GetCondition() const { return _condition; }
|
||||
|
||||
private:
|
||||
bool CheckMessage();
|
||||
bool CheckConnectedDevcieNames();
|
||||
void SetupTempVars();
|
||||
void SetVariableValues(const MidiMessage &);
|
||||
|
||||
Condition _condition = Condition::MIDI_MESSAGE;
|
||||
MidiDevice _device;
|
||||
MidiMessageBuffer _messageBuffer;
|
||||
std::chrono::high_resolution_clock::time_point _lastCheck{};
|
||||
@@ -58,18 +72,29 @@ private slots:
|
||||
void ResetMidiDevices();
|
||||
void ToggleListen();
|
||||
void SetMessageSelectionToLastReceived();
|
||||
void ConditionChanged(int);
|
||||
void DeviceNameChanged(const QString &);
|
||||
void RegexChanged(const RegexConfig &);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void EnableListening(bool);
|
||||
void SetWidgetVisibility();
|
||||
|
||||
std::shared_ptr<MacroConditionMidi> _entryData;
|
||||
|
||||
QComboBox *_conditions;
|
||||
MidiDeviceSelection *_devices;
|
||||
MidiMessageSelection *_message;
|
||||
QPushButton *_resetMidiDevices;
|
||||
QPushButton *_listen;
|
||||
QComboBox *_deviceNames;
|
||||
RegexConfigWidget *_regex;
|
||||
QLabel *_deviceListInfo;
|
||||
QHBoxLayout *_listenLayout;
|
||||
QHBoxLayout *_entryLayout;
|
||||
|
||||
QTimer _listenTimer;
|
||||
MidiMessageBuffer _messageBuffer;
|
||||
bool _currentlyListening = false;
|
||||
|
||||
@@ -22,7 +22,8 @@ static bool setupDeviceObservers()
|
||||
static std::vector<libremidi::observer> observers;
|
||||
for (auto api : libremidi::available_apis()) {
|
||||
libremidi::observer_configuration cbs;
|
||||
cbs.input_added = [=](const libremidi::input_port &p) {
|
||||
cbs.track_virtual = true;
|
||||
cbs.input_added = [](const libremidi::input_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
@@ -32,7 +33,7 @@ static bool setupDeviceObservers()
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.input_removed = [=](const libremidi::input_port &p) {
|
||||
cbs.input_removed = [](const libremidi::input_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
@@ -40,7 +41,7 @@ static bool setupDeviceObservers()
|
||||
blog(LOG_INFO, "MIDI input removed: %s",
|
||||
p.port_name.c_str());
|
||||
};
|
||||
cbs.output_added = [=](const libremidi::output_port &p) {
|
||||
cbs.output_added = [](const libremidi::output_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
@@ -50,7 +51,7 @@ static bool setupDeviceObservers()
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.output_removed = [=](const libremidi::output_port &p) {
|
||||
cbs.output_removed = [](const libremidi::output_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
@@ -328,35 +329,95 @@ advss::MidiDeviceInstance::GetDevice(const libremidi::output_port &p)
|
||||
|
||||
static inline QStringList getInputDeviceNames()
|
||||
{
|
||||
QStringList devices;
|
||||
static QStringList devices;
|
||||
static std::mutex m;
|
||||
static std::vector<libremidi::observer> observers;
|
||||
static bool setupDone = false;
|
||||
|
||||
if (setupDone) {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
return devices;
|
||||
}
|
||||
|
||||
// Set up observers
|
||||
try {
|
||||
libremidi::observer obs;
|
||||
for (const libremidi::input_port &port :
|
||||
obs.get_input_ports()) {
|
||||
devices << QString::fromStdString(
|
||||
getNameFromPortInformation(port));
|
||||
for (auto api : libremidi::available_apis()) {
|
||||
libremidi::observer_configuration cbs;
|
||||
cbs.track_virtual = true;
|
||||
cbs.input_added = [](const libremidi::input_port &p) {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
auto device = QString::fromStdString(
|
||||
getNameFromPortInformation(p));
|
||||
if (devices.indexOf(device) == -1) {
|
||||
devices << device;
|
||||
}
|
||||
};
|
||||
cbs.input_removed = [](const libremidi::input_port &p) {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
auto device = QString::fromStdString(
|
||||
getNameFromPortInformation(p));
|
||||
if (int i = devices.indexOf(device) != -1) {
|
||||
devices.removeAt(i);
|
||||
}
|
||||
};
|
||||
observers.emplace_back(
|
||||
cbs,
|
||||
libremidi::observer_configuration_for(api));
|
||||
}
|
||||
} catch (const libremidi::driver_error &error) {
|
||||
blog(LOG_WARNING, "Failed to get midi input devices: %s",
|
||||
error.what());
|
||||
}
|
||||
|
||||
setupDone = true;
|
||||
return devices;
|
||||
}
|
||||
|
||||
static inline QStringList getOutputDeviceNames()
|
||||
{
|
||||
QStringList devices;
|
||||
static QStringList devices;
|
||||
static std::mutex m;
|
||||
static std::vector<libremidi::observer> observers;
|
||||
static bool setupDone = false;
|
||||
|
||||
if (setupDone) {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
return devices;
|
||||
}
|
||||
|
||||
// Set up observers
|
||||
try {
|
||||
libremidi::observer obs;
|
||||
for (const libremidi::output_port &port :
|
||||
obs.get_output_ports()) {
|
||||
devices << QString::fromStdString(
|
||||
getNameFromPortInformation(port));
|
||||
for (auto api : libremidi::available_apis()) {
|
||||
libremidi::observer_configuration cbs;
|
||||
cbs.track_virtual = true;
|
||||
cbs.output_added = [](const libremidi::output_port &p) {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
auto device = QString::fromStdString(
|
||||
getNameFromPortInformation(p));
|
||||
if (devices.indexOf(device) == -1) {
|
||||
devices << device;
|
||||
}
|
||||
};
|
||||
cbs.output_removed =
|
||||
[](const libremidi::output_port &p) {
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
auto device = QString::fromStdString(
|
||||
getNameFromPortInformation(p));
|
||||
if (int i = devices.indexOf(device) !=
|
||||
-1) {
|
||||
devices.removeAt(i);
|
||||
}
|
||||
};
|
||||
observers.emplace_back(
|
||||
cbs,
|
||||
libremidi::observer_configuration_for(api));
|
||||
}
|
||||
} catch (const libremidi::driver_error &error) {
|
||||
blog(LOG_WARNING, "Failed to get midi output devices: %s",
|
||||
error.what());
|
||||
}
|
||||
|
||||
setupDone = true;
|
||||
return devices;
|
||||
}
|
||||
|
||||
@@ -997,6 +1058,28 @@ void MidiMessageSelection::ValueChanged(const NumberVariable<int> &v)
|
||||
emit MidiMessageChanged((_currentSelection));
|
||||
}
|
||||
|
||||
std::vector<std::string> GetDeviceNames()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
const auto names = GetDeviceNamesAsQStringList();
|
||||
for (const auto &name : names) {
|
||||
result.emplace_back(name.toStdString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList GetDeviceNamesAsQStringList()
|
||||
{
|
||||
QStringList result;
|
||||
for (const auto &input : getInputDeviceNames()) {
|
||||
result << input;
|
||||
}
|
||||
for (const auto &output : getOutputDeviceNames()) {
|
||||
result << output;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList GetAllNotes()
|
||||
{
|
||||
static bool done = false;
|
||||
|
||||
@@ -171,6 +171,8 @@ private:
|
||||
const MidiDeviceType _type;
|
||||
};
|
||||
|
||||
std::vector<std::string> GetDeviceNames();
|
||||
QStringList GetDeviceNamesAsQStringList();
|
||||
QStringList GetAllNotes();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -55,7 +55,9 @@ target_sources(
|
||||
token.cpp
|
||||
token.hpp
|
||||
twitch-helpers.cpp
|
||||
twitch-helpers.hpp)
|
||||
twitch-helpers.hpp
|
||||
twitch-tab.cpp
|
||||
twitch-tab.hpp)
|
||||
|
||||
setup_advss_plugin(${PROJECT_NAME})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
||||
|
||||
@@ -314,7 +314,7 @@ void TwitchCategorySearchButton::SetToken(
|
||||
void TwitchCategorySearchButton::StartManualCategorySearch()
|
||||
{
|
||||
std::string category;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this,
|
||||
obs_module_text("AdvSceneSwitcher.twitchCategories.search"),
|
||||
obs_module_text("AdvSceneSwitcher.twitchCategories.name"),
|
||||
|
||||
@@ -351,6 +351,11 @@ bool TokenIsValid(const std::weak_ptr<TwitchToken> &token_)
|
||||
return token->IsValid();
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<Item>> &GetTwitchTokens()
|
||||
{
|
||||
return twitchTokens;
|
||||
}
|
||||
|
||||
static bool ConnectionNameAvailable(const QString &name)
|
||||
{
|
||||
return !GetTwitchTokenByName(name);
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
std::string GetUserID() const { return _userID; }
|
||||
std::shared_ptr<EventSub> GetEventSub();
|
||||
bool IsValid(bool forceUpdate = false) const;
|
||||
size_t PermissionCount() const { return _tokenOptions.size(); }
|
||||
|
||||
private:
|
||||
std::string _token;
|
||||
@@ -152,6 +153,7 @@ std::weak_ptr<TwitchToken> GetWeakTwitchTokenByName(const std::string &name);
|
||||
std::weak_ptr<TwitchToken> GetWeakTwitchTokenByQString(const QString &name);
|
||||
std::string GetWeakTwitchTokenName(std::weak_ptr<TwitchToken>);
|
||||
bool TokenIsValid(const std::weak_ptr<TwitchToken> &token);
|
||||
std::deque<std::shared_ptr<Item>> &GetTwitchTokens();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
|
||||
221
plugins/twitch/twitch-tab.cpp
Normal file
221
plugins/twitch/twitch-tab.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "twitch-tab.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "sync-helpers.hpp"
|
||||
#include "tab-helpers.hpp"
|
||||
#include "token.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QTabWidget>
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool registerTab();
|
||||
static void setupTab(QTabWidget *);
|
||||
static bool registerTabDone = registerTab();
|
||||
|
||||
static TwitchConnectionsTable *tabWidget = nullptr;
|
||||
|
||||
static bool registerTab()
|
||||
{
|
||||
AddSetupTabCallback("twitchConnectionTab",
|
||||
TwitchConnectionsTable::Create, setupTab);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
{
|
||||
SetTabVisibleByName(
|
||||
tabWidget, visible,
|
||||
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title"));
|
||||
}
|
||||
|
||||
TwitchConnectionsTable *TwitchConnectionsTable::Create()
|
||||
{
|
||||
tabWidget = new TwitchConnectionsTable();
|
||||
return tabWidget;
|
||||
}
|
||||
|
||||
void TwitchConnectionsTable::Add()
|
||||
{
|
||||
auto newToken = std::make_shared<TwitchToken>();
|
||||
auto accepted =
|
||||
TwitchTokenSettingsDialog::AskForSettings(this, *newToken);
|
||||
if (!accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto &tokens = GetTwitchTokens();
|
||||
tokens.emplace_back(newToken);
|
||||
}
|
||||
|
||||
TwitchConnectionSignalManager::Instance()->Add(
|
||||
QString::fromStdString(newToken->Name()));
|
||||
}
|
||||
|
||||
void TwitchConnectionsTable::Remove()
|
||||
{
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList tokenNames;
|
||||
for (auto row : selectedRows) {
|
||||
auto cell = tabWidget->Table()->item(row.row(), 0);
|
||||
if (!cell) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tokenNames << cell->text();
|
||||
}
|
||||
|
||||
int tokenNameCount = tokenNames.size();
|
||||
if (tokenNameCount == 1) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.twitchConnectionTab.removeSingleConnectionPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(tokenNames.at(0)),
|
||||
true)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.twitchConnectionTab.removeMultipleConnectionsPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(tokenNameCount), true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
RemoveItemsByName(GetTwitchTokens(), tokenNames);
|
||||
}
|
||||
|
||||
for (const auto &name : tokenNames) {
|
||||
TwitchConnectionSignalManager::Instance()->Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
static QStringList getCellLabels(TwitchToken *token, bool addName = true)
|
||||
{
|
||||
assert(token);
|
||||
|
||||
auto result = QStringList();
|
||||
if (addName) {
|
||||
result << QString::fromStdString(token->Name());
|
||||
}
|
||||
result << QString::fromStdString(obs_module_text(
|
||||
token->IsValid()
|
||||
? "AdvSceneSwitcher.twitchConnectionTab.yes"
|
||||
: "AdvSceneSwitcher.twitchConnectionTab.no"))
|
||||
<< QString::number(token->PermissionCount());
|
||||
return result;
|
||||
}
|
||||
|
||||
static void openSettingsDialog()
|
||||
{
|
||||
auto selectedRows =
|
||||
tabWidget->Table()->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cell = tabWidget->Table()->item(selectedRows.last().row(), 0);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto weakToken = GetWeakTwitchTokenByQString(cell->text());
|
||||
auto token = weakToken.lock();
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
TwitchTokenSettingsDialog::AskForSettings(tabWidget->Table(),
|
||||
*token.get());
|
||||
}
|
||||
|
||||
static const QStringList headers =
|
||||
QStringList()
|
||||
<< obs_module_text("AdvSceneSwitcher.twitchConnectionTab.name.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.twitchConnectionTab.isValid.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.twitchConnectionTab.permissionCount.header");
|
||||
|
||||
TwitchConnectionsTable::TwitchConnectionsTable(QTabWidget *parent)
|
||||
: ResourceTable(
|
||||
parent,
|
||||
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.help"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.twitchConnectionTab.twitchConnectionAddButton.tooltip"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.twitchConnectionTab.twitchConnectionRemoveButton.tooltip"),
|
||||
headers, openSettingsDialog)
|
||||
{
|
||||
for (const auto &token : GetTwitchTokens()) {
|
||||
auto t = std::static_pointer_cast<TwitchToken>(token);
|
||||
AddItemTableRow(Table(), getCellLabels(t.get()));
|
||||
}
|
||||
|
||||
SetHelpVisible(GetTwitchTokens().empty());
|
||||
}
|
||||
|
||||
static void updateConnectionStatus(QTableWidget *table)
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); row++) {
|
||||
auto item = table->item(row, 0);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto weakToken = GetWeakTwitchTokenByQString(item->text());
|
||||
auto token = weakToken.lock();
|
||||
if (!token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UpdateItemTableRow(table, row,
|
||||
getCellLabels(token.get(), false));
|
||||
}
|
||||
}
|
||||
|
||||
static void setupTab(QTabWidget *tab)
|
||||
{
|
||||
if (GetTwitchTokens().empty()) {
|
||||
setTabVisible(tab, false);
|
||||
}
|
||||
|
||||
QWidget::connect(
|
||||
TwitchConnectionSignalManager::Instance(),
|
||||
&TwitchConnectionSignalManager::Add,
|
||||
[tab](const QString &name) {
|
||||
AddItemTableRow(
|
||||
tabWidget->Table(),
|
||||
getCellLabels(GetWeakTwitchTokenByQString(name)
|
||||
.lock()
|
||||
.get()));
|
||||
tabWidget->SetHelpVisible(false);
|
||||
tabWidget->HighlightAddButton(false);
|
||||
setTabVisible(tab, true);
|
||||
});
|
||||
QWidget::connect(TwitchConnectionSignalManager::Instance(),
|
||||
&TwitchConnectionSignalManager::Remove,
|
||||
[](const QString &name) {
|
||||
RemoveItemTableRow(tabWidget->Table(), name);
|
||||
if (tabWidget->Table()->rowCount() == 0) {
|
||||
tabWidget->SetHelpVisible(true);
|
||||
tabWidget->HighlightAddButton(true);
|
||||
}
|
||||
});
|
||||
|
||||
auto timer = new QTimer(tabWidget);
|
||||
timer->setInterval(1000);
|
||||
QWidget::connect(timer, &QTimer::timeout,
|
||||
[]() { updateConnectionStatus(tabWidget->Table()); });
|
||||
timer->start();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
20
plugins/twitch/twitch-tab.hpp
Normal file
20
plugins/twitch/twitch-tab.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "resource-table.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
class TwitchConnectionsTable final : public ResourceTable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static TwitchConnectionsTable *Create();
|
||||
|
||||
private slots:
|
||||
void Add();
|
||||
void Remove();
|
||||
|
||||
private:
|
||||
TwitchConnectionsTable(QTabWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
Reference in New Issue
Block a user