SceneSwitcher/plugins/base/macro-condition-cursor.cpp
WarmUpTill 7d0332dd0e Restructure library and plugins
The "core" macro conditions and actions have been extracted out to the
"base" plugin.

The library now mostly contains functionality which is required across
all plugins and (e.g. definitions for macro segments).

The goal is to reduce the complexity and cross-dependencies and group
the source files in a better way.

This should relsove the "library limit of 65535 objects exceeded" build
issue occuring in some Windows build environments.
2024-01-27 14:10:34 +01:00

389 lines
10 KiB
C++

#include "macro-condition-cursor.hpp"
#include "cursor-helpers.hpp"
#include "utility.hpp"
#include <QScreen>
#include <QGuiApplication>
namespace advss {
const std::string MacroConditionCursor::id = "cursor";
bool MacroConditionCursor::_registered = MacroConditionFactory::Register(
MacroConditionCursor::id,
{MacroConditionCursor::Create, MacroConditionCursorEdit::Create,
"AdvSceneSwitcher.condition.cursor"});
const static std::map<MacroConditionCursor::Condition, std::string>
cursorConditionTypes = {
{MacroConditionCursor::Condition::REGION,
"AdvSceneSwitcher.condition.cursor.type.region"},
{MacroConditionCursor::Condition::MOVING,
"AdvSceneSwitcher.condition.cursor.type.moving"},
#ifdef _WIN32 // Not implemented for MacOS and Linux
{MacroConditionCursor::Condition::CLICK,
"AdvSceneSwitcher.condition.cursor.type.click"},
#endif
};
const static std::map<MacroConditionCursor::Button, std::string> buttonTypes = {
{MacroConditionCursor::Button::LEFT,
"AdvSceneSwitcher.condition.cursor.button.left"},
{MacroConditionCursor::Button::MIDDLE,
"AdvSceneSwitcher.condition.cursor.button.middle"},
{MacroConditionCursor::Button::RIGHT,
"AdvSceneSwitcher.condition.cursor.button.right"},
};
bool MacroConditionCursor::CheckClick()
{
switch (_button) {
case MacroConditionCursor::Button::LEFT:
return _lastCheckTime < GetLastMouseLeftClickTime();
case MacroConditionCursor::Button::MIDDLE:
return _lastCheckTime < GetLastMouseMiddleClickTime();
case MacroConditionCursor::Button::RIGHT:
return _lastCheckTime < GetLastMouseRightClickTime();
}
return false;
}
bool MacroConditionCursor::CheckCondition()
{
bool ret = false;
std::pair<int, int> cursorPos = GetCursorPos();
switch (_condition) {
case Condition::REGION:
ret = cursorPos.first >= _minX && cursorPos.second >= _minY &&
cursorPos.first <= _maxX && cursorPos.second <= _maxY;
SetVariableValue(std::to_string(cursorPos.first) + " " +
std::to_string(cursorPos.second));
break;
case Condition::MOVING:
ret = cursorPos.first != _lastCursorPosition.first ||
cursorPos.second != _lastCursorPosition.second;
break;
case Condition::CLICK:
ret = CheckClick();
break;
}
_lastCheckTime = std::chrono::high_resolution_clock::now();
_lastCursorPosition = cursorPos;
if (GetVariableValue().empty()) {
SetVariableValue(ret ? "true" : "false");
}
return ret;
}
bool MacroConditionCursor::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_int(obj, "button", static_cast<int>(_button));
_minX.Save(obj, "minX");
_minY.Save(obj, "minY");
_maxX.Save(obj, "maxX");
_maxY.Save(obj, "maxY");
obs_data_set_int(obj, "version", 1);
return true;
}
bool MacroConditionCursor::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
_button = static_cast<Button>(obs_data_get_int(obj, "button"));
if (!obs_data_has_user_value(obj, "version")) {
_minX = obs_data_get_int(obj, "minX");
_minY = obs_data_get_int(obj, "minY");
_maxX = obs_data_get_int(obj, "maxX");
_maxY = obs_data_get_int(obj, "maxY");
} else {
_minX.Load(obj, "minX");
_minY.Load(obj, "minY");
_maxX.Load(obj, "maxX");
_maxY.Load(obj, "maxY");
}
return true;
}
static inline void populateConditionSelection(QComboBox *list)
{
for (const auto &[condition, name] : cursorConditionTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(condition));
}
}
static inline void populateButtonSelection(QComboBox *list)
{
for (const auto &[button, name] : buttonTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(button));
}
}
MacroConditionCursorEdit::MacroConditionCursorEdit(
QWidget *parent, std::shared_ptr<MacroConditionCursor> entryData)
: QWidget(parent),
_minX(new VariableSpinBox()),
_minY(new VariableSpinBox()),
_maxX(new VariableSpinBox()),
_maxY(new VariableSpinBox()),
_conditions(new QComboBox()),
_buttons(new QComboBox()),
_frameToggle(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.cursor.showFrame"))),
_xPos(new QLabel("-")),
_yPos(new QLabel("-")),
_curentPosLayout(new QHBoxLayout())
{
_frame.setAttribute(Qt::WA_TransparentForMouseEvents);
populateConditionSelection(_conditions);
populateButtonSelection(_buttons);
_minX->setPrefix("Min X: ");
_minY->setPrefix("Min Y: ");
_maxX->setPrefix("Max X: ");
_maxY->setPrefix("Max Y: ");
_minX->setMinimum(-1000000);
_minY->setMinimum(-1000000);
_maxX->setMinimum(-1000000);
_maxY->setMinimum(-1000000);
_minX->setMaximum(1000000);
_minY->setMaximum(1000000);
_maxX->setMaximum(1000000);
_maxY->setMaximum(1000000);
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_buttons, SIGNAL(currentIndexChanged(int)), this,
SLOT(ButtonChanged(int)));
QWidget::connect(
_minX,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(MinXChanged(const NumberVariable<int> &)));
QWidget::connect(
_minY,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(MinYChanged(const NumberVariable<int> &)));
QWidget::connect(
_maxX,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(MaxXChanged(const NumberVariable<int> &)));
QWidget::connect(
_maxY,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(MaxYChanged(const NumberVariable<int> &)));
QWidget::connect(_frameToggle, SIGNAL(clicked()), this,
SLOT(ToggleFrame()));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{conditions}}", _conditions},
{"{{buttons}}", _buttons},
{"{{minX}}", _minX},
{"{{minY}}", _minY},
{"{{maxX}}", _maxX},
{"{{maxY}}", _maxY},
{"{{xPos}}", _xPos},
{"{{yPos}}", _yPos},
{"{{toggleFrameButton}}", _frameToggle},
};
QHBoxLayout *line1 = new QHBoxLayout;
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.cursor.entry.line1"),
line1, widgetPlaceholders);
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.cursor.entry.line2"),
_curentPosLayout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1);
mainLayout->addLayout(_curentPosLayout);
setLayout(mainLayout);
connect(&_timer, &QTimer::timeout, this,
&MacroConditionCursorEdit::UpdateCursorPos);
_timer.start(1000);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionCursorEdit::ConditionChanged(int index)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_condition = static_cast<MacroConditionCursor::Condition>(
_conditions->itemData(index).toInt());
SetWidgetVisibility();
}
void MacroConditionCursorEdit::ButtonChanged(int index)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_button = static_cast<MacroConditionCursor::Button>(
_buttons->itemData(index).toInt());
}
void MacroConditionCursorEdit::MinXChanged(const NumberVariable<int> &pos)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_minX = pos;
SetupFrame();
}
void MacroConditionCursorEdit::MinYChanged(const NumberVariable<int> &pos)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_minY = pos;
SetupFrame();
}
void MacroConditionCursorEdit::MaxXChanged(const NumberVariable<int> &pos)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_maxX = pos;
SetupFrame();
}
void MacroConditionCursorEdit::MaxYChanged(const NumberVariable<int> &pos)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_maxY = pos;
SetupFrame();
}
void MacroConditionCursorEdit::UpdateCursorPos()
{
std::pair<int, int> position = GetCursorPos();
_xPos->setText(QString::number(position.first));
_yPos->setText(QString::number(position.second));
}
void MacroConditionCursorEdit::ToggleFrame()
{
SetupFrame();
if (_frame.isVisible()) {
_frameToggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.cursor.showFrame"));
_frame.hide();
} else {
_frameToggle->setText(obs_module_text(
"AdvSceneSwitcher.condition.cursor.hideFrame"));
_frame.show();
}
}
void MacroConditionCursorEdit::SetWidgetVisibility()
{
const bool isRegionCondition = _entryData->_condition ==
MacroConditionCursor::Condition::REGION;
_minX->setVisible(isRegionCondition);
_minY->setVisible(isRegionCondition);
_maxX->setVisible(isRegionCondition);
_maxY->setVisible(isRegionCondition);
_frameToggle->setVisible(isRegionCondition);
SetLayoutVisible(_curentPosLayout, isRegionCondition);
if (_frame.isVisible()) {
ToggleFrame();
}
_buttons->setVisible(_entryData->_condition ==
MacroConditionCursor::Condition::CLICK);
adjustSize();
}
static QRect getScreenUnionRect()
{
QPoint screenTopLeft(0, 0);
QPoint screenBottomRight(0, 0);
auto screens = QGuiApplication::screens();
for (const auto &screen : screens) {
const auto geo = screen->geometry();
if (geo.topLeft().x() < screenTopLeft.x()) {
screenTopLeft.setX(geo.topLeft().x());
}
if (geo.topLeft().y() < screenTopLeft.y()) {
screenTopLeft.setY(geo.topLeft().y());
}
if (geo.bottomRight().x() > screenBottomRight.x()) {
screenBottomRight.setX(geo.bottomRight().x());
}
if (geo.bottomRight().y() > screenBottomRight.y()) {
screenBottomRight.setY(geo.bottomRight().y());
}
}
return QRect(screenTopLeft, screenBottomRight);
}
void MacroConditionCursorEdit::SetupFrame()
{
_frame.setFrameStyle(QFrame::Box | QFrame::Plain);
_frame.setWindowFlags(Qt::FramelessWindowHint | Qt::Tool |
Qt::WindowTransparentForInput |
Qt::WindowDoesNotAcceptFocus |
Qt::WindowStaysOnTopHint);
_frame.setAttribute(Qt::WA_TranslucentBackground, true);
if (!_entryData) {
return;
}
QRect rect(_entryData->_minX, _entryData->_minY,
_entryData->_maxX - _entryData->_minX,
_entryData->_maxY - _entryData->_minY);
if (rect.size().height() <= 0 || rect.size().width() <= 0) {
_frame.setGeometry(0, 0, 1, 1);
return;
}
_frame.setGeometry(getScreenUnionRect().intersected(rect));
}
void MacroConditionCursorEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_buttons->setCurrentIndex(static_cast<int>(_entryData->_button));
_minX->SetValue(_entryData->_minX);
_minY->SetValue(_entryData->_minY);
_maxX->SetValue(_entryData->_maxX);
_maxY->SetValue(_entryData->_maxY);
SetWidgetVisibility();
}
} // namespace advss