HtmlView.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. #include <LibCore/CFile.h>
  2. #include <LibGUI/GApplication.h>
  3. #include <LibGUI/GPainter.h>
  4. #include <LibGUI/GScrollBar.h>
  5. #include <LibHTML/DOM/Element.h>
  6. #include <LibHTML/DOM/HTMLAnchorElement.h>
  7. #include <LibHTML/Dump.h>
  8. #include <LibHTML/Frame.h>
  9. #include <LibHTML/HtmlView.h>
  10. #include <LibHTML/Layout/LayoutNode.h>
  11. #include <LibHTML/Parser/HTMLParser.h>
  12. #include <LibHTML/RenderingContext.h>
  13. #include <LibHTML/ResourceLoader.h>
  14. #include <stdio.h>
  15. HtmlView::HtmlView(GWidget* parent)
  16. : GScrollableWidget(parent)
  17. , m_main_frame(Frame::create())
  18. {
  19. main_frame().on_set_needs_display = [this](auto& content_rect) {
  20. Rect adjusted_rect = content_rect;
  21. adjusted_rect.set_location(to_widget_position(content_rect.location()));
  22. update(adjusted_rect);
  23. };
  24. set_frame_shape(FrameShape::Container);
  25. set_frame_shadow(FrameShadow::Sunken);
  26. set_frame_thickness(2);
  27. set_should_hide_unnecessary_scrollbars(true);
  28. set_background_color(Color::White);
  29. }
  30. HtmlView::~HtmlView()
  31. {
  32. }
  33. void HtmlView::set_document(Document* document)
  34. {
  35. if (document == m_document)
  36. return;
  37. if (m_document)
  38. m_document->on_invalidate_layout = nullptr;
  39. m_document = document;
  40. if (m_document) {
  41. m_document->on_invalidate_layout = [this]() {
  42. m_layout_root = m_document->create_layout_tree(m_document->style_resolver(), nullptr);
  43. layout_and_sync_size();
  44. update();
  45. };
  46. }
  47. m_layout_root = nullptr;
  48. main_frame().set_document(document);
  49. if (document)
  50. m_layout_root = document->create_layout_tree(document->style_resolver(), nullptr);
  51. #ifdef HTML_DEBUG
  52. if (document != nullptr) {
  53. dbgprintf("\033[33;1mLayout tree before layout:\033[0m\n");
  54. ::dump_tree(*m_layout_root);
  55. }
  56. #endif
  57. layout_and_sync_size();
  58. update();
  59. }
  60. void HtmlView::layout_and_sync_size()
  61. {
  62. if (!m_layout_root)
  63. return;
  64. main_frame().set_size(available_size());
  65. m_layout_root->layout();
  66. set_content_size(m_layout_root->rect().size());
  67. #ifdef HTML_DEBUG
  68. dbgprintf("\033[33;1mLayout tree after layout:\033[0m\n");
  69. ::dump_tree(*m_layout_root);
  70. #endif
  71. }
  72. void HtmlView::resize_event(GResizeEvent& event)
  73. {
  74. GScrollableWidget::resize_event(event);
  75. layout_and_sync_size();
  76. }
  77. void HtmlView::paint_event(GPaintEvent& event)
  78. {
  79. GFrame::paint_event(event);
  80. GPainter painter(*this);
  81. painter.add_clip_rect(widget_inner_rect());
  82. painter.add_clip_rect(event.rect());
  83. if (!m_layout_root) {
  84. painter.fill_rect(event.rect(), background_color());
  85. return;
  86. }
  87. painter.fill_rect(event.rect(), m_document->background_color());
  88. painter.translate(frame_thickness(), frame_thickness());
  89. painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
  90. RenderingContext context { painter };
  91. m_layout_root->render(context);
  92. }
  93. void HtmlView::mousemove_event(GMouseEvent& event)
  94. {
  95. if (!m_layout_root)
  96. return GScrollableWidget::mousemove_event(event);
  97. bool hovered_node_changed = false;
  98. auto result = m_layout_root->hit_test(to_content_position(event.position()));
  99. if (result.layout_node) {
  100. auto* node = result.layout_node->node();
  101. hovered_node_changed = node != m_document->hovered_node();
  102. m_document->set_hovered_node(const_cast<Node*>(node));
  103. #ifdef HTML_DEBUG
  104. if (node) {
  105. if (auto* link = node->enclosing_link_element()) {
  106. dbg() << "HtmlView: hovering over a link to " << link->href();
  107. }
  108. }
  109. #endif
  110. }
  111. if (hovered_node_changed) {
  112. update();
  113. auto* hovered_html_element = m_document->hovered_node() ? m_document->hovered_node()->enclosing_html_element() : nullptr;
  114. if (hovered_html_element && !hovered_html_element->title().is_null()) {
  115. auto screen_position = screen_relative_rect().location().translated(event.position());
  116. GApplication::the().show_tooltip(hovered_html_element->title(), screen_position.translated(4, 4));
  117. } else {
  118. GApplication::the().hide_tooltip();
  119. }
  120. }
  121. event.accept();
  122. }
  123. void HtmlView::mousedown_event(GMouseEvent& event)
  124. {
  125. if (!m_layout_root)
  126. return GScrollableWidget::mousemove_event(event);
  127. bool hovered_node_changed = false;
  128. auto result = m_layout_root->hit_test(to_content_position(event.position()));
  129. if (result.layout_node) {
  130. auto* node = result.layout_node->node();
  131. hovered_node_changed = node != m_document->hovered_node();
  132. m_document->set_hovered_node(const_cast<Node*>(node));
  133. if (node) {
  134. if (auto* link = node->enclosing_link_element()) {
  135. dbg() << "HtmlView: clicking on a link to " << link->href();
  136. if (on_link_click)
  137. on_link_click(link->href());
  138. }
  139. }
  140. }
  141. if (hovered_node_changed)
  142. update();
  143. event.accept();
  144. }
  145. void HtmlView::reload()
  146. {
  147. load(main_frame().document()->url());
  148. }
  149. void HtmlView::load(const URL& url)
  150. {
  151. dbg() << "HtmlView::load: " << url;
  152. if (on_load_start)
  153. on_load_start(url);
  154. ResourceLoader::the().load(url, [=](auto data) {
  155. if (data.is_null()) {
  156. dbg() << "Load failed!";
  157. ASSERT_NOT_REACHED();
  158. }
  159. auto document = parse_html(data, url);
  160. set_document(document);
  161. if (on_title_change)
  162. on_title_change(document->title());
  163. });
  164. }