Support double and triple click text selection
Higher order click processing courtesy of https://devblogs.microsoft.com/oldnewthing/20041018-00/?p=37543
This commit is contained in:
parent
5a7182a3b9
commit
18dff15cf1
|
@ -15,7 +15,6 @@ ORCA_EXPORT void oc_on_init(void)
|
||||||
surface = oc_surface_canvas();
|
surface = oc_surface_canvas();
|
||||||
canvas = oc_canvas_create();
|
canvas = oc_canvas_create();
|
||||||
oc_ui_init(&ui);
|
oc_ui_init(&ui);
|
||||||
oc_ui_set_theme(&OC_UI_LIGHT_THEME);
|
|
||||||
|
|
||||||
//NOTE: load font
|
//NOTE: load font
|
||||||
{
|
{
|
||||||
|
|
|
@ -233,7 +233,31 @@ static void oc_win32_process_mouse_event(oc_window_data* window, oc_key_action a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO click/double click
|
if(action == OC_KEY_PRESS)
|
||||||
|
{
|
||||||
|
u32 clickTime = GetMessageTime();
|
||||||
|
if(clickTime - oc_appData.win32.lastClickTime[button] > GetDoubleClickTime())
|
||||||
|
{
|
||||||
|
oc_appData.win32.clickCount[button] = 0;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < OC_MOUSE_BUTTON_COUNT; i++)
|
||||||
|
{
|
||||||
|
if(i != button)
|
||||||
|
{
|
||||||
|
oc_appData.win32.clickCount[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oc_appData.win32.lastClickTime[button] = clickTime;
|
||||||
|
oc_appData.win32.clickCount[button]++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 clickTime = GetMessageTime();
|
||||||
|
if(clickTime - oc_appData.win32.lastClickTime[button] > GetDoubleClickTime())
|
||||||
|
{
|
||||||
|
oc_appData.win32.clickCount[button] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
oc_event event = { 0 };
|
oc_event event = { 0 };
|
||||||
event.window = oc_window_handle_from_ptr(window);
|
event.window = oc_window_handle_from_ptr(window);
|
||||||
|
@ -241,6 +265,7 @@ static void oc_win32_process_mouse_event(oc_window_data* window, oc_key_action a
|
||||||
event.key.action = action;
|
event.key.action = action;
|
||||||
event.key.code = button;
|
event.key.code = button;
|
||||||
event.key.mods = oc_get_mod_keys();
|
event.key.mods = oc_get_mod_keys();
|
||||||
|
event.key.clickCount = oc_appData.win32.clickCount[button];
|
||||||
|
|
||||||
oc_queue_event(&event);
|
oc_queue_event(&event);
|
||||||
}
|
}
|
||||||
|
@ -311,6 +336,15 @@ LRESULT oc_win32_win_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM
|
||||||
|
|
||||||
switch(message)
|
switch(message)
|
||||||
{
|
{
|
||||||
|
case WM_ACTIVATE:
|
||||||
|
{
|
||||||
|
for(int i = 0; i < OC_MOUSE_BUTTON_COUNT; i++)
|
||||||
|
{
|
||||||
|
oc_appData.win32.clickCount[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
{
|
{
|
||||||
oc_event event = { 0 };
|
oc_event event = { 0 };
|
||||||
|
@ -459,6 +493,14 @@ LRESULT oc_win32_win_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM
|
||||||
event.mouse.deltaX = event.mouse.x - oc_appData.win32.lastMousePos.x;
|
event.mouse.deltaX = event.mouse.x - oc_appData.win32.lastMousePos.x;
|
||||||
event.mouse.deltaY = event.mouse.y - oc_appData.win32.lastMousePos.y;
|
event.mouse.deltaY = event.mouse.y - oc_appData.win32.lastMousePos.y;
|
||||||
}
|
}
|
||||||
|
if(abs(event.mouse.x - oc_appData.win32.lastMousePos.x) > GetSystemMetrics(SM_CXDOUBLECLK) / 2
|
||||||
|
|| abs(event.mouse.y - oc_appData.win32.lastMousePos.y) > GetSystemMetrics(SM_CYDOUBLECLK) / 2)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < OC_MOUSE_BUTTON_COUNT; i++)
|
||||||
|
{
|
||||||
|
oc_appData.win32.clickCount[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
oc_appData.win32.lastMousePos = (oc_vec2){ event.mouse.x, event.mouse.y };
|
oc_appData.win32.lastMousePos = (oc_vec2){ event.mouse.x, event.mouse.y };
|
||||||
|
|
||||||
if(!oc_appData.win32.mouseTracked)
|
if(!oc_appData.win32.mouseTracked)
|
||||||
|
|
|
@ -40,6 +40,8 @@ typedef struct oc_win32_app_data
|
||||||
int mouseCaptureMask;
|
int mouseCaptureMask;
|
||||||
bool mouseTracked;
|
bool mouseTracked;
|
||||||
oc_vec2 lastMousePos;
|
oc_vec2 lastMousePos;
|
||||||
|
u32 lastClickTime[OC_MOUSE_BUTTON_COUNT];
|
||||||
|
u32 clickCount[OC_MOUSE_BUTTON_COUNT];
|
||||||
u32 wheelScrollLines;
|
u32 wheelScrollLines;
|
||||||
|
|
||||||
} oc_win32_app_data;
|
} oc_win32_app_data;
|
||||||
|
|
|
@ -67,6 +67,7 @@ extern "C"
|
||||||
float cosf(float);
|
float cosf(float);
|
||||||
|
|
||||||
double fabs(double);
|
double fabs(double);
|
||||||
|
float fabsf(float);
|
||||||
|
|
||||||
double floor(double);
|
double floor(double);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
float fabsf(float x)
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
uint32_t i;
|
||||||
|
} u = { x };
|
||||||
|
|
||||||
|
u.i &= -1U / 2;
|
||||||
|
return u.f;
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ static void oc_update_key_state(oc_input_state* state, oc_key_state* key, oc_key
|
||||||
key->repeatCount = 0;
|
key->repeatCount = 0;
|
||||||
key->sysClicked = false;
|
key->sysClicked = false;
|
||||||
key->sysDoubleClicked = false;
|
key->sysDoubleClicked = false;
|
||||||
|
key->sysTripleClicked = false;
|
||||||
key->lastUpdate = frameCounter;
|
key->lastUpdate = frameCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +94,6 @@ static void oc_update_mouse_leave(oc_input_state* state)
|
||||||
|
|
||||||
static void oc_update_mouse_wheel(oc_input_state* state, f32 deltaX, f32 deltaY)
|
static void oc_update_mouse_wheel(oc_input_state* state, f32 deltaX, f32 deltaY)
|
||||||
{
|
{
|
||||||
oc_log_info("wheel");
|
|
||||||
u64 frameCounter = state->frameCounter;
|
u64 frameCounter = state->frameCounter;
|
||||||
oc_mouse_state* mouse = &state->mouse;
|
oc_mouse_state* mouse = &state->mouse;
|
||||||
if(mouse->lastUpdate != frameCounter)
|
if(mouse->lastUpdate != frameCounter)
|
||||||
|
@ -178,10 +178,15 @@ void oc_input_process_event(oc_input_state* state, oc_event* event)
|
||||||
{
|
{
|
||||||
key->sysClicked = true;
|
key->sysClicked = true;
|
||||||
}
|
}
|
||||||
if(event->key.clickCount >= 2)
|
|
||||||
|
if(event->key.clickCount % 2 == 0)
|
||||||
{
|
{
|
||||||
key->sysDoubleClicked = true;
|
key->sysDoubleClicked = true;
|
||||||
}
|
}
|
||||||
|
else if(event->key.clickCount > 1)
|
||||||
|
{
|
||||||
|
key->sysTripleClicked = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oc_update_key_mods(state, event->key.mods);
|
oc_update_key_mods(state, event->key.mods);
|
||||||
|
@ -314,10 +319,17 @@ bool oc_mouse_clicked(oc_input_state* input, oc_mouse_button button)
|
||||||
bool oc_mouse_double_clicked(oc_input_state* input, oc_mouse_button button)
|
bool oc_mouse_double_clicked(oc_input_state* input, oc_mouse_button button)
|
||||||
{
|
{
|
||||||
oc_key_state state = oc_mouse_button_get_state(input, button);
|
oc_key_state state = oc_mouse_button_get_state(input, button);
|
||||||
bool doubleClicked = state.sysClicked && (state.lastUpdate == input->frameCounter);
|
bool doubleClicked = state.sysDoubleClicked && (state.lastUpdate == input->frameCounter);
|
||||||
return (doubleClicked);
|
return (doubleClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool oc_mouse_triple_clicked(oc_input_state* input, oc_mouse_button button)
|
||||||
|
{
|
||||||
|
oc_key_state state = oc_mouse_button_get_state(input, button);
|
||||||
|
bool tripleClicked = state.sysTripleClicked && (state.lastUpdate == input->frameCounter);
|
||||||
|
return (tripleClicked);
|
||||||
|
}
|
||||||
|
|
||||||
oc_keymod_flags oc_key_mods(oc_input_state* input)
|
oc_keymod_flags oc_key_mods(oc_input_state* input)
|
||||||
{
|
{
|
||||||
return (input->keyboard.mods);
|
return (input->keyboard.mods);
|
||||||
|
|
|
@ -22,6 +22,7 @@ typedef struct oc_key_state
|
||||||
bool down;
|
bool down;
|
||||||
bool sysClicked;
|
bool sysClicked;
|
||||||
bool sysDoubleClicked;
|
bool sysDoubleClicked;
|
||||||
|
bool sysTripleClicked;
|
||||||
|
|
||||||
} oc_key_state;
|
} oc_key_state;
|
||||||
|
|
||||||
|
|
195
src/ui/ui.c
195
src/ui/ui.c
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
*****************************************************************/
|
*****************************************************************/
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "math.h"
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
#include "platform/platform_clock.h"
|
#include "platform/platform_clock.h"
|
||||||
#include "platform/platform_debug.h"
|
#include "platform/platform_debug.h"
|
||||||
|
@ -570,6 +571,7 @@ oc_ui_sig oc_ui_box_sig(oc_ui_box* box)
|
||||||
box->dragging = true;
|
box->dragging = true;
|
||||||
}
|
}
|
||||||
sig.doubleClicked = oc_mouse_double_clicked(input, OC_MOUSE_LEFT);
|
sig.doubleClicked = oc_mouse_double_clicked(input, OC_MOUSE_LEFT);
|
||||||
|
sig.tripleClicked = oc_mouse_triple_clicked(input, OC_MOUSE_LEFT);
|
||||||
sig.rightPressed = oc_mouse_pressed(input, OC_MOUSE_RIGHT);
|
sig.rightPressed = oc_mouse_pressed(input, OC_MOUSE_RIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1531,6 +1533,8 @@ void oc_ui_init(oc_ui_context* ui)
|
||||||
|
|
||||||
oc_ui_set_context(ui);
|
oc_ui_set_context(ui);
|
||||||
oc_ui_set_theme(&OC_UI_DARK_THEME);
|
oc_ui_set_theme(&OC_UI_DARK_THEME);
|
||||||
|
|
||||||
|
ui->editSelectionMode = OC_UI_EDIT_MOVE_CHAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void oc_ui_cleanup(void)
|
void oc_ui_cleanup(void)
|
||||||
|
@ -2735,14 +2739,6 @@ typedef enum
|
||||||
OC_UI_EDIT_SELECT_ALL
|
OC_UI_EDIT_SELECT_ALL
|
||||||
} oc_ui_edit_op;
|
} oc_ui_edit_op;
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
OC_UI_EDIT_MOVE_NONE = 0,
|
|
||||||
OC_UI_EDIT_MOVE_ONE,
|
|
||||||
OC_UI_EDIT_MOVE_WORD,
|
|
||||||
OC_UI_EDIT_MOVE_LINE
|
|
||||||
} oc_ui_edit_move;
|
|
||||||
|
|
||||||
typedef struct oc_ui_edit_command
|
typedef struct oc_ui_edit_command
|
||||||
{
|
{
|
||||||
oc_key_code key;
|
oc_key_code key;
|
||||||
|
@ -2759,13 +2755,13 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_MACOS[] = {
|
||||||
{
|
{
|
||||||
.key = OC_KEY_LEFT,
|
.key = OC_KEY_LEFT,
|
||||||
.operation = OC_UI_EDIT_MOVE,
|
.operation = OC_UI_EDIT_MOVE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = -1 },
|
.direction = -1 },
|
||||||
//NOTE(martin): move one right
|
//NOTE(martin): move one right
|
||||||
{
|
{
|
||||||
.key = OC_KEY_RIGHT,
|
.key = OC_KEY_RIGHT,
|
||||||
.operation = OC_UI_EDIT_MOVE,
|
.operation = OC_UI_EDIT_MOVE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = 1 },
|
.direction = 1 },
|
||||||
//NOTE(martin): move one word left
|
//NOTE(martin): move one word left
|
||||||
{
|
{
|
||||||
|
@ -2808,14 +2804,14 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_MACOS[] = {
|
||||||
.key = OC_KEY_LEFT,
|
.key = OC_KEY_LEFT,
|
||||||
.mods = OC_KEYMOD_SHIFT,
|
.mods = OC_KEYMOD_SHIFT,
|
||||||
.operation = OC_UI_EDIT_SELECT,
|
.operation = OC_UI_EDIT_SELECT,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = -1 },
|
.direction = -1 },
|
||||||
//NOTE(martin): select one right
|
//NOTE(martin): select one right
|
||||||
{
|
{
|
||||||
.key = OC_KEY_RIGHT,
|
.key = OC_KEY_RIGHT,
|
||||||
.mods = OC_KEYMOD_SHIFT,
|
.mods = OC_KEYMOD_SHIFT,
|
||||||
.operation = OC_UI_EDIT_SELECT,
|
.operation = OC_UI_EDIT_SELECT,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = 1 },
|
.direction = 1 },
|
||||||
//NOTE(martin): select one word left
|
//NOTE(martin): select one word left
|
||||||
{
|
{
|
||||||
|
@ -2865,7 +2861,7 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_MACOS[] = {
|
||||||
{
|
{
|
||||||
.key = OC_KEY_DELETE,
|
.key = OC_KEY_DELETE,
|
||||||
.operation = OC_UI_EDIT_DELETE,
|
.operation = OC_UI_EDIT_DELETE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = 1 },
|
.direction = 1 },
|
||||||
//NOTE(martin): delete word
|
//NOTE(martin): delete word
|
||||||
{
|
{
|
||||||
|
@ -2878,7 +2874,7 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_MACOS[] = {
|
||||||
{
|
{
|
||||||
.key = OC_KEY_BACKSPACE,
|
.key = OC_KEY_BACKSPACE,
|
||||||
.operation = OC_UI_EDIT_DELETE,
|
.operation = OC_UI_EDIT_DELETE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = -1 },
|
.direction = -1 },
|
||||||
//NOTE(martin): backspace word
|
//NOTE(martin): backspace word
|
||||||
{
|
{
|
||||||
|
@ -2912,13 +2908,13 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_WINDOWS[] = {
|
||||||
{
|
{
|
||||||
.key = OC_KEY_LEFT,
|
.key = OC_KEY_LEFT,
|
||||||
.operation = OC_UI_EDIT_MOVE,
|
.operation = OC_UI_EDIT_MOVE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = -1 },
|
.direction = -1 },
|
||||||
//NOTE(martin): move one right
|
//NOTE(martin): move one right
|
||||||
{
|
{
|
||||||
.key = OC_KEY_RIGHT,
|
.key = OC_KEY_RIGHT,
|
||||||
.operation = OC_UI_EDIT_MOVE,
|
.operation = OC_UI_EDIT_MOVE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = 1 },
|
.direction = 1 },
|
||||||
//NOTE(martin): move one word left
|
//NOTE(martin): move one word left
|
||||||
{
|
{
|
||||||
|
@ -2959,14 +2955,14 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_WINDOWS[] = {
|
||||||
.key = OC_KEY_LEFT,
|
.key = OC_KEY_LEFT,
|
||||||
.mods = OC_KEYMOD_SHIFT,
|
.mods = OC_KEYMOD_SHIFT,
|
||||||
.operation = OC_UI_EDIT_SELECT,
|
.operation = OC_UI_EDIT_SELECT,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = -1 },
|
.direction = -1 },
|
||||||
//NOTE(martin): select one right
|
//NOTE(martin): select one right
|
||||||
{
|
{
|
||||||
.key = OC_KEY_RIGHT,
|
.key = OC_KEY_RIGHT,
|
||||||
.mods = OC_KEYMOD_SHIFT,
|
.mods = OC_KEYMOD_SHIFT,
|
||||||
.operation = OC_UI_EDIT_SELECT,
|
.operation = OC_UI_EDIT_SELECT,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = 1 },
|
.direction = 1 },
|
||||||
//NOTE(martin): select one word left
|
//NOTE(martin): select one word left
|
||||||
{
|
{
|
||||||
|
@ -3016,7 +3012,7 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_WINDOWS[] = {
|
||||||
{
|
{
|
||||||
.key = OC_KEY_DELETE,
|
.key = OC_KEY_DELETE,
|
||||||
.operation = OC_UI_EDIT_DELETE,
|
.operation = OC_UI_EDIT_DELETE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = 1 },
|
.direction = 1 },
|
||||||
//NOTE(martin): delete word
|
//NOTE(martin): delete word
|
||||||
{
|
{
|
||||||
|
@ -3029,7 +3025,7 @@ const oc_ui_edit_command OC_UI_EDIT_COMMANDS_WINDOWS[] = {
|
||||||
{
|
{
|
||||||
.key = OC_KEY_BACKSPACE,
|
.key = OC_KEY_BACKSPACE,
|
||||||
.operation = OC_UI_EDIT_DELETE,
|
.operation = OC_UI_EDIT_DELETE,
|
||||||
.move = OC_UI_EDIT_MOVE_ONE,
|
.move = OC_UI_EDIT_MOVE_CHAR,
|
||||||
.direction = -1 },
|
.direction = -1 },
|
||||||
//NOTE(martin): backspace word
|
//NOTE(martin): backspace word
|
||||||
{
|
{
|
||||||
|
@ -3073,8 +3069,14 @@ bool oc_ui_edit_is_word_separator(u32 codepoint)
|
||||||
|
|
||||||
bool oc_ui_edit_is_whitespace(u32 codepoint)
|
bool oc_ui_edit_is_whitespace(u32 codepoint)
|
||||||
{
|
{
|
||||||
return (codepoint == ' ' || (0x09 <= codepoint && codepoint <= 0x0d) || codepoint == 0x85 || codepoint == 0xa0
|
return (codepoint == ' '
|
||||||
|| codepoint == 0x1680 || (0x2000 <= codepoint && codepoint <= 0x200a) || codepoint == 0x202f || codepoint == 0x205f || codepoint == 0x3000);
|
|| (0x09 <= codepoint && codepoint <= 0x0d) // HT, LF, VT, FF, CR
|
||||||
|
|| codepoint == 0x85 // NEXT LINE (NEL)
|
||||||
|
|| codepoint == 0xa0 // ↓ Unicode Separator, Space (Zs)
|
||||||
|
|| (0x2000 <= codepoint && codepoint <= 0x200a)
|
||||||
|
|| codepoint == 0x202f
|
||||||
|
|| codepoint == 0x205f
|
||||||
|
|| codepoint == 0x3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void oc_ui_edit_perform_move(oc_ui_context* ui, oc_ui_edit_move move, int direction, oc_str32 codepoints)
|
void oc_ui_edit_perform_move(oc_ui_context* ui, oc_ui_edit_move move, int direction, oc_str32 codepoints)
|
||||||
|
@ -3084,7 +3086,7 @@ void oc_ui_edit_perform_move(oc_ui_context* ui, oc_ui_edit_move move, int direct
|
||||||
case OC_UI_EDIT_MOVE_NONE:
|
case OC_UI_EDIT_MOVE_NONE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OC_UI_EDIT_MOVE_ONE:
|
case OC_UI_EDIT_MOVE_CHAR:
|
||||||
{
|
{
|
||||||
if(direction < 0 && ui->editCursor > 0)
|
if(direction < 0 && ui->editCursor > 0)
|
||||||
{
|
{
|
||||||
|
@ -3181,7 +3183,7 @@ oc_str32 oc_ui_edit_perform_operation(oc_ui_context* ui, oc_ui_edit_op operation
|
||||||
u32 cursor = direction < 0 ? oc_min(ui->editCursor, ui->editMark) : oc_max(ui->editCursor, ui->editMark);
|
u32 cursor = direction < 0 ? oc_min(ui->editCursor, ui->editMark) : oc_max(ui->editCursor, ui->editMark);
|
||||||
ui->editCursor = cursor;
|
ui->editCursor = cursor;
|
||||||
|
|
||||||
if(ui->editCursor == ui->editMark || move != OC_UI_EDIT_MOVE_ONE)
|
if(ui->editCursor == ui->editMark || move != OC_UI_EDIT_MOVE_CHAR)
|
||||||
{
|
{
|
||||||
//NOTE: we special case move-one when there is a selection
|
//NOTE: we special case move-one when there is a selection
|
||||||
// (just place the cursor at begining/end of selection)
|
// (just place the cursor at begining/end of selection)
|
||||||
|
@ -3251,6 +3253,46 @@ oc_str32 oc_ui_edit_perform_operation(oc_ui_context* ui, oc_ui_edit_op operation
|
||||||
return (codepoints);
|
return (codepoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i32 oc_ui_edit_find_word_start(oc_ui_context* ui, oc_str32 codepoints, i32 startChar)
|
||||||
|
{
|
||||||
|
i32 c = startChar;
|
||||||
|
if(oc_ui_edit_is_whitespace(codepoints.ptr[startChar]))
|
||||||
|
{
|
||||||
|
while(c > 0 && oc_ui_edit_is_whitespace(codepoints.ptr[c - 1]))
|
||||||
|
{
|
||||||
|
c--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(!oc_ui_edit_is_word_separator(codepoints.ptr[startChar]))
|
||||||
|
{
|
||||||
|
while(c > 0 && !oc_ui_edit_is_word_separator(codepoints.ptr[c - 1]) && !oc_ui_edit_is_whitespace(codepoints.ptr[c - 1]))
|
||||||
|
{
|
||||||
|
c--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 oc_ui_edit_find_word_end(oc_ui_context* ui, oc_str32 codepoints, i32 startChar)
|
||||||
|
{
|
||||||
|
i32 c = oc_min(startChar + 1, codepoints.len);
|
||||||
|
if(startChar < codepoints.len && oc_ui_edit_is_whitespace(codepoints.ptr[startChar]))
|
||||||
|
{
|
||||||
|
while(c < codepoints.len && oc_ui_edit_is_whitespace(codepoints.ptr[c]))
|
||||||
|
{
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(startChar < codepoints.len && !oc_ui_edit_is_word_separator(codepoints.ptr[startChar]))
|
||||||
|
{
|
||||||
|
while(c < codepoints.len && !oc_ui_edit_is_word_separator(codepoints.ptr[c]) && !oc_ui_edit_is_whitespace(codepoints.ptr[c]))
|
||||||
|
{
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
void oc_ui_text_box_render(oc_ui_box* box, void* data)
|
void oc_ui_text_box_render(oc_ui_box* box, void* data)
|
||||||
{
|
{
|
||||||
oc_str32 codepoints = *(oc_str32*)data;
|
oc_str32 codepoints = *(oc_str32*)data;
|
||||||
|
@ -3417,32 +3459,117 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8
|
||||||
f32 cursorX = pos.x - textBox->rect.x;
|
f32 cursorX = pos.x - textBox->rect.x;
|
||||||
|
|
||||||
oc_str32 codepoints = oc_utf8_push_to_codepoints(&ui->frameArena, text);
|
oc_str32 codepoints = oc_utf8_push_to_codepoints(&ui->frameArena, text);
|
||||||
i32 newCursor = codepoints.len;
|
i32 newCursor = 0;
|
||||||
|
i32 hoveredChar = 0;
|
||||||
f32 x = 0;
|
f32 x = 0;
|
||||||
for(int i = ui->editFirstDisplayedChar; i < codepoints.len; i++)
|
for(int i = ui->editFirstDisplayedChar; i < codepoints.len; i++)
|
||||||
{
|
{
|
||||||
oc_rect bbox = oc_text_bounding_box_utf32(font, fontSize, oc_str32_slice(codepoints, i, i + 1));
|
oc_rect bbox = oc_text_bounding_box_utf32(font, fontSize, oc_str32_slice(codepoints, i, i + 1));
|
||||||
|
if(x < cursorX)
|
||||||
|
{
|
||||||
|
hoveredChar = i;
|
||||||
|
}
|
||||||
if(x + 0.5 * bbox.w > cursorX)
|
if(x + 0.5 * bbox.w > cursorX)
|
||||||
{
|
{
|
||||||
newCursor = i;
|
newCursor = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(i == codepoints.len - 1)
|
||||||
|
{
|
||||||
|
newCursor = codepoints.len;
|
||||||
|
}
|
||||||
x += bbox.w;
|
x += bbox.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(sig.doubleClicked)
|
||||||
|
{
|
||||||
|
ui->editCursor = oc_ui_edit_find_word_end(ui, codepoints, hoveredChar);
|
||||||
|
ui->editMark = oc_ui_edit_find_word_start(ui, codepoints, hoveredChar);
|
||||||
|
ui->editSelectionMode = OC_UI_EDIT_MOVE_WORD;
|
||||||
|
ui->editWordSelectionInitialCursor = ui->editCursor;
|
||||||
|
ui->editWordSelectionInitialMark = ui->editMark;
|
||||||
|
}
|
||||||
|
else if(sig.tripleClicked)
|
||||||
|
{
|
||||||
|
ui->editCursor = codepoints.len;
|
||||||
|
ui->editMark = 0;
|
||||||
|
ui->editSelectionMode = OC_UI_EDIT_MOVE_LINE;
|
||||||
|
}
|
||||||
|
else if(sig.pressed
|
||||||
|
&& (oc_key_mods(&ui->input) & OC_KEYMOD_SHIFT)
|
||||||
|
&& !(newCursor >= oc_min(ui->editCursor, ui->editMark) && newCursor <= oc_max(ui->editCursor, ui->editMark)))
|
||||||
|
{
|
||||||
//NOTE: put cursor the closest to new cursor (this maximizes the resulting selection,
|
//NOTE: put cursor the closest to new cursor (this maximizes the resulting selection,
|
||||||
// and seems to be the standard behaviour across a number of text editor)
|
// and seems to be the standard behaviour across a number of text editor)
|
||||||
if(sig.pressed && abs(newCursor - ui->editCursor) > abs(newCursor - ui->editMark))
|
if(abs(newCursor - ui->editCursor) > abs(newCursor - ui->editMark))
|
||||||
{
|
|
||||||
i32 tmp = ui->editCursor;
|
|
||||||
ui->editCursor = ui->editMark;
|
|
||||||
ui->editMark = tmp;
|
|
||||||
}
|
|
||||||
//NOTE: set the new cursor, and set or leave the mark depending on mode
|
|
||||||
ui->editCursor = newCursor;
|
|
||||||
if(sig.pressed && !(oc_key_mods(&ui->input) & OC_KEYMOD_SHIFT))
|
|
||||||
{
|
{
|
||||||
ui->editMark = ui->editCursor;
|
ui->editMark = ui->editCursor;
|
||||||
|
ui->editCursor = newCursor;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->editCursor = newCursor;
|
||||||
|
}
|
||||||
|
ui->editSelectionMode = OC_UI_EDIT_MOVE_CHAR;
|
||||||
|
}
|
||||||
|
else if(sig.pressed)
|
||||||
|
{
|
||||||
|
ui->editCursor = newCursor;
|
||||||
|
ui->editMark = newCursor;
|
||||||
|
ui->editSelectionMode = OC_UI_EDIT_MOVE_CHAR;
|
||||||
|
}
|
||||||
|
else if(ui->editSelectionMode == OC_UI_EDIT_MOVE_LINE)
|
||||||
|
{
|
||||||
|
oc_rect bbox = oc_text_bounding_box_utf32(font, fontSize, codepoints);
|
||||||
|
if(fabsf(bbox.w - cursorX) < fabsf(cursorX))
|
||||||
|
{
|
||||||
|
ui->editCursor = codepoints.len;
|
||||||
|
ui->editMark = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->editCursor = 0;
|
||||||
|
ui->editMark = codepoints.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ui->editSelectionMode == OC_UI_EDIT_MOVE_WORD)
|
||||||
|
{
|
||||||
|
if(oc_min(ui->editCursor, ui->editMark) == oc_min(ui->editWordSelectionInitialCursor, ui->editWordSelectionInitialMark)
|
||||||
|
&& oc_max(ui->editCursor, ui->editMark) == oc_max(ui->editWordSelectionInitialCursor, ui->editWordSelectionInitialMark))
|
||||||
|
{
|
||||||
|
oc_rect editCursorPrefixBbox = oc_text_bounding_box_utf32(font, fontSize, oc_str32_slice(codepoints, 0, ui->editCursor));
|
||||||
|
oc_rect editMarkPrefixBbox = oc_text_bounding_box_utf32(font, fontSize, oc_str32_slice(codepoints, 0, ui->editMark));
|
||||||
|
f32 editCursorX = editCursorPrefixBbox.w;
|
||||||
|
f32 editMarkX = editMarkPrefixBbox.w;
|
||||||
|
if(fabsf(cursorX - editMarkX) < fabsf(cursorX - editCursorX))
|
||||||
|
{
|
||||||
|
i32 tmp = ui->editMark;
|
||||||
|
ui->editMark = ui->editCursor;
|
||||||
|
ui->editCursor = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ui->editCursor >= ui->editMark)
|
||||||
|
{
|
||||||
|
ui->editCursor = oc_ui_edit_find_word_end(ui, codepoints, hoveredChar);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->editCursor = oc_ui_edit_find_word_start(ui, codepoints, hoveredChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ui->editSelectionMode == OC_UI_EDIT_MOVE_CHAR)
|
||||||
|
{
|
||||||
|
ui->editCursor = newCursor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OC_DEBUG_ASSERT("Unexpected textbox branch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->editSelectionMode = OC_UI_EDIT_MOVE_CHAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sig.hovering)
|
if(sig.hovering)
|
||||||
|
|
14
src/ui/ui.h
14
src/ui/ui.h
|
@ -365,7 +365,7 @@ typedef struct oc_ui_theme
|
||||||
oc_color sliderThumbBorder;
|
oc_color sliderThumbBorder;
|
||||||
oc_color elevatedBorder;
|
oc_color elevatedBorder;
|
||||||
|
|
||||||
oc_ui_palette *palette;
|
oc_ui_palette* palette;
|
||||||
} oc_ui_theme;
|
} oc_ui_theme;
|
||||||
|
|
||||||
extern oc_ui_theme OC_UI_DARK_THEME;
|
extern oc_ui_theme OC_UI_DARK_THEME;
|
||||||
|
@ -451,6 +451,7 @@ typedef struct oc_ui_sig
|
||||||
bool released;
|
bool released;
|
||||||
bool clicked;
|
bool clicked;
|
||||||
bool doubleClicked;
|
bool doubleClicked;
|
||||||
|
bool tripleClicked;
|
||||||
bool rightPressed;
|
bool rightPressed;
|
||||||
|
|
||||||
bool dragging;
|
bool dragging;
|
||||||
|
@ -577,6 +578,14 @@ enum
|
||||||
OC_UI_BOX_MAP_BUCKET_COUNT = 1024
|
OC_UI_BOX_MAP_BUCKET_COUNT = 1024
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
OC_UI_EDIT_MOVE_NONE = 0,
|
||||||
|
OC_UI_EDIT_MOVE_CHAR,
|
||||||
|
OC_UI_EDIT_MOVE_WORD,
|
||||||
|
OC_UI_EDIT_MOVE_LINE
|
||||||
|
} oc_ui_edit_move;
|
||||||
|
|
||||||
typedef struct oc_ui_context
|
typedef struct oc_ui_context
|
||||||
{
|
{
|
||||||
bool init;
|
bool init;
|
||||||
|
@ -609,6 +618,9 @@ typedef struct oc_ui_context
|
||||||
i32 editMark;
|
i32 editMark;
|
||||||
i32 editFirstDisplayedChar;
|
i32 editFirstDisplayedChar;
|
||||||
f64 editCursorBlinkStart;
|
f64 editCursorBlinkStart;
|
||||||
|
oc_ui_edit_move editSelectionMode;
|
||||||
|
i32 editWordSelectionInitialCursor;
|
||||||
|
i32 editWordSelectionInitialMark;
|
||||||
|
|
||||||
oc_ui_theme* theme;
|
oc_ui_theme* theme;
|
||||||
} oc_ui_context;
|
} oc_ui_context;
|
||||||
|
|
Loading…
Reference in New Issue