From 1b4c32982375e503a15290ab037412fd3b7937fb Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Fri, 21 Mar 2025 18:09:06 +0300 Subject: [PATCH 01/17] Rebuilt litebrowser with GTK4 (gtkmm4) --- .gitignore | 1 + CMakeLists.txt | 71 ++- litehtml | 2 +- src/browser_wnd.cpp | 297 +++++---- src/browser_wnd.h | 33 +- src/globals.h | 26 - src/html_widget.cpp | 470 -------------- src/html_widget.h | 76 --- src/main.cpp | 17 +- src/{ => webpage}/html_host.h | 28 +- src/{ => webpage}/http_request.cpp | 0 src/{ => webpage}/http_request.h | 0 src/{ => webpage}/http_requests_pool.cpp | 0 src/{ => webpage}/http_requests_pool.h | 0 src/{ => webpage}/web_history.cpp | 15 +- src/{ => webpage}/web_history.h | 12 +- src/{ => webpage}/web_page.cpp | 110 ++-- src/{ => webpage}/web_page.h | 91 ++- src/widget/html_widget.cpp | 769 +++++++++++++++++++++++ src/widget/html_widget.h | 495 +++++++++++++++ 20 files changed, 1612 insertions(+), 901 deletions(-) delete mode 100644 src/globals.h delete mode 100644 src/html_widget.cpp delete mode 100644 src/html_widget.h rename src/{ => webpage}/html_host.h (58%) rename src/{ => webpage}/http_request.cpp (100%) rename src/{ => webpage}/http_request.h (100%) rename src/{ => webpage}/http_requests_pool.cpp (100%) rename src/{ => webpage}/http_requests_pool.h (100%) rename src/{ => webpage}/web_history.cpp (85%) rename src/{ => webpage}/web_history.h (60%) rename src/{ => webpage}/web_page.cpp (71%) rename src/{ => webpage}/web_page.h (69%) create mode 100644 src/widget/html_widget.cpp create mode 100644 src/widget/html_widget.h diff --git a/.gitignore b/.gitignore index 1f09893..b5c88a3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ build/ .settings .project .idea +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index bffe7ef..4290e22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(litebrowser CXX) find_package(PkgConfig REQUIRED) if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "No build type selected, default to Release") - set(CMAKE_BUILD_TYPE "Release") + message(STATUS "No build type selected, default to Release") + set(CMAKE_BUILD_TYPE "Release") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") @@ -19,48 +19,53 @@ set(LITEBROWSER_PATH src) set(LITEHTML_PATH litehtml) set(CONTAINER_PATH ${LITEHTML_PATH}/containers/cairo) -pkg_check_modules(LB_LIBS REQUIRED gdkmm-3.0 gtkmm-3.0 libcurl cairo pango pangocairo) +pkg_check_modules(LB_LIBS REQUIRED gtkmm-4.0 libcurl cairo pango pangocairo) set(SOURCE ${LITEBROWSER_PATH}/main.cpp - ${LITEBROWSER_PATH}/html_widget.cpp - ${LITEBROWSER_PATH}/browser_wnd.cpp - ${LITEBROWSER_PATH}/web_history.cpp - ${LITEBROWSER_PATH}/http_request.cpp - ${LITEBROWSER_PATH}/web_page.cpp - ${LITEBROWSER_PATH}/http_requests_pool.cpp - ${CONTAINER_PATH}/container_cairo.cpp - ${CONTAINER_PATH}/cairo_borders.cpp - ${CONTAINER_PATH}/container_cairo_pango.cpp - ) + ${LITEBROWSER_PATH}/widget/html_widget.cpp + ${LITEBROWSER_PATH}/browser_wnd.cpp + ${LITEBROWSER_PATH}/webpage/web_history.cpp + ${LITEBROWSER_PATH}/webpage/http_request.cpp + ${LITEBROWSER_PATH}/webpage/web_page.cpp + ${LITEBROWSER_PATH}/webpage/http_requests_pool.cpp + ${CONTAINER_PATH}/container_cairo.cpp + ${CONTAINER_PATH}/cairo_borders.cpp + ${CONTAINER_PATH}/container_cairo_pango.cpp + ) set(HEADERS ${LITEBROWSER_PATH}/browser_wnd.h - ${LITEBROWSER_PATH}/html_widget.h - ${LITEBROWSER_PATH}/globals.h - ${LITEBROWSER_PATH}/web_history.h - ${LITEBROWSER_PATH}/http_request.h - ${LITEBROWSER_PATH}/web_page.h - ${LITEBROWSER_PATH}/html_host.h - ${LITEBROWSER_PATH}/http_requests_pool.h - ${LITEBROWSER_PATH}/html_dumper.h - ${CONTAINER_PATH}/container_cairo.h - ${CONTAINER_PATH}/cairo_borders.h - ${CONTAINER_PATH}/container_cairo_pango.h - ${CONTAINER_PATH}/cairo_images_cache.h - ) + ${LITEBROWSER_PATH}/widget/html_widget.h + ${LITEBROWSER_PATH}/webpage/web_history.h + ${LITEBROWSER_PATH}/webpage/http_request.h + ${LITEBROWSER_PATH}/webpage/web_page.h + ${LITEBROWSER_PATH}/webpage/html_host.h + ${LITEBROWSER_PATH}/webpage/http_requests_pool.h + ${LITEBROWSER_PATH}/html_dumper.h + ${CONTAINER_PATH}/container_cairo.h + ${CONTAINER_PATH}/cairo_borders.h + ${CONTAINER_PATH}/container_cairo_pango.h + ${CONTAINER_PATH}/cairo_images_cache.h + ) + +if (FOR_TESTING) + set(HEADERS ${HEADERS} litehtml-tests/fonts.h) + set(SOURCE ${SOURCE} litehtml-tests/fonts.cpp) + pkg_check_modules(FONTCONFIG REQUIRED fontconfig) + add_compile_options(-DFOR_TESTING) +endif () -option(LITEHTML_BUILD_TESTING "enable testing for litehtml" OFF) add_subdirectory(${LITEHTML_PATH}) # additional warnings add_compile_options(-Wall -Wextra -Wpedantic) add_executable(${PROJECT_NAME} ${SOURCE} ${HEADERS}) -include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${LB_LIBS_INCLUDE_DIRS} ${CONTAINER_PATH}) -target_link_options(${PROJECT_NAME} PRIVATE ${LB_LIBS_LDFLAGS}) -target_link_libraries(${PROJECT_NAME} litehtml ${LB_LIBS_LIBRARIES}) +include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${LITEBROWSER_PATH}/webpage ${LITEBROWSER_PATH}/widget ${LB_LIBS_INCLUDE_DIRS} ${CONTAINER_PATH} litehtml-tests) +target_link_options(${PROJECT_NAME} PRIVATE ${LB_LIBS_LDFLAGS} ${FONTCONFIG_LDFLAGS}) +target_link_libraries(${PROJECT_NAME} litehtml ${LB_LIBS_LIBRARIES} ${FONTCONFIG_LIBRARIES}) set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - C_STANDARD 99 - ) + CXX_STANDARD 17 + C_STANDARD 99 + ) diff --git a/litehtml b/litehtml index 135fbdc..c214c27 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit 135fbdcae3eea2318f8ba8b2d85582322899c5fc +Subproject commit c214c27d2c00153d5ed6de1df97cd6c04bb4d272 diff --git a/src/browser_wnd.cpp b/src/browser_wnd.cpp index a18cc05..5352196 100644 --- a/src/browser_wnd.cpp +++ b/src/browser_wnd.cpp @@ -1,7 +1,8 @@ -#include "globals.h" #include "browser_wnd.h" #include #include +#include +#include "html_dumper.h" struct { @@ -16,141 +17,170 @@ struct {"Obama (Wiki)", "https://en.wikipedia.org/wiki/Barack_Obama?useskin=vector"}, {"Elizabeth II (Wiki)", "https://en.wikipedia.org/wiki/Elizabeth_II?useskin=vector"}, {"std::vector", "https://en.cppreference.com/w/cpp/container/vector"}, + {"BinaryTides", "https://www.binarytides.com/"}, + {"Ubuntu Forums", "https://ubuntuforums.org/"}, }; -browser_window::browser_window(const std::string& url) : - m_prev_state(0), - m_html(this), +static inline void mk_button(Gtk::Button& btn, const std::string& label_text, const std::string& icon_name) +{ + btn.set_focusable(false); + + auto icon = Gtk::make_managed(); + icon->set_from_icon_name(icon_name); + icon->set_icon_size(Gtk::IconSize::NORMAL); + icon->set_expand(true); - m_tools_render1("Single Render"), - m_tools_render10("Render 10 Times"), - m_tools_render100("Render 100 Times"), + auto hbox = Gtk::make_managed(Gtk::Orientation::VERTICAL); + hbox->append(*icon); + hbox->set_expand(false); - m_tools_draw1("Single Draw"), - m_tools_draw10("Draw 10 Times"), - m_tools_draw100("Draw 100 Times"), + btn.set_child(*hbox); + btn.set_tooltip_text(label_text); +} - m_tools_dump("Dump parsed HTML") +browser_window::browser_window(const Glib::RefPtr& app, const std::string& url) : + m_prev_state(0) { - add(m_vbox); - m_vbox.show(); + set_child(m_vbox); set_titlebar(m_header); m_header.show(); - m_header.set_show_close_button(true); - m_header.property_spacing().set_value(0); + m_header.set_show_title_buttons(false); - m_header.pack_start(m_back_button); - m_back_button.show(); + auto title_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); + auto left_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 5); + auto right_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 5); + + left_box->append(m_back_button); + mk_button(m_back_button, "Go Back", "go-previous-symbolic"); m_back_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_back_clicked) ); - m_back_button.set_image_from_icon_name("go-previous-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.pack_start(m_forward_button); - m_forward_button.show(); + left_box->append(m_forward_button); + mk_button(m_forward_button, "Go Forward", "go-next-symbolic"); + m_forward_button.set_margin_end(10); m_forward_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_forward_clicked) ); - m_forward_button.set_image_from_icon_name("go-next-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.pack_start(m_stop_reload_button); - m_stop_reload_button.show(); + left_box->append(m_stop_reload_button); + mk_button(m_stop_reload_button, "Reload Page", "view-refresh-symbolic"); m_stop_reload_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_stop_reload_clicked) ); - m_stop_reload_button.set_image_from_icon_name("view-refresh-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.pack_start(m_home_button); - m_home_button.show(); + left_box->append(m_home_button); + mk_button(m_home_button, "Go Home", "go-home-symbolic"); m_home_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_home_clicked) ); - m_home_button.set_image_from_icon_name("go-home-symbolic", Gtk::ICON_SIZE_BUTTON); - m_header.set_custom_title(m_address_bar); - m_address_bar.set_hexpand_set(true); - m_address_bar.set_hexpand(); - m_address_bar.property_primary_icon_name().set_value("document-open-symbolic"); - m_address_bar.set_margin_start(32); + left_box->set_hexpand(false); + title_box->append(*left_box); - m_address_bar.show(); + title_box->append(m_address_bar); + m_address_bar.set_hexpand(true); + m_address_bar.set_halign(Gtk::Align::FILL); + m_address_bar.set_margin_start(20); + m_address_bar.set_margin_end(3); + m_address_bar.signal_activate().connect(sigc::mem_fun(*this, &browser_window::on_go_clicked)); + m_address_bar.property_primary_icon_name().set_value("insert-link-symbolic"); m_address_bar.set_text("http://www.litehtml.com/"); - m_address_bar.add_events(Gdk::KEY_PRESS_MASK); - m_address_bar.signal_key_press_event().connect( sigc::mem_fun(*this, &browser_window::on_address_key_press), false ); - m_menu_bookmarks.set_halign(Gtk::ALIGN_END); + right_box->append(m_go_button); + mk_button(m_go_button, "Go", "media-playback-start-symbolic"); + m_go_button.set_margin_end(20); + m_go_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_go_clicked) ); + + // Creating bookmarks popover + auto menu_model = Gio::Menu::create(); for(const auto& url : g_bookmarks) { - m_menu_items.emplace_back(url.name); - m_menu_bookmarks.append(m_menu_items.back()); - m_menu_items.back().signal_activate().connect( - sigc::bind( - sigc::mem_fun(m_html, &html_widget::open_url), - litehtml::string(url.url))); + std::string action; + action += "app.open_url('"; + action += url.url; + action += "')"; + menu_model->append(url.name, action); } - m_menu_bookmarks.show_all(); - m_header.pack_end(m_tools_button); - m_tools_button.set_popup(m_menu_tools); - m_tools_button.show(); - m_tools_button.set_image_from_icon_name("preferences-system-symbolic", Gtk::ICON_SIZE_BUTTON); + auto action = Gio::SimpleAction::create("open_url", Glib::VariantType("s")); + action->signal_activate().connect([this](const Glib::VariantBase& parameter) { + auto value = parameter.get_dynamic(); + m_html.open_url(value); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + m_bookmarks_popover.set_menu_model(menu_model); + m_bookmarks_popover.set_has_arrow(true); + m_bookmarks_popover.set_parent(m_bookmarks_button); + + right_box->append(m_bookmarks_button); + mk_button(m_bookmarks_button, "Bookmarks", "user-bookmarks-symbolic"); + m_bookmarks_button.signal_clicked().connect( [this]() { m_bookmarks_popover.popup(); } ); + + // Creating tools popover + menu_model = Gio::Menu::create(); + auto section_render = Gio::Menu::create(); + auto section_draw = Gio::Menu::create(); + auto section_other = Gio::Menu::create(); + + section_render->append("Single Render", "app.test_render(1)"); + section_render->append("Render 10 Times", "app.test_render(10)"); + section_render->append("Render 100 Times", "app.test_render(100)"); + section_draw->append("Single Draw", "app.test_draw(1)"); + section_draw->append("Draw 10 Times", "app.test_draw(10)"); + section_draw->append("Draw 100 Times", "app.test_draw(100)"); + section_other->append("Dump parsed HTML", "app.dump"); + + menu_model->append_section(section_render); + menu_model->append_section(section_draw); + menu_model->append_section(section_other); + + m_tools_popover.set_menu_model(menu_model); + m_tools_popover.set_has_arrow(true); + m_tools_popover.set_parent(m_tools_button); + + action = Gio::SimpleAction::create("test_render", Glib::VariantType("i")); + action->signal_activate().connect([this](const Glib::VariantBase& parameter) { + auto value = parameter.get_dynamic(); + on_render_measure(value); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + action = Gio::SimpleAction::create("test_draw", Glib::VariantType("i")); + action->signal_activate().connect([this](const Glib::VariantBase& parameter) { + auto value = parameter.get_dynamic(); + on_draw_measure(value); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + action = Gio::SimpleAction::create("dump"); + action->signal_activate().connect([this](const Glib::VariantBase& /* parameter */) { + on_dump(); + m_bookmarks_popover.popdown(); + }); + app->add_action(action); + + right_box->append(m_tools_button); + mk_button(m_tools_button, "Tools", "preferences-system-symbolic"); + m_tools_button.signal_clicked().connect( [this]() { m_tools_popover.popup(); } ); + + auto win_ctls = Gtk::make_managed(Gtk::PackType::END); + right_box->append(*win_ctls); + + title_box->append(*right_box); + + title_box->set_halign(Gtk::Align::FILL); + title_box->set_hexpand(true); + m_header.set_title_widget(*title_box); + set_titlebar(m_header); - m_header.pack_end(m_bookmarks_button); - m_bookmarks_button.set_popup(m_menu_bookmarks); - m_bookmarks_button.show(); - m_bookmarks_button.set_image_from_icon_name("user-bookmarks-symbolic", Gtk::ICON_SIZE_BUTTON); + m_vbox.append(m_html); + m_html.set_expand(true); + m_html.signal_set_address().connect( sigc::mem_fun(*this, &browser_window::set_address) ); + m_html.signal_update_state().connect( sigc::mem_fun(*this, &browser_window::update_buttons) ); - m_go_button.signal_clicked().connect( sigc::mem_fun(*this, &browser_window::on_go_clicked) ); - m_go_button.set_margin_end(32); - - m_header.pack_end(m_go_button); - m_go_button.show(); - m_go_button.set_image_from_icon_name("media-playback-start-symbolic", Gtk::ICON_SIZE_BUTTON); - - m_menu_tools.set_halign(Gtk::ALIGN_END); - m_menu_tools.append(m_tools_render1); - m_menu_tools.append(m_tools_render10); - m_menu_tools.append(m_tools_render100); - m_menu_tools.append(m_tools_draw1); - m_menu_tools.append(m_tools_draw10); - m_menu_tools.append(m_tools_draw100); - m_menu_tools.append(m_tools_dump); - - m_menu_tools.show_all(); - - m_tools_render1.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_render_measure), - 1)); - m_tools_render10.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_render_measure), - 10)); - m_tools_render100.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_render_measure), - 100)); - - m_tools_draw1.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_draw_measure), - 1)); - m_tools_draw10.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_draw_measure), - 10)); - m_tools_draw100.signal_activate().connect( - sigc::bind( - sigc::mem_fun(*this, &browser_window::on_draw_measure), - 100)); - - m_tools_dump.signal_activate().connect( - sigc::mem_fun(*this, &browser_window::on_dump)); - - m_vbox.pack_start(m_scrolled_wnd, Gtk::PACK_EXPAND_WIDGET); - m_scrolled_wnd.show(); - - m_scrolled_wnd.add(m_html); - m_html.show(); - - signal_delete_event().connect(sigc::mem_fun(m_html, &html_widget::on_close), false); + signal_close_request().connect(sigc::mem_fun(m_html, &html_widget::on_close), false); set_default_size(1280, 720); @@ -159,23 +189,25 @@ browser_window::browser_window(const std::string& url) : m_html.open_url(url); } - update_buttons(); + update_buttons(0); } browser_window::~browser_window() { - + m_bookmarks_popover.unparent(); + m_tools_popover.unparent(); } void browser_window::on_go_clicked() { litehtml::string url = m_address_bar.get_text(); m_html.open_url(url); + m_html.grab_focus(); } -bool browser_window::on_address_key_press(GdkEventKey* event) +bool browser_window::on_address_key_press(guint keyval, guint /*keycode*/, Gdk::ModifierType /*state*/) { - if(event->keyval == GDK_KEY_Return) + if(keyval == GDK_KEY_Return) { m_address_bar.select_region(0, -1); on_go_clicked(); @@ -195,29 +227,17 @@ void browser_window::on_back_clicked() m_html.go_back(); } -void browser_window::update_buttons() +void browser_window::update_buttons(uint32_t) { uint32_t state = m_html.get_state(); if((m_prev_state & page_state_has_back) != (state & page_state_has_back)) { - if (state & page_state_has_back) - { - m_back_button.set_state(Gtk::STATE_NORMAL); - } else - { - m_back_button.set_state(Gtk::STATE_INSENSITIVE); - } + m_back_button.set_sensitive(state & page_state_has_back); } if((m_prev_state & page_state_has_forward) != (state & page_state_has_forward)) { - if (state & page_state_has_forward) - { - m_forward_button.set_state(Gtk::STATE_NORMAL); - } else - { - m_forward_button.set_state(Gtk::STATE_INSENSITIVE); - } + m_forward_button.set_sensitive(state & page_state_has_forward); } if((m_prev_state & page_state_downloading) != (state & page_state_downloading)) { @@ -240,11 +260,11 @@ void browser_window::on_render_measure(int number) message << time << " ms for " << number << " times rendering"; - m_pDialog.reset(new Gtk::MessageDialog(*this, message.str(), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true)); - - m_pDialog->signal_response().connect( - sigc::hide(sigc::mem_fun(*m_pDialog, &Gtk::Widget::hide))); - m_pDialog->show(); + auto dialog = Gtk::AlertDialog::create(); + dialog->set_message(message.str()); + dialog->set_buttons({"OK"}); + dialog->set_modal(true); + dialog->show(*this); } void browser_window::on_draw_measure(int number) @@ -255,16 +275,23 @@ void browser_window::on_draw_measure(int number) message << time << " ms for " << number << " times measure"; - m_pDialog.reset(new Gtk::MessageDialog(*this, message.str(), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true)); - - m_pDialog->signal_response().connect( - sigc::hide(sigc::mem_fun(*m_pDialog, &Gtk::Widget::hide))); - m_pDialog->show(); + auto dialog = Gtk::AlertDialog::create(); + dialog->set_message(message.str()); + dialog->set_buttons({"OK"}); + dialog->set_modal(true); + dialog->show(*this); } void browser_window::on_dump() { - m_html.dump("/tmp/litehtml-dump.txt"); + html_dumper cout("/tmp/litehtml-dump.txt"); + m_html.dump(cout); + auto dialog = Gtk::AlertDialog::create(); + dialog->set_message("File is saved"); + dialog->set_detail("The parsed HTML tree was saved into he file: /tmp/litehtml-dump.txt"); + dialog->set_buttons({"Close"}); + dialog->set_modal(true); + dialog->show(*this); } void browser_window::on_stop_reload_clicked() diff --git a/src/browser_wnd.h b/src/browser_wnd.h index 331b4a3..6902e76 100644 --- a/src/browser_wnd.h +++ b/src/browser_wnd.h @@ -8,17 +8,15 @@ class browser_window : public Gtk::Window { public: - browser_window(const std::string& url); - virtual ~browser_window(); + browser_window(const Glib::RefPtr& app, const std::string& url); + ~browser_window() override; - void update_buttons(); + void update_buttons(uint32_t); void set_address(const std::string& text) { m_address_bar.set_text(text); } - Gtk::ScrolledWindow* get_scrolled() { return &m_scrolled_wnd; } - private: void on_go_clicked(); void on_forward_clicked(); @@ -27,11 +25,11 @@ class browser_window : public Gtk::Window void on_back_clicked(); void on_render_measure(int number); void on_draw_measure(int number); - bool on_address_key_press(GdkEventKey* event); + bool on_address_key_press(guint keyval, guint /*keycode*/, Gdk::ModifierType /*state*/); void on_dump(); protected: - uint32_t m_prev_state; + uint32_t m_prev_state; html_widget m_html; Gtk::Entry m_address_bar; Gtk::Button m_go_button; @@ -39,23 +37,12 @@ class browser_window : public Gtk::Window Gtk::Button m_back_button; Gtk::Button m_stop_reload_button; Gtk::Button m_home_button; - Gtk::MenuButton m_bookmarks_button; - Gtk::MenuButton m_tools_button; - Gtk::VBox m_vbox; + Gtk::Button m_bookmarks_button; + Gtk::Button m_tools_button; + Gtk::Box m_vbox {Gtk::Orientation::HORIZONTAL}; Gtk::HeaderBar m_header; - Gtk::ScrolledWindow m_scrolled_wnd; - - Gtk::Menu m_menu_bookmarks; - std::vector m_menu_items; - - Gtk::Menu m_menu_tools; - Gtk::MenuItem m_tools_render1; - Gtk::MenuItem m_tools_render10; - Gtk::MenuItem m_tools_render100; - Gtk::MenuItem m_tools_draw1; - Gtk::MenuItem m_tools_draw10; - Gtk::MenuItem m_tools_draw100; - Gtk::MenuItem m_tools_dump; + Gtk::PopoverMenu m_bookmarks_popover; + Gtk::PopoverMenu m_tools_popover; std::unique_ptr m_pDialog; diff --git a/src/globals.h b/src/globals.h deleted file mode 100644 index 83a9e05..0000000 --- a/src/globals.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#if defined __APPLE__ || __OpenBSD__ -#include -#else -#include -#endif -#include -#define _USE_MATH_DEFINES -#include -#include -#include -#include -#include -#include "../litehtml/include/litehtml.h" -#include -#include -#include -#include diff --git a/src/html_widget.cpp b/src/html_widget.cpp deleted file mode 100644 index b1107c6..0000000 --- a/src/html_widget.cpp +++ /dev/null @@ -1,470 +0,0 @@ -#include "globals.h" -#include "html_widget.h" -#include "browser_wnd.h" -#include -#include - -html_widget::html_widget(browser_window* browser) -{ - m_browser = browser; - m_rendered_width = 0; - add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK); -} - -html_widget::~html_widget() -{ - -} - -bool html_widget::on_draw(const Cairo::RefPtr& cr) -{ - litehtml::position pos; - - GdkRectangle rect; - gdk_cairo_get_clip_rectangle(cr->cobj(), &rect); - - pos.width = rect.width; - pos.height = rect.height; - pos.x = rect.x; - pos.y = rect.y; - - cr->rectangle(0, 0, get_allocated_width(), get_allocated_height()); - cr->set_source_rgb(1, 1, 1); - cr->fill(); - - { - auto page = current_page(); - if (page) - { - page->draw((litehtml::uint_ptr) cr->cobj(), 0, 0, &pos); - } - } - - return true; -} - -void html_widget::get_client_rect(litehtml::position& client) const -{ - client.width = get_parent()->get_allocated_width(); - client.height = get_parent()->get_allocated_height(); - client.x = 0; - client.y = 0; -} - -void html_widget::set_caption(const char* caption) -{ - if(get_parent_window()) - { - get_parent_window()->set_title(caption); - } -} - -Gtk::Allocation html_widget::get_parent_allocation() -{ - Gtk::Container* parent = get_parent(); - return parent->get_allocation(); -} - -void html_widget::open_page(const litehtml::string& url, const litehtml::string& hash) -{ - { - std::lock_guard lock(m_page_mutex); - if (m_current_page) - { - m_current_page->stop_loading(); - } - m_next_page = std::make_shared(this, 10); - m_next_page->open(url, hash); - } - m_browser->set_address(url); - m_browser->update_buttons(); -} - -void html_widget::scroll_to(int x, int y) -{ - auto vadj = m_browser->get_scrolled()->get_vadjustment(); - auto hadj = m_browser->get_scrolled()->get_hadjustment(); - vadj->set_value(vadj->get_lower() + y); - hadj->set_value(hadj->get_lower() + x); -} - -void html_widget::on_parent_size_allocate(Gtk::Allocation allocation) -{ - std::shared_ptr page = current_page(); - if(page && m_rendered_width != allocation.get_width()) - { - m_rendered_width = allocation.get_width(); - page->media_changed(); - render(); - } -} - -void html_widget::on_parent_changed(Gtk::Widget* /*previous_parent*/) -{ - Gtk::Widget* viewport = get_parent(); - if(viewport) - { - viewport->signal_size_allocate().connect(sigc::mem_fun(*this, &html_widget::on_parent_size_allocate)); - } -} - -bool html_widget::on_button_press_event(GdkEventButton *event) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->on_lbutton_down((int) event->x, (int) event->y, (int) event->x, (int) event->y); - } - return true; -} - -bool html_widget::on_button_release_event(GdkEventButton *event) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->on_lbutton_up((int) event->x, (int) event->y, (int) event->x, (int) event->y); - } - return true; -} - -bool html_widget::on_motion_notify_event(GdkEventMotion *event) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->on_mouse_over((int) event->x, (int) event->y, (int) event->x, (int) event->y); - } - return true; -} - -void html_widget::update_cursor() -{ - Gdk::CursorType cursType = Gdk::ARROW; - std::shared_ptr page = current_page(); - if(page) - { - if (page->get_cursor() == "pointer") - { - cursType = Gdk::HAND2; - } - } - if(cursType == Gdk::ARROW) - { - get_window()->set_cursor(); - } else - { - get_window()->set_cursor( Gdk::Cursor::create(cursType) ); - } -} - -long html_widget::draw_measure(int number) -{ - std::shared_ptr page = current_page(); - - if(page) - { - auto vadj = m_browser->get_scrolled()->get_vadjustment(); - auto hadj = m_browser->get_scrolled()->get_hadjustment(); - - int width = (int) hadj->get_page_size(); - int height = (int) vadj->get_page_size(); - - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - auto image = (unsigned char *) g_malloc(stride * height); - - cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, width, height, - stride); - cairo_t *cr = cairo_create(surface); - - litehtml::position pos; - pos.width = width; - pos.height = height; - pos.x = 0; - pos.y = 0; - - int x = (int) (hadj->get_value() - hadj->get_lower()); - int y = (int) (vadj->get_value() - vadj->get_lower()); - - cairo_rectangle(cr, 0, 0, width, height); - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_paint(cr); - page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); - cairo_surface_write_to_png(surface, "/tmp/litebrowser.png"); - - auto t1 = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < number; i++) - { - page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); - } - auto t2 = std::chrono::high_resolution_clock::now(); - - cairo_destroy(cr); - cairo_surface_destroy(surface); - g_free(image); - - return (std::chrono::duration_cast(t2 - t1)).count(); - } - return 0; -} - -long html_widget::render_measure(int number) -{ - std::shared_ptr page = current_page(); - - if(page) - { - auto t1 = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < number; i++) - { - page->render(m_rendered_width); - } - auto t2 = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(t2 - t1)).count(); - } - return -1; -} - -void html_widget::on_size_allocate(Gtk::Allocation& allocation) -{ - Gtk::DrawingArea::on_size_allocate(allocation); - std::shared_ptr page = current_page(); - - if(page) - { - page->show_hash_and_reset(); - } -} - -static int action_redraw(void* data) -{ - auto ctl = (Gtk::Widget*) data; - ctl->queue_draw(); - return FALSE; -} - -static int action_render(void* data) -{ - auto ctl = (html_widget*) data; - ctl->render(); - return FALSE; -} - -static int action_update_state(void* data) -{ - auto ctl = (html_widget*) data; - ctl->browser()->update_buttons(); - return FALSE; -} - -void html_widget::redraw() -{ - g_idle_add(action_redraw, this); -} - -void html_widget::redraw_rect(int x, int y, int width, int height) -{ - queue_draw_area(x, y, width, height); -} - -int html_widget::get_render_width() -{ - return get_parent_allocation().get_width(); -} - -void html_widget::on_page_loaded() -{ - std::string url; - { - std::lock_guard lock(m_page_mutex); - m_current_page = m_next_page; - m_next_page = nullptr; - url = m_current_page->url(); - set_size_request(m_current_page->width(), m_current_page->height()); - } - scroll_to(0, 0); - redraw(); - m_browser->update_buttons(); - m_browser->set_address(url); -} - -void html_widget::show_hash(const std::string &hash) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->show_hash(hash); - } -} - -void html_widget::dump(const std::string &file_name) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->dump(file_name); - } -} - -void html_widget::open_url(const std::string &url) -{ - std::string hash; - std::string s_url = url; - - m_browser->set_address(url); - - std::string::size_type hash_pos = s_url.find_first_of(L'#'); - if(hash_pos != std::wstring::npos) - { - hash = s_url.substr(hash_pos + 1); - s_url.erase(hash_pos); - } - - bool open_hash_only = false; - bool reload = false; - - auto current_url = m_history.current(); - hash_pos = current_url.find_first_of(L'#'); - if(hash_pos != std::wstring::npos) - { - current_url.erase(hash_pos); - } - - if(!current_url.empty()) - { - if(m_history.current() != url) - { - if (current_url == s_url) - { - open_hash_only = true; - } - } else - { - reload = true; - } - } - if(!open_hash_only) - { - open_page(url, hash); - } else - { - show_hash(hash); - } - if(!reload) - { - m_history.url_opened(url); - } - m_browser->update_buttons(); -} - -void html_widget::render() -{ - std::shared_ptr page = current_page(); - if(page) - { - page->render(m_rendered_width); - set_size_request(page->width(), page->height()); - queue_draw(); - } -} - -bool html_widget::on_close(GdkEventAny* /*event*/) -{ - if(m_current_page) - { - m_current_page->stop_loading(); - } - if(m_next_page) - { - m_next_page->stop_loading(); - } - return false; -} - -void html_widget::go_forward() -{ - std::string url; - if(m_history.forward(url)) - { - open_url(url); - } -} - -void html_widget::go_back() -{ - std::string url; - if(m_history.back(url)) - { - open_url(url); - } -} - -uint32_t html_widget::get_state() -{ - uint32_t ret = 0; - std::string url; - if(m_history.back(url)) - { - ret |= page_state_has_back; - } - if(m_history.forward(url)) - { - ret |= page_state_has_forward; - } - { - std::lock_guard lock(m_page_mutex); - if(m_next_page) - { - ret |= page_state_downloading; - } - } - if(!(ret & page_state_downloading)) - { - std::shared_ptr page = current_page(); - if(page) - { - if(page->is_downloading()) - { - ret |= page_state_downloading; - } - } - } - return ret; -} - -void html_widget::queue_action(litebrowser::html_host_interface::q_action action) -{ - switch (action) - { - case queue_action_redraw: - g_idle_add(action_redraw, this); - break; - case queue_action_render: - g_idle_add(action_render, this); - break; - case queue_action_update_state: - g_idle_add(action_update_state, this); - break; - } -} - -void html_widget::stop_download() -{ - std::lock_guard lock(m_page_mutex); - if(m_next_page) - { - m_next_page->stop_loading(); - } else if (m_current_page) - { - m_current_page->stop_loading(); - } -} - -void html_widget::reload() -{ - std::shared_ptr page = current_page(); - if(page) - { - open_url(page->url()); - } -} diff --git a/src/html_widget.h b/src/html_widget.h deleted file mode 100644 index d8b5017..0000000 --- a/src/html_widget.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include -#include "html_host.h" -#include "web_page.h" -#include "http_requests_pool.h" -#include "web_history.h" - -class browser_window; - -enum page_state -{ - page_state_has_back = 0x01, - page_state_has_forward = 0x02, - page_state_downloading = 0x04, -}; - -class html_widget : public Gtk::DrawingArea, - public litebrowser::html_host_interface -{ - int m_rendered_width; - browser_window* m_browser; - std::mutex m_page_mutex; - std::shared_ptr m_current_page; - std::shared_ptr m_next_page; - web_history m_history; -public: - explicit html_widget(browser_window* browser); - ~html_widget() override; - - void open_page(const litehtml::string& url, const litehtml::string& hash) override; - void update_cursor() override; - void on_parent_size_allocate(Gtk::Allocation allocation); - void on_size_allocate(Gtk::Allocation& allocation) override; - void redraw() override; - void redraw_rect(int x, int y, int width, int height) override; - int get_render_width() override; - void on_page_loaded() override; - void dump(const litehtml::string& file_name); - void open_url(const std::string& url) override; - void render() override; - void queue_action(litebrowser::html_host_interface::q_action action) override; - void go_forward(); - void go_back(); - uint32_t get_state(); - void stop_download(); - void reload(); - browser_window* browser() const { return m_browser; } - - long render_measure(int number); - long draw_measure(int number); - void show_hash(const std::string& hash); - bool on_close(GdkEventAny* event); - -protected: - bool on_draw(const Cairo::RefPtr& cr) override; - void scroll_to(int x, int y) override; - - void get_client_rect(litehtml::position& client) const override; - void set_caption(const char* caption) override; - - bool on_button_press_event(GdkEventButton* event) override; - bool on_button_release_event(GdkEventButton* event) override; - bool on_motion_notify_event(GdkEventMotion* event) override; - - void on_parent_changed(Gtk::Widget* previous_parent) override; - -private: - std::shared_ptr current_page() - { - std::lock_guard lock(m_page_mutex); - return m_current_page; - } - Gtk::Allocation get_parent_allocation(); -}; diff --git a/src/main.cpp b/src/main.cpp index de91090..2dd66ee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,7 @@ -#include "globals.h" #include "browser_wnd.h" +#ifdef FOR_TESTING +#include "fonts.h" +#endif int on_cmd(const Glib::RefPtr &, Glib::RefPtr &app) { @@ -8,16 +10,15 @@ int on_cmd(const Glib::RefPtr &, Glib::RefPtr app = Gtk::Application::create(argc, argv, "litehtml.browser", Gio::APPLICATION_HANDLES_COMMAND_LINE); - - app->signal_command_line().connect( - sigc::bind(sigc::ptr_fun(on_cmd), app), false); - +#ifdef FOR_TESTING + prepare_fonts_for_testing(); +#endif std::string url; if(argc > 1) { url = argv[1]; } - browser_window win(url); - return app->run(win); + // Open the main window + auto app = Gtk::Application::create("litehtml.testsuite"); + return app->make_window_and_run(argc, argv, app, url); } diff --git a/src/html_host.h b/src/webpage/html_host.h similarity index 58% rename from src/html_host.h rename to src/webpage/html_host.h index 3e60843..7420fc3 100644 --- a/src/html_host.h +++ b/src/webpage/html_host.h @@ -1,6 +1,7 @@ #ifndef LITEBROWSER_HTML_HOST_H #define LITEBROWSER_HTML_HOST_H +#include #include namespace litebrowser @@ -8,12 +9,7 @@ namespace litebrowser class html_host_interface { public: - enum q_action - { - queue_action_redraw, - queue_action_render, - queue_action_update_state, - }; + virtual ~html_host_interface() = default; virtual void open_url(const std::string& url) = 0; virtual void open_page(const litehtml::string& url, const litehtml::string& hash) = 0; @@ -21,13 +17,25 @@ namespace litebrowser virtual void scroll_to(int x, int y) = 0; virtual void get_client_rect(litehtml::position& client) const = 0; virtual void set_caption(const char* caption) = 0; - virtual void redraw_rect(int x, int y, int width, int height) = 0; + virtual void redraw_boxes(const litehtml::position::vector& boxes) = 0; + virtual int get_render_width() = 0; + virtual double get_dpi() = 0; + virtual int get_screen_width() = 0; + virtual int get_screen_height() = 0; + virtual cairo_surface_t* load_image(const std::string& path) = 0; + }; + + class browser_notify_interface + { + public: + virtual ~browser_notify_interface() = default; + virtual void redraw() = 0; virtual void render() = 0; - virtual int get_render_width() = 0; - virtual void on_page_loaded() = 0; - virtual void queue_action(q_action action) = 0; + virtual void update_state() = 0; + virtual void on_page_loaded(uint64_t web_page_id) = 0; }; + } #endif //LITEBROWSER_HTML_HOST_H diff --git a/src/http_request.cpp b/src/webpage/http_request.cpp similarity index 100% rename from src/http_request.cpp rename to src/webpage/http_request.cpp diff --git a/src/http_request.h b/src/webpage/http_request.h similarity index 100% rename from src/http_request.h rename to src/webpage/http_request.h diff --git a/src/http_requests_pool.cpp b/src/webpage/http_requests_pool.cpp similarity index 100% rename from src/http_requests_pool.cpp rename to src/webpage/http_requests_pool.cpp diff --git a/src/http_requests_pool.h b/src/webpage/http_requests_pool.h similarity index 100% rename from src/http_requests_pool.h rename to src/webpage/http_requests_pool.h diff --git a/src/web_history.cpp b/src/webpage/web_history.cpp similarity index 85% rename from src/web_history.cpp rename to src/webpage/web_history.cpp index f75d81e..63b6586 100755 --- a/src/web_history.cpp +++ b/src/webpage/web_history.cpp @@ -1,17 +1,6 @@ -#include "globals.h" #include "web_history.h" -web_history::web_history() -{ - m_current_item = 0; -} - -web_history::~web_history() -{ - -} - void web_history::url_opened( const std::string& url ) { if(!m_items.empty()) @@ -35,7 +24,7 @@ void web_history::url_opened( const std::string& url ) if(m_current_item > 0 && m_items[m_current_item - 1] == url) { m_current_item--; - } else + } else { m_items.push_back(url); m_current_item = m_items.size() - 1; @@ -51,7 +40,7 @@ void web_history::url_opened( const std::string& url ) bool web_history::back( std::string& url ) { if(m_items.empty()) return false; - + if(m_current_item > 0) { url = m_items[m_current_item - 1]; diff --git a/src/web_history.h b/src/webpage/web_history.h similarity index 60% rename from src/web_history.h rename to src/webpage/web_history.h index 22c4884..7975b23 100755 --- a/src/web_history.h +++ b/src/webpage/web_history.h @@ -1,18 +1,22 @@ #pragma once +#include +#include -typedef std::vector string_vector; +using string_vector = std::vector; class web_history { string_vector m_items; - string_vector::size_type m_current_item; + string_vector::size_type m_current_item = 0; public: - web_history(); - virtual ~web_history(); + web_history() = default; + virtual ~web_history() = default; void url_opened(const std::string& url); bool back(std::string& url); bool forward(std::string& url); + + [[nodiscard]] std::string current() const { if(!m_items.empty() && m_current_item < m_items.size()) diff --git a/src/web_page.cpp b/src/webpage/web_page.cpp similarity index 71% rename from src/web_page.cpp rename to src/webpage/web_page.cpp index 2af56f5..b96c37d 100644 --- a/src/web_page.cpp +++ b/src/webpage/web_page.cpp @@ -1,7 +1,6 @@ #include #include #include "web_page.h" -#include "html_dumper.h" void litebrowser::text_file::on_data(void* data, size_t len, size_t /*downloaded*/, size_t /*total*/) { @@ -29,8 +28,8 @@ void litebrowser::web_page::open(const std::string &url, const std::string &hash m_hash = hash; auto data = std::make_shared(); - auto cb_on_data = std::bind(&text_file::on_data, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - auto cb_on_finish = std::bind(&web_page::on_page_downloaded, this, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); + auto cb_on_data = [data](auto && PH1, auto && PH2, auto && PH3, auto && PH4) mutable { data->on_data(std::forward(PH1), std::forward(PH2), std::forward(PH3), std::forward(PH4)); }; + auto cb_on_finish = std::bind(&web_page::on_page_downloaded, shared_from_this(), data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); http_request(m_url, cb_on_data, cb_on_finish); } @@ -99,7 +98,7 @@ cairo_surface_t* litebrowser::web_page::get_image(const std::string& url) void litebrowser::web_page::show_hash(const litehtml::string& hash) { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); if(hash.empty() || !m_html) { m_html_host->scroll_to(0, 0); @@ -139,32 +138,26 @@ void litebrowser::web_page::make_url(const char* url, const char* basepath, lite void litebrowser::web_page::on_mouse_over(int x, int y, int client_x, int client_y) { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); if(m_html) { litehtml::position::vector redraw_boxes; if(m_html->on_mouse_over(x, y, client_x, client_y, redraw_boxes)) { - for(auto& pos : redraw_boxes) - { - m_html_host->redraw_rect(pos.x, pos.y, pos.width, pos.height); - } + m_html_host->redraw_boxes(redraw_boxes); } } } void litebrowser::web_page::on_lbutton_down(int x, int y, int client_x, int client_y) { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); if(m_html) { litehtml::position::vector redraw_boxes; if(m_html->on_lbutton_down(x, y, client_x, client_y, redraw_boxes)) { - for(auto& pos : redraw_boxes) - { - m_html_host->redraw_rect(pos.x, pos.y, pos.width, pos.height); - } + m_html_host->redraw_boxes(redraw_boxes); } } } @@ -174,16 +167,13 @@ void litebrowser::web_page::on_lbutton_up(int x, int y, int client_x, int client if(m_html) { { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); litehtml::position::vector redraw_boxes; m_clicked_url.clear(); if (m_html->on_lbutton_up(x, y, client_x, client_y, redraw_boxes)) { - for (auto &pos: redraw_boxes) - { - m_html_host->redraw_rect(pos.x, pos.y, pos.width, pos.height); - } + m_html_host->redraw_boxes(redraw_boxes); } } if(!m_clicked_url.empty()) @@ -201,36 +191,26 @@ void litebrowser::web_page::on_page_downloaded(std::shared_ptr data, if(err_code == 0) { m_url = url; - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); data->set_ready(); - m_html = litehtml::document::createFromString(data->str().c_str(), this); + m_html_source = data->str(); } else { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); std::stringstream ss; ss << "

