From cc9abff31ff533b77d5d75b596fcf434d3379c79 Mon Sep 17 00:00:00 2001 From: Pascal Thomet Date: Fri, 21 Feb 2025 09:30:48 +0100 Subject: [PATCH] tutorial: prepare split (remove unused files) --- bindings/pyodide_web_demo/justfile | 1 - tutorial.kc/discover_immediate_gui.ipynb | 120 ---- tutorial.kc/gallery.ipynb | 0 tutorial.kc/hello_imgui.ipynb | 135 ---- tutorial.kc/hello_imm.ipynb | 434 ------------ tutorial.kc/imgui.ipynb | 101 --- tutorial.kc/imgui_bundle.ipynb | 58 -- tutorial/.gitignore | 1 + .../_tpl/instantiate_tutorial_template.py | 37 - tutorial/_tpl/tpl.cpp | 14 - tutorial/_tpl/tpl.jpg | Bin 3298 -> 0 bytes tutorial/_tpl/tpl.py | 19 - tutorial/_tpl/tpl_md | 17 - tutorial/gpt4.md | 502 -------------- tutorial/gpt_presentation.md | 652 ------------------ tutorial/justfile | 3 +- 16 files changed, 2 insertions(+), 2092 deletions(-) delete mode 100644 tutorial.kc/discover_immediate_gui.ipynb delete mode 100644 tutorial.kc/gallery.ipynb delete mode 100644 tutorial.kc/hello_imgui.ipynb delete mode 100644 tutorial.kc/hello_imm.ipynb delete mode 100644 tutorial.kc/imgui.ipynb delete mode 100644 tutorial.kc/imgui_bundle.ipynb delete mode 100644 tutorial/_tpl/instantiate_tutorial_template.py delete mode 100644 tutorial/_tpl/tpl.cpp delete mode 100644 tutorial/_tpl/tpl.jpg delete mode 100644 tutorial/_tpl/tpl.py delete mode 100644 tutorial/_tpl/tpl_md delete mode 100644 tutorial/gpt4.md delete mode 100644 tutorial/gpt_presentation.md diff --git a/bindings/pyodide_web_demo/justfile b/bindings/pyodide_web_demo/justfile index f0126be4..948f436d 100644 --- a/bindings/pyodide_web_demo/justfile +++ b/bindings/pyodide_web_demo/justfile @@ -20,7 +20,6 @@ scatter_wheel: # Copy the dist folder from the remote machine copy_dist: fiatlight_wheel scatter_wheel - mkdir -p pyodide_dist mkdir -p pyodide_dist rsync -avz --delete {{REMOTE_USER_HOST}}:{{REMOTE_FOLDER}}/dist/ pyodide_dist/ cp {{FIATLIGHT_FOLDER}}/dist/*.whl pyodide_dist/ diff --git a/tutorial.kc/discover_immediate_gui.ipynb b/tutorial.kc/discover_immediate_gui.ipynb deleted file mode 100644 index 7738badd..00000000 --- a/tutorial.kc/discover_immediate_gui.ipynb +++ /dev/null @@ -1,120 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Discovering Immediate Mode GUIs\n", - "\n", - "## Intro\n", - "\n", - "Graphical User Interfaces (GUIs) handle how your application is presented to the user.\n", - "\n", - "Most GUI frameworks rely on complex patterns like **MVC**, which separate application logic into layers: Model (the data), View (the presentation), and Controller (the logic). Thus, the code which handles the state of the application is split in different places, which can make it hard to follow, and to synchronize, once the application grows.\n", - "\n", - "**Immediate Mode GUI (IMGUI)** frameworks, like **Dear ImGui**, take a radically different approach: *the code which renders the widgets and the code which handles the user interactions are in the same place*, i.e. in the rendering loop. Handling the state of the application, and adding features, becomes extremely simple.\n", - "\n", - "The example below illustrates this concept, where an application could display a counter, together with a button to increase it:\n", - "\n", - "![example](images/demo_immediate.png)\n", - "\n", - "**Python**:\n", - "```python\n", - "# on_frame is called at each frame\n", - "def on_frame():\n", - " # Display a text widget \n", - " imgui.text(f\"Counter: {counter}\")\n", - " # Display a button, and return true if clicked\n", - " if imgui.button(\"Increment\"):\n", - " counter += 1 # Perform an action immediately on interaction\n", - "```\n", - "\n", - "**C++**:\n", - "```cpp\n", - "void on_frame() { \n", - " ImGui::Text(\"Counter: %d\", counter);\n", - " if (ImGui::Button(\"Increment\"))\n", - " counter++;\n", - "}\n", - "```\n", - "\n", - "### Why Use Immediate Mode GUIs?\n", - "\n", - "Here are some key benefits:\n", - "\n", - "1. **Simplicity**: UI code is clean and readable. You describe what to display and interact with, frame by frame.\n", - "2. **Minimal State Management**: No complex state updates; you control state directly in your code.\n", - "3. **Performance**: Optimized for real-time rendering and interaction.\n", - "\n", - "Widgets are defined and user interactions are managed in the same code section. This immediate approach is particularly useful for:\n", - "\n", - "**Notes:**\n", - "- **Separation of concerns**: The immediate mode paradigm is compatible with best practices, such as having a separate Model (or Application State). \n", - " See the next example for an illustration.\n", - "- **Limitations:** The Dear ImGui library is not designed for fully \"skinnable\" UIs (although custom \"themes\" or \"styles\" are available), and will not support complex font rendering (left to right, etc).\n", - "\n", - "\n", - "## Contents of this tutorial \n", - "In this tutorial, you will:\n", - "\n", - "1. Learn the basics of Immediate Mode GUIs.\n", - "2. Explore the fundamental widgets provided by **Dear ImGui**.\n", - "3. Use **Hello ImGui** to quickly set up applications with minimal boilerplate code.\n", - "4. Build a small, interactive project to solidify your understanding.\n", - "\n", - "Let’s dive in and explore the straightforward yet powerful world of Immediate Mode GUI programming!\n", - "\n", - "\n", - "## Introducing *Dear ImGui*, *Hello ImGui*, and *Dear ImGui Bundle*\n", - "\n", - "- **[Dear ImGui](https://github.com/ocornut/imgui)**: A lightweight and fast C++ library for Immediate Mode GUI programming, with over 60k stars on GitHub. It enables rapid creation of UI components such as buttons, sliders, and text fields with minimal code, leveraging GPU rendering for exceptional performance.\n", - "- **[Hello ImGui](https://pthom.github.io/hello_imgui)**: A powerful C++ wrapper around Dear ImGui designed to streamline apps creation with Dear ImGui, and to simplify complex tasks such as layout handling, FPS idling, and creating mobile and desktop applications. It reduces boilerplate and adds enhanced utilities, making it ideal for both simple prototypes and advanced production-grade GUIs.\n", - "- **[Dear ImGui Bundle](https://pthom.github.io/imgui_bundle)**: an extensive set of ready-to-use widgets and libraries, based on ImGui and Hello ImGui. It also provides bindings for Python, enabling you to create GUI applications in **Python**, which we will explore in this tutorial.\n", - "\n", - "\n", - "## Deploy web applications in Python with Dear ImGui Bundle\n", - "\n", - "![Jack](images/jack_tangled_frameworks.jpg)\n", - "![Tom](images/tom_tangled_frameworks.webp)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Installing the required Libraries\n", - "\n", - "*In this web tutorial, `imgui-bundle` is pre-installed*. If you are running this tutorial locally, you can install imgui-bundle following the instructions [for Python](https://pthom.github.io/imgui_bundle/install_python.html) or [for C++](https://pthom.github.io/imgui_bundle/install_cpp.html).\n", - "\n", - "\n", - "# What's next?\n", - "\n", - "* Explore more widgets within ImGui (buttons, inputs, plots)\n", - "* Explore even more widgets withing Dear ImGui Bundle (e.g., plots, 3D guizmo, advanced image viewer, etc.)\n", - "* Learn about Hello ImGui: docking layouts (e.g., for advanced UIs).\n", - "* ... and much more!" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "v312_2", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorial.kc/gallery.ipynb b/tutorial.kc/gallery.ipynb deleted file mode 100644 index e69de29b..00000000 diff --git a/tutorial.kc/hello_imgui.ipynb b/tutorial.kc/hello_imgui.ipynb deleted file mode 100644 index 0b01d902..00000000 --- a/tutorial.kc/hello_imgui.ipynb +++ /dev/null @@ -1,135 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Hello ImGui\n", - "\n", - "\n", - "## Dear ImGui / reminder\n", - "\n", - "* Full ImGui widgets list: Link to ImGui Manual, with image of Python code.\n", - "* Layout\n", - "\n", - "hello_imgui.em_size() and hello_imgui.em_to_vec2() (i.e. how to use the em unit to create layouts, in a DPI-independent way)\n", - "\n", - "\n", - "## Handling assets\n", - "\n", - "## Multiplatform support\n", - "\n", - "## DPI handling\n", - "\n", - "## Idling and frame rate\n", - "\n", - "## How to adjust the window options for your application\n", - "\n", - "\n", - "## Theming\n", - "\n", - "\n", - "## Custom 3D background\n", - "\n", - "\n", - "## Full list of parameters available for Hello ImGui applications\n", - "Link to\n", - "https://pthom.github.io/hello_imgui/book/doc_params.html\n", - "\n", - "[Diagram](https://pthom.github.io/hello_imgui/book/doc_params.html#diagram)\n", - "\n", - "Explain that to translate from C++ to Python, one has to switch from CamelCase to snake_case, and from `::` to `.`\n", - "\n", - "List of parameters types:\n", - "* [Application window params](https://pthom.github.io/hello_imgui/book/doc_params.html#application-window-params)\n", - "* [callbacks](https://pthom.github.io/hello_imgui/book/doc_params.html#runnercallbacks)\n", - "* \n", - "\n", - "## Creating complex layouts with dockable windows\n", - "\n", - "Use demo_docking demo\n", - "\n", - "\n", - "## Additional Widgets available in Dear ImGui Bundle\n", - "\n", - "Use demo_widgets" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ChatGPT advices\n", - "\n", - "```\n", - "Hello ImGui Tutorial\n", - "\n", - " Integrating Custom Fonts and Icons:\n", - " Demonstrate loading custom fonts and using icon libraries like FontAwesome.\n", - "\n", - " hello_imgui.load_fonts(\"path/to/fontawesome.ttf\")\n", - "\n", - "Saving and Restoring User Preferences:\n", - "\n", - " Explain how Hello ImGui handles window size, docked layout, and user-defined preferences via INI files.\n", - " Show how to modify or clear saved settings programmatically.\n", - "\n", - "Advanced Asset Management:\n", - "\n", - " Include examples of embedding assets for cross-platform deployment (e.g., images, icons, shaders).\n", - "\n", - "Multithreading Considerations:\n", - "\n", - " Explain how to separate rendering and computation threads for applications that need real-time updates.\n", - "\n", - "Interactivity with Custom Callbacks:\n", - "\n", - " Demonstrate the use of RunnerCallbacks to enhance behavior:\n", - " before_exit\n", - " post_render\n", - " Mobile-specific callbacks (e.g., on_pause, on_resume).\n", - "\n", - "Debugging Utilities:\n", - "\n", - " Introduce Dear ImGui’s built-in debugging tools and highlight Hello ImGui additions like frame rate displays, status bars, and logs.\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "v312_2", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorial.kc/hello_imm.ipynb b/tutorial.kc/hello_imm.ipynb deleted file mode 100644 index 146c7cc2..00000000 --- a/tutorial.kc/hello_imm.ipynb +++ /dev/null @@ -1,434 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Writing your first Immediate Mode applications\n", - "\n", - "Let's start by creating a simple application \"Hello, World\" application, where we will demonstrate several aspects of ImGui and Hello ImGui.\n", - "\n", - "## Hello, World! Display text and images\n", - "\n", - "We will start by displaying a text and an image in the application window, with the following steps:\n", - "1. We define a gui function that will be called by Hello ImGui at every frame: It will display the GUI of our application *and* handle user events.\n", - "2. We use the `imgui.text()` function to display a text in the window.\n", - "3. We use `hello_imgui.im_texture_id_from_asset()` to load an image from an asset file, and `imgui.image()` to display it, with a size of 100x100 pixels.\n", - "4. We call `hello_imgui.run()` to start the application, optionally specifying the window title and size.\n", - "\n", - "\n", - "Notes: \n", - "- Dear ImGui is not able to load images by itself, so we use Hello ImGui's `image_from_asset()` function to load and display an image. \n", - "- The image will either be loaded from a path relative to the assets folder, or via a full path provided as an argument to `image_from_asset()`.\n", - "- Look at the Hello ImGui tutorial for more information on how to start a Hello ImGui application, and how to load images.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "image/jpeg": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from imgui_bundle import imgui, hello_imgui, ImVec2\n", - "\n", - "def gui(): # 1.\n", - " # Display a label saying \"Hello, \"\n", - " imgui.text(\"Hello, \") # 2.\n", - "\n", - " # Display an image from the \"assets/\" folder\n", - " texture_id = hello_imgui.im_texture_id_from_asset(\"images/world.png\") # 3.\n", - " imgui.image(texture_id, ImVec2(100, 100)) # 3.\n", - "\n", - "\n", - "hello_imgui.run(gui, window_title=\"Hello, World!\", window_size=(150, 150)) # 4." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```cpp\n", - "#include \"imgui.h\"\n", - "#include \n", - "\n", - "void Gui() { // 1.\n", - " // Display a label saying \"Hello,\"\n", - " ImGui::Text(\"Hello, \"); // 2.\n", - "\n", - " // Display an image from the \"assets/\" folder\n", - " auto texture_id = HelloImGui::ImTextureIdFromAsset(\"images/world.png\"); // 3.\n", - " ImGui::Image(texture_id, ImVec2(100, 100)); // 3.\n", - "}\n", - "\n", - "int main() {\n", - " HelloImGui::Run( // 4.\n", - " Gui, // guiFunction\n", - " \"Hello, World!\", // windowTitle\n", - " false, // windowSizeAuto\n", - " false, // windowRestorePreviousGeometry\n", - " {400, 300}); // windowSize\n", - "}\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Handling button clicks\n", - "\n", - "We will now handle the click events, with the following steps:\n", - "\n", - "1. Add an AppState class to store the state of the application. This is a recommended best practice, as it allows to separate the GUI code from the business logic.\n", - "2. Add a counter to the AppState. This counter will be incremented each time a button is clicked.\n", - "3. Let the gui function take an AppState as an argument (and possibly modify it).\n", - "4. Add a button to the GUI, with `imgui.button()`, and increment the counter when the button is clicked.\n", - "5. Add a tooltip to the button, to display a message when the user hovers over it.\n", - "6. Add a button to exit the application (see note below).\n", - "7. Create a main() function to run the application, where we create an AppState object\n", - "8. Create a lambda function to call the gui function with the AppState object as an argument.\n", - "9. Call `hello_imgui.run()` with the lambda function as an argument.\n", - "\n", - "*Note: In the case of a web application, such as in this tutorial, the \"exit\" button will not have any effect. In the case of a desktop application, it will close the window.*" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "data": { - "image/jpeg": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from imgui_bundle import imgui, hello_imgui\n", - "\n", - "class AppState: # 1.\n", - " counter: int = 0 # 2.\n", - "\n", - "def gui(app_state: AppState): # 3.\n", - " imgui.text(f\"Counter: {app_state.counter}\")\n", - " if imgui.button(\"Increment\"): # 4.\n", - " app_state.counter += 1\n", - " imgui.set_item_tooltip(\"Click to increment the counter\") # 5.\n", - "\n", - " if imgui.button(\"Exit\"):\n", - " hello_imgui.get_runner_params().app_shall_exit = True # 6.\n", - "\n", - "def main(): # 7.\n", - " app_state = AppState() # 7.\n", - " gui_fn = lambda: gui(app_state) # 8.\n", - " hello_imgui.run(gui_fn) # 9.\n", - "\n", - "if __name__ == \"__main__\": # 7.\n", - " main() # 7." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```cpp\n", - "#include \"imgui.h\"\n", - "#include \n", - "\n", - "class AppState { // 1.\n", - "public:\n", - " int counter = 0; // 2.\n", - "};\n", - "\n", - "\n", - "void Gui(AppState& appState) { // 3.\n", - " ImGui::Text(\"Counter: %d\", appState.counter);\n", - " if (ImGui::Button(\"Increment\")) . // 4.\n", - " appState.counter++;\n", - " ImGui::SetItemTooltip(\"Click to increment the counter\"); // 5.\n", - "\n", - " if (ImGui::Button(\"Exit\")) { // 6.\n", - " HelloImGui::GetRunnerParams()->appShallExit = true;\n", - " }\n", - "}\n", - "\n", - "int main() {\n", - " AppState appState; // 7.\n", - " auto gui_fn = [&]() { Gui(appState); }; // 8.\n", - " HelloImGui::Run(gui_fn, \"Handling Button Clicks\", false, false, {400, 300}); // 9.\n", - "}\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using widgets to edit values\n", - "\n", - "We will now create an application which displays a globe and allows the user to set the size of the globe.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAGkAaQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAoopkkqRDLHFAD6QkAZPSsm5121TKrLhh6iudvPEt0WKxSgr6Yq405S2E2kdjJfW0X35QKrtqtuR+7lU1yKasbiMmfAPpVK41GNSVVh+FaKg+ouY6m816SEfu3Q/hVBvFF0P44vyrjbm+mYnZk1WDSOcuSPxrVUIJaiu2d0PFN2f4ovyp3/AAk95/ei/KuPiGR1NPcsORU8lMvlkdd/wkl8RkeWfwqzaeIpZX2ylF/CuNgvnUbWHFTSTxEbt+DT9lBke8jvE1uDPzyoBVuHU7Sb7kyk15iNrtkOfzq3b3UtqcxmodDsx3PTVYMMg5pa4OHxFeo4y+1e/Fb1r4it2A82Xn6VlKnJbjub1FQwXUVwoaNsg1NUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKQkKMmo550t4i7Ecds1zGpa2XdkTKntzTUW9gNe+1mG2+U7gfWubudTvLiY+TIzL6GqUlwZD++Oau28kCoCowa2UYx3HytlK4sLicBnHJ61FHp4i++Oa0Zbzy+p61Ue6D96t1WthxpETwRM2KY2lwE5Ip5cZyKHnOzrWTqGqporXFpBFH8g5rAu52gbJOFrdll3DBNZN9Asy4Iq41L7kShYgtdUjfhG+taCzbh1rlriyltG3pwOtXNN1DzMKx56VUkt0RGT6nQKgc806W2Gzgc1eskie2yR81LMoVeKzUmjSyZkBZY+lQyahLC2GNXZm29xVG6ltUQtIASB61pGdyJRSLEF5JcMADkVebbEm4H564i516OKXEBxUB125eTJkOK3SbMLo7y28S3VrMIzKVUe9dlpXii2uFVGkLP3rxiPUllf5jk1bh1GS3ffE+KynRT2LTPf45FlQOvQ0+vL/AA94wEJ23MhbsBmvQdO1SHUEymAcetcsoOO5RfoooqQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKr3V0sEbHIyBnGaLq5S3QksAccZrjtT1NrqTA4xxx3qoxcmAapqRvHJXIA96xpHZmyc59asdiDUT4C4AzW2kdEXGIscDuM5qyImVcZxTbeZY0G7FRT6pEuRkcVk22a6JEM8UpbJfioi+0gE1WutajwcYrGn1fc3FJpsFNI6b7Sirjg1DJdpsxkVzY1HC/ezVaTUWdsAmp5GDqo2rq8CjINZcmpYJyahljmMIkIbBrP8mWWTAVutaxhYynO5rw3qT5R1zmmPYMlyssfCjnAotbQoVLcVsrtEXYmqi2tCNykdYa1XGDgVUuvFJ8vAjbP0NTXUQkJJGKWDToXXkKaTlFFKLZz15rdxMvyl1/CsiW+upM5lYit7W2giUIqqCDjisdIFYEgg5rohZq5jO97EVohlbLGr72pb7pqGKAICd2KkjufLlB6itLkWKsiyQP1NTQXjZwxNOu7tXydgqkpy2RT3FexvJI4AZCc+1dBoXiOaxmXzGYj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FFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/9k=", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from imgui_bundle import imgui, hello_imgui, ImVec2\n", - "\n", - "class AppState:\n", - " # Add a globe_size attribute to the AppState class\n", - " globe_size: float = 100.0\n", - "\n", - "def gui(app_state: AppState):\n", - " # hello_imgui.image_from_asset will keep the aspect ratio of the image\n", - " # if one of the dimensions is set to 0\n", - " hello_imgui.image_from_asset(\"images/world.png\", ImVec2(app_state.globe_size, 0))\n", - "\n", - " # Display a slider to change the globe size\n", - " # In Python, ImGui widgets often return a tuple of (value_changed: bool, new_value: ...), \n", - " # hence we store the first element of the tuple in a variable called _changed, \n", - " # and the second element in app_state.globe_size\n", - " #\n", - " # Note: This is specific to Python bindings. \n", - " # In C++, the return value of ImGui::SliderFloat would be a single bool \n", - " # and it would modify the globe_size variable directly (passed by pointer).\n", - " # The equivalent C++ code would be:\n", - " # bool changed = ImGui::SliderFloat(\n", - " # \"Globe size\", &globe_size, 20.0, 400.0);\n", - " _changed, app_state.globe_size = imgui.slider_float(\"Globe size\", app_state.globe_size, 20.0, 400.0)\n", - "\n", - "\n", - "def main():\n", - " app_state = AppState()\n", - " gui_fn = lambda: gui(app_state)\n", - " hello_imgui.run(gui_fn, window_title=\"Hello, World!\", window_size=(420, 420))\n", - "\n", - "if __name__ == \"__main__\":\n", - " main()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```cpp\n", - "#include \"hello_imgui/hello_imgui.h\"\n", - "#include \"imgui.h\"\n", - "\n", - "class AppState {\n", - "public:\n", - " // Add a globe_size attribute to the AppState class\n", - " float globe_size = 100.0f;\n", - "};\n", - "\n", - "// Define the GUI function\n", - "void Gui(AppState& app_state) {\n", - " // hello_imgui::ImageFromAsset will keep the aspect ratio of the image\n", - " // if one of the dimensions is set to 0\n", - " HelloImGui::ImageFromAsset(\"images/world.png\", ImVec2(app_state.globe_size, 0.0f));\n", - "\n", - " // Display a slider to change the globe size\n", - " // In C++, ImGui widgets modify variables directly using pointers.\n", - " // The return value of ImGui::SliderFloat is a bool indicating whether the value was changed.\n", - " //\n", - " // Note: This differs from Python bindings, where the method returns a tuple (changed, value).\n", - " // Here, we simply pass a pointer to app_state.globe_size.\n", - " bool changed = ImGui::SliderFloat(\"Globe size\", &app_state.globe_size, 20.0f, 400.0f);\n", - " // `changed` is true if the slider's value was modified, but we don't use it here.\n", - "}\n", - "\n", - "int main() {\n", - " AppState app_state;\n", - " auto gui_fn = [&]() { Gui(app_state); };\n", - " HelloImGui::Run(gui_fn, \"Hello, World!\", false, false, {420, 420});\n", - "}\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Advices to layout your GUI\n", - "\n", - "* Widget labels need to be unique, so you can use a suffix to create a unique identifier for a widget by adding a suffix \"##some_suffix\", which will not be displayed : see [FAQ](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system)\n", - "* You can use horizontal and vertical layouts to create columns and rows.\n", - "* You can use the `imgui.set_next_item_with` function to set the width of the next widget.\n", - "\n", - "**Using em units**\n", - "\n", - "It is important to use em units (see https://en.wikipedia.org/wiki/Em_(typography) ), in order to create application whose layout adapts to the font size, and does not depend on the screen resolution and scaling.\n", - "\n", - "Hello ImGui provides several helper functions to convert between pixels and em units:\n", - "\n", - "*In Python*\n", - "```python\n", - "hello_imgui.em_to_vec2(em_width: float, em_height: float) -> ImVec2\n", - "hello_imgui.em_size(em: float = 1.0) -> float\n", - "hello_imgui.pixels_to_em(px_width: float, px_height: float) -> ImVec2\n", - "```\n", - "\n", - "*In C++*\n", - "```cpp\n", - "ImVec2 HelloImGui::EmToVec2(float em_width, float em_height);\n", - "float HelloImGui::EmSize(float em = 1.0f);\n", - "ImVec2 HelloImGui::PixelsToEm(float px_width, float px_height);\n", - "```\n", - "\n", - "The code below illustrates these techniques:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "image/jpeg": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from imgui_bundle import imgui, hello_imgui, imgui_ctx\n", - "\n", - "\n", - "class AppState:\n", - " \"\"\"Our application state\"\"\"\n", - " name: str = \"\"\n", - " first_name: str = \"\"\n", - " city: str = \"\"\n", - " age: int = 0\n", - "\n", - "\n", - "def input_text_aligned(label: str, value: str, width_pixel: float) -> tuple[bool, str]:\n", - " \"\"\"A helper function to create a label and an input text field in the same row, \n", - " where the label is placed to the left, and the input field is right-aligned.\n", - " It returns a tuple of (changed: bool, value: str), following the pattern of imgui.input_text.\n", - " \"\"\"\n", - " # We will use an horizontal layout to place the label and the input field in the same row\n", - " # (by default, imgui places widgets vertically)\n", - " with imgui_ctx.begin_horizontal(label):\n", - " # Display the label on the left side\n", - " imgui.text(label)\n", - " # Add a spring, which will occupy all remaining space:\n", - " # this will push the input field to the right\n", - " imgui.spring()\n", - " imgui.set_next_item_width(width_pixel)\n", - " # Note: by default, imgui.input_text will place the label to the right of the input field.\n", - " # We do not want that, so we will use \"##\" to hide the label. We still need to make it unique,\n", - " # so we append the user provided label to it (which will not be displayed).\n", - " changed, value = imgui.input_text(\"##\" + label, value)\n", - " return changed, value\n", - "\n", - "\n", - "def gui(app_state: AppState):\n", - " # Set the width of the widgets: we will use 10 em for most widgets\n", - " widgets_width = hello_imgui.em_size(10)\n", - "\n", - " # Add some spacing at the top: we will skip 1 line of text\n", - " imgui.dummy(hello_imgui.em_to_vec2(0, 1))\n", - "\n", - " # Enclose the widgets in a vertical layout: this is important, so that \n", - " # the inner horizontal layouts use their parent's layout width.\n", - " with imgui_ctx.begin_vertical(\"main\"):\n", - " imgui.separator_text(\"Personal information\")\n", - " imgui.dummy(hello_imgui.em_to_vec2(20, 0))\n", - " _, app_state.name = input_text_aligned(\"Name\", app_state.name, widgets_width)\n", - " _, app_state.first_name = input_text_aligned(\"First name\", app_state.first_name, widgets_width)\n", - " _, app_state.city = input_text_aligned(\"City\", app_state.city, widgets_width)\n", - "\n", - " # Add a slider to input the age: we enclose it in a horizontal layout\n", - " with imgui_ctx.begin_horizontal(\"Age\"):\n", - " imgui.text(\"Age\")\n", - " # Add a spring to push the slider to the right\n", - " imgui.spring()\n", - " # Set the width of the slider to 10 em (by default, it would be the full width of the window)\n", - " imgui.set_next_item_width(widgets_width)\n", - " # use imgui.slider_int with a hidden label, and a range from 0 to 120\n", - " _, app_state.age = imgui.slider_int(\"##Age\", app_state.age, 0, 120)\n", - "\n", - "\n", - "def main():\n", - " app_state = AppState()\n", - " hello_imgui.run(lambda: gui(app_state))\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " main()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# What's next?\n", - "\n", - "- There are **many** other widgets in Dear ImGui, and Dear ImGui Bundle. Look at the next pages of this tutorial for more examples.\n", - "- Look at [Dear ImGui Manual](https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "v312_2", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorial.kc/imgui.ipynb b/tutorial.kc/imgui.ipynb deleted file mode 100644 index 436be9d4..00000000 --- a/tutorial.kc/imgui.ipynb +++ /dev/null @@ -1,101 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Dear ImGui\n", - "\n", - "\n", - "## References and full list of widgets available in Dear ImGui\n", - "\n", - "Link to ImGui Manual, with image of Python code.\n", - "\n", - "## Difference between C++ and Python API.\n", - "\n", - "## How to layout your GUI with ImGui\n", - "\n", - "\n", - "imgui.same_line()\n", - "imgui.begin_group()\n", - "imgui.set_next_item_width()\n", - "\n", - "\n", - "# Using imgui.begin_horizontal() and imgui.end_horizontal()\n", - "\n", - "Note: this addition is specific to ImGui Bundle\n", - "\n", - "imgui.begin_horizontal()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ChatGPT advices\n", - "```\n", - "Dear ImGui Tutorial\n", - "\n", - " State Management:\n", - " Explain how to manage local state vs. global state in ImGui widgets.\n", - " Provide examples of immediate changes with local variables (e.g., sliders) and persistent state (e.g., static variables in C++ or class attributes in Python).\n", - "\n", - " Input Handling:\n", - " Show how to handle user input with text input, keyboard shortcuts, or mouse events.\n", - " Include an example of imgui.is_item_active() and similar methods to check interaction.\n", - "\n", - " Custom Widgets:\n", - " Briefly introduce how to extend Dear ImGui with custom widgets (C++ focus, Python bindings example if available).\n", - " Mention ImDrawList for custom rendering.\n", - "\n", - " Performance Tips:\n", - " Provide practical tips to optimize Dear ImGui applications for real-time interaction:\n", - " Avoid heavy computations in the GUI loop.\n", - " Use caching techniques for repetitive operations.\n", - "\n", - " Integration Examples:\n", - " Show Dear ImGui integration into existing projects with libraries like GLFW or SDL.\n", - " Highlight key backend differences (e.g., OpenGL3, DirectX).\n", - "```\n", - "\n", - "\n", - "Add a small section in each tutorial highlighting common mistakes or tricky parts:\n", - "\n", - "* DPI scaling issues.\n", - "* Misuse of imgui.same_line() leading to misaligned widgets.\n", - "* Performance bottlenecks in render loops." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "v312_2", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorial.kc/imgui_bundle.ipynb b/tutorial.kc/imgui_bundle.ipynb deleted file mode 100644 index 425aad01..00000000 --- a/tutorial.kc/imgui_bundle.ipynb +++ /dev/null @@ -1,58 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ImGui Bundle\n", - "\n", - "\n", - "## Dear ImGui & Hello ImGui\n", - "\n", - "Links\n", - "\n", - "## Additional Widgets available in Dear ImGui Bundle\n", - "\n", - "Use demo_widgets\n", - "\n", - "\n", - "## ImPlot\n", - "\n", - "\n", - "## ImGuizmo\n", - "\n", - "\n", - "## ImGuiColorTextEdit\n", - "\n", - "\n", - "..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "v312_2", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorial/.gitignore b/tutorial/.gitignore index ee01c51e..c685b848 100644 --- a/tutorial/.gitignore +++ b/tutorial/.gitignore @@ -1,3 +1,4 @@ _build/ node_modules/ dist/ +_brainstorm/ diff --git a/tutorial/_tpl/instantiate_tutorial_template.py b/tutorial/_tpl/instantiate_tutorial_template.py deleted file mode 100644 index 247134ad..00000000 --- a/tutorial/_tpl/instantiate_tutorial_template.py +++ /dev/null @@ -1,37 +0,0 @@ -import os - - -TPL_DIR = os.path.dirname(__file__) -TUTORIAL_DIR = os.path.realpath(os.path.join(TPL_DIR, "..")) - -print(TUTORIAL_DIR) - - -def _process(tutorial_name: str): - print(f"Creating tutorial {tutorial_name}...") - - # Copy TPL_DIR/tpl.jpg to name.jpg - import shutil - shutil.copyfile(f"{TPL_DIR}/tpl.jpg",f"{TUTORIAL_DIR}/{tutorial_name}.jpg") - shutil.copyfile(f"{TPL_DIR}/tpl.cpp",f"{TUTORIAL_DIR}/{tutorial_name}.cpp") - shutil.copyfile(f"{TPL_DIR}/tpl.py",f"{TUTORIAL_DIR}/{tutorial_name}.py") - - # Read content of TPL_DIR/tpl.md - with open(f"{TPL_DIR}/tpl_md", "r") as f: - tpl_md = f.read() - basename = os.path.basename(tutorial_name) - tpl_md = tpl_md.replace("tpl", basename) - # Write content to TUTORIAL_DIR/name.md - with open(f"{TUTORIAL_DIR}/{tutorial_name}.md", "w") as f: - f.write(tpl_md) - - -def cli(): - import sys - tutorial_name = sys.argv[1] - _process(tutorial_name) - - -if __name__ == "__main__": - cli() - diff --git a/tutorial/_tpl/tpl.cpp b/tutorial/_tpl/tpl.cpp deleted file mode 100644 index 92c702a3..00000000 --- a/tutorial/_tpl/tpl.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "imgui.h" -#include - -class AppState { -}; - -void Gui(AppState& appState) { -} - -int main() { - AppState appState; - auto gui_fn = [&]() { Gui(appState); }; - HelloImGui::Run(gui_fn, "TEMPLATE", true); -} diff --git a/tutorial/_tpl/tpl.jpg b/tutorial/_tpl/tpl.jpg deleted file mode 100644 index bd7446ab3bed38b88c34d073e3712a2f4f40d2f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3298 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 - - - - - Immediate GUI Tutorial - - - - -
-

