mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-08 08:50:00 +01:00
New Timeline actions
Smooth and auto cut actions added on the side of the timeline UI.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
Timeline.cpp
17
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<float>(e-i) / static_cast<float>(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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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<MediaSource *>(*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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user