mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:42:43 +00:00 
			
		
		
		
	TextEditor: Replaced 'Find' button with 'Prev' and 'Next' buttons.
This commit is contained in:
		
							parent
							
								
									952baf32cd
								
							
						
					
					
						commit
						e75e33eb46
					
				
					 4 changed files with 87 additions and 11 deletions
				
			
		|  | @ -34,14 +34,39 @@ TextEditorWidget::TextEditorWidget() | |||
| 
 | ||||
|     m_find_textbox = new GTextBox(m_find_widget); | ||||
| 
 | ||||
|     m_find_button = new GButton("Find", m_find_widget); | ||||
|     m_find_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill); | ||||
|     m_find_button->set_preferred_size(100, 0); | ||||
|     m_find_prev_button = new GButton("Prev", m_find_widget); | ||||
|     m_find_prev_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill); | ||||
|     m_find_prev_button->set_preferred_size(50, 0); | ||||
| 
 | ||||
|     m_find_button->on_click = [this](auto&) { | ||||
|     m_find_next_button = new GButton("Next", m_find_widget); | ||||
|     m_find_next_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill); | ||||
|     m_find_next_button->set_preferred_size(50, 0); | ||||
| 
 | ||||
|     m_find_next_button->on_click = [this](auto&) { | ||||
|         auto needle = m_find_textbox->text(); | ||||
|         auto found_range = m_editor->find(needle, m_editor->normalized_selection().end()); | ||||
|         dbg() << "find(\"" << needle << "\") returned " << found_range; | ||||
|         auto found_range = m_editor->find_next(needle, m_editor->normalized_selection().end()); | ||||
|         dbg() << "find_next(\"" << needle << "\") returned " << found_range; | ||||
|         if (found_range.is_valid()) { | ||||
|             m_editor->set_selection(found_range); | ||||
|         } else { | ||||
|             GMessageBox::show( | ||||
|                 String::format("Not found: \"%s\"", needle.characters()), | ||||
|                 "Not found", | ||||
|                 GMessageBox::Type::Information, | ||||
|                 GMessageBox::InputType::OK, window()); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     m_find_prev_button->on_click = [this](auto&) { | ||||
|         auto needle = m_find_textbox->text(); | ||||
| 
 | ||||
|         auto selection_start = m_editor->normalized_selection().start(); | ||||
|         if (!selection_start.is_valid()) | ||||
|             selection_start = m_editor->normalized_selection().end(); | ||||
| 
 | ||||
|         auto found_range = m_editor->find_prev(needle, selection_start); | ||||
|          | ||||
|         dbg() << "find_prev(\"" << needle << "\") returned " << found_range; | ||||
|         if (found_range.is_valid()) { | ||||
|             m_editor->set_selection(found_range); | ||||
|         } else { | ||||
|  | @ -54,7 +79,7 @@ TextEditorWidget::TextEditorWidget() | |||
|     }; | ||||
| 
 | ||||
|     m_find_textbox->on_return_pressed = [this] { | ||||
|         m_find_button->click(); | ||||
|         m_find_next_button->click(); | ||||
|     }; | ||||
| 
 | ||||
|     m_find_textbox->on_escape_pressed = [this] { | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ private: | |||
|     RefPtr<GAction> m_find_action; | ||||
| 
 | ||||
|     GTextBox* m_find_textbox { nullptr }; | ||||
|     GButton* m_find_button { nullptr }; | ||||
|     GButton* m_find_prev_button { nullptr }; | ||||
|     GButton* m_find_next_button { nullptr }; | ||||
|     GWidget* m_find_widget { nullptr }; | ||||
| }; | ||||
|  |  | |||
|  | @ -1105,7 +1105,23 @@ GTextPosition GTextEditor::next_position_after(const GTextPosition& position, Sh | |||
|     return { position.line(), position.column() + 1 }; | ||||
| } | ||||
| 
 | ||||
| GTextRange GTextEditor::find(const StringView& needle, const GTextPosition& start) | ||||
| GTextPosition GTextEditor::prev_position_before(const GTextPosition& position, ShouldWrapAtStartOfDocument should_wrap) | ||||
| { | ||||
|     if (position.column() == 0){ | ||||
|         if (position.line() == 0) { | ||||
|             if (should_wrap == ShouldWrapAtStartOfDocument::Yes) { | ||||
|                 auto& last_line = m_lines[line_count() - 1];  | ||||
|                 return { line_count() - 1, last_line.length() }; | ||||
|             } | ||||
|             return {}; | ||||
|         } | ||||
|         auto& prev_line = m_lines[position.line() - 1]; | ||||
|         return { position.line() - 1, prev_line.length() }; | ||||
|     } | ||||
|     return { position.line(), position.column() - 1 }; | ||||
| } | ||||
| 
 | ||||
| GTextRange GTextEditor::find_next(const StringView& needle, const GTextPosition& start) | ||||
| { | ||||
|     if (needle.is_empty()) | ||||
|         return {}; | ||||
|  | @ -1135,6 +1151,36 @@ GTextRange GTextEditor::find(const StringView& needle, const GTextPosition& star | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| GTextRange GTextEditor::find_prev(const StringView& needle, const GTextPosition& start) | ||||
| { | ||||
|     if (needle.is_empty()) | ||||
|         return {}; | ||||
| 
 | ||||
|     GTextPosition position = start.is_valid() ? start : GTextPosition(0, 0); | ||||
|     GTextPosition original_position = position; | ||||
| 
 | ||||
|     GTextPosition end_of_potential_match; | ||||
|     int needle_index = needle.length() - 1; | ||||
| 
 | ||||
|     do { | ||||
|         auto ch = character_at(position); | ||||
|         if (ch == needle[needle_index]) { | ||||
|             if (needle_index == needle.length() - 1) | ||||
|                 end_of_potential_match = position; | ||||
|             --needle_index; | ||||
|             if (needle_index < 0) | ||||
|                 return { position, next_position_after(end_of_potential_match) }; | ||||
|         } else { | ||||
|             if (needle_index < needle.length() - 1) | ||||
|                 position = end_of_potential_match; | ||||
|             needle_index = needle.length() - 1; | ||||
|         } | ||||
|         position = prev_position_before(position); | ||||
|     } while(position.is_valid() && position != original_position); | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| void GTextEditor::set_selection(const GTextRange& selection) | ||||
| { | ||||
|     if (m_selection == selection) | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ class GScrollBar; | |||
| class Painter; | ||||
| 
 | ||||
| enum class ShouldWrapAtEndOfDocument { No = 0, Yes }; | ||||
| enum class ShouldWrapAtStartOfDocument { No = 0, Yes }; | ||||
| 
 | ||||
| class GTextPosition { | ||||
| public: | ||||
|  | @ -126,9 +127,12 @@ public: | |||
| 
 | ||||
|     bool write_to_file(const StringView& path); | ||||
| 
 | ||||
|     GTextRange find(const StringView&, const GTextPosition& start = {}); | ||||
|     GTextPosition next_position_after(const GTextPosition&, ShouldWrapAtEndOfDocument = ShouldWrapAtEndOfDocument::Yes); | ||||
|     GTextRange find_next(const StringView&, const GTextPosition& start = {}); | ||||
|     GTextRange find_prev(const StringView&, const GTextPosition& start = {}); | ||||
| 
 | ||||
|     GTextPosition next_position_after(const GTextPosition&, ShouldWrapAtEndOfDocument = ShouldWrapAtEndOfDocument::Yes); | ||||
|     GTextPosition prev_position_before(const GTextPosition&, ShouldWrapAtStartOfDocument = ShouldWrapAtStartOfDocument::Yes); | ||||
|      | ||||
|     bool has_selection() const { return m_selection.is_valid(); } | ||||
|     String selected_text() const; | ||||
|     void set_selection(const GTextRange&); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andrew Weller
						Andrew Weller