Serenity Operating System
at master 170 lines 5.0 kB view raw
1/* 2 * Copyright (c) 2020-2022, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include "Spreadsheet.h" 10#include "SpreadsheetModel.h" 11#include <LibGUI/AbstractTableView.h> 12#include <LibGUI/ModelEditingDelegate.h> 13#include <LibGUI/TableView.h> 14#include <LibGUI/Widget.h> 15#include <string.h> 16 17namespace Spreadsheet { 18 19class CellEditor final : public GUI::TextEditor { 20 C_OBJECT(CellEditor); 21 22public: 23 virtual ~CellEditor() = default; 24 25 Function<void(GUI::KeyEvent&)> on_cursor_key_pressed; 26 27private: 28 CellEditor() 29 : TextEditor(TextEditor::Type::SingleLine) 30 { 31 } 32 33 static bool is_navigation(const GUI::KeyEvent& event) 34 { 35 if (event.modifiers() == KeyModifier::Mod_Shift && event.key() == KeyCode::Key_Tab) 36 return true; 37 38 if (event.modifiers()) 39 return false; 40 41 switch (event.key()) { 42 case KeyCode::Key_Tab: 43 case KeyCode::Key_Return: 44 return true; 45 default: 46 return false; 47 } 48 } 49 50 virtual void keydown_event(GUI::KeyEvent& event) override 51 { 52 if (is_navigation(event)) 53 on_cursor_key_pressed(event); 54 else 55 TextEditor::keydown_event(event); 56 } 57}; 58 59class InfinitelyScrollableTableView : public GUI::TableView { 60 C_OBJECT(InfinitelyScrollableTableView) 61public: 62 Function<void()> on_reaching_vertical_end; 63 Function<void()> on_reaching_horizontal_end; 64 65private: 66 InfinitelyScrollableTableView() 67 : m_horizontal_scroll_end_timer(Core::Timer::try_create().release_value_but_fixme_should_propagate_errors()) 68 , m_vertical_scroll_end_timer(Core::Timer::try_create().release_value_but_fixme_should_propagate_errors()) 69 { 70 } 71 virtual void did_scroll() override; 72 virtual void mousemove_event(GUI::MouseEvent&) override; 73 virtual void mousedown_event(GUI::MouseEvent&) override; 74 virtual void mouseup_event(GUI::MouseEvent&) override; 75 virtual void drop_event(GUI::DropEvent&) override; 76 77 bool is_dragging() const { return m_is_dragging_for_cut || m_is_dragging_for_extend || m_is_dragging_for_select; } 78 79 bool m_is_hovering_extend_zone { false }; 80 bool m_is_hovering_cut_zone { false }; 81 bool m_is_dragging_for_select { false }; 82 bool m_is_dragging_for_cut { false }; 83 bool m_is_dragging_for_extend { false }; 84 bool m_has_committed_to_cutting { false }; 85 bool m_has_committed_to_extending { false }; 86 GUI::ModelIndex m_starting_selection_index; 87 GUI::ModelIndex m_target_cell; 88 RefPtr<Core::Timer> m_horizontal_scroll_end_timer; 89 RefPtr<Core::Timer> m_vertical_scroll_end_timer; 90}; 91 92class SpreadsheetView final : public GUI::Widget { 93 C_OBJECT(SpreadsheetView); 94 95public: 96 ~SpreadsheetView() = default; 97 98 Sheet* sheet_if_available() { return m_sheet; } 99 100 const GUI::ModelIndex* cursor() const 101 { 102 return &m_table_view->cursor_index(); 103 } 104 105 Function<void(Vector<Position>&&)> on_selection_changed; 106 Function<void()> on_selection_dropped; 107 108 void move_cursor(GUI::AbstractView::CursorMovement); 109 110 NonnullRefPtr<SheetModel> model() { return m_sheet_model; }; 111 112private: 113 virtual void hide_event(GUI::HideEvent&) override; 114 virtual void show_event(GUI::ShowEvent&) override; 115 116 void update_with_model(); 117 118 SpreadsheetView(Sheet&); 119 120 class EditingDelegate final : public GUI::StringModelEditingDelegate { 121 public: 122 EditingDelegate(Sheet const& sheet) 123 : m_sheet(sheet) 124 { 125 } 126 virtual void set_value(GUI::Variant const&, GUI::ModelEditingDelegate::SelectionBehavior) override; 127 128 virtual RefPtr<Widget> create_widget() override 129 { 130 auto textbox = CellEditor::construct(); 131 textbox->on_escape_pressed = [this] { 132 rollback(); 133 }; 134 textbox->on_cursor_key_pressed = [this](auto& event) { 135 commit(); 136 on_cursor_key_pressed(event); 137 }; 138 textbox->on_focusout = [this] { 139 on_cell_focusout(index(), value()); 140 }; 141 return textbox; 142 } 143 144 Function<void(GUI::KeyEvent&)> on_cursor_key_pressed; 145 Function<void(const GUI::ModelIndex&, const GUI::Variant&)> on_cell_focusout; 146 147 private: 148 bool m_has_set_initial_value { false }; 149 Sheet const& m_sheet; 150 }; 151 152 class TableCellPainter final : public GUI::TableCellPaintingDelegate { 153 public: 154 TableCellPainter(const GUI::TableView& view) 155 : m_table_view(view) 156 { 157 } 158 void paint(GUI::Painter&, Gfx::IntRect const&, Gfx::Palette const&, const GUI::ModelIndex&) override; 159 160 private: 161 const GUI::TableView& m_table_view; 162 }; 163 164 NonnullRefPtr<Sheet> m_sheet; 165 NonnullRefPtr<SheetModel> m_sheet_model; 166 RefPtr<InfinitelyScrollableTableView> m_table_view; 167 RefPtr<GUI::Menu> m_cell_range_context_menu; 168}; 169 170}