Files
panopainter/src/app_layout.cpp

1715 lines
70 KiB
C++

#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"
#include "serializer.h"
#include "font.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<NodeText>("txt-docname"))
docname->set_text(str);
if (auto node = layout[main_id]->find<NodeText>("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<NodeButton>("btn-anim"))
{
button->on_click = [this, button](Node*) {
if (canvas)
{
//canvas->m_canvas->export_anim();
}
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-open"))
{
button->on_click = [this, button](Node*) {
dialog_open();
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-save"))
{
button->on_click = [this, button](Node*) {
dialog_save();
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-undo"))
{
button->on_click = [this, button](Node*) {
ActionManager::undo();
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-redo"))
{
button->on_click = [this, button](Node*) {
ActionManager::redo();
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-clean-memory"))
{
button->on_click = [this](Node*) {
ActionManager::clear();
};
}
if (auto* button = layout[main_id]->find<NodeButton>("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<NodeButton>("btn-popup"))
{
button->on_click = [this](Node*) {
msgbox = new NodeMessageBox();
msgbox->set_manager(&layout);
msgbox->init();
layout[main_id]->add_child(msgbox);
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-settings"))
{
button->on_click = [this](Node*) {
settings = new NodeSettings();
settings->set_manager(&layout);
settings->init();
layout[main_id]->add_child(settings);
};
}
}
template <class T> std::shared_ptr<T> create_panel(LayoutManager& manager)
{
std::shared_ptr<T> ret;
ret = std::make_shared<T>();
ret->set_manager(&manager);
ret->init();
ret->create();
ret->loaded();
return ret;
}
void App::init_sidebar()
{
sidebar = layout[main_id]->find<NodeBorder>("sidebar");
canvas = layout[main_id]->find<NodeCanvas>("paint-canvas");
quick = layout[main_id]->find<NodePanelQuick>("panel-quick");
floatings_container = layout[main_id]->find<Node>("floatings");
//brushes = layout[main_id]->find<NodePanelBrush>("panel-brush");
//layers = layout[main_id]->find<NodePanelLayer>("panel-layer");
//color = layout[main_id]->find<NodePanelColor>("panel-color");
//stroke = layout[main_id]->find<NodePanelStroke>("panel-stroke");
//brushes = find_or_create_panel<NodePanelBrush>(panels);
layers = create_panel<NodePanelLayer>(layout);
color = create_panel<NodePanelColor>(layout);
stroke = create_panel<NodePanelStroke>(layout);
grid = create_panel<NodePanelGrid>(layout);
presets = create_panel<NodePanelBrushPreset>(layout);
animation = create_panel<NodePanelAnimation>(layout);
//presets = find_or_create_panel<NodePanelBrushPreset>(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(true, true);
};
color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
quick->set_color(color);
brush_update(true, false);
};
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(true, true);
};
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(true, true);
};
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(true, true);
};
stroke->on_stroke_change = [this](Node*) {
brush_update(true, true);
};
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<Brush> 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(true, true);
};
layers->on_layer_add = [this](Node*, std::shared_ptr<class Layer> 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;
animation->load_layers();
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->face(i))
continue;
dst->rtt(i).copy(src->rtt(i));
dst->face(i) = src->face(i);
dst->box(i) = src->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;
animation->load_layers();
title_update();
};
layers->on_layer_change = [this](Node*, int old_idx, int new_idx) {
canvas->m_canvas->m_current_layer_idx = new_idx;
animation->load_layers();
};
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;
animation->load_layers();
title_update();
};
layers->on_layer_delete = [this](Node*, int idx) {
canvas->m_canvas->layer_remove(idx);
canvas->m_canvas->m_unsaved = true;
animation->load_layers();
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;
animation->load_layers();
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<NodeButtonCustom>("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<NodePanelFloating*>(stroke->m_parent->m_parent))
{
stroke->remove_from_parent();
fp->destroy();
}
}
layout[main_id]->add_child(stroke);
stroke->SetSize(350, glm::max(100.f, screen.y - pos.y - 50.f));
stroke->find("title")->SetVisibility(true);
auto tick = layout[main_id]->add_child<NodeImage>();
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<NodeScroll>("scroller");
//scroll->SetHeight(glm::max(100.f, screen.y - pos.y - 200.f));
stroke->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
};
}
//if (auto* button = layout[main_id]->find<NodeButtonCustom>("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<NodeButton>("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<NodeButtonCustom>("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->find("title")->SetVisibility(true);
color->SetSize(350, 350);
auto tick = layout[main_id]->add_child<NodeImage>();
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();
color->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("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->find("title")->SetVisibility(true);
layers->SetSize(350, YGUndefined);
if (layers->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(layers->m_parent->m_parent))
{
layers->remove_from_parent();
fp->destroy();
}
}
layout[main_id]->add_child(layers);
auto tick = layout[main_id]->add_child<NodeImage>();
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<NodeScroll>("layers-container");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 200.f));
layers->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("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->find("title")->SetVisibility(true);
grid->SetSize(350, YGUndefined);
if (grid->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(grid->m_parent->m_parent))
{
grid->remove_from_parent();
fp->destroy();
}
}
layout[main_id]->add_child(grid);
auto tick = layout[main_id]->add_child<NodeImage>();
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<NodeScroll>("scroller");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f));
grid->on_popup_close = [this, tick](Node*) {
tick->destroy();
};
};
}
}
template<class T>
void select_button(Node* main, T* button) {
main->find<NodeButtonCustom>("btn-pen")->set_active(false);
main->find<NodeButtonCustom>("btn-erase")->set_active(false);
main->find<NodeButtonCustom>("btn-line")->set_active(false);
main->find<NodeButton>("btn-cam")->set_active(false);
main->find<NodeButton>("btn-grid")->set_active(false);
main->find<NodeButton>("btn-copy")->set_active(false);
main->find<NodeButton>("btn-cut")->set_active(false);
//main->find<NodeButton>("btn-fill")->set_color(color_button_normal);
main->find<NodeButtonCustom>("btn-mask-free")->set_active(false);
main->find<NodeButtonCustom>("btn-mask-line")->set_active(false);
main->find<NodeButtonCustom>("btn-bucket")->set_active(false);
button->set_active(false);
};
void App::init_toolbar_draw()
{
if (auto* button = layout[main_id]->find<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButton>("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<NodeButton>("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<NodeButton>("btn-copy"))
{
button->on_click = [this, button](Node*) {
select_button(layout[main_id], button);
auto m = static_cast<CanvasModeTransform*>(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<NodeButton>("btn-cut"))
{
button->on_click = [this, button](Node*) {
select_button(layout[main_id], button);
auto m = static_cast<CanvasModeTransform*>(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<NodeButton>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("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<NodeButtonCustom>("menu-file"))
{
menu_file->on_click = [=](Node*) {
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
auto popup = layout[const_hash("file-menu")]->m_children[0]->clone<NodePopupMenu>();
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);
if (auto b = popup->find<NodeButtonCustom>("file-newdoc"))
b->on_click = [this, popup](Node*) {
dialog_newdoc();
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("file-import"))
b->on_click = [this, popup](Node*) {
pick_image([this](std::string path){
Image img;
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<CanvasModeTransform*>(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);
}
});
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("file-open"))
b->on_click = [this, popup](Node*) {
//dialog_open();
pick_file({"ppi"}, [this](std::string path){
open_document(path);
});
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("file-browse"))
b->on_click = [this, popup](Node*) {
dialog_browse();
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("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<NodeButtonCustom>("file-save-as"))
b->on_click = [this, popup](Node*) {
dialog_save();
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("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<NodeButtonCustom>("file-export"))
b->on_click = [this, popup](Node*) {
dialog_export(".jpg");
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("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 = layout[const_hash("file-submenu-export")]->m_children[0]->clone<NodePopupMenu>();
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);
subpopup->find<NodeButtonCustom>("file-submenu-export-png")->on_click = [this, subpopup, popup](Node*) {
dialog_export(".png");
popup->mouse_release();
popup->destroy();
subpopup->mouse_release();
subpopup->destroy();
};
subpopup->find<NodeButtonCustom>("file-submenu-export-layers")->on_click = [this, subpopup, popup](Node*) {
dialog_export_layers();
popup->mouse_release();
popup->destroy();
subpopup->mouse_release();
subpopup->destroy();
};
subpopup->find<NodeButtonCustom>("file-submenu-export-cube")->on_click = [this, subpopup, popup](Node*) {
dialog_export_cube_faces();
popup->mouse_release();
popup->destroy();
subpopup->mouse_release();
subpopup->destroy();
};
subpopup->find<NodeButtonCustom>("file-submenu-export-depth")->on_click = [this, subpopup, popup](Node*) {
dialog_export_depth();
popup->mouse_release();
popup->destroy();
subpopup->mouse_release();
subpopup->destroy();
};
subpopup->find<NodeButtonCustom>("file-submenu-export-anim")->on_click = [this, subpopup, popup](Node*) {
dialog_export_layers();
popup->mouse_release();
popup->destroy();
subpopup->mouse_release();
subpopup->destroy();
};
subpopup->find<NodeButtonCustom>("file-submenu-export-timelapse")->on_click = [this, subpopup, popup](Node*) {
dialog_timelapse_export();
popup->mouse_release();
popup->destroy();
subpopup->mouse_release();
subpopup->destroy();
};
};
if (auto b = popup->find<NodeButtonCustom>("file-share"))
b->on_click = [this, popup](Node*) {
share_file(doc_path);
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("file-resize"))
b->on_click = [this, popup](Node*) {
dialog_resize();
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("file-cloud-upload"))
b->on_click = [this, popup](Node*) {
cloud_upload();
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("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<NodeButtonCustom>("menu-edit"))
{
menu_file->on_click = [=](Node*) {
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
auto popup = layout[const_hash("edit-menu")]->m_children[0]->clone<NodePopupMenu>();
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);
};
}
}
void App::init_menu_tools()
{
auto main = layout[main_id];
if (auto menu_exp = main->find<NodeButtonCustom>("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 = layout[const_hash("tools-menu")]->m_children[0]->clone<NodePopupMenu>();
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);
if (auto tick = popup_exp->find<NodeButtonCustom>("tools-timelapse-tick")) tick->on_click = [this, popup_exp](Node* b)
{
if (auto menu_time = popup_exp->find<NodePopupMenu>("tools-timelapse"))
{
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
auto popup_time = layout[const_hash("timelapse-menu")]->m_children[0]->clone<NodePopupMenu>();
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);
if (auto item = popup_time->find<NodeButtonCustom>("timelapse-start"))
{
if (auto text = popup_time->find<NodeText>("menu-label"))
{
text->set_text(rec_running ? "Stop Recording" : "Start Recording");
}
}
popup_time->find<NodeButtonCustom>("timelapse-start")->on_click = [this, popup_time, popup_exp](Node*) {
rec_running ? rec_stop() : rec_start();
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("timelapse-clear")->on_click = [this, popup_time, popup_exp](Node*) {
rec_clear();
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("timelapse-export")->on_click = [this, popup_time, popup_exp](Node*) {
rec_export("");
popup_exp->destroy();
popup_time->destroy();
};
}
};
if (auto tick = popup_exp->find<NodeButtonCustom>("tools-panels-tick")) tick->on_click = [this, popup_exp](Node* b)
{
if (auto menu_time = popup_exp->find<NodePopupMenu>("tools-panels"))
{
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
auto popup_time = layout[const_hash("panels-menu")]->m_children[0]->clone<NodePopupMenu>();
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);
auto visible = [this](Node* panel) {
if (!panel)
return false;
for (auto& c : floatings_container->m_children)
{
if (auto fp = std::static_pointer_cast<NodePanelFloating>(c))
{
if (fp->m_container->is_child(panel))
return true;
}
}
return false;
};
popup_time->find<NodeButtonCustom>("panel-presets")->on_click = [this, popup_time, popup_exp, visible](Node*) {
if (visible(floating_presets.get()))
return;
auto fpanel = floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Presets;
fpanel->SetHeight(300);
fpanel->SetMinHeight(300);
fpanel->SetMinWidth(100);
fpanel->m_title->set_text("Brushes");
if (!floating_presets)
{
floating_presets = fpanel->m_container->add_child_ref<NodePanelBrushPreset>();
floating_presets->SetHeightP(100);
//floating_presets->SetFlexGrow(1);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& 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(true, true);
};
}
else
{
fpanel->m_container->add_child(floating_presets);
}
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("panel-color")->on_click = [this, popup_time, popup_exp, visible](Node*) {
if (visible(floating_color.get()))
return;
auto fpanel = floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Color;
fpanel->SetHeight(300);
fpanel->SetMinHeight(300);
fpanel->m_title->set_text("Color Picker");
if (!floating_color)
{
floating_color = fpanel->m_container->add_child_ref<NodePanelColor>();
floating_color->SetHeightP(100);
//floating_color->SetMinHeight(300);
floating_color->find("title")->SetVisibility(false);
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
brush_update(true, false);
};
}
else
{
fpanel->m_container->add_child(floating_color);
}
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("panel-color-adv")->on_click = [this, popup_time, popup_exp, visible](Node*) {
if (visible(floating_picker.get()))
return;
auto fpanel = floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::ColorAdv;
fpanel->SetHeight(300);
fpanel->SetWidth(300);
fpanel->m_title->set_text("Color Picker");
if (!floating_picker)
{
floating_picker = fpanel->m_container->add_child_ref<NodeColorPicker>();
//floating_picker->find("title")->SetVisibility(false);
//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(true, false);
};
}
else
{
fpanel->m_container->add_child(floating_picker);
}
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("panel-layers")->on_click = [this, popup_time, popup_exp, visible](Node*) {
if (visible(layers.get()))
return;
auto fpanel = floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Layers;
fpanel->SetMinHeight(100);
fpanel->SetHeight(300);
fpanel->m_container->add_child(layers);
fpanel->m_title->set_text("Layers");
layers->SetPositioning(YGPositionTypeRelative);
layers->SetPosition(0, 0);
layers->SetWidthP(100);
layers->SetHeightP(100);
layers->SetFlexShrink(0);
layers->find("title")->SetVisibility(false);
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("panel-brush")->on_click = [this, popup_time, popup_exp, visible](Node*) {
if (visible(stroke.get()))
return;
auto fpanel = floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Brush;
fpanel->m_container->add_child(stroke);
fpanel->SetHeight(300);
fpanel->m_title->set_text("Brush Settings");
stroke->SetPositioning(YGPositionTypeRelative);
stroke->SetPosition(0, 0);
stroke->SetWidthP(100);
stroke->SetHeightP(100);
stroke->find("title")->SetVisibility(false);
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("panel-grids")->on_click = [this, popup_time, popup_exp, visible](Node*) {
if (visible(grid.get()))
return;
auto fpanel = floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Grids;
fpanel->m_container->add_child(grid);
fpanel->SetHeight(300);
fpanel->m_title->set_text("Grid");
grid->SetPositioning(YGPositionTypeRelative);
grid->SetPosition(0, 0);
grid->SetWidthP(100);
grid->SetHeightP(100);
grid->find("title")->SetVisibility(false);
popup_exp->destroy();
popup_time->destroy();
};
popup_time->find<NodeButtonCustom>("panel-animation")->on_click = [this, popup_time, popup_exp, visible](Node*) {
if (visible(grid.get()))
return;
auto fpanel = floatings_container->add_child<NodePanelFloating>();
fpanel->m_class = NodePanelFloating::kClass::Animation;
fpanel->m_container->add_child(animation);
fpanel->SetSize(500, 300);
fpanel->m_title->set_text("Animation");
fpanel->m_droppable = false;
grid->SetPositioning(YGPositionTypeRelative);
grid->SetPosition(0, 0);
grid->SetWidthP(100);
grid->SetHeightP(100);
grid->find("title")->SetVisibility(false);
popup_exp->destroy();
popup_time->destroy();
};
}
};
if (auto ui_scale = popup_exp->find<NodeComboBox>("tools-ui-scale"))
{
// set index to current zoom level (or at least the closest in list)
for (int i = 0; i < ui_scale->m_data.size(); i++)
if (App::I->zoom >= ui_scale->get_float(i))
ui_scale->set_index(i);
ui_scale->on_select = [ui_scale](Node* target, int index)
{
App::I->set_ui_scale(ui_scale->get_float(index));
};
}
if (auto vp_scale = popup_exp->find<NodeComboBox>("tools-vp-scale"))
{
// set index to current zoom level (or at least the closest in list)
for (int i = 0; i < vp_scale->m_data.size(); i++)
if (App::I->canvas->m_density >= vp_scale->get_float(i))
vp_scale->set_index(i);
vp_scale->on_select = [vp_scale](Node* target, int index)
{
float d = vp_scale->get_float(index);
App::I->canvas->set_density(d);
Settings::set("vp-scale", Serializer::Float(d));
Settings::save();
};
}
if (auto rtl_btn = popup_exp->find<NodeButtonCustom>("tools-rtl"))
{
NodeCheckBox* cb = rtl_btn->find<NodeCheckBox>("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<NodeCheckBox>("tools-rtl-check");
cb->set_value(!cb->checked, true);
};
rtl_btn->find<NodeCheckBox>("tools-rtl-check")->on_value_changed = [this, main](Node*, bool checked)
{
set_ui_rtl(checked);
};
}
if (auto vr_btn = popup_exp->find<NodeButtonCustom>("tools-vr"))
{
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
cb->set_value(has_vr);
vr_btn->on_click = [this, popup_exp, vr_btn](Node* b)
{
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
cb->set_value(!cb->checked, true);
};
vr_btn->find<NodeCheckBox>("tools-vr-check")->on_value_changed = [this, main](Node* target, bool checked)
{
if (checked)
{
if (!vr_start())
{
auto cb = static_cast<NodeCheckBox*>(target);
cb->set_value(false);
message_box("VR Failed", "Couldn't start Virtual Reality mode");
}
}
else
{
vr_stop();
}
};
}
popup_exp->find<NodeButtonCustom>("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<NodeButtonCustom>("camera-reset")->on_click = [this, popup_exp](Node*) {
canvas->reset_camera();
popup_exp->mouse_release();
popup_exp->destroy();
};
/*
popup_exp->find<NodeButtonCustom>("mp4test")->on_click = [this, popup_exp](Node*) {
dialog_export_mp4();
popup_exp->mouse_release();
popup_exp->destroy();
};
*/
#if __IOS__
popup_exp->find<NodeButtonCustom>("sonarpen")->on_click = [this, popup_exp](Node*) {
[ios_app sonarpen_start];
popup_exp->mouse_release();
popup_exp->destroy();
};
#endif
};
}
}
void App::init_menu_about()
{
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-about"))
{
menu_file->on_click = [=](Node*) {
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
auto popup = layout[const_hash("about-menu")]->m_children[0]->clone<NodePopupMenu>();
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);
popup->find<NodeButtonCustom>("about-app")->on_click = [this, popup](Node*) {
dialog_about();
popup->mouse_release();
popup->destroy();
};
popup->find<NodeButtonCustom>("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<NodeButtonCustom>("about-news"))
{
if (auto text = item->find<NodeText>("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<NodeButtonCustom>("about-crash"))
{
b->on_click = [this, popup](Node*) {
LOG("crashing");
crash_test();
popup->mouse_release();
popup->destroy();
};
}
if (auto b = popup->find<NodeButtonCustom>("about-perf"))
{
b->on_click = [this, popup](Node*) {
LOG("perf");
static char str[256];
render_task([&]
{
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<std::chrono::milliseconds>(diff).count();
LOG("%lld ms", ms);
sprintf(str, "Time %lld ms", ms);
});
message_box("Performance test", str);
popup->mouse_release();
popup->destroy();
};
}
};
}
}
void App::brush_update(bool update_color, bool update_brush)
{
// brushes->select_brush(canvas->m_brush->id);
// stroke->set_params(canvas->m_brush);
render_task_async([this, update_color, update_brush]
{
if (update_brush)
{
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();
}
if (update_color)
{
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);
}
}, true);
}
void App::init_menu_layer()
{
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-layers"))
{
menu_file->on_click = [=](Node*) {
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
auto popup = layout[const_hash("layers-menu")]->m_children[0]->clone<NodePopupMenu>();
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);
popup->find<NodeButtonCustom>("layer-clear")->on_click = [this, popup](Node*) {
canvas->m_canvas->clear();
popup->mouse_release();
popup->destroy();
};
if (layers->m_current_layer)
popup->find<NodeButtonCustom>("layer-clear")->
find<NodeText>("menu-label")->
set_text(("Clear Layer " + layers->m_current_layer->m_label_text).c_str());
popup->find<NodeButtonCustom>("layer-rename")->on_click = [this, popup](Node*) {
dialog_layer_rename();
popup->mouse_release();
popup->destroy();
};
if (layers->m_current_layer)
popup->find<NodeButtonCustom>("layer-rename")->
find<NodeText>("menu-label")->
set_text(("Rename Layer " + layers->m_current_layer->m_label_text).c_str());
else
popup->find<NodeButtonCustom>("layer-rename")->
find<NodeText>("menu-label")->
set_text("Rename Layer (Select a layer)");
popup->find<NodeButtonCustom>("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<NodeButtonCustom>("layer-merge")->
find<NodeText>("menu-label")->
set_text(("Merge with " + canvas->m_canvas->m_layers[down_layer_idx]->m_name).c_str());
}
else
{
popup->find<NodeButtonCustom>("layer-merge")->
find<NodeText>("menu-label")->
set_text("Merge Layer (Select upper layers)");
}
}
else
popup->find<NodeButtonCustom>("layer-merge")->
find<NodeText>("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();
static std::vector<std::shared_ptr<Layer>> saved_layers;
layout.on_reloading = [&] {
saved_layers = std::move(Canvas::I->m_layers);
ui_save();
NodeStrokePreview::empty_queue();
};
layout.on_loaded = [&] (bool reloaded) {
LOG("initializing layout updating after load %d x %d zoom %f", (int)width, (int)height, zoom);
layout[main_id]->update(width, height, zoom);
LOG("initializing layout components");
init_sidebar();
if (reloaded)
{
for (const auto& l : saved_layers)
layers->add_layer(l->m_name.c_str(), false, true, l);
}
else
{
layers->add_layer("Default", false, true);
Canvas::I->m_unsaved = false;
Canvas::I->timelapse_reset_encoder();
}
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<NodeText>("version"))
{
version_label->set_text(g_version);
}
if (auto x = layout[main_id]->find<NodeBorder>("ext-fbf"))
{
x->m_color = ShaderManager::ext_framebuffer_fetch ? glm::vec4(0, 1, 0, 1) : glm::vec4(1, 0, 0, 1);
}
if (auto x = layout[main_id]->find<NodeBorder>("ext-flt"))
{
if (ShaderManager::ext_float32 || ShaderManager::ext_float16)
{
if (auto t = x->find<NodeText>("ext-flt-text"))
{
if (ShaderManager::ext_float32_linear)
t->set_text("F32L");
else if (ShaderManager::ext_float32)
t->set_text("F32");
else if (ShaderManager::ext_float16)
t->set_text("F16");
}
x->m_color = glm::vec4(0, 1, 0, 1);
}
else
{
x->m_color = glm::vec4(1, 0, 0, 1);
}
}
brush_update(true, true);
// hacky thing to make the toolbar buttons not steal events when moving cursor fast
if (auto* toolbar = layout[main_id]->find<Node>("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();
};
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");
LOG("initializing layout designer xml");
layout_designer.on_loaded = [&](bool reloaded) {
layout_designer.create();
//layout_designer[main_id]->add_child(layout_designer.instantiate("tpl-panel-animation"));
auto p = layout_designer[main_id]->add_child<NodePanelFloating>();
p->SetPosition(300, 300);
p->SetSize(600, 400);
p->m_container->add_child<NodePanelAnimation>();
};
//layout_designer.load("data/dialogs/panel-animation.xml");
}
void App::set_ui_scale(float scale)
{
zoom = scale;
FontManager::change_scale(zoom * display_density);
Settings::set("ui-scale", Serializer::Float(zoom));
Settings::save();
App::I->title_update();
}
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<NodePanelFloating>(c))
{
auto fd = list_floatings.add<Serializer::Descriptor>();
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));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
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<NodePanelFloating>(c))
{
auto fd = list_drop_left.add<Serializer::Descriptor>();
fd->class_id = "ui-dpl";
fd->set("size", Serializer::Vec2(f->m_size));
fd->set("class", Serializer::Integer((int)f->m_class));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
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<NodePanelFloating>(c))
{
auto fd = list_drop_right.add<Serializer::Descriptor>();
fd->class_id = "ui-dpr";
fd->set("size", Serializer::Vec2(f->m_size));
fd->set("class", Serializer::Integer((int)f->m_class));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
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();
#elif __OSX__
[osx_app save_ui_state];
#endif
Settings::save();
}
void App::ui_restore()
{
if (Settings::has("ui-rtl"))
set_ui_rtl(Settings::value<Serializer::Integer>("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<Serializer::Descriptor>("ui");
for (auto const& l : d->get<Serializer::List>("floatings")->items)
{
auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
auto pos = ld->value<Serializer::Vec2>("pos");
auto size = ld->value<Serializer::Vec2>("size");
auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
auto f = floatings->add_child<NodePanelFloating>();
std::string title = "Floating Panel";
ld->value<Serializer::CString>("title", title);
f->m_title->set_text(title.c_str());
switch (cls)
{
case NodePanelFloating::kClass::Presets:
{
floating_presets = f->m_container->add_child_ref<NodePanelBrushPreset>();
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& 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(true, true);
};
break;
}
case NodePanelFloating::kClass::Color:
{
floating_color = f->m_container->add_child_ref<NodePanelColor>();
floating_color->SetHeightP(100);
floating_color->find("title")->SetVisibility(false);
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
brush_update(true, false);
};
break;
}
case NodePanelFloating::kClass::ColorAdv:
{
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
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(true, false);
};
break;
}
case NodePanelFloating::kClass::Layers:
f->m_container->add_child(layers);
f->SetMinHeight(100);
f->SetHeight(300);
layers->find("title")->SetVisibility(false);
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->find("title")->SetVisibility(false);
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->find("title")->SetVisibility(false);
grid->SetPositioning(YGPositionTypeRelative);
grid->SetPosition(0, 0);
grid->SetWidthP(100);
grid->SetHeightP(100);
break;
case NodePanelFloating::kClass::Animation:
f->m_container->add_child(animation);
f->m_droppable = false;
//grid->find("title")->SetVisibility(false);
animation->SetPositioning(YGPositionTypeRelative);
animation->SetPosition(0, 0);
animation->SetWidthP(100);
animation->SetHeightP(100);
break;
case NodePanelFloating::kClass::Generic:
default:
f->m_container->add_child<Node>();
break;
}
f->m_class = cls;
f->SetSize(size);
f->SetPosition(pos);
f->SetPositioning(YGPositionTypeAbsolute);
}
for (auto const& l : d->get<Serializer::List>("drop-left")->items)
{
auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
auto size = ld->value<Serializer::Vec2>("size");
auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
auto f = drop_left->add_child<NodePanelFloating>();
std::string title = "Floating Panel";
ld->value<Serializer::CString>("title", title);
f->m_title->set_text(title.c_str());
switch (cls)
{
case NodePanelFloating::kClass::Presets:
{
auto floating_presets = f->m_container->add_child<NodePanelBrushPreset>();
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& 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(true, true);
};
break;
}
case NodePanelFloating::kClass::Color:
{
auto floating_color = f->m_container->add_child<NodePanelColor>();
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(true, false);
};
break;
}
case NodePanelFloating::kClass::ColorAdv:
{
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
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(true, false);
};
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<Node>();
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<Serializer::List>("drop-right")->items)
{
auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
auto size = ld->value<Serializer::Vec2>("size");
auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
auto f = drop_right->add_child<NodePanelFloating>();
std::string title = "Floating Panel";
ld->value<Serializer::CString>("title", title);
f->m_title->set_text(title.c_str());
switch (cls)
{
case NodePanelFloating::kClass::Presets:
{
auto floating_presets = f->m_container->add_child<NodePanelBrushPreset>();
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& 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(true, true);
};
break;
}
case NodePanelFloating::kClass::Color:
{
auto floating_color = f->m_container->add_child<NodePanelColor>();
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(true, false);
};
break;
}
case NodePanelFloating::kClass::ColorAdv:
{
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
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(true, false);
};
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<Node>();
break;
}
f->m_class = cls;
f->m_dock = drop_right;
f->SetPositioning(YGPositionTypeRelative);
f->SetPosition(0, 0);
f->SetSize(size);
}
}