diff options
author | Kyle Lanmon <kyle.lanmon@gmail.com> | 2023-03-20 22:56:55 -0500 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2023-03-22 10:32:02 +0100 |
commit | fcda397136eaf31ccf60a9c5fba85f62a359ffdd (patch) | |
tree | 99f83b697e06cf6ae4e45beec027ae4d61dee55b | |
parent | 7c312980b08e436e4183e297a33dd515fe818a03 (diff) | |
download | serenity-fcda397136eaf31ccf60a9c5fba85f62a359ffdd.zip |
Presenter: Support multiple frames per slide
-rw-r--r-- | Base/home/anon/Documents/Example Presentation.presenter | 6 | ||||
-rw-r--r-- | Userland/Applications/Presenter/Presentation.cpp | 48 | ||||
-rw-r--r-- | Userland/Applications/Presenter/Presentation.h | 4 | ||||
-rw-r--r-- | Userland/Applications/Presenter/PresenterWidget.cpp | 4 | ||||
-rw-r--r-- | Userland/Applications/Presenter/Slide.cpp | 13 | ||||
-rw-r--r-- | Userland/Applications/Presenter/Slide.h | 8 | ||||
-rw-r--r-- | Userland/Applications/Presenter/SlideObject.cpp | 9 | ||||
-rw-r--r-- | Userland/Applications/Presenter/SlideObject.h | 29 |
8 files changed, 84 insertions, 37 deletions
diff --git a/Base/home/anon/Documents/Example Presentation.presenter b/Base/home/anon/Documents/Example Presentation.presenter index 6e7f4267ab..4cea71b891 100644 --- a/Base/home/anon/Documents/Example Presentation.presenter +++ b/Base/home/anon/Documents/Example Presentation.presenter @@ -11,8 +11,10 @@ "slides": [ { "title": "Introduction", + "frame_count": 2, "objects": [ { + "frame": 0, "type": "text", "text": "Welcome to Presenter!", @@ -25,6 +27,7 @@ "text-alignment": "Center" }, { + "frame": 1, "type": "text", "text": "This program is very cool. It supports:\n - Scaling properly to the window\n - Text\n - Switching between slides\n - that's all for now lol", @@ -40,8 +43,10 @@ }, { "title": "The Second Slide", + "frame_count": 2, "objects": [ { + "frame": 0, "type": "text", "text": "CatDog likes this program!", @@ -53,6 +58,7 @@ "text-alignment": "Center" }, { + "frame": 1, "type": "image", "rect": [50, 50, 200, 100], diff --git a/Userland/Applications/Presenter/Presentation.cpp b/Userland/Applications/Presenter/Presentation.cpp index f8873a7515..7b53b02571 100644 --- a/Userland/Applications/Presenter/Presentation.cpp +++ b/Userland/Applications/Presenter/Presentation.cpp @@ -41,14 +41,20 @@ StringView Presentation::author() const return "Unknown Author"sv; } -bool Presentation::has_a_next_frame() const +bool Presentation::has_next_frame() const { - return m_current_slide < u32(m_slides.size() > 1 ? m_slides.size() - 1 : 0); + if (m_slides.is_empty()) + return false; + if (m_current_slide.value() < m_slides.size() - 1) + return true; + return m_current_frame_in_slide < m_slides[m_current_slide.value()].frame_count() - 1; } -bool Presentation::has_a_previous_frame() const +bool Presentation::has_previous_frame() const { - return m_current_slide > 0u; + if (m_current_slide > 0u) + return true; + return m_current_frame_in_slide > 0u; } void Presentation::next_frame() @@ -65,7 +71,7 @@ void Presentation::previous_frame() m_current_frame_in_slide.sub(1); if (m_current_frame_in_slide.has_overflow()) { m_current_slide.saturating_sub(1); - m_current_frame_in_slide = m_current_slide == 0u ? 0 : current_slide().frame_count() - 1; + m_current_frame_in_slide = current_slide().frame_count() - 1; } } @@ -108,13 +114,15 @@ ErrorOr<NonnullOwnPtr<Presentation>> Presentation::load_from_file(StringView fil auto presentation = Presentation::construct(size, metadata); auto const& slides = maybe_slides.value(); + unsigned i = 0; for (auto const& maybe_slide : slides.values()) { if (!maybe_slide.is_object()) return Error::from_string_view("Slides must be objects"sv); auto const& slide_object = maybe_slide.as_object(); - auto slide = TRY(Slide::parse_slide(slide_object)); + auto slide = TRY(Slide::parse_slide(slide_object, i)); presentation->append_slide(move(slide)); + i++; } return presentation; @@ -163,9 +171,8 @@ ErrorOr<DeprecatedString> Presentation::render() for (size_t i = 0; i < m_slides.size(); ++i) { HTMLElement slide_div; slide_div.tag_name = "div"sv; - TRY(slide_div.style.try_set("display"sv, "none"sv)); TRY(slide_div.attributes.try_set("id"sv, DeprecatedString::formatted("slide{}", i))); - TRY(slide_div.attributes.try_set("class"sv, "slide")); + TRY(slide_div.attributes.try_set("class"sv, "slide hidden"sv)); auto& slide = m_slides[i]; TRY(slide_div.children.try_append(TRY(slide.render(*this)))); main_element.children.append(move(slide_div)); @@ -181,18 +188,29 @@ ErrorOr<DeprecatedString> Presentation::render() width: 100%; height: 100%; } + .hidden { + display: none; + } </style><script> function goto(slideIndex, frameIndex) { - // FIXME: Honor the frameIndex. - let slide; - for (slide of document.getElementsByClassName("slide")) { - slide.style.display = "none"; + for (const slide of document.getElementsByClassName("slide")) { + slide.classList.add("hidden"); + } + for (const frame of document.getElementsByClassName("frame")) { + frame.classList.add("hidden"); + } + + const slide = document.getElementById(`slide${slideIndex}`); + if (slide) slide.classList.remove("hidden"); + + for (let i = 0; i <= frameIndex; i++) { + for (const frame of document.getElementsByClassName(`slide${slideIndex}-frame${i}`)) { + if (frame) frame.classList.remove("hidden"); + } } - if (slide = document.getElementById(`slide${slideIndex}`)) - slide.style.display = "block"; } window.onload = function() { goto(0, 0) } -</script><body> +</script></head><body> )"sv)); TRY(main_element.serialize(builder)); TRY(builder.try_append("</body></html>"sv)); diff --git a/Userland/Applications/Presenter/Presentation.h b/Userland/Applications/Presenter/Presentation.h index 210c9fefd1..47cb07441b 100644 --- a/Userland/Applications/Presenter/Presentation.h +++ b/Userland/Applications/Presenter/Presentation.h @@ -32,8 +32,8 @@ public: unsigned current_slide_number() const { return m_current_slide.value(); } unsigned current_frame_in_slide_number() const { return m_current_frame_in_slide.value(); } - bool has_a_next_frame() const; - bool has_a_previous_frame() const; + bool has_next_frame() const; + bool has_previous_frame() const; void next_frame(); void previous_frame(); void go_to_first_slide(); diff --git a/Userland/Applications/Presenter/PresenterWidget.cpp b/Userland/Applications/Presenter/PresenterWidget.cpp index 52d3bf7719..2620c7d835 100644 --- a/Userland/Applications/Presenter/PresenterWidget.cpp +++ b/Userland/Applications/Presenter/PresenterWidget.cpp @@ -128,8 +128,8 @@ void PresenterWidget::update_web_view() void PresenterWidget::update_slides_actions() { if (m_current_presentation) { - m_next_slide_action->set_enabled(m_current_presentation->has_a_next_frame()); - m_previous_slide_action->set_enabled(m_current_presentation->has_a_previous_frame()); + m_next_slide_action->set_enabled(m_current_presentation->has_next_frame()); + m_previous_slide_action->set_enabled(m_current_presentation->has_previous_frame()); m_full_screen_action->set_enabled(true); m_present_from_first_slide_action->set_enabled(true); } else { diff --git a/Userland/Applications/Presenter/Slide.cpp b/Userland/Applications/Presenter/Slide.cpp index 8e07961a19..b9b8e09213 100644 --- a/Userland/Applications/Presenter/Slide.cpp +++ b/Userland/Applications/Presenter/Slide.cpp @@ -9,17 +9,18 @@ #include "Presentation.h" #include <AK/JsonObject.h> -Slide::Slide(Vector<NonnullRefPtr<SlideObject>> slide_objects, DeprecatedString title) - : m_slide_objects(move(slide_objects)) +Slide::Slide(unsigned frame_count, Vector<NonnullRefPtr<SlideObject>> slide_objects, DeprecatedString title) + : m_frame_count(move(frame_count)) + , m_slide_objects(move(slide_objects)) , m_title(move(title)) { } -ErrorOr<Slide> Slide::parse_slide(JsonObject const& slide_json) +ErrorOr<Slide> Slide::parse_slide(JsonObject const& slide_json, unsigned slide_index) { // FIXME: Use the text with the "title" role for a title, if there is no title given. auto title = slide_json.get_deprecated_string("title"sv).value_or("Untitled slide"); - + auto frame_count = slide_json.get_u32("frame_count"sv).value_or(1); auto maybe_slide_objects = slide_json.get_array("objects"sv); if (!maybe_slide_objects.has_value()) return Error::from_string_view("Slide objects must be an array"sv); @@ -31,11 +32,11 @@ ErrorOr<Slide> Slide::parse_slide(JsonObject const& slide_json) return Error::from_string_view("Slides must be objects"sv); auto const& slide_object_json = maybe_slide_object_json.as_object(); - auto slide_object = TRY(SlideObject::parse_slide_object(slide_object_json)); + auto slide_object = TRY(SlideObject::parse_slide_object(slide_object_json, slide_index)); slide_objects.append(move(slide_object)); } - return Slide { move(slide_objects), title }; + return Slide { frame_count, move(slide_objects), title }; } ErrorOr<HTMLElement> Slide::render(Presentation const& presentation) const diff --git a/Userland/Applications/Presenter/Slide.h b/Userland/Applications/Presenter/Slide.h index f33026c7ef..864cd4e4ec 100644 --- a/Userland/Applications/Presenter/Slide.h +++ b/Userland/Applications/Presenter/Slide.h @@ -14,17 +14,17 @@ // A single slide of a presentation. class Slide final { public: - static ErrorOr<Slide> parse_slide(JsonObject const& slide_json); + static ErrorOr<Slide> parse_slide(JsonObject const& slide_json, unsigned slide_index); - // FIXME: shouldn't be hard-coded to 1. - unsigned frame_count() const { return 1; } + unsigned frame_count() const { return m_frame_count; } StringView title() const { return m_title; } ErrorOr<HTMLElement> render(Presentation const&) const; private: - Slide(Vector<NonnullRefPtr<SlideObject>> slide_objects, DeprecatedString title); + Slide(unsigned frame_count, Vector<NonnullRefPtr<SlideObject>> slide_objects, DeprecatedString title); + unsigned m_frame_count; Vector<NonnullRefPtr<SlideObject>> m_slide_objects; DeprecatedString m_title; }; diff --git a/Userland/Applications/Presenter/SlideObject.cpp b/Userland/Applications/Presenter/SlideObject.cpp index eee1ef8ee8..25d12bf1b6 100644 --- a/Userland/Applications/Presenter/SlideObject.cpp +++ b/Userland/Applications/Presenter/SlideObject.cpp @@ -17,8 +17,9 @@ static DeprecatedString to_css_length(float design_value, Presentation const& pr return DeprecatedString::formatted("{}vw", length_in_vw); } -ErrorOr<NonnullRefPtr<SlideObject>> SlideObject::parse_slide_object(JsonObject const& slide_object_json) +ErrorOr<NonnullRefPtr<SlideObject>> SlideObject::parse_slide_object(JsonObject const& slide_object_json, unsigned slide_index) { + auto frame = slide_object_json.get_u32("frame"sv).value_or(0); auto maybe_type = slide_object_json.get_deprecated_string("type"sv); if (!maybe_type.has_value()) return Error::from_string_view("Slide object must have a type"sv); @@ -26,9 +27,9 @@ ErrorOr<NonnullRefPtr<SlideObject>> SlideObject::parse_slide_object(JsonObject c auto type = maybe_type.value(); RefPtr<SlideObject> object; if (type == "text"sv) - object = TRY(try_make_ref_counted<Text>()); + object = TRY(try_make_ref_counted<Text>(Index { slide_index, frame })); else if (type == "image"sv) - object = TRY(try_make_ref_counted<Image>()); + object = TRY(try_make_ref_counted<Image>(Index { slide_index, frame })); else return Error::from_string_view("Unsupported slide object type"sv); @@ -97,6 +98,7 @@ ErrorOr<HTMLElement> Text::render(Presentation const& presentation) const { HTMLElement div; div.tag_name = "div"sv; + TRY(div.attributes.try_set("class"sv, DeprecatedString::formatted("frame slide{}-frame{}", m_slide_index, m_frame_index))); div.style.set("color"sv, m_color.to_deprecated_string()); div.style.set("font-family"sv, DeprecatedString::formatted("'{}'", m_font_family)); div.style.set("font-size"sv, to_css_length(m_font_size_in_pt * 1.33333333f, presentation)); @@ -125,6 +127,7 @@ ErrorOr<HTMLElement> Image::render(Presentation const& presentation) const HTMLElement image_wrapper; image_wrapper.tag_name = "div"sv; + TRY(image_wrapper.attributes.try_set("class"sv, DeprecatedString::formatted("frame slide{}-frame{}", m_slide_index, m_frame_index))); image_wrapper.children.append(move(img)); image_wrapper.style.set("position"sv, "absolute"sv); image_wrapper.style.set("left"sv, to_css_length(m_rect.left(), presentation)); diff --git a/Userland/Applications/Presenter/SlideObject.h b/Userland/Applications/Presenter/SlideObject.h index 0ace94b7d3..1936f84d46 100644 --- a/Userland/Applications/Presenter/SlideObject.h +++ b/Userland/Applications/Presenter/SlideObject.h @@ -21,19 +21,29 @@ struct HTMLElement { ErrorOr<void> serialize(StringBuilder&) const; }; +struct Index { + unsigned slide; + unsigned frame; +}; // Anything that can be on a slide. class SlideObject : public RefCounted<SlideObject> { public: virtual ~SlideObject() = default; - static ErrorOr<NonnullRefPtr<SlideObject>> parse_slide_object(JsonObject const& slide_object_json); + static ErrorOr<NonnullRefPtr<SlideObject>> parse_slide_object(JsonObject const& slide_object_json, unsigned slide_index); virtual ErrorOr<HTMLElement> render(Presentation const&) const = 0; protected: - SlideObject() = default; + SlideObject(Index index) + : m_frame_index(index.frame) + , m_slide_index(index.slide) + { + } virtual void set_property(StringView name, JsonValue); + unsigned m_frame_index; + unsigned m_slide_index; HashMap<DeprecatedString, JsonValue> m_properties; Gfx::IntRect m_rect; }; @@ -44,7 +54,10 @@ public: virtual ~GraphicsObject() = default; protected: - GraphicsObject() = default; + GraphicsObject(Index index) + : SlideObject(index) + { + } virtual void set_property(StringView name, JsonValue) override; // FIXME: Change the default color based on the color scheme @@ -53,7 +66,10 @@ protected: class Text final : public GraphicsObject { public: - Text() = default; + Text(Index index) + : GraphicsObject(index) + { + } virtual ~Text() = default; private: @@ -69,7 +85,10 @@ private: class Image final : public SlideObject { public: - Image() = default; + Image(Index index) + : SlideObject(index) + { + } virtual ~Image() = default; private: |