From 55d3e4ec096b5c75600a00cd6229e34be808841b Mon Sep 17 00:00:00 2001 From: luboslenco Date: Wed, 1 Jul 2026 11:53:20 +0200 Subject: [PATCH] paint: ui fixes --- paint/sources/physics_sim.c | 19 +++-- paint/sources/render/render_gizmo.c | 4 - paint/sources/types.h | 1 - paint/sources/ui/tab_materials.c | 2 + paint/sources/ui/tab_meshes.c | 126 ++++++++++++++++++++++++++-- paint/sources/ui/tab_scripts.c | 1 - 6 files changed, 134 insertions(+), 19 deletions(-) diff --git a/paint/sources/physics_sim.c b/paint/sources/physics_sim.c index bef2575e..77d9adbe 100644 --- a/paint/sources/physics_sim.c +++ b/paint/sources/physics_sim.c @@ -102,16 +102,23 @@ void sim_remove_body(physics_body_t *pb) { } void sim_duplicate() { - if (g_context->selected_object == NULL) { + // Mesh + mesh_object_t *so = g_context->paint_object; + if (so == NULL) { return; } - - // Mesh - mesh_object_t *so = g_context->selected_object->ext; mesh_object_t *dup = scene_add_mesh_object(so->data, so->material, so->base->parent); transform_set_matrix(dup->base->transform, so->base->transform->local); any_array_push(g_project->_->paint_objects, dup); - dup->base->name = so->base->name; + + // Ensure unique name + char *oname = so->base->name; + char *ext = ""; + i32 i = 0; + while (!_import_mesh_is_unique_name(string("%s%s", oname, ext))) { + ext = string_copy(_import_mesh_number_ext(++i)); + } + dup->base->name = string("%s%s", oname, ext); // Physics physics_body_t *pb = any_imap_get(physics_body_object_map, so->base->uid); @@ -124,7 +131,7 @@ void sim_duplicate() { } void sim_delete() { - mesh_object_t *so = g_context->selected_object->ext; + mesh_object_t *so = g_context->paint_object; array_remove(g_project->_->paint_objects, so); mesh_object_remove(so); physics_body_t *pb = any_imap_get(physics_body_object_map, so->base->uid); diff --git a/paint/sources/render/render_gizmo.c b/paint/sources/render/render_gizmo.c index f89d8725..a1b0e366 100644 --- a/paint/sources/render/render_gizmo.c +++ b/paint/sources/render/render_gizmo.c @@ -19,10 +19,6 @@ void render_gizmo_update() { object_t *paint_object = g_context->paint_object->base; - if (g_context->tool == TOOL_TYPE_CURSOR && g_context->selected_object != NULL) { - paint_object = g_context->selected_object; - } - if (is_object) { gizmo->transform->loc = (vec4_t){transform_world_x(paint_object->transform), transform_world_y(paint_object->transform), transform_world_z(paint_object->transform), 1.0}; diff --git a/paint/sources/types.h b/paint/sources/types.h index e41827a2..46caa7b3 100644 --- a/paint/sources/types.h +++ b/paint/sources/types.h @@ -384,7 +384,6 @@ typedef struct context { f32 lock_start_x; f32 lock_start_y; bool registered; - struct object *selected_object; f32 particle_hit_x; f32 particle_hit_y; f32 particle_hit_z; diff --git a/paint/sources/ui/tab_materials.c b/paint/sources/ui/tab_materials.c index 7016b9dd..19acb009 100644 --- a/paint/sources/ui/tab_materials.c +++ b/paint/sources/ui/tab_materials.c @@ -67,6 +67,7 @@ void tab_materials_delete_material(slot_material_t *m) { history_delete_material(); context_select_material(i == g_project->_->materials->length - 1 ? i - 1 : i + 1); array_splice(g_project->_->materials, i, 1); + tab_meshes_on_material_deleted(i); ui_base_hwnds->buffer[1]->redraws = 2; for (i32 i = 0; i < g_project->_->materials->length; ++i) { slot_material_t *m = g_project->_->materials->buffer[i]; @@ -426,6 +427,7 @@ void tab_materials_accept_material_drop(slot_material_t *material) { array_remove(g_project->_->materials, material); i32 new_pos = tab_materials_drag_pos - mat_pos > 0 ? tab_materials_drag_pos - 1 : tab_materials_drag_pos; array_insert(g_project->_->materials, new_pos, material); + tab_meshes_on_material_reordered(mat_pos, new_pos); } } diff --git a/paint/sources/ui/tab_meshes.c b/paint/sources/ui/tab_meshes.c index 20e3c2f5..7c258236 100644 --- a/paint/sources/ui/tab_meshes.c +++ b/paint/sources/ui/tab_meshes.c @@ -8,6 +8,29 @@ i32 tab_meshes_mesh_name_edit = -1; any_map_t *tab_meshes_preview_map = NULL; any_map_t *tab_meshes_override_map = NULL; // object uid -> overridden material index +void tab_meshes_set_drag_mesh(mesh_object_t *o, f32 off_x, f32 off_y) { + base_drag_off_x = off_x; + base_drag_off_y = off_y; + gc_unroot(base_drag_mesh); + base_drag_mesh = o; + gc_root(base_drag_mesh); + g_context->drag_dest = array_index_of(g_project->_->paint_objects, o); +} + +void tab_meshes_accept_mesh_drop(mesh_object_t *mesh) { + i32 pos = array_index_of(g_project->_->paint_objects, mesh); + if (pos == -1) { + return; + } + i32 dest = g_context->drag_dest; + if (dest == pos || dest == pos + 1) { + return; + } + array_remove(g_project->_->paint_objects, mesh); + i32 new_pos = dest > pos ? dest - 1 : dest; + array_insert(g_project->_->paint_objects, new_pos, mesh); +} + void tab_meshes_set_override(mesh_object_t *o, i32 mat_index) { // Render an object with a chosen material instead of the painted layers if (tab_meshes_override_map == NULL) { @@ -34,6 +57,64 @@ i32 tab_meshes_get_override(mesh_object_t *o) { return v != NULL ? parse_int(v) : -1; } +void tab_meshes_on_material_deleted(i32 deleted_index) { + if (tab_meshes_override_map == NULL) { + return; + } + bool changed = false; + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + mesh_object_t *o = g_project->_->paint_objects->buffer[i]; + i32 idx = tab_meshes_get_override(o); + if (idx < 0) { + continue; + } + if (idx == deleted_index) { + tab_meshes_set_override(o, -1); // Reset + changed = true; + } + else if (idx > deleted_index) { + tab_meshes_set_override(o, idx - 1); // Offset by deleted material + changed = true; + } + } + if (changed) { + g_project->mesh_materials = i32_array_create(0); + g_context->ddirty = 2; + } +} + +void tab_meshes_on_material_reordered(i32 old_index, i32 new_index) { + if (tab_meshes_override_map == NULL || old_index == new_index) { + return; + } + bool changed = false; + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + mesh_object_t *o = g_project->_->paint_objects->buffer[i]; + i32 idx = tab_meshes_get_override(o); + if (idx < 0) { + continue; + } + i32 new_idx = idx; + if (idx == old_index) { + new_idx = new_index; + } + else if (old_index < new_index && idx > old_index && idx <= new_index) { + new_idx = idx - 1; + } + else if (old_index > new_index && idx >= new_index && idx < old_index) { + new_idx = idx + 1; + } + if (new_idx != idx) { + tab_meshes_set_override(o, new_idx); + changed = true; + } + } + if (changed) { + g_project->mesh_materials = i32_array_create(0); + g_context->ddirty = 2; + } +} + void tab_meshes_draw_context_menu_delete_next_frame(mesh_object_t *o) { data_delete_mesh(o->data->_->handle); mesh_object_remove(o); @@ -64,6 +145,10 @@ static char *f32_to_string2(float f) { return f32_to_string((int)(f * 100) / 100.0); } +void tab_meshes_duplicate_next_frame(void *_) { + sim_duplicate(); +} + void tab_meshes_draw_context_menu() { i32 i = _tab_meshes_draw_i; mesh_object_t *o = g_project->_->paint_objects->buffer[i]; @@ -85,10 +170,9 @@ void tab_meshes_draw_context_menu() { } #endif - g_context->selected_object = o->base; - ui_handle_t *h = ui_handle(__ID__); + ui_handle_t *h = ui_handle(__ID__); - transform_t *t = g_context->selected_object->transform; + transform_t *t = o->base->transform; vec4_t rot = quat_get_euler(t->rot); rot = vec4_mult(rot, 180 / 3.141592); f32 f = 0.0; @@ -422,10 +506,18 @@ void tab_meshes_append_shape(char *mesh_name) { mesh_data_t *md = mesh_data_create(raw); md->_->handle = md->name; mesh_object_t *mo = scene_add_mesh_object(md, g_project->_->paint_objects->buffer[0]->material, NULL); - mo->base->name = md->name; - obj_t *o = GC_ALLOC_INIT(obj_t, {0}); - o->_ = GC_ALLOC_INIT(obj_runtime_t, {._gc = scene_raw}); - mo->base->raw = o; + + // Ensure unique name + char *ext = ""; + i32 n = 0; + while (!_import_mesh_is_unique_name(string("%s%s", md->name, ext))) { + ext = string_copy(_import_mesh_number_ext(++n)); + } + mo->base->name = string("%s%s", md->name, ext); + + obj_t *o = GC_ALLOC_INIT(obj_t, {0}); + o->_ = GC_ALLOC_INIT(obj_runtime_t, {._gc = scene_raw}); + mo->base->raw = o; any_map_set(data_cached_meshes, md->_->handle, md); any_array_push(g_project->_->paint_objects, mo); } @@ -646,6 +738,21 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { f32 uix = g_ui->_x; f32 uiy = g_ui->_y; + // Highlight drag destination + if (base_is_dragging && base_drag_mesh != NULL && context_in_meshes()) { + f32 absy = g_ui->_window_y + g_ui->_y; + f32 checkw = (g_ui->_window_w / 100.0 * 8) / (float)UI_SCALE(); + f32 w = (g_ui->_window_w / (float)UI_SCALE() - 2) - checkw; + if (mouse_y > absy + step && mouse_y < absy + step * 3) { + g_context->drag_dest = i + 1; // Insert after this slot + ui_fill(checkw, step * 2, w, 2 * UI_SCALE(), g_theme->HIGHLIGHT_COL); + } + else if (i == 0 && mouse_y < absy + step) { + g_context->drag_dest = 0; // Insert before the first slot + ui_fill(checkw, 0, w, 2 * UI_SCALE(), g_theme->HIGHLIGHT_COL); + } + } + // Eye icon f32_array_t *row = f32_array_create_from_raw( (f32[]){ @@ -732,6 +839,7 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { ui_tooltip(o->base->name); if (g_ui->input_started) { g_context->paint_object = o; + tab_meshes_set_drag_mesh(o, -(mouse_x - uix - g_ui->_window_x - 3), -(mouse_y - uiy - g_ui->_window_y + 1)); } if (g_ui->input_released) { if (sys_time() - g_context->select_time < 0.2) { @@ -763,6 +871,10 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { g_ui->is_delete_down = false; sys_notify_on_next_frame(&tab_meshes_draw_context_menu_delete, g_context->paint_object); } + if (in_focus && g_ui->is_ctrl_down && g_ui->is_key_pressed && g_ui->key_code == KEY_CODE_D) { + g_ui->is_key_pressed = false; + sys_notify_on_next_frame(&tab_meshes_duplicate_next_frame, NULL); + } } g_ui->_x = uix; diff --git a/paint/sources/ui/tab_scripts.c b/paint/sources/ui/tab_scripts.c index d5bf2495..2b050456 100644 --- a/paint/sources/ui/tab_scripts.c +++ b/paint/sources/ui/tab_scripts.c @@ -463,7 +463,6 @@ void tab_scripts_draw(ui_handle_t *htab) { } } - if (ac_selected && g_ui->is_key_pressed) { tab_scripts_minimap_dirty = true; }