From d305b7697a3a072fcdf828ba4c14187cfa69567d Mon Sep 17 00:00:00 2001 From: Turiiya <34311583+ttytm@users.noreply.github.com> Date: Fri, 22 Sep 2023 03:19:13 +0200 Subject: [PATCH 1/4] Fix file dialog in text editor example --- .../C/text-editor/cross_platform_open_file.h | 120 ------------------ examples/C/text-editor/text-editor.c | 84 ------------ examples/C/text-editor/ui/MainWindow.html | 4 +- examples/C/text-editor/ui/js/ui.js | 61 ++++----- 4 files changed, 24 insertions(+), 245 deletions(-) delete mode 100644 examples/C/text-editor/cross_platform_open_file.h diff --git a/examples/C/text-editor/cross_platform_open_file.h b/examples/C/text-editor/cross_platform_open_file.h deleted file mode 100644 index adf21d3d2..000000000 --- a/examples/C/text-editor/cross_platform_open_file.h +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include - -#ifdef _WIN32 - #include - #include -#elif __APPLE__ - #include - #if TARGET_OS_MAC - #include - #include - #endif -#elif __linux__ - #include -#endif - -#define FULL_FILE_BUFFER_SIZE (2 * 1024 * 1024) // 2 MB -char FULL_FILE_BUFFER[FULL_FILE_BUFFER_SIZE]; -char JAVASCRIPT_BUFFER[FULL_FILE_BUFFER_SIZE]; -char FILE_PATH[1024]; - -size_t read_file_into_buffer(const char *filename, char *buffer, size_t buffer_size) { - - FILE *file = fopen(filename, "rb"); - - if (file == NULL) { - fprintf(stderr, "Error: Could not open file %s.\n", filename); - return -1; - } - - size_t bytes_read = fread(buffer, 1, buffer_size, file); - buffer[bytes_read] = '\0'; - - if (ferror(file)) { - fprintf(stderr, "Error: Could not read file %s.\n", filename); - fclose(file); - return -1; - } - - fclose(file); - return bytes_read; -} - -#ifdef _WIN32 - UINT_PTR OpenDialogHooks(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { - BringWindowToTop(hdlg); - return 0; - } -#endif - -char* show_file_open_dialog() { - - #ifdef _WIN32 - - OPENFILENAMEA ofn; - ZeroMemory(&ofn, sizeof(OPENFILENAME)); - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = NULL; - ofn.lpstrFile = FILE_PATH; - ofn.nMaxFile = sizeof(FILE_PATH); - ofn.lpstrFilter = "All Files\0*.*\0"; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = NULL; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = NULL; - ofn.lpfnHook = OpenDialogHooks; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - if (GetOpenFileNameA(&ofn)) { - return FILE_PATH; - } - - #elif __APPLE__ - - @autoreleasepool { - NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - [openPanel setCanChooseFiles:YES]; - [openPanel setCanChooseDirectories:NO]; - [openPanel setAllowsMultipleSelection:NO]; - [openPanel setAllowedFileTypes:nil]; - - if ([openPanel runModal] == NSModalResponseOK) { - NSURL *fileURL = [[openPanel URLs] objectAtIndex:0]; - const char *fileSystemRepresentation = [[fileURL fileSystemRepresentation] UTF8String]; - strncpy(FILE_PATH, fileSystemRepresentation, sizeof(FILE_PATH)); - FILE_PATH[sizeof(FILE_PATH) - 1] = '\0'; - return FILE_PATH; - } - } - - #else - - if (!gtk_init_check(NULL, NULL)) { - fprintf(stderr, "Error: Failed to initialize GTK.\n"); - return NULL; - } - - GtkWidget *dialog = gtk_file_chooser_dialog_new("Open File", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); - GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog); - gtk_file_chooser_set_select_multiple(chooser, FALSE); - gtk_file_chooser_set_current_folder(chooser, g_get_home_dir()); - - gint response = gtk_dialog_run(GTK_DIALOG(dialog)); - if (response == GTK_RESPONSE_ACCEPT) { - char *filename = gtk_file_chooser_get_filename(chooser); - strncpy(FILE_PATH, filename, sizeof(FILE_PATH)); - FILE_PATH[sizeof(FILE_PATH) - 1] = '\0'; - g_free(filename); - gtk_widget_destroy(dialog); - while (g_main_context_iteration(NULL, FALSE)); - return FILE_PATH; - } - - gtk_widget_destroy(dialog); - while (g_main_context_iteration(NULL, FALSE)); - - #endif - - return NULL; -} diff --git a/examples/C/text-editor/text-editor.c b/examples/C/text-editor/text-editor.c index 77b075c5f..c4117bc73 100644 --- a/examples/C/text-editor/text-editor.c +++ b/examples/C/text-editor/text-editor.c @@ -1,10 +1,6 @@ // Text Editor in C using WebUI #include "webui.h" -#include "cross_platform_open_file.h" - -#include -#include void Close(webui_event_t* e) { printf("Exit.\n"); @@ -13,92 +9,12 @@ void Close(webui_event_t* e) { webui_exit(); } -void Save(webui_event_t* e) { - printf("Save.\n"); - - // Save data received from the UI - FILE *file = fopen(FILE_PATH, "w"); - int results = fputs(e->data, file); - if(results == EOF) { - // Failed to write - } - fclose(file); -} - -void Open(webui_event_t* e) { - printf("Open.\n"); - - // Open a new file - - // Open file and save the path to FILE_PATH - if(show_file_open_dialog() == NULL) - return; - - // Read the full content to FULL_FILE_BUFFER - size_t bytes_read = read_file_into_buffer(FILE_PATH, FULL_FILE_BUFFER, FULL_FILE_BUFFER_SIZE); - if(bytes_read < 1) - return; - - // Send file content - // Encode the full content to base64 - char* file_encoded = webui_encode(FULL_FILE_BUFFER); - if(file_encoded == NULL) - return; - sprintf(JAVASCRIPT_BUFFER, "addText('%s')", file_encoded); - webui_run(e->window, JAVASCRIPT_BUFFER); - - // Send file name - // Encode the file path to base64 - char* path_encoded = webui_encode(FILE_PATH); - if(path_encoded == NULL) - return; - sprintf(JAVASCRIPT_BUFFER, "SetFile('%s')", path_encoded); - webui_run(e->window, JAVASCRIPT_BUFFER); - - // Clean - webui_free(file_encoded); - webui_free(path_encoded); - - /* - // Add line by line example: - - FILE *file = fopen(FILE_PATH, "r"); - if(file == NULL) - return; - - char line[1024] = {0}; - while (fgets(line, 1024, file) != NULL) { - - // Line - char* line_encoded = webui_encode(line); - - if(line_encoded != NULL) { - - char js[1024] = {0}; - - // JS - sprintf(js, "addLine('%s')", line_encoded); - - // Send - webui_run(e->window, js); - - // Clean - webui_free(line_encoded); - } - } - - fclose(file); - */ -} - int main() { // Create new windows int MainWindow = webui_new_window(); // Bind HTML element IDs with a C functions - webui_bind(MainWindow, "Open", Open); - webui_bind(MainWindow, "Save", Save); webui_bind(MainWindow, "Close", Close); // Show a new window diff --git a/examples/C/text-editor/ui/MainWindow.html b/examples/C/text-editor/ui/MainWindow.html index c83695964..7d56342c8 100644 --- a/examples/C/text-editor/ui/MainWindow.html +++ b/examples/C/text-editor/ui/MainWindow.html @@ -2,7 +2,7 @@ - + Text Editor in C using WebUI @@ -22,7 +22,7 @@ @@ -46,6 +40,12 @@

