1914 lines
78 KiB
C++
1914 lines
78 KiB
C++
#include "pch.h"
|
|
#include "app.h"
|
|
#include "node_panel_grid.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 "app_core/about_menu.h"
|
|
#include "app_core/app_preferences.h"
|
|
#include "app_core/brush_ui.h"
|
|
#include "app_core/canvas_tool_ui.h"
|
|
#include "app_core/document_layer.h"
|
|
#include "app_core/document_canvas.h"
|
|
#include "app_core/document_export.h"
|
|
#include "app_core/file_menu.h"
|
|
#include "app_core/app_status.h"
|
|
#include "app_core/main_toolbar.h"
|
|
#include "app_core/tools_menu.h"
|
|
#include "legacy_app_preference_services.h"
|
|
#include "legacy_app_shell_services.h"
|
|
#include "legacy_brush_ui_services.h"
|
|
#include "legacy_canvas_tool_services.h"
|
|
#include "legacy_document_layer_services.h"
|
|
#include "legacy_history_services.h"
|
|
#include "legacy_ui_overlay_services.h"
|
|
#include "settings.h"
|
|
#include "serializer.h"
|
|
#include "font.h"
|
|
#include "node_remote_page.h"
|
|
#include "node_shorcuts.h"
|
|
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel)
|
|
{
|
|
return pp::panopainter::apply_legacy_brush_color_plan(app, color, update_quick, update_color_panel);
|
|
}
|
|
|
|
bool apply_brush_texture_plan(App& app, pp::app::BrushUiTextureSlot slot, const std::string& path, const std::string& thumb)
|
|
{
|
|
return pp::panopainter::apply_legacy_brush_texture_plan(app, slot, path, thumb);
|
|
}
|
|
|
|
bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
|
|
{
|
|
return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
|
|
}
|
|
|
|
bool apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind)
|
|
{
|
|
return pp::panopainter::apply_legacy_document_export_menu_plan(app, kind);
|
|
}
|
|
|
|
void apply_file_menu_plan(App& app, pp::app::FileMenuCommand command)
|
|
{
|
|
pp::panopainter::apply_legacy_file_menu_command(app, command);
|
|
}
|
|
|
|
std::shared_ptr<NodePopupMenu> add_menu_popup(
|
|
App& app,
|
|
const char* template_id,
|
|
glm::vec2 position,
|
|
float rtl_anchor_width)
|
|
{
|
|
const auto popup = pp::panopainter::add_legacy_popup_menu(
|
|
app,
|
|
template_id,
|
|
position.x,
|
|
position.y,
|
|
rtl_anchor_width);
|
|
if (!popup) {
|
|
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
|
|
return nullptr;
|
|
}
|
|
return popup.value();
|
|
}
|
|
|
|
pp::app::DocumentLayerMenuPlan make_layer_menu_plan(
|
|
pp::app::DocumentLayerMenuCommand command,
|
|
App& app)
|
|
{
|
|
const bool has_current_layer = app.layers && app.layers->m_current_layer;
|
|
const int current_index = app.canvas && app.canvas->m_canvas
|
|
? app.canvas->m_canvas->m_current_layer_idx
|
|
: 0;
|
|
const int animation_duration = Canvas::I
|
|
? Canvas::I->anim_duration()
|
|
: 0;
|
|
const std::string current_name = has_current_layer
|
|
? app.layers->m_current_layer->m_label_text
|
|
: std::string {};
|
|
std::string lower_name;
|
|
if (app.canvas && app.canvas->m_canvas && current_index > 0
|
|
&& current_index - 1 < static_cast<int>(app.canvas->m_canvas->m_layers.size()))
|
|
{
|
|
lower_name = app.canvas->m_canvas->m_layers[current_index - 1]->m_name;
|
|
}
|
|
|
|
const auto plan = pp::app::plan_document_layer_menu(
|
|
command,
|
|
has_current_layer,
|
|
current_index,
|
|
animation_duration,
|
|
current_name,
|
|
lower_name);
|
|
if (plan)
|
|
return plan.value();
|
|
return {};
|
|
}
|
|
|
|
[[nodiscard]] bool should_open_tools_panel(const pp::app::ToolsPanelPlan& plan) noexcept
|
|
{
|
|
return plan.action == pp::app::ToolsPanelAction::open_floating_panel;
|
|
}
|
|
|
|
void apply_tools_panel_chrome(NodePanelFloating& panel, const pp::app::ToolsPanelPlan& plan)
|
|
{
|
|
if (plan.width > 0 && plan.height > 0) {
|
|
panel.SetSize(static_cast<float>(plan.width), static_cast<float>(plan.height));
|
|
} else {
|
|
if (plan.width > 0)
|
|
panel.SetWidth(static_cast<float>(plan.width));
|
|
if (plan.height > 0)
|
|
panel.SetHeight(static_cast<float>(plan.height));
|
|
}
|
|
if (plan.min_width > 0)
|
|
panel.SetMinWidth(static_cast<float>(plan.min_width));
|
|
if (plan.min_height > 0)
|
|
panel.SetMinHeight(static_cast<float>(plan.min_height));
|
|
panel.m_title->set_text(plan.title.data());
|
|
panel.m_droppable = plan.droppable;
|
|
}
|
|
|
|
void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan)
|
|
{
|
|
pp::panopainter::execute_legacy_main_toolbar_plan(app, plan);
|
|
}
|
|
|
|
void execute_about_menu_plan(App& app, const pp::app::AboutMenuPlan& plan)
|
|
{
|
|
pp::panopainter::execute_legacy_about_menu_plan(app, plan);
|
|
}
|
|
|
|
void execute_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan)
|
|
{
|
|
pp::panopainter::execute_legacy_tools_menu_plan(app, plan);
|
|
}
|
|
|
|
void execute_document_layer_menu_plan(App& app, const pp::app::DocumentLayerMenuPlan& plan)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_document_layer_menu_plan(app, plan);
|
|
if (!status.ok())
|
|
LOG("Layer menu action failed: %s", status.message);
|
|
}
|
|
|
|
void execute_document_layer_merge_plan(App& app, const pp::app::DocumentLayerMergePlan& plan)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_document_layer_merge_plan(app, plan);
|
|
if (!status.ok())
|
|
LOG("Layer merge failed: %s", status.message);
|
|
}
|
|
|
|
void execute_document_layer_operation_plan(
|
|
App& app,
|
|
const pp::app::DocumentLayerOperationPlan& plan,
|
|
const std::shared_ptr<class Layer>& pending_layer = nullptr)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_document_layer_operation_plan(app, plan, pending_layer);
|
|
if (!status.ok())
|
|
LOG("Layer operation failed: %s", status.message);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void App::title_update()
|
|
{
|
|
if (auto docname = layout[main_id]->find<NodeText>("txt-docname"))
|
|
{
|
|
const auto title = pp::app::make_document_title(
|
|
doc_name,
|
|
canvas->m_canvas->m_unsaved,
|
|
canvas->m_canvas->m_width);
|
|
docname->set_text(title.c_str());
|
|
}
|
|
if (auto node = layout[main_id]->find<NodeText>("txt-dpi"))
|
|
{
|
|
const auto label = pp::app::make_dpi_label(zoom);
|
|
node->set_text(label.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*) {
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::open_document);
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
if (auto* button = layout[main_id]->find<NodeButton>("btn-save"))
|
|
{
|
|
button->on_click = [this, button](Node*) {
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::save_document);
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-undo"))
|
|
{
|
|
button->on_click = [this, button](Node*) {
|
|
const auto history = pp::panopainter::legacy_history_snapshot();
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::undo,
|
|
history.undo_count);
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-redo"))
|
|
{
|
|
button->on_click = [this, button](Node*) {
|
|
const auto history = pp::panopainter::legacy_history_snapshot();
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::redo,
|
|
0,
|
|
history.redo_count);
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-clean-memory"))
|
|
{
|
|
button->on_click = [this](Node*) {
|
|
const auto history = pp::panopainter::legacy_history_snapshot();
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::clear_history,
|
|
history.undo_count,
|
|
history.redo_count,
|
|
history.memory_bytes);
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
if (auto* button = layout[main_id]->find<NodeButton>("btn-clear"))
|
|
{
|
|
button->on_click = [this](Node*) {
|
|
//exit(0);
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::clear_canvas,
|
|
0,
|
|
0,
|
|
0,
|
|
static_cast<bool>(canvas));
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
if (auto* button = layout[main_id]->find<NodeButton>("btn-popup"))
|
|
{
|
|
button->on_click = [this](Node*) {
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::show_message_box);
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-settings"))
|
|
{
|
|
button->on_click = [this](Node*) {
|
|
const auto plan = pp::app::plan_main_toolbar_command(
|
|
pp::app::MainToolbarCommand::show_settings);
|
|
if (plan)
|
|
execute_main_toolbar_plan(*this, plan.value());
|
|
};
|
|
}
|
|
}
|
|
|
|
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) {
|
|
apply_brush_color_plan(*this, color, true, false);
|
|
};
|
|
|
|
stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
|
|
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::tip, path, thumb);
|
|
};
|
|
stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
|
|
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::pattern, path, thumb);
|
|
};
|
|
stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
|
|
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::dual, path, thumb);
|
|
};
|
|
stroke->on_stroke_change = [this](Node*) {
|
|
const auto status = pp::panopainter::execute_legacy_brush_stroke_changed_plan(*this);
|
|
if (!status.ok())
|
|
LOG("Brush stroke settings action failed: %s", status.message);
|
|
};
|
|
|
|
quick->on_color_change = [this](Node*, glm::vec3 c) {
|
|
apply_brush_color_plan(*this, glm::vec4(c, 1.f), false, true);
|
|
};
|
|
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) {
|
|
apply_brush_preset_plan(*this, b);
|
|
};
|
|
|
|
layers->on_layer_add = [this](Node*, std::shared_ptr<class Layer> layer, int index) {
|
|
const auto plan = pp::app::plan_document_layer_add(
|
|
static_cast<int>(Canvas::I->m_layers.size()),
|
|
index,
|
|
layers->m_layers.back()->m_label_text);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value(), layer);
|
|
};
|
|
|
|
layers->on_layer_duplicate = [this](Node*, int source_index) {
|
|
const auto plan = pp::app::plan_document_layer_duplicate(
|
|
static_cast<int>(Canvas::I->m_layers.size()),
|
|
source_index);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_change = [this](Node*, int, int new_idx) {
|
|
const auto plan = pp::app::plan_document_layer_select(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
new_idx);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
|
|
const auto plan = pp::app::plan_document_layer_reorder(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
old_idx,
|
|
new_idx);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_delete = [this](Node*, int idx) {
|
|
const auto plan = pp::app::plan_document_layer_remove(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
idx);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
|
|
const auto plan = pp::app::plan_document_layer_opacity(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
idx,
|
|
value);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
|
|
const auto plan = pp::app::plan_document_layer_visibility(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
idx,
|
|
visible);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_alpha_lock_changed = [this](Node*, int idx, bool locked) {
|
|
const auto plan = pp::app::plan_document_layer_alpha_lock(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
idx,
|
|
locked);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_blend_mode_changed = [this](Node*, int idx, int mode) {
|
|
const auto plan = pp::app::plan_document_layer_blend_mode(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
idx,
|
|
mode);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
|
|
layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) {
|
|
const auto plan = pp::app::plan_document_layer_highlight(
|
|
static_cast<int>(canvas->m_canvas->m_layers.size()),
|
|
idx,
|
|
highlight);
|
|
if (!plan)
|
|
return;
|
|
execute_document_layer_operation_plan(*this, plan.value());
|
|
};
|
|
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();
|
|
};
|
|
};
|
|
}
|
|
}
|
|
[[nodiscard]] bool current_canvas_mode_is_draw(App& app) noexcept
|
|
{
|
|
return app.canvas && app.canvas->m_canvas && app.canvas->m_canvas->m_current_mode == kCanvasMode::Draw;
|
|
}
|
|
|
|
template<class T>
|
|
void execute_canvas_tool_toolbar_binding(
|
|
App& app,
|
|
const pp::app::CanvasToolToolbarBinding& binding,
|
|
T* button)
|
|
{
|
|
const auto plan = pp::app::plan_canvas_tool_toolbar_binding_action(
|
|
binding,
|
|
current_canvas_mode_is_draw(app));
|
|
const auto status = binding.action == pp::app::CanvasToolToolbarAction::select_mode
|
|
? pp::panopainter::execute_legacy_canvas_tool_plan(app, plan, button)
|
|
: pp::panopainter::execute_legacy_canvas_tool_plan(app, plan);
|
|
if (!status.ok())
|
|
LOG("Canvas toolbar action failed: %s", status.message);
|
|
}
|
|
|
|
template<class T>
|
|
void bind_canvas_tool_toolbar_button(
|
|
App& app,
|
|
const pp::app::CanvasToolToolbarBinding& binding,
|
|
T* button)
|
|
{
|
|
button->on_click = [&app, binding, button](Node*) {
|
|
execute_canvas_tool_toolbar_binding(app, binding, button);
|
|
};
|
|
}
|
|
|
|
void App::init_toolbar_draw()
|
|
{
|
|
const auto toolbar = pp::app::plan_canvas_tool_toolbar();
|
|
bool apply_default_tool = false;
|
|
for (const auto& binding : toolbar.bindings) {
|
|
if (binding.custom_button) {
|
|
if (auto* button = layout[main_id]->find<NodeButtonCustom>(binding.button_id.data())) {
|
|
bind_canvas_tool_toolbar_button(*this, binding, button);
|
|
apply_default_tool = apply_default_tool || binding.applies_default_on_init;
|
|
}
|
|
} else {
|
|
if (auto* button = layout[main_id]->find<NodeButton>(binding.button_id.data())) {
|
|
bind_canvas_tool_toolbar_button(*this, binding, button);
|
|
apply_default_tool = apply_default_tool || binding.applies_default_on_init;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (apply_default_tool) {
|
|
const auto default_plan = pp::app::plan_canvas_tool_select(toolbar.default_mode);
|
|
const auto status = pp::panopainter::execute_legacy_canvas_tool_plan(*this, default_plan);
|
|
if (!status.ok())
|
|
LOG("Canvas default tool action failed: %s", status.message);
|
|
}
|
|
}
|
|
|
|
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 = add_menu_popup(*this, "file-menu", pos, menu_file->m_size.x);
|
|
if (!popup)
|
|
return;
|
|
|
|
if (auto b = popup->find<NodeButtonCustom>("file-newdoc"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::new_document);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-import"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::import_image);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-open"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::open_project);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-browse"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::browse_cloud);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-save"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::save);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-save-as"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_as);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-save-ver"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_version);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-export"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::export_jpeg);
|
|
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 = add_menu_popup(*this, "file-submenu-export", pos, b->m_size.x);
|
|
if (!subpopup)
|
|
return;
|
|
subpopup->find<NodeButtonCustom>("file-submenu-export-png")->on_click = [this, subpopup, popup](Node*) {
|
|
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::png);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
subpopup->mouse_release();
|
|
subpopup->destroy();
|
|
};
|
|
subpopup->find<NodeButtonCustom>("file-submenu-export-layers")->on_click = [this, subpopup, popup](Node*) {
|
|
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::layers);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
subpopup->mouse_release();
|
|
subpopup->destroy();
|
|
};
|
|
subpopup->find<NodeButtonCustom>("file-submenu-export-cube")->on_click = [this, subpopup, popup](Node*) {
|
|
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::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*) {
|
|
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::depth);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
subpopup->mouse_release();
|
|
subpopup->destroy();
|
|
};
|
|
subpopup->find<NodeButtonCustom>("file-submenu-export-anim")->on_click = [this, subpopup, popup](Node*) {
|
|
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_frames);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
subpopup->mouse_release();
|
|
subpopup->destroy();
|
|
};
|
|
subpopup->find<NodeButtonCustom>("file-submenu-export-anim-mp4")->on_click = [this, subpopup, popup](Node*) {
|
|
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_mp4);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
subpopup->mouse_release();
|
|
subpopup->destroy();
|
|
};
|
|
subpopup->find<NodeButtonCustom>("file-submenu-export-timelapse")->on_click = [this, subpopup, popup](Node*) {
|
|
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::timelapse);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
subpopup->mouse_release();
|
|
subpopup->destroy();
|
|
};
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-share"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::share);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-resize"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::resize);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-cloud-upload"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::cloud_upload);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
if (auto b = popup->find<NodeButtonCustom>("file-cloud-browse"))
|
|
b->on_click = [this, popup](Node*) {
|
|
apply_file_menu_plan(*this, pp::app::FileMenuCommand::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 = add_menu_popup(*this, "edit-menu", pos, menu_file->m_size.x);
|
|
if (!popup)
|
|
return;
|
|
};
|
|
}
|
|
}
|
|
|
|
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 = add_menu_popup(*this, "tools-menu", pos, menu_exp->m_size.x);
|
|
if (!popup_exp)
|
|
return;
|
|
|
|
if (auto tick = popup_exp->find<NodeButtonCustom>("tools-panels")) tick->on_click = [this, popup_exp](Node* b)
|
|
{
|
|
const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels);
|
|
if (menu_plan.action != pp::app::ToolsMenuAction::show_panels_submenu)
|
|
return;
|
|
|
|
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
|
|
auto popup_time = add_menu_popup(*this, "panels-menu", pos, b->m_size.x);
|
|
if (!popup_time)
|
|
return;
|
|
|
|
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*) {
|
|
const auto plan = pp::app::plan_tools_panel(
|
|
pp::app::ToolsPanel::presets,
|
|
visible(floating_presets.get()));
|
|
if (!should_open_tools_panel(plan))
|
|
return;
|
|
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
|
fpanel->m_class = NodePanelFloating::kClass::Presets;
|
|
apply_tools_panel_chrome(*fpanel, plan);
|
|
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) {
|
|
apply_brush_preset_plan(*this, b);
|
|
};
|
|
}
|
|
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*) {
|
|
const auto plan = pp::app::plan_tools_panel(
|
|
pp::app::ToolsPanel::color,
|
|
visible(floating_color.get()));
|
|
if (!should_open_tools_panel(plan))
|
|
return;
|
|
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
|
fpanel->m_class = NodePanelFloating::kClass::Color;
|
|
apply_tools_panel_chrome(*fpanel, plan);
|
|
if (!floating_color)
|
|
{
|
|
floating_color = fpanel->m_container->add_child_ref<NodePanelColor>();
|
|
floating_color->SetHeightP(100);
|
|
//floating_color->SetMinHeight(300);
|
|
if (plan.hides_embedded_title)
|
|
floating_color->find("title")->SetVisibility(false);
|
|
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
|
|
apply_brush_color_plan(*this, color, false, 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*) {
|
|
const auto plan = pp::app::plan_tools_panel(
|
|
pp::app::ToolsPanel::color_advanced,
|
|
visible(floating_picker.get()));
|
|
if (!should_open_tools_panel(plan))
|
|
return;
|
|
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
|
fpanel->m_class = NodePanelFloating::kClass::ColorAdv;
|
|
apply_tools_panel_chrome(*fpanel, plan);
|
|
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) {
|
|
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, 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*) {
|
|
const auto plan = pp::app::plan_tools_panel(
|
|
pp::app::ToolsPanel::layers,
|
|
visible(layers.get()));
|
|
if (!should_open_tools_panel(plan))
|
|
return;
|
|
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
|
fpanel->m_class = NodePanelFloating::kClass::Layers;
|
|
apply_tools_panel_chrome(*fpanel, plan);
|
|
fpanel->m_container->add_child(layers);
|
|
layers->SetPositioning(YGPositionTypeRelative);
|
|
layers->SetPosition(0, 0);
|
|
layers->SetWidthP(100);
|
|
layers->SetHeightP(100);
|
|
layers->SetFlexShrink(0);
|
|
if (plan.hides_embedded_title)
|
|
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*) {
|
|
const auto plan = pp::app::plan_tools_panel(
|
|
pp::app::ToolsPanel::brush,
|
|
visible(stroke.get()));
|
|
if (!should_open_tools_panel(plan))
|
|
return;
|
|
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
|
fpanel->m_class = NodePanelFloating::kClass::Brush;
|
|
fpanel->m_container->add_child(stroke);
|
|
apply_tools_panel_chrome(*fpanel, plan);
|
|
stroke->SetPositioning(YGPositionTypeRelative);
|
|
stroke->SetPosition(0, 0);
|
|
stroke->SetWidthP(100);
|
|
stroke->SetHeightP(100);
|
|
if (plan.hides_embedded_title)
|
|
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*) {
|
|
const auto plan = pp::app::plan_tools_panel(
|
|
pp::app::ToolsPanel::grids,
|
|
visible(grid.get()));
|
|
if (!should_open_tools_panel(plan))
|
|
return;
|
|
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
|
fpanel->m_class = NodePanelFloating::kClass::Grids;
|
|
fpanel->m_container->add_child(grid);
|
|
apply_tools_panel_chrome(*fpanel, plan);
|
|
grid->SetPositioning(YGPositionTypeRelative);
|
|
grid->SetPosition(0, 0);
|
|
grid->SetWidthP(100);
|
|
grid->SetHeightP(100);
|
|
if (plan.hides_embedded_title)
|
|
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*) {
|
|
const auto plan = pp::app::plan_tools_panel(
|
|
pp::app::ToolsPanel::animation,
|
|
visible(animation.get()));
|
|
if (!should_open_tools_panel(plan))
|
|
return;
|
|
auto fpanel = floatings_container->add_child<NodePanelFloating>();
|
|
fpanel->m_class = NodePanelFloating::kClass::Animation;
|
|
fpanel->m_container->add_child(animation);
|
|
apply_tools_panel_chrome(*fpanel, plan);
|
|
animation->SetPositioning(YGPositionTypeRelative);
|
|
animation->SetPosition(0, 0);
|
|
animation->SetWidthP(100);
|
|
animation->SetHeightP(100);
|
|
|
|
popup_exp->destroy();
|
|
popup_time->destroy();
|
|
};
|
|
};
|
|
|
|
if (auto options = popup_exp->find<NodeButtonCustom>("tools-options")) options->on_click = [this, options, main](Node* b)
|
|
{
|
|
const auto menu_plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::options);
|
|
if (menu_plan.action != pp::app::ToolsMenuAction::show_options_submenu)
|
|
return;
|
|
|
|
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
|
|
auto popup_time = add_menu_popup(*this, "options-menu", pos, b->m_size.x);
|
|
if (!popup_time)
|
|
return;
|
|
|
|
if (auto ui_scale = popup_time->find<NodeComboBox>("tools-ui-scale"))
|
|
{
|
|
std::vector<float> scale_options;
|
|
scale_options.reserve(ui_scale->m_data.size());
|
|
for (int i = 0; i < ui_scale->m_data.size(); i++)
|
|
scale_options.push_back(ui_scale->get_float(i));
|
|
|
|
const auto selection = pp::app::plan_scale_option_selection(App::I->zoom, scale_options);
|
|
if (selection.has_selection)
|
|
ui_scale->set_index(static_cast<int>(selection.index));
|
|
|
|
ui_scale->on_select = [ui_scale](Node* target, int index)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_ui_scale_preference(
|
|
*App::I,
|
|
ui_scale->get_float(index));
|
|
if (!status.ok())
|
|
LOG("UI scale preference failed: %s", status.message);
|
|
};
|
|
}
|
|
|
|
if (auto vp_scale = popup_time->find<NodeComboBox>("tools-vp-scale"))
|
|
{
|
|
std::vector<float> scale_options;
|
|
scale_options.reserve(vp_scale->m_data.size());
|
|
for (int i = 0; i < vp_scale->m_data.size(); i++)
|
|
scale_options.push_back(vp_scale->get_float(i));
|
|
|
|
const auto selection = pp::app::plan_scale_option_selection(App::I->canvas->m_density, scale_options);
|
|
if (selection.has_selection)
|
|
vp_scale->set_index(static_cast<int>(selection.index));
|
|
|
|
vp_scale->on_select = [vp_scale](Node* target, int index)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_viewport_scale_preference(
|
|
*App::I,
|
|
vp_scale->get_float(index));
|
|
if (!status.ok())
|
|
LOG("Viewport scale preference failed: %s", status.message);
|
|
};
|
|
}
|
|
|
|
if (auto rtl_btn = popup_time->find<NodeButtonCustom>("tools-rtl"))
|
|
{
|
|
NodeCheckBox* cb = rtl_btn->find<NodeCheckBox>("tools-rtl-check");
|
|
cb->set_value(ui_rtl, false);
|
|
|
|
rtl_btn->on_click = [this, 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)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_interface_direction_preference(
|
|
*this,
|
|
checked);
|
|
if (!status.ok())
|
|
LOG("Interface direction preference failed: %s", status.message);
|
|
};
|
|
}
|
|
|
|
if (auto vr_btn = popup_time->find<NodeButtonCustom>("tools-vr"))
|
|
{
|
|
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-check");
|
|
cb->set_value(has_vr);
|
|
|
|
vr_btn->on_click = [this, 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)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_vr_mode_preference(
|
|
*this,
|
|
checked);
|
|
if (!status.ok()) {
|
|
auto cb = static_cast<NodeCheckBox*>(target);
|
|
cb->set_value(false);
|
|
message_box("VR Failed", "Couldn't start Virtual Reality mode");
|
|
}
|
|
};
|
|
}
|
|
|
|
if (auto vr_btn = popup_time->find<NodeButtonCustom>("tools-vr-controllers"))
|
|
{
|
|
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-controllers-check");
|
|
cb->set_value(vr_controllers_enabled);
|
|
|
|
vr_btn->on_click = [this, vr_btn](Node* b)
|
|
{
|
|
NodeCheckBox* cb = vr_btn->find<NodeCheckBox>("tools-vr-controllers-check");
|
|
cb->set_value(!cb->checked, true);
|
|
};
|
|
|
|
vr_btn->find<NodeCheckBox>("tools-vr-controllers-check")->on_value_changed = [this, main](Node* target, bool checked)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_vr_controllers_preference(
|
|
*this,
|
|
checked);
|
|
if (!status.ok())
|
|
LOG("VR controllers preference failed: %s", status.message);
|
|
};
|
|
}
|
|
|
|
if (auto btn = popup_time->find<NodeButtonCustom>("tools-timelapse"))
|
|
{
|
|
NodeCheckBox* cb = btn->find<NodeCheckBox>("tools-timelapse-check");
|
|
cb->set_value(Settings::value_or<Serializer::Boolean>("auto-timelapse", true), false);
|
|
|
|
btn->on_click = [this, btn](Node* b)
|
|
{
|
|
NodeCheckBox* cb = btn->find<NodeCheckBox>("tools-timelapse-check");
|
|
cb->set_value(!cb->checked, true);
|
|
};
|
|
|
|
btn->find<NodeCheckBox>("tools-timelapse-check")->on_value_changed = [this, main](Node*, bool checked)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_timelapse_preference(
|
|
*this,
|
|
checked);
|
|
if (!status.ok())
|
|
LOG("Timelapse preference failed: %s", status.message);
|
|
};
|
|
}
|
|
|
|
if (auto mode = popup_time->find<NodeComboBox>("tools-show-cursor"))
|
|
{
|
|
mode->set_index(Settings::value_or<Serializer::Integer>("show-cursor", 0));
|
|
|
|
mode->on_select = [mode](Node* target, int index)
|
|
{
|
|
const auto status = pp::panopainter::execute_legacy_canvas_cursor_mode_preference(
|
|
*App::I,
|
|
index);
|
|
if (!status.ok())
|
|
LOG("Cursor mode preference failed: %s", status.message);
|
|
};
|
|
}
|
|
};
|
|
|
|
popup_exp->find<NodeButtonCustom>("clear-grids")->on_click = [this, popup_exp](Node*) {
|
|
const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::clear_grids);
|
|
execute_tools_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
popup_exp->mouse_release();
|
|
popup_exp->destroy();
|
|
}
|
|
};
|
|
|
|
popup_exp->find<NodeButtonCustom>("camera-reset")->on_click = [this, popup_exp](Node*) {
|
|
const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::reset_camera);
|
|
execute_tools_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
popup_exp->mouse_release();
|
|
popup_exp->destroy();
|
|
}
|
|
};
|
|
|
|
popup_exp->find<NodeButtonCustom>("shortcuts")->on_click = [this, popup_exp](Node*) {
|
|
const auto plan = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::shortcuts);
|
|
execute_tools_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
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 (platform_supports_sonarpen())
|
|
{
|
|
popup_exp->find<NodeButtonCustom>("sonarpen")->on_click = [this, popup_exp](Node*) {
|
|
const auto plan = pp::app::plan_tools_menu_command(
|
|
pp::app::ToolsMenuCommand::sonarpen,
|
|
platform_supports_sonarpen());
|
|
execute_tools_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
popup_exp->mouse_release();
|
|
popup_exp->destroy();
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
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 = add_menu_popup(*this, "about-menu", pos, menu_file->m_size.x);
|
|
if (!popup)
|
|
return;
|
|
|
|
popup->find<NodeButtonCustom>("about-app")->on_click = [this, popup](Node*) {
|
|
const auto plan = pp::app::plan_about_menu_command(
|
|
pp::app::AboutMenuCommand::about_app,
|
|
g_version_major,
|
|
g_version_minor,
|
|
g_version_fix);
|
|
execute_about_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
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);
|
|
const auto plan = pp::app::plan_about_menu_command(
|
|
pp::app::AboutMenuCommand::help_guide,
|
|
g_version_major,
|
|
g_version_minor,
|
|
g_version_fix);
|
|
execute_about_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
}
|
|
};
|
|
|
|
if (auto item = popup->find<NodeButtonCustom>("about-news"))
|
|
{
|
|
if (auto text = item->find<NodeText>("menu-label"))
|
|
{
|
|
const auto plan = pp::app::plan_about_menu_command(
|
|
pp::app::AboutMenuCommand::whats_new,
|
|
g_version_major,
|
|
g_version_minor,
|
|
g_version_fix);
|
|
text->set_text(plan.label.c_str());
|
|
}
|
|
item->on_click = [this, popup](Node*) {
|
|
const auto plan = pp::app::plan_about_menu_command(
|
|
pp::app::AboutMenuCommand::whats_new,
|
|
g_version_major,
|
|
g_version_minor,
|
|
g_version_fix);
|
|
execute_about_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
}
|
|
};
|
|
}
|
|
|
|
if (auto b = popup->find<NodeButtonCustom>("about-crash"))
|
|
{
|
|
b->on_click = [this, popup](Node*) {
|
|
const auto plan = pp::app::plan_about_menu_command(
|
|
pp::app::AboutMenuCommand::induce_crash,
|
|
g_version_major,
|
|
g_version_minor,
|
|
g_version_fix);
|
|
execute_about_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
}
|
|
};
|
|
}
|
|
|
|
if (auto b = popup->find<NodeButtonCustom>("about-perf"))
|
|
{
|
|
b->on_click = [this, popup](Node*) {
|
|
const auto plan = pp::app::plan_about_menu_command(
|
|
pp::app::AboutMenuCommand::performance_test,
|
|
g_version_major,
|
|
g_version_minor,
|
|
g_version_fix,
|
|
true,
|
|
Canvas::I != nullptr);
|
|
execute_about_menu_plan(*this, plan);
|
|
if (plan.closes_root_popup)
|
|
{
|
|
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]
|
|
{
|
|
pp::app::BrushUiRefreshInput input;
|
|
input.update_color = update_color;
|
|
input.update_brush = update_brush;
|
|
auto current_brush = Canvas::I ? Canvas::I->m_current_brush : nullptr;
|
|
input.has_current_brush = current_brush != nullptr;
|
|
input.has_floating_picker = floating_picker != nullptr;
|
|
input.has_floating_color_panel = floating_color != nullptr;
|
|
if (input.has_current_brush)
|
|
{
|
|
input.tip_flow = current_brush->m_tip_flow;
|
|
input.tip_size = current_brush->m_tip_size;
|
|
input.r = current_brush->m_tip_color.r;
|
|
input.g = current_brush->m_tip_color.g;
|
|
input.b = current_brush->m_tip_color.b;
|
|
input.a = current_brush->m_tip_color.a;
|
|
}
|
|
|
|
const auto view = pp::app::plan_brush_ui_refresh(input);
|
|
if (!view) {
|
|
LOG("Brush UI refresh failed: %s", view.status().message);
|
|
return;
|
|
}
|
|
|
|
if (view.value().updates_stroke_controls)
|
|
{
|
|
stroke->update_controls();
|
|
}
|
|
if (view.value().updates_quick_flow)
|
|
{
|
|
quick->m_slider_flow->set_value(stroke->m_tip_flow->get_value());
|
|
}
|
|
if (view.value().updates_quick_size)
|
|
{
|
|
quick->m_slider_size->set_value(stroke->m_tip_size->get_value());
|
|
}
|
|
if (view.value().updates_quick_brush_preview && current_brush)
|
|
{
|
|
*quick->m_button_brush_current_preview->m_brush = *current_brush;
|
|
quick->m_button_brush_current_preview->draw_stroke();
|
|
}
|
|
if (view.value().updates_quick_color)
|
|
{
|
|
const glm::vec4 color(view.value().r, view.value().g, view.value().b, view.value().a);
|
|
quick->m_button_color_current_inner->m_color = color;
|
|
if (view.value().updates_floating_picker)
|
|
floating_picker->set_color(color);
|
|
if (view.value().updates_floating_color_panel)
|
|
floating_color->set_color(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 = add_menu_popup(*this, "layers-menu", pos, menu_file->m_size.x);
|
|
if (!popup)
|
|
return;
|
|
|
|
popup->find<NodeButtonCustom>("layer-clear")->on_click = [this, popup](Node*) {
|
|
const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, *this);
|
|
execute_document_layer_menu_plan(*this, plan);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
{
|
|
const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::clear, *this);
|
|
popup->find<NodeButtonCustom>("layer-clear")->
|
|
find<NodeText>("menu-label")->
|
|
set_text(plan.label.c_str());
|
|
}
|
|
|
|
popup->find<NodeButtonCustom>("layer-rename")->on_click = [this, popup](Node*) {
|
|
const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, *this);
|
|
execute_document_layer_menu_plan(*this, plan);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
{
|
|
const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::rename, *this);
|
|
popup->find<NodeButtonCustom>("layer-rename")->
|
|
find<NodeText>("menu-label")->
|
|
set_text(plan.label.c_str());
|
|
}
|
|
|
|
popup->find<NodeButtonCustom>("layer-merge")->on_click = [this, popup](Node*) {
|
|
const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, *this);
|
|
execute_document_layer_menu_plan(*this, plan);
|
|
popup->mouse_release();
|
|
popup->destroy();
|
|
};
|
|
{
|
|
const auto plan = make_layer_menu_plan(pp::app::DocumentLayerMenuCommand::merge_down, *this);
|
|
popup->find<NodeButtonCustom>("layer-merge")->
|
|
find<NodeText>("menu-label")->
|
|
set_text(plan.label.c_str());
|
|
}
|
|
|
|
};
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
const auto renderer_features = ShaderManager::render_device_features();
|
|
const auto renderer_diagnostics = pp::app::plan_renderer_diagnostics({
|
|
.framebuffer_fetch = renderer_features.framebuffer_fetch,
|
|
.float32_render_targets = renderer_features.float32_render_targets,
|
|
.float32_linear_filtering = renderer_features.float32_linear_filtering,
|
|
.float16_render_targets = renderer_features.float16_render_targets,
|
|
});
|
|
|
|
if (auto x = layout[main_id]->find<NodeBorder>("ext-fbf"))
|
|
{
|
|
x->m_color = renderer_diagnostics.framebuffer_fetch.supported ?
|
|
glm::vec4(0, 1, 0, 1) :
|
|
glm::vec4(1, 0, 0, 1);
|
|
}
|
|
|
|
if (auto x = layout[main_id]->find<NodeBorder>("ext-flt"))
|
|
{
|
|
if (renderer_diagnostics.floating_point_targets.supported)
|
|
{
|
|
if (auto t = x->find<NodeText>("ext-flt-text"))
|
|
{
|
|
t->set_text(std::string(renderer_diagnostics.floating_point_targets.label));
|
|
}
|
|
x->m_color = glm::vec4(0, 1, 0, 1);
|
|
}
|
|
else
|
|
{
|
|
x->m_color = glm::vec4(1, 0, 0, 1);
|
|
}
|
|
}
|
|
|
|
dialog_whatsnew(false);
|
|
|
|
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("shortcuts"));
|
|
auto p = layout_designer[main_id]->add_child<NodeShortcuts>();
|
|
//p->SetPosition(300, 300);
|
|
//p->SetSize(600, 400);
|
|
//p->m_container->add_child<NodePanelAnimation>();
|
|
};
|
|
//layout_designer.load("data/dialogs/shortcuts.xml");
|
|
}
|
|
|
|
void App::set_ui_scale(float scale)
|
|
{
|
|
const auto plan = pp::app::plan_ui_scale(scale, display_density);
|
|
zoom = plan.scale;
|
|
FontManager::change_scale(plan.font_scale);
|
|
Settings::set("ui-scale", Serializer::Float(plan.scale));
|
|
Settings::save();
|
|
App::I->title_update();
|
|
}
|
|
|
|
void App::set_ui_rtl(bool rtl)
|
|
{
|
|
const auto plan = pp::app::plan_interface_direction(rtl);
|
|
ui_rtl = plan.direction == pp::app::InterfaceDirection::right_to_left;
|
|
layout[main_id]->find("central-row")->SetRTL(
|
|
ui_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));
|
|
save_platform_ui_state();
|
|
|
|
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) {
|
|
apply_brush_preset_plan(*this, b);
|
|
};
|
|
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) {
|
|
apply_brush_color_plan(*this, color, false, 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) {
|
|
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, 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) {
|
|
apply_brush_preset_plan(*this, b);
|
|
};
|
|
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) {
|
|
apply_brush_color_plan(*this, color, false, 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) {
|
|
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, 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) {
|
|
apply_brush_preset_plan(*this, b);
|
|
};
|
|
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) {
|
|
apply_brush_color_plan(*this, color, false, 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) {
|
|
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, 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);
|
|
}
|
|
}
|