#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" #include "node_panel_floating.h" #include "settings.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); if (auto node = layout[main_id]->find("txt-dpi")) node->set_text(fmt::format("{:.1f}x-dpi", zoom).c_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 create_panel(LayoutManager& manager) { std::shared_ptr ret; ret = std::make_shared(); ret->m_manager = &manager; ret->init(); ret->create(); ret->loaded(); return ret; } void App::init_sidebar() { sidebar = layout[main_id]->find("sidebar"); canvas = layout[main_id]->find("paint-canvas"); quick = layout[main_id]->find("panel-quick"); floatings_container = layout[main_id]->find("floatings"); //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 = create_panel(layout); color = create_panel(layout); stroke = create_panel(layout); grid = create_panel(layout); presets = create_panel(layout); //presets = find_or_create_panel(panels); canvas->m_canvas->on_mode_changed = [this](kCanvasMode prev, kCanvasMode mode) { quick_mode_state[prev] = quick->get_state(); if (quick_mode_state.find(mode) != quick_mode_state.end()) quick->set_state(quick_mode_state[mode], true); else quick->reset_state(true); brush_update(); }; color->on_color_changed = [this](Node* target, glm::vec4 color) { Canvas::I->m_current_brush->m_tip_color = color; quick->set_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); brush_update(); }; stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) { Canvas::I->m_current_brush->load_pattern(path, thumb); brush_update(); }; stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) { Canvas::I->m_current_brush->load_dual(path, thumb); brush_update(); }; stroke->on_stroke_change = [this](Node*) { brush_update(); }; quick->on_color_change = [this](Node*, glm::vec3 c) { Canvas::I->m_current_brush->m_tip_color = glm::vec4(c, 1.f); color->set_color(c); }; quick->on_flow_change = [this](Node*, float value) { stroke->set_flow(value, true, true); }; quick->on_size_change = [this](Node*, float value) { stroke->set_size(value, true, true); }; quick->on_brush_change = [this](Node*, std::shared_ptr b) { auto c = Canvas::I->m_current_brush->m_tip_color; *Canvas::I->m_current_brush = *b; Canvas::I->m_current_brush->m_tip_color = c; brush_update(); }; layers->on_layer_add = [this](Node*, std::shared_ptr layer, int index) { canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str(), layer, index); 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(), nullptr, source_index + 1); auto& dst = Canvas::I->m_layers[source_index + 1]; auto& src = Canvas::I->m_layers[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 = 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[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[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[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[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[idx]->m_hightlight = highlight; }; if (auto* button = layout[main_id]->find("btn-stroke")) { button->on_click = [this, button](Node*) { auto screen = layout[main_id]->m_size; glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); if (stroke->m_parent) { if (auto fp = dynamic_cast(stroke->m_parent->m_parent)) fp->destroy(); } layout[main_id]->add_child(stroke); stroke->SetSize(350, glm::max(100.f, screen.y - pos.y - 50.f)); auto tick = layout[main_id]->add_child(); tick->SetPositioning(YGPositionTypeAbsolute); tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); layout[main_id]->update(); stroke->SetPosition(pos.x - stroke->m_size.x / 2.f, pos.y + 16); stroke->SetPositioning(YGPositionTypeAbsolute); stroke->m_capture_children = false; stroke->m_mouse_ignore = false; stroke->mouse_capture(); auto scroll = stroke->find("scroller"); //scroll->SetHeight(glm::max(100.f, screen.y - pos.y - 200.f)); layout[main_id]->update(); stroke->on_popup_close = [this, tick](Node*) { tick->destroy(); }; }; } //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*) { auto screen = layout[main_id]->m_size; glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); layout[main_id]->add_child(color); color->SetSize(350, 350); auto tick = layout[main_id]->add_child(); tick->SetPositioning(YGPositionTypeAbsolute); tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); layout[main_id]->update(); color->SetPosition(pos.x - color->m_size.x / 2.f, pos.y + 16); color->SetPositioning(YGPositionTypeAbsolute); color->m_capture_children = false; color->m_mouse_ignore = false; color->mouse_capture(); layout[main_id]->update(); color->on_popup_close = [this, tick](Node*) { tick->destroy(); }; }; } if (auto* button = layout[main_id]->find("btn-layer")) { button->on_click = [this, button](Node*) { auto screen = layout[main_id]->m_size; glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); layers->SetSize(350, YGUndefined); if (layers->m_parent) { if (auto fp = dynamic_cast(layers->m_parent->m_parent)) fp->destroy(); } layout[main_id]->add_child(layers); auto tick = layout[main_id]->add_child(); tick->SetPositioning(YGPositionTypeAbsolute); tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); layout[main_id]->update(); layers->SetPosition(pos.x - layers->m_size.x / 2.f, pos.y + 16); layers->SetPositioning(YGPositionTypeAbsolute); layers->m_capture_children = false; layers->m_mouse_ignore = false; layers->mouse_capture(); auto scroll = layers->find("layers-container"); scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 200.f)); layout[main_id]->update(); layers->on_popup_close = [this, tick](Node*) { tick->destroy(); }; }; } if (auto* button = layout[main_id]->find("btn-grids-panel")) { button->on_click = [this, button](Node*) { auto screen = layout[main_id]->m_size; glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y); grid->SetSize(350, YGUndefined); if (grid->m_parent) { if (auto fp = dynamic_cast(grid->m_parent->m_parent)) fp->destroy(); } layout[main_id]->add_child(grid); auto tick = layout[main_id]->add_child(); tick->SetPositioning(YGPositionTypeAbsolute); tick->SetSize(32, 16); tick->SetPosition(pos.x - 16, pos.y); tick->set_image("data/ui/popup-tick-up.png"); layout[main_id]->update(); grid->SetPosition(pos.x - grid->m_size.x / 2.f, pos.y + 16); grid->SetPositioning(YGPositionTypeAbsolute); grid->m_capture_children = false; grid->m_mouse_ignore = false; grid->mouse_capture(); auto scroll = grid->find("scroller"); scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f)); layout[main_id]->update(); grid->on_popup_close = [this, tick](Node*) { tick->destroy(); }; }; } } 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); main->find("btn-bucket")->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")) { // polygon 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, button](Node*) { select_button(layout[main_id], button); Canvas::set_mode(kCanvasMode::FloodFill); }; } } 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*) { 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(); pick_file({"ppi","PPI"}, [this](std::string path){ 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(); }; subpopup->find("file-submenu-export-depth")->on_click = [this, subpopup, popup](Node*) { dialog_export_depth(); 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_tools() { auto main = layout[main_id]; if (auto menu_exp = main->find("menu-tools")) { menu_exp->on_click = [this, menu_exp, main](Node*) { glm::vec2 pos = menu_exp->m_pos + glm::vec2(0, menu_exp->m_size.y); auto popup_exp = (NodePopupMenu*)layout[const_hash("tools-menu")]->m_children[0]->clone(); popup_exp->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup_exp->m_size.x + menu_exp->m_size.x; popup_exp->SetPositioning(YGPositionTypeAbsolute); popup_exp->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup_exp); layout[main_id]->update(); popup_exp->mouse_capture(); popup_exp->m_mouse_ignore = false; popup_exp->m_flood_events = true; popup_exp->m_capture_children = false; if (auto tick = popup_exp->find("tools-timelapse-tick")) tick->on_click = [this, popup_exp](Node* b) { if (auto menu_time = popup_exp->find("tools-timelapse")) { glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); auto popup_time = (NodePopupMenu*)layout[const_hash("timelapse-menu")]->m_children[0]->clone(); popup_time->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup_time->m_size.x + b->m_size.x; popup_time->SetPositioning(YGPositionTypeAbsolute); popup_time->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup_time); layout[main_id]->update(); popup_time->mouse_capture(); popup_time->m_mouse_ignore = false; popup_time->m_flood_events = true; popup_time->m_capture_children = false; if (auto item = popup_time->find("timelapse-start")) { if (auto text = popup_time->find("menu-label")) { text->set_text(rec_running ? "Stop Recording" : "Start Recording"); } } popup_time->find("timelapse-start")->on_click = [this, popup_time, popup_exp](Node*) { rec_running ? rec_stop() : rec_start(); popup_time->destroy(); popup_exp->destroy(); }; popup_time->find("timelapse-clear")->on_click = [this, popup_time, popup_exp](Node*) { rec_clear(); popup_time->destroy(); popup_exp->destroy(); }; popup_time->find("timelapse-export")->on_click = [this, popup_time, popup_exp](Node*) { popup_time->destroy(); popup_exp->destroy(); rec_export(""); }; } }; if (auto tick = popup_exp->find("tools-panels-tick")) tick->on_click = [this, popup_exp](Node* b) { if (auto menu_time = popup_exp->find("tools-panels")) { glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0); auto popup_time = (NodePopupMenu*)layout[const_hash("panels-menu")]->m_children[0]->clone(); popup_time->update(); if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL) pos.x = pos.x - popup_time->m_size.x + b->m_size.x; popup_time->SetPositioning(YGPositionTypeAbsolute); popup_time->SetPosition(pos.x, pos.y); layout[main_id]->add_child(popup_time); layout[main_id]->update(); popup_time->mouse_capture(); popup_time->m_mouse_ignore = false; popup_time->m_flood_events = true; popup_time->m_capture_children = false; auto visible = [this](Node* panel) { if (!panel) return false; for (auto& c : floatings_container->m_children) { if (auto fp = std::static_pointer_cast(c)) { if (fp->m_container->is_child(panel)) return true; } } return false; }; popup_time->find("panel-presets")->on_click = [this, popup_time, popup_exp, visible](Node*) { if (visible(floating_presets.get())) return; auto fpanel = floatings_container->add_child(); fpanel->m_class = NodePanelFloating::kClass::Presets; fpanel->SetHeight(300); fpanel->SetMinHeight(300); if (!floating_presets) { floating_presets = fpanel->m_container->add_child_ref(); floating_presets->SetHeightP(100); //floating_presets->SetFlexGrow(1); floating_presets->find("toolbar")->destroy(); floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr& b) { auto c = Canvas::I->m_current_brush->m_tip_color; *Canvas::I->m_current_brush = *b; Canvas::I->m_current_brush->m_tip_color = c; Canvas::I->m_current_brush->load(); brush_update(); }; } else { fpanel->m_container->add_child(floating_presets); } popup_time->destroy(); popup_exp->destroy(); }; popup_time->find("panel-color")->on_click = [this, popup_time, popup_exp, visible](Node*) { if (visible(floating_color.get())) return; auto fpanel = floatings_container->add_child(); fpanel->m_class = NodePanelFloating::kClass::Color; fpanel->SetHeight(300); fpanel->SetMinHeight(300); if (!floating_color) { floating_color = fpanel->m_container->add_child_ref(); floating_color->SetHeightP(100); //floating_color->SetMinHeight(300); floating_color->find("title")->destroy(); floating_color->on_color_changed = [this](Node* target, glm::vec4 color) { Canvas::I->m_current_brush->m_tip_color = color; brush_update(); }; } else { fpanel->m_container->add_child(floating_color); } popup_time->destroy(); popup_exp->destroy(); }; popup_time->find("panel-color-adv")->on_click = [this, popup_time, popup_exp, visible](Node*) { if (visible(floating_picker.get())) return; auto fpanel = floatings_container->add_child(); fpanel->m_class = NodePanelFloating::kClass::ColorAdv; fpanel->SetHeight(300); fpanel->SetWidth(300); if (!floating_picker) { floating_picker = fpanel->m_container->add_child_ref(); //floating_picker->SetHeightP(100); //floating_picker->SetWidth(250); floating_picker->m_autohide = false; floating_picker->on_color_change = [this](Node* target, glm::vec3 color) { Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f); brush_update(); }; } else { fpanel->m_container->add_child(floating_picker); } popup_time->destroy(); popup_exp->destroy(); }; popup_time->find("panel-layers")->on_click = [this, popup_time, popup_exp, visible](Node*) { if (visible(layers.get())) return; auto fpanel = floatings_container->add_child(); fpanel->m_class = NodePanelFloating::kClass::Layers; fpanel->SetMinHeight(100); fpanel->SetHeight(300); fpanel->m_container->add_child(layers); layers->SetPositioning(YGPositionTypeRelative); layers->SetPosition(0, 0); layers->SetWidthP(100); layers->SetHeightP(100); layers->SetFlexShrink(0); popup_time->destroy(); popup_exp->destroy(); }; popup_time->find("panel-brush")->on_click = [this, popup_time, popup_exp, visible](Node*) { if (visible(stroke.get())) return; auto fpanel = floatings_container->add_child(); fpanel->m_class = NodePanelFloating::kClass::Brush; fpanel->m_container->add_child(stroke); fpanel->SetHeight(300); stroke->SetPositioning(YGPositionTypeRelative); stroke->SetPosition(0, 0); stroke->SetWidthP(100); stroke->SetHeightP(100); popup_time->destroy(); popup_exp->destroy(); }; popup_time->find("panel-grids")->on_click = [this, popup_time, popup_exp, visible](Node*) { if (visible(grid.get())) return; auto fpanel = floatings_container->add_child(); fpanel->m_class = NodePanelFloating::kClass::Grids; fpanel->m_container->add_child(grid); fpanel->SetHeight(300); grid->SetPositioning(YGPositionTypeRelative); grid->SetPosition(0, 0); grid->SetWidthP(100); grid->SetHeightP(100); popup_time->destroy(); popup_exp->destroy(); }; } }; if (auto rtl_btn = popup_exp->find("tools-rtl")) { NodeCheckBox* cb = rtl_btn->find("tools-rtl-check"); cb->set_value(ui_rtl, false); rtl_btn->on_click = [this, popup_exp, rtl_btn](Node* b) { NodeCheckBox* cb = rtl_btn->find("tools-rtl-check"); cb->set_value(!cb->checked, true); }; rtl_btn->find("tools-rtl-check")->on_value_changed = [this, main](Node*, bool checked) { set_ui_rtl(checked); }; } if (auto vr_btn = popup_exp->find("tools-vr")) { NodeCheckBox* cb = vr_btn->find("tools-vr-check"); cb->set_value(has_vr); vr_btn->on_click = [this, popup_exp, vr_btn](Node* b) { NodeCheckBox* cb = vr_btn->find("tools-vr-check"); cb->set_value(!cb->checked, true); }; vr_btn->find("tools-vr-check")->on_value_changed = [this, main](Node* target, bool checked) { if (checked) { if (!vr_start()) { auto cb = static_cast(target); cb->set_value(false); message_box("VR Failed", "Couldn't start Virtual Reality mode"); } } else { vr_stop(); } }; } popup_exp->find("clear-grids")->on_click = [this, popup_exp](Node*) { CanvasModeGrid* mode = (CanvasModeGrid*)Canvas::modes[(int)kCanvasMode::Grid][0]; mode->clear(); popup_exp->mouse_release(); popup_exp->destroy(); }; popup_exp->find("camera-reset")->on_click = [this, popup_exp](Node*) { canvas->reset_camera(); popup_exp->mouse_release(); popup_exp->destroy(); }; popup_exp->find("clear-presets")->on_click = [this, popup_exp](Node*) { auto mb = message_box("Clear Presets", "Do you want to remove all the Brush Presets?", true); mb->btn_ok->m_text->set_text("Yes"); mb->btn_cancel->m_text->set_text("No"); mb->btn_ok->on_click = mb->on_submit = [this, mb](Node*) { presets->clear_brushes(); presets->save(); mb->destroy(); }; popup_exp->mouse_release(); popup_exp->destroy(); }; }; } } 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"); 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); 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); render_task_async([this] { stroke->update_controls(); quick->m_slider_flow->set_value(stroke->m_tip_flow->get_value()); quick->m_slider_size->set_value(stroke->m_tip_size->get_value()); *quick->m_button_brush_current_preview->m_brush = *Canvas::I->m_current_brush; quick->m_button_brush_current_preview->draw_stroke(); quick->m_button_color_current_inner->m_color = Canvas::I->m_current_brush->m_tip_color; if (floating_picker) floating_picker->set_color(Canvas::I->m_current_brush->m_tip_color); if (floating_color) floating_color->set_color(Canvas::I->m_current_brush->m_tip_color); }); } 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("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*) { //layers->get_child_index(layers->) int current_idx_order = Canvas::I->m_current_layer_idx; if (current_idx_order > 0) { layers->merge(current_idx_order, current_idx_order - 1, true); } popup->mouse_release(); popup->destroy(); }; if (layers->m_current_layer) { int current_idx_order = canvas->m_canvas->m_current_layer_idx; if (current_idx_order > 0) { int down_layer_idx = 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_reloading = [&] { ui_save(); NodeStrokePreview::empty_queue(); }; layout.on_loaded = [&] { LOG("initializing layout updating after load"); layout[main_id]->update(width, height, zoom); LOG("initializing layout components"); init_sidebar(); layers->add_layer("Default", false, true); init_toolbar_draw(); init_toolbar_main(); init_menu_file(); init_menu_edit(); init_menu_layer(); init_menu_tools(); 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, 10); //butt->SetSize(30, 45); layout[main_id]->add_child(butt); butt->on_click = [this](Node*){ toggle_ui(); }; if (auto* timeline = layout[main_id]->find("timeline")) { 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); } }; } timeline->destroy(); } /* // test floating panel auto fp_presets = layout[main_id]->add_child(); floating_presets = fp_presets->m_container->add_child(); floating_presets->SetHeightP(100); floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr& b) { auto c = Canvas::I->m_current_brush->m_tip_color; *Canvas::I->m_current_brush = *b; Canvas::I->m_current_brush->m_tip_color = c; Canvas::I->m_current_brush->load(); brush_update(); }; auto fp_color = layout[main_id]->add_child(); floating_color = fp_color->m_container->add_child(); floating_color->SetHeightP(100); floating_color->find("title")->destroy(); floating_color->on_color_changed = [this](Node* target, glm::vec4 color) { Canvas::I->m_current_brush->m_tip_color = color; brush_update(); }; auto fp_picker = layout[main_id]->add_child(); fp_picker->m_container->SetSize(300, 500); floating_picker = fp_picker->m_container->add_child(); floating_picker->m_autohide = false; floating_picker->on_color_change = [this](Node* target, glm::vec3 color) { Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f); brush_update(); }; //picker->SetHeightP(100); //color->find("title")->destroy(); */ ui_restore(); redraw = true; }; LOG("initializing layout xml"); if (layout.m_loaded) { LOG("restore layout"); layout.restore_context(); } else layout.load("data/layout.xml"); LOG("initializing layout completed"); } void App::set_ui_rtl(bool rtl) { ui_rtl = rtl; layout[main_id]->find("central-row")->SetRTL(rtl ? YGDirectionRTL : YGDirectionLTR); } bool App::get_ui_rtl() const { return ui_rtl; } void App::ui_save() { Serializer::Descriptor d; d.class_id = "ui-state"; Serializer::List list_floatings; for (auto const& c : layout[main_id]->find("floatings")->m_children) { if (auto const& f = std::dynamic_pointer_cast(c)) { auto fd = list_floatings.add(); fd->class_id = "ui-flt"; fd->set("pos", Serializer::Vec2(f->GetPosition())); fd->set("size", Serializer::Vec2(f->m_size)); fd->set("class", Serializer::Integer((int)f->m_class)); } } d.set("floatings", list_floatings); Serializer::List list_drop_left; for (auto const& c : layout[main_id]->find("drop-left")->m_children) { if (auto const& f = std::dynamic_pointer_cast(c)) { auto fd = list_drop_left.add(); fd->class_id = "ui-dpl"; fd->set("size", Serializer::Vec2(f->m_size)); fd->set("class", Serializer::Integer((int)f->m_class)); } } d.set("drop-left", list_drop_left); Serializer::List list_drop_right; for (auto const& c : layout[main_id]->find("drop-right")->m_children) { if (auto const& f = std::dynamic_pointer_cast(c)) { auto fd = list_drop_right.add(); fd->class_id = "ui-dpr"; fd->set("size", Serializer::Vec2(f->m_size)); fd->set("class", Serializer::Integer((int)f->m_class)); } } d.set("drop-right", list_drop_right); Settings::set("ui", d); Settings::set("ui-rtl", Serializer::Boolean(ui_rtl)); #if _WIN32 extern void win32_save_window_state(); win32_save_window_state(); #endif Settings::save(); } void App::ui_restore() { if (Settings::has("ui-rtl")) set_ui_rtl(Settings::value("ui-rtl")); if (!Settings::has("ui")) return; auto floatings = layout[main_id]->find_ref("floatings"); auto drop_left = layout[main_id]->find_ref("drop-left"); auto drop_right = layout[main_id]->find_ref("drop-right"); auto d = Settings::get("ui"); for (auto const& l : d->get("floatings")->items) { auto ld = std::static_pointer_cast(l); auto pos = ld->value("pos"); auto size = ld->value("size"); auto cls = static_cast(ld->value("class")); auto f = floatings->add_child(); switch (cls) { case NodePanelFloating::kClass::Presets: { auto floating_presets = f->m_container->add_child(); floating_presets->SetHeightP(100); floating_presets->find("toolbar")->destroy(); floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr& b) { auto c = Canvas::I->m_current_brush->m_tip_color; *Canvas::I->m_current_brush = *b; Canvas::I->m_current_brush->m_tip_color = c; Canvas::I->m_current_brush->load(); brush_update(); }; break; } case NodePanelFloating::kClass::Color: { auto floating_color = f->m_container->add_child(); floating_color->SetHeightP(100); floating_color->find("title")->destroy(); floating_color->on_color_changed = [this](Node* target, glm::vec4 color) { Canvas::I->m_current_brush->m_tip_color = color; brush_update(); }; break; } case NodePanelFloating::kClass::ColorAdv: { floating_picker = f->m_container->add_child_ref(); floating_picker->m_autohide = false; floating_picker->on_color_change = [this](Node* target, glm::vec3 color) { Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f); brush_update(); }; break; } case NodePanelFloating::kClass::Layers: f->m_container->add_child(layers); f->SetMinHeight(100); f->SetHeight(300); layers->SetPositioning(YGPositionTypeRelative); layers->SetPosition(0, 0); layers->SetWidthP(100); layers->SetHeightP(100); layers->SetFlexShrink(0); break; case NodePanelFloating::kClass::Brush: f->m_container->add_child(stroke); stroke->SetPositioning(YGPositionTypeRelative); stroke->SetPosition(0, 0); stroke->SetWidthP(100); stroke->SetHeightP(100); break; case NodePanelFloating::kClass::Grids: f->m_container->add_child(grid); grid->SetPositioning(YGPositionTypeRelative); grid->SetPosition(0, 0); grid->SetWidthP(100); grid->SetHeightP(100); break; case NodePanelFloating::kClass::Generic: default: f->m_container->add_child(); break; } f->m_class = cls; f->SetSize(size); f->SetPosition(pos); f->SetPositioning(YGPositionTypeAbsolute); } for (auto const& l : d->get("drop-left")->items) { auto ld = std::static_pointer_cast(l); auto size = ld->value("size"); auto cls = static_cast(ld->value("class")); auto f = drop_left->add_child(); switch (cls) { case NodePanelFloating::kClass::Presets: { auto floating_presets = f->m_container->add_child(); floating_presets->SetHeightP(100); floating_presets->find("toolbar")->destroy(); floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr& b) { auto c = Canvas::I->m_current_brush->m_tip_color; *Canvas::I->m_current_brush = *b; Canvas::I->m_current_brush->m_tip_color = c; Canvas::I->m_current_brush->load(); brush_update(); }; break; } case NodePanelFloating::kClass::Color: { auto floating_color = f->m_container->add_child(); floating_color->SetHeightP(100); floating_color->find("title")->destroy(); floating_color->on_color_changed = [this](Node* target, glm::vec4 color) { Canvas::I->m_current_brush->m_tip_color = color; brush_update(); }; break; } case NodePanelFloating::kClass::ColorAdv: { floating_picker = f->m_container->add_child_ref(); floating_picker->m_autohide = false; floating_picker->on_color_change = [this](Node* target, glm::vec3 color) { Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f); brush_update(); }; break; } case NodePanelFloating::kClass::Layers: f->m_container->add_child(layers); layers->SetPositioning(YGPositionTypeRelative); layers->SetPosition(0, 0); layers->SetWidthP(100); layers->SetHeightP(100); layers->SetFlexShrink(0); break; case NodePanelFloating::kClass::Brush: f->m_container->add_child(stroke); stroke->SetPositioning(YGPositionTypeRelative); stroke->SetPosition(0, 0); stroke->SetWidthP(100); stroke->SetHeightP(100); break; case NodePanelFloating::kClass::Grids: f->m_container->add_child(grid); grid->SetPositioning(YGPositionTypeRelative); grid->SetPosition(0, 0); grid->SetWidthP(100); grid->SetHeightP(100); break; case NodePanelFloating::kClass::Generic: default: f->m_container->add_child(); break; } f->m_class = cls; f->m_dock = drop_left; f->SetPositioning(YGPositionTypeRelative); f->SetPosition(0, 0); f->SetSize(size); } for (auto const& l : d->get("drop-right")->items) { auto ld = std::static_pointer_cast(l); auto size = ld->value("size"); auto cls = static_cast(ld->value("class")); auto f = drop_right->add_child(); switch (cls) { case NodePanelFloating::kClass::Presets: { auto floating_presets = f->m_container->add_child(); floating_presets->SetHeightP(100); floating_presets->find("toolbar")->destroy(); floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr& b) { auto c = Canvas::I->m_current_brush->m_tip_color; *Canvas::I->m_current_brush = *b; Canvas::I->m_current_brush->m_tip_color = c; Canvas::I->m_current_brush->load(); brush_update(); }; break; } case NodePanelFloating::kClass::Color: { auto floating_color = f->m_container->add_child(); floating_color->SetHeightP(100); floating_color->find("title")->destroy(); floating_color->on_color_changed = [this](Node* target, glm::vec4 color) { Canvas::I->m_current_brush->m_tip_color = color; brush_update(); }; break; } case NodePanelFloating::kClass::ColorAdv: { floating_picker = f->m_container->add_child_ref(); floating_picker->m_autohide = false; floating_picker->on_color_change = [this](Node* target, glm::vec3 color) { Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f); brush_update(); }; break; } case NodePanelFloating::kClass::Layers: f->m_container->add_child(layers); layers->SetPositioning(YGPositionTypeRelative); layers->SetPosition(0, 0); layers->SetWidthP(100); layers->SetHeightP(100); layers->SetFlexShrink(0); break; case NodePanelFloating::kClass::Brush: f->m_container->add_child(stroke); stroke->SetPositioning(YGPositionTypeRelative); stroke->SetPosition(0, 0); stroke->SetWidthP(100); stroke->SetHeightP(100); break; case NodePanelFloating::kClass::Grids: f->m_container->add_child(grid); grid->SetPositioning(YGPositionTypeRelative); grid->SetPosition(0, 0); grid->SetWidthP(100); grid->SetHeightP(100); break; case NodePanelFloating::kClass::Generic: default: f->m_container->add_child(); break; } f->m_class = cls; f->m_dock = drop_right; f->SetPositioning(YGPositionTypeRelative); f->SetPosition(0, 0); f->SetSize(size); } }