SpreadsheetView.h 4.7 KB

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