mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdacfc946c | ||
|
|
26e854f0a3 | ||
|
|
2bc89364b2 | ||
|
|
e080d2de9b | ||
|
|
ca1262aeb7 | ||
|
|
cbc95e2095 | ||
|
|
9c5051cbf8 | ||
|
|
e6ca3390a2 | ||
|
|
e74b531905 | ||
|
|
01437183ac | ||
|
|
e5ab2ceca3 | ||
|
|
e19f4ddf7c | ||
|
|
4e3a062084 | ||
|
|
96db0ad507 | ||
|
|
6b4bcc074a | ||
|
|
14dd390680 | ||
|
|
3fe8ea8961 | ||
|
|
3ee913ea98 | ||
|
|
d18277653c | ||
|
|
8c478cc330 | ||
|
|
003ffc161b | ||
|
|
74dc0c871b | ||
|
|
c8545dea4d | ||
|
|
e1b8edd444 | ||
|
|
f3996855c3 | ||
|
|
ea17e8ea08 | ||
|
|
00fc313609 | ||
|
|
c72f9abc2b | ||
|
|
6df818503e | ||
|
|
b17a6cc109 | ||
|
|
82d1aed472 | ||
|
|
eb6fc6c140 | ||
|
|
2710406f5f | ||
|
|
a634499423 | ||
|
|
71b3fbaa19 | ||
|
|
c656bb4571 | ||
|
|
6c4e4b8cd8 | ||
|
|
7b1a256f8a | ||
|
|
66f9eaf8c2 | ||
|
|
7fa0ba3355 | ||
|
|
eec9244e4c | ||
|
|
e265e4828e | ||
|
|
ad6e720912 | ||
|
|
70dad8ebb4 | ||
|
|
497b3b3e04 | ||
|
|
891811aa47 | ||
|
|
be9ddaa4c9 | ||
|
|
03494c2915 | ||
|
|
bcf7c247bf | ||
|
|
132d89b350 |
1
.github/scripts/.Brewfile
vendored
1
.github/scripts/.Brewfile
vendored
@@ -1,6 +1,5 @@
|
||||
brew "ccache"
|
||||
brew "coreutils"
|
||||
brew "cmake"
|
||||
brew "git"
|
||||
brew "jq"
|
||||
brew "xcbeautify"
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -16,6 +16,7 @@
|
||||
!.cmake-format.json
|
||||
!.gitattributes
|
||||
!.gitignore
|
||||
!.pre-commit-config.yaml
|
||||
!BUILDING.md
|
||||
!buildspec.json
|
||||
!CMakeLists.txt
|
||||
@@ -23,6 +24,9 @@
|
||||
!LICENSE
|
||||
!README.md
|
||||
|
||||
# Exclude .orig leftovers
|
||||
*.orig
|
||||
|
||||
# Exclude lock files
|
||||
*.lock.json
|
||||
|
||||
|
||||
24
.pre-commit-config.yaml
Normal file
24
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v16.0.6
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [-fallback-style=none]
|
||||
types_or: [c++, c]
|
||||
exclude: |
|
||||
(?x)^(
|
||||
deps/.*|
|
||||
tests/catch\.hpp|
|
||||
.*\.mm
|
||||
)$
|
||||
|
||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
rev: v0.6.13
|
||||
hooks:
|
||||
- id: cmake-format
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.3.2
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
types_or: [python]
|
||||
@@ -151,6 +151,8 @@ target_sources(
|
||||
lib/utils/backup.hpp
|
||||
lib/utils/curl-helper.cpp
|
||||
lib/utils/curl-helper.hpp
|
||||
lib/utils/double-slider.cpp
|
||||
lib/utils/double-slider.hpp
|
||||
lib/utils/duration-control.cpp
|
||||
lib/utils/duration-control.hpp
|
||||
lib/utils/duration.cpp
|
||||
@@ -199,6 +201,8 @@ target_sources(
|
||||
lib/utils/section.hpp
|
||||
lib/utils/selection-helpers.cpp
|
||||
lib/utils/selection-helpers.hpp
|
||||
lib/utils/single-char-selection.cpp
|
||||
lib/utils/single-char-selection.hpp
|
||||
lib/utils/slider-spinbox.cpp
|
||||
lib/utils/slider-spinbox.hpp
|
||||
lib/utils/source-helpers.cpp
|
||||
|
||||
@@ -22,4 +22,5 @@ sudo snap install obs-studio
|
||||
## Contributing
|
||||
|
||||
- If you wish to contribute code to the project, have a look at this [section](BUILDING.md) describing how to compile the plugin.
|
||||
- You can optionally use [pre-commit](https://pre-commit.com) to automatically handle formatting.
|
||||
- If you wish to contribute translations, feel free to submit pull requests for the corresponding files under `data/locale`.
|
||||
|
||||
@@ -49,7 +49,16 @@ invoke_formatter() {
|
||||
exit 2
|
||||
}
|
||||
|
||||
local -a source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N))
|
||||
local -a source_files=()
|
||||
for folder ("lib" "module" "plugins" "tests") {
|
||||
source_files+=(${folder}/**/*.(c|cpp|h|hpp)(.N))
|
||||
}
|
||||
|
||||
for file (${source_files}) {
|
||||
if [[ $file == *"catch.hpp" ]]; then
|
||||
source_files=("${source_files[@]/$file}")
|
||||
fi
|
||||
}
|
||||
|
||||
local -a format_args=(-style=file -fallback-style=none)
|
||||
if (( _loglevel > 2 )) format_args+=(--verbose)
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
defaultLocaleFile = "en-US.ini"
|
||||
|
||||
|
||||
class localeEntry:
|
||||
locale = ""
|
||||
placeholders = []
|
||||
widgetPlaceholders = []
|
||||
qStringArgs = []
|
||||
|
||||
def __init__(self, locale, widgets) -> None:
|
||||
def __init__(self, locale, widgetPlaceholders, qStringArgs) -> None:
|
||||
self.locale = locale
|
||||
self.placeholders = widgets
|
||||
self.widgetPlaceholders = widgetPlaceholders
|
||||
self.qStringArgs = qStringArgs
|
||||
|
||||
|
||||
def getNonDefaultLocales(dir):
|
||||
@@ -22,21 +25,33 @@ def getNonDefaultLocales(dir):
|
||||
f = os.path.join(dir, filename)
|
||||
if os.path.isfile(f) and not f.endswith(defaultLocaleFile):
|
||||
files.append(f)
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def getAllLocaleEntriesWithWidgetPlaceholders(file):
|
||||
def getAllLocaleEntries(file):
|
||||
localeEntries = []
|
||||
with open(file, 'r', encoding='UTF-8') as f:
|
||||
with open(file, "r", encoding="UTF-8") as f:
|
||||
for line in f.readlines():
|
||||
widgetPlaceholders = []
|
||||
qStringArgs = []
|
||||
|
||||
if line.startswith(";"):
|
||||
continue
|
||||
|
||||
for word in line.split("{{"):
|
||||
if not "}}" in word:
|
||||
continue
|
||||
word = "{{" + word[:word.rfind("}}")] + "}}"
|
||||
widgetPlaceholders.append(word)
|
||||
localeEntries.append(localeEntry(
|
||||
line.split("=")[0], widgetPlaceholders))
|
||||
placeholder = "{{" + word[: word.rfind("}}")] + "}}"
|
||||
widgetPlaceholders.append(placeholder)
|
||||
|
||||
for match in re.finditer(r"%\d+", line):
|
||||
qStringArgs.append(match.group(0))
|
||||
|
||||
localeEntries.append(
|
||||
localeEntry(line.split("=")[0], widgetPlaceholders, qStringArgs)
|
||||
)
|
||||
|
||||
return localeEntries
|
||||
|
||||
|
||||
@@ -44,47 +59,85 @@ def getLocaleEntryFrom(entry, list):
|
||||
for element in list:
|
||||
if element.locale == entry.locale:
|
||||
return element
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def checkWidgetPlacehodlers(file, expectedPlaceholders):
|
||||
localeEntries = getAllLocaleEntriesWithWidgetPlaceholders(file)
|
||||
def checkLocaleEntries(file, expectedLocaleEntries):
|
||||
localeEntries = getAllLocaleEntries(file)
|
||||
result = True
|
||||
|
||||
for localeEntry in localeEntries:
|
||||
expectedEntry = getLocaleEntryFrom(localeEntry, expectedPlaceholders)
|
||||
if expectedEntry is None:
|
||||
expectedLocaleEntry = getLocaleEntryFrom(localeEntry, expectedLocaleEntries)
|
||||
|
||||
if expectedLocaleEntry is None:
|
||||
result = False
|
||||
print(
|
||||
"WARNING: Locale entry \"{}\" from \"{}\" not found in \"{}\"".format(localeEntry.locale, file, defaultLocaleFile))
|
||||
'ERROR: Locale entry "{}" from "{}" not found in "{}"'.format(
|
||||
localeEntry.locale, file, defaultLocaleFile
|
||||
)
|
||||
)
|
||||
continue
|
||||
for p in localeEntry.placeholders:
|
||||
if p not in expectedEntry.placeholders:
|
||||
print("WARNING: Locale entry \"{}\" from \"{}\" does contain \"{}\" while \"{}\" does not".format(
|
||||
localeEntry.locale, file, p, defaultLocaleFile))
|
||||
for p in expectedEntry.placeholders:
|
||||
if p not in localeEntry.placeholders:
|
||||
|
||||
for placeholder in localeEntry.widgetPlaceholders:
|
||||
if placeholder not in expectedLocaleEntry.widgetPlaceholders:
|
||||
result = False
|
||||
print("ERROR: Locale entry \"{}\" from \"{}\" does not contain \"{}\"".format(
|
||||
localeEntry.locale, file, p))
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does contain "{}" widget placeholder while "{}" does not'.format(
|
||||
localeEntry.locale, file, placeholder, defaultLocaleFile
|
||||
)
|
||||
)
|
||||
|
||||
for placeholder in expectedLocaleEntry.widgetPlaceholders:
|
||||
if placeholder not in localeEntry.widgetPlaceholders:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does not contain "{}" widget placeholder'.format(
|
||||
localeEntry.locale, file, placeholder
|
||||
)
|
||||
)
|
||||
|
||||
for arg in localeEntry.qStringArgs:
|
||||
if arg not in expectedLocaleEntry.qStringArgs:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does contain "{}" QString arg while "{}" does not'.format(
|
||||
localeEntry.locale, file, arg, defaultLocaleFile
|
||||
)
|
||||
)
|
||||
|
||||
for arg in expectedLocaleEntry.qStringArgs:
|
||||
if arg not in localeEntry.qStringArgs:
|
||||
result = False
|
||||
print(
|
||||
'ERROR: Locale entry "{}" from "{}" does not contain "{}" QString arg'.format(
|
||||
localeEntry.locale, file, arg
|
||||
)
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Checks for inconsistencies regarding widget placeholders in the different locale files.')
|
||||
parser.add_argument(
|
||||
'-p', '--path', help='Path to locale folder', required=True)
|
||||
description="Checks for inconsistencies regarding widget placeholders in the different locale files."
|
||||
)
|
||||
parser.add_argument("-p", "--path", help="Path to locale folder", required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
placeholders = getAllLocaleEntriesWithWidgetPlaceholders(
|
||||
os.path.join(args.path, defaultLocaleFile))
|
||||
defaultLocaleEntries = getAllLocaleEntries(
|
||||
os.path.join(args.path, defaultLocaleFile)
|
||||
)
|
||||
nonDefaultLocales = getNonDefaultLocales(args.path)
|
||||
|
||||
result = True
|
||||
for f in nonDefaultLocales:
|
||||
if checkWidgetPlacehodlers(f, placeholders) == False:
|
||||
for file in nonDefaultLocales:
|
||||
if checkLocaleEntries(file, defaultLocaleEntries) == False:
|
||||
result = False
|
||||
|
||||
if result == False:
|
||||
sys.exit(1)
|
||||
|
||||
print("SUCCESS: No issues found!")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@@ -70,11 +70,14 @@ endif()
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
# 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)
|
||||
|
||||
# Add warning for infinite recursion (added in GCC 12)
|
||||
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)
|
||||
add_compile_options(-Winfinite-recursion)
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
add_compile_options(-Wno-error=null-pointer-subtraction)
|
||||
endif()
|
||||
|
||||
# Enable compiler and build tracing (requires Ninja generator)
|
||||
|
||||
@@ -80,8 +80,7 @@ AdvSceneSwitcher.macroTab.runInParallel="Parallel zu anderen Makros ausführen"
|
||||
AdvSceneSwitcher.macroTab.onChange="Nur bei Änderung ausführen"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="Gruppe %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Makro-Name existiert bereits"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Sicher, dass \"%1\" und alle zugehörigen Elemente gelöscht werden?"
|
||||
AdvSceneSwitcher.macroTab.copy="Kopie des aktuellen Makros erstellen"
|
||||
AdvSceneSwitcher.macroTab.group="Ausgewählte Elemente gruppieren"
|
||||
AdvSceneSwitcher.macroTab.ungroup="Gruppierung ausgewählter Gruppen aufheben"
|
||||
@@ -445,7 +444,7 @@ AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Aktivieren"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deaktivieren"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Einstellungen festlegen"
|
||||
AdvSceneSwitcher.action.filter.entry="Auf{{sources}}{{actions}}{{filters}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="Auf{{sources}}{{actions}}{{filters}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.action.source="Quelle"
|
||||
AdvSceneSwitcher.action.source.type.enable="Aktivieren"
|
||||
@@ -454,7 +453,6 @@ AdvSceneSwitcher.action.source.type.settings="Einstellungen festlegen"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="Aktualisieren der Quelleneinstellungen"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="Drücken der Einstellungstaste"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Kann verwendet werden, um Browser-, Medien- usw. Quellen zu aktualisieren"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="Keine Buttons gefunden!"
|
||||
AdvSceneSwitcher.action.source.warning="Warnung: Das globale Aktivieren und Deaktivieren von Quellen kann nicht über die OBS-Benutzeroberfläche gesteuert werden"
|
||||
AdvSceneSwitcher.action.source.getSettings="Aktuelle Einstellungen abfragen"
|
||||
AdvSceneSwitcher.action.media="Medien"
|
||||
@@ -545,7 +543,7 @@ AdvSceneSwitcher.action.sceneCollection="Szenensammlung"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Aktive Szenensammlung umschalten auf {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Hinweis: Alle danach folgenden Aktionen werden nicht ausgeführt, da durch die Änderung der Szenensammlung auch die Einstellungen des Szenenwechslers neu geladen werden.\nDie Szenensammlungsaktion wird ignoriert, solange das Einstellungsfenster geöffnet ist."
|
||||
AdvSceneSwitcher.action.sequence="Sequenz"
|
||||
AdvSceneSwitcher.action.sequence.entry="Jedes Mal, wenn diese Aktion ausgeführt wird, wird das nächste Makro in der Liste ausgeführt (pausierte Makros werden ignoriert)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="Jedes Mal, wenn diese Aktion ausgeführt wird, wird das nächste Makro in der Liste ausgeführt (pausierte Makros werden ignoriert)"
|
||||
AdvSceneSwitcher.action.sequence.status="Zuletzt ausgeführtes Makro: %1 - Nächstes auszuführendes Makro: %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="keines"
|
||||
AdvSceneSwitcher.action.sequence.restart="Neustart am Anfang, wenn das Ende der Liste erreicht ist"
|
||||
@@ -571,6 +569,7 @@ AdvSceneSwitcher.action.variable.actionNoVariableSupport="Das Abrufen von Variab
|
||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Das Abrufen von Variablenwerten aus %1 Bedingungen wird nicht unterstützt!"
|
||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Aktueller Wert:"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="Keine Buttons gefunden!"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
||||
|
||||
@@ -72,14 +72,21 @@ AdvSceneSwitcher.generalTab.transitionBehaviorSelectionError="At least one optio
|
||||
; Variables Tab
|
||||
AdvSceneSwitcher.variableTab.title="Variables"
|
||||
AdvSceneSwitcher.variableTab.help="Variables can be used in many places throughout the plugin.\n\nClick on the highlighted plus symbol to add a new variable."
|
||||
AdvSceneSwitcher.variableTab.tooltip.variableAddButton="Add new variable"
|
||||
AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton="Remove selected variables"
|
||||
AdvSceneSwitcher.variableTab.header.name="Name"
|
||||
AdvSceneSwitcher.variableTab.header.value="Value"
|
||||
AdvSceneSwitcher.variableTab.header.saveLoadBehavior="Save/Load behavior"
|
||||
AdvSceneSwitcher.variableTab.header.lastUse="Last used"
|
||||
AdvSceneSwitcher.variableTab.neverNused="Never"
|
||||
AdvSceneSwitcher.variableTab.lastUsed="%1 seconds ago"
|
||||
AdvSceneSwitcher.variableTab.variableAddButton.tooltip="Add new variable"
|
||||
AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip="Remove selected variables"
|
||||
AdvSceneSwitcher.variableTab.removeSingleVariablePopup.text="Are you sure you want to remove \"%1\"?"
|
||||
AdvSceneSwitcher.variableTab.removeMultipleVariablesPopup.text="Are you sure you want to remove %1 variables?"
|
||||
AdvSceneSwitcher.variableTab.name.header="Name"
|
||||
AdvSceneSwitcher.variableTab.value.header="Value"
|
||||
AdvSceneSwitcher.variableTab.saveLoadBehavior.header="Save/Load behavior"
|
||||
AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default="Default to \"%1\""
|
||||
AdvSceneSwitcher.variableTab.lastUsed.header="Last used"
|
||||
AdvSceneSwitcher.variableTab.lastUsed.text="%1 seconds ago"
|
||||
AdvSceneSwitcher.variableTab.lastUsed.text.never="Never"
|
||||
AdvSceneSwitcher.variableTab.lastChanged.header="Last changed"
|
||||
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"
|
||||
|
||||
; Macro Tab
|
||||
AdvSceneSwitcher.macroTab.title="Macro"
|
||||
@@ -94,6 +101,7 @@ AdvSceneSwitcher.macroTab.edit.logic="Logic type:"
|
||||
AdvSceneSwitcher.macroTab.edit.condition="Condition type:"
|
||||
AdvSceneSwitcher.macroTab.edit.action="Action type:"
|
||||
AdvSceneSwitcher.macroTab.add="Add new macro"
|
||||
AdvSceneSwitcher.macroTab.addGroup="Add new macro group"
|
||||
AdvSceneSwitcher.macroTab.name="Name:"
|
||||
AdvSceneSwitcher.macroTab.run="Run macro"
|
||||
AdvSceneSwitcher.macroTab.run.tooltip="Run all macro actions regardless of condition.\nIf else actions are available, hold Shift and click the button to execute them instead."
|
||||
@@ -103,10 +111,11 @@ AdvSceneSwitcher.macroTab.runInParallel="Run macro in parallel to other macros"
|
||||
AdvSceneSwitcher.macroTab.onChange="Perform actions only on condition change"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="Group %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro name exists already"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Are you sure you want to delete \"%1\" and all its elements?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Are you sure you want to delete %1 macros?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Are you sure you want to delete \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.macroNameExists="The name \"%1\" is already used by a macro."
|
||||
AdvSceneSwitcher.macroTab.groupNameExists="The name \"%1\" is already used by a macro group."
|
||||
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Are you sure you want to remove \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Are you sure you want to remove %1 macros/groups?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Are you sure you want to remove \"%1\" group and all its elements?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="Add"
|
||||
AdvSceneSwitcher.macroTab.copy="Duplicate Macro"
|
||||
AdvSceneSwitcher.macroTab.group="Group Selected Macros"
|
||||
@@ -115,14 +124,18 @@ 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!"
|
||||
AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue importing macro \"%1\" as a macro with the same name already exists!\nDo you want to continue with the import of \"%2\" and choose a new name? (Will be skipped otherwise)"
|
||||
AdvSceneSwitcher.macroTab.import.nameConflict="Cannot continue with import of \"%1\" as the name is already in use!\nDo want to choose a new name for the imported \"%2\"? (It will be skipped otherwise)"
|
||||
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"
|
||||
@@ -133,6 +146,7 @@ AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performe
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro"
|
||||
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup"
|
||||
AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone="Stop and rerun actions of the currently selected macro, if the actions are still running, when a new execution is triggered"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddRunButton="Add button to run the macro"
|
||||
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro"
|
||||
@@ -615,6 +629,15 @@ AdvSceneSwitcher.condition.queue.type.stopped="Stopped"
|
||||
AdvSceneSwitcher.condition.queue.type.size="Size"
|
||||
AdvSceneSwitcher.condition.queue.entry.startStop="Queue{{queues}}is{{conditions}}"
|
||||
AdvSceneSwitcher.condition.queue.entry.size="Queue{{queues}}{{conditions}}is less than{{size}}"
|
||||
AdvSceneSwitcher.condition.clipboard="Clipboard"
|
||||
AdvSceneSwitcher.condition.clipboard.placeholder="Clipboard text"
|
||||
AdvSceneSwitcher.condition.clipboard.urlTooltip="Depending on the OS files might be represented as URLs also!"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.changed="Clipboard content changed"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.isText="Clipboard contains text"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.isImage="Clipboard contains an image"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.isURL="Clipboard contains an URL"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.matches="Clipboard content matches"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.entry="{{conditions}}{{regex}}{{urlInfo}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.scene="Switch scene"
|
||||
@@ -636,6 +659,8 @@ AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
|
||||
AdvSceneSwitcher.action.audio.type.syncOffset="Set sync offset"
|
||||
AdvSceneSwitcher.action.audio.type.monitor="Set audio monitoring"
|
||||
AdvSceneSwitcher.action.audio.type.enableOnTrack="Enable on audio track"
|
||||
AdvSceneSwitcher.action.audio.type.disableOnTrack="Disable on audio track"
|
||||
AdvSceneSwitcher.action.audio.type.balance="Set balance"
|
||||
AdvSceneSwitcher.action.audio.balance.description="Move the slider in the direction of the audio channel you want to focus on."
|
||||
AdvSceneSwitcher.action.audio.fade.type.duration="over a duration of"
|
||||
@@ -644,7 +669,7 @@ AdvSceneSwitcher.action.audio.fade.duration="{{fade}}Fade{{fadeTypes}}{{duration
|
||||
AdvSceneSwitcher.action.audio.fade.rate="{{fade}}Fade{{fadeTypes}}{{rate}}per second."
|
||||
AdvSceneSwitcher.action.audio.fade.wait="Wait for fade to complete."
|
||||
AdvSceneSwitcher.action.audio.fade.abort="Abort already active fade."
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}}{{audioSources}}{{volume}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}{{track}}"
|
||||
AdvSceneSwitcher.action.recording="Recording"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
|
||||
AdvSceneSwitcher.action.recording.type.start="Start recording"
|
||||
@@ -672,7 +697,8 @@ AdvSceneSwitcher.action.streaming.type.username="Set username"
|
||||
AdvSceneSwitcher.action.streaming.type.password="Set password"
|
||||
AdvSceneSwitcher.action.streaming.entry="{{actions}}{{keyFrameInterval}}{{stringValue}}{{showPassword}}"
|
||||
AdvSceneSwitcher.action.run="Run"
|
||||
AdvSceneSwitcher.action.run.entry.wait="{{wait}}Wait for process exit or at most {{timeout}}"
|
||||
AdvSceneSwitcher.action.run.wait.entry="{{wait}}Wait for process exit or at most {{timeout}}{{waitHelp}}"
|
||||
AdvSceneSwitcher.action.run.wait.help.tooltip="Note that macro properties won't work if you leave this unticked, as the process spawns detached from the rest of logic and there's no control over it."
|
||||
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
@@ -685,9 +711,10 @@ AdvSceneSwitcher.action.filter.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
||||
AdvSceneSwitcher.action.filter.type.toggle="Toggle"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Set settings"
|
||||
AdvSceneSwitcher.action.filter.type.pressSettingsButton="Press settings button"
|
||||
AdvSceneSwitcher.action.filter.refresh="Refresh"
|
||||
AdvSceneSwitcher.action.filter.refreshTooltip="Repopulate the filter settings selection with the settings of the filter which's name matches the variable value."
|
||||
AdvSceneSwitcher.action.filter.entry="On{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="On{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Get current settings"
|
||||
AdvSceneSwitcher.action.filter.inputMethod.individualManual="Set to fixed value"
|
||||
@@ -705,7 +732,6 @@ AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field orde
|
||||
AdvSceneSwitcher.action.source.type.openInteractionDialog="Open interaction dialog"
|
||||
AdvSceneSwitcher.action.source.type.openFilterDialog="Open filter dialog"
|
||||
AdvSceneSwitcher.action.source.type.openPropertiesDialog="Open properties dialog"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="No buttons found!"
|
||||
AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.source.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}"
|
||||
AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI\nYou might be looking for \"Scene item visibility\""
|
||||
@@ -833,6 +859,7 @@ AdvSceneSwitcher.action.systray.title="Title:"
|
||||
AdvSceneSwitcher.action.systray.message="Message:"
|
||||
AdvSceneSwitcher.action.systray.icon="Icon:"
|
||||
AdvSceneSwitcher.action.systray.iconHint="Leave blank to use the default icon"
|
||||
AdvSceneSwitcher.action.systray.disabled="System tray notifications seem to be disabled in the OBS settings!"
|
||||
AdvSceneSwitcher.action.screenshot="Screenshot"
|
||||
AdvSceneSwitcher.action.screenshot.save.default="Default"
|
||||
AdvSceneSwitcher.action.screenshot.save.custom="Custom"
|
||||
@@ -893,6 +920,10 @@ AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
|
||||
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
|
||||
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
|
||||
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
|
||||
AdvSceneSwitcher.action.variable.type.padValue="Pad current value"
|
||||
AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
|
||||
AdvSceneSwitcher.action.variable.truncateOrPadDirection.left="Left"
|
||||
AdvSceneSwitcher.action.variable.truncateOrPadDirection.right="Right"
|
||||
AdvSceneSwitcher.action.variable.askForValuePromptDefault="Assign value to variable \"%1\":"
|
||||
AdvSceneSwitcher.action.variable.askForValuePrompt="Assign value to variable:"
|
||||
AdvSceneSwitcher.action.variable.mathExpression.example="( 1 + 2 * 3 ) / 4"
|
||||
@@ -904,7 +935,9 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
|
||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
|
||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
|
||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
||||
AdvSceneSwitcher.action.variable.entry="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{sceneItemIndex}}"
|
||||
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
||||
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
||||
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringRegex="Assign value of{{regexMatchIdx}}match using regular expression:"
|
||||
AdvSceneSwitcher.action.variable.entry.findAndReplace="{{findStr}}{{findRegex}}{{replaceStr}}"
|
||||
@@ -1600,6 +1633,15 @@ AdvSceneSwitcher.tempVar.macro.matchedCount.description="The number of macros of
|
||||
|
||||
AdvSceneSwitcher.tempVar.process.name="Process name"
|
||||
|
||||
AdvSceneSwitcher.tempVar.run.process.id="Process ID"
|
||||
AdvSceneSwitcher.tempVar.run.process.id.description="PID of the process assigned by the system."
|
||||
AdvSceneSwitcher.tempVar.run.process.exitCode="Process exit code"
|
||||
AdvSceneSwitcher.tempVar.run.process.exitCode.description="Numeric code with which the process exited. Usually 0 is positive and other values are error codes."
|
||||
AdvSceneSwitcher.tempVar.run.process.stream.output="Process standard output stream"
|
||||
AdvSceneSwitcher.tempVar.run.process.stream.output.description="Full standard output stream, usually numbered 1 and what you see in CLI after running the command."
|
||||
AdvSceneSwitcher.tempVar.run.process.stream.error="Process standard error stream"
|
||||
AdvSceneSwitcher.tempVar.run.process.stream.error.description="Full standard error stream, usually numbered 2."
|
||||
|
||||
AdvSceneSwitcher.tempVar.recording.durationSeconds="Recording duration"
|
||||
AdvSceneSwitcher.tempVar.recording.durationSeconds.description="Recording duration in seconds.\nThis value does not change while the recording is paused and will be reset to zero if the recording is stopped."
|
||||
|
||||
@@ -1631,6 +1673,9 @@ AdvSceneSwitcher.tempVar.streaming.keyframeInterval.description="Stream keyframe
|
||||
AdvSceneSwitcher.tempVar.streaming.durationSeconds="Stream duration"
|
||||
AdvSceneSwitcher.tempVar.streaming.durationSeconds.description="Seconds passed since the stream was started.\nThis value will be zero if the stream is stopped."
|
||||
|
||||
AdvSceneSwitcher.tempVar.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.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
@@ -1656,10 +1701,27 @@ AdvSceneSwitcher.enterText="--enter text--"
|
||||
AdvSceneSwitcher.enterURL="--enter URL--"
|
||||
AdvSceneSwitcher.selectHotkey="--select hotkey--"
|
||||
AdvSceneSwitcher.selectDisplay="--select display--"
|
||||
AdvSceneSwitcher.selectSetting="--select setting--"
|
||||
AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved"
|
||||
AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB"
|
||||
|
||||
AdvSceneSwitcher.settings.suffix.type.invalid=" (Invalid)"
|
||||
AdvSceneSwitcher.settings.suffix.type.bool=" (Bool)"
|
||||
AdvSceneSwitcher.settings.suffix.type.int=" (Int)"
|
||||
AdvSceneSwitcher.settings.suffix.type.float=" (Float)"
|
||||
AdvSceneSwitcher.settings.suffix.type.text=" (Text)"
|
||||
AdvSceneSwitcher.settings.suffix.type.path=" (Path)"
|
||||
AdvSceneSwitcher.settings.suffix.type.list=" (List)"
|
||||
AdvSceneSwitcher.settings.suffix.type.color=" (Color)"
|
||||
AdvSceneSwitcher.settings.suffix.type.button=" (Button)"
|
||||
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
|
||||
AdvSceneSwitcher.settings.suffix.type.font=" (Font)"
|
||||
AdvSceneSwitcher.settings.suffix.type.editableList=" (Editable list)"
|
||||
AdvSceneSwitcher.settings.suffix.type.frameRate=" (Frame rate)"
|
||||
AdvSceneSwitcher.settings.suffix.type.group=" (Group)"
|
||||
AdvSceneSwitcher.settings.suffix.type.colorAlpha=" (Color alpha)"
|
||||
AdvSceneSwitcher.setting.select="--select setting--"
|
||||
|
||||
|
||||
AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceName="Source name"
|
||||
AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}"
|
||||
@@ -1703,6 +1765,8 @@ AdvSceneSwitcher.audio.monitor.none="Monitor Off"
|
||||
AdvSceneSwitcher.audio.monitor.monitorOnly="Monitor Only (mute output)"
|
||||
AdvSceneSwitcher.audio.monitor.both="Monitor and Output"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="No buttons found!"
|
||||
|
||||
; Legacy tabs below - please don't waste your time adding translations for these :)
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
|
||||
@@ -75,7 +75,6 @@ AdvSceneSwitcher.macroTab.run="Ejecutar macro"
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Ejecutar macro en paralelo a otras macros"
|
||||
AdvSceneSwitcher.macroTab.onChange="Realizar acciones solo en el cambio de condición"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.exists="El nombre de la macro ya existe"
|
||||
AdvSceneSwitcher.macroTab.copy="Crear copia"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Expandir todo"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Contraer todo"
|
||||
@@ -366,7 +365,7 @@ AdvSceneSwitcher.action.filter="Filtro"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Habilitar"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Deshabilitar"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Establecer configuración"
|
||||
AdvSceneSwitcher.action.filter.entry="En{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="En{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Obtener la configuración actual"
|
||||
AdvSceneSwitcher.action.source="Fuente"
|
||||
AdvSceneSwitcher.action.source.type.enable="Habilitar"
|
||||
@@ -455,7 +454,7 @@ AdvSceneSwitcher.action.sceneCollection="Colección de escenas"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Cambiar la colección de escenas activa a {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Nota: Cualquier acción posterior a esta no se ejecutará, ya que la colección de escenas cambiante también volverá a cargar la configuración del conmutador de escenas.\nLa acción de la colección de escenas se ignorará mientras se abra la ventana de configuración".
|
||||
AdvSceneSwitcher.action.sequence="Secuencia"
|
||||
AdvSceneSwitcher.action.sequence.entry="Cada vez que se realiza esta acción, ejecute la siguiente macro de la lista (las macros en pausa se ignoran)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="Cada vez que se realiza esta acción, ejecute la siguiente macro de la lista (las macros en pausa se ignoran)"
|
||||
AdvSceneSwitcher.action.sequence.status="Última macro ejecutada: %1 - Siguiente macro a ejecutar: %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="ninguno"
|
||||
AdvSceneSwitcher.action.sequence.restart="Reiniciar desde el principio una vez que se alcance el final de la lista"
|
||||
|
||||
@@ -82,10 +82,9 @@ AdvSceneSwitcher.macroTab.runInParallel="Exécuter la macro en parallèle avec d
|
||||
AdvSceneSwitcher.macroTab.onChange="Exécuter des actions uniquement en cas de changement de condition"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Macro %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="Groupe %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Le nom de la macro existe déjà"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="Êtes-vous sûr de vouloir supprimer \"%1\" et tous ses éléments ?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="Êtes-vous sûr de vouloir supprimer %1 macros ?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
|
||||
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" ?"
|
||||
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="Êtes-vous sûr de vouloir supprimer %1 macros ?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="Êtes-vous sûr de vouloir supprimer \"%1\" et tous ses éléments ?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="Ajouter"
|
||||
AdvSceneSwitcher.macroTab.copy="Dupliquer la macro"
|
||||
AdvSceneSwitcher.macroTab.group="Grouper les macros sélectionnées"
|
||||
@@ -522,7 +521,7 @@ AdvSceneSwitcher.action.filter.type.enable="Activer"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Désactiver"
|
||||
AdvSceneSwitcher.action.filter.type.toggle="Basculer"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Définir les paramètres"
|
||||
AdvSceneSwitcher.action.filter.entry="Sur{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="Sur{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Obtenir les paramètres actuels"
|
||||
AdvSceneSwitcher.action.source="Source"
|
||||
AdvSceneSwitcher.action.source.type.enable="Activer"
|
||||
@@ -534,7 +533,6 @@ AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="Peut être utilisé
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceMode="Définir le mode de désentrelacement"
|
||||
AdvSceneSwitcher.action.source.type.deinterlaceOrder="Définir l'ordre de désentrelacement des champs"
|
||||
AdvSceneSwitcher.action.source.type.openInteractionDialog="Ouvrir la boîte de dialogue d'interaction"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="Aucun bouton trouvé !"
|
||||
AdvSceneSwitcher.action.source.warning="Avertissement : Activer et désactiver les sources globalement ne peut pas être contrôlé par l'interface utilisateur d'OBS\nVous pourriez rechercher \"Visibilité de l'élément de scène\""
|
||||
AdvSceneSwitcher.action.source.getSettings="Obtenir les paramètres actuels"
|
||||
AdvSceneSwitcher.action.source.deinterlaceMode.disable="Désactiver"
|
||||
@@ -660,7 +658,7 @@ AdvSceneSwitcher.action.sceneCollection="Collection de scènes"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="Changer la collection de scènes active vers{{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="Note : Toutes les actions suivantes ne seront pas exécutées car le changement de collection de scènes rechargera également les paramètres du commutateur de scènes. L'action de collection de scènes sera ignorée lorsque la fenêtre des paramètres est ouverte."
|
||||
AdvSceneSwitcher.action.sequence="Séquence"
|
||||
AdvSceneSwitcher.action.sequence.entry="Chaque fois que cette action est effectuée, exécutez le macro suivant dans la liste (les macros en pause sont ignorés)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="Chaque fois que cette action est effectuée, exécutez le macro suivant dans la liste (les macros en pause sont ignorés)"
|
||||
AdvSceneSwitcher.action.sequence.status="Dernier macro exécuté : %1 - Prochain macro à exécuter : %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="aucun"
|
||||
AdvSceneSwitcher.action.sequence.restart="Recommencer depuis le début une fois la fin de la liste atteinte"
|
||||
@@ -739,6 +737,8 @@ AdvSceneSwitcher.action.twitch.type.channel.info.category.set="Définir la caté
|
||||
AdvSceneSwitcher.action.twitch.type.commercial.start="Démarrer une publicité d'une durée de"
|
||||
AdvSceneSwitcher.action.twitch.categorySelectionDisabled="Impossible de sélectionner une catégorie sans avoir d'abord sélectionné un compte Twitch !"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="Aucun bouton trouvé !"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="Utiliser une transition pour le passage automatisé de la scène A à la scène B"
|
||||
|
||||
@@ -69,7 +69,6 @@ AdvSceneSwitcher.macroTab.edit.action="Тип действия:"
|
||||
AdvSceneSwitcher.macroTab.add="Добавить новый макрос"
|
||||
AdvSceneSwitcher.macroTab.name="Имя:"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Макрос %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Имя макроса уже существует"
|
||||
AdvSceneSwitcher.macroTab.copy="Создать копию"
|
||||
; Macro Logic
|
||||
AdvSceneSwitcher.logic.none="Игнорировать вход"
|
||||
|
||||
@@ -75,7 +75,6 @@ AdvSceneSwitcher.macroTab.run="Makro Çalıştırma"
|
||||
AdvSceneSwitcher.macroTab.runInParallel="Makroyu diğer makrolara paralel olarak çalıştırın"
|
||||
AdvSceneSwitcher.macroTab.onChange="Eylemleri yalnızca koşul değişikliğinde gerçekleştirin"
|
||||
AdvSceneSwitcher.macroTab.defaultname="Makro %1"
|
||||
AdvSceneSwitcher.macroTab.exists="Macro adı zaten mevcut"
|
||||
AdvSceneSwitcher.macroTab.copy="Kopya oluştur"
|
||||
AdvSceneSwitcher.macroTab.expandAll="Hepsini Genişlet"
|
||||
AdvSceneSwitcher.macroTab.collapseAll="Hepsini Küçült"
|
||||
@@ -296,7 +295,7 @@ AdvSceneSwitcher.action.filter="Filtrele"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Etkin"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Etkisiz"
|
||||
AdvSceneSwitcher.action.filter.type.settings="Ayarları yap"
|
||||
AdvSceneSwitcher.action.filter.entry="Açık{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="Açık{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="Mevcut ayarları al"
|
||||
AdvSceneSwitcher.action.source="Kaynak"
|
||||
AdvSceneSwitcher.action.source.type.enable="Etkin"
|
||||
|
||||
@@ -81,10 +81,9 @@ AdvSceneSwitcher.macroTab.runInParallel="与其他宏同时运行宏"
|
||||
AdvSceneSwitcher.macroTab.onChange="仅在条件更改时执行操作"
|
||||
AdvSceneSwitcher.macroTab.defaultname="宏 %1"
|
||||
AdvSceneSwitcher.macroTab.defaultGroupName="组 %1"
|
||||
AdvSceneSwitcher.macroTab.exists="宏名称已存在"
|
||||
AdvSceneSwitcher.macroTab.groupDeleteConfirm="您确定要删除 \"%1\"及其所有元素吗?"
|
||||
AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation="您确定要删除 %1 宏吗?"
|
||||
AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation="你确定你要删除 \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text="你确定你要删除 \"%1\"?"
|
||||
AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text="您确定要删除 %1 宏吗?"
|
||||
AdvSceneSwitcher.macroTab.removeGroupPopup.text="您确定要删除 \"%1\"及其所有元素吗?"
|
||||
AdvSceneSwitcher.macroTab.contextMenuAdd="增加"
|
||||
AdvSceneSwitcher.macroTab.copy="创建副本"
|
||||
AdvSceneSwitcher.macroTab.group="对选定的宏进行分组"
|
||||
@@ -501,7 +500,7 @@ AdvSceneSwitcher.action.filter="滤镜"
|
||||
AdvSceneSwitcher.action.filter.type.enable="开启"
|
||||
AdvSceneSwitcher.action.filter.type.disable="关闭"
|
||||
AdvSceneSwitcher.action.filter.type.settings="参数设置"
|
||||
AdvSceneSwitcher.action.filter.entry="置{{sources}}{{actions}}{{filters}}{{refresh}}"
|
||||
AdvSceneSwitcher.action.filter.entry="置{{sources}}{{actions}}{{filters}}{{refresh}}{{settingsButtons}}"
|
||||
AdvSceneSwitcher.action.filter.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.source="源"
|
||||
AdvSceneSwitcher.action.source.type.enable="启用"
|
||||
@@ -510,7 +509,6 @@ AdvSceneSwitcher.action.source.type.settings="配置设置"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings="刷新源设置"
|
||||
AdvSceneSwitcher.action.source.type.pressSettingsButton="按下设置按钮"
|
||||
AdvSceneSwitcher.action.source.type.refreshSettings.tooltip="可用于刷新浏览器、媒体、源、etc"
|
||||
AdvSceneSwitcher.action.source.noSettingsButtons="未找到按钮!"
|
||||
AdvSceneSwitcher.action.source.warning="警告:OBS UI无法控制全局启用和禁用源,注意,通过这个设置禁用,我测试,你必须通过这个在启用才行,不然点击小眼睛你无法显示!"
|
||||
AdvSceneSwitcher.action.source.getSettings="获取当前设置"
|
||||
AdvSceneSwitcher.action.media="媒体"
|
||||
@@ -607,7 +605,7 @@ AdvSceneSwitcher.action.sceneCollection="场景集合"
|
||||
AdvSceneSwitcher.action.sceneCollection.entry="将活动场景集合切换到 {{sceneCollections}}"
|
||||
AdvSceneSwitcher.action.sceneCollection.warning="注意:在此之后的任何操作都不会执行,因为更改场景集合也会重新加载场景切换器设置。\n打开“设置”窗口时,场景采集操作将被忽略."
|
||||
AdvSceneSwitcher.action.sequence="场景序列"
|
||||
AdvSceneSwitcher.action.sequence.entry="每次执行此操作时,运行列表中的下一个宏(暂停的宏将被忽略)"
|
||||
; AdvSceneSwitcher.action.sequence.entry="每次执行此操作时,运行列表中的下一个宏(暂停的宏将被忽略)"
|
||||
AdvSceneSwitcher.action.sequence.status="上次执行的宏:%1 - 要执行的下一个宏: %2"
|
||||
AdvSceneSwitcher.action.sequence.status.none="无"
|
||||
AdvSceneSwitcher.action.sequence.restart="到达列表末尾后,从开头重新启动"
|
||||
@@ -662,6 +660,8 @@ AdvSceneSwitcher.action.midi.entry="发送消息到 {{device}}:"
|
||||
AdvSceneSwitcher.action.midi.entry.listen="将MIDI消息选择设置为 {{listenDevices}} 上传入的消息: {{listenButton}}"
|
||||
AdvSceneSwitcher.action.osc="打开声音控制"
|
||||
|
||||
AdvSceneSwitcher.noSettingsButtons="未找到按钮!"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="转场特效"
|
||||
AdvSceneSwitcher.transitionTab.transitionForAToB="当自动从场景A切换到场景B时使用的转场特效"
|
||||
|
||||
@@ -902,6 +902,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroInParallel">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.runInParallel</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
@@ -922,6 +925,9 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runMacroOnChange">
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.macroTab.onChange</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
@@ -1648,7 +1654,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.variableTab.tooltip.variableAddButton</string>
|
||||
<string>AdvSceneSwitcher.variableTab.variableAddButton.tooltip</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
@@ -1667,7 +1673,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>AdvSceneSwitcher.variableTab.tooltip.variableRemoveButton</string>
|
||||
<string>AdvSceneSwitcher.variableTab.variableRemoveButton.tooltip</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -135,6 +135,23 @@ bool AdvSceneSwitcher::eventFilter(QObject *obj, QEvent *event)
|
||||
}
|
||||
|
||||
SetElseActionsStateToVisible();
|
||||
} else if (eventType == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
auto pressedKey = keyEvent->key();
|
||||
|
||||
if (obj == ui->macros && ui->macros->isVisible()) {
|
||||
if (pressedKey == Qt::Key_F2) {
|
||||
RenameSelectedMacro();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QDialog::eventFilter(obj, event);
|
||||
|
||||
@@ -81,6 +81,7 @@ public:
|
||||
bool AddNewMacro(std::shared_ptr<Macro> &res, std::string &name,
|
||||
std::string format = "");
|
||||
void RemoveMacro(std::shared_ptr<Macro> &);
|
||||
void RemoveSelectedMacros();
|
||||
void RenameMacro(std::shared_ptr<Macro> &, const QString &name);
|
||||
std::shared_ptr<Macro> GetSelectedMacro();
|
||||
std::vector<std::shared_ptr<Macro>> GetSelectedMacros();
|
||||
@@ -135,7 +136,7 @@ public slots:
|
||||
void ShowMacroElseActionsContextMenu(const QPoint &);
|
||||
void ShowMacroConditionsContextMenu(const QPoint &);
|
||||
void CopyMacro();
|
||||
void RenameCurrentMacro();
|
||||
void RenameSelectedMacro();
|
||||
void ExportMacros();
|
||||
void ImportMacros();
|
||||
void ExpandAllActions();
|
||||
@@ -216,6 +217,9 @@ public slots:
|
||||
void on_variableAdd_clicked();
|
||||
void on_variableRemove_clicked();
|
||||
|
||||
void OpenSettingsForSelectedVariable();
|
||||
void RemoveSelectedVariables();
|
||||
|
||||
/* --- End of variable tab section --- */
|
||||
|
||||
/* --- Begin of legacy tab section --- */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -437,10 +437,8 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
audioVolumeThreshold->setMaximum(100);
|
||||
audioVolumeThreshold->setMinimum(0);
|
||||
|
||||
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
|
||||
audioVolumeThreshold, SLOT(setValue(int)));
|
||||
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
|
||||
volMeter->GetSlider(), SLOT(setValue(int)));
|
||||
QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
|
||||
[=](double) { SyncSliderAndValueSelection(true); });
|
||||
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeThresholdChanged(int)));
|
||||
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
|
||||
@@ -487,6 +485,7 @@ AudioSwitchWidget::AudioSwitchWidget(QWidget *parent, AudioSwitch *s)
|
||||
switchData = s;
|
||||
|
||||
loading = false;
|
||||
SyncSliderAndValueSelection(false);
|
||||
}
|
||||
|
||||
AudioSwitch *AudioSwitchWidget::getSwitchData()
|
||||
@@ -509,6 +508,21 @@ void AudioSwitchWidget::swapSwitchData(AudioSwitchWidget *s1,
|
||||
s2->setSwitchData(t);
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::SyncSliderAndValueSelection(bool sliderMoved)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sliderMoved) {
|
||||
auto sliderPosition = volMeter->GetSlider()->DoubleValue();
|
||||
audioVolumeThreshold->setValue(sliderPosition);
|
||||
} else {
|
||||
volMeter->GetSlider()->SetDoubleVal(
|
||||
switchData->volumeThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::UpdateVolmeterSource()
|
||||
{
|
||||
delete volMeter;
|
||||
@@ -520,10 +534,8 @@ void AudioSwitchWidget::UpdateVolmeterSource()
|
||||
QLayout *layout = this->layout();
|
||||
layout->addWidget(volMeter);
|
||||
|
||||
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
|
||||
audioVolumeThreshold, SLOT(setValue(int)));
|
||||
QWidget::connect(audioVolumeThreshold, SIGNAL(valueChanged(int)),
|
||||
volMeter->GetSlider(), SLOT(setValue(int)));
|
||||
QWidget::connect(volMeter->GetSlider(), &DoubleSlider::DoubleValChanged,
|
||||
[=](double) { SyncSliderAndValueSelection(true); });
|
||||
|
||||
// Slider will default to 0 so set it manually once
|
||||
volMeter->GetSlider()->setValue(switchData->volumeThreshold);
|
||||
@@ -547,8 +559,11 @@ void AudioSwitchWidget::VolumeThresholdChanged(int vol)
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->volumeThreshold = vol;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->volumeThreshold = vol;
|
||||
}
|
||||
SyncSliderAndValueSelection(false);
|
||||
}
|
||||
|
||||
void AudioSwitchWidget::ConditionChanged(int cond)
|
||||
|
||||
@@ -79,6 +79,7 @@ private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void DurationChanged(const Duration &);
|
||||
void IgnoreInactiveChanged(int state);
|
||||
void SyncSliderAndValueSelection(bool sliderMoved);
|
||||
|
||||
private:
|
||||
QComboBox *audioSources;
|
||||
|
||||
@@ -132,6 +132,6 @@ public:
|
||||
};
|
||||
|
||||
QRunnable *CreateFunctionRunnable(std::function<void()> func);
|
||||
}
|
||||
} // namespace Compatability
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -31,7 +31,9 @@ void MacroActionQueue::AddActions(ActionQueue *queue)
|
||||
|
||||
auto actions = *GetMacroActions(macro.get());
|
||||
for (const auto &action : actions) {
|
||||
queue->Add(action);
|
||||
if (action->Enabled()) {
|
||||
queue->Add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ const static std::map<MacroActionVariable::Type, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.variable.type.setToTempvar"},
|
||||
{MacroActionVariable::Type::SCENE_ITEM_NAME,
|
||||
"AdvSceneSwitcher.action.variable.type.sceneItemName"},
|
||||
{MacroActionVariable::Type::PAD,
|
||||
"AdvSceneSwitcher.action.variable.type.padValue"},
|
||||
{MacroActionVariable::Type::TRUNCATE,
|
||||
"AdvSceneSwitcher.action.variable.type.truncateValue"},
|
||||
};
|
||||
|
||||
static void apppend(Variable &var, const std::string &value)
|
||||
@@ -217,6 +221,38 @@ static void askForInput(void *param)
|
||||
parameters->resultReady = true;
|
||||
}
|
||||
|
||||
static std::string truncate(const std::string &input,
|
||||
MacroActionVariable::Direction direction,
|
||||
size_t length)
|
||||
{
|
||||
if (input.length() <= length) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (direction == MacroActionVariable::Direction::RIGHT) {
|
||||
return input.substr(0, length);
|
||||
} else {
|
||||
return input.substr(input.length() - length);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string pad(const std::string &input,
|
||||
MacroActionVariable::Direction direction, size_t length,
|
||||
char paddingChar)
|
||||
{
|
||||
if (input.length() >= length) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (direction == MacroActionVariable::Direction::RIGHT) {
|
||||
return input +
|
||||
std::string(length - input.length(), paddingChar);
|
||||
} else {
|
||||
return std::string(length - input.length(), paddingChar) +
|
||||
input;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroActionVariable::PerformAction()
|
||||
{
|
||||
auto var = _variable.lock();
|
||||
@@ -311,7 +347,10 @@ bool MacroActionVariable::PerformAction()
|
||||
return true;
|
||||
}
|
||||
case Type::ENV_VARIABLE: {
|
||||
var->SetValue(std::getenv(_envVariableName.c_str()));
|
||||
auto value = std::getenv(_envVariableName.c_str());
|
||||
if (value) {
|
||||
var->SetValue(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Type::SCENE_ITEM_COUNT: {
|
||||
@@ -345,6 +384,14 @@ bool MacroActionVariable::PerformAction()
|
||||
case Type::SCENE_ITEM_NAME:
|
||||
SetToSceneItemName(var.get());
|
||||
return true;
|
||||
case Type::PAD:
|
||||
var->SetValue(pad(var->Value(), _direction, _stringLength,
|
||||
_paddingChar));
|
||||
return true;
|
||||
case Type::TRUNCATE:
|
||||
var->SetValue(
|
||||
truncate(var->Value(), _direction, _stringLength));
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -378,6 +425,9 @@ bool MacroActionVariable::Save(obs_data_t *obj) const
|
||||
_scene.Save(obj);
|
||||
_tempVar.Save(obj);
|
||||
_sceneItemIndex.Save(obj, "sceneItemIndex");
|
||||
obs_data_set_int(obj, "direction", static_cast<int>(_direction));
|
||||
_stringLength.Save(obj, "stringLength");
|
||||
obs_data_set_int(obj, "paddingChar", _paddingChar);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -409,6 +459,13 @@ bool MacroActionVariable::Load(obs_data_t *obj)
|
||||
_scene.Load(obj);
|
||||
_tempVar.Load(obj, GetMacro());
|
||||
_sceneItemIndex.Load(obj, "sceneItemIndex");
|
||||
_direction = static_cast<Direction>(obs_data_get_int(obj, "direction"));
|
||||
_stringLength.Load(obj, "stringLength");
|
||||
if (obs_data_has_user_value(obj, "paddingChar")) {
|
||||
_paddingChar = obs_data_get_int(obj, "paddingChar");
|
||||
} else {
|
||||
_paddingChar = ' ';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -527,6 +584,16 @@ static inline void populateTypeSelection(QComboBox *list)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateDirectionSelection(QComboBox *list)
|
||||
{
|
||||
list->addItems(
|
||||
QStringList()
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.truncateOrPadDirection.left")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.truncateOrPadDirection.right"));
|
||||
}
|
||||
|
||||
MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionVariable> entryData)
|
||||
: QWidget(parent),
|
||||
@@ -564,6 +631,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
true)),
|
||||
_tempVars(new TempVariableSelection(this)),
|
||||
_sceneItemIndex(new VariableSpinBox()),
|
||||
_direction(new QComboBox()),
|
||||
_stringLength(new VariableSpinBox()),
|
||||
_paddingCharSelection(new SingleCharSelection()),
|
||||
_entryLayout(new QHBoxLayout())
|
||||
{
|
||||
_numValue->setMinimum(-9999999999);
|
||||
@@ -583,7 +653,9 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
_sceneItemIndex->setMinimum(1);
|
||||
_stringLength->setMaximum(999);
|
||||
populateTypeSelection(_actions);
|
||||
populateDirectionSelection(_direction);
|
||||
|
||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(VariableChanged(const QString &)));
|
||||
@@ -639,8 +711,17 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_sceneItemIndex,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(SceneItemIndexChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_direction, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(DirectionChanged(int)));
|
||||
QWidget::connect(
|
||||
_stringLength,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(StringLengthChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_paddingCharSelection,
|
||||
SIGNAL(CharChanged(const QString &)), this,
|
||||
SLOT(CharSelectionChanged(const QString &)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{variables}}", _variables},
|
||||
{"{{variables2}}", _variables2},
|
||||
{"{{actions}}", _actions},
|
||||
@@ -662,9 +743,13 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{tempVars}}", _tempVars},
|
||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||
{"{{direction}}", _direction},
|
||||
{"{{stringLength}}", _stringLength},
|
||||
{"{{paddingCharSelection}}", _paddingCharSelection},
|
||||
};
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.variable.entry"),
|
||||
_entryLayout, widgetPlaceholders);
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.action.variable.entry.other"),
|
||||
_entryLayout, widgetPlaceholders);
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
@@ -757,6 +842,10 @@ void MacroActionVariableEdit::UpdateEntryData()
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
_tempVars->SetVariable(_entryData->_tempVar);
|
||||
_sceneItemIndex->SetValue(_entryData->_sceneItemIndex);
|
||||
_direction->setCurrentIndex(static_cast<int>(_entryData->_direction));
|
||||
_stringLength->SetValue(_entryData->_stringLength);
|
||||
_paddingCharSelection->setText(
|
||||
QChar::fromLatin1(_entryData->_paddingChar));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -1126,12 +1215,84 @@ void MacroActionVariableEdit::SceneItemIndexChanged(
|
||||
_entryData->_sceneItemIndex = value;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::DirectionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_direction =
|
||||
static_cast<MacroActionVariable::Direction>(value);
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::StringLengthChanged(
|
||||
const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_stringLength = value;
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::CharSelectionChanged(const QString &character)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
if (character.isEmpty()) {
|
||||
_entryData->_paddingChar = ' ';
|
||||
} else {
|
||||
_entryData->_paddingChar = character.toStdString().at(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{variables}}", _variables},
|
||||
{"{{variables2}}", _variables2},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{strValue}}", _strValue},
|
||||
{"{{numValue}}", _numValue},
|
||||
{"{{segmentIndex}}", _segmentIdx},
|
||||
{"{{mathExpression}}", _mathExpression},
|
||||
{"{{envVariableName}}", _envVariable},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{tempVars}}", _tempVars},
|
||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||
{"{{direction}}", _direction},
|
||||
{"{{stringLength}}", _stringLength},
|
||||
{"{{paddingCharSelection}}", _paddingCharSelection},
|
||||
};
|
||||
|
||||
const char *layoutString = "";
|
||||
if (_entryData->_type == MacroActionVariable::Type::PAD) {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.pad");
|
||||
} else if (_entryData->_type == MacroActionVariable::Type::TRUNCATE) {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.truncate");
|
||||
} else {
|
||||
layoutString = obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.entry.other");
|
||||
}
|
||||
|
||||
for (const auto &[_, widget] : widgetPlaceholders) {
|
||||
_entryLayout->removeWidget(widget);
|
||||
}
|
||||
|
||||
ClearLayout(_entryLayout);
|
||||
PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders);
|
||||
|
||||
if (_entryData->_type == MacroActionVariable::Type::SET_FIXED_VALUE ||
|
||||
_entryData->_type == MacroActionVariable::Type::APPEND ||
|
||||
_entryData->_type == MacroActionVariable::Type::MATH_EXPRESSION ||
|
||||
@@ -1222,6 +1383,14 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
MacroActionVariable::Type::SET_TO_TEMPVAR);
|
||||
_sceneItemIndex->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||
_direction->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::PAD ||
|
||||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
|
||||
_stringLength->setVisible(
|
||||
_entryData->_type == MacroActionVariable::Type::PAD ||
|
||||
_entryData->_type == MacroActionVariable::Type::TRUNCATE);
|
||||
_paddingCharSelection->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::PAD);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "regex-config.hpp"
|
||||
#include "resizing-text-edit.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
#include "single-char-selection.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "variable-spinbox.hpp"
|
||||
@@ -17,7 +18,7 @@ public:
|
||||
bool PerformAction();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
bool PostLoad() override;
|
||||
bool PostLoad();
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
@@ -45,6 +46,8 @@ public:
|
||||
EXTRACT_JSON,
|
||||
SET_TO_TEMPVAR,
|
||||
SCENE_ITEM_NAME,
|
||||
PAD,
|
||||
TRUNCATE,
|
||||
};
|
||||
|
||||
Type _type = Type::SET_FIXED_VALUE;
|
||||
@@ -79,6 +82,11 @@ public:
|
||||
TempVariableRef _tempVar;
|
||||
IntVariable _sceneItemIndex = 1;
|
||||
|
||||
enum class Direction { LEFT, RIGHT };
|
||||
Direction _direction = Direction::LEFT;
|
||||
IntVariable _stringLength = 1;
|
||||
char _paddingChar = '0';
|
||||
|
||||
private:
|
||||
void DecrementCurrentSegmentVariableRef();
|
||||
void HandleIndexSubString(Variable *);
|
||||
@@ -135,6 +143,9 @@ private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SelectionChanged(const TempVariableRef &var);
|
||||
void SceneItemIndexChanged(const NumberVariable<int> &);
|
||||
void DirectionChanged(int);
|
||||
void StringLengthChanged(const NumberVariable<int> &);
|
||||
void CharSelectionChanged(const QString &);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
@@ -175,6 +186,9 @@ private:
|
||||
SceneSelectionWidget *_scenes;
|
||||
TempVariableSelection *_tempVars;
|
||||
VariableSpinBox *_sceneItemIndex;
|
||||
QComboBox *_direction;
|
||||
VariableSpinBox *_stringLength;
|
||||
SingleCharSelection *_paddingCharSelection;
|
||||
QHBoxLayout *_entryLayout;
|
||||
|
||||
std::shared_ptr<MacroActionVariable> _entryData;
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
bool PostLoad() override;
|
||||
bool PostLoad();
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
|
||||
@@ -8,8 +8,13 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool usePlainText = false;
|
||||
|
||||
MacroExportImportDialog::MacroExportImportDialog(Type type)
|
||||
: QDialog(nullptr), _importExportString(new QPlainTextEdit(this))
|
||||
: QDialog(nullptr),
|
||||
_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(
|
||||
@@ -29,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);
|
||||
|
||||
@@ -64,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
|
||||
|
||||
@@ -54,6 +54,8 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
|
||||
_currentSkipOnStartup(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
|
||||
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone"))),
|
||||
_currentMacroRegisterDock(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentRegisterDock"))),
|
||||
_currentMacroDockAddRunButton(new QCheckBox(obs_module_text(
|
||||
@@ -96,6 +98,7 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
|
||||
auto generalLayout = new QVBoxLayout;
|
||||
generalLayout->addWidget(_currentSkipOnStartup);
|
||||
generalLayout->addWidget(_currentStopActionsIfNotDone);
|
||||
generalOptions->setLayout(generalLayout);
|
||||
|
||||
int row = 0;
|
||||
@@ -184,14 +187,17 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
|
||||
_conditions->setChecked(prop._highlightConditions);
|
||||
_actions->setChecked(prop._highlightActions);
|
||||
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
|
||||
|
||||
if (!macro || macro->IsGroup()) {
|
||||
hotkeyOptions->hide();
|
||||
generalOptions->hide();
|
||||
_dockOptions->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
|
||||
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
|
||||
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
|
||||
const bool dockEnabled = macro->DockEnabled();
|
||||
_currentMacroRegisterDock->setChecked(dockEnabled);
|
||||
_currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton());
|
||||
@@ -260,6 +266,7 @@ void MacroPropertiesDialog::DockEnableChanged(int enabled)
|
||||
SetGridLayoutRowVisible(
|
||||
_dockLayout, _conditionsFalseTextRow,
|
||||
enabled && _currentMacroDockAddStatusLabel->isChecked());
|
||||
|
||||
Resize();
|
||||
}
|
||||
|
||||
@@ -310,6 +317,8 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
|
||||
macro->EnablePauseHotkeys(
|
||||
dialog._currentMacroRegisterHotkeys->isChecked());
|
||||
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
|
||||
macro->SetStopActionsIfNotDone(
|
||||
dialog._currentStopActionsIfNotDone->isChecked());
|
||||
macro->EnableDock(dialog._currentMacroRegisterDock->isChecked());
|
||||
macro->SetDockHasRunButton(
|
||||
dialog._currentMacroDockAddRunButton->isChecked());
|
||||
@@ -328,6 +337,7 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
|
||||
dialog._conditionsTrueStatusText->text().toStdString());
|
||||
macro->SetConditionsFalseStatusText(
|
||||
dialog._conditionsFalseStatusText->text().toStdString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ private:
|
||||
// Current macro specific settings
|
||||
QCheckBox *_currentMacroRegisterHotkeys;
|
||||
QCheckBox *_currentSkipOnStartup;
|
||||
QCheckBox *_currentStopActionsIfNotDone;
|
||||
QCheckBox *_currentMacroRegisterDock;
|
||||
QCheckBox *_currentMacroDockAddRunButton;
|
||||
QCheckBox *_currentMacroDockAddPauseButton;
|
||||
|
||||
@@ -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 &);
|
||||
|
||||
@@ -13,19 +13,37 @@
|
||||
namespace advss {
|
||||
|
||||
MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue)
|
||||
: _macro(m), _supportsVariableValue(supportsVariableValue)
|
||||
: _macro(m),
|
||||
_supportsVariableValue(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;
|
||||
}
|
||||
@@ -295,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) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "action-queue.hpp"
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-export-import-dialog.hpp"
|
||||
@@ -29,6 +30,25 @@ static bool macroNameExists(const std::string &name)
|
||||
return !!GetMacroByName(name.c_str());
|
||||
}
|
||||
|
||||
static bool newMacroNameIsValid(const std::string &name)
|
||||
{
|
||||
if (!macroNameExists(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto macro = GetMacroByName(name.c_str());
|
||||
if (!macro) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString fmt = obs_module_text(
|
||||
macro->IsGroup() ? "AdvSceneSwitcher.macroTab.groupNameExists"
|
||||
: "AdvSceneSwitcher.macroTab.macroNameExists");
|
||||
|
||||
DisplayMessage(fmt.arg(QString::fromStdString(name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
||||
std::string &name, std::string format)
|
||||
{
|
||||
@@ -47,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);
|
||||
@@ -60,9 +80,7 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (macroNameExists(name)) {
|
||||
DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
|
||||
if (!newMacroNameIsValid(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -93,7 +111,7 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> ¯o)
|
||||
auto name = QString::fromStdString(macro->Name());
|
||||
if (macro->IsGroup() && macro->GroupSize() > 0) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.groupDeleteConfirm");
|
||||
"AdvSceneSwitcher.macroTab.removeGroupPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(name), true)) {
|
||||
return;
|
||||
}
|
||||
@@ -103,6 +121,41 @@ void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> ¯o)
|
||||
emit MacroRemoved(name);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveSelectedMacros()
|
||||
{
|
||||
auto macros = GetSelectedMacros();
|
||||
if (macros.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int macroCount = macros.size();
|
||||
if (macroCount == 1) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.removeSingleMacroPopup.text");
|
||||
auto macro = macros.at(0);
|
||||
deleteWarning = deleteWarning.arg(
|
||||
QString::fromStdString(macro->Name()));
|
||||
|
||||
if ((!macro->IsGroup() || macro->GroupSize() == 0) &&
|
||||
!DisplayMessage(deleteWarning, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveMacro(macro);
|
||||
return;
|
||||
}
|
||||
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.removeMultipleMacrosPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(macroCount), true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto ¯o : macros) {
|
||||
RemoveMacro(macro);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> ¯o,
|
||||
const QString &name)
|
||||
{
|
||||
@@ -116,32 +169,7 @@ void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> ¯o,
|
||||
|
||||
void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
{
|
||||
auto macros = GetSelectedMacros();
|
||||
if (macros.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (macros.size() == 1) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.deleteSingleMacroConfirmation");
|
||||
if (!DisplayMessage(deleteWarning.arg(QString::fromStdString(
|
||||
macros.at(0)->Name())),
|
||||
true)) {
|
||||
return;
|
||||
}
|
||||
RemoveMacro(macros.at(0));
|
||||
return;
|
||||
}
|
||||
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.deleteMultipleMacrosConfirmation");
|
||||
if (!DisplayMessage(deleteWarning.arg(macros.size()), true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto ¯o : macros) {
|
||||
RemoveMacro(macro);
|
||||
}
|
||||
RemoveSelectedMacros();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_macroUp_clicked()
|
||||
@@ -162,32 +190,26 @@ void AdvSceneSwitcher::on_macroDown_clicked()
|
||||
ui->macros->Down(macro);
|
||||
}
|
||||
|
||||
static bool newMacroNameValid(const std::string &name)
|
||||
{
|
||||
if (!macroNameExists(name)) {
|
||||
return true;
|
||||
}
|
||||
DisplayMessage(obs_module_text("AdvSceneSwitcher.macroTab.exists"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RenameCurrentMacro()
|
||||
void AdvSceneSwitcher::RenameSelectedMacro()
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
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))) {
|
||||
return;
|
||||
}
|
||||
if (name.empty() || name == oldName || !newMacroNameValid(name)) {
|
||||
|
||||
if (name.empty() || name == oldName || !newMacroNameIsValid(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenameMacro(macro, QString::fromStdString(name));
|
||||
|
||||
const QSignalBlocker b(ui->macroName);
|
||||
@@ -248,6 +270,7 @@ void AdvSceneSwitcher::ExportMacros()
|
||||
}
|
||||
obs_data_set_array(data, "macros", macroArray);
|
||||
SaveVariables(data);
|
||||
SaveActionQueues(data);
|
||||
obs_data_set_string(data, "version", g_GIT_TAG);
|
||||
auto json = obs_data_get_json(data);
|
||||
QString exportString(json);
|
||||
@@ -347,8 +370,11 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
|
||||
}
|
||||
|
||||
std::string newName;
|
||||
bool accepted = AdvSSNameDialog::AskForName(
|
||||
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
|
||||
bool accepted = NameDialog::AskForName(
|
||||
this,
|
||||
obs_module_text(macro->IsGroup()
|
||||
? "AdvSceneSwitcher.macroTab.addGroup"
|
||||
: "AdvSceneSwitcher.macroTab.add"),
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.name"), newName,
|
||||
placeHolderText);
|
||||
|
||||
@@ -360,9 +386,7 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (macroNameExists(newName)) {
|
||||
DisplayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.exists"));
|
||||
if (!newMacroNameIsValid(newName)) {
|
||||
return ResolveMacroImportNameConflict(macro);
|
||||
}
|
||||
|
||||
@@ -370,26 +394,6 @@ bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool variableWithNameExists(const std::string &name)
|
||||
{
|
||||
return !!GetVariableByName(name);
|
||||
}
|
||||
|
||||
static void importVariables(obs_data_t *obj)
|
||||
{
|
||||
OBSDataArrayAutoRelease array = obs_data_get_array(obj, "variables");
|
||||
size_t count = obs_data_array_count(array);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
OBSDataAutoRelease data = obs_data_array_item(array, i);
|
||||
auto var = Variable::Create();
|
||||
var->Load(data);
|
||||
if (variableWithNameExists(var->Name())) {
|
||||
continue;
|
||||
}
|
||||
GetVariables().emplace_back(var);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ImportMacros()
|
||||
{
|
||||
QString json;
|
||||
@@ -404,7 +408,8 @@ void AdvSceneSwitcher::ImportMacros()
|
||||
ImportMacros();
|
||||
return;
|
||||
}
|
||||
importVariables(data);
|
||||
ImportVariables(data);
|
||||
ImportQueues(data);
|
||||
|
||||
auto version = obs_data_get_string(data, "version");
|
||||
if (strcmp(version, g_GIT_TAG) != 0) {
|
||||
@@ -425,6 +430,7 @@ void AdvSceneSwitcher::ImportMacros()
|
||||
OBSDataAutoRelease array_obj = obs_data_array_item(array, i);
|
||||
auto macro = std::make_shared<Macro>();
|
||||
macro->Load(array_obj);
|
||||
RunPostLoadSteps();
|
||||
|
||||
if (macroNameExists(macro->Name()) &&
|
||||
!ResolveMacroImportNameConflict(macro)) {
|
||||
@@ -469,7 +475,7 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||
QString oldName = QString::fromStdString(macro->Name());
|
||||
|
||||
if (newName.isEmpty() || newName == oldName ||
|
||||
!newMacroNameValid(newName.toStdString())) {
|
||||
!newMacroNameIsValid(newName.toStdString())) {
|
||||
ui->macroName->setText(oldName);
|
||||
return;
|
||||
}
|
||||
@@ -794,6 +800,7 @@ bool shouldRestoreSplitter(const QList<int> &pos)
|
||||
void AdvSceneSwitcher::SetupMacroTab()
|
||||
{
|
||||
ui->macroElseActions->installEventFilter(this);
|
||||
ui->macros->installEventFilter(this);
|
||||
|
||||
if (GetMacros().size() == 0 && !switcher->disableHints) {
|
||||
addPulse = PulseWidget(ui->macroAdd, QColor(Qt::green));
|
||||
@@ -917,7 +924,7 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
||||
|
||||
auto rename = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.rename"), this,
|
||||
&AdvSceneSwitcher::RenameCurrentMacro);
|
||||
&AdvSceneSwitcher::RenameSelectedMacro);
|
||||
rename->setEnabled(ui->macros->SingleItemSelected());
|
||||
|
||||
auto remove = menu.addAction(
|
||||
@@ -936,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"),
|
||||
@@ -951,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()
|
||||
|
||||
@@ -173,6 +173,7 @@ void MacroTreeItem::Update(bool force)
|
||||
|
||||
} else if (_type == Type::Group) {
|
||||
_expand = new SourceTreeSubItemCheckBox();
|
||||
_expand->setProperty("sourceTreeSubItem", true);
|
||||
_expand->setSizePolicy(QSizePolicy::Maximum,
|
||||
QSizePolicy::Maximum);
|
||||
_expand->setMaximumSize(10, 16);
|
||||
|
||||
@@ -17,6 +17,7 @@ class QSpacerItem;
|
||||
class QHBoxLayout;
|
||||
|
||||
// Only used to enable applying "SourceTreeSubItemCheckBox" stylesheet
|
||||
// Can be removed once the minimum supported OBS version is greater than 30.1
|
||||
class SourceTreeSubItemCheckBox : public QCheckBox {
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
@@ -193,9 +193,17 @@ bool Macro::CeckMatch()
|
||||
bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
|
||||
{
|
||||
if (!_done) {
|
||||
vblog(LOG_INFO, "macro %s already running", _name.c_str());
|
||||
return !forceParallel;
|
||||
vblog(LOG_INFO, "Macro %s already running", _name.c_str());
|
||||
|
||||
if (!_stopActionsIfNotDone) {
|
||||
return !forceParallel;
|
||||
}
|
||||
|
||||
Stop();
|
||||
vblog(LOG_INFO, "Stopped macro %s actions to rerun them",
|
||||
_name.c_str());
|
||||
}
|
||||
|
||||
std::function<bool(bool)> runFunc =
|
||||
match ? std::bind(&Macro::RunActions, this,
|
||||
std::placeholders::_1)
|
||||
@@ -213,6 +221,7 @@ bool Macro::PerformActions(bool match, bool forceParallel, bool ignorePause)
|
||||
} else {
|
||||
ret = runFunc(ignorePause);
|
||||
}
|
||||
|
||||
_lastExecutionTime = std::chrono::high_resolution_clock::now();
|
||||
auto group = _parent.lock();
|
||||
if (group) {
|
||||
@@ -550,6 +559,7 @@ bool Macro::Save(obs_data_t *obj) const
|
||||
obs_data_set_bool(obj, "parallel", _runInParallel);
|
||||
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
|
||||
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
|
||||
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
|
||||
|
||||
obs_data_set_bool(obj, "group", _isGroup);
|
||||
if (_isGroup) {
|
||||
@@ -644,6 +654,7 @@ bool Macro::Load(obs_data_t *obj)
|
||||
_runInParallel = obs_data_get_bool(obj, "parallel");
|
||||
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
|
||||
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
|
||||
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
|
||||
|
||||
_isGroup = obs_data_get_bool(obj, "group");
|
||||
if (_isGroup) {
|
||||
@@ -651,6 +662,7 @@ bool Macro::Load(obs_data_t *obj)
|
||||
obs_data_get_obj(obj, "groupData");
|
||||
_isCollapsed = obs_data_get_bool(groupData, "collapsed");
|
||||
_groupSize = obs_data_get_int(groupData, "size");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -735,6 +747,7 @@ bool Macro::Load(obs_data_t *obj)
|
||||
}
|
||||
}
|
||||
UpdateElseActionIndices();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class Macro {
|
||||
public:
|
||||
Macro(const std::string &name = "", const bool addHotkey = false);
|
||||
virtual ~Macro();
|
||||
|
||||
bool CeckMatch();
|
||||
bool PerformActions(bool match, bool forceParallel = false,
|
||||
bool ignorePause = false);
|
||||
@@ -42,6 +43,11 @@ public:
|
||||
bool MatchOnChange() const { return _performActionsOnChange; }
|
||||
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
|
||||
bool SkipExecOnStart() const { return _skipExecOnStart; }
|
||||
void SetStopActionsIfNotDone(bool stopActionsIfNotDone)
|
||||
{
|
||||
_stopActionsIfNotDone = stopActionsIfNotDone;
|
||||
}
|
||||
bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; }
|
||||
int RunCount() const { return _runCount; };
|
||||
void ResetRunCount() { _runCount = 0; };
|
||||
void ResetTimers();
|
||||
@@ -168,6 +174,7 @@ private:
|
||||
bool _conditionSateChanged = false;
|
||||
bool _performActionsOnChange = true;
|
||||
bool _skipExecOnStart = false;
|
||||
bool _stopActionsIfNotDone = false;
|
||||
bool _paused = false;
|
||||
int _runCount = 0;
|
||||
bool _registerHotkeys = true;
|
||||
|
||||
@@ -51,9 +51,13 @@ void ActionQueue::Load(obs_data_t *obj)
|
||||
|
||||
void ActionQueue::Start()
|
||||
{
|
||||
if (_thread.joinable()) {
|
||||
if (!_stop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
_stop = false;
|
||||
_thread = std::thread(&ActionQueue::RunActions, this);
|
||||
}
|
||||
@@ -62,6 +66,10 @@ void ActionQueue::Stop()
|
||||
{
|
||||
_stop = true;
|
||||
_cv.notify_all();
|
||||
if (std::this_thread::get_id() == _thread.get_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
@@ -69,7 +77,7 @@ void ActionQueue::Stop()
|
||||
|
||||
bool ActionQueue::IsRunning() const
|
||||
{
|
||||
return _thread.joinable();
|
||||
return !_stop;
|
||||
}
|
||||
|
||||
bool ActionQueue::RunsOnStartup() const
|
||||
@@ -358,6 +366,27 @@ void LoadActionQueues(obs_data_t *obj)
|
||||
}
|
||||
}
|
||||
|
||||
static bool queueWithNameExists(const std::string &name)
|
||||
{
|
||||
return !GetWeakActionQueueByName(name).expired();
|
||||
}
|
||||
|
||||
void ImportQueues(obs_data_t *data)
|
||||
{
|
||||
OBSDataArrayAutoRelease array =
|
||||
obs_data_get_array(data, "actionQueues");
|
||||
size_t count = obs_data_array_count(array);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
OBSDataAutoRelease arrayElement = obs_data_array_item(array, i);
|
||||
auto queue = ActionQueue::Create();
|
||||
queue->Load(arrayElement);
|
||||
if (queueWithNameExists(queue->Name())) {
|
||||
continue;
|
||||
}
|
||||
queues.emplace_back(queue);
|
||||
}
|
||||
}
|
||||
|
||||
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)
|
||||
{
|
||||
for (const auto &queue : queues) {
|
||||
|
||||
@@ -39,7 +39,7 @@ private:
|
||||
|
||||
bool _runOnStartup = true;
|
||||
bool _resolveVariablesOnAdd = true;
|
||||
std::atomic_bool _stop = {false};
|
||||
std::atomic_bool _stop = {true};
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _cv;
|
||||
std::thread _thread;
|
||||
@@ -98,6 +98,7 @@ signals:
|
||||
void SetupActionQueues();
|
||||
void SaveActionQueues(obs_data_t *);
|
||||
void LoadActionQueues(obs_data_t *);
|
||||
void ImportQueues(obs_data_t *);
|
||||
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name);
|
||||
std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name);
|
||||
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &);
|
||||
|
||||
63
lib/utils/double-slider.cpp
Normal file
63
lib/utils/double-slider.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "double-slider.hpp"
|
||||
#include <QWheelEvent>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace advss {
|
||||
|
||||
SliderIgnoreScroll::SliderIgnoreScroll(QWidget *parent) : QSlider(parent)
|
||||
{
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
}
|
||||
|
||||
SliderIgnoreScroll::SliderIgnoreScroll(Qt::Orientation orientation,
|
||||
QWidget *parent)
|
||||
: QSlider(parent)
|
||||
{
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setOrientation(orientation);
|
||||
}
|
||||
|
||||
void SliderIgnoreScroll::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (!hasFocus()) {
|
||||
event->ignore();
|
||||
} else {
|
||||
QSlider::wheelEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
DoubleSlider::DoubleSlider(QWidget *parent) : SliderIgnoreScroll(parent)
|
||||
{
|
||||
connect(this, &DoubleSlider::valueChanged, [this](int val) {
|
||||
emit DoubleValChanged((minVal / minStep + val) * minStep);
|
||||
});
|
||||
}
|
||||
|
||||
void DoubleSlider::SetDoubleConstraints(double newMin, double newMax,
|
||||
double newStep, double val)
|
||||
{
|
||||
minVal = newMin;
|
||||
maxVal = newMax;
|
||||
minStep = newStep;
|
||||
|
||||
double total = maxVal - minVal;
|
||||
int intMax = int(total / minStep);
|
||||
|
||||
setMinimum(0);
|
||||
setMaximum(intMax);
|
||||
setSingleStep(1);
|
||||
SetDoubleVal(val);
|
||||
}
|
||||
|
||||
double DoubleSlider::DoubleValue()
|
||||
{
|
||||
return (minVal / minStep + value()) * minStep;
|
||||
}
|
||||
|
||||
void DoubleSlider::SetDoubleVal(double val)
|
||||
{
|
||||
setValue(lround((val - minVal) / minStep));
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
40
lib/utils/double-slider.hpp
Normal file
40
lib/utils/double-slider.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Based on OBS DoubleSlider and SliderIgnoreScroll implementation
|
||||
|
||||
#pragma once
|
||||
#include "export-symbol-helper.hpp"
|
||||
|
||||
#include <QSlider>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class SliderIgnoreScroll : public QSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SliderIgnoreScroll(QWidget *parent = nullptr);
|
||||
SliderIgnoreScroll(Qt::Orientation orientation,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
};
|
||||
|
||||
class ADVSS_EXPORT DoubleSlider : public SliderIgnoreScroll {
|
||||
Q_OBJECT
|
||||
|
||||
double minVal = 0, maxVal = 100, minStep = 1;
|
||||
|
||||
public:
|
||||
DoubleSlider(QWidget *parent = nullptr);
|
||||
|
||||
void SetDoubleConstraints(double newMin, double newMax, double newStep,
|
||||
double val);
|
||||
double DoubleValue();
|
||||
signals:
|
||||
void DoubleValChanged(double val);
|
||||
|
||||
public slots:
|
||||
void SetDoubleVal(double val);
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -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()));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
namespace advss {
|
||||
|
||||
Section::Section(const int animationDuration, QWidget *parent)
|
||||
: QWidget(parent), _animationDuration(animationDuration)
|
||||
: QWidget(parent),
|
||||
_animationDuration(animationDuration)
|
||||
{
|
||||
_toggleButton = new QToolButton(this);
|
||||
_headerLine = new QFrame(this);
|
||||
|
||||
22
lib/utils/single-char-selection.cpp
Normal file
22
lib/utils/single-char-selection.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "single-char-selection.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
SingleCharSelection::SingleCharSelection(QWidget *parent) : QLineEdit(parent)
|
||||
{
|
||||
setMaxLength(1);
|
||||
setMaximumWidth(50);
|
||||
connect(this, &QLineEdit::textChanged, this,
|
||||
&SingleCharSelection::CharChanged);
|
||||
}
|
||||
|
||||
void SingleCharSelection::HandleTextChanged(const QString &text)
|
||||
{
|
||||
if (text.length() == 1) {
|
||||
emit CharChanged(text);
|
||||
} else if (text.length() > 1) {
|
||||
setText(text.left(1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
18
lib/utils/single-char-selection.hpp
Normal file
18
lib/utils/single-char-selection.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <QLineEdit>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class SingleCharSelection : public QLineEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SingleCharSelection(QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void CharChanged(const QString &character);
|
||||
|
||||
private slots:
|
||||
void HandleTextChanged(const QString &text);
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -17,7 +17,10 @@ namespace advss {
|
||||
TempVariable::TempVariable(const std::string &id, const std::string &name,
|
||||
const std::string &description,
|
||||
const std::shared_ptr<MacroSegment> &segment)
|
||||
: _id(id), _name(name), _description(description), _segment(segment)
|
||||
: _id(id),
|
||||
_name(name),
|
||||
_description(description),
|
||||
_segment(segment)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -247,6 +250,8 @@ TempVariableSelection::TempVariableSelection(QWidget *parent)
|
||||
_info->setPixmap(pixmap);
|
||||
_info->hide();
|
||||
|
||||
_selection->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_selection->setMaximumWidth(350);
|
||||
_selection->setDuplicatesEnabled(true);
|
||||
PopulateSelection();
|
||||
|
||||
@@ -439,7 +444,9 @@ void NotifyUIAboutTempVarChange()
|
||||
|
||||
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
|
||||
QWidget *parent, std::function<QString()> updateFunc)
|
||||
: QLabel(parent), _updateFunc(updateFunc), _timer(new QTimer(this))
|
||||
: QLabel(parent),
|
||||
_updateFunc(updateFunc),
|
||||
_timer(new QTimer(this))
|
||||
{
|
||||
connect(_timer, &QTimer::timeout, this,
|
||||
&AutoUpdateTooltipLabel::UpdateTooltip);
|
||||
|
||||
@@ -90,15 +90,17 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
|
||||
}
|
||||
|
||||
VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent)
|
||||
: QSlider(parent)
|
||||
: DoubleSlider(parent)
|
||||
{
|
||||
fad = fader;
|
||||
}
|
||||
|
||||
VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
|
||||
QWidget *parent)
|
||||
: QSlider(orientation, parent)
|
||||
: DoubleSlider(parent)
|
||||
{
|
||||
SetDoubleConstraints(0, 100, 0.01, 0);
|
||||
setOrientation(orientation);
|
||||
fad = fader;
|
||||
}
|
||||
|
||||
@@ -219,7 +221,6 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
||||
nameLabel->setText(sourceName);
|
||||
|
||||
slider->setMinimum(0);
|
||||
slider->setMaximum(int(FADER_PRECISION));
|
||||
|
||||
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
|
||||
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
|
||||
@@ -592,7 +593,9 @@ void VolumeMeter::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter,
|
||||
bool vertical)
|
||||
: QWidget(parent), obs_volmeter(obs_volmeter), vertical(vertical)
|
||||
: QWidget(parent),
|
||||
obs_volmeter(obs_volmeter),
|
||||
vertical(vertical)
|
||||
{
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, true);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "double-slider.hpp"
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
@@ -284,7 +285,7 @@ private:
|
||||
QLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
QSlider *slider;
|
||||
DoubleSlider *slider;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
@@ -327,10 +328,10 @@ public:
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
QSlider *GetSlider() const { return slider; }
|
||||
DoubleSlider *GetSlider() const { return slider; }
|
||||
};
|
||||
|
||||
class VolumeSlider : public QSlider {
|
||||
class VolumeSlider : public DoubleSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
#include <QTableWidgetItem>
|
||||
@@ -12,6 +13,7 @@ static void setVariableTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
obs_module_text("AdvSceneSwitcher.variableTab.title")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#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);
|
||||
@@ -23,9 +25,10 @@ static void setVariableTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
}
|
||||
}
|
||||
|
||||
static QString getSaveActionString(Variable *variable)
|
||||
static QString formatSaveActionText(Variable *variable)
|
||||
{
|
||||
QString saveAction;
|
||||
|
||||
switch (variable->GetSaveAction()) {
|
||||
case Variable::SaveAction::DONT_SAVE:
|
||||
saveAction = obs_module_text(
|
||||
@@ -38,26 +41,54 @@ static QString getSaveActionString(Variable *variable)
|
||||
case Variable::SaveAction::SET_DEFAULT:
|
||||
saveAction =
|
||||
QString(obs_module_text(
|
||||
"AdvSceneSwitcher.variable.save.default")) +
|
||||
" \"" +
|
||||
QString::fromStdString(variable->GetDefaultValue()) +
|
||||
"\"";
|
||||
"AdvSceneSwitcher.variableTab.saveLoadBehavior.text.default"))
|
||||
.arg(QString::fromStdString(
|
||||
variable->GetDefaultValue()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return saveAction;
|
||||
}
|
||||
|
||||
static QString getLastUsedString(Variable *variable)
|
||||
static QString formatLastUsedText(Variable *variable)
|
||||
{
|
||||
auto lastUsed = variable->SecondsSinceLastUse();
|
||||
auto lastUsed = variable->GetSecondsSinceLastUse();
|
||||
if (!lastUsed) {
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.neverNused");
|
||||
"AdvSceneSwitcher.variableTab.lastUsed.text.never");
|
||||
}
|
||||
QString fmt = obs_module_text("AdvSceneSwitcher.variableTab.lastUsed");
|
||||
return fmt.arg(QString::number(*lastUsed));
|
||||
|
||||
QString text =
|
||||
obs_module_text("AdvSceneSwitcher.variableTab.lastUsed.text");
|
||||
return text.arg(QString::number(*lastUsed));
|
||||
}
|
||||
|
||||
static QString formatLastChangedText(Variable *variable)
|
||||
{
|
||||
auto lastChanged = variable->GetSecondsSinceLastChange();
|
||||
if (!lastChanged) {
|
||||
return obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastChanged.text.none");
|
||||
}
|
||||
|
||||
QString text = obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastChanged.text");
|
||||
return text.arg(QString::number(*lastChanged));
|
||||
}
|
||||
|
||||
static QString formatLastChangedTooltip(Variable *variable)
|
||||
{
|
||||
auto lastChanged = variable->GetSecondsSinceLastChange();
|
||||
if (!lastChanged) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString tooltip = obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastChanged.tooltip");
|
||||
return tooltip.arg(QString::number(variable->GetValueChangeCount()))
|
||||
.arg(QString::fromStdString(variable->GetPreviousValue()));
|
||||
}
|
||||
|
||||
static void addVariableRow(QTableWidget *table, Variable *variable)
|
||||
@@ -75,16 +106,18 @@ static void addVariableRow(QTableWidget *table, Variable *variable)
|
||||
auto *item =
|
||||
new QTableWidgetItem(QString::fromStdString(variable->Name()));
|
||||
table->setItem(row, col, item);
|
||||
col++;
|
||||
item = new QTableWidgetItem(
|
||||
QString::fromStdString(variable->Value(false)));
|
||||
table->setItem(row, col, item);
|
||||
col++;
|
||||
item = new QTableWidgetItem(getSaveActionString(variable));
|
||||
table->setItem(row, col, item);
|
||||
col++;
|
||||
item = new QTableWidgetItem(getLastUsedString(variable));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -100,24 +133,31 @@ static void removeVariableRow(QTableWidget *table, const QString &name)
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
static void updateVaribleStatus(QTableWidget *table)
|
||||
static void updateVariableStatus(QTableWidget *table)
|
||||
{
|
||||
for (int row = 0; row < table->rowCount(); row++) {
|
||||
auto item = table->item(row, 0);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto weakVariable = GetWeakVariableByQString(item->text());
|
||||
auto variable = weakVariable.lock();
|
||||
if (!variable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item = table->item(row, 1);
|
||||
item->setText(QString::fromStdString(variable->Value(false)));
|
||||
auto varValue = QString::fromStdString(variable->Value(false));
|
||||
item->setText(varValue);
|
||||
item->setToolTip(varValue);
|
||||
item = table->item(row, 2);
|
||||
item->setText(getSaveActionString(variable.get()));
|
||||
item->setText(formatSaveActionText(variable.get()));
|
||||
item = table->item(row, 3);
|
||||
item->setText(getLastUsedString(variable.get()));
|
||||
item->setText(formatLastUsedText(variable.get()));
|
||||
item = table->item(row, 4);
|
||||
item->setText(formatLastChangedText(variable.get()));
|
||||
item->setToolTip(formatLastChangedTooltip(variable.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +169,7 @@ static void renameVariable(QTableWidget *table, const QString &oldName,
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->text() == oldName) {
|
||||
item->setText(newName);
|
||||
table->sortByColumn(0, Qt::AscendingOrder);
|
||||
@@ -140,30 +181,10 @@ static void renameVariable(QTableWidget *table, const QString &oldName,
|
||||
assert(false);
|
||||
}
|
||||
|
||||
static void openSettingsDialogAtRow(QTableWidget *table, int row)
|
||||
{
|
||||
auto item = table->item(row, 0);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
auto weakVariable = GetWeakVariableByQString(item->text());
|
||||
auto variable = weakVariable.lock();
|
||||
if (!variable) {
|
||||
return;
|
||||
}
|
||||
auto oldName = variable->Name();
|
||||
|
||||
bool accepted =
|
||||
VariableSettingsDialog::AskForSettings(table, *variable.get());
|
||||
if (accepted && oldName != variable->Name()) {
|
||||
VariableSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
QString::fromStdString(variable->Name()));
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetupVariableTab()
|
||||
{
|
||||
ui->variables->installEventFilter(this);
|
||||
|
||||
if (GetVariables().empty()) {
|
||||
setVariableTabVisible(ui->tabWidget, false);
|
||||
} else {
|
||||
@@ -172,12 +193,14 @@ void AdvSceneSwitcher::SetupVariableTab()
|
||||
|
||||
static const QStringList horizontalHeaders =
|
||||
QStringList()
|
||||
<< obs_module_text("AdvSceneSwitcher.variableTab.header.name")
|
||||
<< obs_module_text("AdvSceneSwitcher.variableTab.header.value")
|
||||
<< obs_module_text("AdvSceneSwitcher.variableTab.name.header")
|
||||
<< obs_module_text("AdvSceneSwitcher.variableTab.value.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.header.saveLoadBehavior")
|
||||
"AdvSceneSwitcher.variableTab.saveLoadBehavior.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.header.lastUse");
|
||||
"AdvSceneSwitcher.variableTab.lastUsed.header")
|
||||
<< obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.lastChanged.header");
|
||||
|
||||
auto &variables = GetVariables();
|
||||
|
||||
@@ -216,66 +239,47 @@ void AdvSceneSwitcher::SetupVariableTab()
|
||||
ui->variablesHelp->show();
|
||||
}
|
||||
});
|
||||
QWidget::connect(ui->variables, &QTableWidget::cellDoubleClicked,
|
||||
[this](int row, int _) {
|
||||
openSettingsDialogAtRow(ui->variables, row);
|
||||
});
|
||||
QWidget::connect(ui->variables, &QTableWidget::cellDoubleClicked, this,
|
||||
&AdvSceneSwitcher::OpenSettingsForSelectedVariable);
|
||||
|
||||
auto timer = new QTimer(this);
|
||||
timer->setInterval(1000);
|
||||
QWidget::connect(timer, &QTimer::timeout,
|
||||
[this]() { updateVaribleStatus(ui->variables); });
|
||||
[this]() { updateVariableStatus(ui->variables); });
|
||||
timer->start();
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_variableAdd_clicked()
|
||||
void AdvSceneSwitcher::OpenSettingsForSelectedVariable()
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_variableRemove_clicked()
|
||||
{
|
||||
auto selectedItems = ui->variables->selectedItems();
|
||||
QList<int> selectedRows;
|
||||
for (auto item : selectedItems) {
|
||||
int row = item->row();
|
||||
if (!selectedRows.contains(row)) {
|
||||
selectedRows.append(row);
|
||||
}
|
||||
}
|
||||
|
||||
auto selectedRows = ui->variables->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList names;
|
||||
for (int row : selectedRows) {
|
||||
auto item = ui->variables->item(row, 0);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
names << item->text();
|
||||
auto cell = ui->variables->item(selectedRows.last().row(), 0);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &name : names) {
|
||||
VariableSignalManager::Instance()->Remove(name);
|
||||
auto weakVariable = GetWeakVariableByQString(cell->text());
|
||||
auto variable = weakVariable.lock();
|
||||
if (!variable) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
for (const auto &name : names) {
|
||||
auto oldName = variable->Name();
|
||||
bool accepted = VariableSettingsDialog::AskForSettings(ui->variables,
|
||||
*variable.get());
|
||||
if (accepted && oldName != variable->Name()) {
|
||||
VariableSignalManager::Instance()->Rename(
|
||||
QString::fromStdString(oldName),
|
||||
QString::fromStdString(variable->Name()));
|
||||
}
|
||||
}
|
||||
|
||||
static void removeVariablesWithNames(const QStringList &varNames)
|
||||
{
|
||||
for (const auto &name : varNames) {
|
||||
auto variable = GetVariableByQString(name);
|
||||
if (!variable) {
|
||||
continue;
|
||||
@@ -292,4 +296,70 @@ void AdvSceneSwitcher::on_variableRemove_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveSelectedVariables()
|
||||
{
|
||||
auto selectedRows = ui->variables->selectionModel()->selectedRows();
|
||||
if (selectedRows.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList varNames;
|
||||
for (auto row : selectedRows) {
|
||||
auto cell = ui->variables->item(row.row(), 0);
|
||||
if (!cell) {
|
||||
continue;
|
||||
}
|
||||
|
||||
varNames << cell->text();
|
||||
}
|
||||
|
||||
int varNameCount = varNames.size();
|
||||
if (varNameCount == 1) {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.removeSingleVariablePopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(varNames.at(0)), true)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
QString deleteWarning = obs_module_text(
|
||||
"AdvSceneSwitcher.variableTab.removeMultipleVariablesPopup.text");
|
||||
if (!DisplayMessage(deleteWarning.arg(varNameCount), true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
removeVariablesWithNames(varNames);
|
||||
}
|
||||
|
||||
for (const auto &name : varNames) {
|
||||
VariableSignalManager::Instance()->Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_variableAdd_clicked()
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_variableRemove_clicked()
|
||||
{
|
||||
RemoveSelectedVariables();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -13,11 +13,6 @@ static std::deque<std::shared_ptr<Item>> variables;
|
||||
// when resolving strings containing variables, etc.
|
||||
static std::chrono::high_resolution_clock::time_point lastVariableChange{};
|
||||
|
||||
std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime()
|
||||
{
|
||||
return lastVariableChange;
|
||||
}
|
||||
|
||||
Variable::Variable() : Item()
|
||||
{
|
||||
lastVariableChange = std::chrono::high_resolution_clock::now();
|
||||
@@ -34,11 +29,13 @@ void Variable::Load(obs_data_t *obj)
|
||||
_saveAction =
|
||||
static_cast<SaveAction>(obs_data_get_int(obj, "saveAction"));
|
||||
_defaultValue = obs_data_get_string(obj, "defaultValue");
|
||||
|
||||
if (_saveAction == SaveAction::SAVE) {
|
||||
_value = obs_data_get_string(obj, "value");
|
||||
SetValue(obs_data_get_string(obj, "value"));
|
||||
} else if (_saveAction == SaveAction::SET_DEFAULT) {
|
||||
_value = _defaultValue;
|
||||
SetValue(_defaultValue);
|
||||
}
|
||||
|
||||
lastVariableChange = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
@@ -46,9 +43,11 @@ void Variable::Save(obs_data_t *obj) const
|
||||
{
|
||||
Item::Save(obj);
|
||||
obs_data_set_int(obj, "saveAction", static_cast<int>(_saveAction));
|
||||
|
||||
if (_saveAction == SaveAction::SAVE) {
|
||||
obs_data_set_string(obj, "value", _value.c_str());
|
||||
}
|
||||
|
||||
obs_data_set_string(obj, "defaultValue", _defaultValue.c_str());
|
||||
}
|
||||
|
||||
@@ -57,6 +56,7 @@ std::string Variable::Value(bool updateLastUsed) const
|
||||
if (updateLastUsed) {
|
||||
UpdateLastUsed();
|
||||
}
|
||||
|
||||
return _value;
|
||||
}
|
||||
|
||||
@@ -72,128 +72,61 @@ std::optional<int> Variable::IntValue() const
|
||||
|
||||
void Variable::SetValue(const std::string &value)
|
||||
{
|
||||
_previousValue = _value;
|
||||
_value = value;
|
||||
|
||||
UpdateLastUsed();
|
||||
UpdateLastChanged();
|
||||
lastVariableChange = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void Variable::SetValue(double value)
|
||||
{
|
||||
_value = ToString(value);
|
||||
|
||||
UpdateLastUsed();
|
||||
lastVariableChange = std::chrono::high_resolution_clock::now();
|
||||
SetValue(ToString(value));
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Variable::SecondsSinceLastUse() const
|
||||
std::optional<uint64_t> Variable::GetSecondsSinceLastUse() const
|
||||
{
|
||||
if (_lastUsed.time_since_epoch().count() == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(now - _lastUsed)
|
||||
.count();
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Variable::GetSecondsSinceLastChange() const
|
||||
{
|
||||
if (_lastChanged.time_since_epoch().count() == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(now -
|
||||
_lastChanged)
|
||||
.count();
|
||||
}
|
||||
|
||||
void Variable::UpdateLastUsed() const
|
||||
{
|
||||
_lastUsed = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<Item>> &GetVariables()
|
||||
void Variable::UpdateLastChanged()
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
|
||||
Variable *GetVariableByName(const std::string &name)
|
||||
{
|
||||
for (const auto &v : variables) {
|
||||
if (v->Name() == name) {
|
||||
return dynamic_cast<Variable *>(v.get());
|
||||
}
|
||||
if (_previousValue != _value) {
|
||||
_lastChanged = std::chrono::high_resolution_clock::now();
|
||||
++_valueChangeCount;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Variable *GetVariableByQString(const QString &name)
|
||||
{
|
||||
return GetVariableByName(name.toStdString());
|
||||
}
|
||||
|
||||
std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name)
|
||||
{
|
||||
for (const auto &v : variables) {
|
||||
if (v->Name() == name) {
|
||||
std::weak_ptr<Variable> wp =
|
||||
std::dynamic_pointer_cast<Variable>(v);
|
||||
return wp;
|
||||
}
|
||||
}
|
||||
return std::weak_ptr<Variable>();
|
||||
}
|
||||
|
||||
std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name)
|
||||
{
|
||||
return GetWeakVariableByName(name.toStdString());
|
||||
}
|
||||
|
||||
QStringList GetVariablesNameList()
|
||||
{
|
||||
QStringList list;
|
||||
for (const auto &var : variables) {
|
||||
list << QString::fromStdString(var->Name());
|
||||
}
|
||||
list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
std::string GetWeakVariableName(std::weak_ptr<Variable> var_)
|
||||
{
|
||||
auto var = var_.lock();
|
||||
if (!var) {
|
||||
return obs_module_text("AdvSceneSwitcher.variable.invalid");
|
||||
}
|
||||
return var->Name();
|
||||
}
|
||||
|
||||
void SaveVariables(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *variablesArray = obs_data_array_create();
|
||||
for (const auto &v : variables) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
v->Save(array_obj);
|
||||
obs_data_array_push_back(variablesArray, array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_set_array(obj, "variables", variablesArray);
|
||||
obs_data_array_release(variablesArray);
|
||||
}
|
||||
|
||||
void LoadVariables(obs_data_t *obj)
|
||||
{
|
||||
variables.clear();
|
||||
|
||||
obs_data_array_t *variablesArray = obs_data_get_array(obj, "variables");
|
||||
size_t count = obs_data_array_count(variablesArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(variablesArray, i);
|
||||
auto var = Variable::Create();
|
||||
variables.emplace_back(var);
|
||||
variables.back()->Load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(variablesArray);
|
||||
}
|
||||
|
||||
static void populateSaveActionSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.variable.save.dontSave"));
|
||||
list->addItem(obs_module_text("AdvSceneSwitcher.variable.save.save"));
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.variable.save.default"));
|
||||
list->addItems(
|
||||
{obs_module_text("AdvSceneSwitcher.variable.save.dontSave"),
|
||||
obs_module_text("AdvSceneSwitcher.variable.save.save"),
|
||||
obs_module_text("AdvSceneSwitcher.variable.save.default")});
|
||||
}
|
||||
|
||||
VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
|
||||
@@ -220,6 +153,7 @@ VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.variable.name")),
|
||||
row, 0);
|
||||
|
||||
QHBoxLayout *nameLayout = new QHBoxLayout;
|
||||
nameLayout->addWidget(_name);
|
||||
nameLayout->addWidget(_nameHint);
|
||||
@@ -233,13 +167,14 @@ VariableSettingsDialog::VariableSettingsDialog(QWidget *parent,
|
||||
layout->addWidget(
|
||||
new QLabel(obs_module_text("AdvSceneSwitcher.variable.save")),
|
||||
row, 0);
|
||||
|
||||
auto saveLayout = new QVBoxLayout;
|
||||
saveLayout->addWidget(_save);
|
||||
saveLayout->addWidget(_defaultValue);
|
||||
saveLayout->addStretch();
|
||||
|
||||
layout->addLayout(saveLayout, row, 1);
|
||||
++row;
|
||||
layout->addWidget(_buttonbox, row, 0, 1, -1);
|
||||
layout->addWidget(_buttonbox, ++row, 0, 1, -1);
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(layout);
|
||||
}
|
||||
@@ -262,12 +197,13 @@ bool VariableSettingsDialog::AskForSettings(QWidget *parent, Variable &settings)
|
||||
}
|
||||
|
||||
settings._name = dialog._name->text().toStdString();
|
||||
settings._value = dialog._value->toPlainText().toStdString();
|
||||
settings.SetValue(dialog._value->toPlainText().toStdString());
|
||||
settings._defaultValue =
|
||||
dialog._defaultValue->toPlainText().toStdString();
|
||||
settings._saveAction =
|
||||
static_cast<Variable::SaveAction>(dialog._save->currentIndex());
|
||||
lastVariableChange = std::chrono::high_resolution_clock::now();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -277,6 +213,7 @@ static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
|
||||
if (VariableSettingsDialog::AskForSettings(parent, VariableSettings)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -340,4 +277,128 @@ VariableSignalManager *VariableSignalManager::Instance()
|
||||
return &manager;
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<Item>> &GetVariables()
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
|
||||
Variable *GetVariableByName(const std::string &name)
|
||||
{
|
||||
for (const auto &v : variables) {
|
||||
if (v->Name() == name) {
|
||||
return dynamic_cast<Variable *>(v.get());
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Variable *GetVariableByQString(const QString &name)
|
||||
{
|
||||
return GetVariableByName(name.toStdString());
|
||||
}
|
||||
|
||||
std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name)
|
||||
{
|
||||
for (const auto &v : variables) {
|
||||
if (v->Name() == name) {
|
||||
std::weak_ptr<Variable> wp =
|
||||
std::dynamic_pointer_cast<Variable>(v);
|
||||
return wp;
|
||||
}
|
||||
}
|
||||
|
||||
return std::weak_ptr<Variable>();
|
||||
}
|
||||
|
||||
std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name)
|
||||
{
|
||||
return GetWeakVariableByName(name.toStdString());
|
||||
}
|
||||
|
||||
QStringList GetVariablesNameList()
|
||||
{
|
||||
QStringList list;
|
||||
|
||||
for (const auto &var : variables) {
|
||||
list << QString::fromStdString(var->Name());
|
||||
}
|
||||
|
||||
list.sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
std::string GetWeakVariableName(std::weak_ptr<Variable> var_)
|
||||
{
|
||||
auto var = var_.lock();
|
||||
if (!var) {
|
||||
return obs_module_text("AdvSceneSwitcher.variable.invalid");
|
||||
}
|
||||
|
||||
return var->Name();
|
||||
}
|
||||
|
||||
static bool variableWithNameExists(const std::string &name)
|
||||
{
|
||||
return !!GetVariableByName(name);
|
||||
}
|
||||
|
||||
void SaveVariables(obs_data_t *obj)
|
||||
{
|
||||
obs_data_array_t *variablesArray = obs_data_array_create();
|
||||
for (const auto &v : variables) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
v->Save(array_obj);
|
||||
obs_data_array_push_back(variablesArray, array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
|
||||
obs_data_set_array(obj, "variables", variablesArray);
|
||||
obs_data_array_release(variablesArray);
|
||||
}
|
||||
|
||||
void LoadVariables(obs_data_t *obj)
|
||||
{
|
||||
variables.clear();
|
||||
|
||||
obs_data_array_t *variablesArray = obs_data_get_array(obj, "variables");
|
||||
size_t count = obs_data_array_count(variablesArray);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(variablesArray, i);
|
||||
auto var = Variable::Create();
|
||||
variables.emplace_back(var);
|
||||
variables.back()->Load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
|
||||
obs_data_array_release(variablesArray);
|
||||
}
|
||||
|
||||
void ImportVariables(obs_data_t *data)
|
||||
{
|
||||
obs_data_array_t *array = obs_data_get_array(data, "variables");
|
||||
size_t count = obs_data_array_count(array);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *arrayElement = obs_data_array_item(array, i);
|
||||
auto var = Variable::Create();
|
||||
var->Load(arrayElement);
|
||||
obs_data_release(arrayElement);
|
||||
|
||||
if (variableWithNameExists(var->Name())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GetVariables().emplace_back(var);
|
||||
}
|
||||
|
||||
obs_data_array_release(array);
|
||||
}
|
||||
|
||||
std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime()
|
||||
{
|
||||
return lastVariableChange;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -17,14 +17,6 @@ class Variable : public Item {
|
||||
public:
|
||||
Variable();
|
||||
~Variable();
|
||||
void Load(obs_data_t *obj);
|
||||
void Save(obs_data_t *obj) const;
|
||||
EXPORT std::string Value(bool updateLastUsed = true) const;
|
||||
EXPORT std::optional<double> DoubleValue() const;
|
||||
EXPORT std::optional<int> IntValue() const;
|
||||
void SetValue(const std::string &value);
|
||||
void SetValue(double value);
|
||||
std::string GetDefaultValue() const { return _defaultValue; }
|
||||
static std::shared_ptr<Item> Create()
|
||||
{
|
||||
return std::make_shared<Variable>();
|
||||
@@ -35,15 +27,31 @@ public:
|
||||
SAVE,
|
||||
SET_DEFAULT,
|
||||
};
|
||||
|
||||
void Load(obs_data_t *obj);
|
||||
void Save(obs_data_t *obj) const;
|
||||
EXPORT std::string Value(bool updateLastUsed = true) const;
|
||||
EXPORT std::optional<double> DoubleValue() const;
|
||||
EXPORT std::optional<int> IntValue() const;
|
||||
std::string GetPreviousValue() const { return _previousValue; };
|
||||
std::string GetDefaultValue() const { return _defaultValue; }
|
||||
void SetValue(const std::string &value);
|
||||
void SetValue(double value);
|
||||
SaveAction GetSaveAction() const { return _saveAction; }
|
||||
std::optional<uint64_t> SecondsSinceLastUse() const;
|
||||
int GetValueChangeCount() const { return _valueChangeCount; }
|
||||
std::optional<uint64_t> GetSecondsSinceLastUse() const;
|
||||
std::optional<uint64_t> GetSecondsSinceLastChange() const;
|
||||
void UpdateLastUsed() const;
|
||||
void UpdateLastChanged();
|
||||
|
||||
private:
|
||||
SaveAction _saveAction = SaveAction::DONT_SAVE;
|
||||
std::string _value = "";
|
||||
std::string _previousValue = "";
|
||||
std::string _defaultValue = "";
|
||||
int _valueChangeCount = 0;
|
||||
mutable std::chrono::high_resolution_clock::time_point _lastUsed;
|
||||
mutable std::chrono::high_resolution_clock::time_point _lastChanged;
|
||||
|
||||
friend VariableSelection;
|
||||
friend VariableSettingsDialog;
|
||||
@@ -86,11 +94,7 @@ signals:
|
||||
void Remove(const QString &);
|
||||
};
|
||||
|
||||
void SaveVariables(obs_data_t *obj);
|
||||
void LoadVariables(obs_data_t *obj);
|
||||
|
||||
std::deque<std::shared_ptr<Item>> &GetVariables();
|
||||
|
||||
EXPORT Variable *GetVariableByName(const std::string &name);
|
||||
EXPORT Variable *GetVariableByQString(const QString &name);
|
||||
EXPORT std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name);
|
||||
@@ -98,6 +102,10 @@ EXPORT std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name);
|
||||
std::string GetWeakVariableName(std::weak_ptr<Variable>);
|
||||
EXPORT QStringList GetVariablesNameList();
|
||||
|
||||
void SaveVariables(obs_data_t *obj);
|
||||
void LoadVariables(obs_data_t *obj);
|
||||
void ImportVariables(obs_data_t *obj);
|
||||
|
||||
std::chrono::high_resolution_clock::time_point GetLastVariableChangeTime();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -75,6 +75,8 @@ target_sources(
|
||||
macro-action-window.hpp
|
||||
macro-condition-audio.cpp
|
||||
macro-condition-audio.hpp
|
||||
macro-condition-clipboard.cpp
|
||||
macro-condition-clipboard.hpp
|
||||
macro-condition-cursor.cpp
|
||||
macro-condition-cursor.hpp
|
||||
macro-condition-date.cpp
|
||||
@@ -158,6 +160,8 @@ target_sources(
|
||||
utils/scene-item-selection.hpp
|
||||
utils/scene-item-transform-helpers.cpp
|
||||
utils/scene-item-transform-helpers.hpp
|
||||
utils/source-properties-button.cpp
|
||||
utils/source-properties-button.hpp
|
||||
utils/source-settings-helpers.cpp
|
||||
utils/source-settings-helpers.hpp
|
||||
utils/source-setting.cpp
|
||||
|
||||
@@ -29,6 +29,10 @@ static const std::map<MacroActionAudio::Action, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.audio.type.monitor"},
|
||||
{MacroActionAudio::Action::BALANCE,
|
||||
"AdvSceneSwitcher.action.audio.type.balance"},
|
||||
{MacroActionAudio::Action::ENABLE_ON_TRACK,
|
||||
"AdvSceneSwitcher.action.audio.type.enableOnTrack"},
|
||||
{MacroActionAudio::Action::DISABLE_ON_TRACK,
|
||||
"AdvSceneSwitcher.action.audio.type.disableOnTrack"},
|
||||
};
|
||||
|
||||
static const std::map<MacroActionAudio::FadeType, std::string> fadeTypes = {
|
||||
@@ -205,6 +209,28 @@ void MacroActionAudio::StartFade() const
|
||||
}
|
||||
}
|
||||
|
||||
static void setMixerEnable(obs_source_t *source, const int mixerIdx,
|
||||
const bool enable)
|
||||
{
|
||||
if (mixerIdx < 0) {
|
||||
blog(LOG_INFO, "refusing to %s mixer id %d for %s",
|
||||
enable ? "enable" : "disable", mixerIdx,
|
||||
obs_source_get_name(source));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t mixers = obs_source_get_audio_mixers(source);
|
||||
uint32_t new_mixers = mixers;
|
||||
|
||||
if (enable) {
|
||||
new_mixers |= (1 << mixerIdx);
|
||||
} else {
|
||||
new_mixers &= ~(1 << mixerIdx);
|
||||
}
|
||||
|
||||
obs_source_set_audio_mixers(source, new_mixers);
|
||||
}
|
||||
|
||||
bool MacroActionAudio::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_audioSource.GetSource());
|
||||
@@ -232,6 +258,12 @@ bool MacroActionAudio::PerformAction()
|
||||
case Action::BALANCE:
|
||||
obs_source_set_balance_value(s, _balance);
|
||||
break;
|
||||
case Action::ENABLE_ON_TRACK:
|
||||
setMixerEnable(s, _track - 1, true);
|
||||
break;
|
||||
case Action::DISABLE_ON_TRACK:
|
||||
setMixerEnable(s, _track - 1, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -263,6 +295,7 @@ bool MacroActionAudio::Save(obs_data_t *obj) const
|
||||
obs_data_set_int(obj, "monitor", _monitorType);
|
||||
_syncOffset.Save(obj, "syncOffset");
|
||||
_balance.Save(obj, "balance");
|
||||
_track.Save(obj, "track");
|
||||
_volume.Save(obj, "volume");
|
||||
_rate.Save(obj, "rate");
|
||||
obs_data_set_bool(obj, "fade", _fade);
|
||||
@@ -271,7 +304,7 @@ bool MacroActionAudio::Save(obs_data_t *obj) const
|
||||
obs_data_set_bool(obj, "abortActiveFade", _abortActiveFade);
|
||||
obs_data_set_bool(obj, "useDb", _useDb);
|
||||
_volumeDB.Save(obj, "volumeDB");
|
||||
obs_data_set_int(obj, "version", 2);
|
||||
obs_data_set_int(obj, "version", 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -312,13 +345,18 @@ bool MacroActionAudio::Load(obs_data_t *obj)
|
||||
_abortActiveFade = false;
|
||||
}
|
||||
|
||||
if (obs_data_get_int(obj, "version") != 2) {
|
||||
if (obs_data_get_int(obj, "version") < 2) {
|
||||
_useDb = false;
|
||||
_volumeDB = 0.0;
|
||||
} else {
|
||||
_useDb = obs_data_get_bool(obj, "useDb");
|
||||
_volumeDB.Load(obj, "volumeDB");
|
||||
}
|
||||
if (obs_data_get_int(obj, "version") < 3) {
|
||||
_track = 1;
|
||||
} else {
|
||||
_track.Load(obj, "track");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -381,6 +419,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
0., 1., "",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.audio.balance.description"))),
|
||||
_track(new VariableSpinBox),
|
||||
_volumePercent(new VariableSpinBox),
|
||||
_volumeDB(new VariableDoubleSpinBox),
|
||||
_percentDBToggle(new QPushButton),
|
||||
@@ -398,6 +437,9 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
_syncOffset->setMaximum(20000);
|
||||
_syncOffset->setSuffix("ms");
|
||||
|
||||
_track->setMinimum(1);
|
||||
_track->setMaximum(32);
|
||||
|
||||
_volumePercent->setMinimum(0);
|
||||
_volumePercent->setMaximum(2000);
|
||||
_volumePercent->setSuffix("%");
|
||||
@@ -433,6 +475,10 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
this, SLOT(BalanceChanged(const NumberVariable<double> &)));
|
||||
QWidget::connect(_monitorTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(MonitorTypeChanged(int)));
|
||||
QWidget::connect(
|
||||
_track,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(TrackChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(
|
||||
_volumePercent,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
@@ -464,6 +510,7 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
{"{{syncOffset}}", _syncOffset},
|
||||
{"{{monitorTypes}}", _monitorTypes},
|
||||
{"{{balance}}", _balance},
|
||||
{"{{track}}", _track},
|
||||
{"{{volume}}", _volumePercent},
|
||||
{"{{volumeDB}}", _volumeDB},
|
||||
{"{{percentDBToggle}}", _percentDBToggle},
|
||||
@@ -519,6 +566,10 @@ void MacroActionAudioEdit::SetWidgetVisibility()
|
||||
MacroActionAudio::Action::MONITOR);
|
||||
_balance->setVisible(_entryData->_action ==
|
||||
MacroActionAudio::Action::BALANCE);
|
||||
_track->setVisible(_entryData->_action ==
|
||||
MacroActionAudio::Action::ENABLE_ON_TRACK ||
|
||||
_entryData->_action ==
|
||||
MacroActionAudio::Action::DISABLE_ON_TRACK);
|
||||
|
||||
_fadeTypeLayout->removeWidget(_fade);
|
||||
_fadeTypeLayout->removeWidget(_fadeTypes);
|
||||
@@ -583,6 +634,7 @@ void MacroActionAudioEdit::UpdateEntryData()
|
||||
_syncOffset->SetValue(_entryData->_syncOffset);
|
||||
_monitorTypes->setCurrentIndex(_entryData->_monitorType);
|
||||
_balance->SetDoubleValue(_entryData->_balance);
|
||||
_track->SetValue(_entryData->_track);
|
||||
_volumePercent->SetValue(_entryData->_volume);
|
||||
_volumeDB->SetValue(_entryData->_volumeDB);
|
||||
_fade->setChecked(_entryData->_fade);
|
||||
@@ -648,6 +700,16 @@ void MacroActionAudioEdit::BalanceChanged(const NumberVariable<double> &value)
|
||||
_entryData->_balance = value;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::TrackChanged(const NumberVariable<int> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_track = value;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::VolumeDBChanged(const NumberVariable<double> &value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
SYNC_OFFSET,
|
||||
MONITOR,
|
||||
BALANCE,
|
||||
ENABLE_ON_TRACK,
|
||||
DISABLE_ON_TRACK,
|
||||
};
|
||||
|
||||
enum class FadeType {
|
||||
@@ -46,6 +48,7 @@ public:
|
||||
IntVariable _syncOffset = 0;
|
||||
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
|
||||
DoubleVariable _balance = 0.5;
|
||||
IntVariable _track = 1;
|
||||
bool _useDb = false;
|
||||
IntVariable _volume = 0;
|
||||
DoubleVariable _volumeDB = 0.0;
|
||||
@@ -91,6 +94,7 @@ private slots:
|
||||
void SyncOffsetChanged(const NumberVariable<int> &value);
|
||||
void MonitorTypeChanged(int value);
|
||||
void BalanceChanged(const NumberVariable<double> &value);
|
||||
void TrackChanged(const NumberVariable<int> &value);
|
||||
void VolumeChanged(const NumberVariable<int> &value);
|
||||
void VolumeDBChanged(const NumberVariable<double> &value);
|
||||
void PercentDBClicked();
|
||||
@@ -110,6 +114,7 @@ protected:
|
||||
VariableSpinBox *_syncOffset;
|
||||
QComboBox *_monitorTypes;
|
||||
SliderSpinBox *_balance;
|
||||
VariableSpinBox *_track;
|
||||
VariableSpinBox *_volumePercent;
|
||||
VariableDoubleSpinBox *_volumeDB;
|
||||
QPushButton *_percentDBToggle;
|
||||
|
||||
@@ -22,6 +22,8 @@ const static std::map<MacroActionFilter::Action, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.filter.type.toggle"},
|
||||
{MacroActionFilter::Action::SETTINGS,
|
||||
"AdvSceneSwitcher.action.filter.type.settings"},
|
||||
{MacroActionFilter::Action::SETTINGS_BUTTON,
|
||||
"AdvSceneSwitcher.action.filter.type.pressSettingsButton"},
|
||||
};
|
||||
|
||||
const static std::map<MacroActionFilter::SettingsInputMethod, std::string>
|
||||
@@ -39,7 +41,7 @@ static void performActionHelper(
|
||||
MacroActionFilter::SettingsInputMethod settingsInputMethod,
|
||||
const SourceSetting &setting, const std::string &manualSettingValue,
|
||||
const TempVariableRef &tempVar, Macro *macro,
|
||||
const StringVariable &settingsString)
|
||||
const StringVariable &settingsString, const SourceSettingButton &button)
|
||||
{
|
||||
OBSSourceAutoRelease source = obs_weak_source_get_source(filter);
|
||||
switch (action) {
|
||||
@@ -74,6 +76,9 @@ static void performActionHelper(
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MacroActionFilter::Action::SETTINGS_BUTTON:
|
||||
PressSourceButton(button, source);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -85,7 +90,7 @@ bool MacroActionFilter::PerformAction()
|
||||
for (const auto &filter : filters) {
|
||||
performActionHelper(_action, filter, _settingsInputMethod,
|
||||
_setting, _manualSettingValue, _tempVar,
|
||||
GetMacro(), _settingsString);
|
||||
GetMacro(), _settingsString, _button);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -116,6 +121,7 @@ bool MacroActionFilter::Save(obs_data_t *obj) const
|
||||
_manualSettingValue.Save(obj, "manualSettingValue");
|
||||
_tempVar.Save(obj);
|
||||
_settingsString.Save(obj, "settings");
|
||||
_button.Save(obj);
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
@@ -147,6 +153,7 @@ bool MacroActionFilter::Load(obs_data_t *obj)
|
||||
_settingsString.Load(obj, "settings");
|
||||
_manualSettingValue.Load(obj, "manualSettingValue");
|
||||
_tempVar.Load(obj, GetMacro());
|
||||
_button.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -207,7 +214,8 @@ MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
_filterSettings(new SourceSettingSelection(this)),
|
||||
_settingsString(new VariableTextEdit(this)),
|
||||
_refreshSettingSelection(new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.action.filter.refresh")))
|
||||
obs_module_text("AdvSceneSwitcher.action.filter.refresh"))),
|
||||
_settingsButtons(new QComboBox())
|
||||
{
|
||||
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
@@ -245,10 +253,10 @@ MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
SLOT(SelectionChanged(const SourceSetting &)));
|
||||
QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this,
|
||||
SLOT(RefreshVariableSourceSelectionValue()));
|
||||
QWidget::connect(_settingsButtons, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ButtonChanged(int)));
|
||||
|
||||
auto entrylayout = new QHBoxLayout;
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources},
|
||||
{"{{filters}}", _filters},
|
||||
{"{{actions}}", _actions},
|
||||
@@ -256,8 +264,10 @@ MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
{"{{settingsInputMethod}}", _settingsInputMethods},
|
||||
{"{{settingValue}}", _manualSettingValue},
|
||||
{"{{tempVar}}", _tempVars},
|
||||
{"{{refresh}}", _refreshSettingSelection}};
|
||||
{"{{refresh}}", _refreshSettingSelection},
|
||||
{"{{settingsButtons}}", _settingsButtons}};
|
||||
|
||||
auto entrylayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"),
|
||||
entrylayout, widgetPlaceholders);
|
||||
_settingsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -294,12 +304,16 @@ void MacroActionFilterEdit::UpdateEntryData()
|
||||
_settingsString->setPlainText(_entryData->_settingsString);
|
||||
const auto filters =
|
||||
_entryData->_filter.GetFilters(_entryData->_source);
|
||||
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
|
||||
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
|
||||
_filterSettings->SetSource(firstFilter);
|
||||
_filterSettings->SetSetting(_entryData->_setting);
|
||||
_settingsInputMethods->setCurrentIndex(_settingsInputMethods->findData(
|
||||
static_cast<int>(_entryData->_settingsInputMethod)));
|
||||
_tempVars->SetVariable(_entryData->_tempVar);
|
||||
_manualSettingValue->setPlainText(_entryData->_manualSettingValue);
|
||||
PopulateSourceButtonSelection(_settingsButtons, firstFilter);
|
||||
_settingsButtons->setCurrentText(
|
||||
QString::fromStdString(_entryData->_button.ToString()));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -325,7 +339,9 @@ void MacroActionFilterEdit::FilterChanged(const FilterSelection &filter)
|
||||
}
|
||||
const auto filters =
|
||||
_entryData->_filter.GetFilters(_entryData->_source);
|
||||
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
|
||||
OBSWeakSource firstFilter = filters.empty() ? nullptr : filters.at(0);
|
||||
_filterSettings->SetSource(firstFilter);
|
||||
PopulateSourceButtonSelection(_settingsButtons, firstFilter);
|
||||
SetWidgetVisibility();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
@@ -441,6 +457,17 @@ void MacroActionFilterEdit::RefreshVariableSourceSelectionValue()
|
||||
_filterSettings->SetSource(filters.empty() ? nullptr : filters.at(0));
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::ButtonChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_button = qvariant_cast<SourceSettingButton>(
|
||||
_settingsButtons->itemData(idx));
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::SetWidgetVisibility()
|
||||
{
|
||||
SetLayoutVisible(_settingsLayout,
|
||||
@@ -479,6 +506,9 @@ void MacroActionFilterEdit::SetWidgetVisibility()
|
||||
SourceSelection::Type::VARIABLE ||
|
||||
_entryData->_filter.GetType() ==
|
||||
FilterSelection::Type::VARIABLE));
|
||||
_settingsButtons->setVisible(
|
||||
_entryData->_action ==
|
||||
MacroActionFilter::Action::SETTINGS_BUTTON);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "filter-selection.hpp"
|
||||
#include "source-properties-button.hpp"
|
||||
#include "source-setting.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
@@ -29,6 +30,7 @@ public:
|
||||
DISABLE,
|
||||
TOGGLE,
|
||||
SETTINGS,
|
||||
SETTINGS_BUTTON,
|
||||
};
|
||||
|
||||
enum class SettingsInputMethod {
|
||||
@@ -46,6 +48,7 @@ public:
|
||||
TempVariableRef _tempVar;
|
||||
StringVariable _manualSettingValue = "";
|
||||
SourceSetting _setting;
|
||||
SourceSettingButton _button;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -79,10 +82,14 @@ private slots:
|
||||
void SelectionChanged(const SourceSetting &);
|
||||
void ManualSettingsValueChanged();
|
||||
void RefreshVariableSourceSelectionValue();
|
||||
void ButtonChanged(int idx);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
SourceSelectionWidget *_sources;
|
||||
FilterSelectionWidget *_filters;
|
||||
QComboBox *_actions;
|
||||
@@ -94,11 +101,9 @@ protected:
|
||||
SourceSettingSelection *_filterSettings;
|
||||
VariableTextEdit *_settingsString;
|
||||
QPushButton *_refreshSettingSelection;
|
||||
QComboBox *_settingsButtons;
|
||||
|
||||
std::shared_ptr<MacroActionFilter> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "macro-action-run.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QDesktopServices>
|
||||
@@ -16,6 +17,8 @@ bool MacroActionRun::PerformAction()
|
||||
{
|
||||
if (_wait) {
|
||||
_procConfig.StartProcessAndWait(_timeout.Milliseconds());
|
||||
SetTempVarValues();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,6 +31,7 @@ bool MacroActionRun::PerformAction()
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(
|
||||
QString::fromStdString(_procConfig.Path())));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -36,6 +40,45 @@ void MacroActionRun::LogAction() const
|
||||
ablog(LOG_INFO, "run \"%s\"", _procConfig.UnresolvedPath().c_str());
|
||||
}
|
||||
|
||||
void MacroActionRun::SetupTempVars()
|
||||
{
|
||||
MacroAction::SetupTempVars();
|
||||
|
||||
AddTempvar(
|
||||
"process.id",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.run.process.id"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.id.description"));
|
||||
AddTempvar(
|
||||
"process.exitCode",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.exitCode"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.exitCode.description"));
|
||||
AddTempvar(
|
||||
"process.stream.output",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.output"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.output.description"));
|
||||
AddTempvar(
|
||||
"process.stream.error",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.error"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.error.description"));
|
||||
}
|
||||
|
||||
void MacroActionRun::SetTempVarValues()
|
||||
{
|
||||
SetTempVarValue("process.id", _procConfig.GetProcessId());
|
||||
SetTempVarValue("process.exitCode", _procConfig.GetProcessExitCode());
|
||||
SetTempVarValue("process.stream.output",
|
||||
_procConfig.GetProcessOutputStream());
|
||||
SetTempVarValue("process.stream.error",
|
||||
_procConfig.GetProcessErrorStream());
|
||||
}
|
||||
|
||||
bool MacroActionRun::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
@@ -86,8 +129,18 @@ MacroActionRunEdit::MacroActionRunEdit(
|
||||
_procConfig(new ProcessConfigEdit(this)),
|
||||
_waitLayout(new QHBoxLayout()),
|
||||
_wait(new QCheckBox()),
|
||||
_timeout(new DurationSelection(this, true, 0.1))
|
||||
_timeout(new DurationSelection(this, true, 0.1)),
|
||||
_waitHelp(new QLabel())
|
||||
{
|
||||
QString helpIconPath = GetThemeTypeName() == "Light"
|
||||
? ":/res/images/help.svg"
|
||||
: ":/res/images/help_light.svg";
|
||||
QIcon helpIcon(helpIconPath);
|
||||
_waitHelp->setPixmap(helpIcon.pixmap(QSize(16, 16)));
|
||||
_waitHelp->hide();
|
||||
_waitHelp->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.action.run.wait.help.tooltip"));
|
||||
|
||||
QWidget::connect(_procConfig,
|
||||
SIGNAL(ConfigChanged(const ProcessConfig &)), this,
|
||||
SLOT(ProcessConfigChanged(const ProcessConfig &)));
|
||||
@@ -98,9 +151,11 @@ MacroActionRunEdit::MacroActionRunEdit(
|
||||
QWidget::connect(_timeout, SIGNAL(DurationChanged(const Duration &)),
|
||||
this, SLOT(TimeoutChanged(const Duration &)));
|
||||
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.run.entry.wait"),
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.run.wait.entry"),
|
||||
_waitLayout,
|
||||
{{"{{wait}}", _wait}, {"{{timeout}}", _timeout}});
|
||||
{{"{{wait}}", _wait},
|
||||
{"{{timeout}}", _timeout},
|
||||
{"{{waitHelp}}", _waitHelp}});
|
||||
SetLayoutVisible(_waitLayout, false);
|
||||
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
@@ -25,6 +25,9 @@ public:
|
||||
Duration _timeout = 1;
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
void SetTempVarValues();
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -57,6 +60,7 @@ private:
|
||||
QHBoxLayout *_waitLayout;
|
||||
QCheckBox *_wait;
|
||||
DurationSelection *_timeout;
|
||||
QLabel *_waitHelp;
|
||||
|
||||
std::shared_ptr<MacroActionRun> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
bool PostLoad() override;
|
||||
bool PostLoad();
|
||||
std::string GetId() const { return id; };
|
||||
MacroRef GetNextMacro(bool advance = true);
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
|
||||
Q_DECLARE_METATYPE(advss::SourceSettingButton);
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionSource::id = "source";
|
||||
@@ -79,38 +77,6 @@ const static std::map<MacroActionSource::SettingsInputMethod, std::string>
|
||||
"AdvSceneSwitcher.action.source.inputMethod.json"},
|
||||
};
|
||||
|
||||
static std::vector<SourceSettingButton> getSourceButtons(OBSWeakSource source)
|
||||
{
|
||||
auto s = obs_weak_source_get_source(source);
|
||||
std::vector<SourceSettingButton> buttons;
|
||||
obs_properties_t *sourceProperties = obs_source_properties(s);
|
||||
auto it = obs_properties_first(sourceProperties);
|
||||
do {
|
||||
if (!it || obs_property_get_type(it) != OBS_PROPERTY_BUTTON) {
|
||||
continue;
|
||||
}
|
||||
SourceSettingButton button = {obs_property_name(it),
|
||||
obs_property_description(it)};
|
||||
buttons.emplace_back(button);
|
||||
} while (obs_property_next(&it));
|
||||
obs_source_release(s);
|
||||
obs_properties_destroy(sourceProperties);
|
||||
return buttons;
|
||||
}
|
||||
|
||||
static void pressSourceButton(const SourceSettingButton &button,
|
||||
obs_source_t *source)
|
||||
{
|
||||
obs_properties_t *sourceProperties = obs_source_properties(source);
|
||||
obs_property_t *property =
|
||||
obs_properties_get(sourceProperties, button.id.c_str());
|
||||
if (!obs_property_button_clicked(property, source)) {
|
||||
blog(LOG_WARNING, "Failed to press settings button '%s' for %s",
|
||||
button.id.c_str(), obs_source_get_name(source));
|
||||
}
|
||||
obs_properties_destroy(sourceProperties);
|
||||
}
|
||||
|
||||
static void refreshSourceSettings(obs_source_t *s)
|
||||
{
|
||||
if (!s) {
|
||||
@@ -174,7 +140,7 @@ bool MacroActionSource::PerformAction()
|
||||
refreshSourceSettings(s);
|
||||
break;
|
||||
case Action::SETTINGS_BUTTON:
|
||||
pressSourceButton(_button, s);
|
||||
PressSourceButton(_button, s);
|
||||
break;
|
||||
case Action::DEINTERLACE_MODE:
|
||||
obs_source_set_deinterlace_mode(s, _deinterlaceMode);
|
||||
@@ -297,23 +263,6 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateSourceButtonSelection(QComboBox *list,
|
||||
OBSWeakSource source)
|
||||
{
|
||||
list->clear();
|
||||
auto buttons = getSourceButtons(source);
|
||||
if (buttons.empty()) {
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.source.noSettingsButtons"));
|
||||
}
|
||||
|
||||
for (const auto &button : buttons) {
|
||||
QVariant value;
|
||||
value.setValue(button);
|
||||
list->addItem(QString::fromStdString(button.ToString()), value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateDeinterlaceModeSelection(QComboBox *list)
|
||||
{
|
||||
list->clear();
|
||||
@@ -445,7 +394,7 @@ void MacroActionSourceEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
populateSourceButtonSelection(_settingsButtons,
|
||||
PopulateSourceButtonSelection(_settingsButtons,
|
||||
_entryData->_source.GetSource());
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_sources->SetSource(_entryData->_source);
|
||||
@@ -476,7 +425,7 @@ void MacroActionSourceEdit::SourceChanged(const SourceSelection &source)
|
||||
auto lock = LockContext();
|
||||
_entryData->_source = source;
|
||||
}
|
||||
populateSourceButtonSelection(_settingsButtons,
|
||||
PopulateSourceButtonSelection(_settingsButtons,
|
||||
_entryData->_source.GetSource());
|
||||
_sourceSettings->SetSource(_entryData->_source.GetSource());
|
||||
SetWidgetVisibility();
|
||||
@@ -672,31 +621,4 @@ void MacroActionSourceEdit::SetWidgetVisibility()
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
bool SourceSettingButton::Save(obs_data_t *obj) const
|
||||
{
|
||||
auto data = obs_data_create();
|
||||
obs_data_set_string(data, "id", id.c_str());
|
||||
obs_data_set_string(data, "description", description.c_str());
|
||||
obs_data_set_obj(obj, "sourceSettingButton", data);
|
||||
obs_data_release(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SourceSettingButton::Load(obs_data_t *obj)
|
||||
{
|
||||
auto data = obs_data_get_obj(obj, "sourceSettingButton");
|
||||
id = obs_data_get_string(data, "id");
|
||||
description = obs_data_get_string(data, "description");
|
||||
obs_data_release(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SourceSettingButton::ToString() const
|
||||
{
|
||||
if (id.empty()) {
|
||||
return "";
|
||||
}
|
||||
return "[" + id + "] " + description;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "source-properties-button.hpp"
|
||||
#include "source-selection.hpp"
|
||||
#include "source-setting.hpp"
|
||||
|
||||
@@ -11,15 +12,6 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct SourceSettingButton {
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string ToString() const;
|
||||
|
||||
std::string id = "";
|
||||
std::string description = "";
|
||||
};
|
||||
|
||||
class MacroActionSource : public MacroAction {
|
||||
public:
|
||||
MacroActionSource(Macro *m) : MacroAction(m) {}
|
||||
@@ -103,7 +95,9 @@ private slots:
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
SourceSelectionWidget *_sources;
|
||||
QComboBox *_actions;
|
||||
QComboBox *_settingsButtons;
|
||||
@@ -120,9 +114,6 @@ protected:
|
||||
QPushButton *_refreshSettingSelection;
|
||||
|
||||
std::shared_ptr<MacroActionSource> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "layout-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
#include <util/config-file.h>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroActionSystray::id = "systray_notification";
|
||||
@@ -72,7 +75,9 @@ MacroActionSystrayEdit::MacroActionSystrayEdit(
|
||||
: QWidget(parent),
|
||||
_message(new VariableLineEdit(this)),
|
||||
_title(new VariableLineEdit(this)),
|
||||
_iconPath(new FileSelection())
|
||||
_iconPath(new FileSelection()),
|
||||
_trayDisableWarning(
|
||||
new QLabel("AdvSceneSwitcher.action.systray.disabled"))
|
||||
{
|
||||
_iconPath->setToolTip(
|
||||
obs_module_text("AdvSceneSwitcher.action.systray.iconHint"));
|
||||
@@ -85,25 +90,35 @@ MacroActionSystrayEdit::MacroActionSystrayEdit(
|
||||
SLOT(IconPathChanged(const QString &)));
|
||||
|
||||
auto layout = new QGridLayout();
|
||||
int row = 0;
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.systray.title")),
|
||||
0, 0);
|
||||
layout->addWidget(_title, 0, 1);
|
||||
row, 0);
|
||||
layout->addWidget(_title, ++row, 1);
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.systray.message")),
|
||||
1, 0);
|
||||
layout->addWidget(_message, 1, 1);
|
||||
row, 0);
|
||||
layout->addWidget(_message, ++row, 1);
|
||||
layout->addWidget(new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.systray.icon")),
|
||||
2, 0);
|
||||
layout->addWidget(_iconPath, 2, 1);
|
||||
setLayout(layout);
|
||||
row, 0);
|
||||
layout->addWidget(_iconPath, ++row, 1);
|
||||
|
||||
auto mainlayout = new QVBoxLayout();
|
||||
mainlayout->addLayout(layout);
|
||||
mainlayout->addWidget(_trayDisableWarning);
|
||||
setLayout(mainlayout);
|
||||
|
||||
_entryData = entryData;
|
||||
_message->setText(_entryData->_message);
|
||||
_title->setText(_entryData->_title);
|
||||
_iconPath->SetPath(_entryData->_iconPath);
|
||||
_loading = false;
|
||||
|
||||
CheckIfTrayIsDisabled();
|
||||
QWidget::connect(&_checkTrayDisableTimer, SIGNAL(timeout()), this,
|
||||
SLOT(CheckIfTrayIsDisabled()));
|
||||
_checkTrayDisableTimer.start(1000);
|
||||
}
|
||||
|
||||
void MacroActionSystrayEdit::TitleChanged()
|
||||
@@ -126,6 +141,19 @@ void MacroActionSystrayEdit::IconPathChanged(const QString &text)
|
||||
_entryData->_iconPath = text.toStdString();
|
||||
}
|
||||
|
||||
void MacroActionSystrayEdit::CheckIfTrayIsDisabled()
|
||||
{
|
||||
auto config = obs_frontend_get_global_config();
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
_trayDisableWarning->setVisible(
|
||||
!config_get_bool(config, "BasicWindow", "SysTrayEnabled"));
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionSystrayEdit::MessageChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
|
||||
@@ -50,6 +50,7 @@ private slots:
|
||||
void MessageChanged();
|
||||
void TitleChanged();
|
||||
void IconPathChanged(const QString &text);
|
||||
void CheckIfTrayIsDisabled();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionSystray> _entryData;
|
||||
@@ -58,6 +59,9 @@ private:
|
||||
VariableLineEdit *_message;
|
||||
VariableLineEdit *_title;
|
||||
FileSelection *_iconPath;
|
||||
QLabel *_trayDisableWarning;
|
||||
|
||||
QTimer _checkTrayDisableTimer;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -67,12 +67,12 @@ bool MacroActionVCam::Load(obs_data_t *obj)
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionVCam::Create(Macro *m)
|
||||
{
|
||||
return std::shared_ptr<MacroAction>();
|
||||
return std::make_shared<MacroActionVCam>(m);
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroAction> MacroActionVCam::Copy() const
|
||||
{
|
||||
return std::shared_ptr<MacroAction>();
|
||||
return std::make_shared<MacroActionVCam>(*this);
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
|
||||
@@ -307,9 +307,32 @@ void MacroActionWebsocketEdit::APITypeChanged(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_api = static_cast<MacroActionWebsocket::API>(index);
|
||||
{
|
||||
auto lock = LockContext();
|
||||
_entryData->_api =
|
||||
static_cast<MacroActionWebsocket::API>(index);
|
||||
}
|
||||
SetWidgetVisibility();
|
||||
|
||||
// Set example OBS websocket message if the default message or an empty
|
||||
// message was entered as neither of those are valid OBS websocket
|
||||
// messages
|
||||
if ((std::string(_entryData->_message).empty() ||
|
||||
std::string(_entryData->_message) ==
|
||||
obs_module_text("AdvSceneSwitcher.enterText")) &&
|
||||
_entryData->_api == MacroActionWebsocket::API::OBS_WEBSOCKET) {
|
||||
static constexpr std::string_view example =
|
||||
"{\n"
|
||||
" \"d\": {\n"
|
||||
" \"requestData\" : {},\n"
|
||||
" \"requestId\": \"someUniqueIdHere\",\n"
|
||||
" \"requestType\" : \"StartRecord\"\n"
|
||||
" },\n"
|
||||
" \"op\": 6\n"
|
||||
"}";
|
||||
_message->setPlainText(QString(example.data()));
|
||||
}
|
||||
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ bool MacroConditionAudio::Save(obs_data_t *obj) const
|
||||
static_cast<int>(_volumeCondition));
|
||||
obs_data_set_bool(obj, "useDb", _useDb);
|
||||
_volumeDB.Save(obj, "volumeDB");
|
||||
obs_data_set_int(obj, "version", 2);
|
||||
obs_data_set_int(obj, "version", 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -294,9 +294,19 @@ bool MacroConditionAudio::Load(obs_data_t *obj)
|
||||
_volumeCondition = static_cast<VolumeCondition>(
|
||||
obs_data_get_int(obj, "volumeCondition"));
|
||||
_volmeter = AddVolmeterToSource(this, _audioSource.GetSource());
|
||||
if (obs_data_get_int(obj, "version") != 2) {
|
||||
|
||||
if (obs_data_get_int(obj, "version") < 2) {
|
||||
// Set default values for dB handling
|
||||
_useDb = false;
|
||||
_volumeDB = 0.0;
|
||||
|
||||
// In previous versions the scaling happened in CheckCondition()
|
||||
// so these values must now be converted to enable compatibility
|
||||
// with the decibel value based checks.
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, "volume");
|
||||
auto oldFixedValue = obs_data_get_int(data, "value");
|
||||
_volumeDB = ((double)oldFixedValue / 1.7) - 60;
|
||||
_volumePercent = DecibelToPercent(_volumeDB) * 100;
|
||||
} else {
|
||||
_useDb = obs_data_get_bool(obj, "useDb");
|
||||
_volumeDB.Load(obj, "volumeDB");
|
||||
@@ -416,7 +426,7 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
|
||||
_checkTypes(new QComboBox()),
|
||||
_sources(new SourceSelectionWidget(this, QStringList(), true)),
|
||||
_condition(new QComboBox()),
|
||||
_volumePercent(new VariableSpinBox()),
|
||||
_volumePercent(new VariableDoubleSpinBox()),
|
||||
_volumeDB(new VariableDoubleSpinBox),
|
||||
_percentDBToggle(new QPushButton),
|
||||
_syncOffset(new VariableSpinBox()),
|
||||
@@ -444,8 +454,9 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
|
||||
SLOT(CheckTypeChanged(int)));
|
||||
QWidget::connect(
|
||||
_volumePercent,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
this, SLOT(VolumePercentChanged(const NumberVariable<int> &)));
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
|
||||
this,
|
||||
SLOT(VolumePercentChanged(const NumberVariable<double> &)));
|
||||
QWidget::connect(
|
||||
_syncOffset,
|
||||
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
|
||||
@@ -506,11 +517,12 @@ void MacroConditionAudioEdit::UpdateVolmeterSource()
|
||||
auto layout = this->layout();
|
||||
layout->addWidget(_volMeter);
|
||||
|
||||
QWidget::connect(_volMeter->GetSlider(), &QSlider::valueChanged,
|
||||
[=](int) { SyncSliderAndValueSelection(true); });
|
||||
QWidget::connect(_volMeter->GetSlider(),
|
||||
&DoubleSlider::DoubleValChanged,
|
||||
[=](double) { SyncSliderAndValueSelection(true); });
|
||||
|
||||
// Slider will default to 0 so set it manually once
|
||||
_volMeter->GetSlider()->setValue(_entryData->_volumePercent);
|
||||
SyncSliderAndValueSelection(false);
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::SourceChanged(const SourceSelection &source)
|
||||
@@ -531,7 +543,7 @@ void MacroConditionAudioEdit::SourceChanged(const SourceSelection &source)
|
||||
}
|
||||
|
||||
void MacroConditionAudioEdit::VolumePercentChanged(
|
||||
const NumberVariable<int> &vol)
|
||||
const NumberVariable<double> &vol)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
@@ -600,35 +612,35 @@ void MacroConditionAudioEdit::PercentDBClicked()
|
||||
|
||||
void MacroConditionAudioEdit::SyncSliderAndValueSelection(bool sliderMoved)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sliderMoved) {
|
||||
auto sliderPosition = _volMeter->GetSlider()->DoubleValue();
|
||||
// Adjust to the dB scale on the volume meter widget
|
||||
auto dBScaleValue = ((sliderPosition * 3.0) / 5.0) - 60.0;
|
||||
|
||||
if (_entryData->_useDb) {
|
||||
_volumeDB->SetFixedValue(PercentToDecibel(
|
||||
(float)_volMeter->GetSlider()->value() /
|
||||
100.0));
|
||||
_volumeDB->SetFixedValue(dBScaleValue);
|
||||
const QSignalBlocker blocker(this);
|
||||
_volumePercent->SetFixedValue(
|
||||
_volMeter->GetSlider()->value());
|
||||
DecibelToPercent(dBScaleValue) * 100);
|
||||
} else {
|
||||
_volumePercent->SetFixedValue(
|
||||
_volMeter->GetSlider()->value());
|
||||
DecibelToPercent(dBScaleValue) * 100);
|
||||
const QSignalBlocker blocker(this);
|
||||
_volumeDB->SetFixedValue(PercentToDecibel(
|
||||
(float)_volMeter->GetSlider()->value() /
|
||||
100.0));
|
||||
_volumeDB->SetFixedValue(dBScaleValue);
|
||||
}
|
||||
} else {
|
||||
const QSignalBlocker blocker(this);
|
||||
if (_entryData->_useDb) {
|
||||
_volMeter->GetSlider()->setValue(
|
||||
DecibelToPercent(_entryData->_volumeDB) * 100);
|
||||
} else {
|
||||
_volMeter->GetSlider()->setValue(
|
||||
_entryData->_volumePercent);
|
||||
}
|
||||
double dBValue =
|
||||
_entryData->_useDb
|
||||
? _entryData->_volumeDB.GetFixedValue()
|
||||
: PercentToDecibel(_entryData->_volumePercent /
|
||||
100.0);
|
||||
auto sliderPosition = (dBValue + 60.0) * 5 / 3.0;
|
||||
_volMeter->GetSlider()->SetDoubleVal(sliderPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@ public:
|
||||
|
||||
SourceSelection _audioSource;
|
||||
bool _useDb = false;
|
||||
NumberVariable<int> _volumePercent = 0;
|
||||
NumberVariable<double> _volumeDB = 0.0;
|
||||
NumberVariable<int> _syncOffset = 0;
|
||||
DoubleVariable _volumePercent = 0;
|
||||
DoubleVariable _volumeDB = 0.0;
|
||||
IntVariable _syncOffset = 0;
|
||||
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
|
||||
NumberVariable<double> _balance = 0.5;
|
||||
DoubleVariable _balance = 0.5;
|
||||
OutputCondition _outputCondition = OutputCondition::ABOVE;
|
||||
VolumeCondition _volumeCondition = VolumeCondition::ABOVE;
|
||||
obs_volmeter_t *_volmeter = nullptr;
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void SourceChanged(const SourceSelection &);
|
||||
void VolumePercentChanged(const NumberVariable<int> &vol);
|
||||
void VolumePercentChanged(const NumberVariable<double> &vol);
|
||||
void ConditionChanged(int cond);
|
||||
void CheckTypeChanged(int cond);
|
||||
void SyncOffsetChanged(const NumberVariable<int> &value);
|
||||
@@ -117,7 +117,7 @@ private:
|
||||
QComboBox *_checkTypes;
|
||||
SourceSelectionWidget *_sources;
|
||||
QComboBox *_condition;
|
||||
VariableSpinBox *_volumePercent;
|
||||
VariableDoubleSpinBox *_volumePercent;
|
||||
VariableDoubleSpinBox *_volumeDB;
|
||||
QPushButton *_percentDBToggle;
|
||||
VariableSpinBox *_syncOffset;
|
||||
|
||||
312
plugins/base/macro-condition-clipboard.cpp
Normal file
312
plugins/base/macro-condition-clipboard.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
#include "macro-condition-clipboard.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QMimeData>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionClipboard::id = "clipboard";
|
||||
|
||||
bool MacroConditionClipboard::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionClipboard::id,
|
||||
{MacroConditionClipboard::Create, MacroConditionClipboardEdit::Create,
|
||||
"AdvSceneSwitcher.condition.clipboard"});
|
||||
|
||||
const static std::map<MacroConditionClipboard::Condition, std::string>
|
||||
conditionTypes = {
|
||||
{MacroConditionClipboard::Condition::CHANGED,
|
||||
"AdvSceneSwitcher.condition.clipboard.condition.changed"},
|
||||
{MacroConditionClipboard::Condition::IS_TEXT,
|
||||
"AdvSceneSwitcher.condition.clipboard.condition.isText"},
|
||||
{MacroConditionClipboard::Condition::IS_IMAGE,
|
||||
"AdvSceneSwitcher.condition.clipboard.condition.isImage"},
|
||||
{MacroConditionClipboard::Condition::IS_URL,
|
||||
"AdvSceneSwitcher.condition.clipboard.condition.isURL"},
|
||||
{MacroConditionClipboard::Condition::MATCHES,
|
||||
"AdvSceneSwitcher.condition.clipboard.condition.matches"},
|
||||
};
|
||||
|
||||
ClipboardMessageBuffer ClipboardListener::RegisterForClipboardChanges()
|
||||
{
|
||||
return Instance()->_dispatcher.RegisterClient();
|
||||
}
|
||||
|
||||
ClipboardListener *ClipboardListener::Instance()
|
||||
{
|
||||
static ClipboardListener instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
ClipboardListener::ClipboardListener() : QObject(nullptr)
|
||||
{
|
||||
if (!QApplication::clipboard()) {
|
||||
return;
|
||||
}
|
||||
QObject::connect(QApplication::clipboard(), &QClipboard::dataChanged,
|
||||
this, &ClipboardListener::ClipboardDataChanged);
|
||||
}
|
||||
|
||||
void ClipboardListener::ClipboardDataChanged()
|
||||
{
|
||||
auto text = QApplication::clipboard()->text();
|
||||
_dispatcher.DispatchMessage(text.toStdString());
|
||||
}
|
||||
|
||||
MacroConditionClipboard::MacroConditionClipboard(Macro *m) : MacroCondition(m)
|
||||
{
|
||||
_messageBuffer = ClipboardListener::RegisterForClipboardChanges();
|
||||
}
|
||||
|
||||
std::shared_ptr<MacroCondition> MacroConditionClipboard::Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionClipboard>(m);
|
||||
}
|
||||
|
||||
static bool clipboardContainsImage()
|
||||
{
|
||||
const QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *mimeData = clipboard->mimeData();
|
||||
return mimeData->hasImage();
|
||||
}
|
||||
|
||||
static bool clipboardContainsText()
|
||||
{
|
||||
const QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *mimeData = clipboard->mimeData();
|
||||
return mimeData->hasText();
|
||||
}
|
||||
|
||||
static bool clipboardContainsUrl()
|
||||
{
|
||||
const QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *mimeData = clipboard->mimeData();
|
||||
return mimeData->hasUrls();
|
||||
}
|
||||
|
||||
static std::string getCurrentClipboardText()
|
||||
{
|
||||
auto clipboard = QApplication::clipboard();
|
||||
if (!clipboard) {
|
||||
return "";
|
||||
}
|
||||
return clipboard->text().toStdString();
|
||||
}
|
||||
|
||||
bool MacroConditionClipboard::CheckCondition()
|
||||
{
|
||||
switch (_condition) {
|
||||
case Condition::CHANGED:
|
||||
while (!_messageBuffer->Empty()) {
|
||||
auto message = _messageBuffer->ConsumeMessage();
|
||||
if (!message) {
|
||||
continue;
|
||||
}
|
||||
SetTempVarValue("text", *message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Condition::IS_TEXT:
|
||||
if (clipboardContainsText()) {
|
||||
SetTempVarValue("text", getCurrentClipboardText());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Condition::IS_IMAGE:
|
||||
return clipboardContainsImage();
|
||||
case Condition::IS_URL:
|
||||
if (clipboardContainsUrl()) {
|
||||
SetTempVarValue("text", getCurrentClipboardText());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Condition::MATCHES:
|
||||
if (_regex.Enabled()) {
|
||||
auto text = getCurrentClipboardText();
|
||||
if (_regex.Matches(text, _text)) {
|
||||
SetTempVarValue("text", text);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
auto text = getCurrentClipboardText();
|
||||
if (text == std::string(_text)) {
|
||||
SetTempVarValue("text", text);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacroConditionClipboard::SetCondition(Condition condition)
|
||||
{
|
||||
_condition = condition;
|
||||
_messageBuffer.reset();
|
||||
if (_condition == Condition::CHANGED) {
|
||||
_messageBuffer =
|
||||
ClipboardListener::RegisterForClipboardChanges();
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionClipboard::SetupTempVars()
|
||||
{
|
||||
MacroCondition::SetupTempVars();
|
||||
|
||||
AddTempvar(
|
||||
"text",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.clipboard.text"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.clipboard.text.description"));
|
||||
}
|
||||
|
||||
bool MacroConditionClipboard::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
_text.Save(obj, "text");
|
||||
_regex.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionClipboard::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_text.Load(obj, "text");
|
||||
_regex.Load(obj);
|
||||
SetCondition(
|
||||
static_cast<Condition>(obs_data_get_int(obj, "condition")));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void populateCompareModeselection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : conditionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionClipboardEdit::MacroConditionClipboardEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionClipboard> entryData)
|
||||
: QWidget(parent),
|
||||
_conditions(new QComboBox(this)),
|
||||
_text(new VariableTextEdit(this)),
|
||||
_regex(new RegexConfigWidget(parent)),
|
||||
_urlInfo(new QLabel())
|
||||
{
|
||||
populateCompareModeselection(_conditions);
|
||||
|
||||
QString path = GetThemeTypeName() == "Light"
|
||||
? ":/res/images/help.svg"
|
||||
: ":/res/images/help_light.svg";
|
||||
QIcon icon(path);
|
||||
QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||
_urlInfo->setPixmap(pixmap);
|
||||
_urlInfo->hide();
|
||||
_urlInfo->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.clipboard.urlTooltip"));
|
||||
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_text, SIGNAL(textChanged()), this,
|
||||
SLOT(TextChanged()));
|
||||
QWidget::connect(_regex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
|
||||
auto layout = new QHBoxLayout();
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.clipboard.condition.entry"),
|
||||
layout,
|
||||
{{"{{conditions}}", _conditions},
|
||||
{"{{regex}}", _regex},
|
||||
{"{{urlInfo}}", _urlInfo}});
|
||||
|
||||
auto mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(layout);
|
||||
mainLayout->addWidget(_text);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
QWidget *
|
||||
MacroConditionClipboardEdit::Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> action)
|
||||
{
|
||||
return new MacroConditionClipboardEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionClipboard>(action));
|
||||
}
|
||||
|
||||
void MacroConditionClipboardEdit::ConditionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetCondition(
|
||||
static_cast<MacroConditionClipboard::Condition>(value));
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionClipboardEdit::TextChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_text = _text->toPlainText().toStdString();
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionClipboardEdit::RegexChanged(const RegexConfig &conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_regex = conf;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionClipboardEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_conditions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->GetCondition()));
|
||||
_text->setPlainText(_entryData->_text);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionClipboardEdit::SetWidgetVisibility()
|
||||
{
|
||||
const bool requiresTextInput =
|
||||
_entryData->GetCondition() ==
|
||||
MacroConditionClipboard::Condition::MATCHES;
|
||||
_regex->setVisible(requiresTextInput);
|
||||
_text->setVisible(requiresTextInput);
|
||||
_urlInfo->setVisible(_entryData->GetCondition() ==
|
||||
MacroConditionClipboard::Condition::IS_URL);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
92
plugins/base/macro-condition-clipboard.hpp
Normal file
92
plugins/base/macro-condition-clipboard.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "filter-combo-box.hpp"
|
||||
#include "message-buffer.hpp"
|
||||
#include "message-dispatcher.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
using ClipboardMessageBuffer = std::shared_ptr<MessageBuffer<std::string>>;
|
||||
using ClipboardMessageDispatcher = MessageDispatcher<std::string>;
|
||||
|
||||
class ClipboardListener : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
[[nodiscard]] static ClipboardMessageBuffer
|
||||
RegisterForClipboardChanges();
|
||||
|
||||
private slots:
|
||||
void ClipboardDataChanged();
|
||||
|
||||
private:
|
||||
ClipboardListener();
|
||||
static ClipboardListener *Instance();
|
||||
|
||||
ClipboardMessageDispatcher _dispatcher;
|
||||
};
|
||||
|
||||
class MacroConditionClipboard : public MacroCondition {
|
||||
public:
|
||||
MacroConditionClipboard(Macro *m);
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m);
|
||||
std::string GetId() const { return id; };
|
||||
bool CheckCondition();
|
||||
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
|
||||
enum class Condition {
|
||||
CHANGED,
|
||||
IS_TEXT,
|
||||
IS_IMAGE,
|
||||
IS_URL,
|
||||
MATCHES,
|
||||
};
|
||||
void SetCondition(Condition);
|
||||
Condition GetCondition() const { return _condition; }
|
||||
|
||||
StringVariable _text = obs_module_text(
|
||||
"AdvSceneSwitcher.condition.clipboard.placeholder");
|
||||
RegexConfig _regex;
|
||||
|
||||
private:
|
||||
void SetupTempVars();
|
||||
|
||||
Condition _condition = Condition::CHANGED;
|
||||
ClipboardMessageBuffer _messageBuffer;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionClipboardEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionClipboardEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionClipboard> entryData = nullptr);
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> action);
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int);
|
||||
void TextChanged();
|
||||
void RegexChanged(const RegexConfig &conf);
|
||||
|
||||
private:
|
||||
void UpdateEntryData();
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_conditions;
|
||||
VariableTextEdit *_text;
|
||||
RegexConfigWidget *_regex;
|
||||
QLabel *_urlInfo;
|
||||
|
||||
std::shared_ptr<MacroConditionClipboard> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -42,7 +42,8 @@ std::string MacroConditionProfile::GetShortDesc() const
|
||||
|
||||
MacroConditionProfileEdit::MacroConditionProfileEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionProfile> entryData)
|
||||
: QWidget(parent), _profiles(new QComboBox())
|
||||
: QWidget(parent),
|
||||
_profiles(new QComboBox())
|
||||
{
|
||||
PopulateProfileSelection(_profiles);
|
||||
QWidget::connect(_profiles, SIGNAL(currentTextChanged(const QString &)),
|
||||
|
||||
@@ -48,6 +48,7 @@ bool MacroConditionRun::CheckCondition()
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
_threadDone = false;
|
||||
_thread = std::thread(&MacroConditionRun::RunProcess, this);
|
||||
|
||||
@@ -64,7 +65,48 @@ void MacroConditionRun::RunProcess()
|
||||
_error = ProcessConfig::ProcStartError::NONE;
|
||||
_procExitCode = std::get<int>(result);
|
||||
}
|
||||
|
||||
_threadDone = true;
|
||||
SetTempVarValues();
|
||||
}
|
||||
|
||||
void MacroConditionRun::SetupTempVars()
|
||||
{
|
||||
MacroCondition::SetupTempVars();
|
||||
|
||||
AddTempvar(
|
||||
"process.id",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.run.process.id"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.id.description"));
|
||||
AddTempvar(
|
||||
"process.exitCode",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.exitCode"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.exitCode.description"));
|
||||
AddTempvar(
|
||||
"process.stream.output",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.output"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.output.description"));
|
||||
AddTempvar(
|
||||
"process.stream.error",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.error"),
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.run.process.stream.error.description"));
|
||||
}
|
||||
|
||||
void MacroConditionRun::SetTempVarValues()
|
||||
{
|
||||
SetTempVarValue("process.id", _procConfig.GetProcessId());
|
||||
SetTempVarValue("process.exitCode", _procConfig.GetProcessExitCode());
|
||||
SetTempVarValue("process.stream.output",
|
||||
_procConfig.GetProcessOutputStream());
|
||||
SetTempVarValue("process.stream.error",
|
||||
_procConfig.GetProcessErrorStream());
|
||||
}
|
||||
|
||||
bool MacroConditionRun::Save(obs_data_t *obj) const
|
||||
|
||||
@@ -30,6 +30,10 @@ public:
|
||||
|
||||
private:
|
||||
void RunProcess();
|
||||
|
||||
void SetupTempVars();
|
||||
void SetTempVarValues();
|
||||
|
||||
std::thread _thread;
|
||||
std::atomic_bool _threadDone{true};
|
||||
ProcessConfig::ProcStartError _error =
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "macro-condition-timer.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionTimer::id = "timer";
|
||||
@@ -17,6 +19,9 @@ const static std::map<MacroConditionTimer::TimerType, std::string> timerTypes =
|
||||
"AdvSceneSwitcher.condition.timer.type.random"},
|
||||
};
|
||||
|
||||
static std::random_device rd;
|
||||
static std::default_random_engine re(rd());
|
||||
|
||||
bool MacroConditionTimer::CheckCondition()
|
||||
{
|
||||
if (_paused) {
|
||||
@@ -48,7 +53,7 @@ void MacroConditionTimer::SetRandomTimeRemaining()
|
||||
}
|
||||
std::uniform_real_distribution<double> unif(min, max);
|
||||
|
||||
double remainingTime = unif(_re);
|
||||
double remainingTime = unif(re);
|
||||
_duration.SetTimeRemaining(remainingTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroConditionTimer : public MacroCondition {
|
||||
@@ -41,7 +39,6 @@ private:
|
||||
void SetVariables(double seconds);
|
||||
void SetupTempVars();
|
||||
|
||||
std::default_random_engine _re;
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "name-dialog.hpp"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QProcess>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -16,6 +15,7 @@ bool ProcessConfig::Save(obs_data_t *obj) const
|
||||
_args.Save(data, "args", "arg");
|
||||
obs_data_set_obj(obj, "processConfig", data);
|
||||
obs_data_release(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ bool ProcessConfig::Load(obs_data_t *obj)
|
||||
_workingDirectory =
|
||||
obs_data_get_string(obj, "workingDirectory");
|
||||
_args.Load(obj, "args", "arg");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,6 +36,7 @@ bool ProcessConfig::Load(obs_data_t *obj)
|
||||
_workingDirectory.Load(data, "workingDirectory");
|
||||
_args.Load(data, "args", "arg");
|
||||
obs_data_release(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -44,6 +46,7 @@ QStringList ProcessConfig::Args() const
|
||||
for (auto &arg : _args) {
|
||||
result << QString::fromStdString(arg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -61,11 +64,14 @@ void ProcessConfig::ResolveVariables()
|
||||
}
|
||||
|
||||
std::variant<int, ProcessConfig::ProcStartError>
|
||||
ProcessConfig::StartProcessAndWait(int timeout) const
|
||||
ProcessConfig::StartProcessAndWait(int timeout)
|
||||
{
|
||||
ResetFinishedProcessData();
|
||||
|
||||
QProcess process;
|
||||
process.setWorkingDirectory(QString::fromStdString(WorkingDir()));
|
||||
process.start(QString::fromStdString(Path()), Args());
|
||||
SetProcessId(QString::number(process.processId()).toStdString());
|
||||
vblog(LOG_INFO, "run \"%s\" with a timeout of %d ms", Path().c_str(),
|
||||
timeout);
|
||||
|
||||
@@ -75,21 +81,47 @@ ProcessConfig::StartProcessAndWait(int timeout) const
|
||||
Path().c_str());
|
||||
return ProcStartError::FAILED_TO_START;
|
||||
}
|
||||
|
||||
SetFinishedProcessData(process);
|
||||
vblog(LOG_INFO,
|
||||
"timeout while running \"%s\"\nAttempting to kill process!",
|
||||
Path().c_str());
|
||||
process.kill();
|
||||
process.waitForFinished();
|
||||
|
||||
return ProcStartError::TIMEOUT;
|
||||
}
|
||||
|
||||
SetFinishedProcessData(process);
|
||||
|
||||
if (process.exitStatus() == QProcess::NormalExit) {
|
||||
return process.exitCode();
|
||||
}
|
||||
|
||||
vblog(LOG_INFO, "process \"%s\" crashed!", Path().c_str());
|
||||
return ProcStartError::CRASH;
|
||||
}
|
||||
|
||||
void ProcessConfig::SetFinishedProcessData(QProcess &process)
|
||||
{
|
||||
static const QRegularExpression regex("(\\r\\n|\\r|\\n)$");
|
||||
_processExitCode = QString::number(process.exitCode()).toStdString();
|
||||
// Qt reads extra newline, at least on Windows, hence the workaround
|
||||
_processOutputStream = QString(process.readAllStandardOutput())
|
||||
.remove(regex)
|
||||
.toStdString();
|
||||
_processErrorStream = QString(process.readAllStandardError())
|
||||
.remove(regex)
|
||||
.toStdString();
|
||||
}
|
||||
|
||||
void ProcessConfig::ResetFinishedProcessData()
|
||||
{
|
||||
_processExitCode = "";
|
||||
_processOutputStream = "";
|
||||
_processErrorStream = "";
|
||||
}
|
||||
|
||||
ProcessConfigEdit::ProcessConfigEdit(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_filePath(new FileSelection()),
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
#include <obs-data.h>
|
||||
#include <obs-module-helper.hpp>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QListWidget>
|
||||
#include <QProcess>
|
||||
#include <QPushButton>
|
||||
#include <QStringList>
|
||||
#include <QVBoxLayout>
|
||||
#include <variant>
|
||||
@@ -23,6 +24,20 @@ public:
|
||||
std::string WorkingDir() const { return _workingDirectory; }
|
||||
QStringList Args() const; // Resolves variables
|
||||
|
||||
void SetProcessId(std::string processId) { _processId = processId; }
|
||||
std::string GetProcessId() const { return _processId; }
|
||||
void SetFinishedProcessData(QProcess &process);
|
||||
void ResetFinishedProcessData();
|
||||
std::string GetProcessExitCode() const { return _processExitCode; }
|
||||
std::string GetProcessOutputStream() const
|
||||
{
|
||||
return _processOutputStream;
|
||||
}
|
||||
std::string GetProcessErrorStream() const
|
||||
{
|
||||
return _processErrorStream;
|
||||
}
|
||||
|
||||
enum class ProcStartError {
|
||||
NONE,
|
||||
FAILED_TO_START,
|
||||
@@ -30,8 +45,7 @@ public:
|
||||
CRASH,
|
||||
};
|
||||
|
||||
std::variant<int, ProcStartError>
|
||||
StartProcessAndWait(int timeoutInMs) const;
|
||||
std::variant<int, ProcStartError> StartProcessAndWait(int timeoutInMs);
|
||||
bool StartProcessDetached() const;
|
||||
|
||||
void ResolveVariables();
|
||||
@@ -41,6 +55,11 @@ private:
|
||||
StringVariable _workingDirectory = "";
|
||||
StringList _args;
|
||||
|
||||
std::string _processId;
|
||||
std::string _processExitCode;
|
||||
std::string _processOutputStream;
|
||||
std::string _processErrorStream;
|
||||
|
||||
friend class ProcessConfigEdit;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
78
plugins/base/utils/source-properties-button.cpp
Normal file
78
plugins/base/utils/source-properties-button.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "source-properties-button.hpp"
|
||||
#include "log-helper.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
bool SourceSettingButton::Save(obs_data_t *obj) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_string(data, "id", id.c_str());
|
||||
obs_data_set_string(data, "description", description.c_str());
|
||||
obs_data_set_obj(obj, "sourceSettingButton", data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SourceSettingButton::Load(obs_data_t *obj)
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, "sourceSettingButton");
|
||||
id = obs_data_get_string(data, "id");
|
||||
description = obs_data_get_string(data, "description");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SourceSettingButton::ToString() const
|
||||
{
|
||||
if (id.empty()) {
|
||||
return "";
|
||||
}
|
||||
return "[" + id + "] " + description;
|
||||
}
|
||||
|
||||
std::vector<SourceSettingButton> GetSourceButtons(OBSWeakSource source)
|
||||
{
|
||||
OBSSourceAutoRelease s = obs_weak_source_get_source(source);
|
||||
std::vector<SourceSettingButton> buttons;
|
||||
obs_properties_t *sourceProperties = obs_source_properties(s);
|
||||
auto it = obs_properties_first(sourceProperties);
|
||||
do {
|
||||
if (!it || obs_property_get_type(it) != OBS_PROPERTY_BUTTON) {
|
||||
continue;
|
||||
}
|
||||
SourceSettingButton button = {obs_property_name(it),
|
||||
obs_property_description(it)};
|
||||
buttons.emplace_back(button);
|
||||
} while (obs_property_next(&it));
|
||||
obs_properties_destroy(sourceProperties);
|
||||
return buttons;
|
||||
}
|
||||
|
||||
void PressSourceButton(const SourceSettingButton &button, obs_source_t *source)
|
||||
{
|
||||
obs_properties_t *sourceProperties = obs_source_properties(source);
|
||||
obs_property_t *property =
|
||||
obs_properties_get(sourceProperties, button.id.c_str());
|
||||
if (!obs_property_button_clicked(property, source)) {
|
||||
blog(LOG_WARNING, "Failed to press settings button '%s' for %s",
|
||||
button.id.c_str(), obs_source_get_name(source));
|
||||
}
|
||||
obs_properties_destroy(sourceProperties);
|
||||
}
|
||||
|
||||
void PopulateSourceButtonSelection(QComboBox *list, OBSWeakSource source)
|
||||
{
|
||||
list->clear();
|
||||
auto buttons = GetSourceButtons(source);
|
||||
if (buttons.empty()) {
|
||||
list->addItem(
|
||||
obs_module_text("AdvSceneSwitcher.noSettingsButtons"));
|
||||
}
|
||||
|
||||
for (const auto &button : buttons) {
|
||||
QVariant value;
|
||||
value.setValue(button);
|
||||
list->addItem(QString::fromStdString(button.ToString()), value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
25
plugins/base/utils/source-properties-button.hpp
Normal file
25
plugins/base/utils/source-properties-button.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <obs.hpp>
|
||||
#include <QComboBox>
|
||||
#include <qmetatype.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct SourceSettingButton {
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string ToString() const;
|
||||
|
||||
std::string id = "";
|
||||
std::string description = "";
|
||||
};
|
||||
|
||||
std::vector<SourceSettingButton> GetSourceButtons(OBSWeakSource source);
|
||||
void PressSourceButton(const SourceSettingButton &button, obs_source_t *source);
|
||||
void PopulateSourceButtonSelection(QComboBox *list, OBSWeakSource source);
|
||||
|
||||
} // namespace advss
|
||||
|
||||
Q_DECLARE_METATYPE(advss::SourceSettingButton);
|
||||
@@ -42,6 +42,80 @@ bool SourceSetting::operator==(const SourceSetting &other) const
|
||||
return _id == other._id;
|
||||
}
|
||||
|
||||
static const char *getPropertySuffix(obs_property_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case OBS_PROPERTY_INVALID:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.invalid";
|
||||
case OBS_PROPERTY_BOOL:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.bool";
|
||||
case OBS_PROPERTY_INT:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.int";
|
||||
case OBS_PROPERTY_FLOAT:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.float";
|
||||
case OBS_PROPERTY_TEXT:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.text";
|
||||
case OBS_PROPERTY_PATH:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.path";
|
||||
case OBS_PROPERTY_LIST:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.list";
|
||||
case OBS_PROPERTY_COLOR:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.color";
|
||||
case OBS_PROPERTY_BUTTON:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.button";
|
||||
case OBS_PROPERTY_FONT:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.font";
|
||||
case OBS_PROPERTY_EDITABLE_LIST:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.editableList";
|
||||
case OBS_PROPERTY_FRAME_RATE:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.frameRate";
|
||||
case OBS_PROPERTY_GROUP:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.group";
|
||||
case OBS_PROPERTY_COLOR_ALPHA:
|
||||
return "AdvSceneSwitcher.settings.suffix.type.colorAlpha";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static void addSettingsHelper(obs_property_t *property,
|
||||
std::vector<SourceSetting> &settings,
|
||||
const std::string &prefix = "")
|
||||
{
|
||||
do {
|
||||
if (!property) {
|
||||
continue;
|
||||
}
|
||||
auto type = obs_property_get_type(property);
|
||||
if (type == OBS_PROPERTY_GROUP) {
|
||||
auto group = obs_property_group_content(property);
|
||||
auto description = obs_property_description(property);
|
||||
std::string prefix =
|
||||
description
|
||||
? std::string("[") + description + "] "
|
||||
: "";
|
||||
addSettingsHelper(obs_properties_first(group), settings,
|
||||
prefix);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto name = obs_property_name(property);
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
auto description = obs_property_description(property);
|
||||
if (!description) {
|
||||
continue;
|
||||
}
|
||||
std::string descriptionWithPrefixAndSuffix =
|
||||
prefix + description +
|
||||
obs_module_text(getPropertySuffix(type));
|
||||
auto longDescription = obs_property_long_description(property);
|
||||
SourceSetting setting(name, descriptionWithPrefixAndSuffix,
|
||||
longDescription ? longDescription : "");
|
||||
settings.emplace_back(setting);
|
||||
} while (obs_property_next(&property));
|
||||
}
|
||||
|
||||
std::vector<SourceSetting> GetSoruceSettings(obs_source_t *source)
|
||||
{
|
||||
auto properties = obs_source_properties(source);
|
||||
@@ -50,23 +124,7 @@ std::vector<SourceSetting> GetSoruceSettings(obs_source_t *source)
|
||||
}
|
||||
std::vector<SourceSetting> settings;
|
||||
auto it = obs_properties_first(properties);
|
||||
do {
|
||||
if (!it) {
|
||||
continue;
|
||||
}
|
||||
auto name = obs_property_name(it);
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
auto description = obs_property_description(it);
|
||||
if (!description) {
|
||||
continue;
|
||||
}
|
||||
auto longDescription = obs_property_long_description(it);
|
||||
SourceSetting setting(name, description,
|
||||
longDescription ? longDescription : "");
|
||||
settings.emplace_back(setting);
|
||||
} while (obs_property_next(&it));
|
||||
addSettingsHelper(it, settings);
|
||||
obs_properties_destroy(properties);
|
||||
return settings;
|
||||
}
|
||||
@@ -113,11 +171,13 @@ void SetSourceSetting(obs_source_t *source, const SourceSetting &setting,
|
||||
obs_data_set_int(data, id.c_str(), *intValue);
|
||||
break;
|
||||
}
|
||||
|
||||
auto doubleValue = GetDouble(value);
|
||||
if (doubleValue.has_value()) {
|
||||
obs_data_set_int(data, id.c_str(),
|
||||
*doubleValue);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OBS_DATA_NUM_DOUBLE: {
|
||||
@@ -125,7 +185,8 @@ void SetSourceSetting(obs_source_t *source, const SourceSetting &setting,
|
||||
if (!numValue.has_value()) {
|
||||
break;
|
||||
}
|
||||
obs_data_set_int(data, id.c_str(), *numValue);
|
||||
|
||||
obs_data_set_double(data, id.c_str(), *numValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -171,7 +232,7 @@ void SetSourceSetting(obs_source_t *source, const SourceSetting &setting,
|
||||
SourceSettingSelection::SourceSettingSelection(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_settings(new FilterComboBox(
|
||||
this, obs_module_text("AdvSceneSwitcher.selectSetting"))),
|
||||
this, obs_module_text("AdvSceneSwitcher.setting.select"))),
|
||||
_tooltip(new QLabel())
|
||||
{
|
||||
QString path = GetThemeTypeName() == "Light"
|
||||
@@ -182,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;
|
||||
|
||||
@@ -17,6 +17,55 @@ namespace advss {
|
||||
std::map<std::pair<MidiDeviceType, std::string>, MidiDeviceInstance *>
|
||||
MidiDeviceInstance::devices = {};
|
||||
|
||||
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) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI input connected: %s",
|
||||
p.port_name.c_str());
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.input_removed = [=](const libremidi::input_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI input removed: %s",
|
||||
p.port_name.c_str());
|
||||
};
|
||||
cbs.output_added = [=](const libremidi::output_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI output connected: %s",
|
||||
p.port_name.c_str());
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.output_removed = [=](const libremidi::output_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI output removed: %s",
|
||||
p.port_name.c_str());
|
||||
};
|
||||
observers.emplace_back(
|
||||
cbs, libremidi::observer_configuration_for(api));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool deviceObserversAreSetup = setupDeviceObservers();
|
||||
|
||||
void MidiDeviceInstance::ResetAllDevices()
|
||||
{
|
||||
for (auto const &[_, device] : MidiDeviceInstance::devices) {
|
||||
@@ -212,8 +261,9 @@ bool MidiMessage::Matches(const MidiMessage &m) const
|
||||
return channelMatch && noteMatch && valueMatch && typeMatch;
|
||||
}
|
||||
|
||||
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type,
|
||||
const std::string &name)
|
||||
MidiDeviceInstance *
|
||||
MidiDeviceInstance::GetDeviceAndOpen(MidiDeviceType type,
|
||||
const std::string &name)
|
||||
{
|
||||
if (name.empty()) {
|
||||
return nullptr;
|
||||
@@ -252,6 +302,30 @@ getNameFromPortInformation(const libremidi::port_information &info)
|
||||
return "[" + name;
|
||||
}
|
||||
|
||||
MidiDeviceInstance *
|
||||
advss::MidiDeviceInstance::GetDevice(const libremidi::input_port &p)
|
||||
{
|
||||
auto key = std::make_pair(MidiDeviceType::INPUT,
|
||||
getNameFromPortInformation(p));
|
||||
auto it = MidiDeviceInstance::devices.find(key);
|
||||
if (it == devices.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
MidiDeviceInstance *
|
||||
advss::MidiDeviceInstance::GetDevice(const libremidi::output_port &p)
|
||||
{
|
||||
auto key = std::make_pair(MidiDeviceType::OUTPUT,
|
||||
getNameFromPortInformation(p));
|
||||
auto it = MidiDeviceInstance::devices.find(key);
|
||||
if (it == devices.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static inline QStringList getInputDeviceNames()
|
||||
{
|
||||
QStringList devices;
|
||||
@@ -305,10 +379,11 @@ static std::string getPortNameFromNumber(MidiDeviceType type, int port)
|
||||
return devices.at(port).toStdString();
|
||||
}
|
||||
|
||||
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type, int port)
|
||||
MidiDeviceInstance *MidiDeviceInstance::GetDeviceAndOpen(MidiDeviceType type,
|
||||
int port)
|
||||
{
|
||||
std::string name = getPortNameFromNumber(type, port);
|
||||
return GetDevice(type, name);
|
||||
return GetDeviceAndOpen(type, name);
|
||||
}
|
||||
|
||||
void MidiDevice::Save(obs_data_t *obj) const
|
||||
@@ -327,13 +402,13 @@ void MidiDevice::Load(obs_data_t *obj)
|
||||
// TODO: Remove this fallback at some point
|
||||
if (obs_data_has_user_value(data, "port")) {
|
||||
auto port = obs_data_get_int(data, "port");
|
||||
_dev = MidiDeviceInstance::GetDevice(_type, port);
|
||||
_dev = MidiDeviceInstance::GetDeviceAndOpen(_type, port);
|
||||
if (_dev) {
|
||||
_name = _dev->_name;
|
||||
}
|
||||
} else {
|
||||
_name = obs_data_get_string(data, "portName");
|
||||
_dev = MidiDeviceInstance::GetDevice(_type, _name);
|
||||
_dev = MidiDeviceInstance::GetDeviceAndOpen(_type, _name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,8 +447,7 @@ getInPortFromName(const std::string &name)
|
||||
|
||||
bool MidiDeviceInstance::OpenPort()
|
||||
{
|
||||
if ((_type == MidiDeviceType::INPUT && _in.is_port_open()) ||
|
||||
(_type == MidiDeviceType::OUTPUT && _out.is_port_open())) {
|
||||
if (IsOpened()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -437,10 +511,15 @@ bool MidiDeviceInstance::OpenPort()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MidiDeviceInstance::IsOpened() const
|
||||
{
|
||||
return (_type == MidiDeviceType::INPUT && _in.is_port_open()) ||
|
||||
(_type == MidiDeviceType::OUTPUT && _out.is_port_open());
|
||||
}
|
||||
|
||||
void MidiDeviceInstance::ClosePort()
|
||||
{
|
||||
if ((_type == MidiDeviceType::INPUT && !_in.is_port_open()) ||
|
||||
(_type == MidiDeviceType::OUTPUT && !_out.is_port_open())) {
|
||||
if (!IsOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -542,7 +621,6 @@ MidiMessageBuffer MidiDeviceInstance::RegisterForMidiMessages()
|
||||
|
||||
void MidiDeviceInstance::ReceiveMidiMessage(libremidi::message &&msg)
|
||||
{
|
||||
auto lock = LockContext();
|
||||
_dispatcher.DispatchMessage(msg);
|
||||
vblog(LOG_INFO, "received midi: %s",
|
||||
MidiMessage::ToString(msg).c_str());
|
||||
@@ -590,7 +668,7 @@ void MidiDeviceSelection::IdxChangedHelper(int idx)
|
||||
}
|
||||
|
||||
auto name = currentText().toStdString();
|
||||
auto devInstance = MidiDeviceInstance::GetDevice(_type, name);
|
||||
auto devInstance = MidiDeviceInstance::GetDeviceAndOpen(_type, name);
|
||||
if (!devInstance) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.midi.deviceOpenFail"));
|
||||
|
||||
@@ -64,16 +64,22 @@ enum class MidiDeviceType {
|
||||
|
||||
class MidiDeviceInstance {
|
||||
public:
|
||||
static MidiDeviceInstance *GetDevice(MidiDeviceType type,
|
||||
const std::string &);
|
||||
static MidiDeviceInstance *GetDevice(MidiDeviceType type, int port);
|
||||
static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
|
||||
const std::string &);
|
||||
static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
|
||||
int port);
|
||||
static MidiDeviceInstance *GetDevice(const libremidi::input_port &p);
|
||||
static MidiDeviceInstance *GetDevice(const libremidi::output_port &p);
|
||||
|
||||
static void ResetAllDevices();
|
||||
|
||||
bool OpenPort();
|
||||
void ClosePort();
|
||||
|
||||
private:
|
||||
MidiDeviceInstance() = default;
|
||||
~MidiDeviceInstance() = default;
|
||||
bool OpenPort();
|
||||
void ClosePort();
|
||||
bool IsOpened() const;
|
||||
bool SendMessge(const MidiMessage &);
|
||||
[[nodiscard]] MidiMessageBuffer RegisterForMidiMessages();
|
||||
void ReceiveMidiMessage(libremidi::message &&);
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -64,7 +64,9 @@ void Area::Load(obs_data_t *obj, const char *name)
|
||||
}
|
||||
|
||||
SizeSelection::SizeSelection(int min, int max, QWidget *parent)
|
||||
: QWidget(parent), _x(new VariableSpinBox()), _y(new VariableSpinBox())
|
||||
: QWidget(parent),
|
||||
_x(new VariableSpinBox()),
|
||||
_y(new VariableSpinBox())
|
||||
{
|
||||
_x->setMinimum(min);
|
||||
_y->setMinimum(min);
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
int Init(const char *, const char *) { return 0; }
|
||||
void End() {}
|
||||
};
|
||||
}
|
||||
} // namespace tesseract
|
||||
#endif
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
#include "obs-data.h"
|
||||
|
||||
struct obs_data_item {
|
||||
};
|
||||
struct obs_data_item {};
|
||||
|
||||
struct obs_data {
|
||||
};
|
||||
struct obs_data {};
|
||||
|
||||
struct obs_data_array {
|
||||
};
|
||||
struct obs_data_array {};
|
||||
|
||||
struct obs_data_number {
|
||||
};
|
||||
struct obs_data_number {};
|
||||
|
||||
obs_data_t *obs_data_create()
|
||||
{
|
||||
|
||||
@@ -6,18 +6,23 @@
|
||||
TEST_CASE("Variable", "[variable]")
|
||||
{
|
||||
advss::Variable variable;
|
||||
REQUIRE_FALSE(variable.SecondsSinceLastUse());
|
||||
REQUIRE_FALSE(variable.GetSecondsSinceLastUse());
|
||||
|
||||
REQUIRE(variable.Value() == "");
|
||||
REQUIRE(variable.GetPreviousValue() == "");
|
||||
REQUIRE(variable.GetDefaultValue() == "");
|
||||
REQUIRE(variable.GetSaveAction() ==
|
||||
advss::Variable::SaveAction::DONT_SAVE);
|
||||
REQUIRE(variable.GetValueChangeCount() == 0);
|
||||
|
||||
variable.SetValue("testing");
|
||||
REQUIRE(variable.Value() == "testing");
|
||||
REQUIRE(variable.GetPreviousValue() == "");
|
||||
REQUIRE(variable.GetValueChangeCount() == 1);
|
||||
|
||||
variable.SetValue(123);
|
||||
REQUIRE(variable.Value() == "123");
|
||||
REQUIRE(variable.GetPreviousValue() == "testing");
|
||||
|
||||
variable.SetValue(123.0);
|
||||
REQUIRE(variable.Value() == "123");
|
||||
@@ -25,14 +30,23 @@ TEST_CASE("Variable", "[variable]")
|
||||
variable.SetValue(123.123);
|
||||
REQUIRE(variable.Value() == "123.123");
|
||||
|
||||
REQUIRE(*variable.SecondsSinceLastUse() == 0);
|
||||
REQUIRE(*variable.GetSecondsSinceLastUse() == 0);
|
||||
REQUIRE(*variable.GetSecondsSinceLastChange() == 0);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
REQUIRE(*variable.SecondsSinceLastUse() > 1);
|
||||
REQUIRE(*variable.GetSecondsSinceLastUse() > 1);
|
||||
REQUIRE(*variable.GetSecondsSinceLastChange() > 1);
|
||||
|
||||
variable.Value(false);
|
||||
REQUIRE(*variable.SecondsSinceLastUse() > 1);
|
||||
REQUIRE(*variable.GetSecondsSinceLastUse() > 1);
|
||||
|
||||
variable.UpdateLastUsed();
|
||||
REQUIRE(*variable.SecondsSinceLastUse() == 0);
|
||||
REQUIRE(*variable.GetSecondsSinceLastUse() == 0);
|
||||
|
||||
variable.SetValue(123);
|
||||
REQUIRE(*variable.GetSecondsSinceLastChange() == 0);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
variable.SetValue(123);
|
||||
REQUIRE(*variable.GetSecondsSinceLastChange() > 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user