From 11d12c1f297c2c12e805e4b48c4026748a545434 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 24 May 2021 00:58:21 +0200 Subject: [PATCH] New Timeline actions Smooth and auto cut actions added on the side of the timeline UI. --- ImGuiToolkit.cpp | 44 +++++++---- Timeline.cpp | 17 ++++- Timeline.h | 2 + UserInterfaceManager.cpp | 161 ++++++++++++++++++++++++++++++--------- UserInterfaceManager.h | 1 + rsc/images/icons.dds | Bin 1638528 -> 1638528 bytes 6 files changed, 172 insertions(+), 53 deletions(-) diff --git a/ImGuiToolkit.cpp b/ImGuiToolkit.cpp index 7d9b8e1..9fd658b 100644 --- a/ImGuiToolkit.cpp +++ b/ImGuiToolkit.cpp @@ -179,30 +179,42 @@ bool ImGuiToolkit::ButtonIconToggle(int i, int j, int i_toggle, int j_toggle, bo bool ImGuiToolkit::IconButton(int i, int j, const char *tooltip) { - bool ret = false; ImGui::PushID( i * 20 + j ); - float frame_height = ImGui::GetFrameHeight(); - float frame_width = frame_height; - ImVec2 draw_pos = ImGui::GetCursorScreenPos(); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; - // toggle action : operate on the whole area - ImGui::InvisibleButton("##iconbutton", ImVec2(frame_width, frame_height)); - if (ImGui::IsItemClicked()) - ret = true; + // duplicate of ImGui::InvisibleButton to handle ImGuiButtonFlags_Repeat + const ImGuiID id = window->GetID("##iconijbutton"); + float h = ImGui::GetFrameHeight(); + ImVec2 size = ImGui::CalcItemSize(ImVec2(h, h), 0.0f, 0.0f); + ImVec2 draw_pos = window->DC.CursorPos; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ImGui::ItemSize(size); + if (!ImGui::ItemAdd(bb, id)) + return false; - ImGui::SetCursorScreenPos(draw_pos); - Icon(i, j, !ret); + ImGuiButtonFlags flags = 0; + if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags); - if (tooltip != nullptr && ImGui::IsItemHovered()) + // tooltip + if (tooltip != nullptr && hovered) { ImGui::BeginTooltip(); ImGui::Text("%s", tooltip); ImGui::EndTooltip(); } + // draw icon + ImGui::SetCursorScreenPos(draw_pos); + Icon(i, j, !pressed); + ImGui::PopID(); - return ret; + return pressed; } @@ -217,7 +229,7 @@ bool ImGuiToolkit::IconButton(const char* icon, const char *tooltip) ImVec2 draw_pos = ImGui::GetCursorScreenPos(); // toggle action : operate on the whole area - ImGui::InvisibleButton("##iconbutton", ImVec2(frame_width, frame_height)); + ImGui::InvisibleButton("##iconcharbutton", ImVec2(frame_width, frame_height)); if (ImGui::IsItemClicked()) ret = true; @@ -806,7 +818,6 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, const ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const float _h_space = g.Style.WindowPadding.x; - const ImU32 fg_color = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] ); ImVec4 bg_color = hovered ? style.Colors[ImGuiCol_FrameBgHovered] : style.Colors[ImGuiCol_FrameBg]; // enter edit if widget is active @@ -886,11 +897,12 @@ bool ImGuiToolkit::EditPlotHistoLines(const char* label, float *histogram_array, // draw the cursor bar if (hovered) { + const ImU32 cur_color = ImGui::GetColorU32( style.Colors[ImGuiCol_CheckMark] ); mouse_pos_in_canvas.x = CLAMP(mouse_pos_in_canvas.x, _h_space, size.x - _h_space); if (edit_histogram) - window->DrawList->AddLine( canvas_pos + ImVec2(mouse_pos_in_canvas.x, 4.f), canvas_pos + ImVec2(mouse_pos_in_canvas.x, size.y - 4.f), fg_color); + window->DrawList->AddLine( canvas_pos + ImVec2(mouse_pos_in_canvas.x, 4.f), canvas_pos + ImVec2(mouse_pos_in_canvas.x, size.y - 4.f), cur_color); else { - window->DrawList->AddCircle( canvas_pos + mouse_pos_in_canvas, 2.f, fg_color, 6); + window->DrawList->AddCircleFilled(canvas_pos + mouse_pos_in_canvas, 3.f, cur_color, 8); } } diff --git a/Timeline.cpp b/Timeline.cpp index 205bef5..799abfc 100644 --- a/Timeline.cpp +++ b/Timeline.cpp @@ -246,7 +246,6 @@ void Timeline::smoothFading(uint N) void Timeline::autoFading(uint milisecond) { - GstClockTime stepduration = timing_.end / MAX_TIMELINE_ARRAY; stepduration = GST_TIME_AS_MSECONDS(stepduration); uint N = milisecond / stepduration; @@ -281,7 +280,23 @@ void Timeline::autoFading(uint milisecond) for (; i < e; ++i) fadingArray_[i] = static_cast(e-i) / static_cast(n); } +} +bool Timeline::autoCut() +{ + bool changed = false; + for (long i = 0; i < MAX_TIMELINE_ARRAY; ++i) { + if (fadingArray_[i] < EPSILON) { + if (gapsArray_[i] != 1.f) + changed = true; + gapsArray_[i] = 1.f; + } + } + + updateGapsFromArray(gapsArray_, MAX_TIMELINE_ARRAY); + gaps_array_need_update_ = false; + + return changed; } void Timeline::updateGapsFromArray(float *array, size_t array_size) diff --git a/Timeline.h b/Timeline.h index a3194ca..9bf0b90 100644 --- a/Timeline.h +++ b/Timeline.h @@ -120,8 +120,10 @@ public: float fadingAt(const GstClockTime t); inline float *fadingArray() { return fadingArray_; } void clearFading(); + void smoothFading(uint N = 1); void autoFading(uint milisecond = 100); + bool autoCut(); private: diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 6be0605..5d2fdd3 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -1986,6 +1986,7 @@ void SourceController::Render() _h_space = g.Style.ItemInnerSpacing.x; _v_space = g.Style.FramePadding.y; _buttons_height = g.FontSize + _v_space * 4.0f ; + _buttons_width = g.FontSize * 7.0f ; _min_width = 6.f * _buttons_height; _timeline_height = (g.FontSize + _v_space) * 2.0f ; // double line for each timeline _scrollbar = g.Style.ScrollbarSize; @@ -2099,31 +2100,36 @@ void SourceController::RenderSelection(size_t i) { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, _v_space)); - // area horizontal pack - int numcolumns = CLAMP( int(ceil(1.0f * rendersize.x / rendersize.y)), 1, numsources ); - ImGui::Columns( numcolumns, "##selectiongrid", false); for (auto source = selection_.begin(); source != selection_.end(); ++source) { - ImVec2 framesize(ImGui::GetColumnWidth(), ImGui::GetColumnWidth() / (*source)->frame()->aspectRatio()); - framesize.x -= _h_space; - ImVec2 image_top = ImGui::GetCursorPos(); + ImVec2 framesize(1.5f * _timeline_height * (*source)->frame()->aspectRatio(), 1.5f * _timeline_height); + + ImVec2 image_top = ImGui::GetCursorPos(); if (SourceButton(*source, framesize)) UserInterface::manager().showSourceEditor(*source); - // Play icon lower left corner - ImGuiToolkit::PushFont(framesize.x > 350.f ? ImGuiToolkit::FONT_LARGE : ImGuiToolkit::FONT_MONO); - float h = ImGui::GetTextLineHeightWithSpacing(); - ImGui::SetCursorPos(image_top + ImVec2( _h_space, framesize.y - h)); +// ImGui::SetCursorPos(image_top + ImVec2( _h_space, framesize.y)); if ((*source)->active()) ImGui::Text("%s %s", (*source)->playing() ? ICON_FA_PLAY : ICON_FA_PAUSE, GstToolkit::time_to_string((*source)->playtime()).c_str() ); else ImGui::Text("%s %s", ICON_FA_SNOWFLAKE, GstToolkit::time_to_string((*source)->playtime()).c_str() ); - ImGui::PopFont(); + + ImGui::SetCursorPos(image_top + ImVec2( framesize.x + _h_space, 0)); + + MediaSource *ms = dynamic_cast(*source); + if (ms != nullptr) { + MediaPlayer *mp = ms->mediaplayer(); + ImVec2 size(rendersize.x - framesize.x - _h_space - _scrollbar, 2.f * _timeline_height); + bool released = false; + ImGuiToolkit::EditPlotHistoLines("##TimelineArray", + mp->timeline()->gapsArray(), + mp->timeline()->fadingArray(), + MAX_TIMELINE_ARRAY, 0.f, 1.f, true, &released, size); + } ImGui::Spacing(); - ImGui::NextColumn(); } ImGui::Columns(1); @@ -2131,6 +2137,42 @@ void SourceController::RenderSelection(size_t i) } ImGui::EndChild(); +// ImGui::BeginChild("##v_scroll", rendersize, false, ImGuiWindowFlags_AlwaysVerticalScrollbar); +// { +// ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, _v_space)); + +// // area horizontal pack +// int numcolumns = CLAMP( int(ceil(1.0f * rendersize.x / rendersize.y)), 1, numsources ); +// ImGui::Columns( numcolumns, "##selectiongrid", false); + +// for (auto source = selection_.begin(); source != selection_.end(); ++source) { + +// ImVec2 framesize(ImGui::GetColumnWidth(), ImGui::GetColumnWidth() / (*source)->frame()->aspectRatio()); +// framesize.x -= _h_space; +// ImVec2 image_top = ImGui::GetCursorPos(); + +// if (SourceButton(*source, framesize)) +// UserInterface::manager().showSourceEditor(*source); + +// // Play icon lower left corner +// ImGuiToolkit::PushFont(framesize.x > 350.f ? ImGuiToolkit::FONT_LARGE : ImGuiToolkit::FONT_MONO); +// float h = ImGui::GetTextLineHeightWithSpacing(); +// ImGui::SetCursorPos(image_top + ImVec2( _h_space, framesize.y - h)); +// if ((*source)->active()) +// ImGui::Text("%s %s", (*source)->playing() ? ICON_FA_PLAY : ICON_FA_PAUSE, GstToolkit::time_to_string((*source)->playtime()).c_str() ); +// else +// ImGui::Text("%s %s", ICON_FA_SNOWFLAKE, GstToolkit::time_to_string((*source)->playtime()).c_str() ); +// ImGui::PopFont(); + +// ImGui::Spacing(); +// ImGui::NextColumn(); +// } + +// ImGui::Columns(1); +// ImGui::PopStyleVar(); +// } +// ImGui::EndChild(); + } /// @@ -2142,18 +2184,15 @@ void SourceController::RenderSelection(size_t i) /// Selection of sources /// ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.14f, 0.14f, 0.14f, 0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.14f, 0.14f, 0.14f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.14f, 0.14f, 0.14f, 0.5f)); - float width_ = ImGui::GetContentRegionAvail().x - _buttons_height; - float combo_width_ = 170.f; - - if (width_ > combo_width_) + if (width_ > _buttons_width) { - ImGui::SameLine(0, width_ -combo_width_ ); - ImGui::SetNextItemWidth(combo_width_); + ImGui::SameLine(0, width_ -_buttons_width ); + ImGui::SetNextItemWidth(_buttons_width); std::string label = std::string(ICON_FA_CHECK_SQUARE " ") + std::to_string(numsources) + ( numsources > 1 ? " sources" : " source"); if (ImGui::BeginCombo("##SelectionImport", label.c_str())) { @@ -2293,6 +2332,29 @@ void SourceController::RenderSelectedSources() /// Play button bar /// DrawButtonBar(bottom, rendersize.x); + + /// + /// New Selection from active sources + /// + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.14f, 0.14f, 0.14f, 0.5f)); + + float space = ImGui::GetContentRegionAvail().x; + float width = _buttons_height; + std::string label(ICON_FA_PLUS_SQUARE); + if (space > _buttons_width) { // enough space to show full button with label text + label += " New Selection"; + width = _buttons_width; + } + ImGui::SameLine(0, space -width); + ImGui::SetNextItemWidth(width); + if (ImGui::Button( label.c_str() )) { + active_selection_ = Mixer::manager().session()->numPlayGroups(); + active_label_ = std::string("Selection #") + std::to_string(active_selection_); + Mixer::manager().session()->addPlayGroup( ids(playable_only(Mixer::selection().getCopy())) ); + } + + ImGui::PopStyleColor(2); } } @@ -2363,6 +2425,7 @@ void SourceController::RenderSingleSource(Source *s) ImGui::Text(" %s", tooltip.str().c_str()); } } + /// /// Play source button bar /// @@ -2509,17 +2572,17 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp) mp->timeline()->clearGaps(); Action::manager().store("Timeline Reset"); } - if (ImGui::BeginMenu("Smooth curve")) - { - const char* names[] = { "Just a little", "A bit more", "Quite a lot"}; - for (int i = 0; i < IM_ARRAYSIZE(names); ++i) { - if (ImGui::MenuItem(names[i])) { - mp->timeline()->smoothFading( 10 * (int) pow(4, i) ); - Action::manager().store("Timeline Smooth curve"); - } - } - ImGui::EndMenu(); - } +// if (ImGui::BeginMenu("Smooth curve")) +// { +// const char* names[] = { "Just a little", "A bit more", "Quite a lot"}; +// for (int i = 0; i < IM_ARRAYSIZE(names); ++i) { +// if (ImGui::MenuItem(names[i])) { +// mp->timeline()->smoothFading( 10 * (int) pow(4, i) ); +// Action::manager().store("Timeline Smooth curve"); +// } +// } +// ImGui::EndMenu(); +// } if (ImGui::BeginMenu("Auto fading")) { const char* names[] = { "250 ms", "500 ms", "1 second", "2 seconds"}; @@ -2589,14 +2652,40 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp) ImGui::EndChild(); // action mode - bottom += ImVec2(scrollwindow.x, 0); - draw_list->AddRectFilled(bottom, bottom + ImVec2(slider_zoom_width+3.f, 0.5f * _timeline_height), ImGui::GetColorU32(ImGuiCol_FrameBg)); - ImGui::SetCursorScreenPos(bottom + ImVec2(3.f, 0)); - ImGuiToolkit::IconToggle(7,4,8,3, &Settings::application.widget.timeline_editmode); + bottom += ImVec2(scrollwindow.x + 2.f, 0.f); + draw_list->AddRectFilled(bottom, bottom + ImVec2(slider_zoom_width, _timeline_height -1.f), ImGui::GetColorU32(ImGuiCol_FrameBg)); + ImGui::SetCursorScreenPos(bottom + ImVec2(1.f, 0.f)); + const char *tooltip[2] = {"Draw opacity", "Cut sections"}; + ImGuiToolkit::IconToggle(7,4,8,3, &Settings::application.widget.timeline_editmode, tooltip); + + ImGui::SetCursorScreenPos(bottom + ImVec2(1.f, 0.5f * _timeline_height)); + if (Settings::application.widget.timeline_editmode) { + // action auto cut + if (ImGuiToolkit::IconButton(14, 12, "Auto cut")) { + if (mp->timeline()->autoCut()) + Action::manager().store("Timeline Auto cut"); + } + } + else { + static int _actionsmooth = 0; + + // action smooth + ImGui::PushButtonRepeat(true); + if (ImGuiToolkit::IconButton(13, 12, "Smooth")){ + mp->timeline()->smoothFading( 5 ); + ++_actionsmooth; + } + ImGui::PopButtonRepeat(); + + if (_actionsmooth > 0 && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { + Action::manager().store("Timeline Smooth"); + _actionsmooth = 0; + } + } // zoom slider - ImGui::SetCursorScreenPos(bottom + ImVec2(3.f, 0.5f * _timeline_height + 3.f)); - ImGui::VSliderFloat("##TimelineZoom", ImVec2(slider_zoom_width, 1.5f * _timeline_height - 3.f), &timeline_zoom, 1.0, 5.f, ""); + ImGui::SetCursorScreenPos(bottom + ImVec2(0.f, _timeline_height)); + ImGui::VSliderFloat("##TimelineZoom", ImVec2(slider_zoom_width, _timeline_height), &timeline_zoom, 1.0, 5.f, ""); ImGui::PopStyleVar(2); diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index 1ce71df..712ddf2 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -108,6 +108,7 @@ class SourceController float _min_width; float _h_space; float _v_space; + float _buttons_width; float _buttons_height; float _timeline_height; float _scrollbar; diff --git a/rsc/images/icons.dds b/rsc/images/icons.dds index d307ef1501bfb453c1a512370091c482cf5a4228..fa007ae379ace246d09953e8d7f2f44264c10e33 100644 GIT binary patch delta 3150 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