implement animation panel interaction

This commit is contained in:
2019-10-17 20:42:52 +02:00
parent 62863e7224
commit 7487feb168
14 changed files with 399 additions and 16 deletions

View File

@@ -5,26 +5,50 @@
>
<layout id="tpl-layer">
<border height="30" border-color="1" thickness="1" color=".4" dir="row" margin="1 0 1 0">
<node dir="row" height="30" width="100">
<node width="30" pad="1">
<border height="40" border-color="1" thickness="1" color=".4" dir="row" margin="1 0 1 0">
<node dir="row" height="40" width="100">
<node width="40" pad="1">
<checkbox id="cb" icon="data/ui/check-layer-visibility.png"/>
</node>
<node width="1" grow="1" justify="center" pad="5">
<text id="label" text="Layer"/>
</node>
</node>
<border color=".2" grow="1" margin="1">
<border id="container" dir="row" color=".6" margin="1" pad="2" grow="1">
<!--frames list-->
</border>
</border>
</layout>
<layout id="tpl-panel-animation">
<node width="600" margin="0 0 10 0" rtl="ltr">
<scroll id="layers-container" pad="5" color=".4" dir="col-reverse" justify="flex-end" flood-events="1" grow="1" shrink="1">
<node margin="0 0 10 0" rtl="ltr" grow="1" dir="col">
<border color=".4" pad="0 5 0 5"><timeline id="timeline" margin="0 0 0 101" height="20"/></border>
<scroll id="container" pad="0 5 0 5" color=".4" dir="col-reverse" justify="flex-end" flood-events="1" grow="1" shrink="1">
<!--layers list-->
</scroll>
<border height="40" color=".5" dir="row" align="center" flood-events="1">
<button-custom id="btn-add" thickness="1" color="0 0" border-color=".0" shrink="1" margin="0 2 0 5">
<icon width="30" icon="add"/>
</button-custom>
<button-custom id="btn-duplicate" thickness="1" color="0 0" border-color=".0" shrink="1" margin="0 2 0 5">
<icon width="30" icon="page_copy"/>
</button-custom>
<button-custom id="btn-up" thickness="1" color="0 0" border-color=".0" shrink="1" margin="0 2 0 0">
<icon width="30" icon="bullet_arrow_up"/>
</button-custom>
<button-custom id="btn-down" thickness="1" color="0 0" border-color=".0" shrink="1" margin="0 2 0 0">
<icon width="30" icon="bullet_arrow_down"/>
</button-custom>
<node grow="1"></node>
<button-custom id="btn-remove" thickness="1" color="0 0" border-color=".0" shrink="1" margin="0 10 0 0">
<icon width="30" icon="bin_closed"/>
</button-custom>
</border>
</node>
</layout>

View File

@@ -5,7 +5,7 @@
>
<layout id="tpl-panel-floating">
<border thickness="1" border-color=".2" pad="3" max-width="650" dir="col" mouse-capture="true">
<border thickness="1" border-color=".2" pad="3" dir="col" mouse-capture="true">
<border height="30" pad="0 0 0 0" color=".4" align="center" justify="center" dir="row">
<button id="button-close" width="30" height="20" text="X" margin="0 0 0 5"/>
<button id="button-minimize" width="30" height="20" text="--" margin="0 0 0 5"/>

View File

@@ -171,6 +171,7 @@ void App::init_sidebar()
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();
};
@@ -190,22 +191,26 @@ void App::init_sidebar()
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();
};
@@ -218,6 +223,7 @@ void App::init_sidebar()
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();
};
@@ -1437,7 +1443,10 @@ void App::initLayout()
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"));
//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->m_container->add_child<NodePanelAnimation>();
};
//layout_designer.load("data/dialogs/panel-animation.xml");
}
@@ -1615,6 +1624,14 @@ void App::ui_restore()
grid->SetWidthP(100);
grid->SetHeightP(100);
break;
case NodePanelFloating::kClass::Animation:
f->m_container->add_child(animation);
//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>();

View File

