diff --git a/Desktop/analysis/analysis.cpp b/Desktop/analysis/analysis.cpp index c5fad62046..583cda81fd 100644 --- a/Desktop/analysis/analysis.cpp +++ b/Desktop/analysis/analysis.cpp @@ -27,6 +27,7 @@ #include "utilities/qutils.h" #include "utilities/settings.h" #include "utilities/reporter.h" +#include "utilities/helpmodel.h" #include "gui/preferencesmodel.h" #include "results/resultsjsinterface.h" #include "utilities/messageforwarder.h" @@ -357,7 +358,8 @@ void Analysis::createForm(QQuickItem* parentItem) connect(this, &Analysis::titleChanged, _analysisForm, &AnalysisForm::titleChanged ); connect(this, &Analysis::needsRefreshChanged, _analysisForm, &AnalysisForm::needsRefreshChanged ); connect(this, &Analysis::needsRefreshChanged, _analysisForm, &AnalysisForm::rSyntaxTextChanged ); - connect(this, &Analysis::boundValuesChanged, this, &Analysis::setRSyntaxTextInResult, Qt::QueuedConnection ); + connect(this, &Analysis::boundValuesChanged, this, &Analysis::setRSyntaxTextInResult, Qt::QueuedConnection ); + connect(_analysisForm, &AnalysisForm::helpJumpToAnchor, this, &Analysis::helpJumpToAnchor, Qt::QueuedConnection ); setRSyntaxTextInResult(); _analysisForm->setShowRButton(_moduleData->hasWrapper()); @@ -1086,6 +1088,11 @@ void Analysis::onUsedVariablesChanged() DataSetPackage::pkg()->checkComputedColumnDependenciesForAnalysis(this); } +void Analysis::helpJumpToAnchor(const QString &anchor) +{ + HelpModel::singleton()->jumpToAnchor(anchor); +} + void Analysis::checkForRSources() { if(!_results.isMember(".meta")) diff --git a/Desktop/analysis/analysis.h b/Desktop/analysis/analysis.h index bc8847f9c9..f9be79ac51 100644 --- a/Desktop/analysis/analysis.h +++ b/Desktop/analysis/analysis.h @@ -198,6 +198,7 @@ public slots: void setRSyntaxTextInResult(); void filterByNameDone(const QString &name, const QString &error); void onUsedVariablesChanged() override; + void helpJumpToAnchor(const QString & anchor); protected: void abort(); diff --git a/Desktop/utilities/helpmodel.cpp b/Desktop/utilities/helpmodel.cpp index 979afd684a..90fd6db61b 100644 --- a/Desktop/utilities/helpmodel.cpp +++ b/Desktop/utilities/helpmodel.cpp @@ -7,8 +7,13 @@ #include "gui/preferencesmodel.h" #include "log.h" +HelpModel * HelpModel::_singleton = nullptr; + HelpModel::HelpModel(QObject * parent) : QObject(parent) { + assert(!_singleton); + _singleton = this; + setPagePath("index"); connect(this, &HelpModel::pagePathChanged, this, &HelpModel::generateJavascript); connect(PreferencesModel::prefs(), &PreferencesModel::currentThemeNameChanged, this, &HelpModel::setThemeCss, Qt::QueuedConnection); @@ -33,6 +38,8 @@ void HelpModel::runJavaScript(QString renderFunc, QString content) runJavaScriptSignal(renderFunc + "(\"" + content + "\");"); } + + void HelpModel::setVisible(bool visible) { if (_visible == visible) @@ -58,7 +65,7 @@ void HelpModel::loadingSucceeded() generateJavascript(); } -void HelpModel::setMarkdown(QString markdown) +void HelpModel::setMarkdown(const QString & markdown) { if (_markdown == markdown) return; @@ -76,8 +83,12 @@ void HelpModel::setMarkdown(QString markdown) void HelpModel::setPagePath(QString pagePath) { - _pagePath = pagePath; - emit pagePathChanged(_pagePath); + if(_pagePath != pagePath) + _anchorName = ""; + + _pagePath = pagePath; + + emit pagePathChanged(HelpModel::pagePath()); } QString HelpModel::indexURL() @@ -157,6 +168,8 @@ void HelpModel::showOrToggleParticularPageForAnalysis(Analysis * analysis, QStri if(analysis == _analysis && pagePath == _pagePath && _visible) { setVisible(false); + _anchorName = ""; + return; } else @@ -205,7 +218,7 @@ void HelpModel::setFont() } ///Temporary function for https://github.com/jasp-stats/INTERNAL-jasp/issues/1215 -bool HelpModel::pageExists(QString pagePath) +bool HelpModel::pageExists(const QString & pagePath) { QString renderFunc, content; @@ -214,6 +227,26 @@ bool HelpModel::pageExists(QString pagePath) return loadHelpContent(pagePath, false, renderFunc, content) || loadHelpContent(pagePath, true, renderFunc, content); } +void HelpModel::jumpToAnchor(const QString &anchorName) +{ + _anchorName = anchorName; + + emit pagePathChanged(pagePath()); + + jumpToSelectedAnchor(); +} + +void HelpModel::jumpToSelectedAnchor() +{ + if(!_anchorName.isEmpty()) + emit runJavaScriptSignal(QString("document.getElementById('%1').scrollIntoView()").arg(_anchorName)); +} + +QString HelpModel::pagePath() const +{ + return _anchorName.isEmpty() ? _pagePath : _pagePath + "#" + _anchorName; +} + bool HelpModel::loadHelpContent(const QString & pagePath, bool ignorelanguage, QString &renderFunc, QString &content) { @@ -262,12 +295,14 @@ bool HelpModel::loadHelpContent(const QString & pagePath, bool ignorelanguage, Q return found; } -void HelpModel::loadMarkdown(QString md) +void HelpModel::loadMarkdown(const QString & md) { //Log::log() << "loadMarkdown got:\n" << md << std::endl; setVisible(true); runJavaScript("window.render", md); + + jumpToSelectedAnchor(); } void HelpModel::setAnalysis(Analysis *newAnalysis) diff --git a/Desktop/utilities/helpmodel.h b/Desktop/utilities/helpmodel.h index 551f6ba340..fefe15279d 100644 --- a/Desktop/utilities/helpmodel.h +++ b/Desktop/utilities/helpmodel.h @@ -24,9 +24,11 @@ class HelpModel : public QObject void runJavaScript(QString renderFunc, QString content); bool visible() const { return _visible; } - QString pagePath() const { return _pagePath; } + QString pagePath() const; QString markdown() const { return _markdown; } Analysis * analysis() const { return _analysis; } + + static HelpModel * singleton() { return _singleton; } public slots: void setVisible(bool visible); @@ -42,9 +44,11 @@ public slots: void setThemeCss(QString themeName); void setFont(); void loadingSucceeded(); - void setMarkdown(QString markdown); - void loadMarkdown(QString md); - bool pageExists(QString pagePath); + void setMarkdown( const QString & markdown); + void loadMarkdown( const QString & md); + bool pageExists( const QString & pagePath); + void jumpToAnchor( const QString & anchorName); + void jumpToSelectedAnchor(); signals: void renderCode(QString javascript); @@ -61,8 +65,11 @@ public slots: private: bool _visible = false; QString _pagePath = "", - _markdown = ""; + _markdown = "", + _anchorName = ""; Analysis * _analysis = nullptr; + + static HelpModel * _singleton; }; #endif // HELPMODEL_H diff --git a/QMLComponents/analysisform.cpp b/QMLComponents/analysisform.cpp index a1f71eacf5..cc45e030f5 100644 --- a/QMLComponents/analysisform.cpp +++ b/QMLComponents/analysisform.cpp @@ -334,7 +334,9 @@ void AnalysisForm::_setUp() for (JASPControl* control : controls) { _dependsOrderedCtrls.push_back(control); - connect(control, &JASPControl::helpMDChanged, this, &AnalysisForm::helpMDChanged); + connect(control, &JASPControl::helpMDChanged, this, &AnalysisForm::helpMDChanged); + connect(control, &JASPControl::helpJumpToAnchor, this, &AnalysisForm::helpJumpToAnchor); + } _rSyntax->setUp(); diff --git a/QMLComponents/analysisform.h b/QMLComponents/analysisform.h index 091c4770cc..535cf7e885 100644 --- a/QMLComponents/analysisform.h +++ b/QMLComponents/analysisform.h @@ -118,6 +118,7 @@ public slots: void infoChanged(); void infoBottomChanged(); void helpMDChanged(); + void helpJumpToAnchor(const QString & anchor); void errorsChanged(); void warningsChanged(); void analysisChanged(); diff --git a/QMLComponents/controls/comboboxbase.cpp b/QMLComponents/controls/comboboxbase.cpp index 2c85dacc24..d97514d7c9 100644 --- a/QMLComponents/controls/comboboxbase.cpp +++ b/QMLComponents/controls/comboboxbase.cpp @@ -310,8 +310,9 @@ QString ComboBoxBase::generateMDHelp(int depth) const { QStringList markdown; - markdown << printLabelMD(depth); - markdown << info(); + markdown << markdownAnchor() + << printLabelMD(depth) + << info(); // If one of the option has an info property, then display the options as an unordered list if (_hasOptionInfo()) diff --git a/QMLComponents/controls/jaspcontrol.cpp b/QMLComponents/controls/jaspcontrol.cpp index e0bb5b1918..693b7bac03 100644 --- a/QMLComponents/controls/jaspcontrol.cpp +++ b/QMLComponents/controls/jaspcontrol.cpp @@ -60,6 +60,7 @@ JASPControl::JASPControl(QQuickItem *parent) : QQuickItem(parent) connect(this, &JASPControl::debugChanged, [this] () { _setBackgroundColor(); _setVisible(); } ); connect(this, &JASPControl::parentDebugChanged, [this] () { _setBackgroundColor(); _setVisible(); } ); connect(this, &JASPControl::boundValueChanged, this, &JASPControl::_resetBindingValue); + connect(this, &JASPControl::activeFocusChanged, this, &JASPControl::_handleActiveFocusChanged); connect(this, &JASPControl::activeFocusChanged, this, &JASPControl::_setFocus); connect(this, &JASPControl::activeFocusChanged, this, &JASPControl::_notifyFormOfActiveFocus); @@ -496,6 +497,12 @@ void JASPControl::_checkControlName() { checkOptionName(_name); } + +void JASPControl::_handleActiveFocusChanged() +{ + if(hasActiveFocus() && name() != "") + emit helpJumpToAnchor(markdownAnchor(false)); + } bool JASPControl::checkOptionName(const QString &name) { @@ -631,12 +638,28 @@ JASPControls JASPControl::getMDSubItems(const QQuickItem* parentItem) const return MDSubItems; } + +QString JASPControl::markdownAnchor(bool includeHtml) const +{ + if(_name == "") + return ""; + + const QString anchorName = "qml_" + name(); + if(!includeHtml) + return anchorName; + + return QString("").arg(anchorName); +} QString JASPControl::generateMDHelp(int depth) const { - JASPControls MDSubItems = getMDSubItems(); - QStringList markdown; - markdown << printLabelMD(depth) << info() << "\n"; + JASPControls MDSubItems = getMDSubItems(); + QStringList markdown; + + markdown << markdownAnchor() + << printLabelMD(depth) + << info() + << "\n"; if (MDSubItems.size() > 0) { @@ -698,6 +721,7 @@ void JASPControl::setName(const QString &name) { _name = name; emit nameChanged(); + emit helpMDChanged(); } } diff --git a/QMLComponents/controls/jaspcontrol.h b/QMLComponents/controls/jaspcontrol.h index 573da192e9..d5f0581c62 100644 --- a/QMLComponents/controls/jaspcontrol.h +++ b/QMLComponents/controls/jaspcontrol.h @@ -119,6 +119,7 @@ class JASPControl : public QQuickItem virtual bool infoLabelItalic() const { return false; } QString toolTip() const { return _toolTip; } + QString markdownAnchor(bool includeHtml=true) const; ///