mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-11 19:58:49 -05:00
Rework window condition
Layout changes to make controlling the checkboxes less confusing. Added variable support for the winddow title selection. Previously window title matching was mandatory, which is now optional. This would make it difficult to set up condition which are independent of the window title. For example, a condition wich triggers on focus window change. Add support for configuring the regex options used for the window title options. Also adds the option to check the content displayed in a given window using the Windows API as an alternative to the OCR of the video condition.
This commit is contained in:
@@ -161,9 +161,14 @@ AdvSceneSwitcher.condition.scene.previousSceneTransitionBehaviour="During transi
|
||||
AdvSceneSwitcher.condition.scene.entry.line1="{{sceneType}}{{scenes}}{{pattern}}"
|
||||
AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
|
||||
AdvSceneSwitcher.condition.window="Window"
|
||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
||||
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
|
||||
AdvSceneSwitcher.condition.window.entry.line3="Current foreground window: {{focusWindow}}"
|
||||
AdvSceneSwitcher.condition.window.entry.window="{{checkTitle}}Window title matches{{windowRegex}}{{windows}}"
|
||||
AdvSceneSwitcher.condition.window.entry.fullscreen="{{fullscreen}}Window is fullscreen"
|
||||
AdvSceneSwitcher.condition.window.entry.maximized="{{maximized}}Window is maximized"
|
||||
AdvSceneSwitcher.condition.window.entry.focused="{{focused}}Window is focused"
|
||||
AdvSceneSwitcher.condition.window.entry.focusedChange="{{windowFocusChanged}}Focus window changed"
|
||||
AdvSceneSwitcher.condition.window.entry.text="{{checkText}}Window contains text{{textRegex}}{{windowText}}"
|
||||
AdvSceneSwitcher.condition.window.entry.text.note="This option might not work on every text being displayed in a window.\nIf that is the case, you can consider using the Video conditions OCR check instead."
|
||||
AdvSceneSwitcher.condition.window.entry.currentFocus="Current focus window:{{focusWindow}}"
|
||||
AdvSceneSwitcher.condition.file="File"
|
||||
AdvSceneSwitcher.condition.file.type.match="matches"
|
||||
AdvSceneSwitcher.condition.file.type.contentChange="content changed"
|
||||
|
||||
@@ -332,6 +332,12 @@ bool IsFullscreen(const std::string &title)
|
||||
return windowStatesAreSet(title, states);
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &window)
|
||||
{
|
||||
// Not implemented
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifdef USE_PROCPS
|
||||
void GetProcessList(QStringList &processes)
|
||||
{
|
||||
|
||||
@@ -14,41 +14,70 @@ bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
|
||||
{MacroConditionWindow::Create, MacroConditionWindowEdit::Create,
|
||||
"AdvSceneSwitcher.condition.window"});
|
||||
|
||||
bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
|
||||
const std::string ¤tWindowTitle)
|
||||
static bool matchRegex(const RegexConfig &conf, const std::string &msg,
|
||||
const std::string &expr)
|
||||
{
|
||||
bool focus = (!_focus || _window == currentWindowTitle);
|
||||
bool fullscreen = (!_fullscreen || IsFullscreen(_window));
|
||||
bool max = (!_maximized || IsMaximized(_window));
|
||||
|
||||
return focus && fullscreen && max;
|
||||
auto regex = conf.GetRegularExpression(expr);
|
||||
if (!regex.isValid()) {
|
||||
return false;
|
||||
}
|
||||
auto match = regex.match(QString::fromStdString(msg));
|
||||
return match.hasMatch();
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::CheckWindowTitleSwitchRegex(
|
||||
const std::string ¤tWindowTitle,
|
||||
const std::vector<std::string> &windowList)
|
||||
static bool windowContainsText(const std::string &window,
|
||||
const std::string &matchText,
|
||||
const RegexConfig &conf)
|
||||
{
|
||||
bool match = false;
|
||||
for (auto &window : windowList) {
|
||||
try {
|
||||
std::regex expr(_window);
|
||||
if (!std::regex_match(window, expr)) {
|
||||
continue;
|
||||
}
|
||||
} catch (const std::regex_error &) {
|
||||
}
|
||||
|
||||
bool focus = (!_focus || window == currentWindowTitle);
|
||||
bool fullscreen = (!_fullscreen || IsFullscreen(window));
|
||||
bool max = (!_maximized || IsMaximized(window));
|
||||
|
||||
if (focus && fullscreen && max) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
auto text = GetTextInWindow(window);
|
||||
if (!text.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return match;
|
||||
if (conf.Enabled()) {
|
||||
return matchRegex(conf, *text, matchText);
|
||||
}
|
||||
return text == matchText;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::WindowMatches(const std::string &window)
|
||||
{
|
||||
const bool focusCheckOK = (!_focus || window == switcher->currentTitle);
|
||||
if (!focusCheckOK) {
|
||||
return false;
|
||||
}
|
||||
const bool fullscreenCheckOK = (!_fullscreen || IsFullscreen(window));
|
||||
if (!fullscreenCheckOK) {
|
||||
return false;
|
||||
}
|
||||
const bool maxCheckOK = (!_maximized || IsMaximized(window));
|
||||
if (!maxCheckOK) {
|
||||
return false;
|
||||
}
|
||||
const bool textCheckOK =
|
||||
(!_checkText || windowContainsText(window, _text, _textRegex));
|
||||
if (!textCheckOK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_checkText) {
|
||||
auto text = GetTextInWindow(window);
|
||||
SetVariableValue(text.value_or(""));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::WindowRegexMatches(
|
||||
const std::vector<std::string> &windowList)
|
||||
{
|
||||
for (const auto &window : windowList) {
|
||||
if (matchRegex(_windowRegex, window, _window) &&
|
||||
WindowMatches(window)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool foregroundWindowChanged()
|
||||
@@ -58,19 +87,19 @@ static bool foregroundWindowChanged()
|
||||
|
||||
bool MacroConditionWindow::CheckCondition()
|
||||
{
|
||||
const std::string ¤tWindowTitle = switcher->currentTitle;
|
||||
SetVariableValue(currentWindowTitle);
|
||||
SetVariableValue("");
|
||||
if (!_checkText) {
|
||||
SetVariableValue(switcher->currentTitle);
|
||||
}
|
||||
|
||||
std::vector<std::string> windowList;
|
||||
GetWindowList(windowList);
|
||||
|
||||
bool match = false;
|
||||
if (std::find(windowList.begin(), windowList.end(), _window) !=
|
||||
windowList.end()) {
|
||||
match = CheckWindowTitleSwitchDirect(currentWindowTitle);
|
||||
if (_windowRegex.Enabled()) {
|
||||
match = WindowRegexMatches(windowList);
|
||||
} else {
|
||||
match = CheckWindowTitleSwitchRegex(currentWindowTitle,
|
||||
windowList);
|
||||
match = WindowMatches(_window);
|
||||
}
|
||||
match = match && (!_windowFocusChanged || foregroundWindowChanged());
|
||||
return match;
|
||||
@@ -79,22 +108,42 @@ bool MacroConditionWindow::CheckCondition()
|
||||
bool MacroConditionWindow::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
obs_data_set_string(obj, "window", _window.c_str());
|
||||
obs_data_set_bool(obj, "checkTitle", _checkTitle);
|
||||
_window.Save(obj, "window");
|
||||
_windowRegex.Save(obj, "windowRegexConfig");
|
||||
obs_data_set_bool(obj, "fullscreen", _fullscreen);
|
||||
obs_data_set_bool(obj, "maximized", _maximized);
|
||||
obs_data_set_bool(obj, "focus", _focus);
|
||||
obs_data_set_bool(obj, "windowFocusChanged", _windowFocusChanged);
|
||||
obs_data_set_bool(obj, "checkWindowText", _checkText);
|
||||
_text.Save(obj, "text");
|
||||
_textRegex.Save(obj, "textRegexConfig");
|
||||
obs_data_set_int(obj, "version", 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionWindow::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_window = obs_data_get_string(obj, "window");
|
||||
if (!obs_data_has_user_value(obj, "version")) {
|
||||
_checkTitle = true;
|
||||
_windowRegex.CreateBackwardsCompatibleRegex(true, true);
|
||||
} else {
|
||||
_checkTitle = obs_data_get_bool(obj, "checkTitle");
|
||||
_windowRegex.Load(obj, "windowRegexConfig");
|
||||
}
|
||||
_window.Load(obj, "window");
|
||||
_fullscreen = obs_data_get_bool(obj, "fullscreen");
|
||||
_maximized = obs_data_get_bool(obj, "maximized");
|
||||
_focus = obs_data_get_bool(obj, "focus");
|
||||
_windowFocusChanged = obs_data_get_bool(obj, "windowFocusChanged");
|
||||
#ifdef _WIN32
|
||||
_checkText = obs_data_get_bool(obj, "checkWindowText");
|
||||
#else
|
||||
_checkText = false;
|
||||
#endif
|
||||
_text.Load(obj, "text");
|
||||
_textRegex.Load(obj, "textRegexConfig");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -107,19 +156,33 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionWindow> entryData)
|
||||
: QWidget(parent),
|
||||
_windowSelection(new QComboBox()),
|
||||
_windowRegex(new RegexConfigWidget(this)),
|
||||
_checkTitle(new QCheckBox()),
|
||||
_fullscreen(new QCheckBox()),
|
||||
_maximized(new QCheckBox()),
|
||||
_focused(new QCheckBox()),
|
||||
_windowFocusChanged(new QCheckBox()),
|
||||
_checkText(new QCheckBox()),
|
||||
_text(new VariableTextEdit(this)),
|
||||
_textRegex(new RegexConfigWidget(this)),
|
||||
_focusWindow(new QLabel()),
|
||||
_focusLayout(new QHBoxLayout())
|
||||
_currentFocusLayout(new QHBoxLayout())
|
||||
{
|
||||
_windowSelection->setEditable(true);
|
||||
_windowSelection->setMaxVisibleItems(20);
|
||||
|
||||
_checkText->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.text.note"));
|
||||
_text->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.text.note"));
|
||||
|
||||
QWidget::connect(_windowSelection,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(WindowChanged(const QString &)));
|
||||
QWidget::connect(_windowRegex, SIGNAL(RegexConfigChanged(RegexConfig)),
|
||||
this, SLOT(WindowRegexChanged(RegexConfig)));
|
||||
QWidget::connect(_checkTitle, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(CheckTitleChanged(int)));
|
||||
QWidget::connect(_fullscreen, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FullscreenChanged(int)));
|
||||
QWidget::connect(_maximized, SIGNAL(stateChanged(int)), this,
|
||||
@@ -128,6 +191,12 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
|
||||
SLOT(FocusedChanged(int)));
|
||||
QWidget::connect(_windowFocusChanged, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(WindowFocusChanged(int)));
|
||||
QWidget::connect(_checkText, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(CheckTextChanged(int)));
|
||||
QWidget::connect(_text, SIGNAL(textChanged()), this,
|
||||
SLOT(WindowTextChanged()));
|
||||
QWidget::connect(_textRegex, SIGNAL(RegexConfigChanged(RegexConfig)),
|
||||
this, SLOT(TextRegexChanged(RegexConfig)));
|
||||
QWidget::connect(&_timer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateFocusWindow()));
|
||||
|
||||
@@ -135,28 +204,71 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{windows}}", _windowSelection},
|
||||
{"{{windowRegex}}", _windowRegex},
|
||||
{"{{checkTitle}}", _checkTitle},
|
||||
{"{{fullscreen}}", _fullscreen},
|
||||
{"{{maximized}}", _maximized},
|
||||
{"{{focused}}", _focused},
|
||||
{"{{windowFocusChanged}}", _windowFocusChanged},
|
||||
{"{{focusWindow}}", _focusWindow},
|
||||
{"{{checkText}}", _checkText},
|
||||
{"{{windowText}}", _text},
|
||||
{"{{textRegex}}", _textRegex},
|
||||
};
|
||||
|
||||
auto *line1Layout = new QHBoxLayout;
|
||||
auto titleLayout = new QHBoxLayout;
|
||||
titleLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line1"),
|
||||
line1Layout, widgetPlaceholders);
|
||||
auto *line2Layout = new QHBoxLayout;
|
||||
"AdvSceneSwitcher.condition.window.entry.window"),
|
||||
titleLayout, widgetPlaceholders);
|
||||
auto fullscreenLayout = new QHBoxLayout;
|
||||
fullscreenLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.fullscreen"),
|
||||
fullscreenLayout, widgetPlaceholders);
|
||||
auto maximizedLayout = new QHBoxLayout;
|
||||
maximizedLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.maximized"),
|
||||
maximizedLayout, widgetPlaceholders);
|
||||
auto focusedLayout = new QHBoxLayout;
|
||||
focusedLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line2"),
|
||||
line2Layout, widgetPlaceholders);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.line3"),
|
||||
_focusLayout, widgetPlaceholders);
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(line1Layout);
|
||||
mainLayout->addLayout(line2Layout);
|
||||
mainLayout->addLayout(_focusLayout);
|
||||
"AdvSceneSwitcher.condition.window.entry.focused"),
|
||||
focusedLayout, widgetPlaceholders);
|
||||
auto focusChangedLayout = new QHBoxLayout;
|
||||
focusChangedLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.focusedChange"),
|
||||
focusChangedLayout, widgetPlaceholders);
|
||||
auto textLayout = new QHBoxLayout;
|
||||
textLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(
|
||||
obs_module_text("AdvSceneSwitcher.condition.window.entry.text"),
|
||||
textLayout, widgetPlaceholders);
|
||||
_text->setSizePolicy(QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Preferred);
|
||||
textLayout->setStretchFactor(_text, 10);
|
||||
_currentFocusLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.window.entry.currentFocus"),
|
||||
_currentFocusLayout, widgetPlaceholders);
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(titleLayout);
|
||||
mainLayout->addLayout(fullscreenLayout);
|
||||
mainLayout->addLayout(maximizedLayout);
|
||||
mainLayout->addLayout(focusedLayout);
|
||||
mainLayout->addLayout(focusChangedLayout);
|
||||
mainLayout->addLayout(textLayout);
|
||||
#ifndef _WIN32
|
||||
SetLayoutVisible(textLayout, false);
|
||||
#endif
|
||||
mainLayout->addLayout(_currentFocusLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -178,6 +290,72 @@ void MacroConditionWindowEdit::WindowChanged(const QString &text)
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::WindowRegexChanged(RegexConfig conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_windowRegex = conf;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::CheckTitleChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
const QSignalBlocker b1(_windowSelection);
|
||||
const QSignalBlocker b2(_windowRegex);
|
||||
if (!state) {
|
||||
_entryData->_window = ".*";
|
||||
_entryData->_windowRegex.SetEnabled(true);
|
||||
_windowSelection->setCurrentText(".*");
|
||||
_windowRegex->EnableChanged(true);
|
||||
}
|
||||
_entryData->_checkTitle = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::CheckTextChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_checkText = state;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::WindowTextChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_text = _text->toPlainText().toStdString();
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::TextRegexChanged(RegexConfig conf)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_textRegex = conf;
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::FullscreenChanged(int state)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -230,9 +408,14 @@ void MacroConditionWindowEdit::SetWidgetVisibility()
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
SetLayoutVisible(_focusLayout,
|
||||
SetLayoutVisible(_currentFocusLayout,
|
||||
_entryData->_focus || _entryData->_windowFocusChanged);
|
||||
_windowSelection->setVisible(_entryData->_checkTitle);
|
||||
_windowRegex->setVisible(_entryData->_checkTitle);
|
||||
_textRegex->setVisible(_entryData->_checkText);
|
||||
_text->setVisible(_entryData->_checkText);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroConditionWindowEdit::UpdateEntryData()
|
||||
@@ -241,11 +424,17 @@ void MacroConditionWindowEdit::UpdateEntryData()
|
||||
return;
|
||||
}
|
||||
|
||||
_windowSelection->setCurrentText(_entryData->_window.c_str());
|
||||
_windowSelection->setCurrentText(
|
||||
_entryData->_window.UnresolvedValue().c_str());
|
||||
_windowRegex->SetRegexConfig(_entryData->_windowRegex);
|
||||
_checkTitle->setChecked(_entryData->_checkTitle);
|
||||
_fullscreen->setChecked(_entryData->_fullscreen);
|
||||
_maximized->setChecked(_entryData->_maximized);
|
||||
_focused->setChecked(_entryData->_focus);
|
||||
_windowFocusChanged->setChecked(_entryData->_windowFocusChanged);
|
||||
_checkText->setChecked(_entryData->_checkText);
|
||||
_text->setPlainText(_entryData->_text);
|
||||
_textRegex->SetRegexConfig(_entryData->_textRegex);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "variable-text-edit.hpp"
|
||||
#include "regex-config.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
@@ -20,19 +22,23 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool
|
||||
CheckWindowTitleSwitchDirect(const std::string ¤tWindowTitle);
|
||||
bool
|
||||
CheckWindowTitleSwitchRegex(const std::string ¤tWindowTitle,
|
||||
const std::vector<std::string> &windowList);
|
||||
bool WindowMatches(const std::string &window);
|
||||
bool WindowRegexMatches(const std::vector<std::string> &windowList);
|
||||
|
||||
public:
|
||||
std::string _window;
|
||||
StringVariable _window;
|
||||
RegexConfig _windowRegex;
|
||||
bool _checkTitle = true;
|
||||
bool _fullscreen = false;
|
||||
bool _maximized = false;
|
||||
bool _focus = true;
|
||||
bool _windowFocusChanged = false;
|
||||
|
||||
// For now only supported on Windows
|
||||
bool _checkText = false;
|
||||
StringVariable _text;
|
||||
RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig();
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
@@ -56,22 +62,32 @@ public:
|
||||
|
||||
private slots:
|
||||
void WindowChanged(const QString &text);
|
||||
void WindowRegexChanged(RegexConfig);
|
||||
void CheckTitleChanged(int state);
|
||||
void FullscreenChanged(int state);
|
||||
void MaximizedChanged(int state);
|
||||
void FocusedChanged(int state);
|
||||
void WindowFocusChanged(int state);
|
||||
void CheckTextChanged(int state);
|
||||
void WindowTextChanged();
|
||||
void TextRegexChanged(RegexConfig);
|
||||
void UpdateFocusWindow();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
QComboBox *_windowSelection;
|
||||
RegexConfigWidget *_windowRegex;
|
||||
QCheckBox *_checkTitle;
|
||||
QCheckBox *_fullscreen;
|
||||
QCheckBox *_maximized;
|
||||
QCheckBox *_focused;
|
||||
QCheckBox *_windowFocusChanged;
|
||||
QCheckBox *_checkText;
|
||||
VariableTextEdit *_text;
|
||||
RegexConfigWidget *_textRegex;
|
||||
QLabel *_focusWindow;
|
||||
QHBoxLayout *_focusLayout;
|
||||
QHBoxLayout *_currentFocusLayout;
|
||||
QTimer _timer;
|
||||
std::shared_ptr<MacroConditionWindow> _entryData;
|
||||
|
||||
|
||||
@@ -255,6 +255,12 @@ bool IsFullscreen(const std::string &title)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &window)
|
||||
{
|
||||
// Not implemented
|
||||
return {};
|
||||
}
|
||||
|
||||
int SecondsSinceLastInput()
|
||||
{
|
||||
double time = CGEventSourceSecondsSinceLastEventType(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string>
|
||||
#include <QStringList>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -18,6 +19,7 @@ void GetWindowList(QStringList &windows);
|
||||
void GetCurrentWindowTitle(std::string &title);
|
||||
bool IsFullscreen(const std::string &title);
|
||||
bool IsMaximized(const std::string &title);
|
||||
std::optional<std::string> GetTextInWindow(const std::string &window);
|
||||
int SecondsSinceLastInput();
|
||||
void GetProcessList(QStringList &processes);
|
||||
void GetForegroundProcessName(std::string &name);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "platform-funcs.hpp"
|
||||
#include "hotkey.hpp"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <UIAutomation.h>
|
||||
#include <util/platform.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <Psapi.h>
|
||||
@@ -178,6 +178,91 @@ bool IsMaximized(const std::string &title)
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::wstring GetControlText(HWND hwnd, IUIAutomationElement *element)
|
||||
{
|
||||
VARIANT var;
|
||||
std::wstring text = L"";
|
||||
HRESULT hr = element->GetCurrentPropertyValue(UIA_NamePropertyId, &var);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (var.vt == VT_BSTR && var.bstrVal) {
|
||||
text = var.bstrVal;
|
||||
}
|
||||
VariantClear(&var);
|
||||
}
|
||||
|
||||
hr = element->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &var);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (var.vt == VT_BSTR && var.bstrVal) {
|
||||
if (!text.empty()) {
|
||||
text += L"\n";
|
||||
}
|
||||
text += +var.bstrVal;
|
||||
}
|
||||
VariantClear(&var);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetTextInWindow(const std::string &window)
|
||||
{
|
||||
HWND hwnd = getHWNDfromTitle(window);
|
||||
if (!hwnd) {
|
||||
return {};
|
||||
}
|
||||
|
||||
IUIAutomation *automation = nullptr;
|
||||
auto hr = CoCreateInstance(__uuidof(CUIAutomation), nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IUIAutomation),
|
||||
reinterpret_cast<void **>(&automation));
|
||||
if (FAILED(hr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
IUIAutomationElement *rootElement = nullptr;
|
||||
hr = automation->ElementFromHandle(hwnd, &rootElement);
|
||||
if (FAILED(hr)) {
|
||||
automation->Release();
|
||||
return {};
|
||||
}
|
||||
|
||||
IUIAutomationTreeWalker *walker = nullptr;
|
||||
hr = automation->get_ControlViewWalker(&walker);
|
||||
if (FAILED(hr)) {
|
||||
rootElement->Release();
|
||||
automation->Release();
|
||||
return {};
|
||||
}
|
||||
|
||||
IUIAutomationElement *element = nullptr;
|
||||
std::wstring result;
|
||||
|
||||
hr = walker->GetFirstChildElement(rootElement, &element);
|
||||
while (SUCCEEDED(hr) && element != nullptr) {
|
||||
auto text = GetControlText(hwnd, element);
|
||||
if (!text.empty()) {
|
||||
result += text + L"\n";
|
||||
}
|
||||
|
||||
IUIAutomationElement *nextElement = nullptr;
|
||||
hr = walker->GetNextSiblingElement(element, &nextElement);
|
||||
element->Release();
|
||||
element = nextElement;
|
||||
}
|
||||
|
||||
walker->Release();
|
||||
rootElement->Release();
|
||||
automation->Release();
|
||||
|
||||
// Convert to std::string
|
||||
int len = os_wcs_to_utf8(result.c_str(), 0, nullptr, 0);
|
||||
std::string tmp;
|
||||
tmp.resize(len);
|
||||
os_wcs_to_utf8(result.c_str(), 0, &tmp[0], len + 1);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool IsFullscreen(const std::string &title)
|
||||
{
|
||||
RECT appBounds;
|
||||
@@ -539,6 +624,8 @@ static void SetupMouseEeventFilter()
|
||||
void PlatformInit()
|
||||
{
|
||||
SetupMouseEeventFilter();
|
||||
|
||||
CoInitialize(NULL);
|
||||
}
|
||||
|
||||
void PlatformCleanup()
|
||||
@@ -547,6 +634,8 @@ void PlatformCleanup()
|
||||
delete mouseInputFilter;
|
||||
mouseInputFilter = nullptr;
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
Reference in New Issue
Block a user