separating input state from app, so that it can be used in different threads that main thread. E.g. ui context uses its own input state that can be fed events in a second thread

This commit is contained in:
Martin Fouilleul 2023-04-19 11:28:20 +02:00
parent f1d6e0f15c
commit ab150f94f2
10 changed files with 582 additions and 512 deletions

348
src/input_state.c Normal file
View File

@ -0,0 +1,348 @@
/************************************************************//**
*
* @file: input_state.c
* @author: Martin Fouilleul
* @date: 19/04/2023
*
*****************************************************************/
#include"input_state.h"
//---------------------------------------------------------------
// Input state updating
//---------------------------------------------------------------
static void mp_update_key_state(mp_input_state* state, mp_key_state* key, mp_key_action action)
{
u64 frameCounter = state->frameCounter;
if(key->lastUpdate != frameCounter)
{
key->transitionCount = 0;
key->repeatCount = 0;
key->sysClicked = false;
key->sysDoubleClicked = false;
key->lastUpdate = frameCounter;
}
switch(action)
{
case MP_KEY_PRESS:
{
if(!key->down)
{
key->transitionCount++;
}
key->down = true;
} break;
case MP_KEY_REPEAT:
{
key->repeatCount++;
key->down = true;
} break;
case MP_KEY_RELEASE:
{
if(key->down)
{
key->transitionCount++;
}
key->down = false;
} break;
default:
break;
}
}
static void mp_update_key_mods(mp_input_state* state, mp_keymod_flags mods)
{
state->keyboard.mods = mods;
}
static void mp_update_mouse_move(mp_input_state* state, f32 x, f32 y, f32 deltaX, f32 deltaY)
{
u64 frameCounter = state->frameCounter;
mp_mouse_state* mouse = &state->mouse;
if(mouse->lastUpdate != frameCounter)
{
mouse->delta = (vec2){0, 0};
mouse->wheel = (vec2){0, 0};
mouse->lastUpdate = frameCounter;
}
mouse->pos = (vec2){x, y};
mouse->delta.x += deltaX;
mouse->delta.y += deltaY;
}
static void mp_update_mouse_wheel(mp_input_state* state, f32 deltaX, f32 deltaY)
{
u64 frameCounter = state->frameCounter;
mp_mouse_state* mouse = &state->mouse;
if(mouse->lastUpdate != frameCounter)
{
mouse->delta = (vec2){0, 0};
mouse->wheel = (vec2){0, 0};
mouse->lastUpdate = frameCounter;
}
mouse->wheel.x += deltaX;
mouse->wheel.y += deltaY;
}
static void mp_update_text(mp_input_state* state, utf32 codepoint)
{
u64 frameCounter = state->frameCounter;
mp_text_state* text = &state->text;
if(text->lastUpdate != frameCounter)
{
text->codePoints.len = 0;
text->lastUpdate = frameCounter;
}
text->codePoints.ptr = text->backing;
if(text->codePoints.len < MP_INPUT_TEXT_BACKING_SIZE)
{
text->codePoints.ptr[text->codePoints.len] = codepoint;
text->codePoints.len++;
}
else
{
log_warning("too many input codepoints per frame, dropping input");
}
}
void mp_input_next_frame(mp_input_state* state)
{
state->frameCounter++;
}
void mp_input_process_event(mp_input_state* state, mp_event event)
{
switch(event.type)
{
case MP_EVENT_KEYBOARD_KEY:
{
mp_key_state* key = &state->keyboard.keys[event.key.code];
mp_update_key_state(state, key, event.key.action);
mp_update_key_mods(state, event.key.mods);
} break;
case MP_EVENT_KEYBOARD_CHAR:
mp_update_text(state, event.character.codepoint);
break;
case MP_EVENT_KEYBOARD_MODS:
mp_update_key_mods(state, event.key.mods);
break;
case MP_EVENT_MOUSE_MOVE:
mp_update_mouse_move(state, event.move.x, event.move.y, event.move.deltaX, event.move.deltaY);
break;
case MP_EVENT_MOUSE_WHEEL:
mp_update_mouse_wheel(state, event.move.deltaX, event.move.deltaY);
break;
case MP_EVENT_MOUSE_BUTTON:
{
mp_key_state* key = &state->mouse.buttons[event.key.code];
mp_update_key_state(state, key, event.key.action);
if(event.key.action == MP_KEY_PRESS)
{
if(event.key.clickCount >= 1)
{
key->sysClicked = true;
}
if(event.key.clickCount >= 2)
{
key->sysDoubleClicked = true;
}
}
mp_update_key_mods(state, event.key.mods);
} break;
default:
break;
}
}
//--------------------------------------------------------------------
// Input state polling
//--------------------------------------------------------------------
mp_key_state mp_key_get_state(mp_input_state* input, mp_key_code key)
{
mp_key_state state = {0};
if(key <= MP_KEY_COUNT)
{
state = input->keyboard.keys[key];
}
return(state);
}
mp_key_state mp_mouse_button_get_state(mp_input_state* input, mp_mouse_button button)
{
mp_key_state state = {0};
if(button <= MP_MOUSE_BUTTON_COUNT)
{
state = input->mouse.buttons[button];
}
return(state);
}
int mp_key_state_press_count(mp_input_state* input, mp_key_state* key)
{
int count = 0;
if(key->lastUpdate == input->frameCounter)
{
count = key->transitionCount / 2;
if(key->down)
{
//NOTE: add one if state is down transition count is odd
count += (key->transitionCount & 0x01);
}
}
return(count);
}
int mp_key_state_release_count(mp_input_state* input, mp_key_state* key)
{
int count = 0;
if(key->lastUpdate == input->frameCounter)
{
count = key->transitionCount / 2;
if(!key->down)
{
//NOTE: add one if state is up and transition count is odd
count += (key->transitionCount & 0x01);
}
}
return(count);
}
int mp_key_state_repeat_count(mp_input_state* input, mp_key_state* key)
{
int count = 0;
if(key->lastUpdate == input->frameCounter)
{
count = key->repeatCount;
}
return(count);
}
bool mp_key_down(mp_input_state* input, mp_key_code key)
{
mp_key_state state = mp_key_get_state(input, key);
return(state.down);
}
int mp_key_pressed(mp_input_state* input, mp_key_code key)
{
mp_key_state state = mp_key_get_state(input, key);
int res = mp_key_state_press_count(input, &state);
return(res);
}
int mp_key_released(mp_input_state* input, mp_key_code key)
{
mp_key_state state = mp_key_get_state(input, key);
int res = mp_key_state_release_count(input, &state);
return(res);
}
int mp_key_repeated(mp_input_state* input, mp_key_code key)
{
mp_key_state state = mp_key_get_state(input, key);
int res = mp_key_state_repeat_count(input, &state);
return(res);
}
bool mp_mouse_down(mp_input_state* input, mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(input, button);
return(state.down);
}
int mp_mouse_pressed(mp_input_state* input, mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(input, button);
int res = mp_key_state_press_count(input, &state);
return(res);
}
int mp_mouse_released(mp_input_state* input, mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(input, button);
int res = mp_key_state_release_count(input, &state);
return(res);
}
bool mp_mouse_clicked(mp_input_state* input, mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(input, button);
bool clicked = state.sysClicked && (state.lastUpdate == input->frameCounter);
return(clicked);
}
bool mp_mouse_double_clicked(mp_input_state* input, mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(input, button);
bool doubleClicked = state.sysClicked && (state.lastUpdate == input->frameCounter);
return(doubleClicked);
}
mp_keymod_flags mp_key_mods(mp_input_state* input)
{
return(input->keyboard.mods);
}
vec2 mp_mouse_position(mp_input_state* input)
{
return(input->mouse.pos);
}
vec2 mp_mouse_delta(mp_input_state* input)
{
if(input->mouse.lastUpdate == input->frameCounter)
{
return(input->mouse.delta);
}
else
{
return((vec2){0, 0});
}
}
vec2 mp_mouse_wheel(mp_input_state* input)
{
if(input->mouse.lastUpdate == input->frameCounter)
{
return(input->mouse.wheel);
}
else
{
return((vec2){0, 0});
}
}
str32 mp_input_text_utf32(mp_input_state* input, mem_arena* arena)
{
str32 res = {0};
if(input->text.lastUpdate == input->frameCounter)
{
res = str32_push_copy(arena, input->text.codePoints);
}
return(res);
}
str8 mp_input_text_utf8(mp_input_state* input, mem_arena* arena)
{
str8 res = {0};
if(input->text.lastUpdate == input->frameCounter)
{
res = utf8_push_from_codepoints(arena, input->text.codePoints);
}
return(res);
}

