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 <thomas.hartmann@qt.io>
(cherry picked from commit b2052cf807)
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Knud Dollereder
2024-10-22 17:18:53 +02:00
committed by Thomas Hartmann
parent b156abad0d
commit c77f8ea08f
6 changed files with 61 additions and 10 deletions

View File

@@ -77,6 +77,12 @@ ScrollView {
hoverEnabled: true hoverEnabled: true
cursorShape: mouseArea.containsMouse ? Qt.PointingHandCursor cursorShape: mouseArea.containsMouse ? Qt.PointingHandCursor
: Qt.ArrowCursor : Qt.ArrowCursor
Connections {
target: mouseArea
function onClicked() {
messageModel.jumpToCode(index)
}
}
} }
} }
@@ -84,11 +90,26 @@ ScrollView {
id: labelInfo id: labelInfo
color: (type == "Warning") ? StudioTheme.Values.themeAmberLight color: (type == "Warning") ? StudioTheme.Values.themeAmberLight
: StudioTheme.Values.themeRedLight : StudioTheme.Values.themeRedLight
linkColor: StudioTheme.Values.themeInteraction
text: message text: message
font.pixelSize: StudioTheme.Values.baseFontSize font.pixelSize: StudioTheme.Values.baseFontSize
verticalAlignment: Text.AlignTop verticalAlignment: Text.AlignTop
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: row.width - labelIcon.width - labelLocation.width - row.spacing * 2 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)
}
}
} }
} }
} }

View File

@@ -511,6 +511,11 @@ const AbstractView *ViewManager::view() const
return &d->nodeInstanceView; return &d->nodeInstanceView;
} }
TextEditorView *ViewManager::textEditorView()
{
return &d->textEditorView;
}
void ViewManager::emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList, void ViewManager::emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList,
const QList<QVariant> &data) const QList<QVariant> &data)
{ {

View File

@@ -25,6 +25,7 @@ class DesignerActionManager;
class NodeInstanceView; class NodeInstanceView;
class RewriterView; class RewriterView;
class Edit3DView; class Edit3DView;
class TextEditorView;
namespace Internal { class DesignModeWidget; } namespace Internal { class DesignModeWidget; }
@@ -71,6 +72,8 @@ public:
void nextFileIsCalledInternally(); void nextFileIsCalledInternally();
const AbstractView *view() const; const AbstractView *view() const;
TextEditorView *textEditorView();
void emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList, void emitCustomNotification(const QString &identifier, const QList<ModelNode> &nodeList,
const QList<QVariant> &data); const QList<QVariant> &data);

View File

@@ -168,6 +168,11 @@ void TextEditorView::qmlJSEditorContextHelp(const Core::IContext::HelpCallback &
#endif #endif
} }
TextEditor::BaseTextEditor *TextEditorView::textEditor()
{
return m_widget->textEditor();
}
void TextEditorView::nodeIdChanged(const ModelNode& /*node*/, const QString &/*newId*/, const QString &/*oldId*/) void TextEditorView::nodeIdChanged(const ModelNode& /*node*/, const QString &/*newId*/, const QString &/*oldId*/)
{ {
} }

View File

@@ -4,6 +4,11 @@
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <qmldesignerplugin.h>
#include <texteditor/texteditorview.h>
#include <QDesktopServices>
#include <QWindow>
MessageModel::MessageModel(QObject *parent) MessageModel::MessageModel(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
@@ -47,20 +52,31 @@ void MessageModel::jumpToCode(const QVariant &index)
bool ok = false; bool ok = false;
if (int idx = index.toInt(&ok); ok) { if (int idx = index.toInt(&ok); ok) {
if (idx >= 0 && std::cmp_less(idx, m_tasks.size())) { if (idx >= 0 && std::cmp_less(idx, m_tasks.size())) {
// TODO: ProjectExplorer::Task task = m_tasks.at(static_cast<size_t>(idx));
// - 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);
const int column = task.column ? task.column - 1 : 0; const int column = task.column ? task.column - 1 : 0;
Utils::Link link(task.file, task.line, column); if (Core::EditorManager::openEditor(task.file,
Core::EditorManager::openEditorAt(link, Utils::Id(),
{}, Core::EditorManager::DoNotMakeVisible)) {
Core::EditorManager::SwitchSplitIfAlreadyVisible);
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 int MessageModel::rowCount(const QModelIndex &) const
{ {
return static_cast<int>(m_tasks.size()); return static_cast<int>(m_tasks.size());
@@ -78,7 +94,7 @@ QHash<int, QByteArray> MessageModel::roleNames() const
QVariant MessageModel::data(const QModelIndex &index, int role) const QVariant MessageModel::data(const QModelIndex &index, int role) const
{ {
if (index.isValid() && index.row() < rowCount()) { if (index.isValid() && index.row() < rowCount()) {
int row = index.row(); size_t row = static_cast<size_t>(index.row());
if (role == MessageRole) { if (role == MessageRole) {
return m_tasks.at(row).description(); return m_tasks.at(row).description();
} else if (role == FileNameRole) { } else if (role == FileNameRole) {
@@ -140,7 +156,7 @@ void MessageModel::removeTask(const ProjectExplorer::Task &task)
void MessageModel::clearTasks(const Utils::Id &categoryId) void MessageModel::clearTasks(const Utils::Id &categoryId)
{ {
beginResetModel(); 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; return task.category == categoryId;
}); });
endResetModel(); endResetModel();

View File

@@ -34,6 +34,7 @@ public:
int warningCount() const; int warningCount() const;
Q_INVOKABLE void resetModel(); Q_INVOKABLE void resetModel();
Q_INVOKABLE void jumpToCode(const QVariant &index); Q_INVOKABLE void jumpToCode(const QVariant &index);
Q_INVOKABLE void openLink(const QVariant &url);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;