From 0a4b91edb05d0ea17229a5c84e5123456f07b7b9 Mon Sep 17 00:00:00 2001 From: Heath123 Date: Fri, 27 Jan 2023 14:49:28 +0000 Subject: [PATCH 1/9] Cursor movement for equation editing --- include/TeX/TeX.h | 6 ++- include/TeX/classes.h | 12 +++-- include/TeX/edit.h | 46 ++++++++++++++++ include/TeX/env.h | 3 +- include/TeX/flow.h | 13 ++++- include/TeX/node.h | 7 ++- src/TeX.c | 6 +-- src/classes.c | 50 +++++++++++------ src/env.c | 7 +-- src/flow.c | 122 ++++++++++++++++++++++++++++++++++++++++-- src/node.c | 118 +++++++++++++++++++++++++++++++++++++++- 11 files changed, 355 insertions(+), 35 deletions(-) create mode 100644 include/TeX/edit.h diff --git a/include/TeX/TeX.h b/include/TeX/TeX.h index 8407950..4f9efab 100644 --- a/include/TeX/TeX.h +++ b/include/TeX/TeX.h @@ -6,6 +6,8 @@ #define TEX_TEX #include +#include + #include //--- @@ -85,10 +87,12 @@ void TeX_free(struct TeX_Env *formula); /* TeX_draw(): Render a parsed formula @formula Formula to render + @context Used to place the cursor; can be set to NULL is there is none. + This function does not draw the cursor but may its position. @x x coordinate of the left-side of the bounding box (included) @y y coordinate of the top-size of the bounding box (included) @color Requested color for the rendered pixels */ -void TeX_draw(struct TeX_Env *formula, int x, int y, int color); +void TeX_draw(struct TeX_Env *formula, struct editContext * context, int x, int y, int color); /* TeX_interpret(): Parse and render, in sequence This function parses the provided formula, renders it and then frees it. diff --git a/include/TeX/classes.h b/include/TeX/classes.h index 2d6fe82..91ce683 100644 --- a/include/TeX/classes.h +++ b/include/TeX/classes.h @@ -43,11 +43,13 @@ struct TeX_Class void TeX_line(int x1, int y1, int x2, int y2, int color); void TeX_text(char const *str, int x, int y, int color); - @node A node of the described class, for rendering - @x Horizontal coordinate of the requested rendering position - @y Vertical coordinate of the requested rendering position - @color Requested rendering color */ - void (*render)(struct TeX_Node const * node, int x, int y, int color); + @node A node of the described class, for rendering + @context Used to place the cursor; can be set to NULL is there is none. + This function does not draw the cursor but may its position. + @x Horizontal coordinate of the requested rendering position + @y Vertical coordinate of the requested rendering position + @color Requested rendering color */ + void (*render)(struct TeX_Node const * node, struct editContext * context, int x, int y, int color); /* Sub- and superscript mode, see for available modes */ int mode; diff --git a/include/TeX/edit.h b/include/TeX/edit.h new file mode 100644 index 0000000..32547d8 --- /dev/null +++ b/include/TeX/edit.h @@ -0,0 +1,46 @@ +//--- +// edit: Structures related to cursor movement and editing +//--- + +#ifndef TEX_EDIT +#define TEX_EDIT + +#include + +struct editContext { + bool elementIsText; + union { + struct TeX_Flow* cursorFlow; + struct TeX_Node* cursorText; + }; + int offset; + + int cursorX; + int cursorY; +}; + +// An enum for action that the cursor can take +enum cursorAction { + CURSOR_MOVE_LEFT, + CURSOR_MOVE_RIGHT +}; + +// An enum of return values for the move_cursor function +enum cursorMoveResult { + // The cursor was found and updated, and the recursive search can end + SUCCESS, + // The cursor was not found, and the recursive search should continue + CURSOR_NOT_HERE, + + // For these values where the cursor exits, the parent is responsible for + // moving the cursor out of the flow or node. + // It may also choose to ingore the cursor exit and keep the cursor in the + // flow or node, for example if there is no parent to move the cursor to. + + // The cursor exited the flow or node out of the right side + CURSOR_PAST_END, + // The cursor exited the flow or node out of the left side + CURSOR_PAST_START +}; + +#endif /* TEX_EDIT */ diff --git a/include/TeX/env.h b/include/TeX/env.h index 9bbf5a7..937bebd 100644 --- a/include/TeX/env.h +++ b/include/TeX/env.h @@ -6,6 +6,7 @@ #define TEX_ENV #include +#include struct TeX_Node; @@ -34,7 +35,7 @@ struct TeX_Env void (*layout)(TeX_Env *env, int display); /* render(): Render environment */ - void (*render)(TeX_Env *env, int x, int y, int color); + void (*render)(TeX_Env *env, struct editContext * context, int x, int y, int color); /* Dimensions */ uint16_t width; diff --git a/include/TeX/flow.h b/include/TeX/flow.h index 9ad4648..b69b07b 100644 --- a/include/TeX/flow.h +++ b/include/TeX/flow.h @@ -5,6 +5,7 @@ #ifndef TEX_FLOW #define TEX_FLOW +#include #include struct TeX_Node; @@ -19,6 +20,8 @@ struct TeX_Flow struct TeX_Node *first; struct TeX_Node *last; + uint32_t elements; + uint16_t width; uint16_t height; uint16_t line; @@ -64,9 +67,17 @@ void TeX_flow_free(struct TeX_Flow *flow); characters and subscripts/superscripts. Modifies the flow's metadata. */ void TeX_flow_layout(struct TeX_Flow *flow, int display); +/* TeX_flow_cursor_action(): Perform a cursor action on a flow + This function performs a cursor action on a flow, like moving left or right. + This is recursive, and should usually be called on the top-level flow. + If it is called on a non-top-level flow, it will restrict the movement to + within the flow; the cursor will be to able to move within in but not exit. + Returns a cursorMoveResult enum; see edit.h for details of the return value. */ +enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct editContext * context, enum cursorAction action); + /* TeX_flow_render(): Render a flow and all its components This function renders all horizontal objects in a flow. The layout of the flow must have been computed. */ -void TeX_flow_render(struct TeX_Flow const * flow, int x, int y, int color); +void TeX_flow_render(struct TeX_Flow const * flow, struct editContext * context, int x, int y, int color); #endif /* TEX_FLOW */ diff --git a/include/TeX/node.h b/include/TeX/node.h index efb866d..bf679fc 100644 --- a/include/TeX/node.h +++ b/include/TeX/node.h @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -97,6 +99,9 @@ void TeX_node_layout(struct TeX_Node *node, int display); /* TeX_node_render(): Render a node and its children The node's layout must have been computed first. */ -void TeX_node_render(struct TeX_Node const * node, int x, int y, int color); +void TeX_node_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color); + +enum cursorMoveResult move_cursor_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action); +enum cursorMoveResult cursor_enter_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action); #endif /* TEX_NODE */ diff --git a/src/TeX.c b/src/TeX.c index 2cc177a..03d0786 100644 --- a/src/TeX.c +++ b/src/TeX.c @@ -72,9 +72,9 @@ struct TeX_Env *TeX_parse(char const *formula, int display) } /* TeX_draw(): Render a parsed formula */ -void TeX_draw(struct TeX_Env *formula, int x, int y, int color) +void TeX_draw(struct TeX_Env *formula, struct editContext * context, int x, int y, int color) { - formula->render(formula, x, y, color); + formula->render(formula, context, x, y, color); } /* TeX_interpret(): Parse and render, in sequence */ @@ -83,6 +83,6 @@ void TeX_interpret(char const *formula, int display, int x, int y, int color) struct TeX_Env *env = TeX_parse(formula, display); if(!env) return; - TeX_draw(env, x, y, color); + TeX_draw(env, NULL, x, y, color); TeX_free(env); } diff --git a/src/classes.c b/src/classes.c index db2ff89..0695244 100644 --- a/src/classes.c +++ b/src/classes.c @@ -7,10 +7,12 @@ #include #include #include +#include #include #include +#include #include @@ -36,8 +38,22 @@ void text_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = h >> 1; } -void text_render(struct TeX_Node const * node, int x, int y, int color) +void text_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { + if (context != NULL && context->elementIsText && context->cursorText == node) { + // Render a cursor + // Temporarily cap the length of the string to the cursor position + char old = node->text[context->offset + 1]; + node->text[context->offset + 1] = '\0'; + // Measure the width of the string + int w, h; + TeX_size((char const *)node->text, &w, &h); + // Restore the string + node->text[context->offset + 1] = old; + // Update the cursor position to inside the text + context->cursorX = x + w; + context->cursorY = y; + } TeX_text((void *)node->text, x, y, color); } @@ -54,9 +70,9 @@ void env_layout(struct TeX_Node *node, int display) node->line = node->env->line; } -void env_render(struct TeX_Node const * node, int x, int y, int color) +void env_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { - node->env->render(node->env, x, y, color); + node->env->render(node->env, context, x, y, color); } //--- @@ -96,7 +112,7 @@ void frac_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = h(0) + spacing + thickness / 2; } -void frac_render(struct TeX_Node const * node, int x, int y, int color) +void frac_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { /* Fraction bar */ for(int i = 0; i < TEX_FRACTION_BAR_THICKNESS; i++) @@ -107,12 +123,14 @@ void frac_render(struct TeX_Node const * node, int x, int y, int color) /* First argument */ TeX_flow_render(args[0], + context, x + ((node->width - w(0)) >> 1), y, color); /* Second argument */ TeX_flow_render(args[1], + context, x + ((node->width - w(1)) >> 1), y + h(0) + 2*TEX_FRACTION_SPACING + TEX_FRACTION_BAR_THICKNESS, color); @@ -165,7 +183,7 @@ void leftright_layout(struct TeX_Node *node, TEX_UNUSED int display) node->width = 1; } -void leftright_render(struct TeX_Node const * node, int x, int y, int color) +void leftright_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { int h = node->height, w = node->width, l = node->line; @@ -285,10 +303,10 @@ void supsubscript_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = l(0); } -void supsubscript_render(struct TeX_Node const * node, int x, int y, - int color) +void supsubscript_render(struct TeX_Node const * node, struct editContext * context, + int x, int y, int color) { - TeX_flow_render(args[0], x, y, color); + TeX_flow_render(args[0], context, x, y, color); } //--- @@ -306,8 +324,8 @@ void sum_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = 4; } -void sum_render(TEX_UNUSED struct TeX_Node const * node, int x, int y, - int color) +void sum_render(TEX_UNUSED struct TeX_Node const * node, struct editContext * context, + int x, int y, int color) { TeX_line(x, y, x + 8, y, color); TeX_line(x, y, x + 4, y + 4, color); @@ -330,7 +348,7 @@ void prod_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = 4; } -void prod_render(TEX_UNUSED struct TeX_Node const * node, int x, int y, +void prod_render(TEX_UNUSED struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { TeX_line(x, y, x + 8, y, color); @@ -353,7 +371,7 @@ void int_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = TEX_INT_HEIGHT >> 1; } -void int_render(struct TeX_Node const * node, int x, int y, int color) +void int_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { int h = node->height; @@ -397,7 +415,7 @@ void vec_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = l(0) + TEX_VEC_SPACING + arrow_height; } -void vec_render(struct TeX_Node const * node, int x, int y, int color) +void vec_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { int length = TEX_VEC_ARROW_LENGTH; int arrow_height = 2 * length + 1; @@ -411,6 +429,7 @@ void vec_render(struct TeX_Node const * node, int x, int y, int color) /* First argument */ TeX_flow_render(args[0], + context, x + TEX_VEC_MARGIN, y + TEX_VEC_SPACING + arrow_height, color); @@ -431,7 +450,7 @@ void lim_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = h >> 1; } -void lim_render(TEX_UNUSED struct TeX_Node const * node, int x, int y, +void lim_render(TEX_UNUSED struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { TeX_text("lim", x, y, color); @@ -492,7 +511,7 @@ void sqrt_layout(struct TeX_Node *node, TEX_UNUSED int display) node->line = l(0) + TEX_SQRT_MARGIN + 1; } -void sqrt_render(struct TeX_Node const * node, int x, int y, int color) +void sqrt_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { int slanted = TEX_SQRT_SLANTED && h(0) <= TEX_SQRT_SLANT_MAX; int w = node->width; @@ -509,6 +528,7 @@ void sqrt_render(struct TeX_Node const * node, int x, int y, int color) TeX_line(x + w - 1, y, x + w - 1, y + TEX_SQRT_BAR_LENGTH, color); TeX_flow_render(args[0], + context, x + base + TEX_SQRT_MARGIN, y + 1 + TEX_SQRT_MARGIN, color); diff --git a/src/env.c b/src/env.c index 3cabdb8..3354de4 100644 --- a/src/env.c +++ b/src/env.c @@ -63,10 +63,10 @@ static void primary_layout(struct TeX_Env *env, int display) env->line = p->flow->line; } -static void primary_render(struct TeX_Env *env, int x, int y, int color) +static void primary_render(struct TeX_Env *env, struct editContext * context, int x, int y, int color) { struct TeX_Env_Primary *p = (void *)env; - TeX_flow_render(p->flow, x, y, color); + TeX_flow_render(p->flow, context, x, y, color); } struct TeX_Env *TeX_env_primary(void) @@ -200,7 +200,7 @@ static void matrix_layout(struct TeX_Env *env, int display) env->line = env->height >> 1; } -static void matrix_render(struct TeX_Env *env, int x, int y, int color) +static void matrix_render(struct TeX_Env *env, struct editContext * context, int x, int y, int color) { struct TeX_Env_Matrix *m = (void *)env; @@ -218,6 +218,7 @@ static void matrix_render(struct TeX_Env *env, int x, int y, int color) int rh = m->rowline[row] + m->rowdepth[row]; if(flow) TeX_flow_render(flow, + context, x + dx + ((cw - flow->width) >> 1), y + dy + ((rh - flow->height) >> 1), color); diff --git a/src/flow.c b/src/flow.c index e740aa1..81dfa45 100644 --- a/src/flow.c +++ b/src/flow.c @@ -1,9 +1,13 @@ #include #include #include +#include +#include +#include #include #include +#include //--- // Flow construction and destruction functions @@ -19,6 +23,7 @@ struct TeX_Flow *TeX_flow_add_node(struct TeX_Flow *flow, struct TeX_Node*node) { flow->last->next = node; flow->last = node; + flow->elements++; return flow; } @@ -27,6 +32,7 @@ struct TeX_Flow *TeX_flow_add_node(struct TeX_Flow *flow, struct TeX_Node*node) if(!flow) return NULL; flow->first = flow->last = node; + flow->elements = 1; return flow; } @@ -378,15 +384,125 @@ void TeX_flow_layout(struct TeX_Flow *flow, int display) flow->width = max(x - TEX_LAYOUT_SPACING, 0); } +enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct editContext * context, enum cursorAction action) { + if (context->cursorFlow != flow) + { + // Search all the nodes in the flow for the cursor + struct TeX_Node* node = flow->first; + int offset = 0; + while (node != NULL) + { + int returnCode = move_cursor_node(node, context, action); + + if (returnCode == SUCCESS) + { + return SUCCESS; + } + else if (returnCode == CURSOR_NOT_HERE) + { + // Keep searching + } + else if (returnCode == CURSOR_PAST_END) + { + // Put the cursor in this flow after the node + context->elementIsText = false; + context->cursorFlow = flow; + context->offset = offset + 1; + return SUCCESS; + } + else if (returnCode == CURSOR_PAST_START) + { + // Put the cursor in this flow before the node + context->elementIsText = false; + context->cursorFlow = flow; + context->offset = offset; + return SUCCESS; + } + + node = node->next; + offset++; + } + return CURSOR_NOT_HERE; + } + + switch (action) + { + case CURSOR_MOVE_LEFT: + if (context->offset <= 0) + { + context->offset = 0; + return CURSOR_PAST_START; + } + else + { + // Find the node at offset + // As this is a linked list we have to iterate through it which is inefficient + // TODO: Change it to a normal array, or store the pointer to the node in the editContext (which will need a doubly linked list) + struct TeX_Node* node = flow->first; + for (int i = 0; i < context->offset - 1; i++) + { + node = node->next; + } + int returncode = cursor_enter_node(node, context, action); + if (returncode == CURSOR_PAST_START) + { + // Put the cursor in this flow before the node + context->offset--; + } + } + break; + case CURSOR_MOVE_RIGHT: + if (context->offset >= flow->elements) + { + context->offset = flow->elements; + return CURSOR_PAST_END; + } + else + { + // Find the node at offset + // As this is a linked list we have to interate through it which is inefficient + // TODO: Change it to a normal array, or store the pointer to the node in the editContext (which will need a doubly linked list) + struct TeX_Node* node = flow->first; + for (int i = 0; i < context->offset; i++) + { + node = node->next; + } + int returncode = cursor_enter_node(node, context, action); + if (returncode == CURSOR_PAST_END) + { + context->offset++; + } + } + break; + } + return SUCCESS; +} + /* TeX_flow_render(): Render a flow and all its components */ -void TeX_flow_render(struct TeX_Flow const * flow, int x, int y, int color) +void TeX_flow_render(struct TeX_Flow const * flow, struct editContext * context, int x, int y, int color) { struct TeX_Node const * node; + int offset = 0; for(node = flow->first; node; node = node->next) { - TeX_node_render(node, x + node->x, y + flow->line - - node->line + node->l, color); + if (context != NULL && !context->elementIsText && context->cursorFlow == flow && context->offset == offset) + { + // Render a cursor + context->cursorX = x + node->x; + context->cursorY = y + flow->line - 4; + } + + TeX_node_render(node, context, x + node->x, + y + flow->line - node->line + node->l, color); + offset++; + } + + if (context != NULL && !context->elementIsText &&context->cursorFlow == flow && context->offset == offset) + { + // Render a cursor + context->cursorX = x + flow->width; + context->cursorY = y + flow->line - 4; } } diff --git a/src/node.c b/src/node.c index a93f4eb..98ea3e0 100644 --- a/src/node.c +++ b/src/node.c @@ -3,7 +3,10 @@ #include #include #include +#include +#include +#include #include #include @@ -15,6 +18,117 @@ ((node)->type == TEX_NODECLASS_TEXT || \ (node)->type == TEX_NODECLASS_ENV) +enum cursorMoveResult move_cursor_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action) +{ + if (node->type == 0) + { + // Text + if (!context->elementIsText || context->cursorText != node) + return CURSOR_NOT_HERE; + + if (action == CURSOR_MOVE_LEFT) + { + if (context->offset <= 0) + return CURSOR_PAST_START; + + context->offset--; + return SUCCESS; + } + if (action == CURSOR_MOVE_RIGHT) { + if (context->offset >= strlen(node->text) - 2) + return CURSOR_PAST_END; + + context->offset++; + return SUCCESS; + } + } + if (node->type > 1) + { + if (node->args[0] == NULL) + return CURSOR_NOT_HERE; + + int returnCode = TeX_flow_cursor_action(node->args[0], context, action); + if (returnCode == SUCCESS) + return returnCode; + if (returnCode == CURSOR_PAST_END) + { + // Go to the denominator + if (node->args[1] == NULL) + return CURSOR_PAST_END; + + context->elementIsText = false; + context->cursorFlow = node->args[1]; + context->offset = 0; + return SUCCESS; + } + if (returnCode != CURSOR_NOT_HERE) + return returnCode; + + if (node->args[1] == NULL) + return CURSOR_NOT_HERE; + + returnCode = TeX_flow_cursor_action(node->args[1], context, action); + if (returnCode == CURSOR_PAST_START) + { + // Go to the numerator + context->elementIsText = false; + context->cursorFlow = node->args[0]; + context->offset = node->args[0]->elements; + return SUCCESS; + } + return returnCode; + } + // TODO: matrices + return CURSOR_NOT_HERE; +} + +enum cursorMoveResult cursor_enter_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action) +{ + enum cursorMoveResult skipPast = action == CURSOR_MOVE_RIGHT ? CURSOR_PAST_END : CURSOR_PAST_START; + + if (node->type == 0) + { + int len = strlen(node->text); + if (len == 1) return skipPast; + // Text + context->elementIsText = true; + context->cursorText = node; + context->offset = action == CURSOR_MOVE_RIGHT ? 0 : len - 2; + return SUCCESS; + } + if (node->type > 1) + { + if (action == CURSOR_MOVE_RIGHT) + { + // Enter the numerator + if (node->args[0] == NULL) + return CURSOR_PAST_END; + + context->elementIsText = false; + context->cursorFlow = node->args[0]; + context->offset = 0; + return SUCCESS; + } + else if (action == CURSOR_MOVE_LEFT) + { + // Enter the denominator + struct TeX_Flow *denom = node->args[1]; + if (denom == NULL) + denom = node->args[0]; + + if (denom == NULL) + return CURSOR_PAST_START; + + context->elementIsText = false; + context->cursorFlow = denom; + context->offset = denom->elements; + return SUCCESS; + } + } + // TODO: matrices + return skipPast; +} + /* TeX_node_text(): Make a text node */ struct TeX_Node *TeX_node_text(char const *utf8) { @@ -131,13 +245,13 @@ void TeX_node_layout(struct TeX_Node *node, int display) } /* TeX_node_render(): Render a node and its children */ -void TeX_node_render(struct TeX_Node const * node, int x, int y, int color) +void TeX_node_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { /* Don't render hidden or invalid elements */ if(node->hidden) return; /* For non trivial classes, use the class' special function */ - TeX_class_of(node)->render(node, x, y, color); + TeX_class_of(node)->render(node, context, x, y, color); } //--- -- 2.45.0 From 580e5e836913ae0333b80aaa0a75be0f6b6c05da Mon Sep 17 00:00:00 2001 From: Heath123 Date: Fri, 27 Jan 2023 14:58:33 +0000 Subject: [PATCH 2/9] Typo fix --- include/TeX/flow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/TeX/flow.h b/include/TeX/flow.h index b69b07b..076f81f 100644 --- a/include/TeX/flow.h +++ b/include/TeX/flow.h @@ -71,7 +71,7 @@ void TeX_flow_layout(struct TeX_Flow *flow, int display); This function performs a cursor action on a flow, like moving left or right. This is recursive, and should usually be called on the top-level flow. If it is called on a non-top-level flow, it will restrict the movement to - within the flow; the cursor will be to able to move within in but not exit. + within the flow; the cursor will be to able to move within it but not exit. Returns a cursorMoveResult enum; see edit.h for details of the return value. */ enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct editContext * context, enum cursorAction action); -- 2.45.0 From 4bf538e8806df5b46af881103838c19165e499c4 Mon Sep 17 00:00:00 2001 From: Heath123 Date: Fri, 27 Jan 2023 15:00:00 +0000 Subject: [PATCH 3/9] Don't say it's rendering the cursor where it's just setting the position --- src/classes.c | 2 +- src/flow.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/classes.c b/src/classes.c index 0695244..a5e21ea 100644 --- a/src/classes.c +++ b/src/classes.c @@ -41,7 +41,7 @@ void text_layout(struct TeX_Node *node, TEX_UNUSED int display) void text_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color) { if (context != NULL && context->elementIsText && context->cursorText == node) { - // Render a cursor + // Set the cursor position // Temporarily cap the length of the string to the cursor position char old = node->text[context->offset + 1]; node->text[context->offset + 1] = '\0'; diff --git a/src/flow.c b/src/flow.c index 81dfa45..62313bc 100644 --- a/src/flow.c +++ b/src/flow.c @@ -488,7 +488,7 @@ void TeX_flow_render(struct TeX_Flow const * flow, struct editContext * context, { if (context != NULL && !context->elementIsText && context->cursorFlow == flow && context->offset == offset) { - // Render a cursor + // Set the cursor position context->cursorX = x + node->x; context->cursorY = y + flow->line - 4; } @@ -500,7 +500,7 @@ void TeX_flow_render(struct TeX_Flow const * flow, struct editContext * context, if (context != NULL && !context->elementIsText &&context->cursorFlow == flow && context->offset == offset) { - // Render a cursor + // Set the cursor position context->cursorX = x + flow->width; context->cursorY = y + flow->line - 4; } -- 2.45.0 From d4c55b8e0fe62882b37434ef517dc97f1626409d Mon Sep 17 00:00:00 2001 From: Heath123 Date: Sun, 29 Jan 2023 14:00:19 +0000 Subject: [PATCH 4/9] Add comments --- src/flow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/flow.c b/src/flow.c index 62313bc..a266c55 100644 --- a/src/flow.c +++ b/src/flow.c @@ -385,6 +385,7 @@ void TeX_flow_layout(struct TeX_Flow *flow, int display) } enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct editContext * context, enum cursorAction action) { + // If this isn't the flow the cursor is in, continue the recursive search if (context->cursorFlow != flow) { // Search all the nodes in the flow for the cursor @@ -425,6 +426,7 @@ enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct edit return CURSOR_NOT_HERE; } + // If this is the flow the cursor is in, move the cursor switch (action) { case CURSOR_MOVE_LEFT: -- 2.45.0 From 58fa6d7049eeb219b299547c870bb36da9c654e1 Mon Sep 17 00:00:00 2001 From: Heath123 Date: Sun, 29 Jan 2023 14:14:05 +0000 Subject: [PATCH 5/9] More docs and rename a function --- include/TeX/flow.h | 15 ++++++++++----- include/TeX/node.h | 20 ++++++++++++++++++-- src/flow.c | 6 +++--- src/node.c | 4 ++-- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/TeX/flow.h b/include/TeX/flow.h index 076f81f..a37a123 100644 --- a/include/TeX/flow.h +++ b/include/TeX/flow.h @@ -67,6 +67,16 @@ void TeX_flow_free(struct TeX_Flow *flow); characters and subscripts/superscripts. Modifies the flow's metadata. */ void TeX_flow_layout(struct TeX_Flow *flow, int display); + +/* TeX_flow_render(): Render a flow and all its components + This function renders all horizontal objects in a flow. The layout of the + flow must have been computed. */ +void TeX_flow_render(struct TeX_Flow const * flow, struct editContext * context, int x, int y, int color); + +//--- +// Cursor and editing +//--- + /* TeX_flow_cursor_action(): Perform a cursor action on a flow This function performs a cursor action on a flow, like moving left or right. This is recursive, and should usually be called on the top-level flow. @@ -75,9 +85,4 @@ void TeX_flow_layout(struct TeX_Flow *flow, int display); Returns a cursorMoveResult enum; see edit.h for details of the return value. */ enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct editContext * context, enum cursorAction action); -/* TeX_flow_render(): Render a flow and all its components - This function renders all horizontal objects in a flow. The layout of the - flow must have been computed. */ -void TeX_flow_render(struct TeX_Flow const * flow, struct editContext * context, int x, int y, int color); - #endif /* TEX_FLOW */ diff --git a/include/TeX/node.h b/include/TeX/node.h index bf679fc..cc3d397 100644 --- a/include/TeX/node.h +++ b/include/TeX/node.h @@ -101,7 +101,23 @@ void TeX_node_layout(struct TeX_Node *node, int display); The node's layout must have been computed first. */ void TeX_node_render(struct TeX_Node const * node, struct editContext * context, int x, int y, int color); -enum cursorMoveResult move_cursor_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action); -enum cursorMoveResult cursor_enter_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action); +//--- +// Cursor and editing +//--- + +/* TeX_flow_cursor_action(): Perform a cursor action on a flow + See TeX_flow_cursor_action in flow.h for more information. + If this node contains flows, this will propagate the action to them, + and nagivate between them. by updating the cursor context. */ +enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editContext * context, enum cursorAction action); + +/* TeX_node_cursor_enter(): Enter a node + This function is called when the cursor navigates into a node. + If the node is just a simple symbol or character, this will return + CURSOR_PAST_END/START, and the parent flow will skip over the node. + If the node contains flows that can be navigated into, this will usually + nagivate into the first/last flow depending on the direction that the + cursor enters from, and return SUCCESS. */ +enum cursorMoveResult TeX_node_cursor_enter(struct TeX_Node *node, struct editContext * context, enum cursorAction action); #endif /* TEX_NODE */ diff --git a/src/flow.c b/src/flow.c index a266c55..6f873ba 100644 --- a/src/flow.c +++ b/src/flow.c @@ -393,7 +393,7 @@ enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct edit int offset = 0; while (node != NULL) { - int returnCode = move_cursor_node(node, context, action); + int returnCode = TeX_node_cursor_action(node, context, action); if (returnCode == SUCCESS) { @@ -445,7 +445,7 @@ enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct edit { node = node->next; } - int returncode = cursor_enter_node(node, context, action); + int returncode = TeX_node_cursor_enter(node, context, action); if (returncode == CURSOR_PAST_START) { // Put the cursor in this flow before the node @@ -469,7 +469,7 @@ enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct edit { node = node->next; } - int returncode = cursor_enter_node(node, context, action); + int returncode = TeX_node_cursor_enter(node, context, action); if (returncode == CURSOR_PAST_END) { context->offset++; diff --git a/src/node.c b/src/node.c index 98ea3e0..f1d3903 100644 --- a/src/node.c +++ b/src/node.c @@ -18,7 +18,7 @@ ((node)->type == TEX_NODECLASS_TEXT || \ (node)->type == TEX_NODECLASS_ENV) -enum cursorMoveResult move_cursor_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action) +enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editContext * context, enum cursorAction action) { if (node->type == 0) { @@ -82,7 +82,7 @@ enum cursorMoveResult move_cursor_node(struct TeX_Node *node, struct editContext return CURSOR_NOT_HERE; } -enum cursorMoveResult cursor_enter_node(struct TeX_Node *node, struct editContext * context, enum cursorAction action) +enum cursorMoveResult TeX_node_cursor_enter(struct TeX_Node *node, struct editContext * context, enum cursorAction action) { enum cursorMoveResult skipPast = action == CURSOR_MOVE_RIGHT ? CURSOR_PAST_END : CURSOR_PAST_START; -- 2.45.0 From af6f449b1987594233660e9eac339cd0f249a8ca Mon Sep 17 00:00:00 2001 From: Heath123 Date: Sun, 29 Jan 2023 14:21:16 +0000 Subject: [PATCH 6/9] Better comments, again --- src/node.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/node.c b/src/node.c index f1d3903..42207d4 100644 --- a/src/node.c +++ b/src/node.c @@ -86,24 +86,28 @@ enum cursorMoveResult TeX_node_cursor_enter(struct TeX_Node *node, struct editCo { enum cursorMoveResult skipPast = action == CURSOR_MOVE_RIGHT ? CURSOR_PAST_END : CURSOR_PAST_START; + // Text if (node->type == 0) { int len = strlen(node->text); if (len == 1) return skipPast; - // Text context->elementIsText = true; context->cursorText = node; context->offset = action == CURSOR_MOVE_RIGHT ? 0 : len - 2; return SUCCESS; } - if (node->type > 1) + // Regular nodes + else if (node->type > 1) { if (action == CURSOR_MOVE_RIGHT) { - // Enter the numerator + // Enter the first child flow + + // If there is no child, skip past this node if (node->args[0] == NULL) return CURSOR_PAST_END; + // Update the cursor to be in the right place context->elementIsText = false; context->cursorFlow = node->args[0]; context->offset = 0; @@ -111,14 +115,19 @@ enum cursorMoveResult TeX_node_cursor_enter(struct TeX_Node *node, struct editCo } else if (action == CURSOR_MOVE_LEFT) { - // Enter the denominator + // Enter the last child flow + struct TeX_Flow *denom = node->args[1]; + + // If there is only one child, go to it instead if (denom == NULL) denom = node->args[0]; + // If there is no child, skip past this node if (denom == NULL) return CURSOR_PAST_START; + // Update the cursor to be in the right place context->elementIsText = false; context->cursorFlow = denom; context->offset = denom->elements; -- 2.45.0 From 15f038aa855414156ab7df6b439c5518ff3bfe86 Mon Sep 17 00:00:00 2001 From: Heath123 Date: Sun, 29 Jan 2023 14:36:12 +0000 Subject: [PATCH 7/9] Better comments, again (again) --- src/node.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/node.c b/src/node.c index 42207d4..0bb2395 100644 --- a/src/node.c +++ b/src/node.c @@ -20,9 +20,12 @@ enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editContext * context, enum cursorAction action) { + // Text + // TODO: Store the length of the text in the node + // THat way we won't have to call strlen every time if (node->type == 0) { - // Text + // If this isn't where the cursor is, continue the recursive search if (!context->elementIsText || context->cursorText != node) return CURSOR_NOT_HERE; @@ -34,7 +37,14 @@ enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editC context->offset--; return SUCCESS; } - if (action == CURSOR_MOVE_RIGHT) { + else if (action == CURSOR_MOVE_RIGHT) { + // We take away 2 because offset 0 is actually one character into the text, + // and the last possible offset is one character before the end of the text, + // because if the cursor is at the start or end it goes in the parent flow. + + // TODO: It would make more sense to still have offset 0 be the first character, + // but just make sure that offset is never actually set to 0 before the cursor + // returns to the parent flow. if (context->offset >= strlen(node->text) - 2) return CURSOR_PAST_END; @@ -42,35 +52,43 @@ enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editC return SUCCESS; } } - if (node->type > 1) + // Regular nodes + else if (node->type > 1) { + // If this isn't where the cursor is, continue the recursive search if (node->args[0] == NULL) return CURSOR_NOT_HERE; + // Propagate the cursor action to the first child flow int returnCode = TeX_flow_cursor_action(node->args[0], context, action); if (returnCode == SUCCESS) return returnCode; - if (returnCode == CURSOR_PAST_END) + else if (returnCode == CURSOR_PAST_END) { - // Go to the denominator + // If it moved past the end of the first child, move to the second child + + // If there is no second child, leave the node if (node->args[1] == NULL) return CURSOR_PAST_END; - + + // Update the cursor to be in the right place context->elementIsText = false; context->cursorFlow = node->args[1]; context->offset = 0; return SUCCESS; } - if (returnCode != CURSOR_NOT_HERE) + else if (returnCode != CURSOR_NOT_HERE) return returnCode; if (node->args[1] == NULL) return CURSOR_NOT_HERE; + // Propagate the cursor action to the second child flow returnCode = TeX_flow_cursor_action(node->args[1], context, action); if (returnCode == CURSOR_PAST_START) { - // Go to the numerator + // If it moved past the start of the second child, move to the first child + // The first child should always exist if the second child exists so we don't need to check context->elementIsText = false; context->cursorFlow = node->args[0]; context->offset = node->args[0]->elements; -- 2.45.0 From 12b85993703dde9ddcd2b8bdb14b2029de1b37d9 Mon Sep 17 00:00:00 2001 From: Heath123 Date: Sat, 25 Feb 2023 17:52:11 +0000 Subject: [PATCH 8/9] Make the cursor enter text nodes when it's at the start or end of one --- src/classes.c | 6 +++--- src/flow.c | 14 ++++++++++++++ src/node.c | 14 +++----------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/classes.c b/src/classes.c index a5e21ea..4552ce4 100644 --- a/src/classes.c +++ b/src/classes.c @@ -43,13 +43,13 @@ void text_render(struct TeX_Node const * node, struct editContext * context, int if (context != NULL && context->elementIsText && context->cursorText == node) { // Set the cursor position // Temporarily cap the length of the string to the cursor position - char old = node->text[context->offset + 1]; - node->text[context->offset + 1] = '\0'; + char old = node->text[context->offset]; + node->text[context->offset] = '\0'; // Measure the width of the string int w, h; TeX_size((char const *)node->text, &w, &h); // Restore the string - node->text[context->offset + 1] = old; + node->text[context->offset] = old; // Update the cursor position to inside the text context->cursorX = x + w; context->cursorY = y; diff --git a/src/flow.c b/src/flow.c index 6f873ba..70b2976 100644 --- a/src/flow.c +++ b/src/flow.c @@ -405,18 +405,32 @@ enum cursorMoveResult TeX_flow_cursor_action(struct TeX_Flow * flow, struct edit } else if (returnCode == CURSOR_PAST_END) { + bool wasText = context->elementIsText; // Put the cursor in this flow after the node context->elementIsText = false; context->cursorFlow = flow; context->offset = offset + 1; + // If we just left a text node, do the cursor action again + // This is because if we just put the cursor after the text + // node then it will look like it's just at the end of it + // and also it will be immedately put back in again + if (wasText) + { + return TeX_flow_cursor_action(flow, context, action); + } return SUCCESS; } else if (returnCode == CURSOR_PAST_START) { + bool wasText = context->elementIsText; // Put the cursor in this flow before the node context->elementIsText = false; context->cursorFlow = flow; context->offset = offset; + if (wasText) + { + return TeX_flow_cursor_action(flow, context, action); + } return SUCCESS; } diff --git a/src/node.c b/src/node.c index 0bb2395..533b406 100644 --- a/src/node.c +++ b/src/node.c @@ -22,7 +22,7 @@ enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editC { // Text // TODO: Store the length of the text in the node - // THat way we won't have to call strlen every time + // That way we won't have to call strlen every time if (node->type == 0) { // If this isn't where the cursor is, continue the recursive search @@ -38,14 +38,7 @@ enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editC return SUCCESS; } else if (action == CURSOR_MOVE_RIGHT) { - // We take away 2 because offset 0 is actually one character into the text, - // and the last possible offset is one character before the end of the text, - // because if the cursor is at the start or end it goes in the parent flow. - - // TODO: It would make more sense to still have offset 0 be the first character, - // but just make sure that offset is never actually set to 0 before the cursor - // returns to the parent flow. - if (context->offset >= strlen(node->text) - 2) + if (context->offset >= strlen(node->text)) return CURSOR_PAST_END; context->offset++; @@ -108,10 +101,9 @@ enum cursorMoveResult TeX_node_cursor_enter(struct TeX_Node *node, struct editCo if (node->type == 0) { int len = strlen(node->text); - if (len == 1) return skipPast; context->elementIsText = true; context->cursorText = node; - context->offset = action == CURSOR_MOVE_RIGHT ? 0 : len - 2; + context->offset = action == CURSOR_MOVE_RIGHT ? 0 : len; return SUCCESS; } // Regular nodes -- 2.45.0 From e8d249d0011ef21000e7b5f82ebf4685e1e87f47 Mon Sep 17 00:00:00 2001 From: Heath123 Date: Fri, 3 Mar 2023 13:25:41 +0000 Subject: [PATCH 9/9] Make text nodes store the string length --- include/TeX/node.h | 7 +++++-- src/node.c | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/TeX/node.h b/include/TeX/node.h index cc3d397..a0aad51 100644 --- a/include/TeX/node.h +++ b/include/TeX/node.h @@ -30,8 +30,11 @@ struct TeX_Node struct TeX_Node *next; union { - /* Plain text content encoded as UTF-8 */ - uint8_t *text; + /* Plain text content encoded as UTF-8 with length */ + struct { + uint8_t *text; + size_t text_len; + }; /* Environment pointer */ struct TeX_Env *env; /* Functional arguments */ diff --git a/src/node.c b/src/node.c index 533b406..d05aca1 100644 --- a/src/node.c +++ b/src/node.c @@ -21,8 +21,7 @@ enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editContext * context, enum cursorAction action) { // Text - // TODO: Store the length of the text in the node - // That way we won't have to call strlen every time + // TODO: Make sure the editing works with UTF-8 (it probably doesn't as-is) if (node->type == 0) { // If this isn't where the cursor is, continue the recursive search @@ -38,7 +37,7 @@ enum cursorMoveResult TeX_node_cursor_action(struct TeX_Node *node, struct editC return SUCCESS; } else if (action == CURSOR_MOVE_RIGHT) { - if (context->offset >= strlen(node->text)) + if (context->offset >= node->text_len) return CURSOR_PAST_END; context->offset++; @@ -100,10 +99,9 @@ enum cursorMoveResult TeX_node_cursor_enter(struct TeX_Node *node, struct editCo // Text if (node->type == 0) { - int len = strlen(node->text); context->elementIsText = true; context->cursorText = node; - context->offset = action == CURSOR_MOVE_RIGHT ? 0 : len; + context->offset = action == CURSOR_MOVE_RIGHT ? 0 : node->text_len; return SUCCESS; } // Regular nodes @@ -155,9 +153,11 @@ struct TeX_Node *TeX_node_text(char const *utf8) if(!node) return NULL; /* TODO: Don't use strdup(); convert from utf8 instead */ - node->text = malloc(strlen(utf8) + 1); + size_t len = strlen(utf8); + node->text = malloc(len + 1); if(!node->text) { free(node); return NULL; } strcpy((void *)node->text, utf8); + node->text_len = len; node->type = TEX_NODECLASS_TEXT; return node; -- 2.45.0