From 81fe2250a70c892eeb195725117d566a042e5c93 Mon Sep 17 00:00:00 2001 From: s-ol Date: Mon, 2 Dec 2019 15:32:42 +0100 Subject: add draft janet interface for livecoding --- CMakeScripts/DefineDependsandFlags.cmake | 3 +- src/livecode/CMakeLists.txt | 8 +- src/livecode/api.cpp | 289 --------------------------- src/livecode/api.h | 101 ---------- src/livecode/api/api.h | 34 ++++ src/livecode/api/context.cpp | 21 ++ src/livecode/api/input.cpp | 113 +++++++++++ src/livecode/api/point.cpp | 298 ++++++++++++++++++++++++++++ src/livecode/context.cpp | 327 +++++++++++++++++++++++++++++++ src/livecode/context.h | 111 +++++++++++ src/livecode/input.cpp | 12 +- src/livecode/input.h | 8 +- src/livecode/janet-2geom | 1 - src/ui/toolbar/livecode-toolbar.cpp | 5 +- src/ui/tools/livecode-tool.cpp | 19 +- src/ui/tools/livecode-tool.h | 6 +- 16 files changed, 941 insertions(+), 415 deletions(-) delete mode 100644 src/livecode/api.cpp delete mode 100644 src/livecode/api.h create mode 100644 src/livecode/api/api.h create mode 100644 src/livecode/api/context.cpp create mode 100644 src/livecode/api/input.cpp create mode 100644 src/livecode/api/point.cpp create mode 100644 src/livecode/context.cpp create mode 100644 src/livecode/context.h delete mode 160000 src/livecode/janet-2geom diff --git a/CMakeScripts/DefineDependsandFlags.cmake b/CMakeScripts/DefineDependsandFlags.cmake index 4d01f3255..b6e415e07 100644 --- a/CMakeScripts/DefineDependsandFlags.cmake +++ b/CMakeScripts/DefineDependsandFlags.cmake @@ -88,7 +88,8 @@ pkg_check_modules(INKSCAPE_DEP REQUIRED fontconfig gsl gmodule-2.0 - libsoup-2.4>=2.42) + libsoup-2.4>=2.42 + janet) sanitize_ldflags_for_libs(INKSCAPE_DEP_LDFLAGS) list(APPEND INKSCAPE_LIBS ${INKSCAPE_DEP_LDFLAGS}) diff --git a/src/livecode/CMakeLists.txt b/src/livecode/CMakeLists.txt index ba266998e..96e1574fc 100644 --- a/src/livecode/CMakeLists.txt +++ b/src/livecode/CMakeLists.txt @@ -1,12 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(livecode_SRC - api.cpp + context.cpp input.cpp + api/context.cpp + api/input.cpp + api/point.cpp # HEADERS - api.h + context.h input.h + api/api.h ) add_inkscape_source("${livecode_SRC}") diff --git a/src/livecode/api.cpp b/src/livecode/api.cpp deleted file mode 100644 index 4617968c2..000000000 --- a/src/livecode/api.cpp +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * API for the livecoding script language - * - * Authors: - * Sol Bekic - * 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 deleted file mode 100644 index d2974a01d..000000000 --- a/src/livecode/api.h +++ /dev/null @@ -1,101 +0,0 @@ -// 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 - * Copyright (C) 2019 Authors - * - * Released under GNU GPL v2+, read the file 'COPYING' for more information. - */ - -#include -#include <2geom/point.h> -#include <2geom/rect.h> -#include - -#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 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 : diff --git a/src/livecode/api/api.h b/src/livecode/api/api.h new file mode 100644 index 000000000..94694ca56 --- /dev/null +++ b/src/livecode/api/api.h @@ -0,0 +1,34 @@ +#ifndef JANET_2GEOM_POINT_INCLUDED +#define JANET_2GEOM_POINT_INCLUDED + +#include +#include <2geom/point.h> +#include "livecode/context.h" +#include "livecode/input.h" + +namespace Inkscape { +namespace Livecode { + +/* context.cpp */ +void janet_lib_context(JanetTable *env, Context &context); + +Context &ctx(); + +/* point.cpp */ +void janet_lib_geom_point(JanetTable *env); + +extern const JanetAbstractType geom_point_type; + +Janet janet_wrap_point(Geom::Point const &x); +Janet janet_wrap_point(Geom::IntPoint const &x); +Geom::Point janet_unwrap_point(Janet x); + +/* input.cpp */ +void janet_lib_input(JanetTable *env); + +Janet janet_wrap_inputstate(InputState const &state); + +} +} + +#endif // JANET_2GEOM_POINT_INCLUDED diff --git a/src/livecode/api/context.cpp b/src/livecode/api/context.cpp new file mode 100644 index 000000000..43c6881d1 --- /dev/null +++ b/src/livecode/api/context.cpp @@ -0,0 +1,21 @@ +#include "livecode/api/api.h" + +namespace Inkscape { +namespace Livecode { + +static Context *_context = nullptr; + +Context &ctx() { + if (!_context) { + janet_panic("Livecode Context uninitialized!!"); + } + + return *_context; +} + +void janet_lib_context(JanetTable *env, Context &context) { + _context = &context; +} + +} +} diff --git a/src/livecode/api/input.cpp b/src/livecode/api/input.cpp new file mode 100644 index 000000000..0fc820bbd --- /dev/null +++ b/src/livecode/api/input.cpp @@ -0,0 +1,113 @@ +#include "livecode/api/api.h" + +#include + +namespace Inkscape { +namespace Livecode { + +extern "C" Janet cfun_mouse_pos(int32_t argc, Janet *argv) { + janet_fixarity(argc, 0); + return janet_wrap_point(ctx().mouse().pos()); +} + +extern "C" Janet cfun_mouse_delta(int32_t argc, Janet *argv) { + janet_fixarity(argc, 0); + return janet_wrap_point(ctx().mouse().delta()); +} + +extern "C" Janet cfun_mouse_ui_pos(int32_t argc, Janet *argv) { + janet_fixarity(argc, 0); + return janet_wrap_point(ctx().mouse().ui_pos()); +} + +extern "C" Janet cfun_mouse_ui_delta(int32_t argc, Janet *argv) { + janet_fixarity(argc, 0); + return janet_wrap_point(ctx().mouse().ui_delta()); +} + +Janet janet_wrap_inputstate(InputState const &state) { + switch (state) { + case INPUTSTATE_NONE: + return janet_wrap_nil(); + case INPUTSTATE_HOVER: + return janet_ckeywordv("hover"); + case INPUTSTATE_PRESSED: + return janet_ckeywordv("pressed"); + case INPUTSTATE_HELD: + return janet_ckeywordv("held"); + case INPUTSTATE_RELEASED: + return janet_ckeywordv("released"); + default: + janet_panic("unexpected event value"); + } +} + +extern "C" Janet cfun_mouse_event(int32_t argc, Janet *argv) { + janet_arity(argc, 0, 1); + + guint button = janet_getsize(argv, 1); + if (button < 1 || button > 5) { + janet_panic("expected button to be between 1 and 5"); + } + + return janet_wrap_inputstate(ctx().mouse().event(button)); +} + +#define BUTTONMETHOD(name) \ +extern "C" Janet cfun_mouse_##name(int32_t argc, Janet *argv) { \ + janet_arity(argc, 0, 1); \ + guint button = janet_getsize(argv, 1); \ + if (button < 1 || button > 5) { \ + janet_panic("expected button to be between 1 and 5"); \ + } \ + return janet_wrap_boolean(ctx().mouse().name(button)); \ +} + +BUTTONMETHOD(pressed) +BUTTONMETHOD(held) +BUTTONMETHOD(released) + +const JanetReg it_cfuns[] = { + { + "mouse/pos", cfun_mouse_pos, + "(mouse/pos)\n\nGet the current mouse position as a geom/point." + }, + { + "mouse/delta", cfun_mouse_delta, + "(mouse/pos)\n\nGet the mouse motion since last frame as a geom/point." + }, + { + "mouse/ui-pos", cfun_mouse_ui_pos, + "(mouse/pos)\n\nGet the current mouse position as a geom/point, in UI space." + }, + { + "mouse/ui-delta", cfun_mouse_ui_delta, + "(mouse/pos)\n\nGet the mouse motion since last frame as a geom/point, in UI space." + }, + { + "mouse/event", cfun_mouse_event, + "(mouse/event [button])\n\nGet the current event/status for a mouse button.\n" + "Returns either nil, :pressed, :held or :released." + }, + { + "mouse/pressed", cfun_mouse_pressed, + "(mouse/pressed [button])\n\nCheck whether a mouse button was pressed this frame.\n" + }, + { + "mouse/held", cfun_mouse_held, + "(mouse/held [button])\n\nCheck whether a mouse button was held down this frame.\n" + }, + { + "mouse/released", cfun_mouse_released, + "(mouse/released [button])\n\nCheck whether a mouse button was released this frame.\n" + }, + {NULL, NULL, NULL} +}; + + +void janet_lib_input(JanetTable *env) { + janet_cfuns(env, NULL, it_cfuns); +} + +} +} diff --git a/src/livecode/api/point.cpp b/src/livecode/api/point.cpp new file mode 100644 index 000000000..6eab6af1d --- /dev/null +++ b/src/livecode/api/point.cpp @@ -0,0 +1,298 @@ +#include "livecode/api/api.h" + +#include + +namespace Inkscape { +namespace Livecode { + +extern "C" Janet geom_point_get(void *p, Janet key); +extern "C" void geom_point_set(void *p, Janet key, Janet value); + +extern "C" void geom_point_tostring(void *p, JanetBuffer *buffer) { + std::stringstream stream; + stream << "<2geom/point " << *static_cast(p) << ">"; + janet_buffer_push_cstring(buffer, stream.str().c_str()); +} + + +const JanetAbstractType geom_point_type = { + "2geom/point", + NULL, + NULL, + geom_point_get, + geom_point_set, + NULL, + NULL, + geom_point_tostring +}; + + +Janet janet_wrap_point(Geom::Point const &x) { + Geom::Point *box = (Geom::Point *)janet_abstract(&geom_point_type, sizeof(Geom::Point)); + *box = Geom::Point(x); + return janet_wrap_abstract(box); +} + +Janet janet_wrap_point(Geom::IntPoint const &x) { + Geom::Point *box = (Geom::Point *)janet_abstract(&geom_point_type, sizeof(Geom::Point)); + *box = Geom::Point(x); + return janet_wrap_abstract(box); +} + +Geom::Point janet_unwrap_point(Janet x) { + if (janet_checktype(x, JANET_ABSTRACT)) { + void *abst = janet_unwrap_abstract(x); + if (janet_abstract_type(abst) == &geom_point_type) + return *(Geom::Point *)abst; + } + + janet_panic("expected a geom/point"); +} + +Geom::Point janet_unwrap_point_or_scalar(Janet x) { + if (janet_checktype(x, JANET_ABSTRACT)) { + void *abst = janet_unwrap_abstract(x); + if (janet_abstract_type(abst) == &geom_point_type) + return *(Geom::Point *)abst; + } else if (janet_checktype(x, JANET_NUMBER)) { + double val = janet_unwrap_number(x); + return Geom::Point(val, val); + } + + janet_panic("expected a geom/point"); +} + + +Geom::Point& janet_unwrap_point_ref(Janet x) { + if (janet_checktype(x, JANET_ABSTRACT)) { + void *abst = janet_unwrap_abstract(x); + if (janet_abstract_type(abst) == &geom_point_type) + return *(Geom::Point *)abst; + } + + janet_panic("expected a geom/point"); +} + +extern "C" Janet cfun_geom_point_new(int32_t argc, Janet *argv) { + janet_arity(argc, 0, 2); + double x = 0; + double y = 0; + if (argc == 2) { + x = janet_getnumber(argv, 0); + y = janet_getnumber(argv, 1); + } else if (argc == 1) { + x = janet_getnumber(argv, 0); + y = x; + } + return janet_wrap_point(Geom::Point(x, y)); +} + +const JanetReg it_cfuns[] = { + { + "point", cfun_geom_point_new, + "(geom/point x y)\n\nCreate a Point from x/y coordinate values." + }, + {NULL, NULL, NULL} +}; + +static Janet cfun_geom_point_add(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_abstract(&geom_point_type, sizeof(Geom::Point)); + *box = janet_unwrap_point(argv[0]); + for (int i = 1; i < argc; i++) + *box += janet_unwrap_point(argv[i]); + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_add_mut(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_getabstract(argv, 0, &geom_point_type); + for (int i = 1; i < argc; i++) + *box += janet_unwrap_point(argv[i]); + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_sub(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_abstract(&geom_point_type, sizeof(Geom::Point)); + *box = janet_unwrap_point(argv[0]); + for (int i = 1; i < argc; i++) + *box -= janet_unwrap_point(argv[i]); + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_sub_mut(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_getabstract(argv, 0, &geom_point_type); + for (int i = 1; i < argc; i++) + *box -= janet_unwrap_point(argv[i]); + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_mul(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_abstract(&geom_point_type, sizeof(Geom::Point)); + *box = janet_unwrap_point(argv[0]); + for (int i = 1; i < argc; i++) { + Geom::Point arg = janet_unwrap_point_or_scalar(argv[i]); + (*box)[0] *= arg[0]; + (*box)[1] *= arg[1]; + } + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_mul_mut(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_getabstract(argv, 0, &geom_point_type); + for (int i = 1; i < argc; i++) { + Geom::Point arg = janet_unwrap_point_or_scalar(argv[i]); + (*box)[0] *= arg[0]; + (*box)[1] *= arg[1]; + } + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_div(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_abstract(&geom_point_type, sizeof(Geom::Point)); + *box = janet_unwrap_point(argv[0]); + for (int i = 1; i < argc; i++) { + Geom::Point arg = janet_unwrap_point_or_scalar(argv[i]); + (*box)[0] /= arg[0]; + (*box)[1] /= arg[1]; + } + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_div_mut(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + Geom::Point *box = (Geom::Point *)janet_getabstract(argv, 0, &geom_point_type); + for (int i = 1; i < argc; i++) { + Geom::Point arg = janet_unwrap_point_or_scalar(argv[i]); + (*box)[0] /= arg[0]; + (*box)[1] /= arg[1]; + } + return janet_wrap_abstract(box); +} + +static Janet cfun_geom_point_eq(int32_t argc, Janet *argv) { + janet_fixarity(argc, 2); + Geom::Point& v1 = janet_unwrap_point_ref(argv[0]); + Geom::Point& v2 = janet_unwrap_point_ref(argv[1]); + return janet_wrap_boolean(v1 == v2); +} + +static Janet cfun_geom_point_lt(int32_t argc, Janet *argv) { + janet_fixarity(argc, 2); + Geom::Point& v1 = janet_unwrap_point_ref(argv[0]); + Geom::Point& v2 = janet_unwrap_point_ref(argv[1]); + return janet_wrap_boolean(v1 < v2); +} + +#define CONSTMETHOD(name, method, type) \ +static Janet cfun_geom_point_##name(int32_t argc, Janet *argv) { \ + janet_fixarity(argc, 1); \ + Geom::Point *box = (Geom::Point *)janet_getabstract(argv, 0, &geom_point_type); \ + return janet_wrap_##type(box->method()); \ +} + +CONSTMETHOD(is_zero, isZero, boolean) +CONSTMETHOD(is_finite, isFinite, boolean) +CONSTMETHOD(is_normalized, isNormalized, boolean) + +CONSTMETHOD(x, x, number) +CONSTMETHOD(y, y, number) +CONSTMETHOD(length, length, number) + +CONSTMETHOD(cw, cw, point) +CONSTMETHOD(ccw, ccw, point) +CONSTMETHOD(round, round, point) +CONSTMETHOD(floor, floor, point) +CONSTMETHOD(ceil, ceil, point) +CONSTMETHOD(normalize, normalized, point) + +static Janet cfun_geom_point_normalize_mut(int32_t argc, Janet *argv) { + janet_fixarity(argc, 1); + Geom::Point *box = (Geom::Point *)janet_getabstract(argv, 0, &geom_point_type); + box->normalize(); + return janet_wrap_point(*box); +} + +static JanetMethod geom_point_methods[] = { + {"+", cfun_geom_point_add}, + {"-", cfun_geom_point_sub}, + {"*", cfun_geom_point_mul}, + {"/", cfun_geom_point_div}, + {"<", cfun_geom_point_lt}, + {"==", cfun_geom_point_eq}, + + {"+!", cfun_geom_point_add_mut}, + {"-!", cfun_geom_point_sub_mut}, + {"*!", cfun_geom_point_mul_mut}, + {"/!", cfun_geom_point_div_mut}, + + {"zero?", cfun_geom_point_is_zero}, + {"finite?", cfun_geom_point_is_finite}, + {"normalized?", cfun_geom_point_is_normalized}, + + {"x", cfun_geom_point_x}, + {"y", cfun_geom_point_y}, + {"length", cfun_geom_point_length}, + + {"cw", cfun_geom_point_cw}, + {"ccw", cfun_geom_point_ccw}, + {"round", cfun_geom_point_round}, + {"floor", cfun_geom_point_floor}, + {"ceil", cfun_geom_point_ceil}, + {"normalize", cfun_geom_point_normalize}, + + {"normalize!", cfun_geom_point_normalize_mut}, + + {NULL, NULL} +}; + +extern "C" Janet geom_point_get(void *p, Janet key) { + Geom::Point *box = (Geom::Point *)p; + + if (janet_checktype(key, JANET_KEYWORD)) + return janet_getmethod(janet_unwrap_keyword(key), geom_point_methods); + + if (!janet_checksize(key)) janet_panic("expected size as key"); + + size_t const index = (size_t) janet_unwrap_number(key); + switch (index) { + case 0: + return janet_wrap_number((*box)[0]); + case 1: + return janet_wrap_number((*box)[1]); + default: + return janet_wrap_nil(); + } +} + +extern "C" void geom_point_set(void *p, Janet key, Janet value) { + Geom::Point *box = (Geom::Point *)p; + + if (!janet_checksize(key)) janet_panic("expected size as key"); + if (!janet_checktype(key, JANET_NUMBER)) janet_panic("expected number as value"); + + size_t const index = (size_t) janet_unwrap_number(key); + switch (index) { + case 0: + (*box)[0] = janet_unwrap_number(value); + break; + case 1: + (*box)[1] = janet_unwrap_number(value); + break; + default: + janet_panic("index out of bounds"); + } +} + +void janet_lib_geom_point(JanetTable *env) { + janet_cfuns(env, NULL, it_cfuns); + janet_register_abstract_type(&geom_point_type); +} + +} +} diff --git a/src/livecode/context.cpp b/src/livecode/context.cpp new file mode 100644 index 000000000..a4b59e9b0 --- /dev/null +++ b/src/livecode/context.cpp @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Context for the livecoding script language + * + * Authors: + * Sol Bekic + * Copyright (C) 2019 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#include "svg/stringstream.h" +#include "svg/svg-color.h" +#include "svg/svg.h" +#include "document.h" +#include "display/sp-canvas-item.h" + +#include "livecode/context.h" +#include "livecode/api/api.h" + +namespace Inkscape { +namespace Livecode { + +Context::Context(SPDesktop *desktop) + : desktop(desktop) + , doc_root(nullptr) + , ui_root(nullptr) + , _mouse(*this) + , script_function(nullptr) +{ + janet_init(); + + env = janet_core_env(NULL); + janet_lib_context(env, *this); + janet_lib_input(env); + janet_lib_geom_point(env); +} + +Context::~Context() { + janet_deinit(); + + 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 *Context::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 *Context::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 *Context::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 *Context::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 Context::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 Context::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 Context::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 Context::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 &Context::mouse() { + return _mouse; +} + +void Context::push_event(GdkEvent *event) { + _mouse.push_event(event); +} + +void Context::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); + + if (script_file) { + try { + char *data; + gsize length; + script_file->load_contents(data, length); + + Janet result; + janet_dobytes(env, (uint8_t *)data, length, script_file->get_path().c_str(), &result); + if (janet_checktype(result, JANET_FUNCTION)) { + script_function = janet_unwrap_function(result); + } + } catch (...) { + g_message("error loading file"); + } + } + + if (script_function) { + Janet result; + janet_pcall(script_function, 0, NULL, &result, NULL); + } +} + +void Context::finish_frame() { + doc_root->updateRepr(); + ui_root->updateRepr(); + _mouse.finish_frame(); +} + +void Context::load_script(Glib::ustring const &path) { + g_message("loading file %s", path.c_str()); + script_file = Gio::File::create_for_path(path); +} + +void Context::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 Context::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/context.h b/src/livecode/context.h new file mode 100644 index 000000000..fb6b59ff7 --- /dev/null +++ b/src/livecode/context.h @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef INK_LIVECODE_API_H +#define INK_LIVECODE_API_H + +/* + * Context for the livecoding script language + * + * Authors: + * Sol Bekic + * Copyright (C) 2019 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include <2geom/point.h> +#include <2geom/rect.h> +#include +#include + +#include "xml/repr.h" +#include "desktop.h" +#include "livecode/input.h" + + +class SPDocument; +class SPItem; + +namespace Gio { +class File; +}; + +namespace Inkscape { +namespace Livecode { + +class Mouse; + +class Context { +public: + Context(SPDesktop *desktop); + ~Context(); + + 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(); + void load_script(Glib::ustring const &path); + +private: + SPDesktop *desktop; + SPItem *doc_root, *ui_root; + + JanetTable *env; + JanetFunction *script_function; + Glib::RefPtr script_file; + + Mouse _mouse; + + Glib::ustring hot, active; + std::vector 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 : diff --git a/src/livecode/input.cpp b/src/livecode/input.cpp index 4dab43fc9..b3b505712 100644 --- a/src/livecode/input.cpp +++ b/src/livecode/input.cpp @@ -10,15 +10,15 @@ */ #include "livecode/input.h" -#include "livecode/api.h" +#include "livecode/context.h" #include "desktop.h" namespace Inkscape { namespace Livecode { -Mouse::Mouse(API &api) - : api(api) +Mouse::Mouse(Context &context) + : context(context) { } @@ -40,7 +40,7 @@ void Mouse::push_event(GdkEvent *event) break; } case GDK_MOTION_NOTIFY: { - last_pos = Geom::Point(event->motion.x, event->motion.y) * api.dt2ui(); + last_pos = Geom::Point(event->motion.x, event->motion.y) * context.dt2ui(); break; } default: @@ -74,10 +74,10 @@ bool Mouse::released(guint button) const { } Geom::Point Mouse::pos() const { - return ui_pos() * api.ui2doc(); + return ui_pos() * context.ui2doc(); } Geom::Point Mouse::delta() const { - return ui_delta() * api.ui2doc_vec(); + return ui_delta() * context.ui2doc_vec(); } Geom::Point Mouse::ui_pos() const { diff --git a/src/livecode/input.h b/src/livecode/input.h index 0b9977af8..570415836 100644 --- a/src/livecode/input.h +++ b/src/livecode/input.h @@ -20,7 +20,7 @@ namespace Inkscape { namespace Livecode { -class API; +class Context; enum InputState { INPUTSTATE_NONE, @@ -44,17 +44,17 @@ public: bool released(guint button = 1) const; private: - API& api; + Context& context; guint last_mask, prev_mask; Geom::Point last_pos, prev_pos; - Mouse(API &api); + Mouse(Context &context); void push_event(GdkEvent *event); void finish_frame(); - friend class API; + friend class Context; }; } diff --git a/src/livecode/janet-2geom b/src/livecode/janet-2geom deleted file mode 160000 index a3495ede6..000000000 --- a/src/livecode/janet-2geom +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a3495ede6711d3221780ebbc9adb9a2301206551 diff --git a/src/ui/toolbar/livecode-toolbar.cpp b/src/ui/toolbar/livecode-toolbar.cpp index 81533fec7..ee3a8b3c0 100644 --- a/src/ui/toolbar/livecode-toolbar.cpp +++ b/src/ui/toolbar/livecode-toolbar.cpp @@ -46,7 +46,7 @@ static LivecodeTool *get_livecode_tool() static void update_script_list(GtkListStore *model_files) { gtk_list_store_clear(model_files); auto prefs = Inkscape::Preferences::get(); - Glib::ustring library_path= prefs->getString("/tools/livecode/library_dir"); + Glib::ustring const library_path = prefs->getString("/tools/livecode/library_dir"); Glib::RefPtr library_dir = Gio::File::create_for_path(library_path); auto enumerator = library_dir->enumerate_children(); @@ -72,6 +72,7 @@ namespace UI { namespace Toolbar { LivecodeToolbar::LivecodeToolbar(SPDesktop *desktop) : Toolbar(desktop) + , _freeze(false) { auto prefs = Inkscape::Preferences::get(); @@ -132,7 +133,7 @@ void LivecodeToolbar::active_script_value_changed() LivecodeTool *tool = get_livecode_tool(); if (tool) { - // tool->loadScript(path); + tool->load_script(path); } _freeze = false; diff --git a/src/ui/tools/livecode-tool.cpp b/src/ui/tools/livecode-tool.cpp index 0071f47b3..8b096dbf0 100644 --- a/src/ui/tools/livecode-tool.cpp +++ b/src/ui/tools/livecode-tool.cpp @@ -53,7 +53,7 @@ const std::string LivecodeTool::prefsPath = "/tools/livecode"; LivecodeTool::LivecodeTool() : ToolBase(nullptr, false) - , api(SP_ACTIVE_DESKTOP) + , context(SP_ACTIVE_DESKTOP) { } @@ -83,25 +83,30 @@ bool LivecodeTool::root_handler(GdkEvent* event) ret = ToolBase::root_handler(event); if (!ret) { - api.push_event(event); + context.push_event(event); } return ret; } +void LivecodeTool::load_script(Glib::ustring const &path) +{ + context.load_script(path); +} + static Geom::Point p(100, 100); static Geom::Point a(50, 100), b(120, 300); static Geom::Rect rect(Geom::Point(20, 200), Geom::Point(220, 20)); bool LivecodeTool::handle_tick(Glib::RefPtr const &frame_clock) { - api.setup_frame(); + context.setup_frame(); - api.input_point("p", &p); - api.input_arrow("a->b", &a, &b); - api.input_rect("rect", &rect); + context.input_point("p", &p); + context.input_arrow("a->b", &a, &b); + context.input_rect("rect", &rect); - api.finish_frame(); + context.finish_frame(); return true; } diff --git a/src/ui/tools/livecode-tool.h b/src/ui/tools/livecode-tool.h index fdeb61264..63d85c2e8 100644 --- a/src/ui/tools/livecode-tool.h +++ b/src/ui/tools/livecode-tool.h @@ -19,7 +19,7 @@ #include "display/canvas-text.h" #include "display/canvas-temporary-item.h" #include "ui/control-manager.h" -#include "livecode/api.h" +#include "livecode/context.h" #include #define SP_LIVECODE_CONTEXT(obj) (dynamic_cast((Inkscape::UI::Tools::ToolBase*)obj)) @@ -43,11 +43,13 @@ public: bool root_handler(GdkEvent* event) override; const std::string& getPrefsPath() override; + void load_script(Glib::ustring const &path); + private: bool handle_tick(Glib::RefPtr const &frame_clock); guint tick_callback; - Livecode::API api; + Livecode::Context context; }; } -- cgit v1.2.3