| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-15 00:27:50 +01:00
										 |  |  | #include <AK/Utf8View.h>
 | 
					
						
							| 
									
										
										
										
											2021-02-10 08:25:35 +01:00
										 |  |  | #include <LibGfx/Painter.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-03 10:09:10 +01:00
										 |  |  | #include <LibWeb/Layout/FormattingState.h>
 | 
					
						
							| 
									
										
										
										
											2021-09-08 11:27:46 +02:00
										 |  |  | #include <LibWeb/Layout/InitialContainingBlock.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 10:32:51 +01:00
										 |  |  | #include <LibWeb/Layout/LineBoxFragment.h>
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | #include <LibWeb/Layout/TextNode.h>
 | 
					
						
							| 
									
										
										
										
											2020-06-18 21:35:44 +02:00
										 |  |  | #include <LibWeb/Painting/PaintContext.h>
 | 
					
						
							| 
									
										
										
										
											2020-06-13 14:59:17 +02:00
										 |  |  | #include <ctype.h>
 | 
					
						
							| 
									
										
										
										
											2019-10-03 15:20:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | namespace Web::Layout { | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 19:18:01 +01:00
										 |  |  | void LineBoxFragment::paint(PaintContext& context, PaintPhase phase) | 
					
						
							| 
									
										
										
										
											2019-10-03 15:20:13 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-09 21:25:29 +02:00
										 |  |  |     for (auto* ancestor = layout_node().parent(); ancestor; ancestor = ancestor->parent()) { | 
					
						
							|  |  |  |         if (!ancestor->is_visible()) | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 19:30:48 +01:00
										 |  |  |     layout_node().paint_fragment(context, *this, phase); | 
					
						
							| 
									
										
										
										
											2019-10-03 15:20:13 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-20 12:30:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 14:59:17 +02:00
										 |  |  | bool LineBoxFragment::ends_in_whitespace() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto text = this->text(); | 
					
						
							|  |  |  |     if (text.is_empty()) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     return isspace(text[text.length() - 1]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 12:30:25 +02:00
										 |  |  | bool LineBoxFragment::is_justifiable_whitespace() const | 
					
						
							| 
									
										
										
										
											2019-10-20 17:18:28 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     return text() == " "; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | StringView LineBoxFragment::text() const | 
					
						
							| 
									
										
										
										
											2019-10-20 12:30:25 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     if (!is<TextNode>(layout_node())) | 
					
						
							| 
									
										
										
										
											2019-10-20 17:18:28 +02:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2021-06-24 19:53:42 +02:00
										 |  |  |     return verify_cast<TextNode>(layout_node()).text_for_rendering().substring_view(m_start, m_length); | 
					
						
							| 
									
										
										
										
											2019-10-20 12:30:25 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 10:42:29 +02:00
										 |  |  | const Gfx::FloatRect LineBoxFragment::absolute_rect() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Gfx::FloatRect rect { {}, size() }; | 
					
						
							| 
									
										
										
										
											2022-03-09 23:53:41 +01:00
										 |  |  |     rect.set_location(m_layout_node.containing_block()->m_paint_box->absolute_position()); | 
					
						
							| 
									
										
										
										
											2021-04-12 11:47:09 -07:00
										 |  |  |     rect.translate_by(offset()); | 
					
						
							| 
									
										
										
										
											2020-06-10 10:42:29 +02:00
										 |  |  |     return rect; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  | int LineBoxFragment::text_index_at(float x) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-01-01 18:55:47 +01:00
										 |  |  |     if (!is<TextNode>(layout_node())) | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2021-06-24 19:53:42 +02:00
										 |  |  |     auto& layout_text = verify_cast<TextNode>(layout_node()); | 
					
						
							| 
									
										
										
										
											2021-01-06 11:05:23 +01:00
										 |  |  |     auto& font = layout_text.font(); | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  |     Utf8View view(text()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 10:42:29 +02:00
										 |  |  |     float relative_x = x - absolute_x(); | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  |     float glyph_spacing = font.glyph_spacing(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 12:43:45 +02:00
										 |  |  |     if (relative_x < 0) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  |     float width_so_far = 0; | 
					
						
							|  |  |  |     for (auto it = view.begin(); it != view.end(); ++it) { | 
					
						
							|  |  |  |         float glyph_width = font.glyph_or_emoji_width(*it); | 
					
						
							| 
									
										
										
										
											2020-08-26 11:31:11 +12:00
										 |  |  |         if ((width_so_far + (glyph_width + glyph_spacing) / 2) > relative_x) | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  |             return m_start + view.byte_offset_of(it); | 
					
						
							|  |  |  |         width_so_far += glyph_width + glyph_spacing; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-08-26 11:31:11 +12:00
										 |  |  |     return m_start + m_length; | 
					
						
							| 
									
										
										
										
											2019-11-05 22:13:26 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  | Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     if (layout_node().selection_state() == Node::SelectionState::None) | 
					
						
							| 
									
										
										
										
											2020-08-21 17:50:41 +02:00
										 |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     if (layout_node().selection_state() == Node::SelectionState::Full) | 
					
						
							| 
									
										
										
										
											2020-08-21 17:50:41 +02:00
										 |  |  |         return absolute_rect(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 00:24:35 +02:00
										 |  |  |     auto selection = layout_node().root().selection().normalized(); | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  |     if (!selection.is_valid()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2021-01-01 18:55:47 +01:00
										 |  |  |     if (!is<TextNode>(layout_node())) | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto start_index = m_start; | 
					
						
							|  |  |  |     const auto end_index = m_start + m_length; | 
					
						
							| 
									
										
										
										
											2020-06-29 12:43:45 +02:00
										 |  |  |     auto text = this->text(); | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     if (layout_node().selection_state() == Node::SelectionState::StartAndEnd) { | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  |         // we are in the start/end node (both the same)
 | 
					
						
							|  |  |  |         if (start_index > selection.end().index_in_node) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         if (end_index < selection.start().index_in_node) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 21:00:26 +02:00
										 |  |  |         if (selection.start().index_in_node == selection.end().index_in_node) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 12:43:45 +02:00
										 |  |  |         auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start); | 
					
						
							| 
									
										
										
										
											2020-08-26 11:31:11 +12:00
										 |  |  |         auto selection_end_in_this_fragment = min(m_length, selection.end().index_in_node - m_start); | 
					
						
							| 
									
										
										
										
											2020-06-29 12:43:45 +02:00
										 |  |  |         auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment)); | 
					
						
							|  |  |  |         auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1; | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         auto rect = absolute_rect(); | 
					
						
							|  |  |  |         rect.set_x(rect.x() + pixel_distance_to_first_selected_character); | 
					
						
							|  |  |  |         rect.set_width(pixel_width_of_selection); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return rect; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     if (layout_node().selection_state() == Node::SelectionState::Start) { | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  |         // we are in the start node
 | 
					
						
							|  |  |  |         if (end_index < selection.start().index_in_node) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 12:43:45 +02:00
										 |  |  |         auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start); | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  |         auto selection_end_in_this_fragment = m_length; | 
					
						
							| 
									
										
										
										
											2020-06-29 12:43:45 +02:00
										 |  |  |         auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment)); | 
					
						
							|  |  |  |         auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1; | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         auto rect = absolute_rect(); | 
					
						
							|  |  |  |         rect.set_x(rect.x() + pixel_distance_to_first_selected_character); | 
					
						
							|  |  |  |         rect.set_width(pixel_width_of_selection); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return rect; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     if (layout_node().selection_state() == Node::SelectionState::End) { | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  |         // we are in the end node
 | 
					
						
							|  |  |  |         if (start_index > selection.end().index_in_node) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto selection_start_in_this_fragment = 0; | 
					
						
							| 
									
										
										
										
											2021-08-11 22:30:38 +09:00
										 |  |  |         auto selection_end_in_this_fragment = min(selection.end().index_in_node - m_start, m_length); | 
					
						
							| 
									
										
										
										
											2020-06-29 12:43:45 +02:00
										 |  |  |         auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment)); | 
					
						
							|  |  |  |         auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1; | 
					
						
							| 
									
										
										
										
											2020-06-29 00:09:32 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         auto rect = absolute_rect(); | 
					
						
							|  |  |  |         rect.set_x(rect.x() + pixel_distance_to_first_selected_character); | 
					
						
							|  |  |  |         rect.set_width(pixel_width_of_selection); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return rect; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  | } |