summaryrefslogtreecommitdiff
path: root/src/ui_elements
diff options
context:
space:
mode:
authorPhysick <96335032+DegustatorPonos@users.noreply.github.com>2026-07-11 17:57:38 +0500
committerPhysick <96335032+DegustatorPonos@users.noreply.github.com>2026-07-11 17:57:38 +0500
commit64e23a3455ef1e71786c4b3a835a91954e08ab79 (patch)
tree266d56ed56a7bedea6a2187689fac522dab95db9 /src/ui_elements
parent55ef21312888cbfb9d449eee8f7958b74f85ac7c (diff)
Editor position work
Diffstat (limited to 'src/ui_elements')
-rw-r--r--src/ui_elements/TextEditor.c187
-rw-r--r--src/ui_elements/UI.h28
2 files changed, 167 insertions, 48 deletions
diff --git a/src/ui_elements/TextEditor.c b/src/ui_elements/TextEditor.c
index fa85443..620abea 100644
--- a/src/ui_elements/TextEditor.c
+++ b/src/ui_elements/TextEditor.c
@@ -7,14 +7,23 @@
#include <stdlib.h>
#include <string.h>
-void HighlightCharacter(Vector2 lineStartPos, char *line, int index, Font *font, int height, int spacing) {
+float GetCursorXPos(TextArea *ta) {
+ int index = ta->CursorPosition.x;
+ char *line = ta->CurrentNode->text;
float x_position = 0;
for (int i = 0; i < index; i++) {
- x_position += MeasureChar(font, height, line[i]);
- x_position += spacing;
+ x_position += MeasureChar(ta->DisplayParams->font,
+ ta->DisplayParams->font_size,
+ line[i]);
+ x_position += ta->DisplayParams->spacing;
}
+ return x_position;
+}
+
+/// We pass x_position not to call GetCursorXPos twice (it's expencive)
+void DrawCursor(TextArea *ta, Vector2 lineStartPos, float x_position) {
DrawLine(lineStartPos.x + x_position, lineStartPos.y,
- lineStartPos.x + x_position, lineStartPos.y + height, COLORSCHEME_BROWN);
+ lineStartPos.x + x_position, lineStartPos.y + ta->DisplayParams->font_size, COLORSCHEME_BROWN);
}
TextNode *CreateNode(TextNode *prev, TextNode *next) {
@@ -77,7 +86,7 @@ void AppendTextToNode(TextNode *base, char *new_text) {
base->len += new_len;
}
-TextArea *CreateTextArea() {
+TextArea *CreateTextArea(TextAreaDisplayParams *DisplayParams) {
TextArea *outp = malloc(sizeof(TextArea));
if (outp == NULL) return NULL;
@@ -90,9 +99,31 @@ TextArea *CreateTextArea() {
outp->StartNode = start_node;
outp->CurrentNode = start_node;
outp->NodeLenth = 1;
+ outp->DisplayParams = DisplayParams;
return outp;
}
+void EnsureCursorIsInFrame(TextArea *ta) {
+ TextAreaDisplayParams *params = ta->DisplayParams;
+ Vector2 cursorPos = {
+ .x = GetCursorXPos(ta),
+ .y = ta->CursorPosition.y * ta->DisplayParams->font_size,
+ };
+
+ if (cursorPos.y < params->scroll_v) {
+ params->scroll_v = cursorPos.y;
+ } else if (params->scroll_v + params->_area_height - params->font_size < cursorPos.y) {
+ params->scroll_v = cursorPos.y + params->_area_height;
+ }
+
+ if (cursorPos.x < params->scroll_h) {
+ params->scroll_h = cursorPos.x - (params->_area_width / 2.0f);
+ if (params->scroll_h < 0) params->scroll_h = 0;
+ } else if (params->scroll_h + params->_area_width - params->font_size < cursorPos.x) {
+ params->scroll_h = cursorPos.x - (params->_area_width / 2.0f);
+ }
+}
+
bool PushNode(TextArea *ta) {
if (ta == NULL || ta->CurrentNode == NULL) return false;
TextNode *new_node = CreateNode(ta->CurrentNode, ta->CurrentNode->NextNode);
@@ -134,6 +165,7 @@ void PopCurrent(TextArea *ta) {
void FreeTextArea(TextArea *ta) {
FreeNodeRec(ta->StartNode);
+ if (ta->DisplayParams != NULL) free(ta->DisplayParams);
free(ta);
}
@@ -143,6 +175,26 @@ char GetCurrentChar(TextArea *ta) {
return ta->CurrentNode->text[(int)ta->CursorPosition.x];
}
+void MoveCursorDown(TextArea *ta) {
+ if (ta == NULL || ta->CurrentNode == NULL) return;
+ if (ta->CurrentNode->NextNode == NULL) return;
+ ta->CurrentNode = ta->CurrentNode->NextNode;
+
+ ta->CursorPosition.y += 1;
+ size_t line_len = ta->CurrentNode->len;
+ if (ta->CursorPosition.x > line_len) ta->CursorPosition.x = line_len;
+}
+
+void MoveCursorUp(TextArea *ta) {
+ if (ta == NULL || ta->CurrentNode == NULL) return;
+ if (ta->CursorPosition.y == 0 || ta->CurrentNode->PrevNode == NULL) return;
+ ta->CurrentNode = ta->CurrentNode->PrevNode;
+ ta->CursorPosition.y -= 1;
+
+ size_t line_len = ta->CurrentNode->len;
+ if (ta->CursorPosition.x > line_len) ta->CursorPosition.x = line_len;
+}
+
/// This function is used in the skips through words (<C-arrows>)
bool IsSkippable(char c) {
return isalpha(c) || isdigit(c);
@@ -153,7 +205,14 @@ void MoveCursorRight(TextArea *ta) {
ta->CursorPosition.x += 1;
size_t line_len = ta->CurrentNode->len;
- if (ta->CursorPosition.x > line_len) ta->CursorPosition.x = line_len;
+ if (ta->CursorPosition.x > line_len) {
+ if (ta->CurrentNode->NextNode == NULL) {
+ ta->CursorPosition.x = line_len;
+ return;
+ }
+ MoveCursorDown(ta);
+ ta->CursorPosition.x = 0;
+ }
}
void MoveCursorRightWord(TextArea *ta) {
@@ -163,7 +222,14 @@ void MoveCursorRightWord(TextArea *ta) {
} while (IsSkippable(GetCurrentChar(ta)));
size_t line_len = ta->CurrentNode->len;
- if (ta->CursorPosition.x > line_len) ta->CursorPosition.x = line_len;
+ if (ta->CursorPosition.x > line_len) {
+ if (ta->CurrentNode->NextNode == NULL) {
+ ta->CursorPosition.x = line_len;
+ return;
+ }
+ MoveCursorDown(ta);
+ ta->CursorPosition.x = 0;
+ }
}
void MoveCursorEnd(TextArea *ta) {
@@ -174,7 +240,15 @@ void MoveCursorEnd(TextArea *ta) {
void MoveCursorLeft(TextArea *ta) {
if (ta == NULL || ta->CurrentNode == NULL) return;
ta->CursorPosition.x -= 1;
- if (ta->CursorPosition.x < 0) ta->CursorPosition.x = 0;
+
+ if (ta->CursorPosition.x < 0) {
+ if (ta->CursorPosition.y == 0 || ta->CurrentNode->PrevNode == NULL) {
+ ta->CursorPosition.x = 0;
+ return;
+ }
+ MoveCursorUp(ta);
+ MoveCursorEnd(ta);
+ }
}
void MoveCursorLeftWord(TextArea *ta) {
@@ -183,8 +257,14 @@ void MoveCursorLeftWord(TextArea *ta) {
ta->CursorPosition.x -= 1;
} while (IsSkippable(GetCurrentChar(ta)) && ta->CursorPosition.x > 0);
- size_t line_len = ta->CurrentNode->len;
- if (ta->CursorPosition.x > line_len) ta->CursorPosition.x = line_len;
+ if (ta->CursorPosition.x < 0) {
+ if (ta->CursorPosition.y == 0 || ta->CurrentNode->PrevNode == NULL) {
+ ta->CursorPosition.x = 0;
+ return;
+ }
+ MoveCursorUp(ta);
+ MoveCursorEnd(ta);
+ }
}
void MoveCursorHome(TextArea *ta) {
@@ -192,26 +272,6 @@ void MoveCursorHome(TextArea *ta) {
ta->CursorPosition.x = 0;
}
-void MoveCursorDown(TextArea *ta) {
- if (ta == NULL || ta->CurrentNode == NULL) return;
- if (ta->CurrentNode->NextNode == NULL) return;
- ta->CurrentNode = ta->CurrentNode->NextNode;
-
- ta->CursorPosition.y += 1;
- size_t line_len = ta->CurrentNode->len;
- if (ta->CursorPosition.x > line_len) ta->CursorPosition.x = line_len;
-}
-
-void MoveCursorUp(TextArea *ta) {
- if (ta == NULL || ta->CurrentNode == NULL) return;
- if (ta->CursorPosition.y == 0 || ta->CurrentNode->PrevNode == NULL) return;
- ta->CurrentNode = ta->CurrentNode->PrevNode;
- ta->CursorPosition.y -= 1;
-
- size_t line_len = ta->CurrentNode->len;
- if (ta->CursorPosition.x > line_len) ta->CursorPosition.x = line_len;
-}
-
void BreakLine(TextArea *ta) {
if (ta == NULL || ta->CurrentNode == NULL) return;
if (ta->CursorPosition.x == 0) {
@@ -245,10 +305,12 @@ void BreakLine(TextArea *ta) {
ta->CursorPosition.y += 1;
ta->CursorPosition.x = 0;
ta->CurrentNode = ta->CurrentNode->NextNode;
+ EnsureCursorIsInFrame(ta);
}
void InsertChar(TextArea *ta, char new_char) {
if (ta == NULL || ta->CurrentNode == NULL) return;
+ EnsureCursorIsInFrame(ta);
size_t line_len = ta->CurrentNode->len;
if (line_len + 2 > ta->CurrentNode->cap) {
@@ -300,6 +362,7 @@ void DeleteChar(TextArea *ta) {
ta->CursorPosition.x -= 1;
ta->CurrentNode->len -= 1;
if (ta->CurrentNode->len < 0) ta->CurrentNode->len = 0;
+ EnsureCursorIsInFrame(ta);
}
bool IsKeyPressedFix(int key) {
@@ -322,6 +385,16 @@ void HandleControlComands(TextArea *ta) {
if (IsKeyPressedFix(KEY_RIGHT)) MoveCursorRightWord(ta);
if (IsKeyPressedFix(KEY_LEFT)) MoveCursorLeftWord(ta);
+ if (IsKeyPressedFix(KEY_EQUAL)) {
+ if (ta->DisplayParams->font_size < MAX_FONT_SIZE)
+ ta->DisplayParams->font_size += 1;
+ }
+
+ if (IsKeyPressedFix(KEY_MINUS)) {
+ if (ta->DisplayParams->font_size > MIN_FONT_SIZE)
+ ta->DisplayParams->font_size -= 1;
+ }
+
// emacs-like stuff
if (IsKeyPressedFix(KEY_F)) MoveCursorRight(ta);
if (IsKeyPressedFix(KEY_B)) MoveCursorLeft(ta);
@@ -380,27 +453,57 @@ void HandleKeyboard(TextArea *ta) {
if (IsKeyPressedFix(KEY_F3)) PrintAllLines(ta);
}
+/// Assumes that the cursor is in the scroll area
+void HandleScroll(TextArea *ta, Rectangle rec) {
+ int scroll = GetMouseWheelMove() * SCROLL_SENC;
+ if (IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT)) {
+ ta->DisplayParams->scroll_h -= scroll;
+ if (ta->DisplayParams->scroll_h < 0)
+ ta->DisplayParams->scroll_h = 0;
+ // TODO: figure out if we can easily get max width. Probably need some
+ // lazy eval caching
+ } else {
+ int text_height = ta->NodeLenth * ta->DisplayParams->font_size;
+ int max_scroll = text_height - rec.height;
+ ta->DisplayParams->scroll_v -= scroll;
+ if (ta->DisplayParams->scroll_v < 0)
+ ta->DisplayParams->scroll_v = 0;
+ if (text_height > rec.height && ta->DisplayParams->scroll_v > max_scroll)
+ ta->DisplayParams->scroll_v = max_scroll;
+ }
+}
+
/// draws a text area on the screen. Main draw entry point
-void DrawTextArea(TextArea *ta, Rectangle rec, Font *font, int height, int spacing, int *scroll) {
- int vertical_acc = rec.y - *scroll;
- int text_height = ta->NodeLenth * height;
- int max_scroll = text_height - rec.height;
+void DrawTextArea(TextArea *ta, Rectangle rec) {
+ // those are used to move a cursor in frame when typing
+ ta->DisplayParams->_area_height = rec.height;
+ ta->DisplayParams->_area_width = rec.width;
- if (text_height >= rec.height && IsMouseInRec(rec)) {
- *scroll -= GetMouseWheelMove() * SCROLL_SENC;
- if (*scroll < 0) *scroll = 0;
- if (*scroll > max_scroll) *scroll = max_scroll;
+ TextAreaDisplayParams *params = ta->DisplayParams;
+ // int vertical_acc = rec.y - *scroll;
+
+ if (IsMouseInRec(rec)) {
+ HandleScroll(ta, rec);
}
- Vector2 pos = { .x = rec.x + spacing, .y = rec.y - *scroll };
+ Vector2 pos = {
+ .x = rec.x + params->spacing - params->scroll_h,
+ .y = rec.y + params->spacing - params->scroll_v
+ };
+
+ Vector2 cursorPos = {
+ .x = GetCursorXPos(ta),
+ .y = ta->CursorPosition.y * ta->DisplayParams->font_size,
+ };
+
TextNode *node = ta->StartNode;
BeginScissorMode(rec.x, rec.y, rec.width, rec.height);
while (node != NULL) {
- DrawTextEx(*font, node->text, pos, height, spacing, COLORSCHEME_BROWN);
+ DrawTextEx(*params->font, node->text, pos, params->font_size, params->spacing, COLORSCHEME_BROWN);
if (node == ta->CurrentNode)
- HighlightCharacter(pos, node->text, ta->CursorPosition.x, font, height, spacing);
+ DrawCursor(ta, pos, cursorPos.x);
node = node->NextNode;
- pos.y += height;
+ pos.y += params->font_size;
}
EndScissorMode();
}
diff --git a/src/ui_elements/UI.h b/src/ui_elements/UI.h
index eaf17e8..9f126c3 100644
--- a/src/ui_elements/UI.h
+++ b/src/ui_elements/UI.h
@@ -7,6 +7,9 @@
static const float SCROLL_SENC = 10;
static const float TAB_WIDTH = 4;
+static const int MIN_FONT_SIZE = 10;
+static const int MAX_FONT_SIZE = 100;
+
void DrawRecBordered(Rectangle rec, Color bg, Color border_color, int border_width);
void DrawTextInRec(Rectangle rec, char *text, Font *font, int height, Color color, float padding, int *scroll);
float MeasureChar(Font *font, int height, char symbol);
@@ -24,6 +27,21 @@ typedef struct {
size_t cap;
} TextNode;
+
+typedef struct {
+ Font *font;
+ int font_size;
+ int spacing;
+
+ int scroll_h;
+ int scroll_v;
+
+ /// DO NOT MODIFY THAT! It's for internal use only
+ int _area_width;
+ /// DO NOT MODIFY THAT! It's for internal use only
+ int _area_height;
+} TextAreaDisplayParams;
+
typedef struct {
TextNode *StartNode;
/// For the quick access
@@ -31,17 +49,15 @@ typedef struct {
size_t NodeLenth;
Vector2 CursorPosition;
+ TextAreaDisplayParams *DisplayParams;
} TextArea;
-TextArea *CreateTextArea();
-void FreeTextArea(TextArea *ta);
-void DrawTextArea(TextArea *ta, Rectangle rec, Font *font, int height, int spacing, int *scroll);
+TextArea *CreateTextArea(TextAreaDisplayParams *DisplayParams);
+void DrawTextArea(TextArea *ta, Rectangle rec);
void InsertChar(TextArea *ta, char new_char);
void DeleteChar(TextArea *ta);
void HandleKeyboard(TextArea *ta);
-void MoveCursorRight(TextArea *ta);
-void MoveCursorLeft(TextArea *ta);
-void MoveCursorDown(TextArea *ta);
+void FreeTextArea(TextArea *ta);
#endif // UI_ELEMENTS_H