diff --git a/CMakeLists.txt b/CMakeLists.txt index ff95f6cd..f8b319e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ set(HEADER_LITEHTML include/litehtml/flex_line.h include/litehtml/gradient.h include/litehtml/font_description.h + include/litehtml/scroll_view.h ) set(PROJECT_LIB_VERSION ${PROJECT_MAJOR}.${PROJECT_MINOR}.0) diff --git a/containers/cairo/render2png.cpp b/containers/cairo/render2png.cpp index bd6e7bb8..79b5e832 100644 --- a/containers/cairo/render2png.cpp +++ b/containers/cairo/render2png.cpp @@ -219,8 +219,8 @@ namespace html2png std::swap(m_screen_width, best_width); } - width = cfg.get_int("width", doc->content_width() > 0 ? doc->content_width() : 1); - height = cfg.get_int("height", doc->content_height() > 0 ? doc->content_height() : 1); + width = cfg.get_int("width", doc->width() > 0 ? doc->width() : 1); + height = cfg.get_int("height", doc->height() > 0 ? doc->height() : 1); auto surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); if(surface) diff --git a/include/litehtml/document.h b/include/litehtml/document.h index 7c1f5c44..a1d931de 100644 --- a/include/litehtml/document.h +++ b/include/litehtml/document.h @@ -59,7 +59,6 @@ namespace litehtml litehtml::css m_master_css; litehtml::css m_user_css; litehtml::size m_size; - litehtml::size m_content_size; position::vector m_fixed_boxes; std::shared_ptr m_over_element; std::shared_ptr m_active_element; @@ -88,6 +87,8 @@ namespace litehtml pixel_t content_height() const; void add_stylesheet(const char* str, const char* baseurl, const char* media); bool on_mouse_over(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, position::vector& redraw_boxes); + pixel_t on_v_scroll(pixel_t dy, pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, position& scroll_box) const; + pixel_t on_h_scroll(pixel_t dx, pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, position& scroll_box) const; bool on_lbutton_down(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, position::vector& redraw_boxes); bool on_lbutton_up(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, position::vector& redraw_boxes); bool on_button_cancel(position::vector& redraw_boxes); diff --git a/include/litehtml/render_item.h b/include/litehtml/render_item.h index 20a73484..6fd742d3 100644 --- a/include/litehtml/render_item.h +++ b/include/litehtml/render_item.h @@ -10,6 +10,7 @@ #include "table.h" #include "formatting_context.h" #include "element.h" +#include "scroll_view.h" namespace litehtml { @@ -27,6 +28,7 @@ namespace litehtml position m_pos; bool m_skip; std::vector> m_positioned; + std::shared_ptr m_scroll_view; containing_block_context calculate_containing_block_context(const containing_block_context& cb_context); void calc_cb_length(const css_length& len, pixel_t percent_base, containing_block_context::typed_pixel& out_value) const; @@ -40,16 +42,57 @@ namespace litehtml virtual ~render_item() = default; + pixel_t get_scroll_left() const + { + return m_scroll_view ? m_scroll_view->get_left() : 0; + } + + pixel_t get_scroll_top() const + { + return m_scroll_view ? m_scroll_view->get_top() : 0; + } + + void scroll_box(position& box) const + { + if (m_scroll_view) + { + box.x -= m_scroll_view->get_left(); + box.y -= m_scroll_view->get_top(); + } + } + + pixel_t h_scroll(pixel_t dx) + { + return m_scroll_view ? m_scroll_view->h_scroll(dx) : 0; + } + + pixel_t v_scroll(pixel_t dy) + { + return m_scroll_view ? m_scroll_view->v_scroll(dy) : 0; + } + std::list>& children() { return m_children; } + // Access to the m_pos position& pos() { return m_pos; } + // Calculates the position of the element in the document + // This position is relative to the x and y arguments + // Scroll shifts are applied to the position + position calc_placement(int x = 0, int y = 0) const + { + position pos = m_pos; + pos.x += x - get_scroll_left(); + pos.y += y - get_scroll_top(); + return pos; + } + bool skip() const { return m_skip; @@ -392,7 +435,7 @@ namespace litehtml */ virtual pixel_t get_first_baseline() { return height() - margin_bottom(); } /** - * Get last baseline position. Default position is element bottom without bottom margin. + * Get the last baseline position. The default position is element bottom without bottom margin. * @returns offset of the last baseline from element top */ virtual pixel_t get_last_baseline() { return height() - margin_bottom(); } @@ -412,7 +455,7 @@ namespace litehtml std::tuple element_static_offset(const std::shared_ptr &el); void add_positioned(const std::shared_ptr &el); void get_redraw_box(litehtml::position& pos, pixel_t x = 0, pixel_t y = 0); - void calc_document_size( litehtml::size& sz, litehtml::size& content_size, pixel_t x = 0, pixel_t y = 0 ); + void calc_document_size( litehtml::size& sz, pixel_t x = 0, pixel_t y = 0 ); virtual void get_inline_boxes( position::vector& /*boxes*/ ) const {}; virtual void set_inline_boxes( position::vector& /*boxes*/ ) {}; virtual void add_inline_box( const position& /*box*/ ) {}; @@ -420,9 +463,11 @@ namespace litehtml void draw_stacking_context( uint_ptr hdc, pixel_t x, pixel_t y, const position* clip, bool with_positioned ); virtual void draw_children( uint_ptr hdc, pixel_t x, pixel_t y, const position* clip, draw_flag flag, int zindex ); virtual pixel_t get_draw_vertical_offset() { return 0; } - virtual std::shared_ptr get_child_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, draw_flag flag, int zindex); - std::shared_ptr get_element_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y); - bool is_point_inside( pixel_t x, pixel_t y ); + virtual std::shared_ptr get_child_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, draw_flag flag, int zindex, + const std::function&)>& check); + std::shared_ptr get_element_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, + const std::function&)>& check); + bool is_point_inside( pixel_t x, pixel_t y ) const; void dump(litehtml::dumper& cout); position get_placement() const; virtual void y_shift(pixel_t shift); @@ -432,7 +477,7 @@ namespace litehtml * @param redraw_boxes [out] resulting rendering boxes * @return */ - void get_rendering_boxes( position::vector& redraw_boxes); + void get_rendering_boxes( position::vector& redraw_boxes) const; }; } diff --git a/include/litehtml/scroll_view.h b/include/litehtml/scroll_view.h new file mode 100644 index 00000000..f71d955d --- /dev/null +++ b/include/litehtml/scroll_view.h @@ -0,0 +1,66 @@ +#ifndef LITEHTML_SCROLL_VIEW_H +#define LITEHTML_SCROLL_VIEW_H + +#include "types.h" + +namespace litehtml +{ + class scroll_view : std::enable_shared_from_this + { + protected: + position m_viewport; // Viewport position and size + size m_scroll_size; // Size of the scrollable area, not the viewport + pixel_t m_left = 0; // Horizontal scroll position + pixel_t m_top = 0; // Vertical scroll position + + public: + scroll_view(const position& viewport, const size& scroll_size) : + m_viewport(viewport), + m_scroll_size(scroll_size) + { + } + + pixel_t get_left() const { return m_left; } + pixel_t get_top() const { return m_top; } + + pixel_t get_max_h_scroll() const { return std::max(0.f, m_scroll_size.width - m_viewport.width); } + pixel_t get_max_v_scroll() const { return std::max(0.f, m_scroll_size.height - m_viewport.height); } + + void set(const position& viewport, const size& scroll_size) + { + m_viewport = viewport; + m_scroll_size = scroll_size; + m_left = std::clamp(m_left, 0.f, get_max_h_scroll()); + m_top = std::clamp(m_top, 0.f, get_max_v_scroll()); + } + + const position& get_viewport() const { return m_viewport; } + + pixel_t h_scroll(const pixel_t dx) + { + if(m_scroll_size.width == 0) + return 0; + + const pixel_t new_left = std::clamp(m_left + dx, 0.f, get_max_h_scroll()); + const pixel_t ret = new_left - m_left; + m_left = new_left; + return ret; + } + + pixel_t v_scroll(const pixel_t dy) + { + if(m_scroll_size.height == 0) + return 0; + + const pixel_t new_top = std::clamp(m_top + dy, 0.f, get_max_v_scroll()); + const pixel_t ret = new_top - m_top; + m_top = new_top; + return ret; + } + + bool is_point_inside(const pixel_t x, const pixel_t y) const { return m_viewport.is_point_inside(x, y); } + }; + +} // namespace litehtml + +#endif // LITEHTML_SCROLL_VIEW_H diff --git a/src/document.cpp b/src/document.cpp index 9d52b80c..aa8900b0 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -513,9 +513,7 @@ pixel_t document::render( pixel_t max_width, render_type rt ) } m_size.width = 0; m_size.height = 0; - m_content_size.width = 0; - m_content_size.height = 0; - m_root_render->calc_document_size(m_size, m_content_size); + m_root_render->calc_document_size(m_size); } } return ret; @@ -613,17 +611,6 @@ pixel_t document::height() const return m_size.height; } -pixel_t document::content_width() const -{ - return m_content_size.width; -} - -pixel_t document::content_height() const -{ - return m_content_size.height; -} - - void document::add_stylesheet( const char* str, const char* baseurl, const char* media ) { if(str && str[0]) @@ -639,7 +626,7 @@ bool document::on_mouse_over( pixel_t x, pixel_t y, pixel_t client_x, pixel_t cl return false; } - element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y); + element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y, nullptr); bool state_was_changed = false; @@ -677,6 +664,42 @@ bool document::on_mouse_over( pixel_t x, pixel_t y, pixel_t client_x, pixel_t cl return false; } +pixel_t document::on_v_scroll(pixel_t dy, pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, position& /* scroll_box */) const +{ + if (dy == 0) return 0; + + pixel_t ret = 0; + element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y, [&ret, dy](const shared_ptr& el) -> bool { + pixel_t scrolled_by = 0; + if ((scrolled_by = el->v_scroll(dy)) != 0) + { + ret = scrolled_by; + return true; + } + return false; + }); + + return ret; +} + +pixel_t document::on_h_scroll(pixel_t dx, pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, position& /* scroll_box */) const +{ + if (dx == 0) return 0; + + pixel_t ret = 0; + element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y, [&ret, dx](const shared_ptr& el) -> bool { + pixel_t scrolled_by = 0; + if ((scrolled_by = el->h_scroll(dx)) != 0) + { + ret = scrolled_by; + return true; + } + return false; + }); + + return ret; +} + bool document::on_mouse_leave( position::vector& redraw_boxes ) { if(!m_root || !m_root_render) @@ -703,7 +726,7 @@ bool document::on_lbutton_down( pixel_t x, pixel_t y, pixel_t client_x, pixel_t return false; } - element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y); + element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y, nullptr); m_active_element = over_el; bool state_was_changed = false; diff --git a/src/render_item.cpp b/src/render_item.cpp index bfe1d604..d741b0a8 100644 --- a/src/render_item.cpp +++ b/src/render_item.cpp @@ -707,45 +707,60 @@ void litehtml::render_item::get_redraw_box(litehtml::position& pos, pixel_t x /* } } -void litehtml::render_item::calc_document_size( litehtml::size& sz, litehtml::size& content_size, pixel_t x /*= 0*/, pixel_t y /*= 0*/ ) +void litehtml::render_item::calc_document_size(litehtml::size& sz, pixel_t x /*= 0*/, pixel_t y /*= 0*/) { if(css().get_display() != display_inline && css().get_display() != display_table_row) { - if (is_visible() && src_el()->css().get_position() != element_position_fixed) + if(is_visible()) { - sz.width = std::max(sz.width, x + right()); - sz.height = std::max(sz.height, y + bottom()); - - if (!src_el()->is_root() && !src_el()->is_body()) + if(src_el()->css().get_position() != element_position_fixed) { - content_size.width = std::max(content_size.width, x + right()); - content_size.height = std::max(content_size.height, y + bottom()); + sz.width = std::max(sz.width, x + right()); + sz.height = std::max(sz.height, y + bottom()); } - // All children of tables and blocks with style other than "overflow: visible" are inside element. - // We can skip calculating size of children - if (src_el()->css().get_overflow() == overflow_visible && src_el()->css().get_display() != display_table) + if(is_one_of(src_el()->css().get_overflow(), overflow_scroll, overflow_auto)) { - for (auto &el: m_children) + size child_size; + for(const auto& el : m_children) { - el->calc_document_size(sz, content_size, x + m_pos.x, y + m_pos.y); + el->calc_document_size(child_size, 0, 0); } - } - - if (src_el()->is_root() || src_el()->is_body()) + if(!m_scroll_view) + { + m_scroll_view = std::make_shared(m_pos, child_size); + } else + { + m_scroll_view->set(m_pos, child_size); + } + } else { - content_size.width = std::max(content_size.width, x + right()); - content_size.height = std::max(content_size.height, y + bottom()); + // All children of tables and blocks with style other than "overflow: visible" are inside element. + // We can skip calculating the size of children + if(src_el()->css().get_overflow() == overflow_visible && src_el()->css().get_display() != display_table) + { + for(const auto& el : m_children) + { + el->calc_document_size(sz, x + m_pos.x, y + m_pos.y); + } + } else + { + size child_size; + for(const auto& el : m_children) + { + el->calc_document_size(child_size, 0, 0); + } + } } } } else { position::vector boxes; get_inline_boxes(boxes); - for(auto& box : boxes) + for(const auto& box : boxes) { - content_size.width = std::max(content_size.width, x + box.x + box.width); - content_size.height = std::max(content_size.height, y + box.y + box.height); + sz.width = std::max(sz.width, x + box.x + box.width); + sz.height = std::max(sz.height, y + box.y + box.height); } } } @@ -796,8 +811,8 @@ void litehtml::render_item::draw_stacking_context( uint_ptr hdc, pixel_t x, pixe void litehtml::render_item::draw_children(uint_ptr hdc, pixel_t x, pixel_t y, const position* clip, draw_flag flag, int zindex) { position pos = m_pos; - pos.x += x; - pos.y += y; + pos.x += x - get_scroll_left(); + pos.y += y - get_scroll_top(); document::ptr doc = src_el()->get_document(); @@ -806,7 +821,10 @@ void litehtml::render_item::draw_children(uint_ptr hdc, pixel_t x, pixel_t y, co // TODO: Process overflow for inline elements if(src_el()->css().get_display() != display_inline) { - position border_box = pos; + position clip_box = m_pos; + clip_box.x += x; + clip_box.y += y; + position border_box = clip_box; border_box += m_padding; border_box += m_borders; @@ -816,7 +834,7 @@ void litehtml::render_item::draw_children(uint_ptr hdc, pixel_t x, pixel_t y, co bdr_radius -= m_borders; bdr_radius -= m_padding; - doc->container()->set_clip(pos, bdr_radius); + doc->container()->set_clip(clip_box, bdr_radius); } } @@ -901,28 +919,29 @@ void litehtml::render_item::draw_children(uint_ptr hdc, pixel_t x, pixel_t y, co } } -std::shared_ptr litehtml::render_item::get_child_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, draw_flag flag, int zindex) +std::shared_ptr litehtml::render_item::get_child_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, draw_flag flag, int zindex, const std::function&)>& check) { - element::ptr ret = nullptr; - if(src_el()->css().get_overflow() > overflow_visible) { if(!m_pos.is_point_inside(x, y)) { - return ret; + return nullptr; } } + element::ptr ret = nullptr; + position el_pos = m_pos; - el_pos.x = x - el_pos.x; - el_pos.y = y - el_pos.y; + el_pos.x = x - el_pos.x + get_scroll_left(); + el_pos.y = y - el_pos.y + get_scroll_top(); - for(auto i = m_children.rbegin(); i != m_children.rend() && !ret; std::advance(i, 1)) + for(auto i = m_children.crbegin(); i != m_children.crend() && !ret; std::advance(i, 1)) { - auto el = (*i); + const auto& el = *i; if(el->is_visible() && el->src_el()->css().get_display() != display_inline_text) { + bool process = true; switch(flag) { case draw_positioned: @@ -930,41 +949,53 @@ std::shared_ptr litehtml::render_item::get_child_by_point(pi { if(el->src_el()->css().get_position() == element_position_fixed) { - ret = el->get_element_by_point(client_x, client_y, client_x, client_y); - if(!ret && (*i)->is_point_inside(client_x, client_y)) + ret = el->get_element_by_point(client_x, client_y, client_x, client_y, check); + if(!ret && el->is_point_inside(client_x, client_y)) { - ret = (*i)->src_el(); + if (!check || check(el)) + { + ret = el->src_el(); + } } } else { - ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y); - if(!ret && (*i)->is_point_inside(el_pos.x, el_pos.y)) + ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y, check); + if(!ret && el->is_point_inside(el_pos.x, el_pos.y)) { - ret = (*i)->src_el(); + if (!check || check(el)) + { + ret = el->src_el(); + } } } - el = nullptr; + process = false; } break; case draw_block: if(!el->src_el()->is_inline() && el->src_el()->css().get_float() == float_none && !el->src_el()->is_positioned()) { - if(el->is_point_inside(el_pos.x, el_pos.y)) + ret = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex, check); + + if(!ret && el->is_point_inside(el_pos.x, el_pos.y)) { - ret = el->src_el(); + if (!check || check(el)) + { + ret = el->src_el(); + } } + process = (ret == nullptr); } break; case draw_floats: if(el->src_el()->css().get_float() != float_none && !el->src_el()->is_positioned()) { - ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y); + ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y, check); - if(!ret && (*i)->is_point_inside(el_pos.x, el_pos.y)) + if(!ret && el->is_point_inside(el_pos.x, el_pos.y)) { - ret = (*i)->src_el(); + ret = el->src_el(); } - el = nullptr; + process = false; } break; case draw_inlines: @@ -974,12 +1005,15 @@ std::shared_ptr litehtml::render_item::get_child_by_point(pi el->src_el()->css().get_display() == display_inline_table || el->src_el()->css().get_display() == display_inline_flex) { - ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y); - el = nullptr; + ret = el->get_element_by_point(el_pos.x, el_pos.y, client_x, client_y, check); + process = false; } - if(!ret && (*i)->is_point_inside(el_pos.x, el_pos.y)) + if(!ret && el->is_point_inside(el_pos.x, el_pos.y)) { - ret = (*i)->src_el(); + if (!check || check(el)) + { + ret = el->src_el(); + } } } break; @@ -987,24 +1021,30 @@ std::shared_ptr litehtml::render_item::get_child_by_point(pi break; } - if(el && !el->src_el()->is_positioned()) + if(process && !el->src_el()->is_positioned()) { if(flag == draw_positioned) { - element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex); + element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex, check); if(child) { - ret = child; + if (!check || check(el)) + { + ret = child; + } } } else { if( el->src_el()->css().get_float() == float_none && el->src_el()->css().get_display() != display_inline_block && el->src_el()->css().get_display() != display_inline_flex) { - element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex); + element::ptr child = el->get_child_by_point(el_pos.x, el_pos.y, client_x, client_y, flag, zindex, check); if(child) { - ret = child; + if (!check || check(el)) + { + ret = child; + } } } } @@ -1015,7 +1055,7 @@ std::shared_ptr litehtml::render_item::get_child_by_point(pi return ret; } -std::shared_ptr litehtml::render_item::get_element_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y) +std::shared_ptr litehtml::render_item::get_element_by_point(pixel_t x, pixel_t y, pixel_t client_x, pixel_t client_y, const std::function&)>& check) { if(!is_visible()) return nullptr; @@ -1028,11 +1068,11 @@ std::shared_ptr litehtml::render_item::get_element_by_point(p z_indexes[i->src_el()->css().get_z_index()]; } - for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); iter++) + for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); ++iter) { if(iter->first > 0) { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first); + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first, check); if(ret) return ret; } } @@ -1041,26 +1081,26 @@ std::shared_ptr litehtml::render_item::get_element_by_point(p { if(z_index.first == 0) { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, z_index.first); + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, z_index.first, check); if(ret) return ret; } } - ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0); + ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0, check); if(ret) return ret; - ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0); + ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0, check); if(ret) return ret; - ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0); + ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0,check); if(ret) return ret; - for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); iter++) + for(auto iter = z_indexes.rbegin(); iter != z_indexes.rend(); ++iter) { if(iter->first < 0) { - ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first); + ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, iter->first, check); if(ret) return ret; } } @@ -1069,33 +1109,34 @@ std::shared_ptr litehtml::render_item::get_element_by_point(p { if(is_point_inside(client_x, client_y)) { - ret = src_el(); + if (!check || check(this->shared_from_this())) + { + ret = src_el(); + } + } } else { if(is_point_inside(x, y)) { - ret = src_el(); + if (!check || check(this->shared_from_this())) + { + ret = src_el(); + } } } return ret; } -bool litehtml::render_item::is_point_inside( pixel_t x, pixel_t y ) +bool litehtml::render_item::is_point_inside( pixel_t x, pixel_t y ) const { if(src_el()->css().get_display() != display_inline && src_el()->css().get_display() != display_table_row) { position pos = m_pos; pos += m_padding; pos += m_borders; - if(pos.is_point_inside(x, y)) - { - return true; - } else - { - return false; - } + return pos.is_point_inside(x, y); } else { position::vector boxes; @@ -1111,16 +1152,20 @@ bool litehtml::render_item::is_point_inside( pixel_t x, pixel_t y ) return false; } -void litehtml::render_item::get_rendering_boxes( position::vector& redraw_boxes) +void litehtml::render_item::get_rendering_boxes( position::vector& redraw_boxes) const { if(src_el()->css().get_display() == display_inline || src_el()->css().get_display() == display_table_row) { get_inline_boxes(redraw_boxes); + for(auto& box : redraw_boxes) + { + scroll_box(box); + } } else { position pos = m_pos; - pos += m_padding; - pos += m_borders; + pos += m_padding; + pos += m_borders; redraw_boxes.push_back(pos); } @@ -1136,12 +1181,12 @@ void litehtml::render_item::get_rendering_boxes( position::vector& redraw_boxes) { position view_port; src_el()->get_document()->container()->get_viewport(view_port); - add_x += cur_el->m_pos.x + view_port.left(); - add_y += cur_el->m_pos.y + view_port.top(); + add_x += cur_el->m_pos.x + view_port.left() - cur_el->get_scroll_left(); + add_y += cur_el->m_pos.y + view_port.top() - cur_el->get_scroll_top(); break; } - add_x += cur_el->m_pos.x; - add_y += cur_el->m_pos.y; + add_x += cur_el->m_pos.x - cur_el->get_scroll_left(); + add_y += cur_el->m_pos.y - cur_el->get_scroll_top(); cur_el = cur_el->parent(); } for(auto& box : redraw_boxes) diff --git a/support/gtkmm4/html_widget.cpp b/support/gtkmm4/html_widget.cpp index d31d4fb8..6c9bd19b 100644 --- a/support/gtkmm4/html_widget.cpp +++ b/support/gtkmm4/html_widget.cpp @@ -235,6 +235,8 @@ void html_widget::on_button_release_event(int /* n_press */, double x, double y) void html_widget::on_mouse_move(double x, double y) { + m_mouse_x = x; + m_mouse_y = y; bool restart_timer = true; if(m_hscrollbar->is_visible()) { @@ -499,8 +501,40 @@ void html_widget::on_adjustments_changed() bool html_widget::on_scroll(double dx, double dy) { - m_vadjustment->set_value(m_vadjustment->get_value() + dy * 60); - m_hadjustment->set_value(m_hadjustment->get_value() + dx * 60); + litehtml::position scroll_box; + auto page = current_page(); + if (page) + { + bool redraw = false; + if (page->on_v_scroll( (int) dy * 60, + (int) (m_mouse_x + m_draw_buffer.get_left()), + (int) (m_mouse_y + m_draw_buffer.get_top()), + (int) m_mouse_x, + (int) m_mouse_y, scroll_box) == 0) + { + m_vadjustment->set_value(m_vadjustment->get_value() + dy * 60); + } else + { + redraw = true; + } + if (page->on_h_scroll( (int) dx * 60, + (int) (m_mouse_x + m_draw_buffer.get_left()), + (int) (m_mouse_y + m_draw_buffer.get_top()), + (int) m_mouse_x, + (int) m_mouse_y, scroll_box) == 0) + { + m_hadjustment->set_value(m_hadjustment->get_value() + dx * 60); + } else + { + redraw = true; + } + if (redraw) + { + m_draw_buffer.redraw(get_draw_function(current_page())); + queue_draw(); + + } + } return true; } diff --git a/support/gtkmm4/html_widget.h b/support/gtkmm4/html_widget.h index f98064c8..4a8bc2bd 100644 --- a/support/gtkmm4/html_widget.h +++ b/support/gtkmm4/html_widget.h @@ -204,6 +204,8 @@ class html_widget : public Gtk::Widget, { int m_rendered_width = 0; int m_rendered_height = 0; + double m_mouse_x = 0; + double m_mouse_y = 0; bool m_do_force_redraw_on_adjustment = true; std::mutex m_page_mutex; std::shared_ptr m_current_page; @@ -289,7 +291,7 @@ class html_widget : public Gtk::Widget, } litebrowser::draw_buffer::draw_page_function_t get_draw_function(const std::shared_ptr& page) { - return [this, page](cairo_t* cr, int x, int y, const litehtml::position* clip) + return [page](cairo_t* cr, int x, int y, const litehtml::position* clip) { if (page) { diff --git a/support/webpage/web_page.h b/support/webpage/web_page.h index 6080b416..e799a166 100644 --- a/support/webpage/web_page.h +++ b/support/webpage/web_page.h @@ -176,6 +176,19 @@ namespace litebrowser m_html->dump(cout); } } + + int on_h_scroll(int dx, int x, int y, int client_x, int client_y, litehtml::position& scroll_box) + { + std::lock_guard html_lock(m_html_mutex); + return m_html ? m_html->on_h_scroll(dx, x, y, client_x, client_y, scroll_box) : 0; + } + + int on_v_scroll(int dy, int x, int y, int client_x, int client_y, litehtml::position& scroll_box) + { + std::lock_guard html_lock(m_html_mutex); + return m_html ? m_html->on_v_scroll(dy, x, y, client_x, client_y, scroll_box) : 0; + } + private: void http_request(const std::string& url, const std::function& cb_on_data,