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++;
|
current_index++;
|
||||||
}
|
}
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
42
src/node.c
42
src/node.c
|
@ -6,8 +6,28 @@
|
||||||
typedef struct UIWindow UIWindow;
|
typedef struct UIWindow UIWindow;
|
||||||
int window_invalidate_node(UIWindow *window, UINode *node);
|
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)
|
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++) {
|
for (int i = 0; i < node->nodes_count; i++) {
|
||||||
if (node->nodes[i]->flags & UI_NODE_COMPONENT) {
|
if (node->nodes[i]->flags & UI_NODE_COMPONENT) {
|
||||||
int result = node_dispatch(node->nodes[i], ev, d, p);
|
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)
|
UINode *node_by_point(UINode *root, double x, double y)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < root->nodes_count; i++) {
|
for (int i = 0; i < root->nodes_count; i++) {
|
||||||
double local_x = x - root->nodes[i]->window_rel_x;
|
UINode *node = root->nodes[i];
|
||||||
double local_y = y - root->nodes[i]->window_rel_y;
|
double local_x = x - node->window_rel_x;
|
||||||
if (ui_rect_contains_point(&root->nodes[i]->rect, local_x, local_y)) {
|
double local_y = y - node->window_rel_y;
|
||||||
return node_by_point(root->nodes[i], local_x, local_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;
|
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_x = window_rel_x;
|
||||||
node->window_rel_y = window_rel_y;
|
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;
|
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);
|
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 };
|
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++) {
|
for (int i = 0; i < node->nodes_count; i++) {
|
||||||
UINode *current = node->nodes[i];
|
UINode *current = node->nodes[i];
|
||||||
if (current->flags & UI_NODE_COMPONENT) {
|
if (current->flags & UI_NODE_DISABLED) {
|
||||||
cairo_save(current->drw);
|
continue;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
node_repaint(current, &local_rect, false, window_rel_x + node->rect.x, window_rel_y + node->rect.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_restore(node->drw);
|
cairo_restore(node->drw);
|
||||||
|
|
|
@ -39,6 +39,7 @@ enum UIDirection {
|
||||||
|
|
||||||
#define UI_NODE_COMPONENT (1 << 0)
|
#define UI_NODE_COMPONENT (1 << 0)
|
||||||
#define UI_NODE_RETAINS_PRESS (1 << 1)
|
#define UI_NODE_RETAINS_PRESS (1 << 1)
|
||||||
|
#define UI_NODE_DISABLED (1 << 2)
|
||||||
|
|
||||||
typedef struct UINode {
|
typedef struct UINode {
|
||||||
const char *text;
|
const char *text;
|
||||||
|
@ -57,6 +58,7 @@ typedef struct UINode {
|
||||||
int (*handle_proto)(struct UINode *node, enum UIEvent ev, size_t d, void *p);
|
int (*handle_proto)(struct UINode *node, enum UIEvent ev, size_t d, void *p);
|
||||||
} UINode;
|
} 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);
|
int node_dispatch(UINode *node, enum UIEvent ev, size_t d, void *p);
|
||||||
UINode *node_by_point(UINode *root, double x, double y);
|
UINode *node_by_point(UINode *root, double x, double y);
|
||||||
void node_init(UINode *node);
|
void node_init(UINode *node);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "text-node.h"
|
#include "text-node.h"
|
||||||
#include "text-input-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->node.handle_proto = text_input_handle;
|
||||||
n->text = calloc(1, 1);
|
n->text = calloc(1, 1);
|
||||||
n->text_size = 1;
|
n->text_size = 1;
|
||||||
|
n->text_cursor_index = 0;
|
||||||
node_attach(parent, (UINode*)n);
|
node_attach(parent, (UINode*)n);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -24,21 +26,51 @@ int text_input_handle(UINode *node, enum UIEvent ev, size_t d, void *p)
|
||||||
|
|
||||||
switch (ev) {
|
switch (ev) {
|
||||||
case UI_EVENT_KEY_DOWN: {
|
case UI_EVENT_KEY_DOWN: {
|
||||||
unsigned int keycode = d;
|
xkb_keycode_t keycode = d;
|
||||||
struct xkb_state *xkb_state = (struct xkb_state*)p;
|
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;
|
switch (keysym) {
|
||||||
size_t new_text_size = n->text_size + key_size - 1;
|
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);
|
n->text = realloc(n->text, new_text_size);
|
||||||
xkb_state_key_get_utf8(xkb_state, keycode, n->text + n->text_size - 1, key_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;
|
n->text_size = new_text_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (n->text_node) {
|
if (n->text_node) {
|
||||||
n->text_node->pending_text = n->text;
|
n->text_node->pending_text = n->text;
|
||||||
|
n->text_node->caret_index = n->text_cursor_index;
|
||||||
node_request_relayout(&n->text_node->node);
|
node_request_relayout(&n->text_node->node);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
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: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ typedef struct UITextInputNode {
|
||||||
UITextNode *text_node;
|
UITextNode *text_node;
|
||||||
char *text;
|
char *text;
|
||||||
size_t text_size;
|
size_t text_size;
|
||||||
|
size_t text_cursor_index;
|
||||||
} UITextInputNode;
|
} UITextInputNode;
|
||||||
|
|
||||||
UITextInputNode *text_input_new(UINode *parent);
|
UITextInputNode *text_input_new(UINode *parent);
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#include <pango/pango.h>
|
#include <pango/pango.h>
|
||||||
#include <pango/pangocairo.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"
|
#include "text-node.h"
|
||||||
|
|
||||||
UITextNode *text_node_new(UINode *parent, PangoFontDescription *desc, UIRGBA color, char *text)
|
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->desc = desc;
|
||||||
n->layout = NULL;
|
n->layout = NULL;
|
||||||
n->color = color;
|
n->color = color;
|
||||||
|
n->caret_index = -1;
|
||||||
|
n->caret_node = NULL;
|
||||||
n->node.text = "text";
|
n->node.text = "text";
|
||||||
n->node.handle_proto = text_node_handle;
|
n->node.handle_proto = text_node_handle;
|
||||||
node_attach(parent, (UINode*)n);
|
node_attach(parent, (UINode*)n);
|
||||||
|
@ -76,6 +82,26 @@ int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pango_cairo_update_layout(n->node.drw, n->layout);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case UI_EVENT_DESTROY: {
|
case UI_EVENT_DESTROY: {
|
||||||
|
|
|
@ -13,6 +13,8 @@ typedef struct UITextNode {
|
||||||
PangoLayout *layout;
|
PangoLayout *layout;
|
||||||
char *pending_text;
|
char *pending_text;
|
||||||
UIRGBA color;
|
UIRGBA color;
|
||||||
|
ssize_t caret_index;
|
||||||
|
UINode *caret_node;
|
||||||
} UITextNode;
|
} UITextNode;
|
||||||
|
|
||||||
int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p);
|
int text_node_handle(UINode *node, enum UIEvent ev, size_t d, void *p);
|
||||||
|
|
Loading…
Reference in a new issue