diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2019-11-28 22:32:59 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2019-11-28 22:32:59 +0000 |
| commit | e892be093dd86fbdabf7a21f9bfd333bcd0839b7 (patch) | |
| tree | 4123d5e6c9792a4f7a648acf2cb975c20e833520 /src/livecode/api | |
| parent | add livecoding tool to toolbar (diff) | |
| download | inkscape-e892be093dd86fbdabf7a21f9bfd333bcd0839b7.tar.gz inkscape-e892be093dd86fbdabf7a21f9bfd333bcd0839b7.zip | |
add api and input modules for the livecoding tool
Diffstat (limited to '')
| -rw-r--r-- | src/livecode/api.cpp | 289 | ||||
| -rw-r--r-- | src/livecode/api.h | 101 |
2 files changed, 390 insertions, 0 deletions
diff --git a/src/livecode/api.cpp b/src/livecode/api.cpp new file mode 100644 index 000000000..4617968c2 --- /dev/null +++ b/src/livecode/api.cpp @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * API for the livecoding script language + * + * Authors: + * Sol Bekic <s+inkscape@s-ol.nu> + * Copyright (C) 2019 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "svg/stringstream.h" +#include "svg/svg-color.h" +#include "svg/svg.h" +#include "document.h" +#include "display/sp-canvas-item.h" + +#include "livecode/api.h" + +namespace Inkscape { +namespace Livecode { + +API::API(SPDesktop *desktop) + : desktop(desktop) + , doc_root(nullptr) + , ui_root(nullptr) + , _mouse(*this) +{ +} + +API::~API() { + if (doc_root) { + doc_root->deleteObject(true, true); + doc_root = nullptr; + } + + if (ui_root) { + ui_root->deleteObject(true, true); + ui_root = nullptr; + } +} + +Inkscape::XML::Node *API::make_rect(Geom::Rect const &rect, SPCSSAttr *css) { + SPDocument *doc = desktop->getDocument(); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::XML::Node *repr = xml_doc->createElement("svg:rect"); + + sp_repr_set_svg_double(repr, "x", rect.left()); + sp_repr_set_svg_double(repr, "y", rect.top()); + sp_repr_set_svg_double(repr, "width", rect.width()); + sp_repr_set_svg_double(repr, "height", rect.height()); + + if (css) { + Glib::ustring css_str; + sp_repr_css_write_string(css, css_str); + sp_repr_css_attr_unref(css); + repr->setAttribute("style", css_str.c_str()); + } + + return repr; +} + +Inkscape::XML::Node *API::make_line(Geom::Point const &p1, Geom::Point const &p2, SPCSSAttr *css) { + SPDocument *doc = desktop->getDocument(); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::XML::Node *repr = xml_doc->createElement("svg:line"); + + sp_repr_set_svg_double(repr, "x1", p1.x()); + sp_repr_set_svg_double(repr, "y1", p1.y()); + sp_repr_set_svg_double(repr, "x2", p2.x()); + sp_repr_set_svg_double(repr, "y2", p2.y()); + + if (css) { + Glib::ustring css_str; + sp_repr_css_write_string(css, css_str); + sp_repr_css_attr_unref(css); + repr->setAttribute("style", css_str.c_str()); + } + + return repr; +} + +Inkscape::XML::Node *API::make_path(Glib::ustring d, Geom::Point const &pos, SPCSSAttr *css) { + SPDocument *doc = desktop->getDocument(); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); + + sp_repr_set_svg_double(repr, "x", pos.x()); + sp_repr_set_svg_double(repr, "y", pos.y()); + repr->setAttribute("d", d.c_str()); + + if (css) { + Glib::ustring css_str; + sp_repr_css_write_string(css, css_str); + sp_repr_css_attr_unref(css); + repr->setAttribute("style", css_str.c_str()); + } + + return repr; +} + +Inkscape::XML::Node *API::make_arrow(Geom::Point const &from, Geom::Point const &to, SPCSSAttr *css) { + SPDocument *doc = desktop->getDocument(); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); + + Geom::Point back = from - to; + back.normalize(); + Geom::Point const cross_a = back.cw() + back * 2.0; + Geom::Point const cross_b = back.cw() + back * 2.0; + + gchar* d = g_strdup_printf("M %f,%f %f,%f l %f,%f M %f,%f l %f,%f", + from.x(), from.y(), + to.x(), to.y(), + cross_a.x(), cross_a.y(), + to.x(), to.y(), + cross_b.x(), cross_b.y()); + repr->setAttribute("d", d); + g_free(d); + + Glib::ustring css_str; + if (css) { + sp_repr_css_write_string(css, css_str); + sp_repr_css_attr_unref(css); + } else { + css_str = "stroke: #000000;"; + } + repr->setAttribute("style", css_str.c_str()); + + return repr; +} + +namespace { +SPCSSAttr *handle_style() { + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, "fill", "#ffffff"); + sp_repr_css_set_property(css, "stroke-width", "0.2"); + sp_repr_css_set_property(css, "stroke", "#000000"); + + return css; +} +} + +bool API::input_point(Glib::ustring const &id, Geom::Point *point) { + auto const half_handle_size = Geom::Point(2, 2); + auto ui_point = *point * desktop->w2d().inverse(); + Geom::Rect const rect(ui_point - half_handle_size, ui_point + half_handle_size); + + SPCSSAttr *css = handle_style(); + guint32 color = 0xffaaaaff; + + if (rect.interiorContains(_mouse.ui_pos())) { + hot = id; + sp_repr_css_set_property(css, "fill", "#ff0000"); + } + + if (hot == id && _mouse.pressed()) { + active = id; + } + + bool change = false; + + if (active == id) { + sp_repr_css_set_property(css, "fill", "#0000ff"); + + auto const delta = _mouse.delta(); + change = !delta.isZero(); + *point += delta; + if (_mouse.released()) { + active = ""; + } + } + + Inkscape::XML::Node *repr = make_rect(rect, css); + draw_ui(id, repr); + return change; +} + +bool API::input_line(Glib::ustring const &id, Geom::Point *p1, Geom::Point *p2) { + bool change = false; + if (input_point(id + "_p1", p1)) { + change = true; + } + if (input_point(id + "_p2", p2)) { + change = true; + } + draw_doc("", make_line(*p1, *p2)); + return change; +} + +bool API::input_arrow(Glib::ustring const &id, Geom::Point *from, Geom::Point *to) { + bool change = false; + if (input_point(id + "_from", from)) { + change = true; + } + if (input_point(id + "_to", to)) { + change = true; + } + draw_doc("", make_line(*from, *to)); + return change; +} + +bool API::input_rect(Glib::ustring const &id, Geom::Rect *rect) { + Geom::Point min = rect->min(); + Geom::Point max = rect->max(); + + bool change = false; + if (input_point(id + "_min", &min)) { + Geom::Point const delta = min - rect->min(); + *rect += delta; + change = true; + } + if (input_point(id + "_max", &max)) { + rect->setMax(max); + change = true; + } + + draw_doc("", make_rect(*rect)); + return change; +} + +Mouse const &API::mouse() { + return _mouse; +} + +void API::push_event(GdkEvent *event) { + _mouse.push_event(event); +} + +void API::setup_frame() { + hot = ""; + + if (doc_root) { + doc_root->deleteObject(true, true); + doc_root = nullptr; + } + + if (ui_root) { + ui_root->deleteObject(true, true); + ui_root = nullptr; + } + + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); + Inkscape::XML::Node *rdoc = xml_doc->createElement("svg:g"); + Inkscape::XML::Node *rui = xml_doc->createElement("svg:g"); + + doc_root = SP_ITEM(desktop->currentLayer()->appendChildRepr(rdoc)); + doc_root->updateRepr(); + Inkscape::GC::release(rdoc); + + ui_root = SP_ITEM(desktop->currentLayer()->appendChildRepr(rui)); + ui_root->doWriteTransform(ui2doc(), nullptr, true); + Inkscape::GC::release(rui); +} + +void API::finish_frame() { + doc_root->updateRepr(); + ui_root->updateRepr(); + _mouse.finish_frame(); +} + +void API::draw_doc(Glib::ustring const &id, Inkscape::XML::Node *repr) { + repr->setAttribute("inkscape:livecode-id", id); + doc_root->appendChildRepr(repr); + Inkscape::GC::release(repr); +} +void API::draw_ui(Glib::ustring const &id, Inkscape::XML::Node *repr) { + repr->setAttribute("inkscape:livecode-id", id); + ui_root->appendChildRepr(repr); + Inkscape::GC::release(repr); +} + +} +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/livecode/api.h b/src/livecode/api.h new file mode 100644 index 000000000..d2974a01d --- /dev/null +++ b/src/livecode/api.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef INK_LIVECODE_API_H +#define INK_LIVECODE_API_H + +/* + * API for the livecoding script language + * + * Authors: + * Sol Bekic <s+inkscape@s-ol.nu> + * Copyright (C) 2019 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <cstddef> +#include <2geom/point.h> +#include <2geom/rect.h> +#include <gdk/gdk.h> + +#include "xml/repr.h" +#include "desktop.h" +#include "livecode/input.h" + + +class SPDocument; +class SPItem; + +namespace Inkscape { +namespace Livecode { + +class Mouse; + +class API { +public: + API(SPDesktop *desktop); + ~API(); + + Inkscape::XML::Node *make_rect(Geom::Rect const &rect, SPCSSAttr *css = nullptr); + Inkscape::XML::Node *make_line(Geom::Point const &p1, Geom::Point const &p2, SPCSSAttr *css = nullptr); + Inkscape::XML::Node *make_path(Glib::ustring d, Geom::Point const &pos = Geom::Point(), SPCSSAttr *css = nullptr); + Inkscape::XML::Node *make_arrow(Geom::Point const &from, Geom::Point const &to, SPCSSAttr *css = nullptr); + + bool input_point(Glib::ustring const &id, Geom::Point *point); + bool input_line(Glib::ustring const &id, Geom::Point *p1, Geom::Point *p2); + bool input_arrow(Glib::ustring const &id, Geom::Point *from, Geom::Point *to); + bool input_rect(Glib::ustring const &id, Geom::Rect *rect); + + void draw_doc(Glib::ustring const &id, Inkscape::XML::Node *item); + void draw_ui(Glib::ustring const &id, Inkscape::XML::Node *item); + + void push_event(GdkEvent *event); + void setup_frame(); + void finish_frame(); + + inline Geom::Affine ui2dt() const { + return doc_root->i2doc_affine(); + } + inline Geom::Affine dt2ui() const { + return ui2dt().inverse(); + } + + inline Geom::Affine ui2doc() const { + return desktop->w2d(); + } + inline Geom::Affine doc2ui() const { + return ui2doc().inverse(); + } + inline Geom::Affine ui2doc_vec() const { + return desktop->w2d().withoutTranslation(); + } + inline Geom::Affine doc2ui_vec() const { + return ui2doc_vec().inverse(); + } + + Mouse const &mouse(); + +private: + SPDesktop *desktop; + SPItem *doc_root, *ui_root; + + Mouse _mouse; + + Glib::ustring hot, active; + std::vector<SPItem *> drawn_items; +}; + +} +} + +#endif // INK_LIVECODE_API_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |
