From c77f8ea08fe72d11d73a08aa6e9d687d9f3d1bde Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 22 Oct 2024 17:18:53 +0200 Subject: [PATCH] Add link support to the output pane The user can now click on issues to jump into source files and open web pages. Links have to be wrapped into a html href attribute. Fixes: QDS-13722 Change-Id: If66f2c61adaad0ab7a9b949912264b583011071e Reviewed-by: Thomas Hartmann (cherry picked from commit b2052cf8074d58a0b4054f5a006af69bf90486d4) Reviewed-by: Tim Jenssen --- .../qmldesigner/statusbar/IssuesPanel.qml | 21 +++++++++++ .../components/componentcore/viewmanager.cpp | 5 +++ .../components/componentcore/viewmanager.h | 3 ++ .../components/texteditor/texteditorview.cpp | 5 +++ .../components/toolbar/messagemodel.cpp | 36 +++++++++++++------ .../components/toolbar/messagemodel.h | 1 + 6 files changed, 61 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml b/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml index 36e90d024a6..67e219a3d0f 100644 --- a/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml +++ b/share/qtcreator/qmldesigner/statusbar/IssuesPanel.qml @@ -77,6 +77,12 @@ ScrollView { hoverEnabled: true cursorShape: mouseArea.containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + Connections { + target: mouseArea + function onClicked() { + messageModel.jumpToCode(index) + } + } } } @@ -84,11 +90,26 @@ ScrollView { id: labelInfo color: (type == "Warning") ? StudioTheme.Values.themeAmberLight : StudioTheme.Values.themeRedLight + + linkColor: StudioTheme.Values.themeInteraction text: message font.pixelSize: StudioTheme.Values.baseFontSize verticalAlignment: Text.AlignTop wrapMode: Text.WordWrap width: row.width - labelIcon.width - labelLocation.width - row.spacing * 2 + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: labelInfo.hoveredLink === "" ? Qt.ArrowCursor : Qt.PointingHandCursor + } + + Connections { + target: labelInfo + function onLinkActivated(link) { + messageModel.openLink(link) + } + } } } } diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 9694aa4802d..45b7abc0e90 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -511,6 +511,11 @@ const AbstractView *ViewManager::view() const return &d->nodeInstanceView; } +TextEditorView *ViewManager::textEditorView() +{ + return &d->textEditorView; +} + void ViewManager::emitCustomNotification(const QString &identifier, const QList &nodeList, const QList &data) { diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.h b/src/plugins/qmldesigner/components/componentcore/viewmanager.h index e2bb2c833fc..15fd555a11e 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.h @@ -25,6 +25,7 @@ class DesignerActionManager; class NodeInstanceView; class RewriterView; class Edit3DView; +class TextEditorView; namespace Internal { class DesignModeWidget; } @@ -71,6 +72,8 @@ public: void nextFileIsCalledInternally(); const AbstractView *view() const; + TextEditorView *textEditorView(); + void emitCustomNotification(const QString &identifier, const QList &nodeList, const QList &data); diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 3d9cc2cf6ac..a516a8ef7ad 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -168,6 +168,11 @@ void TextEditorView::qmlJSEditorContextHelp(const Core::IContext::HelpCallback & #endif } +TextEditor::BaseTextEditor *TextEditorView::textEditor() +{ + return m_widget->textEditor(); +} + void TextEditorView::nodeIdChanged(const ModelNode& /*node*/, const QString &/*newId*/, const QString &/*oldId*/) { } diff --git a/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp b/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp index 49ff3b8aaff..8a2ace645fa 100644 --- a/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp +++ b/src/plugins/qmldesigner/components/toolbar/messagemodel.cpp @@ -4,6 +4,11 @@ #include #include +#include +#include + +#include +#include MessageModel::MessageModel(QObject *parent) : QAbstractListModel(parent) @@ -47,20 +52,31 @@ void MessageModel::jumpToCode(const QVariant &index) bool ok = false; if (int idx = index.toInt(&ok); ok) { if (idx >= 0 && std::cmp_less(idx, m_tasks.size())) { - // TODO: - // - Check why this does not jump to line/column - // - Only call this when sure that the task, file row etc are valid. - ProjectExplorer::Task task = m_tasks.at(idx); + ProjectExplorer::Task task = m_tasks.at(static_cast(idx)); const int column = task.column ? task.column - 1 : 0; - Utils::Link link(task.file, task.line, column); - Core::EditorManager::openEditorAt(link, - {}, - Core::EditorManager::SwitchSplitIfAlreadyVisible); + if (Core::EditorManager::openEditor(task.file, + Utils::Id(), + Core::EditorManager::DoNotMakeVisible)) { + + auto &viewManager = QmlDesigner::QmlDesignerPlugin::instance()->viewManager(); + if (auto *editorView = viewManager.textEditorView()) { + if (TextEditor::BaseTextEditor *editor = editorView->textEditor()) { + editor->gotoLine(task.line, column); + editor->widget()->setFocus(); + editor->editorWidget()->updateFoldingHighlight(QTextCursor()); + } + } + } } } } +void MessageModel::openLink(const QVariant &url) +{ + QDesktopServices::openUrl(QUrl::fromUserInput(url.toString())); +} + int MessageModel::rowCount(const QModelIndex &) const { return static_cast(m_tasks.size()); @@ -78,7 +94,7 @@ QHash MessageModel::roleNames() const QVariant MessageModel::data(const QModelIndex &index, int role) const { if (index.isValid() && index.row() < rowCount()) { - int row = index.row(); + size_t row = static_cast(index.row()); if (role == MessageRole) { return m_tasks.at(row).description(); } else if (role == FileNameRole) { @@ -140,7 +156,7 @@ void MessageModel::removeTask(const ProjectExplorer::Task &task) void MessageModel::clearTasks(const Utils::Id &categoryId) { beginResetModel(); - std::erase_if(m_tasks, [categoryId](const ProjectExplorer::Task& task) { + std::erase_if(m_tasks, [categoryId](const ProjectExplorer::Task &task) { return task.category == categoryId; }); endResetModel(); diff --git a/src/plugins/qmldesigner/components/toolbar/messagemodel.h b/src/plugins/qmldesigner/components/toolbar/messagemodel.h index ccba4f66b68..716a5c6f9f2 100644 --- a/src/plugins/qmldesigner/components/toolbar/messagemodel.h +++ b/src/plugins/qmldesigner/components/toolbar/messagemodel.h @@ -34,6 +34,7 @@ public: int warningCount() const; Q_INVOKABLE void resetModel(); Q_INVOKABLE void jumpToCode(const QVariant &index); + Q_INVOKABLE void openLink(const QVariant &url); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QHash roleNames() const override;