mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:32:46 +00:00 
			
		
		
		
	LibGUI: Use enum for TextEditor modes & add new DisplayOnly mode
Adds a new, more restrictive read-only state to TextEditor which forbids copying, selecting, editor cursors, and context menus. Provides a unique appearance on focus which accomodates ComboBox widgets. All TextEditor modes are now accessed by enum and set_mode() which sets the editor to Editable, ReadOnly or DisplayOnly. Updates applications still using set_readonly().
This commit is contained in:
		
							parent
							
								
									dc716194c8
								
							
						
					
					
						commit
						b2783a234a
					
				
					 6 changed files with 92 additions and 18 deletions
				
			
		|  | @ -261,7 +261,7 @@ Tab::Tab(Type type) | ||||||
|                 auto window = GUI::Window::construct(); |                 auto window = GUI::Window::construct(); | ||||||
|                 auto& editor = window->set_main_widget<GUI::TextEditor>(); |                 auto& editor = window->set_main_widget<GUI::TextEditor>(); | ||||||
|                 editor.set_text(source); |                 editor.set_text(source); | ||||||
|                 editor.set_readonly(true); |                 editor.set_mode(GUI::TextEditor::ReadOnly); | ||||||
|                 editor.set_ruler_visible(true); |                 editor.set_ruler_visible(true); | ||||||
|                 window->set_rect(150, 150, 640, 480); |                 window->set_rect(150, 150, 640, 480); | ||||||
|                 window->set_title(url); |                 window->set_title(url); | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ ProcessStacksWidget::ProcessStacksWidget() | ||||||
|     set_layout<GUI::VerticalBoxLayout>(); |     set_layout<GUI::VerticalBoxLayout>(); | ||||||
|     layout()->set_margins({ 4, 4, 4, 4 }); |     layout()->set_margins({ 4, 4, 4, 4 }); | ||||||
|     m_stacks_editor = add<GUI::TextEditor>(); |     m_stacks_editor = add<GUI::TextEditor>(); | ||||||
|     m_stacks_editor->set_readonly(true); |     m_stacks_editor->set_mode(GUI::TextEditor::ReadOnly); | ||||||
| 
 | 
 | ||||||
