#include "pch.h" #include "app.h" #include "node_icon.h" #include "node_dialog_open.h" #include "node_text.h" #include "node_progress_bar.h" #include "node_dialog_picker.h" void App::title_update() { static char str[256]; snprintf(str, 256, "Panodoc: %s%s (%s)", doc_name.c_str(), canvas->m_canvas->m_unsaved ? "*" : "", res_to_string(canvas->m_canvas->m_width).c_str()); if (auto docname = layout[main_id]->find("txt-docname")) docname->set_text(str); } void App::init_toolbar_main() { if (auto* button = layout[main_id]->find("btn-anim")) { button->on_click = [this, button](Node*) { if (canvas) { //canvas->m_canvas->export_anim(); } }; } if (auto* button = layout[main_id]->find("btn-open")) { button->on_click = [this, button](Node*) { dialog_open(); }; } if (auto* button = layout[main_id]->find("btn-save")) { button->on_click = [this, button](Node*) { dialog_save(); }; } if (auto* button = layout[main_id]->find("btn-undo")) { button->on_click = [this, button](Node*) { ActionManager::undo(); }; } if (auto* button = layout[main_id]->find("btn-redo")) { button->on_click = [this, button](Node*) { ActionManager::redo(); }; } if (auto* button = layout[main_id]->find("btn-clean-memory")) { button->on_click = [this](Node*) { ActionManager::clear(); }; } if (auto* button = layout[main_id]->find("btn-clear")) { button->on_click = [this](Node*) { //exit(0); if (canvas) canvas->m_canvas->clear({ 0, 0, 0, 0 }); }; } if (auto* button = layout[main_id]->find("btn-popup")) { button->on_click = [this](Node*) { msgbox = new NodeMessageBox(); msgbox->m_manager = &layout; msgbox->init(); layout[main_id]->add_child(msgbox); layout[main_id]->update(); }; } if (auto* button = layout[main_id]->find("btn-settings")) { button->on_click = [this](Node*) { settings = new NodeSettings(); settings->m_manager = &layout; settings->init(); layout[main_id]->add_child(settings); layout[main_id]->update(); }; } } template std::shared_ptr find_or_create_panel(NodeScroll* panels) { std::shared_ptr ret; auto node_find = std::find_if(panels->m_children.begin(), panels->m_children.end(), [](const std::shared_ptr&p) { return (bool)std::dynamic_pointer_cast(p); }); if (node_find != panels->m_children.end()) { ret = std::static_pointer_cast(*node_find); } else { ret = std::make_shared(); ret->m_manager = panels->m_manager; ret->init(); ret->create(); ret->loaded(); } return ret; } void App::init_sidebar() { sidebar = layout[main_id]->find("sidebar"); panels = layout[main_id]->find("panels"); canvas = layout[main_id]->find("paint-canvas"); //brushes = layout[main_id]->find("panel-brush"); //layers = layout[main_id]->find("panel-layer"); //color = layout[main_id]->find("panel-color"); //stroke = layout[main_id]->find("panel-stroke"); //brushes = find_or_create_panel(panels); layers = find_or_create_panel(panels); color = find_or_create_panel(panels); stroke = find_or_create_panel(panels); grid = find_or_create_panel(panels); //presets = find_or_create_panel(panels); canvas->m_canvas->on_mode_changed = [this](kCanvasMode prev, kCanvasMode mode) { }; color->on_color_changed = [this](Node* target, glm::vec4 color) { Canvas::I->m_current_brush->m_tip_color = color; }; stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) { Canvas::I->m_current_brush->load_tip(path, thumb); stroke->m_preview->draw_stroke(); }; stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) { Canvas::I->m_current_brush->load_pattern(path, thumb); stroke->m_preview->draw_stroke(); }; stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) { Canvas::I->m_current_brush->load_dual(path, thumb); stroke->m_preview->draw_stroke(); }; layers->on_layer_add = [this](Node*) { canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str()); canvas->m_canvas->m_unsaved = true; title_update(); }; layers->on_layer_duplicate = [this](Node*, int source_index) { Canvas::I->layer_add(layers->m_layers.back()->m_label_text.c_str()); auto& dst = Canvas::I->m_layers.back(); auto& src = Canvas::I->m_layers[Canvas::I->m_order[source_index]]; for (int i = 0; i < 6; i++) { if (!src->m_dirty_face[i]) continue; dst->m_rtt[i].copy(src->m_rtt[i]); dst->m_dirty_face[i] = src->m_dirty_face[i]; dst->m_dirty_box[i] = src->m_dirty_box[i]; dst->m_opacity = src->m_opacity; dst->m_blend_mode = src->m_blend_mode; dst->m_alpha_locked = src->m_alpha_locked; } Canvas::I->m_unsaved = true; title_update(); }; layers->on_layer_change = [this](Node*, int old_idx, int new_idx) { canvas->m_canvas->m_current_layer_idx = canvas->m_canvas->m_order[new_idx]; }; layers->on_layer_order = [this](Node*, int old_idx, int new_idx) { canvas->m_canvas->layer_order(old_idx, new_idx); canvas->m_canvas->m_unsaved = true; title_update(); }; layers->on_layer_delete = [this](Node*, int idx) { canvas->m_canvas->layer_remove(idx); canvas->m_canvas->m_unsaved = true; title_update(); }; layers->on_layer_opacity_changed = [this](Node*, int idx, float value) { canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_opacity = value; canvas->m_canvas->m_unsaved = true; title_update(); }; layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) { canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_visible = visible; canvas->m_canvas->m_unsaved = true; title_update(); }; layers->on_layer_alpha_lock_changed = [this](Node*, int idx, bool locked) { canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_alpha_locked = locked; canvas->m_canvas->m_unsaved = true; title_update(); }; layers->on_layer_blend_mode_changed = [this](Node*, int idx, int mode) { canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_blend_mode = mode; canvas->m_canvas->m_unsaved = true; title_update(); }; layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) { canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]]->m_hightlight = highlight; }; if (auto* button = layout[main_id]->find("btn-stroke")) { button->on_click = [this, button](Node*) { panels->get_child_index(stroke.get()) == -1 ? panels->add_child(stroke) : panels->remove_child(stroke.get()); panels->fix_scroll(); button->set_active(panels->get_child_index(stroke.get()) != -1); }; } //if (auto* button = layout[main_id]->find("btn-brush")) //{ // button->on_click = [this, button](Node*) { // panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get()); // panels->fix_scroll(); // button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight); // }; //} //if (auto* button = layout[main_id]->find("btn-brush-preset")) //{ // button->on_click = [this, button](Node*) { // panels->get_child_index(presets.get()) == -1 ? panels->add_child(presets) : panels->remove_child(presets.get()); // panels->fix_scroll(); // button->set_color(panels->get_child_index(presets.get()) == -1 ? color_button_normal : color_button_hlight); // }; //} if (auto* button = layout[main_id]->find("btn-color")) { button->on_click = [this, button](Node*) { panels->get_child_index(color.get()) == -1 ? panels->add_child(color) : panels->remove_child(color.get()); panels->fix_scroll(); button->set_active(panels->get_child_index(color.get()) != -1); // auto pick = layout[main_id]->add_child(); // pick->m_color_cur->m_color = Canvas::I->m_current_brush->m_tip_color; }; } if (auto* button = layout[main_id]->find("btn-layer")) { button->on_click = [this, button](Node*) { panels->get_child_index(layers.get()) == -1 ? panels->add_child(layers) : panels->remove_child(layers.get()); panels->fix_scroll(); button->set_active(panels->get_child_index(layers.get()) != -1); }; } if (auto* button = layout[main_id]->find("btn-grids-panel")) { button->on_click = [this, button](Node*) { panels->get_child_index(grid.get()) == -1 ? panels->add_child(grid) : panels->remove_child(grid.get()); panels->fix_scroll(); button->set_active(panels->get_child_index(grid.get()) != -1); }; } } template void select_button(Node* main, T* button) { main->find("btn-pen")->set_active(false); main->find("btn-erase")->set_active(false); main->find("btn-line")->set_active(false); main->find("btn-cam")->set_active(false); main->find("btn-grid")->set_active(false); main->find("btn-copy")->set_active(false); main->find("btn-cut")->set_active(false); //main->find("btn-fill")->set_color(color_button_normal); main->find("btn-mask-free")->set_active(false); main->find("btn-mask-line")->set_active(false); button->set_active(false); }; void App::init_toolbar_draw() { if (auto* button = layout[main_id]->find("btn-pen")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::Draw); }; //button->set_active(true); Canvas::set_mode(kCanvasMode::Draw); } if (auto* button = layout[main_id]->find("btn-pick")) { button->on_click = [this](Node*) { CanvasModePen* mode = (CanvasModePen*)canvas->m_canvas->modes[(int)kCanvasMode::Draw][0]; if (mode && canvas->m_canvas->m_current_mode == kCanvasMode::Draw) { mode->m_picking = !mode->m_picking; } }; } if (auto* button = layout[main_id]->find("btn-touchlock")) { button->on_click = [this](Node*) { canvas->m_canvas->m_touch_lock = !canvas->m_canvas->m_touch_lock; }; } if (auto* button = layout[main_id]->find("btn-erase")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::Erase); }; } if (auto* button = layout[main_id]->find("btn-line")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::Line); }; } if (auto* button = layout[main_id]->find("btn-cam")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::Camera); }; } if (auto* button = layout[main_id]->find("btn-grid")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::Grid); }; } if (auto* button = layout[main_id]->find("btn-copy")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); auto m = static_cast(canvas->m_canvas->modes[(int)kCanvasMode::Copy][0]); m->m_action = CanvasModeTransform::ActionType::Copy; Canvas::set_mode(kCanvasMode::Copy); }; } if (auto* button = layout[main_id]->find("btn-cut")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); auto m = static_cast(canvas->m_canvas->modes[(int)kCanvasMode::Cut][0]); m->m_action = CanvasModeTransform::ActionType::Cut; Canvas::set_mode(kCanvasMode::Cut); }; } if (auto* button = layout[main_id]->find("btn-fill")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::Fill); }; } if (auto* button = layout[main_id]->find("btn-mask-free")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::MaskFree); }; } if (auto* button = layout[main_id]->find("btn-mask-line")) { button->on_click = [this, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::MaskLine); }; } if (auto* button = layout[main_id]->find("btn-bucket")) { button->on_click = [this](Node*) { canvas->m_canvas->clear(Canvas::I->m_current_brush->m_tip_color); }; } } void App::init_menu_file() { if (auto* menu_file = layout[main_id]->find("menu-file")) { menu_file->on_click = [=](Node*) { glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); auto popup = (NodePopupMenu*)layout[const_hash("file-menu")]->m_children[0]->clone(); popup->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup->m_size.x + menu_file->m_size.x; popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup); layout[main_id]->update(); popup->mouse_capture(); popup->m_mouse_ignore = false; popup->m_flood_events = true; popup->m_capture_children = false; if (auto b = popup->find("file-newdoc")) b->on_click = [this, popup](Node*) { dialog_newdoc(); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-import")) b->on_click = [this, popup](Node*) { App::I.pick_file({ "JPG", "PNG", "ABR" }, [this](std::string path){ std::string name, base, ext; std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return; base = m[1].str(); name = m[2].str(); ext = m[3].str(); if (str_iequals(ext, "abr")) { std::thread([this,path] { stroke->import_abr(path); }).detach(); } else { Image img; async_start(); img.load_file(path); if (img.width == img.height / 6 || img.width == img.height * 2) { Canvas::I->import_equirectangular(path); } else { auto m = static_cast(canvas->m_canvas->modes[(int)kCanvasMode::Import][0]); m->m_action = CanvasModeTransform::ActionType::Import; m->m_source_image = std::move(img); Canvas::set_mode(kCanvasMode::Import); } async_redraw(); async_end(); } }); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-open")) b->on_click = [this, popup](Node*) { //dialog_open(); App::I.pick_file({"ppi","PPI"}, [this](std::string path){ App::I.open_document(path); }); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-browse")) b->on_click = [this, popup](Node*) { dialog_browse(); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-save")) b->on_click = [this, popup](Node*) { if (Canvas::I->m_newdoc) { dialog_save(); } else if(Canvas::I->m_unsaved) { canvas->m_canvas->project_save(); } popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-save-as")) b->on_click = [this, popup](Node*) { dialog_save(); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-save-ver")) b->on_click = [this, popup](Node*) { Canvas::I->m_newdoc ? dialog_save() : dialog_save_ver(); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-export")) b->on_click = [this, popup](Node*) { dialog_export(".jpg"); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-export-tick")) b->on_click = [this, b, popup](Node*) { glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); auto subpopup = (NodePopupMenu*)layout[const_hash("file-submenu-export")]->m_children[0]->clone(); subpopup->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - subpopup->m_size.x + b->m_size.x; subpopup->SetPositioning(YGPositionTypeAbsolute); subpopup->SetPosition(pos.x, pos.y); layout[main_id]->add_child(subpopup); layout[main_id]->update(); subpopup->mouse_capture(); subpopup->m_mouse_ignore = false; subpopup->m_flood_events = true; subpopup->m_capture_children = false; subpopup->find("file-submenu-export-png")->on_click = [this, subpopup, popup](Node*) { dialog_export(".png"); subpopup->mouse_release(); subpopup->destroy(); popup->mouse_release(); popup->destroy(); }; subpopup->find("file-submenu-export-layers")->on_click = [this, subpopup, popup](Node*) { dialog_export_layers(); subpopup->mouse_release(); subpopup->destroy(); popup->mouse_release(); popup->destroy(); }; }; if (auto b = popup->find("file-share")) b->on_click = [this, popup](Node*) { share_file(doc_path); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-resize")) b->on_click = [this, popup](Node*) { dialog_resize(); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-export-cubes")) b->on_click = [this, popup](Node*) { dialog_export_cubes(); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-cloud-upload")) b->on_click = [this, popup](Node*) { cloud_upload(); popup->mouse_release(); popup->destroy(); }; if (auto b = popup->find("file-cloud-browse")) b->on_click = [this, popup](Node*) { cloud_browse(); popup->mouse_release(); popup->destroy(); }; }; } } void App::init_menu_edit() { if (auto* menu_file = layout[main_id]->find("menu-edit")) { menu_file->on_click = [=](Node*) { glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); auto popup = (NodePopupMenu*)layout[const_hash("edit-menu")]->m_children[0]->clone(); popup->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup->m_size.x + menu_file->m_size.x; popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup); layout[main_id]->update(); popup->mouse_capture(); popup->m_mouse_ignore = false; popup->m_flood_events = true; popup->m_capture_children = false; }; } } void App::init_menu_timelapse() { if (auto* menu_file = layout[main_id]->find("menu-timelapse")) { menu_file->on_click = [=](Node*) { glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); auto popup = (NodePopupMenu*)layout[const_hash("timelapse-menu")]->m_children[0]->clone(); popup->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup->m_size.x + menu_file->m_size.x; popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup); layout[main_id]->update(); popup->mouse_capture(); popup->m_mouse_ignore = false; popup->m_flood_events = true; popup->m_capture_children = false; if (auto item = popup->find("timelapse-start")) { if (auto text = popup->find("menu-label")) { text->set_text(App::I.rec_running ? "Stop Recording" : "Start Recording"); } } popup->find("timelapse-start")->on_click = [this, popup](Node*) { App::I.rec_running ? App::I.rec_stop() : App::I.rec_start(); popup->mouse_release(); popup->destroy(); }; popup->find("timelapse-clear")->on_click = [this, popup](Node*) { App::I.rec_clear(); popup->mouse_release(); popup->destroy(); }; popup->find("timelapse-export")->on_click = [this, popup](Node*) { popup->mouse_release(); popup->destroy(); App::I.rec_export(""); }; }; } } void App::init_menu_about() { if (auto* menu_file = layout[main_id]->find("menu-about")) { menu_file->on_click = [=](Node*) { glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); auto popup = (NodePopupMenu*)layout[const_hash("about-menu")]->m_children[0]->clone(); popup->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup->m_size.x + menu_file->m_size.x; popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup); layout[main_id]->update(); popup->mouse_capture(); popup->m_mouse_ignore = false; popup->m_flood_events = true; popup->m_capture_children = false; popup->find("about-app")->on_click = [this, popup](Node*) { dialog_about(); popup->mouse_release(); popup->destroy(); }; popup->find("about-doc")->on_click = [this, popup](Node*) { // auto path = Asset::absolute("data/doc/test.pdf"); // display_file(path); dialog_usermanual(); popup->mouse_release(); popup->destroy(); }; if (auto item = popup->find("about-news")) { if (auto text = item->find("menu-label")) { static char label[128]; sprintf(label, "What's new in %d.%d.%d?", g_version_major, g_version_minor, g_version_fix); text->set_text(label); } item->on_click = [this, popup](Node*) { dialog_changelog(); popup->mouse_release(); popup->destroy(); }; } if (auto b = popup->find("about-crash")) { b->on_click = [this, popup](Node*) { LOG("crashing"); App::I.crash_test(); popup->mouse_release(); popup->destroy(); }; } if (auto b = popup->find("about-perf")) { b->on_click = [this, popup](Node*) { LOG("perf"); auto start = std::chrono::high_resolution_clock::now(); Canvas::I->stroke_start({ 0, 0, 0 }, 0.9f); for (int i = 0; i < 100; i++) { Canvas::I->stroke_update({ 100, 100, 0 }, 0.9f); Canvas::I->stroke_update({ 200, 200, 0 }, 0.9f); Canvas::I->stroke_update({ 200, 100, 0 }, 0.9f); Canvas::I->stroke_update({ 100, 200, 0 }, 0.9f); Canvas::I->stroke_update({ 300, 300, 0 }, 0.9f); Canvas::I->stroke_update({ 200, 500, 0 }, 0.9f); Canvas::I->stroke_update({ 500, 500, 0 }, 0.9f); Canvas::I->stroke_update({ 400, 400, 0 }, 0.9f); Canvas::I->stroke_update({ 0, 200, 0 }, 0.9f); Canvas::I->stroke_update({ 200, 0, 0 }, 0.9f); Canvas::I->stroke_draw(); } Canvas::I->stroke_end(); auto diff = std::chrono::high_resolution_clock::now() - start; auto ms = std::chrono::duration_cast(diff).count(); LOG("%lld ms", ms); static char str[256]; sprintf(str, "Time %lld ms", ms); App::I.message_box("Performance test", str); popup->mouse_release(); popup->destroy(); }; } }; } } void App::brush_update() { // brushes->select_brush(canvas->m_brush->id); // stroke->set_params(canvas->m_brush); stroke->update_controls(); } void App::init_menu_layer() { if (auto* menu_file = layout[main_id]->find("menu-layers")) { menu_file->on_click = [=](Node*) { glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y); auto popup = (NodePopupMenu*)layout[const_hash("layers-menu")]->m_children[0]->clone(); popup->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup->m_size.x + menu_file->m_size.x; popup->SetPositioning(YGPositionTypeAbsolute); popup->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup); layout[main_id]->update(); popup->mouse_capture(); popup->m_mouse_ignore = false; popup->m_flood_events = true; popup->m_capture_children = false; popup->find("clear-grids")->on_click = [this, popup](Node*) { CanvasModeGrid* mode = (CanvasModeGrid*)Canvas::modes[(int)kCanvasMode::Grid][0]; mode->clear(); popup->mouse_release(); popup->destroy(); }; popup->find("camera-reset")->on_click = [this, popup](Node*) { canvas->reset_camera(); popup->mouse_release(); popup->destroy(); }; popup->find("layer-clear")->on_click = [this, popup](Node*) { canvas->m_canvas->clear(); popup->mouse_release(); popup->destroy(); }; if (layers->m_current_layer) popup->find("layer-clear")-> find("menu-label")-> set_text(("Clear Layer " + layers->m_current_layer->m_label_text).c_str()); popup->find("layer-rename")->on_click = [this, popup](Node*) { dialog_layer_rename(); popup->mouse_release(); popup->destroy(); }; if (layers->m_current_layer) popup->find("layer-rename")-> find("menu-label")-> set_text(("Rename Layer " + layers->m_current_layer->m_label_text).c_str()); else popup->find("layer-rename")-> find("menu-label")-> set_text("Rename Layer (Select a layer)"); popup->find("layer-merge")->on_click = [this, popup](Node*) { const auto& order = canvas->m_canvas->m_order; //layers->get_child_index(layers->) int current_idx_order = std::distance(order.begin(), std::find(order.begin(), order.end(), canvas->m_canvas->m_current_layer_idx)); if (current_idx_order > 0) { int dest_layer_idx = order[current_idx_order - 1]; canvas->m_canvas->layer_merge(canvas->m_canvas->m_current_layer_idx, dest_layer_idx); canvas->m_canvas->layer_remove(current_idx_order); layers->clear(); for (auto& i : canvas->m_canvas->m_order) layers->add_layer(canvas->m_canvas->m_layers[i]->m_name.c_str()); layers->m_current_layer->m_selected = false; layers->m_current_layer = layers->m_layers[current_idx_order - 1]; layers->m_current_layer->m_selected = true; layers->m_current_layer->on_selected(layers->m_current_layer); } popup->mouse_release(); popup->destroy(); }; if (layers->m_current_layer) { const auto& order = canvas->m_canvas->m_order; int current_idx_order = std::distance(order.begin(), std::find(order.begin(), order.end(), canvas->m_canvas->m_current_layer_idx)); if (current_idx_order > 0) { int down_layer_idx = order[current_idx_order - 1]; popup->find("layer-merge")-> find("menu-label")-> set_text(("Merge with " + canvas->m_canvas->m_layers[down_layer_idx]->m_name).c_str()); } else { popup->find("layer-merge")-> find("menu-label")-> set_text("Merge Layer (Select upper layers)"); } } else popup->find("layer-merge")-> find("menu-label")-> set_text("Merge Layer (Select a layer)"); }; } } void App::initLayout() { LOG("initializing layout statics"); NodeBorder::static_init(); NodeImage::static_init(); NodeIcon::static_init(); NodeStrokePreview::static_init(); layout.on_loaded = [&] { LOG("initializing layout updating after load"); layout[main_id]->update(width, height, zoom); LOG("initializing layout components"); init_sidebar(); canvas->m_canvas->layer_add("Default"); layers->add_layer("Default"); init_toolbar_draw(); init_toolbar_main(); init_menu_file(); init_menu_edit(); init_menu_layer(); init_menu_timelapse(); init_menu_about(); // set version string if (auto* version_label = layout[main_id]->find("version")) { version_label->set_text(g_version); } if (auto x = layout[main_id]->find("ext-fbf")) { x->m_color = ShaderManager::ext_framebuffer_fetch ? glm::vec4(0, 1, 0, 1) : glm::vec4(1, 0, 0, 1); } brush_update(); // hacky thing to make the toolbar buttons not steal events when moving cursor fast if (auto* toolbar = layout[main_id]->find("toolbar")) toolbar->m_flood_events = true; NodeImage* n = new NodeImage; n->m_path = "data/ui/p-black.png"; n->m_tex_id = const_hash("data/ui/p-black.png"); n->SetSize(30, 45); n->create(); NodeButtonCustom* butt = new NodeButtonCustom; butt->create(); butt->add_child(n); butt->SetPositioning(YGPositionTypeAbsolute); butt->set_color({ 0, 0, 0, 0 }); //n->SetPosition(100, 100); YGNodeStyleSetPosition(butt->y_node, YGEdgeBottom, 8); YGNodeStyleSetPosition(butt->y_node, YGEdgeLeft, 15); //butt->SetSize(30, 45); layout[main_id]->add_child(butt); butt->on_click = [this](Node*){ toggle_ui(); }; if (auto* slider = layout[main_id]->find("frames-slider")) { auto frame_text = layout[main_id]->find("timeline-frame"); slider->on_value_changed = [this, frame_text](Node*, float value) { auto& c = *Canvas::I; for (int i = 0; i < c.m_layers.size(); i++) { auto l = layers->get_layer_at(i); layers->handle_layer_opacity(l, .0f); } int current_layer = (int)glm::clamp( floor(value * c.m_layers.size()), 1, (int)c.m_layers.size() - 1); auto l = layers->get_layer_at(current_layer); layers->handle_layer_selected(l); layers->handle_layer_opacity(l, 1.f); if (current_layer > 0) { auto l = layers->get_layer_at(current_layer - 1); layers->handle_layer_opacity(l, .25f); } // First layer always visible { auto l = layers->get_layer_at(0); layers->handle_layer_opacity(l, 1.0f); } if (frame_text) { char str[16]; snprintf(str, sizeof(str), "%02d", current_layer); frame_text->set_text(str); } }; } App::I.redraw = true; }; LOG("initializing layout xml"); if (layout.m_loaded) { LOG("restore layout"); layout.restore_context(); //if (panels->get_child_index(brushes.get()) == -1) brushes->restore_context(); if (panels->get_child_index(layers.get()) == -1) layers->restore_context(); if (panels->get_child_index(color.get()) == -1) color->restore_context(); if (panels->get_child_index(stroke.get()) == -1) stroke->restore_context(); } else layout.load("data/layout.xml"); LOG("initializing layout completed"); }