forked from qt-creator/qt-creator
CompilerExplorer: Fix undo
Fixes context handling for the Editor. This allows Undo/Redo actions to activate correctly. Change-Id: Ieb7fa27215f5746cf5f26e8e7b3b74f44023481c Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -5,5 +5,4 @@
|
||||
|
||||
namespace CompilerExplorer::Constants {
|
||||
const char CE_EDITOR_ID[] = "CompilerExplorer.Editor";
|
||||
const char CE_EDITOR_CONTEXT_ID[] = "CompilerExplorer.Editor.Context";
|
||||
}
|
||||
|
||||
@@ -249,7 +249,8 @@ QString SourceEditorWidget::sourceCode()
|
||||
}
|
||||
|
||||
CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSettings,
|
||||
const std::shared_ptr<CompilerSettings> &compilerSettings)
|
||||
const std::shared_ptr<CompilerSettings> &compilerSettings,
|
||||
QUndoStack *undoStack)
|
||||
: m_sourceSettings(sourceSettings)
|
||||
, m_compilerSettings(compilerSettings)
|
||||
{
|
||||
@@ -266,7 +267,7 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
|
||||
m_delayTimer,
|
||||
qOverload<>(&QTimer::start));
|
||||
|
||||
m_asmEditor = new AsmEditorWidget;
|
||||
m_asmEditor = new AsmEditorWidget(undoStack);
|
||||
m_asmDocument = QSharedPointer<TextDocument>(new TextDocument);
|
||||
m_asmDocument->setFilePath("asm.asm");
|
||||
m_asmEditor->setTextDocument(m_asmDocument);
|
||||
@@ -498,10 +499,7 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
|
||||
actionHandler.updateCurrentEditor();
|
||||
});
|
||||
|
||||
m_context = new Core::IContext(this);
|
||||
m_context->setWidget(this);
|
||||
m_context->setContext(Core::Context(Constants::CE_EDITOR_CONTEXT_ID));
|
||||
Core::ICore::addContextObject(m_context);
|
||||
setupHelpWidget();
|
||||
}
|
||||
|
||||
EditorWidget::~EditorWidget()
|
||||
@@ -521,7 +519,7 @@ void EditorWidget::addCompiler(const std::shared_ptr<SourceSettings> &sourceSett
|
||||
int idx,
|
||||
QDockWidget *parentDockWidget)
|
||||
{
|
||||
auto compiler = new CompilerWidget(sourceSettings, compilerSettings);
|
||||
auto compiler = new CompilerWidget(sourceSettings, compilerSettings, m_undoStack);
|
||||
compiler->setWindowTitle("Compiler #" + QString::number(idx));
|
||||
compiler->setObjectName("compiler_" + QString::number(idx));
|
||||
QDockWidget *dockWidget = addDockForWidget(compiler, parentDockWidget);
|
||||
@@ -573,7 +571,6 @@ void EditorWidget::addSourceEditor(const std::shared_ptr<SourceSettings> &source
|
||||
sourceSettings->compilers.clear();
|
||||
m_document->settings()->m_sources.removeItem(sourceSettings->shared_from_this());
|
||||
m_undoStack->endMacro();
|
||||
|
||||
setupHelpWidget();
|
||||
});
|
||||
|
||||
@@ -615,12 +612,16 @@ void EditorWidget::addSourceEditor(const std::shared_ptr<SourceSettings> &source
|
||||
== compilerSettings;
|
||||
});
|
||||
QTC_ASSERT(it != m_compilerWidgets.end(), return);
|
||||
if (!m_sourceWidgets.isEmpty())
|
||||
m_sourceWidgets.first()->widget()->setFocus(Qt::OtherFocusReason);
|
||||
delete *it;
|
||||
m_compilerWidgets.erase(it);
|
||||
});
|
||||
|
||||
m_sourceWidgets.append(dockWidget);
|
||||
|
||||
sourceEditor->setFocus(Qt::OtherFocusReason);
|
||||
|
||||
setupHelpWidget();
|
||||
}
|
||||
|
||||
@@ -636,6 +637,8 @@ void EditorWidget::removeSourceEditor(const std::shared_ptr<SourceSettings> &sou
|
||||
QTC_ASSERT(it != m_sourceWidgets.end(), return);
|
||||
delete *it;
|
||||
m_sourceWidgets.erase(it);
|
||||
|
||||
setupHelpWidget();
|
||||
}
|
||||
|
||||
void EditorWidget::recreateEditors()
|
||||
@@ -677,24 +680,25 @@ void EditorWidget::setupHelpWidget()
|
||||
{
|
||||
if (m_document->settings()->m_sources.size() == 0) {
|
||||
setCentralWidget(createHelpWidget());
|
||||
centralWidget()->setFocus(Qt::OtherFocusReason);
|
||||
} else {
|
||||
delete takeCentralWidget();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *EditorWidget::createHelpWidget() const
|
||||
HelperWidget::HelperWidget()
|
||||
{
|
||||
using namespace Layouting;
|
||||
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||
|
||||
auto addSourceButton = new QPushButton(Tr::tr("Add source code"));
|
||||
connect(addSourceButton, &QPushButton::clicked, this, [this] {
|
||||
auto newSource = std::make_shared<SourceSettings>(
|
||||
[settings = m_document->settings()] { return settings->apiConfig(); });
|
||||
m_document->settings()->m_sources.addItem(newSource);
|
||||
});
|
||||
|
||||
connect(addSourceButton, &QPushButton::clicked, this, &HelperWidget::addSource);
|
||||
|
||||
// clang-format off
|
||||
return Column {
|
||||
Column {
|
||||
st,
|
||||
Row {
|
||||
st,
|
||||
@@ -705,10 +709,30 @@ QWidget *EditorWidget::createHelpWidget() const
|
||||
st,
|
||||
},
|
||||
st,
|
||||
}.emerge();
|
||||
}.attachTo(this);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void HelperWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
setFocus(Qt::MouseFocusReason);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void EditorWidget::addNewSource()
|
||||
{
|
||||
auto newSource = std::make_shared<SourceSettings>(
|
||||
[settings = m_document->settings()] { return settings->apiConfig(); });
|
||||
m_document->settings()->m_sources.addItem(newSource);
|
||||
}
|
||||
|
||||
QWidget *EditorWidget::createHelpWidget() const
|
||||
{
|
||||
auto w = new HelperWidget;
|
||||
connect(w, &HelperWidget::addSource, this, &EditorWidget::addNewSource);
|
||||
return w;
|
||||
}
|
||||
|
||||
TextEditor::TextEditorWidget *EditorWidget::focusedEditorWidget() const
|
||||
{
|
||||
for (const QDockWidget *sourceWidget : m_sourceWidgets) {
|
||||
@@ -728,12 +752,10 @@ TextEditor::TextEditorWidget *EditorWidget::focusedEditorWidget() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class Editor : public Core::IEditor
|
||||
{
|
||||
public:
|
||||
Editor(TextEditorActionHandler &actionHandler)
|
||||
Editor::Editor(TextEditorActionHandler &actionHandler)
|
||||
: m_document(new JsonSettingsDocument(&m_undoStack))
|
||||
{
|
||||
setContext(Core::Context(Constants::CE_EDITOR_ID));
|
||||
setWidget(new EditorWidget(m_document, &m_undoStack, actionHandler));
|
||||
|
||||
connect(&m_undoStack, &QUndoStack::canUndoChanged, this, [&actionHandler] {
|
||||
@@ -744,33 +766,26 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
~Editor()
|
||||
Editor::~Editor()
|
||||
{
|
||||
if (m_document->isModified()) {
|
||||
auto settings = m_document->settings();
|
||||
if (settings->isDirty()) {
|
||||
settings->apply();
|
||||
Utils::Store store;
|
||||
settings->toMap(store);
|
||||
QJsonDocument doc = QJsonDocument::fromVariant(Utils::mapFromStore(store));
|
||||
|
||||
CompilerExplorer::settings().defaultDocument.setValue(
|
||||
QString::fromUtf8(doc.toJson()));
|
||||
}
|
||||
}
|
||||
delete widget();
|
||||
}
|
||||
|
||||
Core::IDocument *document() const override { return m_document.data(); }
|
||||
QWidget *toolBar() override { return nullptr; }
|
||||
static bool childHasFocus(QWidget *parent)
|
||||
{
|
||||
if (parent->hasFocus())
|
||||
return true;
|
||||
|
||||
QSharedPointer<JsonSettingsDocument> m_document;
|
||||
QUndoStack m_undoStack;
|
||||
};
|
||||
for (QWidget *child : parent->findChildren<QWidget *>())
|
||||
if (childHasFocus(child))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorFactory::EditorFactory()
|
||||
: m_actionHandler(Constants::CE_EDITOR_ID,
|
||||
Constants::CE_EDITOR_CONTEXT_ID,
|
||||
Constants::CE_EDITOR_ID,
|
||||
TextEditor::TextEditorActionHandler::None,
|
||||
[](Core::IEditor *editor) -> TextEditorWidget * {
|
||||
return static_cast<EditorWidget *>(editor->widget())->focusedEditorWidget();
|
||||
@@ -798,7 +813,32 @@ EditorFactory::EditorFactory()
|
||||
return false;
|
||||
});
|
||||
|
||||
m_actionHandler.setUnhandledCallback(
|
||||
[undoStackFromEditor](Utils::Id cmdId, Core::IEditor *editor) {
|
||||
if (cmdId != Core::Constants::UNDO && cmdId != Core::Constants::REDO)
|
||||
return false;
|
||||
|
||||
if (!childHasFocus(editor->widget()))
|
||||
return false;
|
||||
|
||||
QUndoStack *undoStack = undoStackFromEditor(editor);
|
||||
|
||||
if (!undoStack)
|
||||
return false;
|
||||
|
||||
if (cmdId == Core::Constants::UNDO)
|
||||
undoStack->undo();
|
||||
else
|
||||
undoStack->redo();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
setEditorCreator([this]() { return new Editor(m_actionHandler); });
|
||||
}
|
||||
|
||||
AsmEditorWidget::AsmEditorWidget(QUndoStack *stack)
|
||||
: m_undoStack(stack)
|
||||
{}
|
||||
|
||||
} // namespace CompilerExplorer
|
||||
|
||||
@@ -61,7 +61,7 @@ class AsmEditorWidget : public TextEditor::TextEditorWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using TextEditor::TextEditorWidget::TextEditorWidget;
|
||||
AsmEditorWidget(QUndoStack *undoStack);
|
||||
|
||||
void focusInEvent(QFocusEvent *event) override
|
||||
{
|
||||
@@ -69,8 +69,14 @@ public:
|
||||
emit gotFocus();
|
||||
}
|
||||
|
||||
void undo() override { m_undoStack->undo(); }
|
||||
void redo() override { m_undoStack->redo(); }
|
||||
|
||||
signals:
|
||||
void gotFocus();
|
||||
|
||||
private:
|
||||
QUndoStack *m_undoStack;
|
||||
};
|
||||
|
||||
class JsonSettingsDocument : public Core::IDocument
|
||||
@@ -138,7 +144,8 @@ class CompilerWidget : public QWidget
|
||||
Q_OBJECT
|
||||
public:
|
||||
CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSettings,
|
||||
const std::shared_ptr<CompilerSettings> &compilerSettings);
|
||||
const std::shared_ptr<CompilerSettings> &compilerSettings,
|
||||
QUndoStack *undoStack);
|
||||
|
||||
Core::SearchableTerminal *createTerminal();
|
||||
|
||||
@@ -172,6 +179,19 @@ private:
|
||||
QList<TextEditor::TextMark *> m_marks;
|
||||
};
|
||||
|
||||
class HelperWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HelperWidget();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
signals:
|
||||
void addSource();
|
||||
};
|
||||
|
||||
class EditorWidget : public Utils::FancyMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -194,6 +214,8 @@ protected:
|
||||
void setupHelpWidget();
|
||||
QWidget *createHelpWidget() const;
|
||||
|
||||
void addNewSource();
|
||||
|
||||
void addCompiler(const std::shared_ptr<SourceSettings> &sourceSettings,
|
||||
const std::shared_ptr<CompilerSettings> &compilerSettings,
|
||||
int idx,
|
||||
@@ -207,8 +229,6 @@ protected:
|
||||
QVariantMap windowStateCallback();
|
||||
|
||||
private:
|
||||
Core::IContext *m_context;
|
||||
|
||||
QSharedPointer<JsonSettingsDocument> m_document;
|
||||
QUndoStack *m_undoStack;
|
||||
TextEditor::TextEditorActionHandler &m_actionHandler;
|
||||
@@ -217,6 +237,19 @@ private:
|
||||
QList<QDockWidget *> m_sourceWidgets;
|
||||
};
|
||||
|
||||
class Editor : public Core::IEditor
|
||||
{
|
||||
public:
|
||||
Editor(TextEditor::TextEditorActionHandler &actionHandler);
|
||||
~Editor();
|
||||
|
||||
Core::IDocument *document() const override { return m_document.data(); }
|
||||
QWidget *toolBar() override { return nullptr; }
|
||||
|
||||
QSharedPointer<JsonSettingsDocument> m_document;
|
||||
QUndoStack m_undoStack;
|
||||
};
|
||||
|
||||
class EditorFactory : public Core::IEditorFactory
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
|
||||
auto action = new QAction(Tr::tr("Open Compiler Explorer"), this);
|
||||
connect(action, &QAction::triggered, this, [] {
|
||||
QString name("Compiler Explorer");
|
||||
QString name("Compiler Explorer $");
|
||||
Core::EditorManager::openEditorWithContents(Constants::CE_EDITOR_ID,
|
||||
&name,
|
||||
settings().defaultDocument().toUtf8());
|
||||
|
||||
@@ -199,7 +199,6 @@ void CompilerSettings::fillLibraries(const LibrarySelectionAspect::ResultCallbac
|
||||
auto future = Api::libraries(m_apiConfigFunction(), lang);
|
||||
|
||||
auto watcher = new QFutureWatcher<Api::Libraries>(this);
|
||||
watcher->setFuture(future);
|
||||
QObject::connect(watcher,
|
||||
&QFutureWatcher<Api::Libraries>::finished,
|
||||
this,
|
||||
@@ -212,6 +211,7 @@ void CompilerSettings::fillLibraries(const LibrarySelectionAspect::ResultCallbac
|
||||
return;
|
||||
}
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
}
|
||||
|
||||
void SourceSettings::fillLanguageIdModel(const Utils::StringSelectionAspect::ResultCallback &cb)
|
||||
@@ -241,7 +241,6 @@ void SourceSettings::fillLanguageIdModel(const Utils::StringSelectionAspect::Res
|
||||
auto future = Api::languages(m_apiConfigFunction());
|
||||
|
||||
auto watcher = new QFutureWatcher<Api::Languages>(this);
|
||||
watcher->setFuture(future);
|
||||
QObject::connect(watcher,
|
||||
&QFutureWatcher<Api::Languages>::finished,
|
||||
this,
|
||||
@@ -254,6 +253,7 @@ void SourceSettings::fillLanguageIdModel(const Utils::StringSelectionAspect::Res
|
||||
return;
|
||||
}
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
}
|
||||
|
||||
void CompilerSettings::fillCompilerModel(const Utils::StringSelectionAspect::ResultCallback &cb)
|
||||
@@ -277,7 +277,6 @@ void CompilerSettings::fillCompilerModel(const Utils::StringSelectionAspect::Res
|
||||
auto future = Api::compilers(m_apiConfigFunction(), m_languageId);
|
||||
|
||||
auto watcher = new QFutureWatcher<Api::Compilers>(this);
|
||||
watcher->setFuture(future);
|
||||
QObject::connect(watcher,
|
||||
&QFutureWatcher<Api::Compilers>::finished,
|
||||
this,
|
||||
@@ -295,6 +294,7 @@ void CompilerSettings::fillCompilerModel(const Utils::StringSelectionAspect::Res
|
||||
return;
|
||||
}
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
}
|
||||
|
||||
CompilerExplorerSettings::CompilerExplorerSettings()
|
||||
|
||||
@@ -62,8 +62,18 @@ public:
|
||||
Utils::Id menueGroup = Utils::Id(),
|
||||
Core::ActionContainer *container = nullptr)
|
||||
{
|
||||
return registerActionHelper(id, scriptable, title, keySequence, menueGroup, container,
|
||||
[this, slot](bool) { if (m_currentEditorWidget) slot(m_currentEditorWidget); });
|
||||
return registerActionHelper(id,
|
||||
scriptable,
|
||||
title,
|
||||
keySequence,
|
||||
menueGroup,
|
||||
container,
|
||||
[this, slot, id](bool) {
|
||||
if (m_currentEditorWidget)
|
||||
slot(m_currentEditorWidget);
|
||||
else if (m_unhandledCallback)
|
||||
m_unhandledCallback(id, m_currentEditor);
|
||||
});
|
||||
}
|
||||
|
||||
QAction *registerBoolAction(Utils::Id id,
|
||||
@@ -135,6 +145,8 @@ public:
|
||||
|
||||
TextEditorActionHandler::Predicate m_canUndoCallback;
|
||||
TextEditorActionHandler::Predicate m_canRedoCallback;
|
||||
|
||||
TextEditorActionHandler::UnhandledCallback m_unhandledCallback;
|
||||
};
|
||||
|
||||
TextEditorActionHandlerPrivate::TextEditorActionHandlerPrivate
|
||||
@@ -478,17 +490,28 @@ void TextEditorActionHandlerPrivate::updateActions()
|
||||
m_textWrappingAction->setChecked(m_currentEditorWidget->displaySettings().m_textWrapping);
|
||||
}
|
||||
|
||||
bool canRedo = false;
|
||||
bool canUndo = false;
|
||||
bool canCopy = false;
|
||||
|
||||
if (m_currentEditor && m_currentEditor->document()
|
||||
&& m_currentEditor->document()->id() == m_editorId) {
|
||||
canRedo = m_canRedoCallback ? m_canRedoCallback(m_currentEditor) : false;
|
||||
canUndo = m_canUndoCallback ? m_canUndoCallback(m_currentEditor) : false;
|
||||
|
||||
if (m_currentEditorWidget) {
|
||||
updateRedoAction(m_canRedoCallback ? m_canRedoCallback(m_currentEditor)
|
||||
: m_currentEditorWidget->document()->isRedoAvailable());
|
||||
updateUndoAction(m_canUndoCallback ? m_canUndoCallback(m_currentEditor)
|
||||
: m_currentEditorWidget->document()->isUndoAvailable());
|
||||
updateCopyAction(m_currentEditorWidget->textCursor().hasSelection());
|
||||
} else {
|
||||
updateRedoAction(false);
|
||||
updateUndoAction(false);
|
||||
updateCopyAction(false);
|
||||
canRedo = m_canRedoCallback ? canRedo
|
||||
: m_currentEditorWidget->document()->isRedoAvailable();
|
||||
canUndo = m_canUndoCallback ? canUndo
|
||||
: m_currentEditorWidget->document()->isUndoAvailable();
|
||||
canCopy = m_currentEditorWidget->textCursor().hasSelection();
|
||||
}
|
||||
}
|
||||
|
||||
updateRedoAction(canRedo);
|
||||
updateUndoAction(canUndo);
|
||||
updateCopyAction(canCopy);
|
||||
|
||||
updateOptionalActions();
|
||||
}
|
||||
|
||||
@@ -553,20 +576,20 @@ void TextEditorActionHandlerPrivate::updateCurrentEditor(Core::IEditor *editor)
|
||||
m_currentEditor = editor;
|
||||
|
||||
if (editor && editor->document()->id() == m_editorId) {
|
||||
TextEditorWidget *editorWidget = m_findTextWidget(editor);
|
||||
QTC_ASSERT(editorWidget, return); // editor has our id, so shouldn't happen
|
||||
m_currentEditorWidget = editorWidget;
|
||||
connect(editorWidget, &QPlainTextEdit::undoAvailable,
|
||||
m_currentEditorWidget = m_findTextWidget(editor);
|
||||
if (m_currentEditorWidget) {
|
||||
connect(m_currentEditorWidget, &QPlainTextEdit::undoAvailable,
|
||||
this, &TextEditorActionHandlerPrivate::updateUndoAction);
|
||||
connect(editorWidget, &QPlainTextEdit::redoAvailable,
|
||||
connect(m_currentEditorWidget, &QPlainTextEdit::redoAvailable,
|
||||
this, &TextEditorActionHandlerPrivate::updateRedoAction);
|
||||
connect(editorWidget, &QPlainTextEdit::copyAvailable,
|
||||
connect(m_currentEditorWidget, &QPlainTextEdit::copyAvailable,
|
||||
this, &TextEditorActionHandlerPrivate::updateCopyAction);
|
||||
connect(editorWidget, &TextEditorWidget::readOnlyChanged,
|
||||
connect(m_currentEditorWidget, &TextEditorWidget::readOnlyChanged,
|
||||
this, &TextEditorActionHandlerPrivate::updateActions);
|
||||
connect(editorWidget, &TextEditorWidget::optionalActionMaskChanged,
|
||||
connect(m_currentEditorWidget, &TextEditorWidget::optionalActionMaskChanged,
|
||||
this, &TextEditorActionHandlerPrivate::updateOptionalActions);
|
||||
}
|
||||
}
|
||||
updateActions();
|
||||
}
|
||||
|
||||
@@ -614,4 +637,9 @@ void TextEditorActionHandler::setCanRedoCallback(const Predicate &callback)
|
||||
d->m_canRedoCallback = callback;
|
||||
}
|
||||
|
||||
void TextEditorActionHandler::setUnhandledCallback(const UnhandledCallback &callback)
|
||||
{
|
||||
d->m_unhandledCallback = callback;
|
||||
}
|
||||
|
||||
} // namespace TextEditor
|
||||
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
void setCanUndoCallback(const Predicate &callback);
|
||||
void setCanRedoCallback(const Predicate &callback);
|
||||
|
||||
using UnhandledCallback = std::function<void(Utils::Id commandId, Core::IEditor *editor)>;
|
||||
void setUnhandledCallback(const UnhandledCallback &callback);
|
||||
|
||||
private:
|
||||
Internal::TextEditorActionHandlerPrivate *d;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user