|     m_timer = add<Core::Timer>(1000, [this] { refresh(); }); |     m_timer = add<Core::Timer>(1000, [this] { refresh(); }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -734,11 +734,11 @@ void open_file(const String& filename) | ||||||
|     auto project_file = g_project->get_file(filename); |     auto project_file = g_project->get_file(filename); | ||||||
|     if (project_file) { |     if (project_file) { | ||||||
|         current_editor().set_document(const_cast<GUI::TextDocument&>(project_file->document())); |         current_editor().set_document(const_cast<GUI::TextDocument&>(project_file->document())); | ||||||
|         current_editor().set_readonly(false); |         current_editor().set_mode(GUI::TextEditor::Editable); | ||||||
|     } else { |     } else { | ||||||
|         auto external_file = ProjectFile::construct_with_name(filename); |         auto external_file = ProjectFile::construct_with_name(filename); | ||||||
|         current_editor().set_document(const_cast<GUI::TextDocument&>(external_file->document())); |         current_editor().set_document(const_cast<GUI::TextDocument&>(external_file->document())); | ||||||
|         current_editor().set_readonly(true); |         current_editor().set_mode(GUI::TextEditor::ReadOnly); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (filename.ends_with(".cpp") || filename.ends_with(".h")) |     if (filename.ends_with(".cpp") || filename.ends_with(".h")) | ||||||
|  |  | ||||||
|  | @ -177,7 +177,7 @@ void ComboBox::set_only_allow_values_from_model(bool b) | ||||||
|     if (m_only_allow_values_from_model == b) |     if (m_only_allow_values_from_model == b) | ||||||
|         return; |         return; | ||||||
|     m_only_allow_values_from_model = b; |     m_only_allow_values_from_model = b; | ||||||
|     m_editor->set_readonly(m_only_allow_values_from_model); |     m_editor->set_mode(m_only_allow_values_from_model ? TextEditor::DisplayOnly : TextEditor::Editable); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Model* ComboBox::model() | Model* ComboBox::model() | ||||||
|  |  | ||||||
|  | @ -201,6 +201,9 @@ void TextEditor::doubleclick_event(MouseEvent& event) | ||||||
|     if (event.button() != MouseButton::Left) |     if (event.button() != MouseButton::Left) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|  |     if (is_displayonly()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|     // NOTE: This ensures that spans are updated before we look at them.
 |     // NOTE: This ensures that spans are updated before we look at them.
 | ||||||
|     flush_pending_change_notification_if_needed(); |     flush_pending_change_notification_if_needed(); | ||||||
| 
 | 
 | ||||||
|  | @ -236,6 +239,12 @@ void TextEditor::mousedown_event(MouseEvent& event) | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (on_mousedown) | ||||||
|  |         on_mousedown(); | ||||||
|  | 
 | ||||||
|  |     if (is_displayonly()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|     if (m_triple_click_timer.is_valid() && m_triple_click_timer.elapsed() < 250) { |     if (m_triple_click_timer.is_valid() && m_triple_click_timer.elapsed() < 250) { | ||||||
|         m_triple_click_timer = Core::ElapsedTimer(); |         m_triple_click_timer = Core::ElapsedTimer(); | ||||||
| 
 | 
 | ||||||
|  | @ -364,6 +373,19 @@ void TextEditor::paint_event(PaintEvent& event) | ||||||
|     painter.add_clip_rect(event.rect()); |     painter.add_clip_rect(event.rect()); | ||||||
|     painter.fill_rect(event.rect(), widget_background_color); |     painter.fill_rect(event.rect(), widget_background_color); | ||||||
| 
 | 
 | ||||||
|  |     if (is_displayonly() && (is_focused() || has_visible_list())) { | ||||||
|  |         widget_background_color = palette().selection(); | ||||||
|  |         Gfx::IntRect display_rect { | ||||||
|  |             widget_inner_rect().x() + 1, | ||||||
|  |             widget_inner_rect().y() + 1, | ||||||
|  |             widget_inner_rect().width() - button_padding(), | ||||||
|  |             widget_inner_rect().height() - 2 | ||||||
|  |         }; | ||||||
|  |         painter.add_clip_rect(display_rect); | ||||||
|  |         painter.add_clip_rect(event.rect()); | ||||||
|  |         painter.fill_rect(event.rect(), widget_background_color); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     painter.translate(frame_thickness(), frame_thickness()); |     painter.translate(frame_thickness(), frame_thickness()); | ||||||
| 
 | 
 | ||||||
|     auto ruler_rect = ruler_rect_in_inner_coordinates(); |     auto ruler_rect = ruler_rect_in_inner_coordinates(); | ||||||
|  | @ -438,6 +460,8 @@ void TextEditor::paint_event(PaintEvent& event) | ||||||
|             if (!document().has_spans()) { |             if (!document().has_spans()) { | ||||||
|                 // Fast-path for plain text
 |                 // Fast-path for plain text
 | ||||||
|                 auto color = palette().color(is_enabled() ? foreground_role() : Gfx::ColorRole::DisabledText); |                 auto color = palette().color(is_enabled() ? foreground_role() : Gfx::ColorRole::DisabledText); | ||||||
|  |                 if (is_displayonly() && (is_focused() || has_visible_list())) | ||||||
|  |                     color = palette().color(is_enabled() ? Gfx::ColorRole::SelectionText : Gfx::ColorRole::DisabledText); | ||||||
|                 painter.draw_text(visual_line_rect, visual_line_text, m_text_alignment, color); |                 painter.draw_text(visual_line_rect, visual_line_text, m_text_alignment, color); | ||||||
|             } else { |             } else { | ||||||
|                 Gfx::IntRect character_rect = { visual_line_rect.location(), { 0, line_height() } }; |                 Gfx::IntRect character_rect = { visual_line_rect.location(), { 0, line_height() } }; | ||||||
|  | @ -521,7 +545,7 @@ void TextEditor::paint_event(PaintEvent& event) | ||||||
|         painter.draw_scaled_bitmap(icon_rect, *m_icon, m_icon->rect()); |         painter.draw_scaled_bitmap(icon_rect, *m_icon, m_icon->rect()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (is_focused() && m_cursor_state) |     if (is_focused() && m_cursor_state && !is_displayonly()) | ||||||
|         painter.fill_rect(cursor_content_rect(), palette().text_cursor()); |         painter.fill_rect(cursor_content_rect(), palette().text_cursor()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1269,7 +1293,8 @@ void TextEditor::undefer_reflow() | ||||||
| void TextEditor::enter_event(Core::Event&) | void TextEditor::enter_event(Core::Event&) | ||||||
| { | { | ||||||
|     ASSERT(window()); |     ASSERT(window()); | ||||||
|     window()->set_override_cursor(StandardCursor::IBeam); |     if (!is_displayonly()) | ||||||
|  |         window()->set_override_cursor(StandardCursor::IBeam); | ||||||
| 
 | 
 | ||||||
|     m_automatic_selection_scroll_timer->stop(); |     m_automatic_selection_scroll_timer->stop(); | ||||||
| } | } | ||||||
|  | @ -1302,15 +1327,42 @@ void TextEditor::did_change() | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | void TextEditor::set_mode(const Mode mode) | ||||||
| void TextEditor::set_readonly(bool readonly) |  | ||||||
| { | { | ||||||
|     if (m_readonly == readonly) |     if (m_mode == mode) | ||||||
|         return; |         return; | ||||||
|     m_readonly = readonly; |     m_mode = mode; | ||||||
|     m_cut_action->set_enabled(!is_readonly() && has_selection()); |     switch (mode) { | ||||||
|     m_delete_action->set_enabled(!is_readonly()); |     case Editable: | ||||||
|     m_paste_action->set_enabled(!is_readonly()); |         m_cut_action->set_enabled(true && has_selection()); | ||||||
|  |         m_delete_action->set_enabled(true); | ||||||
|  |         m_paste_action->set_enabled(true); | ||||||
|  |         set_accepts_emoji_input(true); | ||||||
|  |         break; | ||||||
|  |     case ReadOnly: | ||||||
|  |     case DisplayOnly: | ||||||
|  |         m_cut_action->set_enabled(false && has_selection()); | ||||||
|  |         m_delete_action->set_enabled(false); | ||||||
|  |         m_paste_action->set_enabled(false); | ||||||
|  |         set_accepts_emoji_input(false); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ASSERT_NOT_REACHED(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TextEditor::set_has_open_button(bool has_button) | ||||||
|  | { | ||||||
|  |     if (m_has_open_button == has_button) | ||||||
|  |         return; | ||||||
|  |     m_has_open_button = has_button; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TextEditor::set_has_visible_list(bool visible) | ||||||
|  | { | ||||||
|  |     if (m_has_visible_list == visible) | ||||||
|  |         return; | ||||||
|  |     m_has_visible_list = visible; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TextEditor::did_update_selection() | void TextEditor::did_update_selection() | ||||||
|  | @ -1327,6 +1379,9 @@ void TextEditor::did_update_selection() | ||||||
| 
 | 
 | ||||||
| void TextEditor::context_menu_event(ContextMenuEvent& event) | void TextEditor::context_menu_event(ContextMenuEvent& event) | ||||||
| { | { | ||||||
|  |     if (is_displayonly()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|     if (!m_context_menu) { |     if (!m_context_menu) { | ||||||
|         m_context_menu = Menu::construct(); |         m_context_menu = Menu::construct(); | ||||||
|         m_context_menu->add_action(undo_action()); |         m_context_menu->add_action(undo_action()); | ||||||
|  |  | ||||||
|  | @ -46,6 +46,13 @@ public: | ||||||
|         MultiLine, |         MultiLine, | ||||||
|         SingleLine |         SingleLine | ||||||
|     }; |     }; | ||||||
|  | 
 | ||||||
|  |     enum Mode { | ||||||
|  |         Editable, | ||||||
|  |         ReadOnly, | ||||||
|  |         DisplayOnly | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     virtual ~TextEditor() override; |     virtual ~TextEditor() override; | ||||||
| 
 | 
 | ||||||
|     const TextDocument& document() const { return *m_document; } |     const TextDocument& document() const { return *m_document; } | ||||||
|  | @ -53,8 +60,10 @@ public: | ||||||
| 
 | 
 | ||||||
|     void set_document(TextDocument&); |     void set_document(TextDocument&); | ||||||
| 
 | 
 | ||||||
|     bool is_readonly() const { return m_readonly; } |     bool has_visible_list() const { return m_has_visible_list; } | ||||||
|     void set_readonly(bool); |     void set_has_visible_list(bool); | ||||||
|  |     bool has_open_button() const { return m_has_open_button; } | ||||||
|  |     void set_has_open_button(bool); | ||||||
| 
 | 
 | ||||||
|     virtual bool is_automatic_indentation_enabled() const final { return m_automatic_indentation_enabled; } |     virtual bool is_automatic_indentation_enabled() const final { return m_automatic_indentation_enabled; } | ||||||
|     void set_automatic_indentation_enabled(bool enabled) { m_automatic_indentation_enabled = enabled; } |     void set_automatic_indentation_enabled(bool enabled) { m_automatic_indentation_enabled = enabled; } | ||||||
|  | @ -71,6 +80,12 @@ public: | ||||||
|     bool is_single_line() const { return m_type == SingleLine; } |     bool is_single_line() const { return m_type == SingleLine; } | ||||||
|     bool is_multi_line() const { return m_type == MultiLine; } |     bool is_multi_line() const { return m_type == MultiLine; } | ||||||
| 
 | 
 | ||||||
|  |     Mode mode() const { return m_mode; } | ||||||
|  |     bool is_editable() const { return m_mode == Editable; } | ||||||
|  |     bool is_readonly() const { return m_mode == ReadOnly; } | ||||||
|  |     bool is_displayonly() const { return m_mode == DisplayOnly; } | ||||||
|  |     void set_mode(const Mode); | ||||||
|  | 
 | ||||||
|     bool is_ruler_visible() const { return m_ruler_visible; } |     bool is_ruler_visible() const { return m_ruler_visible; } | ||||||
|     void set_ruler_visible(bool b) { m_ruler_visible = b; } |     void set_ruler_visible(bool b) { m_ruler_visible = b; } | ||||||
| 
 | 
 | ||||||
|  | @ -153,7 +168,7 @@ protected: | ||||||
|     virtual void context_menu_event(ContextMenuEvent&) override; |     virtual void context_menu_event(ContextMenuEvent&) override; | ||||||
|     virtual void resize_event(ResizeEvent&) override; |     virtual void resize_event(ResizeEvent&) override; | ||||||
|     virtual void theme_change_event(ThemeChangeEvent&) override; |     virtual void theme_change_event(ThemeChangeEvent&) override; | ||||||
|     virtual void cursor_did_change() {} |     virtual void cursor_did_change() { } | ||||||
|     Gfx::IntRect ruler_content_rect(size_t line) const; |     Gfx::IntRect ruler_content_rect(size_t line) const; | ||||||
| 
 | 
 | ||||||
|     TextPosition text_position_at(const Gfx::IntPoint&) const; |     TextPosition text_position_at(const Gfx::IntPoint&) const; | ||||||
|  | @ -182,6 +197,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     int icon_size() const { return 16; } |     int icon_size() const { return 16; } | ||||||
|     int icon_padding() const { return 2; } |     int icon_padding() const { return 2; } | ||||||
|  |     int button_padding() const { return m_has_open_button ? 17 : 2; } | ||||||
| 
 | 
 | ||||||
|     class ReflowDeferrer { |     class ReflowDeferrer { | ||||||
|     public: |     public: | ||||||
|  | @ -194,6 +210,7 @@ private: | ||||||
|         { |         { | ||||||
|             m_editor.undefer_reflow(); |             m_editor.undefer_reflow(); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|     private: |     private: | ||||||
|         TextEditor& m_editor; |         TextEditor& m_editor; | ||||||
|     }; |     }; | ||||||
|  | @ -238,6 +255,7 @@ private: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Type m_type { MultiLine }; |     Type m_type { MultiLine }; | ||||||
|  |     Mode m_mode { Editable }; | ||||||
| 
 | 
 | ||||||
|     TextPosition m_cursor; |     TextPosition m_cursor; | ||||||
|     Gfx::TextAlignment m_text_alignment { Gfx::TextAlignment::CenterLeft }; |     Gfx::TextAlignment m_text_alignment { Gfx::TextAlignment::CenterLeft }; | ||||||
|  | @ -247,7 +265,8 @@ private: | ||||||
|     bool m_has_pending_change_notification { false }; |     bool m_has_pending_change_notification { false }; | ||||||
|     bool m_automatic_indentation_enabled { false }; |     bool m_automatic_indentation_enabled { false }; | ||||||
|     bool m_line_wrapping_enabled { false }; |     bool m_line_wrapping_enabled { false }; | ||||||
|     bool m_readonly { false }; |     bool m_has_visible_list { false }; | ||||||
|  |     bool m_has_open_button { false }; | ||||||
|     int m_line_spacing { 4 }; |     int m_line_spacing { 4 }; | ||||||
|     size_t m_soft_tab_width { 4 }; |     size_t m_soft_tab_width { 4 }; | ||||||
|     int m_horizontal_content_padding { 3 }; |     int m_horizontal_content_padding { 3 }; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 thankyouverycool
						thankyouverycool