|
| 1 | +#include "hotkeyBinder.h" |
1 | 2 | #include <i18n.h> |
2 | 3 | #include "engine.h" |
3 | 4 | #include "hotkeyConfig.h" |
4 | | -#include "hotkeyBinder.h" |
5 | 5 | #include "theme.h" |
6 | 6 |
|
| 7 | +// Track which binder and which key are actively performing a rebind. |
| 8 | +static GuiHotkeyBinder* active_rebinder = nullptr; |
| 9 | +static sp::io::Keybinding* active_key = nullptr; |
7 | 10 |
|
8 | | -GuiHotkeyBinder::GuiHotkeyBinder(GuiContainer* owner, string id, sp::io::Keybinding* key) |
9 | | -: GuiElement(owner, id), has_focus(false), key(key) |
| 11 | +GuiHotkeyBinder::GuiHotkeyBinder(GuiContainer* owner, string id, sp::io::Keybinding* key, |
| 12 | + sp::io::Keybinding::Type display_filter, sp::io::Keybinding::Type capture_filter) |
| 13 | +: GuiElement(owner, id), key(key), display_filter(display_filter), capture_filter(capture_filter) |
10 | 14 | { |
| 15 | + // Use textentry theme styles for binder inputs. |
| 16 | + // Someday, this should allow for icon representations instead of relying |
| 17 | + // on text. |
11 | 18 | front_style = theme->getStyle("textentry.front"); |
12 | 19 | back_style = theme->getStyle("textentry.back"); |
13 | 20 | } |
14 | 21 |
|
| 22 | +bool GuiHotkeyBinder::isAnyRebinding() |
| 23 | +{ |
| 24 | + return active_rebinder != nullptr; |
| 25 | +} |
| 26 | + |
| 27 | +void GuiHotkeyBinder::clearFilteredKeys() |
| 28 | +{ |
| 29 | + // Filter binds for this control by their type. |
| 30 | + int count = 0; |
| 31 | + while (key->getKeyType(count) != sp::io::Keybinding::Type::None) count++; |
| 32 | + for (int i = count - 1; i >= 0; --i) |
| 33 | + if (key->getKeyType(i) & display_filter) key->removeKey(i); |
| 34 | +} |
| 35 | + |
15 | 36 | bool GuiHotkeyBinder::onMouseDown(sp::io::Pointer::Button button, glm::vec2 position, sp::io::Pointer::ID id) |
16 | 37 | { |
17 | | - if (button != sp::io::Pointer::Button::Middle) |
18 | | - key->clearKeys(); |
19 | | - if (button != sp::io::Pointer::Button::Right) |
20 | | - key->startUserRebind(sp::io::Keybinding::Type::Keyboard | sp::io::Keybinding::Type::Joystick | sp::io::Keybinding::Type::Controller | sp::io::Keybinding::Type::Virtual); |
| 38 | + // If this binder is already rebinding, just take the input and skip this. |
| 39 | + // This should allow binding left/middle/right-click without also changing |
| 40 | + // the binder's state at the same time. |
| 41 | + if (active_rebinder == this) return true; |
| 42 | + |
| 43 | + // Left click: Assign input. Middle click: Add input. |
| 44 | + // Right click: Remove last input. Ignore all other mouse buttons. |
| 45 | + if (button == sp::io::Pointer::Button::Left) |
| 46 | + clearFilteredKeys(); |
| 47 | + if (button == sp::io::Pointer::Button::Right) |
| 48 | + { |
| 49 | + int count = 0; |
| 50 | + while (key->getKeyType(count) != sp::io::Keybinding::Type::None) count++; |
| 51 | + for (int i = count - 1; i >= 0; --i) |
| 52 | + { |
| 53 | + if (key->getKeyType(i) & display_filter) |
| 54 | + { |
| 55 | + key->removeKey(i); |
| 56 | + break; |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + if (button == sp::io::Pointer::Button::Left || button == sp::io::Pointer::Button::Middle) |
| 62 | + { |
| 63 | + const sp::io::Keybinding::Type mouse_types = sp::io::Keybinding::Type::Pointer | sp::io::Keybinding::Type::MouseMovement | sp::io::Keybinding::Type::MouseWheel; |
| 64 | + if (capture_filter & mouse_types) |
| 65 | + { |
| 66 | + // Delay startUserRebind until onMouseUp so that the triggering |
| 67 | + // mouse click is not immediately captured as the new binding. |
| 68 | + pending_rebind = true; |
| 69 | + } |
| 70 | + else |
| 71 | + { |
| 72 | + active_rebinder = this; |
| 73 | + active_key = key; |
| 74 | + key->startUserRebind(capture_filter); |
| 75 | + } |
| 76 | + } |
| 77 | + |
21 | 78 | return true; |
22 | 79 | } |
23 | 80 |
|
| 81 | +void GuiHotkeyBinder::onMouseUp(glm::vec2 position, sp::io::Pointer::ID id) |
| 82 | +{ |
| 83 | + // Complete a pending rebind action. |
| 84 | + if (pending_rebind) |
| 85 | + { |
| 86 | + pending_rebind = false; |
| 87 | + active_rebinder = this; |
| 88 | + active_key = key; |
| 89 | + key->startUserRebind(capture_filter); |
| 90 | + } |
| 91 | +} |
| 92 | + |
24 | 93 | void GuiHotkeyBinder::onDraw(sp::RenderTarget& renderer) |
25 | 94 | { |
26 | | - focus = key->isUserRebinding(); |
| 95 | + // Clear the active rebind indicator only when the tracked key's rebind |
| 96 | + // completes. |
| 97 | + if (active_key != nullptr && !active_key->isUserRebinding()) |
| 98 | + { |
| 99 | + active_rebinder = nullptr; |
| 100 | + active_key = nullptr; |
| 101 | + } |
| 102 | + |
| 103 | + bool is_my_rebind = (active_rebinder == this); |
| 104 | + focus = is_my_rebind; |
| 105 | + |
27 | 106 | const auto& back = back_style->get(getState()); |
28 | 107 | const auto& front = front_style->get(getState()); |
29 | 108 |
|
30 | 109 | renderer.drawStretched(rect, back.texture, back.color); |
31 | 110 |
|
32 | | - string text = key->getHumanReadableKeyName(0); |
33 | | - for(int n=1; key->getKeyType(n) != sp::io::Keybinding::Type::None; n++) |
34 | | - text += "," + key->getHumanReadableKeyName(n); |
35 | | - if (key->isUserRebinding()) |
36 | | - text = tr("[New input]"); |
37 | | - renderer.drawText(sp::Rect(rect.position.x + 16, rect.position.y, rect.size.x, rect.size.y), text, sp::Alignment::CenterLeft, front.size, front.font, front.color); |
| 111 | + string text; |
| 112 | + |
| 113 | + // If this is the active rebinder, update its state to indicate that it's |
| 114 | + // ready for input. Otherwise, list the associated binds. |
| 115 | + // TODO: This list can get quite long. What should it do on overflow? |
| 116 | + if (is_my_rebind) text = tr("[New input]"); |
| 117 | + else |
| 118 | + { |
| 119 | + for (int n = 0; key->getKeyType(n) != sp::io::Keybinding::Type::None; n++) |
| 120 | + { |
| 121 | + if (key->getKeyType(n) & display_filter) |
| 122 | + { |
| 123 | + if (!text.empty()) text += ","; |
| 124 | + text += key->getHumanReadableKeyName(n); |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + renderer.drawText(sp::Rect(rect.position.x + 16.0f, rect.position.y, rect.size.x, rect.size.y), text, sp::Alignment::CenterLeft, front.size, front.font, front.color); |
38 | 130 | } |
0 commit comments