96
src/input_state.h Normal file
View File

@ -0,0 +1,96 @@
/************************************************************//**
*
* @file: input_state.h
* @author: Martin Fouilleul
* @date: 19/04/2023
*
*****************************************************************/
#ifndef __INPUT_STATE_H_
#define __INPUT_STATE_H_
#include"platform/platform.h"
#include"util/typedefs.h"
#include"util/strings.h"
#include"util/utf8.h"
#include"mp_app.h"
typedef struct mp_key_state
{
u64 lastUpdate;
u32 transitionCount;
u32 repeatCount;
bool down;
bool sysClicked;
bool sysDoubleClicked;
} mp_key_state;
typedef struct mp_keyboard_state
{
mp_key_state keys[MP_KEY_COUNT];
mp_keymod_flags mods;
} mp_keyboard_state;
typedef struct mp_mouse_state
{
u64 lastUpdate;
bool posValid;
vec2 pos;
vec2 delta;
vec2 wheel;
union
{
mp_key_state buttons[MP_MOUSE_BUTTON_COUNT];
struct
{
mp_key_state left;
mp_key_state right;
mp_key_state middle;
mp_key_state ext1;
mp_key_state ext2;
};
};
} mp_mouse_state;
enum { MP_INPUT_TEXT_BACKING_SIZE = 64 };
typedef struct mp_text_state
{
u64 lastUpdate;
utf32 backing[MP_INPUT_TEXT_BACKING_SIZE];
str32 codePoints;
} mp_text_state;
typedef struct mp_input_state
{
u64 frameCounter;
mp_keyboard_state keyboard;
mp_mouse_state mouse;
mp_text_state text;
} mp_input_state;
MP_API void mp_input_process_event(mp_input_state* state, mp_event event);
MP_API void mp_input_next_frame(mp_input_state* state);
MP_API bool mp_key_down(mp_input_state* state, mp_key_code key);
MP_API int mp_key_pressed(mp_input_state* state, mp_key_code key);
MP_API int mp_key_released(mp_input_state* state, mp_key_code key);
MP_API int mp_key_repeated(mp_input_state* state, mp_key_code key);
MP_API bool mp_mouse_down(mp_input_state* state, mp_mouse_button button);
MP_API int mp_mouse_pressed(mp_input_state* state, mp_mouse_button button);
MP_API int mp_mouse_released(mp_input_state* state, mp_mouse_button button);
MP_API bool mp_mouse_clicked(mp_input_state* state, mp_mouse_button button);
MP_API bool mp_mouse_double_clicked(mp_input_state* state, mp_mouse_button button);
MP_API vec2 mp_mouse_position(mp_input_state* state);
MP_API vec2 mp_mouse_delta(mp_input_state* state);
MP_API vec2 mp_mouse_wheel(mp_input_state* state);
MP_API str32 mp_input_text_utf32(mp_input_state* state, mem_arena* arena);
MP_API str8 mp_input_text_utf8(mp_input_state* state, mem_arena* arena);
MP_API mp_keymod_flags mp_key_mods(mp_input_state* state);
#endif //__INPUT_STATE_H_

