mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 09:05:57 -05:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b561a20dde | ||
|
|
835722af34 | ||
|
|
93efc7cab3 | ||
|
|
d69364ec19 | ||
|
|
d75066df5f | ||
|
|
863c84026e | ||
|
|
a523081d77 | ||
|
|
66534519d6 | ||
|
|
b3b2114c45 | ||
|
|
510f83246e | ||
|
|
c969b21f93 | ||
|
|
6ec40ef8e9 | ||
|
|
27859e83b3 | ||
|
|
263565700a |
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Logs**
|
|
||||||
Please provide a log of your issue with verbose logging enabled (See General tab of the plugin).
|
|
||||||
In case of a crash, please also include the corresponding crash log.
|
|
||||||
See [here](https://obsproject.com/forum/threads/please-post-a-log-with-your-issue-heres-how.23074/) for a description where to find the log files and how to share them.
|
|
||||||
Please share the currently used plugin settings by exporting them them to a file (See General tab of the plugin).
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Version information**
|
|
||||||
- OS: [e.g. Windows 10]
|
|
||||||
- OBS Version [e.g. 27.1.3]
|
|
||||||
- Plugin Version [e.g. 1.17.1]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
111
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
111
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# Based on the OBS issue templates
|
||||||
|
# https://github.com/obsproject/.github/tree/master/.github/ISSUE_TEMPLATE
|
||||||
|
|
||||||
|
name: Bug Report
|
||||||
|
description: Report a bug or crash
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
id: md_welcome
|
||||||
|
attributes:
|
||||||
|
value: This form is for reporting bugs for Advanced Scene Switcher!
|
||||||
|
- type: dropdown
|
||||||
|
id: os_info
|
||||||
|
attributes:
|
||||||
|
label: Operating System Info
|
||||||
|
description: What Operating System are you running?
|
||||||
|
options:
|
||||||
|
- Windows 11
|
||||||
|
- Windows 10
|
||||||
|
- macOS 14
|
||||||
|
- macOS 13
|
||||||
|
- macOS 12
|
||||||
|
- macOS 11
|
||||||
|
- Ubuntu 23.10
|
||||||
|
- Ubuntu 22.04
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: os_info_other
|
||||||
|
attributes:
|
||||||
|
label: Other OS
|
||||||
|
description: "If \"Other\" was selected above, what OS are you using?"
|
||||||
|
placeholder: "e.g., Arch Linux, FreeBSD"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: obs_version
|
||||||
|
attributes:
|
||||||
|
label: OBS Studio Version
|
||||||
|
description: What version of OBS Studio are you using?
|
||||||
|
placeholder: 30.2.2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: advss_version
|
||||||
|
attributes:
|
||||||
|
label: Advanced Scene Switcher Version
|
||||||
|
description: What version of the Advanced Scene Switcher are you using?
|
||||||
|
placeholder: 1.27.2
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: settings
|
||||||
|
attributes:
|
||||||
|
label: Plugin settings
|
||||||
|
description: |
|
||||||
|
Please provide the plugin settings used.
|
||||||
|
Either only [export the macros](https://github.com/WarmUpTill/SceneSwitcher/wiki/Exporting-and-importing-individual-macros) relevant to this issue or [export all settings](https://github.com/WarmUpTill/SceneSwitcher/wiki/Saving-and-loading-settings#how-to-create-and-import-a-backup-of-your-settings) on the General tab of the plugin.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: obs_log_url
|
||||||
|
attributes:
|
||||||
|
label: OBS Studio Log URL
|
||||||
|
description: Please provide the obsproject.com URL (from Help menu > Log Files > Upload Current/Previous Log File) to the OBS log file where this issue occurred.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: obs_crash_log_url
|
||||||
|
attributes:
|
||||||
|
label: OBS Studio Crash Log URL
|
||||||
|
description: If this is a crash report, please provide the obsproject.com URL to the OBS crash log file where this issue occurred.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: expected_behavior
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: "What did you expect to happen?"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: current_behavior
|
||||||
|
attributes:
|
||||||
|
label: Current Behavior
|
||||||
|
description: "What actually happened?"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps_to_reproduce
|
||||||
|
attributes:
|
||||||
|
label: Steps to Reproduce
|
||||||
|
description: "How do you trigger this bug? Please walk us through it step by step."
|
||||||
|
placeholder: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
...
|
||||||
|
value: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: additional_notes
|
||||||
|
attributes:
|
||||||
|
label: Anything else we should know?
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
7
.github/ISSUE_TEMPLATE/config.yml
vendored
7
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Help/Support
|
- name: 📚 Wiki
|
||||||
url: https://github.com/WarmUpTill/SceneSwitcher/wiki
|
url: https://github.com/WarmUpTill/SceneSwitcher/wiki
|
||||||
about: For general questions about how to use and configure the plugin please have a look at the wiki (https://github.com/WarmUpTill/SceneSwitcher/wiki) or ask questions in the OBS forum (https://obsproject.com/forum/threads/advanced-scene-switcher.48264/)
|
about: For explanations on how the plugin works or to see example guides, check out the wiki.
|
||||||
|
- name: 💬 OBS Forum Thread
|
||||||
|
url: https://obsproject.com/forum/threads/advanced-scene-switcher.48264
|
||||||
|
about: To discuss the plugin or get assistance, feel free to use the OBS forum thread or the GitHub discussions page.
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
37
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
37
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Based on the OBS issue templates
|
||||||
|
# https://github.com/obsproject/.github/tree/master/.github/ISSUE_TEMPLATE
|
||||||
|
|
||||||
|
name: Feature request
|
||||||
|
description: Suggest an idea for this projectReport a bug or crash
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
id: md_welcome
|
||||||
|
attributes:
|
||||||
|
value: This form is for requesting new features for Advanced Scene Switcher!
|
||||||
|
- type: textarea
|
||||||
|
id: background
|
||||||
|
attributes:
|
||||||
|
label: Background
|
||||||
|
description: "Is your feature request related to a problem? Please describe."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Solution
|
||||||
|
description: "Describe the solution you'd like."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives
|
||||||
|
description: "Describe alternatives you've considered."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: additional_notes
|
||||||
|
attributes:
|
||||||
|
label: Anything else we should know?
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -686,6 +686,16 @@ AdvSceneSwitcher.condition.twitch.type.polling.channel.live="Stream is currently
|
|||||||
AdvSceneSwitcher.condition.twitch.type.polling.channel.title="Current title matches"
|
AdvSceneSwitcher.condition.twitch.type.polling.channel.title="Current title matches"
|
||||||
AdvSceneSwitcher.condition.twitch.type.polling.channel.category="Current category is"
|
AdvSceneSwitcher.condition.twitch.type.polling.channel.category="Current category is"
|
||||||
AdvSceneSwitcher.condition.twitch.type.chat.message="Chat message received"
|
AdvSceneSwitcher.condition.twitch.type.chat.message="Chat message received"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties="Chat message properties:"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.select="Select chat message property:"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.firstMessage="Is first message"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.emoteOnly="Is only using emotes"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.mod="Is moderator"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.subscriber="Is subscriber"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.turbo="Is Turbo user"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.displayName="Display name matches"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.loginName="User name matches"
|
||||||
|
AdvSceneSwitcher.condition.twitch.type.chat.properties.badge="User has badge"
|
||||||
AdvSceneSwitcher.condition.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
|
AdvSceneSwitcher.condition.twitch.categorySelectionDisabled="Cannot select category without selecting a Twitch account first!"
|
||||||
AdvSceneSwitcher.condition.twitch.entry="Channel{{channel}}{{conditions}}{{pointsReward}}{{streamTitle}}{{regex}}{{category}}"
|
AdvSceneSwitcher.condition.twitch.entry="Channel{{channel}}{{conditions}}{{pointsReward}}{{streamTitle}}{{regex}}{{category}}"
|
||||||
AdvSceneSwitcher.condition.twitch.entry.account="Check using account{{account}}"
|
AdvSceneSwitcher.condition.twitch.entry.account="Check using account{{account}}"
|
||||||
@@ -1670,9 +1680,13 @@ AdvSceneSwitcher.tempVar.twitch.user_login.removeMod.description="The user login
|
|||||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="User name"
|
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="User name"
|
||||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="The display name of the removed moderator."
|
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="The display name of the removed moderator."
|
||||||
|
|
||||||
AdvSceneSwitcher.tempVar.twitch.chatter="User login"
|
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive="User login"
|
||||||
AdvSceneSwitcher.tempVar.twitch.chatter.description="The user login of the person who sent the chat message."
|
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive.description="The user login of the person who sent the chat message."
|
||||||
AdvSceneSwitcher.tempVar.twitch.chat_message="Chat message"
|
AdvSceneSwitcher.tempVar.twitch.user_name.chatReceive="User name"
|
||||||
|
AdvSceneSwitcher.tempVar.twitch.user_name.chatReceive.description="The display name of the person who sent the chat message."
|
||||||
|
AdvSceneSwitcher.tempVar.twitch.chat_message.chatReceive="Chat message"
|
||||||
|
AdvSceneSwitcher.tempVar.twitch.badges.chatReceive="Chatter Badges"
|
||||||
|
AdvSceneSwitcher.tempVar.twitch.badges.chatReceive.description="A string describing the badges available to this user and their state.\nFor example: vip/1,subscriber/42,hype-train/1"
|
||||||
|
|
||||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="Raid creator user ID"
|
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="Raid creator user ID"
|
||||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="The broadcaster ID that created the raid."
|
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="The broadcaster ID that created the raid."
|
||||||
|
|||||||
@@ -1632,9 +1632,9 @@ AdvSceneSwitcher.tempVar.twitch.user_login.removeMod.description="削除され
|
|||||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="ユーザー名"
|
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="ユーザー名"
|
||||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="削除されたモデレーターの表示名"
|
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="削除されたモデレーターの表示名"
|
||||||
|
|
||||||
AdvSceneSwitcher.tempVar.twitch.chatter="ユーザーログイン"
|
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive="ユーザーログイン"
|
||||||
AdvSceneSwitcher.tempVar.twitch.chatter.description="チャットメッセージを送信した人のユーザーログイン情報"
|
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive.description="チャットメッセージを送信した人のユーザーログイン情報"
|
||||||
AdvSceneSwitcher.tempVar.twitch.chat_message="チャットメッセージ"
|
AdvSceneSwitcher.tempVar.twitch.chat_message.chatReceive="チャットメッセージ"
|
||||||
|
|
||||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="RaidクリエイターユーザーID"
|
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="RaidクリエイターユーザーID"
|
||||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="レイドを作成した配信者ID"
|
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="レイドを作成した配信者ID"
|
||||||
|
|||||||
@@ -1657,9 +1657,9 @@ AdvSceneSwitcher.tempVar.twitch.user_login.removeMod.description="O login do usu
|
|||||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="Nome de usuário"
|
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod="Nome de usuário"
|
||||||
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="O nome de exibição do usuário que foi removido como moderador."
|
AdvSceneSwitcher.tempVar.twitch.user_name.removeMod.description="O nome de exibição do usuário que foi removido como moderador."
|
||||||
|
|
||||||
AdvSceneSwitcher.tempVar.twitch.chatter="Nome de login do usuário"
|
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive="Nome de login do usuário"
|
||||||
AdvSceneSwitcher.tempVar.twitch.chatter.description="O nome de login da pessoa que enviou a mensagem de chat."
|
AdvSceneSwitcher.tempVar.twitch.user_login.chatReceive.description="O nome de login da pessoa que enviou a mensagem de chat."
|
||||||
AdvSceneSwitcher.tempVar.twitch.chat_message="Mensagem de chat"
|
AdvSceneSwitcher.tempVar.twitch.chat_message.chatReceive="Mensagem de chat"
|
||||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="ID do usuário criador do raid"
|
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid="ID do usuário criador do raid"
|
||||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="O ID do broadcaster que criou o raid."
|
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_id.raid.description="O ID do broadcaster que criou o raid."
|
||||||
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_login.raid="Nome de login do criador do raid"
|
AdvSceneSwitcher.tempVar.twitch.from_broadcaster_user_login.raid="Nome de login do criador do raid"
|
||||||
|
|||||||
@@ -762,11 +762,6 @@ void OpenSettingsWindow()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *GetSettingsWindow()
|
|
||||||
{
|
|
||||||
return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
|
void AdvSceneSwitcher::HighligthMacroSettingsButton(bool enable)
|
||||||
{
|
{
|
||||||
static QObject *highlight = nullptr;
|
static QObject *highlight = nullptr;
|
||||||
|
|||||||
@@ -411,7 +411,6 @@ public slots:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void OpenSettingsWindow();
|
void OpenSettingsWindow();
|
||||||
QWidget *GetSettingsWindow();
|
|
||||||
void HighligthMacroSettingsButton(bool enable);
|
void HighligthMacroSettingsButton(bool enable);
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -170,8 +170,10 @@ void AdvSceneSwitcher::on_sceneGroupAdd_clicked()
|
|||||||
item->setData(Qt::UserRole, text);
|
item->setData(Qt::UserRole, text);
|
||||||
ui->sceneGroups->setCurrentItem(item);
|
ui->sceneGroups->setCurrentItem(item);
|
||||||
|
|
||||||
addPulse->deleteLater();
|
if (addPulse) {
|
||||||
addPulse = nullptr;
|
addPulse->deleteLater();
|
||||||
|
addPulse = nullptr;
|
||||||
|
}
|
||||||
ui->sceneGroupHelp->setVisible(false);
|
ui->sceneGroupHelp->setVisible(false);
|
||||||
|
|
||||||
emit SceneGroupAdded(QString::fromStdString(name));
|
emit SceneGroupAdded(QString::fromStdString(name));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_triggerAdd_clicked()
|
|||||||
listAddClicked(ui->sceneTriggers,
|
listAddClicked(ui->sceneTriggers,
|
||||||
new SceneTriggerWidget(this,
|
new SceneTriggerWidget(this,
|
||||||
&switcher->sceneTriggers.back()),
|
&switcher->sceneTriggers.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->triggerHelp->setVisible(false);
|
ui->triggerHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_audioAdd_clicked()
|
|||||||
AudioSwitchWidget *sw =
|
AudioSwitchWidget *sw =
|
||||||
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
|
new AudioSwitchWidget(this, &switcher->audioSwitches.back());
|
||||||
|
|
||||||
listAddClicked(ui->audioSwitches, sw, addPulse);
|
listAddClicked(ui->audioSwitches, sw, &addPulse);
|
||||||
|
|
||||||
ui->audioHelp->setVisible(false);
|
ui->audioHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_executableAdd_clicked()
|
|||||||
listAddClicked(ui->executables,
|
listAddClicked(ui->executables,
|
||||||
new ExecutableSwitchWidget(
|
new ExecutableSwitchWidget(
|
||||||
this, &switcher->executableSwitches.back()),
|
this, &switcher->executableSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->exeHelp->setVisible(false);
|
ui->exeHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ void AdvSceneSwitcher::on_fileAdd_clicked()
|
|||||||
listAddClicked(ui->fileSwitches,
|
listAddClicked(ui->fileSwitches,
|
||||||
new FileSwitchWidget(this,
|
new FileSwitchWidget(this,
|
||||||
&switcher->fileSwitches.back()),
|
&switcher->fileSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->fileHelp->setVisible(false);
|
ui->fileHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void AdvSceneSwitcher::on_mediaAdd_clicked()
|
|||||||
listAddClicked(ui->mediaSwitches,
|
listAddClicked(ui->mediaSwitches,
|
||||||
new MediaSwitchWidget(this,
|
new MediaSwitchWidget(this,
|
||||||
&switcher->mediaSwitches.back()),
|
&switcher->mediaSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->mediaHelp->setVisible(false);
|
ui->mediaHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ void AdvSceneSwitcher::on_pauseAdd_clicked()
|
|||||||
listAddClicked(ui->pauseEntries,
|
listAddClicked(ui->pauseEntries,
|
||||||
new PauseEntryWidget(this,
|
new PauseEntryWidget(this,
|
||||||
&switcher->pauseEntries.back()),
|
&switcher->pauseEntries.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->pauseHelp->setVisible(false);
|
ui->pauseHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ void AdvSceneSwitcher::on_randomAdd_clicked()
|
|||||||
listAddClicked(ui->randomSwitches,
|
listAddClicked(ui->randomSwitches,
|
||||||
new RandomSwitchWidget(this,
|
new RandomSwitchWidget(this,
|
||||||
&switcher->randomSwitches.back()),
|
&switcher->randomSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->randomHelp->setVisible(false);
|
ui->randomHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void AdvSceneSwitcher::on_screenRegionAdd_clicked()
|
|||||||
listAddClicked(ui->screenRegionSwitches,
|
listAddClicked(ui->screenRegionSwitches,
|
||||||
new ScreenRegionWidget(
|
new ScreenRegionWidget(
|
||||||
this, &switcher->screenRegionSwitches.back()),
|
this, &switcher->screenRegionSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->regionHelp->setVisible(false);
|
ui->regionHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ void AdvSceneSwitcher::on_sceneSequenceAdd_clicked()
|
|||||||
listAddClicked(ui->sceneSequenceSwitches,
|
listAddClicked(ui->sceneSequenceSwitches,
|
||||||
new SequenceWidget(
|
new SequenceWidget(
|
||||||
this, &switcher->sceneSequenceSwitches.back()),
|
this, &switcher->sceneSequenceSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->sequenceHelp->setVisible(false);
|
ui->sequenceHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ void AdvSceneSwitcher::on_timeAdd_clicked()
|
|||||||
listAddClicked(ui->timeSwitches,
|
listAddClicked(ui->timeSwitches,
|
||||||
new TimeSwitchWidget(this,
|
new TimeSwitchWidget(this,
|
||||||
&switcher->timeSwitches.back()),
|
&switcher->timeSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->timeHelp->setVisible(false);
|
ui->timeHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ void AdvSceneSwitcher::on_videoAdd_clicked()
|
|||||||
VideoSwitchWidget *sw =
|
VideoSwitchWidget *sw =
|
||||||
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
|
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
|
||||||
|
|
||||||
listAddClicked(ui->videoSwitches, sw, addPulse);
|
listAddClicked(ui->videoSwitches, sw, &addPulse);
|
||||||
|
|
||||||
ui->videoHelp->setVisible(false);
|
ui->videoHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ void AdvSceneSwitcher::on_windowAdd_clicked()
|
|||||||
listAddClicked(ui->windowSwitches,
|
listAddClicked(ui->windowSwitches,
|
||||||
new WindowSwitchWidget(this,
|
new WindowSwitchWidget(this,
|
||||||
&switcher->windowSwitches.back()),
|
&switcher->windowSwitches.back()),
|
||||||
addPulse);
|
&addPulse);
|
||||||
|
|
||||||
ui->windowHelp->setVisible(false);
|
ui->windowHelp->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public:
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static std::mutex mutex;
|
static std::recursive_mutex mutex;
|
||||||
|
|
||||||
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
||||||
{
|
{
|
||||||
@@ -31,7 +31,7 @@ std::map<std::string, MacroActionInfo> &MacroActionFactory::GetMap()
|
|||||||
|
|
||||||
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
||||||
GetMap()[id] = info;
|
GetMap()[id] = info;
|
||||||
return true;
|
return true;
|
||||||
@@ -41,7 +41,7 @@ bool MacroActionFactory::Register(const std::string &id, MacroActionInfo info)
|
|||||||
|
|
||||||
bool MacroActionFactory::Deregister(const std::string &id)
|
bool MacroActionFactory::Deregister(const std::string &id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (GetMap().count(id) == 0) {
|
if (GetMap().count(id) == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ static std::shared_ptr<MacroAction> createUnknownAction(Macro *m)
|
|||||||
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
|
std::shared_ptr<MacroAction> MacroActionFactory::Create(const std::string &id,
|
||||||
Macro *m)
|
Macro *m)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._create(m);
|
return it->second._create(m);
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ QWidget *MacroActionFactory::CreateWidget(const std::string &id,
|
|||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
std::shared_ptr<MacroAction> action)
|
std::shared_ptr<MacroAction> action)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._createWidget(parent, action);
|
return it->second._createWidget(parent, action);
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ QWidget *MacroActionFactory::CreateWidget(const std::string &id,
|
|||||||
|
|
||||||
std::string MacroActionFactory::GetActionName(const std::string &id)
|
std::string MacroActionFactory::GetActionName(const std::string &id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._name;
|
return it->second._name;
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ std::string MacroActionFactory::GetActionName(const std::string &id)
|
|||||||
|
|
||||||
std::string MacroActionFactory::GetIdByName(const QString &name)
|
std::string MacroActionFactory::GetIdByName(const QString &name)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
for (auto it : GetMap()) {
|
for (auto it : GetMap()) {
|
||||||
if (name == obs_module_text(it.second._name.c_str())) {
|
if (name == obs_module_text(it.second._name.c_str())) {
|
||||||
return it.first;
|
return it.first;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public:
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static std::mutex mutex;
|
static std::recursive_mutex mutex;
|
||||||
|
|
||||||
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ std::map<std::string, MacroConditionInfo> &MacroConditionFactory::GetMap()
|
|||||||
bool MacroConditionFactory::Register(const std::string &id,
|
bool MacroConditionFactory::Register(const std::string &id,
|
||||||
MacroConditionInfo info)
|
MacroConditionInfo info)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
if (auto it = GetMap().find(id); it == GetMap().end()) {
|
||||||
GetMap()[id] = info;
|
GetMap()[id] = info;
|
||||||
return true;
|
return true;
|
||||||
@@ -38,7 +38,7 @@ bool MacroConditionFactory::Register(const std::string &id,
|
|||||||
|
|
||||||
bool MacroConditionFactory::Deregister(const std::string &id)
|
bool MacroConditionFactory::Deregister(const std::string &id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (GetMap().count(id) == 0) {
|
if (GetMap().count(id) == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ static std::shared_ptr<MacroCondition> createUnknownCondition(Macro *m)
|
|||||||
std::shared_ptr<MacroCondition>
|
std::shared_ptr<MacroCondition>
|
||||||
MacroConditionFactory::Create(const std::string &id, Macro *m)
|
MacroConditionFactory::Create(const std::string &id, Macro *m)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._create(m);
|
return it->second._create(m);
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ QWidget *
|
|||||||
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
|
MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
|
||||||
std::shared_ptr<MacroCondition> cond)
|
std::shared_ptr<MacroCondition> cond)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._createWidget(parent, cond);
|
return it->second._createWidget(parent, cond);
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ MacroConditionFactory::CreateWidget(const std::string &id, QWidget *parent,
|
|||||||
|
|
||||||
std::string MacroConditionFactory::GetConditionName(const std::string &id)
|
std::string MacroConditionFactory::GetConditionName(const std::string &id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._name;
|
return it->second._name;
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ std::string MacroConditionFactory::GetConditionName(const std::string &id)
|
|||||||
|
|
||||||
std::string MacroConditionFactory::GetIdByName(const QString &name)
|
std::string MacroConditionFactory::GetIdByName(const QString &name)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
for (auto it : GetMap()) {
|
for (auto it : GetMap()) {
|
||||||
if (name == obs_module_text(it.second._name.c_str())) {
|
if (name == obs_module_text(it.second._name.c_str())) {
|
||||||
return it.first;
|
return it.first;
|
||||||
@@ -100,7 +100,7 @@ std::string MacroConditionFactory::GetIdByName(const QString &name)
|
|||||||
|
|
||||||
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
|
bool MacroConditionFactory::UsesDurationModifier(const std::string &id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
if (auto it = GetMap().find(id); it != GetMap().end()) {
|
||||||
return it->second._useDurationModifier;
|
return it->second._useDurationModifier;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ void MacroSegmentSelection::MacroSegmentOrderChanged()
|
|||||||
SetupDescription();
|
SetupDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GetMacroSegmentDescription(Macro *macro, int idx, bool isCondition)
|
static QString GetMacroSegmentDescription(Macro *macro, int idx,
|
||||||
|
bool isCondition)
|
||||||
{
|
{
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "advanced-scene-switcher.hpp"
|
#include "advanced-scene-switcher.hpp"
|
||||||
#include "layout-helpers.hpp"
|
#include "layout-helpers.hpp"
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
|
#include "ui-helpers.hpp"
|
||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
@@ -68,7 +69,7 @@ void MacroSelection::HideSelectedMacro()
|
|||||||
void MacroSelection::ShowAllMacros()
|
void MacroSelection::ShowAllMacros()
|
||||||
{
|
{
|
||||||
auto v = qobject_cast<QListView *>(view());
|
auto v = qobject_cast<QListView *>(view());
|
||||||
for (int i = count(); i > 0; i--) {
|
for (int i = count() - 1; i >= 0; i--) {
|
||||||
v->setRowHidden(i, false);
|
v->setRowHidden(i, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,8 +100,10 @@ void AdvSceneSwitcher::on_macroAdd_clicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui->macros->Add(newMacro);
|
ui->macros->Add(newMacro);
|
||||||
addPulse->deleteLater();
|
if (addPulse) {
|
||||||
addPulse = nullptr;
|
addPulse->deleteLater();
|
||||||
|
addPulse = nullptr;
|
||||||
|
}
|
||||||
emit MacroAdded(QString::fromStdString(name));
|
emit MacroAdded(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1218,8 +1220,10 @@ void AdvSceneSwitcher::CopyMacro()
|
|||||||
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
|
Macro::PrepareMoveToGroup(macro->Parent(), newMacro);
|
||||||
|
|
||||||
ui->macros->Add(newMacro, macro);
|
ui->macros->Add(newMacro, macro);
|
||||||
addPulse->deleteLater();
|
if (addPulse) {
|
||||||
addPulse = nullptr;
|
addPulse->deleteLater();
|
||||||
|
addPulse = nullptr;
|
||||||
|
}
|
||||||
emit MacroAdded(QString::fromStdString(name));
|
emit MacroAdded(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ void MacroTreeItem::Update(bool force)
|
|||||||
_spacer = nullptr;
|
_spacer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_type == Type::Group) {
|
if (_type == Type::Group && _expand) {
|
||||||
_boxLayout->removeWidget(_expand);
|
_boxLayout->removeWidget(_expand);
|
||||||
_expand->deleteLater();
|
_expand->deleteLater();
|
||||||
_expand = nullptr;
|
_expand = nullptr;
|
||||||
|
|||||||
@@ -84,8 +84,7 @@ void FilterComboBox::focusOutEvent(QFocusEvent *event)
|
|||||||
// Reset on invalid selection
|
// Reset on invalid selection
|
||||||
if (findText(currentText()) == -1) {
|
if (findText(currentText()) == -1) {
|
||||||
setCurrentIndex(-1);
|
setCurrentIndex(-1);
|
||||||
emit currentIndexChanged(-1);
|
Emit(-1, "");
|
||||||
emit currentTextChanged("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QComboBox::focusOutEvent(event);
|
QComboBox::focusOutEvent(event);
|
||||||
@@ -121,8 +120,7 @@ void FilterComboBox::CompleterHighlightChanged(const QModelIndex &index)
|
|||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit currentIndexChanged(idx);
|
Emit(idx, text);
|
||||||
emit currentTextChanged(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterComboBox::TextChanged(const QString &text)
|
void FilterComboBox::TextChanged(const QString &text)
|
||||||
@@ -134,8 +132,18 @@ void FilterComboBox::TextChanged(const QString &text)
|
|||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit currentIndexChanged(idx);
|
}
|
||||||
emit currentTextChanged(text);
|
|
||||||
|
void FilterComboBox::Emit(int index, const QString &text)
|
||||||
|
{
|
||||||
|
if (_lastEmittedIndex != index) {
|
||||||
|
_lastEmittedIndex = index;
|
||||||
|
emit currentIndexChanged(index);
|
||||||
|
}
|
||||||
|
if (_lastEmittedText != text) {
|
||||||
|
_lastEmittedText = text;
|
||||||
|
emit currentTextChanged(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace advss
|
} // namespace advss
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ private slots:
|
|||||||
void TextChanged(const QString &);
|
void TextChanged(const QString &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Emit(int index, const QString &text);
|
||||||
|
|
||||||
|
int _lastEmittedIndex = -1;
|
||||||
|
QString _lastEmittedText;
|
||||||
|
|
||||||
int _lastCompleterHighlightRow = -1;
|
int _lastCompleterHighlightRow = -1;
|
||||||
static bool _filteringEnabled;
|
static bool _filteringEnabled;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,34 @@ void ListEditor::showEvent(QShowEvent *e)
|
|||||||
UpdateListSize();
|
UpdateListSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QListWidgetItem *getItemFromWidget(QListWidget *list, QWidget *widget)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list->count(); i++) {
|
||||||
|
auto item = list->item(i);
|
||||||
|
if (!item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto itemWidget = list->itemWidget(item);
|
||||||
|
if (itemWidget == widget) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListEditor::GetIndexOfSignal() const
|
||||||
|
{
|
||||||
|
auto sender = this->sender();
|
||||||
|
if (!sender) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto widget = qobject_cast<QWidget *>(sender);
|
||||||
|
if (!widget) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return _list->row(getItemFromWidget(_list, widget));
|
||||||
|
}
|
||||||
|
|
||||||
void ListEditor::UpdateListSize()
|
void ListEditor::UpdateListSize()
|
||||||
{
|
{
|
||||||
SetHeightToContentHeight(_list);
|
SetHeightToContentHeight(_list);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ private slots:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void UpdateListSize();
|
void UpdateListSize();
|
||||||
|
int GetIndexOfSignal() const;
|
||||||
|
|
||||||
QListWidget *_list;
|
QListWidget *_list;
|
||||||
ListControls *_controls;
|
ListControls *_controls;
|
||||||
|
|||||||
@@ -23,13 +23,16 @@ bool MouseWheelWidgetAdjustmentGuard::eventFilter(QObject *o, QEvent *e)
|
|||||||
|
|
||||||
void PreventMouseWheelAdjustWithoutFocus(QWidget *w)
|
void PreventMouseWheelAdjustWithoutFocus(QWidget *w)
|
||||||
{
|
{
|
||||||
// Ignore QScrollBar as there is no danger of accidentally modifying anything
|
// Ignore QScrollBar as there is no danger of accidentally modifying
|
||||||
// and long expanded QComboBox would be difficult to interact with otherwise.
|
// anything and long expanded QComboBox would be difficult to interact
|
||||||
// Ignore OSCMessageElementEdit to allow OSCMessageEdit list to up update
|
// with otherwise.
|
||||||
// current index correctly.
|
// Ignore OSCMessageElementEdit and ChatMessagePropertyEdit to allow
|
||||||
|
// lists to up update the current index correctly.
|
||||||
if (qobject_cast<QScrollBar *>(w) ||
|
if (qobject_cast<QScrollBar *>(w) ||
|
||||||
QString(w->metaObject()->className()) ==
|
QString(w->metaObject()->className()) ==
|
||||||
"advss::OSCMessageElementEdit") {
|
"advss::OSCMessageElementEdit" ||
|
||||||
|
QString(w->metaObject()->className()) ==
|
||||||
|
"advss::ChatMessagePropertyEdit") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
w->setFocusPolicy(Qt::StrongFocus);
|
w->setFocusPolicy(Qt::StrongFocus);
|
||||||
|
|||||||
@@ -48,16 +48,15 @@ private:
|
|||||||
friend RegexConfigDialog;
|
friend RegexConfigDialog;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RegexConfigWidget : public QWidget {
|
class ADVSS_EXPORT RegexConfigWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
EXPORT RegexConfigWidget(QWidget *parent = nullptr,
|
RegexConfigWidget(QWidget *parent = nullptr, bool showEnable = true);
|
||||||
bool showEnable = true);
|
void SetRegexConfig(const RegexConfig &);
|
||||||
EXPORT void SetRegexConfig(const RegexConfig &);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
EXPORT void EnableChanged(bool);
|
void EnableChanged(bool);
|
||||||
EXPORT void OpenSettingsClicked();
|
void OpenSettingsClicked();
|
||||||
signals:
|
signals:
|
||||||
void RegexConfigChanged(const RegexConfig &);
|
void RegexConfigChanged(const RegexConfig &);
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ void StatusControl::SetStarted()
|
|||||||
_status->setText(obs_module_text("AdvSceneSwitcher.status.active"));
|
_status->setText(obs_module_text("AdvSceneSwitcher.status.active"));
|
||||||
if (_pulse) {
|
if (_pulse) {
|
||||||
_pulse->deleteLater();
|
_pulse->deleteLater();
|
||||||
|
_pulse = nullptr;
|
||||||
}
|
}
|
||||||
SetStatusStyleSheet(false);
|
SetStatusStyleSheet(false);
|
||||||
_setToStopped = false;
|
_setToStopped = false;
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ void TempVariableRef::Save(obs_data_t *obj, const char *name) const
|
|||||||
obs_data_set_int(data, "type", static_cast<int>(type));
|
obs_data_set_int(data, "type", static_cast<int>(type));
|
||||||
obs_data_set_int(data, "idx", GetIdx());
|
obs_data_set_int(data, "idx", GetIdx());
|
||||||
obs_data_set_string(data, "id", _id.c_str());
|
obs_data_set_string(data, "id", _id.c_str());
|
||||||
|
obs_data_set_int(data, "version", 1);
|
||||||
obs_data_set_obj(obj, name, data);
|
obs_data_set_obj(obj, name, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,6 +176,14 @@ void TempVariableRef::Load(obs_data_t *obj, Macro *macro, const char *name)
|
|||||||
_id = obs_data_get_string(data, "id");
|
_id = obs_data_get_string(data, "id");
|
||||||
const auto type =
|
const auto type =
|
||||||
static_cast<SegmentType>(obs_data_get_int(data, "type"));
|
static_cast<SegmentType>(obs_data_get_int(data, "type"));
|
||||||
|
|
||||||
|
// Backwards compatibility checks
|
||||||
|
if (obs_data_get_int(data, "version") < 1) {
|
||||||
|
if (_id == "chatter") {
|
||||||
|
_id = "user_login";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AddPostLoadStep([this, idx, type, macro]() {
|
AddPostLoadStep([this, idx, type, macro]() {
|
||||||
this->PostLoad(idx, type, macro);
|
this->PostLoad(idx, type, macro);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "ui-helpers.hpp"
|
#include "ui-helpers.hpp"
|
||||||
|
#include "advanced-scene-switcher.hpp"
|
||||||
#include "non-modal-dialog.hpp"
|
#include "non-modal-dialog.hpp"
|
||||||
#include "obs-module-helper.hpp"
|
#include "obs-module-helper.hpp"
|
||||||
|
#include "plugin-state-helpers.hpp"
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <QGraphicsColorizeEffect>
|
#include <QGraphicsColorizeEffect>
|
||||||
@@ -64,8 +66,12 @@ void SetHeightToContentHeight(QListWidget *list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int scrollBarHeight = getHorizontalScrollBarHeight(list);
|
int scrollBarHeight = getHorizontalScrollBarHeight(list);
|
||||||
int height = (list->sizeHintForRow(0) + list->spacing()) * nrItems +
|
int height = 2 * list->frameWidth() + scrollBarHeight;
|
||||||
2 * list->frameWidth() + scrollBarHeight;
|
|
||||||
|
for (int i = 0; i < nrItems; i++) {
|
||||||
|
height += (list->sizeHintForRow(i) + list->spacing());
|
||||||
|
}
|
||||||
|
|
||||||
list->setMinimumHeight(height);
|
list->setMinimumHeight(height);
|
||||||
list->setMaximumHeight(height);
|
list->setMaximumHeight(height);
|
||||||
}
|
}
|
||||||
@@ -98,7 +104,10 @@ int FindIdxInRagne(QComboBox *list, int start, int stop,
|
|||||||
return foundIdx;
|
return foundIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *GetSettingsWindow();
|
QWidget *GetSettingsWindow()
|
||||||
|
{
|
||||||
|
return SettingsWindowIsOpened() ? AdvSceneSwitcher::window : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool DisplayMessage(const QString &msg, bool question, bool modal)
|
bool DisplayMessage(const QString &msg, bool question, bool modal)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ EXPORT void DisplayTrayMessage(const QString &title, const QString &msg,
|
|||||||
const QIcon &icon = QIcon());
|
const QIcon &icon = QIcon());
|
||||||
|
|
||||||
EXPORT std::string GetThemeTypeName();
|
EXPORT std::string GetThemeTypeName();
|
||||||
|
EXPORT QWidget *GetSettingsWindow();
|
||||||
|
|
||||||
void QeueUITask(void (*task)(void *param), void *param);
|
void QeueUITask(void (*task)(void *param), void *param);
|
||||||
|
|
||||||
|
|||||||
@@ -76,15 +76,15 @@ std::string ToString(double value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
||||||
QObject *addHighlight)
|
QObject **addHighlight)
|
||||||
{
|
{
|
||||||
if (!list || !newWidget) {
|
if (!list || !newWidget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addHighlight) {
|
if (addHighlight && *addHighlight) {
|
||||||
addHighlight->deleteLater();
|
(*addHighlight)->deleteLater();
|
||||||
addHighlight = nullptr;
|
*addHighlight = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QListWidgetItem *item;
|
QListWidgetItem *item;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ std::string ToString(double value);
|
|||||||
/* Legacy helpers */
|
/* Legacy helpers */
|
||||||
|
|
||||||
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
void listAddClicked(QListWidget *list, QWidget *newWidget,
|
||||||
QObject *addHighlight = nullptr);
|
QObject **addHighlight = nullptr);
|
||||||
bool listMoveUp(QListWidget *list);
|
bool listMoveUp(QListWidget *list);
|
||||||
bool listMoveDown(QListWidget *list);
|
bool listMoveDown(QListWidget *list);
|
||||||
|
|
||||||
|
|||||||
@@ -298,8 +298,6 @@ VariableSelectionDialog::VariableSelectionDialog(QWidget *parent)
|
|||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *GetSettingsWindow();
|
|
||||||
|
|
||||||
bool VariableSelectionDialog::AskForVariable(std::string &varName)
|
bool VariableSelectionDialog::AskForVariable(std::string &varName)
|
||||||
{
|
{
|
||||||
VariableSelectionDialog dialog(GetSettingsWindow());
|
VariableSelectionDialog dialog(GetSettingsWindow());
|
||||||
|
|||||||
@@ -660,34 +660,6 @@ void OSCMessageEdit::Down()
|
|||||||
UpdateListSize();
|
UpdateListSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
static QListWidgetItem *getItemFromWidget(QListWidget *list, QWidget *widget)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < list->count(); i++) {
|
|
||||||
auto item = list->item(i);
|
|
||||||
if (!item) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto itemWidget = list->itemWidget(item);
|
|
||||||
if (itemWidget == widget) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int OSCMessageEdit::GetIndexOfSignal()
|
|
||||||
{
|
|
||||||
auto sender = this->sender();
|
|
||||||
if (!sender) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
auto widget = qobject_cast<QWidget *>(sender);
|
|
||||||
if (!widget) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return _list->row(getItemFromWidget(_list, widget));
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSCMessageEdit::ElementFocussed()
|
void OSCMessageEdit::ElementFocussed()
|
||||||
{
|
{
|
||||||
int idx = GetIndexOfSignal();
|
int idx = GetIndexOfSignal();
|
||||||
|
|||||||
@@ -157,7 +157,6 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void InsertElement(const OSCMessageElement &);
|
void InsertElement(const OSCMessageElement &);
|
||||||
int GetIndexOfSignal();
|
|
||||||
|
|
||||||
VariableLineEdit *_address;
|
VariableLineEdit *_address;
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ target_sources(
|
|||||||
channel-selection.hpp
|
channel-selection.hpp
|
||||||
chat-connection.cpp
|
chat-connection.cpp
|
||||||
chat-connection.hpp
|
chat-connection.hpp
|
||||||
|
chat-message-pattern.cpp
|
||||||
|
chat-message-pattern.hpp
|
||||||
event-sub.cpp
|
event-sub.cpp
|
||||||
event-sub.hpp
|
event-sub.hpp
|
||||||
macro-action-twitch.cpp
|
macro-action-twitch.cpp
|
||||||
|
|||||||
@@ -3,38 +3,65 @@
|
|||||||
#include "twitch-helpers.hpp"
|
#include "twitch-helpers.hpp"
|
||||||
|
|
||||||
#include <log-helper.hpp>
|
#include <log-helper.hpp>
|
||||||
#include <plugin-state-helpers.hpp>
|
|
||||||
|
|
||||||
#undef DispatchMessage
|
#undef DispatchMessage
|
||||||
|
|
||||||
namespace advss {
|
namespace advss {
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
using websocketpp::lib::placeholders::_1;
|
using websocketpp::lib::placeholders::_1;
|
||||||
using websocketpp::lib::placeholders::_2;
|
using websocketpp::lib::placeholders::_2;
|
||||||
using websocketpp::lib::bind;
|
using websocketpp::lib::bind;
|
||||||
|
|
||||||
static const int reconnectDelay = 15;
|
static const auto reconnectDelay = 15s;
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static ParsedTags parseTags(const std::string &tags)
|
static std::vector<IRCMessage::Badge> parseBadgeTag(const std::string &tag)
|
||||||
|
{
|
||||||
|
std::vector<std::string> badgePairs;
|
||||||
|
size_t badgePos = 0;
|
||||||
|
size_t badgeEndPos;
|
||||||
|
|
||||||
|
while ((badgeEndPos = tag.find(',', badgePos)) != std::string::npos) {
|
||||||
|
badgePairs.push_back(
|
||||||
|
tag.substr(badgePos, badgeEndPos - badgePos));
|
||||||
|
badgePos = badgeEndPos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgePairs.push_back(tag.substr(badgePos));
|
||||||
|
|
||||||
|
std::vector<IRCMessage::Badge> badges;
|
||||||
|
for (const auto &badgePair : badgePairs) {
|
||||||
|
size_t slashPos = badgePair.find('/');
|
||||||
|
const auto badgeName = badgePair.substr(0, slashPos);
|
||||||
|
const auto badgeValue = (slashPos != std::string::npos)
|
||||||
|
? badgePair.substr(slashPos + 1)
|
||||||
|
: "";
|
||||||
|
badges.push_back({badgeName, badgeValue != "0"});
|
||||||
|
}
|
||||||
|
|
||||||
|
return badges;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parseTags(const std::string &tags, IRCMessage &message)
|
||||||
{
|
{
|
||||||
ParsedTags parsedTags;
|
|
||||||
static constexpr std::array<std::string_view, 2> tagsToIgnore = {
|
static constexpr std::array<std::string_view, 2> tagsToIgnore = {
|
||||||
"client-nonce", "flags"};
|
"client-nonce", "flags"};
|
||||||
|
|
||||||
std::vector<std::string> parsedTagPairs;
|
std::vector<std::string> properties;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
size_t endPos;
|
size_t endPos;
|
||||||
|
|
||||||
while ((endPos = tags.find(';', pos)) != std::string::npos) {
|
while ((endPos = tags.find(';', pos)) != std::string::npos) {
|
||||||
parsedTagPairs.push_back(tags.substr(pos, endPos - pos));
|
properties.push_back(tags.substr(pos, endPos - pos));
|
||||||
pos = endPos + 1;
|
pos = endPos + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedTagPairs.push_back(tags.substr(pos));
|
properties.push_back(tags.substr(pos));
|
||||||
|
|
||||||
for (const auto &tagPair : parsedTagPairs) {
|
for (const auto &tagPair : properties) {
|
||||||
size_t equalsPos = tagPair.find('=');
|
size_t equalsPos = tagPair.find('=');
|
||||||
std::string tagName = tagPair.substr(0, equalsPos);
|
std::string tagName = tagPair.substr(0, equalsPos);
|
||||||
std::string tagValue = (equalsPos != std::string::npos)
|
std::string tagValue = (equalsPos != std::string::npos)
|
||||||
@@ -47,113 +74,26 @@ static ParsedTags parseTags(const std::string &tags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tagValue.empty()) {
|
if (tagValue.empty()) {
|
||||||
parsedTags.tagMap[tagName] = {};
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tagName == "badges" || tagName == "badge-info") {
|
if (tagName == "badges" || tagName == "badge-info") {
|
||||||
ParsedTags::BadgeMap badgeMap;
|
message.properties.badges = parseBadgeTag(tagValue);
|
||||||
std::vector<std::string> badgePairs;
|
message.properties.badgesString = tagValue;
|
||||||
size_t badgePos = 0;
|
} else if (tagName == "display-name") {
|
||||||
size_t badgeEndPos;
|
message.properties.displayName = tagValue;
|
||||||
|
} else if (tagName == "first-msg") {
|
||||||
while ((badgeEndPos = tagValue.find(',', badgePos)) !=
|
message.properties.isFirstMessage = tagValue == "1";
|
||||||
std::string::npos) {
|
} else if (tagName == "emote-only") {
|
||||||
badgePairs.push_back(tagValue.substr(
|
message.properties.isUsingOnlyEmotes = tagValue == "1";
|
||||||
badgePos, badgeEndPos - badgePos));
|
} else if (tagName == "mod") {
|
||||||
badgePos = badgeEndPos + 1;
|
message.properties.isMod = tagValue == "1";
|
||||||
}
|
} else if (tagName == "subscriber") {
|
||||||
|
message.properties.isSubscriber = tagValue == "1";
|
||||||
badgePairs.push_back(tagValue.substr(badgePos));
|
} else if (tagName == "turbo") {
|
||||||
|
message.properties.isTurbo = tagValue == "1";
|
||||||
for (const auto &badgePair : badgePairs) {
|
|
||||||
size_t slashPos = badgePair.find('/');
|
|
||||||
std::string badgeName =
|
|
||||||
badgePair.substr(0, slashPos);
|
|
||||||
std::string badgeValue =
|
|
||||||
(slashPos != std::string::npos)
|
|
||||||
? badgePair.substr(slashPos + 1)
|
|
||||||
: "";
|
|
||||||
|
|
||||||
badgeMap[badgeName] = badgeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedTags.tagMap[tagName] = badgeMap;
|
|
||||||
|
|
||||||
} else if (tagName == "emotes") {
|
|
||||||
ParsedTags::EmoteMap emoteMap;
|
|
||||||
std::vector<std::string> emotePairs;
|
|
||||||
size_t emotePos = 0;
|
|
||||||
size_t emoteEndPos;
|
|
||||||
|
|
||||||
while ((emoteEndPos = tagValue.find('/', emotePos)) !=
|
|
||||||
std::string::npos) {
|
|
||||||
emotePairs.push_back(tagValue.substr(
|
|
||||||
emotePos, emoteEndPos - emotePos));
|
|
||||||
emotePos = emoteEndPos + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
emotePairs.push_back(tagValue.substr(emotePos));
|
|
||||||
|
|
||||||
for (const auto &emotePair : emotePairs) {
|
|
||||||
size_t colonPos = emotePair.find(':');
|
|
||||||
std::string emoteId =
|
|
||||||
emotePair.substr(0, colonPos);
|
|
||||||
std::string positions =
|
|
||||||
(colonPos != std::string::npos)
|
|
||||||
? emotePair.substr(colonPos + 1)
|
|
||||||
: "";
|
|
||||||
|
|
||||||
std::vector<std::pair<int, int>> textPositions;
|
|
||||||
size_t positionPos = 0;
|
|
||||||
size_t positionEndPos;
|
|
||||||
|
|
||||||
while ((positionEndPos = positions.find(
|
|
||||||
',', positionPos)) !=
|
|
||||||
std::string::npos) {
|
|
||||||
std::string position = positions.substr(
|
|
||||||
positionPos,
|
|
||||||
positionEndPos - positionPos);
|
|
||||||
size_t dashPos = position.find('-');
|
|
||||||
int startPos = std::stoi(
|
|
||||||
position.substr(0, dashPos));
|
|
||||||
int endPos = std::stoi(
|
|
||||||
position.substr(dashPos + 1));
|
|
||||||
textPositions.push_back(
|
|
||||||
{startPos, endPos});
|
|
||||||
positionPos = positionEndPos + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
textPositions.push_back(
|
|
||||||
{std::stoi(
|
|
||||||
positions.substr(positionPos)),
|
|
||||||
std::stoi(positions.substr(
|
|
||||||
positionPos))});
|
|
||||||
emoteMap[emoteId] = textPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedTags.tagMap[tagName] = emoteMap;
|
|
||||||
} else if (tagName == "emote-sets") {
|
|
||||||
ParsedTags::EmoteSet emoteSetIds;
|
|
||||||
size_t setIdPos = 0;
|
|
||||||
size_t setIdEndPos;
|
|
||||||
|
|
||||||
while ((setIdEndPos = tagValue.find(',', setIdPos)) !=
|
|
||||||
std::string::npos) {
|
|
||||||
emoteSetIds.push_back(tagValue.substr(
|
|
||||||
setIdPos, setIdEndPos - setIdPos));
|
|
||||||
setIdPos = setIdEndPos + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
emoteSetIds.push_back(tagValue.substr(setIdPos));
|
|
||||||
|
|
||||||
parsedTags.tagMap[tagName] = emoteSetIds;
|
|
||||||
} else {
|
|
||||||
parsedTags.tagMap[tagName] = tagValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedTags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parseSource(const std::string &rawSourceComponent,
|
static void parseSource(const std::string &rawSourceComponent,
|
||||||
@@ -271,7 +211,7 @@ static IRCMessage parseMessage(const std::string &message)
|
|||||||
rawMessageComponent = message.substr(idx);
|
rawMessageComponent = message.substr(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedMessage.tags = parseTags(rawTagsComponent);
|
parseTags(rawTagsComponent, parsedMessage);
|
||||||
parseSource(rawSourceComponent, parsedMessage);
|
parseSource(rawSourceComponent, parsedMessage);
|
||||||
parseCommand(rawCommandComponent, parsedMessage);
|
parseCommand(rawCommandComponent, parsedMessage);
|
||||||
parsedMessage.message = rawMessageComponent;
|
parsedMessage.message = rawMessageComponent;
|
||||||
@@ -390,15 +330,14 @@ TwitchChatConnection::GetChatConnection(const TwitchToken &token_,
|
|||||||
|
|
||||||
void TwitchChatConnection::ConnectThread()
|
void TwitchChatConnection::ConnectThread()
|
||||||
{
|
{
|
||||||
while (!_disconnect) {
|
while (!_stop) {
|
||||||
std::unique_lock<std::mutex> lock(_waitMtx);
|
_state = State::CONNECTING;
|
||||||
_client.reset();
|
_client.reset();
|
||||||
_connected = true;
|
|
||||||
websocketpp::lib::error_code ec;
|
websocketpp::lib::error_code ec;
|
||||||
websocketpp::client<websocketpp::config::asio_tls_client>::
|
websocketpp::client<websocketpp::config::asio_tls_client>::
|
||||||
connection_ptr con = _client.get_connection(_url, ec);
|
connection_ptr con = _client.get_connection(_url, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
blog(LOG_INFO, "Twitch TwitchChatConnection failed: %s",
|
blog(LOG_INFO, "TwitchChatConnection failed: %s",
|
||||||
ec.message().c_str());
|
ec.message().c_str());
|
||||||
} else {
|
} else {
|
||||||
_client.connect(con);
|
_client.connect(con);
|
||||||
@@ -406,52 +345,63 @@ void TwitchChatConnection::ConnectThread()
|
|||||||
_client.run();
|
_client.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
blog(LOG_INFO,
|
blog(LOG_INFO,
|
||||||
"Twitch TwitchChatConnection trying to reconnect to in %d seconds.",
|
"TwitchChatConnection trying to reconnect to in %ld seconds.",
|
||||||
reconnectDelay);
|
(long int)reconnectDelay.count());
|
||||||
|
std::unique_lock<std::mutex> lock(_waitMtx);
|
||||||
_cv.wait_for(lock, std::chrono::seconds(reconnectDelay));
|
_cv.wait_for(lock, std::chrono::seconds(reconnectDelay));
|
||||||
}
|
}
|
||||||
_connected = false;
|
_state = State::DISCONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChatConnection::Connect()
|
void TwitchChatConnection::Connect()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_connectMtx);
|
std::lock_guard<std::mutex> lock(_connectMtx);
|
||||||
if (_connected) {
|
if (_state == State::CONNECTING || _state == State::CONNECTED) {
|
||||||
vblog(LOG_INFO,
|
vblog(LOG_INFO,
|
||||||
"Twitch TwitchChatConnection connect already in progress");
|
"Twitch TwitchChatConnection connect already in progress");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_disconnect = true;
|
|
||||||
if (_thread.joinable()) {
|
if (_thread.joinable()) {
|
||||||
_thread.join();
|
_thread.join();
|
||||||
}
|
}
|
||||||
_disconnect = false;
|
|
||||||
|
_stop = false;
|
||||||
|
_state = State::CONNECTING;
|
||||||
_thread = std::thread(&TwitchChatConnection::ConnectThread, this);
|
_thread = std::thread(&TwitchChatConnection::ConnectThread, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChatConnection::Disconnect()
|
void TwitchChatConnection::Disconnect()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_connectMtx);
|
std::lock_guard<std::mutex> lock(_connectMtx);
|
||||||
_disconnect = true;
|
if (_state == State::DISCONNECTED) {
|
||||||
|
vblog(LOG_INFO, "TwitchChatConnection already disconnected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stop = true;
|
||||||
|
|
||||||
websocketpp::lib::error_code ec;
|
websocketpp::lib::error_code ec;
|
||||||
_client.close(_connection, websocketpp::close::status::normal,
|
_client.close(_connection, websocketpp::close::status::normal,
|
||||||
"Twitch chat connection stopping", ec);
|
"Twitch chat connection stopping", ec);
|
||||||
|
if (ec) {
|
||||||
|
blog(LOG_INFO, "TwitchChatConnection close failed: %s",
|
||||||
|
ec.message().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> waitLock(_waitMtx);
|
std::unique_lock<std::mutex> waitLock(_waitMtx);
|
||||||
_cv.notify_all();
|
_cv.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (_connected) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
||||||
_client.close(_connection, websocketpp::close::status::normal,
|
|
||||||
"Twitch chat connection stopping", ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_thread.joinable()) {
|
if (_thread.joinable()) {
|
||||||
_thread.join();
|
_thread.join();
|
||||||
}
|
}
|
||||||
_connected = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string toLowerCase(const std::string &str)
|
static std::string toLowerCase(const std::string &str)
|
||||||
@@ -468,7 +418,7 @@ static std::string toLowerCase(const std::string &str)
|
|||||||
|
|
||||||
void TwitchChatConnection::ConnectToChat()
|
void TwitchChatConnection::ConnectToChat()
|
||||||
{
|
{
|
||||||
if (!_connected) {
|
if (_state == State::DISCONNECTED) {
|
||||||
Connect();
|
Connect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -556,15 +506,24 @@ void TwitchChatConnection::HandleNotice(const IRCMessage &message) const
|
|||||||
void TwitchChatConnection::HandleReconnect()
|
void TwitchChatConnection::HandleReconnect()
|
||||||
{
|
{
|
||||||
blog(LOG_INFO,
|
blog(LOG_INFO,
|
||||||
"Received RECONNECT notice! Twitch chat connection will be terminated!");
|
"Received RECONNECT notice! Twitch chat connection will be terminated shortly!");
|
||||||
Disconnect();
|
|
||||||
|
// We need a separate thread here as we cannot close the current
|
||||||
|
// connection from within the message handler
|
||||||
|
std::thread reconnectThread([token = this->_token,
|
||||||
|
channel = this->_channel]() {
|
||||||
|
auto chatConnection =
|
||||||
|
TwitchChatConnection::GetChatConnection(token, channel);
|
||||||
|
chatConnection->Disconnect();
|
||||||
|
chatConnection->Connect();
|
||||||
|
});
|
||||||
|
reconnectThread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChatConnection::OnOpen(connection_hdl)
|
void TwitchChatConnection::OnOpen(connection_hdl)
|
||||||
{
|
{
|
||||||
vblog(LOG_INFO, "Twitch chat connection opened");
|
vblog(LOG_INFO, "Twitch chat connection opened");
|
||||||
_connected = true;
|
_state = State::CONNECTED;
|
||||||
|
|
||||||
Authenticate();
|
Authenticate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,13 +532,13 @@ void TwitchChatConnection::OnMessage(
|
|||||||
websocketpp::client<websocketpp::config::asio_tls_client>::message_ptr
|
websocketpp::client<websocketpp::config::asio_tls_client>::message_ptr
|
||||||
message)
|
message)
|
||||||
{
|
{
|
||||||
constexpr std::string_view authOKCommand = "001";
|
static constexpr std::string_view authOKCommand = "001";
|
||||||
constexpr std::string_view pingCommand = "PING";
|
static constexpr std::string_view pingCommand = "PING";
|
||||||
constexpr std::string_view joinOKCommand = "JOIN";
|
static constexpr std::string_view joinOKCommand = "JOIN";
|
||||||
constexpr std::string_view noticeCommand = "NOTICE";
|
static constexpr std::string_view noticeCommand = "NOTICE";
|
||||||
constexpr std::string_view reconnectCommand = "RECONNECT";
|
static constexpr std::string_view reconnectCommand = "RECONNECT";
|
||||||
constexpr std::string_view newMessageCommand = "PRIVMSG";
|
static constexpr std::string_view newMessageCommand = "PRIVMSG";
|
||||||
constexpr std::string_view whisperCommand = "WHISPER";
|
static constexpr std::string_view whisperCommand = "WHISPER";
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return;
|
return;
|
||||||
@@ -589,7 +548,7 @@ void TwitchChatConnection::OnMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string payload = message->get_payload();
|
std::string payload = message->get_payload();
|
||||||
auto messages = parseMessages(payload);
|
const auto messages = parseMessages(payload);
|
||||||
|
|
||||||
for (const auto &message : messages) {
|
for (const auto &message : messages) {
|
||||||
if (message.command.command == authOKCommand) {
|
if (message.command.command == authOKCommand) {
|
||||||
@@ -620,7 +579,6 @@ void TwitchChatConnection::OnClose(connection_hdl hdl)
|
|||||||
con = _client.get_con_from_hdl(hdl);
|
con = _client.get_con_from_hdl(hdl);
|
||||||
auto msg = con->get_ec().message();
|
auto msg = con->get_ec().message();
|
||||||
blog(LOG_INFO, "Twitch chat connection closed: %s", msg.c_str());
|
blog(LOG_INFO, "Twitch chat connection closed: %s", msg.c_str());
|
||||||
_connected = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChatConnection::OnFail(connection_hdl hdl)
|
void TwitchChatConnection::OnFail(connection_hdl hdl)
|
||||||
@@ -629,7 +587,6 @@ void TwitchChatConnection::OnFail(connection_hdl hdl)
|
|||||||
con = _client.get_con_from_hdl(hdl);
|
con = _client.get_con_from_hdl(hdl);
|
||||||
auto msg = con->get_ec().message();
|
auto msg = con->get_ec().message();
|
||||||
blog(LOG_INFO, "Twitch chat connection failed: %s", msg.c_str());
|
blog(LOG_INFO, "Twitch chat connection failed: %s", msg.c_str());
|
||||||
_connected = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChatConnection::Send(const std::string &msg)
|
void TwitchChatConnection::Send(const std::string &msg)
|
||||||
|
|||||||
@@ -14,18 +14,21 @@ namespace advss {
|
|||||||
|
|
||||||
using websocketpp::connection_hdl;
|
using websocketpp::connection_hdl;
|
||||||
|
|
||||||
struct ParsedTags {
|
|
||||||
using BadgeMap = std::unordered_map<std::string, std::string>;
|
|
||||||
using EmoteMap = std::unordered_map<std::string,
|
|
||||||
std::vector<std::pair<int, int>>>;
|
|
||||||
using EmoteSet = std::vector<std::string>;
|
|
||||||
std::unordered_map<std::string, std::variant<std::string, BadgeMap,
|
|
||||||
EmoteMap, EmoteSet>>
|
|
||||||
tagMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IRCMessage {
|
struct IRCMessage {
|
||||||
ParsedTags tags;
|
struct Badge {
|
||||||
|
std::string name;
|
||||||
|
bool enabled;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
std::string badgesString;
|
||||||
|
std::vector<Badge> badges;
|
||||||
|
std::string displayName;
|
||||||
|
bool isFirstMessage = false;
|
||||||
|
bool isUsingOnlyEmotes = false;
|
||||||
|
bool isMod = false;
|
||||||
|
bool isSubscriber = false;
|
||||||
|
bool isTurbo = false;
|
||||||
|
} properties;
|
||||||
struct {
|
struct {
|
||||||
std::string nick;
|
std::string nick;
|
||||||
std::string host;
|
std::string host;
|
||||||
@@ -96,9 +99,12 @@ private:
|
|||||||
std::mutex _waitMtx;
|
std::mutex _waitMtx;
|
||||||
std::mutex _connectMtx;
|
std::mutex _connectMtx;
|
||||||
std::condition_variable _cv;
|
std::condition_variable _cv;
|
||||||
std::atomic_bool _connected{false};
|
|
||||||
|
enum class State { DISCONNECTED, CONNECTING, CONNECTED };
|
||||||
|
std::atomic<State> _state = {State::DISCONNECTED};
|
||||||
|
|
||||||
std::atomic_bool _authenticated{false};
|
std::atomic_bool _authenticated{false};
|
||||||
std::atomic_bool _disconnect{false};
|
std::atomic_bool _stop{false};
|
||||||
std::string _url;
|
std::string _url;
|
||||||
|
|
||||||
ChatMessageDispatcher _messageDispatcher;
|
ChatMessageDispatcher _messageDispatcher;
|
||||||
|
|||||||
530
plugins/twitch/chat-message-pattern.cpp
Normal file
530
plugins/twitch/chat-message-pattern.cpp
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
#include "chat-message-pattern.hpp"
|
||||||
|
|
||||||
|
#include <log-helper.hpp>
|
||||||
|
#include <obs-module-helper.hpp>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <ui-helpers.hpp>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
const std::vector<ChatMessageProperty::PropertyInfo> ChatMessageProperty::_supportedProperties = {
|
||||||
|
{"firstMessage",
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.firstMessage",
|
||||||
|
true,
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
return message.properties.isFirstMessage ==
|
||||||
|
std::get<bool>(property._value);
|
||||||
|
}},
|
||||||
|
{"emoteOnly",
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.emoteOnly",
|
||||||
|
true,
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
return message.properties.isUsingOnlyEmotes ==
|
||||||
|
std::get<bool>(property._value);
|
||||||
|
}},
|
||||||
|
{"mod", "AdvSceneSwitcher.condition.twitch.type.chat.properties.mod",
|
||||||
|
true,
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
return message.properties.isMod ==
|
||||||
|
std::get<bool>(property._value);
|
||||||
|
}},
|
||||||
|
{"subscriber",
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.subscriber",
|
||||||
|
true,
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
return message.properties.isSubscriber ==
|
||||||
|
std::get<bool>(property._value);
|
||||||
|
}},
|
||||||
|
{"turbo",
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.turbo", true,
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
return message.properties.isTurbo ==
|
||||||
|
std::get<bool>(property._value);
|
||||||
|
}},
|
||||||
|
{"displayName",
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.displayName",
|
||||||
|
std::string(),
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
auto value = std::get<StringVariable>(property._value);
|
||||||
|
return !property._regex.Enabled()
|
||||||
|
? message.properties.displayName ==
|
||||||
|
std::string(value)
|
||||||
|
: property._regex.Matches(
|
||||||
|
message.properties.displayName,
|
||||||
|
value);
|
||||||
|
}},
|
||||||
|
{"loginName",
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.loginName",
|
||||||
|
std::string(),
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
auto value = std::get<StringVariable>(property._value);
|
||||||
|
return !property._regex.Enabled()
|
||||||
|
? message.source.nick == std::string(value)
|
||||||
|
: property._regex.Matches(message.source.nick,
|
||||||
|
value);
|
||||||
|
}},
|
||||||
|
{"badge",
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.badge",
|
||||||
|
std::string("broadcaster"),
|
||||||
|
[](const IRCMessage &message, const ChatMessageProperty &property) {
|
||||||
|
auto value = std::get<StringVariable>(property._value);
|
||||||
|
for (const auto &badge : message.properties.badges) {
|
||||||
|
if (!badge.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const bool badgeNameMatches =
|
||||||
|
!property._regex.Enabled()
|
||||||
|
? badge.name == std::string(value)
|
||||||
|
: property._regex.Matches(badge.name,
|
||||||
|
value);
|
||||||
|
if (badgeNameMatches) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
true},
|
||||||
|
};
|
||||||
|
|
||||||
|
PropertySelectionDialog::PropertySelectionDialog(QWidget *parent)
|
||||||
|
: QDialog(parent),
|
||||||
|
_selection(new QComboBox())
|
||||||
|
{
|
||||||
|
setModal(true);
|
||||||
|
setWindowModality(Qt::WindowModality::ApplicationModal);
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
setMinimumWidth(350);
|
||||||
|
setMinimumHeight(70);
|
||||||
|
|
||||||
|
_selection->setPlaceholderText(
|
||||||
|
obs_module_text("AdvSceneSwitcher.item.select"));
|
||||||
|
|
||||||
|
for (const auto &prop : ChatMessageProperty::GetSupportedIds()) {
|
||||||
|
_selection->addItem(
|
||||||
|
ChatMessageProperty::GetLocale(prop.c_str()),
|
||||||
|
prop.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||||
|
QDialogButtonBox::Cancel);
|
||||||
|
buttonbox->button(QDialogButtonBox::Ok)->setDisabled(true);
|
||||||
|
|
||||||
|
// Cast is required to support all versions of Qt 5
|
||||||
|
connect(_selection,
|
||||||
|
static_cast<void (QComboBox::*)(int)>(
|
||||||
|
&QComboBox::currentIndexChanged),
|
||||||
|
this, [buttonbox](int idx) {
|
||||||
|
buttonbox->button(QDialogButtonBox::Ok)
|
||||||
|
->setDisabled(idx == -1);
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonbox->setCenterButtons(true);
|
||||||
|
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout();
|
||||||
|
layout->addWidget(new QLabel(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties.select")));
|
||||||
|
layout->addWidget(_selection);
|
||||||
|
layout->addWidget(buttonbox);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ChatMessageProperty> PropertySelectionDialog::AskForPorperty(
|
||||||
|
const std::vector<ChatMessageProperty> ¤tProperties)
|
||||||
|
{
|
||||||
|
PropertySelectionDialog dialog(GetSettingsWindow());
|
||||||
|
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
|
||||||
|
|
||||||
|
for (const auto ¤tProperty : currentProperties) {
|
||||||
|
if (currentProperty.IsReusable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int idx = dialog._selection->findText(
|
||||||
|
currentProperty.GetLocale());
|
||||||
|
if (idx == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qobject_cast<QListView *>(dialog._selection->view())
|
||||||
|
->setRowHidden(idx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialog.exec() != DialogCode::Accepted) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto currentItem =
|
||||||
|
dialog._selection->itemData(dialog._selection->currentIndex());
|
||||||
|
const auto id = currentItem.toString().toStdString();
|
||||||
|
return ChatMessageProperty{
|
||||||
|
id, ChatMessageProperty::GetDefaultValue(id.c_str())};
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatMessageEdit::ChatMessageEdit(QWidget *parent)
|
||||||
|
: ListEditor(parent, false),
|
||||||
|
_message(new VariableTextEdit(this, 5, 1, 1)),
|
||||||
|
_regex(new RegexConfigWidget(parent))
|
||||||
|
{
|
||||||
|
_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
_list->setAutoScroll(false);
|
||||||
|
|
||||||
|
connect(_message, &VariableTextEdit::textChanged, this,
|
||||||
|
&ChatMessageEdit::MessageChanged);
|
||||||
|
connect(_regex, &RegexConfigWidget::RegexConfigChanged, this,
|
||||||
|
&ChatMessageEdit::RegexChanged);
|
||||||
|
|
||||||
|
auto messageLayout = new QHBoxLayout();
|
||||||
|
messageLayout->addWidget(_message);
|
||||||
|
messageLayout->addWidget(_regex);
|
||||||
|
|
||||||
|
_mainLayout->insertLayout(0, messageLayout);
|
||||||
|
_mainLayout->insertWidget(
|
||||||
|
1,
|
||||||
|
new QLabel(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.twitch.type.chat.properties")));
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::SetMessagePattern(const ChatMessagePattern &pattern)
|
||||||
|
{
|
||||||
|
_currentSelection = pattern;
|
||||||
|
_message->setPlainText(pattern._message);
|
||||||
|
_regex->SetRegexConfig(pattern._regex);
|
||||||
|
|
||||||
|
_list->clear();
|
||||||
|
_currentSelection._properties.clear();
|
||||||
|
for (const auto &property : pattern._properties) {
|
||||||
|
InsertElement(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::Remove()
|
||||||
|
{
|
||||||
|
auto item = _list->currentItem();
|
||||||
|
int idx = _list->currentRow();
|
||||||
|
if (!item || idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete item;
|
||||||
|
_currentSelection._properties.erase(
|
||||||
|
_currentSelection._properties.begin() + idx);
|
||||||
|
emit ChatMessagePatternChanged(_currentSelection);
|
||||||
|
UpdateListSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::PropertyChanged(const ChatMessageProperty &property)
|
||||||
|
{
|
||||||
|
int idx = GetIndexOfSignal();
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentSelection._properties.at(idx) = property;
|
||||||
|
_list->setCurrentRow(idx);
|
||||||
|
emit ChatMessagePatternChanged(_currentSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::ElementFocussed()
|
||||||
|
{
|
||||||
|
int idx = GetIndexOfSignal();
|
||||||
|
if (idx == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_list->setCurrentRow(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::MessageChanged()
|
||||||
|
{
|
||||||
|
_currentSelection._message = _message->toPlainText().toStdString();
|
||||||
|
emit ChatMessagePatternChanged(_currentSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::RegexChanged(const RegexConfig ®ex)
|
||||||
|
{
|
||||||
|
_currentSelection._regex = regex;
|
||||||
|
emit ChatMessagePatternChanged(_currentSelection);
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::InsertElement(const ChatMessageProperty &property)
|
||||||
|
{
|
||||||
|
auto item = new QListWidgetItem(_list);
|
||||||
|
_list->addItem(item);
|
||||||
|
auto propertyEdit = new ChatMessagePropertyEdit(this, property);
|
||||||
|
item->setSizeHint(propertyEdit->minimumSizeHint());
|
||||||
|
_list->setItemWidget(item, propertyEdit);
|
||||||
|
QWidget::connect(propertyEdit,
|
||||||
|
SIGNAL(PropertyChanged(const ChatMessageProperty &)),
|
||||||
|
this,
|
||||||
|
SLOT(PropertyChanged(const ChatMessageProperty &)));
|
||||||
|
QWidget::connect(propertyEdit, SIGNAL(Focussed()), this,
|
||||||
|
SLOT(ElementFocussed()));
|
||||||
|
_currentSelection._properties.push_back(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageEdit::Add()
|
||||||
|
{
|
||||||
|
auto newProp = PropertySelectionDialog::AskForPorperty(
|
||||||
|
_currentSelection._properties);
|
||||||
|
if (!newProp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertElement(*newProp);
|
||||||
|
emit ChatMessagePatternChanged(_currentSelection);
|
||||||
|
UpdateListSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatMessagePropertyEdit::ChatMessagePropertyEdit(
|
||||||
|
QWidget *parent, const ChatMessageProperty &property)
|
||||||
|
: QWidget(parent),
|
||||||
|
_boolValue(new QCheckBox(property.GetLocale(), this)),
|
||||||
|
_textValue(new VariableLineEdit(this)),
|
||||||
|
_regex(new RegexConfigWidget(this)),
|
||||||
|
_property(property)
|
||||||
|
{
|
||||||
|
installEventFilter(this);
|
||||||
|
|
||||||
|
if (std::holds_alternative<bool>(property._value)) {
|
||||||
|
const bool value = std::get<bool>(property._value);
|
||||||
|
_boolValue->setChecked(value);
|
||||||
|
} else if (std::holds_alternative<StringVariable>(property._value)) {
|
||||||
|
const auto value = std::get<StringVariable>(property._value);
|
||||||
|
_textValue->setText(value);
|
||||||
|
_regex->SetRegexConfig(property._regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(_boolValue, &QCheckBox::stateChanged, this, [this](int value) {
|
||||||
|
_property._value = static_cast<bool>(value);
|
||||||
|
emit PropertyChanged(_property);
|
||||||
|
});
|
||||||
|
connect(_textValue, &VariableLineEdit::editingFinished, this, [this]() {
|
||||||
|
_property._value = _textValue->text().toStdString();
|
||||||
|
emit PropertyChanged(_property);
|
||||||
|
});
|
||||||
|
connect(_regex, &RegexConfigWidget::RegexConfigChanged, this,
|
||||||
|
[this](const RegexConfig ®ex) {
|
||||||
|
_property._regex = regex;
|
||||||
|
emit PropertyChanged(_property);
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto layout = new QHBoxLayout();
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->addWidget(_boolValue);
|
||||||
|
layout->addWidget(_textValue);
|
||||||
|
if (std::holds_alternative<StringVariable>(property._value)) {
|
||||||
|
layout->insertWidget(0, new QLabel(property.GetLocale()));
|
||||||
|
layout->addWidget(_regex);
|
||||||
|
}
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
SetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatMessagePropertyEdit::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::MouseButtonPress ||
|
||||||
|
event->type() == QEvent::MouseButtonDblClick) {
|
||||||
|
emit Focussed();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessagePropertyEdit::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
|
||||||
|
QWidgetList childWidgets = findChildren<QWidget *>();
|
||||||
|
for (QWidget *childWidget : childWidgets) {
|
||||||
|
childWidget->installEventFilter(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessagePropertyEdit::SetVisibility()
|
||||||
|
{
|
||||||
|
auto defaultValue = _property.GetDefaultValue();
|
||||||
|
_boolValue->setVisible(std::holds_alternative<bool>(defaultValue));
|
||||||
|
const bool isTextValue =
|
||||||
|
std::holds_alternative<StringVariable>(defaultValue);
|
||||||
|
_textValue->setVisible(isTextValue);
|
||||||
|
_regex->setVisible(isTextValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageProperty::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
obs_data_set_string(obj, "id", _id.c_str());
|
||||||
|
std::visit(
|
||||||
|
[obj, this](auto &&arg) {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
if constexpr (std::is_same_v<T, bool>) {
|
||||||
|
obs_data_set_bool(obj, "boolValue", arg);
|
||||||
|
} else if constexpr (std::is_same_v<T, StringVariable>) {
|
||||||
|
arg.Save(obj, "strValue");
|
||||||
|
_regex.Save(obj);
|
||||||
|
} else {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"cannot save unknown chat message property");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessageProperty::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
_id = obs_data_get_string(obj, "id");
|
||||||
|
|
||||||
|
if (obs_data_has_user_value(obj, "strValue")) {
|
||||||
|
StringVariable value;
|
||||||
|
value.Load(obj, "strValue");
|
||||||
|
_value = value;
|
||||||
|
_regex.Load(obj);
|
||||||
|
} else if (obs_data_has_user_value(obj, "boolValue")) {
|
||||||
|
bool value = obs_data_get_bool(obj, "boolValue");
|
||||||
|
_value = value;
|
||||||
|
} else {
|
||||||
|
blog(LOG_WARNING, "cannot load unknown chat message property");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatMessageProperty::GetLocale(const char *id)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(_supportedProperties.begin(),
|
||||||
|
_supportedProperties.end(),
|
||||||
|
[id](const PropertyInfo &pi) {
|
||||||
|
return std::string(pi._id) == id;
|
||||||
|
});
|
||||||
|
if (it == _supportedProperties.end()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return obs_module_text(it->_locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<bool, StringVariable>
|
||||||
|
ChatMessageProperty::GetDefaultValue(const char *id)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(_supportedProperties.begin(),
|
||||||
|
_supportedProperties.end(),
|
||||||
|
[id](const PropertyInfo &pi) {
|
||||||
|
return std::string(pi._id) == id;
|
||||||
|
});
|
||||||
|
if (it == _supportedProperties.end()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return it->_defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<bool, StringVariable> ChatMessageProperty::GetDefaultValue() const
|
||||||
|
{
|
||||||
|
return GetDefaultValue(_id.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &ChatMessageProperty::GetSupportedIds()
|
||||||
|
{
|
||||||
|
static std::vector<std::string> supportedIds;
|
||||||
|
static bool setupDone = false;
|
||||||
|
if (!setupDone) {
|
||||||
|
for (const auto &prop : _supportedProperties) {
|
||||||
|
supportedIds.emplace_back(prop._id);
|
||||||
|
}
|
||||||
|
setupDone = true;
|
||||||
|
}
|
||||||
|
return supportedIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatMessageProperty::Matches(const IRCMessage &message) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(_supportedProperties.begin(),
|
||||||
|
_supportedProperties.end(),
|
||||||
|
[this](const PropertyInfo &pi) {
|
||||||
|
return std::string(pi._id) == _id;
|
||||||
|
});
|
||||||
|
if (it == _supportedProperties.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->_checkForMatch(message, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatMessageProperty::IsReusable() const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(_supportedProperties.begin(),
|
||||||
|
_supportedProperties.end(),
|
||||||
|
[this](const PropertyInfo &pi) {
|
||||||
|
return std::string(pi._id) == _id;
|
||||||
|
});
|
||||||
|
if (it == _supportedProperties.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->_isReusable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessagePattern::Save(obs_data_t *obj) const
|
||||||
|
{
|
||||||
|
OBSDataAutoRelease data = obs_data_create();
|
||||||
|
_message.Save(data, "message");
|
||||||
|
_regex.Save(data, "regex");
|
||||||
|
|
||||||
|
OBSDataArrayAutoRelease properties = obs_data_array_create();
|
||||||
|
for (const auto &property : _properties) {
|
||||||
|
OBSDataAutoRelease arrayObj = obs_data_create();
|
||||||
|
property.Save(arrayObj);
|
||||||
|
obs_data_array_push_back(properties, arrayObj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(data, "properties", properties);
|
||||||
|
obs_data_set_obj(obj, "chatMessagePattern", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMessagePattern::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
if (!obs_data_has_user_value(obj, "chatMessagePattern")) {
|
||||||
|
// Backward compatibility
|
||||||
|
_message.Load(obj, "chatMessage");
|
||||||
|
_regex.Load(obj, "regexChat");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBSDataAutoRelease data = obs_data_get_obj(obj, "chatMessagePattern");
|
||||||
|
_message.Load(data, "message");
|
||||||
|
_regex.Load(data, "regex");
|
||||||
|
|
||||||
|
OBSDataArrayAutoRelease properties =
|
||||||
|
obs_data_get_array(data, "properties");
|
||||||
|
size_t count = obs_data_array_count(properties);
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
OBSDataAutoRelease arrayObj =
|
||||||
|
obs_data_array_item(properties, i);
|
||||||
|
ChatMessageProperty property;
|
||||||
|
property.Load(arrayObj);
|
||||||
|
_properties.push_back(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatMessagePattern::Matches(const IRCMessage &chatMessage) const
|
||||||
|
{
|
||||||
|
const bool messageMatch =
|
||||||
|
!_regex.Enabled()
|
||||||
|
? chatMessage.message == std::string(_message)
|
||||||
|
: _regex.Matches(chatMessage.message, _message);
|
||||||
|
if (!messageMatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &property : _properties) {
|
||||||
|
if (!property.Matches(chatMessage)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
114
plugins/twitch/chat-message-pattern.hpp
Normal file
114
plugins/twitch/chat-message-pattern.hpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "chat-connection.hpp"
|
||||||
|
|
||||||
|
#include <list-editor.hpp>
|
||||||
|
#include <regex-config.hpp>
|
||||||
|
#include <variable-text-edit.hpp>
|
||||||
|
|
||||||
|
namespace advss {
|
||||||
|
|
||||||
|
class ChatMessageProperty {
|
||||||
|
public:
|
||||||
|
void Save(obs_data_t *obj) const;
|
||||||
|
void Load(obs_data_t *obj);
|
||||||
|
|
||||||
|
std::string _id;
|
||||||
|
std::variant<bool, StringVariable> _value;
|
||||||
|
RegexConfig _regex;
|
||||||
|
|
||||||
|
static QString GetLocale(const char *id);
|
||||||
|
QString GetLocale() const { return GetLocale(_id.c_str()); }
|
||||||
|
static std::variant<bool, StringVariable>
|
||||||
|
GetDefaultValue(const char *id);
|
||||||
|
std::variant<bool, StringVariable> GetDefaultValue() const;
|
||||||
|
static const std::vector<std::string> &GetSupportedIds();
|
||||||
|
bool Matches(const IRCMessage &) const;
|
||||||
|
bool IsReusable() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct PropertyInfo {
|
||||||
|
const char *_id;
|
||||||
|
const char *_locale;
|
||||||
|
std::variant<bool, StringVariable> _defaultValue;
|
||||||
|
std::function<bool(const IRCMessage &,
|
||||||
|
const ChatMessageProperty &)>
|
||||||
|
_checkForMatch;
|
||||||
|
bool _isReusable = false;
|
||||||
|
};
|
||||||
|
static const std::vector<PropertyInfo> _supportedProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChatMessagePattern {
|
||||||
|
public:
|
||||||
|
void Save(obs_data_t *obj) const;
|
||||||
|
void Load(obs_data_t *obj);
|
||||||
|
|
||||||
|
bool Matches(const IRCMessage &) const;
|
||||||
|
|
||||||
|
StringVariable _message;
|
||||||
|
RegexConfig _regex = RegexConfig::PartialMatchRegexConfig();
|
||||||
|
std::vector<ChatMessageProperty> _properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PropertySelectionDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PropertySelectionDialog(QWidget *parent);
|
||||||
|
static std::optional<ChatMessageProperty> AskForPorperty(
|
||||||
|
const std::vector<ChatMessageProperty> ¤tProperties);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QComboBox *_selection;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChatMessagePropertyEdit : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChatMessagePropertyEdit(QWidget *, const ChatMessageProperty &);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void PropertyChanged(const ChatMessageProperty &);
|
||||||
|
void Focussed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetVisibility();
|
||||||
|
|
||||||
|
QCheckBox *_boolValue;
|
||||||
|
VariableLineEdit *_textValue;
|
||||||
|
RegexConfigWidget *_regex;
|
||||||
|
|
||||||
|
ChatMessageProperty _property;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChatMessageEdit final : public ListEditor {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ChatMessageEdit(QWidget *parent);
|
||||||
|
void SetMessagePattern(const ChatMessagePattern &);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void MessageChanged();
|
||||||
|
void RegexChanged(const RegexConfig &);
|
||||||
|
void Add();
|
||||||
|
void Remove();
|
||||||
|
void PropertyChanged(const ChatMessageProperty &);
|
||||||
|
void ElementFocussed();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ChatMessagePatternChanged(const ChatMessagePattern &);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InsertElement(const ChatMessageProperty &);
|
||||||
|
|
||||||
|
VariableTextEdit *_message;
|
||||||
|
RegexConfigWidget *_regex;
|
||||||
|
ChatMessagePattern _currentSelection;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace advss
|
||||||
@@ -384,13 +384,15 @@ bool MacroConditionTwitch::CheckChatMessages(TwitchToken &token)
|
|||||||
if (!message) {
|
if (!message) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!stringMatches(_regexChat, message->message,
|
|
||||||
_chatMessage)) {
|
if (!_chatMessagePattern.Matches(*message)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetTempVarValue("chatter", message->source.nick);
|
SetTempVarValue("user_login", message->source.nick);
|
||||||
|
SetTempVarValue("user_name", message->properties.displayName);
|
||||||
SetTempVarValue("chat_message", message->message);
|
SetTempVarValue("chat_message", message->message);
|
||||||
|
SetTempVarValue("badges", message->properties.badgesString);
|
||||||
|
|
||||||
if (_clearBufferOnMatch) {
|
if (_clearBufferOnMatch) {
|
||||||
_eventBuffer->Clear();
|
_eventBuffer->Clear();
|
||||||
@@ -600,8 +602,7 @@ bool MacroConditionTwitch::Save(obs_data_t *obj) const
|
|||||||
_pointsReward.Save(obj);
|
_pointsReward.Save(obj);
|
||||||
_streamTitle.Save(obj, "streamTitle");
|
_streamTitle.Save(obj, "streamTitle");
|
||||||
_regexTitle.Save(obj, "regexTitle");
|
_regexTitle.Save(obj, "regexTitle");
|
||||||
_chatMessage.Save(obj, "chatMessage");
|
_chatMessagePattern.Save(obj);
|
||||||
_regexChat.Save(obj, "regexChat");
|
|
||||||
_category.Save(obj);
|
_category.Save(obj);
|
||||||
obs_data_set_bool(obj, "clearBufferOnMatch", _clearBufferOnMatch);
|
obs_data_set_bool(obj, "clearBufferOnMatch", _clearBufferOnMatch);
|
||||||
obs_data_set_int(obj, "version", 1);
|
obs_data_set_int(obj, "version", 1);
|
||||||
@@ -619,8 +620,7 @@ bool MacroConditionTwitch::Load(obs_data_t *obj)
|
|||||||
_pointsReward.Load(obj);
|
_pointsReward.Load(obj);
|
||||||
_streamTitle.Load(obj, "streamTitle");
|
_streamTitle.Load(obj, "streamTitle");
|
||||||
_regexTitle.Load(obj, "regexTitle");
|
_regexTitle.Load(obj, "regexTitle");
|
||||||
_chatMessage.Load(obj, "chatMessage");
|
_chatMessagePattern.Load(obj);
|
||||||
_regexChat.Load(obj, "regexChat");
|
|
||||||
_category.Load(obj);
|
_category.Load(obj);
|
||||||
_clearBufferOnMatch = obs_data_get_bool(obj, "clearBufferOnMatch");
|
_clearBufferOnMatch = obs_data_get_bool(obj, "clearBufferOnMatch");
|
||||||
if (!obs_data_has_user_value(obj, "version")) {
|
if (!obs_data_has_user_value(obj, "version")) {
|
||||||
@@ -1236,8 +1236,10 @@ void MacroConditionTwitch::SetupTempVars()
|
|||||||
setupTempVarHelper("is_branded_content");
|
setupTempVarHelper("is_branded_content");
|
||||||
break;
|
break;
|
||||||
case Condition::CHAT_MESSAGE_RECEIVED:
|
case Condition::CHAT_MESSAGE_RECEIVED:
|
||||||
setupTempVarHelper("chat_message");
|
setupTempVarHelper("chat_message", ".chatReceive");
|
||||||
setupTempVarHelper("chatter");
|
setupTempVarHelper("user_login", ".chatReceive");
|
||||||
|
setupTempVarHelper("user_name", ".chatReceive");
|
||||||
|
setupTempVarHelper("badges", ".chatReceive");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -1263,8 +1265,7 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
|||||||
_pointsReward(new TwitchPointsRewardWidget(this)),
|
_pointsReward(new TwitchPointsRewardWidget(this)),
|
||||||
_streamTitle(new VariableLineEdit(this)),
|
_streamTitle(new VariableLineEdit(this)),
|
||||||
_regexTitle(new RegexConfigWidget(parent)),
|
_regexTitle(new RegexConfigWidget(parent)),
|
||||||
_chatMessage(new VariableTextEdit(this, 5, 1, 1)),
|
_chatMesageEdit(new ChatMessageEdit(this)),
|
||||||
_regexChat(new RegexConfigWidget(parent)),
|
|
||||||
_category(new TwitchCategoryWidget(this)),
|
_category(new TwitchCategoryWidget(this)),
|
||||||
_clearBufferOnMatch(new QCheckBox(
|
_clearBufferOnMatch(new QCheckBox(
|
||||||
obs_module_text("AdvSceneSwitcher.clearBufferOnMatch")))
|
obs_module_text("AdvSceneSwitcher.clearBufferOnMatch")))
|
||||||
@@ -1293,11 +1294,11 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
|||||||
QWidget::connect(_regexTitle,
|
QWidget::connect(_regexTitle,
|
||||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||||
SLOT(RegexTitleChanged(const RegexConfig &)));
|
SLOT(RegexTitleChanged(const RegexConfig &)));
|
||||||
QWidget::connect(_chatMessage, SIGNAL(textChanged()), this,
|
QWidget::connect(
|
||||||
SLOT(ChatMessageChanged()));
|
_chatMesageEdit,
|
||||||
QWidget::connect(_regexChat,
|
SIGNAL(ChatMessagePatternChanged(const ChatMessagePattern &)),
|
||||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
this,
|
||||||
SLOT(RegexChatChanged(const RegexConfig &)));
|
SLOT(ChatMessagePatternChanged(const ChatMessagePattern &)));
|
||||||
QWidget::connect(_category,
|
QWidget::connect(_category,
|
||||||
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
|
SIGNAL(CategoreyChanged(const TwitchCategory &)), this,
|
||||||
SLOT(CategoreyChanged(const TwitchCategory &)));
|
SLOT(CategoreyChanged(const TwitchCategory &)));
|
||||||
@@ -1324,10 +1325,7 @@ MacroConditionTwitchEdit::MacroConditionTwitchEdit(
|
|||||||
|
|
||||||
auto mainLayout = new QVBoxLayout();
|
auto mainLayout = new QVBoxLayout();
|
||||||
mainLayout->addLayout(_layout);
|
mainLayout->addLayout(_layout);
|
||||||
auto chatLayout = new QHBoxLayout();
|
mainLayout->addWidget(_chatMesageEdit);
|
||||||
chatLayout->addWidget(_chatMessage);
|
|
||||||
chatLayout->addWidget(_regexChat);
|
|
||||||
mainLayout->addLayout(chatLayout);
|
|
||||||
mainLayout->addLayout(accountLayout);
|
mainLayout->addLayout(accountLayout);
|
||||||
mainLayout->addWidget(_tokenWarning);
|
mainLayout->addWidget(_tokenWarning);
|
||||||
mainLayout->addWidget(_clearBufferOnMatch);
|
mainLayout->addWidget(_clearBufferOnMatch);
|
||||||
@@ -1431,10 +1429,13 @@ void MacroConditionTwitchEdit::StreamTitleChanged()
|
|||||||
_entryData->_streamTitle = _streamTitle->text().toStdString();
|
_entryData->_streamTitle = _streamTitle->text().toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionTwitchEdit::ChatMessageChanged()
|
void MacroConditionTwitchEdit::ChatMessagePatternChanged(
|
||||||
|
const ChatMessagePattern &chatMessagePattern)
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
_entryData->_chatMessage = _chatMessage->toPlainText().toStdString();
|
_entryData->_chatMessagePattern = chatMessagePattern;
|
||||||
|
adjustSize();
|
||||||
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionTwitchEdit::RegexTitleChanged(const RegexConfig &conf)
|
void MacroConditionTwitchEdit::RegexTitleChanged(const RegexConfig &conf)
|
||||||
@@ -1446,15 +1447,6 @@ void MacroConditionTwitchEdit::RegexTitleChanged(const RegexConfig &conf)
|
|||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionTwitchEdit::RegexChatChanged(const RegexConfig &conf)
|
|
||||||
{
|
|
||||||
GUARD_LOADING_AND_LOCK();
|
|
||||||
_entryData->_regexChat = conf;
|
|
||||||
|
|
||||||
adjustSize();
|
|
||||||
updateGeometry();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroConditionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
|
void MacroConditionTwitchEdit::CategoreyChanged(const TwitchCategory &category)
|
||||||
{
|
{
|
||||||
GUARD_LOADING_AND_LOCK();
|
GUARD_LOADING_AND_LOCK();
|
||||||
@@ -1484,10 +1476,7 @@ void MacroConditionTwitchEdit::SetWidgetVisibility()
|
|||||||
condition == MacroConditionTwitch::Condition::TITLE_POLLING);
|
condition == MacroConditionTwitch::Condition::TITLE_POLLING);
|
||||||
_regexTitle->setVisible(condition ==
|
_regexTitle->setVisible(condition ==
|
||||||
MacroConditionTwitch::Condition::TITLE_POLLING);
|
MacroConditionTwitch::Condition::TITLE_POLLING);
|
||||||
_chatMessage->setVisible(
|
_chatMesageEdit->setVisible(
|
||||||
condition ==
|
|
||||||
MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED);
|
|
||||||
_regexChat->setVisible(
|
|
||||||
condition ==
|
condition ==
|
||||||
MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED);
|
MacroConditionTwitch::Condition::CHAT_MESSAGE_RECEIVED);
|
||||||
_category->setVisible(
|
_category->setVisible(
|
||||||
@@ -1525,8 +1514,7 @@ void MacroConditionTwitchEdit::UpdateEntryData()
|
|||||||
_pointsReward->SetPointsReward(_entryData->_pointsReward);
|
_pointsReward->SetPointsReward(_entryData->_pointsReward);
|
||||||
_streamTitle->setText(_entryData->_streamTitle);
|
_streamTitle->setText(_entryData->_streamTitle);
|
||||||
_regexTitle->SetRegexConfig(_entryData->_regexTitle);
|
_regexTitle->SetRegexConfig(_entryData->_regexTitle);
|
||||||
_chatMessage->setPlainText(_entryData->_chatMessage);
|
_chatMesageEdit->SetMessagePattern(_entryData->_chatMessagePattern);
|
||||||
_regexChat->SetRegexConfig(_entryData->_regexChat);
|
|
||||||
_category->SetToken(_entryData->GetToken());
|
_category->SetToken(_entryData->GetToken());
|
||||||
_category->SetCategory(_entryData->_category);
|
_category->SetCategory(_entryData->_category);
|
||||||
_clearBufferOnMatch->setChecked(_entryData->_clearBufferOnMatch);
|
_clearBufferOnMatch->setChecked(_entryData->_clearBufferOnMatch);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "channel-selection.hpp"
|
#include "channel-selection.hpp"
|
||||||
#include "points-reward-selection.hpp"
|
#include "points-reward-selection.hpp"
|
||||||
#include "chat-connection.hpp"
|
#include "chat-connection.hpp"
|
||||||
|
#include "chat-message-pattern.hpp"
|
||||||
|
|
||||||
#include <variable-line-edit.hpp>
|
#include <variable-line-edit.hpp>
|
||||||
#include <variable-text-edit.hpp>
|
#include <variable-text-edit.hpp>
|
||||||
@@ -101,8 +102,7 @@ public:
|
|||||||
StringVariable _streamTitle = obs_module_text(
|
StringVariable _streamTitle = obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.twitch.title.title");
|
"AdvSceneSwitcher.condition.twitch.title.title");
|
||||||
RegexConfig _regexTitle = RegexConfig::PartialMatchRegexConfig();
|
RegexConfig _regexTitle = RegexConfig::PartialMatchRegexConfig();
|
||||||
StringVariable _chatMessage;
|
ChatMessagePattern _chatMessagePattern;
|
||||||
RegexConfig _regexChat = RegexConfig::PartialMatchRegexConfig();
|
|
||||||
TwitchCategory _category;
|
TwitchCategory _category;
|
||||||
bool _clearBufferOnMatch = false;
|
bool _clearBufferOnMatch = false;
|
||||||
|
|
||||||
@@ -166,9 +166,8 @@ private slots:
|
|||||||
void ChannelChanged(const TwitchChannel &);
|
void ChannelChanged(const TwitchChannel &);
|
||||||
void PointsRewardChanged(const TwitchPointsReward &);
|
void PointsRewardChanged(const TwitchPointsReward &);
|
||||||
void StreamTitleChanged();
|
void StreamTitleChanged();
|
||||||
void ChatMessageChanged();
|
|
||||||
void RegexTitleChanged(const RegexConfig &);
|
void RegexTitleChanged(const RegexConfig &);
|
||||||
void RegexChatChanged(const RegexConfig &);
|
void ChatMessagePatternChanged(const ChatMessagePattern &);
|
||||||
void CategoreyChanged(const TwitchCategory &);
|
void CategoreyChanged(const TwitchCategory &);
|
||||||
void ClearBufferOnMatchChanged(int);
|
void ClearBufferOnMatchChanged(int);
|
||||||
|
|
||||||
@@ -189,8 +188,7 @@ private:
|
|||||||
TwitchPointsRewardWidget *_pointsReward;
|
TwitchPointsRewardWidget *_pointsReward;
|
||||||
VariableLineEdit *_streamTitle;
|
VariableLineEdit *_streamTitle;
|
||||||
RegexConfigWidget *_regexTitle;
|
RegexConfigWidget *_regexTitle;
|
||||||
VariableTextEdit *_chatMessage;
|
ChatMessageEdit *_chatMesageEdit;
|
||||||
RegexConfigWidget *_regexChat;
|
|
||||||
TwitchCategoryWidget *_category;
|
TwitchCategoryWidget *_category;
|
||||||
QCheckBox *_clearBufferOnMatch;
|
QCheckBox *_clearBufferOnMatch;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user