From c9b330ab6550d46e6e0c86e5a2bd25f0db7e250c Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 16 Dec 2019 21:17:12 -0300 Subject: [PATCH] lua: Added Dialog:shades{} widget Also ColorShades is a little more generic now (doesn't depends so much on the ColorBar). --- src/app/script/dialog_class.cpp | 139 +++++++++++++++++++++++++++++--- src/app/ui/color_shades.cpp | 94 ++++++--------------- src/app/ui/color_shades.h | 9 ++- src/app/ui/context_bar.cpp | 45 +++++++++-- src/app/ui/context_bar.h | 4 +- src/app/ui/main_window.cpp | 2 +- src/app/ui/main_window.h | 2 +- 7 files changed, 200 insertions(+), 95 deletions(-) diff --git a/src/app/script/dialog_class.cpp b/src/app/script/dialog_class.cpp index 694c1c157..5a4e65f27 100644 --- a/src/app/script/dialog_class.cpp +++ b/src/app/script/dialog_class.cpp @@ -16,6 +16,7 @@ #include "app/script/engine.h" #include "app/script/luacpp.h" #include "app/ui/color_button.h" +#include "app/ui/color_shades.h" #include "app/ui/expr_entry.h" #include "app/ui/filename_field.h" #include "base/bind.h" @@ -124,8 +125,6 @@ void Dialog_connect_signal(lua_State* L, signal.connect( base::Bind([=]() { - callback(); - // In case that the dialog is hidden, we cannot access to the // global LUA_REGISTRYINDEX to get its reference. if (dlg->showRef == LUA_REFNIL) @@ -137,8 +136,15 @@ void Dialog_connect_signal(lua_State* L, lua_getuservalue(L, -1); lua_rawgeti(L, -1, n); - if (lua_isfunction(L, -1)) { - if (lua_pcall(L, 0, 0, 0)) { + // Use the callback with a special table in the Lua stack to + // send it as parameter to the Lua function in the + // lua_pcall() (that table is like an "event data" parameter + // for the function). + lua_newtable(L); + callback(L); + + if (lua_isfunction(L, -2)) { + if (lua_pcall(L, 1, 0, 0)) { if (const char* s = lua_tostring(L, -1)) App::instance() ->scriptEngine() @@ -146,7 +152,6 @@ void Dialog_connect_signal(lua_State* L, } } else { - ASSERT(false); lua_pop(L, 1); // Pop the value which should have been a function } lua_pop(L, 2); // Pop uservalue & userdata @@ -188,7 +193,7 @@ int Dialog_new(lua_State* L) if (type == LUA_TFUNCTION) { Dialog_connect_signal( L, -2, dlg->window.Close, - [](){ + [](lua_State* L){ // Do nothing }); } @@ -390,7 +395,7 @@ int Dialog_button_base(lua_State* L, T** outputWidget = nullptr) auto dlg = get_obj(L, 1); Dialog_connect_signal( L, 1, widget->Click, - [dlg, widget](){ + [dlg, widget](lua_State* L){ dlg->lastButton = widget; }); closeWindowByDefault = false; @@ -553,6 +558,58 @@ int Dialog_color(lua_State* L) return Dialog_add_widget(L, widget); } +int Dialog_shades(lua_State* L) +{ + Shade colors; + // 'pick' is the default mode anyway + ColorShades::ClickType mode = ColorShades::ClickEntries; + + if (lua_istable(L, 2)) { + int type = lua_getfield(L, 2, "mode"); + if (type == LUA_TSTRING) { + if (const char* modeStr = lua_tostring(L, -1)) { + if (base::utf8_icmp(modeStr, "pick") == 0) + mode = ColorShades::ClickEntries; + else if (base::utf8_icmp(modeStr, "sort") == 0) + mode = ColorShades::DragAndDropEntries; + } + } + lua_pop(L, 1); + + type = lua_getfield(L, 2, "colors"); + if (type == LUA_TTABLE) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + app::Color color = convert_args_into_color(L, -1); + colors.push_back(color); + lua_pop(L, 1); + } + } + lua_pop(L, 1); + } + + auto widget = new ColorShades(colors, mode); + + if (lua_istable(L, 2)) { + int type = lua_getfield(L, 2, "onclick"); + if (type == LUA_TFUNCTION) { + Dialog_connect_signal( + L, 1, widget->Click, + [widget](lua_State* L){ + const int i = widget->getHotEntry(); + const Shade shade = widget->getShade(); + if (i >= 0 && i < int(shade.size())) { + push_obj(L, shade[i]); + lua_setfield(L, -2, "color"); + } + }); + } + lua_pop(L, 1); + } + + return Dialog_add_widget(L, widget); +} + int Dialog_file(lua_State* L) { std::string title = "Open File"; @@ -604,7 +661,7 @@ int Dialog_file(lua_State* L) if (type == LUA_TFUNCTION) { Dialog_connect_signal( L, 1, widget->Change, - [](){ + [](lua_State* L){ // Do nothing }); } @@ -667,12 +724,44 @@ int Dialog_get_data(lua_State* L) } break; default: - if (auto colorButton = dynamic_cast(widget)) + if (auto colorButton = dynamic_cast(widget)) { push_obj(L, colorButton->getColor()); - else if (auto filenameField = dynamic_cast(widget)) + } + else if (auto colorShade = dynamic_cast(widget)) { + switch (colorShade->clickType()) { + + case ColorShades::ClickEntries: { + Shade shade = colorShade->getShade(); + int i = colorShade->getHotEntry(); + if (i >= 0 && i < int(shade.size())) + push_obj(L, shade[i]); + else + lua_pushnil(L); + break; + } + + case ColorShades::DragAndDropEntries: { + lua_newtable(L); + Shade shade = colorShade->getShade(); + for (int i=0; i(L, shade[i]); + lua_rawseti(L, -2, i+1); + } + break; + } + + default: + lua_pushnil(L); + break; + + } + } + else if (auto filenameField = dynamic_cast(widget)) { lua_pushstring(L, filenameField->filename().c_str()); - else + } + else { lua_pushnil(L); + } break; } lua_setfield(L, -2, kv.first.c_str()); @@ -726,8 +815,33 @@ int Dialog_set_data(lua_State* L) } break; default: - if (auto colorButton = dynamic_cast(widget)) + if (auto colorButton = dynamic_cast(widget)) { colorButton->setColor(convert_args_into_color(L, -1)); + } + else if (auto colorShade = dynamic_cast(widget)) { + switch (colorShade->clickType()) { + + case ColorShades::ClickEntries: { + // TODO change hot entry? + break; + } + + case ColorShades::DragAndDropEntries: { + Shade shade; + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + app::Color color = convert_args_into_color(L, -1); + shade.push_back(color); + lua_pop(L, 1); + } + } + colorShade->setShade(shade); + break; + } + + } + } else if (auto filenameField = dynamic_cast(widget)) { if (auto p = lua_tostring(L, -1)) filenameField->setFilename(p); @@ -775,6 +889,7 @@ const luaL_Reg Dialog_methods[] = { { "slider", Dialog_slider }, { "combobox", Dialog_combobox }, { "color", Dialog_color }, + { "shades", Dialog_shades }, { "file", Dialog_file }, { nullptr, nullptr } }; diff --git a/src/app/ui/color_shades.cpp b/src/app/ui/color_shades.cpp index 1251acdfc..a0af5decf 100644 --- a/src/app/ui/color_shades.cpp +++ b/src/app/ui/color_shades.cpp @@ -18,6 +18,7 @@ #include "app/ui/color_bar.h" #include "app/ui/skin/skin_theme.h" #include "base/bind.h" +#include "base/clamp.h" #include "doc/color_mode.h" #include "doc/palette.h" #include "doc/palette_picks.h" @@ -28,20 +29,29 @@ #include "ui/size_hint_event.h" #include "ui/system.h" +#include + namespace app { ColorShades::ColorShades(const Shade& colors, ClickType click) : Widget(ui::kGenericWidget) , m_click(click) , m_shade(colors) + , m_minColors(1) , m_hotIndex(-1) , m_dragIndex(-1) , m_boxSize(12) { - setText("Select colors in the palette"); + setText("No colors"); initTheme(); } +void ColorShades::setMinColors(int minColors) +{ + m_minColors = minColors; + invalidate(); +} + void ColorShades::reverseShadeColors() { std::reverse(m_shade.begin(), m_shade.end()); @@ -76,30 +86,12 @@ doc::Remap* ColorShades::createShadeRemap(bool left) int ColorShades::size() const { - int colors = 0; - for (const auto& color : m_shade) { - if ((color.getIndex() >= 0 && - color.getIndex() < get_current_palette()->size()) || - (m_click == ClickWholeShade)) { - ++colors; - } - } - return colors; + return int(m_shade.size()); } Shade ColorShades::getShade() const { - Shade colors; - for (const auto& color : m_shade) { - if ((color.getIndex() >= 0 && - color.getIndex() < get_current_palette()->size()) || - (m_click == ClickWholeShade)) { - colors.push_back(color); - } - else if (m_click == ClickEntries) - colors.push_back(color); - } - return colors; + return m_shade; } void ColorShades::setShade(const Shade& shade) @@ -109,18 +101,6 @@ void ColorShades::setShade(const Shade& shade) parent()->parent()->layout(); } -void ColorShades::updateShadeFromColorBarPicks() -{ - auto colorBar = ColorBar::instance(); - if (!colorBar) - return; - - doc::PalettePicks picks; - colorBar->getPaletteView()->getSelectedEntries(picks); - if (picks.picks() >= 2) - onChangeColorBarSelection(); -} - void ColorShades::onInitTheme(ui::InitThemeEvent& ev) { Widget::onInitTheme(ev); @@ -142,14 +122,6 @@ bool ColorShades::onProcessMessage(ui::Message* msg) { switch (msg->type()) { - case ui::kOpenMessage: - if (m_click == DragAndDropEntries) { - // TODO This connection should be in the ContextBar - m_conn = ColorBar::instance()->ChangeSelection.connect( - base::Bind(&ColorShades::onChangeColorBarSelection, this)); - } - break; - case ui::kSetCursorMessage: if (hasCapture()) { ui::set_mouse_cursor(ui::kMoveCursor); @@ -226,9 +198,10 @@ bool ColorShades::onProcessMessage(ui::Message* msg) bounds.shrink(3*ui::guiscale()); if (bounds.contains(mousePos)) { - int count = size(); - hot = (mousePos.x - bounds.x) / (m_boxSize*ui::guiscale()); - hot = MID(0, hot, count-1); + int count = std::max(1, size()); + int boxWidth = std::max(1, bounds.w / count); + hot = (mousePos.x - bounds.x) / boxWidth; + hot = base::clamp(hot, 0, count-1); } if (m_hotIndex != hot) { @@ -270,20 +243,19 @@ void ColorShades::onPaint(ui::PaintEvent& ev) bounds.shrink(3*ui::guiscale()); - gfx::Rect box(bounds.x, bounds.y, m_boxSize*ui::guiscale(), bounds.h); - Shade colors = getShade(); - if (colors.size() >= 2) { + if (colors.size() >= m_minColors) { + gfx::Rect box(bounds.x, bounds.y, + bounds.w / std::max(1, int(colors.size())), + bounds.h); gfx::Rect hotBounds; int j = 0; for (int i=0; box.xgetPaletteView()->getSelectedEntries(picks); - - m_shade.resize(picks.picks()); - - int i = 0, j = 0; - for (bool pick : picks) { - if (pick) - m_shade[j++] = app::Color::fromIndex(i); - ++i; - } - - parent()->parent()->layout(); -} - } // namespace app diff --git a/src/app/ui/color_shades.h b/src/app/ui/color_shades.h index b96f67a42..7d6326625 100644 --- a/src/app/ui/color_shades.h +++ b/src/app/ui/color_shades.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2018 David Capello // // This program is distributed under the terms of @@ -28,6 +29,9 @@ namespace app { ColorShades(const Shade& colors, ClickType click); + ClickType clickType() const { return m_click; } + + void setMinColors(int minColors); void reverseShadeColors(); doc::Remap* createShadeRemap(bool left); int size() const; @@ -35,8 +39,6 @@ namespace app { Shade getShade() const; void setShade(const Shade& shade); - void updateShadeFromColorBarPicks(); - int getHotEntry() const { return m_hotIndex; } obs::signal Click; @@ -46,18 +48,17 @@ namespace app { bool onProcessMessage(ui::Message* msg) override; void onSizeHint(ui::SizeHintEvent& ev) override; void onPaint(ui::PaintEvent& ev) override; - void onChangeColorBarSelection(); bool isHotEntryVisible() const { return m_click != ClickWholeShade; } ClickType m_click; Shade m_shade; + int m_minColors; int m_hotIndex; int m_dragIndex; bool m_dropBefore; int m_boxSize; - obs::scoped_connection m_conn; }; } // namespace app diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 202b40166..cb438fb46 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -509,15 +509,19 @@ protected: }; class ContextBar::InkShadesField : public HBox { - public: - InkShadesField() + InkShadesField(ColorBar* colorBar) : m_button(SkinTheme::instance()->parts.iconArrowDown()) , m_shade(Shade(), ColorShades::DragAndDropEntries) , m_loaded(false) { addChild(&m_button); addChild(&m_shade); + m_shade.setText("Select colors in the palette"); + m_shade.setMinColors(2); + m_conn = colorBar->ChangeSelection.connect( + base::Bind(&InkShadesField::onChangeColorBarSelection, this)); + m_button.setFocusStop(false); m_button.Click.connect(base::Bind(&InkShadesField::onShowMenu, this)); @@ -545,7 +549,14 @@ public: } void updateShadeFromColorBarPicks() { - m_shade.updateShadeFromColorBarPicks(); + auto colorBar = ColorBar::instance(); + if (!colorBar) + return; + + doc::PalettePicks picks; + colorBar->getPaletteView()->getSelectedEntries(picks); + if (picks.picks() >= 2) + onChangeColorBarSelection(); } private: @@ -654,10 +665,33 @@ private: } } + void onChangeColorBarSelection() { + if (!m_shade.isVisible()) + return; + + doc::PalettePicks picks; + ColorBar::instance()->getPaletteView()->getSelectedEntries(picks); + + Shade newShade = m_shade.getShade(); + newShade.resize(picks.picks()); + + int i = 0, j = 0; + for (bool pick : picks) { + if (pick) + newShade[j++] = app::Color::fromIndex(i); + ++i; + } + + m_shade.setShade(newShade); + + layout(); + } + IconButton m_button; ColorShades m_shade; std::vector m_shades; bool m_loaded; + obs::scoped_connection m_conn; }; class ContextBar::InkOpacityField : public IntEntry { @@ -1415,7 +1449,8 @@ private: std::string m_filter; }; -ContextBar::ContextBar(TooltipManager* tooltipManager) +ContextBar::ContextBar(TooltipManager* tooltipManager, + ColorBar* colorBar) { addChild(m_selectionOptionsBox = new HBox()); m_selectionOptionsBox->addChild(m_dropPixels = new DropPixelsField()); @@ -1443,7 +1478,7 @@ ContextBar::ContextBar(TooltipManager* tooltipManager) addChild(m_inkType = new InkTypeField(this)); addChild(m_inkOpacityLabel = new Label("Opacity:")); addChild(m_inkOpacity = new InkOpacityField()); - addChild(m_inkShades = new InkShadesField()); + addChild(m_inkShades = new InkShadesField(colorBar)); addChild(m_eyedropperField = new EyedropperField()); diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h index 7882f81c6..3a04d6b6c 100644 --- a/src/app/ui/context_bar.h +++ b/src/app/ui/context_bar.h @@ -49,6 +49,7 @@ namespace app { } class BrushSlot; + class ColorBar; class DitheringSelector; class GradientTypeSelector; @@ -56,7 +57,8 @@ namespace app { , public obs::observable , public tools::ActiveToolObserver { public: - ContextBar(ui::TooltipManager* tooltipManager); + ContextBar(ui::TooltipManager* tooltipManager, + ColorBar* colorBar); ~ContextBar(); void updateForActiveTool(); diff --git a/src/app/ui/main_window.cpp b/src/app/ui/main_window.cpp index 5bdf18cb6..a79dac62b 100644 --- a/src/app/ui/main_window.cpp +++ b/src/app/ui/main_window.cpp @@ -107,10 +107,10 @@ MainWindow::MainWindow() m_menuBar->setMenu(AppMenus::instance()->getRootMenu()); m_notifications = new Notifications(); - m_contextBar = new ContextBar(m_tooltipManager); m_statusBar = new StatusBar(m_tooltipManager); m_colorBar = new ColorBar(colorBarPlaceholder()->align(), m_tooltipManager); + m_contextBar = new ContextBar(m_tooltipManager, m_colorBar); m_toolBar = new ToolBar(); m_tabsBar = new WorkspaceTabs(this); m_workspace = new Workspace(); diff --git a/src/app/ui/main_window.h b/src/app/ui/main_window.h index 68869764a..dd2b1d70b 100644 --- a/src/app/ui/main_window.h +++ b/src/app/ui/main_window.h @@ -114,9 +114,9 @@ namespace app { ui::TooltipManager* m_tooltipManager; MainMenuBar* m_menuBar; - ContextBar* m_contextBar; StatusBar* m_statusBar; ColorBar* m_colorBar; + ContextBar* m_contextBar; ui::Widget* m_toolBar; WorkspaceTabs* m_tabsBar; Mode m_mode;