Merge pull request #1 from RustamGamidov/entry_keyEvent

entry. handle key event
This commit is contained in:
Rustam 2020-03-24 07:52:59 +02:00 committed by GitHub
commit cd3f9d29a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 435 additions and 259 deletions

View File

@ -11,7 +11,6 @@
BOOL libui_enabled; BOOL libui_enabled;
} }
- (id)initWithFrame:(NSRect)r area:(uiArea *)a; - (id)initWithFrame:(NSRect)r area:(uiArea *)a;
- (uiModifiers)parseModifiers:(NSEvent *)e;
- (void)doMouseEvent:(NSEvent *)e; - (void)doMouseEvent:(NSEvent *)e;
- (int)sendKeyEvent:(uiAreaKeyEvent *)ke; - (int)sendKeyEvent:(uiAreaKeyEvent *)ke;
- (int)doKeyDownUp:(NSEvent *)e up:(int)up; - (int)doKeyDownUp:(NSEvent *)e up:(int)up;
@ -87,24 +86,6 @@ struct uiArea {
return YES; return YES;
} }
- (uiModifiers)parseModifiers:(NSEvent *)e
{
NSEventModifierFlags mods;
uiModifiers m;
m = 0;
mods = [e modifierFlags];
if ((mods & NSControlKeyMask) != 0)
m |= uiModifierCtrl;
if ((mods & NSAlternateKeyMask) != 0)
m |= uiModifierAlt;
if ((mods & NSShiftKeyMask) != 0)
m |= uiModifierShift;
if ((mods & NSCommandKeyMask) != 0)
m |= uiModifierSuper;
return m;
}
- (void)setupNewTrackingArea - (void)setupNewTrackingArea
{ {
self->libui_ta = [[NSTrackingArea alloc] initWithRect:[self bounds] self->libui_ta = [[NSTrackingArea alloc] initWithRect:[self bounds]
@ -178,7 +159,7 @@ struct uiArea {
break; break;
} }
me.Modifiers = [self parseModifiers:e]; me.Modifiers = parseModifiers(e);
pmb = [NSEvent pressedMouseButtons]; pmb = [NSEvent pressedMouseButtons];
me.Held1To64 = 0; me.Held1To64 = 0;
@ -260,7 +241,7 @@ mouseEvent(otherMouseUp)
ke.ExtKey = 0; ke.ExtKey = 0;
ke.Modifier = 0; ke.Modifier = 0;
ke.Modifiers = [self parseModifiers:e]; ke.Modifiers = parseModifiers(e);
ke.Up = up; ke.Up = up;
@ -292,7 +273,7 @@ mouseEvent(otherMouseUp)
if (!uiprivKeycodeModifier([e keyCode], &whichmod)) if (!uiprivKeycodeModifier([e keyCode], &whichmod))
return 0; return 0;
ke.Modifier = whichmod; ke.Modifier = whichmod;
ke.Modifiers = [self parseModifiers:e]; ke.Modifiers = parseModifiers(e);
ke.Up = (ke.Modifiers & ke.Modifier) == 0; ke.Up = (ke.Modifiers & ke.Modifier) == 0;
// and then drop the current modifier from Modifiers // and then drop the current modifier from Modifiers
ke.Modifiers &= ~ke.Modifier; ke.Modifiers &= ~ke.Modifier;

View File

@ -157,3 +157,21 @@ BOOL uiprivKeycodeModifier(unsigned short keycode, uiModifiers *mod)
} }
return NO; return NO;
} }
uiModifiers parseModifiers(NSEvent *e)
{
NSEventModifierFlags mods;
uiModifiers m;
m = 0;
mods = [e modifierFlags];
if ((mods & NSControlKeyMask) != 0)
m |= uiModifierCtrl;
if ((mods & NSAlternateKeyMask) != 0)
m |= uiModifierAlt;
if ((mods & NSShiftKeyMask) != 0)
m |= uiModifierShift;
if ((mods & NSCommandKeyMask) != 0)
m |= uiModifierSuper;
return m;
}

View File

@ -59,6 +59,7 @@ struct uiEntry {
NSTextField *textfield; NSTextField *textfield;
void (*onChanged)(uiEntry *, void *); void (*onChanged)(uiEntry *, void *);
void *onChangedData; void *onChangedData;
int (*onKeyEvent)(uiEntry *, uiAreaKeyEvent *);
}; };
static BOOL isSearchField(NSTextField *tf) static BOOL isSearchField(NSTextField *tf)
@ -66,8 +67,19 @@ static BOOL isSearchField(NSTextField *tf)
return [tf isKindOfClass:[NSSearchField class]]; return [tf isKindOfClass:[NSSearchField class]];
} }
static void triggerOnKeyEvent(void *key, void *e, void *data)
{
uiEntry *entry = (uiEntry *)e;
void *firstResponder = [entry->textfield window].firstResponder;
BOOL sameObj = (entry->textfield == firstResponder);
BOOL currentEditor = (entry->textfield.currentEditor && entry->textfield.currentEditor == firstResponder);
if (sameObj || currentEditor)
entry->onKeyEvent(entry, data);
}
@interface entryDelegateClass : NSObject<NSTextFieldDelegate> { @interface entryDelegateClass : NSObject<NSTextFieldDelegate> {
uiprivMap *entries; uiprivMap *entries;
id eventMonitor;
} }
- (void)controlTextDidChange:(NSNotification *)note; - (void)controlTextDidChange:(NSNotification *)note;
- (IBAction)onSearch:(id)sender; - (IBAction)onSearch:(id)sender;
@ -82,11 +94,28 @@ static BOOL isSearchField(NSTextField *tf)
self = [super init]; self = [super init];
if (self) if (self)
self->entries = uiprivNewMap(); self->entries = uiprivNewMap();
NSEvent* (^eventHandler)(NSEvent*) = ^(NSEvent *theEvent) {
uiAreaKeyEvent ke;
ke.Key = 0;
ke.ExtKey = 0;
ke.Modifier = 0;
ke.Modifiers = parseModifiers(theEvent);
ke.Up = ([theEvent type] == NSKeyUp ? 1 : 0);
if (uiprivFromKeycode([theEvent keyCode], &ke))
uiprivMapWalkWithData(self->entries, &ke, triggerOnKeyEvent);
return theEvent;
};
eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyDownMask | NSKeyUpMask) handler:eventHandler];
return self; return self;
} }
- (void)dealloc - (void)dealloc
{ {
[NSEvent removeMonitor:eventMonitor];
uiprivMapDestroy(self->entries); uiprivMapDestroy(self->entries);
[super dealloc]; [super dealloc];
} }
@ -155,6 +184,11 @@ void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *, void *), void *data)
e->onChangedData = data; e->onChangedData = data;
} }
void uiEntryOnKeyEvent(uiEntry *e, int (*f)(uiEntry *, uiAreaKeyEvent *))
{
e->onKeyEvent = f;
}
int uiEntryReadOnly(uiEntry *e) int uiEntryReadOnly(uiEntry *e)
{ {
return [e->textfield isEditable] == NO; return [e->textfield isEditable] == NO;
@ -175,6 +209,12 @@ static void defaultOnChanged(uiEntry *e, void *data)
// do nothing // do nothing
} }
static int defaultOnKeyEvent(uiEntry *e, uiAreaKeyEvent *ke)
{
// do nothing
return FALSE;
}
// these are based on interface builder defaults; my comments in the old code weren't very good so I don't really know what talked about what, sorry :/ // these are based on interface builder defaults; my comments in the old code weren't very good so I don't really know what talked about what, sorry :/
void uiprivFinishNewTextField(NSTextField *t, BOOL isEntry) void uiprivFinishNewTextField(NSTextField *t, BOOL isEntry)
{ {
@ -220,6 +260,7 @@ static uiEntry *finishNewEntry(Class class)
} }
[entryDelegate registerEntry:e]; [entryDelegate registerEntry:e];
uiEntryOnChanged(e, defaultOnChanged, NULL); uiEntryOnChanged(e, defaultOnChanged, NULL);
uiEntryOnKeyEvent(e, defaultOnKeyEvent);
return e; return e;
} }

View File

@ -55,6 +55,20 @@ void uiprivMapWalk(uiprivMap *m, void (*f)(void *key, void *value))
NSEndMapTableEnumeration(&e); NSEndMapTableEnumeration(&e);
} }
void uiprivMapWalkWithData(uiprivMap *m, void *data, void (*f)(void *key, void *value, void *data))
{
NSMapEnumerator e;
void *k, *v;
e = NSEnumerateMapTable(m->m);
k = NULL;
v = NULL;
while (NSNextMapEnumeratorPair(&e, &k, &v))
f(k, v, data);
NSEndMapTableEnumeration(&e);
}
void uiprivMapReset(uiprivMap *m) void uiprivMapReset(uiprivMap *m)
{ {
NSResetMapTable(m->m); NSResetMapTable(m->m);

View File

@ -31,6 +31,7 @@ extern void *uiprivMapGet(uiprivMap *m, void *key);
extern void uiprivMapSet(uiprivMap *m, void *key, void *value); extern void uiprivMapSet(uiprivMap *m, void *key, void *value);
extern void uiprivMapDelete(uiprivMap *m, void *key); extern void uiprivMapDelete(uiprivMap *m, void *key);
extern void uiprivMapWalk(uiprivMap *m, void (*f)(void *key, void *value)); extern void uiprivMapWalk(uiprivMap *m, void (*f)(void *key, void *value));
extern void uiprivMapWalkWithData(uiprivMap *m, void *data, void (*f)(void *key, void *value, void *data));
extern void uiprivMapReset(uiprivMap *m); extern void uiprivMapReset(uiprivMap *m);
// menu.m // menu.m
@ -110,6 +111,7 @@ extern int uiprivSendAreaEvents(NSEvent *);
// areaevents.m // areaevents.m
extern BOOL uiprivFromKeycode(unsigned short keycode, uiAreaKeyEvent *ke); extern BOOL uiprivFromKeycode(unsigned short keycode, uiAreaKeyEvent *ke);
extern BOOL uiprivKeycodeModifier(unsigned short keycode, uiModifiers *mod); extern BOOL uiprivKeycodeModifier(unsigned short keycode, uiModifiers *mod);
extern uiModifiers parseModifiers(NSEvent *e);
// draw.m // draw.m
extern uiDrawContext *uiprivDrawNewContext(CGContextRef, CGFloat); extern uiDrawContext *uiprivDrawNewContext(CGContextRef, CGFloat);

12
ui.h
View File

@ -45,6 +45,12 @@ _UI_ENUM(uiForEach) {
uiForEachStop, uiForEachStop,
}; };
typedef struct uiArea uiArea;
typedef struct uiAreaHandler uiAreaHandler;
typedef struct uiAreaDrawParams uiAreaDrawParams;
typedef struct uiAreaMouseEvent uiAreaMouseEvent;
typedef struct uiAreaKeyEvent uiAreaKeyEvent;
typedef struct uiInitOptions uiInitOptions; typedef struct uiInitOptions uiInitOptions;
struct uiInitOptions { struct uiInitOptions {
@ -162,6 +168,7 @@ typedef struct uiEntry uiEntry;
_UI_EXTERN char *uiEntryText(uiEntry *e); _UI_EXTERN char *uiEntryText(uiEntry *e);
_UI_EXTERN void uiEntrySetText(uiEntry *e, const char *text); _UI_EXTERN void uiEntrySetText(uiEntry *e, const char *text);
_UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), void *data); _UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), void *data);
_UI_EXTERN void uiEntryOnKeyEvent(uiEntry *e, int (*f)(uiEntry *e, uiAreaKeyEvent *event));
_UI_EXTERN int uiEntryReadOnly(uiEntry *e); _UI_EXTERN int uiEntryReadOnly(uiEntry *e);
_UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly); _UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly);
_UI_EXTERN uiEntry *uiNewEntry(void); _UI_EXTERN uiEntry *uiNewEntry(void);
@ -297,11 +304,6 @@ _UI_EXTERN char *uiSaveFile(uiWindow *parent);
_UI_EXTERN void uiMsgBox(uiWindow *parent, const char *title, const char *description); _UI_EXTERN void uiMsgBox(uiWindow *parent, const char *title, const char *description);
_UI_EXTERN void uiMsgBoxError(uiWindow *parent, const char *title, const char *description); _UI_EXTERN void uiMsgBoxError(uiWindow *parent, const char *title, const char *description);
typedef struct uiArea uiArea;
typedef struct uiAreaHandler uiAreaHandler;
typedef struct uiAreaDrawParams uiAreaDrawParams;
typedef struct uiAreaMouseEvent uiAreaMouseEvent;
typedef struct uiAreaKeyEvent uiAreaKeyEvent;
typedef struct uiDrawContext uiDrawContext; typedef struct uiDrawContext uiDrawContext;

