1119 lines
34 KiB
TypeScript
1119 lines
34 KiB
TypeScript
|
|
let tab_layers_layer_name_edit: i32 = -1;
|
|
let tab_layers_layer_name_handle: ui_handle_t = ui_handle_create();
|
|
let tab_layers_show_context_menu: bool = false;
|
|
let tab_layers_l: slot_layer_t;
|
|
let tab_layers_mini: bool;
|
|
|
|
function tab_layers_draw(htab: ui_handle_t) {
|
|
let mini: bool = config_raw.layout[layout_size_t.SIDEBAR_W] <= ui_base_sidebar_mini_w;
|
|
mini ? tab_layers_draw_mini(htab) : tab_layers_draw_full(htab);
|
|
}
|
|
|
|
function tab_layers_draw_mini(htab: ui_handle_t) {
|
|
ui_set_hovered_tab_name(tr("Layers"));
|
|
|
|
let _ELEMENT_H: i32 = ui.ops.theme.ELEMENT_H;
|
|
ui.ops.theme.ELEMENT_H = math_floor(ui_base_sidebar_mini_w / 2 / UI_SCALE());
|
|
|
|
ui_begin_sticky();
|
|
ui_separator(5);
|
|
|
|
tab_layers_combo_filter();
|
|
tab_layers_button_2d_view();
|
|
tab_layers_button_new("+");
|
|
|
|
ui_end_sticky();
|
|
ui._y += 2;
|
|
|
|
tab_layers_highlight_odd_lines();
|
|
tab_layers_draw_slots(true);
|
|
|
|
ui.ops.theme.ELEMENT_H = _ELEMENT_H;
|
|
}
|
|
|
|
function tab_layers_draw_full(htab: ui_handle_t) {
|
|
if (ui_tab(htab, tr("Layers"))) {
|
|
ui_begin_sticky();
|
|
let row: f32[] = [1 / 4, 1 / 4, 1 / 2];
|
|
ui_row(row);
|
|
|
|
tab_layers_button_new(tr("New"));
|
|
tab_layers_button_2d_view();
|
|
tab_layers_combo_filter();
|
|
|
|
ui_end_sticky();
|
|
ui._y += 2;
|
|
|
|
tab_layers_highlight_odd_lines();
|
|
tab_layers_draw_slots(false);
|
|
}
|
|
}
|
|
|
|
function tab_layers_button_2d_view() {
|
|
if (ui_button(tr("2D View"))) {
|
|
ui_base_show_2d_view(view_2d_type_t.LAYER);
|
|
}
|
|
else if (ui.is_hovered) {
|
|
ui_tooltip(tr("Show 2D View") + " (" + map_get(config_keymap, "toggle_2d_view") + ")");
|
|
}
|
|
}
|
|
|
|
function tab_layers_draw_slots(mini: bool) {
|
|
for (let i: i32 = 0; i < project_layers.length; ++i) {
|
|
if (i >= project_layers.length) {
|
|
break; // Layer was deleted
|
|
}
|
|
let j: i32 = project_layers.length - 1 - i;
|
|
let l: slot_layer_t = project_layers[j];
|
|
tab_layers_draw_layer_slot(l, j, mini);
|
|
}
|
|
}
|
|
|
|
function tab_layers_highlight_odd_lines() {
|
|
let step: i32 = ui.ops.theme.ELEMENT_H * 2;
|
|
let full_h: i32 = ui._window_h - ui_base_hwnds[0].scroll_offset;
|
|
for (let i: i32 = 0; i < math_floor(full_h / step); ++i) {
|
|
if (i % 2 == 0) {
|
|
ui_fill(0, i * step, (ui._w / UI_SCALE() - 2), step, ui.ops.theme.WINDOW_BG_COL - 0x00040404);
|
|
}
|
|
}
|
|
}
|
|
|
|
function tab_layers_button_new(text: string) {
|
|
if (ui_button(text)) {
|
|
ui_menu_draw(function () {
|
|
let l: slot_layer_t = context_raw.layer;
|
|
if (ui_menu_button(tr("Paint Layer"))) {
|
|
layers_new_layer();
|
|
history_new_layer();
|
|
}
|
|
if (ui_menu_button(tr("Fill Layer"))) {
|
|
layers_create_fill_layer(uv_type_t.UVMAP);
|
|
}
|
|
if (ui_menu_button(tr("Decal Layer"))) {
|
|
layers_create_fill_layer(uv_type_t.PROJECT);
|
|
}
|
|
if (ui_menu_button(tr("Black Mask"))) {
|
|
if (slot_layer_is_mask(l)) {
|
|
context_set_layer(l.parent);
|
|
}
|
|
l = context_raw.layer;
|
|
|
|
let m: slot_layer_t = layers_new_mask(false, l);
|
|
sys_notify_on_next_frame(function (m: slot_layer_t) {
|
|
slot_layer_clear(m, 0x00000000);
|
|
}, m);
|
|
context_raw.layer_preview_dirty = true;
|
|
history_new_black_mask();
|
|
sys_notify_on_next_frame(function () {
|
|
layers_update_fill_layers();
|
|
});
|
|
}
|
|
if (ui_menu_button(tr("White Mask"))) {
|
|
if (slot_layer_is_mask(l)) {
|
|
context_set_layer(l.parent);
|
|
}
|
|
l = context_raw.layer;
|
|
|
|
let m: slot_layer_t = layers_new_mask(false, l);
|
|
sys_notify_on_next_frame(function (m: slot_layer_t) {
|
|
slot_layer_clear(m, 0xffffffff);
|
|
}, m);
|
|
context_raw.layer_preview_dirty = true;
|
|
history_new_white_mask();
|
|
sys_notify_on_next_frame(function () {
|
|
layers_update_fill_layers();
|
|
});
|
|
}
|
|
if (ui_menu_button(tr("Fill Mask"))) {
|
|
if (slot_layer_is_mask(l)) {
|
|
context_set_layer(l.parent);
|
|
}
|
|
l = context_raw.layer;
|
|
|
|
let m: slot_layer_t = layers_new_mask(false, l);
|
|
sys_notify_on_next_frame(function (m: slot_layer_t) {
|
|
slot_layer_to_fill_layer(m);
|
|
}, m);
|
|
context_raw.layer_preview_dirty = true;
|
|
history_new_fill_mask();
|
|
sys_notify_on_next_frame(function () {
|
|
layers_update_fill_layers();
|
|
});
|
|
}
|
|
ui.enabled = !slot_layer_is_group(context_raw.layer) && !slot_layer_is_in_group(context_raw.layer);
|
|
if (ui_menu_button(tr("Group"))) {
|
|
if (slot_layer_is_group(l) || slot_layer_is_in_group(l)) {
|
|
return;
|
|
}
|
|
|
|
if (slot_layer_is_layer_mask(l)) {
|
|
l = l.parent;
|
|
}
|
|
|
|
let pointers: map_t<slot_layer_t, i32> = tab_layers_init_layer_map();
|
|
let group: slot_layer_t = layers_new_group();
|
|
context_set_layer(l);
|
|
array_remove(project_layers, group);
|
|
array_insert(project_layers, array_index_of(project_layers, l) + 1, group);
|
|
l.parent = group;
|
|
for (let i: i32 = 0; i < project_materials.length; ++i) {
|
|
let m: slot_material_t = project_materials[i];
|
|
tab_layers_remap_layer_pointers(m.canvas.nodes, tab_layers_fill_layer_map(pointers));
|
|
}
|
|
context_set_layer(group);
|
|
history_new_group();
|
|
}
|
|
ui.enabled = true;
|
|
});
|
|
}
|
|
}
|
|
|
|
function tab_layers_combo_filter() {
|
|
let ar: string[] = [tr("All")];
|
|
for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
|
|
let p: mesh_object_t = project_paint_objects[i];
|
|
array_push(ar, p.base.name);
|
|
}
|
|
let atlases: string[] = project_get_used_atlases();
|
|
if (atlases != null) {
|
|
for (let i: i32 = 0; i < atlases.length; ++i) {
|
|
let a: string = atlases[i];
|
|
array_push(ar, a);
|
|
}
|
|
}
|
|
let filter_handle: ui_handle_t = ui_handle(__ID__);
|
|
filter_handle.position = context_raw.layer_filter;
|
|
context_raw.layer_filter = ui_combo(filter_handle, ar, tr("Filter"), false, ui_align_t.LEFT);
|
|
if (filter_handle.changed) {
|
|
for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
|
|
let p: mesh_object_t = project_paint_objects[i];
|
|
let filter_name: string = ar[context_raw.layer_filter];
|
|
p.base.visible = context_raw.layer_filter == 0 || p.base.name == filter_name || project_is_atlas_object(p);
|
|
}
|
|
if (context_raw.layer_filter == 0 && context_raw.merged_object_is_atlas) { // All
|
|
util_mesh_merge();
|
|
}
|
|
else if (context_raw.layer_filter > project_paint_objects.length) { // Atlas
|
|
let visibles: mesh_object_t[] = [];
|
|
for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
|
|
let p: mesh_object_t = project_paint_objects[i];
|
|
if (p.base.visible) {
|
|
array_push(visibles, p);
|
|
}
|
|
}
|
|
util_mesh_merge(visibles);
|
|
}
|
|
layers_set_object_mask();
|
|
util_uv_uvmap_cached = false;
|
|
context_raw.ddirty = 2;
|
|
render_path_raytrace_ready = false;
|
|
}
|
|
}
|
|
|
|
function tab_layers_remap_layer_pointers(nodes: ui_node_t[], pointer_map: map_t<i32, i32>) {
|
|
for (let i: i32 = 0; i < nodes.length; ++i) {
|
|
let n: ui_node_t = nodes[i];
|
|
if (n.type == "LAYER" || n.type == "LAYER_MASK") {
|
|
let i: i32 = n.buttons[0].default_value[0];
|
|
if (map_get(pointer_map, i) != -1) {
|
|
n.buttons[0].default_value[0] = map_get(pointer_map, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function tab_layers_init_layer_map(): map_t<slot_layer_t, i32> {
|
|
let res: map_t<slot_layer_t, i32> = map_create();
|
|
for (let i: i32 = 0; i < project_layers.length; ++i) {
|
|
map_set(res, project_layers[i], i);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function tab_layers_fill_layer_map(map: map_t<slot_layer_t, i32>): map_t<i32, i32> {
|
|
let res: map_t<i32, i32> = map_create();
|
|
let keys: string[] = map_keys(map);
|
|
for (let i: i32 = 0; i < keys.length; ++i) {
|
|
let l: string = keys[i];
|
|
map_set(res, map_get(map, l), array_index_of(project_layers, l) > -1 ? array_index_of(project_layers, l) : 9999);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function tab_layers_set_drag_layer(layer: slot_layer_t, off_x: f32, off_y: f32) {
|
|
base_drag_off_x = off_x;
|
|
base_drag_off_y = off_y;
|
|
base_drag_layer = layer;
|
|
context_raw.drag_dest = array_index_of(project_layers, layer);
|
|
}
|
|
|
|
function tab_layers_draw_layer_slot(l: slot_layer_t, i: i32, mini: bool) {
|
|
|
|
if (context_raw.layer_filter > 0 &&
|
|
slot_layer_get_object_mask(l) > 0 &&
|
|
slot_layer_get_object_mask(l) != context_raw.layer_filter) {
|
|
return;
|
|
}
|
|
|
|
if (l.parent != null && !l.parent.show_panel) { // Group closed
|
|
return;
|
|
}
|
|
if (l.parent != null && l.parent.parent != null && !l.parent.parent.show_panel) {
|
|
return;
|
|
}
|
|
|
|
let step: i32 = ui.ops.theme.ELEMENT_H;
|
|
let checkw: f32 = (ui._window_w / 100 * 8) / UI_SCALE();
|
|
|
|
// Highlight drag destination
|
|
let absy: f32 = ui._window_y + ui._y;
|
|
if (base_is_dragging && base_drag_layer != null && context_in_layers()) {
|
|
if (mouse_y > absy + step && mouse_y < absy + step * 3) {
|
|
let down: bool = array_index_of(project_layers, base_drag_layer) >= i;
|
|
context_raw.drag_dest = down ? i : i - 1;
|
|
|
|
let ls: slot_layer_t[] = project_layers;
|
|
let dest: i32 = context_raw.drag_dest;
|
|
let to_group: bool = down ? dest > 0 && ls[dest - 1].parent != null && ls[dest - 1].parent.show_panel : dest < ls.length && ls[dest].parent != null && ls[dest].parent.show_panel;
|
|
let nested_group: bool = slot_layer_is_group(base_drag_layer) && to_group;
|
|
if (!nested_group) {
|
|
if (slot_layer_can_move(context_raw.layer, context_raw.drag_dest)) {
|
|
ui_fill(checkw, step * 2, (ui._window_w / UI_SCALE() - 2) - checkw, 2 * UI_SCALE(), ui.ops.theme.HIGHLIGHT_COL);
|
|
}
|
|
}
|
|
}
|
|
else if (i == project_layers.length - 1 && mouse_y < absy + step) {
|
|
context_raw.drag_dest = project_layers.length - 1;
|
|
if (slot_layer_can_move(context_raw.layer, context_raw.drag_dest)) {
|
|
ui_fill(checkw, 0, (ui._window_w / UI_SCALE() - 2) - checkw, 2 * UI_SCALE(), ui.ops.theme.HIGHLIGHT_COL);
|
|
}
|
|
}
|
|
}
|
|
if (base_is_dragging && (base_drag_material != null || base_drag_swatch != null) && context_in_layers()) {
|
|
if (mouse_y > absy + step && mouse_y < absy + step * 3) {
|
|
context_raw.drag_dest = i;
|
|
if (tab_layers_can_drop_new_layer(i)) {
|
|
ui_fill(checkw, 2 * step, (ui._window_w / UI_SCALE() - 2) - checkw, 2 * UI_SCALE(), ui.ops.theme.HIGHLIGHT_COL);
|
|
}
|
|
}
|
|
else if (i == project_layers.length - 1 && mouse_y < absy + step) {
|
|
context_raw.drag_dest = project_layers.length;
|
|
if (tab_layers_can_drop_new_layer(project_layers.length)) {
|
|
ui_fill(checkw, 0, (ui._window_w / UI_SCALE() - 2) - checkw, 2 * UI_SCALE(), ui.ops.theme.HIGHLIGHT_COL);
|
|
}
|
|
}
|
|
}
|
|
|
|
mini ? tab_layers_draw_layer_slot_mini(l, i) : tab_layers_draw_layer_slot_full(l, i);
|
|
|
|
tab_layers_draw_layer_highlight(l, mini);
|
|
|
|
if (tab_layers_show_context_menu) {
|
|
tab_layers_draw_layer_context_menu(l, mini);
|
|
}
|
|
}
|
|
|
|
function tab_layers_draw_layer_slot_mini(l: slot_layer_t, i: i32) {
|
|
|
|
let row: f32[] = [1, 1];
|
|
ui_row(row);
|
|
let uix: f32 = ui._x;
|
|
let uiy: f32 = ui._y;
|
|
let state: ui_state_t = tab_layers_draw_layer_icon(l, i, uix, uiy, true);
|
|
tab_layers_handle_layer_icon_state(l, i, state, uix, uiy);
|
|
ui_end_element();
|
|
|
|
ui._y += UI_ELEMENT_H();
|
|
ui._y -= UI_ELEMENT_OFFSET();
|
|
}
|
|
|
|
function tab_layers_draw_layer_slot_full(l: slot_layer_t, i: i32) {
|
|
|
|
let step: i32 = ui.ops.theme.ELEMENT_H;
|
|
let uiw: f32 = ui._w;
|
|
|
|
let has_panel: bool = slot_layer_is_group(l) || (slot_layer_is_layer(l) && slot_layer_get_masks(l, false) != null);
|
|
if (has_panel) {
|
|
let row: f32[] = [8 / 100, 16 / 100, 36 / 100, 25 / 100, 15 / 100];
|
|
ui_row(row);
|
|
}
|
|
else {
|
|
let row: f32[] = [8 / 100, 16 / 100, 36 / 100, 30 / 100];
|
|
ui_row(row);
|
|
}
|
|
|
|
// Draw eye icon
|
|
let icons: gpu_texture_t = resource_get("icons.k");
|
|
let r: rect_t = resource_tile18(icons, l.visible ? 0 : 1, 0);
|
|
let center: f32 = (step / 2) * UI_SCALE();
|
|
ui._x += 2;
|
|
ui._y += 3;
|
|
ui._y += center;
|
|
let col: i32 = ui.ops.theme.ACCENT_COL;
|
|
let parent_hidden: bool = l.parent != null && (!l.parent.visible || (l.parent.parent != null && !l.parent.parent.visible));
|
|
if (parent_hidden) {
|
|
col -= 0x99000000;
|
|
}
|
|
|
|
if (ui_sub_image(icons, col, -1.0, r.x, r.y, r.w, r.h) == ui_state_t.RELEASED) {
|
|
tab_layers_layer_toggle_visible(l);
|
|
}
|
|
ui._x -= 2;
|
|
ui._y -= 3;
|
|
ui._y -= center;
|
|
|
|
let uix: f32 = ui._x;
|
|
let uiy: f32 = ui._y;
|
|
ui._x += 2;
|
|
ui._y += 3;
|
|
if (l.parent != null) {
|
|
ui._x += 10 * UI_SCALE();
|
|
if (l.parent.parent != null) ui._x += 10 * UI_SCALE();
|
|
}
|
|
|
|
let state: ui_state_t = tab_layers_draw_layer_icon(l, i, uix, uiy, false);
|
|
|
|
ui._x -= 2;
|
|
ui._y -= 3;
|
|
|
|
if (config_raw.touch_ui) {
|
|
ui._x += 12 * UI_SCALE();
|
|
}
|
|
|
|
tab_layers_handle_layer_icon_state(l, i, state, uix, uiy);
|
|
|
|
// Draw layer name
|
|
ui._y += center;
|
|
if (tab_layers_layer_name_edit == l.id) {
|
|
tab_layers_layer_name_handle.text = l.name;
|
|
l.name = ui_text_input(tab_layers_layer_name_handle);
|
|
if (ui.text_selected_handle != tab_layers_layer_name_handle) {
|
|
tab_layers_layer_name_edit = -1;
|
|
}
|
|
}
|
|
else {
|
|
if (ui.enabled && ui.input_enabled && ui.combo_selected_handle == null &&
|
|
ui.input_x > ui._window_x + ui._x && ui.input_x < ui._window_x + uiw &&
|
|
ui.input_y > ui._window_y + ui._y - center && ui.input_y < ui._window_y + ui._y - center + (step * UI_SCALE()) * 2) {
|
|
if (ui.input_started) {
|
|
context_set_layer(l);
|
|
tab_layers_set_drag_layer(context_raw.layer, -(mouse_x - uix - ui._window_x - 3), -(mouse_y - uiy - ui._window_y + 1));
|
|
}
|
|
else if (ui.input_released_r) {
|
|
context_set_layer(l);
|
|
tab_layers_show_context_menu = true;
|
|
}
|
|
}
|
|
|
|
let state: ui_state_t = ui_text(l.name);
|
|
if (state == ui_state_t.RELEASED) {
|
|
if (sys_time() - context_raw.select_time < 0.25) {
|
|
tab_layers_layer_name_edit = l.id;
|
|
tab_layers_layer_name_handle.text = l.name;
|
|
ui_start_text_edit(tab_layers_layer_name_handle);
|
|
}
|
|
context_raw.select_time = sys_time();
|
|
}
|
|
|
|
let in_focus: bool = ui.input_x > ui._window_x && ui.input_x < ui._window_x + ui._window_w &&
|
|
ui.input_y > ui._window_y && ui.input_y < ui._window_y + ui._window_h;
|
|
if (in_focus && ui.is_delete_down && tab_layers_can_delete(context_raw.layer)) {
|
|
ui.is_delete_down = false;
|
|
sys_notify_on_next_frame(function () {
|
|
tab_layers_delete_layer(context_raw.layer);
|
|
});
|
|
}
|
|
}
|
|
ui._y -= center;
|
|
|
|
if (l.parent != null) {
|
|
ui._x -= 10 * UI_SCALE();
|
|
if (l.parent.parent != null) ui._x -= 10 * UI_SCALE();
|
|
}
|
|
|
|
if (slot_layer_is_group(l)) {
|
|
ui_end_element();
|
|
}
|
|
else {
|
|
if (slot_layer_is_mask(l)) {
|
|
ui._y += center;
|
|
}
|
|
|
|
tab_layers_combo_blending(l);
|
|
|
|
if (slot_layer_is_mask(l)) {
|
|
ui._y -= center;
|
|
}
|
|
}
|
|
|
|
if (has_panel) {
|
|
ui._y += center;
|
|
let layer_panel: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
layer_panel.selected = l.show_panel;
|
|
l.show_panel = ui_panel(layer_panel, "", true, false);
|
|
ui._y -= center;
|
|
}
|
|
|
|
if (slot_layer_is_group(l) || slot_layer_is_mask(l)) {
|
|
ui._y -= UI_ELEMENT_OFFSET();
|
|
ui_end_element();
|
|
}
|
|
else {
|
|
ui._y -= UI_ELEMENT_OFFSET();
|
|
|
|
let row: f32[] = [8 / 100, 16 / 100, 36 / 100, 30 / 100, 10 / 100];
|
|
ui_row(row);
|
|
ui_end_element();
|
|
ui_end_element();
|
|
ui_end_element();
|
|
|
|
if (config_raw.touch_ui) {
|
|
ui._x += 12 * UI_SCALE();
|
|
}
|
|
|
|
tab_layers_combo_object(l);
|
|
ui_end_element();
|
|
}
|
|
|
|
ui._y -= UI_ELEMENT_OFFSET();
|
|
}
|
|
|
|
function tab_layers_combo_object(l: slot_layer_t, label: bool = false): ui_handle_t {
|
|
let ar: string[] = [tr("Shared")];
|
|
for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
|
|
let p: mesh_object_t = project_paint_objects[i];
|
|
array_push(ar, p.base.name);
|
|
}
|
|
let atlases: string[] = project_get_used_atlases();
|
|
if (atlases != null) {
|
|
for (let i: i32 = 0; i < atlases.length; ++i) {
|
|
let a: string = atlases[i];
|
|
array_push(ar, a);
|
|
}
|
|
}
|
|
let object_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
object_handle.position = l.object_mask;
|
|
l.object_mask = ui_combo(object_handle, ar, tr("Object"), label, ui_align_t.LEFT);
|
|
if (object_handle.changed) {
|
|
context_set_layer(l);
|
|
make_material_parse_mesh_material();
|
|
if (l.fill_layer != null) { // Fill layer
|
|
sys_notify_on_next_frame(function (l: slot_layer_t) {
|
|
context_raw.material = l.fill_layer;
|
|
slot_layer_clear(l);
|
|
layers_update_fill_layers();
|
|
}, l);
|
|
}
|
|
else {
|
|
layers_set_object_mask();
|
|
}
|
|
}
|
|
return object_handle;
|
|
}
|
|
|
|
function tab_layers_combo_blending(l: slot_layer_t, label: bool = false): ui_handle_t {
|
|
let blending_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
blending_handle.position = l.blending;
|
|
let blending_combo: string[] = [
|
|
tr("Mix"),
|
|
tr("Darken"),
|
|
tr("Multiply"),
|
|
tr("Burn"),
|
|
tr("Lighten"),
|
|
tr("Screen"),
|
|
tr("Dodge"),
|
|
tr("Add"),
|
|
tr("Overlay"),
|
|
tr("Soft Light"),
|
|
tr("Linear Light"),
|
|
tr("Difference"),
|
|
tr("Subtract"),
|
|
tr("Divide"),
|
|
tr("Hue"),
|
|
tr("Saturation"),
|
|
tr("Color"),
|
|
tr("Value"),
|
|
];
|
|
ui_combo(blending_handle, blending_combo, tr("Blending"), label);
|
|
if (blending_handle.changed) {
|
|
context_set_layer(l);
|
|
history_layer_blending();
|
|
l.blending = blending_handle.position;
|
|
make_material_parse_mesh_material();
|
|
}
|
|
return blending_handle;
|
|
}
|
|
|
|
function tab_layers_layer_toggle_visible(l: slot_layer_t) {
|
|
l.visible = !l.visible;
|
|
ui_view2d_hwnd.redraws = 2;
|
|
make_material_parse_mesh_material();
|
|
}
|
|
|
|
function tab_layers_draw_layer_highlight(l: slot_layer_t, mini: bool) {
|
|
let step: i32 = ui.ops.theme.ELEMENT_H;
|
|
|
|
// Separator line
|
|
ui_fill(0, 0, (ui._w / UI_SCALE() - 2), 1 * UI_SCALE(), ui.ops.theme.SEPARATOR_COL);
|
|
|
|
// Highlight selected
|
|
if (context_raw.layer == l) {
|
|
if (mini) {
|
|
ui_rect(1, -step * 2, ui._w / UI_SCALE() - 1, step * 2 + (mini ? -1 : 1), ui.ops.theme.HIGHLIGHT_COL, 3);
|
|
}
|
|
else {
|
|
ui_rect(1, -step * 2 - 1, ui._w / UI_SCALE() - 2, step * 2 + (mini ? -2 : 1), ui.ops.theme.HIGHLIGHT_COL, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
function tab_layers_handle_layer_icon_state(l: slot_layer_t, i: i32, state: ui_state_t, uix: f32, uiy: f32) {
|
|
|
|
let texpaint_preview: gpu_texture_t = l.texpaint_preview;
|
|
|
|
tab_layers_show_context_menu = false;
|
|
|
|
// Layer preview tooltip
|
|
if (ui.is_hovered && texpaint_preview != null) {
|
|
if (slot_layer_is_mask(l)) {
|
|
tab_layers_make_mask_preview_rgba32(l);
|
|
ui_tooltip_image(context_raw.mask_preview_rgba32);
|
|
}
|
|
else {
|
|
ui_tooltip_image(texpaint_preview);
|
|
}
|
|
if (i < 9) {
|
|
let i1: i32 = (i + 1);
|
|
ui_tooltip(l.name + " - (" + map_get(config_keymap, "select_layer") + " " + i1 + ")");
|
|
}
|
|
else {
|
|
ui_tooltip(l.name);
|
|
}
|
|
}
|
|
|
|
// Show context menu
|
|
if (ui.is_hovered && ui.input_released_r) {
|
|
context_set_layer(l);
|
|
tab_layers_show_context_menu = true;
|
|
}
|
|
|
|
if (state == ui_state_t.STARTED) {
|
|
context_set_layer(l);
|
|
tab_layers_set_drag_layer(context_raw.layer, -(mouse_x - uix - ui._window_x - 3), -(mouse_y - uiy - ui._window_y + 1));
|
|
}
|
|
else if (state == ui_state_t.RELEASED) {
|
|
if (sys_time() - context_raw.select_time < 0.2) {
|
|
ui_base_show_2d_view(view_2d_type_t.LAYER);
|
|
}
|
|
if (sys_time() - context_raw.select_time > 0.2) {
|
|
context_raw.select_time = sys_time();
|
|
}
|
|
if (l.fill_layer != null) {
|
|
context_set_material(l.fill_layer);
|
|
}
|
|
}
|
|
}
|
|
|
|
function tab_layers_draw_layer_icon(l: slot_layer_t, i: i32, uix: f32, uiy: f32, mini: bool): ui_state_t {
|
|
let icons: gpu_texture_t = resource_get("icons.k");
|
|
let icon_h: i32 = (UI_ELEMENT_H() - (mini ? 2 : 3)) * 2;
|
|
|
|
if (mini && UI_SCALE() > 1) {
|
|
ui._x -= 1 * UI_SCALE();
|
|
}
|
|
|
|
if (l.parent != null) {
|
|
ui._x += (icon_h - icon_h * 0.9) / 2;
|
|
icon_h *= 0.9;
|
|
if (l.parent.parent != null) {
|
|
ui._x += (icon_h - icon_h * 0.9) / 2;
|
|
icon_h *= 0.9;
|
|
}
|
|
}
|
|
|
|
if (!slot_layer_is_group(l)) {
|
|
let texpaint_preview: gpu_texture_t = l.texpaint_preview;
|
|
|
|
let icon: gpu_texture_t = l.fill_layer == null ? texpaint_preview : l.fill_layer.image_icon;
|
|
if (l.fill_layer == null) {
|
|
// Checker
|
|
let r: rect_t = resource_tile50(icons, 4, 1);
|
|
let _x: f32 = ui._x;
|
|
let _y: f32 = ui._y;
|
|
let _w: f32 = ui._w;
|
|
ui_sub_image(icons, 0xffffffff, icon_h, r.x, r.y, r.w, r.h);
|
|
ui.current_ratio--;
|
|
ui._x = _x;
|
|
ui._y = _y;
|
|
ui._w = _w;
|
|
}
|
|
if (l.fill_layer == null && slot_layer_is_mask(l)) {
|
|
draw_set_pipeline(ui_view2d_pipe);
|
|
gpu_set_int(ui_view2d_channel_loc, 1);
|
|
}
|
|
|
|
let state: ui_state_t = ui_image(icon, 0xffffffff, icon_h);
|
|
|
|
if (l.fill_layer == null && slot_layer_is_mask(l)) {
|
|
draw_set_pipeline(null);
|
|
}
|
|
|
|
// Draw layer numbers when selecting a layer via keyboard shortcut
|
|
let is_typing: bool = ui.is_typing;
|
|
if (!is_typing) {
|
|
if (i < 9 && operator_shortcut(map_get(config_keymap, "select_layer"), shortcut_type_t.DOWN)) {
|
|
let number: string = i32_to_string(i + 1) ;
|
|
let width: i32 = draw_string_width(ui.ops.font, ui.font_size, number) + 10;
|
|
let height: i32 = draw_font_height(ui.ops.font, ui.font_size);
|
|
draw_set_color(ui.ops.theme.TEXT_COL);
|
|
draw_filled_rect(uix, uiy, width, height);
|
|
draw_set_color(ui.ops.theme.BUTTON_COL);
|
|
draw_string(number, uix + 5, uiy);
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
else { // Group
|
|
let folder_closed: rect_t = resource_tile50(icons, 2, 1);
|
|
let folder_open: rect_t = resource_tile50(icons, 8, 1);
|
|
let folder: rect_t = l.show_panel ? folder_open : folder_closed;
|
|
return ui_sub_image(icons, ui.ops.theme.LABEL_COL - 0x00202020, icon_h, folder.x, folder.y, folder.w, folder.h);
|
|
}
|
|
}
|
|
|
|
function tab_layers_can_merge_down(l: slot_layer_t): bool {
|
|
let index: i32 = array_index_of(project_layers, l);
|
|
// Lowest layer
|
|
if (index == 0) {
|
|
return false;
|
|
}
|
|
// Lowest layer that has masks
|
|
if (slot_layer_is_layer(l) && slot_layer_is_mask(project_layers[0]) && project_layers[0].parent == l) {
|
|
return false;
|
|
}
|
|
// The lowest toplevel layer is a group
|
|
if (slot_layer_is_group(l) && slot_layer_is_in_group(project_layers[0]) && slot_layer_get_containing_group(project_layers[0]) == l) {
|
|
return false;
|
|
}
|
|
// Masks must be merged down to masks
|
|
if (slot_layer_is_mask(l) && !slot_layer_is_mask(project_layers[index - 1])) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function tab_layers_draw_layer_context_menu(l: slot_layer_t, mini: bool) {
|
|
|
|
tab_layers_l = l;
|
|
tab_layers_mini = mini;
|
|
|
|
ui_menu_draw(function () {
|
|
|
|
let l: slot_layer_t = tab_layers_l;
|
|
let mini: bool = tab_layers_mini;
|
|
|
|
if (mini) {
|
|
let visible_handle: ui_handle_t = ui_handle(__ID__);
|
|
visible_handle.selected = l.visible;
|
|
ui_check(visible_handle, tr("Visible"));
|
|
if (visible_handle.changed) {
|
|
tab_layers_layer_toggle_visible(l);
|
|
ui_menu_keep_open = true;
|
|
}
|
|
|
|
if (!slot_layer_is_group(l)) {
|
|
if (tab_layers_combo_blending(l, true).changed) {
|
|
ui_menu_keep_open = true;
|
|
}
|
|
}
|
|
if (slot_layer_is_layer(l)) {
|
|
if (tab_layers_combo_object(l, true).changed) {
|
|
ui_menu_keep_open = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ui_menu_button(tr("Export"))) {
|
|
if (slot_layer_is_mask(l)) {
|
|
ui_files_show("png", true, false, function (path: string) {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
let f: string = ui_files_filename;
|
|
if (f == "") {
|
|
f = tr("untitled");
|
|
}
|
|
if (!ends_with(f, ".png")) {
|
|
f += ".png";
|
|
}
|
|
iron_write_png(path + path_sep + f, gpu_get_texture_pixels(l.texpaint), l.texpaint.width, l.texpaint.height, 3); // RRR1
|
|
});
|
|
}
|
|
else {
|
|
context_raw.layers_export = export_mode_t.SELECTED;
|
|
box_export_show_textures();
|
|
}
|
|
}
|
|
|
|
if (!slot_layer_is_group(l)) {
|
|
let to_fill_string: string = slot_layer_is_layer(l) ? tr("To Fill Layer") : tr("To Fill Mask");
|
|
let to_paint_string: string = slot_layer_is_layer(l) ? tr("To Paint Layer") : tr("To Paint Mask");
|
|
|
|
if (l.fill_layer == null && ui_menu_button(to_fill_string)) {
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
slot_layer_is_layer(l) ? history_to_fill_layer() : history_to_fill_mask();
|
|
slot_layer_to_fill_layer(l);
|
|
});
|
|
}
|
|
if (l.fill_layer != null && ui_menu_button(to_paint_string)) {
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
slot_layer_is_layer(l) ? history_to_paint_layer() : history_to_paint_mask();
|
|
slot_layer_to_paint_layer(l);
|
|
});
|
|
}
|
|
}
|
|
|
|
ui.enabled = tab_layers_can_delete(l);
|
|
if (ui_menu_button(tr("Delete"), "delete")) {
|
|
sys_notify_on_next_frame(function () {
|
|
tab_layers_delete_layer(context_raw.layer);
|
|
});
|
|
}
|
|
ui.enabled = true;
|
|
|
|
if (l.fill_layer == null && ui_menu_button(tr("Clear"))) {
|
|
context_set_layer(l);
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
if (!slot_layer_is_group(l)) {
|
|
history_clear_layer();
|
|
slot_layer_clear(l);
|
|
}
|
|
else {
|
|
for (let i: i32 = 0; i < slot_layer_get_children(l).length; ++i) {
|
|
let c: slot_layer_t = slot_layer_get_children(l)[i];
|
|
context_raw.layer = c;
|
|
history_clear_layer();
|
|
slot_layer_clear(c);
|
|
}
|
|
context_raw.layers_preview_dirty = true;
|
|
context_raw.layer = l;
|
|
}
|
|
});
|
|
}
|
|
if (slot_layer_is_mask(l) && l.fill_layer == null && ui_menu_button(tr("Invert"))) {
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
context_set_layer(l);
|
|
history_invert_mask();
|
|
slot_layer_invert_mask(l);
|
|
});
|
|
}
|
|
if (slot_layer_is_mask(l) && ui_menu_button(tr("Apply"))) {
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
context_raw.layer = l;
|
|
history_apply_mask();
|
|
slot_layer_apply_mask(l);
|
|
context_set_layer(l.parent);
|
|
make_material_parse_mesh_material();
|
|
context_raw.layers_preview_dirty = true;
|
|
});
|
|
}
|
|
if (slot_layer_is_group(l) && ui_menu_button(tr("Merge Group"))) {
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
layers_merge_group(l);
|
|
});
|
|
}
|
|
ui.enabled = tab_layers_can_merge_down(l);
|
|
if (ui_menu_button(tr("Merge Down"))) {
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
context_set_layer(l);
|
|
history_merge_layers();
|
|
layers_merge_down();
|
|
if (context_raw.layer.fill_layer != null) slot_layer_to_paint_layer(context_raw.layer);
|
|
});
|
|
}
|
|
ui.enabled = true;
|
|
if (ui_menu_button(tr("Duplicate"))) {
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
context_set_layer(l);
|
|
history_duplicate_layer();
|
|
layers_duplicate_layer(l);
|
|
});
|
|
}
|
|
|
|
ui_menu_align();
|
|
let layer_opac_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
layer_opac_handle.value = l.mask_opacity;
|
|
ui_slider(layer_opac_handle, tr("Opacity"), 0.0, 1.0, true);
|
|
if (layer_opac_handle.changed) {
|
|
if (ui.input_started) {
|
|
history_layer_opacity();
|
|
}
|
|
l.mask_opacity = layer_opac_handle.value;
|
|
make_material_parse_mesh_material();
|
|
ui_menu_keep_open = true;
|
|
}
|
|
|
|
if (!slot_layer_is_group(l)) {
|
|
ui_menu_align();
|
|
let res_handle_changed_last: bool = base_res_handle.changed;
|
|
///if (arm_android || arm_ios)
|
|
let ar: string[] = ["128", "256", "512", "1K", "2K", "4K"];
|
|
///else
|
|
let ar: string[] = ["128", "256", "512", "1K", "2K", "4K", "8K", "16K"];
|
|
///end
|
|
let _y: i32 = ui._y;
|
|
base_res_handle.value = base_res_handle.position;
|
|
base_res_handle.position = math_floor(ui_slider(base_res_handle, ar[base_res_handle.position], 0, ar.length - 1, false, 1, false, ui_align_t.LEFT, false));
|
|
if (base_res_handle.changed) {
|
|
ui_menu_keep_open = true;
|
|
}
|
|
if (res_handle_changed_last && !base_res_handle.changed) {
|
|
layers_on_resized();
|
|
}
|
|
ui._y = _y;
|
|
ui_draw_string(tr("Res"), -1, 0, ui_align_t.RIGHT, true);
|
|
ui_end_element();
|
|
|
|
///if (arm_android || arm_ios)
|
|
// let bits_items: string[] = ["8bit"];
|
|
// ui_inline_radio(base_bits_handle, bits_items, ui_align_t.LEFT);
|
|
///else
|
|
ui_menu_align();
|
|
let bits_items: string[] = ["8bit", "16bit", "32bit"];
|
|
ui_inline_radio(base_bits_handle, bits_items, ui_align_t.LEFT);
|
|
///end
|
|
if (base_bits_handle.changed) {
|
|
sys_notify_on_next_frame(layers_set_bits);
|
|
make_material_parse_paint_material();
|
|
ui_menu_keep_open = true;
|
|
}
|
|
}
|
|
|
|
if (l.fill_layer != null) {
|
|
ui_menu_align();
|
|
let scale_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
scale_handle.value = l.scale;
|
|
l.scale = ui_slider(scale_handle, tr("UV Scale"), 0.0, 5.0, true);
|
|
if (scale_handle.changed) {
|
|
context_set_material(l.fill_layer);
|
|
context_set_layer(l);
|
|
sys_notify_on_next_frame(function () {
|
|
layers_update_fill_layers();
|
|
});
|
|
ui_menu_keep_open = true;
|
|
}
|
|
|
|
ui_menu_align();
|
|
let angle_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
angle_handle.value = l.angle;
|
|
l.angle = ui_slider(angle_handle, tr("Angle"), 0.0, 360, true, 1);
|
|
if (angle_handle.changed) {
|
|
context_set_material(l.fill_layer);
|
|
context_set_layer(l);
|
|
make_material_parse_paint_material();
|
|
sys_notify_on_next_frame(function () {
|
|
layers_update_fill_layers();
|
|
});
|
|
ui_menu_keep_open = true;
|
|
}
|
|
|
|
ui_menu_align();
|
|
let uv_type_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
uv_type_handle.position = l.uv_type;
|
|
let uv_type_items: string[] = [tr("UV Map"), tr("Triplanar"), tr("Project")];
|
|
l.uv_type = ui_inline_radio(uv_type_handle, uv_type_items, ui_align_t.LEFT);
|
|
if (uv_type_handle.changed) {
|
|
context_set_material(l.fill_layer);
|
|
context_set_layer(l);
|
|
make_material_parse_paint_material();
|
|
sys_notify_on_next_frame(function () {
|
|
layers_update_fill_layers();
|
|
});
|
|
ui_menu_keep_open = true;
|
|
}
|
|
}
|
|
|
|
if (!slot_layer_is_group(l)) {
|
|
let base_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let opac_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let nor_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let nor_blend_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let occ_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let rough_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let met_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let height_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let height_blend_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let emis_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
let subs_handle: ui_handle_t = ui_nest(ui_handle(__ID__), l.id);
|
|
base_handle.selected = l.paint_base;
|
|
opac_handle.selected = l.paint_opac;
|
|
nor_handle.selected = l.paint_nor;
|
|
nor_blend_handle.selected = l.paint_nor_blend;
|
|
occ_handle.selected = l.paint_occ;
|
|
rough_handle.selected = l.paint_rough;
|
|
met_handle.selected = l.paint_met;
|
|
height_handle.selected = l.paint_height;
|
|
height_blend_handle.selected = l.paint_height_blend;
|
|
emis_handle.selected = l.paint_emis;
|
|
subs_handle.selected = l.paint_subs;
|
|
l.paint_base = ui_check(base_handle, tr("Base Color"));
|
|
l.paint_opac = ui_check(opac_handle, tr("Opacity"));
|
|
l.paint_nor = ui_check(nor_handle, tr("Normal"));
|
|
l.paint_nor_blend = ui_check(nor_blend_handle, tr("Normal Blending"));
|
|
l.paint_occ = ui_check(occ_handle, tr("Occlusion"));
|
|
l.paint_rough = ui_check(rough_handle, tr("Roughness"));
|
|
l.paint_met = ui_check(met_handle, tr("Metallic"));
|
|
l.paint_height = ui_check(height_handle, tr("Height"));
|
|
l.paint_height_blend = ui_check(height_blend_handle, tr("Height Blending"));
|
|
l.paint_emis = ui_check(emis_handle, tr("Emission"));
|
|
l.paint_subs = ui_check(subs_handle, tr("Subsurface"));
|
|
if (base_handle.changed ||
|
|
opac_handle.changed ||
|
|
nor_handle.changed ||
|
|
nor_blend_handle.changed ||
|
|
occ_handle.changed ||
|
|
rough_handle.changed ||
|
|
met_handle.changed ||
|
|
height_handle.changed ||
|
|
height_blend_handle.changed ||
|
|
emis_handle.changed ||
|
|
subs_handle.changed) {
|
|
make_material_parse_mesh_material();
|
|
ui_menu_keep_open = true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function tab_layers_make_mask_preview_rgba32(l: slot_layer_t) {
|
|
if (context_raw.mask_preview_rgba32 == null) {
|
|
context_raw.mask_preview_rgba32 = gpu_create_render_target(util_render_layer_preview_size, util_render_layer_preview_size);
|
|
}
|
|
// Convert from R8 to RGBA32 for tooltip display
|
|
if (context_raw.mask_preview_last != l) {
|
|
context_raw.mask_preview_last = l;
|
|
tab_layers_l = l;
|
|
sys_notify_on_next_frame(function () {
|
|
let l: slot_layer_t = tab_layers_l;
|
|
draw_begin(context_raw.mask_preview_rgba32);
|
|
draw_set_pipeline(ui_view2d_pipe);
|
|
gpu_set_int(ui_view2d_channel_loc, 1);
|
|
draw_image(l.texpaint_preview, 0, 0);
|
|
draw_end();
|
|
draw_set_pipeline(null);
|
|
});
|
|
}
|
|
}
|
|
|
|
function tab_layers_delete_layer(l: slot_layer_t) {
|
|
let pointers: map_t<slot_layer_t, i32> = tab_layers_init_layer_map();
|
|
|
|
if (slot_layer_is_layer(l) && slot_layer_has_masks(l, false)) {
|
|
let masks: slot_layer_t[] = slot_layer_get_masks(l, false);
|
|
for (let i: i32 = 0; i < masks.length; ++i) {
|
|
let m: slot_layer_t = masks[i];
|
|
context_raw.layer = m;
|
|
history_delete_layer();
|
|
slot_layer_delete(m);
|
|
}
|
|
}
|
|
if (slot_layer_is_group(l)) {
|
|
let children: slot_layer_t[] = slot_layer_get_children(l);
|
|
for (let i: i32 = 0; i < children.length; ++i) {
|
|
let c: slot_layer_t = children[i];
|
|
if (slot_layer_has_masks(c, false)) {
|
|
let masks: slot_layer_t[] = slot_layer_get_masks(c, false);
|
|
for (let i: i32 = 0; i < masks.length; ++i) {
|
|
let m: slot_layer_t = masks[i];
|
|
context_raw.layer = m;
|
|
history_delete_layer();
|
|
slot_layer_delete(m);
|
|
}
|
|
}
|
|
context_raw.layer = c;
|
|
history_delete_layer();
|
|
slot_layer_delete(c);
|
|
}
|
|
if (slot_layer_has_masks(l)) {
|
|
for (let i: i32 = 0; i < slot_layer_get_masks(l).length; ++i) {
|
|
let m: slot_layer_t = slot_layer_get_masks(l)[i];
|
|
context_raw.layer = m;
|
|
history_delete_layer();
|
|
slot_layer_delete(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
context_raw.layer = l;
|
|
history_delete_layer();
|
|
slot_layer_delete(l);
|
|
|
|
if (slot_layer_is_mask(l)) {
|
|
context_raw.layer = l.parent;
|
|
layers_update_fill_layers();
|
|
}
|
|
|
|
// Remove empty group
|
|
if (slot_layer_is_in_group(l) && slot_layer_get_children(slot_layer_get_containing_group(l)) == null) {
|
|
let g: slot_layer_t = slot_layer_get_containing_group(l);
|
|
// Maybe some group masks are left
|
|
if (slot_layer_has_masks(g)) {
|
|
for (let i: i32 = 0; i < slot_layer_get_masks(g).length; ++i) {
|
|
let m: slot_layer_t = slot_layer_get_masks(g)[i];
|
|
context_raw.layer = m;
|
|
history_delete_layer();
|
|
slot_layer_delete(m);
|
|
}
|
|
}
|
|
context_raw.layer = l.parent;
|
|
history_delete_layer();
|
|
slot_layer_delete(l.parent);
|
|
}
|
|
context_raw.ddirty = 2;
|
|
for (let i: i32 = 0; i < project_materials.length; ++i) {
|
|
let m: slot_material_t = project_materials[i];
|
|
tab_layers_remap_layer_pointers(m.canvas.nodes, tab_layers_fill_layer_map(pointers));
|
|
}
|
|
}
|
|
|
|
function tab_layers_can_delete(l: slot_layer_t): bool {
|
|
let num_layers: i32 = 0;
|
|
|
|
if (slot_layer_is_mask(l)) {
|
|
return true;
|
|
}
|
|
|
|
for (let i: i32 = 0; i < project_layers.length; ++i) {
|
|
let slot: slot_layer_t = project_layers[i];
|
|
if (slot_layer_is_layer(slot)) {
|
|
++num_layers;
|
|
}
|
|
}
|
|
|
|
// All layers are in one group
|
|
if (slot_layer_is_group(l) && slot_layer_get_children(l).length == num_layers) {
|
|
return false;
|
|
}
|
|
|
|
// Do not delete last layer
|
|
return num_layers > 1;
|
|
}
|
|
|
|
function tab_layers_can_drop_new_layer(position: i32): bool {
|
|
if (position > 0 && position < project_layers.length && slot_layer_is_mask(project_layers[position - 1])) {
|
|
// 1. The layer to insert is inserted in the middle
|
|
// 2. The layer below is a mask, i.e. the layer would have to be a (group) mask, too.
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|