mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-17 13:20:45 +00:00
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:
parent
2b1903ee02
commit
c9b330ab65
@ -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 }
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user