mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 13:37:45 +00:00
LibHTML: Implement basic tiled background image support
It's now possible to set a page background image via <body background>. Also, HtmlView now officially handles rendering the body element's background (color, image or both.) LayoutBox is responsible for all other background rendering. Note that it's not yet possible to use CSS background-image properties directly, since we can't parse them yet. :^)
This commit is contained in:
parent
96f10c8de2
commit
5a34225999
11 changed files with 101 additions and 7 deletions
BIN
Base/home/anon/www/90s-bg.png
Normal file
BIN
Base/home/anon/www/90s-bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -6,19 +6,19 @@
|
|||
<style type="text/css">
|
||||
/* css comment */
|
||||
body {
|
||||
background-color: #fff;
|
||||
color: #000; /* another css comment */
|
||||
background-color: #000;
|
||||
color: #fff; /* another css comment */
|
||||
}
|
||||
/* lol
|
||||
a
|
||||
css
|
||||
comment */
|
||||
h1 {
|
||||
color: #800;
|
||||
color: #a00;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body link="#44f" vlink="#c4c" background="90s-bg.png">
|
||||
<h1>Welcome to the Serenity Browser!</h1>
|
||||
<p>This is a very simple browser built on the LibHTML engine.</p>
|
||||
<p>Some small test pages:</p>
|
||||
|
|
|
@ -7,6 +7,7 @@ enum class PropertyID {
|
|||
Invalid,
|
||||
|
||||
BackgroundColor,
|
||||
BackgroundImage,
|
||||
BorderBottomColor,
|
||||
BorderBottomStyle,
|
||||
BorderBottomWidth,
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#include <LibDraw/GraphicsBitmap.h>
|
||||
#include <LibDraw/PNGLoader.h>
|
||||
#include <LibHTML/CSS/StyleValue.h>
|
||||
#include <LibHTML/DOM/Document.h>
|
||||
#include <LibHTML/Frame.h>
|
||||
#include <LibHTML/ResourceLoader.h>
|
||||
|
||||
StyleValue::StyleValue(Type type)
|
||||
: m_type(type)
|
||||
|
@ -28,3 +32,20 @@ Color IdentifierStyleValue::to_color(const Document& document) const
|
|||
return document.link_color();
|
||||
return {};
|
||||
}
|
||||
|
||||
ImageStyleValue::ImageStyleValue(const URL& url, Document& document)
|
||||
: StyleValue(Type::Image)
|
||||
, m_url(url)
|
||||
, m_document(document.make_weak_ptr())
|
||||
{
|
||||
NonnullRefPtr<ImageStyleValue> protector(*this);
|
||||
ResourceLoader::the().load(url, [this, protector](auto& data) {
|
||||
if (!m_document)
|
||||
return;
|
||||
m_bitmap = load_png_from_memory(data.data(), data.size());
|
||||
if (!m_bitmap)
|
||||
return;
|
||||
// FIXME: Do less than a full repaint if possible?
|
||||
m_document->frame()->set_needs_display({});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/URL.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibDraw/Color.h>
|
||||
#include <LibDraw/GraphicsBitmap.h>
|
||||
#include <LibHTML/CSS/Length.h>
|
||||
#include <LibHTML/CSS/PropertyID.h>
|
||||
|
||||
|
@ -32,6 +35,7 @@ public:
|
|||
Length,
|
||||
Color,
|
||||
Identifier,
|
||||
Image,
|
||||
};
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
@ -40,6 +44,7 @@ public:
|
|||
bool is_initial() const { return type() == Type::Initial; }
|
||||
bool is_color() const { return type() == Type::Color; }
|
||||
bool is_identifier() const { return type() == Type::Identifier; }
|
||||
bool is_image() const { return type() == Type::Image; }
|
||||
|
||||
virtual String to_string() const = 0;
|
||||
virtual Length to_length() const { return {}; }
|
||||
|
@ -171,3 +176,20 @@ private:
|
|||
|
||||
CSS::ValueID m_id { CSS::ValueID::Invalid };
|
||||
};
|
||||
|
||||
class ImageStyleValue final : public StyleValue {
|
||||
public:
|
||||
static NonnullRefPtr<ImageStyleValue> create(const URL& url, Document& document) { return adopt(*new ImageStyleValue(url, document)); }
|
||||
virtual ~ImageStyleValue() override {}
|
||||
|
||||
String to_string() const override { return String::format("Image{%s}", m_url.to_string().characters()); }
|
||||
|
||||
const GraphicsBitmap* bitmap() const { return m_bitmap; }
|
||||
|
||||
private:
|
||||
ImageStyleValue(const URL&, Document&);
|
||||
|
||||
URL m_url;
|
||||
WeakPtr<Document> m_document;
|
||||
RefPtr<GraphicsBitmap> m_bitmap;
|
||||
};
|
||||
|
|
|
@ -119,6 +119,27 @@ Color Document::background_color() const
|
|||
return background_color.value()->to_color(*this);
|
||||
}
|
||||
|
||||
RefPtr<GraphicsBitmap> Document::background_image() const
|
||||
{
|
||||
auto* body_element = body();
|
||||
if (!body_element)
|
||||
return {};
|
||||
|
||||
auto* body_layout_node = body_element->layout_node();
|
||||
if (!body_layout_node)
|
||||
return {};
|
||||
|
||||
auto background_image = body_layout_node->style().property(CSS::PropertyID::BackgroundImage);
|
||||
if (!background_image.has_value() || !background_image.value()->is_image())
|
||||
return {};
|
||||
|
||||
auto& image_value = static_cast<const ImageStyleValue&>(*background_image.value());
|
||||
if (!image_value.bitmap())
|
||||
return {};
|
||||
|
||||
return *image_value.bitmap();
|
||||
}
|
||||
|
||||
URL Document::complete_url(const String& string) const
|
||||
{
|
||||
URL url(string);
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
const Frame* frame() const { return m_frame.ptr(); }
|
||||
|
||||
Color background_color() const;
|
||||
RefPtr<GraphicsBitmap> background_image() const;
|
||||
|
||||
Color link_color() const { return m_link_color; }
|
||||
void set_link_color(Color);
|
||||
|
|
|
@ -23,6 +23,8 @@ void HTMLBodyElement::apply_presentational_hints(StyleProperties& style) const
|
|||
auto color = Color::from_string(value);
|
||||
if (color.has_value())
|
||||
style.set_property(CSS::PropertyID::Color, ColorStyleValue::create(color.value()));
|
||||
} else if (name == "background") {
|
||||
style.set_property(CSS::PropertyID::BackgroundImage, ImageStyleValue::create(document().complete_url(value), const_cast<Document&>(document())));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ HtmlView::HtmlView(GWidget* parent)
|
|||
, m_main_frame(Frame::create())
|
||||
{
|
||||
main_frame().on_set_needs_display = [this](auto& content_rect) {
|
||||
if (content_rect.is_empty()) {
|
||||
update();
|
||||
return;
|
||||
}
|
||||
Rect adjusted_rect = content_rect;
|
||||
adjusted_rect.set_location(to_widget_position(content_rect.location()));
|
||||
update(adjusted_rect);
|
||||
|
@ -118,6 +122,10 @@ void HtmlView::paint_event(GPaintEvent& event)
|
|||
|
||||
painter.fill_rect(event.rect(), m_document->background_color());
|
||||
|
||||
if (auto background_bitmap = m_document->background_image()) {
|
||||
painter.draw_tiled_bitmap(event.rect(), *background_bitmap);
|
||||
}
|
||||
|
||||
painter.translate(frame_thickness(), frame_thickness());
|
||||
painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <LibGUI/GPainter.h>
|
||||
#include <LibHTML/DOM/Document.h>
|
||||
#include <LibHTML/DOM/HTMLBodyElement.h>
|
||||
#include <LibHTML/Frame.h>
|
||||
#include <LibHTML/Layout/LayoutBlock.h>
|
||||
#include <LibHTML/Layout/LayoutBox.h>
|
||||
|
@ -26,11 +27,21 @@ void LayoutBox::render(RenderingContext& context)
|
|||
padded_rect.set_y(y() - box_model().padding().top.to_px());
|
||||
padded_rect.set_height(height() + box_model().padding().top.to_px() + box_model().padding().bottom.to_px());
|
||||
|
||||
if (!is_body()) {
|
||||
auto bgcolor = style().property(CSS::PropertyID::BackgroundColor);
|
||||
if (bgcolor.has_value() && bgcolor.value()->is_color()) {
|
||||
context.painter().fill_rect(padded_rect, bgcolor.value()->to_color(document()));
|
||||
}
|
||||
|
||||
auto bgimage = style().property(CSS::PropertyID::BackgroundImage);
|
||||
if (bgimage.has_value() && bgimage.value()->is_image()) {
|
||||
auto& image_value = static_cast<const ImageStyleValue&>(*bgimage.value());
|
||||
if (image_value.bitmap()) {
|
||||
context.painter().draw_tiled_bitmap(padded_rect, *image_value.bitmap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Respect all individual border sides
|
||||
auto border_width_value = style().property(CSS::PropertyID::BorderTopWidth);
|
||||
auto border_color_value = style().property(CSS::PropertyID::BorderTopColor);
|
||||
|
@ -93,3 +104,8 @@ void LayoutBox::set_needs_display()
|
|||
|
||||
LayoutNode::set_needs_display();
|
||||
}
|
||||
|
||||
bool LayoutBox::is_body() const
|
||||
{
|
||||
return node() && node() == document().body();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ public:
|
|||
virtual HitTestResult hit_test(const Point& position) const override;
|
||||
virtual void set_needs_display() override;
|
||||
|
||||
bool is_body() const;
|
||||
|
||||
protected:
|
||||
LayoutBox(const Node* node, NonnullRefPtr<StyleProperties> style)
|
||||
: LayoutNodeWithStyleAndBoxModelMetrics(node, move(style))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue