Skip to content

Commit

Permalink
Improve: improve trigger editor experience (Mudlet#7408)
Browse files Browse the repository at this point in the history
<!-- Keep the title short & concise so anyone non-technical can
understand it,
     the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
* no longer show an informational message when an item is activated or
deactivated - the checkbox status is enough, and it less visually noisy
this way
* no longer show an error right when you create a trigger - since you've
just created it, the trigger can't have any patterns
* reduce the visual noise with error message formatting - they were
bolded, coloured, and italicized all at once
#### Motivation for adding to Mudlet
A nice look'n'feel to the trigger editor
#### Other info (issues closed, discussion etc)
This is how it looked like before:


[before.webm](https://github.com/user-attachments/assets/e9ba1a50-30a0-4fff-b27b-c7275a32106a)

Now:


[after.webm](https://github.com/user-attachments/assets/7b80f5f9-2c5a-49d2-bfcb-dfa4c20080e4)

---------

Co-authored-by: Vadim Peretokin <[email protected]>
Co-authored-by: Zooka <[email protected]>
Co-authored-by: Stephen Lyons <[email protected]>
  • Loading branch information
4 people authored Dec 12, 2024
1 parent 5cd19c6 commit ea173d5
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 114 deletions.
141 changes: 71 additions & 70 deletions src/TTrigger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ static void pcre_deleter(pcre* pointer)
}

//FIXME: lock if code *OR* regex doesn't compile
bool TTrigger::setRegexCodeList(QStringList patterns, QList<int> patternKinds)
bool TTrigger::setRegexCodeList(QStringList patterns, QList<int> patternKinds, bool existingTrigger)
{
patterns.replaceInStrings("\n", "");
mPatterns.clear();
Expand Down Expand Up @@ -189,95 +189,96 @@ bool TTrigger::setRegexCodeList(QStringList patterns, QList<int> patternKinds)
qDebug() << "[CRITICAL ERROR (plz report):] Trigger name=" << mName << " aborting reason: patternKinds.size() != patterns.size()";
}

if ((patternKinds.empty()) && (!isFolder()) && (!mColorTrigger)) {
setError(qsl("<b><font color='blue'>%1</font></b>")
.arg(tr("Error: This trigger has no patterns defined, yet. Add some to activate it.")));
if (existingTrigger && (patternKinds.empty()) && (!isFolder()) && (!mColorTrigger)) {
setError(tr("error: this trigger has no patterns defined"));
mOK_init = false;
return false;
}

bool state = true;

for (int i = 0; i < patterns.size(); i++) {
if (patterns.at(i).isEmpty() && patternKinds.at(i) != REGEX_PROMPT) {
continue;
}
if (existingTrigger) {
for (int i = 0; i < patterns.size(); i++) {
if (patterns.at(i).isEmpty() && patternKinds.at(i) != REGEX_PROMPT) {
continue;
}

mPatterns.append(patterns.at(i));
mPatternKinds.append(patternKinds.at(i));
mPatterns.append(patterns.at(i));
mPatternKinds.append(patternKinds.at(i));

if (patternKinds.at(i) == REGEX_PERL) {
const char* error;
const QByteArray& regexp = patterns.at(i).toUtf8();
if (patternKinds.at(i) == REGEX_PERL) {
const char* error;
const QByteArray& regexp = patterns.at(i).toUtf8();

int erroffset;
int erroffset;

// PCRE_UTF8 needed to run compile in UTF-8 mode
// PCRE_UCP needed for \d, \w etc. to use Unicode properties:
QSharedPointer<pcre> const re(pcre_compile(regexp.constData(), PCRE_UTF8 | PCRE_UCP, &error, &erroffset, nullptr), pcre_deleter);
// PCRE_UTF8 needed to run compile in UTF-8 mode
// PCRE_UCP needed for \d, \w etc. to use Unicode properties:
QSharedPointer<pcre> const re(pcre_compile(regexp.constData(), PCRE_UTF8 | PCRE_UCP, &error, &erroffset, nullptr), pcre_deleter);

if (!re) {
if (mudlet::smDebugMode) {
TDebug(Qt::white, Qt::red) << "REGEX ERROR: failed to compile, reason:\n" << error << "\n" >> mpHost;
TDebug(Qt::red, Qt::gray) << TDebug::csmContinue << R"(in: ")" << regexp.constData() << "\"\n" >> mpHost;
}
setError(qsl("<b><font color='blue'>%1</font></b>")
.arg(tr(R"(Error: in item %1, perl regex "%2" failed to compile, reason: "%3".)")
.arg(QString::number(i + 1), QString(regexp.constData()).toHtmlEscaped(), QString(error).toHtmlEscaped())));
state = false;
} else {
if (mudlet::smDebugMode) {
TDebug(Qt::white, Qt::darkGreen) << "[OK]: REGEX_COMPILE OK\n" >> mpHost;
if (!re) {
if (mudlet::smDebugMode) {
TDebug(Qt::white, Qt::red) << "REGEX ERROR: failed to compile, reason:\n" << error << "\n" >> mpHost;
TDebug(Qt::red, Qt::gray) << TDebug::csmContinue << R"(in: ")" << regexp.constData() << "\"\n" >> mpHost;
}
setError(qsl("<b><font color='blue'>%1</font></b>")
.arg(tr(R"(Error: in item %1, perl regex "%2" failed to compile, reason: "%3".)")
.arg(QString::number(i + 1), QString(regexp.constData()).toHtmlEscaped(), QString(error).toHtmlEscaped())));
state = false;
} else {
if (mudlet::smDebugMode) {
TDebug(Qt::white, Qt::darkGreen) << "[OK]: REGEX_COMPILE OK\n" >> mpHost;
}
}
mRegexMap[i] = re;
mTriggerContainsPerlRegex = true;
}
mRegexMap[i] = re;
mTriggerContainsPerlRegex = true;
}

if (patternKinds.at(i) == REGEX_LUA_CODE) {
std::string funcName;
std::stringstream func;
func << "trigger" << mID << "condition" << i;
funcName = func.str();
const QString code = qsl("function %1() %2\nend").arg(funcName.c_str(), patterns[i]);
QString error;
if (!mpLua->compile(code, error, QString::fromStdString(funcName))) {
setError(qsl("<b><font color='blue'>%1</font></b>")
.arg(tr(R"(Error: in item %1, lua function "%2" failed to compile, reason: "%3".)")
.arg(QString::number(i + 1), patterns.at(i), QString(error).toHtmlEscaped())));
state = false;
if (mudlet::smDebugMode) {
TDebug(Qt::white, Qt::red) << "LUA ERROR: failed to compile, reason:\n" << error << "\n" >> mpHost;
TDebug(Qt::red, Qt::gray) << TDebug::csmContinue << R"(in lua condition function: ")" << patterns.at(i) << "\"\n" >> mpHost;
if (patternKinds.at(i) == REGEX_LUA_CODE) {
std::string funcName;
std::stringstream func;
func << "trigger" << mID << "condition" << i;
funcName = func.str();
const QString code = qsl("function %1() %2\nend").arg(funcName.c_str(), patterns[i]);
QString error;
if (!mpLua->compile(code, error, QString::fromStdString(funcName))) {
setError(qsl("<b><font color='blue'>%1</font></b>")
.arg(tr(R"(Error: in item %1, lua function "%2" failed to compile, reason: "%3".)")
.arg(QString::number(i + 1), patterns.at(i), QString(error).toHtmlEscaped())));
state = false;
if (mudlet::smDebugMode) {
TDebug(Qt::white, Qt::red) << "LUA ERROR: failed to compile, reason:\n" << error << "\n" >> mpHost;
TDebug(Qt::red, Qt::gray) << TDebug::csmContinue << R"(in lua condition function: ")" << patterns.at(i) << "\"\n" >> mpHost;
}
} else {
mLuaConditionMap[i] = funcName;
}
} else {
mLuaConditionMap[i] = funcName;
}
}

if (patternKinds[i] == REGEX_COLOR_PATTERN) {
int textAnsiFg = scmIgnored;
int textAnsiBg = scmIgnored;
// Decode the pattern string to the colour codes wanted:
TTrigger::decodeColorPatternText(patterns.at(i), textAnsiFg, textAnsiBg);

if (textAnsiBg == scmIgnored && textAnsiFg == scmIgnored) {
setError(qsl("<b><font color='blue'>%1</font></b>")
.arg(tr("Error: in item %1, no colors to match were set - at least <i>one</i> of the foreground or background must not be <i>ignored</i>.")
.arg(QString::number(i+1))));
state = false;
continue;
}
if (patternKinds[i] == REGEX_COLOR_PATTERN) {
int textAnsiFg = scmIgnored;
int textAnsiBg = scmIgnored;
// Decode the pattern string to the colour codes wanted:
TTrigger::decodeColorPatternText(patterns.at(i), textAnsiFg, textAnsiBg);

if (textAnsiBg == scmIgnored && textAnsiFg == scmIgnored) {
setError(qsl("<b><font color='blue'>%1</font></b>")
.arg(tr("Error: in item %1, no colors to match were set - at least <i>one</i> of the foreground or background must not be <i>ignored</i>.")
.arg(QString::number(i+1))));
state = false;
continue;
}

// The setupColorTrigger(...) method will push_back the created
// TColorTable instance if it is successful:
if (!setupColorTrigger(textAnsiFg, textAnsiBg)) {
// The setupColorTrigger(...) method will push_back the created
// TColorTable instance if it is successful:
if (!setupColorTrigger(textAnsiFg, textAnsiBg)) {
mColorPatternList.push_back(nullptr);
state = false;
continue;
}
} else {
mColorPatternList.push_back(nullptr);
state = false;
continue;
}
} else {
mColorPatternList.push_back(nullptr);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/TTrigger.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class TTrigger : public Tree<TTrigger>
void compile();
void execute();
bool isFilterChain();
bool setRegexCodeList(QStringList patterns, QList<int> patternKinds);
bool setRegexCodeList(QStringList patterns, QList<int> patternKinds, bool existingTrigger = true);
QString getScript() const { return mScript; }
bool setScript(const QString& script);
bool compileScript();
Expand Down
63 changes: 20 additions & 43 deletions src/dlgTriggerEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2859,15 +2859,9 @@ void dlgTriggerEditor::activeToggle_trigger()
}
}

if (pT->state()) {
if (pT->shouldBeActive()) {
showInfo(tr(R"(Trying to activate a trigger group, filter or trigger or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
} else {
showInfo(tr(R"(Trying to deactivate a trigger group, filter or trigger or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
}
} else {
if (!pT->state()) {
pT->setIsActive(false);
showError(tr(R"(<b>Unable to activate a filter or trigger or the part of a module "<tt>%1</tt>" that contains them; reason: %2.</b></p>
showError(tr(R"(<p>Unable to activate "<tt>%1</tt>": %2</p>
<p><i>You will need to reactivate this after the problem has been corrected.</i></p>)").arg(pT->getName().toHtmlEscaped(), pT->getError()));
icon.addPixmap(QPixmap(qsl(":/icons/tools-report-bug.png")), QIcon::Normal, QIcon::Off);
itemDescription = descError;
Expand Down Expand Up @@ -3054,15 +3048,9 @@ void dlgTriggerEditor::activeToggle_timer()
}
}

if (pT->state()) {
if (pT->shouldBeActive()) {
showInfo(tr(R"(Trying to activate a timer group, offset timer, timer or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
} else {
showInfo(tr(R"(Trying to deactivate a timer group, offset timer, timer or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
}
} else {
if (!pT->state()) {
pT->setIsActive(false);
showError(tr(R"(<p><b>Unable to activate an offset timer or timer or the part of a module "<tt>%1</tt>" that contains them; reason: %2.</b></p>
showError(tr(R"(<p><b>Unable to activate "<tt>%1</tt>": %2.</b></p>
<p><i>You will need to reactivate this after the problem has been corrected.</i></p>)").arg(pT->getName().toHtmlEscaped(), pT->getError()));
icon.addPixmap(QPixmap(qsl(":/icons/tools-report-bug.png")), QIcon::Normal, QIcon::Off);
itemDescription = descError;
Expand Down Expand Up @@ -3197,15 +3185,9 @@ void dlgTriggerEditor::activeToggle_alias()
}
}

if (pT->state()) {
if (pT->shouldBeActive()) {
showInfo(tr(R"(Trying to activate an alias group, alias or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
} else {
showInfo(tr(R"(Trying to deactivate an alias group, alias or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
}
} else {
if (!pT->state()) {
pT->setIsActive(false);
showError(tr(R"(<p><b>Unable to activate an alias or the part of a module "<tt>%1</tt>" that contains them; reason: %2.</b></p>
showError(tr(R"(<p><b>Unable to activate "<tt>%1</tt>"; %2.</b></p>
<p><i>You will need to reactivate this after the problem has been corrected.</i></p>)").arg(pT->getName().toHtmlEscaped(), pT->getError()));
icon.addPixmap(QPixmap(qsl(":/icons/tools-report-bug.png")), QIcon::Normal, QIcon::Off);
itemDescription = descError;
Expand Down Expand Up @@ -3322,15 +3304,9 @@ void dlgTriggerEditor::activeToggle_script()
}
}

if (pT->state()) {
if (pT->shouldBeActive()) {
showInfo(tr(R"(Trying to activate a script group, script or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
} else {
showInfo(tr(R"(Trying to deactivate a script group, script or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
}
} else {
if (!pT->state()) {
pT->setIsActive(false);
showError(tr(R"(<p><b>Unable to activate a script group or script or the part of a module "<tt>%1</tt>" that contains them; reason: %2.</b></p>
showError(tr(R"(<p><b>Unable to activate "<tt>%1</tt>"; %2.</b></p>
<p><i>You will need to reactivate this after the problem has been corrected.</i></p>)").arg(pT->getName().toHtmlEscaped(), pT->getError()));
icon.addPixmap(QPixmap(qsl(":/icons/tools-report-bug.png")), QIcon::Normal, QIcon::Off);
itemDescription = descError;
Expand Down Expand Up @@ -3481,15 +3457,9 @@ void dlgTriggerEditor::activeToggle_action()
}
}

if (pT->state()) {
if (pT->shouldBeActive()) {
showInfo(tr(R"(Trying to activate a button/menu/toolbar or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
} else {
showInfo(tr(R"(Trying to deactivate a button/menu/toolbar or the part of a module "<tt>%1</tt>" that contains them <em>succeeded</em>.)").arg(pT->getName().toHtmlEscaped()));
}
} else {
if (!pT->state()) {
pT->setIsActive(false);
showError(tr(R"(<p><b>Unable to activate a button/menu/toolbar or the part of a module "<tt>%1</tt>" that contains them; reason: %2.</b></p>
showError(tr(R"(<p><b>Unable to activate "<tt>%1</tt>"; %2.</b></p>
<p><i>You will need to reactivate this after the problem has been corrected.</i></p>)").arg(pT->getName().toHtmlEscaped(), pT->getError()));
icon.addPixmap(QPixmap(qsl(":/icons/tools-report-bug.png")), QIcon::Normal, QIcon::Off);
itemDescription = descError;
Expand Down Expand Up @@ -3648,8 +3618,15 @@ void dlgTriggerEditor::activeToggle_key()
pItem->setIcon(0, iconError);
}
pItem->setData(0, Qt::AccessibleDescriptionRole, itemDescription);
showInfo(
QString("Trying to %2 key <em>%1</em> %3.").arg(pT->getName().toHtmlEscaped(), pT->shouldBeActive() ? "activate" : "deactivate", pT->state() ? "succeeded" : QString("failed; reason: %1").arg(pT->getError())));

if (!pT->state()) {
pT->setIsActive(false);
showError(tr(R"(<p><b>Unable to activate "<tt>%1</tt>"; %2.</b></p>
<p><i>You will need to reactivate this after the problem has been corrected.</i></p>)").arg(pT->getName().toHtmlEscaped(), pT->getError()));
icon.addPixmap(QPixmap(qsl(":/icons/tools-report-bug.png")), QIcon::Normal, QIcon::Off);
itemDescription = descError;
}

if (pItem->childCount() > 0) {
children_icon_key(pItem);
}
Expand Down Expand Up @@ -3778,7 +3755,7 @@ void dlgTriggerEditor::addTrigger(bool isFolder)


pT->setName(name);
pT->setRegexCodeList(patterns, patternKinds);
pT->setRegexCodeList(patterns, patternKinds, false);
pT->setScript(script);
pT->setIsFolder(isFolder);
pT->setIsActive(false);
Expand Down

0 comments on commit ea173d5

Please sign in to comment.