Merge pull request #315 from seibelj/allegro5_impl

Allegro5 nuklear backend and touchscreen support
This commit is contained in:
Micha Mettke 2017-01-07 11:01:22 +01:00 committed by GitHub
commit 24c756c264
7 changed files with 824 additions and 2 deletions

View File

@ -0,0 +1,10 @@
#import <UIKit/UIKit.h>
#include <allegro5/allegro.h>
@interface KeyboardHandleriOS : UIView <UIKeyInput>
-(void)setCustomKeyboardEventSource:(ALLEGRO_EVENT_SOURCE*)ev_src;
-(void)show;
-(void)hide;
@end

View File

@ -0,0 +1,120 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#endif
#import "KeyboardHandleriOS.h"
#include <allegro5/allegro.h>
#include <allegro5/allegro_iphone_objc.h>
@interface KeyboardHandleriOS()
{
ALLEGRO_EVENT_SOURCE *event_source;
ALLEGRO_DISPLAY *current_display;
}
@end
@implementation KeyboardHandleriOS
- (id)init {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
self = [self initWithFrame:CGRectMake(-100, -100, 0, 0)];
event_source = NULL;
current_display = al_get_current_display();
UIView* v = al_iphone_get_view(current_display);
[v addSubview:self];
return self;
}
- (void)setCustomKeyboardEventSource:(ALLEGRO_EVENT_SOURCE *)ev_src {
event_source = ev_src;
}
- (UIKeyboardType) keyboardType
{
return UIKeyboardTypeASCIICapable;
}
- (UITextAutocorrectionType) autocorrectionType
{
return UITextAutocorrectionTypeNo;
}
-(BOOL)canBecomeFirstResponder {
return YES;
}
- (void)deleteBackward {
if (!event_source) {
NSLog(@"deleteBackward(): No event source found, not sending events");
return;
}
ALLEGRO_EVENT *event_down = (ALLEGRO_EVENT*)calloc(1, sizeof(ALLEGRO_EVENT));
ALLEGRO_EVENT *event_up = (ALLEGRO_EVENT*)calloc(1, sizeof(ALLEGRO_EVENT));
event_down->type = ALLEGRO_EVENT_KEY_DOWN;
event_down->keyboard.display = current_display;
event_down->keyboard.keycode = ALLEGRO_KEY_BACKSPACE;
event_up->type = ALLEGRO_EVENT_KEY_UP;
event_up->keyboard.display = current_display;
event_up->keyboard.keycode = ALLEGRO_KEY_BACKSPACE;
al_emit_user_event(event_source, event_down, NULL);
al_emit_user_event(event_source, event_up, NULL);
free(event_down);
free(event_up);
}
- (BOOL)hasText {
return YES;
}
- (void)insertText:(NSString *)text
{
if (!event_source) {
NSLog(@"insertText(): No event source found, not sending events");
return;
}
ALLEGRO_EVENT *event_down = (ALLEGRO_EVENT*)calloc(1, sizeof(ALLEGRO_EVENT));
ALLEGRO_EVENT *event_up = (ALLEGRO_EVENT*)calloc(1, sizeof(ALLEGRO_EVENT));
if([text isEqualToString:@"\n"])
{
event_down->type = ALLEGRO_EVENT_KEY_DOWN;
event_down->keyboard.display = current_display;
event_down->keyboard.keycode = ALLEGRO_KEY_ENTER;
event_up->type = ALLEGRO_EVENT_KEY_UP;
event_up->keyboard.display = current_display;
event_up->keyboard.keycode = ALLEGRO_KEY_ENTER;
al_emit_user_event(event_source, event_down, NULL);
al_emit_user_event(event_source, event_up, NULL);
[self hide];
//m_kb->setDonePressed();
}
else {
event_down->type = ALLEGRO_EVENT_KEY_CHAR;
event_down->keyboard.display = current_display;
event_down->keyboard.unichar = [text characterAtIndex:0];
// doesn't matter what keycode is, nuklear backend ignores it as long as it
// isn't a special key
event_down->keyboard.keycode = ALLEGRO_KEY_A;
al_emit_user_event(event_source, event_down, NULL);
}
free(event_down);
free(event_up);
}
-(void)show {
NSLog(@"Should be showing!");
[self performSelectorOnMainThread:@selector(becomeFirstResponder) withObject:nil waitUntilDone:YES];
}
-(void)hide {
NSLog(@"Should be hiding!");
[self performSelectorOnMainThread:@selector(resignFirstResponder) withObject:nil waitUntilDone:YES];
}
- (void)keyboardDidHide:(NSNotification *)notification {
NSLog(@"keyboardDidHide called");
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
}
@end

22
demo/allegro5/Makefile Normal file
View File

@ -0,0 +1,22 @@
# Install
BIN = demo
# Flags
CFLAGS = -std=c99 -pedantic -O2
SRC = main.c
OBJ = $(SRC:.c=.o)
# TODO: Handle Windows build
#ifeq ($(OS),Windows_NT)
#BIN := $(BIN).exe
#LIBS = -lglfw3 -lopengl32 -lm -lGLU32 -lGLEW32
#else
LIBS = -lallegro -lallegro_main -lallegro_image -lallegro_font \
-lallegro_ttf -lallegro_primitives -lm
#endif
$(BIN):
@mkdir -p bin
rm -f bin/$(BIN) $(OBJS)
$(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) $(LIBS)

35
demo/allegro5/Readme.md Normal file
View File

@ -0,0 +1,35 @@
# Allegro v5 nuklear backend
This backend provides support for [Allegro version 5](http://liballeg.org/). It works on on all supported platforms with an OpenGL backend, including iOS and Android.
Touch support is provided by handling the first touch (ignoring any extra simultaneous touches) and emitting nuklear mouse events. nuklear will handle only the first touch like a single left-mouse click. Dragging the touch screen emits mouse-move events.
## Compiling
You must link with image, font, ttf, and primitives Allegro addons. See the `Makefile`.
## Resolutions
Like every nuklear backend, handling many different resolutions and resolution densities can be tricky. 14px font on a desktop may be perfect, but extremely small on a retina iPad. I recommend writing a middleware that will detect what kind of screen is being used, and modify the sizes of widgets accordingly.
## Soft Keyboard for Touch Screen Devices
Information on how to implement soft keyboard callbacks for Android can be on the Allegro community wiki: https://wiki.allegro.cc/index.php?title=Running_Allegro_applications_on_Android#Displaying_the_Android_keyboard
To display a soft keyboard on iOS, you must create a `UIView` subclass that implements the `UIKeyInput` interface. See `KeyboardHandleriOS.h` and `KeyboardHandleriOS.m` Objective-C source code files for an example on how to do this. As the Allegro keyboard driver does not currently listen for iOS events, we use a custom event emitter to emit keyboard events, which is passed in after initialization with `(void)setCustomKeyboardEventSource:(ALLEGRO_EVENT_SOURCE *)ev_src`. This causes normal keyboard events to be emitted and properly caught by the nuklear backend. The provided `main.c` demo file does not implement this, but with the provided source code files it is not difficult to do. See this Allegro community forum thread for more information: https://www.allegro.cc/forums/thread/616672
To know when nuklear wants to open and close the keyboard, you can check edit widget flags:
```
nk_flags ed_flags = nk_edit_string(ctx, NK_EDIT_FIELD, field_buffer, &field_len, 64, nk_filter_default);
if (ed_flags & NK_EDIT_ACTIVATED)
open_ios_soft_keyboard();
if (ed_flags & NK_EDIT_DEACTIVATED)
close_ios_soft_keyboard();
```
### Manual Soft Keyboard Dismissal
As the user can dismiss a keyboard manually, nuklear will not be aware when this occurs, and the text edit cursor will think the entry field is still active. I recommend catching the dismiss event, then emitting `ALLEGRO_EVENT_TOUCH_BEGIN` and `ALLEGRO_EVENT_TOUCH_END` events in an unused portion of the screen (like the bottom-right corner). This will simulate the user touching outside of the text entry widget, which will make the edit field inactive.
### The Keyboard Covers Widgets
If you have a widget near the bottom of the screen, the keyboard opening woll cover it, and the user won't see what they are entering. One way to handle this is to make all text edit widgets view-only, and when tapped you dynamically create a new widget above the keyboard that receives all the key strokes. When the user dismisses the keyboard, copy the result from the new widget into the existing read-only text view and destroy the dynamic one.

165
demo/allegro5/main.c Normal file
View File

@ -0,0 +1,165 @@
/* nuklear - v1.17 - public domain */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <math.h>
#include <limits.h>
#include <time.h>
#include <allegro5/allegro.h>
#define WINDOW_WIDTH 1200
#define WINDOW_HEIGHT 800
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_IMPLEMENTATION
#define NK_ALLEGRO5_IMPLEMENTATION
#include "../../nuklear.h"
#include "nuklear_allegro5.h"
#define UNUSED(a) (void)a
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) < (b) ? (b) : (a))
#define LEN(a) (sizeof(a)/sizeof(a)[0])
/* ===============================================================
*
* EXAMPLE
*
* ===============================================================*/
/* This are some code examples to provide a small overview of what can be
* done with this library. To try out an example uncomment the include
* and the corresponding function. */
/*#include "../style.c"*/
/*#include "../calculator.c"*/
#include "../overview.c"
/*#include "../node_editor.c"*/
/* ===============================================================
*
* DEMO
*
* ===============================================================*/
static void error_callback(int e, const char *d)
{printf("Error %d: %s\n", e, d);}
int main(void)
{
/* Platform */
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
if (!al_init()) {
fprintf(stdout, "failed to initialize allegro5!\n");
exit(1);
}
al_install_mouse();
al_set_mouse_wheel_precision(150);
al_install_keyboard();
al_set_new_display_flags(ALLEGRO_WINDOWED|ALLEGRO_RESIZABLE|ALLEGRO_OPENGL);
al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST);
al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST);
display = al_create_display(WINDOW_WIDTH, WINDOW_HEIGHT);
if (!display) {
fprintf(stdout, "failed to create display!\n");
exit(1);
}
event_queue = al_create_event_queue();
if (!event_queue) {
fprintf(stdout, "failed to create event_queue!\n");
al_destroy_display(display);
exit(1);
}
al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_mouse_event_source());
al_register_event_source(event_queue, al_get_keyboard_event_source());
NkAllegro5Font *font;
font = nk_allegro5_font_create_from_file("../../../extra_font/Roboto-Regular.ttf", 12, 0);
struct nk_context *ctx;
ctx = nk_allegro5_init(font, display, WINDOW_WIDTH, WINDOW_HEIGHT);
/* style.c */
/*set_style(ctx, THEME_WHITE);*/
/*set_style(ctx, THEME_RED);*/
/*set_style(ctx, THEME_BLUE);*/
/*set_style(ctx, THEME_DARK);*/
while(1)
{
ALLEGRO_EVENT ev;
ALLEGRO_TIMEOUT timeout;
al_init_timeout(&timeout, 0.06);
bool get_event = al_wait_for_event_until(event_queue, &ev, &timeout);
if (get_event && ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
break;
}
/* Very Important: Always do nk_input_begin / nk_input_end even if
there are no events, otherwise internal nuklear state gets messed up */
nk_input_begin(ctx);
if (get_event) {
while (get_event) {
nk_allegro5_handle_event(&ev);
get_event = al_get_next_event(event_queue, &ev);
}
}
nk_input_end(ctx);
/* GUI */
if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200),
NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|
NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE))
{
enum {EASY, HARD};
static int op = EASY;
static int property = 20;
nk_layout_row_static(ctx, 30, 80, 1);
if (nk_button_label(ctx, "button"))
fprintf(stdout, "button pressed\n");
nk_layout_row_dynamic(ctx, 30, 2);
if (nk_option_label(ctx, "easy", op == EASY)) op = EASY;
if (nk_option_label(ctx, "hard", op == HARD)) op = HARD;
nk_layout_row_dynamic(ctx, 22, 1);
nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1);
}
nk_end(ctx);
/* -------------- EXAMPLES ---------------- */
/*calculator(ctx);*/
overview(ctx);
/*node_editor(ctx);*/
/* ----------------------------------------- */
/* Draw */
al_clear_to_color(al_map_rgb(19, 43, 81));
/* IMPORTANT: `nk_allegro5_render` changes the target backbuffer
to the display set at initialization and does not restore it.
Change it if you want to draw somewhere else. */
nk_allegro5_render();
al_flip_display();
}
nk_allegro5_font_del(font);
nk_allegro5_shutdown();
al_destroy_display(display);
al_destroy_event_queue(event_queue);
return 0;
}

View File

@ -0,0 +1,464 @@
/*
* Nuklear - v1.17 - public domain
* no warrenty implied; use at your own risk.
* authored from 2015-2016 by Micha Mettke
*/
/*
* ==============================================================
*
* API
*
* ===============================================================
*/
#ifndef NK_ALLEGRO5_H_
#define NK_ALLEGRO5_H_
#include <string.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
typedef struct NkAllegro5Font NkAllegro5Font;
NK_API struct nk_context* nk_allegro5_init(NkAllegro5Font *font, ALLEGRO_DISPLAY *dsp,
unsigned int width, unsigned int height);
NK_API void nk_allegro5_handle_event(ALLEGRO_EVENT *ev);
NK_API void nk_allegro5_shutdown(void);
NK_API void nk_allegro5_render(void);
/* Fonts. We wrap normal allegro fonts in some nuklear book keeping */
NK_API NkAllegro5Font* nk_allegro5_font_create_from_file(const char *file_name, int font_size, int flags);
NK_API void nk_allegro5_font_del(NkAllegro5Font *font);
NK_API void nk_allegro5_font_set_font(NkAllegro5Font *font);
#endif
/*
* ==============================================================
*
* IMPLEMENTATION
*
* ===============================================================
*/
#ifdef NK_ALLEGRO5_IMPLEMENTATION
#ifndef NK_ALLEGRO5_TEXT_MAX
#define NK_ALLEGRO5_TEXT_MAX 256
#endif
struct NkAllegro5Font {
struct nk_user_font nk;
int height;
ALLEGRO_FONT *font;
};
static struct nk_allegro5 {
ALLEGRO_DISPLAY *dsp;
unsigned int width;
unsigned int height;
int is_touch_down;
int touch_down_id;
struct nk_context ctx;
struct nk_buffer cmds;
} allegro5;
/* Flags are identical to al_load_font() flags argument */
NK_API NkAllegro5Font*
nk_allegro5_font_create_from_file(const char *file_name, int font_size, int flags)
{
if (!al_init_image_addon()) {
fprintf(stdout, "Unable to initialize required allegro5 image addon\n");
exit(1);
}
if (!al_init_font_addon()) {
fprintf(stdout, "Unable to initialize required allegro5 font addon\n");
exit(1);
}
if (!al_init_ttf_addon()) {
fprintf(stdout, "Unable to initialize required allegro5 TTF font addon\n");
exit(1);
}
NkAllegro5Font *font = (NkAllegro5Font*)calloc(1, sizeof(NkAllegro5Font));
font->font = al_load_font(file_name, font_size, flags);
if (font->font == NULL) {
fprintf(stdout, "Unable to load font file: %s\n", file_name);
return NULL;
}
font->height = al_get_font_line_height(font->font);
return font;
}
static float
nk_allegro5_font_get_text_width(nk_handle handle, float height, const char *text, int len)
{
NkAllegro5Font *font = (NkAllegro5Font*)handle.ptr;
if (!font || !text) {
return 0;
}
/* We must copy into a new buffer with exact length null-terminated
as nuklear uses variable size buffers and al_get_text_width doesn't
accept a length, it infers length from null-termination
(which is unsafe API design by allegro devs!) */
char strcpy[len+1];
strncpy((char*)&strcpy, text, len);
strcpy[len] = '\0';
return al_get_text_width(font->font, strcpy);
}
NK_API void
nk_allegro5_font_set_font(NkAllegro5Font *allegro5font)
{
struct nk_user_font *font = &allegro5font->nk;
font->userdata = nk_handle_ptr(allegro5font);
font->height = (float)allegro5font->height;
font->width = nk_allegro5_font_get_text_width;
nk_style_set_font(&allegro5.ctx, font);
}
NK_API void
nk_allegro5_font_del(NkAllegro5Font *font)
{
if(!font) return;
al_destroy_font(font->font);
free(font);
}
static ALLEGRO_COLOR
nk_color_to_allegro_color(struct nk_color color)
{
return al_map_rgba((unsigned char)color.r, (unsigned char)color.g,
(unsigned char)color.b, (unsigned char)color.a);
}
NK_API void
nk_allegro5_render()
{
const struct nk_command *cmd;
al_set_target_backbuffer(allegro5.dsp);
nk_foreach(cmd, &allegro5.ctx)
{
ALLEGRO_COLOR color;
switch (cmd->type) {
case NK_COMMAND_NOP: break;
case NK_COMMAND_SCISSOR: {
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);
} break;
case NK_COMMAND_LINE: {
const struct nk_command_line *l = (const struct nk_command_line *)cmd;
color = nk_color_to_allegro_color(l->color);
al_draw_line((float)l->begin.x, (float)l->begin.y, (float)l->end.x,
(float)l->end.y, color, (float)l->line_thickness);
} break;
case NK_COMMAND_RECT: {
const struct nk_command_rect *r = (const struct nk_command_rect *)cmd;
color = nk_color_to_allegro_color(r->color);
al_draw_rounded_rectangle((float)r->x, (float)r->y, (float)(r->x + r->w),
(float)(r->y + r->h), (float)r->rounding, (float)r->rounding, color,
(float)r->line_thickness);
} break;
case NK_COMMAND_RECT_FILLED: {
const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd;
color = nk_color_to_allegro_color(r->color);
al_draw_filled_rounded_rectangle((float)r->x, (float)r->y,
(float)(r->x + r->w), (float)(r->y + r->h), (float)r->rounding,
(float)r->rounding, color);
} break;
case NK_COMMAND_CIRCLE: {
const struct nk_command_circle *c = (const struct nk_command_circle *)cmd;
color = nk_color_to_allegro_color(c->color);
float xr, yr;
xr = (float)c->w/2;
yr = (float)c->h/2;
al_draw_ellipse(((float)(c->x)) + xr, ((float)c->y) + yr,
xr, yr, color, (float)c->line_thickness);
} break;
case NK_COMMAND_CIRCLE_FILLED: {
const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd;
color = nk_color_to_allegro_color(c->color);
float xr, yr;
xr = (float)c->w/2;
yr = (float)c->h/2;
al_draw_filled_ellipse(((float)(c->x)) + xr, ((float)c->y) + yr,
xr, yr, color);
} break;
case NK_COMMAND_TRIANGLE: {
const struct nk_command_triangle*t = (const struct nk_command_triangle*)cmd;
color = nk_color_to_allegro_color(t->color);
al_draw_triangle((float)t->a.x, (float)t->a.y, (float)t->b.x, (float)t->b.y,
(float)t->c.x, (float)t->c.y, color, (float)t->line_thickness);
} break;
case NK_COMMAND_TRIANGLE_FILLED: {
const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd;
color = nk_color_to_allegro_color(t->color);
al_draw_filled_triangle((float)t->a.x, (float)t->a.y, (float)t->b.x,
(float)t->b.y, (float)t->c.x, (float)t->c.y, color);
} break;
case NK_COMMAND_POLYGON: {
const struct nk_command_polygon *p = (const struct nk_command_polygon*)cmd;
color = nk_color_to_allegro_color(p->color);
int i;
float vertices[p->point_count * 2];
for (i = 0; i < p->point_count; i++) {
vertices[i*2] = p->points[i].x;
vertices[(i*2) + 1] = p->points[i].y;
}
al_draw_polyline((const float*)&vertices, (2 * sizeof(float)),
(int)p->point_count, ALLEGRO_LINE_JOIN_ROUND, ALLEGRO_LINE_CAP_CLOSED,
color, (float)p->line_thickness, 0.0);
} break;
case NK_COMMAND_POLYGON_FILLED: {
const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled *)cmd;
color = nk_color_to_allegro_color(p->color);
int i;
float vertices[p->point_count * 2];
for (i = 0; i < p->point_count; i++) {
vertices[i*2] = p->points[i].x;
vertices[(i*2) + 1] = p->points[i].y;
}
al_draw_filled_polygon((const float*)&vertices, (int)p->point_count, color);
} break;
case NK_COMMAND_POLYLINE: {
const struct nk_command_polyline *p = (const struct nk_command_polyline *)cmd;
color = nk_color_to_allegro_color(p->color);
int i;
float vertices[p->point_count * 2];
for (i = 0; i < p->point_count; i++) {
vertices[i*2] = p->points[i].x;
vertices[(i*2) + 1] = p->points[i].y;
}
al_draw_polyline((const float*)&vertices, (2 * sizeof(float)),
(int)p->point_count, ALLEGRO_LINE_JOIN_ROUND, ALLEGRO_LINE_CAP_ROUND,
color, (float)p->line_thickness, 0.0);
} break;
case NK_COMMAND_TEXT: {
const struct nk_command_text *t = (const struct nk_command_text*)cmd;
color = nk_color_to_allegro_color(t->foreground);
NkAllegro5Font *font = (NkAllegro5Font*)t->font->userdata.ptr;
al_draw_text(font->font,
color, (float)t->x, (float)t->y, 0,
(const char*)t->string);
} break;
case NK_COMMAND_CURVE: {
const struct nk_command_curve *q = (const struct nk_command_curve *)cmd;
color = nk_color_to_allegro_color(q->color);
float points[8];
points[0] = (float)q->begin.x;
points[1] = (float)q->begin.y;
points[2] = (float)q->ctrl[0].x;
points[3] = (float)q->ctrl[0].y;
points[4] = (float)q->ctrl[1].x;
points[5] = (float)q->ctrl[1].y;
points[6] = (float)q->end.x;
points[7] = (float)q->end.y;
al_draw_spline(points, color, (float)q->line_thickness);
} break;
case NK_COMMAND_ARC: {
const struct nk_command_arc *a = (const struct nk_command_arc *)cmd;
color = nk_color_to_allegro_color(a->color);
al_draw_arc((float)a->cx, (float)a->cy, (float)a->r, a->a[0],
a->a[1], color, (float)a->line_thickness);
} break;
case NK_COMMAND_RECT_MULTI_COLOR:
case NK_COMMAND_IMAGE:
case NK_COMMAND_ARC_FILLED:
default: break;
}
}
nk_clear(&allegro5.ctx);
}
NK_API void
nk_allegro5_handle_event(ALLEGRO_EVENT *ev)
{
struct nk_context *ctx = &allegro5.ctx;
switch (ev->type) {
case ALLEGRO_EVENT_DISPLAY_RESIZE: {
allegro5.width = (unsigned int)ev->display.width;
allegro5.height = (unsigned int)ev->display.height;
al_acknowledge_resize(ev->display.source);
} break;
case ALLEGRO_EVENT_MOUSE_AXES: {
nk_input_motion(ctx, ev->mouse.x, ev->mouse.y);
if (ev->mouse.dz != 0) {
nk_input_scroll(ctx, (float)ev->mouse.dz / al_get_mouse_wheel_precision());
}
} break;
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
case ALLEGRO_EVENT_MOUSE_BUTTON_UP: {
int button = NK_BUTTON_LEFT;
if (ev->mouse.button == 2) {
button = NK_BUTTON_RIGHT;
}
else if (ev->mouse.button == 3) {
button = NK_BUTTON_MIDDLE;
}
nk_input_button(ctx, button, ev->mouse.x, ev->mouse.y, ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN);
} break;
/* This essentially converts touch events to mouse events */
case ALLEGRO_EVENT_TOUCH_BEGIN:
case ALLEGRO_EVENT_TOUCH_END: {
/* We only acknowledge one touch at a time. Otherwise, each touch
would be manipulating multiple nuklear elements, as if there
were multiple mouse cursors */
if (allegro5.is_touch_down && allegro5.touch_down_id != ev->touch.id) {
return;
}
if (ev->type == ALLEGRO_EVENT_TOUCH_BEGIN) {
allegro5.is_touch_down = 1;
allegro5.touch_down_id = ev->touch.id;
/* FIXME: This is a hack to properly simulate
touches as a mouse with nuklear. If you instantly jump
from one place to another without an nk_input_end(), it
confuses the nuklear state. nuklear expects smooth mouse
movements, which is unlike a touch screen */
nk_input_motion(ctx, (int)ev->touch.x, (int)ev->touch.y);
nk_input_end(ctx);
nk_input_begin(ctx);
}
else {
allegro5.is_touch_down = 0;
allegro5.touch_down_id = -1;
}
nk_input_button(ctx, NK_BUTTON_LEFT, (int)ev->touch.x, (int)ev->touch.y, ev->type == ALLEGRO_EVENT_TOUCH_BEGIN);
} break;
case ALLEGRO_EVENT_TOUCH_MOVE: {
/* Only acknowledge movements of a single touch, we are
simulating a mouse cursor */
if (!allegro5.is_touch_down || allegro5.touch_down_id != ev->touch.id) {
return;
}
nk_input_motion(ctx, (int)ev->touch.x, (int)ev->touch.y);
} break;
case ALLEGRO_EVENT_KEY_DOWN:
case ALLEGRO_EVENT_KEY_UP: {
int kc = ev->keyboard.keycode;
int down = ev->type == ALLEGRO_EVENT_KEY_DOWN;
if (kc == ALLEGRO_KEY_LSHIFT || kc == ALLEGRO_KEY_RSHIFT) nk_input_key(ctx, NK_KEY_SHIFT, down);
else if (kc == ALLEGRO_KEY_DELETE) nk_input_key(ctx, NK_KEY_DEL, down);
else if (kc == ALLEGRO_KEY_ENTER) nk_input_key(ctx, NK_KEY_ENTER, down);
else if (kc == ALLEGRO_KEY_TAB) nk_input_key(ctx, NK_KEY_TAB, down);
else if (kc == ALLEGRO_KEY_LEFT) nk_input_key(ctx, NK_KEY_LEFT, down);
else if (kc == ALLEGRO_KEY_RIGHT) nk_input_key(ctx, NK_KEY_RIGHT, down);
else if (kc == ALLEGRO_KEY_UP) nk_input_key(ctx, NK_KEY_UP, down);
else if (kc == ALLEGRO_KEY_DOWN) nk_input_key(ctx, NK_KEY_DOWN, down);
else if (kc == ALLEGRO_KEY_BACKSPACE) nk_input_key(ctx, NK_KEY_BACKSPACE, down);
else if (kc == ALLEGRO_KEY_ESCAPE) nk_input_key(ctx, NK_KEY_TEXT_RESET_MODE, down);
else if (kc == ALLEGRO_KEY_PGUP) nk_input_key(ctx, NK_KEY_SCROLL_UP, down);
else if (kc == ALLEGRO_KEY_PGDN) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down);
else if (kc == ALLEGRO_KEY_HOME) {
nk_input_key(ctx, NK_KEY_TEXT_START, down);
nk_input_key(ctx, NK_KEY_SCROLL_START, down);
} else if (kc == ALLEGRO_KEY_END) {
nk_input_key(ctx, NK_KEY_TEXT_END, down);
nk_input_key(ctx, NK_KEY_SCROLL_END, down);
}
} break;
case ALLEGRO_EVENT_KEY_CHAR: {
int kc = ev->keyboard.keycode;
int control_mask = (ev->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) ||
(ev->keyboard.modifiers & ALLEGRO_KEYMOD_COMMAND);
if (kc == ALLEGRO_KEY_C && control_mask) {
nk_input_key(ctx, NK_KEY_COPY, 1);
} else if (kc == ALLEGRO_KEY_V && control_mask) {
nk_input_key(ctx, NK_KEY_PASTE, 1);
} else if (kc == ALLEGRO_KEY_X && control_mask) {
nk_input_key(ctx, NK_KEY_CUT, 1);
} else if (kc == ALLEGRO_KEY_Z && control_mask) {
nk_input_key(ctx, NK_KEY_TEXT_UNDO, 1);
} else if (kc == ALLEGRO_KEY_R && control_mask) {
nk_input_key(ctx, NK_KEY_TEXT_REDO, 1);
} else if (kc == ALLEGRO_KEY_A && control_mask) {
nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, 1);
}
else {
if (kc != ALLEGRO_KEY_BACKSPACE &&
kc != ALLEGRO_KEY_LEFT &&
kc != ALLEGRO_KEY_RIGHT &&
kc != ALLEGRO_KEY_UP &&
kc != ALLEGRO_KEY_DOWN &&
kc != ALLEGRO_KEY_HOME &&
kc != ALLEGRO_KEY_DELETE &&
kc != ALLEGRO_KEY_ENTER &&
kc != ALLEGRO_KEY_END &&
kc != ALLEGRO_KEY_ESCAPE &&
kc != ALLEGRO_KEY_PGDN &&
kc != ALLEGRO_KEY_PGUP) {
nk_input_unicode(ctx, ev->keyboard.unichar);
}
}
} break;
default: break;
}
}
NK_INTERN void
nk_allegro5_clipboard_paste(nk_handle usr, struct nk_text_edit *edit)
{
char *text = al_get_clipboard_text(allegro5.dsp);
if (text) nk_textedit_paste(edit, text, nk_strlen(text));
(void)usr;
al_free(text);
}
NK_INTERN void
nk_allegro5_clipboard_copy(nk_handle usr, const char *text, int len)
{
char *str = 0;
(void)usr;
if (!len) return;
str = (char*)malloc((size_t)len+1);
if (!str) return;
memcpy(str, text, (size_t)len);
str[len] = '\0';
al_set_clipboard_text(allegro5.dsp, str);
free(str);
}
NK_API struct nk_context*
nk_allegro5_init(NkAllegro5Font *allegro5font, ALLEGRO_DISPLAY *dsp,
unsigned int width, unsigned int height)
{
if (!al_init_primitives_addon()) {
fprintf(stdout, "Unable to initialize required allegro5 primitives addon\n");
exit(1);
}
struct nk_user_font *font = &allegro5font->nk;
font->userdata = nk_handle_ptr(allegro5font);
font->height = (float)allegro5font->height;
font->width = nk_allegro5_font_get_text_width;
allegro5.dsp = dsp;
allegro5.width = width;
allegro5.height = height;
allegro5.is_touch_down = 0;
allegro5.touch_down_id = -1;
nk_init_default(&allegro5.ctx, font);
allegro5.ctx.clip.copy = nk_allegro5_clipboard_copy;
allegro5.ctx.clip.paste = nk_allegro5_clipboard_paste;
allegro5.ctx.clip.userdata = nk_handle_ptr(0);
return &allegro5.ctx;
}
NK_API
void nk_allegro5_shutdown(void)
{
nk_free(&allegro5.ctx);
memset(&allegro5, 0, sizeof(allegro5));
}
#endif /* NK_ALLEGRO5_IMPLEMENTATION */

