diff --git a/src/libs/karm-kira/number.cpp b/src/libs/karm-kira/number.cpp new file mode 100644 index 00000000..fde87419 --- /dev/null +++ b/src/libs/karm-kira/number.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +#include "number.h" + +namespace Karm::Kira { + +Ui::Child number(f64 value, Ui::OnChange onChange, f64 step) { + return Ui::hflow( + Ui::button( + [onChange, value, step](auto &n) { + onChange(n, value - step); + }, + Ui::ButtonStyle::subtle(), + Mdi::MINUS + ), + Ui::labelMedium("{}", value) | Ui::insets({0, 4}) | Ui::center(), + Ui::button( + [onChange, value, step](auto &n) { + onChange(n, value + step); + }, + Ui::ButtonStyle::subtle(), + Mdi::PLUS + ) + ) | + Ui::box({ + .borderRadii = 4, + .borderWidth = 1, + .borderFill = Ui::GRAY800, + }) | + Ui::focusable(); +} + +} // namespace Karm::Kira diff --git a/src/libs/karm-kira/number.h b/src/libs/karm-kira/number.h new file mode 100644 index 00000000..039d323b --- /dev/null +++ b/src/libs/karm-kira/number.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include "_prelude.h" + +namespace Karm::Kira { + +Ui::Child number(f64 value, Ui::OnChange onChange, f64 step = 1); + +} // namespace Karm::Kira diff --git a/src/libs/karm-kira/print-dialog.cpp b/src/libs/karm-kira/print-dialog.cpp index b926b088..54c9f075 100644 --- a/src/libs/karm-kira/print-dialog.cpp +++ b/src/libs/karm-kira/print-dialog.cpp @@ -18,7 +18,7 @@ namespace Karm::Kira { struct State { PrintPreview preview; Print::Settings settings = {}; - Vec> pages = preview(settings); + Vec pages = preview(settings); }; struct ChangePaper { @@ -37,12 +37,17 @@ struct ToggleHeaderFooter {}; struct ToggleBackgroundGraphics {}; +struct ChangeScale { + f64 scale; +}; + using Action = Union< ChangePaper, ChangeOrientation, ChangeMargin, ToggleHeaderFooter, - ToggleBackgroundGraphics>; + ToggleBackgroundGraphics, + ChangeScale>; static void reduce(State &s, Action a) { bool shouldUpdatePreview = false; @@ -62,6 +67,9 @@ static void reduce(State &s, Action a) { } else if (a.is()) { s.settings.backgroundGraphics = not s.settings.backgroundGraphics; shouldUpdatePreview = true; + } else if (auto changeScale = a.is()) { + s.settings.scale = clamp(changeScale->scale, 0.1, 10.); + shouldUpdatePreview = true; } if (shouldUpdatePreview) { @@ -111,7 +119,7 @@ Ui::Child _printPaper(State const &s, usize index) { return Ui::stack( Ui::canvas( - s.pages[index], + s.pages[index].content(), { .showBackgroundGraphics = s.settings.backgroundGraphics, } @@ -301,6 +309,15 @@ Ui::Child _printSettings(State const &s) { "Margins"s ), + numberRow( + s.settings.scale, + [](auto &n, f64 scale) { + Model::bubble(n, ChangeScale{scale}); + }, + 0.1, + "Scale"s + ), + checkboxRow( s.settings.headerFooter, [&](auto &n, ...) { diff --git a/src/libs/karm-kira/print-dialog.h b/src/libs/karm-kira/print-dialog.h index 6e457886..6968f8ad 100644 --- a/src/libs/karm-kira/print-dialog.h +++ b/src/libs/karm-kira/print-dialog.h @@ -1,13 +1,13 @@ #pragma once -#include +#include #include #include "_prelude.h" namespace Karm::Kira { -using PrintPreview = SharedFunc>(Print::Settings const &)>; +using PrintPreview = SharedFunc(Print::Settings const &)>; Ui::Child printDialog(PrintPreview preview); diff --git a/src/libs/karm-kira/row.cpp b/src/libs/karm-kira/row.cpp index 740b9712..445177d7 100644 --- a/src/libs/karm-kira/row.cpp +++ b/src/libs/karm-kira/row.cpp @@ -7,6 +7,7 @@ #include "checkbox.h" #include "color-input.h" +#include "number.h" #include "radio.h" #include "row.h" #include "select.h" @@ -158,6 +159,15 @@ Ui::Child colorRow(Gfx::Color c, Ui::OnChange onChange, String title ); } +Ui::Child numberRow(f64 value, Ui::OnChange onChange, f64 step, String title) { + return rowContent( + NONE, + title, + NONE, + number(value, std::move(onChange), step) + ); +} + Ui::Child treeRow(Opt leading, String title, Opt subtitle, Ui::Slot child) { return Ui::state(false, [=, leading = std::move(leading), child = std::move(child)](bool state, auto bind) { return vflow( diff --git a/src/libs/karm-kira/row.h b/src/libs/karm-kira/row.h index 07d1e551..f3f7e94e 100644 --- a/src/libs/karm-kira/row.h +++ b/src/libs/karm-kira/row.h @@ -30,6 +30,8 @@ Ui::Child selectRow(Ui::Child value, Ui::Slots options, String title); Ui::Child colorRow(Gfx::Color color, Ui::OnChange onChange, String title); +Ui::Child numberRow(f64 value, Ui::OnChange onChange, f64 step, String title); + Ui::Child treeRow(Opt leading, String title, Opt subtitle, Ui::Slot child); Ui::Child treeRow(Opt leading, String title, Opt subtitle, Ui::Slots children); diff --git a/src/libs/karm-print/manifest.json b/src/libs/karm-print/manifest.json index e1600493..753253a2 100644 --- a/src/libs/karm-print/manifest.json +++ b/src/libs/karm-print/manifest.json @@ -4,6 +4,7 @@ "type": "lib", "description": "Print documents and images.", "requires": [ - "karm-pdf" + "karm-pdf", + "karm-scene" ] } diff --git a/src/libs/karm-print/page.h b/src/libs/karm-print/page.h new file mode 100644 index 00000000..b8b54f37 --- /dev/null +++ b/src/libs/karm-print/page.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include "paper.h" +#include "printer.h" + +namespace Karm::Print { + +struct Page { + PaperStock _paper; + Strong _content; + + Page(PaperStock paper, Opt> content = NONE) + : _paper(paper), _content(content ? content.take() : makeStrong()) {} + + Strong content() const { + return makeStrong(_paper.size(), _content); + } + + void print(Print::Printer &doc, Scene::PaintOptions o = {.showBackgroundGraphics = false}) { + auto &canvas = doc.beginPage(_paper); + content()->paint(canvas, _paper.size().cast(), o); + } + + void repr(Io::Emit &e) const { + e("(page paper:{} root:{})", _paper, content()); + } +}; + +} // namespace Karm::Print diff --git a/src/libs/karm-print/paper.h b/src/libs/karm-print/paper.h index 51e623d6..48be9c3a 100644 --- a/src/libs/karm-print/paper.h +++ b/src/libs/karm-print/paper.h @@ -27,6 +27,10 @@ struct PaperStock { PaperStock landscape() const { return {name, height, width}; } + + void repr(Io::Emit &e) const { + e("(paper {} {}x{})", name, width, height); + } }; struct PaperSeries { @@ -162,7 +166,7 @@ struct Settings { Margins margins = Margins::DEFAULT; Orientation orientation = Orientation::PORTRAIT; - double scale = 1.; + f64 scale = 1.; bool headerFooter = true; bool backgroundGraphics = false; }; diff --git a/src/libs/karm-scene/base.h b/src/libs/karm-scene/base.h index 29451dde..03b23c6e 100644 --- a/src/libs/karm-scene/base.h +++ b/src/libs/karm-scene/base.h @@ -2,7 +2,6 @@ #include #include -#include namespace Karm::Scene { @@ -23,8 +22,6 @@ struct Node { virtual void paint(Gfx::Canvas &, Math::Rectf = Math::Rectf::MAX, PaintOptions = {}) {} - virtual void print(Print::Printer &, PaintOptions = {.showBackgroundGraphics = false}) {} - virtual void repr(Io::Emit &e) const { e("(node z:{})", zIndex); } diff --git a/src/libs/karm-scene/manifest.json b/src/libs/karm-scene/manifest.json index 3b050e2d..b41af16f 100644 --- a/src/libs/karm-scene/manifest.json +++ b/src/libs/karm-scene/manifest.json @@ -4,7 +4,6 @@ "type": "lib", "description": "2D scene graph library", "requires": [ - "karm-gfx", - "karm-print" + "karm-gfx" ] } diff --git a/src/libs/karm-scene/page.h b/src/libs/karm-scene/page.h deleted file mode 100644 index b94eb47b..00000000 --- a/src/libs/karm-scene/page.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "stack.h" - -namespace Karm::Scene { - -struct Page : public Stack { - Print::PaperStock _paper; - - Page(Print::PaperStock paper, Opt transform = NONE) - : Stack(transform), _paper(paper) {} - - void print(Print::Printer &doc, PaintOptions o) override { - Stack::print(doc, o); - paint(doc.beginPage(_paper), _paper.size().cast(), o); - } - - Math::Rectf bound() override { - return _paper.size().cast(); - } - - void repr(Io::Emit &e) const override { - e("(page"); - if (_children) { - e.indentNewline(); - for (auto &child : _children) { - child->repr(e); - e.newline(); - } - e.deindent(); - } - e(")"); - } -}; - -} // namespace Karm::Scene diff --git a/src/libs/karm-scene/stack.h b/src/libs/karm-scene/stack.h index 829c5590..d1a4142b 100644 --- a/src/libs/karm-scene/stack.h +++ b/src/libs/karm-scene/stack.h @@ -6,9 +6,6 @@ namespace Karm::Scene { struct Stack : public Node { Vec> _children; - Opt _transform; - - Stack(Opt transform = NONE) : _transform(transform) {} void add(Strong child) { _children.pushBack(child); @@ -27,6 +24,7 @@ struct Stack : public Node { Math::Rectf rect; for (auto &child : _children) rect = rect.mergeWith(child->bound()); + return rect; } @@ -34,21 +32,8 @@ struct Stack : public Node { if (not bound().colide(r)) return; - if (_transform) { - g.push(); - g.transform(_transform.unwrap()); - } - for (auto &child : _children) child->paint(g, r, o); - - if (_transform) - g.pop(); - } - - void print(Print::Printer &p, PaintOptions o) override { - for (auto &child : _children) - child->print(p, o); } void repr(Io::Emit &e) const override { diff --git a/src/libs/karm-scene/transform.h b/src/libs/karm-scene/transform.h new file mode 100644 index 00000000..4af3a7bc --- /dev/null +++ b/src/libs/karm-scene/transform.h @@ -0,0 +1,40 @@ +#pragma once + +#include "base.h" + +namespace Karm::Scene { + +struct Transform : public Node { + Strong _content; + Math::Trans2f _transform; + + Transform(Strong content, Math::Trans2f transform) + : _content(content), _transform(transform) {} + + void prepare() override { + _content->prepare(); + } + + Math::Rectf bound() override { + return _transform + .inverse() + .apply(_content->bound()) + .bound(); + } + + void paint(Gfx::Canvas &g, Math::Rectf r, PaintOptions o) override { + if (not bound().colide(r)) + return; + + g.push(); + g.transform(_transform); + _content->paint(g, r, o); + g.pop(); + } + + void repr(Io::Emit &e) const override { + e("(transform transform:{} content:{})", _transform, _content); + } +}; + +} // namespace Karm::Scene diff --git a/src/libs/karm-scene/viewbox.h b/src/libs/karm-scene/viewbox.h new file mode 100644 index 00000000..0e48634f --- /dev/null +++ b/src/libs/karm-scene/viewbox.h @@ -0,0 +1,39 @@ + +#include "base.h" + +namespace Karm::Scene { + +struct Viewbox : public Node { + Strong _content; + Math::Rectf _viewbox; + + Viewbox(Math::Rectf viewbox, Strong content) + : _content(content), _viewbox(viewbox) {} + + void prepare() override { + _content->prepare(); + } + + Math::Rectf bound() override { + return _viewbox.size(); + } + + void paint(Gfx::Canvas &g, Math::Rectf r, PaintOptions o) override { + if (not bound().colide(r)) + return; + + g.push(); + g.origin(_viewbox.xy); + g.clip(_viewbox); + + _content->paint(g, r, o); + + g.pop(); + } + + void repr(Io::Emit &e) const override { + e("(viewbox {} content:{})", _viewbox, _content); + } +}; + +} // namespace Karm::Scene diff --git a/src/libs/karm-ui/focus.h b/src/libs/karm-ui/focus.h index 561555ad..ddb69d6e 100644 --- a/src/libs/karm-ui/focus.h +++ b/src/libs/karm-ui/focus.h @@ -60,6 +60,8 @@ struct Focusable : public ProxyNode { } void event(App::Event &e) override { + bool passthrough = false; + if (auto fe = e.is()) { if (fe->type == FocusEvent::STEAL and _focused) { _focused = false; @@ -67,6 +69,7 @@ struct Focusable : public ProxyNode { event(*_child, FocusEvent::LEAVE); } } else if (auto me = e.is()) { + passthrough = true; if (me->type == App::MouseEvent::PRESS) { if (bound().contains(me->pos) and not _focused) { bubble(*this, FocusEvent::STEAL); @@ -81,7 +84,7 @@ struct Focusable : public ProxyNode { } } - if (_focused) + if (_focused or passthrough) ProxyNode::event(e); } }; diff --git a/src/main.cpp b/src/main.cpp index 07771536..f287af18 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -157,7 +157,7 @@ Res<> print(Mime::Url const &, Strong dom, Io::Writer &output, : makeStrong(); for (auto &page : pages) { - page->print( + page.print( *printer, { .showBackgroundGraphics = true, diff --git a/src/web/vaev-driver/print.cpp b/src/web/vaev-driver/print.cpp index 9a7964a4..6c649df5 100644 --- a/src/web/vaev-driver/print.cpp +++ b/src/web/vaev-driver/print.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #include #include #include @@ -250,7 +251,7 @@ static Style::Media _constructMedia(Print::Settings const &settings) { }; } -Vec> print(Markup::Document const &dom, Print::Settings const &settings) { +Vec print(Markup::Document const &dom, Print::Settings const &settings) { auto media = _constructMedia(settings); Style::StyleBook stylebook; @@ -271,7 +272,7 @@ Vec> print(Markup::Document const &dom, Print::Settings cons // MARK: Page and Margins -------------------------------------------------- - Vec> pages; + Vec pages; Style::Computed initialStyle = Style::Computed::initial(); initialStyle.color = Gfx::BLACK; @@ -303,11 +304,7 @@ Vec> print(Markup::Document const &dom, Print::Settings cons auto pageSize = pageRect.size().cast(); - auto scaleMatrix = Math::Trans2f::makeScale(media.resolution.toDppx()); - auto pageScene = pages.emplaceBack( - // FIXME: it can be that specific pages have different dimensions - makeStrong(settings.paper, scaleMatrix) - ); + auto pageStack = makeStrong(); InsetsPx pageMargin = {}; @@ -333,7 +330,7 @@ Vec> print(Markup::Document const &dom, Print::Settings cons contentTree.fc.currSize = pageContent.size(); if (settings.headerFooter and settings.margins != Print::Margins::NONE) - _paintMargins(*pageStyle, pageRect, pageContent, *pageScene); + _paintMargins(*pageStyle, pageRect, pageContent, *pageStack); Layout::Input pageLayoutInput{ .knownSize = {pageContent.width, NONE}, @@ -359,8 +356,10 @@ Vec> print(Markup::Document const &dom, Print::Settings cons .withBreakpointTraverser(Layout::BreakpointTraverser(&prevBreakpoint, &currBreakpoint)) ); - Layout::paint(fragment, *pageScene); - pageScene->prepare(); + Layout::paint(fragment, *pageStack); + pageStack->prepare(); + + pages.emplaceBack(settings.paper, makeStrong(pageStack, Math::Trans2f::makeScale(media.resolution.toDppx()))); if (outReal.completelyLaidOut) break; diff --git a/src/web/vaev-driver/print.h b/src/web/vaev-driver/print.h index 48f172db..b4b488fd 100644 --- a/src/web/vaev-driver/print.h +++ b/src/web/vaev-driver/print.h @@ -1,11 +1,11 @@ #pragma once +#include #include -#include #include namespace Vaev::Driver { -Vec> print(Markup::Document const &dom, Print::Settings const &settings); +Vec print(Markup::Document const &dom, Print::Settings const &settings); } // namespace Vaev::Driver diff --git a/src/web/vaev-driver/render.h b/src/web/vaev-driver/render.h index 931b7241..2ed205e9 100644 --- a/src/web/vaev-driver/render.h +++ b/src/web/vaev-driver/render.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include