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
6 changes: 3 additions & 3 deletions docs/ezycad_code_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Use this style when editing or adding C/C++ code in the EzyCad project (files un
- Prefer clear domain prefixes for related member groups (e.g. `m_underlay_*`) instead of mixed short forms.
- **Constants** (e.g. lookup arrays for enums): `c_` prefix (e.g. `c_mode_strs`, `c_chamfer_mode_strs`).
- **Functions / methods**: snake_case (e.g. `add_new_node`, `get_node_exact`, `try_get_node_idx_snap`).
- **Private methods**: snake_case with trailing underscore (e.g. `update_axis_snap_anno_`).
- **Private methods** and **file-local helpers** (anonymous-namespace free functions in a `.cpp`): snake_case with trailing underscore (e.g. `update_axis_snap_anno_`, `table_row_input_double_`).
- **Type aliases**: snake_case with suffix by role, e.g. `*_ptr` for handles (`AIS_Shape_ptr`, `Shp_ptr`), `*_rslt` for result types (`Shp_rslt`). Typedefs like `ScreenCoords` are PascalCase.
- **Macros**: UPPER_SNAKE_CASE (e.g. `EZY_ASSERT`, `EZY_ASSERT_MSG`, `DBG_MSG`).

Expand Down Expand Up @@ -139,7 +139,7 @@ User-facing Markdown (now in `docs/`: `usage.md`, `usage-*.md`, `scripting.md`,
## Code organization

- **Reader-first order** (`.cpp`): Put **public API and high-level workflow** at the top of the file (constructors, main entry points, orchestration). Put **lower-level details** below so the first screen shows what the module does before how it does it.
- **Helper functions** (`.cpp`): Prefer **file-local static helpers** in an anonymous namespace at the **bottom** of the implementing `.cpp` file. Forward-declare them near the top (or above their first use) when needed. Avoid large helper blocks at the top of the file; the goal is high-level code first, helpers last for readability.
- **Helper functions** (`.cpp`): Prefer **file-local helpers** in an anonymous namespace at the **bottom** of the implementing `.cpp` file (trailing `_` name; see **Naming**). Forward-declare them near the top (or above their first use) when needed. Avoid large helper blocks at the top of the file; the goal is high-level code first, helpers last for readability.
- **PIMPL** (`class Foo; class Foo::Impl`): Use when hiding implementation details or when you want to swap implementations (e.g. `Sketch_nodes`, `Sketch_op_recorder`). Keep the public surface in the header; put data members, private record types, and apply/clone logic in `Impl` inside the `.cpp`.
- **Templates**: Prefer putting template implementations in `.inl` files included from the header (e.g. `utl_types.inl`, `utl.inl`).
- **OCCT handles**: Prefer `opencascade::handle<T>` and project `*_ptr` aliases from `utl_types.h` (e.g. `AIS_Shape_ptr`, `Shp_ptr`). Avoid the OCCT `Handle(T)` macro in new/touched code -- clang-format mishandles it with `PointerAlignment: Left` (e.g. `Handle(Foo) &`). See [agents/conventions/occt-handles.md](../agents/conventions/occt-handles.md).
Expand All @@ -155,7 +155,7 @@ User-facing Markdown (now in `docs/`: `usage.md`, `usage-*.md`, `scripting.md`,
- Too much DRY can increase coupling by forcing unrelated code through one shared abstraction.
- Prefer readability over clever reuse when repetition is small and explicit code is clearer.
- **When duplication is acceptable**: a few lines repeated across nearby UI or glue code can beat a shared helper if call sites are likely to diverge (different tooltips, widths, or disabled logic) or if extracting would scatter one screen across many symbols. Prefer **locality**: keep a small block self-contained so a reader does not jump to understand one pane.
- **Rule of thumb**: extract when the behavior is **the same and stable** (or when a bug fix must touch N copies); wait when the pattern is still moving. If you extract, prefer a **file-local static helper at the bottom of the `.cpp`** (see **Code organization**) over a generic framework.
- **Rule of thumb**: extract when the behavior is **the same and stable** (or when a bug fix must touch N copies); wait when the pattern is still moving. If you extract, prefer a **file-local helper at the bottom of the `.cpp`** (see **Code organization**) over a generic framework.
- Context matters: stronger DRY is often good in monolith/shared-library code; some duplication can be healthier in fast-changing or separated systems.
- Balance DRY with KISS, YAGNI, and overall cognitive load.

Expand Down
2 changes: 1 addition & 1 deletion src/doc/utility.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ CMake IDE group: `src\utl` (pattern `^utl(_|\.)`).

| API | Purpose |
| ---------------------------------------- | ------------------------------------------------ |
| `clear_all(...)` | Reset optional/containers/arithmetic in one call |
| `clear_all(...)` | Reset optional/containers/arithmetic/handles (`Nullify`)/enums/aggregates in one call |
| `unique_sequential_name(base, existing)` | `Name`, `Name.001`, ... for sketches/shapes |
| `load_texture(path)` | Toolbar icon loading |
| `decode_image_bytes(bytes)` | stb_image -> RGBA for underlay import |
Expand Down
69 changes: 34 additions & 35 deletions src/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2610,7 +2610,7 @@ void GUI::shape_list_()
// pair so table rows cannot leave the ImGui tree stack unbalanced (which nested siblings
// under the wrong parent and made Ungroup look like it only moved one child).
std::unordered_set<Shape_id> shape_list_ancestors;
auto draw_shape_row = [&](auto&& self, const Shp_ptr& shape) -> void
auto draw_shape_row = [&](auto&& self, const Shp_ptr& shape) -> void
{
EZY_ASSERT(shape);
if (!shape_list_ancestors.insert(shape->get_id()).second)
Expand Down Expand Up @@ -2972,9 +2972,7 @@ void GUI::shape_info_dialog_()

if (!shape_still_exists)
{
m_shape_info_open = false;
m_shape_info_shp.Nullify();
m_shape_info_lines.clear();
clear_all(m_shape_info_open, m_shape_info_shp, m_shape_info_lines);
return;
}

Expand Down Expand Up @@ -3088,9 +3086,7 @@ void GUI::begin_step_import_(const Step_import_mode mode)
void GUI::finish_step_import_(Status st, Occt_view::Step_import_geom& geom)
{
const bool cancelled = !m_cad_busy_progress.IsNull() && m_cad_busy_progress->cancelled();
m_cad_busy_kind = Cad_busy_kind::Idle;
m_cad_busy_progress = {};
m_cad_busy_modal_open = false;
clear_all(m_cad_busy_kind, m_cad_busy_progress, m_cad_busy_modal_open);

if (cancelled || (!st.is_ok() && st.message().find("cancelled") != std::string::npos))
{
Expand Down Expand Up @@ -3139,8 +3135,8 @@ void GUI::poll_cad_busy_()
}

Occt_view::Step_import_geom geom;
Status st = Occt_view::prepare_step_import(m_cad_busy_bytes, m_cad_busy_import_mode, m_view->step_import_model_scale(),
geom, {});
Status st =
Occt_view::prepare_step_import(m_cad_busy_bytes, m_cad_busy_import_mode, m_view->step_import_model_scale(), geom, {});
finish_step_import_(st, geom);
#else
if (!m_cad_busy_import_fut.valid())
Expand Down Expand Up @@ -3178,8 +3174,7 @@ void GUI::cad_busy_dialog_()
}

ImGui::SetNextWindowSize(ImVec2(280.0f, 0.0f), ImGuiCond_Appearing);
if (!ImGui::BeginPopupModal("##EzyCadCadBusy", nullptr,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
if (!ImGui::BeginPopupModal("##EzyCadCadBusy", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
return;

ImGui::TextUnformatted("Importing...");
Expand Down Expand Up @@ -3432,44 +3427,24 @@ void GUI::message_status_window_()
// Log window implementation
namespace
{

std::string format_log_line(const std::string& base, size_t repeat_count)
{
if (repeat_count <= 1)
return base;
return base + " #" + std::to_string(repeat_count);
}

void append_log_line(std::vector<char>& buffer, const std::string& line)
{
if (buffer.size() > 1)
{
buffer.pop_back(); // trailing '\0'
buffer.push_back('\n');
}
else if (!buffer.empty())
buffer.pop_back();

buffer.insert(buffer.end(), line.begin(), line.end());
buffer.push_back('\0');
}

std::string format_log_line_(const std::string& base, size_t repeat_count);
void append_log_line_(std::vector<char>& buffer, const std::string& line);
} // namespace

void GUI::log_message(const std::string& message)
{
if (!m_log_last_line_base.empty() && message == m_log_last_line_base)
{
++m_log_repeat_count;
const std::string line = format_log_line(m_log_last_line_base, m_log_repeat_count);
const std::string line = format_log_line_(m_log_last_line_base, m_log_repeat_count);
m_log_buffer.resize(m_log_last_line_start);
m_log_buffer.insert(m_log_buffer.end(), line.begin(), line.end());
m_log_buffer.push_back('\0');
m_log_scroll_to_bottom = true;
return;
}

append_log_line(m_log_buffer, message);
append_log_line_(m_log_buffer, message);
m_log_last_line_base = message;
m_log_last_line_start = m_log_buffer.size() - message.size() - 1;
m_log_repeat_count = 1;
Expand Down Expand Up @@ -4484,6 +4459,30 @@ void GUI::on_inspector_file(const std::string& file_path, const std::string& fil
open_file_inspector_(file_path, file_data);
}

namespace
{
std::string format_log_line_(const std::string& base, size_t repeat_count)
{
if (repeat_count <= 1)
return base;
return base + " #" + std::to_string(repeat_count);
}

void append_log_line_(std::vector<char>& buffer, const std::string& line)
{
if (buffer.size() > 1)
{
buffer.pop_back(); // trailing '\0'
buffer.push_back('\n');
}
else if (!buffer.empty())
buffer.pop_back();

buffer.insert(buffer.end(), line.begin(), line.end());
buffer.push_back('\0');
}
} // namespace

#ifdef __EMSCRIPTEN__
void GUI::open_file_dialog_async()
{
Expand Down
28 changes: 14 additions & 14 deletions src/gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,20 +705,20 @@ class GUI
float m_sketch_list_scroll_y{0.f};
bool m_sketch_list_scroll_restore{false};

bool m_show_sketch_list{true};
bool m_show_shape_list{true};
bool m_show_options{true};
bool m_show_settings_dialog{false};
bool m_open_about_popup{false};
bool m_about_popup_open{false};
bool m_shape_info_open{false};
Shp_ptr m_shape_info_shp;
std::vector<shp_info::Line> m_shape_info_lines;
bool m_file_inspector_open{false};
Step_import_mode m_file_inspector_step_mode{Step_import_mode::Preserve_hierarchy};
std::string m_file_inspector_path;
std::string m_file_inspector_bytes;
utl_cad_file_info::Format m_file_inspector_fmt{utl_cad_file_info::Format::Unknown};
bool m_show_sketch_list{true};
bool m_show_shape_list{true};
bool m_show_options{true};
bool m_show_settings_dialog{false};
bool m_open_about_popup{false};
bool m_about_popup_open{false};
bool m_shape_info_open{false};
Shp_ptr m_shape_info_shp;
std::vector<shp_info::Line> m_shape_info_lines;
bool m_file_inspector_open{false};
Step_import_mode m_file_inspector_step_mode{Step_import_mode::Preserve_hierarchy};
std::string m_file_inspector_path;
std::string m_file_inspector_bytes;
utl_cad_file_info::Format m_file_inspector_fmt{utl_cad_file_info::Format::Unknown};

enum class Cad_busy_kind : uint8_t
{
Expand Down
Loading
Loading