Add customizable quicktools keyboard shortcuts to the editor (feature requested by Paul Pridham).

This commit is contained in:
David Capello 2010-10-27 21:02:48 -03:00
parent 3a8711e6a6
commit 20b535bd9c
8 changed files with 199 additions and 110 deletions

View File

@ -148,6 +148,15 @@
<key tool="blur" shortcut="R" />
<key tool="jumble" shortcut="R" />
</tools>
<!-- Editor Quicktools: these are modifiers to select quickly a
tool without changing the current one -->
<quicktools>
<key tool="eyedropper" shortcut="Alt" />
<key tool="move" shortcut="Ctrl" />
<key tool="hand" shortcut="Space" />
</quicktools>
</keyboard>
<menus>

View File

@ -225,8 +225,7 @@ static void proc_one_word(JAccel accel, char* word)
}
}
if (ascii || scancode)
jaccel_add_key(accel, shifts, ascii, scancode);
jaccel_add_key(accel, shifts, ascii, scancode);
}
/* process strings like "<Ctrl+Q> <ESC>" */
@ -467,17 +466,21 @@ bool jaccel_check(JAccel accel, int shifts, int ascii, int scancode)
JI_LIST_FOR_EACH(accel->key_list, link) {
key = (KeyCombo *)link->data;
#ifdef REPORT_KEYS
keycombo_get_string(key, buf);
printf("%3d==%3d %3d==%3d %s==%s ",
key->scancode, scancode, key->ascii, ascii, buf, buf2);
#endif
if (((key->scancode && key->scancode == scancode)
|| (key->ascii && key->ascii == ascii))
&& (key->shifts == (shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG)))) {
&& (key->shifts == (shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG | KB_ALT_FLAG)))) {
#ifdef REPORT_KEYS
printf("true\n");
#endif
return true;
}
#ifdef REPORT_KEYS
@ -487,3 +490,25 @@ bool jaccel_check(JAccel accel, int shifts, int ascii, int scancode)
return false;
}
bool jaccel_check_from_key(JAccel accel)
{
int shifts = 0;
if (key[KEY_LSHIFT]) shifts |= KB_SHIFT_FLAG;
if (key[KEY_RSHIFT]) shifts |= KB_SHIFT_FLAG;
if (key[KEY_LCONTROL]) shifts |= KB_CTRL_FLAG;
if (key[KEY_RCONTROL]) shifts |= KB_CTRL_FLAG;
if (key[KEY_ALT]) shifts |= KB_ALT_FLAG;
JLink link;
JI_LIST_FOR_EACH(accel->key_list, link) {
KeyCombo* keyCombo = (KeyCombo*)link->data;
if ((keyCombo->scancode == 0 || key[keyCombo->scancode]) &&
(keyCombo->shifts == (shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG | KB_ALT_FLAG)))) {
return true;
}
}
return false;
}

View File

@ -20,5 +20,6 @@ bool jaccel_is_empty(JAccel accel);
void jaccel_to_string(JAccel accel, char *buf);
bool jaccel_check(JAccel accel, int shifts, int ascii, int scancode);
bool jaccel_check_from_key(JAccel accel);
#endif

View File

