Add new commands to show color bar menus/options

Added QuickCommand class to create commands in the relevant
place (e.g. the ColorBar commands can be created in
ColorBar::registerCommands()).
This commit is contained in:
David Capello 2017-11-30 14:51:13 -03:00
parent a75c8fb1b8
commit 5f33b55a11
28 changed files with 307 additions and 114 deletions

View File

@ -163,6 +163,11 @@ clear = &Delete
unlink = &Unlink
link_cels = &Link Cels
[commands]
ShowPaletteSortOptions = Show Palette Sort Options
ShowPalettePresets = Show Palette Presets
ShowPaletteOptions = Show Palette Options
[document_tab_popup_menu]
duplicate_view = Duplicate &View
open_with_os = &Open with OS

View File

@ -364,6 +364,7 @@ add_library(app-lib
commands/filters/filter_target_buttons.cpp
commands/filters/filter_window.cpp
commands/filters/filter_worker.cpp
commands/quick_command.cpp
console.cpp
context.cpp
context_flags.cpp

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016 David Capello
// Copyright (C) 2016-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -32,6 +32,7 @@ public:
AddColorCommand();
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onEnabled(Context* ctx) override;
void onExecute(Context* ctx) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -31,7 +31,7 @@ protected:
BackgroundFromLayerCommand::BackgroundFromLayerCommand()
: Command("BackgroundFromLayer",
"BackgroundFromLayer",
"Background From Layer",
CmdRecordableFlag)
{
}

View File

@ -29,6 +29,7 @@ public:
Command* clone() const override { return new CancelCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
void onExecute(Context* context) override;

View File

@ -37,6 +37,7 @@ public:
ChangeBrushCommand();
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
void onExecute(Context* context) override;
std::string onGetFriendlyName() const override;

View File

@ -38,6 +38,7 @@ public:
ChangeColorCommand();
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
void onExecute(Context* context) override;
std::string onGetFriendlyName() const override;

View File

@ -334,6 +334,7 @@ public:
Command* clone() const override { return new ChangePixelFormatCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override;
bool onChecked(Context* context) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -32,6 +32,7 @@ public:
Command* clone() const override { return new FramePropertiesCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;

View File

@ -11,6 +11,7 @@
#include "app/app.h"
#include "app/app_menus.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/context.h"
#include "app/file_selector.h"
#include "app/i18n/strings.h"
@ -39,6 +40,8 @@
#include "keyboard_shortcuts.xml.h"
#include <set>
#define KEYBOARD_FILENAME_EXTENSION "aseprite-keys"
namespace app {
@ -503,8 +506,9 @@ private:
deleteAllKeyItems();
// Load keyboard shortcuts
fillList(menus(), AppMenus::instance()->getRootMenu(), 0);
fillList(tools(), App::instance()->toolBox());
fillMenusList(menus(), AppMenus::instance()->getRootMenu(), 0);
fillToolsList(tools(), App::instance()->toolBox());
for (Key* key : *app::KeyboardShortcuts::instance()) {
if (key->type() == KeyType::Tool ||
key->type() == KeyType::Quicktool) {
@ -682,7 +686,7 @@ private:
}
}
void fillList(ListBox* listbox, Menu* menu, int level) {
void fillMenusList(ListBox* listbox, Menu* menu, int level) {
for (auto child : menu->children()) {
if (AppMenuItem* menuItem = dynamic_cast<AppMenuItem*>(child)) {
if (menuItem == AppMenus::instance()->getRecentListMenuitem())
@ -697,12 +701,12 @@ private:
listbox->addChild(keyItem);
if (menuItem->hasSubmenu())
fillList(listbox, menuItem->getSubmenu(), level+1);
fillMenusList(listbox, menuItem->getSubmenu(), level+1);
}
}
}
void fillList(ListBox* listbox, ToolBox* toolbox) {
void fillToolsList(ListBox* listbox, ToolBox* toolbox) {
for (Tool* tool : *toolbox) {
std::string text = tool->getText();
@ -739,6 +743,8 @@ protected:
void onExecute(Context* context) override;
private:
void addMissingKeyboardShortcutsForCommands();
std::string m_search;
};
@ -756,6 +762,8 @@ void KeyboardShortcutsCommand::onLoadParams(const Params& params)
void KeyboardShortcutsCommand::onExecute(Context* context)
{
addMissingKeyboardShortcutsForCommands();
// Here we copy the m_search field because
// KeyboardShortcutsWindow::fillAllLists() modifies this same
// KeyboardShortcutsCommand instance (so m_search will be "")
@ -787,6 +795,40 @@ void KeyboardShortcutsCommand::onExecute(Context* context)
AppMenus::instance()->syncNativeMenuItemKeyShortcuts();
}
void KeyboardShortcutsCommand::addMissingKeyboardShortcutsForCommands()
{
std::set<std::string> commandsAlreadyAdded;
auto keys = app::KeyboardShortcuts::instance();
for (Key* key : *keys) {
if (key->type() != KeyType::Command)
continue;
if (key->params().empty())
commandsAlreadyAdded.insert(key->command()->id());
}
std::vector<std::string> ids;
Commands* commands = Commands::instance();
commands->getAllIds(ids);
for (const std::string& id : ids) {
Command* command = commands->byId(id.c_str());
// Don't add commands that need params (they will be added to
// the list using the list of keyboard shortcuts from gui.xml).
if (command->needsParams())
continue;
auto it = commandsAlreadyAdded.find(command->id());
if (it != commandsAlreadyAdded.end())
continue;
// Create the new Key element in KeyboardShortcuts for this
// command without params.
keys->command(command->id().c_str());
}
}
Command* CommandFactory::createKeyboardShortcutsCommand()
{
return new KeyboardShortcutsCommand;

View File

@ -29,6 +29,7 @@ public:
LayerOpacityCommand();
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;

View File

@ -39,7 +39,7 @@ protected:
LoadMaskCommand::LoadMaskCommand()
: Command("LoadMask",
"LoadMask",
"Load Mask",
CmdRecordableFlag)
{
m_filename = "";

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -34,6 +34,7 @@ namespace app {
gfx::Point getDelta(Context* context) const;
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -38,6 +38,7 @@ public:
Command* clone() const override { return new ScrollCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2015, 2016 David Capello
// Copyright (C) 2015-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016 David Capello
// Copyright (C) 2016-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -21,6 +21,7 @@ public:
Command* clone() const override { return new SetColorSelectorCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onChecked(Context* context) override;
void onExecute(Context* context) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -23,6 +23,7 @@ public:
Command* clone() const override { return new SetInkTypeCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onChecked(Context* context) override;
void onExecute(Context* context) override;

View File

@ -25,6 +25,7 @@ public:
Command* clone() const override { return new TimelineCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
void onExecute(Context* context) override;
bool onChecked(Context* ctx) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -30,6 +30,7 @@ public:
Command* clone() const override { return new ZoomCommand(*this); }
protected:
bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -30,6 +30,11 @@ std::string Command::friendlyName() const
return onGetFriendlyName();
}
bool Command::needsParams() const
{
return onNeedsParams();
}
void Command::loadParams(const Params& params)
{
onLoadParams(params);
@ -62,33 +67,31 @@ void Command::execute(Context* context)
onExecute(context);
}
/**
* Converts specified parameters to class members.
*/
bool Command::onNeedsParams() const
{
// By default a command can be called without params
return false;
}
// Converts specified parameters to class members.
void Command::onLoadParams(const Params& params)
{
// do nothing
}
/**
* Preconditions to execute the command
*/
// Preconditions to execute the command
bool Command::onEnabled(Context* context)
{
return true;
}
/**
* Should the menu-item be checked?
*/
// Should the menu-item be checked?
bool Command::onChecked(Context* context)
{
return false;
}
/**
* Execute the command (after checking the preconditions).
*/
// Execute the command (after checking the preconditions).
void Command::onExecute(Context* context)
{
// Do nothing

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -30,12 +30,14 @@ namespace app {
const std::string& id() const { return m_id; }
std::string friendlyName() const;
bool needsParams() const;
void loadParams(const Params& params);
bool isEnabled(Context* context);
bool isChecked(Context* context);
void execute(Context* context);
protected:
virtual bool onNeedsParams() const;
virtual void onLoadParams(const Params& params);
virtual bool onEnabled(Context* context);
virtual bool onChecked(Context* context);

View File

@ -70,10 +70,17 @@ Command* Commands::byId(const char* id)
return (it != m_commands.end() ? it->second: nullptr);
}
void Commands::add(Command* command)
Commands* Commands::add(Command* command)
{
auto lid = base::string_to_lower(command->id());
m_commands[lid] = command;
return this;
}
void Commands::getAllIds(std::vector<std::string>& ids)
{
for (auto& it : m_commands)
ids.push_back(it.second->id());
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015, 2017 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -12,6 +12,7 @@
#include <map>
#include <string>
#include <vector>
namespace app {
@ -35,7 +36,9 @@ namespace app {
static Commands* instance();
Command* byId(const char* id);
void add(Command* command);
Commands* add(Command* command);
void getAllIds(std::vector<std::string>& ids);
private:
std::map<std::string, Command*> m_commands;

View File

@ -0,0 +1,44 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// 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/commands/quick_command.h"
#include "app/i18n/strings.h"
namespace app {
QuickCommand::QuickCommand(const char* id, std::function<void()> execute)
: Command(id, id, CmdUIOnlyFlag)
, m_execute(execute)
{
}
QuickCommand::~QuickCommand()
{
}
QuickCommand* QuickCommand::clone() const
{
return new QuickCommand(*this);
}
void QuickCommand::onExecute(Context* context)
{
m_execute();
}
std::string QuickCommand::onGetFriendlyName() const
{
std::string id = "commands.";
id += this->id();
return Strings::instance()->translate(id.c_str());
}
} // namespace app

View File

@ -0,0 +1,33 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_COMMANDS_QUICK_COMMAND_H_INCLUDED
#define APP_COMMANDS_QUICK_COMMAND_H_INCLUDED
#pragma once
#include "app/commands/command.h"
#include <functional>
namespace app {
class QuickCommand : public Command {
public:
QuickCommand(const char* id, std::function<void()> execute);
~QuickCommand();
QuickCommand* clone() const override;
protected:
void onExecute(Context* context) override;
std::string onGetFriendlyName() const override;
std::function<void()> m_execute;
};
} // namespace app
#endif

View File

@ -64,6 +64,8 @@ void Context::executeCommand(Command* command, const Params& params)
try {
m_flags.update(this);
ASSERT(!command->needsParams() || !params.empty());
command->loadParams(params);
CommandExecutionEvent ev(command);

View File

@ -24,6 +24,7 @@
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/commands/quick_command.h"
#include "app/console.h"
#include "app/context_access.h"
#include "app/document_api.h"
@ -277,6 +278,7 @@ ColorBar::ColorBar(int align)
base::Bind<void>(&ColorBar::setupTooltips, this, tooltipManager));
setEditMode(false);
registerCommands();
}
ColorBar::~ColorBar()
@ -487,92 +489,17 @@ void ColorBar::onPaletteButtonClick()
setEditMode(!inEditMode());
break;
case PalButton::SORT: {
gfx::Rect bounds = m_buttons.getItem(item)->bounds();
Menu menu;
MenuItem
rev("Reverse Colors"),
grd("Gradient"),
hue("Sort by Hue"),
sat("Sort by Saturation"),
bri("Sort by Brightness"),
lum("Sort by Luminance"),
red("Sort by Red"),
grn("Sort by Green"),
blu("Sort by Blue"),
alp("Sort by Alpha"),
asc("Ascending"),
des("Descending");
menu.addChild(&rev);
menu.addChild(&grd);
menu.addChild(new ui::MenuSeparator);
menu.addChild(&hue);
menu.addChild(&sat);
menu.addChild(&bri);
menu.addChild(&lum);
menu.addChild(new ui::MenuSeparator);
menu.addChild(&red);
menu.addChild(&grn);
menu.addChild(&blu);
menu.addChild(&alp);
menu.addChild(new ui::MenuSeparator);
menu.addChild(&asc);
menu.addChild(&des);
if (m_ascending) asc.setSelected(true);
else des.setSelected(true);
rev.Click.connect(base::Bind<void>(&ColorBar::onReverseColors, this));
grd.Click.connect(base::Bind<void>(&ColorBar::onGradient, this));
hue.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::HUE));
sat.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::SATURATION));
bri.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::VALUE));
lum.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::LUMA));
red.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::RED));
grn.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::GREEN));
blu.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::BLUE));
alp.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::ALPHA));
asc.Click.connect(base::Bind<void>(&ColorBar::setAscending, this, true));
des.Click.connect(base::Bind<void>(&ColorBar::setAscending, this, false));
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
case PalButton::SORT:
showPaletteSortOptions();
break;
}
case PalButton::PRESETS: {
if (!m_palettePopup) {
try {
m_palettePopup.reset(new PalettePopup());
}
catch (const std::exception& ex) {
Console::showException(ex);
return;
}
}
if (!m_palettePopup->isVisible()) {
gfx::Rect bounds = m_buttons.getItem(item)->bounds();
m_palettePopup->showPopup(
gfx::Rect(bounds.x, bounds.y+bounds.h,
ui::display_w()/2, ui::display_h()*3/4));
}
else {
m_palettePopup->closeWindow(NULL);
}
case PalButton::PRESETS:
showPalettePresets();
break;
}
case PalButton::OPTIONS: {
Menu* menu = AppMenus::instance()->getPalettePopupMenu();
if (menu) {
gfx::Rect bounds = m_buttons.getItem(item)->bounds();
menu->showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
}
case PalButton::OPTIONS:
showPaletteOptions();
break;
}
}
}
@ -1306,4 +1233,111 @@ void ColorBar::fixColorIndex(ColorButton& colorButton)
}
}
void ColorBar::registerCommands()
{
Commands::instance()
->add(
new QuickCommand(
"ShowPaletteSortOptions",
[this]{ this->showPaletteSortOptions(); }))
->add(
new QuickCommand(
"ShowPalettePresets",
[this]{ this->showPalettePresets(); }))
->add(
new QuickCommand(
"ShowPaletteOptions",
[this]{ this->showPaletteOptions(); }));
}
void ColorBar::showPaletteSortOptions()
{
gfx::Rect bounds = m_buttons.getItem(
static_cast<int>(PalButton::SORT))->bounds();
Menu menu;
MenuItem
rev("Reverse Colors"),
grd("Gradient"),
hue("Sort by Hue"),
sat("Sort by Saturation"),
bri("Sort by Brightness"),
lum("Sort by Luminance"),
red("Sort by Red"),
grn("Sort by Green"),
blu("Sort by Blue"),
alp("Sort by Alpha"),
asc("Ascending"),
des("Descending");
menu.addChild(&rev);
menu.addChild(&grd);
menu.addChild(new ui::MenuSeparator);
menu.addChild(&hue);
menu.addChild(&sat);
menu.addChild(&bri);
menu.addChild(&lum);
menu.addChild(new ui::MenuSeparator);
menu.addChild(&red);
menu.addChild(&grn);
menu.addChild(&blu);
menu.addChild(&alp);
menu.addChild(new ui::MenuSeparator);
menu.addChild(&asc);
menu.addChild(&des);
if (m_ascending) asc.setSelected(true);
else des.setSelected(true);
rev.Click.connect(base::Bind<void>(&ColorBar::onReverseColors, this));
grd.Click.connect(base::Bind<void>(&ColorBar::onGradient, this));
hue.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::HUE));
sat.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::SATURATION));
bri.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::VALUE));
lum.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::LUMA));
red.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::RED));
grn.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::GREEN));
blu.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::BLUE));
alp.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::ALPHA));
asc.Click.connect(base::Bind<void>(&ColorBar::setAscending, this, true));
des.Click.connect(base::Bind<void>(&ColorBar::setAscending, this, false));
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
}
void ColorBar::showPalettePresets()
{
if (!m_palettePopup) {
try {
m_palettePopup.reset(new PalettePopup());
}
catch (const std::exception& ex) {
Console::showException(ex);
return;
}
}
if (!m_palettePopup->isVisible()) {
gfx::Rect bounds = m_buttons.getItem(
static_cast<int>(PalButton::PRESETS))->bounds();
m_palettePopup->showPopup(
gfx::Rect(bounds.x, bounds.y+bounds.h,
ui::display_w()/2, ui::display_h()*3/4));
}
else {
m_palettePopup->closeWindow(NULL);
}
}
void ColorBar::showPaletteOptions()
{
Menu* menu = AppMenus::instance()->getPalettePopupMenu();
if (menu) {
gfx::Rect bounds = m_buttons.getItem(
static_cast<int>(PalButton::OPTIONS))->bounds();
menu->showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
}
}
} // namespace app

View File

@ -141,6 +141,10 @@ namespace app {
int setPaletteEntry(const app::Color& color);
void updateCurrentSpritePalette(const char* operationName);
void setupTooltips(ui::TooltipManager* tooltipManager);
void registerCommands();
void showPaletteSortOptions();
void showPalettePresets();
void showPaletteOptions();
static void fixColorIndex(ColorButton& color);
class ScrollableView : public ui::View {