mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 04:52:45 +00:00 
			
		
		
		
	Spreadsheet: Add (limited) support for custom cell formatting
This commit is contained in:
		
							parent
							
								
									e75247a75b
								
							
						
					
					
						commit
						054638c355
					
				
					 10 changed files with 466 additions and 4 deletions
				
			
		|  | @ -5,6 +5,7 @@ set(SOURCES | |||
|     CellType/Numeric.cpp | ||||
|     CellType/String.cpp | ||||
|     CellType/Type.cpp | ||||
|     CellTypeDialog.cpp | ||||
|     HelpWindow.cpp | ||||
|     JSIntegration.cpp | ||||
|     Spreadsheet.cpp | ||||
|  |  | |||
|  | @ -60,12 +60,16 @@ void Cell::set_data(JS::Value new_data) | |||
|     evaluated_data = move(new_data); | ||||
| } | ||||
| 
 | ||||
| void Cell::set_type(const CellType* type) | ||||
| { | ||||
|     m_type = type; | ||||
| } | ||||
| 
 | ||||
| void Cell::set_type(const StringView& name) | ||||
| { | ||||
|     auto* cell_type = CellType::get_by_name(name); | ||||
|     if (cell_type) { | ||||
|         m_type = cell_type; | ||||
|         return; | ||||
|         return set_type(cell_type); | ||||
|     } | ||||
| 
 | ||||
|     ASSERT_NOT_REACHED(); | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ struct Cell : public Weakable<Cell> { | |||
|     void set_data(JS::Value new_data); | ||||
| 
 | ||||
|     void set_type(const StringView& name); | ||||
|     void set_type(const CellType*); | ||||
|     void set_type_metadata(CellTypeMetadata&&); | ||||
| 
 | ||||
|     String typed_display() const; | ||||
|  |  | |||
|  | @ -43,6 +43,14 @@ const CellType* CellType::get_by_name(const StringView& name) | |||
|     return s_cell_types.get(name).value_or(nullptr); | ||||
| } | ||||
| 
 | ||||
| Vector<StringView> CellType::names() | ||||
| { | ||||
|     Vector<StringView> names; | ||||
|     for (auto& it : s_cell_types) | ||||
|         names.append(it.key); | ||||
|     return names; | ||||
| } | ||||
| 
 | ||||
| CellType::CellType(const StringView& name) | ||||
| { | ||||
|     ASSERT(!s_cell_types.contains(name)); | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ | |||
| #include "../Forward.h" | ||||
| #include <AK/Forward.h> | ||||
| #include <AK/String.h> | ||||
| #include <LibGfx/TextAlignment.h> | ||||
| #include <LibJS/Forward.h> | ||||
| 
 | ||||
| namespace Spreadsheet { | ||||
|  | @ -36,11 +37,13 @@ namespace Spreadsheet { | |||
| struct CellTypeMetadata { | ||||
|     int length { -1 }; | ||||
|     String format; | ||||
|     Gfx::TextAlignment alignment { Gfx::TextAlignment::CenterRight }; | ||||
| }; | ||||
| 
 | ||||
| class CellType { | ||||
| public: | ||||
|     static const CellType* get_by_name(const StringView&); | ||||
|     static Vector<StringView> names(); | ||||
| 
 | ||||
|     virtual String display(Cell&, const CellTypeMetadata&) const = 0; | ||||
|     virtual JS::Value js_value(Cell&, const CellTypeMetadata&) const = 0; | ||||
|  |  | |||
							
								
								
									
										341
									
								
								Applications/Spreadsheet/CellTypeDialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								Applications/Spreadsheet/CellTypeDialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,341 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, the SerenityOS developers. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright notice, this | ||||
|  *    list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer in the documentation | ||||
|  *    and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
|  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| 
 | ||||
| #include "CellTypeDialog.h" | ||||
| #include "Cell.h" | ||||
| #include "Spreadsheet.h" | ||||
| #include <AK/StringBuilder.h> | ||||
| #include <LibGUI/BoxLayout.h> | ||||
| #include <LibGUI/Button.h> | ||||
| #include <LibGUI/CheckBox.h> | ||||
| #include <LibGUI/ComboBox.h> | ||||
| #include <LibGUI/ItemListModel.h> | ||||
| #include <LibGUI/Label.h> | ||||
| #include <LibGUI/ListView.h> | ||||
| #include <LibGUI/SpinBox.h> | ||||
| #include <LibGUI/TabWidget.h> | ||||
| #include <LibGUI/TextEditor.h> | ||||
| #include <LibGUI/Widget.h> | ||||
| 
 | ||||
| namespace Spreadsheet { | ||||
| 
 | ||||
| CellTypeDialog::CellTypeDialog(const Vector<Position>& positions, Sheet& sheet, GUI::Window* parent) | ||||
|     : GUI::Dialog(parent) | ||||
| { | ||||
|     ASSERT(!positions.is_empty()); | ||||
| 
 | ||||
|     StringBuilder builder; | ||||
| 
 | ||||
|     if (positions.size() == 1) | ||||
|         builder.appendf("Format Cell %s%zu", positions.first().column.characters(), positions.first().row); | ||||
|     else | ||||
|         builder.appendf("Format %zu Cells", positions.size()); | ||||
| 
 | ||||
|     set_title(builder.string_view()); | ||||
|     resize(270, 360); | ||||
| 
 | ||||
|     auto& main_widget = set_main_widget<GUI::Widget>(); | ||||
|     main_widget.set_layout<GUI::VerticalBoxLayout>().set_margins({ 4, 4, 4, 4 }); | ||||
|     main_widget.set_fill_with_background_color(true); | ||||
| 
 | ||||
|     auto& tab_widget = main_widget.add<GUI::TabWidget>(); | ||||
|     setup_tabs(tab_widget, positions, sheet); | ||||
| 
 | ||||
|     auto& buttonbox = main_widget.add<GUI::Widget>(); | ||||
|     buttonbox.set_preferred_size({ 0, 20 }); | ||||
|     buttonbox.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
|     auto& button_layout = buttonbox.set_layout<GUI::HorizontalBoxLayout>(); | ||||
|     button_layout.set_spacing(10); | ||||
|     button_layout.add_spacer(); | ||||
|     auto& ok_button = buttonbox.add<GUI::Button>("OK"); | ||||
|     ok_button.set_preferred_size({ 80, 0 }); | ||||
|     ok_button.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); | ||||
|     ok_button.on_click = [&](auto) { done(ExecOK); }; | ||||
| } | ||||
| 
 | ||||
| const Vector<String> g_horizontal_alignments { "Left", "Center", "Right" }; | ||||
| const Vector<String> g_vertical_alignments { "Top", "Center", "Bottom" }; | ||||
| Vector<String> g_types; | ||||
| 
 | ||||
| constexpr static CellTypeDialog::VerticalAlignment vertical_alignment_from(Gfx::TextAlignment alignment) | ||||
| { | ||||
|     switch (alignment) { | ||||
|     case Gfx::TextAlignment::CenterRight: | ||||
|     case Gfx::TextAlignment::CenterLeft: | ||||
|     case Gfx::TextAlignment::Center: | ||||
|         return CellTypeDialog::VerticalAlignment::Center; | ||||
| 
 | ||||
|     case Gfx::TextAlignment::TopRight: | ||||
|     case Gfx::TextAlignment::TopLeft: | ||||
|         return CellTypeDialog::VerticalAlignment::Top; | ||||
| 
 | ||||
|     case Gfx::TextAlignment::BottomRight: | ||||
|         return CellTypeDialog::VerticalAlignment::Bottom; | ||||
|     } | ||||
| 
 | ||||
|     return CellTypeDialog::VerticalAlignment::Center; | ||||
| } | ||||
| 
 | ||||
| constexpr static CellTypeDialog::HorizontalAlignment horizontal_alignment_from(Gfx::TextAlignment alignment) | ||||
| { | ||||
|     switch (alignment) { | ||||
|     case Gfx::TextAlignment::Center: | ||||
|         return CellTypeDialog::HorizontalAlignment::Center; | ||||
| 
 | ||||
|     case Gfx::TextAlignment::CenterRight: | ||||
|     case Gfx::TextAlignment::TopRight: | ||||
|     case Gfx::TextAlignment::BottomRight: | ||||
|         return CellTypeDialog::HorizontalAlignment::Right; | ||||
| 
 | ||||
|     case Gfx::TextAlignment::TopLeft: | ||||
|     case Gfx::TextAlignment::CenterLeft: | ||||
|         return CellTypeDialog::HorizontalAlignment::Left; | ||||
|     } | ||||
| 
 | ||||
|     return CellTypeDialog::HorizontalAlignment::Right; | ||||
| } | ||||
| 
 | ||||
| void CellTypeDialog::setup_tabs(GUI::TabWidget& tabs, const Vector<Position>& positions, Sheet& sheet) | ||||
| { | ||||
|     g_types.clear(); | ||||
|     for (auto& type_name : CellType::names()) | ||||
|         g_types.append(type_name); | ||||
| 
 | ||||
|     Vector<Cell*> cells; | ||||
|     for (auto& position : positions) { | ||||
|         if (auto cell = sheet.at(position)) | ||||
|             cells.append(cell); | ||||
|     } | ||||
| 
 | ||||
|     if (cells.size() == 1) { | ||||
|         auto& cell = *cells.first(); | ||||
|         m_format = cell.type_metadata().format; | ||||
|         m_length = cell.type_metadata().length; | ||||
|         m_type = &cell.type(); | ||||
|         m_vertical_alignment = vertical_alignment_from(cell.type_metadata().alignment); | ||||
|         m_horizontal_alignment = horizontal_alignment_from(cell.type_metadata().alignment); | ||||
|     } | ||||
| 
 | ||||
|     auto& type_tab = tabs.add_tab<GUI::Widget>("Type"); | ||||
|     type_tab.set_layout<GUI::HorizontalBoxLayout>().set_margins({ 2, 2, 2, 2 }); | ||||
|     { | ||||
|         auto& left_side = type_tab.add<GUI::Widget>(); | ||||
|         left_side.set_layout<GUI::VerticalBoxLayout>(); | ||||
|         auto& right_side = type_tab.add<GUI::Widget>(); | ||||
|         right_side.set_layout<GUI::VerticalBoxLayout>(); | ||||
|         right_side.set_preferred_size(170, 0); | ||||
|         right_side.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); | ||||
| 
 | ||||
|         auto& type_list = left_side.add<GUI::ListView>(); | ||||
|         type_list.set_model(*GUI::ItemListModel<String>::create(g_types)); | ||||
|         type_list.set_multi_select(false); | ||||
|         type_list.set_should_hide_unnecessary_scrollbars(true); | ||||
|         type_list.on_selection = [&](auto& index) { | ||||
|             if (!index.is_valid()) { | ||||
|                 m_type = nullptr; | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             m_type = CellType::get_by_name(g_types.at(index.row())); | ||||
|         }; | ||||
| 
 | ||||
|         { | ||||
|             auto& checkbox = right_side.add<GUI::CheckBox>("Override max length"); | ||||
|             auto& spinbox = right_side.add<GUI::SpinBox>(); | ||||
|             checkbox.set_checked(m_length != -1); | ||||
|             spinbox.set_min(0); | ||||
|             spinbox.set_enabled(m_length != -1); | ||||
|             if (m_length > -1) | ||||
|                 spinbox.set_value(m_length); | ||||
| 
 | ||||
|             checkbox.set_preferred_size(0, 20); | ||||
|             spinbox.set_preferred_size(0, 20); | ||||
|             checkbox.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
|             spinbox.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
| 
 | ||||
|             checkbox.on_checked = [&](auto checked) { | ||||
|                 spinbox.set_enabled(checked); | ||||
|                 if (!checked) { | ||||
|                     m_length = -1; | ||||
|                     spinbox.set_value(0); | ||||
|                 } | ||||
|             }; | ||||
|             spinbox.on_change = [&](auto value) { | ||||
|                 m_length = value; | ||||
|             }; | ||||
|         } | ||||
|         { | ||||
|             auto& checkbox = right_side.add<GUI::CheckBox>("Override display format"); | ||||
|             auto& editor = right_side.add<GUI::TextEditor>(); | ||||
|             checkbox.set_checked(!m_format.is_null()); | ||||
|             editor.set_should_hide_unnecessary_scrollbars(true); | ||||
|             editor.set_enabled(!m_format.is_null()); | ||||
|             editor.set_text(m_format); | ||||
| 
 | ||||
|             checkbox.set_preferred_size(0, 20); | ||||
|             editor.set_preferred_size(0, 20); | ||||
|             checkbox.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
|             editor.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
| 
 | ||||
|             checkbox.on_checked = [&](auto checked) { | ||||
|                 editor.set_enabled(checked); | ||||
|                 if (!checked) | ||||
|                     m_format = String::empty(); | ||||
|                 editor.set_text(m_format); | ||||
|             }; | ||||
|             editor.on_change = [&] { | ||||
|                 m_format = editor.text(); | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     auto& alignment_tab = tabs.add_tab<GUI::Widget>("Alignment"); | ||||
|     alignment_tab.set_layout<GUI::VerticalBoxLayout>().set_margins({ 2, 2, 2, 2 }); | ||||
| 
 | ||||
|     // FIXME: Frame?
 | ||||
|     // Horizontal alignment
 | ||||
|     { | ||||
|         auto& horizontal_alignment_selection_container = alignment_tab.add<GUI::Widget>(); | ||||
|         horizontal_alignment_selection_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||
|         horizontal_alignment_selection_container.layout()->set_margins({ 0, 4, 0, 0 }); | ||||
|         horizontal_alignment_selection_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
|         horizontal_alignment_selection_container.set_preferred_size(0, 22); | ||||
| 
 | ||||
|         auto& horizontal_alignment_label = horizontal_alignment_selection_container.add<GUI::Label>(); | ||||
|         horizontal_alignment_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||
|         horizontal_alignment_label.set_text("Horizontal Text Alignment"); | ||||
| 
 | ||||
|         auto& horizontal_combobox = alignment_tab.add<GUI::ComboBox>(); | ||||
|         horizontal_combobox.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
|         horizontal_combobox.set_preferred_size(0, 22); | ||||
|         horizontal_combobox.set_only_allow_values_from_model(true); | ||||
|         horizontal_combobox.set_model(*GUI::ItemListModel<String>::create(g_horizontal_alignments)); | ||||
|         horizontal_combobox.set_selected_index((int)m_horizontal_alignment); | ||||
|         horizontal_combobox.on_change = [&](auto&, const GUI::ModelIndex& index) { | ||||
|             switch (index.row()) { | ||||
|             case 0: | ||||
|                 m_horizontal_alignment = HorizontalAlignment::Left; | ||||
|                 break; | ||||
|             case 1: | ||||
|                 m_horizontal_alignment = HorizontalAlignment::Center; | ||||
|                 break; | ||||
|             case 2: | ||||
|                 m_horizontal_alignment = HorizontalAlignment::Right; | ||||
|                 break; | ||||
|             default: | ||||
|                 ASSERT_NOT_REACHED(); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     // Vertical alignment
 | ||||
|     { | ||||
|         auto& vertical_alignment_container = alignment_tab.add<GUI::Widget>(); | ||||
|         vertical_alignment_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||
|         vertical_alignment_container.layout()->set_margins({ 0, 4, 0, 0 }); | ||||
|         vertical_alignment_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
|         vertical_alignment_container.set_preferred_size(0, 22); | ||||
| 
 | ||||
|         auto& vertical_alignment_label = vertical_alignment_container.add<GUI::Label>(); | ||||
|         vertical_alignment_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||
|         vertical_alignment_label.set_text("Vertical Text Alignment"); | ||||
| 
 | ||||
|         auto& vertical_combobox = alignment_tab.add<GUI::ComboBox>(); | ||||
|         vertical_combobox.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||
|         vertical_combobox.set_preferred_size(0, 22); | ||||
|         vertical_combobox.set_only_allow_values_from_model(true); | ||||
|         vertical_combobox.set_model(*GUI::ItemListModel<String>::create(g_vertical_alignments)); | ||||
|         vertical_combobox.set_selected_index((int)m_vertical_alignment); | ||||
|         vertical_combobox.on_change = [&](auto&, const GUI::ModelIndex& index) { | ||||
|             switch (index.row()) { | ||||
|             case 0: | ||||
|                 m_vertical_alignment = VerticalAlignment::Top; | ||||
|                 break; | ||||
|             case 1: | ||||
|                 m_vertical_alignment = VerticalAlignment::Center; | ||||
|                 break; | ||||
|             case 2: | ||||
|                 m_vertical_alignment = VerticalAlignment::Bottom; | ||||
|                 break; | ||||
|             default: | ||||
|                 ASSERT_NOT_REACHED(); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| CellTypeMetadata CellTypeDialog::metadata() const | ||||
| { | ||||
|     CellTypeMetadata metadata; | ||||
|     metadata.format = m_format; | ||||
|     metadata.length = m_length; | ||||
| 
 | ||||
|     switch (m_vertical_alignment) { | ||||
|     case VerticalAlignment::Top: | ||||
|         switch (m_horizontal_alignment) { | ||||
|         case HorizontalAlignment::Left: | ||||
|             metadata.alignment = Gfx::TextAlignment::TopLeft; | ||||
|             break; | ||||
|         case HorizontalAlignment::Center: | ||||
|             metadata.alignment = Gfx::TextAlignment::Center; // TopCenter?
 | ||||
|             break; | ||||
|         case HorizontalAlignment::Right: | ||||
|             metadata.alignment = Gfx::TextAlignment::TopRight; | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     case VerticalAlignment::Center: | ||||
|         switch (m_horizontal_alignment) { | ||||
|         case HorizontalAlignment::Left: | ||||
|             metadata.alignment = Gfx::TextAlignment::CenterLeft; | ||||
|             break; | ||||
|         case HorizontalAlignment::Center: | ||||
|             metadata.alignment = Gfx::TextAlignment::Center; | ||||
|             break; | ||||
|         case HorizontalAlignment::Right: | ||||
|             metadata.alignment = Gfx::TextAlignment::CenterRight; | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     case VerticalAlignment::Bottom: | ||||
|         switch (m_horizontal_alignment) { | ||||
|         case HorizontalAlignment::Left: | ||||
|             metadata.alignment = Gfx::TextAlignment::CenterLeft; // BottomLeft?
 | ||||
|             break; | ||||
|         case HorizontalAlignment::Center: | ||||
|             metadata.alignment = Gfx::TextAlignment::Center; | ||||
|             break; | ||||
|         case HorizontalAlignment::Right: | ||||
|             metadata.alignment = Gfx::TextAlignment::BottomRight; | ||||
|             break; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return metadata; | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										65
									
								
								Applications/Spreadsheet/CellTypeDialog.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								Applications/Spreadsheet/CellTypeDialog.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, the SerenityOS developers. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * Redistribution and use in source and binary forms, with or without | ||||
|  * modification, are permitted provided that the following conditions are met: | ||||
|  * | ||||
|  * 1. Redistributions of source code must retain the above copyright notice, this | ||||
|  *    list of conditions and the following disclaimer. | ||||
|  * | ||||
|  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|  *    this list of conditions and the following disclaimer in the documentation | ||||
|  *    and/or other materials provided with the distribution. | ||||
|  * | ||||
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
|  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
|  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "CellType/Type.h" | ||||
| #include "Forward.h" | ||||
| #include <LibGUI/Dialog.h> | ||||
| 
 | ||||
| namespace Spreadsheet { | ||||
| 
 | ||||
| class CellTypeDialog : public GUI::Dialog { | ||||
|     C_OBJECT(CellTypeDialog); | ||||
| 
 | ||||
| public: | ||||
|     CellTypeMetadata metadata() const; | ||||
|     const CellType* type() const { return m_type; } | ||||
| 
 | ||||
|     enum class HorizontalAlignment : int { | ||||
|         Left = 0, | ||||
|         Center, | ||||
|         Right, | ||||
|     }; | ||||
|     enum class VerticalAlignment : int { | ||||
|         Top = 0, | ||||
|         Center, | ||||
|         Bottom, | ||||
|     }; | ||||
| 
 | ||||
| private: | ||||
|     CellTypeDialog(const Vector<Position>&, Sheet&, GUI::Window* parent = nullptr); | ||||
|     void setup_tabs(GUI::TabWidget&, const Vector<Position>&, Sheet&); | ||||
| 
 | ||||
|     const CellType* m_type { nullptr }; | ||||
| 
 | ||||
|     int m_length { -1 }; | ||||
|     String m_format; | ||||
|     HorizontalAlignment m_horizontal_alignment { HorizontalAlignment::Right }; | ||||
|     VerticalAlignment m_vertical_alignment { VerticalAlignment::Center }; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -67,9 +67,14 @@ GUI::Variant SheetModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) | |||
|         return cell->typed_display(); | ||||
|     } | ||||
| 
 | ||||
|     if (role == GUI::ModelRole::TextAlignment) | ||||
|     if (role == GUI::ModelRole::TextAlignment) { | ||||
|         const auto* cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); | ||||
|         if (!cell) | ||||
|             return {}; | ||||
| 
 | ||||
|         return cell->type_metadata().alignment; | ||||
|     } | ||||
| 
 | ||||
|     if (role == GUI::ModelRole::ForegroundColor) { | ||||
|         const auto* cell = m_sheet->at({ m_sheet->column(index.column()), (size_t)index.row() }); | ||||
|         if (!cell) | ||||
|  |  | |||
|  | @ -25,9 +25,11 @@ | |||
|  */ | ||||
| 
 | ||||
| #include "SpreadsheetView.h" | ||||
| #include "CellTypeDialog.h" | ||||
| #include "SpreadsheetModel.h" | ||||
| #include <LibGUI/BoxLayout.h> | ||||
| #include <LibGUI/HeaderView.h> | ||||
| #include <LibGUI/Menu.h> | ||||
| #include <LibGUI/ModelEditingDelegate.h> | ||||
| #include <LibGUI/Painter.h> | ||||
| #include <LibGUI/TableView.h> | ||||
|  | @ -112,6 +114,37 @@ SpreadsheetView::SpreadsheetView(Sheet& sheet) | |||
|     m_table_view->on_activation = [this](auto&) { | ||||
|         m_table_view->move_cursor(GUI::AbstractView::CursorMovement::Down, GUI::AbstractView::SelectionUpdate::Set); | ||||
|     }; | ||||
| 
 | ||||
|     m_table_view->on_context_menu_request = [&](const GUI::ModelIndex&, const GUI::ContextMenuEvent& event) { | ||||
|         // NOTE: We ignore the specific cell for now.
 | ||||
|         m_cell_range_context_menu->popup(event.screen_position()); | ||||
|     }; | ||||
| 
 | ||||
|     m_cell_range_context_menu = GUI::Menu::construct(); | ||||
|     m_cell_range_context_menu->add_action(GUI::Action::create("Type and Formatting...", [this](auto&) { | ||||
|         Vector<Position> positions; | ||||
|         for (auto& index : m_table_view->selection().indexes()) { | ||||
|             Position position { m_sheet->column(index.column()), (size_t)index.row() }; | ||||
|             positions.append(move(position)); | ||||
|         } | ||||
| 
 | ||||
|         if (positions.is_empty()) { | ||||
|             auto& index = m_table_view->cursor_index(); | ||||
|             Position position { m_sheet->column(index.column()), (size_t)index.row() }; | ||||
|             positions.append(move(position)); | ||||
|         } | ||||
| 
 | ||||
|         auto dialog = CellTypeDialog::construct(positions, *m_sheet, window()); | ||||
|         if (dialog->exec() == GUI::Dialog::ExecOK) { | ||||
|             for (auto& position : positions) { | ||||
|                 auto& cell = m_sheet->ensure(position); | ||||
|                 cell.set_type(dialog->type()); | ||||
|                 cell.set_type_metadata(dialog->metadata()); | ||||
|             } | ||||
| 
 | ||||
|             m_table_view->update(); | ||||
|         } | ||||
|     })); | ||||
| } | ||||
| 
 | ||||
| void SpreadsheetView::hide_event(GUI::HideEvent&) | ||||
|  |  | |||
|  | @ -138,6 +138,7 @@ private: | |||
| 
 | ||||
|     NonnullRefPtr<Sheet> m_sheet; | ||||
|     RefPtr<GUI::TableView> m_table_view; | ||||
|     RefPtr<GUI::Menu> m_cell_range_context_menu; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 AnotherTest
						AnotherTest