/* * Copyright (c) 2020-2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include "Spreadsheet.h" #include "SpreadsheetModel.h" #include #include #include #include #include namespace Spreadsheet { class CellEditor final : public GUI::TextEditor { C_OBJECT(CellEditor); public: virtual ~CellEditor() = default; Function on_cursor_key_pressed; private: CellEditor() : TextEditor(TextEditor::Type::SingleLine) { } static bool is_navigation(const GUI::KeyEvent& event) { if (event.modifiers() == KeyModifier::Mod_Shift && event.key() == KeyCode::Key_Tab) return true; if (event.modifiers()) return false; switch (event.key()) { case KeyCode::Key_Tab: case KeyCode::Key_Left: case KeyCode::Key_Right: case KeyCode::Key_Up: case KeyCode::Key_Down: case KeyCode::Key_Return: return true; default: return false; } } virtual void keydown_event(GUI::KeyEvent& event) override { if (is_navigation(event)) on_cursor_key_pressed(event); else TextEditor::keydown_event(event); } }; class InfinitelyScrollableTableView : public GUI::TableView { C_OBJECT(InfinitelyScrollableTableView) public: Function on_reaching_vertical_end; Function on_reaching_horizontal_end; private: InfinitelyScrollableTableView() : m_horizontal_scroll_end_timer(Core::Timer::construct()) , m_vertical_scroll_end_timer(Core::Timer::construct()) { } virtual void did_scroll() override; virtual void mousemove_event(GUI::MouseEvent&) override; virtual void mousedown_event(GUI::MouseEvent&) override; virtual void mouseup_event(GUI::MouseEvent&) override; virtual void drop_event(GUI::DropEvent&) override; bool is_dragging() const { return m_is_dragging_for_cut || m_is_dragging_for_extend || m_is_dragging_for_select; } bool m_is_hovering_extend_zone { false }; bool m_is_hovering_cut_zone { false }; bool m_is_dragging_for_select { false }; bool m_is_dragging_for_cut { false }; bool m_is_dragging_for_extend { false }; bool m_has_committed_to_cutting { false }; bool m_has_committed_to_extending { false }; GUI::ModelIndex m_starting_selection_index; GUI::ModelIndex m_target_cell; RefPtr m_horizontal_scroll_end_timer; RefPtr m_vertical_scroll_end_timer; }; class SpreadsheetView final : public GUI::Widget { C_OBJECT(SpreadsheetView); public: ~SpreadsheetView() = default; Sheet* sheet_if_available() { return m_sheet; } const GUI::ModelIndex* cursor() const { return &m_table_view->cursor_index(); } Function&&)> on_selection_changed; Function on_selection_dropped; void move_cursor(GUI::AbstractView::CursorMovement); NonnullRefPtr model() { return m_sheet_model; }; private: virtual void hide_event(GUI::HideEvent&) override; virtual void show_event(GUI::ShowEvent&) override; void update_with_model(); SpreadsheetView(Sheet&); class EditingDelegate final : public GUI::StringModelEditingDelegate { public: EditingDelegate(Sheet const& sheet) : m_sheet(sheet) { } virtual void set_value(GUI::Variant const&, GUI::ModelEditingDelegate::SelectionBehavior) override; virtual RefPtr create_widget() override { auto textbox = CellEditor::construct(); textbox->on_escape_pressed = [this] { rollback(); }; textbox->on_cursor_key_pressed = [this](auto& event) { commit(); on_cursor_key_pressed(event); }; textbox->on_focusout = [this] { on_cell_focusout(index(), value()); }; return textbox; } Function on_cursor_key_pressed; Function on_cell_focusout; private: bool m_has_set_initial_value { false }; Sheet const& m_sheet; }; class TableCellPainter final : public GUI::TableCellPaintingDelegate { public: TableCellPainter(const GUI::TableView& view) : m_table_view(view) { } void paint(GUI::Painter&, Gfx::IntRect const&, Gfx::Palette const&, const GUI::ModelIndex&) override; private: const GUI::TableView& m_table_view; }; NonnullRefPtr m_sheet; NonnullRefPtr m_sheet_model; RefPtr m_table_view; RefPtr m_cell_range_context_menu; }; }