diff --git a/Keishiki/UI.cpp b/Keishiki/UI.cpp index b1bd2f0..32b33ee 100644 --- a/Keishiki/UI.cpp +++ b/Keishiki/UI.cpp @@ -871,9 +871,11 @@ namespace K::UI { std::erase(s.selected, i); else s.selected.push_back(i); - } else if (io.KeyShift) { + } + else if (io.KeyShift) { // TODO - } else { + } + else { s.selected.clear(); if (!selected) s.selected.push_back(i); @@ -929,81 +931,99 @@ namespace K::UI { } // Timeline - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{}); - ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{}); - - ImGui::TableNextColumn(); + ImGui::TableSetColumnIndex(4); f32 init_y = ImGui::GetCursorScreenPos().y; + if (!show_curve_editor) { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{}); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2{}); - const static f32 layer_bound_width = 5.0f; - const bool l_le_right_extr = static_cast(current_layer.in) / static_cast(s.frame_max + 1) <= view_right, - l_in = l_le_right_extr && static_cast(current_layer.in) / static_cast(s.frame_max + 1) >= view_left, - l_ge_left_extr = static_cast(current_layer.out) / static_cast(s.frame_max + 1) >= view_left, - l_out = l_ge_left_extr && static_cast(current_layer.out) / static_cast(s.frame_max + 1) <= view_right; - f32 in_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.in, s.frame_max), - out_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.out, s.frame_max); - ImGui::PushID(0); - if (l_in) { - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos); - static f32 l_in_old; - ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666); - ImGui::Button("##Layer_L", {layer_bound_width, view_height}); - if (ImGui::IsItemClicked()) { - l_in_old = in_pos; + const static f32 layer_bound_width = 5.0f; + const bool l_le_right_extr = + static_cast(current_layer.in) / static_cast(s.frame_max + 1) <= view_right, + l_in = l_le_right_extr && + static_cast(current_layer.in) / static_cast(s.frame_max + 1) >= view_left, + l_ge_left_extr = + static_cast(current_layer.out) / static_cast(s.frame_max + 1) >= view_left, + l_out = l_ge_left_extr && + static_cast(current_layer.out) / static_cast(s.frame_max + 1) <= + view_right; + f32 in_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.in, + s.frame_max), + out_pos = TimelineFrameToScreenView(view_left, view_amt, view_width, current_layer.out, + s.frame_max); + ImGui::PushID(0); + if (l_in) { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + in_pos); + static f32 l_in_old; + ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666); + ImGui::Button("##Layer_L", {layer_bound_width, view_height}); + if (ImGui::IsItemClicked()) { + l_in_old = in_pos; + } + if (ImGui::IsItemHovered()) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + } + if (ImGui::IsItemActive()) { + current_layer.in = std::min(TimelineScreenViewToFrame(view_left, view_amt, view_width, + std::clamp( + ImGui::GetMouseDragDelta().x + + l_in_old, 0.0f, view_width), + s.frame_max), current_layer.out); + } + ImGui::PopStyleColor(); + ImGui::SameLine(0.0f, 0.0f); } - if (ImGui::IsItemHovered()) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + if (l_le_right_extr && l_ge_left_extr) { + ImGui::Button(current_layer.name.c_str(), + {std::min(out_pos + fr_step, view_width) - static_cast(l_in) * in_pos - + layer_bound_width * static_cast(l_in + l_out), view_height}); + static u32 in_old, out_old; + if (ImGui::IsItemClicked()) { + in_old = current_layer.in; + out_old = current_layer.out; + } + if (ImGui::IsItemActive()) { + i32 off = static_cast(std::trunc(ImGui::GetMouseDragDelta().x / fr_step)); + off = std::max(-static_cast(in_old), off); + current_layer.in = in_old + off; + current_layer.out = out_old + off; + } } - if (ImGui::IsItemActive()) { - current_layer.in = std::min(TimelineScreenViewToFrame(view_left, view_amt, view_width, - std::clamp(ImGui::GetMouseDragDelta().x + l_in_old, 0.0f, view_width), s.frame_max), current_layer.out); + if (l_out) { + static f32 r_out_old; + ImGui::SameLine(0.0f, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666); + ImGui::Button("##Layer_R", {layer_bound_width, view_height}); + if (ImGui::IsItemClicked()) { + r_out_old = out_pos; + } + if (ImGui::IsItemHovered()) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + } + if (ImGui::IsItemActive()) { + current_layer.out = std::max(TimelineScreenViewToFrame(view_left, view_amt, view_width, + std::clamp( + ImGui::GetMouseDragDelta().x + + r_out_old, 0.0f, view_width), + s.frame_max), current_layer.in); + } + ImGui::PopStyleColor(); } - ImGui::PopStyleColor(); - ImGui::SameLine(0.0f, 0.0f); - } - if (l_le_right_extr && l_ge_left_extr) { - ImGui::Button(current_layer.name.c_str(), - {std::min(out_pos + fr_step, view_width) - static_cast(l_in) * in_pos - - layer_bound_width * static_cast(l_in + l_out), view_height}); - static u32 in_old, out_old; - if (ImGui::IsItemClicked()) { - in_old = current_layer.in; - out_old = current_layer.out; - } - if (ImGui::IsItemActive()) { - i32 off = static_cast(std::trunc(ImGui::GetMouseDragDelta().x / fr_step)); - off = std::max(-static_cast(in_old), off); - current_layer.in = in_old + off; - current_layer.out = out_old + off; - } - } - if (l_out) { - static f32 r_out_old; - ImGui::SameLine(0.0f, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Button, 0xFF666666); - ImGui::Button("##Layer_R", {layer_bound_width, view_height}); - if (ImGui::IsItemClicked()) { - r_out_old = out_pos; - } - if (ImGui::IsItemHovered()) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - } - if (ImGui::IsItemActive()) { - current_layer.out = std::max(TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(ImGui::GetMouseDragDelta().x + r_out_old, 0.0f, view_width), s.frame_max), current_layer.in); - } - ImGui::PopStyleColor(); - } - ImGui::PopID(); + ImGui::PopID(); - ImGui::SetCursorScreenPos(ImVec2{tl_init_pos.x, init_y} - style.CellPadding); - ImGui::InvisibleButton("##TL_BG", ImVec2{ view_width + style.CellPadding.x * 2, row_height + style.CellPadding.y}); - if (ImGui::IsItemActive()) { - s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, std::clamp(mouse_tl_x, 0.0f, view_width), s.frame_max); - } + ImGui::SetCursorScreenPos(ImVec2{tl_init_pos.x, init_y} - style.CellPadding); + ImGui::InvisibleButton("##TL_BG", ImVec2{view_width + style.CellPadding.x * 2, + row_height + style.CellPadding.y}); + if (ImGui::IsItemActive()) { + s.current_frame = TimelineScreenViewToFrame(view_left, view_amt, view_width, + std::clamp(mouse_tl_x, 0.0f, view_width), + s.frame_max); + } - ImGui::PopStyleVar(3); + ImGui::PopStyleVar(3); + } if (layer_open) { i32 j = 0; @@ -1046,7 +1066,7 @@ namespace K::UI { else if (ImGui::IsItemActive()) { if (ImGui::IsKeyPressed(ImGuiKey_LeftShift)) { auto& c = s.plugboard.nodes.Store(Plugboard::Chain{}); - c.extra.chain.segments.emplace_back(s.current_frame, std::get::type>(arg)); + c.extra.chain.segments.emplace_back(s.current_frame, std::get::type>(arg), Plugboard::InterpolationExtra(Plugboard::K_I_Linear)); c.extra.chain.selected.push_back(0); Plugboard::Connect(u.connection.p, u.connection.index, &c, 0); Plugboard::Connect(&c, 0, &s.plugboard.in_instance, 0); @@ -1078,46 +1098,57 @@ namespace K::UI { ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(4); - auto p = ImGui::GetCursorScreenPos(); // restored later + if (!show_curve_editor) { + auto p = ImGui::GetCursorScreenPos(); // restored later - // plot uniform if connected - ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, {0.0f, 0.0f}); - ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, {.1f, .1f}); - ImPlot::PushStyleColor(ImPlotCol_FrameBg, 0); - ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0); - ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0); - if (connected_v.index() == 1 && ImPlot::BeginPlot("uniform", {view_width, row_height}, ImPlotFlags_CanvasOnly | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) { - ImPlot::SetupAxis(ImAxis_X1, "time", ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight); - ImPlot::SetupAxis(ImAxis_Y1, "val", ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_AutoFit); - struct PlotInfo info { - .begin = view_left * static_cast(s.frame_max + 1), - .end = view_right * static_cast(s.frame_max + 1), - .samples = 100, - .s = s, - .connected_v = std::get<1>(connected_v) - }; - ImPlot::SetupAxisLimits(ImAxis_X1, info.begin, info.end, ImGuiCond_Always); - ImPlot::SetupFinish(); - ImPlot::PlotLineG("v", [](int idx, void* user_data) -> ImPlotPoint { - PlotInfo i = *(PlotInfo*)user_data; - f32 x = i.begin + (i.end - i.begin) * static_cast(idx) / static_cast(i.samples); - CompositionState ss = i.s; - ss.current_frame = static_cast(std::round(x)); - bool good; - Vector info; - return {x, std::get::type>(Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), Plugboard::T_Float, good))}; + // plot uniform if connected + ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, {0.0f, 0.0f}); + ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, {.1f, .1f}); + ImPlot::PushStyleColor(ImPlotCol_FrameBg, 0); + ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0); + ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0); + if (connected_v.index() == 1 && ImPlot::BeginPlot("uniform", {view_width, row_height}, + ImPlotFlags_CanvasOnly | + ImPlotFlags_NoFrame | + ImPlotFlags_NoInputs)) { + ImPlot::SetupAxis(ImAxis_X1, "time", + ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight); + ImPlot::SetupAxis(ImAxis_Y1, "val", + ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight | + ImPlotAxisFlags_AutoFit); + struct PlotInfo info{ + .begin = view_left * static_cast(s.frame_max + 1), + .end = view_right * static_cast(s.frame_max + 1), + .samples = 100, + .s = s, + .connected_v = std::get<1>(connected_v) + }; + ImPlot::SetupAxisLimits(ImAxis_X1, info.begin, info.end, ImGuiCond_Always); + ImPlot::SetupFinish(); + ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint { + PlotInfo i = *(PlotInfo *) user_data; + f32 x = i.begin + + (i.end - i.begin) * static_cast(idx) / static_cast(i.samples); + CompositionState ss = i.s; + ss.current_frame = static_cast(std::round(x)); + bool good; + Vector info; + return {x, std::get::type>( + Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), + Plugboard::T_Float, good))}; }, &info, info.samples); - ImPlot::EndPlot(); - } - else { - ImGui::TextUnformatted("Constant"); - } - ImPlot::PopStyleColor(3); - ImPlot::PopStyleVar(2); + ImPlot::EndPlot(); + } + else { + ImGui::TextUnformatted("Constant"); + } + ImPlot::PopStyleColor(3); + ImPlot::PopStyleVar(2); - ImGui::SetCursorScreenPos(p); + ImGui::SetCursorScreenPos(p); - tl_bg_handler(); + tl_bg_handler(); + } if (uniform_open && connected_v.index() == 1) { for (const auto& info: s.plugboard.show_nodes[u.connection.index]) { @@ -1142,9 +1173,6 @@ namespace K::UI { auto& [chain, chain_copy] = std::get(info.p)->extra; - if (tl_clear_selection_request) - chain.selected.clear(); - bool m_empty = chain.segments.empty(), m_has_before = !m_empty && chain.segments.begin()->frame < s.current_frame; ImGui::BeginDisabled(m_empty || !m_has_before); @@ -1190,57 +1218,73 @@ namespace K::UI { ImGui::TableSetColumnIndex(3); ImGui::Text("%lu", chain.segments.size()); ImGui::TableSetColumnIndex(4); - ImVec2 begin_tl = ImGui::GetCursorScreenPos(); + if (!show_curve_editor) { + ImVec2 begin_tl = ImGui::GetCursorScreenPos(); - if (started_dragging) - chain_copy = chain; + if (started_dragging) + chain_copy = chain; - auto key_loop_target = &chain; - if (dragging) { - chain.segments.clear(); - chain.selected.clear(); - key_loop_target = &chain_copy; - } - for (u32 ii = 0; ii < key_loop_target->segments.size(); ii++) { - const auto& [k, val, segment] = (key_loop_target->segments)[ii]; - ImGui::PushID(k); - - auto sel_it = std::ranges::find(key_loop_target->selected, ii); - bool is_sel = sel_it != key_loop_target->selected.end(); - - i32 frame = k; - if (dragging && is_sel) { - frame += static_cast(ImGui::GetMouseDragDelta().x / tl_width * view_amt * - static_cast(s.frame_max + 1)); + auto key_loop_target = &chain; + if (dragging) { + chain.segments.clear(); + chain.selected.clear(); + key_loop_target = &chain_copy; } - ImVec2 draw_pos = {TimelineFrameToScreenView(view_left, view_amt, view_width, frame, - s.frame_max) + begin_tl.x - kf_tab_width / 2.0f, - begin_tl.y}; + for (u32 ii = 0; ii < key_loop_target->segments.size(); ii++) { + const auto &[k, val, segment] = (key_loop_target->segments)[ii]; + ImGui::PushID(k); - ImGui::SetCursorScreenPos(draw_pos); + auto sel_it = std::ranges::find(key_loop_target->selected, ii); + bool is_sel = sel_it != key_loop_target->selected.end(); - ImGui::Button(is_sel ? "s" : "k", {kf_tab_width, row_height * .8f}); + i32 frame = k; + if (dragging && is_sel) { + frame += static_cast(ImGui::GetMouseDragDelta().x / tl_width * + view_amt * + static_cast(s.frame_max + 1)); + } + ImVec2 draw_pos = { + TimelineFrameToScreenView(view_left, view_amt, view_width, frame, + s.frame_max) + begin_tl.x - + kf_tab_width / 2.0f, + begin_tl.y}; - kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel, - sel_it, segment, val, ii, started_dragging, {draw_pos.x + kf_tab_width / 2.0f, draw_pos.y}); + ImGui::SetCursorScreenPos(draw_pos); - ImGui::PopID(); - } - for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) { - auto nit = std::next(it); - if (nit != chain.segments.end() && it->value != nit->value) { - window->DrawList->AddRectFilled({TimelineFrameToScreenView(view_left, view_amt, view_width, it->frame, - s.frame_max) + begin_tl.x + kf_tab_width / 2.0f, - begin_tl.y + row_height / 2.0f - 2.0f}, {TimelineFrameToScreenView(view_left, view_amt, view_width, nit->frame, - s.frame_max) + begin_tl.x - kf_tab_width / 2.0f, - begin_tl.y + row_height / 2.0f + 2.0f}, - std::ranges::find(chain.selected, std::distance(chain.segments.begin(), it)) != chain.selected.end() && - std::ranges::find(chain.selected, std::distance(chain.segments.begin(), nit)) != chain.selected.end() ? 0x88FFAAAA : 0x44AAAAAA); + ImGui::Button(is_sel ? "s" : "k", {kf_tab_width, row_height * .8f}); + + kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel, + sel_it, segment, val, ii, started_dragging, + {draw_pos.x + kf_tab_width / 2.0f, draw_pos.y}); + + ImGui::PopID(); + } + for (auto it = chain.segments.begin(); it != chain.segments.end(); it++) { + auto nit = std::next(it); + if (nit != chain.segments.end() && it->value != nit->value) { + window->DrawList->AddRectFilled( + {TimelineFrameToScreenView(view_left, view_amt, view_width, + it->frame, + s.frame_max) + begin_tl.x + + kf_tab_width / 2.0f, + begin_tl.y + row_height / 2.0f - 2.0f}, + {TimelineFrameToScreenView(view_left, view_amt, view_width, + nit->frame, + s.frame_max) + begin_tl.x - + kf_tab_width / 2.0f, + begin_tl.y + row_height / 2.0f + 2.0f}, + std::ranges::find(chain.selected, + std::distance(chain.segments.begin(), it)) != + chain.selected.end() && + std::ranges::find(chain.selected, + std::distance(chain.segments.begin(), nit)) != + chain.selected.end() ? 0x88FFAAAA : 0x44AAAAAA); + } } - } - ImGui::SetCursorScreenPos(begin_tl); - tl_bg_handler(); + ImGui::SetCursorScreenPos(begin_tl); + tl_bg_handler(); + } } } ImGui::PopID(); @@ -1266,8 +1310,6 @@ namespace K::UI { if (started_dragging && dragging) started_dragging = false; - if (tl_clear_selection_request_this_frame && tl_clear_selection_request) - tl_clear_selection_request = false; tl_end_begin = {tl_init_pos.x, ImGui::GetCursorScreenPos().y}; @@ -1330,7 +1372,7 @@ namespace K::UI { ImPlot::PushStyleColor(ImPlotCol_FrameBg, 0); ImPlot::PushStyleColor(ImPlotCol_PlotBg, 0); ImPlot::PushStyleColor(ImPlotCol_PlotBorder, 0); - if (ImPlot::BeginPlot("uniform", {view_width, ImGui::GetContentRegionAvail().y}, ImPlotFlags_CanvasOnly | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) { + if (ImPlot::BeginPlot("uniform", {view_width, ImGui::GetContentRegionAvail().y}, ImPlotFlags_NoTitle | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMouseText | ImPlotFlags_NoFrame | ImPlotFlags_NoInputs)) { static bool started_dragging = false, dragging = false; ImPlot::SetupAxis(ImAxis_X1, "time", ImPlotAxisFlags_NoHighlight | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines); @@ -1354,6 +1396,7 @@ namespace K::UI { if (!std::holds_alternative(n)) continue; + ImGui::PushID(&n); auto& [chain, chain_copy] = std::get(n)->extra; Plugboard::ConnectInfo i {n, 0}; // it's a chain -- 0 is out @@ -1391,7 +1434,11 @@ namespace K::UI { f64 x = frame, y = v; ImVec2 k_pos = ImPlot::PlotToPixels(x, y); - ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true); + const bool is_last = ii + 1 == key_loop_target->segments.size(); + if (is_last) + ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true); + else + ImPlot::Annotation(x, y, {1.0f, 1.0f, 1.0f, 1.0f}, {15.0f, 0.0f}, true, "%.2f, %.2f, %s", x, y, Plugboard::K_Interpolation_Names[segment.interp]); ImGui::SetCursorScreenPos(k_pos - ImVec2{10.0f / 2.0f, 10.0f / 2.0f}); ImGui::PushStyleColor(ImGuiCol_Button, is_sel ? 0xFFFFFF88 : 0xFFAA7777); @@ -1401,7 +1448,7 @@ namespace K::UI { kf_drag_btn_handler(key_loop_target, dragging, chain, frame, is_sel, sel_it, segment, v, ii, started_dragging, k_pos); - if (segment.interp == Plugboard::K_I_CubicBezier && ii + 1 < key_loop_target->segments.size()) { + if (segment.interp == Plugboard::K_I_CubicBezier && !is_last) { const auto& [k_nxt, val_nxt, _] = (key_loop_target->segments)[ii + 1]; i32 frame_nxt = k_nxt; f64 v_nxt = val_nxt; @@ -1451,10 +1498,10 @@ namespace K::UI { f64 xs[2] = {static_cast(frame), p2x}, ys[2] = {v, p2y}; - ImPlot::PlotLine("cb tangent", xs, ys, 2); + ImPlot::PlotLine("###cb tangent", xs, ys, 2); xs[0] = p3x; xs[1] = static_cast(frame_nxt); ys[0] = p3y; ys[1] = v_nxt; - ImPlot::PlotLine("cb tangent", xs, ys, 2); + ImPlot::PlotLine("###cb tangent", xs, ys, 2); } ImGui::PopID(); @@ -1467,7 +1514,7 @@ namespace K::UI { .s = s, .connected_v = i }; - ImPlot::PlotLineG("v", [](int idx, void *user_data) -> ImPlotPoint { + ImPlot::PlotLineG(s.plugboard.nodes.GetName(static_cast(n.index())), [](int idx, void *user_data) -> ImPlotPoint { PlotInfo i = *(PlotInfo *) user_data; f32 x = i.begin + (i.end - i.begin) * static_cast(idx) / static_cast(i.samples); CompositionState ss = i.s; @@ -1478,6 +1525,7 @@ namespace K::UI { Plugboard::ConvertValue(Plugboard::Eval(ss, i.connected_v), Plugboard::T_Float, good))}; }, &info, info.samples); + ImGui::PopID(); } ImPlot::EndPlot(); @@ -1554,6 +1602,15 @@ namespace K::UI { } } } + + if (tl_clear_selection_request) + for (auto& p : s.plugboard.selected_nodes) + if (auto *n = std::get_if(&p)){ + auto& [chain, _] = (*n)->extra; + chain.selected.clear(); + } + if (tl_clear_selection_request_this_frame && tl_clear_selection_request) + tl_clear_selection_request = false; } ImGui::EndChild();