Impossible to load page

" << std::endl; ss << "

Error #" << err_code << ": " << err_text << "

" << std::endl; - m_html = litehtml::document::createFromString(ss.str().c_str(), this); + m_html_source = ss.str(); } + + m_html = litehtml::document::createFromString(m_html_source, this); if (m_html) { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); int render_width = m_html_host->get_render_width(); m_html->render(render_width); } - m_html_host->on_page_loaded(); -} - -void litebrowser::web_page::dump(const litehtml::string& file_name) -{ - if(m_html) - { - std::unique_lock html_lock(m_html_mutex); - - html_dumper dumper(file_name); - m_html->dump(dumper); - dumper.print(); - } + m_notify->on_page_loaded(id()); } void litebrowser::web_page::on_image_downloaded(std::shared_ptr data, @@ -242,23 +222,16 @@ void litebrowser::web_page::on_image_downloaded(std::shared_ptr data data->close(); if(!data->path().empty() && !err_code && (http_status == 200 || http_status == 0)) { - Glib::RefPtr ptr; - - try - { - ptr = Gdk::Pixbuf::create_from_file(data->path()); - } catch (...) { } + auto ptr = m_html_host->load_image(data->path()); if(ptr) { - { - m_images.add_image(data->url(), surface_from_pixbuf(ptr)); - } + m_images.add_image(data->url(), ptr); if(data->redraw_only()) { - m_html_host->queue_action(html_host_interface::queue_action_redraw); + m_notify->redraw(); } else { - m_html_host->queue_action(html_host_interface::queue_action_render); + m_notify->render(); } } } @@ -279,26 +252,6 @@ void litebrowser::web_page::load_image(const char *src, const char *baseurl, boo } } -cairo_surface_t* litebrowser::web_page::surface_from_pixbuf(const Glib::RefPtr& bmp) -{ - cairo_surface_t* ret; - - if(bmp->get_has_alpha()) - { - ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bmp->get_width(), bmp->get_height()); - } else - { - ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, bmp->get_width(), bmp->get_height()); - } - - Cairo::RefPtr surface(new Cairo::Surface(ret, false)); - Cairo::RefPtr ctx = Cairo::Context::create(surface); - Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0); - ctx->paint(); - - return ret; -} - void litebrowser::web_page::http_request(const std::string &url, const std::function &cb_on_data, const std::function &cb_on_finish) @@ -308,19 +261,32 @@ void litebrowser::web_page::http_request(const std::string &url, void litebrowser::web_page::on_pool_update_state() { - m_html_host->queue_action(html_host_interface::queue_action_update_state); + m_notify->update_state(); } double litebrowser::web_page::get_screen_dpi() const { - GdkScreen* screen = gdk_screen_get_default(); - return gdk_screen_get_resolution(screen); + return m_html_host->get_dpi(); +} + +int litebrowser::web_page::get_screen_width() const +{ + return m_html_host->get_screen_width(); +} + +int litebrowser::web_page::get_screen_height() const +{ + return m_html_host->get_screen_height(); +} + +void litebrowser::web_page::on_mouse_event(const litehtml::element::ptr& /*el*/, litehtml::mouse_event /*event*/) +{ + } ////////////////////////////////////////////////////////// litebrowser::image_file::image_file(std::string url, bool redraw_on_ready) : - m_fd(-1), m_url(std::move(url)), m_redraw_on_ready(redraw_on_ready) { diff --git a/src/web_page.h b/src/webpage/web_page.h similarity index 69% rename from src/web_page.h rename to src/webpage/web_page.h index 014ca13..2a3e8c0 100644 --- a/src/web_page.h +++ b/src/webpage/web_page.h @@ -1,8 +1,8 @@ #ifndef LITEBROWSER_WEB_PAGE_H #define LITEBROWSER_WEB_PAGE_H +#include #include "container_cairo_pango.h" -#include #include "html_host.h" #include "http_requests_pool.h" #include "cairo_images_cache.h" @@ -13,9 +13,9 @@ namespace litebrowser { std::mutex wait_mutex; std::stringstream stream; - bool data_ready; + bool data_ready = false; public: - text_file() : data_ready(false) + text_file() { wait_mutex.lock(); } @@ -33,7 +33,7 @@ namespace litebrowser class image_file { - int m_fd; + int m_fd = -1; std::string m_path; std::string m_url; bool m_redraw_on_ready; @@ -47,31 +47,46 @@ namespace litebrowser ::close(m_fd); } } - const std::string& path() const { return m_path; } + [[nodiscard]] + const std::string &path() const { return m_path; } + + [[nodiscard]] const std::string& url() const { return m_url; } + + [[nodiscard]] bool redraw_only() const { return m_redraw_on_ready; } }; class web_page : public container_cairo_pango, public std::enable_shared_from_this { - litehtml::string m_url; - litehtml::string m_base_url; - litehtml::document::ptr m_html; - std::mutex m_html_mutex; - litehtml::string m_cursor; - litehtml::string m_clicked_url; - std::string m_hash; - html_host_interface* m_html_host; - cairo_images_cache m_images; - litebrowser::http_requests_pool m_requests_pool; + litehtml::string m_url; + litehtml::string m_base_url; + litehtml::document::ptr m_html; + std::recursive_mutex m_html_mutex; + litehtml::string m_cursor; + litehtml::string m_clicked_url; + std::string m_hash; + html_host_interface* m_html_host; + cairo_images_cache m_images; + litebrowser::http_requests_pool m_requests_pool; + std::string m_html_source; + + std::shared_ptr m_notify; public: - explicit web_page(html_host_interface* html_host, int pool_size) : + explicit web_page(html_host_interface* html_host, std::shared_ptr notify, int pool_size) : m_html_host(html_host), - m_requests_pool(pool_size, std::bind(&web_page::on_pool_update_state, this)) + m_requests_pool(pool_size, [this] { on_pool_update_state(); }), + m_notify(std::move(notify)) {} + [[nodiscard]] + uint64_t id() const { return (uint64_t)this; } + + [[nodiscard]] + const std::string& get_html_source() const { return m_html_source; } + void open(const litehtml::string& url, const litehtml::string& hash); void get_client_rect(litehtml::position& client) const override; @@ -83,16 +98,10 @@ namespace litebrowser cairo_surface_t* get_image(const std::string& url) override; void make_url( const char* url, const char* basepath, litehtml::string& out ) override; void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; - static cairo_surface_t* surface_from_pixbuf(const Glib::RefPtr& bmp); + void on_mouse_event(const litehtml::element::ptr& el, litehtml::mouse_event event) override; double get_screen_dpi() const override; - int get_screen_width() const override - { - return Gdk::screen_width(); - } - int get_screen_height() const override - { - return Gdk::screen_height(); - } + int get_screen_width() const override; + int get_screen_height() const override; void show_hash(const litehtml::string& hash); void show_hash_and_reset() @@ -107,34 +116,56 @@ namespace litebrowser void on_mouse_over(int x, int y, int client_x, int client_y); void on_lbutton_down(int x, int y, int client_x, int client_y); void on_lbutton_up(int x, int y, int client_x, int client_y); + + [[nodiscard]] const std::string& get_cursor() const { return m_cursor; } + void draw(litehtml::uint_ptr hdc, int x, int y, const litehtml::position* clip) { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); if(m_html) m_html->draw(hdc, x, y, clip); } + int render(int max_width) { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); return m_html ? m_html->render(max_width) : 0; } + + [[nodiscard]] const std::string& url() const { return m_url; } + + [[nodiscard]] int width() const { return m_html ? m_html->width() : 0; } + + [[nodiscard]] int height() const { return m_html ? m_html->height() : 0; } + bool media_changed() { - std::unique_lock html_lock(m_html_mutex); + std::lock_guard html_lock(m_html_mutex); return m_html && m_html->media_changed(); } + void stop_loading() { m_requests_pool.stop(); } + + [[nodiscard]] bool is_downloading() { return m_requests_pool.is_downloading(); } - void dump(const litehtml::string& file_name); + + void dump(litehtml::dumper& cout) + { + std::lock_guard html_lock(m_html_mutex); + if(m_html) + { + m_html->dump(cout); + } + } private: void http_request(const std::string& url, const std::function& cb_on_data, diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp new file mode 100644 index 0000000..779333c --- /dev/null +++ b/src/widget/html_widget.cpp @@ -0,0 +1,769 @@ +#include "html_widget.h" +#include + +html_widget::html_widget() +{ + add_css_class("litehtml"); + + m_notifier = std::make_shared(); + m_notifier->connect_redraw(sigc::mem_fun(*this, &html_widget::on_redraw)); + m_notifier->connect_render(sigc::mem_fun(*this, &html_widget::render)); + m_notifier->connect_update_state([this]() { m_sig_update_state.emit(get_state()); }); + m_notifier->connect_on_page_loaded(sigc::mem_fun(*this, &html_widget::on_page_loaded)); + + set_focusable(true); + + m_vadjustment = Gtk::Adjustment::create(0.0, 0.0, 0.0, 10); + m_vadjustment->signal_value_changed().connect(sigc::mem_fun(*this, &html_widget::on_vadjustment_changed)); + m_hadjustment = Gtk::Adjustment::create(0.0, 0.0, 0.0, 10); + m_hadjustment->signal_value_changed().connect(sigc::mem_fun(*this, &html_widget::on_hadjustment_changed)); + + m_hadjustment->signal_changed().connect(sigc::mem_fun(*this, &html_widget::on_adjustments_changed)); + m_vadjustment->signal_changed().connect(sigc::mem_fun(*this, &html_widget::on_adjustments_changed)); + + m_vscrollbar = Gtk::make_managed(m_vadjustment, Gtk::Orientation::VERTICAL); + m_vscrollbar->insert_at_end(*this); + m_vscrollbar->set_visible(true); + m_vscrollbar->add_css_class("right"); + m_vscrollbar->add_css_class("overlay-indicator"); + + m_hscrollbar = Gtk::make_managed(m_hadjustment, Gtk::Orientation::HORIZONTAL); + m_hscrollbar->insert_at_end(*this); + m_hscrollbar->set_visible(true); + m_hscrollbar->add_css_class("bottom"); + m_hscrollbar->add_css_class("overlay-indicator"); + + auto controller = Gtk::EventControllerScroll::create(); + controller->set_flags(Gtk::EventControllerScroll::Flags::BOTH_AXES); + controller->signal_scroll().connect(sigc::mem_fun(*this, &html_widget::on_scroll), false); + add_controller(controller); + + auto click_gesture = Gtk::GestureClick::create(); + click_gesture->set_button(GDK_BUTTON_PRIMARY); + click_gesture->signal_pressed().connect(sigc::mem_fun(*this, &html_widget::on_button_press_event)); + click_gesture->signal_released().connect(sigc::mem_fun(*this, &html_widget::on_button_release_event)); + add_controller(click_gesture); + + auto move_gesture = Gtk::EventControllerMotion::create(); + move_gesture->signal_motion().connect(sigc::mem_fun(*this, &html_widget::on_mouse_move)); + move_gesture->signal_leave().connect([this]() { restart_scrollbar_timer(); }); + add_controller(move_gesture); + + auto key_controller = Gtk::EventControllerKey::create(); + key_controller->signal_key_pressed().connect(sigc::mem_fun(*this, &html_widget::on_key_pressed), false); + add_controller(key_controller); +} + +html_widget::~html_widget() +{ + m_notifier->disconnect(); + + if (!gobj()) return; + + while (Widget* child = get_first_child()) + { + child->unparent(); + } +} + +double html_widget::get_dpi() +{ + auto display = Gdk::Display::get_default(); + if (display) + { + auto monitors = display->get_monitors(); + if (monitors->get_n_items() > 0) + { + auto monitor = monitors->get_typed_object(0); + if(monitor) + { + Gdk::Rectangle rect; + monitor->get_geometry(rect); + return 10.0 * (rect.get_height() * 2.54) / monitor->get_height_mm(); + } + } + } + return 96.0; +} + +int html_widget::get_screen_width() +{ + auto display = Gdk::Display::get_default(); + if (display) + { + auto monitors = display->get_monitors(); + if (monitors->get_n_items() > 0) + { + auto monitor = monitors->get_typed_object(0); + Gdk::Rectangle rect; + monitor->get_geometry(rect); + return rect.get_width(); + } + } + return 800; +} + +int html_widget::get_screen_height() +{ + auto display = Gdk::Display::get_default(); + if (display) + { + auto monitors = display->get_monitors(); + if (monitors->get_n_items() > 0) + { + auto monitor = monitors->get_typed_object(0); + Gdk::Rectangle rect; + monitor->get_geometry(rect); + return rect.get_height(); + } + } + return 800; +} + +void html_widget::snapshot_vfunc(const Glib::RefPtr& snapshot) +{ + if (get_allocated_width() <= 0 || get_allocated_height() <= 0) + { + return; + } + + auto allocation = get_allocation(); + auto cr = snapshot->append_cairo(Gdk::Rectangle(0, 0, allocation.get_width(), allocation.get_height())); + if(m_draw_buffer.get_cairo_surface()) + { + cr->scale(1.0 / m_draw_buffer.get_scale_factor(), 1.0 / m_draw_buffer.get_scale_factor()); + cairo_set_source_surface(cr->cobj(), m_draw_buffer.get_cairo_surface(), 0, 0); + cr->paint(); + } + + snapshot_child(*m_vscrollbar, snapshot); + snapshot_child(*m_hscrollbar, snapshot); +} + +void html_widget::get_client_rect(litehtml::position& client) const +{ + client.x = (int) (m_hadjustment->get_value() - m_hadjustment->get_lower()); + client.y = (int) (m_vadjustment->get_value() - m_vadjustment->get_lower()); + client.width = (int) (m_hadjustment->get_page_size()); + client.height = (int) (m_vadjustment->get_page_size()); +} + +void html_widget::set_caption(const char* caption) +{ + auto root = get_root(); + if(root) + { + auto window = dynamic_cast(root); + if (window) + { + window->set_title(caption); + }; + } +} + +cairo_surface_t *html_widget::load_image(const std::string &path) +{ + Glib::RefPtr ptr; + + try + { + ptr = Gdk::Pixbuf::create_from_file(path); + } catch (...) { } + + if(!ptr) return nullptr; + + cairo_surface_t* ret = nullptr; + + if(ptr->get_has_alpha()) + { + ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ptr->get_width(), ptr->get_height()); + } else + { + ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, ptr->get_width(), ptr->get_height()); + } + + Cairo::RefPtr surface(new Cairo::Surface(ret, false)); + Cairo::RefPtr ctx = Cairo::Context::create(surface); + Gdk::Cairo::set_source_pixbuf(ctx, ptr, 0.0, 0.0); + ctx->paint(); + + return ret; + +} + +void html_widget::open_page(const litehtml::string& url, const litehtml::string& hash) +{ + { + std::lock_guard lock(m_page_mutex); + if (m_current_page) + { + m_current_page->stop_loading(); + } + m_next_page = std::make_shared(this, m_notifier, 10); + m_next_page->open(url, hash); + } + m_sig_set_address.emit(url); + m_sig_update_state.emit(get_state()); +} + +void html_widget::scroll_to(int x, int y) +{ + m_hadjustment->set_value(x); + m_vadjustment->set_value(y); +} + +void html_widget::on_redraw() +{ + m_draw_buffer.redraw(current_page()); + queue_draw(); +} + +void html_widget::on_button_press_event(int /* n_press */, double x, double y) +{ + if(!has_focus()) + { + grab_focus(); + } + auto page = current_page(); + if (page) + { + page->on_lbutton_down( (int) (x + m_hadjustment->get_value()), + (int) (y + m_vadjustment->get_value()), + (int) x, (int) y); + } +} + +void html_widget::on_button_release_event(int /* n_press */, double x, double y) +{ + auto page = current_page(); + if(page) + { + page->on_lbutton_up((int) (x + m_hadjustment->get_value()), + (int) (y + m_vadjustment->get_value()), + (int) x, (int) y); + } +} + +void html_widget::on_mouse_move(double x, double y) +{ + bool restart_timer = true; + if(m_hscrollbar->is_visible()) + { + if(y >= get_allocated_height() - 16) + { + m_hscrollbar->add_css_class("hovering"); + restart_timer = false; + } else + { + m_hscrollbar->remove_css_class("hovering"); + } + } + if(m_vscrollbar->is_visible()) + { + if(x >= get_allocated_width() - 16) + { + m_vscrollbar->add_css_class("hovering"); + restart_timer = false; + } else + { + m_vscrollbar->remove_css_class("hovering"); + } + } + m_hscrollbar->set_opacity(1); + m_vscrollbar->set_opacity(1); + if(restart_timer) + { + restart_scrollbar_timer(); + } else + { + m_scrollbar_timer.disconnect(); + } + + std::shared_ptr page = current_page(); + if(page) + { + page->on_mouse_over((int) (x + m_hadjustment->get_value()), + (int) (y + m_vadjustment->get_value()), + (int) x, (int) y); + } +} + +bool html_widget::on_key_pressed(guint keyval, guint /* keycode */, Gdk::ModifierType /* state */) +{ + switch (keyval) + { + case GDK_KEY_KP_Page_Down: + case GDK_KEY_Page_Down: + m_vadjustment->set_value(m_vadjustment->get_value() + m_vadjustment->get_page_size()); + break; + case GDK_KEY_KP_Page_Up: + case GDK_KEY_Page_Up: + m_vadjustment->set_value(m_vadjustment->get_value() - m_vadjustment->get_page_size()); + break; + case GDK_KEY_KP_Down: + case GDK_KEY_Down: + m_vadjustment->set_value(m_vadjustment->get_value() + m_vadjustment->get_step_increment()); + break; + case GDK_KEY_KP_Up: + case GDK_KEY_Up: + m_vadjustment->set_value(m_vadjustment->get_value() - m_vadjustment->get_step_increment()); + break; + case GDK_KEY_KP_Home: + case GDK_KEY_Home: + m_vadjustment->set_value(0); + break; + case GDK_KEY_KP_End: + case GDK_KEY_End: + m_vadjustment->set_value(m_vadjustment->get_upper()); + break; + + default: + break; + } + + return false; +} + +void html_widget::update_cursor() +{ + std::shared_ptr page = current_page(); + if(page) + { + if(page->get_cursor() == "auto") + { + set_cursor("default"); + } else + { + set_cursor(page->get_cursor()); + } + } else + { + set_cursor("default"); + } +} + +long html_widget::draw_measure(int number) +{ + std::shared_ptr page = current_page(); + + if(page) + { + litehtml::position view_port; + get_client_rect(view_port); + + int width = view_port.width; + int height = view_port.height; + + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + auto image = (unsigned char *) g_malloc(stride * height); + + cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, width, height, + stride); + cairo_t *cr = cairo_create(surface); + + litehtml::position pos; + pos.width = width; + pos.height = height; + pos.x = 0; + pos.y = 0; + + int x = view_port.x; + int y = view_port.y; + + cairo_rectangle(cr, 0, 0, width, height); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_paint(cr); + page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); + cairo_surface_write_to_png(surface, "/tmp/litebrowser.png"); + + auto t1 = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < number; i++) + { + page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); + } + auto t2 = std::chrono::high_resolution_clock::now(); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + g_free(image); + + return (std::chrono::duration_cast(t2 - t1)).count(); + } + return 0; +} + +long html_widget::render_measure(int number) +{ + std::shared_ptr page = current_page(); + + if(page) + { + auto t1 = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < number; i++) + { + page->render(m_rendered_width); + } + auto t2 = std::chrono::high_resolution_clock::now(); + return (std::chrono::duration_cast(t2 - t1)).count(); + } + return -1; +} + +void html_widget::size_allocate_vfunc(int width, int height, int /* baseline */) +{ + allocate_scrollbars(width, height); + std::shared_ptr page = current_page(); + if(page) + { + m_rendered_width = width; + page->media_changed(); + page->render(m_rendered_width); + update_view_port(page); + m_draw_buffer.on_size_allocate(page, width, height); + queue_draw(); + } else + { + m_rendered_width = width; + } + +} + +void html_widget::allocate_scrollbars(int width, int height) +{ + int minimum = 0, natural = 0, m_baseline = 0, n_baseline = 0; + + m_vscrollbar->measure(Gtk::Orientation::HORIZONTAL, -1, minimum, natural, m_baseline, n_baseline); + Gtk::Allocation vscrollbar_allocation(width - natural, 0, natural, height); + m_vscrollbar->size_allocate(vscrollbar_allocation, -1); + + minimum = 0, natural = 0, m_baseline = 0, n_baseline = 0; + m_hscrollbar->measure(Gtk::Orientation::VERTICAL, -1, minimum, natural, m_baseline, n_baseline); + Gtk::Allocation hscrollbar_allocation(0, height - natural, width, natural); + m_hscrollbar->size_allocate(hscrollbar_allocation, -1); + + m_hadjustment->set_page_size(width); + m_vadjustment->set_page_size(height); +} + +void html_widget::on_vadjustment_changed() +{ + m_draw_buffer.on_scroll( current_page(), + (int) m_hadjustment->get_value(), + (int) m_vadjustment->get_value()); + force_redraw(); +} + +void html_widget::on_hadjustment_changed() +{ + m_draw_buffer.on_scroll( current_page(), + (int) m_hadjustment->get_value(), + (int) m_vadjustment->get_value()); + force_redraw(); +} + +void html_widget::on_adjustments_changed() +{ + m_hscrollbar->set_visible(m_hadjustment->get_upper() > 0); + m_vscrollbar->set_visible(m_vadjustment->get_upper() > 0); +} + +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); + return true; +} + +bool html_widget::on_scrollbar_timeout() +{ + m_hscrollbar->remove_css_class("hovering"); + m_vscrollbar->remove_css_class("hovering"); + m_hscrollbar->set_opacity(0); + m_vscrollbar->set_opacity(0); + return false; +} + +void html_widget::on_realize() +{ + Gtk::Widget::on_realize(); + + auto native = get_native(); + if(native) + { + auto surface = native->get_surface(); + if(surface) + { + surface->property_scale().signal_changed().connect([this]() + { + auto native = get_native(); + if(native) + { + auto surface = native->get_surface(); + if(surface) + { + m_draw_buffer.set_scale_factor(current_page(), surface->get_scale()); + queue_draw(); + } + } + }); + } + } +} + +void html_widget::update_view_port(std::shared_ptr page) +{ + if(page) + { + auto allocation = get_allocation(); + if(allocation.get_width() < page->width()) + { + m_hadjustment->set_upper(page->width()); + } else + { + m_hadjustment->set_upper(0); + } + if(allocation.get_height() < page->height()) + { + m_vadjustment->set_upper(page->height()); + } else + { + m_vadjustment->set_upper(0); + } + } else + { + m_hadjustment->set_upper(0); + m_vadjustment->set_upper(0); + } +} + +void html_widget::restart_scrollbar_timer() +{ + if (m_scrollbar_timer) + { + m_scrollbar_timer.disconnect(); + } + + m_scrollbar_timer = Glib::signal_timeout().connect_seconds( + sigc::mem_fun(*this, &html_widget::on_scrollbar_timeout), 2); +} + +void html_widget::redraw_boxes(const litehtml::position::vector& boxes) +{ + if(boxes.empty()) return; + + Gdk::Rectangle rect(0, 0, 0, 0); + bool is_first = true; + for(const auto& pos : boxes) + { + if(is_first) + { + rect = Gdk::Rectangle(pos.x, pos.y, pos.width, pos.height); + is_first = false; + } else + { + rect.join(Gdk::Rectangle(pos.x, pos.y, pos.width, pos.height)); + } + } + + if(!rect.has_zero_area()) + { + m_draw_buffer.redraw_area(current_page(), rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height()); + queue_draw(); + } +} + +int html_widget::get_render_width() +{ + return get_width(); +} + +void html_widget::on_page_loaded(uint64_t web_page_id) +{ + std::string url; + { + std::lock_guard lock(m_page_mutex); + if(m_next_page->id() != web_page_id) return; + m_current_page = m_next_page; + m_next_page = nullptr; + url = m_current_page->url(); + update_view_port(m_current_page); + } + scroll_to(0, 0); + queue_draw(); + m_sig_set_address.emit(url); + m_sig_update_state.emit(get_state()); +} + +void html_widget::show_hash(const std::string &hash) +{ + std::shared_ptr page = current_page(); + if(page) + { + page->show_hash(hash); + } +} + +void html_widget::open_url(const std::string &url) +{ + std::string hash; + std::string s_url = url; + + m_sig_set_address.emit(url); + + std::string::size_type hash_pos = s_url.find_first_of(L'#'); + if(hash_pos != std::wstring::npos) + { + hash = s_url.substr(hash_pos + 1); + s_url.erase(hash_pos); + } + + bool open_hash_only = false; + bool reload = false; + + auto current_url = m_history.current(); + hash_pos = current_url.find_first_of(L'#'); + if(hash_pos != std::wstring::npos) + { + current_url.erase(hash_pos); + } + + if(!current_url.empty()) + { + if(m_history.current() != url) + { + if (current_url == s_url) + { + open_hash_only = true; + } + } else + { + reload = true; + } + } + if(!open_hash_only) + { + open_page(url, hash); + } else + { + show_hash(hash); + } + if(!reload) + { + m_history.url_opened(url); + } + m_sig_update_state.emit(get_state()); +} + +void html_widget::render() +{ + std::shared_ptr page = current_page(); + if(page) + { + page->render(m_rendered_width); + update_view_port(page); + m_draw_buffer.redraw(page); + queue_draw(); + } +} + +bool html_widget::on_close() +{ + if(m_current_page) + { + m_current_page->stop_loading(); + } + if(m_next_page) + { + m_next_page->stop_loading(); + } + return false; +} + +void html_widget::dump(litehtml::dumper &cout) +{ + std::shared_ptr page = current_page(); + if(page) + { + page->dump(cout); + } +} + +void html_widget::go_forward() +{ + std::string url; + if(m_history.forward(url)) + { + open_url(url); + } +} + +void html_widget::go_back() +{ + std::string url; + if(m_history.back(url)) + { + open_url(url); + } +} + +uint32_t html_widget::get_state() +{ + uint32_t ret = 0; + std::string url; + if(m_history.back(url)) + { + ret |= page_state_has_back; + } + if(m_history.forward(url)) + { + ret |= page_state_has_forward; + } + { + std::lock_guard lock(m_page_mutex); + if(m_next_page) + { + ret |= page_state_downloading; + } + } + if(!(ret & page_state_downloading)) + { + std::shared_ptr page = current_page(); + if(page) + { + if(page->is_downloading()) + { + ret |= page_state_downloading; + } + } + } + return ret; +} + +void html_widget::stop_download() +{ + std::lock_guard lock(m_page_mutex); + if(m_next_page) + { + m_next_page->stop_loading(); + } else if (m_current_page) + { + m_current_page->stop_loading(); + } +} + +void html_widget::reload() +{ + std::shared_ptr page = current_page(); + if(page) + { + open_url(page->url()); + } +} + +std::string html_widget::get_html_source() +{ + std::lock_guard lock(m_page_mutex); + if(m_current_page) + return m_current_page->get_html_source(); + return {}; +} diff --git a/src/widget/html_widget.h b/src/widget/html_widget.h new file mode 100644 index 0000000..8bd21c4 --- /dev/null +++ b/src/widget/html_widget.h @@ -0,0 +1,495 @@ +#pragma once + +#include +#include "html_host.h" +#include "web_page.h" +#include "http_requests_pool.h" +#include "web_history.h" +#include + +enum page_state +{ + page_state_has_back = 0x01, + page_state_has_forward = 0x02, + page_state_downloading = 0x04, +}; + +class html_widget_notifier : public litebrowser::browser_notify_interface +{ +public: + using redraw_func = std::function; + using render_func = std::function; + using update_state_func = std::function; + using on_page_loaded_func = std::function; +private: + enum func_type + { + func_type_none, + func_type_redraw, + func_type_render, + func_type_update_state, + func_type_on_page_loaded + }; + struct queue_item + { + func_type type; + uint64_t param; + }; + + Glib::Dispatcher m_dispatcher; + redraw_func m_redraw_func; + render_func m_render_func; + update_state_func m_update_state_func; + on_page_loaded_func m_on_page_loaded_func; + + std::mutex m_lock; + bool m_disconnect = false; + std::queue m_queue; + +public: + + html_widget_notifier() + { + m_dispatcher.connect(sigc::mem_fun(*this, &html_widget_notifier::on_notify)); + } + + void disconnect() + { + std::lock_guard lock(m_lock); + m_disconnect = true; + } + + void connect_redraw(redraw_func _redraw_func) + { + m_redraw_func = _redraw_func; + } + + void connect_render(render_func _render_func) + { + m_render_func = _render_func; + } + + void connect_update_state(update_state_func _update_state_func) + { + m_update_state_func = _update_state_func; + } + + void connect_on_page_loaded(on_page_loaded_func _on_page_loaded_func) + { + m_on_page_loaded_func = _on_page_loaded_func; + } + +private: + + void redraw() override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_redraw, 0}); + } + m_dispatcher.emit(); + } + + void render() override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_render, 0}); + } + m_dispatcher.emit(); + } + + void update_state() override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_update_state, 0}); + } + m_dispatcher.emit(); + } + + void on_page_loaded(uint64_t web_page_id) override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_on_page_loaded, web_page_id}); + } + m_dispatcher.emit(); + } + + void on_notify() + { + std::queue local_queue; + { + std::lock_guard lock(m_lock); + if(m_disconnect || m_queue.empty()) return; + + while(!m_queue.empty()) + { + local_queue.push(m_queue.front()); + m_queue.pop(); + } + } + func_type prev_type = func_type_none; + while(!local_queue.empty()) + { + const auto& item = local_queue.front(); + // We don't need do the same action some times + if(item.type != prev_type) + { + prev_type = item.type; + switch (item.type) + { + case func_type_redraw: + if(m_redraw_func) + { + m_redraw_func(); + } + break; + case func_type_render: + if(m_render_func) + { + m_render_func(); + } + break; + case func_type_update_state: + if(m_update_state_func) + { + m_update_state_func(); + } + break; + case func_type_on_page_loaded: + if(m_on_page_loaded_func) + { + m_on_page_loaded_func(item.param); + } + break; + + default: + break; + } + } + local_queue.pop(); + } + } +}; + +class draw_buffer +{ + cairo_surface_t* m_draw_buffer = nullptr; + int m_width = 0; + int m_height = 0; + int m_top = 0; + int m_left = 0; + double m_scale_factor = 1; + int m_min_int_position = 1; +public: + + ~draw_buffer() + { + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + } + + [[nodiscard]] + int get_width() const { return m_width; } + + [[nodiscard]] + int get_height() const { return m_height; } + + [[nodiscard]] + int get_left() const { return m_left; } + + [[nodiscard]] + int get_top() const { return m_top; } + + [[nodiscard]] + cairo_surface_t* get_cairo_surface() const { return m_draw_buffer; } + + [[nodiscard]] + double get_scale_factor() const { return m_scale_factor; } + + void set_scale_factor(std::shared_ptr page, double scale) + { + if(m_scale_factor != scale) + { + m_scale_factor = scale; + m_min_int_position = get_denominator(m_scale_factor); + + m_top = fix_position(m_top); + m_left = fix_position(m_left); + + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + m_draw_buffer = nullptr; + create_draw_buffer(m_width, m_height); + redraw(page); + } + } + + bool create_draw_buffer(int width, int height) + { + if(m_width != width || m_height != height || !m_draw_buffer) + { + m_width = width; + m_height = height; + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + m_draw_buffer = nullptr; + if(m_width > 0 && m_height > 0) + { + m_draw_buffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + std::ceil((double) m_width * m_scale_factor), + std::ceil((double) m_height * m_scale_factor)); + } + } + return m_draw_buffer != nullptr; + } + + void on_size_allocate(std::shared_ptr page, int width, int height) + { + if(create_draw_buffer(width, height)) + { + redraw(page); + } + } + + void on_scroll(std::shared_ptr page, int left, int top) + { + if(m_width <= 0 || m_height <= 0 || !m_draw_buffer) return; + + top = fix_position(top); + left = fix_position(left); + + if(m_left != left || m_top != top) + { + Gdk::Rectangle rec_current(m_left, m_top, m_width, m_height); // Current area + Gdk::Rectangle rec_clean(rec_current); // Clean area + Gdk::Rectangle rec_new(left, top, m_width, m_height); // New area + rec_clean.intersect(rec_new); + if(rec_clean.has_zero_area() || rec_new == rec_current) + { + m_left = left; + m_top = top; + redraw(page); + } else + { + int scaled_width = (int) std::ceil((double) m_width * m_scale_factor); + int scaled_height = (int) std::ceil((double) m_height * m_scale_factor); + int surface_shift_x = (int) std::floor((double) (m_left - left) * m_scale_factor); + int surface_shift_y = (int) std::floor((double) (m_top - top) * m_scale_factor); + //printf("[surface_shift] top:%d m_top:%d x:%d y:%d\n", top, m_top, surface_shift_x, surface_shift_y); + + auto new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, scaled_width, scaled_height); + cairo_t* cr = cairo_create(new_surface); + cairo_rectangle(cr, (rec_clean.get_x() - left) * m_scale_factor - m_scale_factor, + (rec_clean.get_y() - top) * m_scale_factor - m_scale_factor, + std::ceil((double) rec_clean.get_width() * m_scale_factor) + 2.0 * m_scale_factor, + std::ceil((double) rec_clean.get_height() * m_scale_factor) + 2.0 * m_scale_factor); + cairo_clip(cr); + cairo_set_source_surface(cr, m_draw_buffer, surface_shift_x, surface_shift_y); + cairo_paint(cr); + cairo_destroy(cr); + cairo_surface_destroy(m_draw_buffer); + m_draw_buffer = new_surface; + + m_left = left; + m_top = top; + + int right = m_left + m_width; + int bottom = m_top + m_height; + int clean_right = rec_clean.get_x() + rec_clean.get_width(); + int clean_bottom = rec_clean.get_y() + rec_clean.get_height(); + + if(rec_clean.get_x() > m_left) + { + redraw_area(page, m_left - 1, rec_clean.get_y() - 1, rec_clean.get_x() - m_left + 2, rec_clean.get_height() + 2); + } + if(clean_right < right) + { + redraw_area(page, clean_right - 1, rec_clean.get_y() - 1, right - clean_right + 2, rec_clean.get_height() + 2); + } + + if(rec_clean.get_y() > m_top) + { + redraw_area(page, m_left, + m_top - 1, + m_width, + rec_clean.get_y() - m_top + 2); + } + if(clean_bottom < bottom) + { + redraw_area(page, m_left, + clean_bottom - 1, + m_width, + bottom - clean_bottom + 2); + } + } + } + } + + void redraw_area(std::shared_ptr page, int x, int y, int width, int height) + { + if(page && m_draw_buffer) + { + // Calculate scaled position + int s_x = std::floor((double) (x - m_left) * m_scale_factor); + int s_y = std::floor((double) (y - m_top) * m_scale_factor); + int s_width = std::ceil((double) width * m_scale_factor); + int s_height = std::ceil((double) height * m_scale_factor); + + litehtml::position pos {x - m_left, y - m_top, width, height}; + cairo_t* cr = cairo_create(m_draw_buffer); + + // Apply clip with scaled position to avoid artifacts + cairo_rectangle(cr, s_x, s_y, s_width, s_height); + cairo_clip(cr); + + // Apply scale for drawing + cairo_scale(cr, m_scale_factor, m_scale_factor); + + // Draw page + page->draw((litehtml::uint_ptr) cr, -m_left, -m_top, &pos); + + cairo_destroy(cr); + } + } + + void redraw(std::shared_ptr page) + { + redraw_area(page, m_left, m_top, m_width, m_height); + } + +private: + inline int fix_position(int pos) + { + return (pos / m_min_int_position) * m_min_int_position; + } + + static int get_common_divisor(int a, int b) + { + while (b != 0) + { + int t = b; + b = a % b; + a = t; + } + return a; + } + + static int get_denominator(double decimal) + { + int numerator = (int)(decimal * 100); + int denominator = 100; + + int common_divisor = get_common_divisor(numerator, denominator); + numerator /= common_divisor; + return denominator / common_divisor; + } +}; + +class html_widget : public Gtk::Widget, + public litebrowser::html_host_interface +{ + int m_rendered_width = 0; + std::mutex m_page_mutex; + std::shared_ptr m_current_page; + std::shared_ptr m_next_page; + std::shared_ptr m_notifier; + web_history m_history; + + Gtk::Scrollbar* m_vscrollbar; + Gtk::Scrollbar* m_hscrollbar; + Glib::RefPtr m_vadjustment; + Glib::RefPtr m_hadjustment; + + sigc::connection m_scrollbar_timer; + + draw_buffer m_draw_buffer; + +public: + explicit html_widget(); + ~html_widget() override; + + void on_page_loaded(uint64_t web_page_id); + void render(); + void go_forward(); + void go_back(); + uint32_t get_state(); + void stop_download(); + void reload(); + + std::string get_html_source(); + long render_measure(int number); + long draw_measure(int number); + void show_hash(const std::string& hash); + bool on_close(); + void dump(litehtml::dumper& cout); + + void open_url(const std::string& url) override; + +protected: + // litebrowser::html_host_interface override + double get_dpi() override; + int get_screen_width() override; + int get_screen_height() override; + void open_page(const litehtml::string& url, const litehtml::string& hash) override; + void update_cursor() override; + void redraw_boxes(const litehtml::position::vector& boxes) override; + int get_render_width() override; + void scroll_to(int x, int y) override; + void get_client_rect(litehtml::position& client) const override; + void set_caption(const char* caption) override; + cairo_surface_t* load_image(const std::string& path) override; + + void snapshot_vfunc(const Glib::RefPtr& snapshot) override; + void on_redraw(); + + void on_button_press_event(int n_press, double x, double y); + void on_button_release_event(int n_press, double x, double y); + void on_mouse_move(double x, double y); + bool on_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state); + + void size_allocate_vfunc(int width, int height, int baseline) override; + void allocate_scrollbars(int width, int height); + + void on_vadjustment_changed(); + void on_hadjustment_changed(); + void on_adjustments_changed(); + bool on_scroll(double dx, double dy); + bool on_scrollbar_timeout(); + void on_realize() override; + +private: + std::shared_ptr current_page() + { + std::lock_guard lock(m_page_mutex); + return m_current_page; + } + + void update_view_port(std::shared_ptr page); + void restart_scrollbar_timer(); + void force_redraw() + { + queue_draw(); + while (g_main_context_iteration(nullptr, false)) {} + } + +public: + // Signals types + using sig_set_address_t = sigc::signal; + using sig_update_state_t = sigc::signal; + + sig_set_address_t signal_set_address() { return m_sig_set_address; } + sig_update_state_t signal_update_state() { return m_sig_update_state; } + +private: + sig_update_state_t m_sig_update_state; + sig_set_address_t m_sig_set_address; +}; From 1d0e89d5c0651851768a4cfece4d3967366c274d Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Fri, 28 Mar 2025 22:52:29 +0300 Subject: [PATCH 02/17] Added libadwaita init and some optimization --- CMakeLists.txt | 2 +- src/browser_wnd.cpp | 12 +----------- src/main.cpp | 2 ++ src/widget/html_widget.h | 8 ++++++-- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4290e22..7d7d158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set(LITEBROWSER_PATH src) set(LITEHTML_PATH litehtml) set(CONTAINER_PATH ${LITEHTML_PATH}/containers/cairo) -pkg_check_modules(LB_LIBS REQUIRED gtkmm-4.0 libcurl cairo pango pangocairo) +pkg_check_modules(LB_LIBS REQUIRED gtkmm-4.0 libcurl cairo pango pangocairo libadwaita-1) set(SOURCE ${LITEBROWSER_PATH}/main.cpp ${LITEBROWSER_PATH}/widget/html_widget.cpp diff --git a/src/browser_wnd.cpp b/src/browser_wnd.cpp index 5352196..edf17fd 100644 --- a/src/browser_wnd.cpp +++ b/src/browser_wnd.cpp @@ -24,17 +24,7 @@ struct static inline void mk_button(Gtk::Button& btn, const std::string& label_text, const std::string& icon_name) { btn.set_focusable(false); - - auto icon = Gtk::make_managed(); - icon->set_from_icon_name(icon_name); - icon->set_icon_size(Gtk::IconSize::NORMAL); - icon->set_expand(true); - - auto hbox = Gtk::make_managed(Gtk::Orientation::VERTICAL); - hbox->append(*icon); - hbox->set_expand(false); - - btn.set_child(*hbox); + btn.set_image_from_icon_name(icon_name); btn.set_tooltip_text(label_text); } diff --git a/src/main.cpp b/src/main.cpp index 2dd66ee..2cf5535 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #ifdef FOR_TESTING #include "fonts.h" #endif +#include int on_cmd(const Glib::RefPtr &, Glib::RefPtr &app) { @@ -10,6 +11,7 @@ int on_cmd(const Glib::RefPtr &, Glib::RefPtr 0 && m_height > 0) { - m_draw_buffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + m_draw_buffer = cairo_image_surface_create(CAIRO_FORMAT_RGB24, std::ceil((double) m_width * m_scale_factor), std::ceil((double) m_height * m_scale_factor)); } @@ -286,7 +286,7 @@ class draw_buffer int surface_shift_y = (int) std::floor((double) (m_top - top) * m_scale_factor); //printf("[surface_shift] top:%d m_top:%d x:%d y:%d\n", top, m_top, surface_shift_x, surface_shift_y); - auto new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, scaled_width, scaled_height); + auto new_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, scaled_width, scaled_height); cairo_t* cr = cairo_create(new_surface); cairo_rectangle(cr, (rec_clean.get_x() - left) * m_scale_factor - m_scale_factor, (rec_clean.get_y() - top) * m_scale_factor - m_scale_factor, @@ -351,6 +351,10 @@ class draw_buffer cairo_rectangle(cr, s_x, s_y, s_width, s_height); cairo_clip(cr); + cairo_rectangle(cr, s_x, s_y, s_width, s_height); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + // Apply scale for drawing cairo_scale(cr, m_scale_factor, m_scale_factor); From dc45756912b04e984877f22b3948489b9ecfcecb Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Sat, 29 Mar 2025 03:42:18 +0300 Subject: [PATCH 03/17] fix: the first page is not drawn --- src/widget/html_widget.cpp | 3 ++- src/widget/html_widget.h | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp index 779333c..b352769 100644 --- a/src/widget/html_widget.cpp +++ b/src/widget/html_widget.cpp @@ -423,6 +423,7 @@ void html_widget::size_allocate_vfunc(int width, int height, int /* baseline */) queue_draw(); } else { + m_draw_buffer.on_size_allocate(page, width, height); m_rendered_width = width; } @@ -589,7 +590,7 @@ void html_widget::on_page_loaded(uint64_t web_page_id) update_view_port(m_current_page); } scroll_to(0, 0); - queue_draw(); + on_redraw(); m_sig_set_address.emit(url); m_sig_update_state.emit(get_state()); } diff --git a/src/widget/html_widget.h b/src/widget/html_widget.h index 2e85754..dc6b0d8 100644 --- a/src/widget/html_widget.h +++ b/src/widget/html_widget.h @@ -336,7 +336,7 @@ class draw_buffer void redraw_area(std::shared_ptr page, int x, int y, int width, int height) { - if(page && m_draw_buffer) + if(m_draw_buffer) { // Calculate scaled position int s_x = std::floor((double) (x - m_left) * m_scale_factor); @@ -359,7 +359,10 @@ class draw_buffer cairo_scale(cr, m_scale_factor, m_scale_factor); // Draw page - page->draw((litehtml::uint_ptr) cr, -m_left, -m_top, &pos); + if(page) + { + page->draw((litehtml::uint_ptr) cr, -m_left, -m_top, &pos); + } cairo_destroy(cr); } From 4c7d0bbea0a6053a3200695b9371e57e6868e9c5 Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Sun, 30 Mar 2025 01:37:41 +0300 Subject: [PATCH 04/17] Update README.md --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 63e5b0c..87e2b34 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,20 @@ A simple browser based on the [litehtml](https://github.com/litehtml/litehtml) e Install dependencies: - * vim-core for xxd +### Fedora + ``` + dnf install gtkmm4.0-devel libcurl-devel cairo-devel pango-devel + ``` ## Pre-requisites on Mac Install dependencies using [Homebrew](https://brew.sh/): - * [gtkmm3](https://formulae.brew.sh/formula/gtkmm3) - * [gtk+3](https://formulae.brew.sh/formula/gtk+3) + * [gtkmm3](https://formulae.brew.sh/formula/gtkmm4) + * [gtk+3](https://formulae.brew.sh/formula/gtk4) ``` -brew install gtkmm3 gtk+3 +brew install gtkmm4 gtk4 ``` ## Common Build instructions From 7d04e7728a0fa2e5ccebe76e2e8b853bf725336b Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Sun, 30 Mar 2025 03:21:31 +0300 Subject: [PATCH 05/17] Make libadwaita dependency optional --- CMakeLists.txt | 14 ++++++++++---- src/main.cpp | 12 +++++++----- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d7d158..ff8143c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,8 @@ set(LITEBROWSER_PATH src) set(LITEHTML_PATH litehtml) set(CONTAINER_PATH ${LITEHTML_PATH}/containers/cairo) -pkg_check_modules(LB_LIBS REQUIRED gtkmm-4.0 libcurl cairo pango pangocairo libadwaita-1) +pkg_check_modules(LB_LIBS REQUIRED gtkmm-4.0 libcurl cairo pango pangocairo) +pkg_check_modules(LB_ADW libadwaita-1) set(SOURCE ${LITEBROWSER_PATH}/main.cpp ${LITEBROWSER_PATH}/widget/html_widget.cpp @@ -60,9 +61,14 @@ add_subdirectory(${LITEHTML_PATH}) add_compile_options(-Wall -Wextra -Wpedantic) add_executable(${PROJECT_NAME} ${SOURCE} ${HEADERS}) -include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${LITEBROWSER_PATH}/webpage ${LITEBROWSER_PATH}/widget ${LB_LIBS_INCLUDE_DIRS} ${CONTAINER_PATH} litehtml-tests) -target_link_options(${PROJECT_NAME} PRIVATE ${LB_LIBS_LDFLAGS} ${FONTCONFIG_LDFLAGS}) -target_link_libraries(${PROJECT_NAME} litehtml ${LB_LIBS_LIBRARIES} ${FONTCONFIG_LIBRARIES}) + +include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${LITEBROWSER_PATH}/webpage ${LITEBROWSER_PATH}/widget ${LB_LIBS_INCLUDE_DIRS} ${LB_ADW_INCLUDE_DIRS} ${CONTAINER_PATH} litehtml-tests) +target_link_options(${PROJECT_NAME} PRIVATE ${LB_LIBS_LDFLAGS} ${FONTCONFIG_LDFLAGS} ${LB_ADW_LDFLAGS}) +target_link_libraries(${PROJECT_NAME} litehtml ${LB_LIBS_LIBRARIES} ${LB_ADW_LIBRARIES} ${FONTCONFIG_LIBRARIES}) + +if (LB_ADW_FOUND) + target_compile_definitions(${PROJECT_NAME} PUBLIC LIBADWAITA_AVAILABLE=1) +endif () set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 diff --git a/src/main.cpp b/src/main.cpp index 2cf5535..3117660 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,16 +2,18 @@ #ifdef FOR_TESTING #include "fonts.h" #endif + +#ifdef LIBADWAITA_AVAILABLE #include +#endif -int on_cmd(const Glib::RefPtr &, Glib::RefPtr &app) -{ - app->activate(); - return 0; -} int main (int argc, char *argv[]) { + +#ifdef LIBADWAITA_AVAILABLE adw_init(); +#endif + #ifdef FOR_TESTING prepare_fonts_for_testing(); #endif From 7e05df5181d83adca92e290c326fb7687f3f8278 Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Sun, 30 Mar 2025 03:36:18 +0300 Subject: [PATCH 06/17] fixed issue with compiling on macos --- litehtml | 2 +- src/webpage/web_page.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/litehtml b/litehtml index c214c27..c33894e 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit c214c27d2c00153d5ed6de1df97cd6c04bb4d272 +Subproject commit c33894eab4429e4e58f59a49d68b4dc76c91fea3 diff --git a/src/webpage/web_page.h b/src/webpage/web_page.h index 2a3e8c0..6e38c63 100644 --- a/src/webpage/web_page.h +++ b/src/webpage/web_page.h @@ -1,6 +1,7 @@ #ifndef LITEBROWSER_WEB_PAGE_H #define LITEBROWSER_WEB_PAGE_H +#include #include #include "container_cairo_pango.h" #include "html_host.h" From 14c1010d56502d428b63365985930ffedde39d06 Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Sun, 30 Mar 2025 04:27:47 +0300 Subject: [PATCH 07/17] Make dpi always 96 DPI should be standard because the scaling will be applied. --- src/widget/html_widget.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp index b352769..2ea4abc 100644 --- a/src/widget/html_widget.cpp +++ b/src/widget/html_widget.cpp @@ -68,21 +68,7 @@ html_widget::~html_widget() double html_widget::get_dpi() { - auto display = Gdk::Display::get_default(); - if (display) - { - auto monitors = display->get_monitors(); - if (monitors->get_n_items() > 0) - { - auto monitor = monitors->get_typed_object(0); - if(monitor) - { - Gdk::Rectangle rect; - monitor->get_geometry(rect); - return 10.0 * (rect.get_height() * 2.54) / monitor->get_height_mm(); - } - } - } + // DPI is always 96 (default). Scaling will make things larger. return 96.0; } From 208cde091817318ebb372f589b52b43a16c2bdad Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Sun, 30 Mar 2025 04:41:16 +0300 Subject: [PATCH 08/17] README: added optional dependencies description --- README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 87e2b34..d00fbdc 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,28 @@ A simple browser based on the [litehtml](https://github.com/litehtml/litehtml) e ## Pre-requisites on Linux -Install dependencies: +### Install required dependencies: -### Fedora +#### Fedora ``` dnf install gtkmm4.0-devel libcurl-devel cairo-devel pango-devel ``` +### Optional dependencies: + +The optional library ```libadwaita``` will add support for dark themes, High Contrast mode and some other GNOME related features. + +#### Fedora + ``` + dnf install libadwaita-devel + ``` + ## Pre-requisites on Mac Install dependencies using [Homebrew](https://brew.sh/): - * [gtkmm3](https://formulae.brew.sh/formula/gtkmm4) - * [gtk+3](https://formulae.brew.sh/formula/gtk4) + * [gtkmm4](https://formulae.brew.sh/formula/gtkmm4) + * [gtk4](https://formulae.brew.sh/formula/gtk4) ``` brew install gtkmm4 gtk4 From 620fa0a473ac9f928cb7a5019635570a77a45dce Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Mon, 31 Mar 2025 00:34:32 +0300 Subject: [PATCH 09/17] move set_caption to the UI thread --- src/webpage/html_host.h | 2 +- src/webpage/web_page.cpp | 2 +- src/widget/html_widget.cpp | 5 +++-- src/widget/html_widget.h | 36 ++++++++++++++++++++++++++++++------ 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/webpage/html_host.h b/src/webpage/html_host.h index 7420fc3..472b588 100644 --- a/src/webpage/html_host.h +++ b/src/webpage/html_host.h @@ -16,7 +16,6 @@ namespace litebrowser virtual void update_cursor() = 0; virtual void scroll_to(int x, int y) = 0; virtual void get_client_rect(litehtml::position& client) const = 0; - virtual void set_caption(const char* caption) = 0; virtual void redraw_boxes(const litehtml::position::vector& boxes) = 0; virtual int get_render_width() = 0; virtual double get_dpi() = 0; @@ -33,6 +32,7 @@ namespace litebrowser virtual void redraw() = 0; virtual void render() = 0; virtual void update_state() = 0; + virtual void on_set_caption(const std::string& caption_text) = 0; virtual void on_page_loaded(uint64_t web_page_id) = 0; }; diff --git a/src/webpage/web_page.cpp b/src/webpage/web_page.cpp index b96c37d..b7b47ef 100644 --- a/src/webpage/web_page.cpp +++ b/src/webpage/web_page.cpp @@ -77,7 +77,7 @@ void litebrowser::web_page::import_css(litehtml::string& text, const litehtml::s void litebrowser::web_page::set_caption(const char* caption) { - m_html_host->set_caption(caption); + m_notify->on_set_caption(caption); } void litebrowser::web_page::set_base_url(const char* base_url) diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp index 2ea4abc..68f24f5 100644 --- a/src/widget/html_widget.cpp +++ b/src/widget/html_widget.cpp @@ -10,6 +10,7 @@ html_widget::html_widget() m_notifier->connect_render(sigc::mem_fun(*this, &html_widget::render)); m_notifier->connect_update_state([this]() { m_sig_update_state.emit(get_state()); }); m_notifier->connect_on_page_loaded(sigc::mem_fun(*this, &html_widget::on_page_loaded)); + m_notifier->connect_on_set_caption(sigc::mem_fun(*this, &html_widget::set_caption)); set_focusable(true); @@ -134,7 +135,7 @@ void html_widget::get_client_rect(litehtml::position& client) const client.height = (int) (m_vadjustment->get_page_size()); } -void html_widget::set_caption(const char* caption) +void html_widget::set_caption(const std::string& caption) { auto root = get_root(); if(root) @@ -142,7 +143,7 @@ void html_widget::set_caption(const char* caption) auto window = dynamic_cast(root); if (window) { - window->set_title(caption); + window->set_title(caption.c_str()); }; } } diff --git a/src/widget/html_widget.h b/src/widget/html_widget.h index dc6b0d8..9d793b8 100644 --- a/src/widget/html_widget.h +++ b/src/widget/html_widget.h @@ -21,6 +21,7 @@ class html_widget_notifier : public litebrowser::browser_notify_interface using render_func = std::function; using update_state_func = std::function; using on_page_loaded_func = std::function; + using on_set_caption_func = std::function; private: enum func_type { @@ -28,12 +29,14 @@ class html_widget_notifier : public litebrowser::browser_notify_interface func_type_redraw, func_type_render, func_type_update_state, - func_type_on_page_loaded + func_type_on_page_loaded, + func_type_on_set_caption }; struct queue_item { func_type type; uint64_t param; + std::string str_param; }; Glib::Dispatcher m_dispatcher; @@ -41,6 +44,7 @@ class html_widget_notifier : public litebrowser::browser_notify_interface render_func m_render_func; update_state_func m_update_state_func; on_page_loaded_func m_on_page_loaded_func; + on_set_caption_func m_on_set_caption_func; std::mutex m_lock; bool m_disconnect = false; @@ -79,13 +83,18 @@ class html_widget_notifier : public litebrowser::browser_notify_interface m_on_page_loaded_func = _on_page_loaded_func; } + void connect_on_set_caption(on_set_caption_func _on_set_caption_func) + { + m_on_set_caption_func = _on_set_caption_func; + } + private: void redraw() override { { std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_redraw, 0}); + m_queue.push(queue_item{func_type_redraw, 0, {}}); } m_dispatcher.emit(); } @@ -94,7 +103,7 @@ class html_widget_notifier : public litebrowser::browser_notify_interface { { std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_render, 0}); + m_queue.push(queue_item{func_type_render, 0, {}}); } m_dispatcher.emit(); } @@ -103,7 +112,7 @@ class html_widget_notifier : public litebrowser::browser_notify_interface { { std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_update_state, 0}); + m_queue.push(queue_item{func_type_update_state, 0, {}}); } m_dispatcher.emit(); } @@ -112,7 +121,16 @@ class html_widget_notifier : public litebrowser::browser_notify_interface { { std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_on_page_loaded, web_page_id}); + m_queue.push(queue_item{func_type_on_page_loaded, web_page_id, {}}); + } + m_dispatcher.emit(); + } + + void on_set_caption(const std::string& caption) override + { + { + std::lock_guard lock(m_lock); + m_queue.push(queue_item{func_type_on_set_caption, 0, caption}); } m_dispatcher.emit(); } @@ -164,6 +182,12 @@ class html_widget_notifier : public litebrowser::browser_notify_interface m_on_page_loaded_func(item.param); } break; + case func_type_on_set_caption: + if(m_on_set_caption_func) + { + m_on_set_caption_func(item.str_param); + } + break; default: break; @@ -452,7 +476,6 @@ class html_widget : public Gtk::Widget, int get_render_width() override; void scroll_to(int x, int y) override; void get_client_rect(litehtml::position& client) const override; - void set_caption(const char* caption) override; cairo_surface_t* load_image(const std::string& path) override; void snapshot_vfunc(const Glib::RefPtr& snapshot) override; @@ -466,6 +489,7 @@ class html_widget : public Gtk::Widget, void size_allocate_vfunc(int width, int height, int baseline) override; void allocate_scrollbars(int width, int height); + void set_caption(const std::string& caption); void on_vadjustment_changed(); void on_hadjustment_changed(); void on_adjustments_changed(); From 4444d0fe2d6476de4c00da8ebcde67bf65c5b3bf Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Tue, 1 Apr 2025 02:03:04 +0300 Subject: [PATCH 10/17] fix: glitches on page scroll or processing elements hover --- src/widget/html_widget.cpp | 58 ++++++++++--------- src/widget/html_widget.h | 114 +++++++++++++++++++++++++++++-------- 2 files changed, 121 insertions(+), 51 deletions(-) diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp index 68f24f5..99abc50 100644 --- a/src/widget/html_widget.cpp +++ b/src/widget/html_widget.cpp @@ -114,13 +114,15 @@ void html_widget::snapshot_vfunc(const Glib::RefPtr& snapshot) return; } - auto allocation = get_allocation(); - auto cr = snapshot->append_cairo(Gdk::Rectangle(0, 0, allocation.get_width(), allocation.get_height())); - if(m_draw_buffer.get_cairo_surface()) { - cr->scale(1.0 / m_draw_buffer.get_scale_factor(), 1.0 / m_draw_buffer.get_scale_factor()); - cairo_set_source_surface(cr->cobj(), m_draw_buffer.get_cairo_surface(), 0, 0); - cr->paint(); + auto allocation = get_allocation(); + auto cr = snapshot->append_cairo(Gdk::Rectangle(0, 0, allocation.get_width(), allocation.get_height())); + if(m_draw_buffer.get_cairo_surface()) + { + cr->scale(1.0 / m_draw_buffer.get_scale_factor(), 1.0 / m_draw_buffer.get_scale_factor()); + cairo_set_source_surface(cr->cobj(), m_draw_buffer.get_cairo_surface(), 0, 0); + cr->paint(); + } } snapshot_child(*m_vscrollbar, snapshot); @@ -129,10 +131,10 @@ void html_widget::snapshot_vfunc(const Glib::RefPtr& snapshot) void html_widget::get_client_rect(litehtml::position& client) const { - client.x = (int) (m_hadjustment->get_value() - m_hadjustment->get_lower()); - client.y = (int) (m_vadjustment->get_value() - m_vadjustment->get_lower()); - client.width = (int) (m_hadjustment->get_page_size()); - client.height = (int) (m_vadjustment->get_page_size()); + client.x = m_draw_buffer.get_left(); + client.y = m_draw_buffer.get_top(); + client.width = m_draw_buffer.get_width(); + client.height = m_draw_buffer.get_height(); } void html_widget::set_caption(const std::string& caption) @@ -214,8 +216,8 @@ void html_widget::on_button_press_event(int /* n_press */, double x, double y) auto page = current_page(); if (page) { - page->on_lbutton_down( (int) (x + m_hadjustment->get_value()), - (int) (y + m_vadjustment->get_value()), + page->on_lbutton_down( (int) (x + m_draw_buffer.get_left()), + (int) (y + m_draw_buffer.get_top()), (int) x, (int) y); } } @@ -225,8 +227,8 @@ void html_widget::on_button_release_event(int /* n_press */, double x, double y) auto page = current_page(); if(page) { - page->on_lbutton_up((int) (x + m_hadjustment->get_value()), - (int) (y + m_vadjustment->get_value()), + page->on_lbutton_up((int) (x + m_draw_buffer.get_left()), + (int) (y + m_draw_buffer.get_top()), (int) x, (int) y); } } @@ -269,8 +271,8 @@ void html_widget::on_mouse_move(double x, double y) std::shared_ptr page = current_page(); if(page) { - page->on_mouse_over((int) (x + m_hadjustment->get_value()), - (int) (y + m_vadjustment->get_value()), + page->on_mouse_over((int) (x + m_draw_buffer.get_left()), + (int) (y + m_draw_buffer.get_top()), (int) x, (int) y); } } @@ -388,7 +390,7 @@ long html_widget::render_measure(int number) auto t1 = std::chrono::high_resolution_clock::now(); for (int i = 0; i < number; i++) { - page->render(m_rendered_width); + page->render(m_draw_buffer.get_width()); } auto t2 = std::chrono::high_resolution_clock::now(); return (std::chrono::duration_cast(t2 - t1)).count(); @@ -402,16 +404,20 @@ void html_widget::size_allocate_vfunc(int width, int height, int /* baseline */) std::shared_ptr page = current_page(); if(page) { - m_rendered_width = width; - page->media_changed(); - page->render(m_rendered_width); - update_view_port(page); - m_draw_buffer.on_size_allocate(page, width, height); - queue_draw(); + if(m_rendered_width != width || m_rendered_height != height) + { + m_rendered_width = width; + m_rendered_height = height; + m_draw_buffer.on_size_allocate(page, width, height); + page->media_changed(); + page->render(m_rendered_width); + update_view_port(page); + m_draw_buffer.redraw(page); + queue_draw(); + } } else { m_draw_buffer.on_size_allocate(page, width, height); - m_rendered_width = width; } } @@ -562,7 +568,7 @@ void html_widget::redraw_boxes(const litehtml::position::vector& boxes) int html_widget::get_render_width() { - return get_width(); + return m_draw_buffer.get_width(); } void html_widget::on_page_loaded(uint64_t web_page_id) @@ -647,7 +653,7 @@ void html_widget::render() std::shared_ptr page = current_page(); if(page) { - page->render(m_rendered_width); + page->render(m_draw_buffer.get_width()); update_view_port(page); m_draw_buffer.redraw(page); queue_draw(); diff --git a/src/widget/html_widget.h b/src/widget/html_widget.h index 9d793b8..5a80a62 100644 --- a/src/widget/html_widget.h +++ b/src/widget/html_widget.h @@ -198,6 +198,11 @@ class html_widget_notifier : public litebrowser::browser_notify_interface } }; +/// @brief Draw Buffer Class +/// +/// This class performs the draw operations into the cairo surface. +/// The application draws everything to the buffer, then buffer are +/// drawn on widged or window. class draw_buffer { cairo_surface_t* m_draw_buffer = nullptr; @@ -235,6 +240,9 @@ class draw_buffer [[nodiscard]] double get_scale_factor() const { return m_scale_factor; } + /// @brief Set scale factor for draw buffer + /// @param page the webpage to be redraw if required + /// @param scale the scale factor to be applied void set_scale_factor(std::shared_ptr page, double scale) { if(m_scale_factor != scale) @@ -255,6 +263,22 @@ class draw_buffer } } + /// @brief Create cairo surface for draw buffer + /// @param width surface width (not scaled) + /// @param height surface height (not scaled) + /// @param scale_factor scale factor + /// @return poiter to the cairo surface + cairo_surface_t* make_surface(int width, int height, double scale_factor) + { + return cairo_image_surface_create(CAIRO_FORMAT_RGB24, + std::ceil((double) width * scale_factor), + std::ceil((double) height * scale_factor)); + } + + /// @brief Creates new buffer with specified size + /// @param width draw buffer width (not scaled) + /// @param height draw buffer height (not scaled) + /// @return true if new draw buffer was created, false if the old buffer was used bool create_draw_buffer(int width, int height) { if(m_width != width || m_height != height || !m_draw_buffer) @@ -268,14 +292,17 @@ class draw_buffer m_draw_buffer = nullptr; if(m_width > 0 && m_height > 0) { - m_draw_buffer = cairo_image_surface_create(CAIRO_FORMAT_RGB24, - std::ceil((double) m_width * m_scale_factor), - std::ceil((double) m_height * m_scale_factor)); + m_draw_buffer = make_surface(m_width, m_height, m_scale_factor); } + return true; } - return m_draw_buffer != nullptr; + return false; } + /// @brief Call this function when widget size changed + /// @param page webpage to be redraw if buffer size changed + /// @param width new draw buffer width + /// @param height new draw buffer height void on_size_allocate(std::shared_ptr page, int width, int height) { if(create_draw_buffer(width, height)) @@ -284,6 +311,14 @@ class draw_buffer } } + /// @brief Scrolls draw buffer to the position (left, top). + /// + /// Note, the actual position of the draw buffer can be rounded according to the scale factor. + /// Use get_left() and get_top() to know the actual position. + /// + /// @param page webpage to be redraw if the position was changed + /// @param left new horizontal position + /// @param top new vertical position void on_scroll(std::shared_ptr page, int left, int top) { if(m_width <= 0 || m_height <= 0 || !m_draw_buffer) return; @@ -295,7 +330,7 @@ class draw_buffer { Gdk::Rectangle rec_current(m_left, m_top, m_width, m_height); // Current area Gdk::Rectangle rec_clean(rec_current); // Clean area - Gdk::Rectangle rec_new(left, top, m_width, m_height); // New area + Gdk::Rectangle rec_new(left, top, m_width, m_height); // New area rec_clean.intersect(rec_new); if(rec_clean.has_zero_area() || rec_new == rec_current) { @@ -304,13 +339,10 @@ class draw_buffer redraw(page); } else { - int scaled_width = (int) std::ceil((double) m_width * m_scale_factor); - int scaled_height = (int) std::ceil((double) m_height * m_scale_factor); int surface_shift_x = (int) std::floor((double) (m_left - left) * m_scale_factor); int surface_shift_y = (int) std::floor((double) (m_top - top) * m_scale_factor); - //printf("[surface_shift] top:%d m_top:%d x:%d y:%d\n", top, m_top, surface_shift_x, surface_shift_y); - auto new_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, scaled_width, scaled_height); + auto new_surface = make_surface(m_width, m_height, m_scale_factor); cairo_t* cr = cairo_create(new_surface); cairo_rectangle(cr, (rec_clean.get_x() - left) * m_scale_factor - m_scale_factor, (rec_clean.get_y() - top) * m_scale_factor - m_scale_factor, @@ -326,55 +358,84 @@ class draw_buffer m_left = left; m_top = top; - int right = m_left + m_width; - int bottom = m_top + m_height; - int clean_right = rec_clean.get_x() + rec_clean.get_width(); - int clean_bottom = rec_clean.get_y() + rec_clean.get_height(); + int right = fix_position(m_left + m_width); + int bottom = fix_position(m_top + m_height); + int clean_right = fix_position(rec_clean.get_x() + rec_clean.get_width()); + int clean_bottom = fix_position(rec_clean.get_y() + rec_clean.get_height()); if(rec_clean.get_x() > m_left) { - redraw_area(page, m_left - 1, rec_clean.get_y() - 1, rec_clean.get_x() - m_left + 2, rec_clean.get_height() + 2); + redraw_area(page, m_left, + rec_clean.get_y(), + rec_clean.get_x() - m_left, + rec_clean.get_height()); } if(clean_right < right) { - redraw_area(page, clean_right - 1, rec_clean.get_y() - 1, right - clean_right + 2, rec_clean.get_height() + 2); + redraw_area(page, clean_right, + rec_clean.get_y(), + right - clean_right, + rec_clean.get_height()); } if(rec_clean.get_y() > m_top) { redraw_area(page, m_left, - m_top - 1, + m_top, m_width, - rec_clean.get_y() - m_top + 2); + rec_clean.get_y() - m_top); } if(clean_bottom < bottom) { redraw_area(page, m_left, - clean_bottom - 1, + clean_bottom, m_width, - bottom - clean_bottom + 2); + bottom - clean_bottom); } } } } + /// @brief Reraw the defined area of the buffer + /// + /// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, + /// but it must always cover the requested. + /// + /// @param page webpage to be redraw + /// @param x left position of the area + /// @param y top position of the area + /// @param width width of the area + /// @param height height of the area void redraw_area(std::shared_ptr page, int x, int y, int width, int height) { if(m_draw_buffer) { - // Calculate scaled position - int s_x = std::floor((double) (x - m_left) * m_scale_factor); - int s_y = std::floor((double) (y - m_top) * m_scale_factor); - int s_width = std::ceil((double) width * m_scale_factor); - int s_height = std::ceil((double) height * m_scale_factor); + int fixed_left = fix_position(x - m_left); + int fixed_right = fix_position(x - m_left + width); + int fixed_top = fix_position(y - m_top); + int fixed_bottom = fix_position(y - m_top + height); + + if(fixed_right < x + width) fixed_right += m_min_int_position; + if(fixed_bottom < y + height) fixed_bottom += m_min_int_position; + + int fixed_x = fixed_left; + int fixed_y = fixed_top; + int fixed_width = fixed_right - fixed_left; + int fixed_height = fixed_bottom - fixed_top; + + int s_x = (int) std::round((double) fixed_x * m_scale_factor); + int s_y = (int) std::round((double) fixed_y * m_scale_factor); + int s_width = (int) std::round((double) fixed_width * m_scale_factor); + int s_height = (int) std::round((double) fixed_height * m_scale_factor); - litehtml::position pos {x - m_left, y - m_top, width, height}; + litehtml::position pos {fixed_x, fixed_y, fixed_width, fixed_height}; cairo_t* cr = cairo_create(m_draw_buffer); // Apply clip with scaled position to avoid artifacts cairo_rectangle(cr, s_x, s_y, s_width, s_height); cairo_clip(cr); + // Clear rectangle with scaled position cairo_rectangle(cr, s_x, s_y, s_width, s_height); cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_fill(cr); @@ -392,6 +453,8 @@ class draw_buffer } } + /// @brief Redraw entire buffer + /// @param page webpage to be redraw void redraw(std::shared_ptr page) { redraw_area(page, m_left, m_top, m_width, m_height); @@ -429,6 +492,7 @@ class html_widget : public Gtk::Widget, public litebrowser::html_host_interface { int m_rendered_width = 0; + int m_rendered_height = 0; std::mutex m_page_mutex; std::shared_ptr m_current_page; std::shared_ptr m_next_page; From c2305059107bce90fee22736ab66d4f9b1b8449e Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Tue, 1 Apr 2025 02:27:46 +0300 Subject: [PATCH 11/17] update scrollbars position on widget size change --- src/widget/html_widget.cpp | 33 ++++++++++++++++++++++++++++----- src/widget/html_widget.h | 2 ++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp index 99abc50..2271b23 100644 --- a/src/widget/html_widget.cpp +++ b/src/widget/html_widget.cpp @@ -424,6 +424,19 @@ void html_widget::size_allocate_vfunc(int width, int height, int /* baseline */) void html_widget::allocate_scrollbars(int width, int height) { + m_do_force_redraw_on_adjustment = false; + m_hadjustment->set_page_size(width); + m_vadjustment->set_page_size(height); + if(m_vadjustment->get_value() > m_vadjustment->get_upper() - m_vadjustment->get_page_size()) + { + m_vadjustment->set_value(m_vadjustment->get_upper() - m_vadjustment->get_page_size()); + } + if(m_hadjustment->get_value() > m_hadjustment->get_upper() - m_hadjustment->get_page_size()) + { + m_hadjustment->set_value(m_hadjustment->get_upper() - m_hadjustment->get_page_size()); + } + m_do_force_redraw_on_adjustment = true; + int minimum = 0, natural = 0, m_baseline = 0, n_baseline = 0; m_vscrollbar->measure(Gtk::Orientation::HORIZONTAL, -1, minimum, natural, m_baseline, n_baseline); @@ -434,9 +447,6 @@ void html_widget::allocate_scrollbars(int width, int height) m_hscrollbar->measure(Gtk::Orientation::VERTICAL, -1, minimum, natural, m_baseline, n_baseline); Gtk::Allocation hscrollbar_allocation(0, height - natural, width, natural); m_hscrollbar->size_allocate(hscrollbar_allocation, -1); - - m_hadjustment->set_page_size(width); - m_vadjustment->set_page_size(height); } void html_widget::on_vadjustment_changed() @@ -444,7 +454,14 @@ void html_widget::on_vadjustment_changed() m_draw_buffer.on_scroll( current_page(), (int) m_hadjustment->get_value(), (int) m_vadjustment->get_value()); - force_redraw(); + + if(m_do_force_redraw_on_adjustment) + { + force_redraw(); + } else + { + queue_draw(); + } } void html_widget::on_hadjustment_changed() @@ -452,7 +469,13 @@ void html_widget::on_hadjustment_changed() m_draw_buffer.on_scroll( current_page(), (int) m_hadjustment->get_value(), (int) m_vadjustment->get_value()); - force_redraw(); + if(m_do_force_redraw_on_adjustment) + { + force_redraw(); + } else + { + queue_draw(); + } } void html_widget::on_adjustments_changed() diff --git a/src/widget/html_widget.h b/src/widget/html_widget.h index 5a80a62..d037373 100644 --- a/src/widget/html_widget.h +++ b/src/widget/html_widget.h @@ -14,6 +14,7 @@ enum page_state page_state_downloading = 0x04, }; +/// @brief Allows perform actions in the GUI thread via notifications class html_widget_notifier : public litebrowser::browser_notify_interface { public: @@ -493,6 +494,7 @@ class html_widget : public Gtk::Widget, { int m_rendered_width = 0; int m_rendered_height = 0; + bool m_do_force_redraw_on_adjustment = true; std::mutex m_page_mutex; std::shared_ptr m_current_page; std::shared_ptr m_next_page; From dd970ce09edce270da3f516ae36e82d843afcf5c Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Tue, 1 Apr 2025 23:47:43 +0300 Subject: [PATCH 12/17] Move draw buffer class out of html_widget.h --- CMakeLists.txt | 2 + litehtml | 2 +- src/webpage/draw_buffer.cpp | 142 +++++++++++++++++ src/webpage/draw_buffer.h | 182 ++++++++++++++++++++++ src/widget/html_widget.h | 293 +----------------------------------- 5 files changed, 329 insertions(+), 292 deletions(-) create mode 100644 src/webpage/draw_buffer.cpp create mode 100644 src/webpage/draw_buffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ff8143c..da5eac6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCE ${LITEBROWSER_PATH}/main.cpp ${LITEBROWSER_PATH}/widget/html_widget.cpp ${LITEBROWSER_PATH}/browser_wnd.cpp ${LITEBROWSER_PATH}/webpage/web_history.cpp + ${LITEBROWSER_PATH}/webpage/draw_buffer.cpp ${LITEBROWSER_PATH}/webpage/http_request.cpp ${LITEBROWSER_PATH}/webpage/web_page.cpp ${LITEBROWSER_PATH}/webpage/http_requests_pool.cpp @@ -41,6 +42,7 @@ set(HEADERS ${LITEBROWSER_PATH}/browser_wnd.h ${LITEBROWSER_PATH}/webpage/web_page.h ${LITEBROWSER_PATH}/webpage/html_host.h ${LITEBROWSER_PATH}/webpage/http_requests_pool.h + ${LITEBROWSER_PATH}/webpage/draw_buffer.h ${LITEBROWSER_PATH}/html_dumper.h ${CONTAINER_PATH}/container_cairo.h ${CONTAINER_PATH}/cairo_borders.h diff --git a/litehtml b/litehtml index c33894e..36b39f8 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit c33894eab4429e4e58f59a49d68b4dc76c91fea3 +Subproject commit 36b39f8692f6af5b9af48340d1743db66cbc8a0e diff --git a/src/webpage/draw_buffer.cpp b/src/webpage/draw_buffer.cpp new file mode 100644 index 0000000..24eb823 --- /dev/null +++ b/src/webpage/draw_buffer.cpp @@ -0,0 +1,142 @@ +#include "draw_buffer.h" + +/// @brief Scrolls draw buffer to the position (left, top). +/// +/// Note, the actual position of the draw buffer can be rounded according to the scale factor. +/// Use get_left() and get_top() to know the actual position. +/// +/// @param page webpage to be redraw if the position was changed +/// @param left new horizontal position +/// @param top new vertical position +void litebrowser::draw_buffer::on_scroll(std::shared_ptr page, int left, int top) +{ + if(m_width <= 0 || m_height <= 0 || !m_draw_buffer) return; + + top = fix_position(top); + left = fix_position(left); + + if(m_left != left || m_top != top) + { + litehtml::position rec_current(m_left, m_top, m_width, m_height); // Current area + litehtml::position rec_new(left, top, m_width, m_height); // New area + litehtml::position rec_clean = rec_current.intersect(rec_new); // Clean area + if(rec_clean.empty() || rec_new == rec_current) + { + m_left = left; + m_top = top; + redraw(page); + } else + { + int surface_shift_x = (int) std::floor((double) (m_left - left) * m_scale_factor); + int surface_shift_y = (int) std::floor((double) (m_top - top) * m_scale_factor); + + auto new_surface = make_surface(m_width, m_height, m_scale_factor); + cairo_t* cr = cairo_create(new_surface); + cairo_rectangle(cr, (rec_clean.x - left) * m_scale_factor - m_scale_factor, + (rec_clean.y - top) * m_scale_factor - m_scale_factor, + std::ceil((double) rec_clean.width * m_scale_factor) + 2.0 * m_scale_factor, + std::ceil((double) rec_clean.height * m_scale_factor) + 2.0 * m_scale_factor); + cairo_clip(cr); + cairo_set_source_surface(cr, m_draw_buffer, surface_shift_x, surface_shift_y); + cairo_paint(cr); + cairo_destroy(cr); + cairo_surface_destroy(m_draw_buffer); + m_draw_buffer = new_surface; + + m_left = left; + m_top = top; + + int right = fix_position(m_left + m_width); + int bottom = fix_position(m_top + m_height); + int clean_right = fix_position(rec_clean.x + rec_clean.width); + int clean_bottom = fix_position(rec_clean.y + rec_clean.height); + + if(rec_clean.x > m_left) + { + redraw_area(page, m_left, + rec_clean.y, + rec_clean.x - m_left, + rec_clean.height); + } + if(clean_right < right) + { + redraw_area(page, clean_right, + rec_clean.y, + right - clean_right, + rec_clean.height); + } + + if(rec_clean.y > m_top) + { + redraw_area(page, m_left, + m_top, + m_width, + rec_clean.y - m_top); + } + if(clean_bottom < bottom) + { + redraw_area(page, m_left, + clean_bottom, + m_width, + bottom - clean_bottom); + } + } + } +} + +/// @brief Reraw the defined area of the buffer +/// +/// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, +/// but it must always cover the requested. +/// +/// @param page webpage to be redraw +/// @param x left position of the area +/// @param y top position of the area +/// @param width width of the area +/// @param height height of the area +void litebrowser::draw_buffer::redraw_area(std::shared_ptr page, int x, int y, int width, int height) +{ + if(m_draw_buffer) + { + int fixed_left = fix_position(x - m_left); + int fixed_right = fix_position(x - m_left + width); + int fixed_top = fix_position(y - m_top); + int fixed_bottom = fix_position(y - m_top + height); + + if(fixed_right < x + width) fixed_right += m_min_int_position; + if(fixed_bottom < y + height) fixed_bottom += m_min_int_position; + + int fixed_x = fixed_left; + int fixed_y = fixed_top; + int fixed_width = fixed_right - fixed_left; + int fixed_height = fixed_bottom - fixed_top; + + int s_x = (int) std::round((double) fixed_x * m_scale_factor); + int s_y = (int) std::round((double) fixed_y * m_scale_factor); + int s_width = (int) std::round((double) fixed_width * m_scale_factor); + int s_height = (int) std::round((double) fixed_height * m_scale_factor); + + litehtml::position pos {fixed_x, fixed_y, fixed_width, fixed_height}; + cairo_t* cr = cairo_create(m_draw_buffer); + + // Apply clip with scaled position to avoid artifacts + cairo_rectangle(cr, s_x, s_y, s_width, s_height); + cairo_clip(cr); + + // Clear rectangle with scaled position + cairo_rectangle(cr, s_x, s_y, s_width, s_height); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + // Apply scale for drawing + cairo_scale(cr, m_scale_factor, m_scale_factor); + + // Draw page + if(page) + { + page->draw((litehtml::uint_ptr) cr, -m_left, -m_top, &pos); + } + + cairo_destroy(cr); + } +} diff --git a/src/webpage/draw_buffer.h b/src/webpage/draw_buffer.h new file mode 100644 index 0000000..be3b72a --- /dev/null +++ b/src/webpage/draw_buffer.h @@ -0,0 +1,182 @@ +#ifndef LITEBROWSER_DRAW_BUFFER_H +#define LITEBROWSER_DRAW_BUFFER_H + +#include +#include +#include "web_page.h" + +namespace litebrowser +{ + /// @brief Draw Buffer Class + /// + /// This class performs the draw operations into the cairo surface. + /// The application draws everything to the buffer, then buffer are + /// drawn on widged or window. + class draw_buffer + { + cairo_surface_t* m_draw_buffer = nullptr; + int m_width = 0; + int m_height = 0; + int m_top = 0; + int m_left = 0; + double m_scale_factor = 1; + int m_min_int_position = 1; + public: + + ~draw_buffer() + { + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + } + + [[nodiscard]] + int get_width() const { return m_width; } + + [[nodiscard]] + int get_height() const { return m_height; } + + [[nodiscard]] + int get_left() const { return m_left; } + + [[nodiscard]] + int get_top() const { return m_top; } + + [[nodiscard]] + cairo_surface_t* get_cairo_surface() const { return m_draw_buffer; } + + [[nodiscard]] + double get_scale_factor() const { return m_scale_factor; } + + /// @brief Set scale factor for draw buffer + /// @param page the webpage to be redraw if required + /// @param scale the scale factor to be applied + void set_scale_factor(std::shared_ptr page, double scale) + { + if(m_scale_factor != scale) + { + m_scale_factor = scale; + m_min_int_position = get_denominator(m_scale_factor); + + m_top = fix_position(m_top); + m_left = fix_position(m_left); + + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + m_draw_buffer = nullptr; + create_draw_buffer(m_width, m_height); + redraw(page); + } + } + + /// @brief Create cairo surface for draw buffer + /// @param width surface width (not scaled) + /// @param height surface height (not scaled) + /// @param scale_factor scale factor + /// @return poiter to the cairo surface + cairo_surface_t* make_surface(int width, int height, double scale_factor) + { + return cairo_image_surface_create(CAIRO_FORMAT_RGB24, + std::ceil((double) width * scale_factor), + std::ceil((double) height * scale_factor)); + } + + /// @brief Creates new buffer with specified size + /// @param width draw buffer width (not scaled) + /// @param height draw buffer height (not scaled) + /// @return true if new draw buffer was created, false if the old buffer was used + bool create_draw_buffer(int width, int height) + { + if(m_width != width || m_height != height || !m_draw_buffer) + { + m_width = width; + m_height = height; + if(m_draw_buffer) + { + cairo_surface_destroy(m_draw_buffer); + } + m_draw_buffer = nullptr; + if(m_width > 0 && m_height > 0) + { + m_draw_buffer = make_surface(m_width, m_height, m_scale_factor); + } + return true; + } + return false; + } + + /// @brief Call this function when widget size changed + /// @param page webpage to be redraw if buffer size changed + /// @param width new draw buffer width + /// @param height new draw buffer height + void on_size_allocate(std::shared_ptr page, int width, int height) + { + if(create_draw_buffer(width, height)) + { + redraw(page); + } + } + + /// @brief Scrolls draw buffer to the position (left, top). + /// + /// Note, the actual position of the draw buffer can be rounded according to the scale factor. + /// Use get_left() and get_top() to know the actual position. + /// + /// @param page webpage to be redraw if the position was changed + /// @param left new horizontal position + /// @param top new vertical position + void on_scroll(std::shared_ptr page, int left, int top); + + /// @brief Reraw the defined area of the buffer + /// + /// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, + /// but it must always cover the requested. + /// + /// @param page webpage to be redraw + /// @param x left position of the area + /// @param y top position of the area + /// @param width width of the area + /// @param height height of the area + void redraw_area(std::shared_ptr page, int x, int y, int width, int height); + + /// @brief Redraw entire buffer + /// @param page webpage to be redraw + void redraw(std::shared_ptr page) + { + redraw_area(page, m_left, m_top, m_width, m_height); + } + + private: + inline int fix_position(int pos) + { + return (pos / m_min_int_position) * m_min_int_position; + } + + static int get_common_divisor(int a, int b) + { + while (b != 0) + { + int t = b; + b = a % b; + a = t; + } + return a; + } + + static int get_denominator(double decimal) + { + int numerator = (int)(decimal * 100); + int denominator = 100; + + int common_divisor = get_common_divisor(numerator, denominator); + numerator /= common_divisor; + return denominator / common_divisor; + } + }; + +} + +#endif diff --git a/src/widget/html_widget.h b/src/widget/html_widget.h index d037373..19bfb19 100644 --- a/src/widget/html_widget.h +++ b/src/widget/html_widget.h @@ -5,6 +5,7 @@ #include "web_page.h" #include "http_requests_pool.h" #include "web_history.h" +#include "draw_buffer.h" #include enum page_state @@ -199,296 +200,6 @@ class html_widget_notifier : public litebrowser::browser_notify_interface } }; -/// @brief Draw Buffer Class -/// -/// This class performs the draw operations into the cairo surface. -/// The application draws everything to the buffer, then buffer are -/// drawn on widged or window. -class draw_buffer -{ - cairo_surface_t* m_draw_buffer = nullptr; - int m_width = 0; - int m_height = 0; - int m_top = 0; - int m_left = 0; - double m_scale_factor = 1; - int m_min_int_position = 1; -public: - - ~draw_buffer() - { - if(m_draw_buffer) - { - cairo_surface_destroy(m_draw_buffer); - } - } - - [[nodiscard]] - int get_width() const { return m_width; } - - [[nodiscard]] - int get_height() const { return m_height; } - - [[nodiscard]] - int get_left() const { return m_left; } - - [[nodiscard]] - int get_top() const { return m_top; } - - [[nodiscard]] - cairo_surface_t* get_cairo_surface() const { return m_draw_buffer; } - - [[nodiscard]] - double get_scale_factor() const { return m_scale_factor; } - - /// @brief Set scale factor for draw buffer - /// @param page the webpage to be redraw if required - /// @param scale the scale factor to be applied - void set_scale_factor(std::shared_ptr page, double scale) - { - if(m_scale_factor != scale) - { - m_scale_factor = scale; - m_min_int_position = get_denominator(m_scale_factor); - - m_top = fix_position(m_top); - m_left = fix_position(m_left); - - if(m_draw_buffer) - { - cairo_surface_destroy(m_draw_buffer); - } - m_draw_buffer = nullptr; - create_draw_buffer(m_width, m_height); - redraw(page); - } - } - - /// @brief Create cairo surface for draw buffer - /// @param width surface width (not scaled) - /// @param height surface height (not scaled) - /// @param scale_factor scale factor - /// @return poiter to the cairo surface - cairo_surface_t* make_surface(int width, int height, double scale_factor) - { - return cairo_image_surface_create(CAIRO_FORMAT_RGB24, - std::ceil((double) width * scale_factor), - std::ceil((double) height * scale_factor)); - } - - /// @brief Creates new buffer with specified size - /// @param width draw buffer width (not scaled) - /// @param height draw buffer height (not scaled) - /// @return true if new draw buffer was created, false if the old buffer was used - bool create_draw_buffer(int width, int height) - { - if(m_width != width || m_height != height || !m_draw_buffer) - { - m_width = width; - m_height = height; - if(m_draw_buffer) - { - cairo_surface_destroy(m_draw_buffer); - } - m_draw_buffer = nullptr; - if(m_width > 0 && m_height > 0) - { - m_draw_buffer = make_surface(m_width, m_height, m_scale_factor); - } - return true; - } - return false; - } - - /// @brief Call this function when widget size changed - /// @param page webpage to be redraw if buffer size changed - /// @param width new draw buffer width - /// @param height new draw buffer height - void on_size_allocate(std::shared_ptr page, int width, int height) - { - if(create_draw_buffer(width, height)) - { - redraw(page); - } - } - - /// @brief Scrolls draw buffer to the position (left, top). - /// - /// Note, the actual position of the draw buffer can be rounded according to the scale factor. - /// Use get_left() and get_top() to know the actual position. - /// - /// @param page webpage to be redraw if the position was changed - /// @param left new horizontal position - /// @param top new vertical position - void on_scroll(std::shared_ptr page, int left, int top) - { - if(m_width <= 0 || m_height <= 0 || !m_draw_buffer) return; - - top = fix_position(top); - left = fix_position(left); - - if(m_left != left || m_top != top) - { - Gdk::Rectangle rec_current(m_left, m_top, m_width, m_height); // Current area - Gdk::Rectangle rec_clean(rec_current); // Clean area - Gdk::Rectangle rec_new(left, top, m_width, m_height); // New area - rec_clean.intersect(rec_new); - if(rec_clean.has_zero_area() || rec_new == rec_current) - { - m_left = left; - m_top = top; - redraw(page); - } else - { - int surface_shift_x = (int) std::floor((double) (m_left - left) * m_scale_factor); - int surface_shift_y = (int) std::floor((double) (m_top - top) * m_scale_factor); - - auto new_surface = make_surface(m_width, m_height, m_scale_factor); - cairo_t* cr = cairo_create(new_surface); - cairo_rectangle(cr, (rec_clean.get_x() - left) * m_scale_factor - m_scale_factor, - (rec_clean.get_y() - top) * m_scale_factor - m_scale_factor, - std::ceil((double) rec_clean.get_width() * m_scale_factor) + 2.0 * m_scale_factor, - std::ceil((double) rec_clean.get_height() * m_scale_factor) + 2.0 * m_scale_factor); - cairo_clip(cr); - cairo_set_source_surface(cr, m_draw_buffer, surface_shift_x, surface_shift_y); - cairo_paint(cr); - cairo_destroy(cr); - cairo_surface_destroy(m_draw_buffer); - m_draw_buffer = new_surface; - - m_left = left; - m_top = top; - - int right = fix_position(m_left + m_width); - int bottom = fix_position(m_top + m_height); - int clean_right = fix_position(rec_clean.get_x() + rec_clean.get_width()); - int clean_bottom = fix_position(rec_clean.get_y() + rec_clean.get_height()); - - if(rec_clean.get_x() > m_left) - { - redraw_area(page, m_left, - rec_clean.get_y(), - rec_clean.get_x() - m_left, - rec_clean.get_height()); - } - if(clean_right < right) - { - redraw_area(page, clean_right, - rec_clean.get_y(), - right - clean_right, - rec_clean.get_height()); - } - - if(rec_clean.get_y() > m_top) - { - redraw_area(page, m_left, - m_top, - m_width, - rec_clean.get_y() - m_top); - } - if(clean_bottom < bottom) - { - redraw_area(page, m_left, - clean_bottom, - m_width, - bottom - clean_bottom); - } - } - } - } - - /// @brief Reraw the defined area of the buffer - /// - /// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, - /// but it must always cover the requested. - /// - /// @param page webpage to be redraw - /// @param x left position of the area - /// @param y top position of the area - /// @param width width of the area - /// @param height height of the area - void redraw_area(std::shared_ptr page, int x, int y, int width, int height) - { - if(m_draw_buffer) - { - int fixed_left = fix_position(x - m_left); - int fixed_right = fix_position(x - m_left + width); - int fixed_top = fix_position(y - m_top); - int fixed_bottom = fix_position(y - m_top + height); - - if(fixed_right < x + width) fixed_right += m_min_int_position; - if(fixed_bottom < y + height) fixed_bottom += m_min_int_position; - - int fixed_x = fixed_left; - int fixed_y = fixed_top; - int fixed_width = fixed_right - fixed_left; - int fixed_height = fixed_bottom - fixed_top; - - int s_x = (int) std::round((double) fixed_x * m_scale_factor); - int s_y = (int) std::round((double) fixed_y * m_scale_factor); - int s_width = (int) std::round((double) fixed_width * m_scale_factor); - int s_height = (int) std::round((double) fixed_height * m_scale_factor); - - litehtml::position pos {fixed_x, fixed_y, fixed_width, fixed_height}; - cairo_t* cr = cairo_create(m_draw_buffer); - - // Apply clip with scaled position to avoid artifacts - cairo_rectangle(cr, s_x, s_y, s_width, s_height); - cairo_clip(cr); - - // Clear rectangle with scaled position - cairo_rectangle(cr, s_x, s_y, s_width, s_height); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - // Apply scale for drawing - cairo_scale(cr, m_scale_factor, m_scale_factor); - - // Draw page - if(page) - { - page->draw((litehtml::uint_ptr) cr, -m_left, -m_top, &pos); - } - - cairo_destroy(cr); - } - } - - /// @brief Redraw entire buffer - /// @param page webpage to be redraw - void redraw(std::shared_ptr page) - { - redraw_area(page, m_left, m_top, m_width, m_height); - } - -private: - inline int fix_position(int pos) - { - return (pos / m_min_int_position) * m_min_int_position; - } - - static int get_common_divisor(int a, int b) - { - while (b != 0) - { - int t = b; - b = a % b; - a = t; - } - return a; - } - - static int get_denominator(double decimal) - { - int numerator = (int)(decimal * 100); - int denominator = 100; - - int common_divisor = get_common_divisor(numerator, denominator); - numerator /= common_divisor; - return denominator / common_divisor; - } -}; - class html_widget : public Gtk::Widget, public litebrowser::html_host_interface { @@ -508,7 +219,7 @@ class html_widget : public Gtk::Widget, sigc::connection m_scrollbar_timer; - draw_buffer m_draw_buffer; + litebrowser::draw_buffer m_draw_buffer; public: explicit html_widget(); From 5f5325be22a5589dc5c07bc3cf3400efa54ff8a8 Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Tue, 1 Apr 2025 23:48:24 +0300 Subject: [PATCH 13/17] Added support for horizontal scrolling with keyboard --- src/widget/html_widget.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp index 2271b23..eafd04d 100644 --- a/src/widget/html_widget.cpp +++ b/src/widget/html_widget.cpp @@ -305,6 +305,14 @@ bool html_widget::on_key_pressed(guint keyval, guint /* keycode */, Gdk::Modifie case GDK_KEY_End: m_vadjustment->set_value(m_vadjustment->get_upper()); break; + case GDK_KEY_KP_Left: + case GDK_KEY_Left: + m_hadjustment->set_value(m_hadjustment->get_value() - m_hadjustment->get_step_increment()); + break; + case GDK_KEY_KP_Right: + case GDK_KEY_Right: + m_hadjustment->set_value(m_hadjustment->get_value() + m_hadjustment->get_step_increment()); + break; default: break; From aedf4d13364bc1513bd8a68995ba8f96dc147b03 Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Wed, 2 Apr 2025 02:49:10 +0300 Subject: [PATCH 14/17] Move widget and webpage files to the litehtml support folder --- CMakeLists.txt | 29 +- litehtml | 2 +- src/webpage/draw_buffer.cpp | 142 ------ src/webpage/draw_buffer.h | 182 ------- src/webpage/html_host.h | 41 -- src/webpage/http_request.cpp | 129 ----- src/webpage/http_request.h | 75 --- src/webpage/http_requests_pool.cpp | 104 ---- src/webpage/http_requests_pool.h | 62 --- src/webpage/web_history.cpp | 62 --- src/webpage/web_history.h | 28 - src/webpage/web_page.cpp | 310 ----------- src/webpage/web_page.h | 180 ------- src/widget/html_widget.cpp | 794 ----------------------------- src/widget/html_widget.h | 303 ----------- 15 files changed, 16 insertions(+), 2427 deletions(-) delete mode 100644 src/webpage/draw_buffer.cpp delete mode 100644 src/webpage/draw_buffer.h delete mode 100644 src/webpage/html_host.h delete mode 100644 src/webpage/http_request.cpp delete mode 100644 src/webpage/http_request.h delete mode 100644 src/webpage/http_requests_pool.cpp delete mode 100644 src/webpage/http_requests_pool.h delete mode 100755 src/webpage/web_history.cpp delete mode 100755 src/webpage/web_history.h delete mode 100644 src/webpage/web_page.cpp delete mode 100644 src/webpage/web_page.h delete mode 100644 src/widget/html_widget.cpp delete mode 100644 src/widget/html_widget.h diff --git a/CMakeLists.txt b/CMakeLists.txt index da5eac6..c944775 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,32 +18,33 @@ set(CMAKE_C_FLAGS_RELEASE "-O3") set(LITEBROWSER_PATH src) set(LITEHTML_PATH litehtml) set(CONTAINER_PATH ${LITEHTML_PATH}/containers/cairo) +set(SUPPORT_PATH ${LITEHTML_PATH}/support) pkg_check_modules(LB_LIBS REQUIRED gtkmm-4.0 libcurl cairo pango pangocairo) pkg_check_modules(LB_ADW libadwaita-1) set(SOURCE ${LITEBROWSER_PATH}/main.cpp - ${LITEBROWSER_PATH}/widget/html_widget.cpp ${LITEBROWSER_PATH}/browser_wnd.cpp - ${LITEBROWSER_PATH}/webpage/web_history.cpp - ${LITEBROWSER_PATH}/webpage/draw_buffer.cpp - ${LITEBROWSER_PATH}/webpage/http_request.cpp - ${LITEBROWSER_PATH}/webpage/web_page.cpp - ${LITEBROWSER_PATH}/webpage/http_requests_pool.cpp + ${SUPPORT_PATH}/gtkmm4/html_widget.cpp + ${SUPPORT_PATH}/webpage/web_history.cpp + ${SUPPORT_PATH}/webpage/draw_buffer.cpp + ${SUPPORT_PATH}/webpage/http_request.cpp + ${SUPPORT_PATH}/webpage/web_page.cpp + ${SUPPORT_PATH}/webpage/http_requests_pool.cpp ${CONTAINER_PATH}/container_cairo.cpp ${CONTAINER_PATH}/cairo_borders.cpp ${CONTAINER_PATH}/container_cairo_pango.cpp ) set(HEADERS ${LITEBROWSER_PATH}/browser_wnd.h - ${LITEBROWSER_PATH}/widget/html_widget.h - ${LITEBROWSER_PATH}/webpage/web_history.h - ${LITEBROWSER_PATH}/webpage/http_request.h - ${LITEBROWSER_PATH}/webpage/web_page.h - ${LITEBROWSER_PATH}/webpage/html_host.h - ${LITEBROWSER_PATH}/webpage/http_requests_pool.h - ${LITEBROWSER_PATH}/webpage/draw_buffer.h ${LITEBROWSER_PATH}/html_dumper.h + ${SUPPORT_PATH}/gtkmm4/html_widget.h + ${SUPPORT_PATH}/webpage/web_history.h + ${SUPPORT_PATH}/webpage/http_request.h + ${SUPPORT_PATH}/webpage/web_page.h + ${SUPPORT_PATH}/webpage/html_host.h + ${SUPPORT_PATH}/webpage/http_requests_pool.h + ${SUPPORT_PATH}/webpage/draw_buffer.h ${CONTAINER_PATH}/container_cairo.h ${CONTAINER_PATH}/cairo_borders.h ${CONTAINER_PATH}/container_cairo_pango.h @@ -64,7 +65,7 @@ add_compile_options(-Wall -Wextra -Wpedantic) add_executable(${PROJECT_NAME} ${SOURCE} ${HEADERS}) -include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${LITEBROWSER_PATH}/webpage ${LITEBROWSER_PATH}/widget ${LB_LIBS_INCLUDE_DIRS} ${LB_ADW_INCLUDE_DIRS} ${CONTAINER_PATH} litehtml-tests) +include_directories(${PROJECT_NAME} ${LITEHTML_PATH}/include ${SUPPORT_PATH}/webpage ${SUPPORT_PATH}/gtkmm4 ${LB_LIBS_INCLUDE_DIRS} ${LB_ADW_INCLUDE_DIRS} ${CONTAINER_PATH} litehtml-tests) target_link_options(${PROJECT_NAME} PRIVATE ${LB_LIBS_LDFLAGS} ${FONTCONFIG_LDFLAGS} ${LB_ADW_LDFLAGS}) target_link_libraries(${PROJECT_NAME} litehtml ${LB_LIBS_LIBRARIES} ${LB_ADW_LIBRARIES} ${FONTCONFIG_LIBRARIES}) diff --git a/litehtml b/litehtml index 36b39f8..d6041ca 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit 36b39f8692f6af5b9af48340d1743db66cbc8a0e +Subproject commit d6041ca537ae3fb63d6224394ae4ae4a3ec32a17 diff --git a/src/webpage/draw_buffer.cpp b/src/webpage/draw_buffer.cpp deleted file mode 100644 index 24eb823..0000000 --- a/src/webpage/draw_buffer.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "draw_buffer.h" - -/// @brief Scrolls draw buffer to the position (left, top). -/// -/// Note, the actual position of the draw buffer can be rounded according to the scale factor. -/// Use get_left() and get_top() to know the actual position. -/// -/// @param page webpage to be redraw if the position was changed -/// @param left new horizontal position -/// @param top new vertical position -void litebrowser::draw_buffer::on_scroll(std::shared_ptr page, int left, int top) -{ - if(m_width <= 0 || m_height <= 0 || !m_draw_buffer) return; - - top = fix_position(top); - left = fix_position(left); - - if(m_left != left || m_top != top) - { - litehtml::position rec_current(m_left, m_top, m_width, m_height); // Current area - litehtml::position rec_new(left, top, m_width, m_height); // New area - litehtml::position rec_clean = rec_current.intersect(rec_new); // Clean area - if(rec_clean.empty() || rec_new == rec_current) - { - m_left = left; - m_top = top; - redraw(page); - } else - { - int surface_shift_x = (int) std::floor((double) (m_left - left) * m_scale_factor); - int surface_shift_y = (int) std::floor((double) (m_top - top) * m_scale_factor); - - auto new_surface = make_surface(m_width, m_height, m_scale_factor); - cairo_t* cr = cairo_create(new_surface); - cairo_rectangle(cr, (rec_clean.x - left) * m_scale_factor - m_scale_factor, - (rec_clean.y - top) * m_scale_factor - m_scale_factor, - std::ceil((double) rec_clean.width * m_scale_factor) + 2.0 * m_scale_factor, - std::ceil((double) rec_clean.height * m_scale_factor) + 2.0 * m_scale_factor); - cairo_clip(cr); - cairo_set_source_surface(cr, m_draw_buffer, surface_shift_x, surface_shift_y); - cairo_paint(cr); - cairo_destroy(cr); - cairo_surface_destroy(m_draw_buffer); - m_draw_buffer = new_surface; - - m_left = left; - m_top = top; - - int right = fix_position(m_left + m_width); - int bottom = fix_position(m_top + m_height); - int clean_right = fix_position(rec_clean.x + rec_clean.width); - int clean_bottom = fix_position(rec_clean.y + rec_clean.height); - - if(rec_clean.x > m_left) - { - redraw_area(page, m_left, - rec_clean.y, - rec_clean.x - m_left, - rec_clean.height); - } - if(clean_right < right) - { - redraw_area(page, clean_right, - rec_clean.y, - right - clean_right, - rec_clean.height); - } - - if(rec_clean.y > m_top) - { - redraw_area(page, m_left, - m_top, - m_width, - rec_clean.y - m_top); - } - if(clean_bottom < bottom) - { - redraw_area(page, m_left, - clean_bottom, - m_width, - bottom - clean_bottom); - } - } - } -} - -/// @brief Reraw the defined area of the buffer -/// -/// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, -/// but it must always cover the requested. -/// -/// @param page webpage to be redraw -/// @param x left position of the area -/// @param y top position of the area -/// @param width width of the area -/// @param height height of the area -void litebrowser::draw_buffer::redraw_area(std::shared_ptr page, int x, int y, int width, int height) -{ - if(m_draw_buffer) - { - int fixed_left = fix_position(x - m_left); - int fixed_right = fix_position(x - m_left + width); - int fixed_top = fix_position(y - m_top); - int fixed_bottom = fix_position(y - m_top + height); - - if(fixed_right < x + width) fixed_right += m_min_int_position; - if(fixed_bottom < y + height) fixed_bottom += m_min_int_position; - - int fixed_x = fixed_left; - int fixed_y = fixed_top; - int fixed_width = fixed_right - fixed_left; - int fixed_height = fixed_bottom - fixed_top; - - int s_x = (int) std::round((double) fixed_x * m_scale_factor); - int s_y = (int) std::round((double) fixed_y * m_scale_factor); - int s_width = (int) std::round((double) fixed_width * m_scale_factor); - int s_height = (int) std::round((double) fixed_height * m_scale_factor); - - litehtml::position pos {fixed_x, fixed_y, fixed_width, fixed_height}; - cairo_t* cr = cairo_create(m_draw_buffer); - - // Apply clip with scaled position to avoid artifacts - cairo_rectangle(cr, s_x, s_y, s_width, s_height); - cairo_clip(cr); - - // Clear rectangle with scaled position - cairo_rectangle(cr, s_x, s_y, s_width, s_height); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_fill(cr); - - // Apply scale for drawing - cairo_scale(cr, m_scale_factor, m_scale_factor); - - // Draw page - if(page) - { - page->draw((litehtml::uint_ptr) cr, -m_left, -m_top, &pos); - } - - cairo_destroy(cr); - } -} diff --git a/src/webpage/draw_buffer.h b/src/webpage/draw_buffer.h deleted file mode 100644 index be3b72a..0000000 --- a/src/webpage/draw_buffer.h +++ /dev/null @@ -1,182 +0,0 @@ -#ifndef LITEBROWSER_DRAW_BUFFER_H -#define LITEBROWSER_DRAW_BUFFER_H - -#include -#include -#include "web_page.h" - -namespace litebrowser -{ - /// @brief Draw Buffer Class - /// - /// This class performs the draw operations into the cairo surface. - /// The application draws everything to the buffer, then buffer are - /// drawn on widged or window. - class draw_buffer - { - cairo_surface_t* m_draw_buffer = nullptr; - int m_width = 0; - int m_height = 0; - int m_top = 0; - int m_left = 0; - double m_scale_factor = 1; - int m_min_int_position = 1; - public: - - ~draw_buffer() - { - if(m_draw_buffer) - { - cairo_surface_destroy(m_draw_buffer); - } - } - - [[nodiscard]] - int get_width() const { return m_width; } - - [[nodiscard]] - int get_height() const { return m_height; } - - [[nodiscard]] - int get_left() const { return m_left; } - - [[nodiscard]] - int get_top() const { return m_top; } - - [[nodiscard]] - cairo_surface_t* get_cairo_surface() const { return m_draw_buffer; } - - [[nodiscard]] - double get_scale_factor() const { return m_scale_factor; } - - /// @brief Set scale factor for draw buffer - /// @param page the webpage to be redraw if required - /// @param scale the scale factor to be applied - void set_scale_factor(std::shared_ptr page, double scale) - { - if(m_scale_factor != scale) - { - m_scale_factor = scale; - m_min_int_position = get_denominator(m_scale_factor); - - m_top = fix_position(m_top); - m_left = fix_position(m_left); - - if(m_draw_buffer) - { - cairo_surface_destroy(m_draw_buffer); - } - m_draw_buffer = nullptr; - create_draw_buffer(m_width, m_height); - redraw(page); - } - } - - /// @brief Create cairo surface for draw buffer - /// @param width surface width (not scaled) - /// @param height surface height (not scaled) - /// @param scale_factor scale factor - /// @return poiter to the cairo surface - cairo_surface_t* make_surface(int width, int height, double scale_factor) - { - return cairo_image_surface_create(CAIRO_FORMAT_RGB24, - std::ceil((double) width * scale_factor), - std::ceil((double) height * scale_factor)); - } - - /// @brief Creates new buffer with specified size - /// @param width draw buffer width (not scaled) - /// @param height draw buffer height (not scaled) - /// @return true if new draw buffer was created, false if the old buffer was used - bool create_draw_buffer(int width, int height) - { - if(m_width != width || m_height != height || !m_draw_buffer) - { - m_width = width; - m_height = height; - if(m_draw_buffer) - { - cairo_surface_destroy(m_draw_buffer); - } - m_draw_buffer = nullptr; - if(m_width > 0 && m_height > 0) - { - m_draw_buffer = make_surface(m_width, m_height, m_scale_factor); - } - return true; - } - return false; - } - - /// @brief Call this function when widget size changed - /// @param page webpage to be redraw if buffer size changed - /// @param width new draw buffer width - /// @param height new draw buffer height - void on_size_allocate(std::shared_ptr page, int width, int height) - { - if(create_draw_buffer(width, height)) - { - redraw(page); - } - } - - /// @brief Scrolls draw buffer to the position (left, top). - /// - /// Note, the actual position of the draw buffer can be rounded according to the scale factor. - /// Use get_left() and get_top() to know the actual position. - /// - /// @param page webpage to be redraw if the position was changed - /// @param left new horizontal position - /// @param top new vertical position - void on_scroll(std::shared_ptr page, int left, int top); - - /// @brief Reraw the defined area of the buffer - /// - /// All coordinated are not scaled. Actual rectangle could be different according to the scale factor, - /// but it must always cover the requested. - /// - /// @param page webpage to be redraw - /// @param x left position of the area - /// @param y top position of the area - /// @param width width of the area - /// @param height height of the area - void redraw_area(std::shared_ptr page, int x, int y, int width, int height); - - /// @brief Redraw entire buffer - /// @param page webpage to be redraw - void redraw(std::shared_ptr page) - { - redraw_area(page, m_left, m_top, m_width, m_height); - } - - private: - inline int fix_position(int pos) - { - return (pos / m_min_int_position) * m_min_int_position; - } - - static int get_common_divisor(int a, int b) - { - while (b != 0) - { - int t = b; - b = a % b; - a = t; - } - return a; - } - - static int get_denominator(double decimal) - { - int numerator = (int)(decimal * 100); - int denominator = 100; - - int common_divisor = get_common_divisor(numerator, denominator); - numerator /= common_divisor; - return denominator / common_divisor; - } - }; - -} - -#endif diff --git a/src/webpage/html_host.h b/src/webpage/html_host.h deleted file mode 100644 index 472b588..0000000 --- a/src/webpage/html_host.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef LITEBROWSER_HTML_HOST_H -#define LITEBROWSER_HTML_HOST_H - -#include -#include - -namespace litebrowser -{ - class html_host_interface - { - public: - virtual ~html_host_interface() = default; - - virtual void open_url(const std::string& url) = 0; - virtual void open_page(const litehtml::string& url, const litehtml::string& hash) = 0; - virtual void update_cursor() = 0; - virtual void scroll_to(int x, int y) = 0; - virtual void get_client_rect(litehtml::position& client) const = 0; - virtual void redraw_boxes(const litehtml::position::vector& boxes) = 0; - virtual int get_render_width() = 0; - virtual double get_dpi() = 0; - virtual int get_screen_width() = 0; - virtual int get_screen_height() = 0; - virtual cairo_surface_t* load_image(const std::string& path) = 0; - }; - - class browser_notify_interface - { - public: - virtual ~browser_notify_interface() = default; - - virtual void redraw() = 0; - virtual void render() = 0; - virtual void update_state() = 0; - virtual void on_set_caption(const std::string& caption_text) = 0; - virtual void on_page_loaded(uint64_t web_page_id) = 0; - }; - -} - -#endif //LITEBROWSER_HTML_HOST_H diff --git a/src/webpage/http_request.cpp b/src/webpage/http_request.cpp deleted file mode 100644 index d83b2c0..0000000 --- a/src/webpage/http_request.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include "http_request.h" - -bool litebrowser::http_request::open() -{ - m_canceled = false; - CURL* curl = curl_easy_init(); - - if(m_url[0] == '/') - { - std::set chars_to_escape = {' ', ':'}; - std::stringstream new_url; - new_url << "file://"; - for(auto ch : m_url) - { - if( chars_to_escape.find(ch) != chars_to_escape.end()) - { - new_url << '%' << std::hex << (uint32_t) ch; - } else - { - new_url << ch; - } - } - m_url = new_url.str(); - } - - curl_easy_setopt(curl, CURLOPT_URL, m_url.c_str()); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, (long) 1); - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, (long) 10); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_request::write_function); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); - - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, (long) 0); - - curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, (long) 512); - curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, (long) 30); - - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, http_request::progress_function); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); - - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long) 0); - - char errMessage[1024]; - errMessage[0] = 0; - - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errMessage); - curl_easy_setopt(curl, CURLOPT_FAILONERROR, (long) 1); - if(!m_user_agent.empty()) - { - curl_easy_setopt(curl, CURLOPT_USERAGENT, m_user_agent.c_str()); - } else - { - curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0"); - } - - curl_slist* slist = nullptr; - if (!m_headers.empty()) - { - for (auto& item : m_headers) - { - slist = curl_slist_append(slist, item.c_str()); - } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); - } - - std::string post_str; - if (!m_form_data.empty()) - { - for (const auto& field : m_form_data) - { - if (!post_str.empty()) post_str += "&"; - post_str += field.first; - post_str += "="; - post_str += field.second; - } - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_str.c_str()); - } - - m_session = curl; - CURLcode res = curl_easy_perform(curl); - - long httpCode = 0; - - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); - - m_session = nullptr; - curl_easy_cleanup(curl); - - if (slist) - { - curl_slist_free_all(slist); - } - m_form_data.clear(); - m_headers.clear(); - - if(m_on_finish) - { - m_on_finish(httpCode, res, errMessage, m_url); - } - return true; -} - -size_t litebrowser::http_request::write_function(void *ptr, size_t size, size_t nmemb, void *stream) -{ - auto pThis = (http_request*) stream; - - pThis->m_downloaded += size * nmemb; - if(!pThis->m_total) - { - curl_off_t sz = 0; - curl_easy_getinfo((CURL*) pThis->m_session, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &sz); - pThis->m_total = (size_t) sz; - } - if(pThis->m_on_data) - { - pThis->m_on_data(ptr, size * nmemb, pThis->m_downloaded, pThis->m_total); - } - - return size * nmemb; -} - -int litebrowser::http_request::progress_function(void* clientp, curl_off_t /*dltotal*/, curl_off_t /*dlnow*/, curl_off_t /*ultotal*/, curl_off_t /*ulnow*/) -{ - auto pThis = (http_request*) clientp; - return pThis->m_canceled ? 1 : 0; -} - diff --git a/src/webpage/http_request.h b/src/webpage/http_request.h deleted file mode 100644 index 209ad0a..0000000 --- a/src/webpage/http_request.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef LITEBROWSER_HTTP_REQUEST_H -#define LITEBROWSER_HTTP_REQUEST_H - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace litebrowser -{ - class http_requests_pool; - - class http_request : public std::enable_shared_from_this - { - private: - std::function m_on_data; - std::function m_on_finish; - volatile bool m_canceled; - size_t m_downloaded; - size_t m_total; - volatile void* m_session; - std::vector m_headers; - std::vector> m_form_data; - std::string m_user_agent; - std::string m_url; - public: - http_request(std::string url, - std::function cb_on_data, - std::function cb_on_finish) : - m_on_data(std::move(cb_on_data)), - m_on_finish(std::move(cb_on_finish)), - m_canceled(false), - m_downloaded(0), - m_total(0), - m_session(nullptr), - m_url(std::move(url)) - {} - ~http_request() - { - } - - bool open(); - void cancel() - { - m_canceled = true; - } - void add_header(const std::string& header) - { - m_headers.push_back(header); - } - void clear_headers() - { - m_headers.clear(); - } - void add_form_data(const std::string& field, const std::string& value) - { - m_form_data.emplace_back(field, value); - } - void clear_form_data() - { - m_form_data.clear(); - } - const std::string& get_url() { return m_url; } - private: - void thread_proc(); - static size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream ); - static int progress_function(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow ); - }; -} - -#endif //LITEBROWSER_HTTP_REQUEST_H diff --git a/src/webpage/http_requests_pool.cpp b/src/webpage/http_requests_pool.cpp deleted file mode 100644 index e0e5e0c..0000000 --- a/src/webpage/http_requests_pool.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "http_requests_pool.h" - -litebrowser::http_requests_pool::http_requests_pool(int pool_size, const std::function& cb_update_state) : - m_cancel(false), - m_cb_update_state(cb_update_state) -{ - for(int i = 0; i < pool_size; i++) - { - m_threads.emplace_back(std::make_shared(this)); - } -} - -void litebrowser::http_requests_pool::enqueue(const std::string &url, - const std::function& cb_on_data, - const std::function& cb_on_finish) -{ - { - std::unique_lock lock(m_queue_mutex); - auto request = std::make_shared(url, cb_on_data, cb_on_finish); - m_queue.push_back(request); - } - m_mutex_condition.notify_one(); -} - -void litebrowser::http_requests_pool::stop() -{ - { - std::unique_lock lock(m_queue_mutex); - m_cancel = true; - } - m_mutex_condition.notify_all(); - for (auto& active_thread : m_threads) - { - auto request = active_thread->get_request(); - if(request) - { - request->cancel(); - } - } - for (auto& active_thread : m_threads) - { - active_thread->thread().join(); - } - m_threads.clear(); -} - -bool litebrowser::http_requests_pool::is_downloading() -{ - { - std::unique_lock lock(m_queue_mutex); - if(m_cancel) return false; - if(!m_queue.empty()) return true; - } - for (auto& active_thread : m_threads) - { - auto request = active_thread->get_request(); - if(request) - { - return true; - } - } - - return false; -} - -void litebrowser::pool_thread::thread_loop() -{ - while (true) - { - { - std::unique_lock lock(m_pool->m_queue_mutex); - m_pool->m_mutex_condition.wait(lock, [this] - { - return !m_pool->m_queue.empty() || m_pool->m_cancel; - }); - if (m_pool->m_cancel) - { - if(m_pool->m_cb_update_state) - { - m_pool->m_cb_update_state(); - } - return; - } - { - std::unique_lock request_lock(m_request_mutex); - m_request = m_pool->m_queue.front(); - } - m_pool->m_queue.pop_front(); - } - if(m_pool->m_cb_update_state) - { - m_pool->m_cb_update_state(); - } - m_request->open(); - { - std::unique_lock request_lock(m_request_mutex); - m_request = nullptr; - } - if(m_pool->m_cb_update_state) - { - m_pool->m_cb_update_state(); - } - } -} diff --git a/src/webpage/http_requests_pool.h b/src/webpage/http_requests_pool.h deleted file mode 100644 index 9511c1a..0000000 --- a/src/webpage/http_requests_pool.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef LITEBROWSER_HTTP_REQUESTS_POOL_H -#define LITEBROWSER_HTTP_REQUESTS_POOL_H - -#include -#include -#include -#include -#include "http_request.h" - -namespace litebrowser -{ - class http_requests_pool; - - class pool_thread - { - http_requests_pool* m_pool; - std::shared_ptr m_request; - std::mutex m_request_mutex; - std::thread m_thread; - public: - explicit pool_thread(http_requests_pool* pool) : m_pool(pool) - { - m_thread = std::thread(&pool_thread::thread_loop, this); - } - std::shared_ptr get_request() - { - std::unique_lock request_lock(m_request_mutex); - return m_request; - } - std::thread& thread() { return m_thread; } - private: - void thread_loop(); - }; - - class http_requests_pool : public std::enable_shared_from_this - { - friend class pool_thread; - protected: - bool m_cancel; - std::mutex m_queue_mutex; - std::condition_variable m_mutex_condition; - std::list> m_queue; - std::vector> m_threads; - std::function m_queue_empty_cb; - std::function m_cb_update_state; - public: - explicit http_requests_pool(int pool_size, const std::function& cb_update_state); - ~http_requests_pool() - { - stop(); - } - - void enqueue(const std::string& url, - const std::function& cb_on_data, - const std::function& cb_on_finish); - void stop(); - bool is_downloading(); - private: - }; -} - -#endif //LITEBROWSER_HTTP_REQUESTS_POOL_H diff --git a/src/webpage/web_history.cpp b/src/webpage/web_history.cpp deleted file mode 100755 index 63b6586..0000000 --- a/src/webpage/web_history.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "web_history.h" - - -void web_history::url_opened( const std::string& url ) -{ - if(!m_items.empty()) - { - if(m_current_item != m_items.size() - 1) - { - if(m_current_item > 0 && m_items[m_current_item - 1] == url) - { - m_current_item--; - } else if(m_current_item < m_items.size() - 1 && m_items[m_current_item + 1] == url) - { - m_current_item++; - } else - { - m_items.erase(m_items.begin() + m_current_item + 1, m_items.end()); - m_items.push_back(url); - m_current_item = m_items.size() - 1; - } - } else - { - if(m_current_item > 0 && m_items[m_current_item - 1] == url) - { - m_current_item--; - } else - { - m_items.push_back(url); - m_current_item = m_items.size() - 1; - } - } - } else - { - m_items.push_back(url); - m_current_item = m_items.size() - 1; - } -} - -bool web_history::back( std::string& url ) -{ - if(m_items.empty()) return false; - - if(m_current_item > 0) - { - url = m_items[m_current_item - 1]; - return true; - } - return false; -} - -bool web_history::forward( std::string& url ) -{ - if(m_items.empty()) return false; - - if(m_current_item < m_items.size() - 1) - { - url = m_items[m_current_item + 1]; - return true; - } - return false; -} diff --git a/src/webpage/web_history.h b/src/webpage/web_history.h deleted file mode 100755 index 7975b23..0000000 --- a/src/webpage/web_history.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include -#include - -using string_vector = std::vector; - -class web_history -{ - string_vector m_items; - string_vector::size_type m_current_item = 0; -public: - web_history() = default; - virtual ~web_history() = default; - - void url_opened(const std::string& url); - bool back(std::string& url); - bool forward(std::string& url); - - [[nodiscard]] - std::string current() const - { - if(!m_items.empty() && m_current_item < m_items.size()) - { - return m_items[m_current_item]; - } - return ""; - } -}; \ No newline at end of file diff --git a/src/webpage/web_page.cpp b/src/webpage/web_page.cpp deleted file mode 100644 index b7b47ef..0000000 --- a/src/webpage/web_page.cpp +++ /dev/null @@ -1,310 +0,0 @@ -#include -#include -#include "web_page.h" - -void litebrowser::text_file::on_data(void* data, size_t len, size_t /*downloaded*/, size_t /*total*/) -{ - stream.write((const char*) data, (std::streamsize) len); -} - -void litebrowser::text_file::on_page_downloaded(u_int32_t http_status, - u_int32_t err_code, - const std::string &/*err_text*/) -{ - if(err_code == 0 && (http_status == 200 || http_status == 0)) - { - data_ready = true; - } else - { - data_ready = false; - } - wait_mutex.unlock(); -} - -void litebrowser::web_page::open(const std::string &url, const std::string &hash) -{ - m_url = url; - m_base_url = url; - m_hash = hash; - - auto data = std::make_shared(); - auto cb_on_data = [data](auto && PH1, auto && PH2, auto && PH3, auto && PH4) mutable { data->on_data(std::forward(PH1), std::forward(PH2), std::forward(PH3), std::forward(PH4)); }; - auto cb_on_finish = std::bind(&web_page::on_page_downloaded, shared_from_this(), data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - http_request(m_url, cb_on_data, cb_on_finish); -} - -void litebrowser::web_page::get_client_rect(litehtml::position& client) const -{ - m_html_host->get_client_rect(client); -} - -void litebrowser::web_page::on_anchor_click(const char* url, const litehtml::element::ptr& /*el*/) -{ - if(url) - { - make_url(url, m_base_url.c_str(), m_clicked_url); - } -} - -void litebrowser::web_page::set_cursor(const char* cursor) -{ - if(cursor) - { - if(m_cursor != cursor) - { - m_cursor = cursor; - m_html_host->update_cursor(); - } - } -} - -void litebrowser::web_page::import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) -{ - std::string css_url; - make_url(url.c_str(), baseurl.c_str(), css_url); - - auto data = std::make_shared(); - auto cb_on_data = std::bind(&text_file::on_data, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - auto cb_on_finish = std::bind(&text_file::on_page_downloaded, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - http_request(css_url, cb_on_data, cb_on_finish); - data->wait(); - text = data->str(); - if(!text.empty()) - { - baseurl = css_url; - } -} - -void litebrowser::web_page::set_caption(const char* caption) -{ - m_notify->on_set_caption(caption); -} - -void litebrowser::web_page::set_base_url(const char* base_url) -{ - if(base_url) - { - m_base_url = litehtml::resolve(litehtml::url(m_url), litehtml::url(base_url)).str(); - } else - { - m_base_url = m_url; - } -} - -cairo_surface_t* litebrowser::web_page::get_image(const std::string& url) -{ - return m_images.get_image(url); -} - -void litebrowser::web_page::show_hash(const litehtml::string& hash) -{ - std::lock_guard html_lock(m_html_mutex); - if(hash.empty() || !m_html) - { - m_html_host->scroll_to(0, 0); - } else - { - std::string selector = "#" + hash; - litehtml::element::ptr el = m_html->root()->select_one(selector); - if (!el) - { - selector = "[name=" + hash + "]"; - el = m_html->root()->select_one(selector); - } - if (el) - { - litehtml::position pos = el->get_placement(); - m_html_host->scroll_to(0, pos.top()); - } - } -} - -void litebrowser::web_page::make_url(const char* url, const char* basepath, litehtml::string& out) -{ - if(!basepath || !basepath[0]) - { - if(!m_base_url.empty()) - { - out = litehtml::resolve(litehtml::url(m_base_url), litehtml::url(url)).str(); - } else - { - out = url; - } - } else - { - out = litehtml::resolve(litehtml::url(basepath), litehtml::url(url)).str(); - } -} - -void litebrowser::web_page::on_mouse_over(int x, int y, int client_x, int client_y) -{ - std::lock_guard html_lock(m_html_mutex); - if(m_html) - { - litehtml::position::vector redraw_boxes; - if(m_html->on_mouse_over(x, y, client_x, client_y, redraw_boxes)) - { - m_html_host->redraw_boxes(redraw_boxes); - } - } -} - -void litebrowser::web_page::on_lbutton_down(int x, int y, int client_x, int client_y) -{ - std::lock_guard html_lock(m_html_mutex); - if(m_html) - { - litehtml::position::vector redraw_boxes; - if(m_html->on_lbutton_down(x, y, client_x, client_y, redraw_boxes)) - { - m_html_host->redraw_boxes(redraw_boxes); - } - } -} - -void litebrowser::web_page::on_lbutton_up(int x, int y, int client_x, int client_y) -{ - if(m_html) - { - { - std::lock_guard html_lock(m_html_mutex); - - litehtml::position::vector redraw_boxes; - m_clicked_url.clear(); - if (m_html->on_lbutton_up(x, y, client_x, client_y, redraw_boxes)) - { - m_html_host->redraw_boxes(redraw_boxes); - } - } - if(!m_clicked_url.empty()) - { - m_html_host->open_url(m_clicked_url); - } - } -} - -void litebrowser::web_page::on_page_downloaded(std::shared_ptr data, - u_int32_t /*http_status*/, - u_int32_t err_code, - const std::string &err_text, const std::string& url) -{ - if(err_code == 0) - { - m_url = url; - std::lock_guard html_lock(m_html_mutex); - data->set_ready(); - m_html_source = data->str(); - } else - { - std::lock_guard html_lock(m_html_mutex); - std::stringstream ss; - ss << "

