소스 검색

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.)
Andreas Kling 5 년 전
부모
커밋
fdbad6284c

+ 5 - 0
Base/home/anon/www/blink.html

@@ -0,0 +1,5 @@
+<html>
+    <body>
+        <blink>Hello friends!</blink>
+    </body>
+</html>

+ 1 - 0
Base/home/anon/www/welcome.html

@@ -23,6 +23,7 @@ h1 {
         <li><a href="images.html">images</a></li>
         <li><a href="selectors.html">selectors</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>
     </ul>
 </body>

+ 4 - 0
Libraries/LibHTML/CSS/Default.css

@@ -97,3 +97,7 @@ hr {
     border-color: #888888;
     border-style: inset;
 }
+
+blink {
+    display: inline;
+}

+ 27 - 0
Libraries/LibHTML/DOM/HTMLBlinkElement.cpp

@@ -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 - 0
Libraries/LibHTML/DOM/HTMLBlinkElement.h

@@ -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;
+};

+ 7 - 0
Libraries/LibHTML/Frame.cpp

@@ -29,3 +29,10 @@ void Frame::set_size(const Size& size)
         return;
     m_size = size;
 }
+
+void Frame::set_needs_display(const Rect& rect)
+{
+    if (!on_set_needs_display)
+        return;
+    on_set_needs_display(rect);
+}

+ 5 - 0
Libraries/LibHTML/Frame.h

@@ -1,8 +1,10 @@
 #pragma once
 
+#include <AK/Function.h>
 #include <AK/Noncopyable.h>
 #include <AK/RefPtr.h>
 #include <AK/Weakable.h>
+#include <LibDraw/Rect.h>
 #include <LibDraw/Size.h>
 #include <LibHTML/TreeNode.h>
 
@@ -23,6 +25,9 @@ public:
     const Size& size() const { return m_size; }
     void set_size(const Size&);
 
+    void set_needs_display(const Rect&);
+    Function<void(const Rect&)> on_set_needs_display;
+
 private:
     Frame();
 

+ 6 - 0
Libraries/LibHTML/HtmlView.cpp

@@ -17,6 +17,12 @@ HtmlView::HtmlView(GWidget* parent)
     : GScrollableWidget(parent)
     , 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_shadow(FrameShadow::Sunken);
     set_frame_thickness(2);

+ 3 - 0
Libraries/LibHTML/Layout/LayoutBlock.cpp

@@ -194,6 +194,9 @@ void LayoutBlock::compute_height()
 
 void LayoutBlock::render(RenderingContext& context)
 {
+    if (!is_visible())
+        return;
+
     LayoutNode::render(context);
 
     // FIXME: position this properly

+ 12 - 0
Libraries/LibHTML/Layout/LayoutBlock.h

@@ -38,3 +38,15 @@ private:
 
     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;
+        }
+    }
+}

+ 3 - 0
Libraries/LibHTML/Layout/LayoutImage.cpp

@@ -34,6 +34,9 @@ void LayoutImage::layout()
 
 void LayoutImage::render(RenderingContext& context)
 {
+    if (!is_visible())
+        return;
+
     if (renders_as_alt_text()) {
         context.painter().set_font(Font::default_font());
         StylePainter::paint_frame(context.painter(), rect(), FrameShape::Container, FrameShadow::Sunken, 2);

+ 22 - 0
Libraries/LibHTML/Layout/LayoutNode.cpp

@@ -1,6 +1,7 @@
 #include <LibGUI/GPainter.h>
 #include <LibHTML/DOM/Document.h>
 #include <LibHTML/DOM/Element.h>
+#include <LibHTML/Frame.h>
 #include <LibHTML/Layout/LayoutBlock.h>
 #include <LibHTML/Layout/LayoutNode.h>
 
@@ -38,6 +39,9 @@ const LayoutBlock* LayoutNode::containing_block() const
 
 void LayoutNode::render(RenderingContext& context)
 {
+    if (!is_visible())
+        return;
+
 #ifdef DRAW_BOXES_AROUND_LAYOUT_NODES
     context.painter().draw_rect(m_rect, Color::Blue);
 #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;
+    });
+}

+ 20 - 0
Libraries/LibHTML/Layout/LayoutNode.h

@@ -76,6 +76,16 @@ public:
 
     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:
     explicit LayoutNode(const Node*);
 
@@ -88,6 +98,7 @@ private:
     Rect m_rect;
     bool m_inline { false };
     bool m_has_style { false };
+    bool m_visible { true };
 };
 
 class LayoutNodeWithStyle : public LayoutNode {
@@ -119,3 +130,12 @@ inline const LayoutNodeWithStyle* LayoutNode::parent() const
 {
     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 - 0
Libraries/LibHTML/Layout/LineBoxFragment.cpp

@@ -5,6 +5,11 @@
 
 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()) {
         auto& layout_text = static_cast<const LayoutText&>(layout_node());
         layout_text.render_fragment(context, *this);

+ 1 - 0
Libraries/LibHTML/Makefile.shared

@@ -14,6 +14,7 @@ LIBHTML_OBJS = \
     DOM/HTMLFontElement.o \
     DOM/HTMLImageElement.o \
     DOM/HTMLLinkElement.o \
+    DOM/HTMLBlinkElement.o \
     DOM/Document.o \
     DOM/Text.o \
     DOM/DocumentType.o \

+ 3 - 0
Libraries/LibHTML/Parser/HTMLParser.cpp

@@ -4,6 +4,7 @@
 #include <LibHTML/DOM/DocumentType.h>
 #include <LibHTML/DOM/Element.h>
 #include <LibHTML/DOM/HTMLAnchorElement.h>
+#include <LibHTML/DOM/HTMLBlinkElement.h>
 #include <LibHTML/DOM/HTMLBodyElement.h>
 #include <LibHTML/DOM/HTMLFontElement.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));
     if (lowercase_tag_name == "img")
         return adopt(*new HTMLImageElement(document, tag_name));
+    if (lowercase_tag_name == "blink")
+        return adopt(*new HTMLBlinkElement(document, tag_name));
     if (lowercase_tag_name == "h1"
         || lowercase_tag_name == "h2"
         || lowercase_tag_name == "h3"