Sfoglia il codice sorgente

Spreadsheet: Add undo/redo implementation

The Spreadsheet application currently does not support undo/redo,
and with this update, we are starting the process of adding this feature
:-)

Additionally, the save dialog has been updated to use
GUI::MessageBox::ask_about_unsaved_changes() for system cohesity.

Spreadsheet: Add basic undo functinoality

The spreadsheet application now has basic support for undo. Testing of
this feature is limited, and may not work as intended yet.

Spreadsheet: Add callback when a cell's value is changed

In addition to the callback being added, this commit also exposes the
SheetModel class via a getter in SpreadSheetView.

Spreadsheet: Remove debug statements and use cell change callback

This commit uses the on_cell_data_change callback from within the
SheetModel class. This allows for us to push/pop changes to the undo
stack.

With this, we have basic Undo/Redo functionality :-)

Spreadsheet: Actually add window::set_modified

Spreadsheet: Const-correctness :-)

Spreadsheet: Reorder the edit menu actions
Zack Penn 3 anni fa
parent
commit
e41dfa6599

+ 17 - 0
Userland/Applications/Spreadsheet/Cell.cpp

@@ -191,4 +191,21 @@ void Cell::copy_from(const Cell& other)
     m_thrown_value = other.m_thrown_value;
 }
 
+CellUndoCommand::CellUndoCommand(Cell& cell, String const& previous_data)
+    : m_cell(cell)
+    , m_current_data(cell.data())
+    , m_previous_data(previous_data)
+{
+}
+
+void CellUndoCommand::undo()
+{
+    m_cell.set_data(m_previous_data);
+}
+
+void CellUndoCommand::redo()
+{
+    m_cell.set_data(m_current_data);
+}
+
 }

+ 14 - 0
Userland/Applications/Spreadsheet/Cell.h

@@ -14,6 +14,7 @@
 #include <AK/String.h>
 #include <AK/Types.h>
 #include <AK/WeakPtr.h>
+#include <LibGUI/Command.h>
 
 namespace Spreadsheet {
 
@@ -120,4 +121,17 @@ private:
     Format m_evaluated_formats;
 };
 
+class CellUndoCommand : public GUI::Command {
+public:
+    CellUndoCommand(Cell&, String const&);
+
+    virtual void undo() override;
+    virtual void redo() override;
+
+private:
+    Cell& m_cell;
+    String m_current_data;
+    String m_previous_data;
+};
+
 }

+ 3 - 0
Userland/Applications/Spreadsheet/SpreadsheetModel.cpp

@@ -153,7 +153,10 @@ void SheetModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& valu
         return;
 
     auto& cell = m_sheet->ensure({ (size_t)index.column(), (size_t)index.row() });
+    auto previous_data = cell.data();
     cell.set_data(value.to_string());
+    if (on_cell_data_change)
+        on_cell_data_change(cell, previous_data);
     did_update(UpdateFlag::DontInvalidateIndices);
 }
 

+ 2 - 0
Userland/Applications/Spreadsheet/SpreadsheetModel.h

@@ -29,6 +29,8 @@ public:
 
     void update();
 
+    Function<void(Cell&, String&)> on_cell_data_change;
+
 private:
     explicit SheetModel(Sheet& sheet)
         : m_sheet(sheet)

+ 2 - 0
Userland/Applications/Spreadsheet/SpreadsheetView.h

@@ -103,6 +103,8 @@ public:
 
     void move_cursor(GUI::AbstractView::CursorMovement);
 
+    NonnullRefPtr<SheetModel> model() { return m_sheet_model; };
+
 private:
     virtual void hide_event(GUI::HideEvent&) override;
     virtual void show_event(GUI::ShowEvent&) override;

+ 41 - 2
Userland/Applications/Spreadsheet/SpreadsheetWidget.cpp

@@ -205,6 +205,14 @@ SpreadsheetWidget::SpreadsheetWidget(GUI::Window& parent_window, NonnullRefPtrVe
     },
         window());
 