@ -90,7 +90,8 @@ static GfxMode lastWorkingGfxMode;
//////////////////////////////////////////////////////////////////////
enum ShortcutType { Shortcut_ExecuteCommand,
Shortcut_ChangeTool };
Shortcut_ChangeTool,
Shortcut_EditorQuicktool };
struct Shortcut
{
@ -106,12 +107,14 @@ struct Shortcut
~Shortcut();
void add_shortcut(const char* shortcut_string);
bool is_key_pressed(JMessage msg);
bool is_pressed(JMessage msg);
bool is_pressed_from_key_array();
};
static Shortcut* get_keyboard_shortcut_for_command(const char* command_name, Params* params);
static Shortcut* get_keyboard_shortcut_for_tool(Tool* tool);
static Shortcut* get_keyboard_shortcut_for_quicktool(Tool* tool);
//////////////////////////////////////////////////////////////////////
@ -929,6 +932,21 @@ JAccel add_keyboard_shortcut_to_change_tool(const char* shortcut_string, Tool* t
return shortcut->accel;
}
JAccel add_keyboard_shortcut_to_quicktool(const char* shortcut_string, Tool* tool)
{
Shortcut* shortcut = get_keyboard_shortcut_for_quicktool(tool);
if (!shortcut) {
shortcut = new Shortcut(Shortcut_EditorQuicktool);
shortcut->tool = tool;
shortcuts->push_back(shortcut);
}
shortcut->add_shortcut(shortcut_string);
return shortcut->accel;
}
Command* get_command_from_key_message(JMessage msg)
{
for (std::vector<Shortcut*>::iterator
@ -938,7 +956,7 @@ Command* get_command_from_key_message(JMessage msg)
if (shortcut->type == Shortcut_ExecuteCommand &&
// TODO why?
// shortcut->argument.empty() &&
shortcut->is_key_pressed(msg)) {
shortcut->is_pressed(msg)) {
return shortcut->command;
}
}
@ -963,6 +981,23 @@ JAccel get_accel_to_change_tool(Tool* tool)
return NULL;
}
Tool* get_selected_quicktool()
{
ToolBox* toolbox = App::instance()->getToolBox();
// Iterate over all tools
for (ToolIterator it = toolbox->begin(); it != toolbox->end(); ++it) {
Shortcut* shortcut = get_keyboard_shortcut_for_quicktool(*it);
// Collect all tools with the pressed keyboard-shortcut
if (shortcut && shortcut->is_pressed_from_key_array()) {
return *it;
}
}
return NULL;
}
Shortcut::Shortcut(ShortcutType type)
{
this->type = type;
@ -985,7 +1020,7 @@ void Shortcut::add_shortcut(const char* shortcut_string)
jaccel_add_keys_from_string(this->accel, buf);
}
bool Shortcut::is_key_pressed(JMessage msg)
bool Shortcut::is_pressed(JMessage msg)
{
if (accel) {
return jaccel_check(accel,
@ -996,6 +1031,14 @@ bool Shortcut::is_key_pressed(JMessage msg)
return false;
}
bool Shortcut::is_pressed_from_key_array()
{
if (accel) {
return jaccel_check_from_key(accel);
}
return false;
}
static Shortcut* get_keyboard_shortcut_for_command(const char* command_name, Params* params)
{
Command* command = CommandsModule::instance()->get_command_by_name(command_name);
@ -1032,6 +1075,21 @@ static Shortcut* get_keyboard_shortcut_for_tool(Tool* tool)
return NULL;
}
static Shortcut* get_keyboard_shortcut_for_quicktool(Tool* tool)
{
for (std::vector<Shortcut*>::iterator
it = shortcuts->begin(); it != shortcuts->end(); ++it) {
Shortcut* shortcut = *it;
if (shortcut->type == Shortcut_EditorQuicktool &&
shortcut->tool == tool) {
return shortcut;
}
}
return NULL;
}
/**
* Adds a routine to be called each 100 milliseconds to monitor
* whatever you want. It's mainly used to monitor the progress of a
@ -1120,7 +1178,7 @@ static bool manager_msg_proc(JWidget widget, JMessage msg)
it = shortcuts->begin(); it != shortcuts->end(); ++it) {
Shortcut* shortcut = *it;
if (shortcut->is_key_pressed(msg)) {
if (shortcut->is_pressed(msg)) {
switch (shortcut->type) {
case Shortcut_ChangeTool: {
@ -1134,7 +1192,7 @@ static bool manager_msg_proc(JWidget widget, JMessage msg)
Shortcut* shortcut = get_keyboard_shortcut_for_tool(*it);
// Collect all tools with the pressed keyboard-shortcut
if (shortcut && shortcut->is_key_pressed(msg))
if (shortcut && shortcut->is_pressed(msg))
possibles.push_back(*it);
}
@ -1197,6 +1255,12 @@ static bool manager_msg_proc(JWidget widget, JMessage msg)
break;
}
case Shortcut_EditorQuicktool: {
// Do nothing, it is used in the editor through the
// get_selected_quicktool() function.
break;
}
}
break;
}

View File

@ -105,10 +105,12 @@ CheckBox* check_button_new(const char* text, int b1, int b2, int b3, int b4);
JAccel add_keyboard_shortcut_to_execute_command(const char* shortcut, const char* command_name, Params* params);
JAccel add_keyboard_shortcut_to_change_tool(const char* shortcut, Tool* tool);
JAccel add_keyboard_shortcut_to_quicktool(const char* shortcut, Tool* tool);
Command* get_command_from_key_message(JMessage msg);
JAccel get_accel_to_execute_command(const char* command, Params* params = NULL);
JAccel get_accel_to_change_tool(Tool* tool);
Tool* get_selected_quicktool();
//////////////////////////////////////////////////////////////////////
// Monitors

View File

@ -113,6 +113,8 @@ static int load_root_menu()
throw ase_exception("Error loading main menu from file:\n%s\nReinstall the application.",
static_cast<const char*>(path));
PRINTF("Main menu loaded.\n");
layer_popup_menu = load_menu_by_id(handle, "layer_popup");
frame_popup_menu = load_menu_by_id(handle, "frame_popup");
cel_popup_menu = load_menu_by_id(handle, "cel_popup");
@ -190,7 +192,6 @@ static int load_root_menu()
if (tool_id && tool_key) {
Tool* tool = App::instance()->getToolBox()->getToolById(tool_id);
if (tool) {
/* add the keyboard shortcut to the tool */
PRINTF(" - Shortcut for tool `%s': <%s>\n", tool_id, tool_key);
add_keyboard_shortcut_to_change_tool(tool_key, tool);
}
@ -199,6 +200,33 @@ static int load_root_menu()
xmlKey = xmlKey->NextSiblingElement();
}
/**************************************************/
/* load keyboard shortcuts for quicktools */
/**************************************************/
PRINTF(" - Loading tools keyboard shortcuts from \"%s\"...\n", path);
// <gui><keyboard><quicktools><key>
xmlKey = handle
.FirstChild("gui")
.FirstChild("keyboard")
.FirstChild("quicktools")
.FirstChild("key").ToElement();
while (xmlKey) {
const char* tool_id = xmlKey->Attribute("tool");
const char* tool_key = xmlKey->Attribute("shortcut");
if (tool_id && tool_key) {
Tool* tool = App::instance()->getToolBox()->getToolById(tool_id);
if (tool) {
PRINTF(" - Shortcut for quicktool `%s': <%s>\n", tool_id, tool_key);
add_keyboard_shortcut_to_quicktool(tool_key, tool);
}
}
xmlKey = xmlKey->NextSiblingElement();
}
// Sets the "menu" of the "menu-bar" to the new "root-menu"
if (app_get_menubar()) {
jmenubar_set_menu(app_get_menubar(), root_menu);

View File

@ -89,9 +89,9 @@ class Editor : public Widget
// True if the cursor is inside the mask/selection
bool m_insideSelection : 1;
bool m_alt_pressed : 1;
bool m_ctrl_pressed : 1;
bool m_space_pressed : 1;
// Current selected quicktool (this genererally should be NULL if
// the user is not pressing any keyboard key).
Tool* m_quicktool;
/* offset for the sprite */
int m_offset_x;
@ -166,6 +166,7 @@ public:
private:
void editor_update_statusbar_for_pixel_movement();
void editor_update_quicktool();
void editor_draw_cursor(int x, int y, bool refresh = true);
void editor_move_cursor(int x, int y, bool refresh = true);

View File

@ -92,9 +92,8 @@ Editor::Editor()
m_cursor_candraw = false;
m_insideSelection = false;
m_alt_pressed = false;
m_ctrl_pressed = false;
m_space_pressed = false;
m_quicktool = NULL;
m_offset_x = 0;
m_offset_y = 0;
@ -687,10 +686,12 @@ void Editor::dropPixels()
Tool* Editor::getCurrentEditorTool()
{
UIContext* context = UIContext::instance();
Tool* current_tool = context->getSettings()->getCurrentTool();
return current_tool;
if (m_quicktool)
return m_quicktool;
else {
UIContext* context = UIContext::instance();
return context->getSettings()->getCurrentTool();
}
}
void Editor::screen_to_editor(int xin, int yin, int *xout, int *yout)
@ -747,9 +748,11 @@ void Editor::editor_update_statusbar_for_standby()
int x, y;
screen_to_editor(jmouse_x(0), jmouse_y(0), &x, &y);
if (!m_sprite) {
app_get_statusbar()->clearText();
}
// For eye-dropper
if (m_alt_pressed ||
current_tool->getInk(0)->isEyedropper()) {
else if (current_tool->getInk(0)->isEyedropper()) {
int imgtype = m_sprite->getImgType();
ase_uint32 pixel = m_sprite->getPixel(x, y);
Color color = Color::fromImage(imgtype, pixel);
@ -767,11 +770,13 @@ void Editor::editor_update_statusbar_for_standby()
}
// For other tools
else {
Mask* mask = m_sprite->getMask();
app_get_statusbar()->setStatusText
(0, "Pos %d %d, Size %d %d, Frame %d",
x, y,
((m_sprite->getMask()->bitmap)? m_sprite->getMask()->w: m_sprite->getWidth()),
((m_sprite->getMask()->bitmap)? m_sprite->getMask()->h: m_sprite->getHeight()),
((mask && mask->bitmap)? mask->w: m_sprite->getWidth()),
((mask && mask->bitmap)? mask->h: m_sprite->getHeight()),
m_sprite->getCurrentFrame()+1);
}
}
@ -787,6 +792,18 @@ void Editor::editor_update_statusbar_for_pixel_movement()
bounds.x, bounds.y, bounds.w, bounds.h);
}
void Editor::editor_update_quicktool()
{
Tool* old_quicktool = m_quicktool;
m_quicktool = get_selected_quicktool();
// If the tool has changed, we must to update the status bar because
// the new tool can display something different in the status bar (e.g. Eyedropper)
if (old_quicktool != m_quicktool)
editor_update_statusbar_for_standby();
}
void Editor::editor_refresh_region()
{
if (this->update_region) {
@ -981,19 +998,11 @@ bool Editor::onProcessMessage(JMessage msg)
// 'cursor_candraw' field to avoid a heavy if-condition in the
// 'editor_setcursor' routine
editor_update_candraw();
if (msg->any.shifts & KB_ALT_FLAG) m_alt_pressed = true;
if (msg->any.shifts & KB_CTRL_FLAG) m_ctrl_pressed = true;
if (key[KEY_SPACE]) m_space_pressed = true;
editor_update_quicktool();
break;
case JM_MOUSELEAVE:
hide_drawing_cursor();
if (m_alt_pressed) m_alt_pressed = false;
if (m_ctrl_pressed) m_ctrl_pressed = false;
if (m_space_pressed) m_space_pressed = false;
app_get_statusbar()->clearText();
break;
@ -1036,7 +1045,6 @@ bool Editor::onProcessMessage(JMessage msg)
// Start scroll loop
if (msg->mouse.middle ||
m_space_pressed ||
current_tool->getInk(msg->mouse.right ? 1: 0)->isScrollMovement()) {
m_state = EDITOR_STATE_SCROLLING;
@ -1064,9 +1072,7 @@ bool Editor::onProcessMessage(JMessage msg)
}
// Move frames position
if ((m_ctrl_pressed &&
!current_tool->getInk(msg->mouse.right ? 1: 0)->isSelection()) ||
current_tool->getInk(msg->mouse.right ? 1: 0)->isCelMovement()) {
if (current_tool->getInk(msg->mouse.right ? 1: 0)->isCelMovement()) {
if ((m_sprite->getCurrentLayer()) &&
(m_sprite->getCurrentLayer()->getType() == GFXOBJ_LAYER_IMAGE)) {
// TODO you can move the `Background' with tiled mode
@ -1106,7 +1112,7 @@ bool Editor::onProcessMessage(JMessage msg)
delete tmpImage;
// If the CTRL key is pressed start dragging a copy of the selection
if (m_ctrl_pressed)
if (key[KEY_LCONTROL] || key[KEY_RCONTROL]) // TODO configurable
m_pixelsMovement->copyMask();
else
m_pixelsMovement->cutMask();
@ -1124,8 +1130,7 @@ bool Editor::onProcessMessage(JMessage msg)
captureMouse();
}
// Call the eyedropper command
else if (m_alt_pressed ||
current_tool->getInk(msg->mouse.right ? 1: 0)->isEyedropper()) {
else if (current_tool->getInk(msg->mouse.right ? 1: 0)->isEyedropper()) {
Command* eyedropper_cmd =
CommandsModule::instance()->get_command_by_name(CommandId::eyedropper);
@ -1332,30 +1337,18 @@ bool Editor::onProcessMessage(JMessage msg)
}
if (this->hasMouse()) {
switch (msg->key.scancode) {
// Eye-dropper is activated with ALT key
case KEY_ALT:
m_alt_pressed = true;
editor_setcursor(jmouse_x(0), jmouse_y(0));
return true;
editor_update_quicktool();
case KEY_LCONTROL:
case KEY_RCONTROL:
// If the user press the CTRL key when he is dragging pixels (but not pressing the mouse buttons)...
if (!m_ctrl_pressed && !jmouse_b(0) && m_pixelsMovement) {
// Drop pixels (sure the user will press the mouse button to start dragging a copy)
dropPixels();
}
m_ctrl_pressed = true;
editor_setcursor(jmouse_x(0), jmouse_y(0));
return true;
case KEY_SPACE:
m_space_pressed = true;
editor_setcursor(jmouse_x(0), jmouse_y(0));
return true;
if (msg->key.scancode == KEY_LCONTROL || // TODO configurable
msg->key.scancode == KEY_RCONTROL) {
// If the user press the CTRL key when he is dragging pixels (but not pressing the mouse buttons)...
if (!jmouse_b(0) && m_pixelsMovement) {
// Drop pixels (sure the user will press the mouse button to start dragging a copy)
dropPixels();
}
}
editor_setcursor(jmouse_x(0), jmouse_y(0));
}
// When we are drawing, we "eat" all pressed keys
@ -1365,37 +1358,14 @@ bool Editor::onProcessMessage(JMessage msg)
break;
case JM_KEYRELEASED:
switch (msg->key.scancode) {
editor_update_quicktool();
editor_setcursor(jmouse_x(0), jmouse_y(0));
break;
// Eye-dropper is deactivated with ALT key
case KEY_ALT:
if (m_alt_pressed) {
m_alt_pressed = false;
editor_setcursor(jmouse_x(0), jmouse_y(0));
return true;
}
break;
case KEY_LCONTROL:
case KEY_RCONTROL:
if (m_ctrl_pressed) {
m_ctrl_pressed = false;
editor_setcursor(jmouse_x(0), jmouse_y(0));
return true;
}
break;
case KEY_SPACE:
if (m_space_pressed) {
// We have to clear all the KEY_SPACE in buffer
clear_keybuf();
m_space_pressed = false;
editor_setcursor(jmouse_x(0), jmouse_y(0));
return true;
}
break;
}
case JM_FOCUSLEAVE:
// As we use keys like Space-bar as modifier, we can clear the
// keyboard buffer when we lost the focus.
clear_keybuf();
break;
case JM_WHEEL:
@ -1627,23 +1597,6 @@ void Editor::editor_setcursor(int x, int y)
jmouse_set_cursor(JI_CURSOR_FORBIDDEN);
}
}
// Eyedropper
else if (m_alt_pressed) {
hide_drawing_cursor();
jmouse_set_cursor(JI_CURSOR_EYEDROPPER);
}
// Move layer
else if (m_ctrl_pressed &&
(!current_tool ||
!current_tool->getInk(0)->isSelection())) {
hide_drawing_cursor();
jmouse_set_cursor(JI_CURSOR_MOVE);
}
// Scroll
else if (m_space_pressed) {
hide_drawing_cursor();
jmouse_set_cursor(JI_CURSOR_SCROLL);
}
else {
if (current_tool) {
// If the current tool change selection (e.g. rectangular marquee, etc.)
@ -1654,7 +1607,8 @@ void Editor::editor_setcursor(int x, int y)
// Move pixels
if (m_sprite->getMask()->contains_point(x, y)) {
hide_drawing_cursor();
if (m_ctrl_pressed)
if (key[KEY_LCONTROL] ||
key[KEY_RCONTROL]) // TODO configurable keys
jmouse_set_cursor(JI_CURSOR_NORMAL_ADD);
else
jmouse_set_cursor(JI_CURSOR_MOVE);
@ -1676,6 +1630,11 @@ void Editor::editor_setcursor(int x, int y)
jmouse_set_cursor(JI_CURSOR_SCROLL);
return;
}
else if (current_tool->getInk(0)->isCelMovement()) {
hide_drawing_cursor();
jmouse_set_cursor(JI_CURSOR_MOVE);
return;
}
}
if (m_insideSelection) {