mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:07:36 +00:00
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
This commit is contained in:
parent
d00781de36
commit
e41dfa6599
7 changed files with 89 additions and 2 deletions
|
@ -191,4 +191,21 @@ void Cell::copy_from(const Cell& other)
|
||||||
m_thrown_value = other.m_thrown_value;
|
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,6 +14,7 @@
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
#include <AK/WeakPtr.h>
|
#include <AK/WeakPtr.h>
|
||||||
|
#include <LibGUI/Command.h>
|
||||||
|
|
||||||
namespace Spreadsheet {
|
namespace Spreadsheet {
|
||||||
|
|
||||||
|
@ -120,4 +121,17 @@ private:
|
||||||
Format m_evaluated_formats;
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,10 @@ void SheetModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& valu
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& cell = m_sheet->ensure({ (size_t)index.column(), (size_t)index.row() });
|
auto& cell = m_sheet->ensure({ (size_t)index.column(), (size_t)index.row() });
|
||||||
|
auto previous_data = cell.data();
|
||||||
cell.set_data(value.to_string());
|
cell.set_data(value.to_string());
|
||||||
|
if (on_cell_data_change)
|
||||||
|
on_cell_data_change(cell, previous_data);
|
||||||
did_update(UpdateFlag::DontInvalidateIndices);
|
did_update(UpdateFlag::DontInvalidateIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ public:
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
|
Function<void(Cell&, String&)> on_cell_data_change;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit SheetModel(Sheet& sheet)
|
explicit SheetModel(Sheet& sheet)
|
||||||
: m_sheet(sheet)
|
: m_sheet(sheet)
|
||||||
|
|
|
@ -103,6 +103,8 @@ public:
|
||||||
|
|
||||||
void move_cursor(GUI::AbstractView::CursorMovement);
|
void move_cursor(GUI::AbstractView::CursorMovement);
|
||||||
|
|
||||||
|
NonnullRefPtr<SheetModel> model() { return m_sheet_model; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void hide_event(GUI::HideEvent&) override;
|
virtual void hide_event(GUI::HideEvent&) override;
|
||||||
virtual void show_event(GUI::ShowEvent&) override;
|
virtual void show_event(GUI::ShowEvent&) override;
|
||||||
|
|
|
@ -205,6 +205,14 @@ SpreadsheetWidget::SpreadsheetWidget(GUI::Window& parent_window, NonnullRefPtrVe
|
||||||
},
|
},
|
||||||
window());
|
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(
|
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&) {
|
"&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()) {
|
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_cut_action);
|
||||||
toolbar.add_action(*m_copy_action);
|
toolbar.add_action(*m_copy_action);
|
||||||
toolbar.add_action(*m_paste_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)
|
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->on_selection_dropped = nullptr;
|
||||||
}
|
}
|
||||||
m_selected_view = &static_cast<SpreadsheetView&>(selected_widget);
|
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) {
|
m_selected_view->on_selection_changed = [&](Vector<Position>&& selection) {
|
||||||
auto* sheet_ptr = m_selected_view->sheet_if_available();
|
auto* sheet_ptr = m_selected_view->sheet_if_available();
|
||||||
// How did this even happen?
|
// 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)
|
void SpreadsheetWidget::save(StringView filename)
|
||||||
{
|
{
|
||||||
auto result = m_workbook->save(filename);
|
auto result = m_workbook->save(filename);
|
||||||
if (result.is_error())
|
if (result.is_error()) {
|
||||||
GUI::MessageBox::show_error(window(), result.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)
|
void SpreadsheetWidget::load_file(Core::File& file)
|
||||||
|
@ -418,7 +454,7 @@ void SpreadsheetWidget::load_file(Core::File& file)
|
||||||
|
|
||||||
bool SpreadsheetWidget::request_close()
|
bool SpreadsheetWidget::request_close()
|
||||||
{
|
{
|
||||||
if (!m_workbook->dirty())
|
if (!undo_stack().is_current_modified())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), current_filename());
|
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);
|
file_menu.add_action(*m_quit_action);
|
||||||
|
|
||||||
auto& edit_menu = window.add_menu("&Edit");
|
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_cut_action);
|
||||||
edit_menu.add_action(*m_copy_action);
|
edit_menu.add_action(*m_copy_action);
|
||||||
edit_menu.add_action(*m_paste_action);
|
edit_menu.add_action(*m_paste_action);
|
||||||
|
|
|
@ -42,6 +42,10 @@ public:
|
||||||
|
|
||||||
void initialize_menubar(GUI::Window&);
|
void initialize_menubar(GUI::Window&);
|
||||||
|
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
auto& undo_stack() { return m_undo_stack; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void resize_event(GUI::ResizeEvent&) override;
|
virtual void resize_event(GUI::ResizeEvent&) override;
|
||||||
|
|
||||||
|
@ -60,6 +64,7 @@ private:
|
||||||
RefPtr<GUI::Menu> m_tab_context_menu;
|
RefPtr<GUI::Menu> m_tab_context_menu;
|
||||||
RefPtr<SpreadsheetView> m_tab_context_menu_sheet_view;
|
RefPtr<SpreadsheetView> m_tab_context_menu_sheet_view;
|
||||||
bool m_should_change_selected_cells { false };
|
bool m_should_change_selected_cells { false };
|
||||||
|
GUI::UndoStack m_undo_stack;
|
||||||
|
|
||||||
OwnPtr<Workbook> m_workbook;
|
OwnPtr<Workbook> m_workbook;
|
||||||
|
|
||||||
|
@ -69,11 +74,16 @@ private:
|
||||||
RefPtr<GUI::Action> m_save_action;
|
RefPtr<GUI::Action> m_save_action;
|
||||||
RefPtr<GUI::Action> m_save_as_action;
|
RefPtr<GUI::Action> m_save_as_action;
|
||||||
RefPtr<GUI::Action> m_quit_action;
|
RefPtr<GUI::Action> m_quit_action;
|
||||||
|
|
||||||
RefPtr<GUI::Action> m_cut_action;
|
RefPtr<GUI::Action> m_cut_action;
|
||||||
RefPtr<GUI::Action> m_copy_action;
|
RefPtr<GUI::Action> m_copy_action;
|
||||||
RefPtr<GUI::Action> m_paste_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_functions_help_action;
|
||||||
RefPtr<GUI::Action> m_about_action;
|
RefPtr<GUI::Action> m_about_action;
|
||||||
|
|
||||||
RefPtr<GUI::Action> m_rename_action;
|
RefPtr<GUI::Action> m_rename_action;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue