mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-20 17:58:18 +03:00
ef8b754a46
This allows any external actor to signal that the document layout may be stale. This can be used when loading resources, changing the size or placement of an element, adding/removing nodes, or really any time.
187 lines
5.1 KiB
C++
187 lines
5.1 KiB
C++
#include <LibCore/CFile.h>
|
|
#include <LibGUI/GApplication.h>
|
|
#include <LibGUI/GPainter.h>
|
|
#include <LibGUI/GScrollBar.h>
|
|
#include <LibHTML/DOM/Element.h>
|
|
#include <LibHTML/DOM/HTMLAnchorElement.h>
|
|
#include <LibHTML/Dump.h>
|
|
#include <LibHTML/Frame.h>
|
|
#include <LibHTML/HtmlView.h>
|
|
#include <LibHTML/Layout/LayoutNode.h>
|
|
#include <LibHTML/Parser/HTMLParser.h>
|
|
#include <LibHTML/RenderingContext.h>
|
|
#include <stdio.h>
|
|
|
|
HtmlView::HtmlView(GWidget* parent)
|
|
: GScrollableWidget(parent)
|
|
, m_main_frame(Frame::create())
|
|
{
|
|
set_frame_shape(FrameShape::Container);
|
|
set_frame_shadow(FrameShadow::Sunken);
|
|
set_frame_thickness(2);
|
|
set_should_hide_unnecessary_scrollbars(true);
|
|
set_background_color(Color::White);
|
|
}
|
|
|
|
HtmlView::~HtmlView()
|
|
{
|
|
}
|
|
|
|
void HtmlView::set_document(Document* document)
|
|
{
|
|
if (document == m_document)
|
|
return;
|
|
|
|
if (m_document)
|
|
m_document->on_invalidate_layout = nullptr;
|
|
|
|
m_document = document;
|
|
m_document->on_invalidate_layout = [this]() { layout_and_sync_size(); };
|
|
|
|
main_frame().set_document(document);
|
|
|
|
if (document == nullptr)
|
|
m_layout_root = nullptr;
|
|
else
|
|
m_layout_root = document->create_layout_tree(document->style_resolver(), nullptr);
|
|
|
|
#ifdef HTML_DEBUG
|
|
if (document != nullptr) {
|
|
dbgprintf("\033[33;1mLayout tree before layout:\033[0m\n");
|
|
::dump_tree(*m_layout_root);
|
|
}
|
|
#endif
|
|
|
|
layout_and_sync_size();
|
|
update();
|
|
}
|
|
|
|
void HtmlView::layout_and_sync_size()
|
|
{
|
|
if (!m_layout_root)
|
|
return;
|
|
|
|
main_frame().set_size(available_size());
|
|
m_layout_root->layout();
|
|
set_content_size(m_layout_root->rect().size());
|
|
|
|
#ifdef HTML_DEBUG
|
|
dbgprintf("\033[33;1mLayout tree after layout:\033[0m\n");
|
|
::dump_tree(*m_layout_root);
|
|
#endif
|
|
}
|
|
|
|
void HtmlView::resize_event(GResizeEvent& event)
|
|
{
|
|
GScrollableWidget::resize_event(event);
|
|
layout_and_sync_size();
|
|
}
|
|
|
|
void HtmlView::paint_event(GPaintEvent& event)
|
|
{
|
|
GFrame::paint_event(event);
|
|
|
|
GPainter painter(*this);
|
|
painter.add_clip_rect(widget_inner_rect());
|
|
painter.add_clip_rect(event.rect());
|
|
|
|
if (!m_layout_root) {
|
|
painter.fill_rect(event.rect(), background_color());
|
|
return;
|
|
}
|
|
|
|
painter.fill_rect(event.rect(), m_document->background_color());
|
|
|
|
painter.translate(frame_thickness(), frame_thickness());
|
|
painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
|
|
|
|
RenderingContext context { painter };
|
|
m_layout_root->render(context);
|
|
}
|
|
|
|
void HtmlView::mousemove_event(GMouseEvent& event)
|
|
{
|
|
if (!m_layout_root)
|
|
return GScrollableWidget::mousemove_event(event);
|
|
|
|
bool hovered_node_changed = false;
|
|
auto result = m_layout_root->hit_test(to_content_position(event.position()));
|
|
if (result.layout_node) {
|
|
auto* node = result.layout_node->node();
|
|
hovered_node_changed = node != m_document->hovered_node();
|
|
m_document->set_hovered_node(const_cast<Node*>(node));
|
|
#ifdef HTML_DEBUG
|
|
if (node) {
|
|
if (auto* link = node->enclosing_link_element()) {
|
|
dbg() << "HtmlView: hovering over a link to " << link->href();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (hovered_node_changed) {
|
|
update();
|
|
auto* hovered_html_element = m_document->hovered_node() ? m_document->hovered_node()->enclosing_html_element() : nullptr;
|
|
if (hovered_html_element && !hovered_html_element->title().is_null()) {
|
|
auto screen_position = screen_relative_rect().location().translated(event.position());
|
|
GApplication::the().show_tooltip(hovered_html_element->title(), screen_position.translated(4, 4));
|
|
} else {
|
|
GApplication::the().hide_tooltip();
|
|
}
|
|
}
|
|
event.accept();
|
|
}
|
|
|
|
void HtmlView::mousedown_event(GMouseEvent& event)
|
|
{
|
|
if (!m_layout_root)
|
|
return GScrollableWidget::mousemove_event(event);
|
|
|
|
bool hovered_node_changed = false;
|
|
auto result = m_layout_root->hit_test(to_content_position(event.position()));
|
|
if (result.layout_node) {
|
|
auto* node = result.layout_node->node();
|
|
hovered_node_changed = node != m_document->hovered_node();
|
|
m_document->set_hovered_node(const_cast<Node*>(node));
|
|
if (node) {
|
|
if (auto* link = node->enclosing_link_element()) {
|
|
dbg() << "HtmlView: clicking on a link to " << link->href();
|
|
if (on_link_click)
|
|
on_link_click(link->href());
|
|
}
|
|
}
|
|
}
|
|
if (hovered_node_changed)
|
|
update();
|
|
event.accept();
|
|
}
|
|
|
|
void HtmlView::reload()
|
|
{
|
|
load(main_frame().document()->url());
|
|
}
|
|
|
|
void HtmlView::load(const URL& url)
|
|
{
|
|
dbg() << "HtmlView::load: " << url;
|
|
|
|
if (on_load_start)
|
|
on_load_start(url);
|
|
|
|
auto f = CFile::construct();
|
|
f->set_filename(url.path());
|
|
if (!f->open(CIODevice::OpenMode::ReadOnly)) {
|
|
dbg() << "HtmlView::load: Error: " << f->error_string();
|
|
return;
|
|
}
|
|
|
|
String html = String::copy(f->read_all());
|
|
auto document = parse_html(html);
|
|
document->set_url(url);
|
|
document->normalize();
|
|
|
|
set_document(document);
|
|
|
|
if (on_title_change)
|
|
on_title_change(document->title());
|
|
}
|