mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:12:45 +00:00 
			
		
		
		
	LibHTML: Implement the <blink> element
Just in time for Serenity's 1st birthday, here is the <blink> element! This patch adds a bunch of different mechanisms to enable partial repaints of the layout tree (LayoutNode::set_needs_display())) It also adds LayoutNode::is_visible(), which can be toggled to prevent a LayoutNode from rendering anything (it still takes up space though.)
This commit is contained in:
		
							parent
							
								
									33043941f9
								
							
						
					
					
						commit
						fdbad6284c
					
				
					 16 changed files with 140 additions and 0 deletions
				
			
		
							
								
								
									
										5
									
								
								Base/home/anon/www/blink.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Base/home/anon/www/blink.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | <html> | ||||||
|  |     <body> | ||||||
|  |         <blink>Hello friends!</blink> | ||||||
|  |     </body> | ||||||
|  | </html> | ||||||
|  | @ -23,6 +23,7 @@ h1 { | ||||||
|         <li><a href="images.html">images</a></li> |         <li><a href="images.html">images</a></li> | ||||||
|         <li><a href="selectors.html">selectors</a></li> |         <li><a href="selectors.html">selectors</a></li> | ||||||
|         <li><a href="link.html">link element</a></li> |         <li><a href="link.html">link element</a></li> | ||||||
|  |         <li><a href="blink.html">blink element</a></li> | ||||||
|         <li><a href="http://www.serenityos.org/">www.serenityos.org</a></li> |         <li><a href="http://www.serenityos.org/">www.serenityos.org</a></li> | ||||||
|     </ul> |     </ul> | ||||||
| </body> | </body> | ||||||
|  |  | ||||||
|  | @ -97,3 +97,7 @@ hr { | ||||||
|     border-color: #888888; |     border-color: #888888; | ||||||
|     border-style: inset; |     border-style: inset; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | blink { | ||||||
|  |     display: inline; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								Libraries/LibHTML/DOM/HTMLBlinkElement.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Libraries/LibHTML/DOM/HTMLBlinkElement.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | #include <LibCore/CTimer.h> | ||||||
|  | #include <LibHTML/CSS/StyleProperties.h> | ||||||
|  | #include <LibHTML/CSS/StyleValue.h> | ||||||
|  | #include <LibHTML/DOM/HTMLBlinkElement.h> | ||||||
|  | #include <LibHTML/Layout/LayoutNode.h> | ||||||
|  | 
 | ||||||
|  | HTMLBlinkElement::HTMLBlinkElement(Document& document, const String& tag_name) | ||||||
|  |     : HTMLElement(document, tag_name) | ||||||
|  |     , m_timer(CTimer::construct()) | ||||||
|  | { | ||||||
|  |     m_timer->set_interval(500); | ||||||
|  |     m_timer->on_timeout = [this] { blink(); }; | ||||||
|  |     m_timer->start(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HTMLBlinkElement::~HTMLBlinkElement() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HTMLBlinkElement::blink() | ||||||
|  | { | ||||||
|  |     if (!layout_node()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     layout_node()->set_visible(!layout_node()->is_visible()); | ||||||
|  |     layout_node()->set_needs_display(); | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								Libraries/LibHTML/DOM/HTMLBlinkElement.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Libraries/LibHTML/DOM/HTMLBlinkElement.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <LibHTML/DOM/HTMLElement.h> | ||||||
|  | 
 | ||||||
|  | class CTimer; | ||||||
|  | 
 | ||||||
|  | class HTMLBlinkElement : public HTMLElement { | ||||||
|  | public: | ||||||
|  |     HTMLBlinkElement(Document&, const String& tag_name); | ||||||
|  |     virtual ~HTMLBlinkElement() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void blink(); | ||||||
|  | 
 | ||||||
|  |     NonnullRefPtr<CTimer> m_timer; | ||||||
|  | }; | ||||||
|  | @ -29,3 +29,10 @@ void Frame::set_size(const Size& size) | ||||||
|         return; |         return; | ||||||
|     m_size = size; |     m_size = size; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void Frame::set_needs_display(const Rect& rect) | ||||||
|  | { | ||||||
|  |     if (!on_set_needs_display) | ||||||
|  |         return; | ||||||
|  |     on_set_needs_display(rect); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,8 +1,10 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <AK/Function.h> | ||||||
| #include <AK/Noncopyable.h> | #include <AK/Noncopyable.h> | ||||||
| #include <AK/RefPtr.h> | #include <AK/RefPtr.h> | ||||||
| #include <AK/Weakable.h> | #include <AK/Weakable.h> | ||||||
|  | #include <LibDraw/Rect.h> | ||||||
| #include <LibDraw/Size.h> | #include <LibDraw/Size.h> | ||||||
| #include <LibHTML/TreeNode.h> | #include <LibHTML/TreeNode.h> | ||||||
| 
 | 
 | ||||||
|  | @ -23,6 +25,9 @@ public: | ||||||
|     const Size& size() const { return m_size; } |     const Size& size() const { return m_size; } | ||||||
|     void set_size(const Size&); |     void set_size(const Size&); | ||||||
| 
 | 
 | ||||||
|  |     void set_needs_display(const Rect&); | ||||||
|  |     Function<void(const Rect&)> on_set_needs_display; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     Frame(); |     Frame(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,12 @@ HtmlView::HtmlView(GWidget* parent) | ||||||
|     : GScrollableWidget(parent) |     : GScrollableWidget(parent) | ||||||
|     , m_main_frame(Frame::create()) |     , m_main_frame(Frame::create()) | ||||||
| { | { | ||||||
|  |     main_frame().on_set_needs_display = [this](auto& content_rect) { | ||||||
|  |         Rect adjusted_rect = content_rect; | ||||||
|  |         adjusted_rect.set_location(to_widget_position(content_rect.location())); | ||||||
|  |         update(adjusted_rect); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     set_frame_shape(FrameShape::Container); |     set_frame_shape(FrameShape::Container); | ||||||
|     set_frame_shadow(FrameShadow::Sunken); |     set_frame_shadow(FrameShadow::Sunken); | ||||||
|     set_frame_thickness(2); |     set_frame_thickness(2); | ||||||
|  |  | ||||||
|  | @ -194,6 +194,9 @@ void LayoutBlock::compute_height() | ||||||
| 
 | 
 | ||||||
| void LayoutBlock::render(RenderingContext& context) | void LayoutBlock::render(RenderingContext& context) | ||||||
| { | { | ||||||
|  |     if (!is_visible()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|     LayoutNode::render(context); |     LayoutNode::render(context); | ||||||
| 
 | 
 | ||||||
|     // FIXME: position this properly
 |     // FIXME: position this properly
 | ||||||
|  |  | ||||||
|  | @ -38,3 +38,15 @@ private: | ||||||
| 
 | 
 | ||||||
|     Vector<LineBox> m_line_boxes; |     Vector<LineBox> m_line_boxes; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | template<typename Callback> | ||||||
|  | void LayoutNode::for_each_fragment_of_this(Callback callback) | ||||||
|  | { | ||||||
|  |     auto& block = *containing_block(); | ||||||
|  |     for (auto& line_box : block.line_boxes()) { | ||||||
|  |         for (auto& fragment : line_box.fragments()) { | ||||||
|  |             if (callback(fragment) == IterationDecision::Break) | ||||||
|  |                 return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -34,6 +34,9 @@ void LayoutImage::layout() | ||||||
| 
 | 
 | ||||||
| void LayoutImage::render(RenderingContext& context) | void LayoutImage::render(RenderingContext& context) | ||||||
| { | { | ||||||
|  |     if (!is_visible()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|     if (renders_as_alt_text()) { |     if (renders_as_alt_text()) { | ||||||
|         context.painter().set_font(Font::default_font()); |         context.painter().set_font(Font::default_font()); | ||||||
|         StylePainter::paint_frame(context.painter(), rect(), FrameShape::Container, FrameShadow::Sunken, 2); |         StylePainter::paint_frame(context.painter(), rect(), FrameShape::Container, FrameShadow::Sunken, 2); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #include <LibGUI/GPainter.h> | #include <LibGUI/GPainter.h> | ||||||
| #include <LibHTML/DOM/Document.h> | #include <LibHTML/DOM/Document.h> | ||||||
| #include <LibHTML/DOM/Element.h> | #include <LibHTML/DOM/Element.h> | ||||||
|  | #include <LibHTML/Frame.h> | ||||||
| #include <LibHTML/Layout/LayoutBlock.h> | #include <LibHTML/Layout/LayoutBlock.h> | ||||||
| #include <LibHTML/Layout/LayoutNode.h> | #include <LibHTML/Layout/LayoutNode.h> | ||||||
| 
 | 
 | ||||||
|  | @ -38,6 +39,9 @@ const LayoutBlock* LayoutNode::containing_block() const | ||||||
| 
 | 
 | ||||||
| void LayoutNode::render(RenderingContext& context) | void LayoutNode::render(RenderingContext& context) | ||||||
| { | { | ||||||
|  |     if (!is_visible()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
| #ifdef DRAW_BOXES_AROUND_LAYOUT_NODES | #ifdef DRAW_BOXES_AROUND_LAYOUT_NODES | ||||||
|     context.painter().draw_rect(m_rect, Color::Blue); |     context.painter().draw_rect(m_rect, Color::Blue); | ||||||
| #endif | #endif | ||||||
|  | @ -127,3 +131,21 @@ void LayoutNode::split_into_lines(LayoutBlock& container) | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void LayoutNode::set_needs_display() | ||||||
|  | { | ||||||
|  |     auto* frame = document().frame(); | ||||||
|  |     ASSERT(frame); | ||||||
|  | 
 | ||||||
|  |     if (!is_inline()) { | ||||||
|  |         const_cast<Frame*>(frame)->set_needs_display(rect()); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for_each_fragment_of_this([&](auto& fragment) { | ||||||
|  |         if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { | ||||||
|  |             const_cast<Frame*>(frame)->set_needs_display(fragment.rect()); | ||||||
|  |         } | ||||||
|  |         return IterationDecision::Continue; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -76,6 +76,16 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual void split_into_lines(LayoutBlock& container); |     virtual void split_into_lines(LayoutBlock& container); | ||||||
| 
 | 
 | ||||||
|  |     bool is_visible() const { return m_visible; } | ||||||
|  |     void set_visible(bool visible) { m_visible = visible; } | ||||||
|  | 
 | ||||||
|  |     void set_needs_display(); | ||||||
|  | 
 | ||||||
|  |     bool is_ancestor_of(const LayoutNode&) const; | ||||||
|  | 
 | ||||||
|  |     template<typename Callback> | ||||||
|  |     void for_each_fragment_of_this(Callback); | ||||||
|  | 
 | ||||||
| protected: | protected: | ||||||
|     explicit LayoutNode(const Node*); |     explicit LayoutNode(const Node*); | ||||||
| 
 | 
 | ||||||
|  | @ -88,6 +98,7 @@ private: | ||||||
|     Rect m_rect; |     Rect m_rect; | ||||||
|     bool m_inline { false }; |     bool m_inline { false }; | ||||||
|     bool m_has_style { false }; |     bool m_has_style { false }; | ||||||
|  |     bool m_visible { true }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class LayoutNodeWithStyle : public LayoutNode { | class LayoutNodeWithStyle : public LayoutNode { | ||||||
|  | @ -119,3 +130,12 @@ inline const LayoutNodeWithStyle* LayoutNode::parent() const | ||||||
| { | { | ||||||
|     return static_cast<const LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent()); |     return static_cast<const LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent()); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | inline bool LayoutNode::is_ancestor_of(const LayoutNode& other) const | ||||||
|  | { | ||||||
|  |     for (auto* ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) { | ||||||
|  |         if (ancestor == this) | ||||||
|  |             return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -5,6 +5,11 @@ | ||||||
| 
 | 
 | ||||||
| void LineBoxFragment::render(RenderingContext& context) | void LineBoxFragment::render(RenderingContext& context) | ||||||
| { | { | ||||||
|  |     for (auto* ancestor = layout_node().parent(); ancestor; ancestor = ancestor->parent()) { | ||||||
|  |         if (!ancestor->is_visible()) | ||||||
|  |             return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (layout_node().is_text()) { |     if (layout_node().is_text()) { | ||||||
|         auto& layout_text = static_cast<const LayoutText&>(layout_node()); |         auto& layout_text = static_cast<const LayoutText&>(layout_node()); | ||||||
|         layout_text.render_fragment(context, *this); |         layout_text.render_fragment(context, *this); | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ LIBHTML_OBJS = \ | ||||||
|     DOM/HTMLFontElement.o \ |     DOM/HTMLFontElement.o \ | ||||||
|     DOM/HTMLImageElement.o \ |     DOM/HTMLImageElement.o \ | ||||||
|     DOM/HTMLLinkElement.o \ |     DOM/HTMLLinkElement.o \ | ||||||
|  |     DOM/HTMLBlinkElement.o \ | ||||||
|     DOM/Document.o \ |     DOM/Document.o \ | ||||||
|     DOM/Text.o \ |     DOM/Text.o \ | ||||||
|     DOM/DocumentType.o \ |     DOM/DocumentType.o \ | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| #include <LibHTML/DOM/DocumentType.h> | #include <LibHTML/DOM/DocumentType.h> | ||||||
| #include <LibHTML/DOM/Element.h> | #include <LibHTML/DOM/Element.h> | ||||||
| #include <LibHTML/DOM/HTMLAnchorElement.h> | #include <LibHTML/DOM/HTMLAnchorElement.h> | ||||||
|  | #include <LibHTML/DOM/HTMLBlinkElement.h> | ||||||
| #include <LibHTML/DOM/HTMLBodyElement.h> | #include <LibHTML/DOM/HTMLBodyElement.h> | ||||||
| #include <LibHTML/DOM/HTMLFontElement.h> | #include <LibHTML/DOM/HTMLFontElement.h> | ||||||
| #include <LibHTML/DOM/HTMLHRElement.h> | #include <LibHTML/DOM/HTMLHRElement.h> | ||||||
|  | @ -42,6 +43,8 @@ static NonnullRefPtr<Element> create_element(Document& document, const String& t | ||||||
|         return adopt(*new HTMLLinkElement(document, tag_name)); |         return adopt(*new HTMLLinkElement(document, tag_name)); | ||||||
|     if (lowercase_tag_name == "img") |     if (lowercase_tag_name == "img") | ||||||
|         return adopt(*new HTMLImageElement(document, tag_name)); |         return adopt(*new HTMLImageElement(document, tag_name)); | ||||||
|  |     if (lowercase_tag_name == "blink") | ||||||
|  |         return adopt(*new HTMLBlinkElement(document, tag_name)); | ||||||
|     if (lowercase_tag_name == "h1" |     if (lowercase_tag_name == "h1" | ||||||
|         || lowercase_tag_name == "h2" |         || lowercase_tag_name == "h2" | ||||||
|         || lowercase_tag_name == "h3" |         || lowercase_tag_name == "h3" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling