From ee0facda49bf0acf6f1402538a7974aed2ab4cf0 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Sun, 24 Nov 2024 15:34:09 +0100 Subject: Implement horizontal versus vertical scrolling strategy --- README.md | 5 +++ build.sh | 21 +++--------- chatty.c | 65 +++++++++++++++++++++---------------- ui.h | 110 +++++++++++++++++++++++++++++++++++++++++++------------------- 4 files changed, 123 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 74554de..fa561b6 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,8 @@ The idea is the following: - *unicode and wide characters*: [C for dummies](https://c-for-dummies.com/blog/?p=2578) - *sockets*: [Nir Lichtman - Making Minimalist Chat Server in C on Linux](https://www.youtube.com/watch?v=gGfTjKwLQxY) - syscall manpages `man` + +- https://www.youtube.com/watch?v=wvtFGa6XJDU +- https://nullprogram.com/blog/2023/02/11/ +- https://nullprogram.com/blog/2023/02/13/ +- https://nullprogram.com/blog/2023/10/08/ diff --git a/build.sh b/build.sh index 4548af8..9d69974 100755 --- a/build.sh +++ b/build.sh @@ -1,17 +1,6 @@ #!/bin/sh -build () { - ( - set -x - gcc -ggdb -Wall -pedantic -std=c99 -I./external -o ${1%.c} $@ - ) -} - -if [ "$1" ]; then - build "$1" - exit -fi - -[ -x ./external/keyboard ] || build external/keyboard.c -build chatty.c -build server.c -build send.c +set -x +gcc external/keyboard.c +gcc -DDEBUG -ggdb -Wall -pedantic -std=c99 -o chatty chatty.c +gcc -DDEBUG -ggdb -Wall -pedantic -std=c99 -o server server.c +gcc -DDEBUG -ggdb -Wall -pedantic -std=c99 -o send send.c diff --git a/chatty.c b/chatty.c index 9dcc2b0..ae7929f 100644 --- a/chatty.c +++ b/chatty.c @@ -22,8 +22,6 @@ // Number of spaces inserted when pressing Tab/Ctrl+I #define TAB_WIDTH 4 -#define DEBUG - #include "chatty.h" #include "protocol.h" #include "ui.h" @@ -278,21 +276,49 @@ run_command_get_output(char *Command, char *Argv[], u8 *OutputBuffer, int Len) // it displays a prompt with the user input of input_len wide characters // and the received messages from msgsArena void -screen_home(Arena* ScratchArena, +DisplayChat(Arena* ScratchArena, Arena* MessagesArena, u32 MessagesNum, Arena* ClientsArena, struct pollfd* fds, wchar_t Input[], u32 InputLen) { - u32 BoxHeight = 3; +#define MIN_TEXT_WIDTH_FOR_WRAPPING 20 + u32 BoxHeight = GetInputBoxMinimumHeight(); + u32 MinBoxWidth = GetInputBoxMinimumWidth(); + u32 BoxWidth = global.width - 1; + u32 InputBoxTextWidth = BoxWidth - MinBoxWidth + 2; - if (global.height < BoxHeight || - global.width < 8) + if (InputLen >= InputBoxTextWidth && + InputBoxTextWidth > MIN_TEXT_WIDTH_FOR_WRAPPING) + { + BoxHeight++; + } + + u32 FreeHeight = global.height - BoxHeight; + +#undef MIN_TEXT_WIDTH_FOR_WRAPPING + + if (global.height < BoxHeight || global.width < MinBoxWidth) { tb_hide_cursor(); return; } + bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]); + InputBox(0, FreeHeight, BoxWidth, BoxHeight, + Input, InputLen, + True); + + // Print vertical bar + s32 VerticalBarOffset = TIMESTAMP_LEN + AUTHOR_LEN + 2; + for (u32 Y = 0; Y < FreeHeight; Y++) + tb_print(VerticalBarOffset, Y, 0, 0, "│"); + + // show error popup if server disconnected + if (fds[FDS_UNI].fd == -1 || fds[FDS_BI].fd == -1) + { + popup(TB_RED, TB_BLACK, (u8*)"Server disconnected."); + } // Print messages in msgsArena, if there are too many to display, start printing from an offset. // Looks like this: @@ -301,11 +327,9 @@ screen_home(Arena* ScratchArena, // 03:24:33 [TlasT] │ I am fine // 03:24:33 [Fin] │ I am too { - s32 VerticalBarOffset = TIMESTAMP_LEN + AUTHOR_LEN + 2; - u32 FreeHeight = global.height - BoxHeight; - if (FreeHeight <= 0) - goto draw_prompt; + // If there is not enough space to draw, do not draw + if (FreeHeight <= 0) return; // Used to go to the next message in MessagesArena by incrementing with the messages' size. u8* MessageAddress = MessagesArena->addr; @@ -441,22 +465,6 @@ screen_home(Arena* ScratchArena, } } - // Print vertical bar - for (u32 Y = 0; Y < FreeHeight; Y++) - tb_print(VerticalBarOffset, Y, 0, 0, "│"); - - draw_prompt: - InputBox(0, FreeHeight, BoxWidth, BoxHeight, - Input, InputLen, - True); - bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]); - - - if (fds[FDS_UNI].fd == -1 || fds[FDS_BI].fd == -1) - { - // show error popup - popup(TB_RED, TB_BLACK, (u8*)"Server disconnected."); - } } } @@ -567,7 +575,7 @@ main(int argc, char** argv) tb_init(); tb_get_fds(&fds[FDS_TTY].fd, &fds[FDS_RESIZE].fd); - screen_home(&ScratchArena, + DisplayChat(&ScratchArena, &MessagesArena, MessagesNum, &ClientsArena, fds, Input, InputIndex); @@ -713,6 +721,7 @@ main(int argc, char** argv) } break; case TB_KEY_BACKSPACE2: if (InputIndex) InputIndex--; + Input[InputIndex] = 0; break; case TB_KEY_CTRL_D: case TB_KEY_CTRL_C: @@ -780,7 +789,7 @@ main(int argc, char** argv) tb_poll_event(&ev); } - screen_home(&ScratchArena, &MessagesArena, MessagesNum, &ClientsArena, fds, Input, InputIndex); + DisplayChat(&ScratchArena, &MessagesArena, MessagesNum, &ClientsArena, fds, Input, InputIndex); tb_present(); } diff --git a/ui.h b/ui.h index ff98598..21a4a80 100644 --- a/ui.h +++ b/ui.h @@ -295,6 +295,28 @@ preprocess_markdown(Arena* ScratchArena, wchar_t* Text, u32 Len) return Result; } +u32 InputBoxMarginX = 1; +u32 InputBoxPaddingX = 1; +#define INPUT_BOX_BORDER_WIDTH 1 +#define INPUT_BOX_MIN_TEXT_WIDTH 1 +#define INPUT_BOX_MIN_TEXT_HEIGHT 1 + +u32 +GetInputBoxMinimumWidth() +{ + return InputBoxPaddingX * 2 + + InputBoxMarginX * 2 + + INPUT_BOX_BORDER_WIDTH * 2 + + INPUT_BOX_MIN_TEXT_WIDTH; +} + +u32 +GetInputBoxMinimumHeight() +{ + return INPUT_BOX_BORDER_WIDTH * 2 + + INPUT_BOX_MIN_TEXT_HEIGHT; +} + void InputBox(u32 BoxX, u32 BoxY, u32 BoxWidth, u32 BoxHeight, wchar_t *Text, u32 TextLen, @@ -308,17 +330,9 @@ InputBox(u32 BoxX, u32 BoxY, u32 BoxWidth, u32 BoxHeight, // P -> padding (symmetric) // M -> margin (symmetric) - // Configuration options - u32 MarginX = 1; - u32 PaddingX = 1; - u32 MinTextSpace = 3; - u32 BorderWidth = 1; - - // TODO: return early if there is not enough space - if (BoxHeight < 3) - return; - if (BoxWidth < MarginX * 2 + PaddingX * 2 + MinTextSpace) - return; + u32 MarginX = InputBoxMarginX; + u32 PaddingX = InputBoxPaddingX; + u32 BorderWidth = INPUT_BOX_BORDER_WIDTH; // Get 0-based coordinate BoxWidth -= 2* MarginX; @@ -370,35 +384,63 @@ InputBox(u32 BoxX, u32 BoxY, u32 BoxWidth, u32 BoxHeight, u32 TextHeight = BoxHeight - BorderWidth * 2 + 1; u32 TextDisplaySize = TextWidth * TextHeight; - u32 At = 0; - // If there is not enough space to fit the text scroll one line by advancing by textwidth. - if (TextLen >= TextDisplaySize) - { - // TextHeight - 1 : scroll by one line - At = (TextLen / TextWidth - (TextHeight - 1)) * TextWidth; - } - -#ifdef DEBUG - tb_printf(BoxX + 1, BoxY, 0, 0, "%d/%d %dx%d %d", TextLen, MAX_INPUT_LEN, TextWidth, TextHeight, At); -#endif - - // Keep if needed for cursor position + // XOffset and YOffset are needed for setting the cursor position u32 XOffset = 0, YOffset = 0; - while (At < TextLen) + u32 TextOffset = 0; + + // If there is more than one line, implement vertical wrapping otherwise scroll the text + // horizontally. + if (TextHeight > 1) { - for (YOffset = 0; - YOffset < TextHeight && At < TextLen; - YOffset++) + // If there is not enough space to fit the text scroll one line by advancing by textwidth. + if (TextLen >= TextDisplaySize) + { + // TextHeight - 1 : scroll by one line + TextOffset = (TextLen / TextWidth - (TextHeight - 1)) * TextWidth; + } + + // Print the text + while (TextOffset < TextLen) { - for (XOffset = 0; - XOffset < TextWidth && At < TextLen; - XOffset++) + for (YOffset = 0; + YOffset < TextHeight && TextOffset < TextLen; + YOffset++) { - tb_printf(TextX + XOffset, TextY + YOffset, 0, 0, "%lc", Text[At]); - At++; + for (XOffset = 0; + XOffset < TextWidth && TextOffset < TextLen; + XOffset++) + { + tb_printf(TextX + XOffset, TextY + YOffset, 0, 0, "%lc", Text[TextOffset]); + TextOffset++; + } } } } + else + { + // Scrooll the text horizontally + if (TextLen >= TextDisplaySize) + { + TextOffset = TextLen - TextWidth; + XOffset = TextWidth; + } + else + { + XOffset = TextLen; + } + YOffset = 1; + tb_printf(TextX, TextY, 0, 0, "%ls", Text + TextOffset); + } + +#ifdef DEBUG + tb_printf(BoxX + 1, BoxY, 0, 0, "%d/%d [%dx%d] %dx%d %d (%d,%d)+(%d,%d)", + TextLen, MAX_INPUT_LEN, + BoxWidth, BoxHeight, + TextWidth, TextHeight, + TextOffset, + TextX, TextY, + XOffset, YOffset); +#endif // Set the cursor if (Focused) @@ -409,7 +451,7 @@ InputBox(u32 BoxX, u32 BoxY, u32 BoxWidth, u32 BoxHeight, global.cursor_x = TextX; global.cursor_y = TextY; } - else if (TextLen % TextWidth == 0) + else if (TextLen % TextWidth == 0 && TextHeight > 1) { // When at the end of width put the cursor on the next line global.cursor_x = TextX; -- cgit v1.2.3