Adding text editor functionality to text viewer.#868
Adding text editor functionality to text viewer.#868nevumx wants to merge 6 commits intod0k3:masterfrom
Conversation
| * __Search drives and folders__: Just press R+A on the drive / folder you want to search. | ||
| * __Compare and verify files__: Press the A button on the first file, select `Calculate SHA-256`. Do the same for the second file. If the two files are identical, you will get a message about them being identical. On the SDCARD drive (`0:`) you can also write an SHA file, so you can check for any modifications at a later point. | ||
| * __Hexview and hexedit any file__: Press the A button on a file and select `Show in Hexeditor`. A button again enables edit mode, hold the A button and press arrow buttons to edit bytes. You will get an additional confirmation prompt to take over changes. Take note that for certain files, write permissions can't be enabled. | ||
| * __View text files in a text viewer__: Press the A button on a file and select `Show in Textviewer` (only shows up for actual text files). You can enable wordwrapped mode via R+Y, and navigate around the file via R+X and the dpad. |
There was a problem hiding this comment.
Open to "Text Editor"/"Texteditor"/"Hex Editor"/"Hexeditor" guidance here...
| KEY_ALPHA, ' ', KEY_BKSPC | ||
|
|
||
| #define SWKBD_KEYS_NUMPAD \ | ||
| '7', '8', '9', 'F', 'E', \ |
There was a problem hiding this comment.
I changed the arrangement of these to be more consistent with e.g. calculators.
| 6, 0, \ | ||
| 6, 0, \ | ||
| 6, 0, \ | ||
| 3, 32, 46, 32, 0, \ |
There was a problem hiding this comment.
This was bothering me as well.
|
|
||
| #define ShowKeyboardOrPrompt (TouchIsCalibrated() ? ShowKeyboard : ShowStringPrompt) | ||
| 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); |
There was a problem hiding this comment.
This is exposed to prevent rebuilds between keypresses in the MemTextViewer function.
There was a problem hiding this comment.
A lot of these Github comments should probably be code comments instead
There was a problem hiding this comment.
@BlueRaja done. I went through and added code comments where I felt they were relevant. If you have any other concerns, please let me know. 😁
| int n_opt = 0; | ||
| int special = (special_opt) ? ++n_opt : -1; | ||
| int hexviewer = ++n_opt; | ||
| int textviewer = (filetype & TXT_GENERIC) ? ++n_opt : -1; |
There was a problem hiding this comment.
Allowing for editing of empty files.
| } | ||
| else if (user_select == textviewer) { // -> show in text viewer | ||
| FileTextViewer(file_path, scriptable); | ||
| GetDirContents(current_dir, current_path); |
There was a problem hiding this comment.
This is necessary, I assume, to recalculate file sizes?
| { CMD_ID_BKPT , "bkpt" , 0, 0 } | ||
| }; | ||
|
|
||
| // off-screen string indicators |
There was a problem hiding this comment.
Pulled these out so that the cursor-following code has access to them.
|
|
||
| static inline u32 line_len(const char* text, u32 len, u32 ww, const char* line, char** eol) { | ||
| u32 last = ((text + len) - line); | ||
| static inline bool is_crlf(const char* str) { |
There was a problem hiding this comment.
Majority rule seems to make the most sense here... We could do a hotkey to switch newline types on the keyboard, and display the current newline type status on the keyboard screen too, let me know if you think that's a good idea.
| return chr[0] == '\n' || (chr[0] == '\r' && chr[1] == '\n'); | ||
| } | ||
|
|
||
| static inline u32 bytes_in_chars_u32(const char* str, u32 nchars) { |
There was a problem hiding this comment.
These are for slightly saner multibyte character support.
| return chars; | ||
| } | ||
|
|
||
| static inline u32 line_len_chars(const char* text, u32 len, u32 ww, const char* line, const char** eol) { |
There was a problem hiding this comment.
I made these a bit more simplified while supporting multibyte chars, but as a result, their behavior has changed slightly. I changed invocations accordingly where necessary.
| } | ||
| } | ||
|
|
||
| static inline const char* line_start(const char* text, u32 len, u32 ww, const char* ptr) { |
There was a problem hiding this comment.
For example, this can be used now instead of line_seek(..., 0);
|
|
||
| // checks for illegal ASCII symbols | ||
| bool ValidateText(const char* text, u32 len) { | ||
| if (!len) return false; |
There was a problem hiding this comment.
Allow editing empty files.
| // display text on screen | ||
| char txtstr[TV_LLEN_DISP + 1]; | ||
| char* ptr = line0; | ||
| char txtstr[TV_LLEN_DISP * MAX_CHAR_SIZE + 1]; |
There was a problem hiding this comment.
Add UTF-8 char max size.
| if (ncpy > TV_LLEN_DISP) ncpy = TV_LLEN_DISP; | ||
| bool al = !ww && off_disp && (ptr != ptr_next); | ||
| bool ar = !ww && (llen > off_disp + TV_LLEN_DISP); | ||
| int off_disp_bytes = bytes_in_chars_int(ptr, off_disp_chars); |
| if (TV_LNOS > 0) { // line number | ||
| if (ptr != ptr_next) | ||
| DrawStringF(TOP_SCREEN, x_lno, y, ((ptr == text) || (*(ptr-1) == '\n')) ? COLOR_TVOFFS : COLOR_TVOFFSL, COLOR_STD_BG, "%0*lu", TV_LNOS, nln); | ||
| bool prev_ww_line_full = ww && ww == chars_between_pointers(line_seek_chars(text, len, ww, ptr, -1), ptr); |
There was a problem hiding this comment.
Unfortunate edge case to draw line number when the '\0' is word-wrapped onto its own line...
| 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 | ||
| && (cursor != ptr + off_disp_bytes + ncpy_bytes || is_newline(cursor) || cursor == text + len)) { | ||
| DrawRectangle(TOP_SCREEN, x_txt + cursor_line_offset_chars * FONT_WIDTH_EXT, y, FONT_WIDTH_EXT, 1, COLOR_RED); |
There was a problem hiding this comment.
Without a blinking line, a hollow red rect is the next best cursor I could think of. These coords are picked so that they are drawn over by text.
| 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)) { | ||
| DrawRectangle(TOP_SCREEN, x_txt + cursor_line_offset_chars * FONT_WIDTH_EXT, y, FONT_WIDTH_EXT, 1, COLOR_RED); | ||
| DrawRectangle(TOP_SCREEN, x_txt + (cursor_line_offset_chars + 1) * FONT_WIDTH_EXT - ((cursor_line_offset_chars == TV_LLEN_DISP - 1) ? 1 : 0), y, 1, FONT_HEIGHT_EXT, COLOR_RED); |
There was a problem hiding this comment.
Small edge case to squish the cursor box at the end of the line/screen.
| 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); | ||
| } | ||
|
|
||
| // find last allowed lines (ww and nonww) |
There was a problem hiding this comment.
These checks have to be performed every iteration now.
| if (save_path) { | ||
| bool diffs = false; | ||
| if (len != text_cpy_len) diffs = true; | ||
| else for (u32 i = 0; i < len; ++i) if (text[i] != text_cpy[i]) { diffs = true; break; } |
There was a problem hiding this comment.
This could be slow for large files, but I took it form the hex editor; open to suggestions here...
| // text file needs to fit inside the STD_BUFFER_SIZE | ||
| u32 flen, len; | ||
| size_t fileSize = FileGetSize(path); | ||
| if (fileSize >= STD_BUFFER_SIZE) { |
There was a problem hiding this comment.
Max file size check. I tested with a much lower number as well to make sure that the editor would not allow characters to be typed beyond the max file size.
|
Could the hex editing functionality be added to the hex viewer as well? |
The hex viewer has had edit functionality since 2016: 88a62d8 |
|
@nevumx - sorry about the long radio silence, know that your contribution is appreciated. As you see, I didn't merge this p/r for the current release, however that doesn't mean it won't be in a future release. Could you rebase this on the current master branch? |
| // validate text ourselves so we can return a better error | ||
| // MemTextViewer calls ShowPrompt if it's bad, and i don't want that | ||
|
|
||
| if (len >= STD_BUFFER_SIZE) { |
There was a problem hiding this comment.
Adding this to be more in line with FileTextViewer(...) code.
| // MemTextViewer calls ShowPrompt if it's bad, and i don't want that | ||
| // and FileTextViewer calls the above function | ||
|
|
||
| size_t fileSize = FileGetSize(path); |
There was a problem hiding this comment.
Adding this to be more in line with FileTextViewer(...) code.
| size_t flen = FileGetData(path, text, STD_BUFFER_SIZE - 1, 0); | ||
|
|
||
| text[flen] = '\0'; | ||
| u32 len = (ptrdiff_t)memchr(text, '\0', flen + 1) - (ptrdiff_t)text; |
There was a problem hiding this comment.
This line is unnecessary; using flen instead as we do in the FileTextViewer(...) code.
@d0k3 No problem, I rebased, and fixed the Lua invocations of |
|
@d0k3 Bump 😇 |
|
@d0k3 How is the outlook for getting this merged? 😅 |
| "SCRIPTERR_UNCLOSED_CONDITIONAL": "unclosed conditional", | ||
| "SCRIPTERR_ERROR_MESSAGE_FAIL": "error message fail", | ||
| "ERROR_INVALID_TEXT_DATA": "Error: Invalid text data", | ||
| "ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.", |
There was a problem hiding this comment.
You most likely could not have known, but if new strings are added anywhere else btu the bottom of the file, the translation version will need to be incremented, too. I will fix that myself.
|
@nevumx - merged with master. Thank you for your contribution and your patience. This looks very nice and is a great addition to GodMode9. I see you made a merge commit a few hours ago - is there anything in it that current master would need? |
|
@d0k3 No, it was just resolving a merge conflict with header files. If you were able to merge into master without it, it should be fine. |
Pursuant to my feature request #861, I am pleased to present my proposal PR for adding text editor functionality to the existing text viewer by repurposing the existing keyboard that is used for file renames.
Features:
Screenshots:





Considerations:
ifstatements around, for example, when the last line is a certain length.InputWait(1), but that resulted in inputs getting dropped occasionally when pressing a button betweenInputWaitcalls when the screen was being drawn.<threads.h>compiled, the thread would never spawn due tothrd_error... I am guessing threads are out of scope for this project, since I don't see them anywhere else, definitely open to other suggestions on this...Instead "Y" switches between capslock states, because I don't see a purpose for a hardware button to insert a space into the text...EDIT: I am using "Y" for the new clipboard cut/copy/paste feature.Definitely open to suggestions on this, especially as it relates to key bindings/text/etc. ALSO, please do test in any/every way you can think of; I had a reasonably robust set of test files, but it's always possible that there was an edge case I missed. And translations; not sure what can be done in that regard in this PR...
EDIT: I have added clipboard functionality. For example, if we are in this file:








And we press "L+→", we start a selection:
Where we can continue this to the end of the line with additional "L+→" or "R+→" presses:
And go downwards with "L+↓" or "R+↓":
Then we can copy that text with "Y" or cut it with "R+Y":
And paste it in again with "Y":
And paste more times with the same clipboard data:
And then we can clear the clipboard with "R+Y":
Any comments/suggestions are appreciated!