View File

@ -554,6 +554,7 @@ enum nk_keys {
NK_KEY_TEXT_END,
NK_KEY_TEXT_UNDO,
NK_KEY_TEXT_REDO,
NK_KEY_TEXT_SELECT_ALL,
NK_KEY_TEXT_WORD_LEFT,
NK_KEY_TEXT_WORD_RIGHT,
@ -11959,6 +11960,11 @@ retry:
state->has_preferred_x = 0;
break;
case NK_KEY_TEXT_SELECT_ALL:
nk_textedit_select_all(state);
state->has_preferred_x = 0;
break;
case NK_KEY_TEXT_INSERT_MODE:
if (state->mode == NK_TEXT_EDIT_MODE_VIEW)
state->mode = NK_TEXT_EDIT_MODE_INSERT;
@ -18714,7 +18720,7 @@ nk_spacing(struct nk_context *ctx, int cols)
{
struct nk_window *win;
struct nk_panel *layout;
struct nk_rect nil;
struct nk_rect none;
int i, index, rows;
NK_ASSERT(ctx);
@ -18738,7 +18744,7 @@ nk_spacing(struct nk_context *ctx, int cols)
if (layout->row.type != NK_LAYOUT_DYNAMIC_FIXED &&
layout->row.type != NK_LAYOUT_STATIC_FIXED) {
for (i = 0; i < cols; ++i)
nk_panel_alloc_space(&nil, ctx);
nk_panel_alloc_space(&none, ctx);
}
layout->row.index = index;
}