From d8601f23f522ed552055bf5a3f6905fe0342fce1 Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Tue, 25 Apr 2023 14:39:37 +0300 Subject: [PATCH] improve text input and add caret --- src/box-layout-node.c | 2 +- src/node.c | 42 +++++++++++++++++++++++++++++------------ src/node.h | 2 ++ src/text-input-node.c | 44 +++++++++++++++++++++++++++++++++++++------ src/text-input-node.h | 1 + src/text-node.c | 26 +++++++++++++++++++++++++ src/text-node.h | 2 ++ 7 files changed, 100 insertions(+), 19 deletions(-) diff --git a/src/box-layout-node.c b/src/box-layout-node.c index 6086cc2..220e93d 100644 --- a/src/box-layout-node.c +++ b/src/box-layout-node.c @@ -140,7 +140,7 @@ int box_layout_handle(UINode *component, enum UIEvent ev, size_t d, void *p) current_index++; } - return 1; + return 0; } return 0; } diff --git a/src/node.c b/src/node.c index 206e8ec..4f0971b 100644 --- a/src/node.c +++ b/src/node.c @@ -6,8 +6,28 @@ typedef struct UIWindow UIWindow; int window_invalidate_node(UIWindow *window, UINode *node); +int node_send(UINode *node, enum UIEvent ev, size_t d, void *p) +{ + if (node->flags & UI_NODE_DISABLED) { + return 0; + } + + if (node->handle) { + int result = (node->handle)(node, ev, d, p); + if (result) return result; + } + if (node->handle_proto) { + return (node->handle_proto)(node, ev, d, p); + } + return 0; +} + int node_dispatch(UINode *node, enum UIEvent ev, size_t d, void *p) { + if (node->flags & UI_NODE_DISABLED) { + return 0; + } + for (int i = 0; i < node->nodes_count; i++) { if (node->nodes[i]->flags & UI_NODE_COMPONENT) { int result = node_dispatch(node->nodes[i], ev, d, p); @@ -27,10 +47,11 @@ int node_dispatch(UINode *node, enum UIEvent ev, size_t d, void *p) UINode *node_by_point(UINode *root, double x, double y) { for (int i = 0; i < root->nodes_count; i++) { - double local_x = x - root->nodes[i]->window_rel_x; - double local_y = y - root->nodes[i]->window_rel_y; - if (ui_rect_contains_point(&root->nodes[i]->rect, local_x, local_y)) { - return node_by_point(root->nodes[i], local_x, local_y); + UINode *node = root->nodes[i]; + double local_x = x - node->window_rel_x; + double local_y = y - node->window_rel_y; + if (ui_rect_contains_point(&node->rect, local_x, local_y) && !(node->flags & UI_NODE_DISABLED)) { + return node_by_point(node, local_x, local_y); } } return root; @@ -72,7 +93,7 @@ void node_repaint(UINode *node, UIRect *rect, bool do_group, double window_rel_x node->window_rel_x = window_rel_x; node->window_rel_y = window_rel_y; - if (!ui_rect_overlap_rect(rect, &node->rect)) { + if (!ui_rect_overlap_rect(rect, &node->rect) && !(node->flags & UI_NODE_COMPONENT)) { return; } @@ -85,18 +106,15 @@ void node_repaint(UINode *node, UIRect *rect, bool do_group, double window_rel_x cairo_translate(node->drw, node->rect.x, node->rect.y); - node_dispatch(node, UI_EVENT_REPAINT, 0, NULL); + node_send(node, UI_EVENT_REPAINT, 0, 0); UIRect local_rect = { rect->x - node->rect.x, rect->y - node->rect.y, rect->w, rect->h }; for (int i = 0; i < node->nodes_count; i++) { UINode *current = node->nodes[i]; - if (current->flags & UI_NODE_COMPONENT) { - cairo_save(current->drw); - node_dispatch(current, UI_EVENT_REPAINT, 0, NULL); - cairo_restore(current->drw); - } else { - node_repaint(node->nodes[i], &local_rect, false, window_rel_x + node->rect.x, window_rel_y + node->rect.y); + if (current->flags & UI_NODE_DISABLED) { + continue; } + node_repaint(current, &local_rect, false, window_rel_x + node->rect.x, window_rel_y + node->rect.y); } cairo_restore(node->drw); diff --git a/src/node.h b/src/node.h index a77bc46..dd788fa 100644 --- a/src/node.h +++ b/src/node.h @@ -39,6 +39,7 @@ enum UIDirection { #define UI_NODE_COMPONENT (1 << 0) #define UI_NODE_RETAINS_PRESS (1 << 1) +#define UI_NODE_DISABLED (1 << 2) typedef struct UINode { const char *text; @@ -57,6 +58,7 @@ typedef struct UINode { int (*handle_proto)(struct UINode *node, enum UIEvent ev, size_t d, void *p); } UINode; +int node_send(UINode *node, enum UIEvent ev, size_t d, void *p); int node_dispatch(UINode *node, enum UIEvent ev, size_t d, void *p); UINode *node_by_point(UINode *root, double x, double y); void node_init(UINode *node); diff --git a/src/text-input-node.c b/src/text-input-node.c index 8d7cc34..0d693ff 100644 --- a/src/text-input-node.c +++ b/src/text-input-node.c @@ -1,5 +1,6 @@ #include #include +#include #include "node.h" #include "text-node.h" #include "text-input-node.h" @@ -14,6 +15,7 @@ UITextInputNode *text_input_new(UINode *parent) n->node.handle_proto = text_input_handle; n->text = calloc(1, 1); n->text_size = 1; + n->text_cursor_index = 0; node_attach(parent, (UINode*)n); return n; } @@ -24,21 +26,51 @@ int text_input_handle(UINode *node, enum UIEvent ev, size_t d, void *p) switch (ev) { case UI_EVENT_KEY_DOWN: { - unsigned int keycode = d; + xkb_keycode_t keycode = d; struct xkb_state *xkb_state = (struct xkb_state*)p; + xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode); - size_t key_size = xkb_state_key_get_utf8(xkb_state, keycode, NULL, 0) + 1; - size_t new_text_size = n->text_size + key_size - 1; - n->text = realloc(n->text, new_text_size); - xkb_state_key_get_utf8(xkb_state, keycode, n->text + n->text_size - 1, key_size); - n->text_size = new_text_size; + switch (keysym) { + case XKB_KEY_BackSpace: { + if (n->text_cursor_index > 0 && n->text_size > 1) { + n->text[--n->text_cursor_index] = '\0'; + n->text = realloc(n->text, n->text_size--); + } + break; + } + default: { + size_t key_size = xkb_state_key_get_utf8(xkb_state, keycode, NULL, 0); + size_t new_text_size = n->text_size + key_size; + n->text = realloc(n->text, new_text_size); + xkb_state_key_get_utf8(xkb_state, keycode, n->text + n->text_cursor_index, key_size + 1); + n->text_cursor_index += key_size; + n->text_size = new_text_size; + break; + } + } if (n->text_node) { n->text_node->pending_text = n->text; + n->text_node->caret_index = n->text_cursor_index; node_request_relayout(&n->text_node->node); } + return 1; } + case UI_EVENT_PRESSED: { + if (n->text_node) { + n->text_node->caret_index = n->text_cursor_index; + node_request_relayout(&n->text_node->node); + } + break; + } + case UI_EVENT_UNPRESSED: { + if (n->text_node) { + n->text_node->caret_index = -1; + node_request_relayout(&n->text_node->node); + } + break; + } default: { break; } diff --git a/src/text-input-node.h b/src/text-input-node.h index 4c73d24..05634d6 100644 --- a/src/text-input-node.h +++ b/src/text-input-node.h @@ -9,6 +9,7 @@ typedef struct UITextInputNode { UITextNode *text_node; char *text; size_t text_size; + size_t text_cursor_index; } UITextInputNode; UITextInputNode *text_input_new(UINode *parent); diff --git a/src/text-node.c b/src/text-node.c index bfeacf7..1b7684a 100644 --- a/src/text-node.c +++ b/src/text-node.c @@ -1,5 +1,9 @@ #include #include +#include "background-node.h" +#include "node.h" +#include "pango/pango-layout.h" +#include "pango/pango-types.h" #include "text-node.h" UITextNode *text_node_new(UINode *parent, PangoFontDescription *desc, UIRGBA color, char *text) @@ -11,6 +15,8 @@ UITextNode *text_node_new(UINode *parent, PangoFontDescription *desc, UIRGBA col n->desc = desc; n->layout = NULL; n->color = color; + n->caret_index = -1; + n->caret_node = NULL; n->node.text = "text"; n->node.handle_proto = text_node_handle; node_attach(parent, (UINode*)n); @@ -76,6 +82,26 @@ int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p) break; } pango_cairo_update_layout(n->node.drw, n->layout); + if (n->caret_index >= 0) { + if (!n->caret_node) { + n->caret_node = node_new(&n->node, "caret"); + background_node_new(n->caret_node, n->color, 0.0); + } + PangoRectangle rect; + pango_layout_get_cursor_pos(n->layout, n->caret_index, &rect, NULL); + n->caret_node->rect = (UIRect){ + .x = pango_units_to_double(rect.x), + .y = pango_units_to_double(rect.y), + .w = 1.0, + .h = pango_units_to_double(rect.height), + }; + n->caret_node->flags &= ~UI_NODE_DISABLED; + node_dispatch(n->caret_node, UI_EVENT_RELAYOUT, 0, 0); + } else { + if (n->caret_node) { + n->caret_node->flags |= UI_NODE_DISABLED; + } + } break; } case UI_EVENT_DESTROY: { diff --git a/src/text-node.h b/src/text-node.h index b404693..dc5b9de 100644 --- a/src/text-node.h +++ b/src/text-node.h @@ -13,6 +13,8 @@ typedef struct UITextNode { PangoLayout *layout; char *pending_text; UIRGBA color; + ssize_t caret_index; + UINode *caret_node; } UITextNode; int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p);