View File

@ -75,4 +75,5 @@
#error "Unsupported platform"
#endif
#include"input_state.c"
#include"ui.c"

View File

@ -37,6 +37,7 @@
//----------------------------------------------------------------
#include"mp_app.h"
#include"graphics.h"
#include"input_state.h"
#include"ui.h"
#ifdef MG_INCLUDE_GL_API

View File

@ -122,286 +122,3 @@ bool mp_next_event(mp_event* event)
return(false);
}
}
//---------------------------------------------------------------
// Input state updating
//---------------------------------------------------------------
static void mp_update_key_state(mp_key_state* key, mp_key_action action)
{
u64 frameCounter = __mpApp.inputState.frameCounter;
if(key->lastUpdate != frameCounter)
{
key->transitionCount = 0;
key->repeatCount = 0;
key->sysClicked = false;
key->sysDoubleClicked = false;
key->lastUpdate = frameCounter;
}
switch(action)
{
case MP_KEY_PRESS:
{
if(!key->down)
{
key->transitionCount++;
}
key->down = true;
} break;
case MP_KEY_REPEAT:
{
key->repeatCount++;
key->down = true;
} break;
case MP_KEY_RELEASE:
{
if(key->down)
{
key->transitionCount++;
}
key->down = false;
} break;
default:
break;
}
}
static void mp_update_key_mods(mp_keymod_flags mods)
{
__mpApp.inputState.keyboard.mods = mods;
}
static void mp_update_mouse_move(f32 x, f32 y, f32 deltaX, f32 deltaY)
{
u64 frameCounter = __mpApp.inputState.frameCounter;
mp_mouse_state* mouse = &__mpApp.inputState.mouse;
if(mouse->lastUpdate != frameCounter)
{
mouse->delta = (vec2){0, 0};
mouse->wheel = (vec2){0, 0};
mouse->lastUpdate = frameCounter;
}
mouse->pos = (vec2){x, y};
mouse->delta.x += deltaX;
mouse->delta.y += deltaY;
}
static void mp_update_mouse_wheel(f32 deltaX, f32 deltaY)
{
u64 frameCounter = __mpApp.inputState.frameCounter;
mp_mouse_state* mouse = &__mpApp.inputState.mouse;
if(mouse->lastUpdate != frameCounter)
{
mouse->delta = (vec2){0, 0};
mouse->wheel = (vec2){0, 0};
mouse->lastUpdate = frameCounter;
}
mouse->wheel.x += deltaX;
mouse->wheel.y += deltaY;
}
static void mp_update_text(utf32 codepoint)
{
u64 frameCounter = __mpApp.inputState.frameCounter;
mp_text_state* text = &__mpApp.inputState.text;
if(text->lastUpdate != frameCounter)
{
text->codePoints.len = 0;
text->lastUpdate = frameCounter;
}
text->codePoints.ptr = text->backing;
if(text->codePoints.len < MP_INPUT_TEXT_BACKING_SIZE)
{
text->codePoints.ptr[text->codePoints.len] = codepoint;
text->codePoints.len++;
}
else
{
log_warning("too many input codepoints per frame, dropping input");
}
}
//--------------------------------------------------------------------
// Input state polling
//--------------------------------------------------------------------
mp_key_state mp_key_get_state(mp_key_code key)
{
mp_key_state state = {0};
if(key <= MP_KEY_COUNT)
{
state = __mpApp.inputState.keyboard.keys[key];
}
return(state);
}
mp_key_state mp_mouse_button_get_state(mp_mouse_button button)
{
mp_key_state state = {0};
if(button <= MP_MOUSE_BUTTON_COUNT)
{
state = __mpApp.inputState.mouse.buttons[button];
}
return(state);
}
int mp_key_state_press_count(mp_key_state* key)
{
int count = 0;
if(key->lastUpdate == __mpApp.inputState.frameCounter)
{
count = key->transitionCount / 2;
if(key->down)
{
//NOTE: add one if state is down transition count is odd
count += (key->transitionCount & 0x01);
}
}
return(count);
}
int mp_key_state_release_count(mp_key_state* key)
{
int count = 0;
if(key->lastUpdate == __mpApp.inputState.frameCounter)
{
count = key->transitionCount / 2;
if(!key->down)
{
//NOTE: add one if state is up and transition count is odd
count += (key->transitionCount & 0x01);
}
}
return(count);
}
int mp_key_state_repeat_count(mp_key_state* key)
{
int count = 0;
if(key->lastUpdate == __mpApp.inputState.frameCounter)
{
count = key->repeatCount;
}
return(count);
}
bool mp_key_down(mp_key_code key)
{
mp_key_state state = mp_key_get_state(key);
return(state.down);
}
int mp_key_pressed(mp_key_code key)
{
mp_key_state state = mp_key_get_state(key);
int res = mp_key_state_press_count(&state);
return(res);
}
int mp_key_released(mp_key_code key)
{
mp_key_state state = mp_key_get_state(key);
int res = mp_key_state_release_count(&state);
return(res);
}
int mp_key_repeated(mp_key_code key)
{
mp_key_state state = mp_key_get_state(key);
int res = mp_key_state_repeat_count(&state);
return(res);
}
bool mp_mouse_down(mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(button);
return(state.down);
}
int mp_mouse_pressed(mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(button);
int res = mp_key_state_press_count(&state);
return(res);
}
int mp_mouse_released(mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(button);
int res = mp_key_state_release_count(&state);
return(res);
}
bool mp_mouse_clicked(mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(button);
bool clicked = state.sysClicked && (state.lastUpdate == __mpApp.inputState.frameCounter);
return(clicked);
}
bool mp_mouse_double_clicked(mp_mouse_button button)
{
mp_key_state state = mp_mouse_button_get_state(button);
bool doubleClicked = state.sysClicked && (state.lastUpdate == __mpApp.inputState.frameCounter);
return(doubleClicked);
}
mp_keymod_flags mp_key_mods()
{
return(__mpApp.inputState.keyboard.mods);
}
vec2 mp_mouse_position()
{
return(__mpApp.inputState.mouse.pos);
}
vec2 mp_mouse_delta()
{
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
{
return(__mpApp.inputState.mouse.delta);
}
else
{
return((vec2){0, 0});
}
}
vec2 mp_mouse_wheel()
{
if(__mpApp.inputState.mouse.lastUpdate == __mpApp.inputState.frameCounter)
{
return(__mpApp.inputState.mouse.wheel);
}
else
{
return((vec2){0, 0});
}
}
str32 mp_input_text_utf32(mem_arena* arena)
{
str32 res = {0};
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
{
res = str32_push_copy(arena, __mpApp.inputState.text.codePoints);
}
return(res);
}
str8 mp_input_text_utf8(mem_arena* arena)
{
str8 res = {0};
if(__mpApp.inputState.text.lastUpdate == __mpApp.inputState.frameCounter)
{
res = utf8_push_from_codepoints(arena, __mpApp.inputState.text.codePoints);
}
return(res);
}

View File

@ -319,30 +319,6 @@ MP_API void mp_window_center(mp_window window);
MP_API mp_rect mp_window_content_rect_for_frame_rect(mp_rect frameRect, mp_window_style style);
MP_API mp_rect mp_window_frame_rect_for_content_rect(mp_rect contentRect, mp_window_style style);
//--------------------------------------------------------------------
// Input state polling
//--------------------------------------------------------------------
MP_API bool mp_key_down(mp_key_code key);
MP_API int mp_key_pressed(mp_key_code key);
MP_API int mp_key_released(mp_key_code key);
MP_API int mp_key_repeated(mp_key_code key);
MP_API bool mp_mouse_down(mp_mouse_button button);
MP_API int mp_mouse_pressed(mp_mouse_button button);
MP_API int mp_mouse_released(mp_mouse_button button);
MP_API bool mp_mouse_clicked(mp_mouse_button button);
MP_API bool mp_mouse_double_clicked(mp_mouse_button button);
MP_API vec2 mp_mouse_position(void);
MP_API vec2 mp_mouse_delta(void);
MP_API vec2 mp_mouse_wheel(void);
MP_API str32 mp_input_text_utf32(mem_arena* arena);
MP_API str8 mp_input_text_utf8(mem_arena* arena);
MP_API mp_keymod_flags mp_key_mods();
//--------------------------------------------------------------------
// Clipboard
//--------------------------------------------------------------------

View File

@ -22,72 +22,6 @@
#error "platform not supported yet"
#endif
//---------------------------------------------------------------
// Input State
//---------------------------------------------------------------
typedef struct mp_key_utf8
{
u8 labelLen;
char label[8];
} mp_key_utf8;
typedef struct mp_key_state
{
u64 lastUpdate;
u32 transitionCount;
u32 repeatCount;
bool down;
bool sysClicked;
bool sysDoubleClicked;
} mp_key_state;
typedef struct mp_keyboard_state
{
mp_key_state keys[MP_KEY_COUNT];
mp_keymod_flags mods;
} mp_keyboard_state;
typedef struct mp_mouse_state
{
u64 lastUpdate;
bool posValid;
vec2 pos;
vec2 delta;
vec2 wheel;
union
{
mp_key_state buttons[MP_MOUSE_BUTTON_COUNT];
struct
{
mp_key_state left;
mp_key_state right;
mp_key_state middle;
mp_key_state ext1;
mp_key_state ext2;
};
};
} mp_mouse_state;
enum { MP_INPUT_TEXT_BACKING_SIZE = 64 };
typedef struct mp_text_state
{
u64 lastUpdate;
utf32 backing[MP_INPUT_TEXT_BACKING_SIZE];
str32 codePoints;
} mp_text_state;
typedef struct mp_input_state
{
u64 frameCounter;
mp_keyboard_state keyboard;
mp_mouse_state mouse;
mp_text_state text;
} mp_input_state;
//---------------------------------------------------------------
// Window structure
//---------------------------------------------------------------
@ -121,6 +55,13 @@ typedef struct mp_window_data
// Global App State
//---------------------------------------------------------------
typedef struct mp_key_utf8
{
u8 labelLen;
char label[8];
} mp_key_utf8;
enum { MP_APP_MAX_WINDOWS = 128 };
typedef struct mp_app
@ -142,8 +83,6 @@ typedef struct mp_app
mp_live_resize_callback liveResizeCallback;
void* liveResizeData;
mp_input_state inputState;
mp_key_utf8 keyLabels[512];
int keyCodes[512];
int nativeKeys[MP_KEY_COUNT];

View File

