Adding clipboard select/cut/copy/paste support.

This commit is contained in:
Nicholaos Mouzourakis 2024-07-02 23:46:37 -04:00 committed by d0k3
parent d65a5b6771
commit 04072d5b62
4 changed files with 211 additions and 40 deletions

View File

@ -222,7 +222,7 @@ static char KeyboardWait(TouchBox* swkbd, bool uppercase, const bool multi_line)
else if (pressed & BUTTON_B) return KEY_ESCAPE; else if (pressed & BUTTON_B) return KEY_ESCAPE;
else if (pressed & BUTTON_A) return KEY_ENTER; else if (pressed & BUTTON_A) return KEY_ENTER;
else if (pressed & BUTTON_X) return KEY_BKSPC; else if (pressed & BUTTON_X) return KEY_BKSPC;
else if (pressed & BUTTON_Y) return multi_line ? KEY_CAPS : KEY_INSERT; else if (pressed & BUTTON_Y) return multi_line ? KEY_CLIP : KEY_INSERT;
else if (!multi_line && pressed & BUTTON_R1) return KEY_CAPS; else if (!multi_line && pressed & BUTTON_R1) return KEY_CAPS;
else if (pressed & BUTTON_RIGHT) return KEY_RIGHT; else if (pressed & BUTTON_RIGHT) return KEY_RIGHT;
else if (pressed & BUTTON_LEFT) return KEY_LEFT; else if (pressed & BUTTON_LEFT) return KEY_LEFT;
@ -392,14 +392,14 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) {
return ret; return ret;
} }
char ShowMultiLineKeyboard(TouchBox* swkbd_alphabet, TouchBox* swkbd_special, TouchBox* swkbd_numpad, TouchBox** swkbd, TouchBox** swkbd_prev, u32* uppercase) { char ShowMultiLineKeyboard(const char* instructions, TouchBox* swkbd_alphabet, TouchBox* swkbd_special, TouchBox* swkbd_numpad, TouchBox** swkbd, TouchBox** swkbd_prev, u32* uppercase) {
if (!*swkbd) { if (instructions) {
u32 str_width = GetDrawStringWidth(STR_TEXTEDITOR_CONTROLS_KEYBOARD); u32 str_width = GetDrawStringWidth(instructions);
if (str_width < (24 * FONT_WIDTH_EXT)) str_width = 24 * FONT_WIDTH_EXT; if (str_width < (24 * FONT_WIDTH_EXT)) str_width = 24 * FONT_WIDTH_EXT;
u32 str_x = (str_width >= SCREEN_WIDTH_BOT) ? 0 : (SCREEN_WIDTH_BOT - str_width) / 2; u32 str_x = (str_width >= SCREEN_WIDTH_BOT) ? 0 : (SCREEN_WIDTH_BOT - str_width) / 2;
ClearScreen(BOT_SCREEN, COLOR_STD_BG); ClearScreen(BOT_SCREEN, COLOR_STD_BG);
DrawStringF(BOT_SCREEN, str_x, 20, COLOR_STD_FONT, COLOR_STD_BG, "%s", STR_TEXTEDITOR_CONTROLS_KEYBOARD); DrawStringF(BOT_SCREEN, str_x, 20, COLOR_STD_FONT, COLOR_STD_BG, "%s", instructions);
*swkbd = swkbd_alphabet; *swkbd_prev = NULL; // Force keyboard redraw
} }
// handle keyboard // handle keyboard

View File

@ -23,6 +23,7 @@ enum {
KEY_UNICODE = 0x8C, KEY_UNICODE = 0x8C,
KEY_UP = 0x8D, KEY_UP = 0x8D,
KEY_DOWN = 0x8E, KEY_DOWN = 0x8E,
KEY_CLIP = 0x8F,
KEY_TXTBOX = 0xFF KEY_TXTBOX = 0xFF
}; };
@ -108,4 +109,4 @@ enum {
#define ShowKeyboardOrPrompt (TouchIsCalibrated() ? ShowKeyboard : ShowStringPrompt) #define ShowKeyboardOrPrompt (TouchIsCalibrated() ? ShowKeyboard : ShowStringPrompt)
bool PRINTF_ARGS(3) ShowKeyboard(char* inputstr, u32 max_size, const char *format, ...); bool PRINTF_ARGS(3) ShowKeyboard(char* inputstr, u32 max_size, const char *format, ...);
bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout, bool multi_line); bool BuildKeyboard(TouchBox* swkbd, const char* keys, const u8* layout, bool multi_line);
char ShowMultiLineKeyboard(TouchBox* swkbd_alphabet, TouchBox* swkbd_special, TouchBox* swkbd_numpad, TouchBox** swkbd, TouchBox** swkbd_prev, u32* uppercase); char ShowMultiLineKeyboard(const char* instructions, TouchBox* swkbd_alphabet, TouchBox* swkbd_special, TouchBox* swkbd_numpad, TouchBox** swkbd, TouchBox** swkbd_prev, u32* uppercase);

View File

@ -336,7 +336,7 @@ static inline u32 line_len_chars(const char* text, u32 len, u32 ww, const char*
static inline const char* line_seek_chars(const char* text, u32 len, u32 ww, const char* line, int add) { static inline const char* line_seek_chars(const char* text, u32 len, u32 ww, const char* line, int add) {
// safety check // safety check
if ((line <= text && add <= 0) || (line >= text + len && add >= 0)) return line; if ((line <= text && add <= 0) || (line >= text + len && add >= 0)) return line;
const char* l0 = line; const char* l0 = line;
if (!ww) { // non wordwrapped mode if (!ww) { // non wordwrapped mode
@ -1671,7 +1671,7 @@ bool ValidateText(const char* text, u32 len) {
return true; return true;
} }
void MemTextView(const char* text, u32 len, const char* line0, int off_disp_chars, int lno, u32 ww, u32 mno, bool is_script, const char* cursor) { void MemTextView(const char* text, u32 len, const char* line0, int off_disp_chars, int lno, u32 ww, u32 mno, bool is_script, const char* cursor, const char* cursor_end) {
// block placements // block placements
u32 x_txt = (TV_LNOS >= 0) ? TV_HPAD + ((TV_LNOS+1)*FONT_WIDTH_EXT) : TV_HPAD; u32 x_txt = (TV_LNOS >= 0) ? TV_HPAD + ((TV_LNOS+1)*FONT_WIDTH_EXT) : TV_HPAD;
u32 x_lno = TV_HPAD; u32 x_lno = TV_HPAD;
@ -1738,7 +1738,97 @@ void MemTextView(const char* text, u32 len, const char* line0, int off_disp_char
else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", TV_LNOS, TV_LNOS, " "); else DrawStringF(TOP_SCREEN, x_lno, y, COLOR_TVOFFSL, COLOR_STD_BG, "%*.*s", TV_LNOS, TV_LNOS, " ");
} }
if (cursor) { const int x_txt_end = x_txt + TV_LLEN_DISP * FONT_WIDTH_EXT;
bool draw_ar_select = false, draw_al_select = false;
// account for selections drawn across lines
DrawPixel(TOP_SCREEN, x_txt, y - 1, COLOR_STD_BG);
DrawPixel(TOP_SCREEN, x_txt_end - 1, y - 1, COLOR_STD_BG);
DrawPixel(TOP_SCREEN, x_txt, y + FONT_HEIGHT_EXT, COLOR_STD_BG);
DrawPixel(TOP_SCREEN, x_txt_end - 1, y + FONT_HEIGHT_EXT, COLOR_STD_BG);
// draw cursor / selection
if (cursor_end) {
int x_hline_start = -1, x_hline_end = -1;
u32 cursor_line_offset_chars = chars_between_pointers(ptr + off_disp_bytes, cursor);
bool draw_cursor = cursor >= ptr + off_disp_bytes && cursor <= ptr + off_disp_bytes + ncpy_bytes && cursor_line_offset_chars < TV_LLEN_DISP
&& (cursor != ptr + off_disp_bytes + ncpy_bytes || is_newline(cursor) || cursor == text + len);
if (draw_cursor) {
x_hline_start = x_txt + cursor_line_offset_chars * FONT_WIDTH_EXT;
DrawRectangle(TOP_SCREEN, x_hline_start, y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
} else if (cursor < ptr + off_disp_bytes) x_hline_start = x_txt;
const char* cursor_end_prev = GetPrevChar(cursor_end);
u32 cursor_end_line_offset_chars = chars_between_pointers(ptr + off_disp_bytes, cursor_end_prev);
bool draw_cursor_end = cursor_end_prev >= ptr + off_disp_bytes && cursor_end_prev <= ptr + off_disp_bytes + ncpy_bytes && cursor_end_line_offset_chars < TV_LLEN_DISP
&& (!ww || cursor_end_prev != ptr + off_disp_bytes + ncpy_bytes || is_newline(cursor_end_prev));
if (draw_cursor_end) {
x_hline_end = x_txt + (cursor_end_line_offset_chars + 1) * FONT_WIDTH_EXT;
DrawRectangle(TOP_SCREEN, x_hline_end - ((cursor_end_line_offset_chars == TV_LLEN_DISP - 1) ? 1 : 0), y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
cursor_end = NULL; // prevent cursor from being drawn multiple times at the end of the screen
} else if (cursor_end_prev >= ptr + off_disp_bytes + ncpy_bytes) x_hline_end = x_txt_end;
if (draw_cursor && draw_cursor_end) {
DrawRectangle(TOP_SCREEN, x_hline_start, y, x_hline_end - x_hline_start, 1, COLOR_YELLOW);
DrawRectangle(TOP_SCREEN, x_hline_start, y + FONT_HEIGHT_EXT - 1, x_hline_end - x_hline_start, 1, COLOR_YELLOW);
} else if (draw_cursor) {
DrawRectangle(TOP_SCREEN, x_hline_start, y, x_txt_end - x_hline_start, 1, COLOR_YELLOW);
if (cursor_end > ptr_next) {
DrawRectangle(TOP_SCREEN, x_txt, y + FONT_HEIGHT_EXT - 1, x_hline_start - x_txt, 1, COLOR_YELLOW);
DrawPixel(TOP_SCREEN, x_txt, y + FONT_HEIGHT_EXT, COLOR_YELLOW);
DrawPixel(TOP_SCREEN, x_txt_end - 1, y + FONT_HEIGHT_EXT, COLOR_YELLOW);
} else {
DrawRectangle(TOP_SCREEN, x_hline_start, y + FONT_HEIGHT_EXT - 1, x_txt_end - x_hline_start, 1, COLOR_YELLOW);
cursor_end = NULL;
}
DrawRectangle(TOP_SCREEN, x_txt_end - 1, y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
draw_ar_select = true;
} else if (draw_cursor_end) {
if (cursor < ptr) {
DrawRectangle(TOP_SCREEN, x_hline_end, y, x_txt_end - x_hline_end, 1, COLOR_YELLOW);
DrawPixel(TOP_SCREEN, x_txt, y - 1, COLOR_YELLOW);
DrawPixel(TOP_SCREEN, x_txt_end - 1, y - 1, COLOR_YELLOW);
} else {
DrawRectangle(TOP_SCREEN, x_txt, y, x_hline_end - x_txt, 1, COLOR_YELLOW);
}
DrawRectangle(TOP_SCREEN, x_txt, y + FONT_HEIGHT_EXT - 1, x_hline_end - x_txt, 1, COLOR_YELLOW);
DrawRectangle(TOP_SCREEN, x_txt, y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
draw_al_select = true;
} else if (cursor < ptr_next) {
if (cursor < ptr + off_disp_bytes) {
DrawRectangle(TOP_SCREEN, x_txt, y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
if (cursor >= ptr) DrawRectangle(TOP_SCREEN, x_txt, y, x_txt_end - x_txt, 1, COLOR_YELLOW);
else {
DrawPixel(TOP_SCREEN, x_txt, y - 1, COLOR_YELLOW);
DrawPixel(TOP_SCREEN, x_txt_end - 1, y - 1, COLOR_YELLOW);
}
draw_al_select = true;
} else if (cursor >= ptr + off_disp_bytes + ncpy_bytes) {
DrawRectangle(TOP_SCREEN, x_txt_end - 1, y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
DrawRectangle(TOP_SCREEN, x_txt, y + FONT_HEIGHT_EXT - 1, x_txt_end - x_txt, 1, COLOR_YELLOW);
draw_ar_select = true;
}
if (cursor_end_prev < ptr + off_disp_bytes) {
DrawRectangle(TOP_SCREEN, x_txt, y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
DrawRectangle(TOP_SCREEN, x_txt, y, x_txt_end - x_txt, 1, COLOR_YELLOW);
cursor_end = NULL;
draw_al_select = true;
} else if (cursor_end_prev >= ptr + off_disp_bytes + ncpy_bytes) {
DrawRectangle(TOP_SCREEN, x_txt_end - 1, y, 1, FONT_HEIGHT_EXT, COLOR_YELLOW);
if (cursor_end_prev < ptr_next) {
DrawRectangle(TOP_SCREEN, x_txt, y + FONT_HEIGHT_EXT - 1, x_txt_end - x_txt, 1, COLOR_YELLOW);
cursor_end = NULL;
} else {
DrawPixel(TOP_SCREEN, x_txt, y + FONT_HEIGHT_EXT, COLOR_YELLOW);
DrawPixel(TOP_SCREEN, x_txt_end - 1, y + FONT_HEIGHT_EXT, COLOR_YELLOW);
}
draw_ar_select = true;
}
}
if (!cursor_end) cursor = NULL;
} else if (cursor) {
u32 cursor_line_offset_chars = chars_between_pointers(ptr + off_disp_bytes, cursor); u32 cursor_line_offset_chars = chars_between_pointers(ptr + off_disp_bytes, cursor);
if (cursor >= ptr + off_disp_bytes && cursor <= ptr + off_disp_bytes + ncpy_bytes && cursor_line_offset_chars < TV_LLEN_DISP if (cursor >= ptr + off_disp_bytes && cursor <= ptr + off_disp_bytes + ncpy_bytes && cursor_line_offset_chars < TV_LLEN_DISP
&& (cursor != ptr + off_disp_bytes + ncpy_bytes || is_newline(cursor) || cursor == text + len)) { && (cursor != ptr + off_disp_bytes + ncpy_bytes || is_newline(cursor) || cursor == text + len)) {
@ -1758,8 +1848,8 @@ void MemTextView(const char* text, u32 len, const char* line0, int off_disp_char
} }
// colorize arrows // colorize arrows
if (al) DrawStringF(TOP_SCREEN, x_al, y, COLOR_TVOFFS, COLOR_TRANSPARENT, "%s", al_str); if (al) DrawStringF(TOP_SCREEN, x_al, y, draw_al_select ? COLOR_TINTEDYELLOW : COLOR_TVOFFS, COLOR_TRANSPARENT, "%s", al_str);
if (ar) DrawStringF(TOP_SCREEN, x_ar, y, COLOR_TVOFFS, COLOR_TRANSPARENT, "%s", ar_str); if (ar) DrawStringF(TOP_SCREEN, x_ar, y, draw_ar_select ? COLOR_TINTEDYELLOW : COLOR_TVOFFS, COLOR_TRANSPARENT, "%s", ar_str);
// advance pointer / line number // advance pointer / line number
for (const char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln; for (const char* c = ptr; c < ptr_next; c++) if (*c == '\n') ++nln;
@ -1818,13 +1908,17 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max
bool crlf = is_crlf(text); bool crlf = is_crlf(text);
bool display_view_instructions = true; bool display_view_instructions = true;
const char* cursor = NULL; const char* cursor = NULL;
const char* cursor_end = NULL;
const char** last_cursor = &cursor;
char* clipboard = NULL;
const char* instructions = NULL;
const char* line0 = text; const char* line0 = text;
int lcurr = 1; // Current line number int lcurr = 1; // Current line number
int off_disp_chars = 0; // non-word-wrapped offset int off_disp_chars = 0; // non-word-wrapped offset
for (; lcurr < (int) start; line0 = line_seek_chars(text, len, 0, line0, 1), lcurr++); for (; lcurr < (int) start; line0 = line_seek_chars(text, len, 0, line0, 1), lcurr++);
while (true) { while (true) {
// display text on screen // display text on screen
MemTextView(text, len, line0, off_disp_chars, lcurr, ww, 0, as_script, cursor); MemTextView(text, len, line0, off_disp_chars, lcurr, ww, 0, as_script, cursor, cursor_end);
const char* line0_next = line0; const char* line0_next = line0;
@ -1848,8 +1942,9 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max
cursor = line0; cursor = line0;
off_disp_chars = 0; off_disp_chars = 0;
uppercase = 0; uppercase = 0;
swkbd = NULL; swkbd = swkbd_alphabet;
swkbd_prev = NULL; swkbd_prev = NULL;
instructions = clipboard ? STR_TEXTEDITOR_CONTROLS_CLIPBOARD : STR_TEXTEDITOR_CONTROLS_KEYBOARD;
} }
else if (switched && pad_state & BUTTON_X) { else if (switched && pad_state & BUTTON_X) {
u64 lnext64 = ShowNumberPrompt(lcurr, STR_CURRENT_LINE_N_ENTER_NEW_LINE_BELOW, lcurr); u64 lnext64 = ShowNumberPrompt(lcurr, STR_CURRENT_LINE_N_ENTER_NEW_LINE_BELOW, lcurr);
@ -1861,48 +1956,73 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max
line0_next = line_start(text, len, ww, line0); line0_next = line_start(text, len, ww, line0);
} else if (pad_state & (BUTTON_B|BUTTON_START)) break; } else if (pad_state & (BUTTON_B|BUTTON_START)) break;
} else { // edit mode } else { // edit mode
char key_pressed = ShowMultiLineKeyboard(swkbd_alphabet, swkbd_special, swkbd_numpad, &swkbd, &swkbd_prev, &uppercase); char key_pressed = ShowMultiLineKeyboard(instructions, swkbd_alphabet, swkbd_special, swkbd_numpad, &swkbd, &swkbd_prev, &uppercase);
instructions = NULL;
char key_character = 0; char key_character = 0;
bool switched = HID_ReadState() & BUTTON_R1; u32 pad_state = HID_ReadState();
bool switched = pad_state & BUTTON_R1;
bool selected = pad_state & BUTTON_L1 || (cursor_end && switched);
const char** prev_last_cursor = last_cursor;
last_cursor = &cursor;
if (key_pressed == KEY_ESCAPE) { if (key_pressed == KEY_ESCAPE) {
cursor = NULL; cursor = NULL;
cursor_end = NULL;
display_view_instructions = true; display_view_instructions = true;
} else if (key_pressed == KEY_DOWN) { } else if (key_pressed == KEY_DOWN) {
const char* cursor_line_start = line_start(text, len, ww, cursor); const char** down_cursor = selected ? last_cursor = &cursor_end : &cursor;
u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, cursor); if (!*down_cursor) *down_cursor = cursor;
cursor = line_seek_chars(text, len, ww, cursor_line_start, switched ? TV_NLIN_DISP : 1); const char* down_cursor_char = down_cursor == &cursor_end && cursor_end > cursor ? GetPrevChar(*down_cursor) : *down_cursor;
cursor = line_start(text, len, ww, cursor); const char* cursor_line_start = line_start(text, len, ww, down_cursor_char);
const char* next_line_start = line_seek_chars(text, len, ww, cursor, 1); u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, down_cursor_char);
for (u32 i = 0; GetNextChar(cursor) < next_line_start && i < cursor_chars_from_line_start; IncChar(&cursor), ++i); *down_cursor = line_seek_chars(text, len, ww, cursor_line_start, switched ? TV_NLIN_DISP : 1);
*down_cursor = line_start(text, len, ww, *down_cursor);
const char* next_line_start = line_seek_chars(text, len, ww, *down_cursor, 1);
if (down_cursor == &cursor_end) for (u32 i = 0; *down_cursor < next_line_start && i <= cursor_chars_from_line_start; IncChar(down_cursor), ++i);
else for (u32 i = 0; GetNextChar(*down_cursor) < next_line_start && i < cursor_chars_from_line_start; IncChar(down_cursor), ++i);
if (!selected) cursor_end = NULL;
} else if (key_pressed == KEY_UP) { } else if (key_pressed == KEY_UP) {
if (selected && !cursor_end) cursor_end = cursor;
const char* cursor_line_start = line_start(text, len, ww, cursor); const char* cursor_line_start = line_start(text, len, ww, cursor);
u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, cursor); u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, cursor);
cursor = line_seek_chars(text, len, ww, cursor_line_start, -(switched ? TV_NLIN_DISP : 1)); cursor = line_seek_chars(text, len, ww, cursor_line_start, -(switched ? TV_NLIN_DISP : 1));
const char* next_line_start = line_seek_chars(text, len, ww, cursor, 1); const char* next_line_start = line_seek_chars(text, len, ww, cursor, 1);
for (u32 i = 0; GetNextChar(cursor) < next_line_start && i < cursor_chars_from_line_start; IncChar(&cursor), ++i); for (u32 i = 0; GetNextChar(cursor) < next_line_start && i < cursor_chars_from_line_start; IncChar(&cursor), ++i);
if (!selected) cursor_end = NULL;
} else if (key_pressed == KEY_RIGHT) { } else if (key_pressed == KEY_RIGHT) {
const char** right_cursor = selected ? last_cursor = &cursor_end : &cursor;
if (!*right_cursor) *right_cursor = cursor;
if (switched) { if (switched) {
const char* cursor_line_start = line_start(text, len, ww, cursor); const char* cursor_line_start = line_start(text, len, ww, right_cursor == &cursor_end && cursor_end > cursor ? GetPrevChar(*right_cursor) : *right_cursor);
const char* next_line_start = line_seek_chars(text, len, ww, cursor_line_start, 1); const char* next_line_start = line_seek_chars(text, len, ww, cursor_line_start, 1);
if (next_line_start == text + len && (!ww || chars_between_pointers(cursor_line_start, next_line_start) != TV_LLEN_DISP)) IncChar(&next_line_start); if (right_cursor == &cursor && next_line_start == text + len && (!ww || chars_between_pointers(cursor_line_start, next_line_start) != TV_LLEN_DISP))
while (GetNextChar(cursor) < next_line_start && !is_newline(cursor)) IncChar(&cursor); IncChar(&next_line_start);
if (right_cursor == &cursor_end) for (u32 chars = 0; *right_cursor < next_line_start && chars < TV_LLEN_DISP; ++chars) IncChar(right_cursor);
else for (u32 chars = 0; GetNextChar(*right_cursor) < next_line_start && !is_newline(*right_cursor) && chars < TV_LLEN_DISP; ++chars) IncChar(right_cursor);
} }
else if (cursor < text + len) IncChar(&cursor); else if (*right_cursor < text + len) IncChar(right_cursor);
if (!selected) cursor_end = NULL;
} else if (key_pressed == KEY_LEFT) { } else if (key_pressed == KEY_LEFT) {
if (selected && !cursor_end) cursor_end = cursor;
if (switched) { if (switched) {
const char* cursor_line_start = line_start(text, len, ww, cursor); const char* cursor_line_start = line_start(text, len, ww, cursor);
while (cursor > cursor_line_start) DecChar(&cursor); while (cursor > cursor_line_start) DecChar(&cursor);
} }
else if (cursor > text) DecChar(&cursor); else if (cursor > text) DecChar(&cursor);
if (!selected) cursor_end = NULL;
} else if (key_pressed == KEY_BKSPC) { } else if (key_pressed == KEY_BKSPC) {
if (cursor > text) { if (cursor_end && cursor_end > cursor) {
memmove((char *) cursor, cursor_end, text + len - cursor_end + 1);
len -= cursor_end - cursor;
cursor_end = NULL;
} else if (cursor > text) {
u32 size = GetPrevCharSize(cursor); u32 size = GetPrevCharSize(cursor);
memmove((char *) cursor - size, cursor, text + len - cursor + 1); memmove((char *) cursor - size, cursor, text + len - cursor + 1);
len -= size; len -= size;
cursor -= size; cursor -= size;
} }
} else if (key_pressed == KEY_UNICODE) { } else if (key_pressed == KEY_UNICODE) {
if (cursor >= text + 4 && cursor <= text + len) { if (cursor_end) last_cursor = prev_last_cursor;
else if (cursor >= text + 4 && cursor <= text + len) {
u16 codepoint = 0; u16 codepoint = 0;
for (const char *c = cursor - 4; c < cursor; c++) { for (const char *c = cursor - 4; c < cursor; c++) {
if ((*c >= '0' && *c <= '9') || (*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f')) { if ((*c >= '0' && *c <= '9') || (*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f')) {
@ -1926,9 +2046,52 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max
len -= 4 - char_size; len -= 4 - char_size;
} }
} }
} else if (key_pressed == KEY_CLIP) { // clipboard
if (clipboard) {
if (switched) { // clear
free(clipboard);
clipboard = NULL;
instructions = STR_TEXTEDITOR_CONTROLS_KEYBOARD;
last_cursor = prev_last_cursor;
} else { // paste
int clip_size = strlen(clipboard);
int select_size = cursor_end ? cursor_end - cursor : 0;
if (clip_size && len - select_size + clip_size <= max_len) {
if (clip_size != select_size) {
const char* select_end = cursor_end ? cursor_end : cursor;
memmove((char *) cursor + clip_size, select_end, text + len - select_end + 1);
}
memcpy((char *) cursor, clipboard, clip_size);
cursor += clip_size;
cursor_end = NULL;
len += clip_size - select_size;
}
}
} else if (cursor_end && cursor_end > cursor) { // copy
int select_size = cursor_end - cursor;
clipboard = malloc(select_size + 1);
if (clipboard) {
memcpy(clipboard, cursor, select_size);
clipboard[select_size] = '\0';
if (switched) { // cut
memmove((char *) cursor, cursor_end, text + len - cursor_end + 1);
len -= select_size;
cursor_end = NULL;
} else last_cursor = prev_last_cursor;
instructions = STR_TEXTEDITOR_CONTROLS_CLIPBOARD;
}
}
} else if (key_pressed == KEY_ENTER) key_character = crlf ? '\r' : '\n'; } else if (key_pressed == KEY_ENTER) key_character = crlf ? '\r' : '\n';
else if (key_pressed < 0x80) key_character = key_pressed; else if (key_pressed < 0x80) key_character = key_pressed;
// delete selection if typing standard char
if (key_character && cursor_end && cursor_end > cursor) {
memmove((char *) cursor, cursor_end, text + len - cursor_end + 1);
len -= cursor_end - cursor;
cursor_end = NULL;
}
// type standard char
if (key_character && len + (key_character == '\r' ? 1 : 0) < max_len) { if (key_character && len + (key_character == '\r' ? 1 : 0) < max_len) {
if (uppercase == 1) { if (uppercase == 1) {
uppercase = 0; uppercase = 0;
@ -1941,14 +2104,18 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max
} }
} }
if (cursor && !ww) { if (cursor_end <= cursor) cursor_end = NULL;
const char* cursor_line_start = line_start(text, len, ww, cursor);
u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, cursor); // adjust screen to view last cursor moved
const char* last_cursor_value = cursor_end && last_cursor == &cursor_end ? GetPrevChar(cursor_end) : cursor;
if (last_cursor_value && !ww) {
const char* cursor_line_start = line_start(text, len, ww, last_cursor_value);
u32 cursor_chars_from_line_start = chars_between_pointers(cursor_line_start, last_cursor_value);
if (cursor_chars_from_line_start < off_disp_chars + strlen(al_str)) off_disp_chars = cursor_chars_from_line_start - strlen(al_str); if (cursor_chars_from_line_start < off_disp_chars + strlen(al_str)) off_disp_chars = cursor_chars_from_line_start - strlen(al_str);
if (cursor_chars_from_line_start >= off_disp_chars + TV_LLEN_DISP - strlen(ar_str)) off_disp_chars = cursor_chars_from_line_start + strlen(ar_str) - TV_LLEN_DISP + 1; if (cursor_chars_from_line_start >= off_disp_chars + TV_LLEN_DISP - strlen(ar_str)) off_disp_chars = cursor_chars_from_line_start + strlen(ar_str) - TV_LLEN_DISP + 1;
} }
while (cursor && cursor < line0_next) line0_next = line_seek_chars(text, len, ww, line0_next, -1); while (last_cursor_value && last_cursor_value < line0_next) line0_next = line_seek_chars(text, len, ww, line0_next, -1);
while (cursor && line0_next < line_seek_chars(text, len, ww, GetNextChar(cursor), -TV_NLIN_DISP)) line0_next = line_seek_chars(text, len, ww, line0_next, 1); while (last_cursor_value && line0_next < line_seek_chars(text, len, ww, GetNextChar(last_cursor_value), -TV_NLIN_DISP)) line0_next = line_seek_chars(text, len, ww, line0_next, 1);
} }
// find last allowed lines (ww and nonww) // find last allowed lines (ww and nonww)
@ -1979,6 +2146,8 @@ bool MemTextViewer(const char* text, u32 len, u32 start, bool as_script, u32 max
if (off_disp_chars < 0 || ww) off_disp_chars = 0; if (off_disp_chars < 0 || ww) off_disp_chars = 0;
} }
if (clipboard) free(clipboard);
// check for user edits // check for user edits
if (text_cpy) { if (text_cpy) {
if (save_path) { if (save_path) {
@ -2014,7 +2183,7 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
// clear screens / view start of readme on top // clear screens / view start of readme on top
ClearScreenF(true, true, COLOR_STD_BG); ClearScreenF(true, true, COLOR_STD_BG);
MemTextView(text, len, text, 0, 1, ww, 0, false, NULL); MemTextView(text, len, text, 0, 1, ww, 0, false, NULL, NULL);
// parse text for markdown captions // parse text for markdown captions
u32 n_captions = 0; u32 n_captions = 0;
@ -2055,15 +2224,15 @@ bool MemToCViewer(const char* text, u32 len, const char* title) {
u32 pad_state = InputWait(0); u32 pad_state = InputWait(0);
if ((cursor >= 0) && (pad_state & BUTTON_A)) { if ((cursor >= 0) && (pad_state & BUTTON_A)) {
if (!MemTextViewer(text, len, lineno[cursor], false, 0, NULL)) return false; if (!MemTextViewer(text, len, lineno[cursor], false, 0, NULL)) return false;
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL); MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL, NULL);
} else if (pad_state & BUTTON_B) { } else if (pad_state & BUTTON_B) {
break; break;
} else if (pad_state & BUTTON_UP) { } else if (pad_state & BUTTON_UP) {
cursor = (cursor <= 0) ? ((int) n_captions - 1) : cursor - 1; cursor = (cursor <= 0) ? ((int) n_captions - 1) : cursor - 1;
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL); MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL, NULL);
} else if (pad_state & BUTTON_DOWN) { } else if (pad_state & BUTTON_DOWN) {
if (++cursor >= (int) n_captions) cursor = 0; if (++cursor >= (int) n_captions) cursor = 0;
MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL); MemTextView(text, len, captions[cursor], 0, lineno[cursor], ww, 0, false, NULL, NULL);
} }
} }
@ -2198,11 +2367,11 @@ bool ExecuteGM9Script(const char* path_script) {
} }
if (show_preview) { if (show_preview) {
if (lno <= (TV_NLIN_DISP/2)) { if (lno <= (TV_NLIN_DISP/2)) {
MemTextView(script, script_size, script, 0, 1, 0, lno, true, NULL); MemTextView(script, script_size, script, 0, 1, 0, lno, true, NULL, NULL);
} else { } else {
const char* ptr_view = line_seek_chars(script, script_size, 0, ptr, -(TV_NLIN_DISP/2)); const char* ptr_view = line_seek_chars(script, script_size, 0, ptr, -(TV_NLIN_DISP/2));
u32 lno_view = lno - (TV_NLIN_DISP/2); u32 lno_view = lno - (TV_NLIN_DISP/2);
MemTextView(script, script_size, ptr_view, 0, lno_view, 0, lno, true, NULL); MemTextView(script, script_size, ptr_view, 0, lno_view, 0, lno, true, NULL, NULL);
} }
} }
} }

View File

@ -795,7 +795,8 @@
"ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.", "ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.",
"TEXTVIEWER_CONTROLS_DETAILS": "Textviewer Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nB - Exit\n", "TEXTVIEWER_CONTROLS_DETAILS": "Textviewer Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nB - Exit\n",
"TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n", "TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n",
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nY - Caps / Capslock\nX - Delete char\nA - Insert newline\nB - Enter view mode\n", "TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - COPY / [+R] CUT\nB - Enter view mode\n",
"TEXTEDITOR_CONTROLS_CLIPBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - PASTE / [+R] CLEAR\nB - Enter view mode\n",
"TEXT_EDITS_SAVE_CHANGES": "You made text edits.\nWrite changes to file?", "TEXT_EDITS_SAVE_CHANGES": "You made text edits.\nWrite changes to file?",
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Current line: %i\nEnter new line below.", "CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Current line: %i\nEnter new line below.",
"PREVIEW_DISABLED": "(preview disabled)", "PREVIEW_DISABLED": "(preview disabled)",