Impossible to load page

" << std::endl; - ss << "

Error #" << err_code << ": " << err_text << "

" << std::endl; - m_html_source = ss.str(); - } - - m_html = litehtml::document::createFromString(m_html_source, this); - if (m_html) - { - std::lock_guard html_lock(m_html_mutex); - int render_width = m_html_host->get_render_width(); - m_html->render(render_width); - } - m_notify->on_page_loaded(id()); -} - -void litebrowser::web_page::on_image_downloaded(std::shared_ptr data, - u_int32_t http_status, - u_int32_t err_code, - const std::string &/*err_text*/, - const std::string& /*url*/) -{ - data->close(); - if(!data->path().empty() && !err_code && (http_status == 200 || http_status == 0)) - { - auto ptr = m_html_host->load_image(data->path()); - if(ptr) - { - m_images.add_image(data->url(), ptr); - if(data->redraw_only()) - { - m_notify->redraw(); - } else - { - m_notify->render(); - } - } - } - unlink(data->path().c_str()); -} - -void litebrowser::web_page::load_image(const char *src, const char *baseurl, bool redraw_on_ready) -{ - std::string url; - make_url(src, baseurl, url); - - if(m_images.reserve(url)) - { - auto data = std::make_shared(url, redraw_on_ready); - auto cb_on_data = std::bind(&image_file::on_data, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - auto cb_on_finish = std::bind(&web_page::on_image_downloaded, this, data, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - http_request(url, cb_on_data, cb_on_finish); - } -} - -void litebrowser::web_page::http_request(const std::string &url, - const std::function &cb_on_data, - const std::function &cb_on_finish) -{ - m_requests_pool.enqueue(url, cb_on_data, cb_on_finish); -} - -void litebrowser::web_page::on_pool_update_state() -{ - m_notify->update_state(); -} - -double litebrowser::web_page::get_screen_dpi() const -{ - return m_html_host->get_dpi(); -} - -int litebrowser::web_page::get_screen_width() const -{ - return m_html_host->get_screen_width(); -} - -int litebrowser::web_page::get_screen_height() const -{ - return m_html_host->get_screen_height(); -} - -void litebrowser::web_page::on_mouse_event(const litehtml::element::ptr& /*el*/, litehtml::mouse_event /*event*/) -{ - -} - -////////////////////////////////////////////////////////// - -litebrowser::image_file::image_file(std::string url, bool redraw_on_ready) : - m_url(std::move(url)), - m_redraw_on_ready(redraw_on_ready) -{ -} - -void litebrowser::image_file::on_data(void *data, size_t len, size_t /*downloaded*/, size_t /*total*/) -{ - if(m_fd < 0) - { - char nameBuff[] = "/tmp/litebrowser-XXXXXX"; - m_fd = mkstemp(nameBuff); - if(m_fd >= 0) - { - m_path = nameBuff; - } - } - if(m_fd >= 0) - { - write(m_fd, data, len); - } -} diff --git a/src/webpage/web_page.h b/src/webpage/web_page.h deleted file mode 100644 index 6e38c63..0000000 --- a/src/webpage/web_page.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef LITEBROWSER_WEB_PAGE_H -#define LITEBROWSER_WEB_PAGE_H - -#include -#include -#include "container_cairo_pango.h" -#include "html_host.h" -#include "http_requests_pool.h" -#include "cairo_images_cache.h" - -namespace litebrowser -{ - class text_file - { - std::mutex wait_mutex; - std::stringstream stream; - bool data_ready = false; - public: - text_file() - { - wait_mutex.lock(); - } - - void set_ready() { data_ready = true; } - - std::string str() const { return data_ready ? stream.str() : ""; } - void wait() - { - wait_mutex.lock(); - } - void on_data(void* data, size_t len, size_t downloaded, size_t total); - void on_page_downloaded(u_int32_t http_status, u_int32_t err_code, const std::string& err_text); - }; - - class image_file - { - int m_fd = -1; - std::string m_path; - std::string m_url; - bool m_redraw_on_ready; - public: - explicit image_file(std::string url, bool redraw_on_ready); - void on_data(void* data, size_t len, size_t downloaded, size_t total); - void close() const - { - if(m_fd > 0) - { - ::close(m_fd); - } - } - [[nodiscard]] - const std::string &path() const { return m_path; } - - [[nodiscard]] - const std::string& url() const { return m_url; } - - [[nodiscard]] - bool redraw_only() const { return m_redraw_on_ready; } - }; - - class web_page : public container_cairo_pango, - public std::enable_shared_from_this - { - litehtml::string m_url; - litehtml::string m_base_url; - litehtml::document::ptr m_html; - std::recursive_mutex m_html_mutex; - litehtml::string m_cursor; - litehtml::string m_clicked_url; - std::string m_hash; - html_host_interface* m_html_host; - cairo_images_cache m_images; - litebrowser::http_requests_pool m_requests_pool; - std::string m_html_source; - - std::shared_ptr m_notify; - - public: - explicit web_page(html_host_interface* html_host, std::shared_ptr notify, int pool_size) : - m_html_host(html_host), - m_requests_pool(pool_size, [this] { on_pool_update_state(); }), - m_notify(std::move(notify)) - {} - - [[nodiscard]] - uint64_t id() const { return (uint64_t)this; } - - [[nodiscard]] - const std::string& get_html_source() const { return m_html_source; } - - void open(const litehtml::string& url, const litehtml::string& hash); - - void get_client_rect(litehtml::position& client) const override; - void on_anchor_click(const char* url, const litehtml::element::ptr& el) override; - void set_cursor(const char* cursor) override; - void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) override; - void set_caption(const char* caption) override; - void set_base_url(const char* base_url) override; - cairo_surface_t* get_image(const std::string& url) override; - void make_url( const char* url, const char* basepath, litehtml::string& out ) override; - void load_image(const char* src, const char* baseurl, bool redraw_on_ready) override; - void on_mouse_event(const litehtml::element::ptr& el, litehtml::mouse_event event) override; - double get_screen_dpi() const override; - int get_screen_width() const override; - int get_screen_height() const override; - - void show_hash(const litehtml::string& hash); - void show_hash_and_reset() - { - if(!m_hash.empty() && m_html) - { - show_hash(m_hash); - m_hash = ""; - } - } - - void on_mouse_over(int x, int y, int client_x, int client_y); - void on_lbutton_down(int x, int y, int client_x, int client_y); - void on_lbutton_up(int x, int y, int client_x, int client_y); - - [[nodiscard]] - const std::string& get_cursor() const { return m_cursor; } - - void draw(litehtml::uint_ptr hdc, int x, int y, const litehtml::position* clip) - { - std::lock_guard html_lock(m_html_mutex); - if(m_html) m_html->draw(hdc, x, y, clip); - } - - int render(int max_width) - { - std::lock_guard html_lock(m_html_mutex); - return m_html ? m_html->render(max_width) : 0; - } - - [[nodiscard]] - const std::string& url() const { return m_url; } - - [[nodiscard]] - int width() const { return m_html ? m_html->width() : 0; } - - [[nodiscard]] - int height() const { return m_html ? m_html->height() : 0; } - - bool media_changed() - { - std::lock_guard html_lock(m_html_mutex); - return m_html && m_html->media_changed(); - } - - void stop_loading() - { - m_requests_pool.stop(); - } - - [[nodiscard]] - bool is_downloading() - { - return m_requests_pool.is_downloading(); - } - - void dump(litehtml::dumper& cout) - { - std::lock_guard html_lock(m_html_mutex); - if(m_html) - { - m_html->dump(cout); - } - } - private: - void http_request(const std::string& url, - const std::function& cb_on_data, - const std::function& cb_on_finish); - void on_page_downloaded(std::shared_ptr data, u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url); - void on_image_downloaded(std::shared_ptr data, u_int32_t http_status, u_int32_t err_code, const std::string& err_text, const std::string& url); - void on_pool_update_state(); - }; -} - -#endif //LITEBROWSER_WEB_PAGE_H diff --git a/src/widget/html_widget.cpp b/src/widget/html_widget.cpp deleted file mode 100644 index eafd04d..0000000 --- a/src/widget/html_widget.cpp +++ /dev/null @@ -1,794 +0,0 @@ -#include "html_widget.h" -#include - -html_widget::html_widget() -{ - add_css_class("litehtml"); - - m_notifier = std::make_shared(); - m_notifier->connect_redraw(sigc::mem_fun(*this, &html_widget::on_redraw)); - m_notifier->connect_render(sigc::mem_fun(*this, &html_widget::render)); - m_notifier->connect_update_state([this]() { m_sig_update_state.emit(get_state()); }); - m_notifier->connect_on_page_loaded(sigc::mem_fun(*this, &html_widget::on_page_loaded)); - m_notifier->connect_on_set_caption(sigc::mem_fun(*this, &html_widget::set_caption)); - - set_focusable(true); - - m_vadjustment = Gtk::Adjustment::create(0.0, 0.0, 0.0, 10); - m_vadjustment->signal_value_changed().connect(sigc::mem_fun(*this, &html_widget::on_vadjustment_changed)); - m_hadjustment = Gtk::Adjustment::create(0.0, 0.0, 0.0, 10); - m_hadjustment->signal_value_changed().connect(sigc::mem_fun(*this, &html_widget::on_hadjustment_changed)); - - m_hadjustment->signal_changed().connect(sigc::mem_fun(*this, &html_widget::on_adjustments_changed)); - m_vadjustment->signal_changed().connect(sigc::mem_fun(*this, &html_widget::on_adjustments_changed)); - - m_vscrollbar = Gtk::make_managed(m_vadjustment, Gtk::Orientation::VERTICAL); - m_vscrollbar->insert_at_end(*this); - m_vscrollbar->set_visible(true); - m_vscrollbar->add_css_class("right"); - m_vscrollbar->add_css_class("overlay-indicator"); - - m_hscrollbar = Gtk::make_managed(m_hadjustment, Gtk::Orientation::HORIZONTAL); - m_hscrollbar->insert_at_end(*this); - m_hscrollbar->set_visible(true); - m_hscrollbar->add_css_class("bottom"); - m_hscrollbar->add_css_class("overlay-indicator"); - - auto controller = Gtk::EventControllerScroll::create(); - controller->set_flags(Gtk::EventControllerScroll::Flags::BOTH_AXES); - controller->signal_scroll().connect(sigc::mem_fun(*this, &html_widget::on_scroll), false); - add_controller(controller); - - auto click_gesture = Gtk::GestureClick::create(); - click_gesture->set_button(GDK_BUTTON_PRIMARY); - click_gesture->signal_pressed().connect(sigc::mem_fun(*this, &html_widget::on_button_press_event)); - click_gesture->signal_released().connect(sigc::mem_fun(*this, &html_widget::on_button_release_event)); - add_controller(click_gesture); - - auto move_gesture = Gtk::EventControllerMotion::create(); - move_gesture->signal_motion().connect(sigc::mem_fun(*this, &html_widget::on_mouse_move)); - move_gesture->signal_leave().connect([this]() { restart_scrollbar_timer(); }); - add_controller(move_gesture); - - auto key_controller = Gtk::EventControllerKey::create(); - key_controller->signal_key_pressed().connect(sigc::mem_fun(*this, &html_widget::on_key_pressed), false); - add_controller(key_controller); -} - -html_widget::~html_widget() -{ - m_notifier->disconnect(); - - if (!gobj()) return; - - while (Widget* child = get_first_child()) - { - child->unparent(); - } -} - -double html_widget::get_dpi() -{ - // DPI is always 96 (default). Scaling will make things larger. - return 96.0; -} - -int html_widget::get_screen_width() -{ - auto display = Gdk::Display::get_default(); - if (display) - { - auto monitors = display->get_monitors(); - if (monitors->get_n_items() > 0) - { - auto monitor = monitors->get_typed_object(0); - Gdk::Rectangle rect; - monitor->get_geometry(rect); - return rect.get_width(); - } - } - return 800; -} - -int html_widget::get_screen_height() -{ - auto display = Gdk::Display::get_default(); - if (display) - { - auto monitors = display->get_monitors(); - if (monitors->get_n_items() > 0) - { - auto monitor = monitors->get_typed_object(0); - Gdk::Rectangle rect; - monitor->get_geometry(rect); - return rect.get_height(); - } - } - return 800; -} - -void html_widget::snapshot_vfunc(const Glib::RefPtr& snapshot) -{ - if (get_allocated_width() <= 0 || get_allocated_height() <= 0) - { - return; - } - - { - auto allocation = get_allocation(); - auto cr = snapshot->append_cairo(Gdk::Rectangle(0, 0, allocation.get_width(), allocation.get_height())); - if(m_draw_buffer.get_cairo_surface()) - { - cr->scale(1.0 / m_draw_buffer.get_scale_factor(), 1.0 / m_draw_buffer.get_scale_factor()); - cairo_set_source_surface(cr->cobj(), m_draw_buffer.get_cairo_surface(), 0, 0); - cr->paint(); - } - } - - snapshot_child(*m_vscrollbar, snapshot); - snapshot_child(*m_hscrollbar, snapshot); -} - -void html_widget::get_client_rect(litehtml::position& client) const -{ - client.x = m_draw_buffer.get_left(); - client.y = m_draw_buffer.get_top(); - client.width = m_draw_buffer.get_width(); - client.height = m_draw_buffer.get_height(); -} - -void html_widget::set_caption(const std::string& caption) -{ - auto root = get_root(); - if(root) - { - auto window = dynamic_cast(root); - if (window) - { - window->set_title(caption.c_str()); - }; - } -} - -cairo_surface_t *html_widget::load_image(const std::string &path) -{ - Glib::RefPtr ptr; - - try - { - ptr = Gdk::Pixbuf::create_from_file(path); - } catch (...) { } - - if(!ptr) return nullptr; - - cairo_surface_t* ret = nullptr; - - if(ptr->get_has_alpha()) - { - ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ptr->get_width(), ptr->get_height()); - } else - { - ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, ptr->get_width(), ptr->get_height()); - } - - Cairo::RefPtr surface(new Cairo::Surface(ret, false)); - Cairo::RefPtr ctx = Cairo::Context::create(surface); - Gdk::Cairo::set_source_pixbuf(ctx, ptr, 0.0, 0.0); - ctx->paint(); - - return ret; - -} - -void html_widget::open_page(const litehtml::string& url, const litehtml::string& hash) -{ - { - std::lock_guard lock(m_page_mutex); - if (m_current_page) - { - m_current_page->stop_loading(); - } - m_next_page = std::make_shared(this, m_notifier, 10); - m_next_page->open(url, hash); - } - m_sig_set_address.emit(url); - m_sig_update_state.emit(get_state()); -} - -void html_widget::scroll_to(int x, int y) -{ - m_hadjustment->set_value(x); - m_vadjustment->set_value(y); -} - -void html_widget::on_redraw() -{ - m_draw_buffer.redraw(current_page()); - queue_draw(); -} - -void html_widget::on_button_press_event(int /* n_press */, double x, double y) -{ - if(!has_focus()) - { - grab_focus(); - } - auto page = current_page(); - if (page) - { - page->on_lbutton_down( (int) (x + m_draw_buffer.get_left()), - (int) (y + m_draw_buffer.get_top()), - (int) x, (int) y); - } -} - -void html_widget::on_button_release_event(int /* n_press */, double x, double y) -{ - auto page = current_page(); - if(page) - { - page->on_lbutton_up((int) (x + m_draw_buffer.get_left()), - (int) (y + m_draw_buffer.get_top()), - (int) x, (int) y); - } -} - -void html_widget::on_mouse_move(double x, double y) -{ - bool restart_timer = true; - if(m_hscrollbar->is_visible()) - { - if(y >= get_allocated_height() - 16) - { - m_hscrollbar->add_css_class("hovering"); - restart_timer = false; - } else - { - m_hscrollbar->remove_css_class("hovering"); - } - } - if(m_vscrollbar->is_visible()) - { - if(x >= get_allocated_width() - 16) - { - m_vscrollbar->add_css_class("hovering"); - restart_timer = false; - } else - { - m_vscrollbar->remove_css_class("hovering"); - } - } - m_hscrollbar->set_opacity(1); - m_vscrollbar->set_opacity(1); - if(restart_timer) - { - restart_scrollbar_timer(); - } else - { - m_scrollbar_timer.disconnect(); - } - - std::shared_ptr page = current_page(); - if(page) - { - page->on_mouse_over((int) (x + m_draw_buffer.get_left()), - (int) (y + m_draw_buffer.get_top()), - (int) x, (int) y); - } -} - -bool html_widget::on_key_pressed(guint keyval, guint /* keycode */, Gdk::ModifierType /* state */) -{ - switch (keyval) - { - case GDK_KEY_KP_Page_Down: - case GDK_KEY_Page_Down: - m_vadjustment->set_value(m_vadjustment->get_value() + m_vadjustment->get_page_size()); - break; - case GDK_KEY_KP_Page_Up: - case GDK_KEY_Page_Up: - m_vadjustment->set_value(m_vadjustment->get_value() - m_vadjustment->get_page_size()); - break; - case GDK_KEY_KP_Down: - case GDK_KEY_Down: - m_vadjustment->set_value(m_vadjustment->get_value() + m_vadjustment->get_step_increment()); - break; - case GDK_KEY_KP_Up: - case GDK_KEY_Up: - m_vadjustment->set_value(m_vadjustment->get_value() - m_vadjustment->get_step_increment()); - break; - case GDK_KEY_KP_Home: - case GDK_KEY_Home: - m_vadjustment->set_value(0); - break; - case GDK_KEY_KP_End: - case GDK_KEY_End: - m_vadjustment->set_value(m_vadjustment->get_upper()); - break; - case GDK_KEY_KP_Left: - case GDK_KEY_Left: - m_hadjustment->set_value(m_hadjustment->get_value() - m_hadjustment->get_step_increment()); - break; - case GDK_KEY_KP_Right: - case GDK_KEY_Right: - m_hadjustment->set_value(m_hadjustment->get_value() + m_hadjustment->get_step_increment()); - break; - - default: - break; - } - - return false; -} - -void html_widget::update_cursor() -{ - std::shared_ptr page = current_page(); - if(page) - { - if(page->get_cursor() == "auto") - { - set_cursor("default"); - } else - { - set_cursor(page->get_cursor()); - } - } else - { - set_cursor("default"); - } -} - -long html_widget::draw_measure(int number) -{ - std::shared_ptr page = current_page(); - - if(page) - { - litehtml::position view_port; - get_client_rect(view_port); - - int width = view_port.width; - int height = view_port.height; - - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - auto image = (unsigned char *) g_malloc(stride * height); - - cairo_surface_t *surface = cairo_image_surface_create_for_data(image, CAIRO_FORMAT_ARGB32, width, height, - stride); - cairo_t *cr = cairo_create(surface); - - litehtml::position pos; - pos.width = width; - pos.height = height; - pos.x = 0; - pos.y = 0; - - int x = view_port.x; - int y = view_port.y; - - cairo_rectangle(cr, 0, 0, width, height); - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_paint(cr); - page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); - cairo_surface_write_to_png(surface, "/tmp/litebrowser.png"); - - auto t1 = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < number; i++) - { - page->draw((litehtml::uint_ptr) cr, -x, -y, &pos); - } - auto t2 = std::chrono::high_resolution_clock::now(); - - cairo_destroy(cr); - cairo_surface_destroy(surface); - g_free(image); - - return (std::chrono::duration_cast(t2 - t1)).count(); - } - return 0; -} - -long html_widget::render_measure(int number) -{ - std::shared_ptr page = current_page(); - - if(page) - { - auto t1 = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < number; i++) - { - page->render(m_draw_buffer.get_width()); - } - auto t2 = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(t2 - t1)).count(); - } - return -1; -} - -void html_widget::size_allocate_vfunc(int width, int height, int /* baseline */) -{ - allocate_scrollbars(width, height); - std::shared_ptr page = current_page(); - if(page) - { - if(m_rendered_width != width || m_rendered_height != height) - { - m_rendered_width = width; - m_rendered_height = height; - m_draw_buffer.on_size_allocate(page, width, height); - page->media_changed(); - page->render(m_rendered_width); - update_view_port(page); - m_draw_buffer.redraw(page); - queue_draw(); - } - } else - { - m_draw_buffer.on_size_allocate(page, width, height); - } - -} - -void html_widget::allocate_scrollbars(int width, int height) -{ - m_do_force_redraw_on_adjustment = false; - m_hadjustment->set_page_size(width); - m_vadjustment->set_page_size(height); - if(m_vadjustment->get_value() > m_vadjustment->get_upper() - m_vadjustment->get_page_size()) - { - m_vadjustment->set_value(m_vadjustment->get_upper() - m_vadjustment->get_page_size()); - } - if(m_hadjustment->get_value() > m_hadjustment->get_upper() - m_hadjustment->get_page_size()) - { - m_hadjustment->set_value(m_hadjustment->get_upper() - m_hadjustment->get_page_size()); - } - m_do_force_redraw_on_adjustment = true; - - int minimum = 0, natural = 0, m_baseline = 0, n_baseline = 0; - - m_vscrollbar->measure(Gtk::Orientation::HORIZONTAL, -1, minimum, natural, m_baseline, n_baseline); - Gtk::Allocation vscrollbar_allocation(width - natural, 0, natural, height); - m_vscrollbar->size_allocate(vscrollbar_allocation, -1); - - minimum = 0, natural = 0, m_baseline = 0, n_baseline = 0; - m_hscrollbar->measure(Gtk::Orientation::VERTICAL, -1, minimum, natural, m_baseline, n_baseline); - Gtk::Allocation hscrollbar_allocation(0, height - natural, width, natural); - m_hscrollbar->size_allocate(hscrollbar_allocation, -1); -} - -void html_widget::on_vadjustment_changed() -{ - m_draw_buffer.on_scroll( current_page(), - (int) m_hadjustment->get_value(), - (int) m_vadjustment->get_value()); - - if(m_do_force_redraw_on_adjustment) - { - force_redraw(); - } else - { - queue_draw(); - } -} - -void html_widget::on_hadjustment_changed() -{ - m_draw_buffer.on_scroll( current_page(), - (int) m_hadjustment->get_value(), - (int) m_vadjustment->get_value()); - if(m_do_force_redraw_on_adjustment) - { - force_redraw(); - } else - { - queue_draw(); - } -} - -void html_widget::on_adjustments_changed() -{ - m_hscrollbar->set_visible(m_hadjustment->get_upper() > 0); - m_vscrollbar->set_visible(m_vadjustment->get_upper() > 0); -} - -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); - return true; -} - -bool html_widget::on_scrollbar_timeout() -{ - m_hscrollbar->remove_css_class("hovering"); - m_vscrollbar->remove_css_class("hovering"); - m_hscrollbar->set_opacity(0); - m_vscrollbar->set_opacity(0); - return false; -} - -void html_widget::on_realize() -{ - Gtk::Widget::on_realize(); - - auto native = get_native(); - if(native) - { - auto surface = native->get_surface(); - if(surface) - { - surface->property_scale().signal_changed().connect([this]() - { - auto native = get_native(); - if(native) - { - auto surface = native->get_surface(); - if(surface) - { - m_draw_buffer.set_scale_factor(current_page(), surface->get_scale()); - queue_draw(); - } - } - }); - } - } -} - -void html_widget::update_view_port(std::shared_ptr page) -{ - if(page) - { - auto allocation = get_allocation(); - if(allocation.get_width() < page->width()) - { - m_hadjustment->set_upper(page->width()); - } else - { - m_hadjustment->set_upper(0); - } - if(allocation.get_height() < page->height()) - { - m_vadjustment->set_upper(page->height()); - } else - { - m_vadjustment->set_upper(0); - } - } else - { - m_hadjustment->set_upper(0); - m_vadjustment->set_upper(0); - } -} - -void html_widget::restart_scrollbar_timer() -{ - if (m_scrollbar_timer) - { - m_scrollbar_timer.disconnect(); - } - - m_scrollbar_timer = Glib::signal_timeout().connect_seconds( - sigc::mem_fun(*this, &html_widget::on_scrollbar_timeout), 2); -} - -void html_widget::redraw_boxes(const litehtml::position::vector& boxes) -{ - if(boxes.empty()) return; - - Gdk::Rectangle rect(0, 0, 0, 0); - bool is_first = true; - for(const auto& pos : boxes) - { - if(is_first) - { - rect = Gdk::Rectangle(pos.x, pos.y, pos.width, pos.height); - is_first = false; - } else - { - rect.join(Gdk::Rectangle(pos.x, pos.y, pos.width, pos.height)); - } - } - - if(!rect.has_zero_area()) - { - m_draw_buffer.redraw_area(current_page(), rect.get_x(), rect.get_y(), rect.get_width(), rect.get_height()); - queue_draw(); - } -} - -int html_widget::get_render_width() -{ - return m_draw_buffer.get_width(); -} - -void html_widget::on_page_loaded(uint64_t web_page_id) -{ - std::string url; - { - std::lock_guard lock(m_page_mutex); - if(m_next_page->id() != web_page_id) return; - m_current_page = m_next_page; - m_next_page = nullptr; - url = m_current_page->url(); - update_view_port(m_current_page); - } - scroll_to(0, 0); - on_redraw(); - m_sig_set_address.emit(url); - m_sig_update_state.emit(get_state()); -} - -void html_widget::show_hash(const std::string &hash) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->show_hash(hash); - } -} - -void html_widget::open_url(const std::string &url) -{ - std::string hash; - std::string s_url = url; - - m_sig_set_address.emit(url); - - std::string::size_type hash_pos = s_url.find_first_of(L'#'); - if(hash_pos != std::wstring::npos) - { - hash = s_url.substr(hash_pos + 1); - s_url.erase(hash_pos); - } - - bool open_hash_only = false; - bool reload = false; - - auto current_url = m_history.current(); - hash_pos = current_url.find_first_of(L'#'); - if(hash_pos != std::wstring::npos) - { - current_url.erase(hash_pos); - } - - if(!current_url.empty()) - { - if(m_history.current() != url) - { - if (current_url == s_url) - { - open_hash_only = true; - } - } else - { - reload = true; - } - } - if(!open_hash_only) - { - open_page(url, hash); - } else - { - show_hash(hash); - } - if(!reload) - { - m_history.url_opened(url); - } - m_sig_update_state.emit(get_state()); -} - -void html_widget::render() -{ - std::shared_ptr page = current_page(); - if(page) - { - page->render(m_draw_buffer.get_width()); - update_view_port(page); - m_draw_buffer.redraw(page); - queue_draw(); - } -} - -bool html_widget::on_close() -{ - if(m_current_page) - { - m_current_page->stop_loading(); - } - if(m_next_page) - { - m_next_page->stop_loading(); - } - return false; -} - -void html_widget::dump(litehtml::dumper &cout) -{ - std::shared_ptr page = current_page(); - if(page) - { - page->dump(cout); - } -} - -void html_widget::go_forward() -{ - std::string url; - if(m_history.forward(url)) - { - open_url(url); - } -} - -void html_widget::go_back() -{ - std::string url; - if(m_history.back(url)) - { - open_url(url); - } -} - -uint32_t html_widget::get_state() -{ - uint32_t ret = 0; - std::string url; - if(m_history.back(url)) - { - ret |= page_state_has_back; - } - if(m_history.forward(url)) - { - ret |= page_state_has_forward; - } - { - std::lock_guard lock(m_page_mutex); - if(m_next_page) - { - ret |= page_state_downloading; - } - } - if(!(ret & page_state_downloading)) - { - std::shared_ptr page = current_page(); - if(page) - { - if(page->is_downloading()) - { - ret |= page_state_downloading; - } - } - } - return ret; -} - -void html_widget::stop_download() -{ - std::lock_guard lock(m_page_mutex); - if(m_next_page) - { - m_next_page->stop_loading(); - } else if (m_current_page) - { - m_current_page->stop_loading(); - } -} - -void html_widget::reload() -{ - std::shared_ptr page = current_page(); - if(page) - { - open_url(page->url()); - } -} - -std::string html_widget::get_html_source() -{ - std::lock_guard lock(m_page_mutex); - if(m_current_page) - return m_current_page->get_html_source(); - return {}; -} diff --git a/src/widget/html_widget.h b/src/widget/html_widget.h deleted file mode 100644 index 19bfb19..0000000 --- a/src/widget/html_widget.h +++ /dev/null @@ -1,303 +0,0 @@ -#pragma once - -#include -#include "html_host.h" -#include "web_page.h" -#include "http_requests_pool.h" -#include "web_history.h" -#include "draw_buffer.h" -#include - -enum page_state -{ - page_state_has_back = 0x01, - page_state_has_forward = 0x02, - page_state_downloading = 0x04, -}; - -/// @brief Allows perform actions in the GUI thread via notifications -class html_widget_notifier : public litebrowser::browser_notify_interface -{ -public: - using redraw_func = std::function; - using render_func = std::function; - using update_state_func = std::function; - using on_page_loaded_func = std::function; - using on_set_caption_func = std::function; -private: - enum func_type - { - func_type_none, - func_type_redraw, - func_type_render, - func_type_update_state, - func_type_on_page_loaded, - func_type_on_set_caption - }; - struct queue_item - { - func_type type; - uint64_t param; - std::string str_param; - }; - - Glib::Dispatcher m_dispatcher; - redraw_func m_redraw_func; - render_func m_render_func; - update_state_func m_update_state_func; - on_page_loaded_func m_on_page_loaded_func; - on_set_caption_func m_on_set_caption_func; - - std::mutex m_lock; - bool m_disconnect = false; - std::queue m_queue; - -public: - - html_widget_notifier() - { - m_dispatcher.connect(sigc::mem_fun(*this, &html_widget_notifier::on_notify)); - } - - void disconnect() - { - std::lock_guard lock(m_lock); - m_disconnect = true; - } - - void connect_redraw(redraw_func _redraw_func) - { - m_redraw_func = _redraw_func; - } - - void connect_render(render_func _render_func) - { - m_render_func = _render_func; - } - - void connect_update_state(update_state_func _update_state_func) - { - m_update_state_func = _update_state_func; - } - - void connect_on_page_loaded(on_page_loaded_func _on_page_loaded_func) - { - m_on_page_loaded_func = _on_page_loaded_func; - } - - void connect_on_set_caption(on_set_caption_func _on_set_caption_func) - { - m_on_set_caption_func = _on_set_caption_func; - } - -private: - - void redraw() override - { - { - std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_redraw, 0, {}}); - } - m_dispatcher.emit(); - } - - void render() override - { - { - std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_render, 0, {}}); - } - m_dispatcher.emit(); - } - - void update_state() override - { - { - std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_update_state, 0, {}}); - } - m_dispatcher.emit(); - } - - void on_page_loaded(uint64_t web_page_id) override - { - { - std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_on_page_loaded, web_page_id, {}}); - } - m_dispatcher.emit(); - } - - void on_set_caption(const std::string& caption) override - { - { - std::lock_guard lock(m_lock); - m_queue.push(queue_item{func_type_on_set_caption, 0, caption}); - } - m_dispatcher.emit(); - } - - void on_notify() - { - std::queue local_queue; - { - std::lock_guard lock(m_lock); - if(m_disconnect || m_queue.empty()) return; - - while(!m_queue.empty()) - { - local_queue.push(m_queue.front()); - m_queue.pop(); - } - } - func_type prev_type = func_type_none; - while(!local_queue.empty()) - { - const auto& item = local_queue.front(); - // We don't need do the same action some times - if(item.type != prev_type) - { - prev_type = item.type; - switch (item.type) - { - case func_type_redraw: - if(m_redraw_func) - { - m_redraw_func(); - } - break; - case func_type_render: - if(m_render_func) - { - m_render_func(); - } - break; - case func_type_update_state: - if(m_update_state_func) - { - m_update_state_func(); - } - break; - case func_type_on_page_loaded: - if(m_on_page_loaded_func) - { - m_on_page_loaded_func(item.param); - } - break; - case func_type_on_set_caption: - if(m_on_set_caption_func) - { - m_on_set_caption_func(item.str_param); - } - break; - - default: - break; - } - } - local_queue.pop(); - } - } -}; - -class html_widget : public Gtk::Widget, - public litebrowser::html_host_interface -{ - int m_rendered_width = 0; - int m_rendered_height = 0; - bool m_do_force_redraw_on_adjustment = true; - std::mutex m_page_mutex; - std::shared_ptr m_current_page; - std::shared_ptr m_next_page; - std::shared_ptr m_notifier; - web_history m_history; - - Gtk::Scrollbar* m_vscrollbar; - Gtk::Scrollbar* m_hscrollbar; - Glib::RefPtr m_vadjustment; - Glib::RefPtr m_hadjustment; - - sigc::connection m_scrollbar_timer; - - litebrowser::draw_buffer m_draw_buffer; - -public: - explicit html_widget(); - ~html_widget() override; - - void on_page_loaded(uint64_t web_page_id); - void render(); - void go_forward(); - void go_back(); - uint32_t get_state(); - void stop_download(); - void reload(); - - std::string get_html_source(); - long render_measure(int number); - long draw_measure(int number); - void show_hash(const std::string& hash); - bool on_close(); - void dump(litehtml::dumper& cout); - - void open_url(const std::string& url) override; - -protected: - // litebrowser::html_host_interface override - double get_dpi() override; - int get_screen_width() override; - int get_screen_height() override; - void open_page(const litehtml::string& url, const litehtml::string& hash) override; - void update_cursor() override; - void redraw_boxes(const litehtml::position::vector& boxes) override; - int get_render_width() override; - void scroll_to(int x, int y) override; - void get_client_rect(litehtml::position& client) const override; - cairo_surface_t* load_image(const std::string& path) override; - - void snapshot_vfunc(const Glib::RefPtr& snapshot) override; - void on_redraw(); - - void on_button_press_event(int n_press, double x, double y); - void on_button_release_event(int n_press, double x, double y); - void on_mouse_move(double x, double y); - bool on_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state); - - void size_allocate_vfunc(int width, int height, int baseline) override; - void allocate_scrollbars(int width, int height); - - void set_caption(const std::string& caption); - void on_vadjustment_changed(); - void on_hadjustment_changed(); - void on_adjustments_changed(); - bool on_scroll(double dx, double dy); - bool on_scrollbar_timeout(); - void on_realize() override; - -private: - std::shared_ptr current_page() - { - std::lock_guard lock(m_page_mutex); - return m_current_page; - } - - void update_view_port(std::shared_ptr page); - void restart_scrollbar_timer(); - void force_redraw() - { - queue_draw(); - while (g_main_context_iteration(nullptr, false)) {} - } - -public: - // Signals types - using sig_set_address_t = sigc::signal; - using sig_update_state_t = sigc::signal; - - sig_set_address_t signal_set_address() { return m_sig_set_address; } - sig_update_state_t signal_update_state() { return m_sig_update_state; } - -private: - sig_update_state_t m_sig_update_state; - sig_set_address_t m_sig_set_address; -}; From b789e549d7a7be3de047a99b69d0e69f241499bd Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Thu, 3 Apr 2025 03:32:04 +0300 Subject: [PATCH 15/17] Update for the latest litehtml --- litehtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litehtml b/litehtml index d6041ca..2af7d0d 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit d6041ca537ae3fb63d6224394ae4ae4a3ec32a17 +Subproject commit 2af7d0d2a674370d5b7f5b95288ea49c3f0ba1a8 From 76981f2e834c5ba30cbdfee131a3f5d2dc79a56a Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Sun, 6 Apr 2025 02:57:30 +0300 Subject: [PATCH 16/17] update litehtml --- .gitignore | 3 +++ litehtml | 2 +- src/browser_wnd.cpp | 12 ------------ src/browser_wnd.h | 1 - 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index b5c88a3..2dd8c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ build/ .idea .vscode +.cache/ +html/ + diff --git a/litehtml b/litehtml index 2af7d0d..2a89d5e 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit 2af7d0d2a674370d5b7f5b95288ea49c3f0ba1a8 +Subproject commit 2a89d5ed4bd866690326ab5b2255be8cc1cb0721 diff --git a/src/browser_wnd.cpp b/src/browser_wnd.cpp index edf17fd..019548d 100644 --- a/src/browser_wnd.cpp +++ b/src/browser_wnd.cpp @@ -195,18 +195,6 @@ void browser_window::on_go_clicked() m_html.grab_focus(); } -bool browser_window::on_address_key_press(guint keyval, guint /*keycode*/, Gdk::ModifierType /*state*/) -{ - if(keyval == GDK_KEY_Return) - { - m_address_bar.select_region(0, -1); - on_go_clicked(); - return true; - } - - return false; -} - void browser_window::on_forward_clicked() { m_html.go_forward(); diff --git a/src/browser_wnd.h b/src/browser_wnd.h index 6902e76..690576d 100644 --- a/src/browser_wnd.h +++ b/src/browser_wnd.h @@ -25,7 +25,6 @@ class browser_window : public Gtk::Window void on_back_clicked(); void on_render_measure(int number); void on_draw_measure(int number); - bool on_address_key_press(guint keyval, guint /*keycode*/, Gdk::ModifierType /*state*/); void on_dump(); protected: From 6d2983360399e6cd4e371fbac2e9ae5389cd3775 Mon Sep 17 00:00:00 2001 From: Yuri Kobets Date: Wed, 9 Apr 2025 00:06:34 +0300 Subject: [PATCH 17/17] update to the latest litehtml --- litehtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litehtml b/litehtml index 2a89d5e..1274232 160000 --- a/litehtml +++ b/litehtml @@ -1 +1 @@ -Subproject commit 2a89d5ed4bd866690326ab5b2255be8cc1cb0721 +Subproject commit 127423217120ded1b8c03428259e2b2f33228a95