+    m_undo_action = GUI::CommonActions::make_undo_action([&](auto&) {
+        undo();
+    });
+
+    m_redo_action = GUI::CommonActions::make_redo_action([&](auto&) {
+        redo();
+    });
+
     m_functions_help_action = GUI::Action::create(
         "&Functions Help", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/app-help.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
             if (auto* worksheet_ptr = current_worksheet_if_available()) {
@@ -227,6 +235,8 @@ SpreadsheetWidget::SpreadsheetWidget(GUI::Window& parent_window, NonnullRefPtrVe
     toolbar.add_action(*m_cut_action);
     toolbar.add_action(*m_copy_action);
     toolbar.add_action(*m_paste_action);
+    toolbar.add_action(*m_undo_action);
+    toolbar.add_action(*m_redo_action);
 }
 
 void SpreadsheetWidget::resize_event(GUI::ResizeEvent& event)
@@ -251,6 +261,10 @@ void SpreadsheetWidget::setup_tabs(NonnullRefPtrVector<Sheet> new_sheets)
             m_selected_view->on_selection_dropped = nullptr;
         }
         m_selected_view = &static_cast<SpreadsheetView&>(selected_widget);
+        m_selected_view->model()->on_cell_data_change = [&](auto& cell, auto& previous_data) {
+            undo_stack().push(make<CellUndoCommand>(cell, previous_data));
+            window()->set_modified(true);
+        };
         m_selected_view->on_selection_changed = [&](Vector<Position>&& selection) {
             auto* sheet_ptr = m_selected_view->sheet_if_available();
             // How did this even happen?
@@ -390,11 +404,33 @@ void SpreadsheetWidget::try_generate_tip_for_input_expression(StringView source,
     }
 }
 
+void SpreadsheetWidget::undo()
+{
+    if (!m_undo_stack.can_undo())
+        return;
+
+    m_undo_stack.undo();
+    update();
+}
+
+void SpreadsheetWidget::redo()
+{
+    if (!m_undo_stack.can_redo())
+        return;
+
+    m_undo_stack.redo();
+    update();
+}
+
 void SpreadsheetWidget::save(StringView filename)
 {
     auto result = m_workbook->save(filename);
-    if (result.is_error())
+    if (result.is_error()) {
         GUI::MessageBox::show_error(window(), result.error());
+        return;
+    }
+    undo_stack().set_current_unmodified();
+    window()->set_modified(false);
 }
 
 void SpreadsheetWidget::load_file(Core::File& file)
@@ -418,7 +454,7 @@ void SpreadsheetWidget::load_file(Core::File& file)
 
 bool SpreadsheetWidget::request_close()
 {
-    if (!m_workbook->dirty())
+    if (!undo_stack().is_current_modified())
         return true;
 
     auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), current_filename());
@@ -533,6 +569,9 @@ void SpreadsheetWidget::initialize_menubar(GUI::Window& window)
     file_menu.add_action(*m_quit_action);
 
     auto& edit_menu = window.add_menu("&Edit");
+    edit_menu.add_action(*m_undo_action);
+    edit_menu.add_action(*m_redo_action);
+    edit_menu.add_separator();
     edit_menu.add_action(*m_cut_action);
     edit_menu.add_action(*m_copy_action);
     edit_menu.add_action(*m_paste_action);

+ 10 - 0
Userland/Applications/Spreadsheet/SpreadsheetWidget.h

@@ -42,6 +42,10 @@ public:
 
     void initialize_menubar(GUI::Window&);
 
+    void undo();
+    void redo();
+    auto& undo_stack() { return m_undo_stack; }
+
 private:
     virtual void resize_event(GUI::ResizeEvent&) override;
 
@@ -60,6 +64,7 @@ private:
     RefPtr<GUI::Menu> m_tab_context_menu;
     RefPtr<SpreadsheetView> m_tab_context_menu_sheet_view;
     bool m_should_change_selected_cells { false };
+    GUI::UndoStack m_undo_stack;
 
     OwnPtr<Workbook> m_workbook;
 
@@ -69,11 +74,16 @@ private:
     RefPtr<GUI::Action> m_save_action;
     RefPtr<GUI::Action> m_save_as_action;
     RefPtr<GUI::Action> m_quit_action;
+
     RefPtr<GUI::Action> m_cut_action;
     RefPtr<GUI::Action> m_copy_action;
     RefPtr<GUI::Action> m_paste_action;
+    RefPtr<GUI::Action> m_undo_action;
+    RefPtr<GUI::Action> m_redo_action;
+
     RefPtr<GUI::Action> m_functions_help_action;
     RefPtr<GUI::Action> m_about_action;
+
     RefPtr<GUI::Action> m_rename_action;
 };