#include "osview_osx.inl"
#include "oslistener.inl"
#include "osscroll_osx.inl"
#include "oscontrol_osx.inl"
#include "ospanel_osx.inl"
#include "oswindow_osx.inl"
#include "../osview.h"
#include "../osview.inl"
#include "../osgui.inl"
#include "../osscrolls.inl"
#include <draw2d/dctxh.h>
#include <core/event.h>
#include <core/heap.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
#if !defined(__MACOS__)
#error This file is only for OSX
#endif
@interface OSXView : NSView
{
@public
OSScrolls *scroll;
DCtx *ctx;
uint32_t flags;
NSTrackingArea *tracking_area;
ViewListeners listeners;
OSDraw osdraw;
Listener *OnFocus;
Listener *OnResignFocus;
Listener *OnAcceptFocus;
Listener *OnOverlay;
BOOL mouse_inside;
BOOL focused;
BOOL allow_tab;
}
@end
@implementation OSXView
- (void)drawFocusRingMask
{
NSRectFill([self bounds]);
}
- (NSRect)focusRingMaskBounds
{
return [self bounds];
}
- (void)drawRect:(NSRect)rect
{
if (self->listeners.OnDraw != NULL)
{
EvDraw params;
rect = [self frame];
params.ctx = NULL;
params.x = 0;
params.y = 0;
params.width = (real32_t)rect.size.width;
params.height = (real32_t)rect.size.height;
if (self->scroll != NULL)
{
params.x = (real32_t)_osscrolls_x_pos(self->scroll);
params.y = (real32_t)_osscrolls_y_pos(self->scroll);
}
if ((self->flags & ekVIEW_OPENGL) == 0)
{
NSGraphicsContext *nscontext = [NSGraphicsContext currentContext];
if (nscontext != nil)
{
if (self->ctx == NULL)
{
self->ctx = dctx_create();
dctx_set_flipped(self->ctx, (bool_t)[self isFlipped]);
dctx_data(self->ctx, &self->osdraw, NULL, OSDraw);
}
params.ctx = self->ctx;
dctx_set_gcontext(self->ctx, nscontext, (uint32_t)rect.size.width, (uint32_t)rect.size.height, params.x, params.y, 0, TRUE);
listener_event(self->listeners.OnDraw, ekGUI_EVENT_DRAW, cast(self, OSView), ¶ms, NULL, OSView, EvDraw, void);
dctx_unset_gcontext(self->ctx);
if (self->OnOverlay != NULL)
{
params.x = 0;
params.y = 0;
dctx_set_gcontext(self->ctx, nscontext, (uint32_t)rect.size.width, (uint32_t)rect.size.height, 0, 0, 0, TRUE);
listener_event(self->OnOverlay, ekGUI_EVENT_OVERLAY, cast(self, OSView), ¶ms, NULL, OSView, EvDraw, void);
dctx_unset_gcontext(self->ctx);
}
}
}
else
{
listener_event(self->listeners.OnDraw, ekGUI_EVENT_DRAW, cast(self, OSView), ¶ms, NULL, OSView, EvDraw, void);
}
}
#if defined(MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
#else
if (self->flags & ekVIEW_BORDER)
{
if (self->focused == YES)
{
NSRect r = [self bounds];
if (self->scroll != NULL)
{
r.size.width -= (CGFloat)(_osscrolls_bar_width(self->scroll, TRUE) + 2);
r.size.height -= (CGFloat)(_osscrolls_bar_height(self->scroll, TRUE) + 2);
}
NSSetFocusRingStyle(NSFocusRingOnly);
NSRectFill(r);
}
}
#endif
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (BOOL)mouseDownCanMoveWindow
{
return NO;
}
- (void)mouseEntered:(NSEvent *)theEvent
{
self->mouse_inside = YES;
_oslistener_mouse_enter(self, theEvent, self->scroll, &self->listeners);
}
- (void)mouseExited:(NSEvent *)theEvent
{
unref(theEvent);
self->mouse_inside = NO;
_oslistener_mouse_exit(self, &self->listeners);
}
- (void)mouseMoved:(NSEvent *)theEvent
{
if (self->mouse_inside == YES)
_oslistener_mouse_moved(self, theEvent, self->scroll, &self->listeners);
}
- (void)mouseDown:(NSEvent *)theEvent
{
if (_oswindow_mouse_down(cast(self, OSControl)) == TRUE)
_oslistener_mouse_down(self, theEvent, ekGUI_MOUSE_LEFT, self->scroll, &self->listeners);
}
- (void)rightMouseDown:(NSEvent *)theEvent
{
if (_oswindow_mouse_down(cast(self, OSControl)) == TRUE)
_oslistener_mouse_down(self, theEvent, ekGUI_MOUSE_RIGHT, self->scroll, &self->listeners);
}
- (void)otherMouseDown:(NSEvent *)theEvent
{
if (_oswindow_mouse_down(cast(self, OSControl)) == TRUE)
_oslistener_mouse_down(self, theEvent, ekGUI_MOUSE_MIDDLE, self->scroll, &self->listeners);
}
- (void)mouseUp:(NSEvent *)theEvent
{
_oslistener_mouse_up(self, theEvent, ekGUI_MOUSE_LEFT, self->scroll, &self->listeners);
}
- (void)rightMouseUp:(NSEvent *)theEvent
{
_oslistener_mouse_up(self, theEvent, ekGUI_MOUSE_RIGHT, self->scroll, &self->listeners);
}
- (void)otherMouseUp:(NSEvent *)theEvent
{
_oslistener_mouse_up(self, theEvent, ekGUI_MOUSE_MIDDLE, self->scroll, &self->listeners);
}
- (void)mouseDragged:(NSEvent *)theEvent
{
_oslistener_mouse_dragged(self, theEvent, ekGUI_MOUSE_LEFT, self->scroll, &self->listeners);
}
- (void)rightMouseDragged:(NSEvent *)theEvent
{
_oslistener_mouse_dragged(self, theEvent, ekGUI_MOUSE_RIGHT, self->scroll, &self->listeners);
}
- (void)otherMouseDragged:(NSEvent *)theEvent
{
_oslistener_mouse_dragged(self, theEvent, ekGUI_MOUSE_MIDDLE, self->scroll, &self->listeners);
}
- (void)scrollWheel:(NSEvent *)theEvent
{
if (self->scroll != nil)
{
gui_scroll_t ev = _osscroll_wheel_event(theEvent);
if (ev != ENUM_MAX(gui_scroll_t))
_osview_scroll_event(self, ekGUI_VERTICAL, ev);
}
_oslistener_scroll_whell(self, theEvent, self->scroll, &self->listeners);
}
- (void)keyDown:(NSEvent *)theEvent
{
if (_oswindow_key_down(cast(self, OSControl), theEvent) == FALSE)
_oslistener_key_down(self, theEvent, &self->listeners);
}
- (void)keyUp:(NSEvent *)theEvent
{
_oslistener_key_up(self, theEvent, &self->listeners);
}
- (void)flagsChanged:(NSEvent *)theEvent
{
_oslistener_key_flags_changed(self, theEvent, &self->listeners);
}
- (BOOL)isFlipped
{
return YES;
}
- (void)NAppGUIOSX_setOpenGL
{
self->flags |= ekVIEW_OPENGL;
}
- (void)NAppGUIOSX_unsetOpenGL
{
self->flags &= (uint32_t)~ekVIEW_OPENGL;
}
@end
OSView *osview_create(const uint32_t flags)
{
OSXView *view = [[OSXView alloc] initWithFrame:NSZeroRect];
heap_auditor_add("OSXView");
_oscontrol_init(view);
view->flags = flags;
view->ctx = NULL;
view->tracking_area = NULL;
view->OnFocus = NULL;
view->OnResignFocus = NULL;
view->OnAcceptFocus = NULL;
view->OnOverlay = NULL;
view->mouse_inside = NO;
view->focused = NO;
view->allow_tab = NO;
view->osdraw.view = view;
_oslistener_init(&view->listeners);
#if defined(MAC_OS_VERSION_14_0) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_14
[view setClipsToBounds:YES];
#endif
if (flags & ekVIEW_HSCROLL || flags & ekVIEW_VSCROLL)
{
view->scroll = _osscrolls_create(cast(view, OSControl), (bool_t)(flags & ekVIEW_HSCROLL) != 0, (bool_t)(flags & ekVIEW_VSCROLL) != 0);
}
else
{
view->scroll = nil;
}
if (flags & ekVIEW_BORDER)
[view setFocusRingType:NSFocusRingTypeExterior];
else
[view setFocusRingType:NSFocusRingTypeNone];
return cast(view, OSView);
}
static OSXView *i_get_view(const OSView *view)
{
cassert_no_null(view);
if ([cast(view, NSView) isKindOfClass:[OSXView class]])
{
return cast(view, OSXView);
}
return nil;
}
void osview_destroy(OSView **view)
{
OSXView *lview = nil;
cassert_no_null(view);
lview = i_get_view(*view);
cassert_no_null(lview);
_oslistener_release(&lview->listeners);
listener_destroy(&lview->OnFocus);
listener_destroy(&lview->OnResignFocus);
listener_destroy(&lview->OnAcceptFocus);
listener_destroy(&lview->OnOverlay);
if (lview->tracking_area != nil)
{
[lview removeTrackingArea:lview->tracking_area];
[lview->tracking_area release];
}
if (lview->ctx != NULL)
dctx_destroy(&lview->ctx);
if (lview->scroll != NULL)
_osscrolls_destroy(&lview->scroll);
[lview release];
*view = NULL;
heap_auditor_delete("OSXView");
}
void osview_OnDraw(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnDraw, listener);
}
void osview_OnOverlay(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->OnOverlay, listener);
}
static bool_t i_needs_tracking_area(OSXView *view)
{
cassert_no_null(view);
if (view->listeners.OnClick != NULL)
return TRUE;
if (view->listeners.OnMoved != NULL)
return TRUE;
if (view->listeners.OnEnter != NULL)
return TRUE;
if (view->listeners.OnExit != NULL)
return TRUE;
return FALSE;
}
static void i_update_tracking_area(OSXView *view)
{
NSSize track_size = [view frame].size;
if (track_size.width > 0 && track_size.height > 0)
{
bool_t with_area = i_needs_tracking_area(view);
NSUInteger options = NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect;
if (view->scroll != NULL)
{
track_size.width -= (CGFloat)_osscrolls_bar_width(view->scroll, TRUE);
track_size.height -= (CGFloat)_osscrolls_bar_height(view->scroll, TRUE);
}
if (view->tracking_area != nil && with_area == TRUE)
{
NSSize current_size = [view->tracking_area rect].size;
if (NSEqualSizes(track_size, current_size) == NO)
{
[view removeTrackingArea:view->tracking_area];
[view->tracking_area release];
view->tracking_area = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0, track_size.width, track_size.height) options:(NSTrackingAreaOptions)options owner:view userInfo:nil];
[view addTrackingArea:view->tracking_area];
cassert([[view trackingAreas] count] == 1);
}
}
else if (view->tracking_area != nil)
{
cassert(with_area == FALSE);
[view removeTrackingArea:view->tracking_area];
[view->tracking_area release];
view->tracking_area = nil;
}
else if (with_area == TRUE)
{
cassert(view->tracking_area == nil);
view->tracking_area = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0, track_size.width, track_size.height) options:(NSTrackingAreaOptions)options owner:view userInfo:nil];
[view addTrackingArea:view->tracking_area];
cassert([[view trackingAreas] count] == 1);
}
}
}
void osview_OnEnter(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnEnter, listener);
i_update_tracking_area(lview);
}
void osview_OnExit(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnExit, listener);
i_update_tracking_area(lview);
}
void osview_OnMoved(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnMoved, listener);
i_update_tracking_area(lview);
}
void osview_OnDown(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnDown, listener);
}
void osview_OnUp(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnUp, listener);
}
void osview_OnClick(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnClick, listener);
i_update_tracking_area(lview);
}
void osview_OnDrag(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnDrag, listener);
}
void osview_OnWheel(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnWheel, listener);
}
void osview_OnKeyDown(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnKeyDown, listener);
}
void osview_OnKeyUp(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->listeners.OnKeyUp, listener);
}
void osview_OnFocus(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->OnFocus, listener);
}
void osview_OnResignFocus(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->OnResignFocus, listener);
}
void osview_OnAcceptFocus(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
listener_update(&lview->OnAcceptFocus, listener);
}
void osview_OnScroll(OSView *view, Listener *listener)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
_osscrolls_OnScroll(lview->scroll, listener);
}
void osview_allow_key(OSView *view, const vkey_t key, const uint32_t value)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
cassert_unref(key == ekKEY_TAB, key);
cassert(value == 0 || value == 1);
lview->allow_tab = (BOOL)value;
}
void osview_scroll(OSView *view, const real32_t x, const real32_t y)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
_osscrolls_set(lview->scroll, x >= 0 ? (uint32_t)x : UINT32_MAX, y >= 0 ? (uint32_t)y : UINT32_MAX, FALSE);
}
void osview_scroll_get(const OSView *view, real32_t *x, real32_t *y)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
if (x != NULL)
*x = (real32_t)_osscrolls_x_pos(lview->scroll);
if (y != NULL)
*y = (real32_t)_osscrolls_y_pos(lview->scroll);
}
void osview_scroller_size(const OSView *view, real32_t *width, real32_t *height)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
if (width != NULL)
*width = (real32_t)_osscrolls_bar_width(lview->scroll, TRUE);
if (height != NULL)
*height = (real32_t)_osscrolls_bar_height(lview->scroll, TRUE);
}
void osview_scroller_visible(OSView *view, const bool_t horizontal, const bool_t vertical)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
_osscrolls_visible(lview->scroll, horizontal, vertical);
}
void osview_content_size(OSView *view, const real32_t width, const real32_t height, const real32_t line_width, const real32_t line_height)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
_osscrolls_content_size(lview->scroll, (uint32_t)width, (uint32_t)height, (uint32_t)line_width, (uint32_t)line_height);
i_update_tracking_area(lview);
}
real32_t osview_scale_factor(const OSView *view)
{
#if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
OSXView *lview = i_get_view(view);
NSWindow *window = nil;
cassert_no_null(lview);
window = [lview window];
if (window != nil)
return (real32_t)[window backingScaleFactor];
return (real32_t)[[NSScreen mainScreen] backingScaleFactor];
#else
unref(view);
return 1;
#endif
}
void osview_set_need_display(OSView *view)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
[lview setNeedsDisplay:YES];
}
void *osview_get_native_view(const OSView *view)
{
return cast(view, void);
}
void osview_attach(OSView *view, OSPanel *panel)
{
_ospanel_attach_control(panel, cast(view, NSView));
}
void osview_detach(OSView *view, OSPanel *panel)
{
_ospanel_detach_control(panel, cast(view, NSView));
}
void osview_visible(OSView *view, const bool_t visible)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
_oscontrol_set_visible(lview, visible);
}
void osview_enabled(OSView *view, const bool_t enabled)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
_oslistener_set_enabled(&lview->listeners, enabled);
}
void osview_size(const OSView *view, real32_t *width, real32_t *height)
{
_oscontrol_get_size(cast(view, NSView), width, height);
}
void osview_origin(const OSView *view, real32_t *x, real32_t *y)
{
_oscontrol_get_origin(cast(view, NSView), x, y);
}
void osview_frame(OSView *view, const real32_t x, const real32_t y, const real32_t width, const real32_t height)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
_oscontrol_set_frame(cast(lview, NSView), x, y, width, height);
if (lview->scroll)
_osscrolls_control_size(lview->scroll, (uint32_t)width, (uint32_t)height);
i_update_tracking_area(lview);
[lview setNeedsDisplay:YES];
}
bool_t _osview_resign_focus(const OSView *view)
{
OSXView *lview = i_get_view(view);
bool_t resign = TRUE;
cassert_no_null(lview);
if (lview->OnResignFocus != NULL)
listener_event(lview->OnResignFocus, ekGUI_EVENT_FOCUS_RESIGN, view, NULL, &resign, OSView, void, bool_t);
return resign;
}
bool_t _osview_accept_focus(const OSView *view)
{
OSXView *lview = i_get_view(view);
bool_t accept = TRUE;
cassert_no_null(view);
if (lview->OnAcceptFocus != NULL)
listener_event(lview->OnAcceptFocus, ekGUI_EVENT_FOCUS_ACCEPT, view, NULL, &accept, OSView, void, bool_t);
return accept;
}
void _osview_focus(OSView *view, const bool_t focus)
{
OSXView *lview = i_get_view(cast(view, OSView));
cassert_no_null(lview);
if (lview->listeners.is_enabled == YES && lview->OnFocus != NULL)
{
bool_t params = focus;
listener_event(lview->OnFocus, ekGUI_EVENT_FOCUS, cast(lview, OSView), ¶ms, NULL, OSView, bool_t, void);
}
lview->focused = (BOOL)focus;
#if defined(MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
#else
if (lview->flags & ekVIEW_BORDER)
[lview setKeyboardFocusRingNeedsDisplayInRect:[lview bounds]];
#endif
}
bool_t _osview_capture_tab(const OSView *view)
{
OSXView *lview = i_get_view(view);
cassert_no_null(lview);
if (lview->listeners.OnKeyDown == NULL)
return FALSE;
return (bool_t)lview->allow_tab;
}
BOOL _osview_is(NSView *view)
{
return (BOOL)(i_get_view(cast(view, OSView)) != nil);
}
NSView *_osview_focus_widget(NSView *view)
{
OSXView *lview = i_get_view(cast(view, OSView));
cassert_no_null(lview);
return cast(lview, NSView);
}
void _osview_scroll_event(NSView *view, const gui_orient_t orient, const gui_scroll_t event)
{
OSXView *lview = cast(view, OSXView);
cassert_no_null(lview);
cassert_no_null(lview->scroll);
cassert([view isKindOfClass:[OSXView class]]);
if (_osscrolls_event(lview->scroll, orient, event, FALSE) == TRUE)
[lview setNeedsDisplay:YES];
}