mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00:00
LibHTML: Implement basic partial style invalidation
This patch makes it possible to call Node::invalidate_style() and have that node and all of its ancestors recompute their style. We then figure out if the new style is visually different from the old style, and if so do a paint invalidation with set_needs_display(). Note that the "are they visually different" code is very incomplete! Use this to make hover effects a lot more efficient. They no longer cause a full relayout+repaint, but only a style invalidation. Style invalidations are still quite heavy though, and there's a lot of room for improvement there. :^)
This commit is contained in:
parent
667b31746a
commit
735f02900b
Notes:
sideshowbarker
2024-07-19 11:41:49 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/735f02900bb
8 changed files with 94 additions and 1 deletions
|
@ -97,3 +97,23 @@ int StyleProperties::line_height() const
|
|||
// FIXME: Allow overriding the line-height. We currently default to 140% which seems to look nice.
|
||||
return (int)(font().glyph_height() * 1.4f);
|
||||
}
|
||||
|
||||
bool StyleProperties::operator==(const StyleProperties& other) const
|
||||
{
|
||||
if (m_property_values.size() != other.m_property_values.size())
|
||||
return false;
|
||||
|
||||
for (auto& it : m_property_values) {
|
||||
auto jt = other.m_property_values.find(it.key);
|
||||
if (jt == other.m_property_values.end())
|
||||
return false;
|
||||
auto& my_value = *it.value;
|
||||
auto& other_value = *jt->value;
|
||||
if (my_value.type() != other_value.type())
|
||||
return false;
|
||||
if (my_value.to_string() != other_value.to_string())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,9 @@ public:
|
|||
|
||||
int line_height() const;
|
||||
|
||||
bool operator==(const StyleProperties&) const;
|
||||
bool operator!=(const StyleProperties& other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
HashMap<unsigned, NonnullRefPtr<StyleValue>> m_property_values;
|
||||
|
||||
|
|
|
@ -201,7 +201,12 @@ void Document::set_hovered_node(Node* node)
|
|||
if (m_hovered_node == node)
|
||||
return;
|
||||
|
||||
RefPtr<Node> old_hovered_node = move(m_hovered_node);
|
||||
m_hovered_node = node;
|
||||
update_style();
|
||||
|
||||
if (old_hovered_node)
|
||||
old_hovered_node->invalidate_style();
|
||||
if (m_hovered_node)
|
||||
m_hovered_node->invalidate_style();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <LibHTML/CSS/StyleResolver.h>
|
||||
#include <LibHTML/DOM/Document.h>
|
||||
#include <LibHTML/DOM/Element.h>
|
||||
#include <LibHTML/Layout/LayoutBlock.h>
|
||||
#include <LibHTML/Layout/LayoutInline.h>
|
||||
|
@ -92,3 +93,48 @@ RefPtr<LayoutNode> Element::create_layout_node(const StyleResolver& resolver, co
|
|||
void Element::parse_attribute(const String&, const String&)
|
||||
{
|
||||
}
|
||||
|
||||
enum class StyleDifference {
|
||||
None,
|
||||
NeedsRepaint,
|
||||
NeedsRelayout,
|
||||
};
|
||||
|
||||
static StyleDifference compute_style_difference(const StyleProperties& old_style, const StyleProperties& new_style, const Document& document)
|
||||
{
|
||||
if (old_style == new_style)
|
||||
return StyleDifference::None;
|
||||
|
||||
bool needs_repaint = false;
|
||||
bool needs_relayout = false;
|
||||
|
||||
if (new_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black))
|
||||
needs_repaint = true;
|
||||
else if (new_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black))
|
||||
needs_repaint = true;
|
||||
|
||||
if (needs_relayout)
|
||||
return StyleDifference::NeedsRelayout;
|
||||
if (needs_repaint)
|
||||
return StyleDifference::NeedsRepaint;
|
||||
return StyleDifference::None;
|
||||
}
|
||||
|
||||
void Element::recompute_style()
|
||||
{
|
||||
ASSERT(parent());
|
||||
auto* parent_layout_node = parent()->layout_node();
|
||||
ASSERT(parent_layout_node);
|
||||
auto style = document().style_resolver().resolve_style(*this, &parent_layout_node->style());
|
||||
ASSERT(layout_node());
|
||||
auto diff = compute_style_difference(layout_node()->style(), *style, document());
|
||||
if (diff == StyleDifference::None)
|
||||
return;
|
||||
layout_node()->set_style(*style);
|
||||
if (diff == StyleDifference::NeedsRelayout) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
if (diff == StyleDifference::NeedsRepaint) {
|
||||
layout_node()->set_needs_display();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include <AK/String.h>
|
||||
#include <LibHTML/DOM/ParentNode.h>
|
||||
#include <LibHTML/Layout/LayoutNode.h>
|
||||
|
||||
class LayoutNodeWithStyle;
|
||||
|
||||
class Attribute {
|
||||
public:
|
||||
|
@ -45,6 +48,11 @@ public:
|
|||
virtual void apply_presentational_hints(StyleProperties&) const {}
|
||||
virtual void parse_attribute(const String& name, const String& value);
|
||||
|
||||
void recompute_style();
|
||||
|
||||
LayoutNodeWithStyle* layout_node() { return static_cast<LayoutNodeWithStyle*>(Node::layout_node()); }
|
||||
const LayoutNodeWithStyle* layout_node() const { return static_cast<const LayoutNodeWithStyle*>(Node::layout_node()); }
|
||||
|
||||
private:
|
||||
RefPtr<LayoutNode> create_layout_node(const StyleResolver&, const StyleProperties* parent_style) const override;
|
||||
|
||||
|
|
|
@ -102,3 +102,11 @@ RefPtr<LayoutNode> Node::create_layout_node(const StyleResolver&, const StylePro
|
|||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Node::invalidate_style()
|
||||
{
|
||||
for (auto* node = this; node; node = node->parent()) {
|
||||
if (is<Element>(*node))
|
||||
to<Element>(*node).recompute_style();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
|
||||
virtual bool is_child_allowed(const Node&) const { return true; }
|
||||
|
||||
void invalidate_style();
|
||||
|
||||
protected:
|
||||
Node(Document&, NodeType);
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ public:
|
|||
virtual ~LayoutNodeWithStyle() override {}
|
||||
|
||||
const StyleProperties& style() const { return m_style; }
|
||||
void set_style(const StyleProperties& style) { m_style = style; }
|
||||
|
||||
protected:
|
||||
explicit LayoutNodeWithStyle(const Node* node, NonnullRefPtr<StyleProperties> style)
|
||||
|
|
Loading…
Add table
Reference in a new issue