mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-03 16:13:43 +00:00
Add mouse wheel customization for sprite editor
Fix #680, #970, https://community.aseprite.org/t/668 and possibility to change alpha with mouse wheel: https://github.com/aseprite/aseprite/issues/680#issuecomment-344721509
This commit is contained in:
parent
f33091dbfa
commit
7740f6a710
@ -190,6 +190,7 @@ BackgroundFromLayer = Background From Layer
|
||||
BrightnessContrast = Adjust Brightness/Contrast
|
||||
Cancel = Cancel Current Operation
|
||||
CanvasSize = Canvas Size
|
||||
CelOpacity = Set Cel Opacity to {0} ({1}%)
|
||||
CelProperties = Cel Properties
|
||||
ChangeBrush = Change Brush: {0}
|
||||
ChangeBrush_CustomBrush = Custom Brush #{0}
|
||||
@ -612,6 +613,14 @@ title = Keyboard Shortcuts
|
||||
import = &Import
|
||||
export = &Export
|
||||
reset = &Reset
|
||||
section_menus = Menus
|
||||
section_commands = Commands
|
||||
section_tools = Tools
|
||||
section_action_modifiers = Action Modifiers
|
||||
section_mouse_wheel = Mouse Wheel
|
||||
default_wheel_behavior = Default
|
||||
custom_wheel_behavior = Custom
|
||||
slide_as_wheel = Interpret two fingers slide on Trackpad as mouse wheel
|
||||
ok = &OK
|
||||
cancel = &Cancel
|
||||
|
||||
|
@ -7,14 +7,20 @@
|
||||
<vbox>
|
||||
<search id="search" magnet="true" />
|
||||
<view width="80" expansive="true">
|
||||
<listbox id="section" expansive="true" />
|
||||
<listbox id="section" expansive="true">
|
||||
<listitem text="@.section_menus" />
|
||||
<listitem text="@.section_commands" />
|
||||
<listitem text="@.section_tools" />
|
||||
<listitem text="@.section_action_modifiers" />
|
||||
<listitem text="@.section_mouse_wheel" />
|
||||
</listbox>
|
||||
</view>
|
||||
<separator horizontal="true" />
|
||||
<button text="@keyboard_shortcuts.import" id="import_button" />
|
||||
<button text="@keyboard_shortcuts.export" id="export_button" />
|
||||
<button text="@keyboard_shortcuts.reset" id="reset_button" />
|
||||
</vbox>
|
||||
<vbox expansive="true">
|
||||
<vbox id="lists_placeholder" expansive="true">
|
||||
<view id="search_view" expansive="true">
|
||||
<listbox id="search_list" />
|
||||
</view>
|
||||
@ -30,6 +36,21 @@
|
||||
<view id="actions_view" expansive="true">
|
||||
<listbox id="actions" />
|
||||
</view>
|
||||
<vbox id="wheel_section" expansive="true">
|
||||
<hbox>
|
||||
<buttonset columns="2" id="wheel_behavior">
|
||||
<item text="@.default_wheel_behavior" />
|
||||
<item text="@.custom_wheel_behavior" />
|
||||
</buttonset>
|
||||
</hbox>
|
||||
<check text="@options.wheel_zoom" id="wheel_zoom"
|
||||
pref="editor.zoom_with_wheel" />
|
||||
<check text="@options.slide_zoom" id="slide_zoom"
|
||||
pref="editor.zoom_with_slide" />
|
||||
<view expansive="true">
|
||||
<listbox id="wheel_actions" />
|
||||
</view>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<hbox>
|
||||
|
@ -117,8 +117,10 @@
|
||||
<!-- Editor -->
|
||||
<vbox id="section_editor">
|
||||
<separator text="@.section_editor" horizontal="true" />
|
||||
<check text="@.wheel_zoom" id="wheel_zoom" />
|
||||
<check text="@.slide_zoom" id="slide_zoom" />
|
||||
<check text="@.wheel_zoom" id="wheel_zoom"
|
||||
pref="editor.zoom_with_wheel" />
|
||||
<check text="@.slide_zoom" id="slide_zoom"
|
||||
pref="editor.zoom_with_slide" />
|
||||
<check text="@.zoom_from_center_with_wheel" id="zoom_from_center_with_wheel" />
|
||||
<check text="@.zoom_from_center_with_keys" id="zoom_from_center_with_keys" />
|
||||
<check text="@.show_scrollbars" id="show_scrollbars" tooltip="@.show_scrollbars_tooltip" />
|
||||
|
@ -175,6 +175,7 @@ if(ENABLE_UI)
|
||||
commands/cmd_background_from_layer.cpp
|
||||
commands/cmd_cancel.cpp
|
||||
commands/cmd_canvas_size.cpp
|
||||
commands/cmd_cel_opacity.cpp
|
||||
commands/cmd_cel_properties.cpp
|
||||
commands/cmd_change_brush.cpp
|
||||
commands/cmd_change_color.cpp
|
||||
|
112
src/app/commands/cmd_cel_opacity.cpp
Normal file
112
src/app/commands/cmd_cel_opacity.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 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/app.h"
|
||||
#include "app/cmd/set_cel_opacity.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/timeline/timeline.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/cels_range.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
|
||||
class CelOpacityCommand : public Command {
|
||||
public:
|
||||
CelOpacityCommand();
|
||||
|
||||
protected:
|
||||
bool onNeedsParams() const override { return true; }
|
||||
void onLoadParams(const Params& params) override;
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
std::string onGetFriendlyName() const override;
|
||||
|
||||
private:
|
||||
int m_opacity;
|
||||
};
|
||||
|
||||
CelOpacityCommand::CelOpacityCommand()
|
||||
: Command(CommandId::CelOpacity(), CmdUIOnlyFlag)
|
||||
{
|
||||
m_opacity = 255;
|
||||
}
|
||||
|
||||
void CelOpacityCommand::onLoadParams(const Params& params)
|
||||
{
|
||||
m_opacity = params.get_as<int>("opacity");
|
||||
m_opacity = MID(0, m_opacity, 255);
|
||||
}
|
||||
|
||||
bool CelOpacityCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
||||
ContextFlags::HasActiveCel);
|
||||
}
|
||||
|
||||
void CelOpacityCommand::onExecute(Context* context)
|
||||
{
|
||||
ContextWriter writer(context);
|
||||
Layer* layer = writer.layer();
|
||||
Cel* cel = writer.cel();
|
||||
if (!cel ||
|
||||
layer->isBackground() ||
|
||||
!layer->isEditable() ||
|
||||
cel->opacity() == m_opacity)
|
||||
return;
|
||||
|
||||
{
|
||||
Transaction transaction(writer.context(), "Set Cel Opacity");
|
||||
|
||||
// TODO the range of selected cels should be in app::Site.
|
||||
DocRange range = App::instance()->timeline()->range();
|
||||
if (!range.enabled()) {
|
||||
range.startRange(layer, cel->frame(), DocRange::kCels);
|
||||
range.endRange(layer, cel->frame());
|
||||
}
|
||||
|
||||
for (Cel* cel : cel->sprite()->uniqueCels(range.selectedFrames())) {
|
||||
if (range.contains(cel->layer())) {
|
||||
if (!cel->layer()->isBackground() &&
|
||||
cel->layer()->isEditable() &&
|
||||
m_opacity != cel->opacity()) {
|
||||
transaction.execute(new cmd::SetCelOpacity(cel, m_opacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
update_screen_for_document(writer.document());
|
||||
}
|
||||
|
||||
std::string CelOpacityCommand::onGetFriendlyName() const
|
||||
{
|
||||
return fmt::format(getBaseFriendlyName(),
|
||||
m_opacity,
|
||||
int(100.0 * m_opacity / 255.0));
|
||||
}
|
||||
|
||||
Command* CommandFactory::createCelOpacityCommand()
|
||||
{
|
||||
return new CelOpacityCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -44,6 +44,7 @@
|
||||
|
||||
#include "keyboard_shortcuts.xml.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#define KEYBOARD_FILENAME_EXTENSION "aseprite-keys"
|
||||
@ -186,6 +187,8 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void onAccelChange(const Accelerator& accel);
|
||||
|
||||
void onChangeAccel(int index) {
|
||||
LockButtons lock(this);
|
||||
Accelerator origAccel = m_key->accels()[index];
|
||||
@ -193,6 +196,8 @@ private:
|
||||
window.openWindowInForeground();
|
||||
|
||||
if (window.isModified()) {
|
||||
onAccelChange(window.accel());
|
||||
|
||||
m_key->disableAccel(origAccel);
|
||||
if (!window.accel().isEmpty())
|
||||
m_key->add(window.accel(), KeySource::UserDefined);
|
||||
@ -223,7 +228,9 @@ private:
|
||||
SelectAccelerator window(accel, m_key ? m_key->keycontext(): KeyContext::Any);
|
||||
window.openWindowInForeground();
|
||||
|
||||
if (window.isModified()) {
|
||||
if ((window.isModified()) ||
|
||||
// We can assign a "None" accelerator to mouse wheel actions
|
||||
(m_key && m_key->type() == KeyType::WheelAction && window.isOK())) {
|
||||
if (!m_key) {
|
||||
ASSERT(m_menuitem);
|
||||
if (!m_menuitem)
|
||||
@ -236,6 +243,7 @@ private:
|
||||
m_menuitem->setKey(m_key);
|
||||
}
|
||||
|
||||
onAccelChange(window.accel());
|
||||
m_key->add(window.accel(), KeySource::UserDefined);
|
||||
}
|
||||
|
||||
@ -245,7 +253,7 @@ private:
|
||||
void onSizeHint(SizeHintEvent& ev) override {
|
||||
gfx::Size size = textSize();
|
||||
size.w = size.w + border().width();
|
||||
size.h = size.h + border().height() + 4*guiscale();
|
||||
size.h = size.h + border().height() + 6*guiscale();
|
||||
|
||||
if (m_key && m_key->keycontext() != KeyContext::Any) {
|
||||
int w =
|
||||
@ -291,8 +299,9 @@ private:
|
||||
{
|
||||
int x = bounds.x + m_level*16 * guiscale();
|
||||
IntersectClip clip(g, gfx::Rect(x, y, keyXPos - x, th));
|
||||
if (clip)
|
||||
if (clip) {
|
||||
g->drawUIText(text(), fg, bg, gfx::Point(x, y), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_key && !m_key->accels().empty()) {
|
||||
@ -311,7 +320,7 @@ private:
|
||||
for (const Accelerator& accel : m_key->accels()) {
|
||||
if (i != m_hotAccel || !m_changeButton) {
|
||||
g->drawText(
|
||||
accel.toString(), fg, bg,
|
||||
getAccelText(accel), fg, bg,
|
||||
gfx::Point(keyXPos, y));
|
||||
}
|
||||
y += dh;
|
||||
@ -336,6 +345,9 @@ private:
|
||||
}
|
||||
|
||||
case kMouseMoveMessage: {
|
||||
if (!isEnabled())
|
||||
break;
|
||||
|
||||
gfx::Rect bounds = this->bounds();
|
||||
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
||||
|
||||
@ -346,7 +358,7 @@ private:
|
||||
|
||||
for (int i=0; i<maxi; ++i, y += dh) {
|
||||
int w = Graphics::measureUITextLength(
|
||||
(accels && i < (int)accels->size() ? (*accels)[i].toString().c_str(): ""),
|
||||
(accels && i < (int)accels->size() ? getAccelText((*accels)[i]).c_str(): ""),
|
||||
font());
|
||||
gfx::Rect itemBounds(bounds.x + m_headerItem->keyXPos(), y, w, dh);
|
||||
itemBounds = itemBounds.enlarge(
|
||||
@ -375,7 +387,7 @@ private:
|
||||
|
||||
m_changeButton->setBgColor(gfx::ColorNone);
|
||||
m_changeButton->setBounds(itemBounds);
|
||||
m_changeButton->setText((*accels)[i].toString());
|
||||
m_changeButton->setText(getAccelText((*accels)[i]));
|
||||
|
||||
const char* label = "x";
|
||||
m_deleteButton->setBgColor(gfx::ColorNone);
|
||||
@ -436,6 +448,16 @@ private:
|
||||
m_hotAccel = -1;
|
||||
}
|
||||
|
||||
std::string getAccelText(const Accelerator& accel) const {
|
||||
if (m_key && m_key->type() == KeyType::WheelAction &&
|
||||
accel.isEmpty()) {
|
||||
return "(Default Action)";
|
||||
}
|
||||
else {
|
||||
return accel.toString();
|
||||
}
|
||||
}
|
||||
|
||||
KeyPtr m_key;
|
||||
KeyPtr m_keyOrig;
|
||||
AppMenuItem* m_menuitem;
|
||||
@ -455,18 +477,42 @@ private:
|
||||
class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts {
|
||||
public:
|
||||
KeyboardShortcutsWindow(const std::string& searchText)
|
||||
: m_searchChange(false) {
|
||||
: m_searchChange(false)
|
||||
, m_wasDefault(false) {
|
||||
setAutoRemap(false);
|
||||
|
||||
section()->addChild(new ListItem("Menus"));
|
||||
section()->addChild(new ListItem("Commands"));
|
||||
section()->addChild(new ListItem("Tools"));
|
||||
section()->addChild(new ListItem("Action Modifiers"));
|
||||
|
||||
m_listBoxes.push_back(menus());
|
||||
m_listBoxes.push_back(commands());
|
||||
m_listBoxes.push_back(tools());
|
||||
m_listBoxes.push_back(actions());
|
||||
m_listBoxes.push_back(wheelActions());
|
||||
|
||||
#ifdef __APPLE__ // Zoom sliding two fingers option only on macOS
|
||||
slideZoom()->setVisible(true);
|
||||
#else
|
||||
slideZoom()->setVisible(false);
|
||||
#endif
|
||||
|
||||
wheelBehavior()->setSelectedItem(
|
||||
app::KeyboardShortcuts::instance()->hasMouseWheelCustomization() ? 1: 0);
|
||||
if (isDefaultWheelBehavior()) {
|
||||
m_wheelKeys = app::KeyboardShortcuts::instance()
|
||||
->getDefaultMouseWheelTable(wheelZoom()->isSelected());
|
||||
m_wasDefault = true;
|
||||
}
|
||||
else {
|
||||
for (const KeyPtr& key : *app::KeyboardShortcuts::instance()) {
|
||||
if (key->type() == KeyType::WheelAction)
|
||||
m_wheelKeys.push_back(std::make_shared<Key>(*key));
|
||||
}
|
||||
}
|
||||
updateSlideZoomText();
|
||||
addMissingWheelKeys();
|
||||
|
||||
onWheelBehaviorChange();
|
||||
|
||||
wheelBehavior()->ItemChange.connect(base::Bind<void>(&KeyboardShortcutsWindow::onWheelBehaviorChange, this));
|
||||
wheelZoom()->Click.connect(base::Bind<void>(&KeyboardShortcutsWindow::onWheelZoomChange, this));
|
||||
|
||||
search()->Change.connect(base::Bind<void>(&KeyboardShortcutsWindow::onSearchChange, this));
|
||||
section()->Change.connect(base::Bind<void>(&KeyboardShortcutsWindow::onSectionChange, this));
|
||||
@ -492,6 +538,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const Keys& wheelKeys() {
|
||||
return m_wheelKeys;
|
||||
}
|
||||
|
||||
bool isDefaultWheelBehavior() {
|
||||
return (wheelBehavior()->selectedItem() == 0);
|
||||
}
|
||||
|
||||
void disableWheelKey(const Accelerator& accel) {
|
||||
for (KeyPtr& key : m_wheelKeys) {
|
||||
for (int i=0; i<key->accels().size(); ++i) {
|
||||
if (key->accels()[i] == accel) {
|
||||
key->disableAccel(accel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void deleteAllKeyItems() {
|
||||
deleteList(searchList());
|
||||
@ -499,6 +564,7 @@ private:
|
||||
deleteList(commands());
|
||||
deleteList(tools());
|
||||
deleteList(actions());
|
||||
deleteList(wheelActions());
|
||||
ASSERT(m_allKeyItems.empty());
|
||||
}
|
||||
|
||||
@ -511,7 +577,8 @@ private:
|
||||
|
||||
for (const KeyPtr& key : *app::KeyboardShortcuts::instance()) {
|
||||
if (key->type() == KeyType::Tool ||
|
||||
key->type() == KeyType::Quicktool) {
|
||||
key->type() == KeyType::Quicktool ||
|
||||
key->type() == KeyType::WheelAction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -552,6 +619,8 @@ private:
|
||||
tools()->sortItems();
|
||||
actions()->sortItems();
|
||||
|
||||
fillWheelActionsList();
|
||||
|
||||
section()->selectIndex(0);
|
||||
updateViews();
|
||||
}
|
||||
@ -599,6 +668,9 @@ private:
|
||||
keyItem->menuitem(), 0,
|
||||
&m_headerItem);
|
||||
|
||||
if (!item->isEnabled())
|
||||
copyItem->setEnabled(false);
|
||||
|
||||
m_allKeyItems.push_back(copyItem);
|
||||
searchList()->addChild(copyItem);
|
||||
}
|
||||
@ -608,6 +680,51 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void onWheelBehaviorChange() {
|
||||
const bool isDefault = isDefaultWheelBehavior();
|
||||
wheelActions()->setEnabled(!isDefault);
|
||||
wheelZoom()->setVisible(isDefault);
|
||||
|
||||
if (isDefault) {
|
||||
m_wheelKeys = app::KeyboardShortcuts::instance()
|
||||
->getDefaultMouseWheelTable(wheelZoom()->isSelected());
|
||||
m_wasDefault = true;
|
||||
}
|
||||
else if (m_wasDefault) {
|
||||
m_wasDefault = false;
|
||||
for (KeyPtr& key : m_wheelKeys)
|
||||
key->copyOriginalToUser();
|
||||
}
|
||||
updateSlideZoomText();
|
||||
addMissingWheelKeys();
|
||||
|
||||
fillWheelActionsList();
|
||||
updateViews();
|
||||
}
|
||||
|
||||
void updateSlideZoomText() {
|
||||
slideZoom()->setText(
|
||||
isDefaultWheelBehavior() ?
|
||||
Strings::options_slide_zoom():
|
||||
Strings::keyboard_shortcuts_slide_as_wheel());
|
||||
}
|
||||
|
||||
void fillWheelActionsList() {
|
||||
deleteList(wheelActions());
|
||||
for (const KeyPtr& key : m_wheelKeys) {
|
||||
KeyItem* keyItem = new KeyItem(
|
||||
key->triggerString(), key, nullptr, 0, &m_headerItem);
|
||||
wheelActions()->addChild(keyItem);
|
||||
}
|
||||
wheelActions()->sortItems();
|
||||
}
|
||||
|
||||
void onWheelZoomChange() {
|
||||
const bool isDefault = isDefaultWheelBehavior();
|
||||
if (isDefault)
|
||||
onWheelBehaviorChange();
|
||||
}
|
||||
|
||||
void onSearchChange() {
|
||||
base::ScopedValue<bool> flag(m_searchChange, true, false);
|
||||
std::string searchText = search()->text();
|
||||
@ -637,6 +754,7 @@ private:
|
||||
commandsView()->setVisible(s == 1);
|
||||
toolsView()->setVisible(s == 2);
|
||||
actionsView()->setVisible(s == 3);
|
||||
wheelSection()->setVisible(s == 4);
|
||||
|
||||
if (m_headerItem.parent())
|
||||
m_headerItem.parent()->removeChild(&m_headerItem);
|
||||
@ -645,7 +763,7 @@ private:
|
||||
else
|
||||
m_listBoxes[s]->insertChild(0, &m_headerItem);
|
||||
|
||||
layout();
|
||||
listsPlaceholder()->layout();
|
||||
}
|
||||
|
||||
void onImport() {
|
||||
@ -662,7 +780,6 @@ private:
|
||||
filename.front(), KeySource::UserDefined);
|
||||
|
||||
fillAllLists();
|
||||
layout();
|
||||
}
|
||||
|
||||
void onExport() {
|
||||
@ -682,7 +799,7 @@ private:
|
||||
void onReset() {
|
||||
if (ui::Alert::show(Strings::alerts_restore_all_shortcuts()) == 1) {
|
||||
app::KeyboardShortcuts::instance()->reset();
|
||||
layout();
|
||||
listsPlaceholder()->layout();
|
||||
}
|
||||
}
|
||||
|
||||
@ -725,12 +842,39 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Adds missing WhellAction in m_wheelKeys
|
||||
void addMissingWheelKeys() {
|
||||
for (int wheelAction=int(WheelAction::First);
|
||||
wheelAction<=int(WheelAction::Last); ++wheelAction) {
|
||||
auto it = std::find_if(
|
||||
m_wheelKeys.begin(), m_wheelKeys.end(),
|
||||
[wheelAction](const KeyPtr& key) -> bool {
|
||||
return key->wheelAction() == (WheelAction)wheelAction;
|
||||
});
|
||||
if (it == m_wheelKeys.end()) {
|
||||
KeyPtr key = std::make_shared<Key>((WheelAction)wheelAction);
|
||||
m_wheelKeys.push_back(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ListBox*> m_listBoxes;
|
||||
std::vector<KeyItem*> m_allKeyItems;
|
||||
bool m_searchChange;
|
||||
bool m_wasDefault;
|
||||
HeaderItem m_headerItem;
|
||||
Keys m_wheelKeys;
|
||||
};
|
||||
|
||||
void KeyItem::onAccelChange(const Accelerator& accel)
|
||||
{
|
||||
if (m_key && m_key->type() == KeyType::WheelAction) {
|
||||
auto window = dynamic_cast<KeyboardShortcutsWindow*>(this->window());
|
||||
if (window)
|
||||
window->disableWheelKey(accel);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class KeyboardShortcutsCommand : public Command {
|
||||
@ -778,6 +922,26 @@ void KeyboardShortcutsCommand::onExecute(Context* context)
|
||||
if (window.closer() == window.ok()) {
|
||||
KeyboardShortcuts::instance()->UserChange();
|
||||
|
||||
// Save preferences in widgets that are bound to options automatically
|
||||
{
|
||||
Message* msg = new Message(kSavePreferencesMessage);
|
||||
msg->setPropagateToChildren(msg);
|
||||
window.sendMessage(msg);
|
||||
}
|
||||
|
||||
if (window.isDefaultWheelBehavior()) {
|
||||
for (KeyPtr& key : *KeyboardShortcuts::instance()) {
|
||||
if (key->type() == KeyType::WheelAction)
|
||||
key->reset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const KeyPtr& srcKey : window.wheelKeys()) {
|
||||
KeyPtr dstKey = KeyboardShortcuts::instance()->wheelAction(srcKey->wheelAction());
|
||||
*dstKey = *srcKey;
|
||||
}
|
||||
}
|
||||
|
||||
// Save keyboard shortcuts in configuration file
|
||||
{
|
||||
ResourceFinder rf;
|
||||
|
@ -315,13 +315,7 @@ public:
|
||||
rightClickBehavior()->addItem("Lasso");
|
||||
rightClickBehavior()->setSelectedItemIndex((int)m_pref.editor.rightClickMode());
|
||||
|
||||
// Zoom with Scroll Wheel
|
||||
wheelZoom()->setSelected(m_pref.editor.zoomWithWheel());
|
||||
|
||||
// Zoom sliding two fingers
|
||||
#if __APPLE__
|
||||
slideZoom()->setSelected(m_pref.editor.zoomWithSlide());
|
||||
#else
|
||||
#ifndef __APPLE__ // Zoom sliding two fingers option only on macOS
|
||||
slideZoom()->setVisible(false);
|
||||
#endif
|
||||
|
||||
@ -434,10 +428,6 @@ public:
|
||||
m_pref.editor.autoScroll(autoScroll()->isSelected());
|
||||
m_pref.editor.straightLinePreview(straightLinePreview()->isSelected());
|
||||
m_pref.eyedropper.discardBrush(discardBrush()->isSelected());
|
||||
m_pref.editor.zoomWithWheel(wheelZoom()->isSelected());
|
||||
#if __APPLE__
|
||||
m_pref.editor.zoomWithSlide(slideZoom()->isSelected());
|
||||
#endif
|
||||
m_pref.editor.rightClickMode(static_cast<app::gen::RightClickMode>(rightClickBehavior()->getSelectedItemIndex()));
|
||||
m_pref.cursor.paintingCursorType(static_cast<app::gen::PaintingCursorType>(paintingCursorType()->getSelectedItemIndex()));
|
||||
m_pref.cursor.cursorColor(cursorColor()->getColor());
|
||||
|
@ -24,6 +24,7 @@ FOR_EACH_COMMAND(BackgroundFromLayer)
|
||||
FOR_EACH_COMMAND(BrightnessContrast)
|
||||
FOR_EACH_COMMAND(Cancel)
|
||||
FOR_EACH_COMMAND(CanvasSize)
|
||||
FOR_EACH_COMMAND(CelOpacity)
|
||||
FOR_EACH_COMMAND(CelProperties)
|
||||
FOR_EACH_COMMAND(ChangeBrush)
|
||||
FOR_EACH_COMMAND(ChangeColor)
|
||||
|
@ -12,11 +12,19 @@
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/site.h"
|
||||
#include "app/tools/active_tool.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
#include "app/ui/toolbar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/string.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/theme.h"
|
||||
@ -25,95 +33,96 @@ namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
enum WHEEL_ACTION { WHEEL_NONE,
|
||||
WHEEL_ZOOM,
|
||||
WHEEL_VSCROLL,
|
||||
WHEEL_HSCROLL,
|
||||
WHEEL_FG,
|
||||
WHEEL_BG,
|
||||
WHEEL_FRAME };
|
||||
|
||||
bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
gfx::Point delta = msg->wheelDelta();
|
||||
double dz = delta.x + delta.y;
|
||||
WHEEL_ACTION wheelAction = WHEEL_NONE;
|
||||
WheelAction wheelAction = WheelAction::None;
|
||||
bool scrollBigSteps = false;
|
||||
|
||||
// Alt+mouse wheel changes the fg/bg colors
|
||||
if (msg->altPressed()) {
|
||||
if (msg->shiftPressed())
|
||||
wheelAction = WHEEL_BG;
|
||||
if (KeyboardShortcuts::instance()->hasMouseWheelCustomization()) {
|
||||
if (!Preferences::instance().editor.zoomWithSlide() && msg->preciseWheel())
|
||||
wheelAction = WheelAction::VScroll;
|
||||
else
|
||||
wheelAction = WHEEL_FG;
|
||||
wheelAction = KeyboardShortcuts::instance()
|
||||
->getWheelActionFromMouseMessage(KeyContext::MouseWheel, msg);
|
||||
}
|
||||
// Normal behavior: mouse wheel zooms If the message is from a
|
||||
// precise wheel i.e. a trackpad/touch-like device, we scroll by
|
||||
// default.
|
||||
else if (Preferences::instance().editor.zoomWithWheel() && !msg->preciseWheel()) {
|
||||
if (msg->ctrlPressed())
|
||||
wheelAction = WHEEL_FRAME;
|
||||
else if (delta.x != 0 || msg->shiftPressed())
|
||||
wheelAction = WHEEL_HSCROLL;
|
||||
else
|
||||
wheelAction = WHEEL_ZOOM;
|
||||
}
|
||||
// Zoom sliding two fingers
|
||||
else if (Preferences::instance().editor.zoomWithSlide() && msg->preciseWheel()) {
|
||||
if (msg->ctrlPressed())
|
||||
wheelAction = WHEEL_FRAME;
|
||||
else if (std::abs(delta.x) > std::abs(delta.y)) {
|
||||
delta.y = 0;
|
||||
dz = delta.x;
|
||||
wheelAction = WHEEL_HSCROLL;
|
||||
}
|
||||
else if (msg->shiftPressed()) {
|
||||
delta.x = 0;
|
||||
dz = delta.y;
|
||||
wheelAction = WHEEL_VSCROLL;
|
||||
}
|
||||
else {
|
||||
delta.x = 0;
|
||||
dz = delta.y;
|
||||
wheelAction = WHEEL_ZOOM;
|
||||
}
|
||||
}
|
||||
// For laptops, it's convenient to that Ctrl+wheel zoom (because
|
||||
// it's the "pinch" gesture).
|
||||
// Default behavior
|
||||
// TODO replace this code using KeyboardShortcuts::getDefaultMouseWheelTable()
|
||||
else {
|
||||
if (msg->ctrlPressed())
|
||||
wheelAction = WHEEL_ZOOM;
|
||||
else if (delta.x != 0 || msg->shiftPressed())
|
||||
wheelAction = WHEEL_HSCROLL;
|
||||
else
|
||||
wheelAction = WHEEL_VSCROLL;
|
||||
// Alt+mouse wheel changes the fg/bg colors
|
||||
if (msg->altPressed()) {
|
||||
if (msg->shiftPressed())
|
||||
wheelAction = WheelAction::BgColor;
|
||||
else
|
||||
wheelAction = WheelAction::FgColor;
|
||||
}
|
||||
// Normal behavior: mouse wheel zooms If the message is from a
|
||||
// precise wheel i.e. a trackpad/touch-like device, we scroll by
|
||||
// default.
|
||||
else if (Preferences::instance().editor.zoomWithWheel() && !msg->preciseWheel()) {
|
||||
if (msg->ctrlPressed())
|
||||
wheelAction = WheelAction::Frame;
|
||||
else if (delta.x != 0 || msg->shiftPressed())
|
||||
wheelAction = WheelAction::HScroll;
|
||||
else
|
||||
wheelAction = WheelAction::Zoom;
|
||||
}
|
||||
// Zoom sliding two fingers
|
||||
else if (Preferences::instance().editor.zoomWithSlide() && msg->preciseWheel()) {
|
||||
if (msg->ctrlPressed())
|
||||
wheelAction = WheelAction::Frame;
|
||||
else if (std::abs(delta.x) > std::abs(delta.y)) {
|
||||
delta.y = 0;
|
||||
dz = delta.x;
|
||||
wheelAction = WheelAction::HScroll;
|
||||
}
|
||||
else if (msg->shiftPressed()) {
|
||||
delta.x = 0;
|
||||
dz = delta.y;
|
||||
wheelAction = WheelAction::VScroll;
|
||||
}
|
||||
else {
|
||||
delta.x = 0;
|
||||
dz = delta.y;
|
||||
wheelAction = WheelAction::Zoom;
|
||||
}
|
||||
}
|
||||
// For laptops, it's convenient to that Ctrl+wheel zoom (because
|
||||
// it's the "pinch" gesture).
|
||||
else {
|
||||
if (msg->ctrlPressed())
|
||||
wheelAction = WheelAction::Zoom;
|
||||
else if (delta.x != 0 || msg->shiftPressed())
|
||||
wheelAction = WheelAction::HScroll;
|
||||
else
|
||||
wheelAction = WheelAction::VScroll;
|
||||
}
|
||||
}
|
||||
|
||||
switch (wheelAction) {
|
||||
|
||||
case WHEEL_NONE:
|
||||
case WheelAction::None:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case WHEEL_FG:
|
||||
{
|
||||
int lastIndex = get_current_palette()->size()-1;
|
||||
int newIndex = ColorBar::instance()->getFgColor().getIndex() + int(dz);
|
||||
newIndex = MID(0, newIndex, lastIndex);
|
||||
ColorBar::instance()->setFgColor(app::Color::fromIndex(newIndex));
|
||||
}
|
||||
case WheelAction::FgColor: {
|
||||
int lastIndex = get_current_palette()->size()-1;
|
||||
int newIndex = ColorBar::instance()->getFgColor().getIndex() + int(dz);
|
||||
newIndex = MID(0, newIndex, lastIndex);
|
||||
ColorBar::instance()->setFgColor(app::Color::fromIndex(newIndex));
|
||||
break;
|
||||
}
|
||||
|
||||
case WHEEL_BG:
|
||||
{
|
||||
int lastIndex = get_current_palette()->size()-1;
|
||||
int newIndex = ColorBar::instance()->getBgColor().getIndex() + int(dz);
|
||||
newIndex = MID(0, newIndex, lastIndex);
|
||||
ColorBar::instance()->setBgColor(app::Color::fromIndex(newIndex));
|
||||
}
|
||||
case WheelAction::BgColor: {
|
||||
int lastIndex = get_current_palette()->size()-1;
|
||||
int newIndex = ColorBar::instance()->getBgColor().getIndex() + int(dz);
|
||||
newIndex = MID(0, newIndex, lastIndex);
|
||||
ColorBar::instance()->setBgColor(app::Color::fromIndex(newIndex));
|
||||
break;
|
||||
}
|
||||
|
||||
case WHEEL_FRAME: {
|
||||
case WheelAction::Frame: {
|
||||
Command* command = nullptr;
|
||||
|
||||
if (dz < 0.0)
|
||||
@ -126,7 +135,7 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
|
||||
break;
|
||||
}
|
||||
|
||||
case WHEEL_ZOOM: {
|
||||
case WheelAction::Zoom: {
|
||||
render::Zoom zoom = editor->zoom();
|
||||
|
||||
if (msg->preciseWheel()) {
|
||||
@ -141,15 +150,15 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
|
||||
break;
|
||||
}
|
||||
|
||||
case WHEEL_HSCROLL:
|
||||
case WHEEL_VSCROLL: {
|
||||
case WheelAction::HScroll:
|
||||
case WheelAction::VScroll: {
|
||||
View* view = View::getView(editor);
|
||||
gfx::Point scroll = view->viewScroll();
|
||||
|
||||
if (!msg->preciseWheel()) {
|
||||
gfx::Rect vp = view->viewportBounds();
|
||||
|
||||
if (wheelAction == WHEEL_HSCROLL) {
|
||||
if (wheelAction == WheelAction::HScroll) {
|
||||
delta.x = int(dz * vp.w);
|
||||
}
|
||||
else {
|
||||
@ -168,6 +177,191 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::BrushSize: {
|
||||
tools::Tool* tool = App::instance()->activeToolManager()->activeTool();
|
||||
ToolPreferences::Brush& brush =
|
||||
Preferences::instance().tool(tool).brush;
|
||||
|
||||
brush.size(MID(doc::Brush::kMinBrushSize,
|
||||
brush.size()+dz,
|
||||
doc::Brush::kMaxBrushSize));
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::BrushAngle: {
|
||||
tools::Tool* tool = App::instance()->activeToolManager()->activeTool();
|
||||
ToolPreferences::Brush& brush =
|
||||
Preferences::instance().tool(tool).brush;
|
||||
|
||||
int angle = brush.angle()+dz;
|
||||
while (angle < 0)
|
||||
angle += 180;
|
||||
angle %= 181;
|
||||
|
||||
brush.angle(MID(0, angle, 180));
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::ToolSameGroup: {
|
||||
tools::Tool* tool = App::instance()->activeToolManager()->selectedTool();
|
||||
|
||||
auto toolBox = App::instance()->toolBox();
|
||||
std::vector<tools::Tool*> tools;
|
||||
for (tools::Tool* t : *toolBox) {
|
||||
if (tool->getGroup() == t->getGroup())
|
||||
tools.push_back(t);
|
||||
}
|
||||
|
||||
auto begin = tools.begin();
|
||||
auto end = tools.end();
|
||||
auto it = std::find(begin, end, tool);
|
||||
if (it != end) {
|
||||
if (dz < 0) {
|
||||
if (it == begin)
|
||||
it = end;
|
||||
--it;
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
if (it == end)
|
||||
it = begin;
|
||||
}
|
||||
if (tool != *it)
|
||||
ToolBar::instance()->selectTool(*it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::ToolOtherGroup: {
|
||||
tools::Tool* tool = App::instance()->activeToolManager()->selectedTool();
|
||||
auto toolBox = App::instance()->toolBox();
|
||||
auto begin = toolBox->begin_group();
|
||||
auto end = toolBox->end_group();
|
||||
auto it = std::find(begin, end, tool->getGroup());
|
||||
if (it != end) {
|
||||
if (dz < 0) {
|
||||
if (it == begin)
|
||||
it = end;
|
||||
--it;
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
if (it == end)
|
||||
it = begin;
|
||||
}
|
||||
ToolBar::instance()->selectToolGroup(*it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::Layer: {
|
||||
Command* command = nullptr;
|
||||
if (dz < 0.0)
|
||||
command = Commands::instance()->byId(CommandId::GotoNextLayer());
|
||||
else if (dz > 0.0)
|
||||
command = Commands::instance()->byId(CommandId::GotoPreviousLayer());
|
||||
if (command)
|
||||
UIContext::instance()->executeCommand(command);
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::InkOpacity: {
|
||||
tools::Tool* tool = App::instance()->activeToolManager()->activeTool();
|
||||
auto& toolPref = Preferences::instance().tool(tool);
|
||||
int opacity = toolPref.opacity();
|
||||
opacity = MID(0, opacity+dz*255/10, 255);
|
||||
toolPref.opacity(opacity);
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::LayerOpacity: {
|
||||
Site site = UIContext::instance()->activeSite();
|
||||
if (site.layer() &&
|
||||
site.layer()->isImage() &&
|
||||
site.layer()->isEditable()) {
|
||||
Command* command = Commands::instance()->byId(CommandId::LayerOpacity());
|
||||
if (command) {
|
||||
int opacity = static_cast<doc::LayerImage*>(site.layer())->opacity();
|
||||
opacity = MID(0, opacity+dz*255/10, 255);
|
||||
|
||||
Params params;
|
||||
params.set("opacity",
|
||||
base::convert_to<std::string>(opacity).c_str());
|
||||
UIContext::instance()->executeCommand(command, params);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::CelOpacity: {
|
||||
Site site = UIContext::instance()->activeSite();
|
||||
if (site.layer() &&
|
||||
site.layer()->isImage() &&
|
||||
site.layer()->isEditable() &&
|
||||
site.cel()) {
|
||||
Command* command = Commands::instance()->byId(CommandId::CelOpacity());
|
||||
if (command) {
|
||||
int opacity = site.cel()->opacity();
|
||||
opacity = MID(0, opacity+dz*255/10, 255);
|
||||
Params params;
|
||||
params.set("opacity",
|
||||
base::convert_to<std::string>(opacity).c_str());
|
||||
UIContext::instance()->executeCommand(command, params);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::Alpha: {
|
||||
ColorBar* colorBar = ColorBar::instance();
|
||||
Color c = colorBar->getFgColor();
|
||||
int a = c.getAlpha();
|
||||
a = MID(0, a+dz*255/10, 255);
|
||||
c.setAlpha(a);
|
||||
colorBar->setFgColor(c);
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::HslHue:
|
||||
case WheelAction::HslSaturation:
|
||||
case WheelAction::HslLightness: {
|
||||
ColorBar* colorBar = ColorBar::instance();
|
||||
Color c = colorBar->getFgColor();
|
||||
double
|
||||
h = c.getHslHue(),
|
||||
s = c.getHslSaturation(),
|
||||
l = c.getHslLightness();
|
||||
switch (wheelAction) {
|
||||
case WheelAction::HslHue: h = h+dz*10.0; break;
|
||||
case WheelAction::HslSaturation: s = s+dz/10.0; break;
|
||||
case WheelAction::HslLightness: l = l+dz/10.0; break;
|
||||
}
|
||||
colorBar->setFgColor(Color::fromHsl(MID(0.0, h, 360.0),
|
||||
MID(0.0, s, 1.0),
|
||||
MID(0.0, l, 1.0)));
|
||||
break;
|
||||
}
|
||||
|
||||
case WheelAction::HsvHue:
|
||||
case WheelAction::HsvSaturation:
|
||||
case WheelAction::HsvValue: {
|
||||
ColorBar* colorBar = ColorBar::instance();
|
||||
Color c = colorBar->getFgColor();
|
||||
double
|
||||
h = c.getHsvHue(),
|
||||
s = c.getHsvSaturation(),
|
||||
v = c.getHsvValue();
|
||||
switch (wheelAction) {
|
||||
case WheelAction::HsvHue: h = h+dz*10.0; break;
|
||||
case WheelAction::HsvSaturation: s = s+dz/10.0; break;
|
||||
case WheelAction::HsvValue: v = v+dz/10.0; break;
|
||||
}
|
||||
colorBar->setFgColor(Color::fromHsv(MID(0.0, h, 360.0),
|
||||
MID(0.0, s, 1.0),
|
||||
MID(0.0, v, 1.0)));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -37,6 +37,7 @@ namespace app {
|
||||
Tool,
|
||||
Quicktool,
|
||||
Action,
|
||||
WheelAction,
|
||||
};
|
||||
|
||||
// TODO This should be called "KeyActionModifier" or something similar
|
||||
@ -61,6 +62,35 @@ namespace app {
|
||||
RotateShape = 0x00010000,
|
||||
};
|
||||
|
||||
enum class WheelAction {
|
||||
None,
|
||||
Zoom,
|
||||
VScroll,
|
||||
HScroll,
|
||||
FgColor,
|
||||
BgColor,
|
||||
Frame,
|
||||
BrushSize,
|
||||
BrushAngle,
|
||||
ToolSameGroup,
|
||||
ToolOtherGroup,
|
||||
Layer,
|
||||
InkOpacity,
|
||||
LayerOpacity,
|
||||
CelOpacity,
|
||||
Alpha,
|
||||
HslHue,
|
||||
HslSaturation,
|
||||
HslLightness,
|
||||
HsvHue,
|
||||
HsvSaturation,
|
||||
HsvValue,
|
||||
|
||||
// Range
|
||||
First = Zoom,
|
||||
Last = HsvValue,
|
||||
};
|
||||
|
||||
inline KeyAction operator&(KeyAction a, KeyAction b) {
|
||||
return KeyAction(int(a) & int(b));
|
||||
}
|
||||
@ -70,6 +100,7 @@ namespace app {
|
||||
Key(Command* command, const Params& params, KeyContext keyContext);
|
||||
Key(KeyType type, tools::Tool* tool);
|
||||
explicit Key(KeyAction action);
|
||||
explicit Key(WheelAction action);
|
||||
|
||||
KeyType type() const { return m_type; }
|
||||
const ui::Accelerators& accels() const {
|
||||
@ -80,7 +111,7 @@ namespace app {
|
||||
const ui::Accelerators& userRemovedAccels() const { return m_userRemoved; }
|
||||
|
||||
void add(const ui::Accelerator& accel, KeySource source);
|
||||
bool isPressed(ui::Message* msg) const;
|
||||
bool isPressed(const ui::Message* msg) const;
|
||||
bool isPressed() const;
|
||||
bool isLooselyPressed() const;
|
||||
|
||||
@ -90,6 +121,8 @@ namespace app {
|
||||
// Resets user accelerators to the original ones.
|
||||
void reset();
|
||||
|
||||
void copyOriginalToUser();
|
||||
|
||||
// for KeyType::Command
|
||||
Command* command() const { return m_command; }
|
||||
const Params& params() const { return m_params; }
|
||||
@ -98,6 +131,8 @@ namespace app {
|
||||
tools::Tool* tool() const { return m_tool; }
|
||||
// for KeyType::Action
|
||||
KeyAction action() const { return m_action; }
|
||||
// for KeyType::WheelAction
|
||||
WheelAction wheelAction() const { return m_wheelAction; }
|
||||
|
||||
std::string triggerString() const;
|
||||
|
||||
@ -116,6 +151,8 @@ namespace app {
|
||||
tools::Tool* m_tool;
|
||||
// for KeyType::Action
|
||||
KeyAction m_action;
|
||||
// for KeyType::WheelAction
|
||||
WheelAction m_wheelAction;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Key> KeyPtr;
|
||||
@ -131,6 +168,9 @@ namespace base {
|
||||
template<> app::KeyAction convert_to(const std::string& from);
|
||||
template<> std::string convert_to(const app::KeyAction& from);
|
||||
|
||||
template<> app::WheelAction convert_to(const std::string& from);
|
||||
template<> std::string convert_to(const app::WheelAction& from);
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@ namespace app {
|
||||
MoveTool,
|
||||
FreehandTool,
|
||||
ShapeTool,
|
||||
MouseWheel,
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -54,8 +54,36 @@ namespace {
|
||||
{ NULL , NULL , app::KeyAction::None }
|
||||
};
|
||||
|
||||
const char* get_shortcut(TiXmlElement* elem)
|
||||
{
|
||||
static struct {
|
||||
const char* name;
|
||||
const char* userfriendly;
|
||||
app::WheelAction action;
|
||||
} wheel_actions[] = {
|
||||
{ "Zoom" , "Zoom" , app::WheelAction::Zoom },
|
||||
{ "VScroll" , "Scroll: Vertically" , app::WheelAction::VScroll },
|
||||
{ "HScroll" , "Scroll: Horizontally" , app::WheelAction::HScroll },
|
||||
{ "FgColor" , "Color: Foreground Palette Entry" , app::WheelAction::FgColor },
|
||||
{ "BgColor" , "Color: Background Palette Entry" , app::WheelAction::BgColor },
|
||||
{ "Frame" , "Change Frame" , app::WheelAction::Frame },
|
||||
{ "BrushSize" , "Change Brush Size" , app::WheelAction::BrushSize },
|
||||
{ "BrushAngle" , "Change Brush Angle" , app::WheelAction::BrushAngle },
|
||||
{ "ToolSameGroup" , "Change Tool (same group)" , app::WheelAction::ToolSameGroup },
|
||||
{ "ToolOtherGroup" , "Change Tool" , app::WheelAction::ToolOtherGroup },
|
||||
{ "Layer" , "Change Layer" , app::WheelAction::Layer },
|
||||
{ "InkOpacity" , "Change Ink Opacity" , app::WheelAction::InkOpacity },
|
||||
{ "LayerOpacity" , "Change Layer Opacity" , app::WheelAction::LayerOpacity },
|
||||
{ "CelOpacity" , "Change Cel Opacity" , app::WheelAction::CelOpacity },
|
||||
{ "Alpha" , "Color: Alpha" , app::WheelAction::Alpha },
|
||||
{ "HslHue" , "Color: HSL Hue" , app::WheelAction::HslHue },
|
||||
{ "HslSaturation", "Color: HSL Saturation" , app::WheelAction::HslSaturation },
|
||||
{ "HslLightness" , "Color: HSL Lightness" , app::WheelAction::HslLightness },
|
||||
{ "HsvHue" , "Color: HSV Hue" , app::WheelAction::HsvHue },
|
||||
{ "HsvSaturation", "Color: HSV Saturation" , app::WheelAction::HsvSaturation },
|
||||
{ "HsvValue" , "Color: HSV Value" , app::WheelAction::HsvValue },
|
||||
{ nullptr , nullptr , app::WheelAction::None }
|
||||
};
|
||||
|
||||
const char* get_shortcut(TiXmlElement* elem) {
|
||||
const char* shortcut = NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -72,13 +100,20 @@ namespace {
|
||||
return shortcut;
|
||||
}
|
||||
|
||||
std::string get_user_friendly_string_for_keyaction(app::KeyAction action)
|
||||
{
|
||||
std::string get_user_friendly_string_for_keyaction(app::KeyAction action) {
|
||||
for (int c=0; actions[c].name; ++c) {
|
||||
if (action == actions[c].action)
|
||||
return actions[c].userfriendly;
|
||||
}
|
||||
return "";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string get_user_friendly_string_for_wheelaction(app::WheelAction wheelAction) {
|
||||
for (int c=0; wheel_actions[c].name; ++c) {
|
||||
if (wheelAction == wheel_actions[c].action)
|
||||
return wheel_actions[c].userfriendly;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@ -87,12 +122,10 @@ namespace base {
|
||||
|
||||
template<> app::KeyAction convert_to(const std::string& from) {
|
||||
app::KeyAction action = app::KeyAction::None;
|
||||
|
||||
for (int c=0; actions[c].name; ++c) {
|
||||
if (from == actions[c].name)
|
||||
return actions[c].action;
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@ -104,6 +137,23 @@ namespace base {
|
||||
return "";
|
||||
}
|
||||
|
||||
template<> app::WheelAction convert_to(const std::string& from) {
|
||||
app::WheelAction action = app::WheelAction::None;
|
||||
for (int c=0; wheel_actions[c].name; ++c) {
|
||||
if (from == wheel_actions[c].name)
|
||||
return wheel_actions[c].action;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
template<> std::string convert_to(const app::WheelAction& from) {
|
||||
for (int c=0; wheel_actions[c].name; ++c) {
|
||||
if (from == wheel_actions[c].action)
|
||||
return wheel_actions[c].name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
namespace app {
|
||||
@ -178,6 +228,15 @@ Key::Key(KeyAction action)
|
||||
}
|
||||
}
|
||||
|
||||
Key::Key(WheelAction wheelAction)
|
||||
: m_type(KeyType::WheelAction)
|
||||
, m_useUsers(false)
|
||||
, m_keycontext(KeyContext::MouseWheel)
|
||||
, m_action(KeyAction::None)
|
||||
, m_wheelAction(wheelAction)
|
||||
{
|
||||
}
|
||||
|
||||
void Key::add(const ui::Accelerator& accel, KeySource source)
|
||||
{
|
||||
Accelerators* accels = &m_accels;
|
||||
@ -200,20 +259,31 @@ void Key::add(const ui::Accelerator& accel, KeySource source)
|
||||
accels->add(accel);
|
||||
}
|
||||
|
||||
bool Key::isPressed(Message* msg) const
|
||||
bool Key::isPressed(const Message* msg) const
|
||||
{
|
||||
ASSERT(dynamic_cast<KeyMessage*>(msg) != NULL);
|
||||
|
||||
for (const Accelerator& accel : accels()) {
|
||||
if (accel.isPressed(msg->modifiers(),
|
||||
static_cast<KeyMessage*>(msg)->scancode(),
|
||||
static_cast<KeyMessage*>(msg)->unicodeChar()) &&
|
||||
(m_keycontext == KeyContext::Any ||
|
||||
m_keycontext == KeyboardShortcuts::instance()->getCurrentKeyContext())) {
|
||||
return true;
|
||||
if (auto keyMsg = dynamic_cast<const KeyMessage*>(msg)) {
|
||||
for (const Accelerator& accel : accels()) {
|
||||
if (accel.isPressed(keyMsg->modifiers(),
|
||||
keyMsg->scancode(),
|
||||
keyMsg->unicodeChar()) &&
|
||||
(m_keycontext == KeyContext::Any ||
|
||||
m_keycontext == KeyboardShortcuts::instance()->getCurrentKeyContext())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto mouseMsg = dynamic_cast<const MouseMessage*>(msg)) {
|
||||
for (const Accelerator& accel : accels()) {
|
||||
if ((accel.modifiers() == mouseMsg->modifiers()) &&
|
||||
(m_keycontext == KeyContext::Any ||
|
||||
// TODO we could have multiple mouse wheel key-context,
|
||||
// like "sprite editor" context, or "timeline" context,
|
||||
// etc.
|
||||
m_keycontext == KeyContext::MouseWheel)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -260,6 +330,13 @@ void Key::reset()
|
||||
m_useUsers = false;
|
||||
}
|
||||
|
||||
void Key::copyOriginalToUser()
|
||||
{
|
||||
m_users = m_accels;
|
||||
m_userRemoved.clear();
|
||||
m_useUsers = true;
|
||||
}
|
||||
|
||||
std::string Key::triggerString() const
|
||||
{
|
||||
switch (m_type) {
|
||||
@ -275,6 +352,8 @@ std::string Key::triggerString() const
|
||||
}
|
||||
case KeyType::Action:
|
||||
return get_user_friendly_string_for_keyaction(m_action);
|
||||
case KeyType::WheelAction:
|
||||
return get_user_friendly_string_for_wheelaction(m_wheelAction);
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
@ -370,7 +449,7 @@ void KeyboardShortcuts::importFile(TiXmlElement* rootElement, KeySource source)
|
||||
}
|
||||
|
||||
// Load keyboard shortcuts for tools
|
||||
// <gui><keyboard><tools><key>
|
||||
// <keyboard><tools><key>
|
||||
xmlKey = handle
|
||||
.FirstChild("tools")
|
||||
.FirstChild("key").ToElement();
|
||||
@ -398,7 +477,7 @@ void KeyboardShortcuts::importFile(TiXmlElement* rootElement, KeySource source)
|
||||
}
|
||||
|
||||
// Load keyboard shortcuts for quicktools
|
||||
// <gui><keyboard><quicktools><key>
|
||||
// <keyboard><quicktools><key>
|
||||
xmlKey = handle
|
||||
.FirstChild("quicktools")
|
||||
.FirstChild("key").ToElement();
|
||||
@ -426,22 +505,52 @@ void KeyboardShortcuts::importFile(TiXmlElement* rootElement, KeySource source)
|
||||
}
|
||||
|
||||
// Load special keyboard shortcuts for sprite editor customization
|
||||
// <gui><keyboard><spriteeditor>
|
||||
// <keyboard><actions><key>
|
||||
xmlKey = handle
|
||||
.FirstChild("actions")
|
||||
.FirstChild("key").ToElement();
|
||||
while (xmlKey) {
|
||||
const char* tool_action = xmlKey->Attribute("action");
|
||||
const char* tool_key = get_shortcut(xmlKey);
|
||||
const char* action_id = xmlKey->Attribute("action");
|
||||
const char* action_key = get_shortcut(xmlKey);
|
||||
bool removed = bool_attr_is_true(xmlKey, "removed");
|
||||
|
||||
if (tool_action) {
|
||||
KeyAction action = base::convert_to<KeyAction, std::string>(tool_action);
|
||||
if (action_id) {
|
||||
KeyAction action = base::convert_to<KeyAction, std::string>(action_id);
|
||||
if (action != KeyAction::None) {
|
||||
KeyPtr key = this->action(action);
|
||||
if (key && tool_key) {
|
||||
LOG(VERBOSE) << "KEYS: Shortcut for action " << tool_action << ": " << tool_key << "\n";
|
||||
Accelerator accel(tool_key);
|
||||
if (key && action_key) {
|
||||
LOG(VERBOSE) << "KEYS: Shortcut for action " << action_id
|
||||
<< ": " << action_key << "\n";
|
||||
Accelerator accel(action_key);
|
||||
|
||||
if (!removed)
|
||||
key->add(accel, source);
|
||||
else
|
||||
key->disableAccel(accel);
|
||||
}
|
||||
}
|
||||
}
|
||||
xmlKey = xmlKey->NextSiblingElement();
|
||||
}
|
||||
|
||||
// Load special keyboard shortcuts for mouse wheel customization
|
||||
// <keyboard><wheel><key>
|
||||
xmlKey = handle
|
||||
.FirstChild("wheel")
|
||||
.FirstChild("key").ToElement();
|
||||
while (xmlKey) {
|
||||
const char* action_id = xmlKey->Attribute("action");
|
||||
const char* action_key = get_shortcut(xmlKey);
|
||||
bool removed = bool_attr_is_true(xmlKey, "removed");
|
||||
|
||||
if (action_id) {
|
||||
WheelAction action = base::convert_to<WheelAction, std::string>(action_id);
|
||||
if (action != WheelAction::None) {
|
||||
KeyPtr key = this->wheelAction(action);
|
||||
if (key && action_key) {
|
||||
LOG(VERBOSE) << "KEYS: Shortcut for wheel action " << action_id
|
||||
<< ": " << action_key << "\n";
|
||||
Accelerator accel(action_key);
|
||||
|
||||
if (!removed)
|
||||
key->add(accel, source);
|
||||
@ -472,6 +581,7 @@ void KeyboardShortcuts::exportFile(const std::string& filename)
|
||||
TiXmlElement tools("tools");
|
||||
TiXmlElement quicktools("quicktools");
|
||||
TiXmlElement actions("actions");
|
||||
TiXmlElement wheel("wheel");
|
||||
|
||||
keyboard.SetAttribute("version", XML_KEYBOARD_FILE_VERSION);
|
||||
|
||||
@ -479,11 +589,13 @@ void KeyboardShortcuts::exportFile(const std::string& filename)
|
||||
exportKeys(tools, KeyType::Tool);
|
||||
exportKeys(quicktools, KeyType::Quicktool);
|
||||
exportKeys(actions, KeyType::Action);
|
||||
exportKeys(wheel, KeyType::WheelAction);
|
||||
|
||||
keyboard.InsertEndChild(commands);
|
||||
keyboard.InsertEndChild(tools);
|
||||
keyboard.InsertEndChild(quicktools);
|
||||
keyboard.InsertEndChild(actions);
|
||||
keyboard.InsertEndChild(wheel);
|
||||
|
||||
TiXmlDeclaration declaration("1.0", "utf-8", "");
|
||||
doc->InsertEndChild(declaration);
|
||||
@ -540,6 +652,11 @@ void KeyboardShortcuts::exportAccel(TiXmlElement& parent, const Key* key, const
|
||||
elem.SetAttribute("action",
|
||||
base::convert_to<std::string>(key->action()).c_str());
|
||||
break;
|
||||
|
||||
case KeyType::WheelAction:
|
||||
elem.SetAttribute("action",
|
||||
base::convert_to<std::string>(key->wheelAction()).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
elem.SetAttribute("shortcut", accel.toString().c_str());
|
||||
@ -618,6 +735,20 @@ KeyPtr KeyboardShortcuts::action(KeyAction action)
|
||||
return key;
|
||||
}
|
||||
|
||||
KeyPtr KeyboardShortcuts::wheelAction(WheelAction wheelAction)
|
||||
{
|
||||
for (KeyPtr& key : m_keys) {
|
||||
if (key->type() == KeyType::WheelAction &&
|
||||
key->wheelAction() == wheelAction) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
KeyPtr key = std::make_shared<Key>(wheelAction);
|
||||
m_keys.push_back(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
void KeyboardShortcuts::disableAccel(const ui::Accelerator& accel,
|
||||
const KeyContext keyContext,
|
||||
const Key* newKey)
|
||||
@ -646,7 +777,7 @@ KeyContext KeyboardShortcuts::getCurrentKeyContext()
|
||||
return KeyContext::Normal;
|
||||
}
|
||||
|
||||
bool KeyboardShortcuts::getCommandFromKeyMessage(Message* msg, Command** command, Params* params)
|
||||
bool KeyboardShortcuts::getCommandFromKeyMessage(const Message* msg, Command** command, Params* params)
|
||||
{
|
||||
for (KeyPtr& key : m_keys) {
|
||||
if (key->type() == KeyType::Command && key->isPressed(msg)) {
|
||||
@ -685,7 +816,7 @@ KeyAction KeyboardShortcuts::getCurrentActionModifiers(KeyContext context)
|
||||
{
|
||||
KeyAction flags = KeyAction::None;
|
||||
|
||||
for (KeyPtr& key : m_keys) {
|
||||
for (const KeyPtr& key : m_keys) {
|
||||
if (key->type() == KeyType::Action &&
|
||||
key->keycontext() == context &&
|
||||
key->isLooselyPressed()) {
|
||||
@ -696,6 +827,64 @@ KeyAction KeyboardShortcuts::getCurrentActionModifiers(KeyContext context)
|
||||
return flags;
|
||||
}
|
||||
|
||||
WheelAction KeyboardShortcuts::getWheelActionFromMouseMessage(const KeyContext context,
|
||||
const ui::Message* msg)
|
||||
{
|
||||
for (const KeyPtr& key : m_keys) {
|
||||
if (key->type() == KeyType::WheelAction &&
|
||||
key->keycontext() == context &&
|
||||
key->isPressed(msg))
|
||||
return key->wheelAction();
|
||||
}
|
||||
return WheelAction::None;
|
||||
}
|
||||
|
||||
bool KeyboardShortcuts::hasMouseWheelCustomization() const
|
||||
{
|
||||
for (const KeyPtr& key : m_keys) {
|
||||
if (key->type() == KeyType::WheelAction &&
|
||||
!key->userAccels().empty())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Keys KeyboardShortcuts::getDefaultMouseWheelTable(const bool zoomWithWheel) const
|
||||
{
|
||||
Keys keys;
|
||||
KeyPtr key;
|
||||
|
||||
key = std::make_shared<Key>(WheelAction::Zoom);
|
||||
key->add(Accelerator(zoomWithWheel ? kKeyNoneModifier: kKeyCtrlModifier, kKeyNil, 0), KeySource::Original);
|
||||
keys.push_back(key);
|
||||
|
||||
if (!zoomWithWheel) {
|
||||
key = std::make_shared<Key>(WheelAction::VScroll);
|
||||
key->add(Accelerator(kKeyNoneModifier, kKeyNil, 0), KeySource::Original);
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
key = std::make_shared<Key>(WheelAction::HScroll);
|
||||
key->add(Accelerator(kKeyShiftModifier, kKeyNil, 0), KeySource::Original);
|
||||
keys.push_back(key);
|
||||
|
||||
key = std::make_shared<Key>(WheelAction::FgColor);
|
||||
key->add(Accelerator(kKeyAltModifier, kKeyNil, 0), KeySource::Original);
|
||||
keys.push_back(key);
|
||||
|
||||
key = std::make_shared<Key>(WheelAction::BgColor);
|
||||
key->add(Accelerator((KeyModifiers)(kKeyAltModifier | kKeyShiftModifier), kKeyNil, 0), KeySource::Original);
|
||||
keys.push_back(key);
|
||||
|
||||
if (zoomWithWheel) {
|
||||
key = std::make_shared<Key>(WheelAction::Frame);
|
||||
key->add(Accelerator(kKeyCtrlModifier, kKeyNil, 0), KeySource::Original);
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::string key_tooltip(const char* str, const app::Key* key)
|
||||
{
|
||||
std::string res;
|
||||
|
@ -40,15 +40,20 @@ namespace app {
|
||||
KeyPtr tool(tools::Tool* tool);
|
||||
KeyPtr quicktool(tools::Tool* tool);
|
||||
KeyPtr action(KeyAction action);
|
||||
KeyPtr wheelAction(WheelAction action);
|
||||
|
||||
void disableAccel(const ui::Accelerator& accel,
|
||||
const KeyContext keyContext,
|
||||
const Key* newKey);
|
||||
|
||||
KeyContext getCurrentKeyContext();
|
||||
bool getCommandFromKeyMessage(ui::Message* msg, Command** command, Params* params);
|
||||
bool getCommandFromKeyMessage(const ui::Message* msg, Command** command, Params* params);
|
||||
tools::Tool* getCurrentQuicktool(tools::Tool* currentTool);
|
||||
KeyAction getCurrentActionModifiers(KeyContext context);
|
||||
WheelAction getWheelActionFromMouseMessage(const KeyContext context,
|
||||
const ui::Message* msg);
|
||||
bool hasMouseWheelCustomization() const;
|
||||
Keys getDefaultMouseWheelTable(const bool zoomWithWheel) const;
|
||||
|
||||
// Generated when the tooltips are modified by the user.
|
||||
// Useful to regenerate tooltips with shortcuts.
|
||||
|
@ -89,6 +89,7 @@ SelectAccelerator::SelectAccelerator(const ui::Accelerator& accel, KeyContext ke
|
||||
: m_keyField(new KeyField(accel))
|
||||
, m_keyContext(keyContext)
|
||||
, m_accel(accel)
|
||||
, m_ok(false)
|
||||
, m_modified(false)
|
||||
{
|
||||
updateModifiers();
|
||||
@ -149,6 +150,7 @@ void SelectAccelerator::onClear()
|
||||
|
||||
void SelectAccelerator::onOK()
|
||||
{
|
||||
m_ok = true;
|
||||
m_modified = (m_origAccel != m_accel);
|
||||
closeWindow(NULL);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace app {
|
||||
public:
|
||||
SelectAccelerator(const ui::Accelerator& accelerator, KeyContext keyContext);
|
||||
|
||||
bool isOK() const { return m_ok; }
|
||||
bool isModified() const { return m_modified; }
|
||||
const ui::Accelerator& accel() const { return m_accel; }
|
||||
|
||||
@ -39,6 +40,7 @@ namespace app {
|
||||
KeyContext m_keyContext;
|
||||
ui::Accelerator m_origAccel;
|
||||
ui::Accelerator m_accel;
|
||||
bool m_ok;
|
||||
bool m_modified;
|
||||
};
|
||||
|
||||
|
@ -564,6 +564,14 @@ void ToolBar::selectTool(Tool* tool)
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ToolBar::selectToolGroup(tools::ToolGroup* toolGroup)
|
||||
{
|
||||
ASSERT(toolGroup);
|
||||
ASSERT(m_selectedInGroup[toolGroup]);
|
||||
if (m_selectedInGroup[toolGroup])
|
||||
selectTool(m_selectedInGroup[toolGroup]);
|
||||
}
|
||||
|
||||
void ToolBar::onClosePopup()
|
||||
{
|
||||
closeTipWindow();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -43,8 +43,9 @@ namespace app {
|
||||
|
||||
bool isToolVisible(tools::Tool* tool);
|
||||
void selectTool(tools::Tool* tool);
|
||||
void selectToolGroup(tools::ToolGroup* toolGroup);
|
||||
|
||||
void openTipWindow(tools::ToolGroup* tool_group, tools::Tool* tool);
|
||||
void openTipWindow(tools::ToolGroup* toolGroup, tools::Tool* tool);
|
||||
void closeTipWindow();
|
||||
|
||||
protected:
|
||||
|
Loading…
x
Reference in New Issue
Block a user