mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-11-15 03:36:53 -06:00
1281 lines
31 KiB
C++
1281 lines
31 KiB
C++
#include "catch.hpp"
|
|
#include "macro-test-helpers.hpp"
|
|
#include "ui-test-helpers.hpp"
|
|
|
|
#include <macro.hpp>
|
|
#include <macro-helpers.hpp>
|
|
#include <macro-search.hpp>
|
|
#include <macro-signals.hpp>
|
|
#include <macro-tree.hpp>
|
|
#include <regex-config.hpp>
|
|
|
|
#include <QComboBox>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QPushButton>
|
|
#include <QVBoxLayout>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <random>
|
|
|
|
namespace {
|
|
|
|
using namespace advss;
|
|
using namespace advss::test;
|
|
|
|
using Names = std::vector<std::string>;
|
|
|
|
const std::string newGroupName = "AdvSceneSwitcher.macroTab.defaultGroupName";
|
|
|
|
bool isHiddenByCollapsedAncestor(const Macro *macro)
|
|
{
|
|
for (auto parent = macro->Parent(); parent; parent = parent->Parent()) {
|
|
if (parent->IsCollapsed()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Group names cannot be generated in tests as obs_module_text() returns the
|
|
// locale key, which does not contain a placeholder for the number
|
|
void renameNewGroup(const std::string &name)
|
|
{
|
|
auto group = GetMacro(newGroupName);
|
|
REQUIRE(group);
|
|
group->SetName(name);
|
|
emit MacroSignalManager::Instance()->Rename(
|
|
QString::fromStdString(newGroupName),
|
|
QString::fromStdString(name));
|
|
}
|
|
|
|
class TreeFixture {
|
|
public:
|
|
explicit TreeFixture(const std::string &layout)
|
|
: _window(std::make_unique<QWidget>()),
|
|
_tree(new MacroTree(_window.get()))
|
|
{
|
|
GetMacroSearchSettings() = {};
|
|
SetupMacros(layout);
|
|
|
|
// Same settings as in forms/advanced-scene-switcher.ui
|
|
_tree->setDragEnabled(true);
|
|
_tree->setDragDropMode(QAbstractItemView::InternalMove);
|
|
_tree->setDefaultDropAction(Qt::TargetMoveAction);
|
|
_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
_tree->setDropIndicatorShown(true);
|
|
|
|
auto layoutWidget = new QVBoxLayout(_window.get());
|
|
layoutWidget->addWidget(_tree);
|
|
_window->resize(300, 450);
|
|
_window->show();
|
|
|
|
Reset();
|
|
}
|
|
|
|
~TreeFixture()
|
|
{
|
|
GetMacroSearchSettings() = {};
|
|
_window.reset();
|
|
ProcessEvents();
|
|
GetTopLevelMacros().clear();
|
|
}
|
|
|
|
MacroTree *Tree() const { return _tree; }
|
|
QWidget *Viewport() const { return _tree->viewport(); }
|
|
|
|
void Reset()
|
|
{
|
|
_tree->Reset(GetTopLevelMacros(), false);
|
|
_checker = new ModelChecker(_tree->model(),
|
|
Qt::AccessibleTextRole);
|
|
ProcessEvents();
|
|
}
|
|
|
|
void SetupSearch()
|
|
{
|
|
auto searchLayout = new QHBoxLayout();
|
|
_searchText = new QLineEdit();
|
|
auto clear = new QPushButton();
|
|
auto type = new QComboBox();
|
|
auto regex = new RegexConfigWidget();
|
|
auto showSettings = new QPushButton();
|
|
searchLayout->addWidget(_searchText);
|
|
searchLayout->addWidget(clear);
|
|
searchLayout->addWidget(type);
|
|
searchLayout->addWidget(regex);
|
|
searchLayout->addWidget(showSettings);
|
|
static_cast<QVBoxLayout *>(_window->layout())
|
|
->insertLayout(0, searchLayout);
|
|
|
|
GetMacroSearchSettings().showAlways = true;
|
|
auto tree = _tree;
|
|
SetupMacroSearchWidgets(searchLayout, _searchText, clear, type,
|
|
regex, showSettings,
|
|
[tree]() { tree->RefreshFilter(); });
|
|
ProcessEvents();
|
|
}
|
|
|
|
void Search(const QString &text)
|
|
{
|
|
_searchText->setText(text);
|
|
ProcessEvents();
|
|
}
|
|
|
|
QModelIndex IndexOf(const std::string &name) const
|
|
{
|
|
return IndexOf(QString::fromStdString(name), QModelIndex());
|
|
}
|
|
|
|
QPoint RowCenter(const std::string &name) const
|
|
{
|
|
const auto index = IndexOf(name);
|
|
REQUIRE(index.isValid());
|
|
return _tree->visualRect(index).center();
|
|
}
|
|
|
|
QPoint RowTop(const std::string &name) const
|
|
{
|
|
const auto index = IndexOf(name);
|
|
REQUIRE(index.isValid());
|
|
return _tree->visualRect(index).topLeft() + QPoint(40, 1);
|
|
}
|
|
|
|
QPoint RowBottom(const std::string &name) const
|
|
{
|
|
const auto index = IndexOf(name);
|
|
REQUIRE(index.isValid());
|
|
return _tree->visualRect(index).bottomLeft() + QPoint(40, -1);
|
|
}
|
|
|
|
void ClickRow(const std::string &name,
|
|
Qt::KeyboardModifiers modifiers = Qt::NoModifier)
|
|
{
|
|
Click(Viewport(), RowCenter(name), modifiers);
|
|
}
|
|
|
|
void SelectRows(const Names &names)
|
|
{
|
|
bool first = true;
|
|
for (const auto &name : names) {
|
|
ClickRow(name,
|
|
first ? Qt::NoModifier : Qt::ControlModifier);
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
void Drag(const QPoint &from, const QPoint &to)
|
|
{
|
|
DragAndDrop(Viewport(), from, to);
|
|
}
|
|
|
|
QWidget *RowWidget(const std::string &name) const
|
|
{
|
|
const auto index = IndexOf(name);
|
|
REQUIRE(index.isValid());
|
|
return _tree->indexWidget(index);
|
|
}
|
|
|
|
QCheckBox *ExpandCheckBox(const std::string &name) const
|
|
{
|
|
auto widget = RowWidget(name);
|
|
REQUIRE(widget);
|
|
return widget->findChild<SourceTreeSubItemCheckBox *>();
|
|
}
|
|
|
|
QCheckBox *RunningCheckBox(const std::string &name) const
|
|
{
|
|
auto widget = RowWidget(name);
|
|
REQUIRE(widget);
|
|
for (auto checkBox : widget->findChildren<QCheckBox *>()) {
|
|
if (!qobject_cast<SourceTreeSubItemCheckBox *>(
|
|
checkBox)) {
|
|
return checkBox;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void ClickCheckBox(QCheckBox *checkBox)
|
|
{
|
|
REQUIRE(checkBox);
|
|
Click(checkBox, checkBox->rect().center());
|
|
}
|
|
|
|
Names CurrentMacros() const
|
|
{
|
|
Names result;
|
|
for (const auto ¯o : _tree->GetCurrentMacros()) {
|
|
result.emplace_back(macro->Name());
|
|
}
|
|
std::sort(result.begin(), result.end());
|
|
return result;
|
|
}
|
|
|
|
Names VisibleRows() const
|
|
{
|
|
std::vector<std::pair<int, std::string>> rows;
|
|
CollectVisibleRows(QModelIndex(), rows);
|
|
std::sort(rows.begin(), rows.end());
|
|
Names result;
|
|
for (const auto &[_, name] : rows) {
|
|
result.emplace_back(name);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Names ExpectedVisibleRows() const
|
|
{
|
|
Names result;
|
|
for (const auto ¯o : GetTopLevelMacros()) {
|
|
if (isHiddenByCollapsedAncestor(macro.get()) ||
|
|
!MacroMatchesSearchFilter(macro.get())) {
|
|
continue;
|
|
}
|
|
result.emplace_back(macro->Name());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void CheckInvariants() const
|
|
{
|
|
ProcessEvents();
|
|
_checker->CheckStructure();
|
|
for (const auto &error : _checker->Errors()) {
|
|
FAIL_CHECK(error);
|
|
}
|
|
CheckGroupStructure();
|
|
REQUIRE(VisibleRows() == ExpectedVisibleRows());
|
|
for (const auto &name : VisibleRows()) {
|
|
CheckRowWidget(name);
|
|
}
|
|
}
|
|
|
|
private:
|
|
QModelIndex IndexOf(const QString &name,
|
|
const QModelIndex &parent) const
|
|
{
|
|
auto model = _tree->model();
|
|
for (int row = 0; row < model->rowCount(parent); row++) {
|
|
const auto index = model->index(row, 0, parent);
|
|
if (index.data(Qt::AccessibleTextRole).toString() ==
|
|
name) {
|
|
return index;
|
|
}
|
|
const auto child = IndexOf(name, index);
|
|
if (child.isValid()) {
|
|
return child;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void
|
|
CollectVisibleRows(const QModelIndex &parent,
|
|
std::vector<std::pair<int, std::string>> &rows) const
|
|
{
|
|
auto model = _tree->model();
|
|
for (int row = 0; row < model->rowCount(parent); row++) {
|
|
const auto index = model->index(row, 0, parent);
|
|
const auto rect = _tree->visualRect(index);
|
|
if (rect.isValid() && rect.height() > 0) {
|
|
rows.emplace_back(
|
|
rect.top(),
|
|
index.data(Qt::AccessibleTextRole)
|
|
.toString()
|
|
.toStdString());
|
|
}
|
|
CollectVisibleRows(index, rows);
|
|
}
|
|
}
|
|
|
|
void CheckGroupStructure() const
|
|
{
|
|
const auto ¯os = GetTopLevelMacros();
|
|
std::vector<Macro *> openGroups;
|
|
for (const auto ¯o : macros) {
|
|
INFO("macro \"" << macro->Name() << "\" in "
|
|
<< DescribeMacros());
|
|
const auto parent = macro->Parent().get();
|
|
while (!openGroups.empty() &&
|
|
openGroups.back() != parent) {
|
|
openGroups.pop_back();
|
|
}
|
|
if (parent) {
|
|
REQUIRE(!openGroups.empty());
|
|
}
|
|
if (macro->IsGroup()) {
|
|
openGroups.emplace_back(macro.get());
|
|
}
|
|
}
|
|
|
|
for (const auto ¯o : macros) {
|
|
if (!macro->IsGroup()) {
|
|
continue;
|
|
}
|
|
INFO("group \"" << macro->Name() << "\" in "
|
|
<< DescribeMacros());
|
|
const auto descendants = std::count_if(
|
|
macros.begin(), macros.end(),
|
|
[¯o](const std::shared_ptr<Macro> &m) {
|
|
return IsMacroInGroup(m.get(),
|
|
macro.get());
|
|
});
|
|
REQUIRE(macro->GroupSize() == (uint32_t)descendants);
|
|
}
|
|
}
|
|
|
|
void CheckRowWidget(const std::string &name) const
|
|
{
|
|
INFO("row \"" << name << "\" in " << DescribeMacros());
|
|
auto macro = GetMacro(name);
|
|
REQUIRE(macro);
|
|
auto widget = RowWidget(name);
|
|
REQUIRE(widget);
|
|
|
|
QString label;
|
|
for (auto child : widget->findChildren<QLabel *>()) {
|
|
if (!child->text().isEmpty()) {
|
|
label = child->text();
|
|
}
|
|
}
|
|
REQUIRE(label.toStdString() == name);
|
|
|
|
auto expand = ExpandCheckBox(name);
|
|
REQUIRE(macro->IsGroup() == (expand != nullptr));
|
|
if (expand) {
|
|
REQUIRE(expand->isChecked() == macro->IsCollapsed());
|
|
}
|
|
|
|
auto running = RunningCheckBox(name);
|
|
REQUIRE(running);
|
|
if (!macro->IsGroup()) {
|
|
REQUIRE(running->isChecked() == !macro->Paused());
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<QWidget> _window;
|
|
MacroTree *_tree;
|
|
ModelChecker *_checker = nullptr;
|
|
QLineEdit *_searchText = nullptr;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("Macro tree shows macros and groups", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D, G2{E, F}, H");
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"A", "G1", "B", "C", "D", "G2", "H"});
|
|
}
|
|
|
|
TEST_CASE("Macro tree shows nested groups", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, G1[B, G2{C, G3[D]}, E], F");
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"A", "G1", "B", "G2", "E", "F"});
|
|
|
|
f.ClickCheckBox(f.ExpandCheckBox("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() ==
|
|
Names{"A", "G1", "B", "G2", "C", "G3", "D", "E", "F"});
|
|
|
|
f.Tree()->Up(GetMacro("G3"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, G2[G3[D], C], E], F");
|
|
|
|
f.Tree()->Remove(GetMacro("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, E], F");
|
|
}
|
|
|
|
TEST_CASE("Macro tree selection", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, G1{B, C}, D, G2[E, F], H");
|
|
|
|
SECTION("Single click")
|
|
{
|
|
f.ClickRow("E");
|
|
REQUIRE(f.Tree()->GetCurrentMacro()->Name() == "E");
|
|
REQUIRE(f.Tree()->SingleItemSelected());
|
|
REQUIRE(f.Tree()->CanGroupSelection());
|
|
REQUIRE_FALSE(f.Tree()->GroupsSelected());
|
|
}
|
|
|
|
SECTION("Rows after collapsed group")
|
|
{
|
|
f.ClickRow("H");
|
|
REQUIRE(f.Tree()->GetCurrentMacro()->Name() == "H");
|
|
f.ClickRow("D");
|
|
REQUIRE(f.Tree()->GetCurrentMacro()->Name() == "D");
|
|
}
|
|
|
|
SECTION("Ctrl click")
|
|
{
|
|
f.ClickRow("A");
|
|
f.ClickRow("F", Qt::ControlModifier);
|
|
f.ClickRow("G1", Qt::ControlModifier);
|
|
REQUIRE(f.CurrentMacros() == Names{"A", "F", "G1"});
|
|
REQUIRE(f.Tree()->GroupsSelected());
|
|
REQUIRE_FALSE(f.Tree()->CanGroupSelection());
|
|
REQUIRE_FALSE(f.Tree()->SingleItemSelected());
|
|
}
|
|
|
|
SECTION("Shift click")
|
|
{
|
|
f.ClickRow("D");
|
|
f.ClickRow("F", Qt::ShiftModifier);
|
|
REQUIRE(f.CurrentMacros() == Names{"D", "E", "F", "G2"});
|
|
// E and F are part of the selected G2
|
|
REQUIRE(f.Tree()->CanGroupSelection());
|
|
}
|
|
|
|
f.CheckInvariants();
|
|
}
|
|
|
|
TEST_CASE("Macro tree expand and collapse", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D, G2{E}");
|
|
|
|
SECTION("Expand checkbox")
|
|
{
|
|
f.ClickCheckBox(f.ExpandCheckBox("G1"));
|
|
REQUIRE(GetMacro("G1")->IsCollapsed());
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"A", "G1", "D", "G2"});
|
|
|
|
f.ClickCheckBox(f.ExpandCheckBox("G2"));
|
|
REQUIRE_FALSE(GetMacro("G2")->IsCollapsed());
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"A", "G1", "D", "G2", "E"});
|
|
|
|
f.ClickCheckBox(f.ExpandCheckBox("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() ==
|
|
Names{"A", "G1", "B", "C", "D", "G2", "E"});
|
|
}
|
|
|
|
SECTION("Double click")
|
|
{
|
|
DoubleClick(f.Viewport(), f.RowCenter("G2"));
|
|
REQUIRE_FALSE(GetMacro("G2")->IsCollapsed());
|
|
f.CheckInvariants();
|
|
|
|
DoubleClick(f.Viewport(), f.RowCenter("G1"));
|
|
REQUIRE(GetMacro("G1")->IsCollapsed());
|
|
f.CheckInvariants();
|
|
}
|
|
|
|
SECTION("Expand and collapse all")
|
|
{
|
|
f.Tree()->CollapseAll();
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"A", "G1", "D", "G2"});
|
|
|
|
f.Tree()->ExpandAll();
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() ==
|
|
Names{"A", "G1", "B", "C", "D", "G2", "E"});
|
|
}
|
|
|
|
SECTION("Selecting row after collapsing")
|
|
{
|
|
f.ClickCheckBox(f.ExpandCheckBox("G1"));
|
|
f.ClickRow("D");
|
|
REQUIRE(f.Tree()->GetCurrentMacro()->Name() == "D");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree add", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, G1[B], C, G2{D}");
|
|
auto newMacro = std::make_shared<Macro>("X");
|
|
|
|
SECTION("Append")
|
|
{
|
|
f.Tree()->Add(newMacro);
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B], C, G2{D}, X");
|
|
REQUIRE(f.Tree()->GetCurrentMacro() == newMacro);
|
|
}
|
|
|
|
SECTION("After top level macro")
|
|
{
|
|
f.Tree()->Add(newMacro, GetMacro("A"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, X, G1[B], C, G2{D}");
|
|
}
|
|
|
|
SECTION("After macro following collapsed group")
|
|
{
|
|
f.Tree()->Add(newMacro, GetMacro("C"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B], C, X, G2{D}");
|
|
}
|
|
|
|
// Mirrors AdvSceneSwitcher::on_macroAdd_clicked() with a group selected
|
|
SECTION("Into group")
|
|
{
|
|
auto group = GetMacro("G2");
|
|
f.Tree()->ExpandGroup(group);
|
|
Macro::PrepareMoveToGroup(group, newMacro);
|
|
f.Tree()->AddToGroup(newMacro, group);
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B], C, G2[X, D]");
|
|
}
|
|
|
|
// Mirrors AdvSceneSwitcher::on_macroAdd_clicked() with a subitem
|
|
// selected
|
|
SECTION("After subitem")
|
|
{
|
|
Macro::PrepareMoveToGroup(GetMacro("G1"), newMacro);
|
|
f.Tree()->Add(newMacro, GetMacro("B"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, X], C, G2{D}");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree remove", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D, G2{E, F}, H");
|
|
|
|
SECTION("Subitem")
|
|
{
|
|
f.Tree()->Remove(GetMacro("B"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[C], D, G2{E, F}, H");
|
|
|
|
f.Tree()->Remove(GetMacro("C"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[], D, G2{E, F}, H");
|
|
}
|
|
|
|
SECTION("Expanded group")
|
|
{
|
|
f.Tree()->Remove(GetMacro("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, D, G2{E, F}, H");
|
|
}
|
|
|
|
SECTION("Collapsed group")
|
|
{
|
|
f.Tree()->Remove(GetMacro("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, C], D, H");
|
|
}
|
|
|
|
SECTION("Macro after collapsed group")
|
|
{
|
|
f.Tree()->Remove(GetMacro("H"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, C], D, G2{E, F}");
|
|
}
|
|
|
|
SECTION("Everything")
|
|
{
|
|
for (const auto &name : {"H", "A", "G2", "B", "G1", "D"}) {
|
|
f.Tree()->Remove(GetMacro(name));
|
|
f.CheckInvariants();
|
|
}
|
|
REQUIRE(GetTopLevelMacros().empty());
|
|
}
|
|
|
|
SECTION("Selected macro")
|
|
{
|
|
f.ClickRow("D");
|
|
f.Tree()->Remove(f.Tree()->GetCurrentMacro());
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, C], G2{E, F}, H");
|
|
REQUIRE(f.Tree()->SelectionEmpty());
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree up and down", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, B, G1[C, D], E, G2{F}, H");
|
|
auto up = [&](const char *name) {
|
|
f.Tree()->Up(GetMacro(name));
|
|
f.CheckInvariants();
|
|
};
|
|
auto down = [&](const char *name) {
|
|
f.Tree()->Down(GetMacro(name));
|
|
f.CheckInvariants();
|
|
};
|
|
|
|
SECTION("Top level")
|
|
{
|
|
up("B");
|
|
REQUIRE(DescribeMacros() == "B, A, G1[C, D], E, G2{F}, H");
|
|
down("B");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[C, D], E, G2{F}, H");
|
|
}
|
|
|
|
SECTION("First and last")
|
|
{
|
|
up("A");
|
|
down("H");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[C, D], E, G2{F}, H");
|
|
}
|
|
|
|
SECTION("Within group")
|
|
{
|
|
up("D");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[D, C], E, G2{F}, H");
|
|
up("D");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[D, C], E, G2{F}, H");
|
|
down("D");
|
|
down("D");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[C, D], E, G2{F}, H");
|
|
}
|
|
|
|
SECTION("Macro past expanded group")
|
|
{
|
|
down("B");
|
|
REQUIRE(DescribeMacros() == "A, G1[C, D], B, E, G2{F}, H");
|
|
up("E");
|
|
up("E");
|
|
REQUIRE(DescribeMacros() == "A, E, G1[C, D], B, G2{F}, H");
|
|
}
|
|
|
|
SECTION("Macro past collapsed group")
|
|
{
|
|
down("E");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[C, D], G2{F}, E, H");
|
|
up("E");
|
|
up("H");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[C, D], E, H, G2{F}");
|
|
}
|
|
|
|
SECTION("Expanded group")
|
|
{
|
|
down("G1");
|
|
REQUIRE(DescribeMacros() == "A, B, E, G1[C, D], G2{F}, H");
|
|
down("G1");
|
|
REQUIRE(DescribeMacros() == "A, B, E, G2{F}, G1[C, D], H");
|
|
up("G1");
|
|
up("G1");
|
|
up("G1");
|
|
REQUIRE(DescribeMacros() == "A, G1[C, D], B, E, G2{F}, H");
|
|
}
|
|
|
|
SECTION("Collapsed group")
|
|
{
|
|
up("G2");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[C, D], G2{F}, E, H");
|
|
up("G2");
|
|
REQUIRE(DescribeMacros() == "A, B, G2{F}, G1[C, D], E, H");
|
|
down("G2");
|
|
down("G2");
|
|
down("G2");
|
|
REQUIRE(DescribeMacros() == "A, B, G1[C, D], E, H, G2{F}");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree group and ungroup", "[macro-tree]")
|
|
{
|
|
SECTION("Group adjacent macros")
|
|
{
|
|
TreeFixture f("A, B, C, D");
|
|
f.SelectRows({"B", "C"});
|
|
f.Tree()->GroupSelectedItems();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, " + newGroupName + "[B, C], D");
|
|
}
|
|
|
|
SECTION("Group non adjacent macros")
|
|
{
|
|
TreeFixture f("A, B, C, D");
|
|
f.SelectRows({"D", "B"});
|
|
f.Tree()->GroupSelectedItems();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, " + newGroupName + "[B, D], C");
|
|
}
|
|
|
|
SECTION("Group after collapsed group")
|
|
{
|
|
TreeFixture f("G1{X}, A, B, C");
|
|
f.SelectRows({"B", "C"});
|
|
f.Tree()->GroupSelectedItems();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() ==
|
|
"G1{X}, A, " + newGroupName + "[B, C]");
|
|
}
|
|
|
|
SECTION("Ungroup expanded group")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D");
|
|
f.SelectRows({"G1"});
|
|
f.Tree()->UngroupSelectedGroups();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, B, C, D");
|
|
}
|
|
|
|
SECTION("Ungroup collapsed group")
|
|
{
|
|
TreeFixture f("A, G1{B, C}, D");
|
|
f.SelectRows({"G1"});
|
|
f.Tree()->UngroupSelectedGroups();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, B, C, D");
|
|
REQUIRE(f.VisibleRows() == Names{"A", "B", "C", "D"});
|
|
}
|
|
|
|
SECTION("Ungroup multiple groups")
|
|
{
|
|
TreeFixture f("G1{A}, B, G2[C, D]");
|
|
f.SelectRows({"G1", "G2"});
|
|
f.Tree()->UngroupSelectedGroups();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, B, C, D");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree drag and drop", "[macro-tree]")
|
|
{
|
|
if (!DragAndDropSupported()) {
|
|
WARN("Skipped: drag and drop is not supported by the Qt platform");
|
|
return;
|
|
}
|
|
|
|
SECTION("Macro below macro")
|
|
{
|
|
TreeFixture f("A, B, C, D");
|
|
f.Drag(f.RowCenter("A"), f.RowBottom("C"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "B, C, A, D");
|
|
}
|
|
|
|
SECTION("Macro above macro")
|
|
{
|
|
TreeFixture f("A, B, C, D");
|
|
f.Drag(f.RowCenter("D"), f.RowTop("A"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "D, A, B, C");
|
|
}
|
|
|
|
SECTION("Macro below last row")
|
|
{
|
|
TreeFixture f("A, B, C");
|
|
f.Drag(f.RowCenter("A"),
|
|
QPoint(40, f.Viewport()->height() - 5));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "B, C, A");
|
|
}
|
|
|
|
SECTION("Macro onto expanded group")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D");
|
|
f.Drag(f.RowCenter("A"), f.RowCenter("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[A, B, C], D");
|
|
}
|
|
|
|
SECTION("Macro between subitems")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D");
|
|
f.Drag(f.RowCenter("D"), f.RowTop("C"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, D, C]");
|
|
}
|
|
|
|
SECTION("Subitem out of group")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D");
|
|
f.Drag(f.RowCenter("B"), f.RowBottom("D"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[C], D, B");
|
|
}
|
|
|
|
SECTION("Subitem to top")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D");
|
|
f.Drag(f.RowCenter("C"), f.RowTop("A"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "C, A, G1[B], D");
|
|
}
|
|
|
|
SECTION("Macro past collapsed group")
|
|
{
|
|
TreeFixture f("A, G1{B, C}, D");
|
|
f.Drag(f.RowCenter("D"), f.RowTop("A"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "D, A, G1{B, C}");
|
|
}
|
|
|
|
SECTION("Expanded group")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D, E");
|
|
f.Drag(f.RowCenter("G1"), f.RowBottom("D"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, D, G1[B, C], E");
|
|
}
|
|
|
|
SECTION("Collapsed group")
|
|
{
|
|
TreeFixture f("A, G1{B, C}, D, E");
|
|
f.Drag(f.RowCenter("G1"), f.RowBottom("D"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, D, G1{B, C}, E");
|
|
}
|
|
|
|
SECTION("Group onto group")
|
|
{
|
|
TreeFixture f("G1[A], G2[B]");
|
|
f.Drag(f.RowCenter("G2"), f.RowCenter("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[G2[B], A]");
|
|
}
|
|
|
|
SECTION("Group onto collapsed group")
|
|
{
|
|
TreeFixture f("G1{A}, G2[B]");
|
|
f.Drag(f.RowCenter("G2"), f.RowCenter("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1{G2[B], A}");
|
|
}
|
|
|
|
SECTION("Group into own nested group is rejected")
|
|
{
|
|
TreeFixture f("G1[A, G2[B]]");
|
|
f.Drag(f.RowCenter("G1"), f.RowCenter("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[A, G2[B]]");
|
|
}
|
|
|
|
SECTION("Macro onto nested group")
|
|
{
|
|
TreeFixture f("A, G1[G2[B]]");
|
|
f.Drag(f.RowCenter("A"), f.RowCenter("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[G2[A, B]]");
|
|
}
|
|
|
|
SECTION("Macro between nested subitems")
|
|
{
|
|
TreeFixture f("G1[G2[A, B]], C");
|
|
f.Drag(f.RowCenter("C"), f.RowTop("B"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[G2[A, C, B]]");
|
|
}
|
|
|
|
SECTION("Nested group to top level")
|
|
{
|
|
TreeFixture f("G1[G2[B], C], D");
|
|
f.Drag(f.RowCenter("G2"), f.RowBottom("D"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[C], D, G2[B]");
|
|
}
|
|
|
|
SECTION("Multiple macros from different levels")
|
|
{
|
|
TreeFixture f("A, G1[B, C], D");
|
|
f.SelectRows({"A", "C"});
|
|
f.Drag(f.RowCenter("C"), f.RowBottom("D"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[B], D, A, C");
|
|
}
|
|
|
|
SECTION("Multiple macros")
|
|
{
|
|
TreeFixture f("A, B, C, D");
|
|
f.SelectRows({"A", "C"});
|
|
f.Drag(f.RowCenter("C"), f.RowBottom("D"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "B, D, A, C");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree drop onto collapsed group", "[macro-tree]")
|
|
{
|
|
if (!DragAndDropSupported()) {
|
|
WARN("Skipped: drag and drop is not supported by the Qt platform");
|
|
return;
|
|
}
|
|
|
|
TreeFixture f("A, G1{B, C}, D");
|
|
f.Drag(f.RowCenter("D"), f.RowCenter("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1{D, B, C}");
|
|
}
|
|
|
|
TEST_CASE("Macro tree search", "[macro-tree]")
|
|
{
|
|
SECTION("Filter rows")
|
|
{
|
|
TreeFixture f("A1, G1[B1, C2], D2, G2{E1}, G3[F1]");
|
|
f.SetupSearch();
|
|
f.Search("2");
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"G1", "C2", "D2"});
|
|
|
|
f.Search("");
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() ==
|
|
Names{"A1", "G1", "B1", "C2", "D2", "G2", "G3", "F1"});
|
|
}
|
|
|
|
SECTION("Filter rows after collapsed group")
|
|
{
|
|
TreeFixture f("G1{X1}, A1, B2, C2");
|
|
f.SetupSearch();
|
|
f.Search("2");
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"B2", "C2"});
|
|
}
|
|
|
|
SECTION("Filter survives modifications")
|
|
{
|
|
TreeFixture f("A1, G1[B1, C2], D2, E2");
|
|
f.SetupSearch();
|
|
f.Search("2");
|
|
f.Tree()->Remove(GetMacro("D2"));
|
|
f.CheckInvariants();
|
|
f.Tree()->Up(GetMacro("E2"));
|
|
f.CheckInvariants();
|
|
f.ClickCheckBox(f.ExpandCheckBox("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"E2", "G1"});
|
|
f.Tree()->Add(std::make_shared<Macro>("X1"));
|
|
f.CheckInvariants();
|
|
f.Tree()->Add(std::make_shared<Macro>("Y2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"E2", "G1", "Y2"});
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree running state", "[macro-tree]")
|
|
{
|
|
SECTION("Group checkbox pauses all subitems")
|
|
{
|
|
TreeFixture f("A, G1[B, C]");
|
|
auto groupCheckBox = f.RunningCheckBox("G1");
|
|
REQUIRE(groupCheckBox->checkState() == Qt::Checked);
|
|
|
|
f.ClickCheckBox(groupCheckBox);
|
|
REQUIRE(GetMacro("B")->Paused());
|
|
REQUIRE(GetMacro("C")->Paused());
|
|
REQUIRE_FALSE(GetMacro("A")->Paused());
|
|
REQUIRE(groupCheckBox->checkState() == Qt::Unchecked);
|
|
f.CheckInvariants();
|
|
}
|
|
|
|
SECTION("Subitem checkbox updates group")
|
|
{
|
|
TreeFixture f("A, G1[B, C]");
|
|
f.ClickCheckBox(f.RunningCheckBox("B"));
|
|
REQUIRE(GetMacro("B")->Paused());
|
|
REQUIRE(f.RunningCheckBox("G1")->checkState() ==
|
|
Qt::PartiallyChecked);
|
|
f.CheckInvariants();
|
|
}
|
|
|
|
SECTION("Update after backend change")
|
|
{
|
|
TreeFixture f("G0{X}, A, B");
|
|
GetMacro("B")->SetPaused(true);
|
|
f.Tree()->UpdateRunningStates();
|
|
f.CheckInvariants();
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree running state after collapsed group", "[macro-tree]")
|
|
{
|
|
TreeFixture f("G0{X}, A, G1[B, C]");
|
|
f.ClickCheckBox(f.RunningCheckBox("B"));
|
|
REQUIRE(GetMacro("B")->Paused());
|
|
REQUIRE(f.RunningCheckBox("G1")->checkState() == Qt::PartiallyChecked);
|
|
}
|
|
|
|
TEST_CASE("Macro tree rename", "[macro-tree]")
|
|
{
|
|
TreeFixture f("A, G1{B}, C");
|
|
GetMacro("C")->SetName("Z");
|
|
emit MacroSignalManager::Instance()->Rename("C", "Z");
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"A", "G1", "Z"});
|
|
}
|
|
|
|
TEST_CASE("Macro tree nested groups", "[macro-tree]")
|
|
{
|
|
SECTION("Group macros within group")
|
|
{
|
|
TreeFixture f("A, G1[B, C, D]");
|
|
f.SelectRows({"C", "D"});
|
|
REQUIRE(f.Tree()->CanGroupSelection());
|
|
f.Tree()->GroupSelectedItems();
|
|
renameNewGroup("G2");
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, G2[C, D]]");
|
|
}
|
|
|
|
SECTION("Group selection containing group")
|
|
{
|
|
TreeFixture f("A, G1[B], C");
|
|
f.SelectRows({"G1", "C"});
|
|
f.Tree()->GroupSelectedItems();
|
|
renameNewGroup("G2");
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G2[G1[B], C]");
|
|
}
|
|
|
|
SECTION("Group collapsed nested group")
|
|
{
|
|
TreeFixture f("G1[G2{A, B}, C]");
|
|
f.SelectRows({"G2", "C"});
|
|
f.Tree()->GroupSelectedItems();
|
|
renameNewGroup("G3");
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[G3[G2{A, B}, C]]");
|
|
}
|
|
|
|
SECTION("Group macros of different groups is rejected")
|
|
{
|
|
TreeFixture f("A, G1[B]");
|
|
f.SelectRows({"A", "B"});
|
|
REQUIRE_FALSE(f.Tree()->CanGroupSelection());
|
|
f.Tree()->GroupSelectedItems();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B]");
|
|
}
|
|
|
|
SECTION("Ungroup inner group")
|
|
{
|
|
TreeFixture f("A, G1[B, G2[C, D], E]");
|
|
f.SelectRows({"G2"});
|
|
f.Tree()->UngroupSelectedGroups();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, G1[B, C, D, E]");
|
|
}
|
|
|
|
SECTION("Ungroup outer group")
|
|
{
|
|
TreeFixture f("A, G1[B, G2[C, D], E]");
|
|
f.SelectRows({"G1"});
|
|
f.Tree()->UngroupSelectedGroups();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, B, G2[C, D], E");
|
|
}
|
|
|
|
SECTION("Ungroup inner and outer group")
|
|
{
|
|
TreeFixture f("A, G1[B, G2[C, D], E]");
|
|
f.SelectRows({"G1", "G2"});
|
|
f.Tree()->UngroupSelectedGroups();
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, B, C, D, E");
|
|
}
|
|
|
|
SECTION("Remove group containing groups")
|
|
{
|
|
TreeFixture f("A, G1[B, G2[C, G3{D}]], E");
|
|
f.Tree()->Remove(GetMacro("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "A, E");
|
|
}
|
|
|
|
// Mirrors AdvSceneSwitcher::on_macroAdd_clicked()
|
|
SECTION("Add after nested subitem")
|
|
{
|
|
TreeFixture f("G1[G2[A, B]]");
|
|
auto newMacro = std::make_shared<Macro>("X");
|
|
Macro::PrepareMoveToGroup(GetMacro("G2"), newMacro);
|
|
f.Tree()->Add(newMacro, GetMacro("A"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[G2[A, X, B]]");
|
|
}
|
|
|
|
SECTION("Add into nested group")
|
|
{
|
|
TreeFixture f("G1[G2{A, B}]");
|
|
auto newMacro = std::make_shared<Macro>("X");
|
|
auto group = GetMacro("G2");
|
|
f.Tree()->ExpandGroup(group);
|
|
Macro::PrepareMoveToGroup(group, newMacro);
|
|
f.Tree()->AddToGroup(newMacro, group);
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[G2[X, A, B]]");
|
|
}
|
|
|
|
SECTION("Up and down stay within group")
|
|
{
|
|
TreeFixture f("G1[A, G2[B], C]");
|
|
f.Tree()->Down(GetMacro("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[A, C, G2[B]]");
|
|
f.Tree()->Down(GetMacro("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[A, C, G2[B]]");
|
|
f.Tree()->Up(GetMacro("G2"));
|
|
f.Tree()->Up(GetMacro("G2"));
|
|
f.Tree()->Up(GetMacro("G2"));
|
|
f.CheckInvariants();
|
|
REQUIRE(DescribeMacros() == "G1[G2[B], A, C]");
|
|
}
|
|
|
|
SECTION("Collapse outer group")
|
|
{
|
|
TreeFixture f("G1[G2{A}, B]");
|
|
f.ClickRow("B");
|
|
f.ClickCheckBox(f.ExpandCheckBox("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"G1"});
|
|
REQUIRE(f.Tree()->SelectionEmpty());
|
|
|
|
f.ClickCheckBox(f.ExpandCheckBox("G1"));
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"G1", "G2", "B"});
|
|
}
|
|
|
|
SECTION("Running state")
|
|
{
|
|
TreeFixture f("G1[A, G2[B, C]]");
|
|
f.ClickCheckBox(f.RunningCheckBox("G1"));
|
|
REQUIRE(GetMacro("A")->Paused());
|
|
REQUIRE(GetMacro("B")->Paused());
|
|
REQUIRE(GetMacro("C")->Paused());
|
|
REQUIRE(f.RunningCheckBox("G2")->checkState() == Qt::Unchecked);
|
|
|
|
f.ClickCheckBox(f.RunningCheckBox("C"));
|
|
REQUIRE(f.RunningCheckBox("G2")->checkState() ==
|
|
Qt::PartiallyChecked);
|
|
REQUIRE(f.RunningCheckBox("G1")->checkState() ==
|
|
Qt::PartiallyChecked);
|
|
f.CheckInvariants();
|
|
}
|
|
|
|
SECTION("Search")
|
|
{
|
|
TreeFixture f("G1[G2[A1, B2], C1], D1");
|
|
f.SetupSearch();
|
|
f.Search("2");
|
|
f.CheckInvariants();
|
|
REQUIRE(f.VisibleRows() == Names{"G1", "G2", "B2"});
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Macro tree random operations", "[macro-tree]")
|
|
{
|
|
const auto seed = GENERATE(1u, 2u, 3u, 4u, 5u);
|
|
std::mt19937 rng(seed);
|
|
auto random = [&rng](int max) {
|
|
return std::uniform_int_distribution<int>(0, max)(rng);
|
|
};
|
|
|
|
TreeFixture f("A, G1[B, G2[C, D], E], F, G3{G, H}, I");
|
|
f.SetupSearch();
|
|
int macroCount = 0;
|
|
int groupCount = 3;
|
|
|
|
auto randomVisibleRow = [&]() -> std::string {
|
|
const auto rows = f.VisibleRows();
|
|
return rows.empty() ? "" : rows[random((int)rows.size() - 1)];
|
|
};
|
|
auto randomDropPoint = [&]() {
|
|
const auto row = randomVisibleRow();
|
|
if (row.empty() || random(10) == 0) {
|
|
return QPoint(40, f.Viewport()->height() - 5);
|
|
}
|
|
switch (random(2)) {
|
|
case 0:
|
|
return f.RowTop(row);
|
|
case 1:
|
|
return f.RowCenter(row);
|
|
default:
|
|
return f.RowBottom(row);
|
|
}
|
|
};
|
|
|
|
for (int step = 0; step < 150; step++) {
|
|
const int operation = random(10);
|
|
INFO("seed " << seed << ", step " << step << ", operation "
|
|
<< operation << ", before: " << DescribeMacros());
|
|
const auto row = randomVisibleRow();
|
|
const auto macro = row.empty() ? nullptr : GetMacro(row);
|
|
|
|
switch (operation) {
|
|
case 0:
|
|
if (macro) {
|
|
f.Tree()->Up(macro);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (macro) {
|
|
f.Tree()->Down(macro);
|
|
}
|
|
break;
|
|
case 2:
|
|
if (macro && random(1) == 0) {
|
|
f.Tree()->Remove(macro);
|
|
}
|
|
break;
|
|
case 3: {
|
|
auto newMacro = std::make_shared<Macro>(
|
|
"M" + std::to_string(macroCount++));
|
|
if (macro && macro->IsGroup()) {
|
|
f.Tree()->ExpandGroup(macro);
|
|
Macro::PrepareMoveToGroup(macro, newMacro);
|
|
f.Tree()->AddToGroup(newMacro, macro);
|
|
} else if (macro) {
|
|
Macro::PrepareMoveToGroup(macro->Parent(),
|
|
newMacro);
|
|
f.Tree()->Add(newMacro, macro);
|
|
} else {
|
|
f.Tree()->Add(newMacro);
|
|
}
|
|
break;
|
|
}
|
|
case 4: {
|
|
Names rows;
|
|
for (int i = random(2); i >= 0; i--) {
|
|
const auto name = randomVisibleRow();
|
|
if (!name.empty()) {
|
|
rows.emplace_back(name);
|
|
}
|
|
}
|
|
if (rows.empty()) {
|
|
break;
|
|
}
|
|
f.SelectRows(rows);
|
|
const bool canGroup = f.Tree()->CanGroupSelection();
|
|
f.Tree()->GroupSelectedItems();
|
|
if (canGroup) {
|
|
renameNewGroup("G" +
|
|
std::to_string(++groupCount));
|
|
}
|
|
break;
|
|
}
|
|
case 5:
|
|
if (macro && macro->IsGroup()) {
|
|
f.SelectRows({row});
|
|
f.Tree()->UngroupSelectedGroups();
|
|
}
|
|
break;
|
|
case 6:
|
|
if (macro && macro->IsGroup()) {
|
|
f.ClickCheckBox(f.ExpandCheckBox(row));
|
|
}
|
|
break;
|
|
case 7:
|
|
case 8:
|
|
if (macro && DragAndDropSupported()) {
|
|
f.Drag(f.RowCenter(row), randomDropPoint());
|
|
}
|
|
break;
|
|
case 9:
|
|
f.Search(random(3) == 0 ? "1" : "");
|
|
break;
|
|
default:
|
|
if (random(1)) {
|
|
f.Tree()->ExpandAll();
|
|
} else {
|
|
f.Tree()->CollapseAll();
|
|
}
|
|
break;
|
|
}
|
|
|
|
f.CheckInvariants();
|
|
}
|
|
}
|