SceneSwitcher/lib/macro/macro-tab.cpp
2025-05-08 21:10:13 +02:00

1640 lines
44 KiB
C++

#include "advanced-scene-switcher.hpp"
#include "action-queue.hpp"
#include "cursor-shape-changer.hpp"
#include "macro-action-edit.hpp"
#include "macro-condition-edit.hpp"
#include "macro-export-import-dialog.hpp"
#include "macro-settings.hpp"
#include "macro-segment-copy-paste.hpp"
#include "macro-tree.hpp"
#include "macro.hpp"
#include "math-helpers.hpp"
#include "name-dialog.hpp"
#include "path-helpers.hpp"
#include "switcher-data.hpp"
#include "ui-helpers.hpp"
#include "utility.hpp"
#include "version.h"
#include <obs-frontend-api.h>
#include <QColor>
#include <QGraphicsOpacityEffect>
#include <QMenu>
#include <QPropertyAnimation>
namespace advss {
static QObject *addPulse = nullptr;
static QTimer onChangeHighlightTimer;
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)
{
QString fmt;
int i = 1;
if (format.empty()) {
fmt = QString(obs_module_text(
"AdvSceneSwitcher.macroTab.defaultname"));
} else {
fmt = QString::fromStdString(format);
i = 2;
}
QString placeHolderText = fmt.arg(i);
while ((macroNameExists(placeHolderText.toUtf8().constData()))) {
placeHolderText = fmt.arg(++i);
}
bool accepted = NameDialog::AskForName(
this, obs_module_text("AdvSceneSwitcher.macroTab.add"),
obs_module_text("AdvSceneSwitcher.macroTab.name"), name,
placeHolderText);
if (!accepted) {
return false;
}
if (name.empty()) {
return false;
}
if (!newMacroNameIsValid(name)) {
return false;
}
res = std::make_shared<Macro>(name, GetGlobalMacroSettings());
return true;
}
void AdvSceneSwitcher::on_macroAdd_clicked()
{
std::string name;
std::shared_ptr<Macro> newMacro;
if (!AddNewMacro(newMacro, name)) {
return;
}
if (addPulse) {
addPulse->deleteLater();
addPulse = nullptr;
}
auto selectedMacro = GetSelectedMacro();
if (!selectedMacro) {
ui->macros->Add(newMacro);
emit MacroAdded(QString::fromStdString(name));
return;
}
if (selectedMacro->IsGroup()) {
ui->macros->Add(newMacro, selectedMacro);
emit MacroAdded(QString::fromStdString(name));
return;
}
auto selectedMacroGroup = selectedMacro->Parent();
if (!selectedMacroGroup) {
ui->macros->Add(newMacro, selectedMacro);
emit MacroAdded(QString::fromStdString(name));
return;
}
Macro::PrepareMoveToGroup(selectedMacroGroup, newMacro);
ui->macros->Add(newMacro, selectedMacro);
emit MacroAdded(QString::fromStdString(name));
}
void AdvSceneSwitcher::RemoveMacro(std::shared_ptr<Macro> &macro)
{
if (!macro) {
return;
}
auto name = QString::fromStdString(macro->Name());
if (macro->IsGroup() && macro->GroupSize() > 0) {
QString deleteWarning = obs_module_text(
"AdvSceneSwitcher.macroTab.removeGroupPopup.text");
if (!DisplayMessage(deleteWarning.arg(name), true)) {
return;
}
}
ui->macros->Remove(macro);
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 &macro : macros) {
RemoveMacro(macro);
}
}
void AdvSceneSwitcher::RenameMacro(std::shared_ptr<Macro> &macro,
const QString &name)
{
auto oldName = QString::fromStdString(macro->Name());
{
auto lock = LockContext();
macro->SetName(name.toStdString());
}
emit MacroRenamed(oldName, name);
}
void AdvSceneSwitcher::on_macroRemove_clicked()
{
RemoveSelectedMacros();
}
void AdvSceneSwitcher::on_macroUp_clicked() const
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
ui->macros->Up(macro);
}
void AdvSceneSwitcher::on_macroDown_clicked() const
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
ui->macros->Down(macro);
}
void AdvSceneSwitcher::RenameSelectedMacro()
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
std::string oldName = macro->Name();
std::string name;
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 || !newMacroNameIsValid(name)) {
return;
}
RenameMacro(macro, QString::fromStdString(name));
const QSignalBlocker b(ui->macroName);
ui->macroName->setText(QString::fromStdString(name));
}
static void addGroupSubitems(std::vector<std::shared_ptr<Macro>> &macros,
const std::shared_ptr<Macro> &group)
{
std::vector<std::shared_ptr<Macro>> subitems;
subitems.reserve(group->GroupSize());
// Find all subitems
auto allMacros = GetMacros();
for (auto it = allMacros.begin(); it < allMacros.end(); it++) {
if ((*it)->Name() == group->Name()) {
for (uint32_t i = 1; i <= group->GroupSize(); i++) {
subitems.emplace_back(*std::next(it, i));
}
break;
}
}
// Remove subitems which were already selected to avoid duplicates
for (const auto &subitem : subitems) {
auto it = std::find(macros.begin(), macros.end(), subitem);
if (it == macros.end()) {
continue;
}
macros.erase(it);
}
// Add group subitems
auto it = std::find(macros.begin(), macros.end(), group);
if (it == macros.end()) {
return;
}
it = std::next(it);
macros.insert(it, subitems.begin(), subitems.end());
}
void AdvSceneSwitcher::ExportMacros() const
{
auto selectedMacros = GetSelectedMacros();
auto macros = selectedMacros;
for (const auto &macro : selectedMacros) {
if (macro->IsGroup() && macro->GroupSize() > 0) {
addGroupSubitems(macros, macro);
}
}
OBSDataAutoRelease data = obs_data_create();
OBSDataArrayAutoRelease macroArray = obs_data_array_create();
for (const auto &macro : macros) {
OBSDataAutoRelease obj = obs_data_create();
macro->Save(obj);
obs_data_array_push_back(macroArray, obj);
}
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);
MacroExportImportDialog::ExportMacros(exportString);
}
static bool
isValidMacroSegmentIdx(const std::deque<std::shared_ptr<MacroSegment>> &list,
int idx)
{
return (idx > 0 || (unsigned)idx < list.size());
}
void AdvSceneSwitcher::SetupMacroSegmentSelection(MacroSection type, int idx)
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
MacroSegmentList *setList = nullptr, *resetList1 = nullptr,
*resetList2 = nullptr;
int *setIdx = nullptr, *resetIdx1 = nullptr, *resetIdx2 = nullptr;
std::deque<std::shared_ptr<MacroSegment>> segements;
switch (type) {
case AdvSceneSwitcher::MacroSection::CONDITIONS:
setList = ui->conditionsList;
setIdx = &currentConditionIdx;
segements = {macro->Conditions().begin(),
macro->Conditions().end()};
resetList1 = ui->actionsList;
resetList2 = ui->elseActionsList;
resetIdx1 = &currentActionIdx;
resetIdx2 = &currentElseActionIdx;
break;
case AdvSceneSwitcher::MacroSection::ACTIONS:
setList = ui->actionsList;
setIdx = &currentActionIdx;
segements = {macro->Actions().begin(), macro->Actions().end()};
resetList1 = ui->conditionsList;
resetList2 = ui->elseActionsList;
resetIdx1 = &currentConditionIdx;
resetIdx2 = &currentElseActionIdx;
break;
case AdvSceneSwitcher::MacroSection::ELSE_ACTIONS:
setList = ui->elseActionsList;
setIdx = &currentElseActionIdx;
segements = {macro->ElseActions().begin(),
macro->ElseActions().end()};
resetList1 = ui->actionsList;
resetList2 = ui->conditionsList;
resetIdx1 = &currentActionIdx;
resetIdx2 = &currentConditionIdx;
break;
default:
break;
}
setList->SetSelection(idx);
resetList1->SetSelection(-1);
resetList2->SetSelection(-1);
if (isValidMacroSegmentIdx(segements, idx)) {
*setIdx = idx;
} else {
*setIdx = -1;
}
*resetIdx1 = -1;
*resetIdx2 = -1;
lastInteracted = type;
HighlightControls();
}
bool AdvSceneSwitcher::ResolveMacroImportNameConflict(
std::shared_ptr<Macro> &macro)
{
QString errorMesg = obs_module_text(
"AdvSceneSwitcher.macroTab.import.nameConflict");
errorMesg = errorMesg.arg(QString::fromStdString(macro->Name()),
QString::fromStdString(macro->Name()));
bool continueResolve = DisplayMessage(errorMesg, true);
if (!continueResolve) {
return false;
}
QString format = QString::fromStdString(macro->Name()) + " %1";
int i = 2;
QString placeHolderText = format.arg(i);
while ((macroNameExists(placeHolderText.toUtf8().constData()))) {
placeHolderText = format.arg(++i);
}
std::string newName;
bool accepted = NameDialog::AskForName(
this,
obs_module_text(macro->IsGroup()
? "AdvSceneSwitcher.macroTab.addGroup"
: "AdvSceneSwitcher.macroTab.add"),
obs_module_text("AdvSceneSwitcher.macroTab.name"), newName,
placeHolderText);
if (!accepted) {
return false;
}
if (newName.empty()) {
return false;
}
if (!newMacroNameIsValid(newName)) {
return ResolveMacroImportNameConflict(macro);
}
macro->SetName(newName);
return true;
}
void AdvSceneSwitcher::ImportMacros()
{
QString json;
if (!MacroExportImportDialog::ImportMacros(json)) {
return;
}
OBSDataAutoRelease data =
obs_data_create_from_json(json.toStdString().c_str());
if (!data) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.macroTab.import.invalid"));
ImportMacros();
return;
}
ImportVariables(data);
ImportQueues(data);
auto version = obs_data_get_string(data, "version");
if (strcmp(version, g_GIT_TAG) != 0) {
blog(LOG_WARNING,
"importing macros from non matching plugin version \"%s\"",
version);
}
OBSDataArrayAutoRelease array = obs_data_get_array(data, "macros");
size_t count = obs_data_array_count(array);
int groupSize = 0;
std::shared_ptr<Macro> group;
std::vector<std::shared_ptr<Macro>> importedMacros;
auto lock = LockContext();
for (size_t i = 0; i < count; i++) {
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)) {
groupSize--;
continue;
}
importedMacros.emplace_back(macro);
GetMacros().emplace_back(macro);
if (groupSize > 0 && !macro->IsGroup()) {
Macro::PrepareMoveToGroup(group, macro);
groupSize--;
}
if (macro->IsGroup()) {
group = macro;
groupSize = macro->GroupSize();
// We are not sure if all elements will be added so we
// have to reset the group size to zero and add elements
// to the group as they come up.
macro->ResetGroupSize();
}
}
for (const auto &macro : importedMacros) {
macro->PostLoad();
}
RunPostLoadSteps();
ui->macros->Reset(GetMacros(),
GetGlobalMacroSettings()._highlightExecuted);
}
void AdvSceneSwitcher::on_macroName_editingFinished()
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
QString newName = ui->macroName->text();
QString oldName = QString::fromStdString(macro->Name());
if (newName.isEmpty() || newName == oldName ||
!newMacroNameIsValid(newName.toStdString())) {
ui->macroName->setText(oldName);
return;
}
RenameMacro(macro, newName);
}
void AdvSceneSwitcher::on_runMacroInParallel_stateChanged(int value) const
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
auto lock = LockContext();
macro->SetRunInParallel(value);
}
void AdvSceneSwitcher::on_runMacroOnChange_stateChanged(int value) const
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
auto lock = LockContext();
macro->SetMatchOnChange(value);
}
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx)
{
auto &actions = m.Actions();
for (; afterIdx < actions.size(); afterIdx++) {
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
actions[afterIdx]->GetId());
ui->actionsList->Add(newEntry);
}
ui->actionsList->SetHelpMsgVisible(actions.size() == 0);
}
void AdvSceneSwitcher::PopulateMacroElseActions(Macro &m, uint32_t afterIdx)
{
auto &actions = m.ElseActions();
for (; afterIdx < actions.size(); afterIdx++) {
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
actions[afterIdx]->GetId());
ui->elseActionsList->Add(newEntry);
}
ui->elseActionsList->SetHelpMsgVisible(actions.size() == 0);
}
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
{
bool root = afterIdx == 0;
auto &conditions = m.Conditions();
for (; afterIdx < conditions.size(); afterIdx++) {
auto newEntry = new MacroConditionEdit(
this, &conditions[afterIdx],
conditions[afterIdx]->GetId(), root);
ui->conditionsList->Add(newEntry);
root = false;
}
ui->conditionsList->SetHelpMsgVisible(conditions.size() == 0);
}
void AdvSceneSwitcher::SetActionData(Macro &m) const
{
auto &actions = m.Actions();
for (int idx = 0; idx < ui->actionsList->ContentLayout()->count();
idx++) {
auto item = ui->actionsList->ContentLayout()->itemAt(idx);
if (!item) {
continue;
}
auto widget = static_cast<MacroActionEdit *>(item->widget());
if (!widget) {
continue;
}
widget->SetEntryData(&*(actions.begin() + idx));
}
}
void AdvSceneSwitcher::SetElseActionData(Macro &m) const
{
auto &actions = m.ElseActions();
for (int idx = 0; idx < ui->elseActionsList->ContentLayout()->count();
idx++) {
auto item = ui->elseActionsList->ContentLayout()->itemAt(idx);
if (!item) {
continue;
}
auto widget = static_cast<MacroActionEdit *>(item->widget());
if (!widget) {
continue;
}
widget->SetEntryData(&*(actions.begin() + idx));
}
}
void AdvSceneSwitcher::SetConditionData(Macro &m) const
{
auto &conditions = m.Conditions();
for (int idx = 0; idx < ui->conditionsList->ContentLayout()->count();
idx++) {
auto item = ui->conditionsList->ContentLayout()->itemAt(idx);
if (!item) {
continue;
}
auto widget = static_cast<MacroConditionEdit *>(item->widget());
if (!widget) {
continue;
}
widget->SetEntryData(&*(conditions.begin() + idx));
}
}
static void maximizeFirstSplitterEntry(QSplitter *splitter)
{
QList<int> newSizes;
newSizes << 999999;
for (int i = 0; i < splitter->sizes().size() - 1; i++) {
newSizes << 0;
}
splitter->setSizes(newSizes);
}
static void centerSplitterPosition(QSplitter *splitter)
{
splitter->setSizes(QList<int>() << 999999 << 999999);
}
void AdvSceneSwitcher::SetEditMacro(Macro &m)
{
{
const QSignalBlocker b1(ui->macroName);
const QSignalBlocker b2(ui->runMacroInParallel);
const QSignalBlocker b3(ui->runMacroOnChange);
ui->macroName->setText(m.Name().c_str());
ui->runMacroInParallel->setChecked(m.RunInParallel());
ui->runMacroOnChange->setChecked(m.MatchOnChange());
}
ui->conditionsList->Clear();
ui->actionsList->Clear();
ui->elseActionsList->Clear();
m.ResetUIHelpers();
PopulateMacroConditions(m);
PopulateMacroActions(m);
PopulateMacroElseActions(m);
SetMacroEditAreaDisabled(false);
currentActionIdx = -1;
currentElseActionIdx = -1;
currentConditionIdx = -1;
HighlightControls();
if (m.IsGroup()) {
SetMacroEditAreaDisabled(true);
ui->macroName->setEnabled(true);
centerSplitterPosition(ui->macroActionConditionSplitter);
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
return;
}
if (!m.HasValidSplitterPositions()) {
centerSplitterPosition(ui->macroActionConditionSplitter);
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
return;
}
ui->macroActionConditionSplitter->setSizes(
m.GetActionConditionSplitterPosition());
ui->macroElseActionSplitter->setSizes(
m.GetElseActionSplitterPosition());
}
void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) const
{
ui->macroName->setDisabled(disable);
ui->runMacro->setDisabled(disable);
ui->runMacroInParallel->setDisabled(disable);
ui->runMacroOnChange->setDisabled(disable);
ui->macroActions->setDisabled(disable);
ui->macroConditions->setDisabled(disable);
ui->macroActionConditionSplitter->setDisabled(disable);
}
void AdvSceneSwitcher::HighlightAction(int idx, QColor color) const
{
ui->actionsList->Highlight(idx, color);
}
void AdvSceneSwitcher::HighlightElseAction(int idx, QColor color) const
{
ui->elseActionsList->Highlight(idx, color);
}
void AdvSceneSwitcher::HighlightCondition(int idx, QColor color) const
{
ui->conditionsList->Highlight(idx, color);
}
std::shared_ptr<Macro> AdvSceneSwitcher::GetSelectedMacro() const
{
return ui->macros->GetCurrentMacro();
}
std::vector<std::shared_ptr<Macro>> AdvSceneSwitcher::GetSelectedMacros() const
{
return ui->macros->GetCurrentMacros();
}
void AdvSceneSwitcher::MacroSelectionAboutToChange() const
{
if (loading) {
return;
}
if (!ui->macroName->isEnabled()) { // No macro is selected
return;
}
auto macro = GetMacroByQString(ui->macroName->text());
if (!macro) {
return;
}
macro->SetActionConditionSplitterPosition(
ui->macroActionConditionSplitter->sizes());
auto elsePos = ui->macroElseActionSplitter->sizes();
// If only conditions are visible maximize the actions to avoid neither
// actions nor elseActions being visible when the condition <-> action
// splitter is moved
if (elsePos[0] == 0 && elsePos[1] == 0) {
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
return;
}
macro->SetElseActionSplitterPosition(
ui->macroElseActionSplitter->sizes());
}
void AdvSceneSwitcher::MacroSelectionChanged()
{
if (loading) {
return;
}
auto macro = GetSelectedMacro();
if (!macro) {
SetMacroEditAreaDisabled(true);
ui->conditionsList->Clear();
ui->actionsList->Clear();
ui->elseActionsList->Clear();
ui->conditionsList->SetHelpMsgVisible(true);
ui->actionsList->SetHelpMsgVisible(true);
ui->elseActionsList->SetHelpMsgVisible(true);
centerSplitterPosition(ui->macroActionConditionSplitter);
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
return;
}
SetEditMacro(*macro);
if (GetGlobalMacroSettings()._saveSettingsOnMacroChange) {
obs_frontend_save();
}
}
void AdvSceneSwitcher::HighlightOnChange() const
{
if (!GetGlobalMacroSettings()._highlightActions &&
!GetGlobalMacroSettings()._highlightExecuted) {
return;
}
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (macro->OnChangePreventedActionsRecently()) {
HighlightWidget(ui->runMacroOnChange, Qt::yellow,
Qt::transparent, true);
}
}
static void resetSegmentHighlights(MacroSegmentList *list)
{
MacroSegmentEdit *widget = nullptr;
for (int i = 0; (widget = list->WidgetAt(i)); i++) {
if (widget && widget->Data()) {
(void)widget->Data()->GetHighlightAndReset();
}
}
}
void AdvSceneSwitcher::on_macroSettings_clicked()
{
GlobalMacroSettings prop = GetGlobalMacroSettings();
bool accepted = MacroSettingsDialog::AskForSettings(
this, prop, GetSelectedMacro().get());
if (!accepted) {
return;
}
GetGlobalMacroSettings() = prop;
emit HighlightMacrosChanged(prop._highlightExecuted);
// Reset highlights to avoid all macro segments being highlighted, which
// would have been highlighted at least once since the moment the
// highlighting was disabled
if (prop._highlightConditions) {
resetSegmentHighlights(ui->conditionsList);
}
if (prop._highlightActions) {
resetSegmentHighlights(ui->actionsList);
resetSegmentHighlights(ui->elseActionsList);
}
SetCheckIntervalTooLowVisibility();
}
static void moveControlsToSplitter(QSplitter *splitter, int idx,
QLayoutItem *item)
{
static int splitterHandleWidth = 32;
auto handle = splitter->handle(idx);
auto layout = item->layout();
int leftMargin, rightMargin;
layout->getContentsMargins(&leftMargin, nullptr, &rightMargin, nullptr);
layout->setContentsMargins(leftMargin, 0, rightMargin, 0);
handle->setLayout(layout);
splitter->setHandleWidth(splitterHandleWidth);
splitter->setStyleSheet("QSplitter::handle {background: transparent;}");
}
static bool shouldRestoreSplitter(const QList<int> &pos)
{
if (pos.size() == 0) {
return false;
}
for (int i = 0; i < pos.size(); ++i) {
if (pos[i] == 0) {
return false;
}
}
return true;
}
static void runSegmentHighligtChecksHelper(MacroSegmentList *list)
{
MacroSegmentEdit *widget = nullptr;
for (int i = 0; (widget = list->WidgetAt(i)); i++) {
if (widget->Data() && widget->Data()->GetHighlightAndReset()) {
list->Highlight(i);
}
}
}
static void runSegmentHighligtChecks(AdvSceneSwitcher *ss)
{
if (!ss || !HighlightUIElementsEnabled()) {
return;
}
auto macro = ss->GetSelectedMacro();
if (!macro) {
return;
}
const auto &settings = GetGlobalMacroSettings();
if (settings._highlightConditions) {
runSegmentHighligtChecksHelper(ss->ui->conditionsList);
}
if (settings._highlightActions) {
runSegmentHighligtChecksHelper(ss->ui->actionsList);
runSegmentHighligtChecksHelper(ss->ui->elseActionsList);
}
}
static QToolBar *
setupToolBar(const std::initializer_list<std::initializer_list<QWidget *>>
&widgetGroups)
{
auto toolbar = new QToolBar();
toolbar->setIconSize({16, 16});
QAction *lastSeperator = nullptr;
for (const auto &widgetGroup : widgetGroups) {
for (const auto &widget : widgetGroup) {
toolbar->addWidget(widget);
}
lastSeperator = toolbar->addSeparator();
}
if (lastSeperator) {
toolbar->removeAction(lastSeperator);
}
// Prevent "extension" button from showing up
toolbar->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
return toolbar;
}
void AdvSceneSwitcher::SetupMacroTab()
{
ui->macroElseActions->installEventFilter(this);
ui->macros->installEventFilter(this);
if (GetMacros().size() == 0 && !switcher->disableHints) {
addPulse = HighlightWidget(ui->macroAdd, QColor(Qt::green));
}
auto macroControls = setupToolBar({{ui->macroAdd, ui->macroRemove},
{ui->macroUp, ui->macroDown}});
ui->macroControlLayout->addWidget(macroControls);
ui->macros->Reset(GetMacros(),
GetGlobalMacroSettings()._highlightExecuted);
connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this,
SLOT(MacroSelectionAboutToChange()));
connect(ui->macros, SIGNAL(MacroSelectionChanged()), this,
SLOT(MacroSelectionChanged()));
ui->runMacro->SetMacroTree(ui->macros);
ui->conditionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editConditionHelp"));
connect(ui->conditionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroConditionSelectionChanged);
connect(ui->conditionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroConditionReorder);
ui->actionsList->SetHelpMsg(
obs_module_text("AdvSceneSwitcher.macroTab.editActionHelp"));
connect(ui->actionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroActionSelectionChanged);
connect(ui->actionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroActionReorder);
ui->elseActionsList->SetHelpMsg(obs_module_text(
"AdvSceneSwitcher.macroTab.editElseActionHelp"));
connect(ui->elseActionsList, &MacroSegmentList::SelectionChanged, this,
&AdvSceneSwitcher::MacroElseActionSelectionChanged);
connect(ui->elseActionsList, &MacroSegmentList::Reorder, this,
&AdvSceneSwitcher::MacroElseActionReorder);
ui->macros->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->macros, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroContextMenu);
ui->actionsList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->actionsList, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroActionsContextMenu);
ui->elseActionsList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->elseActionsList, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroElseActionsContextMenu);
ui->conditionsList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->conditionsList, &QWidget::customContextMenuRequested, this,
&AdvSceneSwitcher::ShowMacroConditionsContextMenu);
SetMacroEditAreaDisabled(true);
ui->macroPriorityWarning->setVisible(
switcher->functionNamesByPriority[0] != macro_func);
onChangeHighlightTimer.setInterval(1500);
connect(&onChangeHighlightTimer, SIGNAL(timeout()), this,
SLOT(HighlightOnChange()));
onChangeHighlightTimer.start();
// Set action and condition toolbars
const std::string pathPrefix =
GetDataFilePath("res/images/" + GetThemeTypeName());
SetButtonIcon(ui->actionTop, (pathPrefix + "DoubleUp.svg").c_str());
SetButtonIcon(ui->actionBottom,
(pathPrefix + "DoubleDown.svg").c_str());
SetButtonIcon(ui->elseActionTop, (pathPrefix + "DoubleUp.svg").c_str());
SetButtonIcon(ui->elseActionBottom,
(pathPrefix + "DoubleDown.svg").c_str());
SetButtonIcon(ui->conditionTop, (pathPrefix + "DoubleUp.svg").c_str());
SetButtonIcon(ui->conditionBottom,
(pathPrefix + "DoubleDown.svg").c_str());
SetButtonIcon(ui->toggleElseActions,
(pathPrefix + "NotEqual.svg").c_str());
auto conditionToolbar =
setupToolBar({{ui->conditionAdd, ui->conditionRemove},
{ui->conditionTop, ui->conditionUp,
ui->conditionDown, ui->conditionBottom}});
auto actionToolbar = setupToolBar({{ui->actionAdd, ui->actionRemove},
{ui->actionTop, ui->actionUp,
ui->actionDown, ui->actionBottom}});
auto elseActionToolbar =
setupToolBar({{ui->elseActionAdd, ui->elseActionRemove},
{ui->elseActionTop, ui->elseActionUp,
ui->elseActionDown, ui->elseActionBottom}});
ui->conditionControlsLayout->addWidget(conditionToolbar);
ui->actionControlsLayout->insertWidget(0, actionToolbar);
ui->elseActionControlsLayout->addWidget(elseActionToolbar);
// Move condition controls into splitter handle layout
moveControlsToSplitter(ui->macroActionConditionSplitter, 1,
ui->macroConditionsLayout->takeAt(1));
moveControlsToSplitter(ui->macroElseActionSplitter, 1,
ui->macroActionsLayout->takeAt(1));
// Override splitter cursor icon when hovering over controls in splitter
SetCursorOnWidgetHover(ui->conditionAdd, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionRemove,
Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionTop, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionUp, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionDown, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->conditionBottom,
Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionAdd, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionRemove, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionTop, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionUp, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionDown, Qt::CursorShape::ArrowCursor);
SetCursorOnWidgetHover(ui->actionBottom, Qt::CursorShape::ArrowCursor);
// Reserve more space for macro edit area than for the macro list
ui->macroListMacroEditSplitter->setStretchFactor(0, 1);
ui->macroListMacroEditSplitter->setStretchFactor(1, 4);
centerSplitterPosition(ui->macroActionConditionSplitter);
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
if (switcher->saveWindowGeo) {
if (shouldRestoreSplitter(
switcher->macroListMacroEditSplitterPosition)) {
ui->macroListMacroEditSplitter->setSizes(
switcher->macroListMacroEditSplitterPosition);
}
}
SetupSegmentCopyPasteShortcutHandlers(this);
// Macro segment highlight
auto timer = new QTimer(this);
connect(timer, &QTimer::timeout,
[this]() { runSegmentHighligtChecks(this); });
timer->start(1500);
}
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
{
QPoint globalPos = ui->macros->mapToGlobal(pos);
QMenu menu;
menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.contextMenuAdd"),
this, &AdvSceneSwitcher::on_macroAdd_clicked);
auto copy = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.copy"), this,
&AdvSceneSwitcher::CopyMacro);
copy->setEnabled(ui->macros->SingleItemSelected() &&
!ui->macros->GroupsSelected());
menu.addSeparator();
auto group = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.group"), ui->macros,
&MacroTree::GroupSelectedItems);
group->setDisabled(ui->macros->GroupedItemsSelected() ||
ui->macros->GroupsSelected() ||
ui->macros->SelectionEmpty());
auto ungroup = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.ungroup"),
ui->macros, &MacroTree::UngroupSelectedGroups);
ungroup->setEnabled(ui->macros->GroupsSelected());
menu.addSeparator();
auto rename = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.rename"), this,
&AdvSceneSwitcher::RenameSelectedMacro);
rename->setEnabled(ui->macros->SingleItemSelected());
auto remove = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.remove"), this,
&AdvSceneSwitcher::on_macroRemove_clicked);
remove->setDisabled(ui->macros->SelectionEmpty());
menu.addSeparator();
auto exportAction = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.export"), this,
&AdvSceneSwitcher::ExportMacros);
exportAction->setDisabled(ui->macros->SelectionEmpty());
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.import"),
this, &AdvSceneSwitcher::ImportMacros);
menu.exec(globalPos);
}
static bool handleCustomLabelRename(MacroSegmentEdit *segmentEdit)
{
std::string label;
auto segment = segmentEdit->Data();
if (!segment) {
return false;
}
bool accepted = NameDialog::AskForName(
GetSettingsWindow(),
obs_module_text(
"AdvSceneSwitcher.macroTab.segment.setCustomLabel"),
"", label, QString::fromStdString(segment->GetCustomLabel()));
if (!accepted) {
return false;
}
segment->SetCustomLabel(label);
segmentEdit->HeaderInfoChanged("");
return true;
}
static void handleCustomLabelEnableChange(MacroSegmentEdit *segmentEdit,
QAction *contextMenuOption)
{
bool enable = contextMenuOption->isChecked();
auto segment = segmentEdit->Data();
segment->SetUseCustomLabel(enable);
if (!enable) {
segmentEdit->HeaderInfoChanged(
QString::fromStdString(segment->GetShortDesc()));
return;
}
if (!handleCustomLabelRename(segmentEdit)) {
segment->SetUseCustomLabel(false);
}
}
static void setupSegmentLabelContextMenuEntries(MacroSegmentEdit *segmentEdit,
QMenu &menu)
{
if (!segmentEdit) {
return;
}
auto segment = segmentEdit ? segmentEdit->Data() : nullptr;
const bool customLabelIsEnabled = segment &&
segment->GetUseCustomLabel();
auto enableCustomLabel = menu.addAction(obs_module_text(
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
enableCustomLabel->setCheckable(true);
enableCustomLabel->setChecked(customLabelIsEnabled);
QWidget::connect(enableCustomLabel, &QAction::triggered,
[segmentEdit, enableCustomLabel]() {
handleCustomLabelEnableChange(
segmentEdit, enableCustomLabel);
});
if (!customLabelIsEnabled) {
return;
}
auto customLabelRename = menu.addAction(obs_module_text(
"AdvSceneSwitcher.macroTab.segment.customLabelRename"));
QWidget::connect(customLabelRename, &QAction::triggered,
[segmentEdit]() {
handleCustomLabelRename(segmentEdit);
});
}
static void setupCopyPasteContextMenuEnry(AdvSceneSwitcher *ss,
MacroSegmentEdit *segmentEdit,
QMenu &menu)
{
auto copy = menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
[ss]() { ss->CopyMacroSegment(); });
copy->setEnabled(!!segmentEdit);
bool pasteAsElseAction = true;
const char *pasteText = "AdvSceneSwitcher.macroTab.segment.paste";
if (MacroActionIsInClipboard()) {
if (IsCursorInWidgetArea(ss->ui->macroActions)) {
pasteAsElseAction = false;
pasteText =
"AdvSceneSwitcher.macroTab.segment.pasteAction";
} else if (IsCursorInWidgetArea(ss->ui->macroElseActions)) {
pasteAsElseAction = true;
pasteText =
"AdvSceneSwitcher.macroTab.segment.pasteElseAction";
}
}
auto paste = menu.addAction(
obs_module_text(pasteText), ss, [ss, pasteAsElseAction]() {
SetCopySegmentTargetActionType(pasteAsElseAction);
ss->PasteMacroSegment();
});
paste->setEnabled(MacroSegmentIsInClipboard());
}
static void setupRemoveContextMenuEnry(
AdvSceneSwitcher *ss,
const std::function<void(AdvSceneSwitcher *, int)> &remove,
const QPoint &pos, MacroSegmentList *list, QMenu &menu)
{
const auto segmentEditIndex = list->IndexAt(pos);
if (segmentEditIndex == -1) {
return;
}
menu.addAction(
obs_module_text("AdvSceneSwitcher.macroTab.segment.remove"), ss,
[ss, remove, segmentEditIndex]() {
remove(ss, segmentEditIndex);
});
}
static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
std::function<void(AdvSceneSwitcher *, int)> remove,
std::function<void(AdvSceneSwitcher *)> expand,
std::function<void(AdvSceneSwitcher *)> collapse,
std::function<void(AdvSceneSwitcher *)> maximize,
std::function<void(AdvSceneSwitcher *)> minimize,
MacroSegmentList *list)
{
QMenu menu;
auto segmentEdit = list->WidgetAt(pos);
setupCopyPasteContextMenuEnry(ss, segmentEdit, menu);
menu.addSeparator();
setupRemoveContextMenuEnry(ss, remove, pos, list, menu);
menu.addSeparator();
setupSegmentLabelContextMenuEntries(segmentEdit, menu);
menu.addSeparator();
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
ss, [ss, expand]() { expand(ss); });
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
ss, [ss, collapse]() { collapse(ss); });
menu.addSeparator();
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
ss, [ss, maximize]() { maximize(ss); });
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
ss, [ss, minimize]() { minimize(ss); });
menu.exec(list->mapToGlobal(pos));
}
void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroAction,
&AdvSceneSwitcher::ExpandAllActions,
&AdvSceneSwitcher::CollapseAllActions,
&AdvSceneSwitcher::MaximizeActions,
&AdvSceneSwitcher::MinimizeActions, ui->actionsList);
}
void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroElseAction,
&AdvSceneSwitcher::ExpandAllElseActions,
&AdvSceneSwitcher::CollapseAllElseActions,
&AdvSceneSwitcher::MaximizeElseActions,
&AdvSceneSwitcher::MinimizeElseActions,
ui->elseActionsList);
}
void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos)
{
setupConextMenu(this, pos, &AdvSceneSwitcher::RemoveMacroCondition,
&AdvSceneSwitcher::ExpandAllConditions,
&AdvSceneSwitcher::CollapseAllConditions,
&AdvSceneSwitcher::MaximizeConditions,
&AdvSceneSwitcher::MinimizeConditions,
ui->conditionsList);
}
void AdvSceneSwitcher::CopyMacro()
{
const auto macro = GetSelectedMacro();
if (!macro) {
return;
}
std::string format = macro->Name() + " %1";
std::string name;
std::shared_ptr<Macro> newMacro;
if (!AddNewMacro(newMacro, name, format)) {
return;
}
OBSDataAutoRelease data = obs_data_create();
macro->Save(data, true);
newMacro->Load(data);
newMacro->PostLoad();
newMacro->SetName(name);
RunPostLoadSteps();
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
ui->macros->Add(newMacro, macro);
if (addPulse) {
addPulse->deleteLater();
addPulse = nullptr;
}
emit MacroAdded(QString::fromStdString(name));
}
static void setCollapsedHelper(const std::shared_ptr<Macro> &m,
MacroSegmentList *list, bool collapsed)
{
if (!m) {
return;
}
list->SetCollapsed(collapsed);
}
void AdvSceneSwitcher::ExpandAllActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, false);
}
void AdvSceneSwitcher::ExpandAllElseActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, false);
}
void AdvSceneSwitcher::ExpandAllConditions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, false);
}
void AdvSceneSwitcher::CollapseAllActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->actionsList, true);
}
void AdvSceneSwitcher::CollapseAllElseActions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, true);
}
void AdvSceneSwitcher::CollapseAllConditions() const
{
setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, true);
}
static void reduceSizeOfSplitterIdx(QSplitter *splitter, int idx)
{
auto sizes = splitter->sizes();
int sum = sizes[0] + sizes[1];
int reducedSize = sum / 10;
sizes[idx] = reducedSize;
sizes[(idx + 1) % 2] = sum - reducedSize;
splitter->setSizes(sizes);
}
void AdvSceneSwitcher::MinimizeActions() const
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (macro->ElseActions().size() == 0) {
reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 1);
} else {
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 1);
}
}
void AdvSceneSwitcher::MaximizeActions() const
{
MinimizeElseActions();
MinimizeConditions();
}
void AdvSceneSwitcher::MinimizeElseActions() const
{
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
if (macro->ElseActions().size() == 0) {
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
} else {
reduceSizeOfSplitterIdx(ui->macroElseActionSplitter, 1);
}
}
void AdvSceneSwitcher::MaximizeElseActions() const
{
MinimizeConditions();
reduceSizeOfSplitterIdx(ui->macroElseActionSplitter, 0);
}
void AdvSceneSwitcher::MinimizeConditions() const
{
reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 0);
}
void AdvSceneSwitcher::MaximizeConditions() const
{
MinimizeElseActions();
MinimizeActions();
}
void AdvSceneSwitcher::on_toggleElseActions_clicked() const
{
auto elsePosition = ui->macroElseActionSplitter->sizes();
if (elsePosition[1] == 0) {
centerSplitterPosition(ui->macroElseActionSplitter);
return;
}
maximizeFirstSplitterEntry(ui->macroElseActionSplitter);
}
void AdvSceneSwitcher::SetElseActionsStateToHidden() const
{
ui->toggleElseActions->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.toggleElseActions.show.tooltip"));
ui->toggleElseActions->setChecked(false);
}
void AdvSceneSwitcher::SetElseActionsStateToVisible() const
{
ui->toggleElseActions->setToolTip(obs_module_text(
"AdvSceneSwitcher.macroTab.toggleElseActions.hide.tooltip"));
ui->toggleElseActions->setChecked(true);
}
bool AdvSceneSwitcher::MacroTabIsInFocus()
{
return isActiveWindow() && isAncestorOf(focusWidget()) &&
(ui->tabWidget->currentWidget()->objectName() == "macroTab");
}
void AdvSceneSwitcher::UpMacroSegementHotkey()
{
if (!MacroTabIsInFocus()) {
return;
}
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
int actionSize = macro->Actions().size();
int conditionSize = macro->Conditions().size();
if (currentActionIdx == -1 && currentConditionIdx == -1) {
if (lastInteracted == MacroSection::CONDITIONS) {
if (conditionSize == 0) {
MacroActionSelectionChanged(0);
} else {
MacroConditionSelectionChanged(0);
}
} else {
if (actionSize == 0) {
MacroConditionSelectionChanged(0);
} else {
MacroActionSelectionChanged(0);
}
}
return;
}
if (currentActionIdx > 0) {
MacroActionSelectionChanged(currentActionIdx - 1);
return;
}
if (currentConditionIdx > 0) {
MacroConditionSelectionChanged(currentConditionIdx - 1);
return;
}
if (currentActionIdx == 0) {
if (conditionSize == 0) {
MacroActionSelectionChanged(actionSize - 1);
} else {
MacroConditionSelectionChanged(conditionSize - 1);
}
return;
}
if (currentConditionIdx == 0) {
if (actionSize == 0) {
MacroConditionSelectionChanged(conditionSize - 1);
} else {
MacroActionSelectionChanged(actionSize - 1);
}
return;
}
}
void AdvSceneSwitcher::DownMacroSegementHotkey()
{
if (!MacroTabIsInFocus()) {
return;
}
auto macro = GetSelectedMacro();
if (!macro) {
return;
}
int actionSize = macro->Actions().size();
int conditionSize = macro->Conditions().size();
if (currentActionIdx == -1 && currentConditionIdx == -1) {
if (lastInteracted == MacroSection::CONDITIONS) {
if (conditionSize == 0) {
MacroActionSelectionChanged(0);
} else {
MacroConditionSelectionChanged(0);
}
} else {
if (actionSize == 0) {
MacroConditionSelectionChanged(0);
} else {
MacroActionSelectionChanged(0);
}
}
return;
}
if (currentActionIdx < actionSize - 1) {
MacroActionSelectionChanged(currentActionIdx + 1);
return;
}
if (currentConditionIdx < conditionSize - 1) {
MacroConditionSelectionChanged(currentConditionIdx + 1);
return;
}
if (currentActionIdx == actionSize - 1) {
if (conditionSize == 0) {
MacroActionSelectionChanged(0);
} else {
MacroConditionSelectionChanged(0);
}
return;
}
if (currentConditionIdx == conditionSize - 1) {
if (actionSize == 0) {
MacroConditionSelectionChanged(0);
} else {
MacroActionSelectionChanged(0);
}
return;
}
}
void AdvSceneSwitcher::DeleteMacroSegementHotkey()
{
if (!MacroTabIsInFocus()) {
return;
}
if (currentActionIdx != -1) {
RemoveMacroAction(currentActionIdx);
} else if (currentConditionIdx != -1) {
RemoveMacroCondition(currentConditionIdx);
}
}
static void fade(QWidget *widget, bool fadeOut)
{
const double fadeOutOpacity = 0.3;
// Don't use exactly 1.0 as for some reason this causes buttons in
// macroSplitter handle layout to not be redrawn unless mousing over
// them
const double fadeInOpacity = 0.99;
auto curEffect = widget->graphicsEffect();
if (curEffect) {
auto curOpacity =
dynamic_cast<QGraphicsOpacityEffect *>(curEffect);
if (curOpacity &&
((fadeOut && DoubleEquals(curOpacity->opacity(),
fadeOutOpacity, 0.0001)) ||
(!fadeOut && DoubleEquals(curOpacity->opacity(),
fadeInOpacity, 0.0001)))) {
return;
}
} else if (!fadeOut) {
return;
}
delete curEffect;
QGraphicsOpacityEffect *opacityEffect = new QGraphicsOpacityEffect();
widget->setGraphicsEffect(opacityEffect);
QPropertyAnimation *animation =
new QPropertyAnimation(opacityEffect, "opacity");
animation->setDuration(350);
animation->setStartValue(fadeOut ? fadeInOpacity : fadeOutOpacity);
animation->setEndValue(fadeOut ? fadeOutOpacity : fadeInOpacity);
animation->setEasingCurve(QEasingCurve::OutQuint);
animation->start(QPropertyAnimation::DeleteWhenStopped);
}
static void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
{
for (const auto &widget : widgets) {
fade(widget, fadeOut);
}
}
void AdvSceneSwitcher::HighlightControls() const
{
const std::vector<QWidget *> conditionControls{
ui->conditionAdd, ui->conditionRemove, ui->conditionTop,
ui->conditionUp, ui->conditionDown, ui->conditionBottom,
};
const std::vector<QWidget *> actionControls{
ui->actionAdd, ui->actionRemove, ui->actionTop,
ui->actionUp, ui->actionDown, ui->actionBottom,
};
const std::vector<QWidget *> elseActionControls{
ui->elseActionAdd, ui->elseActionRemove, ui->elseActionTop,
ui->elseActionUp, ui->elseActionDown, ui->elseActionBottom,
};
if ((currentActionIdx == -1 && currentConditionIdx == -1 &&
currentElseActionIdx == -1)) {
fadeWidgets(conditionControls, false);
fadeWidgets(actionControls, false);
fadeWidgets(elseActionControls, false);
} else if (currentConditionIdx != -1) {
fadeWidgets(conditionControls, false);
fadeWidgets(actionControls, true);
fadeWidgets(elseActionControls, true);
} else if (currentActionIdx != -1) {
fadeWidgets(conditionControls, true);
fadeWidgets(actionControls, false);
fadeWidgets(elseActionControls, true);
} else if (currentElseActionIdx != -1) {
fadeWidgets(conditionControls, true);
fadeWidgets(actionControls, true);
fadeWidgets(elseActionControls, false);
} else {
assert(false);
}
}
} // namespace advss