forked from qt-creator/qt-creator
Copilot: move CopilotHoverHandler to TextEditor
And rename it to SuggestionHoverHandler in order to reuse it in the lua text editor interface. Change-Id: I8be9a0c2b22c20fc35adf020f8483aee573ab826 Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
@@ -5,7 +5,6 @@ add_qtc_plugin(Copilot
|
|||||||
copilot.qrc
|
copilot.qrc
|
||||||
copilotclient.cpp copilotclient.h
|
copilotclient.cpp copilotclient.h
|
||||||
copilotconstants.h
|
copilotconstants.h
|
||||||
copilothoverhandler.cpp copilothoverhandler.h
|
|
||||||
copilotplugin.cpp
|
copilotplugin.cpp
|
||||||
copilotprojectpanel.cpp copilotprojectpanel.h
|
copilotprojectpanel.cpp copilotprojectpanel.h
|
||||||
copilotsettings.cpp copilotsettings.h
|
copilotsettings.cpp copilotsettings.h
|
||||||
|
@@ -16,8 +16,6 @@ QtcPlugin {
|
|||||||
"copilotclient.cpp",
|
"copilotclient.cpp",
|
||||||
"copilotclient.h",
|
"copilotclient.h",
|
||||||
"copilotconstants.h",
|
"copilotconstants.h",
|
||||||
"copilothoverhandler.cpp",
|
|
||||||
"copilothoverhandler.h",
|
|
||||||
"copilotplugin.cpp",
|
"copilotplugin.cpp",
|
||||||
"copilotprojectpanel.cpp",
|
"copilotprojectpanel.cpp",
|
||||||
"copilotprojectpanel.h",
|
"copilotprojectpanel.h",
|
||||||
|
@@ -97,13 +97,7 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath)
|
|||||||
openDoc(doc);
|
openDoc(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
CopilotClient::~CopilotClient()
|
CopilotClient::~CopilotClient() = default;
|
||||||
{
|
|
||||||
for (IEditor *editor : DocumentModel::editorsForOpenedDocuments()) {
|
|
||||||
if (auto textEditor = qobject_cast<BaseTextEditor *>(editor))
|
|
||||||
textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopilotClient::openDocument(TextDocument *document)
|
void CopilotClient::openDocument(TextDocument *document)
|
||||||
{
|
{
|
||||||
@@ -240,7 +234,6 @@ void CopilotClient::handleCompletions(const GetCompletionRequest::Response &resp
|
|||||||
return;
|
return;
|
||||||
editor->insertSuggestion(
|
editor->insertSuggestion(
|
||||||
std::make_unique<TextEditor::CyclicSuggestion>(suggestions, editor->document()));
|
std::make_unique<TextEditor::CyclicSuggestion>(suggestions, editor->document()));
|
||||||
editor->addHoverHandler(&m_hoverHandler);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "copilothoverhandler.h"
|
|
||||||
#include "requests/checkstatus.h"
|
#include "requests/checkstatus.h"
|
||||||
#include "requests/getcompletions.h"
|
#include "requests/getcompletions.h"
|
||||||
#include "requests/seteditorinfo.h"
|
#include "requests/seteditorinfo.h"
|
||||||
@@ -63,7 +62,6 @@ private:
|
|||||||
QTimer *timer = nullptr;
|
QTimer *timer = nullptr;
|
||||||
};
|
};
|
||||||
QHash<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
|
QHash<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
|
||||||
CopilotHoverHandler m_hoverHandler;
|
|
||||||
bool m_isAskingForPassword{false};
|
bool m_isAskingForPassword{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,169 +0,0 @@
|
|||||||
// Copyright (C) 2023 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "copilothoverhandler.h"
|
|
||||||
|
|
||||||
#include "copilotclient.h"
|
|
||||||
#include "copilottr.h"
|
|
||||||
|
|
||||||
#include <texteditor/textdocument.h>
|
|
||||||
#include <texteditor/textdocumentlayout.h>
|
|
||||||
#include <texteditor/texteditor.h>
|
|
||||||
|
|
||||||
#include <utils/tooltip/tooltip.h>
|
|
||||||
#include <utils/utilsicons.h>
|
|
||||||
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QScopeGuard>
|
|
||||||
#include <QToolBar>
|
|
||||||
#include <QToolButton>
|
|
||||||
|
|
||||||
using namespace TextEditor;
|
|
||||||
using namespace LanguageServerProtocol;
|
|
||||||
using namespace Utils;
|
|
||||||
|
|
||||||
namespace Copilot::Internal {
|
|
||||||
|
|
||||||
class CopilotCompletionToolTip : public QToolBar
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CopilotCompletionToolTip(QList<CyclicSuggestion::Data> suggestions,
|
|
||||||
int currentSuggestion,
|
|
||||||
TextEditorWidget *editor)
|
|
||||||
: m_numberLabel(new QLabel)
|
|
||||||
, m_suggestions(suggestions)
|
|
||||||
, m_currentSuggestion(std::max(0, std::min<int>(currentSuggestion, suggestions.size() - 1)))
|
|
||||||
, m_editor(editor)
|
|
||||||
{
|
|
||||||
auto prev = addAction(Utils::Icons::PREV_TOOLBAR.icon(),
|
|
||||||
Tr::tr("Select Previous Copilot Suggestion"));
|
|
||||||
prev->setEnabled(m_suggestions.size() > 1);
|
|
||||||
addWidget(m_numberLabel);
|
|
||||||
auto next = addAction(Utils::Icons::NEXT_TOOLBAR.icon(),
|
|
||||||
Tr::tr("Select Next Copilot Suggestion"));
|
|
||||||
next->setEnabled(m_suggestions.size() > 1);
|
|
||||||
|
|
||||||
auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
|
|
||||||
auto applyWord = addAction(
|
|
||||||
Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString()));
|
|
||||||
auto applyLine = addAction(Tr::tr("Apply Line"));
|
|
||||||
|
|
||||||
connect(prev, &QAction::triggered, this, &CopilotCompletionToolTip::selectPrevious);
|
|
||||||
connect(next, &QAction::triggered, this, &CopilotCompletionToolTip::selectNext);
|
|
||||||
connect(apply, &QAction::triggered, this, &CopilotCompletionToolTip::apply);
|
|
||||||
connect(applyWord, &QAction::triggered, this, &CopilotCompletionToolTip::applyWord);
|
|
||||||
connect(applyLine, &QAction::triggered, this, &CopilotCompletionToolTip::applyLine);
|
|
||||||
|
|
||||||
updateLabels();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void updateLabels()
|
|
||||||
{
|
|
||||||
m_numberLabel->setText(Tr::tr("%1 of %2")
|
|
||||||
.arg(m_currentSuggestion + 1)
|
|
||||||
.arg(m_suggestions.count()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void selectPrevious()
|
|
||||||
{
|
|
||||||
--m_currentSuggestion;
|
|
||||||
if (m_currentSuggestion < 0)
|
|
||||||
m_currentSuggestion = m_suggestions.size() - 1;
|
|
||||||
setCurrentSuggestion();
|
|
||||||
}
|
|
||||||
|
|
||||||
void selectNext()
|
|
||||||
{
|
|
||||||
++m_currentSuggestion;
|
|
||||||
if (m_currentSuggestion >= m_suggestions.size())
|
|
||||||
m_currentSuggestion = 0;
|
|
||||||
setCurrentSuggestion();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCurrentSuggestion()
|
|
||||||
{
|
|
||||||
updateLabels();
|
|
||||||
if (TextSuggestion *suggestion = m_editor->currentSuggestion())
|
|
||||||
suggestion->reset();
|
|
||||||
m_editor->insertSuggestion(std::make_unique<CyclicSuggestion>(
|
|
||||||
m_suggestions, m_editor->document(), m_currentSuggestion));
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply()
|
|
||||||
{
|
|
||||||
if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
|
|
||||||
if (!suggestion->apply())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ToolTip::hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyWord()
|
|
||||||
{
|
|
||||||
if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
|
|
||||||
if (!suggestion->applyWord(m_editor))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ToolTip::hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyLine()
|
|
||||||
{
|
|
||||||
if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
|
|
||||||
if (!suggestion->applyLine(m_editor))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ToolTip::hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
QLabel *m_numberLabel;
|
|
||||||
QList<CyclicSuggestion::Data> m_suggestions;
|
|
||||||
int m_currentSuggestion = 0;
|
|
||||||
TextEditorWidget *m_editor;
|
|
||||||
};
|
|
||||||
|
|
||||||
void CopilotHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
|
|
||||||
int pos,
|
|
||||||
ReportPriority report)
|
|
||||||
{
|
|
||||||
QScopeGuard cleanup([&] { report(Priority_None); });
|
|
||||||
if (!editorWidget->suggestionVisible())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QTextCursor cursor(editorWidget->document());
|
|
||||||
cursor.setPosition(pos);
|
|
||||||
m_block = cursor.block();
|
|
||||||
auto *suggestion = dynamic_cast<CyclicSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
|
||||||
|
|
||||||
if (!suggestion)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const QList<CyclicSuggestion::Data> suggestions = suggestion->suggestions();
|
|
||||||
if (suggestions.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
cleanup.dismiss();
|
|
||||||
report(Priority_Suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
|
|
||||||
{
|
|
||||||
Q_UNUSED(point)
|
|
||||||
auto *suggestion = dynamic_cast<CyclicSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
|
||||||
|
|
||||||
if (!suggestion)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto tooltipWidget = new CopilotCompletionToolTip(suggestion->suggestions(),
|
|
||||||
suggestion->currentSuggestion(),
|
|
||||||
editorWidget);
|
|
||||||
|
|
||||||
const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor());
|
|
||||||
QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft())
|
|
||||||
- Utils::ToolTip::offsetFromPosition();
|
|
||||||
pos.ry() -= tooltipWidget->sizeHint().height();
|
|
||||||
ToolTip::show(pos, tooltipWidget, editorWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Copilot::Internal
|
|
@@ -1,32 +0,0 @@
|
|||||||
// Copyright (C) 2023 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "requests/getcompletions.h"
|
|
||||||
|
|
||||||
#include <texteditor/basehoverhandler.h>
|
|
||||||
|
|
||||||
#include <QTextBlock>
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace TextEditor { class TextSuggestion; }
|
|
||||||
namespace Copilot::Internal {
|
|
||||||
|
|
||||||
class CopilotClient;
|
|
||||||
|
|
||||||
class CopilotHoverHandler final : public TextEditor::BaseHoverHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CopilotHoverHandler() = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
|
||||||
int pos,
|
|
||||||
ReportPriority report) final;
|
|
||||||
void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QTextBlock m_block;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Copilot::Internal
|
|
@@ -10218,6 +10218,7 @@ TextEditorFactory::TextEditorFactory()
|
|||||||
: d(new TextEditorFactoryPrivate(this))
|
: d(new TextEditorFactoryPrivate(this))
|
||||||
{
|
{
|
||||||
setEditorCreator([]() { return new BaseTextEditor; });
|
setEditorCreator([]() { return new BaseTextEditor; });
|
||||||
|
addHoverHandler(new SuggestionHoverHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEditorFactory::~TextEditorFactory()
|
TextEditorFactory::~TextEditorFactory()
|
||||||
|
@@ -5,9 +5,13 @@
|
|||||||
|
|
||||||
#include "textdocumentlayout.h"
|
#include "textdocumentlayout.h"
|
||||||
#include "texteditor.h"
|
#include "texteditor.h"
|
||||||
|
#include "texteditortr.h"
|
||||||
|
|
||||||
|
#include <QToolBar>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
|
#include <utils/tooltip/tooltip.h>
|
||||||
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -98,4 +102,142 @@ bool CyclicSuggestion::applyPart(Part part, TextEditorWidget *widget)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SuggestionToolTip : public QToolBar
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SuggestionToolTip(
|
||||||
|
QList<CyclicSuggestion::Data> suggestions, int currentSuggestion, TextEditorWidget *editor)
|
||||||
|
: m_numberLabel(new QLabel)
|
||||||
|
, m_suggestions(suggestions)
|
||||||
|
, m_currentSuggestion(std::max(0, std::min<int>(currentSuggestion, suggestions.size() - 1)))
|
||||||
|
, m_editor(editor)
|
||||||
|
{
|
||||||
|
auto prev = addAction(
|
||||||
|
Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Select Previous Copilot Suggestion"));
|
||||||
|
prev->setEnabled(m_suggestions.size() > 1);
|
||||||
|
addWidget(m_numberLabel);
|
||||||
|
auto next
|
||||||
|
= addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Copilot Suggestion"));
|
||||||
|
next->setEnabled(m_suggestions.size() > 1);
|
||||||
|
|
||||||
|
auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString()));
|
||||||
|
auto applyWord = addAction(
|
||||||
|
Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString()));
|
||||||
|
auto applyLine = addAction(Tr::tr("Apply Line"));
|
||||||
|
|
||||||
|
connect(prev, &QAction::triggered, this, &SuggestionToolTip::selectPrevious);
|
||||||
|
connect(next, &QAction::triggered, this, &SuggestionToolTip::selectNext);
|
||||||
|
connect(apply, &QAction::triggered, this, &SuggestionToolTip::apply);
|
||||||
|
connect(applyWord, &QAction::triggered, this, &SuggestionToolTip::applyWord);
|
||||||
|
connect(applyLine, &QAction::triggered, this, &SuggestionToolTip::applyLine);
|
||||||
|
|
||||||
|
updateLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateLabels()
|
||||||
|
{
|
||||||
|
m_numberLabel->setText(
|
||||||
|
Tr::tr("%1 of %2").arg(m_currentSuggestion + 1).arg(m_suggestions.count()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectPrevious()
|
||||||
|
{
|
||||||
|
--m_currentSuggestion;
|
||||||
|
if (m_currentSuggestion < 0)
|
||||||
|
m_currentSuggestion = m_suggestions.size() - 1;
|
||||||
|
setCurrentSuggestion();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectNext()
|
||||||
|
{
|
||||||
|
++m_currentSuggestion;
|
||||||
|
if (m_currentSuggestion >= m_suggestions.size())
|
||||||
|
m_currentSuggestion = 0;
|
||||||
|
setCurrentSuggestion();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCurrentSuggestion()
|
||||||
|
{
|
||||||
|
updateLabels();
|
||||||
|
if (TextSuggestion *suggestion = m_editor->currentSuggestion())
|
||||||
|
suggestion->reset();
|
||||||
|
m_editor->insertSuggestion(std::make_unique<CyclicSuggestion>(
|
||||||
|
m_suggestions, m_editor->document(), m_currentSuggestion));
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply()
|
||||||
|
{
|
||||||
|
if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
|
||||||
|
if (!suggestion->apply())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ToolTip::hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyWord()
|
||||||
|
{
|
||||||
|
if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
|
||||||
|
if (!suggestion->applyWord(m_editor))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ToolTip::hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyLine()
|
||||||
|
{
|
||||||
|
if (TextSuggestion *suggestion = m_editor->currentSuggestion()) {
|
||||||
|
if (!suggestion->applyLine(m_editor))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ToolTip::hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel *m_numberLabel;
|
||||||
|
QList<CyclicSuggestion::Data> m_suggestions;
|
||||||
|
int m_currentSuggestion = 0;
|
||||||
|
TextEditorWidget *m_editor;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SuggestionHoverHandler::identifyMatch(
|
||||||
|
TextEditorWidget *editorWidget, int pos, ReportPriority report)
|
||||||
|
{
|
||||||
|
QScopeGuard cleanup([&] { report(Priority_None); });
|
||||||
|
if (!editorWidget->suggestionVisible())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTextCursor cursor(editorWidget->document());
|
||||||
|
cursor.setPosition(pos);
|
||||||
|
m_block = cursor.block();
|
||||||
|
auto *suggestion = dynamic_cast<CyclicSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
||||||
|
|
||||||
|
if (!suggestion)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QList<CyclicSuggestion::Data> suggestions = suggestion->suggestions();
|
||||||
|
if (suggestions.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
cleanup.dismiss();
|
||||||
|
report(Priority_Suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuggestionHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
|
||||||
|
{
|
||||||
|
Q_UNUSED(point)
|
||||||
|
auto *suggestion = dynamic_cast<CyclicSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
||||||
|
|
||||||
|
if (!suggestion)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto tooltipWidget = new SuggestionToolTip(
|
||||||
|
suggestion->suggestions(), suggestion->currentSuggestion(), editorWidget);
|
||||||
|
|
||||||
|
const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor());
|
||||||
|
QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft())
|
||||||
|
- Utils::ToolTip::offsetFromPosition();
|
||||||
|
pos.ry() -= tooltipWidget->sizeHint().height();
|
||||||
|
ToolTip::show(pos, tooltipWidget, editorWidget);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace TextEditor
|
} // namespace TextEditor
|
||||||
|
@@ -5,8 +5,11 @@
|
|||||||
|
|
||||||
#include "texteditor_global.h"
|
#include "texteditor_global.h"
|
||||||
|
|
||||||
|
#include "basehoverhandler.h"
|
||||||
|
|
||||||
#include <utils/textutils.h>
|
#include <utils/textutils.h>
|
||||||
|
|
||||||
|
#include <QTextBlock>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
|
|
||||||
@@ -67,4 +70,19 @@ private:
|
|||||||
QTextDocument *m_sourceDocument = nullptr;
|
QTextDocument *m_sourceDocument = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SuggestionHoverHandler final : public BaseHoverHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SuggestionHoverHandler() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
||||||
|
int pos,
|
||||||
|
ReportPriority report) final;
|
||||||
|
void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTextBlock m_block;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace TextEditor
|
} // namespace TextEditor
|
||||||
|
Reference in New Issue
Block a user