-
Notifications
You must be signed in to change notification settings - Fork 194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding text editor functionality to text viewer. #868
base: master
Are you sure you want to change the base?
Conversation
@@ -116,7 +116,7 @@ With the possibilites GodMode9 provides, not everything may be obvious at first | |||
* __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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open to "Text Editor"/"Texteditor"/"Hex Editor"/"Hexeditor" guidance here...
@@ -53,9 +65,9 @@ enum { | |||
KEY_ALPHA, ' ', KEY_BKSPC | |||
|
|||
#define SWKBD_KEYS_NUMPAD \ | |||
'7', '8', '9', 'F', 'E', \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the arrangement of these to be more consistent with e.g. calculators.
#define SWKBD_LAYOUT_SPECIAL \ | ||
6, 0, \ | ||
6, 0, \ | ||
6, 0, \ | ||
6, 0, \ | ||
3, 32, 46, 32, 0, \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was bothering me as well.
@@ -87,3 +107,5 @@ enum { | |||
|
|||
#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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exposed to prevent rebuilds between keypresses in the MemTextViewer
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of these Github comments should probably be code comments instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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. 😁
@@ -1225,7 +1225,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allowing for editing of empty files.
@@ -1316,6 +1316,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan | |||
} | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is necessary, I assume, to recalculate file sizes?
@@ -205,6 +207,10 @@ static const Gm9ScriptCmd cmd_list[] = { | |||
{ CMD_ID_BKPT , "bkpt" , 0, 0 } | |||
}; | |||
|
|||
// off-screen string indicators |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulled these out so that the cursor-following code has access to them.
@@ -259,86 +265,125 @@ static inline u32 hexntostr(const u8* hex, char* str, u32 len) { | |||
return len; | |||
} | |||
|
|||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
return l0; | ||
} | ||
} | ||
|
||
static inline const char* line_start(const char* text, u32 len, u32 ww, const char* ptr) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, this can be used now instead of line_seek(..., 0);
@@ -1614,7 +1659,7 @@ bool run_line(const char* line_start, const char* line_end, u32* flags, char* er | |||
|
|||
// checks for illegal ASCII symbols | |||
bool ValidateText(const char* text, u32 len) { | |||
if (!len) return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multibyte support.
|
||
// draw line number & text | ||
DrawString(TOP_SCREEN, txtstr, x_txt, y, color_text, COLOR_STD_BG); | ||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be slow for large files, but I took it form the hex editor; open to suggestions here...
@@ -1846,18 +2074,20 @@ bool MemToCViewer(const char* text, u32 len, const char* title) { | |||
bool FileTextViewer(const char* path, bool as_script) { | |||
// load text file (completely into memory) | |||
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
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:
if
statements 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 betweenInputWait
calls 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!