diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt
index 509d2a0..fc25afe 100644
--- a/android/CMakeLists.txt
+++ b/android/CMakeLists.txt
@@ -36,6 +36,9 @@ add_library(
../engine/shader.cpp
../engine/shape.cpp
../engine/app.cpp
+ ../engine/app_events.cpp
+ ../engine/app_layout.cpp
+ ../engine/app_shaders.cpp
../engine/brush.cpp
../engine/canvas.cpp
../engine/canvas_modes.cpp
@@ -65,6 +68,7 @@ add_library(
../engine/node_text.cpp
../engine/node_text_input.cpp
../engine/node_viewport.cpp
+ ../engine/node_scroll.cpp
)
target_include_directories(native-lib PRIVATE
diff --git a/data/layout.xml b/data/layout.xml
index 0649ddd..89ae41a 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -382,7 +382,7 @@
-
+
@@ -391,7 +391,7 @@
-
+
diff --git a/engine.vcxproj b/engine.vcxproj
index 7c7b6fc..5832870 100644
--- a/engine.vcxproj
+++ b/engine.vcxproj
@@ -152,6 +152,9 @@
+
+
+
@@ -180,6 +183,7 @@
+
@@ -247,6 +251,7 @@
+
diff --git a/engine.vcxproj.filters b/engine.vcxproj.filters
index 2a31841..4cd7ca9 100644
--- a/engine.vcxproj.filters
+++ b/engine.vcxproj.filters
@@ -156,6 +156,18 @@
Source Files\ui
+
+ Source Files\ui
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -284,5 +296,8 @@
Header Files\ui
+
+ Header Files
+
\ No newline at end of file
diff --git a/engine/app.cpp b/engine/app.cpp
index eba3f75..a14fe08 100644
--- a/engine/app.cpp
+++ b/engine/app.cpp
@@ -21,333 +21,6 @@ void App::clear()
glClear(GL_COLOR_BUFFER_BIT);
}
-void App::initShaders()
-{
- static const char* shader_v =
- SHADER_VERSION
- "uniform mat4 mvp;"
- "in vec4 pos;"
- "in vec2 uvs;"
- "out vec3 uv;"
- "void main(){"
- " uv = vec3(uvs, pos.w);"
- " gl_Position = mvp * vec4(pos.xyz, 1.0);"
- "}";
- static const char* shader_f =
- SHADER_VERSION
- "uniform sampler2D tex;"
- "in mediump vec3 uv;"
- "out mediump vec4 frag;"
- "void main(){"
- //" frag = texture(tex, uv.xy/uv.z);"
- " frag = texture(tex, uv.xy);"
- "}";
- static const char* shader_uv_f =
- SHADER_VERSION
- "uniform sampler2D tex;"
- "in mediump vec3 uv;"
- "out mediump vec4 frag;"
- "void main(){"
- " frag = vec4(uv.xy, 0.0, 1.0);"
- "}";
- // TEXTURE ALPHA
- static const char* shader_alpha_f =
- SHADER_VERSION
- "uniform sampler2D tex;\n"
- "uniform mediump float alpha;\n"
- "in mediump vec3 uv;\n"
- "out mediump vec4 frag;\n"
- "void main(){\n"
- " frag = texture(tex, uv.xy) * vec4(1,1,1,alpha);\n"
- "}\n";
-
- // TEXTURE ATLAS
- static const char* shader_atlas_v =
- SHADER_VERSION
- "uniform mat4 mvp;"
- "uniform vec2 tof;"
- "uniform vec2 tsz;"
- "in vec2 pos;"
- "in vec2 uvs;"
- "out vec2 uv;"
- "void main(){"
- " uv = tof + uvs * tsz;"
- " gl_Position = mvp * vec4(pos, 0.0, 1.0);"
- "}";
- static const char* shader_atlas_f =
- SHADER_VERSION
- "uniform sampler2D tex;"
- "in mediump vec2 uv;"
- "out mediump vec4 frag;"
- "void main(){"
- " frag = texture(tex, uv);"
- "}";
-
- // SOLID COLOR
- static const char* shader_color_v =
- SHADER_VERSION
- "uniform mat4 mvp;"
- "in vec4 pos;"
- "void main(){"
- " gl_Position = mvp * pos;"
- " gl_PointSize = 15.0;"
- "}";
- static const char* shader_color_f =
- SHADER_VERSION
- "uniform mediump vec4 col;"
- "out mediump vec4 frag;"
- "void main(){"
- " frag = col;"
- "}";
-
- // COLOR QUAD
- static const char* shader_color_quad_v =
- SHADER_VERSION
- "uniform mat4 mvp;"
- "in vec4 pos;"
- "in vec2 uvs;"
- "out vec3 uv;"
- "void main(){"
- " gl_Position = mvp * pos;"
- " uv = vec3(uvs, pos.w);"
- "}";
- static const char* shader_color_quad_f =
- SHADER_VERSION
- "uniform mediump vec4 col;"
- "in mediump vec3 uv;"
- "out mediump vec4 frag;"
- "void main(){"
- " mediump vec4 gradient_x = mix(col, vec4(1.0, 1.0, 1.0, 1.0), uv.x);"
- " frag = mix(gradient_x, vec4(0.0, 0.0, 0.0, 1.0), uv.y);"
- "}";
-
- // HUE
- static const char* shader_color_hue_v =
- SHADER_VERSION
- "uniform mat4 mvp;"
- "in vec4 pos;"
- "in vec2 uvs;"
- "out vec3 uv;"
- "void main(){"
- " gl_Position = mvp * pos;"
- " uv = vec3(uvs, pos.w);"
- "}";
- static const char* shader_color_hue_f =
- SHADER_VERSION
- "uniform mediump vec4 col;"
- "in mediump vec3 uv;"
- "out mediump vec4 frag;"
- "mediump vec3 hsv2rgb(mediump vec3 c) {"
- " mediump vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);"
- " mediump vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);"
- " return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);"
- "}"
- "void main(){"
- " frag = vec4(hsv2rgb(vec3(uv.y, 1.0, 1.0)), 1.0);"
- "}";
-
- // FONT
- static const char* shader_font_v =
- SHADER_VERSION
- "uniform mat4 mvp;"
- "in vec2 pos;"
- "in vec2 uvs;"
- "out vec2 uv;"
- "void main(){"
- " uv = uvs;"
- " gl_Position = mvp * vec4(pos, 0.0, 1.0);"
- "}";
- static const char* shader_font_f =
- SHADER_VERSION
- "uniform mediump sampler2D tex;"
- "uniform mediump vec4 col;"
- "in mediump vec2 uv;"
- "out mediump vec4 frag;"
- "void main(){"
- " mediump float a = texture(tex, uv).r;"
- " frag = vec4(col.rgb, a);"
- "}";
-
- // STROKE
- static const char* shader_stroke_v =
- SHADER_VERSION
- "uniform mat4 mvp;\n"
- "in vec4 pos;\n"
- "in vec2 uvs;\n"
- "out vec2 uv;\n"
- "void main(){\n"
- " uv = uvs;\n"
- " gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
- "}\n";
- static const char* shader_stroke_f =
- SHADER_VERSION
- "uniform mediump sampler2D tex;\n"
- "uniform mediump sampler2D tex_bg;\n"
- "uniform mediump vec4 col;\n"
- "uniform mediump vec2 resolution;\n"
- "uniform mediump float alpha;\n"
- "in mediump vec2 uv;\n"
- "out mediump vec4 frag;\n"
- "void main(){\n"
- " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
- " mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n"
- " mediump vec4 fg = vec4(col.rgb, brush_alpha);\n"
- " mediump vec4 bg = texture(tex_bg, uv2);\n"
- " if (fg.a < (1.0/255.0)) { frag = bg; return; }\n"
- " mediump float alpha_tot = fg.a + (1.0 - fg.a) * bg.a;"
- " mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
- " frag = vec4(rgb, alpha_tot);\n"
- "}\n";
- // ALPHA LOCK
- static const char* shader_stroke_lock_f =
- SHADER_VERSION
- "uniform mediump sampler2D tex;\n"
- "uniform mediump sampler2D tex_bg;\n"
- "uniform mediump sampler2D tex_mask;\n"
- "uniform mediump vec4 col;\n"
- "uniform mediump vec2 resolution;\n"
- "uniform mediump float alpha;\n"
- "in mediump vec2 uv;\n"
- "out mediump vec4 frag;\n"
- "void main(){\n"
- " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
- " mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n"
- " mediump vec4 fg = vec4(col.rgb, brush_alpha);\n"
- " mediump vec4 bg = texture(tex_bg, uv2);\n"
- " mediump vec4 msk = texture(tex_mask, uv2);\n"
- " if (fg.a < (1.0/255.0)) { frag = bg; return; }\n"
- " mediump float alpha_tot = fg.a + (1.0 - fg.a) * bg.a;"
- " mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
- " frag = vec4(rgb, min(alpha_tot, msk.a));\n"
- "}\n";
- // ERASER
- static const char* shader_stroke_erase_f =
- SHADER_VERSION
- "uniform mediump sampler2D tex;\n"
- "uniform mediump sampler2D tex_bg;\n"
- "uniform mediump vec4 col;\n"
- "uniform mediump vec2 resolution;\n"
- "uniform mediump float alpha;\n"
- "in mediump vec2 uv;\n"
- "out mediump vec4 frag;\n"
- "void main(){\n"
- " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
- " mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n"
- " mediump vec4 bg = texture(tex_bg, uv2);\n"
- " frag = vec4(bg.rgb, bg.a - bg.a * brush_alpha);\n"
- "}\n";
-
- // STROKE LAYER BLEND
- static const char* shader_stroke_layer_v =
- SHADER_VERSION
- "uniform mat4 mvp;"
- "in vec4 pos;"
- "in vec2 uvs;"
- "out vec2 uv;"
- "void main(){"
- " uv = uvs;"
- " gl_Position = mvp * vec4(pos.xyz, 1.0);"
- "}";
- static const char* shader_stroke_layer_f =
- SHADER_VERSION
- "uniform mediump sampler2D tex;\n"
- "uniform mediump sampler2D tex_bg;\n"
- "uniform mediump vec2 resolution;\n"
- "uniform mediump float alpha;\n"
- "in mediump vec2 uv;\n"
- "out mediump vec4 frag;\n"
- "void main(){\n"
- " mediump vec4 fg = texture(tex, uv) * vec4(1,1,1,alpha);\n"
- " mediump vec4 bg = texture(tex_bg, uv);\n"
- " if (fg.a < (1.0/255.0)) { frag = bg; return; }\n"
- " mediump float alpha_tot = fg.a + (1.0 - fg.a) * bg.a;"
- " mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
- " frag = vec4(rgb, alpha_tot);\n"
- "}\n";
-
- static const char* shader_checkerboard_v =
- SHADER_VERSION
- "uniform mat4 mvp;\n"
- "in vec4 pos;\n"
- "in vec2 uvs;\n"
- "out vec2 uv;\n"
- "void main(){\n"
- " uv = uvs;\n"
- " gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
- "}";
- static const char* shader_checkerboard_f =
- SHADER_VERSION
- "in mediump vec2 uv;\n"
- "out mediump vec4 frag;\n"
- "void main(){\n"
- " const mediump vec4 c1 = vec4(1.0, 1.0, 1.0, 1.0);\n"
- " const mediump vec4 c2 = vec4(0.9, 0.9, 0.9, 1.0);\n"
- " mediump vec2 c = floor(fract(uv * 10.0) * 2.0);\n"
- " mediump float alpha = mix(c.x, 1.0 - c.x, c.y);\n"
- " frag = mix(c1, c2, alpha);\n"
- "}";
-
- static const char* shader_equirect_v =
- SHADER_VERSION
- "#define PI 3.1415926535897932384626433832795\n"
- "#define TWO_PI 6.283185307179586476925286766559\n"
- "uniform mat4 mvp;\n"
- "in vec4 pos;\n"
- "in vec2 uvs;\n"
- "out vec2 uv;\n"
- "void main(){\n"
- " uv = (vec2(1.0) - uvs + vec2(0.25,0.0)) * vec2(TWO_PI, PI);\n"
- " gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
- "}";
- static const char* shader_equirect_f =
- SHADER_VERSION
- "uniform samplerCube tex;\n"
- "in highp vec2 uv;\n"
- "out mediump vec4 frag;\n"
- "void main(){\n"
- " highp float anglex = uv.x;\n"
- " highp float angley = uv.y;\n"
- " highp float sx = sin(anglex);\n"
- " highp float cx = cos(anglex);\n"
- " highp vec3 dir = vec3(0.0, 0.0, 0.0);\n"
- " dir.x = sin(angley) * cx;\n"
- " dir.y = cos(angley);\n"
- " dir.z = sin(angley) * sx;\n"
- " frag = texture(tex, dir);\n"
- "}";
-
- LOG("initializing shaders");
- if (!ShaderManager::create(kShader::Texture, shader_v, shader_f))
- LOG("Failed to create shader Texture");
- if (!ShaderManager::create(kShader::TextureAlpha, shader_v, shader_alpha_f))
- LOG("Failed to create shader TextureAlpha");
- if (!ShaderManager::create(kShader::Color, shader_color_v, shader_color_f))
- LOG("Failed to create shader Color");
- if (!ShaderManager::create(kShader::ColorQuad, shader_color_quad_v, shader_color_quad_f))
- LOG("Failed to create shader ColorQuad");
- if (!ShaderManager::create(kShader::ColorHue, shader_color_hue_v, shader_color_hue_f))
- LOG("Failed to create shader ColorHue");
- if (!ShaderManager::create(kShader::UVs, shader_v, shader_uv_f))
- LOG("Failed to create shader UVs");
- if (!ShaderManager::create(kShader::Font, shader_font_v, shader_font_f))
- LOG("Failed to create shader Font");
- if (!ShaderManager::create(kShader::Atlas, shader_atlas_v, shader_atlas_f))
- LOG("Failed to create shader Atlas");
- if (!ShaderManager::create(kShader::Stroke, shader_stroke_v, shader_stroke_f))
- LOG("Failed to create shader Stroke");
- if (!ShaderManager::create(kShader::StrokeLock, shader_stroke_v, shader_stroke_lock_f))
- LOG("Failed to create shader StrokeLock");
- if (!ShaderManager::create(kShader::StrokeErase, shader_stroke_v, shader_stroke_erase_f))
- LOG("Failed to create shader StrokeErase");
- if (!ShaderManager::create(kShader::StrokeLayer, shader_stroke_layer_v, shader_stroke_layer_f))
- LOG("Failed to create shader StrokeLayer");
- if (!ShaderManager::create(kShader::Checkerboard, shader_checkerboard_v, shader_checkerboard_f))
- LOG("Failed to create shader Checkerboard");
- if (!ShaderManager::create(kShader::Equirect, shader_equirect_v, shader_equirect_f))
- LOG("Failed to create shader Equirect");
- LOG("shaders initialized");
-}
-
void App::initAssets()
{
LOG("initializing assets");
@@ -364,383 +37,11 @@ void App::initAssets()
LOG("initializing assets completed");
}
-void App::initLayout()
-{
- LOG("initializing layout statics");
- NodeBorder::static_init();
- NodeImage::static_init();
- NodeIcon::static_init();
- NodeStrokePreview::static_init();
-
- layout.on_loaded = [&] {
- LOG("initializing layout updating after load");
- layout[main_id]->update(width, height, zoom);
-
- LOG("initializing layout components");
- sidebar = layout[main_id]->find("sidebar");
- panels = layout[main_id]->find("panels");
- canvas = layout[main_id]->find("paint-canvas");
- canvas->data_path = data_path;
-
- //brushes = layout[main_id]->find("panel-brush");
- //layers = layout[main_id]->find("panel-layer");
- //color = layout[main_id]->find("panel-color");
- //stroke = layout[main_id]->find("panel-stroke");
-
- brushes = std::make_shared();
- brushes->m_manager = &layout;
- brushes->init();
- brushes->create();
- brushes->loaded();
-
- layers = std::make_shared();
- layers->m_manager = &layout;
- layers->init();
- layers->create();
- layers->loaded();
-
- color = std::make_shared();
- color->m_manager = &layout;
- color->init();
- color->create();
- color->loaded();
-
- stroke = std::make_shared();
- stroke->m_manager = &layout;
- stroke->init();
- stroke->create();
- stroke->loaded();
-
- if (canvas)
- {
- stroke->m_canvas->m_brush.m_tip_color = color->m_color;
- stroke->m_canvas->draw_stroke();
- canvas->m_brush = stroke->m_canvas->m_brush;
- }
-
- brushes->on_brush_changed = [this](Node* target, int index) {
- auto tid = brushes->get_texture_id(index);
- stroke->m_canvas->m_brush.m_tex_id = tid;
- stroke->m_canvas->draw_stroke();
- canvas->m_brush = stroke->m_canvas->m_brush;
- if (on_brush_select)
- on_brush_select(index);
- };
-
- color->on_color_changed = [this](Node* target, glm::vec4 color) {
- stroke->m_canvas->m_brush.m_tip_color = color;
-// stroke->m_canvas->draw_stroke();
- if (canvas)
- canvas->m_brush = stroke->m_canvas->m_brush;
- if (on_color_change)
- on_color_change(color);
- };
-
- stroke->on_stroke_change = [this](Node*target) {
- if (canvas)
- canvas->m_brush = stroke->m_canvas->m_brush;
- if (on_stroke_change)
- on_stroke_change();
- };
-
- layers->on_layer_add = [this](Node*) {
- canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str());
- };
-
- layers->on_layer_change = [this](Node*, int old_idx, int new_idx) {
- canvas->m_canvas->m_current_layer_idx = canvas->m_canvas->m_order[new_idx];
- };
-
- layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
- canvas->m_canvas->layer_order(old_idx, new_idx);
- };
-
- layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
- canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_opacity = value;
- };
-
- layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
- canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_alpha_locked = visible;
- };
-
- canvas->m_canvas->layer_add("Default");
- layers->add_layer("Default");
-
- static glm::vec4 color_button_normal{.1, .1, .1, 1};
- static glm::vec4 color_button_hlight{ 1, .0, .0, 1};
- if (auto* button = layout[main_id]->find("btn-pen"))
- {
- button->on_click = [this](Node*) {
- layout[main_id]->find("btn-pen")->set_color(color_button_hlight);
- layout[main_id]->find("btn-erase")->set_color(color_button_normal);
- layout[main_id]->find("btn-line")->set_color(color_button_normal);
- layout[main_id]->find("btn-cam")->set_color(color_button_normal);
- layout[main_id]->find("btn-grid")->set_color(color_button_normal);
- layout[main_id]->find("btn-fill")->set_color(color_button_normal);
- Canvas::set_mode(Canvas::kCanvasMode::Draw);
- };
- layout[main_id]->find("btn-pen")->set_color(color_button_hlight);
- Canvas::set_mode(Canvas::kCanvasMode::Draw);
- }
- if (auto* button = layout[main_id]->find("btn-erase"))
- {
- button->on_click = [this](Node*) {
- layout[main_id]->find("btn-pen")->set_color(color_button_normal);
- layout[main_id]->find("btn-erase")->set_color(color_button_hlight);
- layout[main_id]->find("btn-line")->set_color(color_button_normal);
- layout[main_id]->find("btn-cam")->set_color(color_button_normal);
- layout[main_id]->find("btn-grid")->set_color(color_button_normal);
- layout[main_id]->find("btn-fill")->set_color(color_button_normal);
- Canvas::set_mode(Canvas::kCanvasMode::Erase);
- };
- }
- if (auto* button = layout[main_id]->find("btn-line"))
- {
- button->on_click = [this](Node*) {
- layout[main_id]->find("btn-pen")->set_color(color_button_normal);
- layout[main_id]->find("btn-erase")->set_color(color_button_normal);
- layout[main_id]->find("btn-line")->set_color(color_button_hlight);
- layout[main_id]->find("btn-cam")->set_color(color_button_normal);
- layout[main_id]->find("btn-grid")->set_color(color_button_normal);
- layout[main_id]->find("btn-fill")->set_color(color_button_normal);
- Canvas::set_mode(Canvas::kCanvasMode::Line);
- };
- }
- if (auto* button = layout[main_id]->find("btn-cam"))
- {
- button->on_click = [this](Node*) {
- layout[main_id]->find("btn-pen")->set_color(color_button_normal);
- layout[main_id]->find("btn-erase")->set_color(color_button_normal);
- layout[main_id]->find("btn-line")->set_color(color_button_normal);
- layout[main_id]->find("btn-cam")->set_color(color_button_hlight);
- layout[main_id]->find("btn-grid")->set_color(color_button_normal);
- layout[main_id]->find("btn-fill")->set_color(color_button_normal);
- Canvas::set_mode(Canvas::kCanvasMode::Camera);
- };
- }
- if (auto* button = layout[main_id]->find("btn-grid"))
- {
- button->on_click = [this](Node*) {
- layout[main_id]->find("btn-pen")->set_color(color_button_normal);
- layout[main_id]->find("btn-erase")->set_color(color_button_normal);
- layout[main_id]->find("btn-line")->set_color(color_button_normal);
- layout[main_id]->find("btn-cam")->set_color(color_button_normal);
- layout[main_id]->find("btn-grid")->set_color(color_button_hlight);
- layout[main_id]->find("btn-fill")->set_color(color_button_normal);
- Canvas::set_mode(Canvas::kCanvasMode::Grid);
- };
- }
- if (auto* button = layout[main_id]->find("btn-fill"))
- {
- button->on_click = [this](Node*) {
- layout[main_id]->find("btn-pen")->set_color(color_button_normal);
- layout[main_id]->find("btn-erase")->set_color(color_button_normal);
- layout[main_id]->find("btn-line")->set_color(color_button_normal);
- layout[main_id]->find("btn-cam")->set_color(color_button_normal);
- layout[main_id]->find("btn-grid")->set_color(color_button_normal);
- layout[main_id]->find("btn-fill")->set_color(color_button_hlight);
- Canvas::set_mode(Canvas::kCanvasMode::Fill);
- };
- }
- if (auto* button = layout[main_id]->find("btn-bucket"))
- {
- button->on_click = [this](Node*) {
- canvas->m_canvas->clear(canvas->m_brush.m_tip_color);
- };
- }
- if (auto* button = layout[main_id]->find("btn-stroke"))
- {
- button->on_click = [this, button](Node*) {
- panels->get_child_index(stroke.get()) == -1 ? panels->add_child(stroke) : panels->remove_child(stroke.get());
- button->set_color(panels->get_child_index(stroke.get()) == -1 ? color_button_normal : color_button_hlight);
- };
- }
- if (auto* button = layout[main_id]->find("btn-brush"))
- {
- button->on_click = [this, button](Node*) {
- panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get());
- button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
- };
- }
- if (auto* button = layout[main_id]->find("btn-color"))
- {
- button->on_click = [this, button](Node*) {
- panels->get_child_index(color.get()) == -1 ? panels->add_child(color) : panels->remove_child(color.get());
- button->set_color(panels->get_child_index(color.get()) == -1 ? color_button_normal : color_button_hlight);
- };
- }
- if (auto* button = layout[main_id]->find("btn-layer"))
- {
- button->on_click = [this, button](Node*) {
- panels->get_child_index(layers.get()) == -1 ? panels->add_child(layers) : panels->remove_child(layers.get());
- button->set_color(panels->get_child_index(layers.get()) == -1 ? color_button_normal : color_button_hlight);
- };
- }
-
- if (auto* button = layout[main_id]->find("btn-export"))
- {
- button->on_click = [this,button](Node*) {
- if (canvas)
- {
- canvas->m_canvas->export_equirectangular(data_path);
- }
- };
- }
- if (auto* button = layout[main_id]->find("btn-anim"))
- {
- button->on_click = [this,button](Node*) {
- if (canvas)
- {
- //canvas->m_canvas->export_anim(data_path);
- }
- };
- }
- if (auto* button = layout[main_id]->find("btn-open"))
- {
- button->on_click = [this,button](Node*) {
- if (canvas)
- {
- // load thumbnail test
- auto open_dialog = std::make_shared();
- open_dialog->m_manager = &layout;
- open_dialog->data_path = data_path;
- open_dialog->init();
- open_dialog->create();
- open_dialog->loaded();
-
- layout[main_id]->add_child(open_dialog);
- layout[main_id]->update();
-
- open_dialog->btn_ok->on_click = [this,open_dialog](Node*)
- {
- layers->clear();
- canvas->m_canvas->project_open(data_path);
- for (auto& i : canvas->m_canvas->m_order)
- layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
- open_dialog->destroy();
- };
- }
- };
- }
- if (auto* button = layout[main_id]->find("btn-save"))
- {
- button->on_click = [this,button](Node*) {
- if (canvas)
- {
- canvas->m_canvas->project_save(data_path);
- }
- };
- }
- if (auto* button = layout[main_id]->find("btn-undo"))
- {
- button->on_click = [this,button](Node*) {
- if (!ActionManager::empty())
- ActionManager::undo();
- };
- }
- if (auto* button = layout[main_id]->find("btn-clean-memory"))
- {
- button->on_click = [this](Node*) {
- ActionManager::clear();
- };
- }
- if (auto* button = layout[main_id]->find("btn-clear"))
- {
- button->on_click = [this](Node*) {
- //exit(0);
- if (canvas)
- canvas->m_canvas->clear({ 0, 0, 0, 0 });
- };
- }
- if (auto* button = layout[main_id]->find("btn-popup"))
- {
- button->on_click = [this](Node*) {
- msgbox = new NodeMessageBox();
- msgbox->m_manager = &layout;
- msgbox->init();
- layout[main_id]->add_child(msgbox);
- layout[main_id]->update();
- };
- }
- if (auto* button = layout[main_id]->find("btn-settings"))
- {
- button->on_click = [this](Node*) {
- settings = new NodeSettings();
- settings->m_manager = &layout;
- settings->init();
- layout[main_id]->add_child(settings);
- layout[main_id]->update();
- };
- }
- if (auto* menu_file = layout[main_id]->find("menu-file"))
- {
- menu_file->on_click = [=](Node*) {
- glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
- popup = (NodePopupMenu*)layout[const_hash("file-menu")]->m_children[0]->clone();
- popup->SetPositioning(YGPositionTypeAbsolute);
- popup->SetPosition(pos.x, pos.y);
- layout[main_id]->add_child(popup);
- layout[main_id]->update();
- popup->mouse_capture();
- popup->m_mouse_ignore = false;
- };
- }
- if (auto* menu_file = layout[main_id]->find("menu-edit"))
- {
- menu_file->on_click = [=](Node*) {
- glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
- popup = (NodePopupMenu*)layout[const_hash("edit-menu")]->m_children[0]->clone();
- popup->SetPositioning(YGPositionTypeAbsolute);
- popup->SetPosition(pos.x, pos.y);
- layout[main_id]->add_child(popup);
- layout[main_id]->update();
- popup->mouse_capture();
- popup->m_mouse_ignore = false;
- };
- }
- if (auto* menu_file = layout[main_id]->find("menu-layers"))
- {
- menu_file->on_click = [=](Node*) {
- glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
- popup = (NodePopupMenu*)layout[const_hash("layers-menu")]->m_children[0]->clone();
- popup->SetPositioning(YGPositionTypeAbsolute);
- popup->SetPosition(pos.x, pos.y);
- layout[main_id]->add_child(popup);
- layout[main_id]->update();
- popup->mouse_capture();
- popup->m_mouse_ignore = false;
- popup->find("clear-grids")->on_click = [this](Node*) {
- CanvasModeGrid* mode = (CanvasModeGrid*)ui::Canvas::modes[(int)ui::Canvas::kCanvasMode::Grid][0];
- mode->clear();
- popup->mouse_release();
- popup->destroy();
- };
- };
- }
- if (auto* toolbar = layout[main_id]->find("toolbar"))
- {
- toolbar->m_flood_events = true;
- }
- };
- LOG("initializing layout xml");
- if (layout.m_loaded)
- {
- LOG("restore layout");
- layout.restore_context();
- if (panels->get_child_index(brushes.get()) == -1) brushes->restore_context();
- if (panels->get_child_index(layers.get()) == -1) layers->restore_context();
- if (panels->get_child_index(color.get()) == -1) color->restore_context();
- if (panels->get_child_index(stroke.get()) == -1) stroke->restore_context();
- }
- else
- layout.load("data/layout.xml");
- LOG("initializing layout completed");
-}
-
void App::initLog()
{
LogRemote::I.start();
}
+
void App::init()
{
#ifdef _WIN32
@@ -836,121 +137,6 @@ void App::update(float dt)
glDisable(GL_SCISSOR_TEST);
}
-void App::resize(float w, float h)
-{
- width = w;
- height = h;
- if (auto* main = layout[main_id])
- main->update(w , h, zoom);
-}
-
-bool App::mouse_down(int button, float x, float y)
-{
- MouseEvent e;
- e.m_type = button ? kEventType::MouseDownR : kEventType::MouseDownL;
- e.m_pos = { x / zoom, y / zoom };
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::mouse_move(float x, float y)
-{
- MouseEvent e;
- e.m_type = kEventType::MouseMove;
- e.m_pos = { x / zoom, y / zoom };
- kEventResult ret = kEventResult::Available;
- if (auto* main = layout[main_id])
- ret = main->on_event(&e);
- return ret == kEventResult::Consumed;
-}
-bool App::mouse_up(int button, float x, float y)
-{
- MouseEvent e;
- e.m_type = button ? kEventType::MouseUpR : kEventType::MouseUpL;
- e.m_pos = { x / zoom, y / zoom };
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::mouse_scroll(float x, float y, float delta)
-{
- MouseEvent e;
- e.m_type = kEventType::MouseScroll;
- e.m_pos = { x / zoom, y / zoom };
- e.m_scroll_delta = delta * 0.1f;
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::mouse_cancel(int button)
-{
- MouseEvent e;
- e.m_type = kEventType::MouseCancel;
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::gesture_start(const glm::vec2& p0, const glm::vec2& p1)
-{
- GestureEvent e;
- glm::vec2 p = glm::lerp(p0, p1, 0.5f);
- e.m_type = kEventType::GestureStart;
- e.m_pos = p;
- e.m_distance = glm::distance(p0, p1);
- gesture_p0 = p0;
- gesture_p1 = p1;
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::gesture_move(const glm::vec2& p0, const glm::vec2& p1)
-{
- GestureEvent e;
- glm::vec2 p = glm::lerp(p0, p1, 0.5f);
- e.m_type = kEventType::GestureMove;
- e.m_pos = p;
- e.m_distance = glm::distance(p0, p1);
- e.m_distance_delta = e.m_distance - glm::distance(gesture_p0, gesture_p1);
- e.m_pos_delta = p - glm::lerp(gesture_p0, gesture_p1, 0.5f);
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::gesture_end()
-{
- GestureEvent e;
- e.m_type = kEventType::GestureEnd;
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::key_down(kKey key)
-{
- KeyEvent e;
- e.m_type = kEventType::KeyDown;
- e.m_key = key;
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::key_up(kKey key)
-{
- KeyEvent e;
- e.m_type = kEventType::KeyUp;
- e.m_key = key;
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
-bool App::key_char(char key)
-{
- KeyEvent e;
- e.m_type = kEventType::KeyChar;
- e.m_char = key;
- auto ret = layout[main_id]->on_event(&e);
- layout[main_id]->update();
- return ret == kEventResult::Consumed;
-}
void App::terminate()
{
diff --git a/engine/app_events.cpp b/engine/app_events.cpp
new file mode 100644
index 0000000..fb9a39d
--- /dev/null
+++ b/engine/app_events.cpp
@@ -0,0 +1,121 @@
+#include "pch.h"
+#include "app.h"
+
+using namespace ui;
+
+void App::resize(float w, float h)
+{
+ width = w;
+ height = h;
+ if (auto* main = layout[main_id])
+ main->update(w , h, zoom);
+}
+
+bool App::mouse_down(int button, float x, float y)
+{
+ MouseEvent e;
+ e.m_type = button ? kEventType::MouseDownR : kEventType::MouseDownL;
+ e.m_pos = { x / zoom, y / zoom };
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::mouse_move(float x, float y)
+{
+ MouseEvent e;
+ e.m_type = kEventType::MouseMove;
+ e.m_pos = { x / zoom, y / zoom };
+ kEventResult ret = kEventResult::Available;
+ if (auto* main = layout[main_id])
+ ret = main->on_event(&e);
+ return ret == kEventResult::Consumed;
+}
+bool App::mouse_up(int button, float x, float y)
+{
+ MouseEvent e;
+ e.m_type = button ? kEventType::MouseUpR : kEventType::MouseUpL;
+ e.m_pos = { x / zoom, y / zoom };
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::mouse_scroll(float x, float y, float delta)
+{
+ MouseEvent e;
+ e.m_type = kEventType::MouseScroll;
+ e.m_pos = { x / zoom, y / zoom };
+ e.m_scroll_delta = delta * 0.1f;
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ LOG("wheel x %.2f y %.2f", x, y);
+ return ret == kEventResult::Consumed;
+}
+bool App::mouse_cancel(int button)
+{
+ MouseEvent e;
+ e.m_type = kEventType::MouseCancel;
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::gesture_start(const glm::vec2& p0, const glm::vec2& p1)
+{
+ GestureEvent e;
+ glm::vec2 p = glm::lerp(p0, p1, 0.5f);
+ e.m_type = kEventType::GestureStart;
+ e.m_pos = p;
+ e.m_distance = glm::distance(p0, p1);
+ gesture_p0 = p0;
+ gesture_p1 = p1;
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::gesture_move(const glm::vec2& p0, const glm::vec2& p1)
+{
+ GestureEvent e;
+ glm::vec2 p = glm::lerp(p0, p1, 0.5f);
+ e.m_type = kEventType::GestureMove;
+ e.m_pos = p;
+ e.m_distance = glm::distance(p0, p1);
+ e.m_distance_delta = e.m_distance - glm::distance(gesture_p0, gesture_p1);
+ e.m_pos_delta = p - glm::lerp(gesture_p0, gesture_p1, 0.5f);
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::gesture_end()
+{
+ GestureEvent e;
+ e.m_type = kEventType::GestureEnd;
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::key_down(kKey key)
+{
+ KeyEvent e;
+ e.m_type = kEventType::KeyDown;
+ e.m_key = key;
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::key_up(kKey key)
+{
+ KeyEvent e;
+ e.m_type = kEventType::KeyUp;
+ e.m_key = key;
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
+bool App::key_char(char key)
+{
+ KeyEvent e;
+ e.m_type = kEventType::KeyChar;
+ e.m_char = key;
+ auto ret = layout[main_id]->on_event(&e);
+ layout[main_id]->update();
+ return ret == kEventResult::Consumed;
+}
diff --git a/engine/app_layout.cpp b/engine/app_layout.cpp
new file mode 100644
index 0000000..61c4eee
--- /dev/null
+++ b/engine/app_layout.cpp
@@ -0,0 +1,385 @@
+#include "pch.h"
+#include "app.h"
+#include "node_icon.h"
+#include "node_dialog_open.h"
+
+using namespace ui;
+
+void App::initLayout()
+{
+ LOG("initializing layout statics");
+ NodeBorder::static_init();
+ NodeImage::static_init();
+ NodeIcon::static_init();
+ NodeStrokePreview::static_init();
+
+ layout.on_loaded = [&] {
+ LOG("initializing layout updating after load");
+ layout[main_id]->update(width, height, zoom);
+
+ LOG("initializing layout components");
+ sidebar = layout[main_id]->find("sidebar");
+ panels = layout[main_id]->find("panels");
+ canvas = layout[main_id]->find("paint-canvas");
+ canvas->data_path = data_path;
+
+ //brushes = layout[main_id]->find("panel-brush");
+ //layers = layout[main_id]->find("panel-layer");
+ //color = layout[main_id]->find("panel-color");
+ //stroke = layout[main_id]->find("panel-stroke");
+
+ brushes = std::make_shared();
+ brushes->m_manager = &layout;
+ brushes->init();
+ brushes->create();
+ brushes->loaded();
+
+ layers = std::make_shared();
+ layers->m_manager = &layout;
+ layers->init();
+ layers->create();
+ layers->loaded();
+
+ color = std::make_shared();
+ color->m_manager = &layout;
+ color->init();
+ color->create();
+ color->loaded();
+
+ stroke = std::make_shared();
+ stroke->m_manager = &layout;
+ stroke->init();
+ stroke->create();
+ stroke->loaded();
+
+ if (canvas)
+ {
+ stroke->m_canvas->m_brush.m_tip_color = color->m_color;
+ stroke->m_canvas->draw_stroke();
+ canvas->m_brush = stroke->m_canvas->m_brush;
+ }
+
+ brushes->on_brush_changed = [this](Node* target, int index) {
+ auto tid = brushes->get_texture_id(index);
+ stroke->m_canvas->m_brush.m_tex_id = tid;
+ stroke->m_canvas->draw_stroke();
+ canvas->m_brush = stroke->m_canvas->m_brush;
+ if (on_brush_select)
+ on_brush_select(index);
+ };
+
+ color->on_color_changed = [this](Node* target, glm::vec4 color) {
+ stroke->m_canvas->m_brush.m_tip_color = color;
+// stroke->m_canvas->draw_stroke();
+ if (canvas)
+ canvas->m_brush = stroke->m_canvas->m_brush;
+ if (on_color_change)
+ on_color_change(color);
+ };
+
+ stroke->on_stroke_change = [this](Node*target) {
+ if (canvas)
+ canvas->m_brush = stroke->m_canvas->m_brush;
+ if (on_stroke_change)
+ on_stroke_change();
+ };
+
+ layers->on_layer_add = [this](Node*) {
+ canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str());
+ };
+
+ layers->on_layer_change = [this](Node*, int old_idx, int new_idx) {
+ canvas->m_canvas->m_current_layer_idx = canvas->m_canvas->m_order[new_idx];
+ };
+
+ layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
+ canvas->m_canvas->layer_order(old_idx, new_idx);
+ };
+
+ layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
+ canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_opacity = value;
+ };
+
+ layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
+ canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_alpha_locked = visible;
+ };
+
+ canvas->m_canvas->layer_add("Default");
+ layers->add_layer("Default");
+
+ static glm::vec4 color_button_normal{.1, .1, .1, 1};
+ static glm::vec4 color_button_hlight{ 1, .0, .0, 1};
+ if (auto* button = layout[main_id]->find("btn-pen"))
+ {
+ button->on_click = [this](Node*) {
+ layout[main_id]->find("btn-pen")->set_color(color_button_hlight);
+ layout[main_id]->find("btn-erase")->set_color(color_button_normal);
+ layout[main_id]->find("btn-line")->set_color(color_button_normal);
+ layout[main_id]->find("btn-cam")->set_color(color_button_normal);
+ layout[main_id]->find("btn-grid")->set_color(color_button_normal);
+ layout[main_id]->find("btn-fill")->set_color(color_button_normal);
+ Canvas::set_mode(Canvas::kCanvasMode::Draw);
+ };
+ layout[main_id]->find("btn-pen")->set_color(color_button_hlight);
+ Canvas::set_mode(Canvas::kCanvasMode::Draw);
+ }
+ if (auto* button = layout[main_id]->find("btn-erase"))
+ {
+ button->on_click = [this](Node*) {
+ layout[main_id]->find("btn-pen")->set_color(color_button_normal);
+ layout[main_id]->find("btn-erase")->set_color(color_button_hlight);
+ layout[main_id]->find("btn-line")->set_color(color_button_normal);
+ layout[main_id]->find("btn-cam")->set_color(color_button_normal);
+ layout[main_id]->find("btn-grid")->set_color(color_button_normal);
+ layout[main_id]->find("btn-fill")->set_color(color_button_normal);
+ Canvas::set_mode(Canvas::kCanvasMode::Erase);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-line"))
+ {
+ button->on_click = [this](Node*) {
+ layout[main_id]->find("btn-pen")->set_color(color_button_normal);
+ layout[main_id]->find("btn-erase")->set_color(color_button_normal);
+ layout[main_id]->find("btn-line")->set_color(color_button_hlight);
+ layout[main_id]->find("btn-cam")->set_color(color_button_normal);
+ layout[main_id]->find("btn-grid")->set_color(color_button_normal);
+ layout[main_id]->find("btn-fill")->set_color(color_button_normal);
+ Canvas::set_mode(Canvas::kCanvasMode::Line);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-cam"))
+ {
+ button->on_click = [this](Node*) {
+ layout[main_id]->find("btn-pen")->set_color(color_button_normal);
+ layout[main_id]->find("btn-erase")->set_color(color_button_normal);
+ layout[main_id]->find("btn-line")->set_color(color_button_normal);
+ layout[main_id]->find("btn-cam")->set_color(color_button_hlight);
+ layout[main_id]->find("btn-grid")->set_color(color_button_normal);
+ layout[main_id]->find("btn-fill")->set_color(color_button_normal);
+ Canvas::set_mode(Canvas::kCanvasMode::Camera);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-grid"))
+ {
+ button->on_click = [this](Node*) {
+ layout[main_id]->find("btn-pen")->set_color(color_button_normal);
+ layout[main_id]->find("btn-erase")->set_color(color_button_normal);
+ layout[main_id]->find("btn-line")->set_color(color_button_normal);
+ layout[main_id]->find("btn-cam")->set_color(color_button_normal);
+ layout[main_id]->find("btn-grid")->set_color(color_button_hlight);
+ layout[main_id]->find("btn-fill")->set_color(color_button_normal);
+ Canvas::set_mode(Canvas::kCanvasMode::Grid);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-fill"))
+ {
+ button->on_click = [this](Node*) {
+ layout[main_id]->find("btn-pen")->set_color(color_button_normal);
+ layout[main_id]->find("btn-erase")->set_color(color_button_normal);
+ layout[main_id]->find("btn-line")->set_color(color_button_normal);
+ layout[main_id]->find("btn-cam")->set_color(color_button_normal);
+ layout[main_id]->find("btn-grid")->set_color(color_button_normal);
+ layout[main_id]->find("btn-fill")->set_color(color_button_hlight);
+ Canvas::set_mode(Canvas::kCanvasMode::Fill);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-bucket"))
+ {
+ button->on_click = [this](Node*) {
+ canvas->m_canvas->clear(canvas->m_brush.m_tip_color);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-stroke"))
+ {
+ button->on_click = [this, button](Node*) {
+ panels->get_child_index(stroke.get()) == -1 ? panels->add_child(stroke) : panels->remove_child(stroke.get());
+ button->set_color(panels->get_child_index(stroke.get()) == -1 ? color_button_normal : color_button_hlight);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-brush"))
+ {
+ button->on_click = [this, button](Node*) {
+ panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get());
+ button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-color"))
+ {
+ button->on_click = [this, button](Node*) {
+ panels->get_child_index(color.get()) == -1 ? panels->add_child(color) : panels->remove_child(color.get());
+ button->set_color(panels->get_child_index(color.get()) == -1 ? color_button_normal : color_button_hlight);
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-layer"))
+ {
+ button->on_click = [this, button](Node*) {
+ panels->get_child_index(layers.get()) == -1 ? panels->add_child(layers) : panels->remove_child(layers.get());
+ button->set_color(panels->get_child_index(layers.get()) == -1 ? color_button_normal : color_button_hlight);
+ };
+ }
+
+ if (auto* button = layout[main_id]->find("btn-export"))
+ {
+ button->on_click = [this,button](Node*) {
+ if (canvas)
+ {
+ canvas->m_canvas->export_equirectangular(data_path);
+ }
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-anim"))
+ {
+ button->on_click = [this,button](Node*) {
+ if (canvas)
+ {
+ //canvas->m_canvas->export_anim(data_path);
+ }
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-open"))
+ {
+ button->on_click = [this,button](Node*) {
+ if (canvas)
+ {
+ // load thumbnail test
+ auto open_dialog = std::make_shared();
+ open_dialog->m_manager = &layout;
+ open_dialog->data_path = data_path;
+ open_dialog->init();
+ open_dialog->create();
+ open_dialog->loaded();
+
+ layout[main_id]->add_child(open_dialog);
+ layout[main_id]->update();
+
+ open_dialog->btn_ok->on_click = [this,open_dialog](Node*)
+ {
+ layers->clear();
+ canvas->m_canvas->project_open(data_path);
+ for (auto& i : canvas->m_canvas->m_order)
+ layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str());
+ open_dialog->destroy();
+ };
+ }
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-save"))
+ {
+ button->on_click = [this,button](Node*) {
+ if (canvas)
+ {
+ canvas->m_canvas->project_save(data_path);
+ }
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-undo"))
+ {
+ button->on_click = [this,button](Node*) {
+ if (!ActionManager::empty())
+ ActionManager::undo();
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-clean-memory"))
+ {
+ button->on_click = [this](Node*) {
+ ActionManager::clear();
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-clear"))
+ {
+ button->on_click = [this](Node*) {
+ //exit(0);
+ if (canvas)
+ canvas->m_canvas->clear({ 0, 0, 0, 0 });
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-popup"))
+ {
+ button->on_click = [this](Node*) {
+ msgbox = new NodeMessageBox();
+ msgbox->m_manager = &layout;
+ msgbox->init();
+ layout[main_id]->add_child(msgbox);
+ layout[main_id]->update();
+ };
+ }
+ if (auto* button = layout[main_id]->find("btn-settings"))
+ {
+ button->on_click = [this](Node*) {
+ settings = new NodeSettings();
+ settings->m_manager = &layout;
+ settings->init();
+ layout[main_id]->add_child(settings);
+ layout[main_id]->update();
+ };
+ }
+ if (auto* menu_file = layout[main_id]->find("menu-file"))
+ {
+ menu_file->on_click = [=](Node*) {
+ glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
+ popup = (NodePopupMenu*)layout[const_hash("file-menu")]->m_children[0]->clone();
+ popup->SetPositioning(YGPositionTypeAbsolute);
+ popup->SetPosition(pos.x, pos.y);
+ layout[main_id]->add_child(popup);
+ layout[main_id]->update();
+ popup->mouse_capture();
+ popup->m_mouse_ignore = false;
+ popup->m_flood_events = true;
+ popup->m_capture_children = false;
+ };
+ }
+ if (auto* menu_file = layout[main_id]->find("menu-edit"))
+ {
+ menu_file->on_click = [=](Node*) {
+ glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
+ popup = (NodePopupMenu*)layout[const_hash("edit-menu")]->m_children[0]->clone();
+ popup->SetPositioning(YGPositionTypeAbsolute);
+ popup->SetPosition(pos.x, pos.y);
+ layout[main_id]->add_child(popup);
+ layout[main_id]->update();
+ popup->mouse_capture();
+ popup->m_mouse_ignore = false;
+ popup->m_flood_events = true;
+ popup->m_capture_children = false;
+ };
+ }
+ if (auto* menu_file = layout[main_id]->find("menu-layers"))
+ {
+ menu_file->on_click = [=](Node*) {
+ glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
+ popup = (NodePopupMenu*)layout[const_hash("layers-menu")]->m_children[0]->clone();
+ popup->SetPositioning(YGPositionTypeAbsolute);
+ popup->SetPosition(pos.x, pos.y);
+ layout[main_id]->add_child(popup);
+ layout[main_id]->update();
+ popup->mouse_capture();
+ popup->m_mouse_ignore = false;
+ popup->m_flood_events = true;
+ popup->m_capture_children = false;
+ popup->find("clear-grids")->on_click = [this](Node*) {
+ CanvasModeGrid* mode = (CanvasModeGrid*)ui::Canvas::modes[(int)ui::Canvas::kCanvasMode::Grid][0];
+ mode->clear();
+ popup->mouse_release();
+ popup->destroy();
+ };
+ };
+ }
+ if (auto* toolbar = layout[main_id]->find("toolbar"))
+ {
+ toolbar->m_flood_events = true;
+ }
+ };
+ LOG("initializing layout xml");
+ if (layout.m_loaded)
+ {
+ LOG("restore layout");
+ layout.restore_context();
+ if (panels->get_child_index(brushes.get()) == -1) brushes->restore_context();
+ if (panels->get_child_index(layers.get()) == -1) layers->restore_context();
+ if (panels->get_child_index(color.get()) == -1) color->restore_context();
+ if (panels->get_child_index(stroke.get()) == -1) stroke->restore_context();
+ }
+ else
+ layout.load("data/layout.xml");
+ LOG("initializing layout completed");
+}
diff --git a/engine/app_shaders.cpp b/engine/app_shaders.cpp
new file mode 100644
index 0000000..0f5b53a
--- /dev/null
+++ b/engine/app_shaders.cpp
@@ -0,0 +1,334 @@
+#include "pch.h"
+#include "app.h"
+#include "shader.h"
+
+using namespace ui;
+
+void App::initShaders()
+{
+ static const char* shader_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "in vec4 pos;"
+ "in vec2 uvs;"
+ "out vec3 uv;"
+ "void main(){"
+ " uv = vec3(uvs, pos.w);"
+ " gl_Position = mvp * vec4(pos.xyz, 1.0);"
+ "}";
+ static const char* shader_f =
+ SHADER_VERSION
+ "uniform sampler2D tex;"
+ "in mediump vec3 uv;"
+ "out mediump vec4 frag;"
+ "void main(){"
+ //" frag = texture(tex, uv.xy/uv.z);"
+ " frag = texture(tex, uv.xy);"
+ "}";
+ static const char* shader_uv_f =
+ SHADER_VERSION
+ "uniform sampler2D tex;"
+ "in mediump vec3 uv;"
+ "out mediump vec4 frag;"
+ "void main(){"
+ " frag = vec4(uv.xy, 0.0, 1.0);"
+ "}";
+ // TEXTURE ALPHA
+ static const char* shader_alpha_f =
+ SHADER_VERSION
+ "uniform sampler2D tex;\n"
+ "uniform mediump float alpha;\n"
+ "in mediump vec3 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main(){\n"
+ " frag = texture(tex, uv.xy) * vec4(1,1,1,alpha);\n"
+ "}\n";
+
+ // TEXTURE ATLAS
+ static const char* shader_atlas_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "uniform vec2 tof;"
+ "uniform vec2 tsz;"
+ "in vec2 pos;"
+ "in vec2 uvs;"
+ "out vec2 uv;"
+ "void main(){"
+ " uv = tof + uvs * tsz;"
+ " gl_Position = mvp * vec4(pos, 0.0, 1.0);"
+ "}";
+ static const char* shader_atlas_f =
+ SHADER_VERSION
+ "uniform sampler2D tex;"
+ "in mediump vec2 uv;"
+ "out mediump vec4 frag;"
+ "void main(){"
+ " frag = texture(tex, uv);"
+ "}";
+
+ // SOLID COLOR
+ static const char* shader_color_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "in vec4 pos;"
+ "void main(){"
+ " gl_Position = mvp * pos;"
+ " gl_PointSize = 15.0;"
+ "}";
+ static const char* shader_color_f =
+ SHADER_VERSION
+ "uniform mediump vec4 col;"
+ "out mediump vec4 frag;"
+ "void main(){"
+ " frag = col;"
+ "}";
+
+ // COLOR QUAD
+ static const char* shader_color_quad_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "in vec4 pos;"
+ "in vec2 uvs;"
+ "out vec3 uv;"
+ "void main(){"
+ " gl_Position = mvp * pos;"
+ " uv = vec3(uvs, pos.w);"
+ "}";
+ static const char* shader_color_quad_f =
+ SHADER_VERSION
+ "uniform mediump vec4 col;"
+ "in mediump vec3 uv;"
+ "out mediump vec4 frag;"
+ "void main(){"
+ " mediump vec4 gradient_x = mix(col, vec4(1.0, 1.0, 1.0, 1.0), uv.x);"
+ " frag = mix(gradient_x, vec4(0.0, 0.0, 0.0, 1.0), uv.y);"
+ "}";
+
+ // HUE
+ static const char* shader_color_hue_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "in vec4 pos;"
+ "in vec2 uvs;"
+ "out vec3 uv;"
+ "void main(){"
+ " gl_Position = mvp * pos;"
+ " uv = vec3(uvs, pos.w);"
+ "}";
+ static const char* shader_color_hue_f =
+ SHADER_VERSION
+ "uniform mediump vec4 col;"
+ "in mediump vec3 uv;"
+ "out mediump vec4 frag;"
+ "mediump vec3 hsv2rgb(mediump vec3 c) {"
+ " mediump vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);"
+ " mediump vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);"
+ " return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);"
+ "}"
+ "void main(){"
+ " frag = vec4(hsv2rgb(vec3(uv.y, 1.0, 1.0)), 1.0);"
+ "}";
+
+ // FONT
+ static const char* shader_font_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "in vec2 pos;"
+ "in vec2 uvs;"
+ "out vec2 uv;"
+ "void main(){"
+ " uv = uvs;"
+ " gl_Position = mvp * vec4(pos, 0.0, 1.0);"
+ "}";
+ static const char* shader_font_f =
+ SHADER_VERSION
+ "uniform mediump sampler2D tex;"
+ "uniform mediump vec4 col;"
+ "in mediump vec2 uv;"
+ "out mediump vec4 frag;"
+ "void main(){"
+ " mediump float a = texture(tex, uv).r;"
+ " frag = vec4(col.rgb, a);"
+ "}";
+
+ // STROKE
+ static const char* shader_stroke_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;\n"
+ "in vec4 pos;\n"
+ "in vec2 uvs;\n"
+ "out vec2 uv;\n"
+ "void main(){\n"
+ " uv = uvs;\n"
+ " gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
+ "}\n";
+ static const char* shader_stroke_f =
+ SHADER_VERSION
+ "uniform mediump sampler2D tex;\n"
+ "uniform mediump sampler2D tex_bg;\n"
+ "uniform mediump vec4 col;\n"
+ "uniform mediump vec2 resolution;\n"
+ "uniform mediump float alpha;\n"
+ "in mediump vec2 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main(){\n"
+ " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
+ " mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n"
+ " mediump vec4 fg = vec4(col.rgb, brush_alpha);\n"
+ " mediump vec4 bg = texture(tex_bg, uv2);\n"
+ " if (fg.a < (1.0/255.0)) { frag = bg; return; }\n"
+ " mediump float alpha_tot = fg.a + (1.0 - fg.a) * bg.a;"
+ " mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
+ " frag = vec4(rgb, alpha_tot);\n"
+ "}\n";
+ // ALPHA LOCK
+ static const char* shader_stroke_lock_f =
+ SHADER_VERSION
+ "uniform mediump sampler2D tex;\n"
+ "uniform mediump sampler2D tex_bg;\n"
+ "uniform mediump sampler2D tex_mask;\n"
+ "uniform mediump vec4 col;\n"
+ "uniform mediump vec2 resolution;\n"
+ "uniform mediump float alpha;\n"
+ "in mediump vec2 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main(){\n"
+ " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
+ " mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n"
+ " mediump vec4 fg = vec4(col.rgb, brush_alpha);\n"
+ " mediump vec4 bg = texture(tex_bg, uv2);\n"
+ " mediump vec4 msk = texture(tex_mask, uv2);\n"
+ " if (fg.a < (1.0/255.0)) { frag = bg; return; }\n"
+ " mediump float alpha_tot = fg.a + (1.0 - fg.a) * bg.a;"
+ " mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
+ " frag = vec4(rgb, min(alpha_tot, msk.a));\n"
+ "}\n";
+ // ERASER
+ static const char* shader_stroke_erase_f =
+ SHADER_VERSION
+ "uniform mediump sampler2D tex;\n"
+ "uniform mediump sampler2D tex_bg;\n"
+ "uniform mediump vec4 col;\n"
+ "uniform mediump vec2 resolution;\n"
+ "uniform mediump float alpha;\n"
+ "in mediump vec2 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main(){\n"
+ " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n"
+ " mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n"
+ " mediump vec4 bg = texture(tex_bg, uv2);\n"
+ " frag = vec4(bg.rgb, bg.a - bg.a * brush_alpha);\n"
+ "}\n";
+
+ // STROKE LAYER BLEND
+ static const char* shader_stroke_layer_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "in vec4 pos;"
+ "in vec2 uvs;"
+ "out vec2 uv;"
+ "void main(){"
+ " uv = uvs;"
+ " gl_Position = mvp * vec4(pos.xyz, 1.0);"
+ "}";
+ static const char* shader_stroke_layer_f =
+ SHADER_VERSION
+ "uniform mediump sampler2D tex;\n"
+ "uniform mediump sampler2D tex_bg;\n"
+ "uniform mediump vec2 resolution;\n"
+ "uniform mediump float alpha;\n"
+ "in mediump vec2 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main(){\n"
+ " mediump vec4 fg = texture(tex, uv) * vec4(1,1,1,alpha);\n"
+ " mediump vec4 bg = texture(tex_bg, uv);\n"
+ " if (fg.a < (1.0/255.0)) { frag = bg; return; }\n"
+ " mediump float alpha_tot = fg.a + (1.0 - fg.a) * bg.a;"
+ " mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n"
+ " frag = vec4(rgb, alpha_tot);\n"
+ "}\n";
+
+ static const char* shader_checkerboard_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;\n"
+ "in vec4 pos;\n"
+ "in vec2 uvs;\n"
+ "out vec2 uv;\n"
+ "void main(){\n"
+ " uv = uvs;\n"
+ " gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
+ "}";
+ static const char* shader_checkerboard_f =
+ SHADER_VERSION
+ "in mediump vec2 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main(){\n"
+ " const mediump vec4 c1 = vec4(1.0, 1.0, 1.0, 1.0);\n"
+ " const mediump vec4 c2 = vec4(0.9, 0.9, 0.9, 1.0);\n"
+ " mediump vec2 c = floor(fract(uv * 10.0) * 2.0);\n"
+ " mediump float alpha = mix(c.x, 1.0 - c.x, c.y);\n"
+ " frag = mix(c1, c2, alpha);\n"
+ "}";
+
+ static const char* shader_equirect_v =
+ SHADER_VERSION
+ "#define PI 3.1415926535897932384626433832795\n"
+ "#define TWO_PI 6.283185307179586476925286766559\n"
+ "uniform mat4 mvp;\n"
+ "in vec4 pos;\n"
+ "in vec2 uvs;\n"
+ "out vec2 uv;\n"
+ "void main(){\n"
+ " uv = (vec2(1.0) - uvs + vec2(0.25,0.0)) * vec2(TWO_PI, PI);\n"
+ " gl_Position = mvp * vec4(pos.xyz, 1.0);\n"
+ "}";
+ static const char* shader_equirect_f =
+ SHADER_VERSION
+ "uniform samplerCube tex;\n"
+ "in highp vec2 uv;\n"
+ "out mediump vec4 frag;\n"
+ "void main(){\n"
+ " highp float anglex = uv.x;\n"
+ " highp float angley = uv.y;\n"
+ " highp float sx = sin(anglex);\n"
+ " highp float cx = cos(anglex);\n"
+ " highp vec3 dir = vec3(0.0, 0.0, 0.0);\n"
+ " dir.x = sin(angley) * cx;\n"
+ " dir.y = cos(angley);\n"
+ " dir.z = sin(angley) * sx;\n"
+ " frag = texture(tex, dir);\n"
+ "}";
+
+ LOG("initializing shaders");
+ if (!ShaderManager::create(kShader::Texture, shader_v, shader_f))
+ LOG("Failed to create shader Texture");
+ if (!ShaderManager::create(kShader::TextureAlpha, shader_v, shader_alpha_f))
+ LOG("Failed to create shader TextureAlpha");
+ if (!ShaderManager::create(kShader::Color, shader_color_v, shader_color_f))
+ LOG("Failed to create shader Color");
+ if (!ShaderManager::create(kShader::ColorQuad, shader_color_quad_v, shader_color_quad_f))
+ LOG("Failed to create shader ColorQuad");
+ if (!ShaderManager::create(kShader::ColorHue, shader_color_hue_v, shader_color_hue_f))
+ LOG("Failed to create shader ColorHue");
+ if (!ShaderManager::create(kShader::UVs, shader_v, shader_uv_f))
+ LOG("Failed to create shader UVs");
+ if (!ShaderManager::create(kShader::Font, shader_font_v, shader_font_f))
+ LOG("Failed to create shader Font");
+ if (!ShaderManager::create(kShader::Atlas, shader_atlas_v, shader_atlas_f))
+ LOG("Failed to create shader Atlas");
+ if (!ShaderManager::create(kShader::Stroke, shader_stroke_v, shader_stroke_f))
+ LOG("Failed to create shader Stroke");
+ if (!ShaderManager::create(kShader::StrokeLock, shader_stroke_v, shader_stroke_lock_f))
+ LOG("Failed to create shader StrokeLock");
+ if (!ShaderManager::create(kShader::StrokeErase, shader_stroke_v, shader_stroke_erase_f))
+ LOG("Failed to create shader StrokeErase");
+ if (!ShaderManager::create(kShader::StrokeLayer, shader_stroke_layer_v, shader_stroke_layer_f))
+ LOG("Failed to create shader StrokeLayer");
+ if (!ShaderManager::create(kShader::Checkerboard, shader_checkerboard_v, shader_checkerboard_f))
+ LOG("Failed to create shader Checkerboard");
+ if (!ShaderManager::create(kShader::Equirect, shader_equirect_v, shader_equirect_f))
+ LOG("Failed to create shader Equirect");
+ LOG("shaders initialized");
+}
+
+
diff --git a/engine/main.cpp b/engine/main.cpp
index 4edecef..e7a1c46 100644
--- a/engine/main.cpp
+++ b/engine/main.cpp
@@ -659,20 +659,30 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
break;
case WM_LBUTTONDOWN:
App::I.mouse_down(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp));
+ SetCapture(hWnd);
break;
case WM_LBUTTONUP:
App::I.mouse_up(0, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp));
+ ReleaseCapture();
break;
case WM_RBUTTONDOWN:
App::I.mouse_down(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp));
+ SetCapture(hWnd);
break;
case WM_RBUTTONUP:
App::I.mouse_up(1, (float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp));
+ ReleaseCapture();
break;
case WM_MOUSEWHEEL:
- App::I.mouse_scroll((float)GET_X_LPARAM(lp), (float)GET_Y_LPARAM(lp),
+ {
+ POINT pt;
+ pt.x = GET_X_LPARAM(lp);
+ pt.y = GET_Y_LPARAM(lp);
+ ScreenToClient(hWnd, &pt);
+ App::I.mouse_scroll((float)pt.x, (float)pt.y,
(float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA);
break;
+ }
case WM_POINTERUPDATE:
{
POINTER_TOUCH_INFO touchInfo;
diff --git a/engine/node.cpp b/engine/node.cpp
index 91fd0d8..0190e3f 100644
--- a/engine/node.cpp
+++ b/engine/node.cpp
@@ -23,6 +23,7 @@
#include "node_color_quad.h"
#include "node_stroke_preview.h"
#include "node_canvas.h"
+#include "node_scroll.h"
void Node::watch(std::function observer)
{
@@ -47,26 +48,35 @@ Node* Node::root()
kEventResult Node::on_event(Event* e)
{
+ kEventResult ret = kEventResult::Available;
+
if (current_mouse_capture)
return current_mouse_capture->on_event(e);
- kEventResult ret = kEventResult::Available;
- for (auto it = m_children.rbegin(); it != m_children.rend(); ++it)
+ bool skip_children = false;
+ skip_children |= (e->m_cat == kEventCategory::MouseEvent) &&
+ (m_mouse_captured) && (root()->current_mouse_capture == this) && m_capture_children;
+
+ if (!skip_children)
{
- if ((*it)->on_event(e) == kEventResult::Consumed)
+ for (auto it = m_children.rbegin(); it != m_children.rend(); ++it)
{
- if (m_flood_events)
+ if ((*it)->on_event(e) == kEventResult::Consumed)
{
- ret = kEventResult::Consumed;
- }
- else
- {
- return kEventResult::Consumed;
+ if (m_flood_events)
+ {
+ ret = kEventResult::Consumed;
+ }
+ else
+ {
+ return kEventResult::Consumed;
+ }
}
}
+ if (ret == kEventResult::Consumed)
+ return ret;
}
- if (ret == kEventResult::Consumed)
- return ret;
+
switch (e->m_cat)
{
case kEventCategory::MouseEvent:
@@ -79,6 +89,7 @@ kEventResult Node::on_event(Event* e)
m_mouse_inside = inside;
switch (e->m_type)
{
+ case kEventType::MouseScroll:
case kEventType::MouseDownL:
case kEventType::MouseDownR:
case kEventType::MouseUpL:
@@ -240,24 +251,39 @@ int Node::get_child_index(Node* n)
return -1;
}
+glm::vec4 Node::get_children_rect() const
+{
+ if (m_children.empty())
+ return glm::vec4(0);
+ glm::vec4 ret = m_children[0]->m_clip_uncut;
+ for (auto& c : m_children)
+ ret = rect_union(ret, c->m_clip_uncut);
+ return ret;
+}
+
void Node::mouse_capture()
{
- root()->current_mouse_capture = this; m_mouse_captured = true;
+ root()->current_mouse_capture = this;
+ m_mouse_captured = true;
}
void Node::mouse_release()
{
- root()->current_mouse_capture = nullptr; m_mouse_captured = false;
+ if (root()->current_mouse_capture == this)
+ root()->current_mouse_capture = nullptr;
+ m_mouse_captured = false;
}
void Node::key_capture()
{
- root()->current_key_capture = this; m_key_captured = true;
+ root()->current_key_capture = this;
+ m_key_captured = true;
}
void Node::key_release()
{
- root()->current_key_capture = nullptr; m_key_captured = false;
+ root()->current_key_capture = nullptr;
+ m_key_captured = false;
}
Node&& Node::operator=(Node&& o)
@@ -335,6 +361,15 @@ void Node::SetPadding(float t, float r, float b, float l)
YGNodeStyleSetPadding(y_node, YGEdgeLeft, l);
}
+glm::vec4 Node::GetPadding() const
+{
+ float t = YGNodeLayoutGetPadding(y_node, YGEdgeTop);
+ float r = YGNodeLayoutGetPadding(y_node, YGEdgeRight);
+ float b = YGNodeLayoutGetPadding(y_node, YGEdgeBottom);
+ float l = YGNodeLayoutGetPadding(y_node, YGEdgeLeft);
+ return{ t, r, b, l };
+}
+
void Node::SetPosition(const glm::vec2 pos)
{
SetPosition(pos.x, pos.y);
@@ -414,12 +449,26 @@ glm::vec2 Node::GetSize()
return{ GetWidth(), GetHeight() };
}
-glm::vec4 Node::rect_intersection(glm::vec4 a, glm::vec4 b)
+glm::vec4 Node::rect_intersection(glm::vec4 a, glm::vec4 b) const
{
// convert from [x,y,w,h] to [x1,y1,x2,y1]
a = glm::vec4(a.xy(), a.xy() + a.zw());
b = glm::vec4(b.xy(), b.xy() + b.zw());
+ // compute intersection
auto o = glm::vec4(glm::max(a.xy(), b.xy()), glm::min(a.zw(), b.zw()));
+ // back to rect form
+ o = glm::vec4(o.xy(), glm::max({ 0, 0 }, o.zw() - o.xy()));
+ return o;
+}
+
+glm::vec4 Node::rect_union(glm::vec4 a, glm::vec4 b) const
+{
+ // convert from rect [x,y,w,h] to bb [x1,y1,x2,y1]
+ a = glm::vec4(a.xy(), a.xy() + a.zw());
+ b = glm::vec4(b.xy(), b.xy() + b.zw());
+ // compute union
+ glm::vec4 o = { glm::min(a.xy(), b.xy()), glm::max(a.zw(), b.zw()) };
+ // back to rect form
o = glm::vec4(o.xy(), glm::max({ 0, 0 }, o.zw() - o.xy()));
return o;
}
@@ -459,7 +508,9 @@ void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj)
float w = YGNodeLayoutGetWidth(y_node);
float h = YGNodeLayoutGetHeight(y_node);
auto old_size = m_size;
- m_pos = glm::floor(origin + glm::vec2(x, y));
+ glm::vec2 parent_offset = parent ? parent->m_pos_offset_childred : glm::vec2(0.f);
+ m_pos = glm::floor(origin + glm::vec2(x, y) + m_pos_offset + parent_offset);
+ m_pos_origin = glm::floor(origin + glm::vec2(x, y));
m_size = glm::floor(glm::vec2(w, h));
if (parent)
@@ -478,7 +529,7 @@ void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj)
}
else
{
- m_clip = glm::vec4(m_pos, m_size);
+ m_clip_uncut = m_clip = glm::vec4(m_pos, m_size);
}
glm::mat4 pivot = glm::translate(glm::vec3(.5f, .5f, 0.f));
@@ -726,6 +777,7 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node)
CASE(kWidget::ColorQuad, NodeColorQuad);
CASE(kWidget::StrokePreview, NodeStrokePreview);
CASE(kWidget::Canvas, NodeCanvas);
+ CASE(kWidget::Scroll, NodeScroll);
#undef CASE
case kWidget::Ref:
{
diff --git a/engine/node.h b/engine/node.h
index 889194d..bc18568 100644
--- a/engine/node.h
+++ b/engine/node.h
@@ -67,6 +67,7 @@ enum class kWidget : uint16_t
ColorQuad = const_hash("color-quad"),
StrokePreview = const_hash("stroke-preview"),
Canvas = const_hash("canvas"),
+ Scroll = const_hash("scroll"),
};
class Node
@@ -88,12 +89,16 @@ public:
glm::mat4 m_mvp;
bool m_mouse_inside = false;
bool m_flood_events = false;
+ bool m_capture_children = true; // wether to capture children events when xx_capture() is used
bool m_destroyed = false;
bool m_mouse_ignore = true;
float m_zoom = 1.f;
glm::vec2 m_scale{ 1.f };
glm::vec2 m_pos;
+ glm::vec2 m_pos_origin; // original layout position without offset
+ glm::vec2 m_pos_offset; // artificial position offset for scrolling
+ glm::vec2 m_pos_offset_childred; // artificial position offset for scrolling
glm::vec2 m_size;
glm::vec4 m_clip;
glm::vec4 m_clip_uncut;
@@ -114,6 +119,7 @@ public:
void SetSize(float w, float h);
void SetPadding(float t, float r, float b, float l);
+ glm::vec4 GetPadding() const;
void SetPosition(float l, float t, float r, float b);
void SetPosition(float l, float t);
void SetPosition(const glm::vec2 pos);
@@ -132,7 +138,8 @@ public:
float GetHeight();
glm::vec2 GetSize();
- glm::vec4 rect_intersection(glm::vec4 a, glm::vec4 b);
+ glm::vec4 rect_intersection(glm::vec4 a, glm::vec4 b) const;
+ glm::vec4 rect_union(glm::vec4 a, glm::vec4 b) const;
virtual void restore_context();;
virtual void clear_context();
@@ -177,6 +184,7 @@ public:
void move_child(Node* n, int index);
void move_child_offset(Node* n, int offset);
int get_child_index(Node* n);
+ glm::vec4 get_children_rect() const;
void mouse_capture();
void mouse_release();
void key_capture();
diff --git a/engine/node_button_custom.cpp b/engine/node_button_custom.cpp
index 3e02ba7..04a57a4 100644
--- a/engine/node_button_custom.cpp
+++ b/engine/node_button_custom.cpp
@@ -46,11 +46,16 @@ kEventResult NodeButtonCustom::handle_event(Event* e)
break;
case kEventType::MouseDownL:
m_color = color_down;
+ mouse_capture();
break;
case kEventType::MouseUpL:
m_color = m_mouse_inside ? color_hover : color_normal;
if (m_mouse_inside && on_click != nullptr)
on_click(this);
+ mouse_release();
+ break;
+ case kEventType::MouseCancel:
+ mouse_release();
break;
default:
return kEventResult::Available;
diff --git a/engine/node_canvas.cpp b/engine/node_canvas.cpp
index 5d44acb..00ddfe0 100644
--- a/engine/node_canvas.cpp
+++ b/engine/node_canvas.cpp
@@ -162,12 +162,12 @@ kEventResult NodeCanvas::handle_event(Event* e)
switch (e->m_type)
{
+ case kEventType::MouseScroll:
case kEventType::MouseDownL:
case kEventType::MouseUpL:
case kEventType::MouseDownR:
case kEventType::MouseUpR:
case kEventType::MouseMove:
- case kEventType::MouseScroll:
case kEventType::MouseCancel:
for (auto& mode : *m_canvas->m_mode)
mode->on_MouseEvent(me, loc);
diff --git a/engine/node_popup_menu.cpp b/engine/node_popup_menu.cpp
index c27d4aa..f197dc9 100644
--- a/engine/node_popup_menu.cpp
+++ b/engine/node_popup_menu.cpp
@@ -15,6 +15,7 @@ void NodePopupMenu::init()
SetHeight(400);
SetPositioning(YGPositionTypeAbsolute);
m_mouse_ignore = false;
+ m_capture_children = false;
}
kEventResult NodePopupMenu::handle_event(Event* e)
diff --git a/engine/node_scroll.cpp b/engine/node_scroll.cpp
new file mode 100644
index 0000000..bb41c06
--- /dev/null
+++ b/engine/node_scroll.cpp
@@ -0,0 +1,50 @@
+#include "pch.h"
+#include "log.h"
+#include "node_scroll.h"
+#include "event.h"
+
+Node* NodeScroll::clone_instantiate() const
+{
+ return new NodeScroll;
+}
+
+kEventResult NodeScroll::handle_event(Event* e)
+{
+ NodeBorder::handle_event(e);
+ auto me = static_cast(e);
+ auto loc = (me->m_pos - m_pos) * root()->m_zoom;
+ switch (e->m_type)
+ {
+ case kEventType::MouseDownL:
+ m_dragging = true;
+ m_drag_start = me->m_pos;
+ m_offset_start = m_offset;
+ mouse_capture();
+ break;
+ case kEventType::MouseMove:
+ if (m_dragging)
+ {
+ auto pad = GetPadding();
+ glm::vec2 padoff = { pad.y + pad.w, pad.x + pad.z };
+ auto rect = get_children_rect();
+ m_offset = m_offset_start + (me->m_pos - m_drag_start) * m_mask;
+ m_offset = glm::clamp(m_offset, -rect.zw() + m_clip_uncut.zw() - padoff, { 0, 0 });
+ m_pos_offset_childred = m_offset;
+ }
+ break;
+ case kEventType::MouseUpL:
+ mouse_release();
+ m_dragging = false;
+ break;
+// case kEventType::MouseScroll:
+// break;
+ case kEventType::MouseCancel:
+ mouse_release();
+ m_dragging = false;
+ break;
+ default:
+ return kEventResult::Available;
+ break;
+ }
+ return kEventResult::Consumed;
+}
diff --git a/engine/node_scroll.h b/engine/node_scroll.h
new file mode 100644
index 0000000..3478c81
--- /dev/null
+++ b/engine/node_scroll.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "node_border.h"
+
+class NodeScroll : public NodeBorder
+{
+ bool m_dragging = false;
+ glm::vec2 m_drag_start;
+ glm::vec2 m_offset_start;
+ glm::vec2 m_offset;
+ glm::vec2 m_mask{ 0, 1 };
+public:
+ virtual Node* clone_instantiate() const override;
+ virtual kEventResult handle_event(Event* e) override;
+};