mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:02:43 +00:00 
			
		
		
		
	LibWeb: Add a blinking text cursor :^)
Each Web::Frame now has a cursor that sits at a DOM::Position. It will blink and look like a nice regular text cursor. It doesn't really do anything yet, but it will eventually.
This commit is contained in:
		
							parent
							
								
									e496a74bb3
								
							
						
					
					
						commit
						2c679d0c8b
					
				
					 5 changed files with 66 additions and 0 deletions
				
			
		|  | @ -32,6 +32,7 @@ | ||||||
| #include <LibWeb/DOM/Document.h> | #include <LibWeb/DOM/Document.h> | ||||||
| #include <LibWeb/Layout/LayoutBlock.h> | #include <LibWeb/Layout/LayoutBlock.h> | ||||||
| #include <LibWeb/Layout/LayoutText.h> | #include <LibWeb/Layout/LayoutText.h> | ||||||
|  | #include <LibWeb/Page/Frame.h> | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| 
 | 
 | ||||||
| namespace Web { | namespace Web { | ||||||
|  | @ -101,6 +102,28 @@ void LayoutText::paint_fragment(PaintContext& context, const LineBoxFragment& fr | ||||||
|         painter.add_clip_rect(enclosing_int_rect(selection_rect)); |         painter.add_clip_rect(enclosing_int_rect(selection_rect)); | ||||||
|         painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::TopLeft, context.palette().selection_text()); |         painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::TopLeft, context.palette().selection_text()); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     paint_cursor_if_needed(context, fragment); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LayoutText::paint_cursor_if_needed(PaintContext& context, const LineBoxFragment& fragment) const | ||||||
|  | { | ||||||
|  |     if (!frame().cursor_blink_state()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (frame().cursor_position().node() != &node()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (!(frame().cursor_position().offset() >= (unsigned)fragment.start() && frame().cursor_position().offset() < (unsigned)(fragment.start() + fragment.length()))) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     auto fragment_rect = fragment.absolute_rect(); | ||||||
|  | 
 | ||||||
|  |     float cursor_x = fragment_rect.x() + specified_style().font().width(fragment.text().substring_view(0, frame().cursor_position().offset() - fragment.start())); | ||||||
|  |     float cursor_top = fragment_rect.top(); | ||||||
|  |     float cursor_height = fragment_rect.height(); | ||||||
|  |     Gfx::IntRect cursor_rect(cursor_x, cursor_top, 1, cursor_height); | ||||||
|  |     context.painter().draw_rect(cursor_rect, context.palette().text_cursor()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<typename Callback> | template<typename Callback> | ||||||
|  |  | ||||||
|  | @ -54,6 +54,7 @@ public: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void split_into_lines_by_rules(LayoutBlock& container, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks); |     void split_into_lines_by_rules(LayoutBlock& container, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks); | ||||||
|  |     void paint_cursor_if_needed(PaintContext&, const LineBoxFragment&) const; | ||||||
| 
 | 
 | ||||||
|     template<typename Callback> |     template<typename Callback> | ||||||
|     void for_each_chunk(Callback, LayoutMode, bool do_wrap_lines, bool do_wrap_breaks) const; |     void for_each_chunk(Callback, LayoutMode, bool do_wrap_lines, bool do_wrap_breaks) const; | ||||||
|  |  | ||||||
|  | @ -150,6 +150,7 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         if (button == GUI::MouseButton::Left) { |         if (button == GUI::MouseButton::Left) { | ||||||
|  |             m_frame.set_cursor_position(DOM::Position(*node, result.index_in_node)); | ||||||
|             layout_root()->selection().set({ result.layout_node, result.index_in_node }, {}); |             layout_root()->selection().set({ result.layout_node, result.index_in_node }, {}); | ||||||
|             dump_selection("MouseDown"); |             dump_selection("MouseDown"); | ||||||
|             m_in_mouse_selection = true; |             m_in_mouse_selection = true; | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ Frame::Frame(DOM::Element& host_element, Frame& main_frame) | ||||||
|     , m_event_handler({}, *this) |     , m_event_handler({}, *this) | ||||||
|     , m_host_element(host_element.make_weak_ptr()) |     , m_host_element(host_element.make_weak_ptr()) | ||||||
| { | { | ||||||
|  |     setup(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Frame::Frame(Page& page) | Frame::Frame(Page& page) | ||||||
|  | @ -48,12 +49,23 @@ Frame::Frame(Page& page) | ||||||
|     , m_loader(*this) |     , m_loader(*this) | ||||||
|     , m_event_handler({}, *this) |     , m_event_handler({}, *this) | ||||||
| { | { | ||||||
|  |     setup(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Frame::~Frame() | Frame::~Frame() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Frame::setup() | ||||||
|  | { | ||||||
|  |     m_cursor_blink_timer = Core::Timer::construct(500, [this] { | ||||||
|  |         if (m_cursor_position.node() && m_cursor_position.node()->layout_node()) { | ||||||
|  |             m_cursor_blink_state = !m_cursor_blink_state; | ||||||
|  |             m_cursor_position.node()->layout_node()->set_needs_display(); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Frame::set_document(DOM::Document* document) | void Frame::set_document(DOM::Document* document) | ||||||
| { | { | ||||||
|     if (m_document == document) |     if (m_document == document) | ||||||
|  | @ -168,4 +180,20 @@ Gfx::IntPoint Frame::to_main_frame_position(const Gfx::IntPoint& a_position) | ||||||
|     return position; |     return position; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Frame::set_cursor_position(const DOM::Position & position) | ||||||
|  | { | ||||||
|  |     if (m_cursor_position == position) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (m_cursor_position.node() && m_cursor_position.node()->layout_node()) | ||||||
|  |         m_cursor_position.node()->layout_node()->set_needs_display(); | ||||||
|  | 
 | ||||||
|  |     m_cursor_position = position; | ||||||
|  | 
 | ||||||
|  |     if (m_cursor_position.node() && m_cursor_position.node()->layout_node()) | ||||||
|  |         m_cursor_position.node()->layout_node()->set_needs_display(); | ||||||
|  | 
 | ||||||
|  |     dbg() << "Cursor position: " << m_cursor_position; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -30,9 +30,11 @@ | ||||||
| #include <AK/Noncopyable.h> | #include <AK/Noncopyable.h> | ||||||
| #include <AK/RefPtr.h> | #include <AK/RefPtr.h> | ||||||
| #include <AK/WeakPtr.h> | #include <AK/WeakPtr.h> | ||||||
|  | #include <LibCore/Timer.h> | ||||||
| #include <LibGfx/Bitmap.h> | #include <LibGfx/Bitmap.h> | ||||||
| #include <LibGfx/Rect.h> | #include <LibGfx/Rect.h> | ||||||
| #include <LibGfx/Size.h> | #include <LibGfx/Size.h> | ||||||
|  | #include <LibWeb/DOM/Position.h> | ||||||
| #include <LibWeb/Loader/FrameLoader.h> | #include <LibWeb/Loader/FrameLoader.h> | ||||||
| #include <LibWeb/Page/EventHandler.h> | #include <LibWeb/Page/EventHandler.h> | ||||||
| #include <LibWeb/TreeNode.h> | #include <LibWeb/TreeNode.h> | ||||||
|  | @ -83,10 +85,17 @@ public: | ||||||
|     Gfx::IntPoint to_main_frame_position(const Gfx::IntPoint&); |     Gfx::IntPoint to_main_frame_position(const Gfx::IntPoint&); | ||||||
|     Gfx::IntRect to_main_frame_rect(const Gfx::IntRect&); |     Gfx::IntRect to_main_frame_rect(const Gfx::IntRect&); | ||||||
| 
 | 
 | ||||||
|  |     const DOM::Position& cursor_position() const { return m_cursor_position; } | ||||||
|  |     void set_cursor_position(const DOM::Position&); | ||||||
|  | 
 | ||||||
|  |     bool cursor_blink_state() const { return m_cursor_blink_state; } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     explicit Frame(DOM::Element& host_element, Frame& main_frame); |     explicit Frame(DOM::Element& host_element, Frame& main_frame); | ||||||
|     explicit Frame(Page&); |     explicit Frame(Page&); | ||||||
| 
 | 
 | ||||||
|  |     void setup(); | ||||||
|  | 
 | ||||||
|     Page& m_page; |     Page& m_page; | ||||||
|     Frame& m_main_frame; |     Frame& m_main_frame; | ||||||
| 
 | 
 | ||||||
|  | @ -97,6 +106,10 @@ private: | ||||||
|     RefPtr<DOM::Document> m_document; |     RefPtr<DOM::Document> m_document; | ||||||
|     Gfx::IntSize m_size; |     Gfx::IntSize m_size; | ||||||
|     Gfx::IntRect m_viewport_rect; |     Gfx::IntRect m_viewport_rect; | ||||||
|  | 
 | ||||||
|  |     DOM::Position m_cursor_position; | ||||||
|  |     RefPtr<Core::Timer> m_cursor_blink_timer; | ||||||
|  |     bool m_cursor_blink_state { false }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling