2014-03-29 22:57:49 -05:00
// 29 march 2014
package ui
import (
"unsafe"
2014-03-30 13:25:01 -05:00
"image"
2014-03-29 22:57:49 -05:00
)
// #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit
// #include <stdlib.h>
//// #include <HIToolbox/Events.h>
// #include "objc_darwin.h"
2014-05-13 08:33:37 -05:00
// #include "area_darwin.h"
2014-03-29 22:57:49 -05:00
import "C"
var (
2014-04-04 20:32:10 -05:00
_NSView = objc_getClass ( "NSView" )
2014-03-29 22:57:49 -05:00
)
2014-05-10 13:59:11 -05:00
func makeArea ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-05-13 08:33:37 -05:00
area := C . makeArea ( )
2014-04-13 11:52:10 -05:00
area = newScrollView ( area )
addControl ( parentWindow , area )
return area
}
func areaInScrollView ( scrollview C . id ) C . id {
return getScrollViewContent ( scrollview )
}
2014-03-30 13:25:01 -05:00
var (
_drawAtPoint = sel_getUid ( "drawAtPoint:" )
)
2014-03-29 22:57:49 -05:00
//export areaView_drawRect
func areaView_drawRect ( self C . id , rect C . struct_xrect ) {
2014-03-30 13:25:01 -05:00
s := getSysData ( self )
// TODO clear clip rect
2014-03-30 13:29:02 -05:00
// rectangles in Cocoa are origin/size, not point0/point1; if we don't watch for this, weird things will happen when scrolling
// TODO change names EVERYWHERE ELSE to match
cliprect := image . Rect ( int ( rect . x ) , int ( rect . y ) , int ( rect . x + rect . width ) , int ( rect . y + rect . height ) )
2014-03-30 13:25:01 -05:00
max := C . objc_msgSend_stret_rect_noargs ( self , _frame )
cliprect = image . Rect ( 0 , 0 , int ( max . width ) , int ( max . height ) ) . Intersect ( cliprect )
if cliprect . Empty ( ) { // no intersection; nothing to paint
return
}
i := s . handler . Paint ( cliprect )
C . drawImage (
2014-04-09 17:26:20 -05:00
unsafe . Pointer ( pixelData ( i ) ) , C . int64_t ( i . Rect . Dx ( ) ) , C . int64_t ( i . Rect . Dy ( ) ) , C . int64_t ( i . Stride ) ,
2014-03-30 13:25:01 -05:00
C . int64_t ( cliprect . Min . X ) , C . int64_t ( cliprect . Min . Y ) )
}
2014-05-10 20:03:04 -05:00
var (
_addTrackingArea = sel_getUid ( "addTrackingArea:" )
_removeTrackingArea = sel_getUid ( "removeTrackingArea:" )
)
2014-03-30 16:52:27 -05:00
var (
2014-04-03 22:52:26 -05:00
_NSEvent = objc_getClass ( "NSEvent" )
2014-03-30 16:52:27 -05:00
_modifierFlags = sel_getUid ( "modifierFlags" )
_buttonNumber = sel_getUid ( "buttonNumber" )
_clickCount = sel_getUid ( "clickCount" )
2014-04-03 22:52:26 -05:00
_pressedMouseButtons = sel_getUid ( "pressedMouseButtons" )
2014-03-30 16:52:27 -05:00
)
func parseModifiers ( e C . id ) ( m Modifiers ) {
const (
_NSShiftKeyMask = 1 << 17
_NSControlKeyMask = 1 << 18
_NSAlternateKeyMask = 1 << 19
_NSCommandKeyMask = 1 << 20
)
2014-05-13 08:33:37 -05:00
mods := uintptr ( C . modifierFlags ( e ) )
2014-03-30 16:52:27 -05:00
if ( mods & _NSShiftKeyMask ) != 0 {
m |= Shift
}
if ( mods & _NSControlKeyMask ) != 0 {
// TODO
}
if ( mods & _NSAlternateKeyMask ) != 0 {
m |= Alt
}
if ( mods & _NSCommandKeyMask ) != 0 {
m |= Ctrl
}
return m
}
2014-05-07 18:40:46 -05:00
// TODO pressing both buttons 1 and 3 simultaneously gets turned into button 2; see if we can turn that off for our NSView only
2014-03-30 16:52:27 -05:00
func areaMouseEvent ( self C . id , e C . id , click bool , up bool ) {
var me MouseEvent
s := getSysData ( self )
xp := C . getTranslatedEventPoint ( self , e )
me . Pos = image . Pt ( int ( xp . x ) , int ( xp . y ) )
2014-05-12 10:34:24 -05:00
// for the most part, Cocoa won't geenerate an event outside the Area... except when dragging outside the Area, so check for this
max := C . objc_msgSend_stret_rect_noargs ( self , _frame )
if ! me . Pos . In ( image . Rect ( 0 , 0 , int ( max . width ) , int ( max . height ) ) ) {
return
}
2014-03-30 16:52:27 -05:00
me . Modifiers = parseModifiers ( e )
2014-05-13 08:33:37 -05:00
which := uint ( C . buttonNumber ( e ) ) + 1
2014-03-30 16:52:27 -05:00
if which == 3 { // swap middle and right button numbers
which = 2
} else if which == 2 {
which = 3
}
if click && up {
me . Up = which
} else if click {
me . Down = which
me . Count = uint ( C . objc_msgSend_intret_noargs ( e , _clickCount ) )
} else {
which = 0 // reset for Held processing below
}
2014-05-10 20:03:04 -05:00
// the docs do say don't use this for tracking since it returns the state now, and mouse move events work by tracking, but as far as I can tell dragging the mouse over the inactive window does n ot generate an event on Mac OS X, so :/ (TODO see what happens when the program is the current one; in my own separate tests no harm was done so eh; also no need for this if tracking doesn't touch dragging)
2014-05-13 08:33:37 -05:00
held := C . pressedMouseButtons ( )
2014-03-30 16:52:27 -05:00
if which != 1 && ( held & 1 ) != 0 { // button 1
me . Held = append ( me . Held , 1 )
}
if which != 2 && ( held & 4 ) != 0 { // button 2; mind the swap
me . Held = append ( me . Held , 2 )
}
if which != 3 && ( held & 2 ) != 0 { // button 3
me . Held = append ( me . Held , 3 )
}
held >>= 3
for i := uint ( 4 ) ; held != 0 ; i ++ {
if which != i && ( held & 1 ) != 0 {
me . Held = append ( me . Held , i )
}
held >>= 1
}
repaint := s . handler . Mouse ( me )
if repaint {
C . objc_msgSend_noargs ( self , _display )
}
}
2014-05-07 18:40:46 -05:00
//export areaView_mouseMoved_mouseDragged
2014-05-13 08:33:37 -05:00
func areaView_mouseMoved_mouseDragged ( self C . id , e C . id ) {
2014-05-10 20:03:04 -05:00
// for moving, this is handled by the tracking rect stuff above
2014-05-10 13:31:22 -05:00
// for dragging, if multiple buttons are held, only one of their xxxMouseDragged: messages will be sent, so this is OK to do
2014-03-30 16:52:27 -05:00
areaMouseEvent ( self , e , false , false )
}
2014-05-07 18:40:46 -05:00
//export areaView_mouseDown
2014-05-13 08:33:37 -05:00
func areaView_mouseDown ( self C . id , e C . id ) {
2014-03-30 16:52:27 -05:00
areaMouseEvent ( self , e , true , false )
}
//export areaView_mouseUp
2014-05-13 08:33:37 -05:00
func areaView_mouseUp ( self C . id , e C . id ) {
2014-03-30 16:52:27 -05:00
areaMouseEvent ( self , e , true , true )
}
2014-03-30 18:53:44 -05:00
var (
_keyCode = sel_getUid ( "keyCode" )
)
2014-05-13 08:33:37 -05:00
func sendKeyEvent ( self C . id , e C . id , ke KeyEvent ) bool {
2014-03-30 18:53:44 -05:00
s := getSysData ( self )
handled , repaint := s . handler . Key ( ke )
if repaint {
C . objc_msgSend_noargs ( self , _display )
}
2014-05-13 08:33:37 -05:00
return handled
2014-03-30 18:53:44 -05:00
}
2014-05-13 08:33:37 -05:00
func areaKeyEvent ( self C . id , e C . id , up bool ) bool {
2014-03-30 18:53:44 -05:00
var ke KeyEvent
2014-05-13 08:33:37 -05:00
keyCode := uintptr ( C . keyCode ( e ) )
2014-03-30 18:53:44 -05:00
ke , ok := fromKeycode ( keyCode )
if ! ok {
// no such key; modifiers by themselves are handled by -[self flagsChanged:]
2014-05-13 08:33:37 -05:00
return false
2014-03-30 18:53:44 -05:00
}
// either ke.Key or ke.ExtKey will be set at this point
ke . Modifiers = parseModifiers ( e )
ke . Up = up
2014-05-13 08:33:37 -05:00
return sendKeyEvent ( self , e , ke )
2014-03-30 18:53:44 -05:00
}
//export areaView_keyDown
2014-05-13 08:33:37 -05:00
func areaView_keyDown ( self C . id , e C . id ) C . BOOL {
return toBOOL ( areaKeyEvent ( self , e , false ) )
2014-03-30 18:53:44 -05:00
}
//export areaView_keyUp
2014-05-13 08:33:37 -05:00
func areaView_keyUp ( self C . id , e C . id ) C . BOOL {
return toBOOL ( areaKeyEvent ( self , e , true ) )
2014-03-30 18:53:44 -05:00
}
//export areaView_flagsChanged
2014-05-13 08:33:37 -05:00
func areaView_flagsChanged ( self C . id , e C . id ) C . BOOL {
2014-03-30 18:53:44 -05:00
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
2014-05-13 08:33:37 -05:00
keyCode := uintptr ( C . keyCode ( e ) )
2014-03-30 18:53:44 -05:00
mod , ok := keycodeModifiers [ keyCode ] // comma-ok form to avoid adding entries
if ! ok { // unknown modifier; ignore
2014-05-13 08:33:37 -05:00
return C . NO
2014-03-30 18:53:44 -05:00
}
ke . Modifiers = parseModifiers ( e )
ke . Up = ( ke . Modifiers & mod ) == 0
2014-05-13 08:33:37 -05:00
return toBOOL ( sendKeyEvent ( self , e , ke ) )
2014-03-30 18:53:44 -05:00
}