@ -804,19 +804,6 @@ static void mp_process_mouse_button(NSEvent* nsEvent, mp_window_data* window, mp
event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]);
event.key.clickCount = [nsEvent clickCount];
mp_key_state* keyState = &__mpApp.inputState.mouse.buttons[event.key.code];
mp_update_key_state(keyState, action);
if(action == MP_KEY_PRESS)
{
if(event.key.clickCount >= 1)
{
keyState->sysClicked = true;
}
if(event.key.clickCount >= 2)
{
keyState->sysDoubleClicked = true;
}
}
mp_queue_event(&event);
}
@ -870,8 +857,6 @@ static void mp_process_mouse_button(NSEvent* nsEvent, mp_window_data* window, mp
event.move.deltaY = [nsEvent deltaY];
event.move.mods = mp_convert_osx_mods([nsEvent modifierFlags]);
mp_update_mouse_move(event.move.x, event.move.y, event.move.deltaX, event.move.deltaY);
mp_queue_event(&event);
}
@ -888,8 +873,6 @@ static void mp_process_mouse_button(NSEvent* nsEvent, mp_window_data* window, mp
event.move.deltaY = -[nsEvent scrollingDeltaY]*factor;
event.move.mods = mp_convert_osx_mods([nsEvent modifierFlags]);
mp_update_mouse_wheel(event.move.deltaX, event.move.deltaY);
mp_queue_event(&event);
}
@ -924,8 +907,6 @@ static void mp_process_mouse_button(NSEvent* nsEvent, mp_window_data* window, mp
event.key.labelLen = label.len;
memcpy(event.key.label, label.ptr, label.len);
mp_update_key_state(&__mpApp.inputState.keyboard.keys[event.key.code], action);
mp_queue_event(&event);
[self interpretKeyEvents:@[nsEvent]];
@ -940,8 +921,6 @@ static void mp_process_mouse_button(NSEvent* nsEvent, mp_window_data* window, mp
event.key.code = mp_convert_osx_key([nsEvent keyCode]);
event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]);
mp_update_key_state(&__mpApp.inputState.keyboard.keys[event.key.code], MP_KEY_RELEASE);
mp_queue_event(&event);
}
@ -952,8 +931,6 @@ static void mp_process_mouse_button(NSEvent* nsEvent, mp_window_data* window, mp
event.type = MP_EVENT_KEYBOARD_MODS;
event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]);
mp_update_key_mods(event.key.mods);
mp_queue_event(&event);
}
@ -1092,8 +1069,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
str8 seq = utf8_encode(event.character.sequence, event.character.codepoint);
event.character.seqLen = seq.len;
mp_update_text(codepoint);
mp_queue_event(&event);
}
}
@ -1956,8 +1931,6 @@ void mp_set_live_resize_callback(mp_live_resize_callback callback, void* data)
void mp_pump_events(f64 timeout)
{
__mpApp.inputState.frameCounter++;
@autoreleasepool
{
bool accumulate = false;
@ -2037,7 +2010,7 @@ void mp_end_input_frame()
//////////////////////////////////////////////////////////////////////////////////////////////
//TODO: make sure we call arena clear once per event frame, even when using runloop etc...
//////////////////////////////////////////////////////////////////////////////////////////////
__mpApp.inputState.frameCounter++;
mem_arena_clear(&__mpApp.eventArena);
}

155
src/ui.c
View File

@ -28,71 +28,6 @@ static ui_style UI_STYLE_DEFAULTS =
.color = {0, 0, 0, 1},
.fontSize = 16,
};
//-----------------------------------------------------------------------------
// context
//-----------------------------------------------------------------------------
enum { UI_MAX_INPUT_CHAR_PER_FRAME = 64 };
typedef struct ui_input_text
{
u8 count;
utf32 codePoints[UI_MAX_INPUT_CHAR_PER_FRAME];
} ui_input_text;
typedef struct ui_stack_elt ui_stack_elt;
struct ui_stack_elt
{
ui_stack_elt* parent;
union
{
ui_box* box;
ui_size size;
mp_rect clip;
};
};
typedef struct ui_tag_elt
{
list_elt listElt;
ui_tag tag;
} ui_tag_elt;
enum { UI_BOX_MAP_BUCKET_COUNT = 1024 };
typedef struct ui_context
{
bool init;
u64 frameCounter;
f64 frameTime;
f64 lastFrameDuration;
mem_arena frameArena;
mem_pool boxPool;
list_info boxMap[UI_BOX_MAP_BUCKET_COUNT];
ui_box* root;
ui_box* overlay;
list_info overlayList;
ui_stack_elt* boxStack;
ui_stack_elt* clipStack;
list_info nextBoxBeforeRules;
list_info nextBoxAfterRules;
list_info nextBoxTags;
u32 z;
ui_box* hovered;
ui_box* focus;
i32 editCursor;
i32 editMark;
i32 editFirstDisplayedChar;
f64 editCursorBlinkStart;
} ui_context;
mp_thread_local ui_context __uiThreadContext = {0};
mp_thread_local ui_context* __uiCurrentContext = 0;
@ -388,6 +323,37 @@ void ui_style_box_after(ui_box* box, ui_pattern pattern, ui_style* style, ui_sty
}
}
//-----------------------------------------------------------------------------
// input
//-----------------------------------------------------------------------------
void ui_process_event(mp_event event)
{
ui_context* ui = ui_get_context();
mp_input_process_event(&ui->input, event);
}
vec2 ui_mouse_position(void)
{
ui_context* ui = ui_get_context();
vec2 mousePos = mp_mouse_position(&ui->input);
return(mousePos);
}
vec2 ui_mouse_delta(void)
{
ui_context* ui = ui_get_context();
vec2 delta = mp_mouse_delta(&ui->input);
return(delta);
}
vec2 ui_mouse_wheel(void)
{
ui_context* ui = ui_get_context();
vec2 delta = mp_mouse_wheel(&ui->input);
return(delta);
}
//-----------------------------------------------------------------------------
// ui boxes
//-----------------------------------------------------------------------------
@ -411,26 +377,6 @@ bool ui_box_hovering(ui_box* box, vec2 p)
return(result);
}
vec2 ui_mouse_position(void)
{
vec2 mousePos = mp_mouse_position();
return(mousePos);
}
vec2 ui_mouse_delta(void)
{
ui_context* ui = ui_get_context();
vec2 delta = mp_mouse_delta();
return(delta);
}
vec2 ui_mouse_wheel(void)
{
ui_context* ui = ui_get_context();
vec2 delta = mp_mouse_wheel();
return(delta);
}
ui_box* ui_box_make_str8(str8 string, ui_flags flags)
{
ui_context* ui = ui_get_context();
@ -569,6 +515,9 @@ ui_sig ui_box_sig(ui_box* box)
//NOTE: compute input signals
ui_sig sig = {0};
ui_context* ui = ui_get_context();
mp_input_state* input = &ui->input;
sig.box = box;
if(!box->closed && !box->parentClosed)
@ -581,16 +530,16 @@ ui_sig ui_box_sig(ui_box* box)
{
if(sig.hovering)
{
sig.pressed = mp_mouse_pressed(MP_MOUSE_LEFT);
sig.pressed = mp_mouse_pressed(input, MP_MOUSE_LEFT);
if(sig.pressed)
{
box->dragging = true;
}
sig.doubleClicked = mp_mouse_double_clicked(MP_MOUSE_LEFT);
sig.rightPressed = mp_mouse_pressed(MP_MOUSE_RIGHT);
sig.doubleClicked = mp_mouse_double_clicked(input, MP_MOUSE_LEFT);
sig.rightPressed = mp_mouse_pressed(input, MP_MOUSE_RIGHT);
}
sig.released = mp_mouse_released(MP_MOUSE_LEFT);
sig.released = mp_mouse_released(input, MP_MOUSE_LEFT);
if(sig.released)
{
if(box->dragging && sig.hovering)
@ -599,7 +548,7 @@ ui_sig ui_box_sig(ui_box* box)
}
}
if(!mp_mouse_down(MP_MOUSE_LEFT))
if(!mp_mouse_down(input, MP_MOUSE_LEFT))
{
box->dragging = false;
}
@ -1513,15 +1462,14 @@ void ui_end_frame(void)
}
}
}
mp_input_next_frame(&ui->input);
}
//-----------------------------------------------------------------------------
// Init / cleanup
//-----------------------------------------------------------------------------
void ui_init(void)
{
ui_context* ui = &__uiThreadContext;
if(!ui->init)
void ui_init(ui_context* ui)
{
__uiCurrentContext = &__uiThreadContext;
@ -1529,7 +1477,8 @@ void ui_init(void)
mem_arena_init(&ui->frameArena);
mem_pool_init(&ui->boxPool, sizeof(ui_box));
ui->init = true;
}
ui_set_context(ui);
}
void ui_cleanup(void)
@ -1985,7 +1934,8 @@ void ui_menu_bar_begin(const char* name)
ui_box* bar = ui_box_begin(name, UI_FLAG_DRAW_BACKGROUND);
ui_sig sig = ui_box_sig(bar);
if(!sig.hovering && mp_mouse_released(MP_MOUSE_LEFT))
ui_context* ui = ui_get_context();
if(!sig.hovering && mp_mouse_released(&ui->input, MP_MOUSE_LEFT))
{
ui_box_deactivate(bar);
}
@ -2247,7 +2197,8 @@ ui_select_popup_info ui_select_popup(const char* name, ui_select_popup_info* inf
}
ui_box_pop();
if(ui_box_active(panel) && mp_mouse_pressed(MP_MOUSE_LEFT))
ui_context* ui = ui_get_context();
if(ui_box_active(panel) && mp_mouse_pressed(&ui->input, MP_MOUSE_LEFT))
{
ui_box_deactivate(panel);
}
@ -2753,7 +2704,7 @@ ui_text_box_result ui_text_box(const char* name, mem_arena* arena, str8 text)
}
//NOTE: set the new cursor, and set or leave the mark depending on mode
ui->editCursor = newCursor;
if(sig.pressed && !(mp_key_mods() & MP_KEYMOD_SHIFT))
if(sig.pressed && !(mp_key_mods(&ui->input) & MP_KEYMOD_SHIFT))
{
ui->editMark = ui->editCursor;
}
@ -2783,7 +2734,7 @@ ui_text_box_result ui_text_box(const char* name, mem_arena* arena, str8 text)
ui->editMark = Clamp(ui->editMark, 0, codepoints.len);
//NOTE replace selection with input codepoints
str32 input = mp_input_text_utf32(&ui->frameArena);
str32 input = mp_input_text_utf32(&ui->input, &ui->frameArena);
if(input.len)
{
codepoints = ui_edit_replace_selection_with_codepoints(ui, codepoints, input);
@ -2791,13 +2742,13 @@ ui_text_box_result ui_text_box(const char* name, mem_arena* arena, str8 text)
}
//NOTE handle shortcuts
mp_keymod_flags mods = mp_key_mods();
mp_keymod_flags mods = mp_key_mods(&ui->input);
for(int i=0; i<UI_EDIT_COMMAND_COUNT; i++)
{
const ui_edit_command* command = &(UI_EDIT_COMMANDS[i]);
if( (mp_key_pressed(command->key) || mp_key_repeated(command->key))
if( (mp_key_pressed(&ui->input, command->key) || mp_key_repeated(&ui->input, command->key))
&& mods == command->mods)
{
codepoints = ui_edit_perform_operation(ui, command->operation, command->move, command->direction, codepoints);
@ -2812,7 +2763,7 @@ ui_text_box_result ui_text_box(const char* name, mem_arena* arena, str8 text)
result.text = utf8_push_from_codepoints(arena, codepoints);
}
if(mp_key_pressed(MP_KEY_ENTER))
if(mp_key_pressed(&ui->input, MP_KEY_ENTER))
{
//TODO(martin): extract in gui_edit_complete() (and use below)
result.accepted = true;

View File

@ -11,6 +11,7 @@
#include"typedefs.h"
#include"lists.h"
#include"input_state.h"
#include"graphics.h"
#ifdef __cplusplus
@ -319,15 +320,82 @@ struct ui_box
f32 activeTransition;
};
typedef struct ui_context ui_context;
//-----------------------------------------------------------------------------
// context
//-----------------------------------------------------------------------------
enum { UI_MAX_INPUT_CHAR_PER_FRAME = 64 };
typedef struct ui_input_text
{
u8 count;
utf32 codePoints[UI_MAX_INPUT_CHAR_PER_FRAME];
} ui_input_text;
typedef struct ui_stack_elt ui_stack_elt;
struct ui_stack_elt
{
ui_stack_elt* parent;
union
{
ui_box* box;
ui_size size;
mp_rect clip;
};
};
typedef struct ui_tag_elt
{
list_elt listElt;
ui_tag tag;
} ui_tag_elt;
enum { UI_BOX_MAP_BUCKET_COUNT = 1024 };
typedef struct ui_context
{
bool init;
mp_input_state input;
u64 frameCounter;
f64 frameTime;
f64 lastFrameDuration;
mem_arena frameArena;
mem_pool boxPool;
list_info boxMap[UI_BOX_MAP_BUCKET_COUNT];
ui_box* root;
ui_box* overlay;
list_info overlayList;
ui_stack_elt* boxStack;
ui_stack_elt* clipStack;
list_info nextBoxBeforeRules;
list_info nextBoxAfterRules;
list_info nextBoxTags;
u32 z;
ui_box* hovered;
ui_box* focus;
i32 editCursor;
i32 editMark;
i32 editFirstDisplayedChar;
f64 editCursorBlinkStart;
} ui_context;
//-------------------------------------------------------------------------------------
// UI context initialization and frame cycle
//-------------------------------------------------------------------------------------
MP_API void ui_init(void);
MP_API void ui_init(ui_context* context);
MP_API ui_context* ui_get_context(void);
MP_API void ui_set_context(ui_context* context);
MP_API void ui_process_event(mp_event event);
MP_API void ui_begin_frame(ui_style* defaultStyle, ui_style_mask mask);
MP_API void ui_end_frame(void);
MP_API void ui_draw(void);