Merge branch 'canvas' (fix aseprite/api#87)

This commit is contained in:
David Capello 2022-12-16 11:28:13 -03:00
commit 5a5ac179cd
16 changed files with 866 additions and 5 deletions

2
laf

@ -1 +1 @@
Subproject commit ad742295fb92edaa9c51c7cf8c68fe1e7d550357
Subproject commit 817e0c5b1c27f974f0fc298c96bb89f144d5715e

View File

@ -164,7 +164,9 @@ if(ENABLE_SCRIPTING)
script/app_command_object.cpp
script/app_fs_object.cpp
script/app_object.cpp
script/app_theme_object.cpp
script/brush_class.cpp
script/canvas_widget.cpp
script/cel_class.cpp
script/cels_class.cpp
script/color_class.cpp
@ -174,6 +176,7 @@ if(ENABLE_SCRIPTING)
script/events_class.cpp
script/frame_class.cpp
script/frames_class.cpp
script/graphics_context.cpp
script/grid_class.cpp
script/image_class.cpp
script/image_iterator_class.cpp

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -37,6 +37,14 @@ gfx::Color color_utils::blackandwhite_neg(gfx::Color color)
return gfx::rgba(0, 0, 0);
}
app::Color color_utils::color_from_ui(const gfx::Color color)
{
return app::Color::fromRgb(gfx::getr(color),
gfx::getg(color),
gfx::getb(color),
gfx::geta(color));
}
gfx::Color color_utils::color_for_ui(const app::Color& color)
{
gfx::Color c = gfx::ColorNone;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello
//
// This program is distributed under the terms of
@ -25,6 +25,7 @@ namespace app {
gfx::Color blackandwhite(gfx::Color color);
gfx::Color blackandwhite_neg(gfx::Color color);
app::Color color_from_ui(const gfx::Color color);
gfx::Color color_for_ui(const app::Color& color);
doc::color_t color_for_image(const app::Color& color, doc::PixelFormat format);
doc::color_t color_for_image_without_alpha(const app::Color& color, doc::PixelFormat format);

View File

@ -480,6 +480,12 @@ int App_get_events(lua_State* L)
return 1;
}
int App_get_theme(lua_State* L)
{
push_app_theme(L);
return 1;
}
int App_get_activeSprite(lua_State* L)
{
app::Context* ctx = App::instance()->context();
@ -761,6 +767,7 @@ const Property App_properties[] = {
{ "isUIAvailable", App_get_isUIAvailable, nullptr },
{ "defaultPalette", App_get_defaultPalette, App_set_defaultPalette },
{ "events", App_get_events, nullptr },
{ "theme", App_get_theme, nullptr },
{ nullptr, nullptr, nullptr }
};

View File

@ -0,0 +1,107 @@
// Aseprite
// Copyright (c) 2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/color.h"
#include "app/color_utils.h"
#include "app/script/luacpp.h"
#include "app/ui/skin/skin_theme.h"
namespace app {
namespace script {
namespace {
struct Theme { };
struct ThemeDimension { };
struct ThemeColor { };
int ThemeDimension_index(lua_State* L)
{
const char* id = lua_tostring(L, 2);
if (!id)
return luaL_error(L, "id in app.theme.dimension.id must be a string");
#ifdef ENABLE_UI
const int value = skin::SkinTheme::instance()->getDimensionById(id);
lua_pushinteger(L, value);
#else
lua_pushinteger(L, 0);
#endif
return 1;
}
int ThemeColor_index(lua_State* L)
{
const char* id = lua_tostring(L, 2);
if (!id)
return luaL_error(L, "id in app.theme.color.id must be a string");
#ifdef ENABLE_UI
const gfx::Color uiColor = skin::SkinTheme::instance()->getColorById(id);
push_obj<app::Color>(L, color_utils::color_from_ui(uiColor));
#else
push_obj<app::Color>(L, app::Color::fromMask());
#endif
return 1;
}
int Theme_get_dimension(lua_State* L)
{
push_obj<ThemeDimension>(L, ThemeDimension());
return 1;
}
int Theme_get_color(lua_State* L)
{
push_obj<ThemeColor>(L, ThemeColor());
return 1;
}
const luaL_Reg Theme_methods[] = {
{ nullptr, nullptr }
};
const Property Theme_properties[] = {
{ "dimension", Theme_get_dimension, nullptr },
{ "color", Theme_get_color, nullptr },
{ nullptr, nullptr, nullptr }
};
const luaL_Reg ThemeDimension_methods[] = {
{ "__index", ThemeDimension_index },
{ nullptr, nullptr }
};
const luaL_Reg ThemeColor_methods[] = {
{ "__index", ThemeColor_index },
{ nullptr, nullptr }
};
} // anonymous namespace
DEF_MTNAME(Theme);
DEF_MTNAME(ThemeDimension);
DEF_MTNAME(ThemeColor);
void register_theme_classes(lua_State* L)
{
REG_CLASS(L, Theme);
REG_CLASS(L, ThemeDimension);
REG_CLASS(L, ThemeColor);
REG_CLASS_PROPERTIES(L, Theme);
}
void push_app_theme(lua_State* L)
{
push_obj<Theme>(L, Theme());
}
} // namespace script
} // namespace app

View File

@ -0,0 +1,118 @@
// Aseprite
// Copyright (c) 2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#include "app/script/canvas_widget.h"
#include "app/script/graphics_context.h"
#include "app/ui/skin/skin_theme.h"
#include "os/system.h"
#include "ui/message.h"
#include "ui/paint_event.h"
#include "ui/resize_event.h"
#include "ui/size_hint_event.h"
#ifdef ENABLE_UI
namespace app {
namespace script {
// static
ui::WidgetType Canvas::Type()
{
static ui::WidgetType type = ui::kGenericWidget;
if (type == ui::kGenericWidget)
type = ui::register_widget_type();
return type;
}
Canvas::Canvas() : ui::Widget(Type())
{
}
void Canvas::callPaint()
{
if (!m_surface)
return;
os::Paint p;
p.color(bgColor());
m_surface->drawRect(m_surface->bounds(), p);
// Draw only on resize (onPaint we draw the cached m_surface)
GraphicsContext gc(m_surface);
gc.font(AddRef(font()));
Paint(gc);
}
void Canvas::onInitTheme(ui::InitThemeEvent& ev)
{
Widget::onInitTheme(ev);
gfx::Color bg;
if (auto theme = skin::SkinTheme::get(this))
bg = theme->colors.windowFace();
else
bg = gfx::rgba(0, 0, 0);
setBgColor(bg);
}
bool Canvas::onProcessMessage(ui::Message* msg)
{
switch (msg->type()) {
case ui::kMouseMoveMessage: {
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
MouseMove(mouseMsg);
break;
}
case ui::kMouseDownMessage: {
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
MouseDown(mouseMsg);
break;
}
case ui::kMouseUpMessage: {
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
MouseUp(mouseMsg);
break;
}
}
return ui::Widget::onProcessMessage(msg);
}
void Canvas::onResize(ui::ResizeEvent& ev)
{
Widget::onResize(ev);
if (os::instance() && !ev.bounds().isEmpty()) {
const int w = ev.bounds().w;
const int h = ev.bounds().h;
if (!m_surface ||
m_surface->width() != w ||
m_surface->height() != h) {
m_surface = os::instance()->makeSurface(w, h);
callPaint();
}
}
else
m_surface.reset();
}
void Canvas::onPaint(ui::PaintEvent& ev)
{
auto g = ev.graphics();
const gfx::Rect rc = clientBounds();
if (m_surface)
g->drawSurface(m_surface.get(), rc.x, rc.y);
}
} // namespace script
} // namespace app
#endif // ENABLE_UI

View File

@ -0,0 +1,46 @@
// Aseprite
// Copyright (c) 2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_SCRIPT_CANVAS_H_INCLUDED
#define APP_SCRIPT_CANVAS_H_INCLUDED
#pragma once
#include "os/surface.h"
#include "ui/widget.h"
namespace app {
namespace script {
class GraphicsContext;
// The canvas widget of a Dialog() created with Dialog:canvas{ ... }
// This is a generic widget where all its events can be listened.
class Canvas : public ui::Widget {
public:
static ui::WidgetType Type();
Canvas();
void callPaint();
obs::signal<void(GraphicsContext&)> Paint;
obs::signal<void(ui::MouseMessage*)> MouseMove;
obs::signal<void(ui::MouseMessage*)> MouseDown;
obs::signal<void(ui::MouseMessage*)> MouseUp;
private:
void onInitTheme(ui::InitThemeEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onResize(ui::ResizeEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
os::SurfaceRef m_surface;
};
} // namespace script
} // namespace app
#endif

View File

@ -13,7 +13,9 @@
#include "app/color.h"
#include "app/color_utils.h"
#include "app/file_selector.h"
#include "app/script/canvas_widget.h"
#include "app/script/engine.h"
#include "app/script/graphics_context.h"
#include "app/script/luacpp.h"
#include "app/ui/color_button.h"
#include "app/ui/color_shades.h"
@ -30,11 +32,13 @@
#include "ui/grid.h"
#include "ui/label.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/separator.h"
#include "ui/slider.h"
#include "ui/window.h"
#include <map>
#include <stack>
#include <string>
#include <vector>
@ -334,6 +338,9 @@ int Dialog_add_widget(lua_State* L, Widget* widget)
const char* label = nullptr;
std::string id;
bool visible = true;
bool hexpand = true;
// Canvas is vertically expansive by default too
bool vexpand = (widget->type() == Canvas::Type());
// This is to separate different kind of widgets without label in
// different rows.
@ -380,6 +387,15 @@ int Dialog_add_widget(lua_State* L, Widget* widget)
widget->setVisible(visible);
}
lua_pop(L, 1);
// Expand horizontally/vertically, it allows to indicate that a
// specific widget is not expansive (e.g. a canvas with a fixed
// size)
type = lua_getfield(L, 2, "hexpand");
type = lua_getfield(L, 2, "vexpand");
if (type != LUA_TNIL) hexpand = lua_toboolean(L, -2);
if (type != LUA_TNIL) vexpand = lua_toboolean(L, -1);
lua_pop(L, 2);
}
if (label || !dlg->hbox) {
@ -398,11 +414,15 @@ int Dialog_add_widget(lua_State* L, Widget* widget)
auto hbox = new ui::HBox;
if (widget->type() == ui::kButtonWidget)
hbox->enableFlags(ui::HOMOGENEOUS);
dlg->grid.addChildInCell(hbox, 1, 1, ui::HORIZONTAL | ui::TOP);
dlg->grid.addChildInCell(
hbox, 1, 1,
ui::HORIZONTAL | (vexpand ? ui::VERTICAL: ui::TOP));
dlg->hbox = hbox;
}
widget->setExpansive(true);
widget->setExpansive(hexpand);
dlg->hbox->addChild(widget);
lua_pushvalue(L, 1);
@ -872,6 +892,78 @@ int Dialog_file(lua_State* L)
return Dialog_add_widget(L, widget);
}
int Dialog_canvas(lua_State* L)
{
auto widget = new Canvas;
if (lua_istable(L, 2)) {
gfx::Size sz(0, 0);
int type = lua_getfield(L, 2, "width");
if (type != LUA_TNIL) {
sz.w = lua_tointegerx(L, -1, nullptr);
}
lua_pop(L, 1);
type = lua_getfield(L, 2, "height");
if (type != LUA_TNIL) {
sz.h = lua_tointegerx(L, -1, nullptr);
}
lua_pop(L, 1);
widget->setSizeHint(sz);
if (lua_istable(L, 2)) {
int type = lua_getfield(L, 2, "onpaint");
if (type == LUA_TFUNCTION) {
Dialog_connect_signal(
L, 1, widget->Paint,
[](lua_State* L, GraphicsContext& gc) {
push_new<GraphicsContext>(L, std::move(gc));
lua_setfield(L, -2, "context");
});
}
lua_pop(L, 1);
auto mouseCallback =
[](lua_State* L, ui::MouseMessage* msg) {
ASSERT(msg->recipient());
if (!msg->recipient())
return;
lua_pushinteger(L, msg->position().x - msg->recipient()->bounds().x);
lua_setfield(L, -2, "x");
lua_pushinteger(L, msg->position().y - msg->recipient()->bounds().y);
lua_setfield(L, -2, "y");
lua_pushinteger(L, int(msg->button()));
lua_setfield(L, -2, "button");
};
type = lua_getfield(L, 2, "onmousemove");
if (type == LUA_TFUNCTION) {
Dialog_connect_signal(L, 1, widget->MouseMove, mouseCallback);
}
lua_pop(L, 1);
type = lua_getfield(L, 2, "onmousedown");
if (type == LUA_TFUNCTION) {
Dialog_connect_signal(L, 1, widget->MouseDown, mouseCallback);
}
lua_pop(L, 1);
type = lua_getfield(L, 2, "onmouseup");
if (type == LUA_TFUNCTION) {
Dialog_connect_signal(L, 1, widget->MouseUp, mouseCallback);
}
lua_pop(L, 1);
}
}
return Dialog_add_widget(L, widget);
}
int Dialog_modify(lua_State* L)
{
auto dlg = get_obj<Dialog>(L, 1);
@ -1059,6 +1151,27 @@ int Dialog_modify(lua_State* L)
return 1;
}
int Dialog_repaint(lua_State* L)
{
auto dlg = get_obj<Dialog>(L, 1);
std::stack<ui::Widget*> widgets;
widgets.push(&dlg->grid);
while (!widgets.empty()) {
auto child = widgets.top();
widgets.pop();
if (child->type() == Canvas::Type()) {
static_cast<Canvas*>(child)->callPaint();
child->invalidate();
}
for (auto subchild : child->children())
widgets.push(subchild);
}
return 0;
}
int Dialog_get_data(lua_State* L)
{
auto dlg = get_obj<Dialog>(L, 1);
@ -1275,7 +1388,9 @@ const luaL_Reg Dialog_methods[] = {
{ "color", Dialog_color },
{ "shades", Dialog_shades },
{ "file", Dialog_file },
{ "canvas", Dialog_canvas },
{ "modify", Dialog_modify },
{ "repaint", Dialog_repaint },
{ nullptr, nullptr }
};

View File

@ -159,6 +159,7 @@ void register_color_class(lua_State* L);
void register_color_space_class(lua_State* L);
#ifdef ENABLE_UI
void register_dialog_class(lua_State* L);
void register_graphics_context_class(lua_State* L);
#endif
void register_events_class(lua_State* L);
void register_frame_class(lua_State* L);
@ -185,6 +186,7 @@ void register_sprite_class(lua_State* L);
void register_sprites_class(lua_State* L);
void register_tag_class(lua_State* L);
void register_tags_class(lua_State* L);
void register_theme_classes(lua_State* L);
void register_tileset_class(lua_State* L);
void register_tilesets_class(lua_State* L);
void register_tool_class(lua_State* L);
@ -412,6 +414,7 @@ Engine::Engine()
register_color_space_class(L);
#ifdef ENABLE_UI
register_dialog_class(L);
register_graphics_context_class(L);
#endif
register_events_class(L);
register_frame_class(L);
@ -438,6 +441,7 @@ Engine::Engine()
register_sprites_class(L);
register_tag_class(L);
register_tags_class(L);
register_theme_classes(L);
register_tileset_class(L);
register_tilesets_class(L);
register_tool_class(L);

View File

@ -135,6 +135,7 @@ namespace app {
};
void push_app_events(lua_State* L);
void push_app_theme(lua_State* L);
int push_image_iterator_function(lua_State* L, const doc::Image* image, int extraArgIndex);
void push_brush(lua_State* L, const doc::BrushRef& brush);
void push_cel_image(lua_State* L, doc::Cel* cel);

View File

@ -0,0 +1,341 @@
// Aseprite
// Copyright (C) 2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/script/graphics_context.h"
#include "app/color.h"
#include "app/color_utils.h"
#include "app/modules/palettes.h"
#include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/ui/skin/skin_theme.h"
#include "app/util/conversion_to_surface.h"
#include "os/draw_text.h"
#ifdef ENABLE_UI
namespace app {
namespace script {
void GraphicsContext::fillText(const std::string& text, int x, int y)
{
os::draw_text(m_surface.get(), m_font.get(),
text, m_paint.color(), 0, x, y, nullptr);
}
gfx::Size GraphicsContext::measureText(const std::string& text) const
{
return os::draw_text(nullptr, m_font.get(), text,
0, 0, 0, 0, nullptr).size();
}
void GraphicsContext::drawImage(const doc::Image* img, int x, int y)
{
convert_image_to_surface(
img,
get_current_palette(),
m_surface.get(),
0, 0,
x, y,
img->width(), img->height());
}
void GraphicsContext::drawThemeImage(const std::string& partId, const gfx::Point& pt)
{
if (auto theme = skin::SkinTheme::instance()) {
skin::SkinPartPtr part = theme->getPartById(partId);
if (part && part->bitmap(0))
m_surface->drawRgbaSurface(part->bitmap(0), pt.x, pt.y);
}
}
void GraphicsContext::drawThemeRect(const std::string& partId, const gfx::Rect& rc)
{
if (auto theme = skin::SkinTheme::instance()) {
skin::SkinPartPtr part = theme->getPartById(partId);
if (part && part->bitmap(0)) {
ui::Graphics g(nullptr, m_surface, 0, 0);
theme->drawRect(&g, rc, part.get(), true);
}
}
}
void GraphicsContext::stroke()
{
m_paint.style(os::Paint::Stroke);
m_surface->drawPath(m_path, m_paint);
}
void GraphicsContext::fill()
{
m_paint.style(os::Paint::Fill);
m_surface->drawPath(m_path, m_paint);
}
namespace {
int GraphicsContext_gc(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
gc->~GraphicsContext();
return 0;
}
int GraphicsContext_save(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
gc->save();
return 0;
}
int GraphicsContext_restore(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
gc->restore();
return 0;
}
int GraphicsContext_strokeRect(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
const gfx::Rect rc = convert_args_into_rect(L, 2);
gc->strokeRect(rc);
return 0;
}
int GraphicsContext_fillRect(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
const gfx::Rect rc = convert_args_into_rect(L, 2);
gc->fillRect(rc);
return 0;
}
int GraphicsContext_fillText(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
if (const char* text = lua_tostring(L, 2)) {
int x = lua_tointeger(L, 3);
int y = lua_tointeger(L, 4);
gc->fillText(text, x, y);
}
return 0;
}
int GraphicsContext_measureText(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
if (const char* text = lua_tostring(L, 2)) {
push_obj(L, gc->measureText(text));
return 1;
}
return 0;
}
int GraphicsContext_drawImage(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
if (const doc::Image* img = may_get_image_from_arg(L, 2)) {
int x = lua_tointeger(L, 3);
int y = lua_tointeger(L, 4);
gc->drawImage(img, x, y);
}
return 0;
}
int GraphicsContext_drawThemeImage(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
if (const char* id = lua_tostring(L, 2)) {
const gfx::Point pt = convert_args_into_point(L, 3);
gc->drawThemeImage(id, pt);
}
return 0;
}
int GraphicsContext_drawThemeRect(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
if (const char* id = lua_tostring(L, 2)) {
const gfx::Rect rc = convert_args_into_rect(L, 3);
gc->drawThemeRect(id, rc);
}
return 0;
}
int GraphicsContext_beginPath(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
gc->beginPath();
lua_pushvalue(L, 1);
return 1;
}
int GraphicsContext_closePath(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
gc->closePath();
lua_pushvalue(L, 1);
return 1;
}
int GraphicsContext_moveTo(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
float x = lua_tonumber(L, 2);
float y = lua_tonumber(L, 3);
gc->moveTo(x, y);
lua_pushvalue(L, 1);
return 1;
}
int GraphicsContext_lineTo(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
float x = lua_tonumber(L, 2);
float y = lua_tonumber(L, 3);
gc->lineTo(x, y);
lua_pushvalue(L, 1);
return 1;
}
int GraphicsContext_cubicTo(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
float cp1x = lua_tonumber(L, 2);
float cp1y = lua_tonumber(L, 3);
float cp2x = lua_tonumber(L, 4);
float cp2y = lua_tonumber(L, 5);
float x = lua_tonumber(L, 6);
float y = lua_tonumber(L, 7);
gc->cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
lua_pushvalue(L, 1);
return 1;
}
int GraphicsContext_stroke(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
gc->stroke();
lua_pushvalue(L, 1);
return 1;
}
int GraphicsContext_fill(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
gc->fill();
lua_pushvalue(L, 1);
return 1;
}
int GraphicsContext_get_width(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
lua_pushinteger(L, gc->width());
return 1;
}
int GraphicsContext_get_height(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
lua_pushinteger(L, gc->height());
return 1;
}
int GraphicsContext_get_antialias(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
lua_pushboolean(L, gc->antialias());
return 1;
}
int GraphicsContext_set_antialias(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
const bool antialias = lua_toboolean(L, 2);
gc->antialias(antialias);
return 1;
}
int GraphicsContext_get_color(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
push_obj(L, color_utils::color_from_ui(gc->color()));
return 1;
}
int GraphicsContext_set_color(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
const app::Color color = convert_args_into_color(L, 2);
gc->color(color_utils::color_for_ui(color));
return 1;
}
int GraphicsContext_get_strokeWidth(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
lua_pushnumber(L, gc->strokeWidth());
return 1;
}
int GraphicsContext_set_strokeWidth(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
const float strokeWidth = lua_tonumber(L, 2);
gc->strokeWidth(strokeWidth);
return 1;
}
const luaL_Reg GraphicsContext_methods[] = {
{ "__gc", GraphicsContext_gc },
{ "save", GraphicsContext_save },
{ "restore", GraphicsContext_restore },
{ "strokeRect", GraphicsContext_strokeRect },
{ "fillRect", GraphicsContext_fillRect },
{ "fillText", GraphicsContext_fillText },
{ "measureText", GraphicsContext_measureText },
{ "drawImage", GraphicsContext_drawImage },
{ "drawThemeImage", GraphicsContext_drawThemeImage },
{ "drawThemeRect", GraphicsContext_drawThemeRect },
{ "beginPath", GraphicsContext_beginPath },
{ "closePath", GraphicsContext_closePath },
{ "moveTo", GraphicsContext_moveTo },
{ "lineTo", GraphicsContext_lineTo },
{ "cubicTo", GraphicsContext_cubicTo },
{ "stroke", GraphicsContext_stroke },
{ "fill", GraphicsContext_fill },
{ nullptr, nullptr }
};
const Property GraphicsContext_properties[] = {
{ "width", GraphicsContext_get_width, nullptr },
{ "height", GraphicsContext_get_height, nullptr },
{ "antialias", GraphicsContext_get_antialias, GraphicsContext_set_antialias },
{ "color", GraphicsContext_get_color, GraphicsContext_set_color },
{ "strokeWidth", GraphicsContext_get_strokeWidth, GraphicsContext_set_strokeWidth },
{ nullptr, nullptr, nullptr }
};
} // anonymous namespace
DEF_MTNAME(GraphicsContext);
void register_graphics_context_class(lua_State* L)
{
REG_CLASS(L, GraphicsContext);
REG_CLASS_PROPERTIES(L, GraphicsContext);
}
} // namespace script
} // namespace app
#endif // ENABLE_UI

View File

@ -0,0 +1,107 @@
// Aseprite
// Copyright (c) 2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_SCRIPT_GRAPHICS_CONTEXT_H_INCLUDED
#define APP_SCRIPT_GRAPHICS_CONTEXT_H_INCLUDED
#pragma once
#ifdef ENABLE_UI
#include "gfx/path.h"
#include "os/font.h"
#include "os/paint.h"
#include "os/surface.h"
#include <stack>
namespace doc {
class Image;
}
namespace app {
namespace script {
class GraphicsContext {
public:
GraphicsContext(const os::SurfaceRef& surface) : m_surface(surface) { }
GraphicsContext(GraphicsContext&& gc) {
std::swap(m_surface, gc.m_surface);
std::swap(m_paint, gc.m_paint);
std::swap(m_font, gc.m_font);
std::swap(m_path, gc.m_path);
}
os::FontRef font() const { return m_font; }
void font(const os::FontRef& font) { m_font = font; }
int width() const { return m_surface->width(); }
int height() const { return m_surface->height(); }
void save() {
m_saved.push(m_paint);
m_surface->save();
}
void restore() {
if (!m_saved.empty()) {
m_paint = m_saved.top();
m_saved.pop();
m_surface->restore();
}
}
bool antialias() const { return m_paint.antialias(); }
void antialias(bool value) { m_paint.antialias(value); }
gfx::Color color() const { return m_paint.color(); }
void color(gfx::Color color) { m_paint.color(color); }
float strokeWidth() const { return m_paint.strokeWidth(); }
void strokeWidth(float value) { m_paint.strokeWidth(value); }
void strokeRect(const gfx::Rect& rc) {
m_paint.style(os::Paint::Stroke);
m_surface->drawRect(rc, m_paint);
}
void fillRect(const gfx::Rect& rc) {
m_paint.style(os::Paint::Fill);
m_surface->drawRect(rc, m_paint);
}
void fillText(const std::string& text, int x, int y);
gfx::Size measureText(const std::string& text) const;
void drawImage(const doc::Image* img, int x, int y);
void drawThemeImage(const std::string& partId, const gfx::Point& pt);
void drawThemeRect(const std::string& partId, const gfx::Rect& rc);
// Path
void beginPath() { m_path.reset(); }
void closePath() { m_path.close(); }
void moveTo(float x, float y) { m_path.moveTo(x, y); }
void lineTo(float x, float y) { m_path.lineTo(x, y); }
void cubicTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) {
m_path.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
}
void stroke();
void fill();
private:
os::SurfaceRef m_surface = nullptr;
os::Paint m_paint;
os::FontRef m_font;
gfx::Path m_path;
std::stack<os::Paint> m_saved;
};
} // namespace script
} // namespace app
#endif
#endif

View File

@ -538,6 +538,7 @@ bool ComboBoxEntry::onProcessMessage(Message* msg)
kMouseDownMessage,
*mouseMsg,
mouseMsg->positionForDisplay(pick->display()));
mouseMsg2.setRecipient(pick);
mouseMsg2.setDisplay(pick->display());
pick->sendMessage(&mouseMsg2);
return true;

View File

@ -98,6 +98,7 @@ bool IntEntry::onProcessMessage(Message* msg)
MouseMessage mouseMsg2(kMouseDownMessage,
*mouseMsg,
mouseMsg->positionForDisplay(pick->display()));
mouseMsg2.setRecipient(pick);
mouseMsg2.setDisplay(pick->display());
pick->sendMessage(&mouseMsg2);
}

View File

@ -1557,6 +1557,7 @@ bool Widget::onProcessMessage(Message* msg)
MouseMessage mouseMsg2(kMouseDownMessage,
*mouseMsg,
mouseMsg->position());
mouseMsg2.setRecipient(this);
mouseMsg2.setDisplay(mouseMsg->display());
sendMessage(&mouseMsg2);
break;