1 | // Aseprite |
2 | // Copyright (C) 2020 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/ui/select_accelerator.h" |
13 | |
14 | #include "app/ui/key.h" |
15 | #include "app/ui/keyboard_shortcuts.h" |
16 | #include "obs/signal.h" |
17 | #include "ui/entry.h" |
18 | #include "ui/message.h" |
19 | |
20 | #include <cctype> |
21 | |
22 | namespace app { |
23 | |
24 | using namespace ui; |
25 | |
26 | class SelectAccelerator::KeyField : public ui::Entry { |
27 | public: |
28 | KeyField(const Accelerator& accel) : ui::Entry(256, "" ) { |
29 | setTranslateDeadKeys(false); |
30 | setExpansive(true); |
31 | setFocusMagnet(true); |
32 | setAccel(accel); |
33 | } |
34 | |
35 | void setAccel(const Accelerator& accel) { |
36 | m_accel = accel; |
37 | updateText(); |
38 | } |
39 | |
40 | obs::signal<void(const ui::Accelerator*)> AccelChange; |
41 | |
42 | protected: |
43 | bool onProcessMessage(Message* msg) override { |
44 | switch (msg->type()) { |
45 | |
46 | case kKeyDownMessage: |
47 | if (hasFocus() && !isReadOnly()) { |
48 | KeyMessage* keymsg = static_cast<KeyMessage*>(msg); |
49 | if (!keymsg->scancode() && keymsg->unicodeChar() < 32) |
50 | break; |
51 | |
52 | KeyModifiers modifiers = keymsg->modifiers(); |
53 | |
54 | if (keymsg->scancode() == kKeySpace) |
55 | modifiers = (KeyModifiers)(modifiers & ~kKeySpaceModifier); |
56 | |
57 | m_accel = Accelerator( |
58 | modifiers, |
59 | keymsg->scancode(), |
60 | keymsg->unicodeChar() > 32 ? |
61 | std::tolower(keymsg->unicodeChar()): 0); |
62 | |
63 | // Convert the accelerator to a string, and parse it |
64 | // again. Just to obtain the exact accelerator we'll read |
65 | // when we import the gui.xml file or an .aseprite-keys file. |
66 | m_accel = Accelerator(m_accel.toString()); |
67 | |
68 | updateText(); |
69 | |
70 | AccelChange(&m_accel); |
71 | return true; |
72 | } |
73 | break; |
74 | } |
75 | return Entry::onProcessMessage(msg); |
76 | } |
77 | |
78 | void updateText() { |
79 | setText( |
80 | Accelerator( |
81 | kKeyNoneModifier, |
82 | m_accel.scancode(), |
83 | m_accel.unicodeChar()).toString().c_str()); |
84 | } |
85 | |
86 | Accelerator m_accel; |
87 | }; |
88 | |
89 | SelectAccelerator::SelectAccelerator(const ui::Accelerator& accel, |
90 | const KeyContext keyContext, |
91 | const KeyboardShortcuts& currentKeys) |
92 | : m_keyField(new KeyField(accel)) |
93 | , m_keyContext(keyContext) |
94 | , m_currentKeys(currentKeys) |
95 | , m_accel(accel) |
96 | , m_ok(false) |
97 | , m_modified(false) |
98 | { |
99 | updateModifiers(); |
100 | updateAssignedTo(); |
101 | |
102 | keyPlaceholder()->addChild(m_keyField); |
103 | |
104 | alt()->Click.connect([this]{ onModifierChange(kKeyAltModifier, alt()); }); |
105 | cmd()->Click.connect([this]{ onModifierChange(kKeyCmdModifier, cmd()); }); |
106 | ctrl()->Click.connect([this]{ onModifierChange(kKeyCtrlModifier, ctrl()); }); |
107 | shift()->Click.connect([this]{ onModifierChange(kKeyShiftModifier, shift()); }); |
108 | space()->Click.connect([this]{ onModifierChange(kKeySpaceModifier, space()); }); |
109 | win()->Click.connect([this]{ onModifierChange(kKeyWinModifier, win()); }); |
110 | |
111 | m_keyField->AccelChange.connect(&SelectAccelerator::onAccelChange, this); |
112 | clearButton()->Click.connect([this]{ onClear(); }); |
113 | okButton()->Click.connect([this]{ onOK(); }); |
114 | cancelButton()->Click.connect([this]{ onCancel(); }); |
115 | |
116 | addChild(&m_tooltipManager); |
117 | } |
118 | |
119 | void SelectAccelerator::onModifierChange(KeyModifiers modifier, CheckBox* checkbox) |
120 | { |
121 | bool state = (checkbox->isSelected()); |
122 | KeyModifiers modifiers = m_accel.modifiers(); |
123 | KeyScancode scancode = m_accel.scancode(); |
124 | int unicodeChar = m_accel.unicodeChar(); |
125 | |
126 | modifiers = (KeyModifiers)((modifiers & ~modifier) | (state ? modifier : 0)); |
127 | if (modifiers == kKeySpaceModifier && scancode == kKeySpace) |
128 | modifiers = kKeyNoneModifier; |
129 | |
130 | m_accel = Accelerator(modifiers, scancode, unicodeChar); |
131 | |
132 | m_keyField->setAccel(m_accel); |
133 | m_keyField->requestFocus(); |
134 | updateAssignedTo(); |
135 | } |
136 | |
137 | void SelectAccelerator::onAccelChange(const ui::Accelerator* accel) |
138 | { |
139 | m_accel = *accel; |
140 | updateModifiers(); |
141 | updateAssignedTo(); |
142 | } |
143 | |
144 | void SelectAccelerator::onClear() |
145 | { |
146 | m_accel = Accelerator(kKeyNoneModifier, kKeyNil, 0); |
147 | |
148 | m_keyField->setAccel(m_accel); |
149 | updateModifiers(); |
150 | updateAssignedTo(); |
151 | |
152 | m_keyField->requestFocus(); |
153 | } |
154 | |
155 | void SelectAccelerator::onOK() |
156 | { |
157 | m_ok = true; |
158 | m_modified = (m_origAccel != m_accel); |
159 | closeWindow(NULL); |
160 | } |
161 | |
162 | void SelectAccelerator::onCancel() |
163 | { |
164 | closeWindow(NULL); |
165 | } |
166 | |
167 | void SelectAccelerator::updateModifiers() |
168 | { |
169 | alt()->setSelected(m_accel.modifiers() & kKeyAltModifier ? true: false); |
170 | ctrl()->setSelected(m_accel.modifiers() & kKeyCtrlModifier ? true: false); |
171 | shift()->setSelected(m_accel.modifiers() & kKeyShiftModifier ? true: false); |
172 | space()->setSelected(m_accel.modifiers() & kKeySpaceModifier ? true: false); |
173 | #if __APPLE__ |
174 | win()->setVisible(false); |
175 | cmd()->setSelected(m_accel.modifiers() & kKeyCmdModifier ? true: false); |
176 | #else |
177 | #if __linux__ |
178 | win()->setText(kWinKeyName); |
179 | m_tooltipManager.addTooltipFor(win(), "Also known as Windows key, logo key,\ncommand key, or system key." , TOP); |
180 | #endif |
181 | win()->setSelected(m_accel.modifiers() & kKeyWinModifier ? true: false); |
182 | cmd()->setVisible(false); |
183 | #endif |
184 | } |
185 | |
186 | void SelectAccelerator::updateAssignedTo() |
187 | { |
188 | std::string res = "None" ; |
189 | |
190 | for (const KeyPtr& key : m_currentKeys) { |
191 | if (key->keycontext() == m_keyContext && |
192 | key->hasAccel(m_accel)) { |
193 | res = key->triggerString(); |
194 | break; |
195 | } |
196 | } |
197 | |
198 | assignedTo()->setText(res); |
199 | } |
200 | |
201 | } // namespace app |
202 | |