forked from qt-creator/qt-creator
Markdown: Save and restore state
This restores the state of the markdown editor, including text editor state, preview scroll position and button states, when closing and re- opening a file. Change-Id: Ibf1cadd5e0e80149123c6c5f599157e931330343 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -20,6 +20,8 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace TextEditor::Internal {
|
namespace TextEditor::Internal {
|
||||||
|
|
||||||
const char MARKDOWNVIEWER_ID[] = "Editors.MarkdownViewer";
|
const char MARKDOWNVIEWER_ID[] = "Editors.MarkdownViewer";
|
||||||
@@ -50,10 +52,10 @@ public:
|
|||||||
m_splitter = new Core::MiniSplitter;
|
m_splitter = new Core::MiniSplitter;
|
||||||
|
|
||||||
// preview
|
// preview
|
||||||
auto browser = new QTextBrowser();
|
m_previewWidget = new QTextBrowser();
|
||||||
browser->setOpenExternalLinks(true);
|
m_previewWidget->setOpenExternalLinks(true);
|
||||||
browser->setFrameShape(QFrame::NoFrame);
|
m_previewWidget->setFrameShape(QFrame::NoFrame);
|
||||||
new Utils::MarkdownHighlighter(browser->document());
|
new Utils::MarkdownHighlighter(m_previewWidget->document());
|
||||||
|
|
||||||
// editor
|
// editor
|
||||||
m_textEditorWidget = new TextEditorWidget;
|
m_textEditorWidget = new TextEditorWidget;
|
||||||
@@ -65,13 +67,8 @@ public:
|
|||||||
context->setContext(Core::Context(MARKDOWNVIEWER_TEXT_CONTEXT));
|
context->setContext(Core::Context(MARKDOWNVIEWER_TEXT_CONTEXT));
|
||||||
Core::ICore::addContextObject(context);
|
Core::ICore::addContextObject(context);
|
||||||
|
|
||||||
if (textEditorRight) {
|
m_splitter->addWidget(m_previewWidget);
|
||||||
m_splitter->addWidget(browser);
|
m_splitter->addWidget(m_textEditorWidget);
|
||||||
m_splitter->addWidget(m_textEditorWidget);
|
|
||||||
} else {
|
|
||||||
m_splitter->addWidget(m_textEditorWidget);
|
|
||||||
m_splitter->addWidget(browser);
|
|
||||||
}
|
|
||||||
|
|
||||||
setContext(Core::Context(MARKDOWNVIEWER_ID));
|
setContext(Core::Context(MARKDOWNVIEWER_ID));
|
||||||
|
|
||||||
@@ -90,11 +87,11 @@ public:
|
|||||||
}
|
}
|
||||||
agg->add(m_widget.get());
|
agg->add(m_widget.get());
|
||||||
|
|
||||||
auto togglePreviewVisible = new QToolButton;
|
m_togglePreviewVisible = new QToolButton;
|
||||||
togglePreviewVisible->setText(Tr::tr("Show Preview"));
|
m_togglePreviewVisible->setText(Tr::tr("Show Preview"));
|
||||||
togglePreviewVisible->setCheckable(true);
|
m_togglePreviewVisible->setCheckable(true);
|
||||||
togglePreviewVisible->setChecked(showPreview);
|
m_togglePreviewVisible->setChecked(showPreview);
|
||||||
browser->setVisible(showPreview);
|
m_previewWidget->setVisible(showPreview);
|
||||||
|
|
||||||
m_toggleEditorVisible = new QToolButton;
|
m_toggleEditorVisible = new QToolButton;
|
||||||
m_toggleEditorVisible->setText(Tr::tr("Show Editor"));
|
m_toggleEditorVisible->setText(Tr::tr("Show Editor"));
|
||||||
@@ -106,37 +103,33 @@ public:
|
|||||||
swapViews->setText(Tr::tr("Swap Views"));
|
swapViews->setText(Tr::tr("Swap Views"));
|
||||||
swapViews->setEnabled(showEditor && showPreview);
|
swapViews->setEnabled(showEditor && showPreview);
|
||||||
|
|
||||||
auto toolbarLayout = new QHBoxLayout(&m_toolbar);
|
m_toolbarLayout = new QHBoxLayout(&m_toolbar);
|
||||||
toolbarLayout->setSpacing(0);
|
m_toolbarLayout->setSpacing(0);
|
||||||
toolbarLayout->setContentsMargins(0, 0, 0, 0);
|
m_toolbarLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
toolbarLayout->addStretch();
|
m_toolbarLayout->addStretch();
|
||||||
if (textEditorRight) {
|
m_toolbarLayout->addWidget(m_togglePreviewVisible);
|
||||||
toolbarLayout->addWidget(togglePreviewVisible);
|
m_toolbarLayout->addWidget(m_toggleEditorVisible);
|
||||||
toolbarLayout->addWidget(m_toggleEditorVisible);
|
m_toolbarLayout->addWidget(swapViews);
|
||||||
} else {
|
|
||||||
toolbarLayout->addWidget(m_toggleEditorVisible);
|
setWidgetOrder(textEditorRight);
|
||||||
toolbarLayout->addWidget(togglePreviewVisible);
|
|
||||||
}
|
|
||||||
toolbarLayout->addWidget(swapViews);
|
|
||||||
|
|
||||||
connect(m_document.data(),
|
connect(m_document.data(),
|
||||||
&TextDocument::mimeTypeChanged,
|
&TextDocument::mimeTypeChanged,
|
||||||
m_document.data(),
|
m_document.data(),
|
||||||
&TextDocument::changed);
|
&TextDocument::changed);
|
||||||
|
|
||||||
const auto updatePreview = [this, browser] {
|
const auto updatePreview = [this] {
|
||||||
QHash<QScrollBar *, int> positions;
|
|
||||||
const auto scrollBars = browser->findChildren<QScrollBar *>();
|
|
||||||
|
|
||||||
// save scroll positions
|
// save scroll positions
|
||||||
for (QScrollBar *scrollBar : scrollBars)
|
const QPoint positions = m_previewRestoreScrollPosition
|
||||||
positions.insert(scrollBar, scrollBar->value());
|
? *m_previewRestoreScrollPosition
|
||||||
|
: QPoint(m_previewWidget->horizontalScrollBar()->value(),
|
||||||
|
m_previewWidget->verticalScrollBar()->value());
|
||||||
|
m_previewRestoreScrollPosition.reset();
|
||||||
|
|
||||||
browser->setMarkdown(m_document->plainText());
|
m_previewWidget->setMarkdown(m_document->plainText());
|
||||||
|
|
||||||
// restore scroll positions
|
m_previewWidget->horizontalScrollBar()->setValue(positions.x());
|
||||||
for (QScrollBar *scrollBar : scrollBars)
|
m_previewWidget->verticalScrollBar()->setValue(positions.y());
|
||||||
scrollBar->setValue(positions.value(scrollBar));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto viewToggled =
|
const auto viewToggled =
|
||||||
@@ -154,10 +147,10 @@ public:
|
|||||||
}
|
}
|
||||||
swapViews->setEnabled(view->isVisible() && otherView->isVisible());
|
swapViews->setEnabled(view->isVisible() && otherView->isVisible());
|
||||||
};
|
};
|
||||||
const auto saveViewSettings = [this, togglePreviewVisible] {
|
const auto saveViewSettings = [this] {
|
||||||
Utils::QtcSettings *s = Core::ICore::settings();
|
Utils::QtcSettings *s = Core::ICore::settings();
|
||||||
s->setValueWithDefault(MARKDOWNVIEWER_SHOW_PREVIEW,
|
s->setValueWithDefault(MARKDOWNVIEWER_SHOW_PREVIEW,
|
||||||
togglePreviewVisible->isChecked(),
|
m_togglePreviewVisible->isChecked(),
|
||||||
kShowPreviewDefault);
|
kShowPreviewDefault);
|
||||||
s->setValueWithDefault(MARKDOWNVIEWER_SHOW_EDITOR,
|
s->setValueWithDefault(MARKDOWNVIEWER_SHOW_EDITOR,
|
||||||
m_toggleEditorVisible->isChecked(),
|
m_toggleEditorVisible->isChecked(),
|
||||||
@@ -167,15 +160,18 @@ public:
|
|||||||
connect(m_toggleEditorVisible,
|
connect(m_toggleEditorVisible,
|
||||||
&QToolButton::toggled,
|
&QToolButton::toggled,
|
||||||
this,
|
this,
|
||||||
[this, browser, togglePreviewVisible, viewToggled, saveViewSettings](bool visible) {
|
[this, viewToggled, saveViewSettings](bool visible) {
|
||||||
viewToggled(m_textEditorWidget, visible, browser, togglePreviewVisible);
|
viewToggled(m_textEditorWidget,
|
||||||
|
visible,
|
||||||
|
m_previewWidget,
|
||||||
|
m_togglePreviewVisible);
|
||||||
saveViewSettings();
|
saveViewSettings();
|
||||||
});
|
});
|
||||||
connect(togglePreviewVisible,
|
connect(m_togglePreviewVisible,
|
||||||
&QToolButton::toggled,
|
&QToolButton::toggled,
|
||||||
this,
|
this,
|
||||||
[this, browser, viewToggled, updatePreview, saveViewSettings](bool visible) {
|
[this, viewToggled, updatePreview, saveViewSettings](bool visible) {
|
||||||
viewToggled(browser, visible, m_textEditorWidget, m_toggleEditorVisible);
|
viewToggled(m_previewWidget, visible, m_textEditorWidget, m_toggleEditorVisible);
|
||||||
if (visible && m_performDelayedUpdate) {
|
if (visible && m_performDelayedUpdate) {
|
||||||
m_performDelayedUpdate = false;
|
m_performDelayedUpdate = false;
|
||||||
updatePreview();
|
updatePreview();
|
||||||
@@ -183,31 +179,21 @@ public:
|
|||||||
saveViewSettings();
|
saveViewSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(swapViews, &QToolButton::clicked, m_textEditorWidget, [this, toolbarLayout] {
|
connect(swapViews, &QToolButton::clicked, m_textEditorWidget, [this] {
|
||||||
QTC_ASSERT(m_splitter->count() > 1, return);
|
const bool textEditorRight = isTextEditorRight();
|
||||||
// switch views
|
setWidgetOrder(!textEditorRight);
|
||||||
auto placeholder = std::make_unique<QWidget>();
|
|
||||||
auto second = m_splitter->replaceWidget(1, placeholder.get());
|
|
||||||
auto first = m_splitter->replaceWidget(0, second);
|
|
||||||
m_splitter->replaceWidget(1, first);
|
|
||||||
// switch buttons
|
|
||||||
const int rightIndex = toolbarLayout->count() - 2;
|
|
||||||
QLayoutItem *right = toolbarLayout->takeAt(rightIndex);
|
|
||||||
toolbarLayout->insertItem(rightIndex - 1, right);
|
|
||||||
// save settings
|
// save settings
|
||||||
Utils::QtcSettings *s = Core::ICore::settings();
|
Utils::QtcSettings *s = Core::ICore::settings();
|
||||||
s->setValueWithDefault(MARKDOWNVIEWER_TEXTEDITOR_RIGHT,
|
s->setValueWithDefault(MARKDOWNVIEWER_TEXTEDITOR_RIGHT,
|
||||||
!s->value(MARKDOWNVIEWER_TEXTEDITOR_RIGHT,
|
!textEditorRight,
|
||||||
kTextEditorRightDefault)
|
|
||||||
.toBool(),
|
|
||||||
kTextEditorRightDefault);
|
kTextEditorRightDefault);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO directly update when we build with Qt 6.5.2
|
// TODO directly update when we build with Qt 6.5.2
|
||||||
m_previewTimer.setInterval(500);
|
m_previewTimer.setInterval(500);
|
||||||
m_previewTimer.setSingleShot(true);
|
m_previewTimer.setSingleShot(true);
|
||||||
connect(&m_previewTimer, &QTimer::timeout, this, [this, togglePreviewVisible, updatePreview] {
|
connect(&m_previewTimer, &QTimer::timeout, this, [this, updatePreview] {
|
||||||
if (togglePreviewVisible->isChecked())
|
if (m_togglePreviewVisible->isChecked())
|
||||||
updatePreview();
|
updatePreview();
|
||||||
else
|
else
|
||||||
m_performDelayedUpdate = true;
|
m_performDelayedUpdate = true;
|
||||||
@@ -218,6 +204,25 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isTextEditorRight() const { return m_splitter->widget(0) == m_previewWidget; }
|
||||||
|
|
||||||
|
void setWidgetOrder(bool textEditorRight)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_splitter->count() > 1, return);
|
||||||
|
QWidget *left = textEditorRight ? static_cast<QWidget *>(m_previewWidget)
|
||||||
|
: m_textEditorWidget;
|
||||||
|
QWidget *right = textEditorRight ? static_cast<QWidget *>(m_textEditorWidget)
|
||||||
|
: m_previewWidget;
|
||||||
|
m_splitter->insertWidget(0, left);
|
||||||
|
m_splitter->insertWidget(1, right);
|
||||||
|
// buttons
|
||||||
|
QWidget *leftButton = textEditorRight ? m_togglePreviewVisible : m_toggleEditorVisible;
|
||||||
|
QWidget *rightButton = textEditorRight ? m_toggleEditorVisible : m_togglePreviewVisible;
|
||||||
|
const int rightIndex = m_toolbarLayout->count() - 2;
|
||||||
|
m_toolbarLayout->insertWidget(rightIndex, leftButton);
|
||||||
|
m_toolbarLayout->insertWidget(rightIndex, rightButton);
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *toolBar() override { return &m_toolbar; }
|
QWidget *toolBar() override { return &m_toolbar; }
|
||||||
|
|
||||||
Core::IDocument *document() const override { return m_document.data(); }
|
Core::IDocument *document() const override { return m_document.data(); }
|
||||||
@@ -247,14 +252,63 @@ public:
|
|||||||
return Core::IEditor::eventFilter(obj, ev);
|
return Core::IEditor::eventFilter(obj, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray saveState() const override
|
||||||
|
{
|
||||||
|
QByteArray state;
|
||||||
|
QDataStream stream(&state, QIODevice::WriteOnly);
|
||||||
|
stream << 1; // version number
|
||||||
|
stream << m_textEditorWidget->saveState();
|
||||||
|
stream << m_previewWidget->horizontalScrollBar()->value();
|
||||||
|
stream << m_previewWidget->verticalScrollBar()->value();
|
||||||
|
stream << isTextEditorRight();
|
||||||
|
stream << m_togglePreviewVisible->isChecked();
|
||||||
|
stream << m_toggleEditorVisible->isChecked();
|
||||||
|
stream << m_splitter->saveState();
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreState(const QByteArray &state) override
|
||||||
|
{
|
||||||
|
if (state.isEmpty())
|
||||||
|
return;
|
||||||
|
int version;
|
||||||
|
QByteArray editorState;
|
||||||
|
int previewHV;
|
||||||
|
int previewVV;
|
||||||
|
bool textEditorRight;
|
||||||
|
bool previewShown;
|
||||||
|
bool textEditorShown;
|
||||||
|
QByteArray splitterState;
|
||||||
|
QDataStream stream(state);
|
||||||
|
stream >> version;
|
||||||
|
stream >> editorState;
|
||||||
|
stream >> previewHV;
|
||||||
|
stream >> previewVV;
|
||||||
|
stream >> textEditorRight;
|
||||||
|
stream >> previewShown;
|
||||||
|
stream >> textEditorShown;
|
||||||
|
stream >> splitterState;
|
||||||
|
m_textEditorWidget->restoreState(editorState);
|
||||||
|
m_previewRestoreScrollPosition.emplace(previewHV, previewVV);
|
||||||
|
setWidgetOrder(textEditorRight);
|
||||||
|
m_splitter->restoreState(splitterState);
|
||||||
|
m_togglePreviewVisible->setChecked(previewShown);
|
||||||
|
// ensure at least one is shown
|
||||||
|
m_toggleEditorVisible->setChecked(textEditorShown || !previewShown);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer m_previewTimer;
|
QTimer m_previewTimer;
|
||||||
bool m_performDelayedUpdate = false;
|
bool m_performDelayedUpdate = false;
|
||||||
Core::MiniSplitter *m_splitter;
|
Core::MiniSplitter *m_splitter;
|
||||||
|
QTextBrowser *m_previewWidget;
|
||||||
TextEditorWidget *m_textEditorWidget;
|
TextEditorWidget *m_textEditorWidget;
|
||||||
TextDocumentPtr m_document;
|
TextDocumentPtr m_document;
|
||||||
QWidget m_toolbar;
|
QWidget m_toolbar;
|
||||||
|
QHBoxLayout *m_toolbarLayout;
|
||||||
QToolButton *m_toggleEditorVisible;
|
QToolButton *m_toggleEditorVisible;
|
||||||
|
QToolButton *m_togglePreviewVisible;
|
||||||
|
std::optional<QPoint> m_previewRestoreScrollPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
MarkdownEditorFactory::MarkdownEditorFactory()
|
MarkdownEditorFactory::MarkdownEditorFactory()
|
||||||
|
|||||||
Reference in New Issue
Block a user