Files
armorpaint/paint/sources/ui/ui_nodes.c
T
2026-06-14 11:32:00 +02:00

1227 lines
45 KiB
C

#include "../global.h"
bool ui_nodes_show_menu = false;
bool ui_nodes_show_menu_first = true;
bool ui_nodes_hide_menu = false;
i32 ui_nodes_menu_category = 0;
f32 ui_nodes_popup_x = 0.0;
f32 ui_nodes_popup_y = 0.0;
i32 ui_nodes_node_search_x;
i32 ui_nodes_node_search_y;
bool ui_nodes_uichanged_last = false;
bool ui_nodes_recompile_mat = false; // Mat preview
bool ui_nodes_recompile_mat_final = false;
ui_node_t *ui_nodes_node_search_spawn = NULL;
i32 ui_nodes_node_search_offset = 0;
ui_node_canvas_t *ui_nodes_last_canvas = NULL;
i32 ui_nodes_last_node_selected_id = -1;
bool ui_nodes_release_link = false;
bool ui_nodes_is_node_menu_op = false;
gpu_texture_t *ui_nodes_grid = NULL;
bool ui_nodes_controls_down = false;
slot_material_t_array_t *ui_nodes_tabs = NULL;
ui_node_link_t *_ui_nodes_on_link_drag_link_drag;
ui_node_t *_ui_nodes_on_link_drag_node;
ui_node_socket_t *_ui_nodes_on_socket_released_socket;
ui_node_t *_ui_nodes_on_socket_released_node;
ui_node_t *_ui_nodes_on_canvas_released_selected;
bool _ui_nodes_node_search_first;
void (*_ui_nodes_node_search_done)(void);
ui_node_t *ui_nodes_node_changed = NULL;
void (*_ui_nodes_render_tmp)(i32);
void ui_viewnodes_on_link_drag_on_node_search_done() {
ui_nodes_t *ui_nodes = ui_nodes_get_nodes();
i32 node_selected_id = _ui_nodes_on_link_drag_node->id;
if (ui_nodes->nodes_selected_id->length > 0) {
node_selected_id = ui_nodes->nodes_selected_id->buffer[0];
}
ui_node_link_t *link_drag = _ui_nodes_on_link_drag_link_drag;
ui_node_t_array_t *nodes = ui_nodes_get_canvas(true)->nodes;
ui_node_t *n = ui_get_node(nodes, node_selected_id);
if (link_drag->to_id == -1 && n->inputs->length > 0) {
link_drag->to_id = n->id;
char *from_type = _ui_nodes_on_link_drag_node->outputs->buffer[link_drag->from_socket]->type;
// Connect to the first socket
link_drag->to_socket = 0;
// Try to find the first type-matching socket and use it if present
for (i32 i = 0; i < n->inputs->length; ++i) {
ui_node_socket_t *socket = n->inputs->buffer[i];
if (string_equals(socket->type, from_type)) {
link_drag->to_socket = array_index_of(n->inputs, socket);
break;
}
}
any_array_push(ui_nodes_get_canvas(true)->links, link_drag);
}
else if (link_drag->from_id == -1 && n->outputs->length > 0) {
link_drag->from_id = n->id;
link_drag->from_socket = 0;
any_array_push(ui_nodes_get_canvas(true)->links, link_drag);
}
}
void ui_nodes_node_search_menu() {
ui_menu_h = UI_ELEMENT_H() * 8;
ui_handle_t *search_handle = ui_handle(__ID__);
char *search = to_lower_case(ui_text_input(search_handle, "", UI_ALIGN_LEFT, true, true));
g_ui->changed = false;
if (_ui_nodes_node_search_first) {
_ui_nodes_node_search_first = false;
search_handle->text = "";
ui_start_text_edit(search_handle, UI_ALIGN_LEFT); // Focus search bar
}
if (search_handle->changed) {
ui_nodes_node_search_offset = 0;
}
if (g_ui->is_key_pressed) { // Move selection
if (g_ui->key_code == KEY_CODE_DOWN && ui_nodes_node_search_offset < 6) {
ui_nodes_node_search_offset++;
}
if (g_ui->key_code == KEY_CODE_UP && ui_nodes_node_search_offset > 0) {
ui_nodes_node_search_offset--;
}
}
bool enter = keyboard_down("enter");
i32 count = 0;
i32 _BUTTON_COL = g_theme->BUTTON_COL;
bool _FILL_BUTTON_BG = g_theme->FILL_BUTTON_BG;
g_theme->FILL_BUTTON_BG = true;
bool _SHADOWS = g_theme->SHADOWS;
g_theme->SHADOWS = false;
node_list_t_array_t *node_list = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_list : nodes_brush_list;
for (i32 i = 0; i < node_list->length; ++i) {
ui_node_t_array_t *list = node_list->buffer[i];
for (i32 i = 0; i < list->length; ++i) {
ui_node_t *n = list->buffer[i];
if (string_index_of(to_lower_case(tr(n->name)), search) >= 0) {
g_theme->BUTTON_COL = count == ui_nodes_node_search_offset ? g_theme->HIGHLIGHT_COL : g_theme->SEPARATOR_COL;
if (ui_button(tr(n->name), UI_ALIGN_LEFT, "") || (enter && count == ui_nodes_node_search_offset)) {
ui_nodes_push_undo(NULL);
ui_nodes_t *nodes = ui_nodes_get_nodes();
ui_node_canvas_t *canvas = ui_nodes_get_canvas(true);
gc_unroot(ui_nodes_node_search_spawn);
ui_nodes_node_search_spawn = ui_nodes_make_node(n, nodes, canvas); // Spawn selected node
gc_root(ui_nodes_node_search_spawn);
any_array_push(canvas->nodes, ui_nodes_node_search_spawn);
nodes->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
ui_nodes_node_search_spawn->id,
},
1);
nodes->nodes_drag = true;
ui_nodes_hwnd->redraws = 2;
if (enter) {
g_ui->changed = true;
count = 6; // Trigger break
}
if (_ui_nodes_node_search_done != NULL) {
_ui_nodes_node_search_done();
}
}
if (++count > 6) {
break;
}
}
}
if (count > 6) {
break;
}
}
if (enter && count == 0) { // Hide popup on enter when node is not found
g_ui->changed = true;
search_handle->text = "";
}
g_theme->BUTTON_COL = _BUTTON_COL;
g_theme->FILL_BUTTON_BG = _FILL_BUTTON_BG;
g_theme->SHADOWS = _SHADOWS;
}
void ui_nodes_node_search(i32 x, i32 y, void (*done)(void)) {
_ui_nodes_node_search_first = true;
gc_unroot(_ui_nodes_node_search_done);
_ui_nodes_node_search_done = done;
gc_root(_ui_nodes_node_search_done);
ui_menu_draw(&ui_nodes_node_search_menu, x, y);
}
void ui_viewnodes_on_link_drag(i32 link_drag_id, bool is_new_link) {
if (!is_new_link) {
return;
}
ui_node_link_t_array_t *links = ui_nodes_get_canvas(true)->links;
ui_node_link_t *link_drag = ui_get_link(links, link_drag_id);
ui_node_t_array_t *nodes = ui_nodes_get_canvas(true)->nodes;
ui_node_t *node = ui_get_node(nodes, link_drag->from_id > -1 ? link_drag->from_id : link_drag->to_id);
i32 link_x = g_ui->_window_x + UI_NODE_X(node);
i32 link_y = g_ui->_window_y + UI_NODE_Y(node);
if (link_drag->from_id > -1) {
link_x += UI_NODE_W(node);
link_y += UI_OUTPUT_Y(node, link_drag->from_socket);
}
else {
link_y += ui_nodes_INPUT_Y(ui_nodes_get_canvas(true), node, link_drag->to_socket) + UI_OUTPUTS_H(node, -1) + UI_BUTTONS_H(node);
}
if (math_abs(mouse_x - link_x) > 5 || math_abs(mouse_y - link_y) > 5) { // Link length
gc_unroot(_ui_nodes_on_link_drag_link_drag);
_ui_nodes_on_link_drag_link_drag = link_drag;
gc_root(_ui_nodes_on_link_drag_link_drag);
gc_unroot(_ui_nodes_on_link_drag_node);
_ui_nodes_on_link_drag_node = node;
gc_root(_ui_nodes_on_link_drag_node);
ui_nodes_node_search(-1, -1, &ui_viewnodes_on_link_drag_on_node_search_done);
}
// Selecting which node socket to preview
else if (link_drag->from_id > -1) {
i32_imap_set(g_context->node_preview_socket_map, node->id, link_drag->from_socket);
gc_unroot(ui_nodes_node_changed);
ui_nodes_node_changed = node;
gc_root(ui_nodes_node_changed);
}
}
void ui_viewnodes_on_socket_released_group_edit_box() {
ui_node_socket_t *socket = _ui_nodes_on_socket_released_socket;
ui_node_t *node = _ui_nodes_on_socket_released_node;
string_array_t *type_combo = any_array_create_from_raw(
(void *[]){
tr("Color"),
tr("Vector"),
tr("Value"),
},
3);
i32 type = ui_combo(_ui_nodes_htype, type_combo, tr("Type"), true, UI_ALIGN_LEFT, true);
if (_ui_nodes_htype->changed) {
_ui_nodes_hname->text = type == 0 ? tr("Color") : type == 1 ? tr("Vector") : tr("Value");
}
char *name = ui_text_input(_ui_nodes_hname, tr("Name"), UI_ALIGN_LEFT, true, false);
f32 min = ui_float_input(_ui_nodes_hmin, tr("Min"), UI_ALIGN_LEFT, 1000.0);
f32 max = ui_float_input(_ui_nodes_hmax, tr("Max"), UI_ALIGN_LEFT, 1000.0);
f32_array_t *default_value = NULL;
if (type == 0) {
ui_row4();
ui_float_input(_ui_nodes_hval0, tr("R"), UI_ALIGN_LEFT, 1000.0);
ui_float_input(_ui_nodes_hval1, tr("G"), UI_ALIGN_LEFT, 1000.0);
ui_float_input(_ui_nodes_hval2, tr("B"), UI_ALIGN_LEFT, 1000.0);
ui_float_input(_ui_nodes_hval3, tr("A"), UI_ALIGN_LEFT, 1000.0);
default_value = f32_array_create_xyzw(_ui_nodes_hval0->f, _ui_nodes_hval1->f, _ui_nodes_hval2->f, _ui_nodes_hval3->f);
}
else if (type == 1) {
ui_row3();
_ui_nodes_hval0->f = ui_float_input(_ui_nodes_hval0, tr("X"), UI_ALIGN_LEFT, 1000.0);
_ui_nodes_hval1->f = ui_float_input(_ui_nodes_hval1, tr("Y"), UI_ALIGN_LEFT, 1000.0);
_ui_nodes_hval2->f = ui_float_input(_ui_nodes_hval2, tr("Z"), UI_ALIGN_LEFT, 1000.0);
default_value = f32_array_create_xyz(_ui_nodes_hval0->f, _ui_nodes_hval1->f, _ui_nodes_hval2->f);
}
else {
f32 f = ui_float_input(_ui_nodes_hval0, tr("Value"), UI_ALIGN_LEFT, 1000.0);
default_value = f32_array_create_x(f);
}
if (ui_icon_button(tr("OK"), ICON_CHECK, UI_ALIGN_CENTER)) {
socket->name = string_copy(name);
socket->type = type == 0 ? "RGBA" : type == 1 ? "VECTOR" : "VALUE";
socket->color = nodes_material_get_socket_color(socket->type);
socket->min = min;
socket->max = max;
socket->default_value = default_value;
ui_box_hide();
nodes_material_sync_sockets(node);
ui_nodes_hwnd->redraws = 2;
}
}
void ui_viewnodes_on_socket_released_group_edit(void *_) {
ui_end_input();
ui_box_show_custom(&ui_viewnodes_on_socket_released_group_edit_box, 400, 250, NULL, true, tr("Socket"));
}
void ui_viewnodes_on_socket_released_group_menu_draw() {
ui_node_socket_t *socket = _ui_nodes_on_socket_released_socket;
ui_node_t *node = _ui_nodes_on_socket_released_node;
if (ui_menu_button(tr("Edit"), "", ICON_EDIT)) {
_ui_nodes_htype->i = string_equals(socket->type, "RGBA") ? 0 : string_equals(socket->type, "VECTOR") ? 1 : 2;
_ui_nodes_hname->text = string_copy(socket->name);
_ui_nodes_hmin->f = socket->min;
_ui_nodes_hmax->f = socket->max;
if (string_equals(socket->type, "RGBA") || string_equals(socket->type, "VECTOR")) {
_ui_nodes_hval0->f = socket->default_value->buffer[0];
_ui_nodes_hval1->f = socket->default_value->buffer[1];
_ui_nodes_hval2->f = socket->default_value->buffer[2];
if (string_equals(socket->type, "RGBA")) {
_ui_nodes_hval3->f = socket->default_value->buffer[3];
}
}
else {
_ui_nodes_hval0->f = socket->default_value->buffer[0];
}
sys_notify_on_next_frame(&ui_viewnodes_on_socket_released_group_edit, NULL);
}
if (ui_menu_button(tr("Delete"), "", ICON_DELETE)) {
i32 i = 0;
ui_node_canvas_t *canvas = ui_nodes_get_canvas(true);
// Remove links connected to the socket
while (i < canvas->links->length) {
ui_node_link_t *l = canvas->links->buffer[i];
if ((l->from_id == node->id && l->from_socket == array_index_of(node->outputs, socket)) ||
(l->to_id == node->id && l->to_socket == array_index_of(node->inputs, socket))) {
array_splice(canvas->links, i, 1);
}
else {
i++;
}
}
// Remove socket
array_remove(node->inputs, socket);
array_remove(node->outputs, socket);
nodes_material_sync_sockets(node);
}
}
void ui_viewnodes_on_socket_released_group_menu(void *_) {
ui_menu_draw(&ui_viewnodes_on_socket_released_group_menu_draw, -1, -1);
}
void ui_viewnodes_on_canvas_context_menu() {
ui_node_t *selected = _ui_nodes_on_canvas_released_selected;
g_ui->_y += 1;
bool is_protected = selected == NULL || string_equals(selected->type, "OUTPUT_MATERIAL_PBR") || string_equals(selected->type, "GROUP_INPUT") ||
string_equals(selected->type, "GROUP_OUTPUT") || string_equals(selected->type, "brush_output_node");
g_ui->enabled = !is_protected;
if (ui_menu_button(tr("Cut"), "ctrl+x", ICON_CUT)) {
sys_notify_on_next_frame(&ui_viewnodes_on_canvas_cut, NULL);
}
if (ui_menu_button(tr("Copy"), "ctrl+c", ICON_COPY)) {
sys_notify_on_next_frame(&ui_viewnodes_on_canvas_copy, NULL);
}
g_ui->enabled = !string_equals(ui_clipboard, "");
if (ui_menu_button(tr("Paste"), "ctrl+v", ICON_PASTE)) {
sys_notify_on_next_frame(&ui_viewnodes_on_canvas_paste, NULL);
}
g_ui->enabled = !is_protected;
if (ui_menu_button(tr("Delete"), "delete", ICON_DELETE)) {
sys_notify_on_next_frame(&ui_viewnodes_on_canvas_delete, NULL);
}
if (ui_menu_button(tr("Duplicate"), "ctrl+d", ICON_DUPLICATE)) {
sys_notify_on_next_frame(&ui_viewnodes_on_canvas_released_duplicate, NULL);
}
if (selected != NULL && string_equals(selected->type, "RGB")) {
if (ui_menu_button(tr("Add Swatch"), "", ICON_PALETTE)) {
f32_array_t *color = selected->outputs->buffer[0]->default_value;
swatch_color_t *new_swatch = project_make_swatch(color_from_floats(color->buffer[0], color->buffer[1], color->buffer[2], color->buffer[3]));
g_context->swatch = new_swatch;
any_array_push(g_project->swatches, new_swatch);
ui_base_hwnds->buffer[TAB_AREA_STATUS]->redraws = 1;
}
}
if (ui_menu_button(tr("Capture Output"), "", ICON_PHOTO)) {
sys_notify_on_next_frame(&ui_viewnodes_on_canvas_capture_output, NULL);
}
if (ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL) {
ui_menu_separator();
if (ui_menu_button(tr("2D View"), "", ICON_NONE)) {
ui_base_show_2d_view(VIEW_2D_TYPE_NODE);
}
}
g_ui->enabled = true;
}
void ui_viewnodes_on_canvas_released() {
if (g_ui->input_released_r && context_in_nodes() && math_abs(g_ui->input_x - g_ui->input_started_x) < 2 &&
math_abs(g_ui->input_y - g_ui->input_started_y) < 2) {
// Node selection
ui_nodes_t *nodes = ui_nodes_get_nodes();
ui_node_canvas_t *canvas = ui_nodes_get_canvas(true);
ui_node_t *selected = NULL;
for (i32 i = 0; i < canvas->nodes->length; ++i) {
ui_node_t *node = canvas->nodes->buffer[i];
if (ui_input_in_rect(g_ui->_window_x + UI_NODE_X(node), g_ui->_window_y + UI_NODE_Y(node), UI_NODE_W(node), UI_NODE_H(canvas, node))) {
selected = node;
break;
}
}
if (selected == NULL) {
nodes->nodes_selected_id = i32_array_create_from_raw((i32[]){}, 0);
}
else if (i32_array_index_of(nodes->nodes_selected_id, selected->id) == -1) {
nodes->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
selected->id,
},
1);
}
// Node context menu
if (!ui_nodes_socket_released) {
gc_unroot(_ui_nodes_on_canvas_released_selected);
_ui_nodes_on_canvas_released_selected = selected;
gc_root(_ui_nodes_on_canvas_released_selected);
ui_menu_draw(&ui_viewnodes_on_canvas_context_menu, -1, -1);
}
}
if (g_ui->input_released) {
ui_nodes_t *nodes = ui_nodes_get_nodes();
ui_node_canvas_t *canvas = ui_nodes_get_canvas(true);
for (i32 i = 0; i < canvas->nodes->length; ++i) {
ui_node_t *node = canvas->nodes->buffer[i];
if (ui_input_in_rect(g_ui->_window_x + UI_NODE_X(node), g_ui->_window_y + UI_NODE_Y(node), UI_NODE_W(node), UI_NODE_H(canvas, node))) {
if (nodes->nodes_selected_id->length > 0 && node->id == nodes->nodes_selected_id->buffer[0]) {
ui_view2d_hwnd->redraws = 2;
if (sys_time() - g_context->select_time < 0.2)
ui_base_show_2d_view(VIEW_2D_TYPE_NODE);
g_context->select_time = sys_time();
}
break;
}
}
}
}
void ui_viewnodes_on_socket_released(i32 socket_id) {
ui_node_canvas_t *canvas = ui_nodes_get_canvas(true);
ui_node_socket_t *socket = ui_get_socket(canvas->nodes, socket_id);
ui_node_t *node = ui_get_node(canvas->nodes, socket->node_id);
if (g_ui->input_released_r) {
if (string_equals(node->type, "GROUP_INPUT") || string_equals(node->type, "GROUP_OUTPUT")) {
gc_unroot(_ui_nodes_on_socket_released_socket);
_ui_nodes_on_socket_released_socket = socket;
gc_root(_ui_nodes_on_socket_released_socket);
gc_unroot(_ui_nodes_on_socket_released_node);
_ui_nodes_on_socket_released_node = node;
gc_root(_ui_nodes_on_socket_released_node);
sys_notify_on_next_frame(&ui_viewnodes_on_socket_released_group_menu, NULL);
}
else {
ui_viewnodes_on_canvas_released();
}
}
// Selecting which node socket to preview
// else {
// let i: i32 = array_index_of(node.outputs, socket);
// if (i > -1) {
// i32_imap_set(g_context.node_preview_socket_map, node.id, i);
// ui_nodes_node_changed = node;
// }
// }
}
ui_canvas_control_t *ui_viewnodes_on_canvas_control() {
ui_canvas_control_t *control = ui_nodes_get_canvas_control(ui_nodes_controls_down, true);
ui_nodes_controls_down = control->controls_down;
return control;
}
f32 ui_nodes_get_zoom_delta() {
return g_config->zoom_direction == ZOOM_DIRECTION_VERTICAL ? -g_ui->input_dy
: g_config->zoom_direction == ZOOM_DIRECTION_VERTICAL_INVERTED ? -g_ui->input_dy
: g_config->zoom_direction == ZOOM_DIRECTION_HORIZONTAL ? g_ui->input_dx
: g_config->zoom_direction == ZOOM_DIRECTION_HORIZONTAL_INVERTED ? g_ui->input_dx
: -(g_ui->input_dy - g_ui->input_dx);
}
ui_canvas_control_t *ui_nodes_get_canvas_control(bool controls_down, bool is_node_view) {
if (g_config->wrap_mouse && controls_down) {
if (g_ui->input_x < g_ui->_window_x) {
g_ui->input_x = g_ui->_window_x + g_ui->_window_w;
iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y));
}
else if (g_ui->input_x > g_ui->_window_x + g_ui->_window_w) {
g_ui->input_x = g_ui->_window_x;
iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y));
}
else if (g_ui->input_y < g_ui->_window_y) {
g_ui->input_y = g_ui->_window_y + g_ui->_window_h;
iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y));
}
else if (g_ui->input_y > g_ui->_window_y + g_ui->_window_h) {
g_ui->input_y = g_ui->_window_y;
iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y));
}
}
if (operator_shortcut(any_map_get(g_keymap, "action_pan"), SHORTCUT_TYPE_STARTED) ||
operator_shortcut(any_map_get(g_keymap, "action_zoom"), SHORTCUT_TYPE_STARTED) || g_ui->input_started_r || g_ui->input_wheel_delta != 0.0) {
controls_down = true;
}
else if (!operator_shortcut(any_map_get(g_keymap, "action_pan"), SHORTCUT_TYPE_DOWN) &&
!operator_shortcut(any_map_get(g_keymap, "action_zoom"), SHORTCUT_TYPE_DOWN) && !g_ui->input_down_r && g_ui->input_wheel_delta == 0.0) {
controls_down = false;
}
if (!controls_down) {
ui_canvas_control_t *cc = GC_ALLOC_INIT(ui_canvas_control_t, {.pan_x = 0, .pan_y = 0, .zoom = 0, .controls_down = controls_down});
return cc;
}
bool pan = g_ui->input_down_r || operator_shortcut(any_map_get(g_keymap, "action_pan"), SHORTCUT_TYPE_DOWN);
f32 zoom_delta = operator_shortcut(any_map_get(g_keymap, "action_zoom"), SHORTCUT_TYPE_DOWN) ? ui_nodes_get_zoom_delta() / 100.0 : 0.0;
ui_canvas_control_t *control = GC_ALLOC_INIT(ui_canvas_control_t, {.pan_x = pan ? g_ui->input_dx : 0.0,
.pan_y = pan ? g_ui->input_dy : 0.0,
.zoom = g_ui->input_wheel_delta != 0.0 ? -g_ui->input_wheel_delta / 10 : zoom_delta,
.controls_down = controls_down});
if (is_node_view && g_ui->input_x < g_ui->_window_x) {
control->pan_x = 0.0;
control->pan_y = 0.0;
control->zoom = 0.0;
}
if (g_ui->combo_selected_handle != NULL) {
control->zoom = 0.0;
}
if (control->zoom != 0.0) {
ui_nodes_grid_redraw = true;
}
return control;
}
gpu_texture_t *ui_nodes_draw_grid(f32 zoom) {
i32 ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W];
if (!ui_base_show) {
ww += g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] + ui_toolbar_w(true);
}
if (!base_view3d_show) {
ww += base_view3d_w();
}
i32 wh = sys_h();
f32 step = ui_nodes_grid_cell_w * zoom;
i32 mult = 5 * UI_SCALE();
i32 w = math_floor(ww + step * mult);
i32 h = math_floor(wh + step * mult);
if (w < 1) {
w = 1;
}
if (h < 1) {
h = 1;
}
gpu_texture_t *grid = gpu_create_render_target(w, h, GPU_TEXTURE_FORMAT_RGBA32);
draw_begin(grid, true, g_theme->SEPARATOR_COL);
i32 sep_col = g_theme->SEPARATOR_COL;
i32 line_primary = base_darker(sep_col, 0x00050505);
if (line_primary < 0xff000000) {
line_primary = sep_col + 0x00050505;
}
i32 line_secondary = base_darker(sep_col, 0x00090909);
if (line_secondary < 0xff000000) {
line_secondary = sep_col + 0x00090909;
}
draw_set_color(line_primary);
step = ui_nodes_grid_small_cell_w * zoom;
for (i32 i = 0; i < math_floor(h / (float)step) + 1; ++i) {
draw_line(0, i * step, w, i * step, 1.0);
}
for (i32 i = 0; i < math_floor(w / (float)step) + 1; ++i) {
draw_line(i * step, 0, i * step, h, 1.0);
}
draw_set_color(line_secondary);
step = ui_nodes_grid_cell_w * zoom;
for (i32 i = 0; i < math_floor(h / (float)step) + 1; ++i) {
draw_line(0, i * step, w, i * step, 1.0);
}
for (i32 i = 0; i < math_floor(w / (float)step) + 1; ++i) {
draw_line(i * step, 0, i * step, h, 1.0);
}
draw_end();
return grid;
}
void ui_nodes_render_color_picker_callback(swatch_color_t *color) {
_ui_nodes_render_tmp(color->base);
ui_nodes_hwnd->redraws = 2;
bool material_live = g_config->material_live;
if (material_live) {
ui_nodes_canvas_changed();
}
}
void ui_nodes_draw_menubar_search(void *_) {
ui_nodes_node_search(math_floor(ui_nodes_node_search_x), math_floor(ui_nodes_node_search_y), NULL);
}
void ui_nodes_draw_menubar() {
ui_node_canvas_t *c = ui_nodes_get_canvas(true);
i32 ew = math_floor(UI_ELEMENT_W() * 0.7);
i32 top_y = ui_menu_top_y();
draw_set_color(g_theme->WINDOW_BG_COL);
draw_filled_rect(0, top_y, ui_nodes_ww, UI_ELEMENT_H() + UI_ELEMENT_OFFSET() * 2);
draw_set_color(0xffffffff);
i32 start_y = top_y + UI_ELEMENT_OFFSET();
g_ui->_x = 0;
g_ui->_y = 2 + start_y;
if (g_config->touch_ui && !base_view3d_show && !ui_view2d_show) {
g_ui->_w = math_floor(ew + 3);
if (ui_icon_button("Back", ICON_ARROW_LEFT, UI_ALIGN_CENTER)) {
g_ui->input_released = false;
g_config->workspace = WORKSPACE_PAINT_3D;
config_save();
base_update_workspace();
}
g_ui->_x += ew + 3;
g_ui->_y = 2 + start_y;
}
// Editable canvas name
g_ui->_w = ew;
ui_handle_t *h = ui_handle(__ID__);
h->text = string_copy(c->name);
g_ui->_w = math_floor(math_min(draw_string_width(g_font, g_ui->font_size, h->text) + 15 * UI_SCALE(), 100 * UI_SCALE()));
char *new_name = ui_text_input(h, "", UI_ALIGN_LEFT, true, false);
g_ui->_x += g_ui->_w + 3;
g_ui->_y = 2 + start_y;
g_ui->_w = ew;
if (h->changed) { // Check whether renaming is possible and update group links
if (ui_nodes_group_stack->length > 0) {
bool can_rename = true;
for (i32 i = 0; i < g_project->_->material_groups->length; ++i) {
node_group_t *m = g_project->_->material_groups->buffer[i];
if (string_equals(m->canvas->name, new_name)) {
can_rename = false; // Name already used
}
}
if (can_rename) {
char *old_name = c->name;
c->name = string_copy(new_name);
ui_node_canvas_t_array_t *canvases = any_array_create_from_raw((void *[]){}, 0);
for (i32 i = 0; i < g_project->_->materials->length; ++i) {
slot_material_t *m = g_project->_->materials->buffer[i];
any_array_push(canvases, m->canvas);
}
for (i32 i = 0; i < g_project->_->material_groups->length; ++i) {
node_group_t *m = g_project->_->material_groups->buffer[i];
any_array_push(canvases, m->canvas);
}
for (i32 i = 0; i < canvases->length; ++i) {
ui_node_canvas_t *canvas = canvases->buffer[i];
for (i32 i = 0; i < canvas->nodes->length; ++i) {
ui_node_t *n = canvas->nodes->buffer[i];
if (string_equals(n->type, "GROUP") && string_equals(n->name, old_name)) {
n->name = string_copy(c->name);
}
}
}
}
}
else {
c->name = string_copy(new_name);
}
}
i32 _BUTTON_COL = g_theme->BUTTON_COL;
bool _SHADOWS = g_theme->SHADOWS;
g_theme->BUTTON_COL = g_theme->WINDOW_BG_COL;
g_theme->SHADOWS = false;
string_array_t *cats = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_categories : nodes_brush_categories;
for (i32 i = 0; i < cats->length; ++i) {
if ((ui_menubar_button(tr(cats->buffer[i]))) || (g_ui->is_hovered && ui_nodes_show_menu)) {
ui_nodes_show_menu = true;
ui_nodes_menu_category = i;
ui_nodes_popup_x = ui_nodes_wx + g_ui->_x;
ui_nodes_popup_y = ui_nodes_wy + g_ui->_y;
if (g_config->touch_ui) {
ui_nodes_show_menu_first = true;
i32 menuw = math_floor(ew * 2.3);
ui_nodes_popup_x -= menuw / 2.0;
ui_nodes_popup_x += g_ui->_w / 2.0;
}
}
g_ui->_x += g_ui->_w + 3;
g_ui->_y = 2 + start_y;
}
if (g_config->touch_ui) {
i32 _w = g_ui->_w;
g_ui->_w = math_floor(36 * UI_SCALE());
g_ui->_y = 4 * UI_SCALE() + start_y;
if (ui_menubar_icon_button(ICON_SEARCH)) {
ui_nodes_node_search(math_floor(g_ui->_window_x + g_ui->_x), math_floor(g_ui->_window_y + g_ui->_y), NULL);
}
g_ui->_w = _w;
}
else {
if (ui_menubar_button(tr("Search"))) {
ui_nodes_node_search_x = g_ui->_window_x + g_ui->_x;
ui_nodes_node_search_y = g_ui->_window_y + g_ui->_y;
// Allow for node menu to be closed first
sys_notify_on_next_frame(&ui_nodes_draw_menubar_search, NULL);
}
}
if (g_ui->is_hovered) {
ui_tooltip(string("%s (%s)", tr("Search for nodes"), any_map_get(g_keymap, "node_search")));
}
g_ui->_x += g_ui->_w + 3;
g_ui->_y = 2 + start_y;
g_theme->BUTTON_COL = _BUTTON_COL;
g_theme->SHADOWS = _SHADOWS;
// Close node group
if (ui_nodes_group_stack->length > 0 && ui_menubar_button(tr("Close"))) {
array_pop(ui_nodes_group_stack);
}
}
void ui_nodes_update(void *_) {
if (ui_nodes_show && base_ui_enabled) {
ui_nodes_wx = math_floor(sys_w()) + ui_toolbar_w(true);
ui_nodes_wy = ui_header_h * 2;
if (ui_view2d_show && base_view3d_show) {
ui_nodes_wy += sys_h() - g_config->layout->buffer[LAYOUT_SIZE_NODES_H];
}
i32 ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W];
if (!ui_base_show) {
ww += g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] + ui_toolbar_w(true);
ui_nodes_wx -= ui_toolbar_w(true);
ui_nodes_wy = 0;
}
if (!base_view3d_show) {
ww += base_view3d_w();
}
if (!base_view3d_show && ui_view2d_show) {
ui_nodes_wx = base_view3d_w();
ui_nodes_wy = 0;
ui_nodes_ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W];
ui_nodes_wh = sys_h();
if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) {
ui_nodes_wh += ui_header_h * 2;
}
}
if (!base_view3d_show) {
ui_nodes_wh -= ui_header_h * 4;
}
i32 mx = mouse_x;
i32 my = mouse_y;
bool enabled = true;
if (mx < ui_nodes_wx || mx > ui_nodes_wx + ww || my < ui_nodes_wy) {
enabled = false;
}
if (g_ui->is_typing || !g_ui->input_enabled) {
enabled = false;
}
if (enabled) {
ui_nodes_t *nodes = ui_nodes_get_nodes();
if (nodes->nodes_selected_id->length > 0 && g_ui->is_key_pressed) {
if (g_ui->key_code == KEY_CODE_LEFT) {
for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) {
i32 n = nodes->nodes_selected_id->buffer[i];
ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes;
ui_get_node(_nodes, n)->x -= 1;
}
}
else if (g_ui->key_code == KEY_CODE_RIGHT) {
for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) {
i32 n = nodes->nodes_selected_id->buffer[i];
ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes;
ui_get_node(_nodes, n)->x += 1;
}
}
if (g_ui->key_code == KEY_CODE_UP) {
for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) {
i32 n = nodes->nodes_selected_id->buffer[i];
ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes;
ui_get_node(_nodes, n)->y -= 1;
}
}
else if (g_ui->key_code == KEY_CODE_DOWN) {
for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) {
i32 n = nodes->nodes_selected_id->buffer[i];
ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes;
ui_get_node(_nodes, n)->y += 1;
}
}
}
// Node search popup
if (operator_shortcut(any_map_get(g_keymap, "node_search"), SHORTCUT_TYPE_STARTED)) {
ui_nodes_node_search(-1, -1, NULL);
}
if (ui_nodes_node_search_spawn != NULL) {
g_ui->input_x = mouse_x; // Fix input_dx after popup removal
g_ui->input_y = mouse_y;
gc_unroot(ui_nodes_node_search_spawn);
ui_nodes_node_search_spawn = NULL;
}
if (operator_shortcut(any_map_get(g_keymap, "view_reset"), SHORTCUT_TYPE_STARTED)) {
nodes->pan_x = 0.0;
nodes->pan_y = 0.0;
nodes->zoom = 1.0;
}
if (operator_shortcut(any_map_get(g_keymap, "node_overview"), SHORTCUT_TYPE_STARTED)) {
nodes->zoom = nodes->zoom == 1.0 ? 0.2 : 1.0;
nodes->uiw = ui_nodes_ww;
nodes->uih = ui_nodes_wh;
}
}
}
// Render
ui_nodes_recompile();
ui_nodes_t *ui_nodes = ui_nodes_get_nodes();
// Remove dragged link when mouse is released out of the node viewport
ui_node_canvas_t *c = ui_nodes_get_canvas(true);
if (ui_nodes_release_link && ui_nodes->link_drag_id != -1) {
array_remove(c->links, ui_get_link(c->links, ui_nodes->link_drag_id));
ui_nodes->link_drag_id = -1;
}
ui_nodes_release_link = g_ui->input_released;
if (!ui_nodes_show) {
return;
}
g_ui->input_enabled = base_ui_enabled;
if (ui_nodes_last_zoom != ui_nodes->zoom) {
ui_nodes_last_zoom = ui_nodes->zoom;
ui_nodes_grid_redraw = true;
}
if (ui_nodes_grid_redraw) {
if (ui_nodes_grid != NULL) {
gpu_delete_texture(ui_nodes_grid);
}
gc_unroot(ui_nodes_grid);
ui_nodes_grid = ui_nodes_draw_grid(ui_nodes->zoom);
gc_root(ui_nodes_grid);
ui_nodes_grid_redraw = false;
}
// Selected node preview
if (ui_nodes->nodes_selected_id->length > 0 && ui_nodes->nodes_selected_id->buffer[0] != ui_nodes_last_node_selected_id) {
ui_nodes_last_node_selected_id = ui_nodes->nodes_selected_id->buffer[0];
ui_node_t *sel = ui_get_node(c->nodes, ui_nodes->nodes_selected_id->buffer[0]);
ui_nodes_make_node_preview(sel);
// If script node is present, update selected node preview on every frame
bool has_script_node = false;
ui_node_canvas_t *canvas = g_context->material->canvas;
for (i32 i = 0; i < canvas->nodes->length; ++i) {
ui_node_t *n = canvas->nodes->buffer[i];
if (string_equals(n->type, "SCRIPT_CPU")) {
has_script_node = true;
break;
}
}
if (has_script_node) {
ui_nodes_last_node_selected_id = -1;
ui_view2d_hwnd->redraws = 2;
iron_delay_idle_sleep();
}
}
else if (ui_nodes->nodes_selected_id->length == 0) {
ui_nodes_last_node_selected_id = -1;
}
// Update node previews
if (ui_nodes_node_changed != NULL && !g_ui->input_down) {
ui_node_t_array_t *linked_nodes = any_array_create_from_raw((void *[]){}, 0);
ui_nodes_get_linked_nodes(linked_nodes, ui_nodes_node_changed, c);
ui_node_t *sel = ui_nodes->nodes_selected_id->length > 0 ? ui_get_node(c->nodes, ui_nodes->nodes_selected_id->buffer[0]) : NULL;
if (sel != NULL && array_index_of(linked_nodes, sel) == -1) {
any_array_push(linked_nodes, sel);
}
for (i32 i = 0; i < linked_nodes->length; ++i) {
ui_nodes_make_node_preview(linked_nodes->buffer[i]);
}
gc_unroot(ui_nodes_node_changed);
ui_nodes_node_changed = NULL;
}
// Start with UI
ui_begin(g_ui);
// Make window
ui_nodes_ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W];
ui_nodes_wx = math_floor(sys_w()) + ui_toolbar_w(true);
ui_nodes_wy = 0;
if (!ui_base_show) {
ui_nodes_ww += g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] + ui_toolbar_w(true);
ui_nodes_wx -= ui_toolbar_w(true);
}
if (!base_view3d_show) {
ui_nodes_ww += base_view3d_w();
}
i32 ew = math_floor(UI_ELEMENT_W() * 0.7);
ui_nodes_wh = sys_h();
if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) {
ui_nodes_wh += ui_header_h * 2;
}
if (ui_view2d_show) {
ui_nodes_wh = g_config->layout->buffer[LAYOUT_SIZE_NODES_H];
ui_nodes_wy = sys_h() - g_config->layout->buffer[LAYOUT_SIZE_NODES_H] + ui_header_h;
if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) {
ui_nodes_wy += ui_header_h;
}
else {
ui_nodes_wy -= ui_header_h;
}
if (!ui_base_show) {
ui_nodes_wy -= ui_header_h * 2;
}
}
if (!base_view3d_show && ui_view2d_show) {
ui_nodes_wx = base_view3d_w();
ui_nodes_wy = 0;
ui_nodes_ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W];
ui_nodes_wh = sys_h();
if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) {
ui_nodes_wh += ui_header_h * 2;
}
}
if (!base_view3d_show) {
ui_nodes_wh -= ui_header_h * 4;
}
if (ui_window(ui_nodes_hwnd, ui_nodes_wx, ui_nodes_wy, ui_nodes_ww, ui_nodes_wh, false)) {
if (!g_config->touch_ui) {
bool expand = !base_view3d_show && g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] == 0;
ui_tab(ui_nodes_htab, expand ? string("%s ", tr("Nodes")) : tr("Nodes"), false, -1, !base_view3d_show);
// Additional tabs
if (ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL) {
if (ui_nodes_tabs == NULL) {
gc_unroot(ui_nodes_tabs);
ui_nodes_tabs = any_array_create_from_raw((void *[]){}, 0);
gc_root(ui_nodes_tabs);
}
for (i32 i = 0; i < ui_nodes_tabs->length; ++i) {
ui_tab(ui_nodes_htab, ui_nodes_tabs->buffer[i]->canvas->name, false, -1, false);
if (ui_tab(ui_nodes_htab, tr("x"), false, -1, false)) {
array_splice(ui_nodes_tabs, i, 1);
ui_nodes_htab->i = 0;
}
}
if (ui_tab(ui_nodes_htab, tr("+"), false, -1, false)) {
any_array_push(ui_nodes_tabs, g_context->material);
}
}
}
// Grid
draw_set_color(0xffffffff);
f32 step = ui_nodes_grid_cell_w * ui_nodes->zoom;
ui_canvas_control_t *pending_control = ui_nodes_on_canvas_control();
f32 x = math_fmod(UI_NODES_PAN_X() + pending_control->pan_x, step) - step;
f32 y = math_fmod(UI_NODES_PAN_Y() + pending_control->pan_y, step) - step;
draw_image(ui_nodes_grid, x, y);
// Undo
if (g_ui->input_started || g_ui->is_key_pressed) {
gc_unroot(ui_nodes_last_canvas);
ui_nodes_last_canvas = util_clone_canvas(ui_nodes_get_canvas(true));
gc_root(ui_nodes_last_canvas);
}
// Nodes
bool _input_enabled = g_ui->input_enabled;
i32 header_h = UI_ELEMENT_H() * 2 + UI_ELEMENT_OFFSET() * 2;
bool header_hover = g_ui->input_y < g_ui->_window_y + header_h;
g_ui->input_enabled = _input_enabled && !ui_nodes_show_menu && !header_hover;
g_ui->window_border_right = g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W];
g_ui->window_border_top = ui_header_h * 2;
g_ui->window_border_bottom = g_config->layout->buffer[LAYOUT_SIZE_STATUS_H];
ui_node_canvas(ui_nodes, c);
g_ui->input_enabled = _input_enabled;
if (ui_nodes->color_picker_callback != NULL) {
g_context->color_picker_previous_tool = g_context->tool;
context_select_tool(TOOL_TYPE_PICKER);
gc_unroot(_ui_nodes_render_tmp);
_ui_nodes_render_tmp = ui_nodes->color_picker_callback;
gc_root(_ui_nodes_render_tmp);
g_context->color_picker_callback = &ui_nodes_render_color_picker_callback;
ui_nodes->color_picker_callback = NULL;
}
// Remove nodes with unknown id for this canvas type
if (ui_is_paste) {
node_list_t_array_t *node_list = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_list : nodes_brush_list;
i32 i = 0;
while (i++ < c->nodes->length) {
ui_node_t *canvas_node = c->nodes->buffer[i - 1];
if (string_array_index_of(ui_nodes_exclude_remove, canvas_node->type) >= 0) {
continue;
}
bool found = false;
for (i32 i = 0; i < node_list->length; ++i) {
ui_node_t_array_t *list = node_list->buffer[i];
for (i32 i = 0; i < list->length; ++i) {
ui_node_t *list_node = list->buffer[i];
if (string_equals(canvas_node->type, list_node->type)) {
found = true;
break;
}
}
if (found) {
break;
}
}
if (string_equals(canvas_node->type, "GROUP") && !ui_nodes_can_place_group(canvas_node->name)) {
found = false;
}
if (!found) {
ui_remove_node(canvas_node, c);
i32_array_remove(ui_nodes->nodes_selected_id, canvas_node->id);
i--;
}
}
}
if (ui_nodes_is_node_menu_op) {
ui_is_copy = false;
ui_is_cut = false;
ui_is_paste = false;
g_ui->is_delete_down = false;
}
// Recompile material on change
if (g_ui->changed) {
ui_nodes_recompile_mat = (g_ui->input_dx != 0 || g_ui->input_dy != 0 || !ui_nodes_uichanged_last) && g_config->material_live; // Instant preview
}
else if (ui_nodes_uichanged_last) {
ui_nodes_canvas_changed();
ui_nodes_push_undo(ui_nodes_last_canvas);
}
ui_nodes_uichanged_last = g_ui->changed;
// Node previews
if (g_context->selected_node_preview && ui_nodes->nodes_selected_id->length > 0) {
ui_node_t *sel = ui_get_node(c->nodes, ui_nodes->nodes_selected_id->buffer[0]);
gpu_texture_t *img = ui_nodes_get_node_preview_image(sel);
if (img != NULL && !(sel->flags & UI_NODE_FLAG_PREVIEW)) {
f32 tw = 128 * UI_SCALE();
f32 th = tw * (img->height / (float)img->width);
f32 tx = ui_nodes_ww - tw - 8 * UI_SCALE();
f32 ty = ui_nodes_wh - th - 8 * UI_SCALE();
if (img == any_imap_get(g_context->node_preview_map, sel->id)) {
ui_draw_shadow(tx, ty, tw, th);
}
bool single_channel = string_equals(sel->type, "LAYER_MASK");
if (single_channel) {
draw_set_pipeline(ui_view2d_pipe);
gpu_set_int(ui_view2d_channel_loc, 1);
}
draw_set_color(0xffffffff);
draw_scaled_image(img, tx, ty, tw, th);
if (single_channel) {
draw_set_pipeline(NULL);
}
}
}
ui_nodes_draw_menubar();
g_ui->window_border_right = 0;
g_ui->window_border_top = 0;
g_ui->window_border_bottom = 0;
}
ui_end();
if (ui_nodes_show_menu) {
node_list_t_array_t *list = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_list : nodes_brush_list;
ui_node_t_array_t *category = list->buffer[ui_nodes_menu_category];
bool is_group_category =
ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL && string_equals(nodes_material_categories->buffer[ui_nodes_menu_category], "Group");
i32 py = ui_nodes_popup_y;
i32 menuw = math_floor(ew * 2.3);
draw_begin(NULL, false, 0);
ui_begin_region(g_ui, math_floor(ui_nodes_popup_x), math_floor(py), menuw);
i32 _FILL_BUTTON_BG = g_theme->FILL_BUTTON_BG;
g_theme->FILL_BUTTON_BG = false;
i32 _ELEMENT_OFFSET = g_theme->ELEMENT_OFFSET;
g_theme->ELEMENT_OFFSET = 0;
i32 _ELEMENT_H = g_theme->ELEMENT_H;
g_theme->ELEMENT_H = g_config->touch_ui ? (28 + 2) : 28;
ui_menu_h = category->length * UI_ELEMENT_H();
if (is_group_category) {
ui_menu_h += g_project->_->material_groups->length * UI_ELEMENT_H();
}
ui_menu_begin();
for (i32 i = 0; i < category->length; ++i) {
ui_node_t *n = category->buffer[i];
if (ui_menu_button(tr(n->name), "", ICON_NONE)) {
ui_nodes_push_undo(NULL);
ui_node_canvas_t *canvas = ui_nodes_get_canvas(true);
ui_nodes_t *nodes = ui_nodes_get_nodes();
ui_node_t *node = ui_nodes_make_node(n, nodes, canvas);
any_array_push(canvas->nodes, node);
nodes->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
node->id,
},
1);
nodes->nodes_drag = true;
}
// Next column
if (g_ui->_y - ui_nodes_wy + UI_ELEMENT_H() / 2.0 > ui_nodes_wh) {
g_ui->_x += menuw;
g_ui->_y = py;
}
}
if (is_group_category) {
for (i32 i = 0; i < g_project->_->material_groups->length; ++i) {
node_group_t *g = g_project->_->material_groups->buffer[i];
g_ui->enabled = ui_nodes_can_place_group(g->canvas->name);
f32_array_t *row = f32_array_create_from_raw(
(f32[]){
5 / 6.0,
1 / 6.0,
},
2);
ui_row(row);
if (ui_button(string("%s%s", config_button_spacing, g->canvas->name), UI_ALIGN_LEFT, "")) {
ui_nodes_push_undo(NULL);
ui_node_canvas_t *canvas = ui_nodes_get_canvas(true);
ui_nodes_t *nodes = ui_nodes_get_nodes();
ui_node_t *node = ui_nodes_make_group_node(g->canvas, nodes, canvas);
any_array_push(canvas->nodes, node);
nodes->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
node->id,
},
1);
nodes->nodes_drag = true;
}
g_ui->enabled = !project_is_material_group_in_use(g);
if (ui_button("x", UI_ALIGN_CENTER, "")) {
history_delete_material_group(g);
array_remove(g_project->_->material_groups, g);
}
g_ui->enabled = true;
}
}
ui_nodes_hide_menu = g_ui->combo_selected_handle == NULL && !ui_nodes_show_menu_first &&
(g_ui->changed || g_ui->input_released || g_ui->input_released_r || g_ui->is_escape_down);
ui_nodes_show_menu_first = false;
g_theme->FILL_BUTTON_BG = _FILL_BUTTON_BG;
g_theme->ELEMENT_OFFSET = _ELEMENT_OFFSET;
g_theme->ELEMENT_H = _ELEMENT_H;
ui_menu_end();
ui_end_region();
draw_end();
}
if (ui_nodes_hide_menu) {
ui_nodes_show_menu = false;
ui_nodes_show_menu_first = true;
}
g_ui->input_enabled = true;
}
void ui_nodes_accept_asset_drop(i32 index) {
ui_nodes_push_undo(NULL);
node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL;
ui_node_t *n = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_create_node("TEX_IMAGE", g) : nodes_brush_create_node("TEX_IMAGE");
n->buttons->buffer[0]->default_value->buffer[0] = index;
ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
n->id,
},
1);
}
void ui_nodes_accept_layer_drop(i32 index) {
ui_nodes_push_undo(NULL);
if (slot_layer_is_group(g_project->_->layers->buffer[index])) {
return;
}
node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL;
ui_node_t *n = nodes_material_create_node(slot_layer_is_mask(g_context->layer) ? "LAYER_MASK" : "LAYER", g);
n->buttons->buffer[0]->default_value->buffer[0] = index;
ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
n->id,
},
1);
}
void ui_nodes_accept_material_drop(i32 index) {
ui_nodes_push_undo(NULL);
node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL;
ui_node_t *n = nodes_material_create_node("MATERIAL", g);
n->buttons->buffer[0]->default_value->buffer[0] = index;
ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
n->id,
},
1);
}
void ui_nodes_accept_swatch_drop(swatch_color_t *swatch) {
ui_nodes_push_undo(NULL);
node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL;
ui_node_t *n = nodes_material_create_node("RGB", g);
n->outputs->buffer[0]->default_value = f32_array_create_xyzw(color_get_rb(swatch->base) / 255.0, color_get_gb(swatch->base) / 255.0,
color_get_bb(swatch->base) / 255.0, color_get_ab(swatch->base) / 255.0);
ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw(
(i32[]){
n->id,
},
1);
}
void ui_viewnodes_init() {
ui_nodes_preview_image = ui_nodes_get_node_preview_image;
gc_root(ui_nodes_preview_image);
ui_nodes_on_link_drag = ui_viewnodes_on_link_drag;
gc_root(ui_nodes_on_link_drag);
ui_nodes_on_node_remove = ui_viewnodes_on_node_remove;
gc_root(ui_nodes_on_node_remove);
ui_nodes_on_node_changed = ui_viewnodes_on_node_changed;
gc_root(ui_nodes_on_node_changed);
ui_nodes_on_socket_released = ui_viewnodes_on_socket_released;
gc_root(ui_nodes_on_socket_released);
ui_nodes_on_canvas_released = ui_viewnodes_on_canvas_released;
gc_root(ui_nodes_on_canvas_released);
ui_nodes_on_canvas_control = ui_viewnodes_on_canvas_control;
gc_root(ui_nodes_on_canvas_control);
ui_nodes_grid_snap = g_config->grid_snap;
nodes_material_init();
}