From 3dcd6963818c0f368d6ae5713d3b1b14f4d98217 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Tue, 11 Jul 2023 17:20:31 +0200 Subject: [PATCH 01/10] GUI: prevent coreview from losing focus when new machine is created --- src/gui/mainwindow/mainwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index 50287201..eb343248 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -226,8 +226,10 @@ void MainWindow::create_core( machine.reset(new_machine); // Create machine view + auto focused_index = central_window->currentIndex(); corescene.reset(); show_hide_coreview(coreview_shown); + central_window->setCurrentIndex(focused_index); set_speed(); // Update machine speed to current settings From 16c78c16948799a008b47569ea53989623c33e1f Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Sun, 16 Jul 2023 23:40:44 +0200 Subject: [PATCH 02/10] GUI: split central widget tabs to coreview and editor --- src/gui/mainwindow/mainwindow.cpp | 17 +++++++++++------ src/gui/mainwindow/mainwindow.h | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index eb343248..fc40a94b 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -43,13 +43,17 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) setWindowTitle(APP_NAME); setDockNestingEnabled(true); - central_window.reset(new HidingTabWidget(this)); - central_window->setTabBarAutoHide(true); - this->setCentralWidget(central_window.data()); + central_widget_tabs.reset(new HidingTabWidget(this)); + central_widget_tabs->setTabBarAutoHide(true); + this->setCentralWidget(central_widget_tabs.data()); - // Prepare empty core view coreview.reset(new GraphicsView(this)); - central_window->addTab(coreview.data(), "Core"); + central_widget_tabs->addTab(coreview.data(), "Core"); + + editor_tabs.reset(new HidingTabWidget(this)); + editor_tabs->setTabBarAutoHide(true); + central_widget_tabs->addTab(editor_tabs.data(), "Editor"); + central_widget_tabs->setTabVisible(central_widget_tabs->indexOf(editor_tabs.data()), false); // Create/prepare other widgets ndialog.reset(new NewDialog(this, settings)); @@ -458,7 +462,8 @@ void MainWindow::save_exit_or_ignore(bool cancel, const QStringList &tosavelist) #ifndef __EMSCRIPTEN__ editor->saveFile(); #else - central_window->setCurrentWidget(editor); + editor_tabs->setCurrentWidget(editor); + central_widget_tabs->setCurrentWidget(editor_tabs.data()); save_source(); return; #endif diff --git a/src/gui/mainwindow/mainwindow.h b/src/gui/mainwindow/mainwindow.h index 1e219324..29328f61 100644 --- a/src/gui/mainwindow/mainwindow.h +++ b/src/gui/mainwindow/mainwindow.h @@ -117,7 +117,8 @@ protected slots: Box ui {}; Box ndialog {}; - Box central_window {}; + Box central_widget_tabs {}; + Box editor_tabs {}; Box coreview {}; Box corescene; From 323ad607721d70e0d6f7827095367f4ab8dbd3e6 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Sun, 16 Jul 2023 23:41:57 +0200 Subject: [PATCH 03/10] GUI: support recursive hiding in nested tab widgets --- src/gui/mainwindow/mainwindow.cpp | 69 +++++++++++++++-------------- src/gui/widgets/HidingTabWidget.cpp | 13 +++++- src/gui/widgets/HidingTabWidget.h | 6 +++ 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index fc40a94b..70b2820c 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -54,6 +54,9 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) editor_tabs->setTabBarAutoHide(true); central_widget_tabs->addTab(editor_tabs.data(), "Editor"); central_widget_tabs->setTabVisible(central_widget_tabs->indexOf(editor_tabs.data()), false); + connect( + editor_tabs.data(), &HidingTabWidget::requestSetVisible, central_widget_tabs.data(), + &HidingTabWidget::setTabVisibleRequested); // Create/prepare other widgets ndialog.reset(new NewDialog(this, settings)); @@ -138,7 +141,7 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) // Source editor related actions connect( - central_window.data(), &HidingTabWidget::currentChanged, this, + editor_tabs.data(), &HidingTabWidget::currentChanged, this, &MainWindow::central_tab_changed); foreach (QString file_name, settings->value("openSrcFiles").toStringList()) { @@ -177,7 +180,7 @@ void MainWindow::show_hide_coreview(bool show) { if (!show) { if (corescene == nullptr) { } else { - central_window->removeTab(central_window->indexOf(coreview.data())); + central_widget_tabs->setTabVisibleRequested(coreview.data(), false); corescene.reset(); if (coreview != nullptr) { coreview->setScene(corescene.data()); } } @@ -191,10 +194,7 @@ void MainWindow::show_hide_coreview(bool show) { } else { corescene.reset(new CoreViewSceneSimple(machine.data())); } - central_window->insertTab(0, coreview.data(), "Core"); - // Ensures correct zoom. - coreview->setScene(corescene.data()); - this->setCentralWidget(central_window.data()); + central_widget_tabs->setTabVisibleRequested(coreview.data(), true); // Connect scene signals to actions connect(corescene.data(), &CoreViewScene::request_registers, this, &MainWindow::show_registers); @@ -230,10 +230,10 @@ void MainWindow::create_core( machine.reset(new_machine); // Create machine view - auto focused_index = central_window->currentIndex(); + auto focused_index = central_widget_tabs->currentIndex(); corescene.reset(); show_hide_coreview(coreview_shown); - central_window->setCurrentIndex(focused_index); + central_widget_tabs->setCurrentIndex(focused_index); set_speed(); // Update machine speed to current settings @@ -469,13 +469,13 @@ void MainWindow::save_exit_or_ignore(bool cancel, const QStringList &tosavelist) #endif } } - if (save_unnamed && (central_window != nullptr)) { - for (int i = 0; i < central_window->count(); i++) { - QWidget *w = central_window->widget(i); + if (save_unnamed && (editor_tabs != nullptr)) { + for (int i = 0; i < editor_tabs->count(); i++) { + QWidget *w = editor_tabs->widget(i); auto *editor = dynamic_cast(w); if (editor == nullptr) { continue; } if (!editor->saveAsRequired()) { continue; } - central_window->setCurrentWidget(editor); + editor_tabs->setCurrentWidget(editor); save_source_as(); return; } @@ -557,22 +557,23 @@ void MainWindow::tab_widget_destroyed(QObject *obj) { } void MainWindow::central_tab_changed(int index) { - QWidget *widget = central_window->widget(index); + QWidget *widget = editor_tabs->widget(index); auto *srceditor = dynamic_cast(widget); if (srceditor != nullptr) { setCurrentSrcEditor(srceditor); } } void MainWindow::add_src_editor_to_tabs(SrcEditor *editor) { - central_window->addTab(editor, editor->title()); - central_window->setCurrentWidget(editor); + editor_tabs->addTab(editor, editor->title()); + editor_tabs->setCurrentWidget(editor); + central_widget_tabs->setCurrentWidget(editor_tabs.data()); connect(editor, &QObject::destroyed, this, &MainWindow::tab_widget_destroyed); } void MainWindow::update_open_file_list() { QStringList open_src_files; - if ((central_window == nullptr) || (settings == nullptr)) { return; } - for (int i = 0; i < central_window->count(); i++) { - QWidget *w = central_window->widget(i); + if ((editor_tabs == nullptr) || (settings == nullptr)) { return; } + for (int i = 0; i < editor_tabs->count(); i++) { + QWidget *w = editor_tabs->widget(i); auto *editor = dynamic_cast(w); if (editor == nullptr) { continue; } if (editor->filename() == "") { continue; } @@ -584,9 +585,9 @@ void MainWindow::update_open_file_list() { bool MainWindow::modified_file_list(QStringList &list, bool report_unnamed) { bool ret = false; list.clear(); - if (central_window == nullptr) { return false; } - for (int i = 0; i < central_window->count(); i++) { - QWidget *w = central_window->widget(i); + if (editor_tabs == nullptr) { return false; } + for (int i = 0; i < editor_tabs->count(); i++) { + QWidget *w = editor_tabs->widget(i); auto *editor = dynamic_cast(w); if (editor == nullptr) { continue; } if ((editor->filename() == "") && !report_unnamed) { continue; } @@ -608,11 +609,11 @@ static int compare_filenames(const QString &filename1, const QString &filename2) } SrcEditor *MainWindow::source_editor_for_file(const QString &filename, bool open) { - if (central_window == nullptr) { return nullptr; } + if (editor_tabs == nullptr) { return nullptr; } int found_match = 0; SrcEditor *found_editor = nullptr; - for (int i = 0; i < central_window->count(); i++) { - QWidget *w = central_window->widget(i); + for (int i = 0; i < editor_tabs->count(); i++) { + QWidget *w = editor_tabs->widget(i); auto *editor = dynamic_cast(w); if (editor == nullptr) { continue; } int match = compare_filenames(filename, editor->filename()); @@ -649,7 +650,7 @@ void MainWindow::open_source() { if (!file_name.isEmpty()) { SrcEditor *editor = source_editor_for_file(file_name, false); if (editor != nullptr) { - if (central_window != nullptr) { central_window->setCurrentWidget(editor); } + if (editor_tabs != nullptr) { editor_tabs->setCurrentWidget(editor); } return; } editor = new SrcEditor(); @@ -684,8 +685,8 @@ void MainWindow::save_source_as() { showAsyncCriticalBox(this, "Simulator Error", tr("Cannot save file '%1'.").arg(fn)); return; } - int idx = central_window->indexOf(current_srceditor); - if (idx >= 0) { central_window->setTabText(idx, current_srceditor->title()); } + int idx = editor_tabs->indexOf(current_srceditor); + if (idx >= 0) { editor_tabs->setTabText(idx, current_srceditor->title()); } update_open_file_list(); #else QString filename = current_srceditor->filename(); @@ -707,8 +708,8 @@ void MainWindow::src_editor_save_to(const QString &filename) { if (filename.isEmpty() || (current_srceditor == nullptr)) { return; } current_srceditor->setFileName(filename); if (!current_srceditor->filename().isEmpty()) { save_source(); } - int idx = central_window->indexOf(current_srceditor); - if (idx >= 0) { central_window->setTabText(idx, current_srceditor->title()); } + int idx = editor_tabs->indexOf(current_srceditor); + if (idx >= 0) { editor_tabs->setTabText(idx, current_srceditor->title()); } update_open_file_list(); } @@ -767,8 +768,8 @@ void MainWindow::close_source() { if (current_srceditor == nullptr) { return; } SrcEditor *editor = current_srceditor; setCurrentSrcEditor(nullptr); - int idx = central_window->indexOf(editor); - if (idx >= 0) { central_window->removeTab(idx); } + int idx = editor_tabs->indexOf(editor); + if (idx >= 0) { editor_tabs->removeTab(idx); } delete editor; update_open_file_list(); } @@ -816,7 +817,7 @@ void MainWindow::message_selected( if (editor == nullptr) { return; } editor->setCursorToLine(line); editor->setFocus(); - if (central_window != nullptr) { central_window->setCurrentWidget(editor); } + if (editor_tabs != nullptr) { editor_tabs->setCurrentWidget(editor); } } bool SimpleAsmWithEditorCheck::process_file(const QString &filename, QString *error_ptr) { @@ -864,8 +865,8 @@ bool SimpleAsmWithEditorCheck::process_pragma( if (op == "tab") { if ((operands.count() < 3) || error_occured) { return true; } if (!QString::compare(operands.at(2), "core", Qt::CaseInsensitive) - && (mainwindow->central_window != nullptr) && (mainwindow->coreview != nullptr)) { - mainwindow->central_window->setCurrentWidget(mainwindow->coreview.data()); + && (mainwindow->editor_tabs != nullptr) && (mainwindow->coreview != nullptr)) { + mainwindow->editor_tabs->setCurrentWidget(mainwindow->coreview.data()); } return true; } diff --git a/src/gui/widgets/HidingTabWidget.cpp b/src/gui/widgets/HidingTabWidget.cpp index e3fc71ae..42b10274 100644 --- a/src/gui/widgets/HidingTabWidget.cpp +++ b/src/gui/widgets/HidingTabWidget.cpp @@ -2,9 +2,18 @@ void HidingTabWidget::tabInserted(int index) { QTabWidget::tabInserted(index); - if (count() == 1) { show(); } + if (count() == 1) { + show(); + requestSetVisible(this, true); + } } void HidingTabWidget::tabRemoved(int index) { QTabWidget::tabRemoved(index); - if (count() == 0) { hide(); } + if (count() == 0) { + hide(); + requestSetVisible(this, false); + } +} +void HidingTabWidget::setTabVisibleRequested(QWidget *tab, bool visible) { + setTabVisible(indexOf(tab), visible); } diff --git a/src/gui/widgets/HidingTabWidget.h b/src/gui/widgets/HidingTabWidget.h index d3946036..eef1e1a2 100644 --- a/src/gui/widgets/HidingTabWidget.h +++ b/src/gui/widgets/HidingTabWidget.h @@ -14,6 +14,12 @@ class HidingTabWidget : public QTabWidget { void tabInserted(int index) override; void tabRemoved(int index) override; + +signals: + void requestSetVisible(QWidget *tab, bool visible); + +public slots: + void setTabVisibleRequested(QWidget *tab, bool visible); }; #endif // HIDINGTABWIDGET_H From 98e69e175344a7a5cdef5850f7ab4711af760802 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Sun, 16 Jul 2023 23:43:51 +0200 Subject: [PATCH 04/10] GUI: fix file naming scheme --- src/gui/CMakeLists.txt | 4 ++-- src/gui/mainwindow/mainwindow.cpp | 2 -- src/gui/mainwindow/mainwindow.h | 2 +- src/gui/widgets/{HidingTabWidget.cpp => hidingtabwidget.cpp} | 2 +- src/gui/widgets/{HidingTabWidget.h => hidingtabwidget.h} | 0 5 files changed, 4 insertions(+), 6 deletions(-) rename src/gui/widgets/{HidingTabWidget.cpp => hidingtabwidget.cpp} (94%) rename src/gui/widgets/{HidingTabWidget.h => hidingtabwidget.h} (100%) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index fcf79d8f..f094249c 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -45,7 +45,7 @@ set(gui_SOURCES textsignalaction.cpp windows/coreview/components/value_handlers.cpp windows/coreview/components/cache.cpp - widgets/HidingTabWidget.cpp + widgets/hidingtabwidget.cpp ) set(gui_HEADERS dialogs/about/aboutdialog.h @@ -86,7 +86,7 @@ set(gui_HEADERS windows/coreview/data.h windows/coreview/components/cache.h helper/async_modal.h - widgets/HidingTabWidget.h + widgets/hidingtabwidget.h ) set(gui_UI dialogs/gotosymbol/gotosymboldialog.ui diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index 70b2820c..d982e73c 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -1,5 +1,3 @@ -#include "widgets/HidingTabWidget.h" - #include #include #include diff --git a/src/gui/mainwindow/mainwindow.h b/src/gui/mainwindow/mainwindow.h index 29328f61..0a9a4fa3 100644 --- a/src/gui/mainwindow/mainwindow.h +++ b/src/gui/mainwindow/mainwindow.h @@ -8,7 +8,7 @@ #include "machine/machineconfig.h" #include "scene.h" #include "ui_MainWindow.h" -#include "widgets/HidingTabWidget.h" +#include "widgets/hidingtabwidget.h" #include "windows/cache/cachedock.h" #include "windows/csr/csrdock.h" #include "windows/editor/srceditor.h" diff --git a/src/gui/widgets/HidingTabWidget.cpp b/src/gui/widgets/hidingtabwidget.cpp similarity index 94% rename from src/gui/widgets/HidingTabWidget.cpp rename to src/gui/widgets/hidingtabwidget.cpp index 42b10274..80c01060 100644 --- a/src/gui/widgets/HidingTabWidget.cpp +++ b/src/gui/widgets/hidingtabwidget.cpp @@ -1,4 +1,4 @@ -#include "HidingTabWidget.h" +#include "hidingtabwidget.h" void HidingTabWidget::tabInserted(int index) { QTabWidget::tabInserted(index); diff --git a/src/gui/widgets/HidingTabWidget.h b/src/gui/widgets/hidingtabwidget.h similarity index 100% rename from src/gui/widgets/HidingTabWidget.h rename to src/gui/widgets/hidingtabwidget.h From 610a0ae4b7d4c872be4e9c3a64a4cebf053e4a89 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Mon, 17 Jul 2023 00:30:49 +0200 Subject: [PATCH 05/10] GUI: use older and less nice Qt API --- src/gui/mainwindow/mainwindow.cpp | 14 ++++++++------ src/gui/widgets/hidingtabwidget.cpp | 12 ++++++++---- src/gui/widgets/hidingtabwidget.h | 4 ++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index d982e73c..42122a0b 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -50,11 +50,10 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) editor_tabs.reset(new HidingTabWidget(this)); editor_tabs->setTabBarAutoHide(true); - central_widget_tabs->addTab(editor_tabs.data(), "Editor"); - central_widget_tabs->setTabVisible(central_widget_tabs->indexOf(editor_tabs.data()), false); + editor_tabs->setWindowTitle("Editor"); connect( - editor_tabs.data(), &HidingTabWidget::requestSetVisible, central_widget_tabs.data(), - &HidingTabWidget::setTabVisibleRequested); + editor_tabs.data(), &HidingTabWidget::requestAddRemoveTab, central_widget_tabs.data(), + &HidingTabWidget::addRemoveTabRequested); // Create/prepare other widgets ndialog.reset(new NewDialog(this, settings)); @@ -178,7 +177,7 @@ void MainWindow::show_hide_coreview(bool show) { if (!show) { if (corescene == nullptr) { } else { - central_widget_tabs->setTabVisibleRequested(coreview.data(), false); + central_widget_tabs->removeTab(central_widget_tabs->indexOf(coreview.data())); corescene.reset(); if (coreview != nullptr) { coreview->setScene(corescene.data()); } } @@ -192,7 +191,10 @@ void MainWindow::show_hide_coreview(bool show) { } else { corescene.reset(new CoreViewSceneSimple(machine.data())); } - central_widget_tabs->setTabVisibleRequested(coreview.data(), true); + central_widget_tabs->insertTab(0, coreview.data(), "Core"); + // Ensures correct zoom. + coreview->setScene(corescene.data()); + this->setCentralWidget(central_widget_tabs.data()); // Connect scene signals to actions connect(corescene.data(), &CoreViewScene::request_registers, this, &MainWindow::show_registers); diff --git a/src/gui/widgets/hidingtabwidget.cpp b/src/gui/widgets/hidingtabwidget.cpp index 80c01060..eb770b31 100644 --- a/src/gui/widgets/hidingtabwidget.cpp +++ b/src/gui/widgets/hidingtabwidget.cpp @@ -4,16 +4,20 @@ void HidingTabWidget::tabInserted(int index) { QTabWidget::tabInserted(index); if (count() == 1) { show(); - requestSetVisible(this, true); + requestAddRemoveTab(this, true); } } void HidingTabWidget::tabRemoved(int index) { QTabWidget::tabRemoved(index); if (count() == 0) { hide(); - requestSetVisible(this, false); + requestAddRemoveTab(this, false); } } -void HidingTabWidget::setTabVisibleRequested(QWidget *tab, bool visible) { - setTabVisible(indexOf(tab), visible); +void HidingTabWidget::addRemoveTabRequested(QWidget *tab, bool exists) { + if (!exists) { + removeTab(indexOf(tab)); + } else { + addTab(tab, tab->windowTitle()); + } } diff --git a/src/gui/widgets/hidingtabwidget.h b/src/gui/widgets/hidingtabwidget.h index eef1e1a2..af0e84db 100644 --- a/src/gui/widgets/hidingtabwidget.h +++ b/src/gui/widgets/hidingtabwidget.h @@ -16,10 +16,10 @@ class HidingTabWidget : public QTabWidget { void tabRemoved(int index) override; signals: - void requestSetVisible(QWidget *tab, bool visible); + void requestAddRemoveTab(QWidget *tab, bool visible); public slots: - void setTabVisibleRequested(QWidget *tab, bool visible); + void addRemoveTabRequested(QWidget *tab, bool exists); }; #endif // HIDINGTABWIDGET_H From d42def81933a9f38515590f451f608c274fb4120 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 19 Jul 2023 17:37:21 +0200 Subject: [PATCH 06/10] GUI: use QPlainTextEdit for code editor --- src/gui/windows/editor/srceditor.cpp | 9 +++++++-- src/gui/windows/editor/srceditor.h | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/gui/windows/editor/srceditor.cpp b/src/gui/windows/editor/srceditor.cpp index 6380fdaa..c10bb60a 100644 --- a/src/gui/windows/editor/srceditor.cpp +++ b/src/gui/windows/editor/srceditor.cpp @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include #include #include @@ -31,7 +34,9 @@ void SrcEditor::setup_common() { p.setColor(QPalette::Disabled, QPalette::Base, Qt::white); setPalette(p); - setTextColor(Qt::black); + QTextCharFormat fmt; + fmt.setForeground(Qt::black); + mergeCurrentCharFormat(fmt); // Set tab width to 4 spaces setTabStopDistance(fontMetrics().horizontalAdvance(' ') * TAB_WIDTH); @@ -170,7 +175,7 @@ void SrcEditor::keyPressEvent(QKeyEvent *event) { } } - QTextEdit::keyPressEvent(event); + QPlainTextEdit::keyPressEvent(event); } void SrcEditor::indent_selection(QTextCursor &cursor) { diff --git a/src/gui/windows/editor/srceditor.h b/src/gui/windows/editor/srceditor.h index 48275eb0..840861c4 100644 --- a/src/gui/windows/editor/srceditor.h +++ b/src/gui/windows/editor/srceditor.h @@ -7,10 +7,11 @@ #include #include #include +#include -class SrcEditor : public QTextEdit { +class SrcEditor : public QPlainTextEdit { Q_OBJECT - using Super = QTextEdit; + using Super = QPlainTextEdit; public: explicit SrcEditor(const QString &text, QWidget *parent = nullptr); From e59f8d486ae5dc4a2b65b6aeee3583335a2a47f6 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 19 Jul 2023 19:02:35 +0200 Subject: [PATCH 07/10] GUI: editor line numbers --- src/gui/CMakeLists.txt | 2 + src/gui/windows/editor/linenumberarea.cpp | 46 +++++++++++++++++ src/gui/windows/editor/linenumberarea.h | 23 +++++++++ src/gui/windows/editor/srceditor.cpp | 63 +++++++++++++++-------- src/gui/windows/editor/srceditor.h | 11 +++- 5 files changed, 123 insertions(+), 22 deletions(-) create mode 100644 src/gui/windows/editor/linenumberarea.cpp create mode 100644 src/gui/windows/editor/linenumberarea.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index f094249c..11f6746a 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -27,6 +27,7 @@ set(gui_SOURCES ui/hexlineedit.cpp windows/editor/highlighterasm.cpp windows/editor/highlighterc.cpp + windows/editor/linenumberarea.cpp hinttabledelegate.cpp windows/lcd/lcddisplaydock.cpp windows/lcd/lcddisplayview.cpp @@ -67,6 +68,7 @@ set(gui_HEADERS ui/hexlineedit.h windows/editor/highlighterasm.h windows/editor/highlighterc.h + windows/editor/linenumberarea.h hinttabledelegate.h windows/lcd/lcddisplaydock.h windows/lcd/lcddisplayview.h diff --git a/src/gui/windows/editor/linenumberarea.cpp b/src/gui/windows/editor/linenumberarea.cpp new file mode 100644 index 00000000..96111cdb --- /dev/null +++ b/src/gui/windows/editor/linenumberarea.cpp @@ -0,0 +1,46 @@ +#include "linenumberarea.h" + +#include "srceditor.h" + +#include +#include + +constexpr int RIGHT_MARGIN = 10; +constexpr int RIGHT_PADDING = 5; +constexpr int LEFT_PADDING = 5; + +LineNumberArea::LineNumberArea(SrcEditor *editor_) : QWidget(editor_), editor(editor_) {} + +QSize LineNumberArea::sizeHint() const { + int digits = std::log10(std::max(1, editor->blockCount())) + 2; + int space = editor->fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits + LEFT_PADDING + + RIGHT_PADDING + RIGHT_MARGIN; + return { space, 0 }; +} +void LineNumberArea::paintEvent(QPaintEvent *event) { + QPainter painter(this); + painter.fillRect(event->rect(), palette().base()); + painter.drawLine( + event->rect().right() - RIGHT_MARGIN, 0, event->rect().right() - RIGHT_MARGIN, + event->rect().bottom()); + + QTextBlock block = editor->firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top + = qRound(editor->blockBoundingGeometry(block).translated(editor->contentOffset()).top()); + int bottom = top + qRound(editor->blockBoundingRect(block).height()); + while (block.isValid() && top <= event->rect().bottom()) { + if (block.isVisible() && bottom >= event->rect().top()) { + QString number = QString::number(blockNumber + 1); + painter.setPen(palette().windowText().color()); + painter.drawText( + 0, top, this->sizeHint().width() - RIGHT_PADDING - RIGHT_MARGIN, + editor->fontMetrics().height(), Qt::AlignRight, number); + } + + block = block.next(); + top = bottom; + bottom = top + qRound(editor->blockBoundingRect(block).height()); + ++blockNumber; + } +} diff --git a/src/gui/windows/editor/linenumberarea.h b/src/gui/windows/editor/linenumberarea.h new file mode 100644 index 00000000..b868e2c1 --- /dev/null +++ b/src/gui/windows/editor/linenumberarea.h @@ -0,0 +1,23 @@ +#ifndef LINENUMBERAREA_H +#define LINENUMBERAREA_H + +#include "common/memory_ownership.h" + +#include + +class SrcEditor; + +class LineNumberArea : public QWidget { +public: + explicit LineNumberArea(BORROWED SrcEditor *editor_); + + [[nodiscard]] QSize sizeHint() const override; + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + BORROWED SrcEditor *editor; +}; + +#endif // LINENUMBERAREA_H \ No newline at end of file diff --git a/src/gui/windows/editor/srceditor.cpp b/src/gui/windows/editor/srceditor.cpp index c10bb60a..2d93d645 100644 --- a/src/gui/windows/editor/srceditor.cpp +++ b/src/gui/windows/editor/srceditor.cpp @@ -1,53 +1,52 @@ #include "srceditor.h" #include "common/logging.h" +#include "linenumberarea.h" #include "windows/editor/highlighterasm.h" #include "windows/editor/highlighterc.h" #include #include +#include #include #include #include #include #include +#include #include #include -#include #include LOG_CATEGORY("gui.src_editor"); -void SrcEditor::setup_common() { - QFont font; +SrcEditor::SrcEditor(QWidget *parent) : SrcEditor("", parent) {} + +SrcEditor::SrcEditor(const QString &text, QWidget *parent) + : Super(text, parent) + , line_number_area(new LineNumberArea(this)) { + QFont font1; saveAsRequiredFl = true; - font.setFamily("Courier"); - font.setFixedPitch(true); - font.setPointSize(10); - setFont(font); + font1.setFamily("Courier"); + font1.setFixedPitch(true); + font1.setPointSize(10); + setFont(font1); tname = "Unknown"; highlighter.reset(new HighlighterAsm(document())); QPalette p = palette(); - p.setColor(QPalette::Active, QPalette::Base, Qt::white); - p.setColor(QPalette::Inactive, QPalette::Base, Qt::white); - p.setColor(QPalette::Disabled, QPalette::Base, Qt::white); + p.setColor(QPalette::Base, Qt::white); + p.setColor(QPalette::Text, Qt::black); + p.setColor(QPalette::WindowText, Qt::darkGray); setPalette(p); - QTextCharFormat fmt; - fmt.setForeground(Qt::black); - mergeCurrentCharFormat(fmt); - // Set tab width to 4 spaces setTabStopDistance(fontMetrics().horizontalAdvance(' ') * TAB_WIDTH); -} -SrcEditor::SrcEditor(QWidget *parent) : Super(parent) { - setup_common(); -} + connect(this, &SrcEditor::blockCountChanged, this, &SrcEditor::updateMargins); + connect(this, &SrcEditor::updateRequest, this, &SrcEditor::updateLineNumberArea); -SrcEditor::SrcEditor(const QString &text, QWidget *parent) : Super(text, parent) { - setup_common(); + updateMargins(0); } QString SrcEditor::filename() { @@ -246,4 +245,26 @@ void SrcEditor::toggle_selection_comment(QTextCursor &cursor, bool is_comment) { cursor.movePosition(QTextCursor::Down); } cursor.endEditBlock(); -}; +} + +void SrcEditor::updateMargins(int /* newBlockCount */) { + setViewportMargins(line_number_area->sizeHint().width(), 0, 0, 0); +} + +void SrcEditor::updateLineNumberArea(const QRect &rect, int dy) { + if (dy) { + line_number_area->scroll(0, dy); + } else { + line_number_area->update(0, rect.y(), line_number_area->width(), rect.height()); + } + + if (rect.contains(viewport()->rect())) updateMargins(0); +} + +void SrcEditor::resizeEvent(QResizeEvent *event) { + QPlainTextEdit::resizeEvent(event); + + QRect cr = contentsRect(); + line_number_area->setGeometry( + QRect(cr.left(), cr.top(), line_number_area->sizeHint().width(), cr.height())); +} diff --git a/src/gui/windows/editor/srceditor.h b/src/gui/windows/editor/srceditor.h index 840861c4..ac602248 100644 --- a/src/gui/windows/editor/srceditor.h +++ b/src/gui/windows/editor/srceditor.h @@ -2,12 +2,14 @@ #define SRCEDITOR_H #include "common/memory_ownership.h" +#include "linenumberarea.h" #include "machine/machine.h" #include #include #include #include +#include class SrcEditor : public QPlainTextEdit { Q_OBJECT @@ -30,10 +32,15 @@ class SrcEditor : public QPlainTextEdit { protected: void keyPressEvent(QKeyEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private slots: + void updateMargins(int newBlockCount); + void updateLineNumberArea(const QRect &rect, int dy); private: ::Box highlighter {}; - void setup_common(); + QWidget *line_number_area; QString fname; QString tname; bool saveAsRequiredFl {}; @@ -53,6 +60,8 @@ class SrcEditor : public QPlainTextEdit { /** Comments out all lines in the selection. */ void toggle_selection_comment(QTextCursor &cursor, bool is_comment); + + friend class LineNumberArea; }; #endif // SRCEDITOR_H From 38dd2a420c79bbbd2d4d29abd93a3728733a0b33 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Sat, 28 Oct 2023 00:42:31 +0200 Subject: [PATCH 08/10] GUI: editor reworked --- src/gui/CMakeLists.txt | 3 + .../dialogs/savechanged/savechangeddialog.cpp | 21 +- .../dialogs/savechanged/savechangeddialog.h | 6 +- src/gui/mainwindow/MainWindow.ui | 138 +++--- src/gui/mainwindow/mainwindow.cpp | 430 ++++-------------- src/gui/mainwindow/mainwindow.h | 32 +- src/gui/widgets/hidingtabwidget.cpp | 2 + src/gui/widgets/hidingtabwidget.h | 3 + src/gui/windows/editor/editordock.cpp | 310 +++++++++++++ src/gui/windows/editor/editordock.h | 63 +++ src/gui/windows/editor/editortab.cpp | 53 +++ src/gui/windows/editor/editortab.h | 35 ++ src/gui/windows/editor/linenumberarea.cpp | 9 +- src/gui/windows/editor/linenumberarea.h | 2 + src/gui/windows/editor/srceditor.cpp | 28 +- src/gui/windows/editor/srceditor.h | 14 +- 16 files changed, 684 insertions(+), 465 deletions(-) create mode 100644 src/gui/windows/editor/editordock.cpp create mode 100644 src/gui/windows/editor/editordock.h create mode 100644 src/gui/windows/editor/editortab.cpp create mode 100644 src/gui/windows/editor/editortab.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 11f6746a..817e0fe1 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -28,6 +28,8 @@ set(gui_SOURCES windows/editor/highlighterasm.cpp windows/editor/highlighterc.cpp windows/editor/linenumberarea.cpp + windows/editor/editordock.cpp + windows/editor/editortab.cpp hinttabledelegate.cpp windows/lcd/lcddisplaydock.cpp windows/lcd/lcddisplayview.cpp @@ -69,6 +71,7 @@ set(gui_HEADERS windows/editor/highlighterasm.h windows/editor/highlighterc.h windows/editor/linenumberarea.h + windows/editor/editordock.h hinttabledelegate.h windows/lcd/lcddisplaydock.h windows/lcd/lcddisplayview.h diff --git a/src/gui/dialogs/savechanged/savechangeddialog.cpp b/src/gui/dialogs/savechanged/savechangeddialog.cpp index 69e71b8c..88cdf402 100644 --- a/src/gui/dialogs/savechanged/savechangeddialog.cpp +++ b/src/gui/dialogs/savechanged/savechangeddialog.cpp @@ -8,8 +8,7 @@ #include #include -SaveChnagedDialog::SaveChnagedDialog(QStringList &changedlist, QWidget *parent) - : QDialog(parent) { +SaveChangedDialog::SaveChangedDialog(QStringList &changedlist, QWidget *parent) : QDialog(parent) { setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_ShowModal); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -49,15 +48,9 @@ SaveChnagedDialog::SaveChnagedDialog(QStringList &changedlist, QWidget *parent) auto *ignoreButton = new QPushButton(tr("&Ignore"), parent); auto *saveButton = new QPushButton(tr("&Save"), parent); saveButton->setFocus(); - connect( - cancelButton, &QAbstractButton::clicked, this, - &SaveChnagedDialog::cancel_clicked); - connect( - ignoreButton, &QAbstractButton::clicked, this, - &SaveChnagedDialog::ignore_clicked); - connect( - saveButton, &QAbstractButton::clicked, this, - &SaveChnagedDialog::save_clicked); + connect(cancelButton, &QAbstractButton::clicked, this, &SaveChangedDialog::cancel_clicked); + connect(ignoreButton, &QAbstractButton::clicked, this, &SaveChangedDialog::ignore_clicked); + connect(saveButton, &QAbstractButton::clicked, this, &SaveChangedDialog::save_clicked); hlBtn->addWidget(cancelButton); hlBtn->addStretch(); hlBtn->addWidget(ignoreButton); @@ -69,19 +62,19 @@ SaveChnagedDialog::SaveChnagedDialog(QStringList &changedlist, QWidget *parent) setMinimumSize(400, 300); } -void SaveChnagedDialog::cancel_clicked() { +void SaveChangedDialog::cancel_clicked() { QStringList list; emit user_decision(true, list); close(); } -void SaveChnagedDialog::ignore_clicked() { +void SaveChangedDialog::ignore_clicked() { QStringList list; emit user_decision(false, list); close(); } -void SaveChnagedDialog::save_clicked() { +void SaveChangedDialog::save_clicked() { QStringList list; for (int r = 0; r < model->rowCount(); ++r) { if (model->item(r)->checkState() == Qt::Checked) { diff --git a/src/gui/dialogs/savechanged/savechangeddialog.h b/src/gui/dialogs/savechanged/savechangeddialog.h index 0d4ba7de..32884d5e 100644 --- a/src/gui/dialogs/savechanged/savechangeddialog.h +++ b/src/gui/dialogs/savechanged/savechangeddialog.h @@ -6,13 +6,11 @@ #include #include -class SaveChnagedDialog : public QDialog { +class SaveChangedDialog : public QDialog { Q_OBJECT public: - explicit SaveChnagedDialog( - QStringList &changedlist, - QWidget *parent = nullptr); + explicit SaveChangedDialog(QStringList &changedlist, QWidget *parent = nullptr); signals: void user_decision(bool cancel, QStringList tosavelist); private slots: diff --git a/src/gui/mainwindow/MainWindow.ui b/src/gui/mainwindow/MainWindow.ui index 0a2ace12..948cdd18 100644 --- a/src/gui/mainwindow/MainWindow.ui +++ b/src/gui/mainwindow/MainWindow.ui @@ -14,11 +14,10 @@ MainWindow - - + + :/icons/gui.png - :/icons/gui.png + :/icons/gui.png @@ -55,16 +54,16 @@ 0 0 900 - 22 + 32 - &File + &File - &Examples + &Examples @@ -81,7 +80,7 @@ - &Windows + &Windows @@ -97,7 +96,7 @@ - M&achine + M&achine @@ -118,14 +117,21 @@ - &Help + &Help + + + Options + + + + @@ -167,11 +173,11 @@ - + :/icons/document-import.png:/icons/document-import.png - &New simulation... + &New simulation... Ctrl+N @@ -179,16 +185,16 @@ - R&estart + R&estart - + :/icons/new.png:/icons/new.png - Ne&w source + Ne&w source New source @@ -199,11 +205,11 @@ - + :/icons/open.png:/icons/open.png - &Open source + &Open source Open source @@ -217,11 +223,11 @@ false - + :/icons/save.png:/icons/save.png - &Save source + &Save source Save source @@ -235,7 +241,7 @@ false - Save source &as + Save source &as Save source as @@ -246,11 +252,11 @@ false - + :/icons/closetab.png:/icons/closetab.png - &Close source + &Close source Close source @@ -264,11 +270,11 @@ false - + :/icons/compfile-256.png:/icons/compfile-256.png - &Compile Source + &Compile Source Compile source and update memory @@ -282,11 +288,11 @@ true - + :/icons/build-256.png:/icons/build-256.png - &Build Executable + &Build Executable Build executable by external make @@ -297,11 +303,11 @@ - + :/icons/application-exit.png:/icons/application-exit.png - E&xit + E&xit Exit program @@ -312,11 +318,11 @@ - + :/icons/play.png:/icons/play.png - &Run + &Run Ctrl+R @@ -324,11 +330,11 @@ - + :/icons/next.png:/icons/next.png - &Step + &Step Ctrl+T @@ -336,11 +342,11 @@ - + :/icons/pause.png:/icons/pause.png - &Pause + &Pause Ctrl+P @@ -351,7 +357,7 @@ true - &1 instruction per second + &1 instruction per second 1x @@ -368,7 +374,7 @@ true - &5 instructions per second + &5 instructions per second 5x @@ -385,7 +391,7 @@ true - 1&0 instructions per second + 1&0 instructions per second 10x @@ -402,7 +408,7 @@ true - &Unlimited + &Unlimited Run CPU without any clock constrains @@ -413,7 +419,7 @@ - &Memory + &Memory Data memory view @@ -424,7 +430,7 @@ - &Program + &Program Program memory view @@ -435,7 +441,7 @@ - &Registers + &Registers Ctrl+D @@ -443,7 +449,7 @@ - C&ontrol and Status Registers + C&ontrol and Status Registers Ctrl+I @@ -451,11 +457,11 @@ - + :/icons/reload.png:/icons/reload.png - &Reload simulation + &Reload simulation Ctrl+Shift+R @@ -463,7 +469,7 @@ - &Print + &Print Ctrl+Alt+P @@ -471,7 +477,7 @@ - Pro&gram Cache + Pro&gram Cache Ctrl+Shift+P @@ -479,7 +485,7 @@ - &Data Cache + &Data Cache Ctrl+Shift+M @@ -490,7 +496,7 @@ true - &2 instructions per second + &2 instructions per second 2x @@ -504,7 +510,7 @@ - About Qt&RVSim + About Qt&RVSim @@ -512,7 +518,7 @@ true - &Max + &Max Run at maximal speed, skip visualization for 100 msec @@ -523,27 +529,27 @@ - About &Qt + About &Qt - P&eripherals + P&eripherals - &Terminal + &Terminal - &LCD display + &LCD display - Sh&ow Symbol + Sh&ow Symbol Show Symbol @@ -557,12 +563,12 @@ true - &Core View + &Core View - Me&ssages + Me&ssages Show compile/build messages @@ -576,13 +582,27 @@ false - M&nemonics Registers + M&nemonics Registers + + + + + true + + + true + + + Show &Line Numbers + + + Show line number in the code editor. - + diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index 42122a0b..1223e88e 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -1,6 +1,11 @@ +#include "windows/editor/editordock.h" +#include "windows/editor/editortab.h" + #include #include +#include #include + #ifdef WITH_PRINTING #include #include @@ -30,32 +35,50 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) : QMainWindow(parent) , settings(settings) { - ignore_unsaved = false; machine.reset(); corescene.reset(); - current_srceditor = nullptr; - coreview_shown = true; ui.reset(new Ui::MainWindow()); ui->setupUi(this); setWindowTitle(APP_NAME); setDockNestingEnabled(true); + // Setup central widget + central_widget_tabs.reset(new HidingTabWidget(this)); central_widget_tabs->setTabBarAutoHide(true); this->setCentralWidget(central_widget_tabs.data()); coreview.reset(new GraphicsView(this)); - central_widget_tabs->addTab(coreview.data(), "Core"); + coreview->setWindowTitle("&Core"); + central_widget_tabs->addTab(coreview.data(), coreview->windowTitle()); + + // Setup editor - editor_tabs.reset(new HidingTabWidget(this)); + editor_tabs.reset(new EditorDock(this->settings, central_widget_tabs.data())); editor_tabs->setTabBarAutoHide(true); - editor_tabs->setWindowTitle("Editor"); + editor_tabs->setWindowTitle("&Editor"); connect( - editor_tabs.data(), &HidingTabWidget::requestAddRemoveTab, central_widget_tabs.data(), + editor_tabs.data(), &EditorDock::requestAddRemoveTab, central_widget_tabs.data(), &HidingTabWidget::addRemoveTabRequested); + connect( + editor_tabs.data(), &EditorDock::editor_available_changed, this, [this](bool available) { + ui->actionSave->setEnabled(available); + ui->actionSaveAs->setEnabled(available); + ui->actionClose->setEnabled(available); + ui->actionCompileSource->setEnabled(available); + }); + connect( + ui->actionEditorShowLineNumbers, &QAction::triggered, editor_tabs.data(), + &EditorDock::set_show_line_numbers); + + bool line_numbers_visible = settings->value("EditorShowLineNumbers", true).toBool(); + editor_tabs->set_show_line_numbers(line_numbers_visible); + ui->actionEditorShowLineNumbers->setChecked(line_numbers_visible); + // Create/prepare other widgets + ndialog.reset(new NewDialog(this, settings)); registers.reset(new RegistersDock(this, machine::Xlen::_32)); registers->hide(); @@ -94,11 +117,16 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) connect(ui->actionNewMachine, &QAction::triggered, this, &MainWindow::new_machine); connect(ui->actionReload, &QAction::triggered, this, [this] { machine_reload(false, false); }); connect(ui->actionPrint, &QAction::triggered, this, &MainWindow::print_action); - connect(ui->actionNew, &QAction::triggered, this, &MainWindow::new_source); - connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::open_source); - connect(ui->actionSave, &QAction::triggered, this, &MainWindow::save_source); - connect(ui->actionSaveAs, &QAction::triggered, this, &MainWindow::save_source_as); - connect(ui->actionClose, &QAction::triggered, this, &MainWindow::close_source_check); + + // Editor actions + connect(ui->actionNew, &QAction::triggered, editor_tabs.data(), &EditorDock::create_empty_tab); + connect(ui->actionOpen, &QAction::triggered, editor_tabs.data(), &EditorDock::open_file_dialog); + connect(ui->actionSave, &QAction::triggered, editor_tabs.data(), &EditorDock::save_current_tab); + connect( + ui->actionSaveAs, &QAction::triggered, editor_tabs.data(), + &EditorDock::save_current_tab_as); + connect( + ui->actionClose, &QAction::triggered, editor_tabs.data(), &EditorDock::close_current_tab); connect( ui->actionMnemonicRegisters, &QAction::triggered, this, &MainWindow::view_mnemonics_registers); @@ -136,19 +164,8 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) ui->actionMnemonicRegisters->trigger(); } - // Source editor related actions - connect( - editor_tabs.data(), &HidingTabWidget::currentChanged, this, - &MainWindow::central_tab_changed); - - foreach (QString file_name, settings->value("openSrcFiles").toStringList()) { - if (file_name.isEmpty()) { continue; } - auto *editor = new SrcEditor(); - if (editor->loadFile(file_name)) { - add_src_editor_to_tabs(editor); - } else { - delete (editor); - } + for (const QString &file_name : settings->value("openSrcFiles").toStringList()) { + editor_tabs->open_file(file_name); } QDir samples_dir(":/samples"); @@ -191,7 +208,7 @@ void MainWindow::show_hide_coreview(bool show) { } else { corescene.reset(new CoreViewSceneSimple(machine.data())); } - central_widget_tabs->insertTab(0, coreview.data(), "Core"); + central_widget_tabs->insertTab(0, coreview.data(), coreview->windowTitle()); // Ensures correct zoom. coreview->setScene(corescene.data()); this->setCentralWidget(central_widget_tabs.data()); @@ -436,54 +453,32 @@ void MainWindow::view_mnemonics_registers(bool enable) { void MainWindow::closeEvent(QCloseEvent *event) { settings->setValue("windowGeometry", saveGeometry()); settings->setValue("windowState", saveState()); + settings->setValue("openSrcFiles", editor_tabs->get_open_file_list()); settings->sync(); QStringList list; - if (modified_file_list(list, true) && !ignore_unsaved) { + if (!ignore_unsaved && editor_tabs->get_modified_tab_filenames(list, true)) { event->ignore(); - auto *dialog = new SaveChnagedDialog(list, this); - int id = qMetaTypeId(); - if (!QMetaType::isRegistered(id)) { qRegisterMetaType(); } + auto *dialog = new SaveChangedDialog(list, this); + if (!QMetaType::isRegistered(qMetaTypeId())) { + qRegisterMetaType(); + } connect( - dialog, &SaveChnagedDialog::user_decision, this, &MainWindow::save_exit_or_ignore, + dialog, &SaveChangedDialog::user_decision, this, + [this](bool cancel, const QStringList &tabs_to_save) { + if (cancel) return; + for (const auto &name : tabs_to_save) { + auto tab_id = editor_tabs->find_tab_id_by_filename(name); + if (tab_id.has_value()) editor_tabs->save_tab(tab_id.value()); + } + ignore_unsaved = true; + close(); + }, Qt::QueuedConnection); dialog->open(); } } -void MainWindow::save_exit_or_ignore(bool cancel, const QStringList &tosavelist) { - bool save_unnamed = false; - if (cancel) { return; } - for (const auto &fname : tosavelist) { - SrcEditor *editor = source_editor_for_file(fname, false); - if (editor->saveAsRequired()) { - save_unnamed = true; - } else if (editor != nullptr) { -#ifndef __EMSCRIPTEN__ - editor->saveFile(); -#else - editor_tabs->setCurrentWidget(editor); - central_widget_tabs->setCurrentWidget(editor_tabs.data()); - save_source(); - return; -#endif - } - } - if (save_unnamed && (editor_tabs != nullptr)) { - for (int i = 0; i < editor_tabs->count(); i++) { - QWidget *w = editor_tabs->widget(i); - auto *editor = dynamic_cast(w); - if (editor == nullptr) { continue; } - if (!editor->saveAsRequired()) { continue; } - editor_tabs->setCurrentWidget(editor); - save_source_as(); - return; - } - } - ignore_unsaved = true; - close(); -} - void MainWindow::show_dockwidget(QDockWidget *dw, Qt::DockWidgetArea area) { if (dw == nullptr) { return; } if (dw->isHidden()) { @@ -537,266 +532,15 @@ void MainWindow::machine_trap(machine::SimulatorException &e) { showAsyncCriticalBox(this, "Machine trapped", e.msg(false), e.msg(true)); } -void MainWindow::setCurrentSrcEditor(SrcEditor *srceditor) { - current_srceditor = srceditor; - if (srceditor == nullptr) { - ui->actionSave->setEnabled(false); - ui->actionSaveAs->setEnabled(false); - ui->actionClose->setEnabled(false); - ui->actionCompileSource->setEnabled(false); - } else { - ui->actionSave->setEnabled(true); - ui->actionSaveAs->setEnabled(true); - ui->actionClose->setEnabled(true); - ui->actionCompileSource->setEnabled(true); - } -} - -void MainWindow::tab_widget_destroyed(QObject *obj) { - if (obj == current_srceditor) { setCurrentSrcEditor(nullptr); } -} - -void MainWindow::central_tab_changed(int index) { - QWidget *widget = editor_tabs->widget(index); - auto *srceditor = dynamic_cast(widget); - if (srceditor != nullptr) { setCurrentSrcEditor(srceditor); } -} - -void MainWindow::add_src_editor_to_tabs(SrcEditor *editor) { - editor_tabs->addTab(editor, editor->title()); - editor_tabs->setCurrentWidget(editor); - central_widget_tabs->setCurrentWidget(editor_tabs.data()); - connect(editor, &QObject::destroyed, this, &MainWindow::tab_widget_destroyed); -} - -void MainWindow::update_open_file_list() { - QStringList open_src_files; - if ((editor_tabs == nullptr) || (settings == nullptr)) { return; } - for (int i = 0; i < editor_tabs->count(); i++) { - QWidget *w = editor_tabs->widget(i); - auto *editor = dynamic_cast(w); - if (editor == nullptr) { continue; } - if (editor->filename() == "") { continue; } - open_src_files.append(editor->filename()); - } - settings->setValue("openSrcFiles", open_src_files); -} - -bool MainWindow::modified_file_list(QStringList &list, bool report_unnamed) { - bool ret = false; - list.clear(); - if (editor_tabs == nullptr) { return false; } - for (int i = 0; i < editor_tabs->count(); i++) { - QWidget *w = editor_tabs->widget(i); - auto *editor = dynamic_cast(w); - if (editor == nullptr) { continue; } - if ((editor->filename() == "") && !report_unnamed) { continue; } - if (!editor->isModified()) { continue; } - ret = true; - list.append(editor->filename()); - } - return ret; -} - -static int compare_filenames(const QString &filename1, const QString &filename2) { - QFileInfo fi1(filename1); - QFileInfo fi2(filename2); - QString canon1 = fi1.canonicalFilePath(); - QString canon2 = fi2.canonicalFilePath(); - if (!canon1.isEmpty() && (canon1 == canon2)) { return 2; } - if (filename1 == filename2) { return 1; } - return 0; -} - -SrcEditor *MainWindow::source_editor_for_file(const QString &filename, bool open) { - if (editor_tabs == nullptr) { return nullptr; } - int found_match = 0; - SrcEditor *found_editor = nullptr; - for (int i = 0; i < editor_tabs->count(); i++) { - QWidget *w = editor_tabs->widget(i); - auto *editor = dynamic_cast(w); - if (editor == nullptr) { continue; } - int match = compare_filenames(filename, editor->filename()); - if ((match > found_match) || ((editor == current_srceditor) && (match >= found_match))) { - found_editor = editor; - found_match = match; - } - } - if (found_match > 0) { return found_editor; } - - if (!open) { return nullptr; } - - auto *editor = new SrcEditor(); - if (!editor->loadFile(filename)) { - delete editor; - return nullptr; - } - add_src_editor_to_tabs(editor); - update_open_file_list(); - return editor; -} - -void MainWindow::new_source() { - auto *editor = new SrcEditor(); - add_src_editor_to_tabs(editor); - update_open_file_list(); -} - -void MainWindow::open_source() { -#ifndef __EMSCRIPTEN__ - QString file_name = QFileDialog::getOpenFileName( - this, tr("Open File"), "", "Source Files (*.asm *.S *.s *.c Makefile)"); - - if (!file_name.isEmpty()) { - SrcEditor *editor = source_editor_for_file(file_name, false); - if (editor != nullptr) { - if (editor_tabs != nullptr) { editor_tabs->setCurrentWidget(editor); } - return; - } - editor = new SrcEditor(); - - if (editor->loadFile(file_name)) { - add_src_editor_to_tabs(editor); - } else { - showAsyncCriticalBox( - this, "Simulator Error", tr("Cannot open file '%1' for reading.").arg(file_name)); - delete (editor); - } - } -#else - QHtml5File::load("*", [&](const QByteArray &content, const QString &filename) { - SrcEditor *editor = new SrcEditor(); - editor->loadByteArray(content, filename); - add_src_editor_to_tabs(editor); - }); -#endif - update_open_file_list(); -} - -void MainWindow::save_source_as() { - if (current_srceditor == nullptr) { return; } -#ifndef __EMSCRIPTEN__ - QFileDialog fileDialog(this, tr("Save as...")); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setDefaultSuffix("s"); - if (fileDialog.exec() != QDialog::Accepted) { return; } - const QString fn = fileDialog.selectedFiles().first(); - if (!current_srceditor->saveFile(fn)) { - showAsyncCriticalBox(this, "Simulator Error", tr("Cannot save file '%1'.").arg(fn)); - return; - } - int idx = editor_tabs->indexOf(current_srceditor); - if (idx >= 0) { editor_tabs->setTabText(idx, current_srceditor->title()); } - update_open_file_list(); -#else - QString filename = current_srceditor->filename(); - if (filename.isEmpty()) filename = "unknown.s"; - QInputDialog *dialog = new QInputDialog(this); - dialog->setWindowTitle("Select file name"); - dialog->setLabelText("File name:"); - dialog->setTextValue(filename); - dialog->setMinimumSize(QSize(200, 100)); - dialog->setAttribute(Qt::WA_DeleteOnClose); - connect( - dialog, &QInputDialog::textValueSelected, this, &MainWindow::src_editor_save_to, - Qt::QueuedConnection); - dialog->open(); -#endif -} - -void MainWindow::src_editor_save_to(const QString &filename) { - if (filename.isEmpty() || (current_srceditor == nullptr)) { return; } - current_srceditor->setFileName(filename); - if (!current_srceditor->filename().isEmpty()) { save_source(); } - int idx = editor_tabs->indexOf(current_srceditor); - if (idx >= 0) { editor_tabs->setTabText(idx, current_srceditor->title()); } - update_open_file_list(); -} - -void MainWindow::save_source() { - if (current_srceditor == nullptr) { return; } - if (current_srceditor->saveAsRequired()) { return save_source_as(); } -#ifndef __EMSCRIPTEN__ - if (!current_srceditor->saveFile()) { - showAsyncCriticalBox( - this, "Simulator Error", - tr("Cannot save file '%1'.").arg(current_srceditor->filename())); - } -#else - QHtml5File::save( - current_srceditor->document()->toPlainText().toUtf8(), current_srceditor->filename()); - current_srceditor->setModified(false); -#endif -} - -void MainWindow::close_source_check() { - if (current_srceditor == nullptr) { return; } - SrcEditor *editor = current_srceditor; - if (!editor->isModified()) { - close_source(); - return; - } - auto *msgbox = new QMessageBox(this); - msgbox->setWindowTitle("Close unsaved source"); - msgbox->setText("Close unsaved source."); - msgbox->setInformativeText("Do you want to save your changes?"); - msgbox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgbox->setDefaultButton(QMessageBox::Save); - msgbox->setMinimumSize(QSize(200, 150)); - msgbox->setAttribute(Qt::WA_DeleteOnClose); - connect( - msgbox, &QDialog::finished, this, &MainWindow::close_source_decided, Qt::QueuedConnection); - msgbox->open(); -} - -void MainWindow::close_source_decided(int result) { - if (current_srceditor == nullptr) { return; } - SrcEditor *editor = current_srceditor; - if (result == QMessageBox::Save) { - if (editor->saveAsRequired()) { - save_source_as(); - return; - } - save_source(); - } else if (result != QMessageBox::Discard) { - return; - } - close_source(); -} - -void MainWindow::close_source() { - if (current_srceditor == nullptr) { return; } - SrcEditor *editor = current_srceditor; - setCurrentSrcEditor(nullptr); - int idx = editor_tabs->indexOf(editor); - if (idx >= 0) { editor_tabs->removeTab(idx); } - delete editor; - update_open_file_list(); -} - void MainWindow::close_source_by_name(QString &filename, bool ask) { - SrcEditor *editor = source_editor_for_file(filename, false); - if (editor == nullptr) return; - setCurrentSrcEditor(editor); - if (ask) { - close_source_check(); - } else { - close_source(); - } + editor_tabs->close_tab_by_name(filename, ask); } void MainWindow::example_source(const QString &source_file) { - auto *editor = new SrcEditor(); - - if (editor->loadFile(source_file)) { - editor->setSaveAsRequired(true); - add_src_editor_to_tabs(editor); - update_open_file_list(); - } else { + if (!editor_tabs->open_file(source_file, true)) { showAsyncCriticalBox( this, "Simulator Error", tr("Cannot open example file '%1' for reading.").arg(source_file)); - delete (editor); } } @@ -813,7 +557,7 @@ void MainWindow::message_selected( (void)hint; if (file.isEmpty()) { return; } - SrcEditor *editor = source_editor_for_file(file, true); + SrcEditor *editor = editor_tabs->open_file_if_not_open(file, true)->get_editor(); if (editor == nullptr) { return; } editor->setCursorToLine(line); editor->setFocus(); @@ -821,7 +565,7 @@ void MainWindow::message_selected( } bool SimpleAsmWithEditorCheck::process_file(const QString &filename, QString *error_ptr) { - SrcEditor *editor = mainwindow->source_editor_for_file(filename, false); + SrcEditor *editor = mainwindow->editor_tabs->find_tab_by_filename(filename)->get_editor(); if (editor == nullptr) { return Super::process_file(filename, error_ptr); } QTextDocument *doc = editor->document(); int ln = 1; @@ -912,7 +656,6 @@ bool SimpleAsmWithEditorCheck::process_pragma( void MainWindow::compile_source() { bool error_occured = false; - if (current_srceditor == nullptr) { return; } if (machine != nullptr) { if (machine->config().reset_at_compile()) { machine_reload(true); } } @@ -927,11 +670,12 @@ void MainWindow::compile_source() { this, "Simulator Error", tr("No physical addresspace to store program.")); return; } - QString filename = current_srceditor->filename(); machine->cache_sync(); - SrcEditor *editor = current_srceditor; - QTextDocument *doc = editor->document(); + + auto editor = editor_tabs->get_current_editor(); + auto filename = editor->filename(); + auto content = editor->document(); emit clear_messages(); SimpleAsmWithEditorCheck sasm(this); @@ -941,7 +685,7 @@ void MainWindow::compile_source() { sasm.setup(mem, &symtab, machine::Address(0x00000200), machine->core()->get_xlen()); int ln = 1; - for (QTextBlock block = doc->begin(); block.isValid(); block = block.next(), ln++) { + for (QTextBlock block = content->begin(); block.isValid(); block = block.next(), ln++) { QString line = block.text(); if (!sasm.process_line(line, filename, ln)) { error_occured = true; } } @@ -952,10 +696,10 @@ void MainWindow::compile_source() { void MainWindow::build_execute() { QStringList list; - if (modified_file_list(list)) { - auto *dialog = new SaveChnagedDialog(list, this); + if (editor_tabs->get_modified_tab_filenames(list, false)) { + auto *dialog = new SaveChangedDialog(list, this); connect( - dialog, &SaveChnagedDialog::user_decision, this, &MainWindow::build_execute_with_save); + dialog, &SaveChangedDialog::user_decision, this, &MainWindow::build_execute_with_save); dialog->open(); } else { build_execute_no_check(); @@ -966,9 +710,8 @@ void MainWindow::build_execute_with_save( bool cancel, QStringList tosavelist) { // NOLINT(performance-unnecessary-value-param) if (cancel) { return; } - for (const auto &fname : tosavelist) { - SrcEditor *editor = source_editor_for_file(fname, false); - editor->saveFile(); + for (const auto &filename : tosavelist) { + editor_tabs->find_tab_by_filename(filename)->get_editor()->saveFile(); } build_execute_no_check(); } @@ -989,12 +732,17 @@ void MainWindow::build_execute_no_check() { connect(proc, &ExtProcess::report_message, this, &MainWindow::report_message); connect( proc, QOverload::of(&QProcess::finished), this, - &MainWindow::build_execute_finished); - if (current_srceditor != nullptr) { - if (!current_srceditor->filename().isEmpty()) { - QFileInfo fi(current_srceditor->filename()); - work_dir = fi.dir().path(); - } + [this](int exitCode, QProcess::ExitStatus exitStatus) { + if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { return; } + + if (machine != nullptr) { + if (machine->config().reset_at_compile()) { machine_reload(true, true); } + } + }); + auto current_srceditor = editor_tabs->get_current_editor(); + if (current_srceditor != nullptr && !current_srceditor->filename().isEmpty()) { + QFileInfo fi(current_srceditor->filename()); + work_dir = fi.dir().path(); } if (work_dir.isEmpty() && (machine != nullptr)) { if (!machine->config().elf().isEmpty()) { @@ -1005,12 +753,4 @@ void MainWindow::build_execute_no_check() { if (!work_dir.isEmpty()) { proc->setWorkingDirectory(work_dir); } // API without args has been deprecated. proc->start("make", {}, QProcess::Unbuffered | QProcess::ReadOnly); -} - -void MainWindow::build_execute_finished(int exitCode, QProcess::ExitStatus exitStatus) { - if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { return; } - - if (machine != nullptr) { - if (machine->config().reset_at_compile()) { machine_reload(true, true); } - } -} +} \ No newline at end of file diff --git a/src/gui/mainwindow/mainwindow.h b/src/gui/mainwindow/mainwindow.h index 0a9a4fa3..bee97a7a 100644 --- a/src/gui/mainwindow/mainwindow.h +++ b/src/gui/mainwindow/mainwindow.h @@ -11,6 +11,8 @@ #include "widgets/hidingtabwidget.h" #include "windows/cache/cachedock.h" #include "windows/csr/csrdock.h" +#include "windows/editor/editordock.h" +#include "windows/editor/editortab.h" #include "windows/editor/srceditor.h" #include "windows/lcd/lcddisplaydock.h" #include "windows/memory/memorydock.h" @@ -59,13 +61,6 @@ public slots: bool force_memory_reset = false, bool force_elf_load = false); void print_action(); - void new_source(); - void open_source(); - void save_source(); - void save_source_as(); - void close_source(); - void close_source_check(); - void close_source_decided(int result); void close_source_by_name(QString &filename, bool ask = false); void example_source(const QString &source_file); void compile_source(); @@ -93,8 +88,6 @@ public slots: void machine_status(enum machine::Machine::Status st); void machine_exit(); void machine_trap(machine::SimulatorException &e); - void central_tab_changed(int index); - void tab_widget_destroyed(QObject *obj); void view_mnemonics_registers(bool enable); void message_selected( messagetype::Type type, @@ -103,22 +96,16 @@ public slots: int column, const QString &text, const QString &hint); - void save_exit_or_ignore(bool cancel, const QStringList &tosavelist); protected: - void closeEvent(QCloseEvent *event) override; - void setCurrentSrcEditor(SrcEditor *srceditor); - -protected slots: - void src_editor_save_to(const QString &filename); - void build_execute_finished(int exitCode, QProcess::ExitStatus exitStatus); + void closeEvent(QCloseEvent *cancel) override; private: Box ui {}; Box ndialog {}; Box central_widget_tabs {}; - Box editor_tabs {}; + Box editor_tabs {}; Box coreview {}; Box corescene; @@ -132,22 +119,17 @@ protected slots: Box lcd_display {}; CsrDock *csrdock {}; MessagesDock *messages {}; - bool coreview_shown; - SrcEditor *current_srceditor; + bool coreview_shown = true; QActionGroup *speed_group {}; - Box settings; + QSharedPointer settings; Box machine; // Current simulated machine void show_dockwidget(QDockWidget *w, Qt::DockWidgetArea area = Qt::RightDockWidgetArea); - void add_src_editor_to_tabs(SrcEditor *editor); - void update_open_file_list(); - bool modified_file_list(QStringList &list, bool report_unnamed = false); - SrcEditor *source_editor_for_file(const QString &filename, bool open); QPointer build_process; - bool ignore_unsaved; + bool ignore_unsaved = false; }; class SimpleAsmWithEditorCheck : public SimpleAsm { diff --git a/src/gui/widgets/hidingtabwidget.cpp b/src/gui/widgets/hidingtabwidget.cpp index eb770b31..d2fc0876 100644 --- a/src/gui/widgets/hidingtabwidget.cpp +++ b/src/gui/widgets/hidingtabwidget.cpp @@ -6,6 +6,7 @@ void HidingTabWidget::tabInserted(int index) { show(); requestAddRemoveTab(this, true); } + tabCountChanged(); } void HidingTabWidget::tabRemoved(int index) { QTabWidget::tabRemoved(index); @@ -13,6 +14,7 @@ void HidingTabWidget::tabRemoved(int index) { hide(); requestAddRemoveTab(this, false); } + tabCountChanged(); } void HidingTabWidget::addRemoveTabRequested(QWidget *tab, bool exists) { if (!exists) { diff --git a/src/gui/widgets/hidingtabwidget.h b/src/gui/widgets/hidingtabwidget.h index af0e84db..297e90f7 100644 --- a/src/gui/widgets/hidingtabwidget.h +++ b/src/gui/widgets/hidingtabwidget.h @@ -20,6 +20,9 @@ class HidingTabWidget : public QTabWidget { public slots: void addRemoveTabRequested(QWidget *tab, bool exists); + +protected: + virtual void tabCountChanged() {}; }; #endif // HIDINGTABWIDGET_H diff --git a/src/gui/windows/editor/editordock.cpp b/src/gui/windows/editor/editordock.cpp new file mode 100644 index 00000000..bbf9f545 --- /dev/null +++ b/src/gui/windows/editor/editordock.cpp @@ -0,0 +1,310 @@ +#include "editordock.h" + +#include "common/logging.h" +#include "dialogs/savechanged/savechangeddialog.h" +#include "editortab.h" +#include "helper/async_modal.h" + +#include +#include +#include +#include +#include +#include + +LOG_CATEGORY("gui.editordock"); + +#ifdef __EMSCRIPTEN__ + #include "qhtml5file.h" +#endif + +int compare_filenames(const QString &filename1, const QString &filename2) { + QFileInfo fi1(filename1); + QFileInfo fi2(filename2); + QString canon1 = fi1.canonicalFilePath(); + QString canon2 = fi2.canonicalFilePath(); + if (!canon1.isEmpty() && (canon1 == canon2)) { return 2; } + if (filename1 == filename2) { return 1; } + return 0; +} + +EditorDock::EditorDock(QSharedPointer settings, QTabWidget *parent_tabs, QWidget *parent) + : Super(parent) + , settings(std::move(settings)) { + { + auto bar = tabBar(); + bar->setMovable(true); + QFont font = bar->font(); + font.setPointSize(10); + font.setBold(false); + bar->setFont(font); + } + + setTabsClosable(true); + connect(this, &EditorDock::tabCloseRequested, this, [this](int index) { close_tab(index); }); + + connect( + this, &EditorDock::currentChanged, parent_tabs, + [this, parent_tabs](int index) { + // Update parent title + if (count() == 0) return; + auto *editor = get_tab(index)->get_editor(); + QString title = QString("&Editor (%1)").arg(editor->title()); + parent_tabs->setTabText(parent_tabs->indexOf(this), title); + // IMPORTANT: This repeated call solved a very annoying QT resize bug. Do not remove it! + parent_tabs->setTabText(parent_tabs->indexOf(this), title); + parent_tabs->setCurrentIndex(parent_tabs->indexOf(this)); + }, + Qt::QueuedConnection); +} + +EditorTab *EditorDock::get_tab(int index) const { + return dynamic_cast(widget(index)); +} + +EditorTab *EditorDock::open_file(const QString &filename, bool save_as_required) { + auto tab = new EditorTab(line_numbers_visible, this); + if (tab->get_editor()->loadFile(filename)) { + addTab(tab, tab->title()); + setCurrentWidget(tab); + tab->get_editor()->setSaveAsRequired(save_as_required); + return tab; + } else { + delete tab; + return nullptr; + } +} + +EditorTab *EditorDock::open_file_if_not_open(const QString &filename, bool save_as_required) { + auto tab = find_tab_by_filename(filename); + if (tab == nullptr) { + return open_file(filename, save_as_required); + } else { + setCurrentWidget(tab); + return tab; + } +} + +EditorTab *EditorDock::create_empty_tab() { + auto tab = new EditorTab(line_numbers_visible, this); + addTab(tab, tab->title()); + setCurrentWidget(tab); + return tab; +} + +std::optional EditorDock::find_tab_id_by_filename(const QString &filename) const { + int best_match = 0; + int best_match_index = -1; + for (int i = 0; i < this->count(); i++) { + auto *editor = get_tab(i)->get_editor(); + int match = compare_filenames(filename, editor->filename()); + if (match == 2) { return i; } + if (match > best_match) { + best_match = match; + best_match_index = i; + } + } + if (best_match_index >= 0) { return best_match_index; } + return std::nullopt; +} + +EditorTab *EditorDock::find_tab_by_filename(const QString &filename) const { + auto index = find_tab_id_by_filename(filename); + if (index.has_value()) { + return get_tab(index.value()); + } else { + return nullptr; + } +} + +SrcEditor *EditorDock::get_current_editor() const { + if (count() == 0) return nullptr; + return get_tab(currentIndex())->get_editor(); +} + +QStringList EditorDock::get_open_file_list() const { + QStringList open_src_files; + for (int i = 0; i < this->count(); i++) { + auto *editor = get_tab(i)->get_editor(); + if (editor->filename().isEmpty()) { continue; } + open_src_files.append(editor->filename()); + } + return open_src_files; +} + +bool EditorDock::get_modified_tab_filenames(QStringList &output, bool report_unnamed) const { + output.clear(); + for (int i = 0; i < this->count(); i++) { + auto editor = get_tab(i)->get_editor(); + if (editor->filename().isEmpty() && !report_unnamed) { continue; } + if (!editor->isModified()) { continue; } + output.append(editor->filename()); + } + return !output.empty(); +} + +void EditorDock::set_show_line_numbers(bool visible) { + line_numbers_visible = visible; + settings->setValue("editorShowLineNumbers", visible); + for (int i = 0; i < this->count(); i++) { + get_tab(i)->set_show_line_number(visible); + } +} + +void EditorDock::tabCountChanged() { + Super::tabCountChanged(); + emit editor_available_changed(count() > 0); +} + +void EditorDock::open_file_dialog() { +#ifndef __EMSCRIPTEN__ + QString file_name = QFileDialog::getOpenFileName( + this, tr("Open File"), "", "Source Files (*.asm *.S *.s *.c Makefile)"); + + if (file_name.isEmpty()) { return; } + + auto tab_id = find_tab_id_by_filename(file_name); + if (tab_id.has_value()) { + setCurrentIndex(tab_id.value()); + return; + } + + if (!open_file(file_name)) { + showAsyncCriticalBox( + this, "Simulator Error", tr("Cannot open file '%1' for reading.").arg(file_name)); + } +#else + QHtml5File::load("*", [&](const QByteArray &content, const QString &filename) { + auto tab = create_empty_tab(); + tab->get_editor()->loadByteArray(content, filename); + setTabText(indexOf(tab), tab->get_editor()->title()); + }); +#endif +} + +void EditorDock::save_tab(int index) { + auto editor = get_tab(index)->get_editor(); + if (editor->saveAsRequired()) { return save_tab_as(index); } +#ifndef __EMSCRIPTEN__ + if (!editor->saveFile()) { + showAsyncCriticalBox( + this, "Simulator Error", tr("Cannot save file '%1'.").arg(editor->filename())); + } +#else + QHtml5File::save(editor->document()->toPlainText().toUtf8(), editor->filename()); + editor->setModified(false); +#endif +} + +void EditorDock::save_current_tab() { + if (count() == 0) return; + save_tab(currentIndex()); +} + +void EditorDock::save_tab_as(int index) { +#ifndef __EMSCRIPTEN__ + QFileDialog fileDialog(this, tr("Save as...")); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setDefaultSuffix("s"); + if (fileDialog.exec() != QDialog::Accepted) { return; } + const QString fn = fileDialog.selectedFiles().first(); + auto tab = get_tab(index); + if (!tab->get_editor()->saveFile(fn)) { + showAsyncCriticalBox(this, "Simulator Error", tr("Cannot save file '%1'.").arg(fn)); + return; + } + setTabText(index, tab->get_editor()->title()); + emit currentChanged(index); +#else + QString filename = get_tab(index)->get_editor()->filename(); + if (filename.isEmpty()) filename = "unknown.s"; + auto *dialog = new QInputDialog(this); + dialog->setWindowTitle("Select file name"); + dialog->setLabelText("File name:"); + dialog->setTextValue(filename); + dialog->setMinimumSize(QSize(200, 100)); + dialog->setAttribute(Qt::WA_DeleteOnClose); + connect( + dialog, &QInputDialog::textValueSelected, this, + [this, index](const QString &filename) { save_tab_to(index, filename); }, + Qt::QueuedConnection); + dialog->open(); +#endif +} + +void EditorDock::save_current_tab_as() { + if (count() == 0) return; + save_tab_as(currentIndex()); +} + +void EditorDock::save_tab_to(int index, const QString &filename) { + if (filename.isEmpty()) { + WARN("Cannot save file '%s'.", filename.toStdString().c_str()); + return; + } + + auto editor = get_tab(index)->get_editor(); + if (filename.isEmpty() || (editor == nullptr)) { return; } + editor->setFileName(filename); + if (!editor->filename().isEmpty()) { save_current_tab(); } +} + +void EditorDock::save_current_tab_to(const QString &filename) { + if (count() == 0) return; + save_tab_to(currentIndex(), filename); +} + +void EditorDock::close_tab(int index) { + auto editor = get_tab(index)->get_editor(); + if (!editor->isModified()) { + close_tab_unchecked(index); + } else { + confirm_close_tab_dialog(index); + } +} + +void EditorDock::close_current_tab() { + if (count() == 0) return; + close_tab(currentIndex()); +} + +void EditorDock::close_tab_by_name(QString &filename, bool ask) { + auto *tab = find_tab_by_filename(filename); + if (tab == nullptr) { + WARN("Cannot find tab for file '%s'. Unable to close it.", filename.toStdString().c_str()); + return; + } + if (!ask) { + close_tab(indexOf(tab)); + } else { + confirm_close_tab_dialog(indexOf(tab)); + } +} +void EditorDock::close_tab_unchecked(int index) { + auto *tab = get_tab(index); + removeTab(index); + delete tab; +} + +void EditorDock::confirm_close_tab_dialog(int index) { + auto *msgbox = new QMessageBox(this); + msgbox->setWindowTitle("Close unsaved source"); + msgbox->setText("Close unsaved source."); + msgbox->setInformativeText("Do you want to save your changes?"); + msgbox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgbox->setDefaultButton(QMessageBox::Save); + msgbox->setMinimumSize(QSize(200, 150)); + msgbox->setAttribute(Qt::WA_DeleteOnClose); + connect( + msgbox, &QDialog::finished, this, + [this, index](int result) { + if (result == QMessageBox::Save) { + save_tab(index); + close_tab_unchecked(index); + } else if (result == QMessageBox::Discard) { + close_tab_unchecked(index); + } + }, + Qt::QueuedConnection); + msgbox->open(); +} diff --git a/src/gui/windows/editor/editordock.h b/src/gui/windows/editor/editordock.h new file mode 100644 index 00000000..2bd449eb --- /dev/null +++ b/src/gui/windows/editor/editordock.h @@ -0,0 +1,63 @@ +#ifndef EDITORDOCK_H +#define EDITORDOCK_H + +#include "common/memory_ownership.h" +#include "editortab.h" +#include "widgets/hidingtabwidget.h" + +#include +#include + +class EditorDock : public HidingTabWidget { + Q_OBJECT + using Super = HidingTabWidget; + +public: + /** + * @param parent_tabs used to update parent tab based on current state + */ + explicit EditorDock( + QSharedPointer settings, + QTabWidget *parent_tabs, + QWidget *parent = nullptr); + BORROWED [[nodiscard]] EditorTab *get_tab(int index) const; + BORROWED EditorTab *create_empty_tab(); + BORROWED EditorTab *open_file(const QString &filename, bool save_as_required = false); + BORROWED EditorTab * + open_file_if_not_open(const QString &filename, bool save_as_required = false); + BORROWED [[nodiscard]] std::optional find_tab_id_by_filename(const QString &filename) const; + BORROWED [[nodiscard]] EditorTab *find_tab_by_filename(const QString &filename) const; + BORROWED [[nodiscard]] SrcEditor *get_current_editor() const; + [[nodiscard]] QStringList get_open_file_list() const; + bool get_modified_tab_filenames(QStringList &output, bool report_unnamed = false) const; + +protected: + void tabCountChanged() override; + +signals: + void editor_available_changed(bool available); + +public slots: + void set_show_line_numbers(bool visible); + + void open_file_dialog(); + void save_tab(int index); + void save_current_tab(); + void save_tab_as(int index); + void save_current_tab_as(); + void save_tab_to(int index, const QString &filename); + void save_current_tab_to(const QString &filename); + void close_tab(int index); + void close_current_tab(); + void close_tab_by_name(QString &filename, bool ask = false); + +private: + void close_tab_unchecked(int index); + void confirm_close_tab_dialog(int index); + +private: + QSharedPointer settings; + bool line_numbers_visible = true; +}; + +#endif // EDITORDOCK_H \ No newline at end of file diff --git a/src/gui/windows/editor/editortab.cpp b/src/gui/windows/editor/editortab.cpp new file mode 100644 index 00000000..c20d103e --- /dev/null +++ b/src/gui/windows/editor/editortab.cpp @@ -0,0 +1,53 @@ +#include "editortab.h" + +#include "srceditor.h" + +#include +#include + +EditorTab::EditorTab(bool show_line_numbers, QWidget *parent) + : Super(parent) + , layout(new QVBoxLayout(this)) + , editor(new SrcEditor(this)) + , status_bar_layout(new QHBoxLayout()) + , status_bar_location(new QLabel("Unknown", this)) + , status_bar_line(new QLabel("0:0", this)) { + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(reinterpret_cast(editor)); + status_bar_layout->setSpacing(0); + status_bar_layout->setContentsMargins(5, 0, 5, 0); + status_bar_layout->addWidget(status_bar_location); + status_bar_layout->addWidget(status_bar_line); + layout->addLayout(status_bar_layout); + status_bar_location->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + status_bar_line->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + connect(editor, &SrcEditor::cursorPositionChanged, this, [this]() { + auto cursor = editor->textCursor(); + status_bar_line->setText( + QString("%1:%2").arg(cursor.blockNumber() + 1).arg(cursor.columnNumber() + 1)); + }); + connect(editor, &SrcEditor::file_name_change, this, [this]() { elide_file_name(); }); + set_show_line_number(show_line_numbers); +} + +QString EditorTab::title() { + return editor->title(); +} + +void EditorTab::set_show_line_number(bool visible) { + editor->setShowLineNumbers(visible); +} + +void EditorTab::resizeEvent(QResizeEvent *event) { + QWidget::resizeEvent(event); + elide_file_name(); +} + +void EditorTab::elide_file_name() { + auto filename = editor->filename().isEmpty() ? "Unknown" : editor->filename(); + QFontMetrics matrix(status_bar_location->font()); + int width = status_bar_location->width(); + QString clippedText = matrix.elidedText(filename, Qt::ElideMiddle, width); + status_bar_location->setText(clippedText); +} diff --git a/src/gui/windows/editor/editortab.h b/src/gui/windows/editor/editortab.h new file mode 100644 index 00000000..51250cbb --- /dev/null +++ b/src/gui/windows/editor/editortab.h @@ -0,0 +1,35 @@ +#ifndef EDITORTAB_H +#define EDITORTAB_H + +#include "common/memory_ownership.h" +#include "srceditor.h" + +#include +#include +#include + +class EditorTab : public QWidget { + Q_OBJECT + using Super = QWidget; + +public: + explicit EditorTab(bool show_line_numbers, QWidget *parent = nullptr); + [[nodiscard]] SrcEditor *get_editor() const { return editor; } + QString title(); + +public slots: + void set_show_line_number(bool visible); + +protected: + void resizeEvent(QResizeEvent *event) override; + void elide_file_name(); + +private: + QT_OWNED QVBoxLayout *layout; + QT_OWNED SrcEditor *editor; + QT_OWNED QHBoxLayout *status_bar_layout; + QT_OWNED QLabel *status_bar_location; + QT_OWNED QLabel *status_bar_line; +}; + +#endif // EDITORTAB_H \ No newline at end of file diff --git a/src/gui/windows/editor/linenumberarea.cpp b/src/gui/windows/editor/linenumberarea.cpp index 96111cdb..fa17b7ed 100644 --- a/src/gui/windows/editor/linenumberarea.cpp +++ b/src/gui/windows/editor/linenumberarea.cpp @@ -2,16 +2,19 @@ #include "srceditor.h" +#include #include #include -constexpr int RIGHT_MARGIN = 10; +constexpr int RIGHT_MARGIN = 5; constexpr int RIGHT_PADDING = 5; constexpr int LEFT_PADDING = 5; LineNumberArea::LineNumberArea(SrcEditor *editor_) : QWidget(editor_), editor(editor_) {} QSize LineNumberArea::sizeHint() const { + if (!line_numbers_visible) { return { 0, 0 }; } + int digits = std::log10(std::max(1, editor->blockCount())) + 2; int space = editor->fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits + LEFT_PADDING + RIGHT_PADDING + RIGHT_MARGIN; @@ -44,3 +47,7 @@ void LineNumberArea::paintEvent(QPaintEvent *event) { ++blockNumber; } } +void LineNumberArea::set(bool visible) { + QWidget::setVisible(visible); + line_numbers_visible = visible; +} diff --git a/src/gui/windows/editor/linenumberarea.h b/src/gui/windows/editor/linenumberarea.h index b868e2c1..41e4708d 100644 --- a/src/gui/windows/editor/linenumberarea.h +++ b/src/gui/windows/editor/linenumberarea.h @@ -10,6 +10,7 @@ class SrcEditor; class LineNumberArea : public QWidget { public: explicit LineNumberArea(BORROWED SrcEditor *editor_); + void set(bool visible); [[nodiscard]] QSize sizeHint() const override; @@ -18,6 +19,7 @@ class LineNumberArea : public QWidget { private: BORROWED SrcEditor *editor; + bool line_numbers_visible = false; }; #endif // LINENUMBERAREA_H \ No newline at end of file diff --git a/src/gui/windows/editor/srceditor.cpp b/src/gui/windows/editor/srceditor.cpp index 2d93d645..45ed2146 100644 --- a/src/gui/windows/editor/srceditor.cpp +++ b/src/gui/windows/editor/srceditor.cpp @@ -1,30 +1,26 @@ #include "srceditor.h" #include "common/logging.h" +#include "editordock.h" +#include "editortab.h" #include "linenumberarea.h" #include "windows/editor/highlighterasm.h" #include "windows/editor/highlighterc.h" +#include #include #include #include -#include #include #include #include -#include #include #include #include -#include LOG_CATEGORY("gui.src_editor"); -SrcEditor::SrcEditor(QWidget *parent) : SrcEditor("", parent) {} - -SrcEditor::SrcEditor(const QString &text, QWidget *parent) - : Super(text, parent) - , line_number_area(new LineNumberArea(this)) { +SrcEditor::SrcEditor(QWidget *parent) : Super(parent), line_number_area(new LineNumberArea(this)) { QFont font1; saveAsRequiredFl = true; font1.setFamily("Courier"); @@ -49,7 +45,7 @@ SrcEditor::SrcEditor(const QString &text, QWidget *parent) updateMargins(0); } -QString SrcEditor::filename() { +QString SrcEditor::filename() const { return fname; } @@ -69,6 +65,8 @@ void SrcEditor::setFileName(const QString &filename) { } else { highlighter.reset(new HighlighterAsm(document())); } + + emit file_name_change(); } bool SrcEditor::loadFile(const QString &filename) { @@ -184,7 +182,7 @@ void SrcEditor::indent_selection(QTextCursor &cursor) { cursor.movePosition(QTextCursor::StartOfLine); while (cursor.position() < end) { cursor.insertText("\t"); - cursor.movePosition(QTextCursor::Down); + if (!cursor.movePosition(QTextCursor::Down)) break; } cursor.endEditBlock(); } @@ -208,7 +206,7 @@ void SrcEditor::unindent_selection(QTextCursor &cursor) { to_delete--; } } - cursor.movePosition(QTextCursor::Down); + if (!cursor.movePosition(QTextCursor::Down)) break; } cursor.endEditBlock(); } @@ -224,7 +222,7 @@ bool SrcEditor::is_selection_comment() { all_commented = false; break; } - cursor.movePosition(QTextCursor::Down); + if (!cursor.movePosition(QTextCursor::Down)) break; } return all_commented; @@ -242,7 +240,7 @@ void SrcEditor::toggle_selection_comment(QTextCursor &cursor, bool is_comment) { } else { cursor.insertText("//"); } - cursor.movePosition(QTextCursor::Down); + if (!cursor.movePosition(QTextCursor::Down)) break; } cursor.endEditBlock(); } @@ -268,3 +266,7 @@ void SrcEditor::resizeEvent(QResizeEvent *event) { line_number_area->setGeometry( QRect(cr.left(), cr.top(), line_number_area->sizeHint().width(), cr.height())); } +void SrcEditor::setShowLineNumbers(bool show) { + line_number_area->set(show); + updateMargins(0); +} diff --git a/src/gui/windows/editor/srceditor.h b/src/gui/windows/editor/srceditor.h index ac602248..57264843 100644 --- a/src/gui/windows/editor/srceditor.h +++ b/src/gui/windows/editor/srceditor.h @@ -16,9 +16,8 @@ class SrcEditor : public QPlainTextEdit { using Super = QPlainTextEdit; public: - explicit SrcEditor(const QString &text, QWidget *parent = nullptr); - explicit SrcEditor(QWidget *parent = nullptr); - QString filename(); + explicit SrcEditor(QWidget *parent); + QString filename() const; QString title(); bool loadFile(const QString &filename); bool saveFile(QString filename = ""); @@ -34,13 +33,20 @@ class SrcEditor : public QPlainTextEdit { void keyPressEvent(QKeyEvent *event) override; void resizeEvent(QResizeEvent *event) override; +signals: + void file_name_change(); + +public slots: + void setShowLineNumbers(bool visible); + private slots: void updateMargins(int newBlockCount); void updateLineNumberArea(const QRect &rect, int dy); private: ::Box highlighter {}; - QWidget *line_number_area; + LineNumberArea *line_number_area; + bool line_numbers_visible = true; QString fname; QString tname; bool saveAsRequiredFl {}; From 0f088c40a8847c9981ebf3a6803f4b66c9fe453d Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Fri, 27 Oct 2023 23:12:32 +0200 Subject: [PATCH 09/10] GUI: highlight error in the editor on message click --- src/gui/mainwindow/mainwindow.cpp | 16 +++++++++++++--- src/gui/windows/editor/srceditor.cpp | 12 ++++++++++-- src/gui/windows/editor/srceditor.h | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index 1223e88e..5a8040c2 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -557,10 +557,20 @@ void MainWindow::message_selected( (void)hint; if (file.isEmpty()) { return; } - SrcEditor *editor = editor_tabs->open_file_if_not_open(file, true)->get_editor(); + auto editor = (file == "Unknown") ? editor_tabs->get_current_editor() + : editor_tabs->find_tab_by_filename(file)->get_editor(); if (editor == nullptr) { return; } - editor->setCursorToLine(line); + editor->setCursorTo(line, column); editor->setFocus(); + + // Highlight the line + QTextEdit::ExtraSelection selection; + selection.format.setBackground(QColor(Qt::red).lighter(160)); + selection.format.setProperty(QTextFormat::FullWidthSelection, true); + selection.cursor = editor->textCursor(); + selection.cursor.clearSelection(); + editor->setExtraSelections({ selection }); + if (editor_tabs != nullptr) { editor_tabs->setCurrentWidget(editor); } } @@ -674,7 +684,7 @@ void MainWindow::compile_source() { machine->cache_sync(); auto editor = editor_tabs->get_current_editor(); - auto filename = editor->filename(); + auto filename = editor->filename().isEmpty() ? "Unknown" : editor->filename(); auto content = editor->document(); emit clear_messages(); diff --git a/src/gui/windows/editor/srceditor.cpp b/src/gui/windows/editor/srceditor.cpp index 45ed2146..36087708 100644 --- a/src/gui/windows/editor/srceditor.cpp +++ b/src/gui/windows/editor/srceditor.cpp @@ -42,6 +42,9 @@ SrcEditor::SrcEditor(QWidget *parent) : Super(parent), line_number_area(new Line connect(this, &SrcEditor::blockCountChanged, this, &SrcEditor::updateMargins); connect(this, &SrcEditor::updateRequest, this, &SrcEditor::updateLineNumberArea); + // Clear error highlight on typing + connect(this, &SrcEditor::textChanged, [this]() { setExtraSelections({}); }); + updateMargins(0); } @@ -102,6 +105,12 @@ void SrcEditor::setCursorToLine(int ln) { setTextCursor(cursor); } +void SrcEditor::setCursorTo(int ln, int col) { + QTextCursor cursor(document()->findBlockByLineNumber(ln - 1)); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, col - 1); + setTextCursor(cursor); +} + bool SrcEditor::isModified() const { return document()->isModified(); } @@ -113,10 +122,10 @@ void SrcEditor::setModified(bool val) { void SrcEditor::setSaveAsRequired(bool val) { saveAsRequiredFl = val; } - bool SrcEditor::saveAsRequired() const { return saveAsRequiredFl; } + void SrcEditor::keyPressEvent(QKeyEvent *event) { QTextCursor cursor = textCursor(); if (cursor.hasSelection()) { @@ -258,7 +267,6 @@ void SrcEditor::updateLineNumberArea(const QRect &rect, int dy) { if (rect.contains(viewport()->rect())) updateMargins(0); } - void SrcEditor::resizeEvent(QResizeEvent *event) { QPlainTextEdit::resizeEvent(event); diff --git a/src/gui/windows/editor/srceditor.h b/src/gui/windows/editor/srceditor.h index 57264843..c90b7b39 100644 --- a/src/gui/windows/editor/srceditor.h +++ b/src/gui/windows/editor/srceditor.h @@ -23,6 +23,7 @@ class SrcEditor : public QPlainTextEdit { bool saveFile(QString filename = ""); bool loadByteArray(const QByteArray &content, const QString &filename = ""); void setCursorToLine(int ln); + void setCursorTo(int ln, int col); void setFileName(const QString &filename); [[nodiscard]] bool isModified() const; void setModified(bool val); From 2f14bf0db0ebba86a7d62f7f65828a2c78a684ae Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Fri, 27 Oct 2023 23:45:23 +0200 Subject: [PATCH 10/10] GUI: disable makefile action if it would fail --- src/gui/mainwindow/mainwindow.cpp | 42 +++++++++++++++++++------------ 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index 5a8040c2..94c61859 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -30,6 +30,10 @@ #include "qhtml5file.h" #include + +constexpr bool WEB_ASSEMBLY = true; +#else +constexpr bool WEB_ASSEMBLY = false; #endif MainWindow::MainWindow(QSettings *settings, QWidget *parent) @@ -58,6 +62,15 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) editor_tabs.reset(new EditorDock(this->settings, central_widget_tabs.data())); editor_tabs->setTabBarAutoHide(true); editor_tabs->setWindowTitle("&Editor"); + ui->actionBuildExe->setEnabled(false); + connect(ui->actionNew, &QAction::triggered, editor_tabs.data(), &EditorDock::create_empty_tab); + connect(ui->actionOpen, &QAction::triggered, editor_tabs.data(), &EditorDock::open_file_dialog); + connect(ui->actionSave, &QAction::triggered, editor_tabs.data(), &EditorDock::save_current_tab); + connect( + ui->actionSaveAs, &QAction::triggered, editor_tabs.data(), + &EditorDock::save_current_tab_as); + connect( + ui->actionClose, &QAction::triggered, editor_tabs.data(), &EditorDock::close_current_tab); connect( editor_tabs.data(), &EditorDock::requestAddRemoveTab, central_widget_tabs.data(), &HidingTabWidget::addRemoveTabRequested); @@ -69,6 +82,15 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) ui->actionClose->setEnabled(available); ui->actionCompileSource->setEnabled(available); }); + if constexpr (!WEB_ASSEMBLY) { + // Only enable build action if we know there to look for the Makefile. + connect(editor_tabs.data(), &EditorDock::currentChanged, this, [this](int index) { + bool has_elf_file = machine != nullptr && !machine->config().elf().isEmpty(); + bool current_tab_is_file + = (index >= 0) && !editor_tabs->get_tab(index)->get_editor()->filename().isEmpty(); + ui->actionBuildExe->setEnabled(has_elf_file || current_tab_is_file); + }); + } connect( ui->actionEditorShowLineNumbers, &QAction::triggered, editor_tabs.data(), &EditorDock::set_show_line_numbers); @@ -118,15 +140,6 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) connect(ui->actionReload, &QAction::triggered, this, [this] { machine_reload(false, false); }); connect(ui->actionPrint, &QAction::triggered, this, &MainWindow::print_action); - // Editor actions - connect(ui->actionNew, &QAction::triggered, editor_tabs.data(), &EditorDock::create_empty_tab); - connect(ui->actionOpen, &QAction::triggered, editor_tabs.data(), &EditorDock::open_file_dialog); - connect(ui->actionSave, &QAction::triggered, editor_tabs.data(), &EditorDock::save_current_tab); - connect( - ui->actionSaveAs, &QAction::triggered, editor_tabs.data(), - &EditorDock::save_current_tab_as); - connect( - ui->actionClose, &QAction::triggered, editor_tabs.data(), &EditorDock::close_current_tab); connect( ui->actionMnemonicRegisters, &QAction::triggered, this, &MainWindow::view_mnemonics_registers); @@ -174,10 +187,6 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) ui->menuExamples->addAction(textsigac); connect(textsigac, &TextSignalAction::activated, this, &MainWindow::example_source); } - -#ifdef __EMSCRIPTEN__ - ui->actionBuildExe->setEnabled(false); -#endif } MainWindow::~MainWindow() { @@ -760,7 +769,8 @@ void MainWindow::build_execute_no_check() { work_dir = fi.dir().path(); } } - if (!work_dir.isEmpty()) { proc->setWorkingDirectory(work_dir); } - // API without args has been deprecated. - proc->start("make", {}, QProcess::Unbuffered | QProcess::ReadOnly); + if (!work_dir.isEmpty()) { + proc->setWorkingDirectory(work_dir); + proc->start("make", {}, QProcess::Unbuffered | QProcess::ReadOnly); + } } \ No newline at end of file