mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-10-31 05:10:57 +00:00 
			
		
		
		
	 ef8b754a46
			
		
	
	
		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.
		
			
				
	
	
		
			186 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			186 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());
 | |
| }
 |