MessagesForMacintosh/nuklear_quickdraw.h

1385 lines
49 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Nuklear - 1.32.0 - public domain
* no warranty implied; use at your own risk.
* based on allegro5 version authored from 2015-2016 by Micha Mettke
* quickdraw version camhenlin 2021
*
* v1 intent:
* - only default system font support
* - no graphics/images support - quickdraw has very limited support for this
*/
/*
* ==============================================================
*
* API
*
* ===============================================================
*/
#ifndef NK_QUICKDRAW_H_
#define NK_QUICKDRAW_H_
#include <MacTypes.h>
#include <Types.h>
#include <Quickdraw.h>
#include <Scrap.h>
#include <Serial.h>
#include "SerialHelper.h"
typedef struct NkQuickDrawFont NkQuickDrawFont;
NK_API struct nk_context* nk_quickdraw_init(unsigned int width, unsigned int height);
NK_API int nk_quickdraw_handle_event(EventRecord *event, struct nk_context *nuklear_context);
NK_API void nk_quickdraw_shutdown(void);
NK_API void nk_quickdraw_render(WindowPtr window, struct nk_context *ctx);
NK_API struct nk_image* nk_quickdraw_create_image(const char* file_name);
NK_API void nk_quickdraw_del_image(struct nk_image* image);
NK_API NkQuickDrawFont* nk_quickdraw_font_create_from_file();
#endif
/*
* ==============================================================
*
* IMPLEMENTATION
*
* ===============================================================
*/
#ifdef NK_QUICKDRAW_IMPLEMENTATION
#ifndef NK_QUICKDRAW_TEXT_MAX
#define NK_QUICKDRAW_TEXT_MAX 256
#endif
#endif
struct NkQuickDrawFont {
struct nk_user_font nk;
char *font;
};
int lastEventWasKey = 0;
void *last;
void *buf;
// constant keyboard mappings for convenenience
// See Inside Macintosh: Text pg A-7, A-8
int homeKey = (int)0x01;
int enterKey = (int)0x03;
int endKey = (int)0x04;
int helpKey = (int)0x05;
int backspaceKey = (int)0x08;
int deleteKey = (int)0x7F;
int tabKey = (int)0x09;
int pageUpKey = (int)0x0B;
int pageDownKey = (int)0x0C;
int returnKey = (int)0x0D;
int rightArrowKey = (int)0x1D;
int leftArrowKey = (int)0x1C;
int downArrowKey = (int)0x1F;
int upArrowKey = (int)0x1E;
int eitherShiftKey = (int)0x0F;
int escapeKey = (int)0x1B;
// #define NK_QUICKDRAW_GRAPHICS_DEBUGGING
// #def#ifdef NK_QUICKDRAW_EVENTS_DEBUGG
typedef struct {
Ptr Address;
long RowBytes;
GrafPtr bits;
Rect bounds;
BitMap BWBits;
GrafPort BWPort;
Handle OrigBits;
} ShockBitmap;
void NewShockBitmap(ShockBitmap *theMap, short width, short height) {
theMap->bits = 0L;
SetRect(&theMap->bounds, 0, 0, width, height);
theMap->BWBits.bounds = theMap->bounds;
theMap->BWBits.rowBytes = ((width+15) >> 4)<<1; // round to even
theMap->BWBits.baseAddr = NewPtr(((long) height * (long) theMap->BWBits.rowBytes));
theMap->BWBits.baseAddr = StripAddress(theMap->BWBits.baseAddr);
OpenPort(&theMap->BWPort);
SetPort(&theMap->BWPort);
SetPortBits(&theMap->BWBits);
SetRectRgn(theMap->BWPort.visRgn, theMap->bounds.left, theMap->bounds.top, theMap->bounds.right, theMap->bounds.bottom);
SetRectRgn(theMap->BWPort.clipRgn, theMap->bounds.left, theMap->bounds.top, theMap->bounds.right, theMap->bounds.bottom);
EraseRect(&theMap->bounds);
theMap->Address = theMap->BWBits.baseAddr;
theMap->RowBytes = (long) theMap->BWBits.rowBytes;
theMap->bits = (GrafPtr) &theMap->BWPort;
}
ShockBitmap gMainOffScreen;
// bezier code is from http://preserve.mactech.com/articles/mactech/Vol.05/05.01/BezierCurve/index.html
// as it is not built in to quickdraw like other "modern" graphics environments
/*
The greater the number of curve segments, the smoother the curve,
and the longer it takes to generate and draw. The number below was pulled
out of a hat, and seems to work o.k.
*/
#define SEGMENTS 16
static Fixed weight1[SEGMENTS + 1];
static Fixed weight2[SEGMENTS + 1];
#define w1(s) weight1[s]
#define w2(s) weight2[s]
#define w3(s) weight2[SEGMENTS - s]
#define w4(s) weight1[SEGMENTS - s]
/*
* SetupBezier -- one-time setup code.
* Compute the weights for the Bezier function.
* For the those concerned with space, the tables can be precomputed.
Setup is done here for purposes of illustration.
*/
void setupBezier() {
Fixed t, zero, one;
int s;
zero = FixRatio(0, 1);
one = FixRatio(1, 1);
weight1[0] = one;
weight2[0] = zero;
for (s = 1; s < SEGMENTS; ++s) {
t = FixRatio(s, SEGMENTS);
weight1[s] = FixMul(one - t, FixMul(one - t, one - t));
weight2[s] = 3 * FixMul(t, FixMul(t - one, t - one));
}
weight1[SEGMENTS] = zero;
weight2[SEGMENTS] = zero;
}
/*
* computeSegments -- compute segments for the Bezier curve
* Compute the segments along the curve.
* The curve touches the endpoints, so dont bother to compute them.
*/
static void computeSegments(p1, p2, p3, p4, segment) Point p1, p2, p3, p4; Point segment[]; {
int s;
segment[0] = p1;
for (s = 1; s < SEGMENTS; ++s) {
segment[s].v = FixRound(w1(s) * p1.v + w2(s) * p2.v + w3(s) * p3.v + w4(s) * p4.v);
segment[s].h = FixRound(w1(s) * p1.h + w2(s) * p2.h + w3(s) * p3.h + w4(s) * p4.h);
}
segment[SEGMENTS] = p4;
}
/*
* BezierCurve -- Draw a Bezier Curve
* Draw a curve with the given endpoints (p1, p4), and the given
* control points (p2, p3).
* Note that we make no assumptions about pen or pen mode.
*/
void BezierCurve(p1, p2, p3, p4) Point p1, p2, p3, p4; {
int s;
Point segment[SEGMENTS + 1];
computeSegments(p1, p2, p3, p4, segment);
MoveTo(segment[0].h, segment[0].v);
for (s = 1 ; s <= SEGMENTS ; ++s) {
if (segment[s].h != segment[s - 1].h || segment[s].v != segment[s - 1].v) {
LineTo(segment[s].h, segment[s].v);
}
}
}
// ex usage:
// Point control[4] = {{144,72}, {72,144}, {216,144}, {144,216}};
// BezierCurve(c[0], c[1], c[2], c[3]);
static struct nk_quickdraw {
unsigned int width;
unsigned int height;
struct nk_context nuklear_context;
struct nk_buffer cmds;
} quickdraw;
// TODO: maybe V2 - skipping images for first pass
NK_API struct nk_image* nk_quickdraw_create_image(const char* file_name) {
// TODO: just read the file to a char, we can draw it using quickdraw
// b&w bitmaps are pretty easy to draw...
// for us to do this, we need to figure out the format, then figure out if we can make it in to a quickdraw rect
// and set the buffer to the image handle pointer in the image struct
// i assume this gets consumed elsewhere in the code, thinking NK_COMMAND_IMAGE
char* bitmap = ""; //al_load_bitmap(file_name); // TODO: https://www.allegro.cc/manual/5/al_load_bitmap, loads file to in-memory buffer understood by allegro
if (bitmap == NULL) {
fprintf(stdout, "Unable to load image file: %s\n", file_name);
return NULL;
}
struct nk_image *image = (struct nk_image*)calloc(1, sizeof(struct nk_image));
image->handle.ptr = bitmap;
image->w = 0; // al_get_bitmap_width(bitmap); // TODO: this can be retrieved from a bmp file
image->h = 0; // al_get_bitmap_height(bitmap); // TODO: this can be retrieved from a bmp file
return image;
}
// TODO: maybe V2 - skipping images for first pass
NK_API void nk_quickdraw_del_image(struct nk_image* image) {
if (!image) {
return;
}
// al_destroy_bitmap(image->handle.ptr); // TODO: https://www.allegro.cc/manual/5/al_destroy_bitmap
//this is just de-allocating the memory from a loaded bitmap
free(image);
}
int widthFor12ptFont[128] = {
0,
10,
10,
10,
10,
10,
10,
10,
10,
8,
10,
10,
10,
0,
10,
10,
10,
11,
11,
9,
11,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
10,
4,
6,
7,
10,
7,
11,
10,
3,
5,
5,
7,
7,
4,
7,
4,
7,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
4,
4,
6,
8,
6,
8,
11,
8,
8,
8,
8,
7,
7,
8,
8,
6,
7,
9,
7,
12,
9,
8,
8,
8,
8,
7,
6,
8,
8,
12,
8,
8,
8,
5,
7,
5,
8,
8,
6,
8,
8,
7,
8,
8,
6,
8,
8,
4,
6,
8,
4,
12,
8,
8,
8,
8,
6,
7,
6,
8,
8,
12,
8,
8,
8,
5,
5,
5,
8,
8
};
// note: if this produces a greater value than the actual length of the text,
// the cursor will walk off to the right
// too small, it will precede the end of the text
// TODO: fully convert
// TODO: assuming system font for v1, support other fonts in v2
// doing this in a "fast" way by using a precomputed table for a 12pt font
static int nk_quickdraw_font_get_text_width(nk_handle handle, int height, const char *text, int len) {
// writeSerialPortDebug(boutRefNum, "nk_quickdraw_font_get_text_width");
if (!text || len == 0) {
return 0;
}
int width = 0;
for (int i = 0; i < len; i++) {
width += widthFor12ptFont[(int)text[i]];
}
return width;
}
static int _get_text_width(const char *text, int len) {
// writeSerialPortDebug(boutRefNum, "nk_quickdraw_font_get_text_width");
if (!text || len == 0) {
return 0;
}
int width = 0;
for (int i = 0; i < len; i++) {
width += widthFor12ptFont[(int)text[i]];
}
return width;
}
static int nk_color_to_quickdraw_bw_color(struct nk_color color) {
// TODO: since we are operating under a b&w display - we need to convert these colors to black and white
// look up a simple algorithm for taking RGBA values and making the call on black or white and try it out here
// as a future upgrade, we could support color quickdraw
// using an algorithm from https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
// if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff
// return al_map_rgba((unsigned char)color.r, (unsigned char)color.g, (unsigned char)color.b, (unsigned char)color.a);
float magicColorNumber = color.r * 0.299 + color.g * 0.587 + color.b * 0.114;
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
char stringMagicColorNumber[255];
sprintf(stringMagicColorNumber, "stringMagicColorNumber: %f", magicColorNumber);
writeSerialPortDebug(boutRefNum, stringMagicColorNumber);
#endif
if (magicColorNumber > 37) {
return blackColor;
}
return blackColor;//whiteColor;
}
// i split this in to a 2nd routine because we can use the various shades of gray when filling rectangles and whatnot
static Pattern nk_color_to_quickdraw_color(struct nk_color color) {
// as a future upgrade, we could support color quickdraw
// using an algorithm from https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
// if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff
uint8_t red;
uint8_t blue;
uint8_t green;
float magicColorNumber = color.r * 0.299 + color.g * 0.587 + color.b * 0.114;
if (magicColorNumber > 150) {
return qd.black;
} else if (magicColorNumber > 100) {
return qd.dkGray;
} else if (magicColorNumber > 75) {
return qd.gray;
} else if (magicColorNumber > 49) {
return qd.ltGray;
}
return qd.white;
}
/* Flags are identical to al_load_font() flags argument */
NK_API NkQuickDrawFont* nk_quickdraw_font_create_from_file() {
NkQuickDrawFont *font = (NkQuickDrawFont*)calloc(1, sizeof(NkQuickDrawFont));
font->font = calloc(1, 1024);
if (font->font == NULL) {
fprintf(stdout, "Unable to load font file\n");
return NULL;
}
font->nk.userdata = nk_handle_ptr(font);
font->nk.height = (int)12;
font->nk.width = nk_quickdraw_font_get_text_width;
return font;
}
NK_API void nk_quickdraw_render(WindowPtr window, struct nk_context *ctx) {
long start;
long end;
long total;
start = TickCount();
long renderTime1;
long renderTime2;
long renderTime3;
void *cmds = nk_buffer_memory(&ctx->memory);
// do not render if the buffer did not change from the previous rendering run
if (!memcmp(cmds, last, ctx->memory.allocated)) {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_NOP");
#endif
return;
}
memcpy(last, cmds, ctx->memory.allocated);
const struct nk_command *cmd = 0;
if (!lastEventWasKey) {
OpenPort(&gMainOffScreen.BWPort);
SetPort(&gMainOffScreen.BWPort);
SetPortBits(&gMainOffScreen.BWBits);
// EraseRect(&gMainOffScreen.bounds);
} else {
SetPort(window);
}
end = TickCount();
total = end - start;
renderTime1 = total;// / 60.0;
start = TickCount();
nk_foreach(cmd, ctx) {
int color;
ForeColor(blackColor);
if (lastEventWasKey && cmd->type == NK_COMMAND_TEXT) {
//writeSerialPortDebug(boutRefNum, "FAST INPUT");
const struct nk_command_text *t = (const struct nk_command_text*)cmd;
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_TEXT");
char log[255];
sprintf(log, "%f: %c, %d", (int)t->height, &t->string, (int)t->length);
writeSerialPortDebug(boutRefNum, log);
#endif
MoveTo((int)t->x + _get_text_width(&t->string, (int)t->length - 1), (int)t->y + (int)t->height);
// DrawText((const char*)t->string, 0, (int)t->length);
DrawChar(t->string[t->length - 1]);
} else if (!lastEventWasKey) {
// writeSerialPortDebug(boutRefNum, "SLOW INPUT");
switch (cmd->type) {
case NK_COMMAND_NOP:
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_NOP");
#endif
break;
case NK_COMMAND_SCISSOR: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_SCISSOR");
#endif
const struct nk_command_scissor *s =(const struct nk_command_scissor*)cmd;
// // al_set_clipping_rectangle((int)s->x, (int)s->y, (int)s->w, (int)s->h); // TODO: https://www.allegro.cc/manual/5/al_set_clipping_rectangle
// // this essentially just sets the region of the screen that we are going to write to
// // initially, i thought that this would be SetClip, but now believe this should be ClipRect, see:
// // Inside Macintosh: Imaging with Quickdraw pages 2-48 and 2-49 for more info
// // additionally, see page 2-61 for a data structure example for the rectangle OR
// // http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-60.html
// // for usage example
// Rect quickDrawRectangle;
// quickDrawRectangle.top = (int)s->y;
// quickDrawRectangle.left = (int)s->x;
// quickDrawRectangle.bottom = (int)s->y + (int)s->h;
// quickDrawRectangle.right = (int)s->x + (int)s->w;
// ClipRect(&quickDrawRectangle);
}
break;
case NK_COMMAND_LINE: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_LINE");
#endif
const struct nk_command_line *l = (const struct nk_command_line *)cmd;
color = nk_color_to_quickdraw_bw_color(l->color);
// great reference: http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-60.html
// al_draw_line((float)l->begin.x, (float)l->begin.y, (float)l->end.x, (float)l->end.y, color, (float)l->line_thickness); // TODO: look up and convert al_draw_line
ForeColor(color);
PenSize((int)l->line_thickness, (int)l->line_thickness);
MoveTo((int)l->begin.x, (int)l->begin.y);
LineTo((int)l->end.x, (int)l->end.y);
}
break;
case NK_COMMAND_RECT: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_RECT");
#endif
// http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-102.html#MARKER-9-372
// http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-103.html#HEADING103-0
const struct nk_command_rect *r = (const struct nk_command_rect *)cmd;
color = nk_color_to_quickdraw_bw_color(r->color);
ForeColor(color);
PenSize((int)r->line_thickness, (int)r->line_thickness);
Rect quickDrawRectangle;
quickDrawRectangle.top = (int)r->y;
quickDrawRectangle.left = (int)r->x;
quickDrawRectangle.bottom = (int)r->y + (int)r->h;
quickDrawRectangle.right = (int)r->x + (int)r->w;
FrameRoundRect(&quickDrawRectangle, (float)r->rounding, (float)r->rounding);
}
break;
case NK_COMMAND_RECT_FILLED: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_RECT_FILLED");
#endif
const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd;
color = nk_color_to_quickdraw_bw_color(r->color);
ForeColor(color);
Pattern colorPattern = nk_color_to_quickdraw_color(r->color);
// BackPat(&colorPattern); // inside macintosh: imaging with quickdraw 3-48
PenSize(1.0, 1.0); // no member line thickness on this struct so assume we want a thin line
// might actually need to build this with SetRect, search inside macintosh: imaging with quickdraw
Rect quickDrawRectangle;
quickDrawRectangle.top = (int)r->y;
quickDrawRectangle.left = (int)r->x;
quickDrawRectangle.bottom = (int)r->y + (int)r->h;
quickDrawRectangle.right = (int)r->x + (int)r->w;
FillRoundRect(&quickDrawRectangle, (float)r->rounding, (float)r->rounding, &colorPattern);
FrameRoundRect(&quickDrawRectangle, (float)r->rounding, (float)r->rounding); // http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-105.html#HEADING105-0
}
break;
case NK_COMMAND_CIRCLE: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_CIRCLE");
#endif
const struct nk_command_circle *c = (const struct nk_command_circle *)cmd;
color = nk_color_to_quickdraw_bw_color(c->color);
ForeColor(color);
Rect quickDrawRectangle;
quickDrawRectangle.top = (int)c->y;
quickDrawRectangle.left = (int)c->x;
quickDrawRectangle.bottom = (int)c->y + (int)c->h;
quickDrawRectangle.right = (int)c->x + (int)c->w;
FrameOval(&quickDrawRectangle); // An oval is a circular or elliptical shape defined by the bounding rectangle that encloses it. inside macintosh: imaging with quickdraw 3-25
}
break;
case NK_COMMAND_CIRCLE_FILLED: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_CIRCLE_FILLED");
#endif
const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd;
color = nk_color_to_quickdraw_bw_color(c->color);
ForeColor(color);
Pattern colorPattern = nk_color_to_quickdraw_color(c->color);
// BackPat(&colorPattern); // inside macintosh: imaging with quickdraw 3-48
PenSize(1.0, 1.0);
Rect quickDrawRectangle;
quickDrawRectangle.top = (int)c->y;
quickDrawRectangle.left = (int)c->x;
quickDrawRectangle.bottom = (int)c->y + (int)c->h;
quickDrawRectangle.right = (int)c->x + (int)c->w;
FillOval(&quickDrawRectangle, &colorPattern);
FrameOval(&quickDrawRectangle);// An oval is a circular or elliptical shape defined by the bounding rectangle that encloses it. inside macintosh: imaging with quickdraw 3-25
// http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-111.html#HEADING111-0
}
break;
case NK_COMMAND_TRIANGLE: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_TRIANGLE");
#endif
const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd;
color = nk_color_to_quickdraw_bw_color(t->color);
ForeColor(color);
PenSize((int)t->line_thickness, (int)t->line_thickness);
MoveTo((int)t->a.x, (int)t->a.y);
LineTo((int)t->b.x, (int)t->b.y);
LineTo((int)t->c.x, (int)t->c.y);
LineTo((int)t->a.x, (int)t->a.y);
}
break;
case NK_COMMAND_TRIANGLE_FILLED: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_TRIANGLE_FILLED");
#endif
const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd;
Pattern colorPattern = nk_color_to_quickdraw_color(t->color);
color = nk_color_to_quickdraw_bw_color(t->color);
PenSize(1.0, 1.0);
// BackPat(&colorPattern); // inside macintosh: imaging with quickdraw 3-48
ForeColor(color);
PolyHandle trianglePolygon = OpenPoly();
MoveTo((int)t->a.x, (int)t->a.y);
LineTo((int)t->b.x, (int)t->b.y);
LineTo((int)t->c.x, (int)t->c.y);
LineTo((int)t->a.x, (int)t->a.y);
ClosePoly();
FillPoly(trianglePolygon, &colorPattern);
KillPoly(trianglePolygon);
}
break;
case NK_COMMAND_POLYGON: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_POLYGON");
#endif
const struct nk_command_polygon *p = (const struct nk_command_polygon*)cmd;
color = nk_color_to_quickdraw_bw_color(p->color);
ForeColor(color);
int i;
for (i = 0; i < p->point_count; i++) {
if (i == 0) {
MoveTo(p->points[i].x, p->points[i].y);
}
LineTo(p->points[i].x, p->points[i].y);
if (i == p->point_count - 1) {
LineTo(p->points[0].x, p->points[0].y);
}
}
}
break;
case NK_COMMAND_POLYGON_FILLED: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_POLYGON_FILLED");
#endif
const struct nk_command_polygon *p = (const struct nk_command_polygon*)cmd;
Pattern colorPattern = nk_color_to_quickdraw_color(p->color);
color = nk_color_to_quickdraw_bw_color(p->color);
// BackPat(&colorPattern); // inside macintosh: imaging with quickdraw 3-48 -- but might actually need PenPat -- look into this
ForeColor(color);
int i;
PolyHandle trianglePolygon = OpenPoly();
for (i = 0; i < p->point_count; i++) {
if (i == 0) {
MoveTo(p->points[i].x, p->points[i].y);
}
LineTo(p->points[i].x, p->points[i].y);
if (i == p->point_count - 1) {
LineTo(p->points[0].x, p->points[0].y);
}
}
ClosePoly();
FillPoly(trianglePolygon, &colorPattern);
KillPoly(trianglePolygon);
}
break;
case NK_COMMAND_POLYLINE: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_POLYLINE");
#endif
// this is similar to polygons except the polygon does not get closed to the 0th point
// check out the slight difference in the for loop
const struct nk_command_polygon *p = (const struct nk_command_polygon*)cmd;
color = nk_color_to_quickdraw_bw_color(p->color);
ForeColor(color);
int i;
for (i = 0; i < p->point_count; i++) {
if (i == 0) {
MoveTo(p->points[i].x, p->points[i].y);
}
LineTo(p->points[i].x, p->points[i].y);
}
}
break;
case NK_COMMAND_TEXT: {
const struct nk_command_text *t = (const struct nk_command_text*)cmd;
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_TEXT");
char log[255];
sprintf(log, "%f: %c, %d", (int)t->height, &t->string, (int)t->length);
writeSerialPortDebug(boutRefNum, log);
#endif
color = nk_color_to_quickdraw_bw_color(t->foreground);
ForeColor(color);
MoveTo((int)t->x, (int)t->y + (int)t->height);
DrawText((const char*)t->string, 0, (int)t->length);
// DrawChar(t->string[t->length - 1]);
}
break;
case NK_COMMAND_CURVE: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_CURVE");
#endif
const struct nk_command_curve *q = (const struct nk_command_curve *)cmd;
color = nk_color_to_quickdraw_bw_color(q->color);
ForeColor(color);
Point p1 = { (int)q->begin.x, (int)q->begin.y};
Point p2 = { (int)q->ctrl[0].x, (int)q->ctrl[0].y};
Point p3 = { (int)q->ctrl[1].x, (int)q->ctrl[1].y};
Point p4 = { (int)q->end.x, (int)q->end.y};
BezierCurve(p1, p2, p3, p4);
}
break;
case NK_COMMAND_ARC: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_ARC");
#endif
const struct nk_command_arc *a = (const struct nk_command_arc *)cmd;
color = nk_color_to_quickdraw_bw_color(a->color);
ForeColor(color);
Rect arcBoundingBoxRectangle;
// this is kind of silly because the cx is at the center of the arc and we need to create a rectangle around it
// http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/QuickDraw/QuickDraw-60.html#MARKER-2-116
int x1 = (int)a->cx - (int)a->r;
int y1 = (int)a->cy - (int)a->r;
int x2 = (int)a->cx + (int)a->r;
int y2 = (int)a->cy + (int)a->r;
SetRect(&arcBoundingBoxRectangle, x1, y1, x2, y2);
// SetRect(secondRect,90,20,140,70);
FrameArc(&arcBoundingBoxRectangle, a->a[0], a->a[1]);
}
break;
case NK_COMMAND_IMAGE: {
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_IMAGE");
#endif
const struct nk_command_image *i = (const struct nk_command_image *)cmd;
// al_draw_bitmap_region(i->img.handle.ptr, 0, 0, i->w, i->h, i->x, i->y, 0); // TODO: look up and convert al_draw_bitmap_region
// TODO: consider implementing a bitmap drawing routine. we could iterate pixel by pixel and draw
// here is some super naive code that could work, used for another project that i was working on with a custom format but would be
// easy to modify for standard bitmap files (just need to know how many bytes represent each pixel and iterate from there):
//
// for (int i = 0; i < strlen(string); i++) {
// printf("\nchar: %c", string[i]);
// char pixel[1];
// memcpy(pixel, &string[i], 1);
// if (strcmp(pixel, "0") == 0) { // white pixel
// MoveTo(++x, y);
// } else if (strcmp(pixel, "1") == 0) { // black pixel
// // advance the pen and draw a 1px x 1px "line"
// MoveTo(++x, y);
// LineTo(x, y);
// } else if (strcmp(pixel, "|") == 0) { // next line
// x = 1;
// MoveTo(x, ++y);
// } else if (strcmp(pixel, "/") == 0) { // end
// }
// }
}
break;
// why are these cases not implemented?
case NK_COMMAND_RECT_MULTI_COLOR:
case NK_COMMAND_ARC_FILLED:
default:
#ifdef NK_QUICKDRAW_GRAPHICS_DEBUGGING
writeSerialPortDebug(boutRefNum, "NK_COMMAND_RECT_MULTI_COLOR/NK_COMMAND_ARC_FILLED/default");
#endif
break;
}
}
}
end = TickCount();
total = end - start;
renderTime2 = total;// / 60.0;
start = TickCount();
if (!lastEventWasKey) {
SetPort(window);
// our offscreen bitmap is the same size as our port rectangle, so we
// get away with using the portRect sizing for source and destination
CopyBits(&gMainOffScreen.bits->portBits, &window->portBits, &window->portRect, &window->portRect, srcCopy, 0L);
}
end = TickCount();
total = end - start;
renderTime3 = total;// / 60.0;
start = TickCount();
// char logx[255];
// sprintf(logx, "nk_quickdraw_render() renderTime1 (pre-render) %ld, renderTime2 (render loop) %ld, renderTime3 (post-render) %ld ticks to execute\n", renderTime1, renderTime2, renderTime3);
// writeSerialPortDebug(boutRefNum, logx);
lastEventWasKey = 0;
}
NK_API int nk_quickdraw_handle_event(EventRecord *event, struct nk_context *nuklear_context) {
// see: inside macintosh: toolbox essentials 2-4
// and inside macintosh toolbox essentials 2-79
WindowPtr window;
FindWindow(event->where, &window);
// char logb[255];
// sprintf(logb, "nk_quickdraw_handle_event event %d", event->what);
// writeSerialPortDebug(boutRefNum, logb);
switch (event->what) {
case updateEvt: {
return 1;
}
break;
case osEvt: {
// the quicktime osEvts are supposed to cover mouse movement events
// notice that we are actually calling nk_input_motion in the EventLoop for the program
// instead, as handling this event directly does not appear to work for whatever reason
// TODO: research this
writeSerialPortDebug(boutRefNum, "osEvt");
switch (event->message) {
case mouseMovedMessage: {
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "mouseMovedMessage");
#endif
// event->where should have coordinates??? or is it just a pointer to what the mouse is over?
// TODO need to figure this out
nk_input_motion(nuklear_context, event->where.h, event->where.v); // TODO figure out mouse coordinates - not sure if this is right
break;
}
return 1;
}
}
break;
case mouseUp:
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "mouseUp!!!");
#endif
case mouseDown: {
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "mouseUp/Down");
#endif
short part = FindWindow(event->where, &window);
switch (part) {
case inContent: {
// event->where should have coordinates??? or is it just a pointer to what the mouse is over?
// TODO need to figure this out
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "mouseUp/Down IN DEFAULT ZONE!!!!");
#endif
// this converts the offset of the window to the actual location of the mouse within the window
Point tempPoint;
SetPt(&tempPoint, event->where.h, event->where.v);
GlobalToLocal(&tempPoint);
if (!event->where.h) {
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "no event location for mouse!!!!");
#endif
return 1;
}
int x = tempPoint.h;
int y = tempPoint.v;
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
char logx[255];
sprintf(logx, "mouse location at time of click h: %d, v: %d", x, y);
writeSerialPortDebug(boutRefNum, logx);
#endif
// nk_input_motion(nuklear_context, x, y); // you can enable this if you don't want to use motion tracking
// in the Mac event loop handler as in the nuklear quickdraw sample, and this will allow mouse clicks to
// work properly, but will not allow hover states to work
nk_input_button(nuklear_context, NK_BUTTON_LEFT, x, y, event->what == mouseDown);
}
break;
return 1;
}
break;
case keyDown:
case autoKey: {/* check for menukey equivalents */
char charKey = event->message & charCodeMask;
int key = (int)charKey;
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "keyDown/autoKey");
char logy[255];
sprintf(logy, "key pressed: key: '%c', 02x: '%02X', return: '%02X', %d == %d ??", key, key, returnKey, (int)(key), (int)(returnKey));
writeSerialPortDebug(boutRefNum, logy);
#endif
const Boolean isKeyDown = event->what == keyDown;
if (event->modifiers & cmdKey) {/* Command key down */
if (isKeyDown) {
// AdjustMenus(); /* enable/disable/check menu items properly */
// DoMenuCommand(MenuKey(key));
}
if (key == 'c') {
nk_input_key(nuklear_context, NK_KEY_COPY, 1);
} else if (key == 'v') {
nk_input_key(nuklear_context, NK_KEY_PASTE, 1);
} else if (key == 'x') {
nk_input_key(nuklear_context, NK_KEY_CUT, 1);
} else if (key == 'z') {
nk_input_key(nuklear_context, NK_KEY_TEXT_UNDO, 1);
} else if (key == 'r') {
nk_input_key(nuklear_context, NK_KEY_TEXT_REDO, 1);
}
} else if (key == eitherShiftKey) {
nk_input_key(nuklear_context, NK_KEY_SHIFT, isKeyDown);
} else if (key == deleteKey) {
nk_input_key(nuklear_context, NK_KEY_DEL, isKeyDown);
} else if (key == enterKey) {
nk_input_key(nuklear_context, NK_KEY_ENTER, isKeyDown);
} else if (key == returnKey) {
nk_input_key(nuklear_context, NK_KEY_ENTER, isKeyDown);
} else if (key == tabKey) {
nk_input_key(nuklear_context, NK_KEY_TAB, isKeyDown);
} else if (key == leftArrowKey) {
nk_input_key(nuklear_context, NK_KEY_LEFT, isKeyDown);
} else if (key == rightArrowKey) {
nk_input_key(nuklear_context, NK_KEY_RIGHT, isKeyDown);
} else if (key == upArrowKey) {
nk_input_key(nuklear_context, NK_KEY_UP, isKeyDown);
} else if (key == downArrowKey) {
nk_input_key(nuklear_context, NK_KEY_DOWN, isKeyDown);
} else if (key == backspaceKey) {
nk_input_key(nuklear_context, NK_KEY_BACKSPACE, isKeyDown);
} else if (key == escapeKey) {
// nk_input_key(nuklear_context, NK_KEY_TEXT_RESET_MODE, isKeyDown);
} else if (key == pageUpKey) {
nk_input_key(nuklear_context, NK_KEY_SCROLL_UP, isKeyDown);
} else if (key == pageDownKey) {
nk_input_key(nuklear_context, NK_KEY_SCROLL_DOWN, isKeyDown);
} else if (key == homeKey) {
// nk_input_key(nuklear_context, NK_KEY_TEXT_START, isKeyDown);
nk_input_key(nuklear_context, NK_KEY_SCROLL_START, isKeyDown);
} else if (key == endKey) {
// nk_input_key(nuklear_context, NK_KEY_TEXT_END, isKeyDown);
nk_input_key(nuklear_context, NK_KEY_SCROLL_END, isKeyDown);
} else {
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "default keydown/autokey event");
#endif
nk_input_unicode(nuklear_context, charKey);
}
lastEventWasKey = 1;
return 1;
}
break;
default: {
#ifdef NK_QUICKDRAW_EVENTS_DEBUGGING
writeSerialPortDebug(boutRefNum, "default unhandled event");
#endif
return 1;
}
break;
}
}
}
// i think these functions are close to correct, but throw an error around invalid storage class
NK_INTERN void nk_quickdraw_clipboard_paste(nk_handle usr, struct nk_text_edit *edit) {
Handle hDest = NewHandle(0); // { automatically resizes it}
HLock(hDest);
// {put data into memory referenced thru hDest handle}
int sizeOfSurfData = GetScrap(hDest, 'TEXT', 0);
HUnlock(hDest);
nk_textedit_paste(edit, (char *)hDest, sizeOfSurfData);
DisposeHandle(hDest);
}
NK_INTERN void nk_quickdraw_clipboard_copy(nk_handle usr, const char *text, int len) {
// in Macintosh Toolbox the clipboard is referred to as "scrap manager"
PutScrap(len, 'TEXT', text);
}
// it us up to our "main" function to call this code
NK_API struct nk_context* nk_quickdraw_init(unsigned int width, unsigned int height) {
// needed to calculate bezier info, see mactech article.
setupBezier();
NewShockBitmap(&gMainOffScreen, width, height);
NkQuickDrawFont *quickdrawfont = nk_quickdraw_font_create_from_file();
struct nk_user_font *font = &quickdrawfont->nk;
last = calloc(1, 64 * 1024);
buf = calloc(1, 64 * 1024);
nk_init_fixed(&quickdraw.nuklear_context, buf, 64 * 1024, font);
// nk_init_default(&quickdraw.nuklear_context, font);
nk_style_push_font(&quickdraw.nuklear_context, font);
// this is pascal code but i think we would need to do something like this if we want this function
// to be responsible for setting the window size
// Region locUpdateRgn = NewRgn();
// SetRect(limitRect, kMinDocSize, kMinDocSize, kMaxDocSize, kMaxDocSize);
// // {call Window Manager to let user drag size box}
// growSize = GrowWindow(thisWindow, event.where, limitRect);
// SizeWindow(thisWindow, LoWord(growSize), HiWord(growSize), TRUE);
// SectRect(oldViewRect, myData^^.editRec^^.viewRect, oldViewRect);
// // {validate the intersection (don't update)}
// ValidRect(oldViewRect);
// // {invalidate any prior update region}
// InvalRgn(locUpdateRgn);
// DisposeRgn(locUpdateRgn);
quickdraw.nuklear_context.clip.copy = nk_quickdraw_clipboard_copy;
quickdraw.nuklear_context.clip.paste = nk_quickdraw_clipboard_paste;
quickdraw.nuklear_context.clip.userdata = nk_handle_ptr(0);
// fix styles to be more "mac-like"
struct nk_style *style;
struct nk_style_toggle *toggle;
struct nk_style_button *button;
style = &quickdraw.nuklear_context.style;
/* checkbox toggle */
toggle = &style->checkbox;
nk_zero_struct(*toggle);
toggle->normal = nk_style_item_color(nk_rgba(45, 45, 45, 255));
toggle->hover = nk_style_item_color(nk_rgba(80, 80, 80, 255)); // this is the "background" hover state regardless of checked status - we want light gray
toggle->active = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // i can't tell what this does yet
toggle->cursor_normal = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the "checked" box itself - we want "black"
toggle->cursor_hover = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the hover state of a "checked" box - anything lighter than black is ok
toggle->userdata = nk_handle_ptr(0);
toggle->text_background = nk_rgba(255, 255, 255, 255);
toggle->text_normal = nk_rgba(70, 70, 70, 255);
toggle->text_hover = nk_rgba(70, 70, 70, 255);
toggle->text_active = nk_rgba(70, 70, 70, 255);
toggle->padding = nk_vec2(3.0f, 3.0f);
toggle->touch_padding = nk_vec2(0,0);
toggle->border_color = nk_rgba(0,0,0,0);
toggle->border = 0.0f;
toggle->spacing = 5;
/* option toggle */
toggle = &style->option;
nk_zero_struct(*toggle);
toggle->normal = nk_style_item_color(nk_rgba(45, 45, 45, 255));
toggle->hover = nk_style_item_color(nk_rgba(80, 80, 80, 255)); // this is the "background" hover state regardless of checked status - we want light gray
toggle->active = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // i can't tell what this does yet
toggle->cursor_normal = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the "checked" box itself - we want "black"
toggle->cursor_hover = nk_style_item_color(nk_rgba(255, 255, 255, 255)); // this is the hover state of a "checked" box - anything lighter than black is ok
toggle->userdata = nk_handle_ptr(0);
toggle->text_background = nk_rgba(255, 255, 255, 255);
toggle->text_normal = nk_rgba(70, 70, 70, 255);
toggle->text_hover = nk_rgba(70, 70, 70, 255);
toggle->text_active = nk_rgba(70, 70, 70, 255);
toggle->padding = nk_vec2(3.0f, 3.0f);
toggle->touch_padding = nk_vec2(0,0);
toggle->border_color = nk_rgba(0,0,0,0);
toggle->border = 0.0f;
toggle->spacing = 5;
// button
button = &style->button;
nk_zero_struct(*button);
button->normal = nk_style_item_color(nk_rgba(0, 0, 0, 255));
button->hover = nk_style_item_color(nk_rgba(80, 80, 80, 255));
button->active = nk_style_item_color(nk_rgba(150, 150, 150, 255));
button->border_color = nk_rgba(255, 255, 255, 255);
button->text_background = nk_rgba(255, 255, 255, 255);
button->text_normal = nk_rgba(70, 70, 70, 255);
button->text_hover = nk_rgba(70, 70, 70, 255);
button->text_active = nk_rgba(0, 0, 0, 255);
button->padding = nk_vec2(2.0f,2.0f);
button->image_padding = nk_vec2(0.0f,0.0f);
button->touch_padding = nk_vec2(0.0f, 0.0f);
button->userdata = nk_handle_ptr(0);
button->text_alignment = NK_TEXT_CENTERED;
button->border = 1.0f;
button->rounding = 4.0f;
button->draw_begin = 0;
button->draw_end = 0;
ForeColor(blackColor);
TextSize(13);
return &quickdraw.nuklear_context;
}
NK_API void nk_quickdraw_shutdown(void) {
nk_free(&quickdraw.nuklear_context);
memset(&quickdraw, 0, sizeof(quickdraw));
}