View File

@ -1,5 +1,6 @@
// 4 september 2015 // 4 september 2015
#include "uipriv_unix.h" #include "uipriv_unix.h"
#include "keyboard.h"
// notes: // notes:
// - G_DECLARE_DERIVABLE/FINAL_INTERFACE() requires glib 2.44 and that's starting with debian stretch (testing) (GTK+ 3.18) and ubuntu 15.04 (GTK+ 3.14) - debian jessie has 2.42 (GTK+ 3.14) // - G_DECLARE_DERIVABLE/FINAL_INTERFACE() requires glib 2.44 and that's starting with debian stretch (testing) (GTK+ 3.18) and ubuntu 15.04 (GTK+ 3.14) - debian jessie has 2.42 (GTK+ 3.14)
@ -170,36 +171,6 @@ static void areaWidget_get_preferred_width(GtkWidget *w, gint *min, gint *nat)
} }
} }
static guint translateModifiers(guint state, GdkWindow *window)
{
GdkModifierType statetype;
// GDK doesn't initialize the modifier flags fully; we have to explicitly tell it to (thanks to Daniel_S and daniels (two different people) in irc.gimp.net/#gtk+)
statetype = state;
gdk_keymap_add_virtual_modifiers(
gdk_keymap_get_for_display(gdk_window_get_display(window)),
&statetype);
return statetype;
}
static uiModifiers toModifiers(guint state)
{
uiModifiers m;
m = 0;
if ((state & GDK_CONTROL_MASK) != 0)
m |= uiModifierCtrl;
if ((state & GDK_META_MASK) != 0)
m |= uiModifierAlt;
if ((state & GDK_MOD1_MASK) != 0) // GTK+ itself requires this to be Alt (just read through gtkaccelgroup.c)
m |= uiModifierAlt;
if ((state & GDK_SHIFT_MASK) != 0)
m |= uiModifierShift;
if ((state & GDK_SUPER_MASK) != 0)
m |= uiModifierSuper;
return m;
}
// capture on drag is done automatically on GTK+ // capture on drag is done automatically on GTK+
static void finishMouseEvent(uiArea *a, uiAreaMouseEvent *me, guint mb, gdouble x, gdouble y, guint state, GdkWindow *window) static void finishMouseEvent(uiArea *a, uiAreaMouseEvent *me, guint mb, gdouble x, gdouble y, guint state, GdkWindow *window)
{ {
@ -326,99 +297,14 @@ static gboolean areaWidget_leave_notify_event(GtkWidget *w, GdkEventCrossing *e)
// note: there is no equivalent to WM_CAPTURECHANGED on GTK+; there literally is no way to break a grab like that (at least not on X11 and Wayland) // note: there is no equivalent to WM_CAPTURECHANGED on GTK+; there literally is no way to break a grab like that (at least not on X11 and Wayland)
// even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons // even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons
// therefore, no DragBroken() // therefore, no DragBroken()
// we use GDK_KEY_Print as a sentinel because libui will never support the print screen key; that key belongs to the user
static const struct {
guint keyval;
uiExtKey extkey;
} extKeys[] = {
{ GDK_KEY_Escape, uiExtKeyEscape },
{ GDK_KEY_Insert, uiExtKeyInsert },
{ GDK_KEY_Delete, uiExtKeyDelete },
{ GDK_KEY_Home, uiExtKeyHome },
{ GDK_KEY_End, uiExtKeyEnd },
{ GDK_KEY_Page_Up, uiExtKeyPageUp },
{ GDK_KEY_Page_Down, uiExtKeyPageDown },
{ GDK_KEY_Up, uiExtKeyUp },
{ GDK_KEY_Down, uiExtKeyDown },
{ GDK_KEY_Left, uiExtKeyLeft },
{ GDK_KEY_Right, uiExtKeyRight },
{ GDK_KEY_F1, uiExtKeyF1 },
{ GDK_KEY_F2, uiExtKeyF2 },
{ GDK_KEY_F3, uiExtKeyF3 },
{ GDK_KEY_F4, uiExtKeyF4 },
{ GDK_KEY_F5, uiExtKeyF5 },
{ GDK_KEY_F6, uiExtKeyF6 },
{ GDK_KEY_F7, uiExtKeyF7 },
{ GDK_KEY_F8, uiExtKeyF8 },
{ GDK_KEY_F9, uiExtKeyF9 },
{ GDK_KEY_F10, uiExtKeyF10 },
{ GDK_KEY_F11, uiExtKeyF11 },
{ GDK_KEY_F12, uiExtKeyF12 },
// numpad numeric keys and . are handled in events.c
{ GDK_KEY_KP_Enter, uiExtKeyNEnter },
{ GDK_KEY_KP_Add, uiExtKeyNAdd },
{ GDK_KEY_KP_Subtract, uiExtKeyNSubtract },
{ GDK_KEY_KP_Multiply, uiExtKeyNMultiply },
{ GDK_KEY_KP_Divide, uiExtKeyNDivide },
{ GDK_KEY_Print, 0 },
};
static const struct {
guint keyval;
uiModifiers mod;
} modKeys[] = {
{ GDK_KEY_Control_L, uiModifierCtrl },
{ GDK_KEY_Control_R, uiModifierCtrl },
{ GDK_KEY_Alt_L, uiModifierAlt },
{ GDK_KEY_Alt_R, uiModifierAlt },
{ GDK_KEY_Meta_L, uiModifierAlt },
{ GDK_KEY_Meta_R, uiModifierAlt },
{ GDK_KEY_Shift_L, uiModifierShift },
{ GDK_KEY_Shift_R, uiModifierShift },
{ GDK_KEY_Super_L, uiModifierSuper },
{ GDK_KEY_Super_R, uiModifierSuper },
{ GDK_KEY_Print, 0 },
};
static int areaKeyEvent(uiArea *a, int up, GdkEventKey *e) static int areaKeyEvent(uiArea *a, int up, GdkEventKey *e)
{ {
uiAreaKeyEvent ke; uiAreaKeyEvent ke;
guint state; if (fillUiKeyEvent(&ke, e)) {
int i; ke.Up = up;
return (*(a->ah->KeyEvent))(a->ah, a, &ke);
ke.Key = 0; }
ke.ExtKey = 0;
ke.Modifier = 0;
state = translateModifiers(e->state, e->window);
ke.Modifiers = toModifiers(state);
ke.Up = up;
for (i = 0; extKeys[i].keyval != GDK_KEY_Print; i++)
if (extKeys[i].keyval == e->keyval) {
ke.ExtKey = extKeys[i].extkey;
goto keyFound;
}
for (i = 0; modKeys[i].keyval != GDK_KEY_Print; i++)
if (modKeys[i].keyval == e->keyval) {
ke.Modifier = modKeys[i].mod;
// don't include the modifier in ke.Modifiers
ke.Modifiers &= ~ke.Modifier;
goto keyFound;
}
if (uiprivFromScancode(e->hardware_keycode - 8, &ke))
goto keyFound;
// no supported key found; treat as unhandled
return 0; return 0;
keyFound:
return (*(a->ah->KeyEvent))(a->ah, a, &ke);
} }
static gboolean areaWidget_key_press_event(GtkWidget *w, GdkEventKey *e) static gboolean areaWidget_key_press_event(GtkWidget *w, GdkEventKey *e)

View File

@ -1,5 +1,6 @@
// 11 june 2015 // 11 june 2015
#include "uipriv_unix.h" #include "uipriv_unix.h"
#include "keyboard.h"
struct uiEntry { struct uiEntry {
uiUnixControl c; uiUnixControl c;
@ -9,6 +10,7 @@ struct uiEntry {
void (*onChanged)(uiEntry *, void *); void (*onChanged)(uiEntry *, void *);
void *onChangedData; void *onChangedData;
gulong onChangedSignal; gulong onChangedSignal;
int (*onKeyEvent)(uiEntry *, uiAreaKeyEvent *);
}; };
uiUnixControlAllDefaults(uiEntry) uiUnixControlAllDefaults(uiEntry)
@ -25,6 +27,22 @@ static void defaultOnChanged(uiEntry *e, void *data)
// do nothing // do nothing
} }
static gboolean onKeyEvent(GtkEditable *editable, GdkEventKey *event, gpointer data)
{
uiEntry *e = (uiEntry*)(data);
uiAreaKeyEvent ke;
if (fillUiKeyEvent(&ke, event))
return (*(e->onKeyEvent))(e, &ke);
return GDK_EVENT_PROPAGATE;
}
static int defaultOnKeyEvent(uiEntry *e, uiAreaKeyEvent *uke)
{
// do nothing
// return GDK_EVENT_STOP; // to stop further key handling
return GDK_EVENT_PROPAGATE;
}
char *uiEntryText(uiEntry *e) char *uiEntryText(uiEntry *e)
{ {
return uiUnixStrdupText(gtk_entry_get_text(e->entry)); return uiUnixStrdupText(gtk_entry_get_text(e->entry));
@ -45,6 +63,11 @@ void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *, void *), void *data)
e->onChangedData = data; e->onChangedData = data;
} }
void uiEntryOnKeyEvent(uiEntry *e, int (*f)(uiEntry *, uiAreaKeyEvent *))
{
e->onKeyEvent = f;
}
int uiEntryReadOnly(uiEntry *e) int uiEntryReadOnly(uiEntry *e)
{ {
return gtk_editable_get_editable(e->editable) == FALSE; return gtk_editable_get_editable(e->editable) == FALSE;
@ -73,6 +96,11 @@ static uiEntry *finishNewEntry(GtkWidget *w, const gchar *signal)
e->onChangedSignal = g_signal_connect(e->widget, signal, G_CALLBACK(onChanged), e); e->onChangedSignal = g_signal_connect(e->widget, signal, G_CALLBACK(onChanged), e);
uiEntryOnChanged(e, defaultOnChanged, NULL); uiEntryOnChanged(e, defaultOnChanged, NULL);
gtk_widget_add_events(e->widget, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
g_signal_connect(e->widget, "key_press_event", G_CALLBACK(onKeyEvent), e);
g_signal_connect(e->widget, "key_release_event", G_CALLBACK(onKeyEvent), e);
uiEntryOnKeyEvent(e, defaultOnKeyEvent);
return e; return e;
} }

130
unix/keyboard.h Normal file
View File

@ -0,0 +1,130 @@
#pragma once
#include "uipriv_unix.h"
static guint translateModifiers(guint state, GdkWindow *window)
{
GdkModifierType statetype;
// GDK doesn't initialize the modifier flags fully; we have to explicitly tell it to (thanks to Daniel_S and daniels (two different people) in irc.gimp.net/#gtk+)
statetype = state;
gdk_keymap_add_virtual_modifiers(
gdk_keymap_get_for_display(gdk_window_get_display(window)),
&statetype);
return statetype;
}
static uiModifiers toModifiers(guint state)
{
uiModifiers m;
m = 0;
if ((state & GDK_CONTROL_MASK) != 0)
m |= uiModifierCtrl;
if ((state & GDK_META_MASK) != 0)
m |= uiModifierAlt;
if ((state & GDK_MOD1_MASK) != 0) // GTK+ itself requires this to be Alt (just read through gtkaccelgroup.c)
m |= uiModifierAlt;
if ((state & GDK_SHIFT_MASK) != 0)
m |= uiModifierShift;
if ((state & GDK_SUPER_MASK) != 0)
m |= uiModifierSuper;
return m;
}
// we use GDK_KEY_Print as a sentinel because libui will never support the print screen key; that key belongs to the user
static const struct {
guint keyval;
uiExtKey extkey;
} extKeys[] = {
{ GDK_KEY_Escape, uiExtKeyEscape },
{ GDK_KEY_Insert, uiExtKeyInsert },
{ GDK_KEY_Delete, uiExtKeyDelete },
{ GDK_KEY_Home, uiExtKeyHome },
{ GDK_KEY_End, uiExtKeyEnd },
{ GDK_KEY_Page_Up, uiExtKeyPageUp },
{ GDK_KEY_Page_Down, uiExtKeyPageDown },
{ GDK_KEY_Up, uiExtKeyUp },
{ GDK_KEY_Down, uiExtKeyDown },
{ GDK_KEY_Left, uiExtKeyLeft },
{ GDK_KEY_Right, uiExtKeyRight },
{ GDK_KEY_F1, uiExtKeyF1 },
{ GDK_KEY_F2, uiExtKeyF2 },
{ GDK_KEY_F3, uiExtKeyF3 },
{ GDK_KEY_F4, uiExtKeyF4 },
{ GDK_KEY_F5, uiExtKeyF5 },
{ GDK_KEY_F6, uiExtKeyF6 },
{ GDK_KEY_F7, uiExtKeyF7 },
{ GDK_KEY_F8, uiExtKeyF8 },
{ GDK_KEY_F9, uiExtKeyF9 },
{ GDK_KEY_F10, uiExtKeyF10 },
{ GDK_KEY_F11, uiExtKeyF11 },
{ GDK_KEY_F12, uiExtKeyF12 },
// numpad numeric keys and . are handled in events.c
{ GDK_KEY_KP_Enter, uiExtKeyNEnter },
{ GDK_KEY_KP_Add, uiExtKeyNAdd },
{ GDK_KEY_KP_Subtract, uiExtKeyNSubtract },
{ GDK_KEY_KP_Multiply, uiExtKeyNMultiply },
{ GDK_KEY_KP_Divide, uiExtKeyNDivide },
{ GDK_KEY_Print, 0 },
};
static const struct {
guint keyval;
uiModifiers mod;
} modKeys[] = {
{ GDK_KEY_Control_L, uiModifierCtrl },
{ GDK_KEY_Control_R, uiModifierCtrl },
{ GDK_KEY_Alt_L, uiModifierAlt },
{ GDK_KEY_Alt_R, uiModifierAlt },
{ GDK_KEY_Meta_L, uiModifierAlt },
{ GDK_KEY_Meta_R, uiModifierAlt },
{ GDK_KEY_Shift_L, uiModifierShift },
{ GDK_KEY_Shift_R, uiModifierShift },
{ GDK_KEY_Super_L, uiModifierSuper },
{ GDK_KEY_Super_R, uiModifierSuper },
{ GDK_KEY_Print, 0 },
};
static gboolean fillUiKeyEvent(uiAreaKeyEvent *ke, GdkEventKey *e)
{
guint state;
int i;
ke->Key = 0;
ke->ExtKey = 0;
ke->Modifier = 0;
state = translateModifiers(e->state, e->window);
ke->Modifiers = toModifiers(state);
for (i = 0; extKeys[i].keyval != GDK_KEY_Print; i++)
if (extKeys[i].keyval == e->keyval) {
ke->ExtKey = extKeys[i].extkey;
goto keyFound;
}
for (i = 0; modKeys[i].keyval != GDK_KEY_Print; i++)
if (modKeys[i].keyval == e->keyval) {
ke->Modifier = modKeys[i].mod;
// don't include the modifier in ke->Modifiers
ke->Modifiers &= ~ke->Modifier;
goto keyFound;
}
if (uiprivFromScancode(e->hardware_keycode - 8, ke))
goto keyFound;
// no supported key found; treat as unhandled
return FALSE;
keyFound:
ke->Up = 0;
if (e->type == GDK_KEY_PRESS)
ke->Up = 1;
return TRUE;
}

View File

@ -1,4 +1,5 @@
// 22 april 2015 // 22 april 2015
#pragma once
#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_40 #define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_40
#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_40 #define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_40
#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_10 #define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_10

View File

@ -1,26 +1,10 @@
// 8 september 2015 // 8 september 2015
#include "uipriv_windows.hpp" #include "uipriv_windows.hpp"
#include "area.hpp" #include "area.hpp"
#include "keyboard.hpp"
// TODO https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp notes on explicit RTL handling under MirrorXCoordinate(); also in areadraw.cpp too? // TODO https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp notes on explicit RTL handling under MirrorXCoordinate(); also in areadraw.cpp too?
static uiModifiers getModifiers(void)
{
uiModifiers m = 0;
if ((GetKeyState(VK_CONTROL) & 0x80) != 0)
m |= uiModifierCtrl;
if ((GetKeyState(VK_MENU) & 0x80) != 0)
m |= uiModifierAlt;
if ((GetKeyState(VK_SHIFT) & 0x80) != 0)
m |= uiModifierShift;
if ((GetKeyState(VK_LWIN) & 0x80) != 0)
m |= uiModifierSuper;
if ((GetKeyState(VK_RWIN) & 0x80) != 0)
m |= uiModifierSuper;
return m;
}
/* /*
Windows doesn't natively support mouse crossing events. Windows doesn't natively support mouse crossing events.
@ -177,81 +161,6 @@ static void onMouseLeft(uiArea *a)
uiprivClickCounterReset(&(a->cc)); uiprivClickCounterReset(&(a->cc));
} }
// we use VK_SNAPSHOT as a sentinel because libui will never support the print screen key; that key belongs to the user
struct extkeymap {
WPARAM vk;
uiExtKey extkey;
};
// all mappings come from GLFW - https://github.com/glfw/glfw/blob/master/src/win32_window.c#L152
static const struct extkeymap numpadExtKeys[] = {
{ VK_HOME, uiExtKeyN7 },
{ VK_UP, uiExtKeyN8 },
{ VK_PRIOR, uiExtKeyN9 },
{ VK_LEFT, uiExtKeyN4 },
{ VK_CLEAR, uiExtKeyN5 },
{ VK_RIGHT, uiExtKeyN6 },
{ VK_END, uiExtKeyN1 },
{ VK_DOWN, uiExtKeyN2 },
{ VK_NEXT, uiExtKeyN3 },
{ VK_INSERT, uiExtKeyN0 },
{ VK_DELETE, uiExtKeyNDot },
{ VK_SNAPSHOT, 0 },
};
static const struct extkeymap extKeys[] = {
{ VK_ESCAPE, uiExtKeyEscape },
{ VK_INSERT, uiExtKeyInsert },
{ VK_DELETE, uiExtKeyDelete },
{ VK_HOME, uiExtKeyHome },
{ VK_END, uiExtKeyEnd },
{ VK_PRIOR, uiExtKeyPageUp },
{ VK_NEXT, uiExtKeyPageDown },
{ VK_UP, uiExtKeyUp },
{ VK_DOWN, uiExtKeyDown },
{ VK_LEFT, uiExtKeyLeft },
{ VK_RIGHT, uiExtKeyRight },
{ VK_F1, uiExtKeyF1 },
{ VK_F2, uiExtKeyF2 },
{ VK_F3, uiExtKeyF3 },
{ VK_F4, uiExtKeyF4 },
{ VK_F5, uiExtKeyF5 },
{ VK_F6, uiExtKeyF6 },
{ VK_F7, uiExtKeyF7 },
{ VK_F8, uiExtKeyF8 },
{ VK_F9, uiExtKeyF9 },
{ VK_F10, uiExtKeyF10 },
{ VK_F11, uiExtKeyF11 },
{ VK_F12, uiExtKeyF12 },
// numpad numeric keys and . are handled in common/areaevents.c
// numpad enter is handled in code below
{ VK_ADD, uiExtKeyNAdd },
{ VK_SUBTRACT, uiExtKeyNSubtract },
{ VK_MULTIPLY, uiExtKeyNMultiply },
{ VK_DIVIDE, uiExtKeyNDivide },
{ VK_SNAPSHOT, 0 },
};
static const struct {
WPARAM vk;
uiModifiers mod;
} modKeys[] = {
// even if the separate left/right aren't necessary, have them here anyway, just to be safe
{ VK_CONTROL, uiModifierCtrl },
{ VK_LCONTROL, uiModifierCtrl },
{ VK_RCONTROL, uiModifierCtrl },
{ VK_MENU, uiModifierAlt },
{ VK_LMENU, uiModifierAlt },
{ VK_RMENU, uiModifierAlt },
{ VK_SHIFT, uiModifierShift },
{ VK_LSHIFT, uiModifierShift },
{ VK_RSHIFT, uiModifierShift },
// there's no combined Windows key virtual-key code as there is with the others
{ VK_LWIN, uiModifierSuper },
{ VK_RWIN, uiModifierSuper },
{ VK_SNAPSHOT, 0 },
};
static int areaKeyEvent(uiArea *a, int up, WPARAM wParam, LPARAM lParam) static int areaKeyEvent(uiArea *a, int up, WPARAM wParam, LPARAM lParam)
{ {
uiAreaKeyEvent ke; uiAreaKeyEvent ke;
@ -261,9 +170,7 @@ static int areaKeyEvent(uiArea *a, int up, WPARAM wParam, LPARAM lParam)
ke.Key = 0; ke.Key = 0;
ke.ExtKey = 0; ke.ExtKey = 0;
ke.Modifier = 0; ke.Modifier = 0;
ke.Modifiers = getModifiers(); ke.Modifiers = getModifiers();
ke.Up = up; ke.Up = up;
// the numeric keypad keys when Num Lock is off are considered left-hand keys as the separate navigation buttons were added later // the numeric keypad keys when Num Lock is off are considered left-hand keys as the separate navigation buttons were added later
@ -282,25 +189,7 @@ static int areaKeyEvent(uiArea *a, int up, WPARAM wParam, LPARAM lParam)
goto keyFound; goto keyFound;
} }
// okay, those above cases didn't match anything if (fillKeyEvent(ke, wParam, lParam))
// first try the extended keys
for (i = 0; extKeys[i].vk != VK_SNAPSHOT; i++)
if (extKeys[i].vk == wParam) {
ke.ExtKey = extKeys[i].extkey;
goto keyFound;
}
// then try modifier keys
for (i = 0; modKeys[i].vk != VK_SNAPSHOT; i++)
if (modKeys[i].vk == wParam) {
ke.Modifier = modKeys[i].mod;
// and don't include the key in Modifiers
ke.Modifiers &= ~ke.Modifier;
goto keyFound;
}
// and finally everything else
if (uiprivFromScancode((lParam >> 16) & 0xFF, &ke))
goto keyFound; goto keyFound;
// not a supported key, assume unhandled // not a supported key, assume unhandled

View File

@ -1,5 +1,6 @@
// 8 april 2015 // 8 april 2015
#include "uipriv_windows.hpp" #include "uipriv_windows.hpp"
#include "keyboard.hpp"
struct uiEntry { struct uiEntry {
uiWindowsControl c; uiWindowsControl c;
@ -7,8 +8,46 @@ struct uiEntry {
void (*onChanged)(uiEntry *, void *); void (*onChanged)(uiEntry *, void *);
void *onChangedData; void *onChangedData;
BOOL inhibitChanged; BOOL inhibitChanged;
int (*onKeyEvent)(uiEntry *, uiAreaKeyEvent *);
void *self;
WNDPROC native_wndproc;
}; };
static LRESULT CALLBACK entryWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL righthand = FALSE;
uiAreaKeyEvent ke;
ke.Key = 0;
ke.ExtKey = 0;
ke.Modifier = 0;
ke.Modifiers = getModifiers();
ke.Up = 0;
int (*onKeyEvent)(uiEntry *, uiAreaKeyEvent *) = (int (*)(uiEntry *, uiAreaKeyEvent *))GetProp(hwnd, L"ON_KEY_EVENT");
uiEntry *self = (uiEntry *)GetProp(hwnd, L"SELF");
switch (uMsg)
{
case WM_GETDLGCODE:
return DLGC_HASSETSEL | DLGC_WANTALLKEYS;
break;
case WM_KEYUP:
ke.Up = 1;
case WM_KEYDOWN:
if (onKeyEvent) {
fillKeyEvent(ke, wParam, lParam);
if ((*onKeyEvent)(self, &ke)) {
return TRUE;
}
}
break;
}
WNDPROC native_wndproc = (WNDPROC)GetProp(hwnd, L"NATIVE_WNDPROC");
return CallWindowProcW(native_wndproc, hwnd, uMsg, wParam, lParam);
}
static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult)
{ {
uiEntry *e = uiEntry(c); uiEntry *e = uiEntry(c);
@ -26,6 +65,11 @@ static void uiEntryDestroy(uiControl *c)
{ {
uiEntry *e = uiEntry(c); uiEntry *e = uiEntry(c);
(WNDPROC)SetWindowLongPtrW(e->hwnd, GWLP_WNDPROC, (LONG_PTR)e->native_wndproc);
RemoveProp(e->hwnd, L"NATIVE_WNDPROC");
RemoveProp(e->hwnd, L"ON_KEY_EVENT");
RemoveProp(e->hwnd, L"SELF");
uiWindowsUnregisterWM_COMMANDHandler(e->hwnd); uiWindowsUnregisterWM_COMMANDHandler(e->hwnd);
uiWindowsEnsureDestroyWindow(e->hwnd); uiWindowsEnsureDestroyWindow(e->hwnd);
uiFreeControl(uiControl(e)); uiFreeControl(uiControl(e));
@ -56,6 +100,13 @@ static void defaultOnChanged(uiEntry *e, void *data)
// do nothing // do nothing
} }
static int defaultOnKeyEvent(uiEntry *e, uiAreaKeyEvent *event)
{
// do nothing
return FALSE;
}
char *uiEntryText(uiEntry *e) char *uiEntryText(uiEntry *e)
{ {
return uiWindowsWindowText(e->hwnd); return uiWindowsWindowText(e->hwnd);
@ -76,6 +127,12 @@ void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *, void *), void *data)
e->onChangedData = data; e->onChangedData = data;
} }
void uiEntryOnKeyEvent(uiEntry *e, int (*f)(uiEntry *, uiAreaKeyEvent *))
{
e->onKeyEvent = f;
SetProp(e->hwnd, L"ON_KEY_EVENT", (HGLOBAL)(f));
}
int uiEntryReadOnly(uiEntry *e) int uiEntryReadOnly(uiEntry *e)
{ {
return (getStyle(e->hwnd) & ES_READONLY) != 0; return (getStyle(e->hwnd) & ES_READONLY) != 0;
@ -105,7 +162,13 @@ static uiEntry *finishNewEntry(DWORD style)
TRUE); TRUE);
uiWindowsRegisterWM_COMMANDHandler(e->hwnd, onWM_COMMAND, uiControl(e)); uiWindowsRegisterWM_COMMANDHandler(e->hwnd, onWM_COMMAND, uiControl(e));
e->native_wndproc = (WNDPROC)SetWindowLongPtrW(e->hwnd, GWLP_WNDPROC, (LONG_PTR)entryWndProc);
SetProp(e->hwnd, L"NATIVE_WNDPROC", (HGLOBAL)e->native_wndproc);
SetProp(e->hwnd, L"SELF", (HGLOBAL)e);
uiEntryOnChanged(e, defaultOnChanged, NULL); uiEntryOnChanged(e, defaultOnChanged, NULL);
uiEntryOnKeyEvent(e, defaultOnKeyEvent);
return e; return e;
} }

121
windows/keyboard.hpp Normal file
View File

@ -0,0 +1,121 @@
#pragma once
// we use VK_SNAPSHOT as a sentinel because libui will never support the print screen key; that key belongs to the user
struct extkeymap {
WPARAM vk;
uiExtKey extkey;
};
// all mappings come from GLFW - https://github.com/glfw/glfw/blob/master/src/win32_window.c#L152
static const struct extkeymap numpadExtKeys[] = {
{ VK_HOME, uiExtKeyN7 },
{ VK_UP, uiExtKeyN8 },
{ VK_PRIOR, uiExtKeyN9 },
{ VK_LEFT, uiExtKeyN4 },
{ VK_CLEAR, uiExtKeyN5 },
{ VK_RIGHT, uiExtKeyN6 },
{ VK_END, uiExtKeyN1 },
{ VK_DOWN, uiExtKeyN2 },
{ VK_NEXT, uiExtKeyN3 },
{ VK_INSERT, uiExtKeyN0 },
{ VK_DELETE, uiExtKeyNDot },
{ VK_SNAPSHOT, 0 },
};
static const struct extkeymap extKeys[] = {
{ VK_ESCAPE, uiExtKeyEscape },
{ VK_INSERT, uiExtKeyInsert },
{ VK_DELETE, uiExtKeyDelete },
{ VK_HOME, uiExtKeyHome },
{ VK_END, uiExtKeyEnd },
{ VK_PRIOR, uiExtKeyPageUp },
{ VK_NEXT, uiExtKeyPageDown },
{ VK_UP, uiExtKeyUp },
{ VK_DOWN, uiExtKeyDown },
{ VK_LEFT, uiExtKeyLeft },
{ VK_RIGHT, uiExtKeyRight },
{ VK_F1, uiExtKeyF1 },
{ VK_F2, uiExtKeyF2 },
{ VK_F3, uiExtKeyF3 },
{ VK_F4, uiExtKeyF4 },
{ VK_F5, uiExtKeyF5 },
{ VK_F6, uiExtKeyF6 },
{ VK_F7, uiExtKeyF7 },
{ VK_F8, uiExtKeyF8 },
{ VK_F9, uiExtKeyF9 },
{ VK_F10, uiExtKeyF10 },
{ VK_F11, uiExtKeyF11 },
{ VK_F12, uiExtKeyF12 },
// numpad numeric keys and . are handled in common/areaevents.c
// numpad enter is handled in code below
{ VK_ADD, uiExtKeyNAdd },
{ VK_SUBTRACT, uiExtKeyNSubtract },
{ VK_MULTIPLY, uiExtKeyNMultiply },
{ VK_DIVIDE, uiExtKeyNDivide },
{ VK_SNAPSHOT, 0 },
};
static const struct {
WPARAM vk;
uiModifiers mod;
} modKeys[] = {
// even if the separate left/right aren't necessary, have them here anyway, just to be safe
{ VK_CONTROL, uiModifierCtrl },
{ VK_LCONTROL, uiModifierCtrl },
{ VK_RCONTROL, uiModifierCtrl },
{ VK_MENU, uiModifierAlt },
{ VK_LMENU, uiModifierAlt },
{ VK_RMENU, uiModifierAlt },
{ VK_SHIFT, uiModifierShift },
{ VK_LSHIFT, uiModifierShift },
{ VK_RSHIFT, uiModifierShift },
// there's no combined Windows key virtual-key code as there is with the others
{ VK_LWIN, uiModifierSuper },
{ VK_RWIN, uiModifierSuper },
{ VK_SNAPSHOT, 0 },
};
static uiModifiers getModifiers(void)
{
uiModifiers m = 0;
if ((GetKeyState(VK_CONTROL) & 0x80) != 0)
m |= uiModifierCtrl;
if ((GetKeyState(VK_MENU) & 0x80) != 0)
m |= uiModifierAlt;
if ((GetKeyState(VK_SHIFT) & 0x80) != 0)
m |= uiModifierShift;
if ((GetKeyState(VK_LWIN) & 0x80) != 0)
m |= uiModifierSuper;
if ((GetKeyState(VK_RWIN) & 0x80) != 0)
m |= uiModifierSuper;
return m;
}
static BOOL fillKeyEvent(uiAreaKeyEvent &ke, WPARAM wParam, LPARAM lParam)
{
int i = 0;
for (i = 0; extKeys[i].vk != VK_SNAPSHOT; i++) {
if (extKeys[i].vk == wParam) {
ke.ExtKey = extKeys[i].extkey;
return TRUE;
}
}
for (i = 0; modKeys[i].vk != VK_SNAPSHOT; i++) {
if (modKeys[i].vk == wParam) {
ke.Modifier = modKeys[i].mod;
ke.Modifiers &= ~ke.Modifier;
return TRUE;
}
}
// TODO the original code only did this if ke.Modifiers == 0 - why?
if (uiprivFromScancode((lParam >> 16) & 0xFF, &ke)) {
return TRUE;
}
return FALSE;
}