Added key events to Mac OS X Areas. Now Area is feature-complete (but buggy) on all platforms :D Also more TODOs.
This commit is contained in:
parent
41a7e3dab8
commit
6a7cb73dda
|
@ -17,6 +17,9 @@ import (
|
||||||
// extern void areaView_mouseMoved(id, SEL, id);
|
// extern void areaView_mouseMoved(id, SEL, id);
|
||||||
// extern void areaView_mouseDown_mouseDragged(id, SEL, id);
|
// extern void areaView_mouseDown_mouseDragged(id, SEL, id);
|
||||||
// extern void areaView_mouseUp(id, SEL, id);
|
// extern void areaView_mouseUp(id, SEL, id);
|
||||||
|
// extern void areaView_keyDown(id, SEL, id);
|
||||||
|
// extern void areaView_keyUp(id, SEL, id);
|
||||||
|
// extern void areaView_flagsChanged(id, SEL, id);
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -47,6 +50,9 @@ var eventMethods = []eventMethod{
|
||||||
eventMethod{"otherMouseDown:", uintptr(C.areaView_mouseDown_mouseDragged)},
|
eventMethod{"otherMouseDown:", uintptr(C.areaView_mouseDown_mouseDragged)},
|
||||||
eventMethod{"otherMouseDragged:", uintptr(C.areaView_mouseDown_mouseDragged)},
|
eventMethod{"otherMouseDragged:", uintptr(C.areaView_mouseDown_mouseDragged)},
|
||||||
eventMethod{"otherMouseUp:", uintptr(C.areaView_mouseUp)},
|
eventMethod{"otherMouseUp:", uintptr(C.areaView_mouseUp)},
|
||||||
|
eventMethod{"keyDown:", uintptr(C.areaView_keyDown)},
|
||||||
|
eventMethod{"keyUp:", uintptr(C.areaView_keyUp)},
|
||||||
|
eventMethod{"flagsChanged:", uintptr(C.areaView_flagsChanged)},
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkAreaClass() error {
|
func mkAreaClass() error {
|
||||||
|
@ -202,6 +208,68 @@ func areaView_mouseUp(self C.id, sel C.SEL, e C.id) {
|
||||||
areaMouseEvent(self, e, true, true)
|
areaMouseEvent(self, e, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_keyCode = sel_getUid("keyCode")
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleKeyEvent(self C.id) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendKeyEvent(self C.id, ke KeyEvent) {
|
||||||
|
s := getSysData(self)
|
||||||
|
handled, repaint := s.handler.Key(ke)
|
||||||
|
if repaint {
|
||||||
|
C.objc_msgSend_noargs(self, _display)
|
||||||
|
}
|
||||||
|
if !handled {
|
||||||
|
handleKeyEvent(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func areaKeyEvent(self C.id, e C.id, up bool) {
|
||||||
|
var ke KeyEvent
|
||||||
|
|
||||||
|
keyCode := uintptr(C.objc_msgSend_ushortret_noargs(e, _keyCode))
|
||||||
|
ke, ok := fromKeycode(keyCode)
|
||||||
|
if !ok {
|
||||||
|
// no such key; modifiers by themselves are handled by -[self flagsChanged:]
|
||||||
|
handleKeyEvent(self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// either ke.Key or ke.ExtKey will be set at this point
|
||||||
|
ke.Modifiers = parseModifiers(e)
|
||||||
|
ke.Up = up
|
||||||
|
sendKeyEvent(self, ke)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export areaView_keyDown
|
||||||
|
func areaView_keyDown(self C.id, sel C.SEL, e C.id) {
|
||||||
|
areaKeyEvent(self, e, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export areaView_keyUp
|
||||||
|
func areaView_keyUp(self C.id, sel C.SEL, e C.id) {
|
||||||
|
areaKeyEvent(self, e, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export areaView_flagsChanged
|
||||||
|
func areaView_flagsChanged(self C.id, sel C.SEL, e C.id) {
|
||||||
|
var ke KeyEvent
|
||||||
|
|
||||||
|
// Mac OS X sends this event on both key up and key down.
|
||||||
|
// Fortunately -[e keyCode] IS valid here, so we can simply map from key code to Modifiers, get the value of [e modifierFlags], the respective bit is set or not — that will give us the up/down state
|
||||||
|
keyCode := uintptr(C.objc_msgSend_ushortret_noargs(e, _keyCode))
|
||||||
|
mod, ok := keycodeModifiers[keyCode] // comma-ok form to avoid adding entries
|
||||||
|
if !ok { // unknown modifier; ignore
|
||||||
|
handleKeyEvent(self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ke.Modifiers = parseModifiers(e)
|
||||||
|
ke.Up = (ke.Modifiers & mod) == 0
|
||||||
|
sendKeyEvent(self, ke)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO combine these with the listbox functions?
|
// TODO combine these with the listbox functions?
|
||||||
|
|
||||||
func newAreaScrollView(area C.id) C.id {
|
func newAreaScrollView(area C.id) C.id {
|
||||||
|
|
|
@ -66,6 +66,15 @@ id objc_msgSend_id_int(id obj, SEL sel, id a, intptr_t b)
|
||||||
return objc_msgSend(obj, sel, a, (NSInteger) b);
|
return objc_msgSend(obj, sel, a, (NSInteger) b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
same as above, but for unsigned short
|
||||||
|
*/
|
||||||
|
|
||||||
|
uintptr_t objc_msgSend_ushortret_noargs(id obj, SEL sel)
|
||||||
|
{
|
||||||
|
return (uintptr_t) ((unsigned short) objc_msgSend(obj, sel));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
These are the objc_msgSend() wrappers around NSRect. The problem is that while on 32-bit systems, NSRect is a concrete structure, on 64-bit systems it's just a typedef to CGRect. While in practice just using CGRect everywhere seems to work, better to be safe than sorry.
|
These are the objc_msgSend() wrappers around NSRect. The problem is that while on 32-bit systems, NSRect is a concrete structure, on 64-bit systems it's just a typedef to CGRect. While in practice just using CGRect everywhere seems to work, better to be safe than sorry.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
// 30 march 2014
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mac OS X uses its own set of hardware key codes that are different from PC keyboard scancodes, but are positional (like PC keyboard scancodes). These are defined in <HIToolbox/Events.h>, a Carbon header. As far as I can tell, there's no way to include this header without either using an absolute path or linking Carbon into the program, so the constant values are used here instead.
|
||||||
|
|
||||||
|
The Cocoa docs do guarantee that -[NSEvent keyCode] results in key codes that are the same as those returned by Carbon; that is, these codes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// use uintptr to be safe
|
||||||
|
var keycodeKeys = map[uintptr]byte{
|
||||||
|
0x00: 'a',
|
||||||
|
0x01: 's',
|
||||||
|
0x02: 'd',
|
||||||
|
0x03: 'f',
|
||||||
|
0x04: 'h',
|
||||||
|
0x05: 'g',
|
||||||
|
0x06: 'z',
|
||||||
|
0x07: 'x',
|
||||||
|
0x08: 'c',
|
||||||
|
0x09: 'v',
|
||||||
|
0x0B: 'b',
|
||||||
|
0x0C: 'q',
|
||||||
|
0x0D: 'w',
|
||||||
|
0x0E: 'e',
|
||||||
|
0x0F: 'r',
|
||||||
|
0x10: 'y',
|
||||||
|
0x11: 't',
|
||||||
|
0x12: '1',
|
||||||
|
0x13: '2',
|
||||||
|
0x14: '3',
|
||||||
|
0x15: '4',
|
||||||
|
0x16: '6',
|
||||||
|
0x17: '5',
|
||||||
|
0x18: '=',
|
||||||
|
0x19: '9',
|
||||||
|
0x1A: '7',
|
||||||
|
0x1B: '-',
|
||||||
|
0x1C: '8',
|
||||||
|
0x1D: '0',
|
||||||
|
0x1E: ']',
|
||||||
|
0x1F: 'o',
|
||||||
|
0x20: 'u',
|
||||||
|
0x21: '[',
|
||||||
|
0x22: 'i',
|
||||||
|
0x23: 'p',
|
||||||
|
0x25: 'l',
|
||||||
|
0x26: 'j',
|
||||||
|
0x27: '\'',
|
||||||
|
0x28: 'k',
|
||||||
|
0x29: ';',
|
||||||
|
0x2A: '\\',
|
||||||
|
0x2B: ',',
|
||||||
|
0x2C: '/',
|
||||||
|
0x2D: 'n',
|
||||||
|
0x2E: 'm',
|
||||||
|
0x2F: '.',
|
||||||
|
0x32: '`',
|
||||||
|
0x24: '\n',
|
||||||
|
0x30: '\t',
|
||||||
|
0x31: ' ',
|
||||||
|
0x33: '\b',
|
||||||
|
}
|
||||||
|
|
||||||
|
var keycodeExtKeys = map[uintptr]ExtKey{
|
||||||
|
0x41: NDot,
|
||||||
|
0x43: NMultiply,
|
||||||
|
0x45: NAdd,
|
||||||
|
// 0x47: kVK_ANSI_KeypadClear,
|
||||||
|
0x4B: NDivide,
|
||||||
|
0x4C: NEnter,
|
||||||
|
0x4E: NSubtract,
|
||||||
|
0x52: N0,
|
||||||
|
0x53: N1,
|
||||||
|
0x54: N2,
|
||||||
|
0x55: N3,
|
||||||
|
0x56: N4,
|
||||||
|
0x57: N5,
|
||||||
|
0x58: N6,
|
||||||
|
0x59: N7,
|
||||||
|
0x5B: N8,
|
||||||
|
0x5C: N9,
|
||||||
|
0x35: Escape,
|
||||||
|
0x60: F5,
|
||||||
|
0x61: F6,
|
||||||
|
0x62: F7,
|
||||||
|
0x63: F3,
|
||||||
|
0x64: F8,
|
||||||
|
0x65: F9,
|
||||||
|
0x67: F11,
|
||||||
|
0x6D: F10,
|
||||||
|
0x6F: F12,
|
||||||
|
// 0x72: kVK_Help,
|
||||||
|
0x73: Home,
|
||||||
|
0x74: PageUp,
|
||||||
|
0x75: Delete,
|
||||||
|
0x76: F4,
|
||||||
|
0x77: End,
|
||||||
|
0x78: F2,
|
||||||
|
0x79: PageDown,
|
||||||
|
0x7A: F1,
|
||||||
|
0x7B: Left,
|
||||||
|
0x7C: Right,
|
||||||
|
0x7D: Down,
|
||||||
|
0x7E: Up,
|
||||||
|
}
|
||||||
|
|
||||||
|
var keycodeModifiers = map[uintptr]Modifiers{
|
||||||
|
0x37: Ctrl, // left command (TODO both commands?)
|
||||||
|
0x38: Shift, // left shift
|
||||||
|
0x3A: Alt, // left option
|
||||||
|
// 0x3B: kVK_Control,
|
||||||
|
0x3C: Shift, // right shift
|
||||||
|
0x3D: Alt, // right alt
|
||||||
|
// 0x3E: kVK_RightControl,
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromKeycode(keycode uintptr) (ke KeyEvent, ok bool) {
|
||||||
|
if key, ok := keycodeKeys[keycode]; ok {
|
||||||
|
ke.Key = key
|
||||||
|
return ke, true
|
||||||
|
}
|
||||||
|
if extkey, ok := keycodeExtKeys[keycode]; ok {
|
||||||
|
ke.ExtKey = extkey
|
||||||
|
return ke, true
|
||||||
|
}
|
||||||
|
return ke, false
|
||||||
|
}
|
|
@ -56,6 +56,8 @@ extern uintptr_t objc_msgSend_uintret_noargs(id objc, SEL sel);
|
||||||
|
|
||||||
extern intptr_t objc_msgSend_intret_noargs(id obj, SEL sel);
|
extern intptr_t objc_msgSend_intret_noargs(id obj, SEL sel);
|
||||||
|
|
||||||
|
extern uintptr_t objc_msgSend_ushortret_noargs(id objc, SEL sel);
|
||||||
|
|
||||||
#define m1(name, type1) \
|
#define m1(name, type1) \
|
||||||
static inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a) \
|
static inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a) \
|
||||||
{ \
|
{ \
|
||||||
|
|
6
todo.md
6
todo.md
|
@ -51,6 +51,12 @@ important things:
|
||||||
- make sure mouse events don't trigger if the control size is larger than the Area size and the mouse event happens outside the Area range on all platforms
|
- make sure mouse events don't trigger if the control size is larger than the Area size and the mouse event happens outside the Area range on all platforms
|
||||||
|
|
||||||
super ultra important things:
|
super ultra important things:
|
||||||
|
- formalize what happens if Modifiers by themselves are held
|
||||||
|
- OS X: find out if multiple DIFFERENT modifiers released at once produces multiple events
|
||||||
|
- in general, figure out what to do on multiple events, period
|
||||||
|
- OS X: handle Insert/Help key change in a sane and deterministic way
|
||||||
|
- will need old and new Mac keyboards...
|
||||||
|
- should pressing modifier+key in the keyboard test mark the key alone as pressed as well? I'm leaning toward no, in which case make sure this behavior exists on all platforms
|
||||||
- formalize dragging
|
- formalize dragging
|
||||||
- implement dragging on windows
|
- implement dragging on windows
|
||||||
- may need to drop Held depending on weirdness I see in OS X
|
- may need to drop Held depending on weirdness I see in OS X
|
||||||
|
|
Loading…
Reference in New Issue