Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### UI layout

- **ImGui docking:** Panels (Sketch List, Shape List, Options, Log, consoles, and similar) can be docked, tabbed, and split inside the main window. On desktop, panels can also be dragged into separate OS windows. The web build supports in-canvas docking only. Saved layout in settings includes dock nodes; older installs without a `[Docking]` section get a default layout on first launch.
- **Settings -> UI:** **Dark mode** and separate **Dark theme** / **Light theme** ImGui controls (window transparency, rounding, borders, padding, spacing). Legacy `imgui_rounding_*` keys migrate to both themes on load.

### Project format (`.ezy`)

- **Format v3:** Saved projects are a ZIP archive (`manifest.json` plus deduplicated `assets/*.rgba` underlay blobs). Sketch underlays reference assets by content hash instead of inline base64. Legacy plain-JSON `.ezy` files (v1/v2, including `rgba_b64` underlays) still load. Document snapshots for undo/redo reference shared assets rather than re-embedding pixel data.
Expand Down
30 changes: 25 additions & 5 deletions docs/usage-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Between those, the pane has **six** collapsible sections. Expand a section to se

1. **3D view navigation** — **View rotation step** (degrees per key press for <kbd>NumPad 8</kbd>/<kbd>2</kbd>/<kbd>4</kbd>/<kbd>6</kbd> orbit and <kbd>Shift</kbd>+<kbd>NumPad 4</kbd>/<kbd>6</kbd> roll; default **45**; **`?`** opens [view roll](https://ezycad.readthedocs.io/en/latest/usage.html#view-roll) on Read the Docs). **Zoom scroll scale** (multiplier for wheel and **+**/**-** zoom; default **4**). Hold <kbd>Shift</kbd> while zooming for a Blender-style finer step (multiply by **0.1**). Numpad shortcuts are documented with <kbd>Num Lock</kbd> off; with <kbd>Num Lock</kbd> on, use main-row alternatives in [usage.md -> View navigation](usage.md#view-navigation). Stored as **`gui.view_roll_step_deg`** and **`gui.view_zoom_scroll_scale`**. See **[usage-occt-view.md](usage-occt-view.md)**.

2. **UI corner rounding** — Sliders **0** to **16** for **Windows, frames, popups**; **Scrollbars and sliders** (has **?**); **Tabs**.
2. **UI** — **Dark mode** checkbox. ImGui layout sliders apply to the **active theme** (switch **Dark mode** to edit the other theme). Grouped under **Transparency**, **Rounding**, **Borders**, **Padding**, and **Spacing**: **Window transparency** (**0.1** to **1.0**); corner rounding sliders **0** to **16** for **Windows, frames, popups**, **Scrollbars and sliders**, and **Tabs**; **Window border**, **Frame border** (**0** to **2**); **Window padding** X/Y, **Frame padding** X/Y, and **Item spacing** X/Y (**0** to **24**).

3. **View presentation** — **Background color 1** and **Background color 2** (float RGB fields and swatches). **Gradient blend** — combo: **Horizontal**, **Vertical**, **Diagonal 1**, **Diagonal 2**, **Corner 1** … **Corner 4**. **Element hover color** — highlight for rows hovered in the **Shape List** or **Sketch List** (**Dimensions** table); stored as **`gui.elm_list_hover_color`**.

Expand Down Expand Up @@ -136,7 +136,11 @@ String **`"1"`**. If the version is missing or does not match, the app replaces

### `imgui_ini`

String: ImGui `.ini` text for window positions and docking saved with **SaveIniSettingsToMemory**. Loaded with **LoadIniSettingsFromMemory** on startup.
String: ImGui `.ini` text for window layout and docking saved with **SaveIniSettingsToMemory**. Loaded with **LoadIniSettingsFromMemory** on startup. Includes dock-node data when panels have been docked or tabbed.

Panels (Sketch List, Shape List, Options, Log, consoles, and similar) can be dragged to dock edges, tabbed together, or split. On desktop, panels can also be dragged outside the main window into separate OS windows. The web build supports the same in-canvas docking inside the browser canvas only. The 3D view stays in the central passthrough region of the main window; it is not moved into dock nodes.

If saved layout text has no `[Docking]` section (older installs), a default dock layout is applied once on next launch.

### `occt_view`

Expand Down Expand Up @@ -182,16 +186,32 @@ String: ImGui `.ini` text for window positions and docking saved with **SaveIniS
| `snap_guide_mode` | integer | **0** *Traditional* (local markers), **1** *Fullscreen* (view-spanning axis lines), **2** *Both* (default **2**). |
| `snap_guide_line_width` | number | Open CASCADE line width for snap guides (axis lines, markers, co-axial overlay; **0.5** to **8.0**; default **1.0**). |
| `annotate_all_coaxial_nodes` | boolean | When true (default), show axis guides and markers for *all* co-axial nodes (current sketch plus other visible sketches). When false, only the closest node per active axis is annotated. Also in sketch **Options**. |
| `imgui_rounding_general` | number | Window/child/frame/popup rounding (**0** to **32** clamped in code; sliders stop at 16 in the UI). |
| `imgui_rounding_scroll` | number | Scrollbar and grab rounding (same clamp). |
| `imgui_rounding_tabs` | number | Tab rounding (same clamp). |
| `imgui_style_dark` | object | ImGui layout for **dark mode** (see keys below). |
| `imgui_style_light` | object | ImGui layout for **light mode** (same keys). |
| `imgui_rounding_general` | number | **Legacy:** copied into both theme objects on load when `imgui_style_*` is absent. |
| `imgui_rounding_scroll` | number | **Legacy:** same. |
| `imgui_rounding_tabs` | number | **Legacy:** same. |
| `underlay_highlight_color` | array of 3 numbers | Default underlay tint (float RGB **0** to **1** per channel; default **0.64**, **0.56**, **0.31**). |
| `elm_list_hover_color` | array of 4 numbers | RGBA highlight for rows hovered in the **Shape List** or **Sketch List** dimensions table (float **0** to **1** per channel; default purple **0.40**, **0.10**, **0.47**, **1**). |
| `view_roll_step_deg` | number | Degrees per **NumPad 8**/**2**/**4**/**6** orbit and **Shift+NumPad 4**/**6** roll (allowed range **0.1** to **180** in code; default **45**). |
| `view_zoom_scroll_scale` | number | Multiplier for `UpdateZoom` scroll delta from wheel and keyboard zoom (allowed range **0.25** to **64** in code; default **4**). With **Shift** held, the effective step is multiplied by **0.1** (Blender-style finer zoom). |
| `load_last_opened_on_startup` | boolean | Desktop: open the last `.ezy` on launch. **Legacy:** `load_last_saved_on_startup` is read as a fallback if the newer key is absent. |
| `last_opened_project_path` | string | Path of the last opened project for the option above. **Legacy:** `last_saved_project_path` is accepted if the newer key is missing. |

Each **`imgui_style_dark`** / **`imgui_style_light`** object may contain:

| Key | Type | Meaning |
| --- | --- | --- |
| `rounding_general` | number | Window/child/frame/popup rounding (**0** to **32** in JSON; Settings sliders **0** to **16**). |
| `rounding_scroll` | number | Scrollbar and grab rounding. |
| `rounding_tabs` | number | Tab rounding. |
| `window_alpha` | number | Panel window background opacity (**0.1** to **1.0**). |
| `window_border` | number | Window border thickness (**0** to **2**). |
| `frame_border` | number | Widget frame border thickness (**0** to **2**). |
| `window_padding_x`, `window_padding_y` | number | Inner padding of windows (**0** to **24**). |
| `frame_padding_x`, `frame_padding_y` | number | Padding inside framed widgets (**0** to **24**). |
| `item_spacing_x`, `item_spacing_y` | number | Spacing between widgets (**0** to **24**). |

Scripting API **`ezy.occt_view_settings_json()`** returns a JSON string with **`occt_view`** plus selected **`gui`** keys (including dimension and snap keys above, **`gui.permanent_node_anno_scale`**, **`gui.inspection_orthographic`**, **`gui.view_roll_step_deg`**, **`gui.view_zoom_scroll_scale`** when saved). See [scripting.md](scripting.md).

---
Expand Down
102 changes: 71 additions & 31 deletions res/ezycad_settings.json
Original file line number Diff line number Diff line change
@@ -1,57 +1,97 @@
{
"gui": {
"add_mid_pt_edges": false,
"add_mid_pt_rect_edges": true,
"add_mid_pt_slot_edges": true,
"annotate_all_coaxial_nodes": true,
"dark_mode": true,
"edge_dim_arrow_orientation": 0,
"edge_dim_arrow_size": 2.0399999618530273,
"edge_dim_arrow_style": 0,
"edge_dim_color": [
0.5423728823661804,
0.5423728823661804,
0.21373170614242554
],
"edge_dim_label_h": 10,
"edge_dim_line_width": 2.0,
"imgui_rounding_general": 8.0,
"imgui_rounding_scroll": 11.0,
"imgui_rounding_tabs": 6.0,
"edge_dim_label_h": 3,
"edge_dim_line_width": 0.5,
"edge_dim_text_render_mode": 5,
"edge_dim_text_scale": 1.0,
"elm_list_hover_color": [
0.4020639955997467,
0.1025567501783371,
0.47457629442214966,
1.0
],
"imgui_style_dark": {
"frame_border": 0.0,
"frame_padding_x": 5.0,
"frame_padding_y": 3.0,
"item_spacing_x": 5.0,
"item_spacing_y": 5.0,
"rounding_general": 8.0,
"rounding_scroll": 12.0,
"rounding_tabs": 6.0,
"window_alpha": 0.8199999928474426,
"window_border": 1.0,
"window_padding_x": 10.0,
"window_padding_y": 10.0
},
"imgui_style_light": {
"frame_border": 0.0,
"frame_padding_x": 5.0,
"frame_padding_y": 3.0,
"item_spacing_x": 10.0,
"item_spacing_y": 5.0,
"rounding_general": 8.0,
"rounding_scroll": 11.0,
"rounding_tabs": 6.0,
"window_alpha": 0.5,
"window_border": 1.0,
"window_padding_x": 10.0,
"window_padding_y": 10.0
},
"inspection_orthographic": false,
"last_opened_project_path": "",
"load_last_opened_on_startup": false,
"log_window_visible": true,
"origin_marker_color": [
0.0,
0.75,
1.0
],
"permanent_node_anno_scale": 0.30000001192092896,
"show_dbg": false,
"show_lua_console": true,
"show_options": true,
"show_python_console": true,
"ui_verbosity": 6,
"view_roll_step_deg": 45.0,
"view_zoom_scroll_scale": 4.0,
"show_settings_dialog": true,
"show_settings_dialog": false,
"show_shape_list": true,
"show_sketch_dimensions": true,
"show_sketch_list": true,
"add_mid_pt_edges": false,
"add_mid_pt_rect_edges": true,
"add_mid_pt_slot_edges": false,
"snap_guide_mode": 2,
"annotate_all_coaxial_nodes": true,
"snap_guide_color_node": [
0.8232946395874023,
0.5494110584259033,
0.9533898234367371
],
"snap_guide_color_axis": [
0.9576271176338196,
0.06492385268211365,
0.5415366888046265
],
"snap_guide_color_node": [
0.8232946395874023,
0.5494110584259033,
0.9533898234367371
],
"snap_guide_line_width": 1.0,
"snap_guide_mode": 2,
"ui_verbosity": 6,
"underlay_highlight_color": [
0.639830470085144,
0.5619884133338928,
0.31178179383277893
],
"elm_list_hover_color": [
0.4020639955997467,
0.1025567501783371,
0.47457629442214966,
0.31178179383277893,
1.0
]
],
"view_roll_step_deg": 45.0,
"view_zoom_scroll_scale": 4.0
},
"imgui_ini": "[Window][##MainMenuBar]\nPos=0,0\nSize=1920,22\n\n[Window][Toolbar]\nPos=7,24\nSize=1310,58\n\n[Window][Sketch List]\nPos=8,86\nSize=321,225\n\n[Window][Shape List]\nPos=8,332\nSize=220,360\n\n[Window][Options]\nPos=1322,26\nSize=389,277\n\n[Window][Lua Console]\nPos=5,855\nSize=1291,313\nCollapsed=1\n\n[Window][Python Console]\nPos=6,831\nSize=1290,225\nCollapsed=1\n\n[Window][Log]\nPos=3,975\nSize=1280,180\nCollapsed=1\n\n[Window][Settings]\nPos=796,86\nSize=520,560\nCollapsed=1\n\n[Window][Debug##Default]\nPos=60,60\nSize=400,400\n\n[Window][dbg]\nPos=260,420\nSize=320,200\n\n[Window][Sketch properties]\nPos=330,86\nSize=400,116\n\n",
"imgui_ini": "[Window][##MainMenuBar]\nPos=0,0\nSize=1920,22\nCollapsed=0\n\n[Window][Toolbar]\nPos=252,21\nSize=1190,58\nCollapsed=0\n\n[Window][Sketch List]\nPos=0,22\nSize=252,443\nCollapsed=0\nDockId=0x00000005,0\n\n[Window][Shape List]\nPos=0,467\nSize=252,551\nCollapsed=0\nDockId=0x00000006,0\n\n[Window][Options]\nPos=1443,34\nSize=345,431\nCollapsed=0\n\n[Window][Lua Console]\nPos=0,1020\nSize=1920,101\nCollapsed=0\nDockId=0x0000000E,0\n\n[Window][Python Console]\nPos=0,1020\nSize=1920,101\nCollapsed=0\nDockId=0x0000000E,1\n\n[Window][Log]\nPos=0,1020\nSize=1920,101\nCollapsed=0\nDockId=0x0000000E,2\n\n[Window][Settings]\nPos=603,99\nSize=603,701\nCollapsed=0\n\n[Window][Debug##Default]\nPos=60,60\nSize=400,400\nCollapsed=0\n\n[Window][dbg]\nPos=344,110\nSize=320,200\nCollapsed=0\n\n[Window][Sketch properties]\nPos=1060,381\nSize=701,477\nCollapsed=0\n\n[Window][About]\nPos=700,300\nSize=520,520\nCollapsed=0\n\n[Window][New sketch]\nPos=801,473\nSize=317,175\nCollapsed=0\n\n[Window][WindowOverViewport_11111111]\nPos=0,22\nSize=1920,1099\nCollapsed=0\n\n[Docking][Data]\nDockSpace ID=0xD79204D4 Window=0x1BBC0F80 Pos=0,51 Size=1920,1099 Split=Y\n DockNode ID=0x0000000D Parent=0xD79204D4 SizeRef=1920,996 Split=X\n DockNode ID=0x00000007 Parent=0x0000000D SizeRef=252,1099 Split=Y Selected=0xCA8EA105\n DockNode ID=0x00000005 Parent=0x00000007 SizeRef=167,443 Selected=0xCA8EA105\n DockNode ID=0x00000006 Parent=0x00000007 SizeRef=167,551 Selected=0x3BE507E9\n DockNode ID=0x00000008 Parent=0x0000000D SizeRef=1666,1099 Split=X\n DockNode ID=0x00000001 Parent=0x00000008 SizeRef=168,1121\n DockNode ID=0x00000002 Parent=0x00000008 SizeRef=1750,1121 CentralNode=1 Selected=0x0C01D6D5\n DockNode ID=0x0000000E Parent=0xD79204D4 SizeRef=1920,101 Selected=0x139FDA3F\n\n",
"occt_view": {
"bg_color1": [
0.037551622837781906,
Expand All @@ -74,10 +114,10 @@
0.11791712790727615,
0.13513511419296265
],
"grid_padding": 1000,
"grid_graphic_z_offset": 0,
"grid_step": 10,
"grid_visible": true
"grid_graphic_z_offset": 0.0,
"grid_padding": 1000.0,
"grid_step": 10.0,
"grid_visible": false
},
"version": "1"
}
40 changes: 39 additions & 1 deletion src/doc/gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Maintainers: update this file when GUI input routing, mode/options behavior, set

Typical responsibilities:

- ImGui frame: menu bar, toolbar, Sketch List, Shape List, Options, Settings, dist/angle popups.
- ImGui frame: menu bar, dock space (passthrough central node for 3D input), toolbar, Sketch List, Shape List, Options, Settings, dist/angle popups.
- Mode switching (`Mode` enum in [`mode.h`](../mode.h)) and parent-mode Esc behavior.
- Persisted preferences (`ezycad_settings.json` via [`gui_settings.cpp`](../gui_settings.cpp)).
- Project I/O (`.ezy` load/save, import/export dialogs).
Expand Down Expand Up @@ -76,6 +76,44 @@ GUI (gui.h / gui.cpp)

CMake IDE group: `src\gui` (files matching `gui*` or `occt*` prefix).

## ImGui docking and viewports

Dear ImGui **docking branch** (`third_party/imgui`, tag `v1.92.7-docking`) is vendored with `IMGUI_HAS_DOCK`.

| Platform | Flags | Behavior |
| --- | --- | --- |
| Native | `DockingEnable`, `ViewportsEnable` | In-canvas dock/tab/split; panels may detach to OS windows |
| WASM | `DockingEnable` | In-canvas dock/tab/split only (no multi-viewport OS windows) |

### WASM HiDPI / canvas sizing

Shared `#canvas` is used by ImGui and OCCT. Model follows the physical-pixel approach from
[imgui#7519](https://github.com/ocornut/imgui/issues/7519#issuecomment-2629628233):

| Piece | Role |
| --- | --- |
| HTML CSS (`width`/`height: 100%`) | On-screen (CSS) size of the canvas |
| `ImGui_ImplGlfw_OnCanvasSizeChange` (vendored patch) | Sets `canvas.width/height` and GLFW window to `CSS * devicePixelRatio` |
| ImGui `main_scale` | `devicePixelRatio` via `ScaleAllSizes` / `FontScaleDpi` so widgets keep CSS-logical on-screen size |
| `io.DisplayFramebufferScale` | Stays `1` (window size == framebuffer size in physical px) - fonts/icons are not upscaled |
| OCCT `Wasm_Window("#canvas", false)` + `SetDevicePixelRatio(1)` | Shares the ImGui-sized backing store; mouse/view coords match GLFW |

Do **not** set `GLFW_SCALE_TO_MONITOR` on wasm: Emscripten then forces canvas CSS size to the GLFW window size (`!important`), which fights the 100% CSS layout.

Startup: dispatch a couple of `resize` events so the CSS*DPR sync runs after the browser CSS size settles.

Initialization in [`main.cpp`](../main.cpp): config flags, native-only `UpdatePlatformWindows` / `RenderPlatformWindowsDefault` after the main draw pass.

With `ViewportsEnable`, ImGui `MousePos` is in screen coordinates; OCCT picking uses GLFW client-area coordinates via `GUI::cursor_screen_coords()` (see `on_mouse_button` and the `main` cursor callback). Do not pass ImGui `MousePos` to `Occt_view` on native builds.

In [`main.cpp`](../main.cpp), GLFW **mouse-move** callbacks always forward to `GUI` (sketch rubber-band and OCCT hover must not stop when a float edit or docked panel is hovered). **Mouse-button** and **scroll** callbacks forward only when the cursor is in the dock central passthrough region and no ImGui window is hovered (so toolbar clicks do not clear OCCT selection).

Each frame, [`gui.cpp`](../gui.cpp) `dock_space_()` sets the OCCT passthrough rectangle via `DockSpaceOverViewport` with `ImGuiDockNodeFlags_PassthruCentralNode`, then reads the central node bounds. `dock_space_()` calls `SetNextFrameWantCaptureMouse(false)` when the cursor is over the passthrough region.

Default dock layout (left: Shape/Sketch lists tabbed, right: Options, bottom: Log) is seeded once via `DockBuilder*` when loaded `imgui_ini` has no `[Docking]` section (`m_seed_default_dock_layout` in `gui_settings.cpp`). The Toolbar uses `ImGuiWindowFlags_NoDocking` so it cannot occupy the central passthrough node.

Overlay popups (`FloatEdit`, `AngleEdit`, `MessageStatus`, modals) keep `NoSavedSettings` and do not participate in docking.

## Input routing (GLFW -> `GUI` -> downstream)

`main` forwards GLFW callbacks to `GUI`. The view receives mouse events first in some paths (`on_mouse_button` calls `m_view->on_mouse_button` before `on_left_click_`).
Expand Down
Loading
Loading