improve text input and add caret
This commit is contained in:
parent
3b534ad231
commit
d8601f23f5
7 changed files with 100 additions and 19 deletions
|
@ -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;
|
||||
}
|
||||
|
|
42
src/node.c
42
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#include <pango/pango.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#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: {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue