lua: Added Dialog:shades{} widget

Also ColorShades is a little more generic now (doesn't depends so much
on the ColorBar).
This commit is contained in:
David Capello 2019-12-16 21:17:12 -03:00
parent 2b1903ee02
commit c9b330ab65
7 changed files with 200 additions and 95 deletions

View File

@ -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<void>([=]() {
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<Dialog>(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<app::Color>(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<const ColorButton*>(widget))
if (auto colorButton = dynamic_cast<const ColorButton*>(widget)) {
push_obj<app::Color>(L, colorButton->getColor());
else if (auto filenameField = dynamic_cast<const FilenameField*>(widget))
}
else if (auto colorShade = dynamic_cast<const ColorShades*>(widget)) {
switch (colorShade->clickType()) {
case ColorShades::ClickEntries: {
Shade shade = colorShade->getShade();
int i = colorShade->getHotEntry();
if (i >= 0 && i < int(shade.size()))
push_obj<app::Color>(L, shade[i]);
else
lua_pushnil(L);
break;
}
case ColorShades::DragAndDropEntries: {
lua_newtable(L);
Shade shade = colorShade->getShade();
for (int i=0; i<int(shade.size()); ++i) {
push_obj<app::Color>(L, shade[i]);
lua_rawseti(L, -2, i+1);
}
break;
}
default:
lua_pushnil(L);
break;
}
}
else if (auto filenameField = dynamic_cast<const FilenameField*>(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<ColorButton*>(widget))
if (auto colorButton = dynamic_cast<ColorButton*>(widget)) {
colorButton->setColor(convert_args_into_color(L, -1));
}
else if (auto colorShade = dynamic_cast<ColorShades*>(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<FilenameField*>(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 }
};

View File

@ -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 <limits>
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<void>(&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.x<bounds.x2(); ++i, box.x += box.w) {
// Make the last box a little bigger to just use all
// available size
if (i == int(colors.size())-1) {
if (bounds.x+bounds.w-box.x <= m_boxSize+m_boxSize/2)
box.w = bounds.x+bounds.w-box.x;
}
if (i == int(colors.size())-1)
box.w = bounds.x2()-box.x;
app::Color color;
@ -324,24 +296,4 @@ void ColorShades::onPaint(ui::PaintEvent& ev)
}
}
void ColorShades::onChangeColorBarSelection()
{
if (!isVisible())
return;
doc::PalettePicks picks;
ColorBar::instance()->getPaletteView()->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

View File

@ -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<void()> 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

View File

@ -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<void>(&InkShadesField::onChangeColorBarSelection, this));
m_button.setFocusStop(false);
m_button.Click.connect(base::Bind<void>(&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<Shade> 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());

View File

@ -49,6 +49,7 @@ namespace app {
}
class BrushSlot;
class ColorBar;
class DitheringSelector;
class GradientTypeSelector;
@ -56,7 +57,8 @@ namespace app {
, public obs::observable<ContextBarObserver>
, public tools::ActiveToolObserver {
public:
ContextBar(ui::TooltipManager* tooltipManager);
ContextBar(ui::TooltipManager* tooltipManager,
ColorBar* colorBar);
~ContextBar();
void updateForActiveTool();

View File

@ -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();

View File

@ -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;