mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 21:47:46 +00:00
Presenter: Rearchitect on top of LibWeb
This patch replaces the bespoke rendering engine in Presenter with a simple pipeline that turns presentations into single-page HTML files. The HTML is then loaded into an OutOfProcessWebView. This achieves a number of things, most importantly: - Access to all the CSS features supported by LibWeb - Sandboxed, multi-process rendering The code could be simplified a lot further, but I wanted to get the new architecture in place without changing anything about the file format.
This commit is contained in:
parent
ed3c2cbdf6
commit
3110f5b328
10 changed files with 267 additions and 268 deletions
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
|
||||
* Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "PresenterWidget.h"
|
||||
#include "Presentation.h"
|
||||
#include <AK/Format.h>
|
||||
#include <LibCore/MimeData.h>
|
||||
#include <LibFileSystemAccessClient/Client.h>
|
||||
#include <LibGUI/Action.h>
|
||||
|
@ -15,13 +15,39 @@
|
|||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Orientation.h>
|
||||
|
||||
PresenterWidget::PresenterWidget()
|
||||
{
|
||||
set_min_size(100, 100);
|
||||
set_fill_with_background_color(true);
|
||||
m_web_view = add<WebView::OutOfProcessWebView>();
|
||||
m_web_view->set_frame_thickness(0);
|
||||
m_web_view->set_scrollbars_enabled(false);
|
||||
m_web_view->set_focus_policy(GUI::FocusPolicy::NoFocus);
|
||||
m_web_view->set_content_scales_to_viewport(true);
|
||||
}
|
||||
|
||||
void PresenterWidget::resize_event(GUI::ResizeEvent& event)
|
||||
{
|
||||
Widget::resize_event(event);
|
||||
|
||||
if (!m_current_presentation)
|
||||
return;
|
||||
|
||||
auto normative_size = m_current_presentation->normative_size().to_type<float>();
|
||||
float widget_ratio = static_cast<float>(event.size().width()) / static_cast<float>(event.size().height());
|
||||
float wh_ratio = normative_size.width() / normative_size.height();
|
||||
|
||||
Gfx::IntRect rect;
|
||||
if (widget_ratio >= wh_ratio) {
|
||||
rect.set_width(static_cast<int>(ceilf(static_cast<float>(event.size().height()) * wh_ratio)));
|
||||
rect.set_height(event.size().height());
|
||||
} else {
|
||||
float hw_ratio = normative_size.height() / normative_size.width();
|
||||
rect.set_width(event.size().width());
|
||||
rect.set_height(static_cast<int>(ceilf(static_cast<float>(event.size().width()) * hw_ratio)));
|
||||
}
|
||||
m_web_view->set_relative_rect(rect.centered_within(this->rect()));
|
||||
}
|
||||
|
||||
ErrorOr<void> PresenterWidget::initialize_menubar()
|
||||
|
@ -44,15 +70,13 @@ ErrorOr<void> PresenterWidget::initialize_menubar()
|
|||
auto next_slide_action = GUI::Action::create("&Next", { KeyCode::Key_Right }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-forward.png"sv)), [this](auto&) {
|
||||
if (m_current_presentation) {
|
||||
m_current_presentation->next_frame();
|
||||
outln("Switched forward to slide {} frame {}", m_current_presentation->current_slide_number(), m_current_presentation->current_frame_in_slide_number());
|
||||
update();
|
||||
update_web_view();
|
||||
}
|
||||
});
|
||||
auto previous_slide_action = GUI::Action::create("&Previous", { KeyCode::Key_Left }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-back.png"sv)), [this](auto&) {
|
||||
if (m_current_presentation) {
|
||||
m_current_presentation->previous_frame();
|
||||
outln("Switched backward to slide {} frame {}", m_current_presentation->current_slide_number(), m_current_presentation->current_frame_in_slide_number());
|
||||
update();
|
||||
update_web_view();
|
||||
}
|
||||
});
|
||||
TRY(presentation_menu.try_add_action(next_slide_action));
|
||||
|
@ -64,25 +88,31 @@ ErrorOr<void> PresenterWidget::initialize_menubar()
|
|||
this->window()->set_fullscreen(true);
|
||||
})));
|
||||
TRY(presentation_menu.try_add_action(GUI::Action::create("Present From First &Slide", { KeyCode::Key_F5 }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/play.png"sv)), [this](auto&) {
|
||||
if (m_current_presentation)
|
||||
if (m_current_presentation) {
|
||||
m_current_presentation->go_to_first_slide();
|
||||
update_web_view();
|
||||
}
|
||||
this->window()->set_fullscreen(true);
|
||||
})));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void PresenterWidget::update_web_view()
|
||||
{
|
||||
m_web_view->run_javascript(DeprecatedString::formatted("goto({}, {})", m_current_presentation->current_slide_number(), m_current_presentation->current_frame_in_slide_number()));
|
||||
}
|
||||
|
||||
void PresenterWidget::set_file(StringView file_name)
|
||||
{
|
||||
auto presentation = Presentation::load_from_file(file_name, *window());
|
||||
auto presentation = Presentation::load_from_file(file_name);
|
||||
if (presentation.is_error()) {
|
||||
GUI::MessageBox::show_error(window(), DeprecatedString::formatted("The presentation \"{}\" could not be loaded.\n{}", file_name, presentation.error()));
|
||||
} else {
|
||||
m_current_presentation = presentation.release_value();
|
||||
window()->set_title(DeprecatedString::formatted(title_template, m_current_presentation->title(), m_current_presentation->author()));
|
||||
set_min_size(m_current_presentation->normative_size());
|
||||
// This will apply the new minimum size.
|
||||
update();
|
||||
m_web_view->load_html(MUST(m_current_presentation->render()), "presenter://slide.html"sv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,24 +144,19 @@ void PresenterWidget::keydown_event(GUI::KeyEvent& event)
|
|||
}
|
||||
}
|
||||
|
||||
void PresenterWidget::paint_event([[maybe_unused]] GUI::PaintEvent& event)
|
||||
void PresenterWidget::paint_event(GUI::PaintEvent& event)
|
||||
{
|
||||
GUI::Painter painter(*this);
|
||||
painter.clear_rect(event.rect(), Gfx::Color::Black);
|
||||
}
|
||||
|
||||
void PresenterWidget::second_paint_event(GUI::PaintEvent& event)
|
||||
{
|
||||
if (!m_current_presentation)
|
||||
return;
|
||||
auto normative_size = m_current_presentation->normative_size();
|
||||
// Choose an aspect-correct size which doesn't exceed actual widget dimensions.
|
||||
auto width_corresponding_to_height = height() * normative_size.aspect_ratio();
|
||||
auto dimension_to_preserve = (width_corresponding_to_height > width()) ? Orientation::Horizontal : Orientation::Vertical;
|
||||
auto display_size = size().match_aspect_ratio(normative_size.aspect_ratio(), dimension_to_preserve);
|
||||
|
||||
GUI::Painter painter { *this };
|
||||
auto clip_rect = Gfx::IntRect::centered_at({ width() / 2, height() / 2 }, display_size);
|
||||
painter.clear_clip_rect();
|
||||
// FIXME: This currently leaves a black border when the window aspect ratio doesn't match.
|
||||
// Figure out a way to apply the background color here as well.
|
||||
painter.add_clip_rect(clip_rect);
|
||||
|
||||
m_current_presentation->paint(painter);
|
||||
GUI::Painter painter(*this);
|
||||
painter.add_clip_rect(event.rect());
|
||||
painter.draw_text(m_web_view->relative_rect(), m_current_presentation->current_slide().title(), Gfx::TextAlignment::BottomCenter);
|
||||
}
|
||||
|
||||
void PresenterWidget::drag_enter_event(GUI::DragEvent& event)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue