mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-21 21:41:02 +00:00
Fix several keyboard issues deadling with special characters/dead keys
This change adds support to write text with dead keys, and assign keyboard shortcuts to special key combinations with Unicode characters on macOS and Windows. Fix #1083, close #796
This commit is contained in:
parent
b537b5261a
commit
00099390da
@ -383,6 +383,8 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
break;
|
||||
|
||||
case kKeyDownMessage: {
|
||||
auto keymsg = static_cast<KeyMessage*>(msg);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Ctrl+Shift+Q generates a crash (useful to test the anticrash feature)
|
||||
if (msg->ctrlPressed() &&
|
||||
@ -396,7 +398,7 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
// Ctrl+Shift+R recover active sprite from the backup store
|
||||
if (msg->ctrlPressed() &&
|
||||
msg->shiftPressed() &&
|
||||
static_cast<KeyMessage*>(msg)->scancode() == kKeyR &&
|
||||
keymsg->scancode() == kKeyR &&
|
||||
App::instance()->dataRecovery() &&
|
||||
App::instance()->dataRecovery()->activeSession() &&
|
||||
current_editor &&
|
||||
|
@ -23,6 +23,7 @@ using namespace ui;
|
||||
class SelectAccelerator::KeyField : public ui::Entry {
|
||||
public:
|
||||
KeyField(const Accelerator& accel) : ui::Entry(256, "") {
|
||||
setTranslateDeadKeys(false);
|
||||
setExpansive(true);
|
||||
setFocusMagnet(true);
|
||||
setAccel(accel);
|
||||
@ -38,9 +39,13 @@ public:
|
||||
protected:
|
||||
bool onProcessMessage(Message* msg) override {
|
||||
switch (msg->type()) {
|
||||
|
||||
case kKeyDownMessage:
|
||||
if (hasFocus() && !isReadOnly()) {
|
||||
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
|
||||
if (!keymsg->scancode() && keymsg->unicodeChar() < 32)
|
||||
break;
|
||||
|
||||
KeyModifiers modifiers = keymsg->modifiers();
|
||||
|
||||
if (keymsg->scancode() == kKeySpace)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2015 David Capello
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -21,14 +21,12 @@ int key_repeated[KEY_MAX];
|
||||
int she_keyboard_ucallback(int unicode_char, int* scancode)
|
||||
{
|
||||
int c = ((*scancode) & 0x7f);
|
||||
Event ev;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setScancode(static_cast<KeyScancode>(c));
|
||||
if (unicode_char > 0)
|
||||
ev.setUnicodeChar(unicode_char);
|
||||
else
|
||||
ev.setUnicodeChar(::scancode_to_ascii(c));
|
||||
ev.setRepeat(key_repeated[c]++);
|
||||
queue_event(ev);
|
||||
|
||||
|
@ -206,6 +206,10 @@ public:
|
||||
return sur;
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) override {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
System* create_system() {
|
||||
@ -247,6 +251,14 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
return key[scancode] ? true: false;
|
||||
}
|
||||
|
||||
int get_unicode_from_scancode(KeyScancode scancode)
|
||||
{
|
||||
if (is_key_pressed(scancode))
|
||||
return scancode_to_ascii(scancode);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear_keyboard_buffer()
|
||||
{
|
||||
clear_keybuf();
|
||||
|
@ -56,6 +56,7 @@ namespace she {
|
||||
m_scancode(kKeyNil),
|
||||
m_modifiers(kKeyUninitializedModifier),
|
||||
m_unicodeChar(0),
|
||||
m_isDead(false),
|
||||
m_repeat(0),
|
||||
m_preciseWheel(false),
|
||||
m_pointerType(PointerType::Unknown),
|
||||
@ -70,6 +71,7 @@ namespace she {
|
||||
KeyScancode scancode() const { return m_scancode; }
|
||||
KeyModifiers modifiers() const { return m_modifiers; }
|
||||
int unicodeChar() const { return m_unicodeChar; }
|
||||
bool isDeadKey() const { return m_isDead; }
|
||||
int repeat() const { return m_repeat; }
|
||||
gfx::Point position() const { return m_position; }
|
||||
gfx::Point wheelDelta() const { return m_wheelDelta; }
|
||||
@ -92,6 +94,7 @@ namespace she {
|
||||
void setScancode(KeyScancode scancode) { m_scancode = scancode; }
|
||||
void setModifiers(KeyModifiers modifiers) { m_modifiers = modifiers; }
|
||||
void setUnicodeChar(int unicodeChar) { m_unicodeChar = unicodeChar; }
|
||||
void setDeadKey(bool state) { m_isDead = state; }
|
||||
void setRepeat(int repeat) { m_repeat = repeat; }
|
||||
void setPosition(const gfx::Point& pos) { m_position = pos; }
|
||||
void setWheelDelta(const gfx::Point& delta) { m_wheelDelta = delta; }
|
||||
@ -108,6 +111,7 @@ namespace she {
|
||||
KeyScancode m_scancode;
|
||||
KeyModifiers m_modifiers;
|
||||
int m_unicodeChar;
|
||||
bool m_isDead;
|
||||
int m_repeat; // repeat=0 means the first time the key is pressed
|
||||
gfx::Point m_position;
|
||||
gfx::Point m_wheelDelta;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2013, 2015 David Capello
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -156,9 +156,17 @@ namespace she {
|
||||
kKeyScancodes = 127
|
||||
};
|
||||
|
||||
// Deprecated API, use modifiers in she::Event
|
||||
// TODO mark these functions as deprecated
|
||||
// TODO move these functions to she::System
|
||||
|
||||
// Returns true if the the given scancode key is pressed/actived.
|
||||
bool is_key_pressed(KeyScancode scancode);
|
||||
|
||||
// Returns the latest unicode character that activated the given
|
||||
// scancode.
|
||||
int get_unicode_from_scancode(KeyScancode scancode);
|
||||
|
||||
// Clears the keyboard buffer (used only in the Allegro port).
|
||||
// TODO (deprecated)
|
||||
void clear_keyboard_buffer();
|
||||
|
||||
} // namespace she
|
||||
|
@ -53,6 +53,8 @@
|
||||
- (void)updateCurrentCursor;
|
||||
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender;
|
||||
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender;
|
||||
- (void)doCommandBySelector:(SEL)selector;
|
||||
- (void)setTranslateDeadKeys:(BOOL)state;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
@ -17,14 +17,18 @@
|
||||
#include "she/osx/generate_drop_files.h"
|
||||
#include "she/osx/window.h"
|
||||
|
||||
#include <Carbon/Carbon.h> // For VK codes
|
||||
|
||||
using namespace she;
|
||||
|
||||
namespace {
|
||||
|
||||
// Internal array of pressed keys used in is_key_pressed()
|
||||
bool pressed_keys[kKeyScancodes];
|
||||
int g_pressedKeys[kKeyScancodes];
|
||||
bool g_translateDeadKeys = false;
|
||||
UInt32 g_lastDeadKeyState = 0;
|
||||
|
||||
inline gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event)
|
||||
gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event)
|
||||
{
|
||||
NSPoint point = [view convertPoint:[event locationInWindow]
|
||||
fromView:nil];
|
||||
@ -37,7 +41,7 @@ inline gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event)
|
||||
(view.bounds.size.height - point.y) / scale);
|
||||
}
|
||||
|
||||
inline Event::MouseButton get_mouse_buttons(NSEvent* event)
|
||||
Event::MouseButton get_mouse_buttons(NSEvent* event)
|
||||
{
|
||||
// Some Wacom drivers on OS X report right-clicks with
|
||||
// buttonNumber=0, so we've to check the type event anyway.
|
||||
@ -61,7 +65,7 @@ inline Event::MouseButton get_mouse_buttons(NSEvent* event)
|
||||
return Event::MouseButton::NoneButton;
|
||||
}
|
||||
|
||||
inline KeyModifiers get_modifiers_from_nsevent(NSEvent* event)
|
||||
KeyModifiers get_modifiers_from_nsevent(NSEvent* event)
|
||||
{
|
||||
int modifiers = kKeyNoneModifier;
|
||||
NSEventModifierFlags nsFlags = event.modifierFlags;
|
||||
@ -73,17 +77,43 @@ inline KeyModifiers get_modifiers_from_nsevent(NSEvent* event)
|
||||
return (KeyModifiers)modifiers;
|
||||
}
|
||||
|
||||
inline int get_unicodechar_from_nsevent(NSEvent* event)
|
||||
// Based on code from:
|
||||
// http://stackoverflow.com/questions/22566665/how-to-capture-unicode-from-key-events-without-an-nstextview
|
||||
// http://stackoverflow.com/questions/12547007/convert-key-code-into-key-equivalent-string
|
||||
// http://stackoverflow.com/questions/8263618/convert-virtual-key-code-to-unicode-string
|
||||
//
|
||||
// It includes a "translateDeadKeys" flag to avoid processing dead
|
||||
// keys in case that we want to use key
|
||||
CFStringRef get_unicode_from_key_code(NSEvent* event,
|
||||
const bool translateDeadKeys)
|
||||
{
|
||||
int chr = 0;
|
||||
// TODO we should use "event.characters" for a new kind of event (Event::Char or something like that)
|
||||
NSString* chars = event.charactersIgnoringModifiers;
|
||||
if (chars && chars.length >= 1) {
|
||||
chr = [chars characterAtIndex:0];
|
||||
if (chr < 32)
|
||||
chr = 0;
|
||||
}
|
||||
return chr;
|
||||
TISInputSourceRef inputSource = TISCopyCurrentKeyboardInputSource();
|
||||
CFDataRef keyLayoutData = (CFDataRef)TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData);
|
||||
const UCKeyboardLayout* keyLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(keyLayoutData);
|
||||
|
||||
UInt32 deadKeyState = (translateDeadKeys ? g_lastDeadKeyState: 0);
|
||||
UniChar output[4];
|
||||
UniCharCount length;
|
||||
|
||||
// Reference here:
|
||||
// https://developer.apple.com/reference/coreservices/1390584-uckeytranslate?language=objc
|
||||
UCKeyTranslate(
|
||||
keyLayout,
|
||||
event.keyCode,
|
||||
kUCKeyActionDown,
|
||||
((event.modifierFlags >> 16) & 0xFF),
|
||||
LMGetKbdType(),
|
||||
(translateDeadKeys ? 0: kUCKeyTranslateNoDeadKeysMask),
|
||||
&deadKeyState,
|
||||
sizeof(output) / sizeof(output[0]),
|
||||
&length,
|
||||
output);
|
||||
|
||||
if (translateDeadKeys)
|
||||
g_lastDeadKeyState = deadKeyState;
|
||||
|
||||
CFRelease(inputSource);
|
||||
return CFStringCreateWithCharacters(kCFAllocatorDefault, output, length);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@ -93,11 +123,19 @@ namespace she {
|
||||
bool is_key_pressed(KeyScancode scancode)
|
||||
{
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
return pressed_keys[scancode];
|
||||
return (g_pressedKeys[scancode] != 0);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int get_unicode_from_scancode(KeyScancode scancode)
|
||||
{
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
return g_pressedKeys[scancode];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace she
|
||||
|
||||
@implementation OSXView
|
||||
@ -167,17 +205,47 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
[super keyDown:event];
|
||||
|
||||
KeyScancode scancode = cocoavk_to_scancode(event.keyCode);
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
pressed_keys[scancode] = true;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setScancode(scancode);
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setRepeat(event.ARepeat ? 1: 0);
|
||||
ev.setUnicodeChar(get_unicodechar_from_nsevent(event));
|
||||
ev.setUnicodeChar(0);
|
||||
|
||||
queue_event(ev);
|
||||
bool sendMsg = true;
|
||||
|
||||
CFStringRef strRef = get_unicode_from_key_code(event, false);
|
||||
if (strRef) {
|
||||
int length = CFStringGetLength(strRef);
|
||||
if (length == 1)
|
||||
ev.setUnicodeChar(CFStringGetCharacterAtIndex(strRef, 0));
|
||||
CFRelease(strRef);
|
||||
}
|
||||
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
g_pressedKeys[scancode] = (ev.unicodeChar() ? ev.unicodeChar(): 1);
|
||||
|
||||
if (g_translateDeadKeys) {
|
||||
strRef = get_unicode_from_key_code(event, true);
|
||||
if (strRef) {
|
||||
int length = CFStringGetLength(strRef);
|
||||
if (length > 0) {
|
||||
sendMsg = false;
|
||||
for (int i=0; i<length; ++i) {
|
||||
ev.setUnicodeChar(CFStringGetCharacterAtIndex(strRef, i));
|
||||
queue_event(ev);
|
||||
}
|
||||
g_lastDeadKeyState = 0;
|
||||
}
|
||||
else {
|
||||
ev.setDeadKey(true);
|
||||
}
|
||||
CFRelease(strRef);
|
||||
}
|
||||
}
|
||||
|
||||
if (sendMsg)
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent*)event
|
||||
@ -186,14 +254,14 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
|
||||
KeyScancode scancode = cocoavk_to_scancode(event.keyCode);
|
||||
if (scancode >= 0 && scancode < kKeyScancodes)
|
||||
pressed_keys[scancode] = false;
|
||||
g_pressedKeys[scancode] = 0;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyUp);
|
||||
ev.setScancode(scancode);
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setRepeat(event.ARepeat ? 1: 0);
|
||||
ev.setUnicodeChar(get_unicodechar_from_nsevent(event));
|
||||
ev.setUnicodeChar(0);
|
||||
|
||||
queue_event(ev);
|
||||
}
|
||||
@ -230,7 +298,7 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
((newFlags & flags[i]) != 0 ? Event::KeyDown:
|
||||
Event::KeyUp));
|
||||
|
||||
pressed_keys[scancodes[i]] = ((newFlags & flags[i]) != 0);
|
||||
g_pressedKeys[scancodes[i]] = ((newFlags & flags[i]) != 0);
|
||||
|
||||
ev.setScancode(scancodes[i]);
|
||||
ev.setModifiers(modifiers);
|
||||
@ -517,4 +585,15 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)selector
|
||||
{
|
||||
// Do nothing (avoid beep pressing Escape key)
|
||||
}
|
||||
|
||||
- (void)setTranslateDeadKeys:(BOOL)state
|
||||
{
|
||||
g_translateDeadKeys = (state ? true: false);
|
||||
g_lastDeadKeyState = 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -184,6 +184,11 @@ void SkiaDisplay::setLayout(const std::string& layout)
|
||||
m_window.setLayout(layout);
|
||||
}
|
||||
|
||||
void SkiaDisplay::setTranslateDeadKeys(bool state)
|
||||
{
|
||||
m_window.setTranslateDeadKeys(state);
|
||||
}
|
||||
|
||||
DisplayHandle SkiaDisplay::nativeHandle()
|
||||
{
|
||||
return (DisplayHandle)m_window.handle();
|
||||
|
@ -60,6 +60,8 @@ public:
|
||||
std::string getLayout() override;
|
||||
void setLayout(const std::string& layout) override;
|
||||
|
||||
void setTranslateDeadKeys(bool state);
|
||||
|
||||
// Returns the HWND on Windows.
|
||||
DisplayHandle nativeHandle() override;
|
||||
|
||||
|
@ -135,6 +135,15 @@ public:
|
||||
return loadSurface(filename);
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) override {
|
||||
if (m_defaultDisplay)
|
||||
m_defaultDisplay->setTranslateDeadKeys(state);
|
||||
|
||||
#ifdef _WIN32
|
||||
g_queue.setTranslateDeadKeys(state);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
SkiaDisplay* m_defaultDisplay;
|
||||
bool m_gpuAcceleration;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2015 David Capello
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -46,6 +46,7 @@ public:
|
||||
void updateWindow(const gfx::Rect& bounds);
|
||||
std::string getLayout() { return ""; }
|
||||
void setLayout(const std::string& layout) { }
|
||||
void setTranslateDeadKeys(bool state);
|
||||
void* handle();
|
||||
|
||||
private:
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "gfx/size.h"
|
||||
#include "she/event.h"
|
||||
#include "she/event_queue.h"
|
||||
#include "she/osx/view.h"
|
||||
#include "she/osx/window.h"
|
||||
#include "she/skia/skia_display.h"
|
||||
#include "she/skia/skia_surface.h"
|
||||
@ -122,6 +123,11 @@ public:
|
||||
[view displayIfNeeded];
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) {
|
||||
OSXView* view = (OSXView*)m_window.contentView;
|
||||
[view setTranslateDeadKeys:(state ? YES: NO)];
|
||||
}
|
||||
|
||||
void* handle() {
|
||||
return (__bridge void*)m_window;
|
||||
}
|
||||
@ -450,6 +456,12 @@ void SkiaWindow::updateWindow(const gfx::Rect& bounds)
|
||||
m_impl->updateWindow(bounds);
|
||||
}
|
||||
|
||||
void SkiaWindow::setTranslateDeadKeys(bool state)
|
||||
{
|
||||
if (m_impl)
|
||||
m_impl->setTranslateDeadKeys(state);
|
||||
}
|
||||
|
||||
void* SkiaWindow::handle()
|
||||
{
|
||||
if (m_impl)
|
||||
|
@ -44,6 +44,10 @@ public:
|
||||
std::string getLayout() { return ""; }
|
||||
void setLayout(const std::string& layout) { }
|
||||
|
||||
void setTranslateDeadKeys(bool state) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private:
|
||||
void onExpose() override;
|
||||
|
||||
|
@ -49,6 +49,11 @@ namespace she {
|
||||
virtual Surface* loadRgbaSurface(const char* filename) = 0;
|
||||
virtual Font* loadSpriteSheetFont(const char* filename, int scale = 1) = 0;
|
||||
virtual Font* loadTrueTypeFont(const char* filename, int height) = 0;
|
||||
|
||||
// Indicates if you want to use dead keys or not. By default it's
|
||||
// false, which behaves as regular shortcuts. You should set this
|
||||
// to true when you're inside a text field in your app.
|
||||
virtual void setTranslateDeadKeys(bool state) = 0;
|
||||
};
|
||||
|
||||
System* create_system();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2015 David Capello
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -19,6 +19,9 @@ namespace she {
|
||||
|
||||
class WinEventQueue : public EventQueue {
|
||||
public:
|
||||
WinEventQueue() : m_translateDeadKeys(false) {
|
||||
}
|
||||
|
||||
void getEvent(Event& ev, bool canWait) override {
|
||||
MSG msg;
|
||||
|
||||
@ -34,7 +37,18 @@ public:
|
||||
}
|
||||
|
||||
if (res) {
|
||||
TranslateMessage(&msg);
|
||||
// Avoid transforming WM_KEYDOWN/UP into WM_DEADCHAR/WM_CHAR
|
||||
// messages when m_translateDeadKeys is disabled.
|
||||
//
|
||||
// From MSDN TranslateMessage() documentation:
|
||||
// "WM_KEYDOWN and WM_KEYUP combinations produce a WM_CHAR
|
||||
// or WM_DEADCHAR message."
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955.aspx
|
||||
if ((m_translateDeadKeys) ||
|
||||
(msg.message != WM_KEYDOWN &&
|
||||
msg.message != WM_KEYUP)) {
|
||||
TranslateMessage(&msg);
|
||||
}
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
else if (!canWait)
|
||||
@ -54,8 +68,13 @@ public:
|
||||
m_events.push(ev);
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) {
|
||||
m_translateDeadKeys = state;
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<Event> m_events;
|
||||
bool m_translateDeadKeys;
|
||||
};
|
||||
|
||||
typedef WinEventQueue EventQueueImpl;
|
||||
|
@ -8,9 +8,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "she/keys.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include "she/win/vk.h"
|
||||
|
||||
namespace she {
|
||||
|
||||
@ -337,6 +335,8 @@ static int scancode_to_win32vk(KeyScancode scancode)
|
||||
return keymap[scancode];
|
||||
}
|
||||
|
||||
// TODO Move these functions to she::System class
|
||||
|
||||
bool is_key_pressed(KeyScancode scancode)
|
||||
{
|
||||
int vk = scancode_to_win32vk(scancode);
|
||||
@ -346,4 +346,39 @@ bool is_key_pressed(KeyScancode scancode)
|
||||
return false;
|
||||
}
|
||||
|
||||
int get_unicode_from_scancode(KeyScancode scancode)
|
||||
{
|
||||
int vk = scancode_to_win32vk(scancode);
|
||||
if (vk && (GetAsyncKeyState(vk) & 0x8000 ? true: false)) {
|
||||
VkToUnicode tu;
|
||||
if (tu) {
|
||||
tu.toUnicode(vk, 0);
|
||||
if (tu.size() > 0)
|
||||
return tu[0];
|
||||
}
|
||||
|
||||
#if 0
|
||||
BYTE keystate[256];
|
||||
if (GetKeyboardState(&keystate[0])) {
|
||||
WCHAR buffer[8];
|
||||
int charsInBuffer = ToUnicode(vk, scancode, keystate, buffer,
|
||||
sizeof(buffer)/sizeof(buffer[0]), 0);
|
||||
|
||||
// ToUnicode() returns -1 if there is dead-key waiting
|
||||
if (charsInBuffer == -1) {
|
||||
return buffer[0];
|
||||
}
|
||||
// ToUnicode returns several characters inside the buffer in
|
||||
// case that a dead-key wasn't combined with the next pressed
|
||||
// character.
|
||||
else if (charsInBuffer > 0) {
|
||||
// return buffer[charsInBuffer-1];
|
||||
return buffer[0];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace she
|
||||
|
@ -17,11 +17,12 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "base/base.h"
|
||||
#include "base/debug.h"
|
||||
#include "gfx/size.h"
|
||||
#include "she/event.h"
|
||||
#include "she/keys.h"
|
||||
#include "she/native_cursor.h"
|
||||
#include "she/win/system.h"
|
||||
#include "she/win/vk.h"
|
||||
#include "she/win/window_dde.h"
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
@ -30,9 +31,6 @@
|
||||
|
||||
namespace she {
|
||||
|
||||
KeyScancode win32vk_to_scancode(int vk);
|
||||
KeyModifiers get_modifiers_from_last_win32_message();
|
||||
|
||||
#define SHE_WND_CLASS_NAME L"Aseprite.Window"
|
||||
|
||||
template<typename T>
|
||||
@ -42,6 +40,7 @@ namespace she {
|
||||
: m_clientSize(1, 1)
|
||||
, m_restoredSize(0, 0)
|
||||
, m_isCreated(false)
|
||||
, m_translateDeadKeys(false)
|
||||
, m_hasMouse(false)
|
||||
, m_captureMouse(false)
|
||||
, m_hpenctx(nullptr)
|
||||
@ -321,6 +320,35 @@ namespace she {
|
||||
}
|
||||
}
|
||||
|
||||
void setTranslateDeadKeys(bool state) {
|
||||
m_translateDeadKeys = state;
|
||||
|
||||
// Here we clear dead keys so we don't get those keys in the new
|
||||
// "translate dead keys" state. E.g. If we focus a text entry
|
||||
// field and the translation of dead keys is enabled, we don't
|
||||
// want to get previous dead keys. The same in case we leave the
|
||||
// text field with a pending dead key, that dead key must be
|
||||
// discarded.
|
||||
VkToUnicode tu;
|
||||
if (tu) {
|
||||
tu.toUnicode(VK_SPACE, 0);
|
||||
if (tu.size() != 0)
|
||||
tu.toUnicode(VK_SPACE, 0);
|
||||
}
|
||||
#if 0
|
||||
BYTE keystate[256];
|
||||
if (GetKeyboardState(keystate)) {
|
||||
WCHAR buffer[8];
|
||||
int charsInBuffer =
|
||||
ToUnicode(VK_SPACE, 0, keystate, buffer,
|
||||
sizeof(buffer)/sizeof(buffer[0]), 0);
|
||||
if (charsInBuffer != 0)
|
||||
ToUnicode(VK_SPACE, 0, keystate, buffer,
|
||||
sizeof(buffer)/sizeof(buffer[0]), 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HWND handle() {
|
||||
return m_hwnd;
|
||||
}
|
||||
@ -531,8 +559,6 @@ namespace she {
|
||||
(msg == WM_MOUSEWHEEL ? -z: 0));
|
||||
ev.setWheelDelta(delta);
|
||||
|
||||
//LOG("WHEEL: %d %d\n", delta.x, delta.y);
|
||||
|
||||
queueEvent(ev);
|
||||
break;
|
||||
}
|
||||
@ -578,8 +604,6 @@ namespace she {
|
||||
(msg == WM_VSCROLL ? (z-50): 0));
|
||||
ev.setWheelDelta(delta);
|
||||
|
||||
//LOG("SCROLL: %d %d\n", delta.x, delta.y);
|
||||
|
||||
SetScrollPos(m_hwnd, bar, 50, FALSE);
|
||||
|
||||
queueEvent(ev);
|
||||
@ -590,34 +614,67 @@ namespace she {
|
||||
case WM_KEYDOWN: {
|
||||
int vk = wparam;
|
||||
int scancode = (lparam >> 16) & 0xff;
|
||||
BYTE keystate[256];
|
||||
WCHAR buffer[8];
|
||||
int charsInBuffer = 0;
|
||||
|
||||
if (GetKeyboardState(&keystate[0])) {
|
||||
// ToUnicode can return several characters inside the
|
||||
// buffer in case that a dead-key wasn't combined with the
|
||||
// next pressed character.
|
||||
charsInBuffer = ToUnicode(vk, scancode, keystate, buffer,
|
||||
sizeof(buffer)/sizeof(buffer[0]), 0);
|
||||
}
|
||||
bool sendMsg = true;
|
||||
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setScancode(win32vk_to_scancode(vk));
|
||||
ev.setUnicodeChar(0);
|
||||
ev.setRepeat(MAX(0, (lparam & 0xffff)-1));
|
||||
|
||||
if (charsInBuffer < 1) {
|
||||
ev.setUnicodeChar(0);
|
||||
queueEvent(ev);
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<charsInBuffer; ++i) {
|
||||
ev.setUnicodeChar(buffer[i]);
|
||||
queueEvent(ev);
|
||||
if (!m_translateDeadKeys) {
|
||||
VkToUnicode tu;
|
||||
if (tu) {
|
||||
tu.toUnicode(vk, scancode);
|
||||
if (tu.isDeadKey()) {
|
||||
ev.setUnicodeChar(tu[0]);
|
||||
tu.toUnicode(vk, scancode); // Call again to remove dead-key
|
||||
}
|
||||
else if (tu.size() > 0) {
|
||||
sendMsg = false;
|
||||
for (int chr : tu) {
|
||||
ev.setUnicodeChar(chr);
|
||||
queueEvent(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
BYTE keystate[256];
|
||||
if (GetKeyboardState(keystate)) {
|
||||
WCHAR buffer[8];
|
||||
|
||||
// ToUnicode can return several characters inside the
|
||||
// buffer in case that a dead-key wasn't combined with the
|
||||
// next pressed character.
|
||||
int charsInBuffer =
|
||||
ToUnicode(vk, scancode, keystate, buffer,
|
||||
sizeof(buffer)/sizeof(buffer[0]), 0);
|
||||
|
||||
// ToUnicode() returns -1 if there is dead-key waiting
|
||||
if (charsInBuffer == -1) {
|
||||
// Call again to remove dead-key
|
||||
ToUnicode(vk, scancode, keystate, buffer,
|
||||
sizeof(buffer)/sizeof(buffer[0]), 0);
|
||||
ev.setUnicodeChar(buffer[0]);
|
||||
}
|
||||
// ToUnicode returns several characters inside the buffer in
|
||||
// case that a dead-key wasn't combined with the next pressed
|
||||
// character.
|
||||
else if (charsInBuffer > 0) {
|
||||
sendMsg = false;
|
||||
for (int i=0; i<charsInBuffer; ++i) {
|
||||
ev.setUnicodeChar(buffer[i]);
|
||||
queueEvent(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sendMsg)
|
||||
queueEvent(ev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -636,6 +693,25 @@ namespace she {
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_DEADCHAR: {
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setDeadKey(true);
|
||||
ev.setUnicodeChar(wparam);
|
||||
ev.setRepeat(lparam & 0xffff);
|
||||
queueEvent(ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_CHAR: {
|
||||
Event ev;
|
||||
ev.setType(Event::KeyDown);
|
||||
ev.setUnicodeChar(wparam);
|
||||
ev.setRepeat(lparam & 0xffff);
|
||||
queueEvent(ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_MENUCHAR:
|
||||
// Avoid playing a sound when Alt+key is pressed and it's not in a native menu
|
||||
return MAKELONG(0, MNC_CLOSE);
|
||||
@ -856,6 +932,7 @@ namespace she {
|
||||
gfx::Size m_restoredSize;
|
||||
int m_scale;
|
||||
bool m_isCreated;
|
||||
bool m_translateDeadKeys;
|
||||
bool m_hasMouse;
|
||||
bool m_captureMouse;
|
||||
bool m_customHcursor;
|
||||
|
@ -23,9 +23,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// #define REPORT_KEYS
|
||||
#define PREPROCESS_KEYS
|
||||
|
||||
namespace ui {
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -34,7 +31,117 @@ namespace ui {
|
||||
const char* kWinKeyName = "Super";
|
||||
#endif
|
||||
|
||||
static KeyModifiers get_pressed_modifiers_from_she()
|
||||
namespace {
|
||||
|
||||
const char* scancode_to_string[] = { // Same order that she::KeyScancode
|
||||
nullptr,
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"O",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"0 Pad",
|
||||
"1 Pad",
|
||||
"2 Pad",
|
||||
"3 Pad",
|
||||
"4 Pad",
|
||||
"5 Pad",
|
||||
"6 Pad",
|
||||
"7 Pad",
|
||||
"8 Pad",
|
||||
"9 Pad",
|
||||
"F1",
|
||||
"F2",
|
||||
"F3",
|
||||
"F4",
|
||||
"F5",
|
||||
"F6",
|
||||
"F7",
|
||||
"F8",
|
||||
"F9",
|
||||
"F10",
|
||||
"F11",
|
||||
"F12",
|
||||
"Esc",
|
||||
"~",
|
||||
"-",
|
||||
"=",
|
||||
"Backspace",
|
||||
"Tab",
|
||||
"[",
|
||||
"]",
|
||||
"Enter",
|
||||
";",
|
||||
"\'",
|
||||
"\\",
|
||||
"KEY_BACKSLASH2",
|
||||
",",
|
||||
".",
|
||||
"/",
|
||||
"Space",
|
||||
"Ins",
|
||||
"Del",
|
||||
"Home",
|
||||
"End",
|
||||
"PgUp",
|
||||
"PgDn",
|
||||
"Left",
|
||||
"Right",
|
||||
"Up",
|
||||
"Down",
|
||||
"/ Pad",
|
||||
"* Pad",
|
||||
"- Pad",
|
||||
"+ Pad",
|
||||
"Del Pad",
|
||||
"Enter Pad",
|
||||
"PrtScr",
|
||||
"Pause",
|
||||
"KEY_ABNT_C1",
|
||||
"Yen",
|
||||
"Kana",
|
||||
"KEY_CONVERT",
|
||||
"KEY_NOCONVERT",
|
||||
"KEY_AT",
|
||||
"KEY_CIRCUMFLEX",
|
||||
"KEY_COLON2",
|
||||
"Kanji",
|
||||
};
|
||||
int scancode_to_string_size =
|
||||
sizeof(scancode_to_string) / sizeof(scancode_to_string[0]);
|
||||
|
||||
KeyModifiers get_pressed_modifiers_from_she()
|
||||
{
|
||||
KeyModifiers mods = kKeyNoneModifier;
|
||||
if (she::is_key_pressed(kKeyLShift) ) mods = KeyModifiers(int(mods) | int(kKeyShiftModifier));
|
||||
@ -48,6 +155,8 @@ static KeyModifiers get_pressed_modifiers_from_she()
|
||||
return mods;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Accelerator::Accelerator()
|
||||
: m_modifiers(kKeyNoneModifier)
|
||||
, m_scancode(kKeyNil)
|
||||
@ -111,36 +220,8 @@ Accelerator::Accelerator(const std::string& str)
|
||||
ASSERT(false && "Something wrong converting utf-8 to wchar string");
|
||||
continue;
|
||||
}
|
||||
|
||||
wchar_t wchr = wstr[0];
|
||||
wchr = tolower(wchr);
|
||||
|
||||
if ((wchr >= 'a') && (wchr <= 'z')) {
|
||||
m_unicodeChar = wchr;
|
||||
m_scancode = (KeyScancode)((int)kKeyA + wchr - 'a');
|
||||
}
|
||||
else {
|
||||
m_unicodeChar = wchr;
|
||||
|
||||
if ((wchr >= '0') && (wchr <= '9'))
|
||||
m_scancode = (KeyScancode)((int)kKey0 + wchr - '0');
|
||||
else {
|
||||
switch (wchr) {
|
||||
case '~': m_scancode = kKeyTilde; break;
|
||||
case '-': m_scancode = kKeyMinus; break;
|
||||
case '=': m_scancode = kKeyEquals; break;
|
||||
case '[': m_scancode = kKeyOpenbrace; break;
|
||||
case ']': m_scancode = kKeyClosebrace; break;
|
||||
case ';': m_scancode = kKeyColon; break;
|
||||
case '\'': m_scancode = kKeyQuote; break;
|
||||
case '\\': m_scancode = kKeyBackslash; break;
|
||||
case ',': m_scancode = kKeyComma; break;
|
||||
case '.': m_scancode = kKeyStop; break;
|
||||
case '/': m_scancode = kKeySlash; break;
|
||||
case '*': m_scancode = kKeyAsterisk; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_unicodeChar = std::tolower(wstr[0]);
|
||||
m_scancode = kKeyNil;
|
||||
}
|
||||
// Other ones
|
||||
else {
|
||||
@ -218,19 +299,8 @@ Accelerator::Accelerator(const std::string& str)
|
||||
|
||||
bool Accelerator::operator==(const Accelerator& other) const
|
||||
{
|
||||
if (m_modifiers != other.m_modifiers)
|
||||
return false;
|
||||
|
||||
if (m_scancode == other.m_scancode) {
|
||||
if (m_scancode != kKeyNil)
|
||||
return true;
|
||||
else if (m_unicodeChar != 0)
|
||||
return (std::tolower(m_unicodeChar) == std::tolower(other.m_unicodeChar));
|
||||
else // Only comparing modifiers, and they are equal
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// TODO improve this, avoid conversion to std::string
|
||||
return toString() == other.toString();
|
||||
}
|
||||
|
||||
bool Accelerator::isEmpty() const
|
||||
@ -243,114 +313,6 @@ bool Accelerator::isEmpty() const
|
||||
|
||||
std::string Accelerator::toString() const
|
||||
{
|
||||
// Same order that she::KeyScancode
|
||||
static const char* table[] = {
|
||||
NULL,
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"O",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"0 Pad",
|
||||
"1 Pad",
|
||||
"2 Pad",
|
||||
"3 Pad",
|
||||
"4 Pad",
|
||||
"5 Pad",
|
||||
"6 Pad",
|
||||
"7 Pad",
|
||||
"8 Pad",
|
||||
"9 Pad",
|
||||
"F1",
|
||||
"F2",
|
||||
"F3",
|
||||
"F4",
|
||||
"F5",
|
||||
"F6",
|
||||
"F7",
|
||||
"F8",
|
||||
"F9",
|
||||
"F10",
|
||||
"F11",
|
||||
"F12",
|
||||
"Esc",
|
||||
"~",
|
||||
"-",
|
||||
"=",
|
||||
"Backspace",
|
||||
"Tab",
|
||||
"[",
|
||||
"]",
|
||||
"Enter",
|
||||
";",
|
||||
"\'",
|
||||
"\\",
|
||||
"KEY_BACKSLASH2",
|
||||
",",
|
||||
".",
|
||||
"/",
|
||||
"Space",
|
||||
"Ins",
|
||||
"Del",
|
||||
"Home",
|
||||
"End",
|
||||
"PgUp",
|
||||
"PgDn",
|
||||
"Left",
|
||||
"Right",
|
||||
"Up",
|
||||
"Down",
|
||||
"/ Pad",
|
||||
"* Pad",
|
||||
"- Pad",
|
||||
"+ Pad",
|
||||
"Del Pad",
|
||||
"Enter Pad",
|
||||
"PrtScr",
|
||||
"Pause",
|
||||
"KEY_ABNT_C1",
|
||||
"Yen",
|
||||
"Kana",
|
||||
"KEY_CONVERT",
|
||||
"KEY_NOCONVERT",
|
||||
"KEY_AT",
|
||||
"KEY_CIRCUMFLEX",
|
||||
"KEY_COLON2",
|
||||
"Kanji",
|
||||
};
|
||||
static std::size_t table_size = sizeof(table) / sizeof(table[0]);
|
||||
|
||||
std::string buf;
|
||||
|
||||
// Shifts
|
||||
@ -367,11 +329,12 @@ std::string Accelerator::toString() const
|
||||
// Key
|
||||
if (m_unicodeChar) {
|
||||
std::wstring wideUnicodeChar;
|
||||
wideUnicodeChar.push_back((wchar_t)toupper(m_unicodeChar));
|
||||
wideUnicodeChar.push_back((wchar_t)std::toupper(m_unicodeChar));
|
||||
buf += base::to_utf8(wideUnicodeChar);
|
||||
}
|
||||
else if (m_scancode && m_scancode > 0 && m_scancode < (int)table_size)
|
||||
buf += table[m_scancode];
|
||||
else if (m_scancode > 0 &&
|
||||
m_scancode < scancode_to_string_size)
|
||||
buf += scancode_to_string[m_scancode];
|
||||
else if (!buf.empty() && buf[buf.size()-1] == '+')
|
||||
buf.erase(buf.size()-1);
|
||||
|
||||
@ -380,97 +343,51 @@ std::string Accelerator::toString() const
|
||||
|
||||
bool Accelerator::isPressed(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const
|
||||
{
|
||||
// Preprocess the character to be compared with the accelerator
|
||||
#ifdef PREPROCESS_KEYS
|
||||
// Directly scancode
|
||||
if ((scancode >= kKeyF1 && scancode <= kKeyF12) ||
|
||||
(scancode == kKeyEsc) ||
|
||||
(scancode == kKeyBackspace) ||
|
||||
(scancode == kKeyTab) ||
|
||||
(scancode == kKeyEnter) ||
|
||||
(scancode == kKeyBackslash) ||
|
||||
(scancode == kKeyBackslash2) ||
|
||||
(scancode >= kKeySpace && scancode <= kKeyDown) ||
|
||||
(scancode >= kKeyEnterPad && scancode <= kKeyNoconvert) ||
|
||||
(scancode == kKeyKanji)) {
|
||||
unicodeChar = 0;
|
||||
}
|
||||
// For Ctrl+number
|
||||
/* scancode unicodeChar
|
||||
Ctrl+0 27 0
|
||||
Ctrl+1 28 2
|
||||
Ctrl+2 29 0
|
||||
Ctrl+3 30 27
|
||||
Ctrl+4 31 28
|
||||
Ctrl+5 32 29
|
||||
Ctrl+6 33 30
|
||||
Ctrl+7 34 31
|
||||
Ctrl+8 35 127
|
||||
Ctrl+9 36 2
|
||||
*/
|
||||
else if ((scancode >= kKey0 && scancode <= kKey9) &&
|
||||
(unicodeChar < 32 || unicodeChar == 127)) {
|
||||
unicodeChar = '0' + scancode - kKey0;
|
||||
scancode = kKeyNil;
|
||||
}
|
||||
// For Ctrl+letter
|
||||
else if (unicodeChar >= 1 && unicodeChar <= 'z'-'a'+1) {
|
||||
unicodeChar = 'a'+unicodeChar-1;
|
||||
scancode = kKeyNil;
|
||||
}
|
||||
// For any other legal Unicode code
|
||||
else if (unicodeChar >= ' ') {
|
||||
unicodeChar = std::tolower(unicodeChar);
|
||||
|
||||
/* without shift (because characters like '*' can be trigger with
|
||||
"Shift+8", so we don't want "Shift+*") */
|
||||
if (!(unicodeChar >= 'a' && unicodeChar <= 'z'))
|
||||
modifiers = (KeyModifiers)((int)modifiers & ((int)~kKeyShiftModifier));
|
||||
|
||||
scancode = kKeyNil;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef REPORT_KEYS
|
||||
printf("%3d==%3d %3d==%3d %s==%s ",
|
||||
m_scancode, scancode,
|
||||
m_unicodeChar, unicodeChar,
|
||||
toString().c_str(),
|
||||
Accelerator(modifiers, scancode, unicodeChar).toString().c_str());
|
||||
#endif
|
||||
|
||||
if ((m_modifiers == modifiers) &&
|
||||
((m_scancode != kKeyNil && m_scancode == scancode) ||
|
||||
(m_unicodeChar && m_unicodeChar == unicodeChar) ||
|
||||
(m_scancode == kKeyNil && scancode == kKeyNil && !m_unicodeChar && !unicodeChar))) {
|
||||
#ifdef REPORT_KEYS
|
||||
printf("true\n");
|
||||
fflush(stdout);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef REPORT_KEYS
|
||||
printf("false\n");
|
||||
fflush(stdout);
|
||||
#endif
|
||||
return false;
|
||||
return ((scancode && *this == Accelerator(modifiers, scancode, 0)) ||
|
||||
(unicodeChar && *this == Accelerator(modifiers, kKeyNil, unicodeChar)));
|
||||
}
|
||||
|
||||
bool Accelerator::isPressed() const
|
||||
{
|
||||
KeyModifiers modifiers = get_pressed_modifiers_from_she();
|
||||
KeyModifiers pressedModifiers = get_pressed_modifiers_from_she();
|
||||
|
||||
return ((m_scancode == 0 || she::is_key_pressed(m_scancode)) &&
|
||||
(m_modifiers == modifiers));
|
||||
// Check if this shortcut is only
|
||||
if (m_scancode == kKeyNil && m_unicodeChar == 0)
|
||||
return (m_modifiers == pressedModifiers);
|
||||
|
||||
// Compare with all pressed scancodes
|
||||
for (int s=int(kKeyNil); s<int(kKeyFirstModifierScancode); ++s) {
|
||||
if (she::is_key_pressed(KeyScancode(s)) &&
|
||||
isPressed(pressedModifiers,
|
||||
KeyScancode(s),
|
||||
she::get_unicode_from_scancode(KeyScancode(s))))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Accelerator::isLooselyPressed() const
|
||||
{
|
||||
KeyModifiers modifiers = get_pressed_modifiers_from_she();
|
||||
KeyModifiers pressedModifiers = get_pressed_modifiers_from_she();
|
||||
|
||||
return ((m_scancode == 0 || she::is_key_pressed(m_scancode)) &&
|
||||
(int(m_modifiers & modifiers) == m_modifiers));
|
||||
if ((m_modifiers & pressedModifiers) != m_modifiers)
|
||||
return false;
|
||||
|
||||
// Check if this shortcut is only
|
||||
if (m_scancode == kKeyNil && m_unicodeChar == 0)
|
||||
return true;
|
||||
|
||||
// Compare with all pressed scancodes
|
||||
for (int s=int(kKeyNil); s<int(kKeyFirstModifierScancode); ++s) {
|
||||
if (she::is_key_pressed(KeyScancode(s)) &&
|
||||
isPressed(m_modifiers, // Use same modifiers (we've already compared the modifiers)
|
||||
KeyScancode(s),
|
||||
she::get_unicode_from_scancode(KeyScancode(s))))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -17,6 +17,7 @@ namespace ui {
|
||||
|
||||
extern const char* kWinKeyName;
|
||||
|
||||
// TODO rename this class to Shortcut
|
||||
class Accelerator {
|
||||
public:
|
||||
Accelerator();
|
||||
@ -29,12 +30,12 @@ namespace ui {
|
||||
|
||||
bool isPressed(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const;
|
||||
|
||||
// Returns true if the key is pressed and only its modifiers are
|
||||
// Returns true if the key is pressed and ONLY its modifiers are
|
||||
// pressed.
|
||||
bool isPressed() const;
|
||||
|
||||
// Returns true if the key is pressed and the accelerator
|
||||
// modifiers are pressed (other modifiers are allowed).
|
||||
// Returns true if the key + its modifiers are pressed (other
|
||||
// modifiers are allowed too).
|
||||
bool isLooselyPressed() const;
|
||||
|
||||
bool operator==(const Accelerator& other) const;
|
||||
@ -52,6 +53,7 @@ namespace ui {
|
||||
int m_unicodeChar;
|
||||
};
|
||||
|
||||
// TODO rename this class to Shortcuts
|
||||
class Accelerators {
|
||||
public:
|
||||
typedef std::vector<Accelerator> List;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "base/string.h"
|
||||
#include "clip/clip.h"
|
||||
#include "she/font.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/manager.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/message.h"
|
||||
@ -41,6 +42,7 @@ Entry::Entry(std::size_t maxsize, const char* format, ...)
|
||||
, m_password(false)
|
||||
, m_recent_focused(false)
|
||||
, m_lock_selection(false)
|
||||
, m_translate_dead_keys(true)
|
||||
{
|
||||
enableFlags(CTRL_RIGHT_CLICK);
|
||||
|
||||
@ -165,6 +167,11 @@ void Entry::setSuffix(const std::string& suffix)
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void Entry::setTranslateDeadKeys(bool state)
|
||||
{
|
||||
m_translate_dead_keys = state;
|
||||
}
|
||||
|
||||
void Entry::getEntryThemeInfo(int* scroll, int* caret, int* state,
|
||||
int* selbeg, int* selend)
|
||||
{
|
||||
@ -213,6 +220,10 @@ bool Entry::onProcessMessage(Message* msg)
|
||||
selectAllText();
|
||||
m_recent_focused = true;
|
||||
}
|
||||
|
||||
// Start processing dead keys
|
||||
if (m_translate_dead_keys)
|
||||
she::instance()->setTranslateDeadKeys(true);
|
||||
break;
|
||||
|
||||
case kFocusLeaveMessage:
|
||||
@ -224,6 +235,10 @@ bool Entry::onProcessMessage(Message* msg)
|
||||
deselectText();
|
||||
|
||||
m_recent_focused = false;
|
||||
|
||||
// Stop processing dead keys
|
||||
if (m_translate_dead_keys)
|
||||
she::instance()->setTranslateDeadKeys(false);
|
||||
break;
|
||||
|
||||
case kKeyDownMessage:
|
||||
@ -300,20 +315,24 @@ bool Entry::onProcessMessage(Message* msg)
|
||||
}
|
||||
}
|
||||
else if (manager()->isFocusMovementKey(msg)) {
|
||||
break;
|
||||
}
|
||||
else if (keymsg->unicodeChar() >= 32) {
|
||||
// Ctrl and Alt must be unpressed to insert a character
|
||||
// in the text-field.
|
||||
if ((msg->modifiers() & (kKeyCtrlModifier | kKeyAltModifier)) == 0) {
|
||||
cmd = EntryCmd::InsertChar;
|
||||
}
|
||||
return Widget::onProcessMessage(msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd == EntryCmd::NoOp)
|
||||
if (cmd == EntryCmd::NoOp) {
|
||||
if (keymsg->unicodeChar() >= 32) {
|
||||
executeCmd(EntryCmd::InsertChar, keymsg->unicodeChar(),
|
||||
(msg->shiftPressed()) ? true: false);
|
||||
|
||||
// Select dead-key
|
||||
if (keymsg->isDeadKey()) {
|
||||
selectText(m_caret-1, m_caret);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
executeCmd(cmd, keymsg->unicodeChar(),
|
||||
(msg->shiftPressed()) ? true: false);
|
||||
|
@ -37,6 +37,8 @@ namespace ui {
|
||||
void setSuffix(const std::string& suffix);
|
||||
const std::string& getSuffix() { return m_suffix; }
|
||||
|
||||
void setTranslateDeadKeys(bool state);
|
||||
|
||||
// for themes
|
||||
void getEntryThemeInfo(int* scroll, int* caret, int* state,
|
||||
int* selbeg, int* selend);
|
||||
@ -95,6 +97,7 @@ namespace ui {
|
||||
bool m_password;
|
||||
bool m_recent_focused;
|
||||
bool m_lock_selection;
|
||||
bool m_translate_dead_keys;
|
||||
std::string m_suffix;
|
||||
};
|
||||
|
||||
|
@ -118,7 +118,7 @@ bool IntEntry::onProcessMessage(Message* msg)
|
||||
if (hasFocus() && !isReadOnly()) {
|
||||
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
|
||||
int chr = keymsg->unicodeChar();
|
||||
if (chr < '0' || chr > '9') {
|
||||
if (chr && (chr < '0' || chr > '9')) {
|
||||
// By-pass Entry::onProcessMessage()
|
||||
return Widget::onProcessMessage(msg);
|
||||
}
|
||||
|
@ -317,6 +317,10 @@ void Manager::generateMessagesFromSheEvents()
|
||||
sheEvent.modifiers(),
|
||||
sheEvent.unicodeChar(),
|
||||
sheEvent.repeat());
|
||||
|
||||
if (sheEvent.isDeadKey())
|
||||
static_cast<KeyMessage*>(msg)->setDeadKey(true);
|
||||
|
||||
broadcastKeyMsg(msg);
|
||||
enqueueMessage(msg);
|
||||
break;
|
||||
|
@ -485,9 +485,15 @@ bool MenuBox::onProcessMessage(Message* msg)
|
||||
if (((this->type() == kMenuBoxWidget) && (msg->modifiers() == kKeyNoneModifier || // <-- Inside menu-boxes we can use letters without Alt modifier pressed
|
||||
msg->modifiers() == kKeyAltModifier)) ||
|
||||
((this->type() == kMenuBarWidget) && (msg->modifiers() == kKeyAltModifier))) {
|
||||
// TODO use scancode instead of unicodeChar
|
||||
selected = check_for_letter(menu,
|
||||
static_cast<KeyMessage*>(msg)->unicodeChar());
|
||||
int unicode = static_cast<KeyMessage*>(msg)->unicodeChar();
|
||||
selected = check_for_letter(menu, unicode);
|
||||
if (!selected) {
|
||||
KeyScancode scancode = static_cast<KeyMessage*>(msg)->scancode();
|
||||
if (scancode >= kKeyA && scancode <= kKeyZ)
|
||||
selected = check_for_letter(menu, 'a' + scancode - kKeyA);
|
||||
else if (scancode >= kKey0 && scancode <= kKey9)
|
||||
selected = check_for_letter(menu, '0' + scancode - kKey0);
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
menu->highlightItem(selected, true, true, true);
|
||||
@ -1196,7 +1202,7 @@ static MenuItem* check_for_letter(Menu* menu, int ascii)
|
||||
|
||||
MenuItem* menuitem = static_cast<MenuItem*>(child);
|
||||
int mnemonic = menuitem->mnemonicChar();
|
||||
if (mnemonic > 0 && mnemonic == tolower(ascii))
|
||||
if (mnemonic > 0 && mnemonic == std::tolower(ascii))
|
||||
return menuitem;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2001-2013, 2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -11,6 +11,7 @@
|
||||
#include "ui/message.h"
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/manager.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
@ -29,6 +30,7 @@ Message::Message(MessageType type, KeyModifiers modifiers)
|
||||
((she::is_key_pressed(kKeyLShift) || she::is_key_pressed(kKeyRShift) ? kKeyShiftModifier: 0) |
|
||||
(she::is_key_pressed(kKeyLControl) || she::is_key_pressed(kKeyRControl) ? kKeyCtrlModifier: 0) |
|
||||
(she::is_key_pressed(kKeyAlt) ? kKeyAltModifier: 0) |
|
||||
(she::is_key_pressed(kKeyAltGr) ? (kKeyCtrlModifier | kKeyAltModifier): 0) |
|
||||
(she::is_key_pressed(kKeyCommand) ? kKeyCmdModifier: 0) |
|
||||
(she::is_key_pressed(kKeySpace) ? kKeySpaceModifier: 0) |
|
||||
(she::is_key_pressed(kKeyLWin) || she::is_key_pressed(kKeyRWin) ? kKeyWinModifier: 0));
|
||||
@ -84,6 +86,7 @@ KeyMessage::KeyMessage(MessageType type,
|
||||
, m_scancode(scancode)
|
||||
, m_unicodeChar(unicodeChar)
|
||||
, m_repeat(repeat)
|
||||
, m_isDead(false)
|
||||
, m_propagate_to_children(false)
|
||||
, m_propagate_to_parent(true)
|
||||
{
|
||||
|
@ -74,6 +74,8 @@ namespace ui {
|
||||
KeyScancode scancode() const { return m_scancode; }
|
||||
int unicodeChar() const { return m_unicodeChar; }
|
||||
int repeat() const { return m_repeat; }
|
||||
bool isDeadKey() const { return m_isDead; }
|
||||
void setDeadKey(bool state) { m_isDead = state; }
|
||||
bool propagateToChildren() const { return m_propagate_to_children; }
|
||||
bool propagateToParent() const { return m_propagate_to_parent; }
|
||||
void setPropagateToChildren(bool flag) { m_propagate_to_children = flag; }
|
||||
@ -83,8 +85,9 @@ namespace ui {
|
||||
KeyScancode m_scancode;
|
||||
int m_unicodeChar;
|
||||
int m_repeat; // repeat=0 means the first time the key is pressed
|
||||
bool m_propagate_to_children : 1;
|
||||
bool m_propagate_to_parent : 1;
|
||||
bool m_isDead;
|
||||
bool m_propagate_to_children;
|
||||
bool m_propagate_to_parent;
|
||||
};
|
||||
|
||||
class PaintMessage : public Message {
|
||||
|
Loading…
x
Reference in New Issue
Block a user