WebUI Text Editor

webui.me | (C)2023 Hassan Draga

+ + + + + + diff --git a/examples/C/text-editor/ui/js/ui.js b/examples/C/text-editor/ui/js/ui.js index 4a1030720..30cab7aec 100644 --- a/examples/C/text-editor/ui/js/ui.js +++ b/examples/C/text-editor/ui/js/ui.js @@ -1,66 +1,57 @@ // Text Editor // Elements -let About = document.getElementById("About"); -let aboutBox = document.getElementById("about-box"); +const aboutBtn = document.getElementById('about-button'); +const aboutBox = document.getElementById('about-box'); +const saveBtn = document.getElementById('save-button'); let fileHandle = null; // About show -About.onclick = function() { - // Open ABout - aboutBox.style.display = "block"; -} - +aboutBtn.onclick = () => (aboutBox.style.display = 'block'); // About hide -window.onclick = function(event) { - if (event.target == aboutBox) { - // Close About - aboutBox.style.display = "none"; - } -} +aboutBox.onclick = () => (aboutBox.style.display = 'none'); // Create the editor -const editor = document.getElementById("editor"); +const editor = document.getElementById('editor'); const codeMirrorInstance = CodeMirror.fromTextArea(editor, { - mode: "text/x-csrc", + mode: 'text/x-csrc', lineNumbers: true, tabSize: 4, indentUnit: 2, lineWrapping: true, - theme: "lucario" + theme: 'lucario', }); // Change editor language function SetFileModeExtension(extension) { - let mode = ""; + let mode = ''; switch (extension) { - case "js": - mode = "text/javascript"; + case 'js': + mode = 'text/javascript'; break; - case "c": - case "cpp": - case "h": - mode = "text/x-csrc"; + case 'c': + case 'cpp': + case 'h': + mode = 'text/x-csrc'; break; - case "py": - mode = "text/x-python"; + case 'py': + mode = 'text/x-python'; break; - case "html": - mode = "text/html"; + case 'html': + mode = 'text/html'; break; default: - mode = "text/x-csrc"; + mode = 'text/x-csrc'; } - codeMirrorInstance.setOption("mode", mode); + codeMirrorInstance.setOption('mode', mode); } // Add full text to the editor function addText(text) { codeMirrorInstance.setValue(text); - const element = document.getElementById('SaveLi'); - element.style.color = '#ddecf9'; - element.style.pointerEvents = 'all'; + saveBtn.style.color = '#ddecf9'; + saveBtn.style.pointerEvents = 'all'; } async function OpenFile() { @@ -87,6 +78,6 @@ async function SaveFile() { await writableStream.close(); } -window.addEventListener("DOMContentLoaded", (event) => { - codeMirrorInstance.setSize("100%", "99%"); +window.addEventListener('DOMContentLoaded', () => { + codeMirrorInstance.setSize('100%', '99%'); }); From 6059a193ab955c1b7f4494ad0aa243fbe825eae9 Mon Sep 17 00:00:00 2001 From: Turiiya <34311583+ttytm@users.noreply.github.com> Date: Fri, 22 Sep 2023 18:05:49 +0200 Subject: [PATCH 3/4] Fix cross-browser compatibility --- examples/C/text-editor/text-editor.c | 3 +- examples/C/text-editor/ui/js/ui.js | 68 +++++++++++++++++++++------- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/examples/C/text-editor/text-editor.c b/examples/C/text-editor/text-editor.c index d1a7b16a4..3e22f4457 100644 --- a/examples/C/text-editor/text-editor.c +++ b/examples/C/text-editor/text-editor.c @@ -19,7 +19,8 @@ int main() { // Show a new window webui_set_root_folder(MainWindow, "ui"); - webui_show(MainWindow, "MainWindow.html"); + if (!webui_show_browser(MainWindow, "MainWindow.html", ChromiumBased)) + webui_show(MainWindow, "MainWindow.html"); // Wait until all windows get closed webui_wait(); diff --git a/examples/C/text-editor/ui/js/ui.js b/examples/C/text-editor/ui/js/ui.js index 30cab7aec..dbcd74ee5 100644 --- a/examples/C/text-editor/ui/js/ui.js +++ b/examples/C/text-editor/ui/js/ui.js @@ -4,7 +4,12 @@ const aboutBtn = document.getElementById('about-button'); const aboutBox = document.getElementById('about-box'); const saveBtn = document.getElementById('save-button'); -let fileHandle = null; +const supportsFilePicker = 'showSaveFilePicker' in window; +let fileHandle; +let openFile = { + name: '', + ext: '', +}; // About show aboutBtn.onclick = () => (aboutBox.style.display = 'block'); @@ -54,28 +59,59 @@ function addText(text) { saveBtn.style.pointerEvents = 'all'; } -async function OpenFile() { - [fileHandle] = await showOpenFilePicker({ multiple: false }); - const fileData = await fileHandle.getFile(); - - // Read File +function readFile(file) { const reader = new FileReader(); reader.onload = (e) => addText(e.target.result); - reader.readAsText(fileData); + reader.readAsText(file); +} - // Set file title and language - document.title = fileData.name; - SetFileModeExtension(fileData.name.split('.').pop()); +function setFile(file) { + openFile.name = file.name; + openFile.ext = file.name.split('.').pop(); + // Set file title and language in editor + document.title = file.name; + SetFileModeExtension(openFile.ext); +} + +async function OpenFile() { + if (supportsFilePicker) { + [fileHandle] = await showOpenFilePicker({ multiple: false }); + fileData = await fileHandle.getFile(); + readFile(fileData); + setFile(fileData); + } else { + let input = document.createElement('input'); + input.type = 'file'; + input.onchange = (e) => { + readFile(e.target.files[0]); + setFile(e.target.files[0]); + }; + input.click(); + input.remove(); + } } async function SaveFile() { - // Create a FileSystemWritableFileStream to write to - const writableStream = await fileHandle.createWritable(); const content = codeMirrorInstance.getValue(); - await writableStream.write(content); - - // Write to disk - await writableStream.close(); + if (supportsFilePicker && fileHandle) { + // Create a FileSystemWritableFileStream to write to + const writableStream = await fileHandle.createWritable(); + await writableStream.write(content); + // Write to disk + await writableStream.close(); + } else { + // Download the file if using filePicker with a fileHandle for saving + // is not supported by the browser. E.g., in Firefox. + const blobData = new Blob([content], { type: 'text/${openFile.ext}' }); + const urlToBlob = window.URL.createObjectURL(blobData); + const a = document.createElement('a'); + a.style.setProperty('display', 'none'); + a.href = urlToBlob; + a.download = document.title; + a.click(); + window.URL.revokeObjectURL(urlToBlob); + a.remove(); + } } window.addEventListener('DOMContentLoaded', () => { From 5a6df39da9e8372f986592c84bb8db4c5670e5fa Mon Sep 17 00:00:00 2001 From: Turiiya <34311583+ttytm@users.noreply.github.com> Date: Fri, 22 Sep 2023 18:39:36 +0200 Subject: [PATCH 4/4] Update comments --- examples/C/text-editor/text-editor.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/C/text-editor/text-editor.c b/examples/C/text-editor/text-editor.c index 3e22f4457..94af4c22e 100644 --- a/examples/C/text-editor/text-editor.c +++ b/examples/C/text-editor/text-editor.c @@ -11,14 +11,16 @@ void Close(webui_event_t* e) { int main() { - // Create new windows + // Create a new window int MainWindow = webui_new_window(); - // Bind HTML element IDs with a C functions + // Set the root folder for the UI + webui_set_root_folder(MainWindow, "ui"); + + // Bind HTML elements with the specified ID to C functions webui_bind(MainWindow, "close-button", Close); - // Show a new window - webui_set_root_folder(MainWindow, "ui"); + // Show the window, preferably in a chromium based browser if (!webui_show_browser(MainWindow, "MainWindow.html", ChromiumBased)) webui_show(MainWindow, "MainWindow.html");