mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-10-26 10:29:50 -05:00
Add desktop drawing for cursor condition regions
This commit is contained in:
@@ -395,6 +395,8 @@ AdvSceneSwitcher.condition.cursor.button.middle="Middle mouse button"
|
||||
AdvSceneSwitcher.condition.cursor.button.right="Right mouse button"
|
||||
AdvSceneSwitcher.condition.cursor.showFrame="Show frame"
|
||||
AdvSceneSwitcher.condition.cursor.hideFrame="Hide frame"
|
||||
AdvSceneSwitcher.condition.cursor.drawFrame="Draw frame"
|
||||
AdvSceneSwitcher.condition.cursor.drawFrame.prompt="Drag to draw a cursor region. Release to apply. Press Esc or right-click to cancel."
|
||||
AdvSceneSwitcher.condition.cursor.entry.line1="Cursor{{conditions}}{{buttons}}{{minX}}{{minY}}{{maxX}}{{maxY}}{{toggleFrameButton}}"
|
||||
AdvSceneSwitcher.condition.cursor.entry.line2="Cursor is currently at{{xPos}}x{{yPos}}"
|
||||
AdvSceneSwitcher.condition.scene="Scene"
|
||||
|
||||
@@ -180,6 +180,8 @@ target_sources(
|
||||
utils/process-config.hpp
|
||||
utils/profile-helpers.cpp
|
||||
utils/profile-helpers.hpp
|
||||
utils/screen-region-selection.cpp
|
||||
utils/screen-region-selection.hpp
|
||||
utils/scene-item-selection.cpp
|
||||
utils/scene-item-selection.hpp
|
||||
utils/scene-item-transform-helpers.cpp
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "macro-condition-cursor.hpp"
|
||||
#include "cursor-helpers.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "screen-region-selection.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <QGuiApplication>
|
||||
@@ -144,11 +145,14 @@ MacroConditionCursorEdit::MacroConditionCursorEdit(
|
||||
_buttons(new QComboBox()),
|
||||
_frameToggle(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.cursor.showFrame"))),
|
||||
_drawFrame(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.cursor.drawFrame"))),
|
||||
_xPos(new QLabel("-")),
|
||||
_yPos(new QLabel("-")),
|
||||
_curentPosLayout(new QHBoxLayout())
|
||||
{
|
||||
_frame.setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_drawFrame->setAttribute(Qt::WA_NoMousePropagation);
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
populateButtonSelection(_buttons);
|
||||
@@ -190,6 +194,8 @@ MacroConditionCursorEdit::MacroConditionCursorEdit(
|
||||
this, SLOT(MaxYChanged(const NumberVariable<int> &)));
|
||||
QWidget::connect(_frameToggle, SIGNAL(clicked()), this,
|
||||
SLOT(ToggleFrame()));
|
||||
connect(_drawFrame, &QPushButton::clicked, this,
|
||||
&MacroConditionCursorEdit::DrawFrame, Qt::QueuedConnection);
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{conditions}}", _conditions},
|
||||
@@ -209,6 +215,7 @@ MacroConditionCursorEdit::MacroConditionCursorEdit(
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.cursor.entry.line2"),
|
||||
_curentPosLayout, widgetPlaceholders);
|
||||
_curentPosLayout->addWidget(_drawFrame);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1);
|
||||
mainLayout->addLayout(_curentPosLayout);
|
||||
@@ -287,6 +294,49 @@ void MacroConditionCursorEdit::ToggleFrame()
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionCursorEdit::DrawFrame()
|
||||
{
|
||||
if (_loading || !_entryData || !_drawFrame->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
const bool frameVisible = _frame.isVisible();
|
||||
_frame.hide();
|
||||
_drawFrame->setEnabled(false);
|
||||
auto selection = new ScreenRegionSelection(this);
|
||||
connect(selection, &ScreenRegionSelection::RegionSelected, this,
|
||||
[this](const QRect ®ion) {
|
||||
GUARD_LOADING_AND_LOCK();
|
||||
if (_entryData->_condition !=
|
||||
MacroConditionCursor::Condition::REGION) {
|
||||
return;
|
||||
}
|
||||
_entryData->_minX = region.left();
|
||||
_entryData->_minY = region.top();
|
||||
_entryData->_maxX = region.right();
|
||||
_entryData->_maxY = region.bottom();
|
||||
|
||||
const QSignalBlocker b1(_minX);
|
||||
const QSignalBlocker b2(_minY);
|
||||
const QSignalBlocker b3(_maxX);
|
||||
const QSignalBlocker b4(_maxY);
|
||||
_minX->SetValue(_entryData->_minX);
|
||||
_minY->SetValue(_entryData->_minY);
|
||||
_maxX->SetValue(_entryData->_maxX);
|
||||
_maxY->SetValue(_entryData->_maxY);
|
||||
SetupFrame();
|
||||
});
|
||||
connect(selection, &ScreenRegionSelection::Finished, this,
|
||||
[this, frameVisible]() {
|
||||
_drawFrame->setEnabled(true);
|
||||
if (frameVisible &&
|
||||
_entryData->_condition ==
|
||||
MacroConditionCursor::Condition::REGION) {
|
||||
SetupFrame();
|
||||
_frame.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MacroConditionCursorEdit::SetWidgetVisibility()
|
||||
{
|
||||
const bool isRegionCondition = _entryData->_condition ==
|
||||
@@ -296,6 +346,7 @@ void MacroConditionCursorEdit::SetWidgetVisibility()
|
||||
_maxX->setVisible(isRegionCondition);
|
||||
_maxY->setVisible(isRegionCondition);
|
||||
_frameToggle->setVisible(isRegionCondition);
|
||||
_drawFrame->setVisible(isRegionCondition);
|
||||
SetLayoutVisible(_curentPosLayout, isRegionCondition);
|
||||
if (_frame.isVisible()) {
|
||||
ToggleFrame();
|
||||
|
||||
@@ -74,6 +74,7 @@ private slots:
|
||||
void MaxYChanged(const NumberVariable<int> &pos);
|
||||
void UpdateCursorPos();
|
||||
void ToggleFrame();
|
||||
void DrawFrame();
|
||||
|
||||
protected:
|
||||
VariableSpinBox *_minX;
|
||||
@@ -83,6 +84,7 @@ protected:
|
||||
QComboBox *_conditions;
|
||||
QComboBox *_buttons;
|
||||
QPushButton *_frameToggle;
|
||||
QPushButton *_drawFrame;
|
||||
QLabel *_xPos;
|
||||
QLabel *_yPos;
|
||||
QHBoxLayout *_curentPosLayout;
|
||||
|
||||
165
plugins/base/utils/screen-region-selection.cpp
Normal file
165
plugins/base/utils/screen-region-selection.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#include "screen-region-selection.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QGuiApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
namespace advss {
|
||||
|
||||
ScreenRegionSelection::ScreenRegionSelection(QWidget *parent) : QObject(parent)
|
||||
{
|
||||
QWidget *activeOverlay = nullptr;
|
||||
const auto cursor = QCursor::pos();
|
||||
for (auto screen : QGuiApplication::screens()) {
|
||||
auto overlay = std::make_unique<QWidget>(
|
||||
parent->window(), Qt::Tool | Qt::FramelessWindowHint |
|
||||
Qt::WindowStaysOnTopHint);
|
||||
// Keep native overlay windows separate from the macro's widgets.
|
||||
overlay->setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
overlay->setAttribute(Qt::WA_NoMousePropagation);
|
||||
overlay->setAttribute(Qt::WA_TranslucentBackground);
|
||||
overlay->setCursor(Qt::CrossCursor);
|
||||
overlay->setFocusPolicy(Qt::StrongFocus);
|
||||
overlay->setMouseTracking(true);
|
||||
overlay->installEventFilter(this);
|
||||
// Each monitor needs its own window for mixed display scaling.
|
||||
overlay->winId();
|
||||
overlay->windowHandle()->setScreen(screen);
|
||||
overlay->setGeometry(screen->geometry());
|
||||
overlay->show();
|
||||
if (screen->geometry().contains(cursor)) {
|
||||
activeOverlay = overlay.get();
|
||||
}
|
||||
_overlays.emplace_back(std::move(overlay));
|
||||
}
|
||||
if (activeOverlay) {
|
||||
activeOverlay->raise();
|
||||
activeOverlay->activateWindow();
|
||||
activeOverlay->setFocus();
|
||||
}
|
||||
connect(qGuiApp, &QGuiApplication::screenRemoved, this,
|
||||
[this]() { Finish(false); });
|
||||
connect(qGuiApp, &QGuiApplication::screenAdded, this,
|
||||
[this]() { Finish(false); });
|
||||
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this,
|
||||
[this](Qt::ApplicationState state) {
|
||||
if (state != Qt::ApplicationActive) {
|
||||
Finish(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ScreenRegionSelection::~ScreenRegionSelection()
|
||||
{
|
||||
for (auto &overlay : _overlays) {
|
||||
overlay->removeEventFilter(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenRegionSelection::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (_finished) {
|
||||
return false;
|
||||
}
|
||||
auto overlay = static_cast<QWidget *>(watched);
|
||||
switch (event->type()) {
|
||||
case QEvent::Paint: {
|
||||
QPainter painter(overlay);
|
||||
painter.fillRect(overlay->rect(), QColor(0, 0, 0, 70));
|
||||
if (_drawing) {
|
||||
const QRect region(
|
||||
overlay->mapFromGlobal(_selection.topLeft()),
|
||||
overlay->mapFromGlobal(
|
||||
_selection.bottomRight()));
|
||||
painter.fillRect(region, QColor(60, 150, 255, 60));
|
||||
painter.setPen(QPen(Qt::white, 2));
|
||||
painter.drawRect(region.adjusted(0, 0, -1, -1));
|
||||
}
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(
|
||||
overlay->rect().adjusted(24, 24, -24, -24),
|
||||
Qt::AlignTop | Qt::AlignHCenter | Qt::TextWordWrap,
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.cursor.drawFrame.prompt"));
|
||||
return true;
|
||||
}
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonDblClick: {
|
||||
event->accept();
|
||||
auto mouse = static_cast<QMouseEvent *>(event);
|
||||
if (mouse->button() == Qt::RightButton) {
|
||||
Finish(false);
|
||||
} else if (mouse->button() == Qt::LeftButton) {
|
||||
_start = QCursor::pos();
|
||||
_drawing = true;
|
||||
UpdateSelection();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case QEvent::MouseMove:
|
||||
event->accept();
|
||||
if (_drawing) {
|
||||
UpdateSelection();
|
||||
}
|
||||
return true;
|
||||
case QEvent::MouseButtonRelease:
|
||||
event->accept();
|
||||
if (_drawing && static_cast<QMouseEvent *>(event)->button() ==
|
||||
Qt::LeftButton) {
|
||||
UpdateSelection();
|
||||
if (_selection.width() > 1 && _selection.height() > 1) {
|
||||
Finish(true);
|
||||
} else {
|
||||
_drawing = false;
|
||||
UpdateSelection();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case QEvent::KeyPress:
|
||||
event->accept();
|
||||
if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) {
|
||||
Finish(false);
|
||||
}
|
||||
return true;
|
||||
case QEvent::ShortcutOverride:
|
||||
event->accept();
|
||||
return true;
|
||||
case QEvent::Close:
|
||||
Finish(false);
|
||||
return true;
|
||||
default:
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenRegionSelection::UpdateSelection()
|
||||
{
|
||||
// Use the same global coordinate system as the Cursor condition.
|
||||
_selection = QRect(_start, QCursor::pos()).normalized();
|
||||
for (auto &overlay : _overlays) {
|
||||
overlay->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenRegionSelection::Finish(bool accept)
|
||||
{
|
||||
if (_finished) {
|
||||
return;
|
||||
}
|
||||
_finished = true;
|
||||
for (auto &overlay : _overlays) {
|
||||
overlay->hide();
|
||||
}
|
||||
if (accept) {
|
||||
emit RegionSelected(_selection);
|
||||
}
|
||||
emit Finished();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
38
plugins/base/utils/screen-region-selection.hpp
Normal file
38
plugins/base/utils/screen-region-selection.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <QWidget>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class ScreenRegionSelection : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ScreenRegionSelection(QWidget *parent);
|
||||
~ScreenRegionSelection() override;
|
||||
|
||||
signals:
|
||||
void RegionSelected(const QRect ®ion);
|
||||
void Finished();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
void UpdateSelection();
|
||||
void Finish(bool accept);
|
||||
|
||||
std::vector<std::unique_ptr<QWidget>> _overlays;
|
||||
QPoint _start;
|
||||
QRect _selection;
|
||||
bool _drawing = false;
|
||||
bool _finished = false;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
Reference in New Issue
Block a user