Immediate GUI Tutorial

-
-
- -
- -
-
-
- -
- - - -``` -=========================================================================== -File resources_singlepage/app.js -=========================================================================== -``` -import { initTOC, tocRoot } from "./toc_loader.js"; -import { initResizer } from "./resizer.js"; -import { loadPage } from "./page_loader.js"; - -async function initializeAll() { - await initTOC(); - initResizer(); - - const rootPage = tocRoot() - loadPage(rootPage.file + ".md"); - // loadPage("discover/hello_world.md") -} - -document.addEventListener("DOMContentLoaded", initializeAll); -``` -=========================================================================== -File resources_singlepage/breadcrumbs.js -=========================================================================== -``` -export function updateBreadcrumbs() { - const contentArea = document.getElementById("content-area"); - const h2Elements = contentArea.querySelectorAll("h2"); - const breadcrumbContainer = document.getElementById("breadcrumb-container"); - - // Clear previous breadcrumbs - breadcrumbContainer.innerHTML = ""; - - // Create a simple list of H2 headings as links - // Each H2 can have an id (if not, we add one) - h2Elements.forEach((h2, index) => { - if (!h2.id) { - // create a slug from the text - const slug = h2.textContent.toLowerCase().replace(/\s+/g, '-'); - h2.id = slug; - } - - const link = document.createElement('a'); - link.href = `#${h2.id}`; - link.textContent = h2.textContent; - breadcrumbContainer.appendChild(link); - breadcrumbContainer.appendChild(document.createTextNode(" | ")); - }); - - if (breadcrumbContainer.lastChild) { - breadcrumbContainer.removeChild(breadcrumbContainer.lastChild); // remove trailing separator - } -} -``` -=========================================================================== -File resources_singlepage/page_loader.js -=========================================================================== -``` -import { marked } from "marked"; -import { baseUrl } from "marked-base-url"; -import { updateBreadcrumbs } from "./breadcrumbs.js"; - -export async function loadPage(mdPath) { - const baseUrlPath = mdPath.substring(0, mdPath.lastIndexOf('/') + 1); - marked.use(baseUrl(baseUrlPath)); - - const response = await fetch(mdPath); - const mdText = await response.text(); - const html = marked.parse(mdText); - - const contentArea = document.getElementById("content-area"); - contentArea.innerHTML = html; - - updateBreadcrumbs(); // after content is loaded -} -``` -=========================================================================== -File resources_singlepage/resizer.js -=========================================================================== -``` -let isResizing = false; -let startX = 0; -let startContentWidth = 0; - -function _onResizerMouseDown(e) -{ - const layout = document.querySelector('.layout-container'); - isResizing = true; - startX = e.clientX; - // Get current width of content area (second column) - // We assume something like: [TOC:200px] [content:auto] [handle:5px] [code:33%] - // We'll convert the content column to a fixed width during resizing - const computedStyle = window.getComputedStyle(layout); - const columns = computedStyle.getPropertyValue('grid-template-columns').split(' '); - // columns[1] is the content area width - startContentWidth = parseFloat(columns[1]); - // If it's 'auto', we might need to set a default width or measure #content-area’s offsetWidth - if (isNaN(startContentWidth)) { - const contentArea = document.getElementById('content-area'); - startContentWidth = contentArea.offsetWidth; - } - document.body.style.userSelect = 'none'; // Prevent text selection -} - -function _onResizerMouseMove(e) -{ - const layout = document.querySelector('.layout-container'); - - if (!isResizing) return; - const dx = e.clientX - startX; - const newContentWidth = startContentWidth + dx; - if (newContentWidth > 100) { // A minimum width for content area - layout.style.gridTemplateColumns = `200px ${newContentWidth}px 5px auto`; - } -} - -function _onResizerMouseUp() -{ - isResizing = false; - document.body.style.userSelect = 'auto'; -} - -export function initResizer() -{ - const resizer = document.getElementById('resizer'); - resizer.addEventListener('mousedown', _onResizerMouseDown); - document.addEventListener('mousemove', _onResizerMouseMove); - document.addEventListener('mouseup', _onResizerMouseUp); -} -``` -=========================================================================== -File resources_singlepage/style.css -=========================================================================== -``` -body { - font-size: 14px; -} - -.layout-container { - display: grid; - grid-template-columns: 400px auto 5px 33% /* TOC | content | handle | code */; - height: 100vh; - overflow: hidden; -} - -#toc-sidebar { - border-right: 1px solid #ccc; - padding: 1rem; - overflow-y: auto; -} - -#content-area { - padding: 1rem; - overflow-y: auto; -} - -#resizer { - background: #ccc; - cursor: col-resize; - width: 5px; /* Matches the grid template size */ - height: 100%; -} - -#code-panel { - border-left: 1px solid #ccc; - padding: 1rem; - overflow-y: auto; -}``` -=========================================================================== -File resources_singlepage/toc_loader.js -=========================================================================== -``` -import { loadPage } from "./page_loader.js"; - - -// The global `gToc` variable will store the TOC data (from toc.json) -let gToc; - -export function tocRoot() { -return gToc.root; -} - -export async function initTOC() { -const response = await fetch("resources_singlepage/toc.json"); -gToc = await response.json(); -const tocSidebar = document.getElementById("toc-sidebar"); - - // Build a simple TOC list from toc's `chapters` - // toc format example: - // { - // "format": "jb-book", - // "root": "discover_immediate", - // "chapters": [ - // { "file": "discover/hello_world", "sections": [...] } - // ] - // } - _buildTocList(gToc, tocSidebar); -} - -function _buildTocList(toc, container) { -const ul = document.createElement("ul"); - - // root page - const rootLi = _createTocItem(toc.root); - ul.appendChild(rootLi); - - // chapters - toc.chapters.forEach(chapter => { - const li = _createTocItem(chapter); - ul.appendChild(li); - - if (chapter.sections) { - const subUl = document.createElement("ul"); - chapter.sections.forEach(section => { - const subLi = _createTocItem(section.file); - subUl.appendChild(subLi); - }); - li.appendChild(subUl); - } - }); - - container.appendChild(ul); -} - -function _createTocItem(item) { -const li = document.createElement("li"); -const link = document.createElement("a"); -link.href = "#"; -link.textContent = item.title || item.file; // fallback if title not found -link.addEventListener("click", (e) => { -e.preventDefault(); -loadPage(item.file + ".md"); -}); -li.appendChild(link); -return li; -} -``` -=========================================================================== - diff --git a/tutorial/gpt_presentation.md b/tutorial/gpt_presentation.md deleted file mode 100644 index d4aaf31a..00000000 --- a/tutorial/gpt_presentation.md +++ /dev/null @@ -1,652 +0,0 @@ -I'm working on creating a complete tutorial for ImGui Bundle and Hello ImGui. -It will be web-based, and it will use pyodide to run the code examples, since -I now have a very efficient way to run imgui python apps in the browser. - -In order to simplify my process, I created the tutorial files in a manner which is compatible with jupyter-book: this way, I can build the tutorial as a jupyter-book, and then convert it to a web-based tutorial. However, in this web-based tutorial, the code cannot be run. - -I did explore ways to hack into sphinx, in order to make the code renewable inside a Jupiter book: it is a dead-end. -The reason for that is that jupyter-book reloads the whole HTML at each new page, and pyodide is way too slow to reload at each page. -Another reason is that sphinx + jupyter-book is too complex for me to handle. - -So I would like to explore with you the possibility to display the tutorial with a Single Page Web application: -We would have to write code to parse the toc, display markdown, and then to run pyodide code. - -We will take it slowly as it is a complex endeavor. Please let's work step by step, and first discuss together -about the possible directions we can take before exploring them. - -In my opinion, the first step is too check whether it is possible to display the md files in a single page web application, while respecting the toc, and displaying it in a nice way. We will work on running the code much later (and I do have solutions for that). - -I will want you to help me to get very organized code: -- separate concerns in different files and functions -- Do not nest to much calls in javascript -- Use existing js libraries when possible, and provide a way to load them in the html file - - -Here is the list of files I have at the moment: -``` -. -. -├── _config.yml -├── _toc.yml -├── discover/ -│ ├── button.cpp -│ ├── button.jpg -│ ├── button.py -│ ├── hello_world.cpp -│ ├── hello_world.jpg -│ ├── hello_world.md -│ ├── hello_world.py -│ ├── layout_advices.cpp -│ ├── layout_advices.jpg -│ ├── layout_advices.md -│ ├── layout_advices.py -│ ├── whats_next.md -│ ├── widget_edit.cpp -│ ├── widget_edit.jpg -│ ├── widget_edit.md -│ └── widget_edit.py -├── discover_immediate.md -``` - -Below is an extract of their content: -========================================== - -=========================================================================== -File discover/hello_world.md -=========================================================================== -``` -# Discover Immediate Mode GUIs - -We'll start by creating a simple applications, where we will demonstrate several aspects of ImGui and Hello ImGui. - -## Hello, World! - -We will start by running a simple Hello World application, which displays: -![](hello_world.jpg) - -We will follow the steps below: -1. We define a gui function that will be called by Hello ImGui at every frame: It will display the GUI of our application *and* handle user events. -2. We use the `imgui.text()` (Python) or `ImGui::Text` (C++) function to display a text in the window. -3. We call `hello_imgui.run()` (Python) or `HelloImGui::Run()` (C++) to start the application, optionally specifying the window title and size, or if we want it to set its size automatically. - - -**Python** -```{literalinclude} hello_world.py -``` - - -**C++** -```{literalinclude} hello_world.cpp -``` - - -## Handling button events - -We will now create an application which handles clicks on a button, and updates a counter each time the button is clicked: - -![](button.jpg) - -We will follow the steps below: -1. Add an AppState class to store the state of the application. This is a recommended best practice, as it allows to separate the GUI code from the business logic. -2. Add a counter to the AppState. This counter will be incremented each time a button is clicked. -3. Let the gui function take an AppState as an argument (and possibly modify it). -4. Add a button to the GUI, with `imgui.button()`, and increment the counter when the button is clicked. -5. Add a tooltip to the button, to display a message when the user hovers over it. -6. Add a button to exit the application (see note below). -7. Create a main() function to run the application, where we create an AppState object -8. Create a lambda function to call the gui function with the AppState object as an argument. -9. Call `hello_imgui.run()` with the lambda function as an argument. - -*Note: In the case of a web application, such as in this tutorial, the "exit" button will not have any effect. In the case of a desktop application, it will close the window.* - - -**Python** -```{literalinclude} button.py -``` - - -**C++** -```{literalinclude} button.cpp -``` -``` -=========================================================================== -File discover/hello_world.py -=========================================================================== -``` -from imgui_bundle import imgui, hello_imgui - - -def gui(): # 1. -imgui.text("Hello, World!") # 2. - - -hello_imgui.run(gui, window_title="Hello, World!", window_size_auto=True) # 3. -``` -=========================================================================== -File _config.yml -=========================================================================== -``` -# Book settings -# Learn more at https://jupyterbook.org/customize/config.html - -# -# Main book settings -# ----------------------------------------------------------- -title: "Immediate GUI Tutorial" -author: Pascal Thomet -#logo: ../assets/logo/logo.jpg -copyright: "2025" - -# -# Notebook execution during book build -# See https://jupyterbook.org/content/execute.html -# ----------------------------------------------------------- -# Never execute notebooks during the build process -# (to preserve our nice fiatlight execution thumbnails) -execute: -execute_notebooks: off - -# Information about where the book exists on the web -# ----------------------------------------------------------- -#repository: -# url: https://github.com/pthom/fiatlight # Online location of your book -# path_to_book: src/python/fiatlight/doc # Optional path to your book, relative to the repository root -# branch: master # Which branch of the repository should be used when creating links (optional) -# -##launch_buttons: -## notebook_interface: "jupyterlab" # or "classic" -## binderhub_url: "https://mybinder.org" # The URL for your BinderHub (e.g., https://mybinder.org) -# -## Add GitHub buttons to your book -## See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository -#html: -# use_issues_button: true -# use_repository_button: true -# -## Misc -## ----------------------------------------------------------- -## Define the name of the latex output file for PDF builds -#latex: -# latex_documents: -# targetname: book.tex - - -``` -=========================================================================== -File _toc.yml -=========================================================================== -``` -# Syntax inspired from jupyter-book -format: jb-book -root: discover_immediate - -chapters: -- file: discover/hello_world - sections: - - file: discover/widget_edit - - file: discover/layout_advices - - file: discover/whats_next -``` -=========================================================================== - - -Can we discuss together about the possible directions we can take before exploring them? -First, we'll concentrate on displaying the md files in a single page web application, while respecting the toc, and displaying it in a nice way. We will work on running the code much later (and I do have solutions for that). - -Please ask question if you feel you need more details, or information about the project or files. - - - - - -=========================================================================== - -Clarifications asked by GPT: - -> Project Goal: You want a single-page web application that: -> Displays the content from these markdown files in a structured way. -> ... - -=> Correct. Please note that in the end, I will want to add a resizable canvas on the right of the page, where the code will be executed (probably with a splitter). - -> Environment Assumptions: - -We are going to run on a web server. The file structure will be known. -So the JavaScript code can query the markdown and images files. -Concerning the translation from Markdown to HTML, I think this should be done via javascript. - -> Would you like to use a known JS library to handle markdown rendering (e.g., marked.js)? -Yes - - -> Desired Implementation Details: -> You mentioned separation of concerns and using existing JS libraries. For instance, using marked.js or markdown-it for rendering markdown would simplify code. Is that acceptable? - -Yes! - -> For the TOC: The _toc.yml can be parsed into a JS object. From this, we can build a navigation sidebar that, when a user clicks on a section, fetches and displays the corresponding markdown. Is this approach what you envision? - -Yes. It would be desirable to also parse the md file for H2 titles, and display them in a breadcrumbs specific to the page. -(H1 would be the title of the page, there will be only one per page). - - -> Technical Constraints: -> Are there any constraints on which frameworks/libraries can be used? For example, can we use a framework like React or Vue, or do you prefer a vanilla JS solution plus small libraries? - -I'd very much prefer a vanilla JS solution plus small libraries. I'm not familiar with React or Vue, and I'd like to keep the project as simple as possible. - -> How important is it to maintain a pure static structure (no build steps) versus a solution that could involve a small build pipeline (like a one-time preprocessing step that converts .md to .html or .json)? - -We could envision a small build pipeline, preferably in Python, in which I am proficient. - -> Do you have preferences for the layout and styling? For instance, would a standard "sidebar on the left, main content on the right" layout be acceptable? - -I would like something beautiful and modern. -For the toc, a sidebar on the left is the way to go. -As for the inner page navigation, I would like to save space on the right since we will have a canvas there. -So I thought about a breadcrumb on top of the page, or any other solution that would save space on the right. -(knowing that the number of H2 titles in a page will be limited to <= 6). - -> Will styling be handled by a CSS framework (like Bootstrap or Tailwind) or custom CSS? - -I don't know. I will rely on your advices. - -> Future Steps: -> Eventually, we’ll integrate code execution with pyodide. Will the code blocks appear as runnable code cells in the final UI, or just as static code blocks until triggered? - -The code blocks will appear as editable code blocks, with syntax highlighting, and a run button. -Once the run button is clicked, the code will be executed and the app will be displayed in the canvas on the right. -If there are errors, a dedicated panel should appear. -If there is stdout output to be displayed, I have not decided yet. Maybe we should envision two displays below the code. - -> Are we aiming for an experience similar to a Jupyter Book but all contained in one page, with dynamic navigation? -That is the idea. -- Each code cell will start an app from scratch (using hello_imgu.run() for instance) -- the canvas will be refreshed at each run -- However the python runtime will not be restarted at each run - -Do you have other questions or can we start exploring possible alternatives? - -=========================================================================== - -> Questions for you: -> Would you be comfortable adding a minimal build step to convert _toc.yml to JSON using Python? This would simplify the JavaScript code. -Absolutely. We would need to create a scripts/ folder, and store it there. Then I have a justfile that helps me remember the commands to run; we will populate it with the command to run the build step. - -> Are you open to using a minimal CSS framework like Pico.css to speed up styling? - -Absolutely. I would ask that we add a recipe in the justfile if needed to simplify the process. - -> For the layout, do you prefer starting simple (just TOC on left, content in center), and later add the canvas panel on the right once we handle code execution? - -Actually I would like to have a feeling of the final look. So we will put a dummy code execution panel, with an dummy app image. -In terms of size, I think the execution panel should initially occupy 1/3 of the screen, and the code panel 2/3 (not taking into account the TOC). The user will resize the panels as needed (and later, it may be initially hidden and appear once the code is run). - -I do not know the answer to your question "Two columns or three columns ... Or a two-column layout (TOC on the left, main content in the middle), and a draggable splitter to open a right panel overlay for code output. We need to consider vertical/horizontal splits and what feels natural." Please explain what the difference might be. - -Let's now try to work on implementing this. Once again, please let's work with organized code, and separate concerns in different files and functions. - -=========================================================================== - -> With this setup, you have a three-column layout: TOC on the left, content in the center, and a dummy code panel on the right. Once the basic structure is in place, we can refine functionality, add the resizing splitter, integrate the breadcrumbs, and prepare for code execution steps. -> Does this direction seem good to you? - -Yes, it does! - -The script convert_toc works, and I now have a toc.json file. -I changed the files structure a bit (I do not want to pollute the root of the jupyter-book). -See: -. -├── CMakeLists.txt -├── _config.yml -├── _toc.yml -├── discover/ -│ ├── hello_world.cpp -│ ├── hello_world.jpg -│ ├── hello_world.md -│ ├── hello_world.py -│ ├── layout_advices.cpp -│ ├── ... -├── discover_immediate.md -├── index_singlepage.html -├── justfile -├── resources_singlepage -│ ├── app.js -│ └── style.css -├── scripts -│ └── convert_toc.py -└── toc.json - -I'll want the canvas to be resizable in a later step, and to apply some styling to the page. -I guess this is the next step. Do you agree? - -=========================================================================== - -The resizer work well, and the columns width seem ok. - -In your proposed css, there is no more #toc-sidebar, is that normal? -At the moment, I kept it as before. - -As far as the separation of concerns, I'm afraid that app.js will quickly get huge and difficult to navigate. -I started to split it: - -```bash -ls -1 resources_singlepage -app.js -resizer.js # Contains the resizer code you provided (and referenced in html) -style.css -``` -We will continue to split it in several files as needed: I suspect that the navigation handling will deserve its own file. - -Also, please note that I refactored resizer.js like this, which is more readable: -```javascript -let isResizing = false; -let startX = 0; -let startContentWidth = 0; - -function _onResizerMouseDown(e) -{ - const layout = document.querySelector('.layout-container'); - isResizing = true; - startX = e.clientX; - // Get current width of content area (second column) - // We assume something like: [TOC:200px] [content:auto] [handle:5px] [code:33%] - // We'll convert the content column to a fixed width during resizing - const computedStyle = window.getComputedStyle(layout); - const columns = computedStyle.getPropertyValue('grid-template-columns').split(' '); - // columns[1] is the content area width - startContentWidth = parseFloat(columns[1]); - // If it's 'auto', we might need to set a default width or measure #content-area’s offsetWidth - if (isNaN(startContentWidth)) { - const contentArea = document.getElementById('content-area'); - startContentWidth = contentArea.offsetWidth; - } - document.body.style.userSelect = 'none'; // Prevent text selection -} - -function _onResizerMouseMove(e) -{ - const layout = document.querySelector('.layout-container'); - - if (!isResizing) return; - const dx = e.clientX - startX; - const newContentWidth = startContentWidth + dx; - if (newContentWidth > 100) { // A minimum width for content area - layout.style.gridTemplateColumns = `200px ${newContentWidth}px 5px auto`; - } -} - -function _onResizerMouseUp() -{ - isResizing = false; - document.body.style.userSelect = 'auto'; -} - -function initResizer() -{ - const resizer = document.getElementById('resizer'); - resizer.addEventListener('mousedown', _onResizerMouseDown); - document.addEventListener('mousemove', _onResizerMouseMove); - document.addEventListener('mouseup', _onResizerMouseUp); -} - -document.addEventListener('DOMContentLoaded', initResizer); -``` -I do not like to use long anonymous functions, it makes the code difficult to read. - -Also, note that once we will have added pyodide, we will need to be extremely precise about the order of operations: -pyodide is loaded in async mode, and python code is also executed in async mode. -Thus, we cannot rely on using document.addEventListener('DOMContentLoaded') in too many places; because it makes the initialization sequence of the code difficult to follow. The app is ought to become complex, and we need to be organized. -I'd like us to work towards having an `initializeAll()` function in app.js, which will call every js file initialization function in the right order. - -=========================================================================== - -OK, it works. - -Notes: -1. I had to refactor a bit: - -app.js -``` -// Initialize all parts of the app -async function initializeAll() { - await loadToc(); - initResizer(); - // In the future, initTOC(), initMarkdownLoading(), etc. - loadPage("discover/hello_world.md"); -} - -document.addEventListener("DOMContentLoaded", initializeAll); -``` - -2. When using export/import, I get errors: -``` -export function initResizer() -{ -... -} -``` -==> Uncaught SyntaxError: export declarations may only appear at top level of a module -==> Uncaught SyntaxError: import declarations may only appear at top level of a module - -At the moment, I removed those keywords, and it works. I'm not proficient with this. I don't know if it is important. -If it is complex, maybe we can keep that for a later time. - -3. I see issues in the java console: -``` -The resource at “https://unpkg.com/@picocss/pico@latest/css/pico.min.css” was blocked due to its Cross-Origin-Resource-Policy header (or lack thereof). See https://developer.mozilla.org/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP)# index_singlepage.html - -==> I'm using a local webserver where I activated CORS. I do not know why this error appears. - Anyhow, let's download the css file and put it in the resources_singlepage folder. Please provide me with the link to download it. - - -GET - http://localhost:8005/favicon.ico - [HTTP/1 404 File not found 1ms] - ==> not important - -GET -http://localhost:8005/hello_world.jpg -[HTTP/1 404 File not found 1ms] -GET -http://localhost:8005/button.jpg -[HTTP/1 404 File not found 2ms] - -==> We have to account for the fact that they are inside the discover folder. There will be other folders in the future, so we have to be careful about that. Image paths are to be considered relative to the md file. - -(I did add a dummy image of the app and it works) -``` - -Let's fix the issues, and then move on to the next step. - -=========================================================================== - -discover/hello_world.jpg does exist. - -However, loadPage() is executed in the top folder: -we probably need to scan for relative paths in the markdown file, and adjust them accordingly. -``` -async function loadPage(mdPath) { - console.log("loadPage " + mdPath); // will receive discover/hello_world.md - const response = await fetch(mdPath); - const mdText = await response.text(); - const html = marked.parse(mdText); - const contentArea = document.getElementById("content-area"); - contentArea.innerHTML = html; - buildBreadcrumbs(); -} -``` - -I downloaded pico.css, and the style is now better. The default font size is too big. I have to display the page at 70% to have a good view. - -Let's correct these issues, and move to the next step. Is it navigation and breadcrumbs? - -=========================================================================== - -I solved the issues on my side. The default font size is 14, and the images show correctly. - -Here is what I did: - -* Switched to npm + modules -Here is my package.json: - { - "name": "tutorial", - "version": "1.0.0", - "main": "index.js", - "scripts": { - "dev": "vite", - "build": "vite build", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "description": "", - "dependencies": { - "marked": "^15.0.4", - "marked-base-url": "^1.1.6" - }, - "devDependencies": { - "vite": "^6.0.5" - } - } - -* use of marked-base-url to handle relative paths in markdown files -* now, we have: -app.js -```js -import { marked } from "marked"; -import { baseUrl } from "marked-base-url"; - -async function loadToc() { - const response = await fetch("toc.json"); - const toc = await response.json(); - // Build sidebar navigation from toc -} - -async function loadPage(mdPath) { - console.log("loadPage " + mdPath); - - const baseUrlPath = mdPath.substring(0, mdPath.lastIndexOf('/') + 1); - - marked.use(baseUrl(baseUrlPath)); - - // Fetch and parse the Markdown - const response = await fetch(mdPath); - const mdText = await response.text(); - const html = marked.parse(mdText); - - // Update the content area - const contentArea = document.getElementById("content-area"); - contentArea.innerHTML = html; - - buildBreadcrumbs(); -} -function buildBreadcrumbs() { - // scan content for h2 headings -} - -// Initialize all parts of the app -async function initializeAll() { - await loadToc(); - initResizer(); - // In the future, initTOC(), initMarkdownLoading(), etc. - loadPage("discover/hello_world.md"); -} - -document.addEventListener("DOMContentLoaded", initializeAll); -``` -* I now run a server with npm run dev, and it works well. - - -Note: app.js might grow quickly. It should only be an orchestrator. We will need to place the navigation and breadcrumbs logic in their own file(s). - - -Let's continue! - -=========================================================================== - -This separation in js files is good. It starts to work. - -Some issues: - -## error with breadcrumbs: -I did the changes you specified, but I get an error: -``` -Uncaught SyntaxError: export declarations may only appear at top level of a module resizer.js:43:1 -[vite] connecting... client:742:8 -[vite] connected. client:861:14 -GET -http://localhost:5173/favicon.ico -[HTTP/1.1 404 Not Found 0ms] - -Uncaught (in promise) TypeError: breadcrumbContainer is null - updateBreadcrumbs breadcrumbs.js:7 - loadPage page_loader.js:16 - initializeAll app.js:8 - async* app.js:11 -breadcrumbs.js:7:4 -``` -I do have this in html -```html -
- - -
-``` -and this in breadcrumbs.js -```js -export function updateBreadcrumbs() { - const contentArea = document.getElementById("content-area"); - const h2Elements = contentArea.querySelectorAll("h2"); - const breadcrumbContainer = document.getElementById("breadcrumb-container"); - - // Clear previous breadcrumbs - breadcrumbContainer.innerHTML = ""; - - // Create a simple list of H2 headings as links - // Each H2 can have an id (if not, we add one) - h2Elements.forEach((h2, index) => { - if (!h2.id) { - // create a slug from the text - const slug = h2.textContent.toLowerCase().replace(/\s+/g, '-'); - h2.id = slug; - } - - const link = document.createElement('a'); - link.href = `#${h2.id}`; - link.textContent = h2.textContent; - breadcrumbContainer.appendChild(link); - breadcrumbContainer.appendChild(document.createTextNode(" | ")); - }); - - if (breadcrumbContainer.lastChild) { - breadcrumbContainer.removeChild(breadcrumbContainer.lastChild); // remove trailing separator - } -} -``` - -## Issue in the main navigation: -Let's give an example: - -I updated toc.yml to have two chapters (+ the intro: discover_immediate.md) -``` -format: jb-book -root: discover_immediate - -chapters: -- file: discover/hello_world - sections: - - file: discover/widget_edit - - file: discover/layout_advices - - file: discover/whats_next -- file: imgui/intro -# Possibly some sections here -``` - -In that case, the main navigation should display the H1 titles of the intro + the 2 chapters, i.e.: -- Intro (first H1 from discover_immediate.md) -- Hello, World! (first H1 from discover/hello_world.md) -- Discover ImGui (first H1 from imgui/intro.md) - -At the moment it display all file names (discover/hello_world, discover/widget_edit, discover/layout_advices, discover/whats_next, imgui/intro) diff --git a/tutorial/justfile b/tutorial/justfile index 9589856a..70e0adbe 100644 --- a/tutorial/justfile +++ b/tutorial/justfile @@ -12,10 +12,9 @@ gpt_files_content: for f in $(cat f.txt); do echo "===========================================================================";echo "File $f"; echo "==========================================================================="; echo '```'; cat $f; echo '```'; done # Create a json version of the table of content (for Single Page HTML) -tutorial-json_toc: +tutorial_json_toc: python scripts/convert_toc.py - tutorial_prepare: tutorial-json_toc cd single_page_book_app && npm install