@@ -1252,6 +1252,16 @@ void Canvas::layer_add(std::string name, std::shared_ptr<Layer> layer /*= nullpt
}
m_current_layer_idx = index;
}
std::shared_ptr<Layer> Canvas::layer_with_id(uint32_t id)
{
auto layer = std::find_if(m_layers.begin(), m_layers.end(),
[id](const auto& l) { return l->id == id; });
if (layer != m_layers.end())
return *layer;
return nullptr;
}
void Canvas::layer_remove(int idx) // m_order index
{
LOG("canvas layer_remove %d", idx);
@@ -1324,6 +1334,21 @@ void Canvas::layer_merge(int source_idx, int dest_idx) // m_layer index
});
}
int Canvas::anim_duration() const noexcept
{
int frames = 1;
for (auto& l : m_layers)
frames = glm::max(frames, l->total_duration());
return frames;
}
void Canvas::anim_goto_frame(int frame) noexcept
{
m_anim_frame = frame;
for (auto& l : m_layers)
l->goto_frame(frame);
}
void Canvas::flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, FloodData& plane_data,
float threshold, glm::vec4 dest_color, std::unique_ptr<glm::vec4>& source_color)
{

View File

@@ -108,6 +108,7 @@ public:
std::unique_ptr<Stroke> m_dual_stroke;
bool m_show_tmp = false;
std::vector<std::shared_ptr<Layer>> m_layers;
int m_anim_frame = 1;
Layer m_layers_merge;
std::vector<glm::vec2> m_plane_shape[6]; // screen space projection of the plane
glm::mat4 m_plane_unproject[6] = SIXPLETTE(glm::mat4(1));
@@ -176,10 +177,13 @@ public:
bool create(int width, int height);
void resize(int width, int height);
Layer& layer() { return *m_layers[m_current_layer_idx]; }
std::shared_ptr<Layer> layer_with_id(uint32_t id);
void layer_remove(int idx);
void layer_add(std::string name, std::shared_ptr<Layer> layer = nullptr, int index = 0);
void layer_order(int idx, int pos);
void layer_merge(int source_idx, int dest_idx);
int anim_duration() const noexcept;
void anim_goto_frame(int frame) noexcept;
void flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, FloodData& plane_data,
float threshold, glm::vec4 dest_color, std::unique_ptr<glm::vec4>& source_color);
void stroke_start(glm::vec3 point, float pressure);

View File

@@ -227,8 +227,23 @@ bool Layer::create(int width, int height, std::string name)
bool Layer::add_frame()
{
m_frames.emplace_back();
m_frame_index = m_frames.size() - 1;
return frame().create(w, h);
return m_frames.back().create(w, h);
}
int Layer::total_duration() const noexcept
{
int duration = 0;
for (auto& f : m_frames)
duration += f.m_duration;
return duration;
}
void Layer::goto_frame(int frame) noexcept
{
int i = 0;
for (i = 0; i < m_frames.size() && frame >= 0; i++)
frame -= m_frames[i].m_duration;
m_frame_index = i - 1;
}
void Layer::resize(int width, int height)

View File

@@ -61,6 +61,8 @@ public:
void resize(int width, int height);
bool create(int width, int height, std::string name);
bool add_frame();
int total_duration() const noexcept;
void goto_frame(int frame) noexcept;
void clear(const glm::vec4& c);
Snapshot snapshot(std::array<glm::vec4, 6>* dirty_box = nullptr, std::array<bool, 6>* dirty_face = nullptr);
TextureCube gen_cube();

View File

@@ -36,6 +36,7 @@
#include "node_usermanual.h"
#include "node_panel_quick.h"
#include "node_tool_bucket.h"
#include "node_panel_animation.h"
void Node::app_redraw()
{
@@ -1390,6 +1391,7 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node)
CASE(kWidget::Changelog, NodeChangelog);
CASE(kWidget::UserManual, NodeUserManual);
CASE(kWidget::ToolBucket, NodeToolBucket);
CASE(kWidget::Timeline, NodeAnimationTimeline);
#undef CASE
case kWidget::Ref:
{

View File

@@ -95,6 +95,7 @@ enum class kWidget : uint16_t
Changelog = const_hash("changelog"),
UserManual = const_hash("usermanual"),
ToolBucket = const_hash("tool-bucket"),
Timeline = const_hash("timeline"),
};
class Node : public std::enable_shared_from_this<Node>

View File

@@ -1,5 +1,8 @@
#include "pch.h"
#include "node_panel_animation.h"
#include "node_button.h"
#include "node_button_custom.h"
#include "canvas.h"
Node* NodePanelAnimation::clone_instantiate() const
{
@@ -22,4 +25,197 @@ void NodePanelAnimation::init()
void NodePanelAnimation::init_controls()
{
m_container = find<NodeScroll>("container");
m_timeline = find<NodeAnimationTimeline>("timeline");
btn_add = find<NodeButtonCustom>("btn-add");
btn_remove = find<NodeButtonCustom>("btn-remove");
btn_up = find<NodeButtonCustom>("btn-up");
btn_down = find<NodeButtonCustom>("btn-down");
btn_duplicate = find<NodeButtonCustom>("btn-duplicate");
btn_add->on_click = [this](Node*) {
Canvas::I->layer().add_frame();
load_layers();
};
btn_up->on_click = [this](Node*) {
auto& layers = Canvas::I->m_layers;
auto layer = std::find_if(layers.begin(), layers.end(),
[id = m_selected_frame_layer_id](const auto& l) { return l->id == id; });
if (layer != layers.end())
(*layer)->m_frames[m_selected_frame_index].m_duration++;
};
btn_up->on_click = [this](Node*) {
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
layer->m_frames[m_selected_frame_index].m_duration++;
load_layers();
};
btn_down->on_click = [this](Node*) {
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
layer->m_frames[m_selected_frame_index].m_duration--;
load_layers();
};
m_timeline->on_frame_changed = [this] (NodeAnimationTimeline* target, int frame) {
LOG("goto frame %d", frame);
Canvas::I->anim_goto_frame(frame);
load_layers();
};
}
void NodePanelAnimation::load_layers()
{
if (!added_to_root())
return;
m_container->remove_all_children();
auto& layers = Canvas::I->m_layers;
m_selected_frame = nullptr;
for (int i = 0; i < layers.size(); i++)
{
auto l = m_container->add_child<NodeAnimationLayer>();
l->set_text(layers[i]->m_name);
l->set_selected(Canvas::I->m_current_layer_idx == i);
l->set_chekcbox(layers[i]->m_visible);
for (int fi = 0; fi < layers[i]->m_frames.size(); fi++)
{
auto b = l->add_frame(layers[i]->m_frames[fi].m_duration);
if (m_selected_frame_layer_id == layers[i]->id && m_selected_frame_index == fi)
{
b->set_active(true);
m_selected_frame = b.get();
}
b->on_click = [this, fi, lid=layers[i]->id] (Node* target) {
auto frame = static_cast<NodeAnimationFrame*>(target);
frame->set_active(true);
if (m_selected_frame)
m_selected_frame->set_active(false);
m_selected_frame = frame;
m_selected_frame_layer_id = lid;
m_selected_frame_index = fi;
};
}
}
}
void NodePanelAnimation::added(Node* parent)
{
parent::added(parent);
load_layers();
}
//////////////////////////////////////////////////////////////////////////
Node* NodeAnimationLayer::clone_instantiate() const
{
return new this_class;
}
void NodeAnimationLayer::clone_finalize(Node* dest) const
{
parent::clone_finalize(dest);
auto n = static_cast<this_class*>(dest);
n->init_controls();
}
void NodeAnimationLayer::init()
{
parent::init();
init_template_file("data/dialogs/panel-animation.xml", "tpl-layer");
init_controls();
}
void NodeAnimationLayer::init_controls()
{
m_label = find<NodeText>("label");
m_visibility = find<NodeCheckBox>("cb");
m_container = find<NodeScroll>("container");
}
void NodeAnimationLayer::draw()
{
auto c = m_selected ? m_color_selected : m_color_normal;
m_thinkness = m_selected ? 1.f : 0.f;
m_color = m_mouse_inside ? m_color_hover : c;
parent::draw();
}
std::shared_ptr<NodeAnimationFrame> NodeAnimationLayer::add_frame(int duration)
{
auto b = m_container->add_child_ref<NodeAnimationFrame>();
b->SetWidth(30 * duration + 5 * (duration - 1));
//b->SetFlexGrow(duration);
b->SetHeightP(100);
b->SetMargin(0, 5, 0, 0);
return b;
}
//////////////////////////////////////////////////////////////////////////
void NodeAnimationTimeline::draw()
{
parent::draw();
ShaderManager::use(kShader::Color);
ShaderManager::u_vec4(kShaderUniform::Col, m_cursor_color);
glDisable(GL_BLEND);
float step = 35.f;
glm::vec2 cur_pos = {
m_pos.x + step * m_frame + step * 0.5f,
m_pos.y + m_size.y * 0.5f
};
ShaderManager::u_mat4(kShaderUniform::MVP, m_proj
* glm::translate(glm::vec3(cur_pos, 0))
* glm::scale(glm::vec3(step * 0.25f, m_size.y * 0.5f, 1))
);
m_plane.draw_fill();
//bool scissor = glIsEnabled(GL_SCISSOR_TEST);
//glDisable(GL_SCISSOR_TEST);
ShaderManager::u_mat4(kShaderUniform::MVP, m_proj
* glm::translate(glm::vec3(cur_pos, 0))
* glm::scale(glm::vec3(step * 0.15f, m_size.y * 0.5f, 1))
* glm::translate(glm::vec3(0, .5, 0))
);
m_plane.draw_fill();
//scissor ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST);
}
kEventResult NodeAnimationTimeline::handle_event(Event* e)
{
parent::handle_event(e);
static int signaled_frame = -1;
auto me = static_cast<MouseEvent*>(e);
auto ge = static_cast<GestureEvent*>(e);
auto update = [&](){
auto loc = me->m_pos - m_pos;
m_frame = glm::clamp((int)glm::floor(loc.x / 35.f), 0, Canvas::I->anim_duration() - 1);
if (on_frame_changed && signaled_frame != m_frame)
on_frame_changed(this, m_frame);
signaled_frame = m_frame;
};
switch (e->m_type)
{
case kEventType::MouseDownL:
mouse_capture();
m_dragging = true;
m_drag_start_frame = m_frame;
update();
break;
case kEventType::MouseMove:
if (m_dragging)
update();
break;
case kEventType::MouseUpL:
m_dragging = false;
mouse_release();
break;
case kEventType::MouseCancel:
m_dragging = false;
m_frame = m_drag_start_frame;
mouse_release();
break;
default:
return kEventResult::Available;
break;
}
return kEventResult::Consumed;
}

View File

@@ -1,12 +1,98 @@
#include "node_border.h"
#pragma once
class NodePanelAnimation : public NodeBorder
#include "node_border.h"
#include "node_scroll.h"
#include "node_text.h"
#include "node_checkbox.h"
#include "node_button_custom.h"
class NodeAnimationFrame : public NodeButtonCustom
{
public:
using this_class = NodePanelAnimation;
std::function<void(NodeAnimationFrame* target)> on_selected;
using this_class = NodeAnimationFrame;
using parent = NodeButtonCustom;
virtual Node* clone_instantiate() const override { return new this_class; }
};
//////////////////////////////////////////////////////////////////////////
class NodeAnimationTimeline : public NodeBorder
{
bool m_dragging = false;
glm::vec2 m_drag_start_pos = { 0, 0 };
int m_drag_start_frame = 0;
public:
using this_class = NodeAnimationTimeline;
using parent = NodeBorder;
std::function<void(NodeAnimationTimeline* target, int frame)> on_frame_changed;
int m_frames_count = 1;
int m_frame = 0;
glm::vec4 m_cursor_color = { 1, 0, 0, 1 };
virtual Node* clone_instantiate() const override { return new this_class; }
virtual void draw() override;
virtual kEventResult handle_event(Event* e) override;
};
//////////////////////////////////////////////////////////////////////////
class NodePanelAnimation : public Node
{
NodeButtonCustom* btn_add = nullptr;
NodeButtonCustom* btn_remove = nullptr;
NodeButtonCustom* btn_up = nullptr;
NodeButtonCustom* btn_down = nullptr;
NodeButtonCustom* btn_duplicate = nullptr;
NodeScroll* m_container = nullptr;
NodeAnimationFrame* m_selected_frame = nullptr;
NodeAnimationTimeline* m_timeline = nullptr;
int m_selected_frame_index = -1;
uint32_t m_selected_frame_layer_id = 0;
public:
using this_class = NodePanelAnimation;
using parent = Node;
virtual Node* clone_instantiate() const override;
virtual void clone_finalize(Node* dest) const override;
virtual void init() override;
virtual void added(Node* parent) override;
void init_controls();
void load_layers();
};
//////////////////////////////////////////////////////////////////////////
class NodeAnimationLayer : public NodeBorder
{
bool m_selected = false;
glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1);
glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1);
glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1);
NodeText* m_label = nullptr;
NodeCheckBox* m_visibility = nullptr;
NodeScroll* m_container = nullptr;
public:
std::function<void(NodeAnimationLayer* target)> on_selected;
std::function<void(NodeAnimationLayer* target, bool visible)> on_visibility_changed;
std::function<void(NodeAnimationLayer* target, bool highlight)> on_highlight;
using this_class = NodeAnimationLayer;
using parent = NodeBorder;
virtual Node* clone_instantiate() const override;
virtual void clone_finalize(Node* dest) const override;
virtual void init() override;
virtual void draw() override;
void init_controls();
std::shared_ptr<NodeAnimationFrame> add_frame(int duration);
void set_text(const std::string& text) { m_label->set_text(text.c_str()); }
void set_selected(bool selected) { m_selected = selected; }
void set_chekcbox(bool checked) { m_visibility->set_value(checked); }
};

View File

@@ -114,7 +114,9 @@ kEventResult NodePanelFloating::handle_event(Event* e)
m_outline->SetPosition(m_parent->m_pos + m_drag_start_pos + me->m_pos - m_drag_start_cur);
m_outline->SetSize(GetSize());
auto nodes = root()->find("ui-root")->get_children_at_point(me->m_pos);
std::vector<std::shared_ptr<Node>> nodes;
if (auto uir = root()->find("ui-root"))
nodes = uir->get_children_at_point(me->m_pos);
bool docked = false;
for (auto const& c : nodes)
{
@@ -181,7 +183,9 @@ kEventResult NodePanelFloating::handle_event(Event* e)
m_drop_placeholder->destroy();
m_drop_placeholder->remove_from_parent();
}
auto nodes = root()->find("ui-root")->get_children_at_point(me->m_pos);
std::vector<std::shared_ptr<Node>> nodes;
if (auto uir = root()->find("ui-root"))
nodes = uir->get_children_at_point(me->m_pos);
bool docked = false;
auto ref = m_parent->m_children[m_parent->get_child_index(this)];
for (auto const& c : nodes)

View File

@@ -101,6 +101,9 @@ void NodeLayer::set_name(const char* s)
m_label_text = s;
m_label->set_text(s);
}
//////////////////////////////////////////////////////////////////////////
Node* NodePanelLayer::clone_instantiate() const
{
return new NodePanelLayer();

View File

@@ -40,6 +40,7 @@
#include <dirent.h>
#include <dispatch/dispatch.h>
#define BT_SetTerminate void
#define WITH_CURL 1
#elif __ANDROID__
@@ -58,6 +59,7 @@
#define __block
//#define STBI_NEON
#define BT_SetTerminate void
#define WITH_CURL 1
#elif _WIN32
@@ -83,6 +85,7 @@
#define SHADER_VERSION "#version 150\n"
#define PP_OS "win"
#define __block
#define WITH_CURL 1
#elif __linux__
@@ -102,6 +105,7 @@
#define __GL__ 1
#define __block
#define BT_SetTerminate void
#define WITH_CURL 1
#elif defined(EMSCRIPTEN)