Skip to content

Instantly share code, notes, and snippets.

@cmsj
Created February 23, 2015 17:11
Show Gist options
  • Save cmsj/43c14adbbe8381033de7 to your computer and use it in GitHub Desktop.
Save cmsj/43c14adbbe8381033de7 to your computer and use it in GitHub Desktop.
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
#import <lauxlib.h>
/// === hs.drawing ===
///
/// Primitives for drawing on the screen in various ways
// Useful definitions
#define USERDATA_TAG "hs.drawing"
#define get_item_arg(L, idx) ((drawing_t *)luaL_checkudata(L, idx, USERDATA_TAG))
// Declare our Lua userdata object and a storage container for them
typedef struct _drawing_t {
void *window;
} drawing_t;
NSMutableArray *drawingWindows;
// Objective-C class interface definitions
@interface HSDrawingWindow : NSWindow <NSWindowDelegate>
@end
@interface HSDrawingView : NSView
@property BOOL HSFill;
@property BOOL HSStroke;
@property CGFloat HSLineWidth;
@property (nonatomic, strong) NSColor *HSFillColor;
@property (nonatomic, strong) NSColor *HSStrokeColor;
@end
@interface HSDrawingViewCircle : HSDrawingView
@end
// Objective-C class interface implementations
@implementation HSDrawingWindow
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger __unused)windowStyle backing:(NSBackingStoreType __unused)bufferingType defer:(BOOL __unused)deferCreation {
NSLog(@"HSDrawingWindow::initWithContentRect");
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES ];
if (self) {
[self setDelegate:self];
self.releasedWhenClosed = NO;
self.backgroundColor = [NSColor clearColor];
self.opaque = NO;
self.hasShadow = NO;
self.ignoresMouseEvents = YES;
self.restorable = NO;
self.animationBehavior = NSWindowAnimationBehaviorNone;
self.level = NSFloatingWindowLevel;
}
return self;
}
- (void)setLevelTop {
self.level = NSFloatingWindowLevel;
}
- (void)setLevelBottom {
self.level = CGWindowLevelForKey(kCGDesktopIconWindowLevelKey) - 1;
}
// NSWindowDelegate method, declining to close the window
- (BOOL)windowShouldClose:(id __unused)sender {
NSLog(@"HSDrawingWindow::windowShouldClose");
return NO;
}
// TODO: Do we need to implement this to GC ourselves?
//- (void)windowWillClose:(NSNotification *)notification {
//}
@end
@implementation HSDrawingView
- (id)initWithFrame:(NSRect)frameRect {
NSLog(@"HSDrawingView::initWithFrame");
self = [super initWithFrame:frameRect];
if (self) {
// Set up our defaults
self.HSFill = YES;
self.HSStroke = YES;
self.HSLineWidth = [NSBezierPath defaultLineWidth];
self.HSFillColor = [NSColor redColor];
self.HSStrokeColor = [NSColor blackColor];
}
return self;
}
@end
@implementation HSDrawingViewCircle
- (void)drawRect:(NSRect)rect {
NSLog(@"HSDrawingViewCircle::drawRect");
// Get the graphics context that we are currently executing under
NSGraphicsContext* gc = [NSGraphicsContext currentContext];
// Save the current graphics context settings
[gc saveGraphicsState];
// Set the color in the current graphics context for future draw operations
[[self HSStrokeColor] setStroke];
[[self HSFillColor] setFill];
// Create our circle path
//NSRect rect = NSMakeRect(10, 10, 10, 10);
NSBezierPath* circlePath = [NSBezierPath bezierPath];
[circlePath appendBezierPathWithOvalInRect: rect];
// Outline and fill the path
circlePath.lineWidth = self.HSLineWidth;
if (self.HSStroke) {
[circlePath stroke];
}
if (self.HSFill) {
[circlePath fill];
}
// Restore the context to what it was before we messed with it
[gc restoreGraphicsState];
}
@end
// Lua API implementation
/// hs.drawing.circle(sizeRect) -> drawingObject or nil
/// Constructor
/// Creates a new circle object
///
/// Parameters:
/// * sizeRect - A rect-table containing the location/size of the circle
///
/// Returns:
/// * An `hs.drawing` object, or nil if an error occurs
static int drawing_newCircle(lua_State *L) {
NSRect windowRect;
switch (lua_type(L, 1)) {
case LUA_TTABLE:
lua_getfield(L, 1, "x");
windowRect.origin.x = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "y");
windowRect.origin.y = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "w");
windowRect.size.width = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "h");
windowRect.size.height = lua_tointeger(L, -1);
lua_pop(L, 1);
break;
default:
NSLog(@"ERROR: Unexpected type passed to hs.drawing.circle(): %d", lua_type(L, 1));
lua_pushnil(L);
return 1;
break;
}
HSDrawingWindow *circleWindow = [[HSDrawingWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
if (circleWindow) {
drawing_t *drawingCircle = lua_newuserdata(L, sizeof(drawing_t));
memset(drawingCircle, 0, sizeof(drawing_t));
drawingCircle->window = (__bridge_retained void*)circleWindow;
luaL_getmetatable(L, USERDATA_TAG);
lua_setmetatable(L, -2);
NSRect contentRect;
contentRect.origin.x = 0;
contentRect.origin.y = 0;
contentRect.size = circleWindow.contentMaxSize;
HSDrawingViewCircle *circleView = [[HSDrawingViewCircle alloc] initWithFrame:contentRect];
circleWindow.contentView = circleView;
if (!drawingWindows) {
drawingWindows = [[NSMutableArray alloc] init];
}
[drawingWindows addObject:circleWindow];
} else {
lua_pushnil(L);
}
return 1;
}
NSColor *getColorFromStack(lua_State *L, int idx) {
CGFloat red, green, blue, alpha;
switch (lua_type(L, idx)) {
case LUA_TTABLE:
lua_getfield(L, idx, "red");
red = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "green");
green = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "blue");
blue = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, idx, "alpha");
alpha = lua_tonumber(L, -1);
lua_pop(L, 1);
break;
default:
NSLog(@"ERROR: Unexpected type passed to an hs.drawing color method: %d", lua_type(L, 1));
return 0;
break;
}
return [NSColor colorWithSRGBRed:red green:green blue:blue alpha:alpha];
}
/// hs.drawing:setFillColor(color)
/// Method
/// Sets the fill color of a drawing object
///
/// Parameters:
/// * color - A table containing color component values between 0.0 and 1.0 for each of the keys:
/// * red
/// * green
/// * blue
/// * alpha
///
/// Returns:
/// * None
static int drawing_setFillColor(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
NSColor *fillColor = getColorFromStack(L, 2);
HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window;
HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView;
drawingView.HSFillColor = fillColor;
drawingView.needsDisplay = YES;
return 0;
}
/// hs.drawing:setStrokeColor(color)
/// Method
/// Sets the stroke color of a drawing object
///
/// Parameters:
/// * color - A table containing color component values between 0.0 and 1.0 for each of the keys:
/// * red
/// * green
/// * blue
/// * alpha
///
/// Returns:
/// * None
static int drawing_setStrokeColor(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
NSColor *strokeColor = getColorFromStack(L, 2);
HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window;
HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView;
drawingView.HSStrokeColor = strokeColor;
drawingView.needsDisplay = YES;
return 0;
}
/// hs.drawing:setFill(doFill)
/// Method
/// Sets whether or not to fill a drawing object
///
/// Parameters:
/// * doFill - A boolean, true to fill the drawing object, false to not fill
///
/// Returns:
/// * None
static int drawing_setFill(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window;
HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView;
drawingView.HSFill = lua_toboolean(L, 2);
drawingView.needsDisplay = YES;
return 0;
}
/// hs.drawing:setStroke(doStroke)
/// Method
/// Sets whether or not to stroke a drawing object
///
/// Parameters:
/// * doStroke - A boolean, true to stroke the drawing object, false to not stroke
///
/// Returns:
/// * None
static int drawing_setStroke(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window;
HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView;
drawingView.HSStroke = lua_toboolean(L, 2);
drawingView.needsDisplay = YES;
return 0;
}
/// hs.drawing:setStrokeWidth(width)
/// Method
/// Sets the stroke width of a drawing object
///
/// Parameters:
/// * width - A number containing the width in points to stroke a drawing object
///
/// Returns:
/// * None
static int drawing_setStrokeWidth(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
HSDrawingWindow *drawingWindow = (__bridge HSDrawingWindow *)drawingObject->window;
HSDrawingView *drawingView = (HSDrawingView *)drawingWindow.contentView;
drawingView.HSLineWidth = lua_tonumber(L, 2);
drawingView.needsDisplay = YES;
return 0;
}
/// hs.drawing:show()
/// Method
/// Displays the drawing object
///
/// Parameters:
/// * None
///
/// Returns:
/// * None
static int drawing_show(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
[(__bridge HSDrawingWindow *)drawingObject->window makeKeyAndOrderFront:nil];
return 0;
}
/// hs.drawing:hide()
/// Method
/// Hides the drawing object
///
/// Parameters:
/// * None
///
/// Returns:
/// * None
static int drawing_hide(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
[(__bridge HSDrawingWindow *)drawingObject->window orderOut:nil];
return 0;
}
/// hs.drawing:delete()
/// Method
/// Destroys the drawing object
///
/// Parameters:
/// * None
///
/// Returns:
/// * None
static int drawing_delete(lua_State *L) {
drawing_t *drawingObject = get_item_arg(L, 1);
HSDrawingWindow *drawingWindow = (__bridge_transfer HSDrawingWindow *)drawingObject->window;
[drawingWindows removeObject:drawingWindow];
[drawingWindow close];
drawingWindow = nil;
drawingObject->window = nil;
drawingObject = nil;
return 0;
}
// Lua metadata
static const luaL_Reg drawinglib[] = {
{"circle", drawing_newCircle},
{}
};
static const luaL_Reg drawing_metalib[] = {
{"setStroke", drawing_setStroke},
{"setStrokeWidth", drawing_setStrokeWidth},
{"setStrokeColor", drawing_setStrokeColor},
{"setFill", drawing_setFill},
{"setFillColor", drawing_setFillColor},
{"show", drawing_show},
{"hide", drawing_hide},
{"delete", drawing_delete},
{}
};
static const luaL_Reg drawing_gclib[] = {
{"__gc", drawing_delete},
{}
};
int luaopen_hs_drawing_internal(lua_State *L) {
// Metatable for creted objects
luaL_newlib(L, drawing_metalib);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, USERDATA_TAG);
// Table for luaopen
luaL_newlib(L, drawinglib);
luaL_newlib(L, drawing_gclib);
lua_setmetatable(L, -2);
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment