mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-12 02:40:00 +01:00
Timeline management in Player
Actions at key times (durations of all videos) to allow to adjust other videos duration (change speed of cut)
This commit is contained in:
@@ -145,7 +145,10 @@ bool ImGuiToolkit::ButtonIcon(int i, int j, const char *tooltip)
|
|||||||
ImVec2 uv1( uv0.x + 0.05, uv0.y + 0.05 );
|
ImVec2 uv1( uv0.x + 0.05, uv0.y + 0.05 );
|
||||||
|
|
||||||
ImGui::PushID( i*20 + j);
|
ImGui::PushID( i*20 + j);
|
||||||
bool ret = ImGui::ImageButton((void*)(intptr_t)textureicons, ImVec2(ImGui::GetTextLineHeightWithSpacing(),ImGui::GetTextLineHeightWithSpacing()), uv0, uv1, 3);
|
ImGuiContext& g = *GImGui;
|
||||||
|
bool ret = ImGui::ImageButton((void*)(intptr_t)textureicons,
|
||||||
|
ImVec2(g.FontSize, g.FontSize),
|
||||||
|
uv0, uv1, g.Style.FramePadding.y);
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
|
|
||||||
if (tooltip != nullptr && ImGui::IsItemHovered())
|
if (tooltip != nullptr && ImGui::IsItemHovered())
|
||||||
@@ -353,6 +356,23 @@ bool ImGuiToolkit::ComboIcon (std::vector<std::pair<int, int> > icons, std::vect
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImGuiToolkit::MenuItemIcon (int i, int j, const char* label, bool selected, bool enabled)
|
||||||
|
{
|
||||||
|
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
|
||||||
|
|
||||||
|
// make some space
|
||||||
|
char text_buf[256];
|
||||||
|
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), " %s", label);
|
||||||
|
|
||||||
|
// draw menu item
|
||||||
|
bool ret = ImGui::MenuItem(text_buf, NULL, selected, enabled);
|
||||||
|
|
||||||
|
// draw icon
|
||||||
|
ImGui::SetCursorScreenPos(draw_pos);
|
||||||
|
Icon(i, j, enabled);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void ImGuiToolkit::ShowIconsWindow(bool* p_open)
|
void ImGuiToolkit::ShowIconsWindow(bool* p_open)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ namespace ImGuiToolkit
|
|||||||
bool IconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[] = nullptr);
|
bool IconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[] = nullptr);
|
||||||
void ShowIconsWindow(bool* p_open);
|
void ShowIconsWindow(bool* p_open);
|
||||||
|
|
||||||
// icon buttons
|
// buttons and gui items with icon
|
||||||
bool ButtonIcon (int i, int j, const char* tooltip = nullptr);
|
bool ButtonIcon (int i, int j, const char* tooltip = nullptr);
|
||||||
bool ButtonIconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle);
|
bool ButtonIconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle);
|
||||||
bool ButtonIconMultistate (std::vector<std::pair<int, int> > icons, int* state);
|
bool ButtonIconMultistate (std::vector<std::pair<int, int> > icons, int* state);
|
||||||
bool ComboIcon (std::vector<std::pair<int, int> > icons, std::vector<std::string> labels, int* state);
|
bool ComboIcon (std::vector<std::pair<int, int> > icons, std::vector<std::string> labels, int* state);
|
||||||
|
bool MenuItemIcon (int i, int j, const char* label, bool selected = false, bool enabled = true);
|
||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
bool ButtonToggle (const char* label, bool* toggle);
|
bool ButtonToggle (const char* label, bool* toggle);
|
||||||
|
|||||||
@@ -676,7 +676,7 @@ bool MediaPlayer::go_to(GstClockTime pos)
|
|||||||
|
|
||||||
GstClockTime jumpPts = pos;
|
GstClockTime jumpPts = pos;
|
||||||
|
|
||||||
if (timeline_.gapAt(pos, gap)) {
|
if (timeline_.getGapAt(pos, gap)) {
|
||||||
// if in a gap, find closest seek target
|
// if in a gap, find closest seek target
|
||||||
if (gap.is_valid()) {
|
if (gap.is_valid()) {
|
||||||
// jump in one or the other direction
|
// jump in one or the other direction
|
||||||
@@ -922,7 +922,7 @@ void MediaPlayer::update()
|
|||||||
else {
|
else {
|
||||||
// manage timeline: test if position falls into a gap
|
// manage timeline: test if position falls into a gap
|
||||||
TimeInterval gap;
|
TimeInterval gap;
|
||||||
if (position_ != GST_CLOCK_TIME_NONE && timeline_.gapAt(position_, gap)) {
|
if (position_ != GST_CLOCK_TIME_NONE && timeline_.getGapAt(position_, gap)) {
|
||||||
// if in a gap, seek to next section
|
// if in a gap, seek to next section
|
||||||
if (gap.is_valid()) {
|
if (gap.is_valid()) {
|
||||||
// jump in one or the other direction
|
// jump in one or the other direction
|
||||||
|
|||||||
98
Timeline.cpp
98
Timeline.cpp
@@ -90,7 +90,7 @@ GstClockTime Timeline::next(GstClockTime time) const
|
|||||||
GstClockTime next_time = time;
|
GstClockTime next_time = time;
|
||||||
|
|
||||||
TimeInterval gap;
|
TimeInterval gap;
|
||||||
if (gapAt(time, gap) && gap.is_valid())
|
if (getGapAt(time, gap) && gap.is_valid())
|
||||||
next_time = gap.end;
|
next_time = gap.end;
|
||||||
|
|
||||||
return next_time;
|
return next_time;
|
||||||
@@ -100,7 +100,7 @@ GstClockTime Timeline::previous(GstClockTime time) const
|
|||||||
{
|
{
|
||||||
GstClockTime prev_time = time;
|
GstClockTime prev_time = time;
|
||||||
TimeInterval gap;
|
TimeInterval gap;
|
||||||
if (gapAt(time, gap) && gap.is_valid())
|
if (getGapAt(time, gap) && gap.is_valid())
|
||||||
prev_time = gap.begin;
|
prev_time = gap.begin;
|
||||||
|
|
||||||
return prev_time;
|
return prev_time;
|
||||||
@@ -125,7 +125,13 @@ void Timeline::refresh()
|
|||||||
fillArrayFromGaps(gapsArray_, MAX_TIMELINE_ARRAY);
|
fillArrayFromGaps(gapsArray_, MAX_TIMELINE_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Timeline::gapAt(const GstClockTime t, TimeInterval &gap) const
|
bool Timeline::gapAt(const GstClockTime t) const
|
||||||
|
{
|
||||||
|
TimeIntervalSet::const_iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
||||||
|
return ( g != gaps_.end() );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timeline::getGapAt(const GstClockTime t, TimeInterval &gap) const
|
||||||
{
|
{
|
||||||
TimeIntervalSet::const_iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
TimeIntervalSet::const_iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
||||||
|
|
||||||
@@ -142,34 +148,78 @@ bool Timeline::addGap(GstClockTime begin, GstClockTime end)
|
|||||||
return addGap( TimeInterval(begin, end) );
|
return addGap( TimeInterval(begin, end) );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Timeline::cut(GstClockTime t)
|
bool Timeline::cut(GstClockTime t, bool left, bool join_extremity)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
if (timing_.includes(t))
|
if (timing_.includes(t))
|
||||||
{
|
{
|
||||||
TimeIntervalSet::iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
TimeIntervalSet::iterator gap = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
|
||||||
// cut a gap
|
|
||||||
if ( g != gaps_.end() )
|
// cut left part
|
||||||
{
|
if (left) {
|
||||||
GstClockTime b = g->begin;
|
// cut a gap
|
||||||
gaps_.erase(g);
|
if ( gap != gaps_.end() )
|
||||||
ret = addGap(b, t);
|
{
|
||||||
}
|
GstClockTime b = gap->begin;
|
||||||
// create a gap
|
gaps_.erase(gap);
|
||||||
else {
|
|
||||||
TimeIntervalSet::iterator previous = gaps_.end();
|
|
||||||
for (g = gaps_.begin(); g != gaps_.end(); previous = g++) {
|
|
||||||
if ( g->begin > t)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (previous == gaps_.end())
|
|
||||||
ret = addGap( TimeInterval(timing_.begin, t) );
|
|
||||||
else {
|
|
||||||
GstClockTime b = previous->begin;
|
|
||||||
gaps_.erase(previous);
|
|
||||||
ret = addGap(b, t);
|
ret = addGap(b, t);
|
||||||
}
|
}
|
||||||
|
// create a gap
|
||||||
|
else {
|
||||||
|
auto previous = gaps_.end();
|
||||||
|
for (auto g = gaps_.begin(); g != gaps_.end(); previous = g++) {
|
||||||
|
if ( g->begin > t)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (join_extremity) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (previous == gaps_.end())
|
||||||
|
ret = addGap( TimeInterval(timing_.begin, t) );
|
||||||
|
else {
|
||||||
|
GstClockTime b = previous->begin;
|
||||||
|
gaps_.erase(previous);
|
||||||
|
ret = addGap( TimeInterval(b, t) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// cut right part
|
||||||
|
else {
|
||||||
|
// cut a gap
|
||||||
|
if ( gap != gaps_.end() )
|
||||||
|
{
|
||||||
|
GstClockTime e = gap->end;
|
||||||
|
gaps_.erase(gap);
|
||||||
|
ret = addGap(t, e);
|
||||||
|
}
|
||||||
|
// create a gap
|
||||||
|
else {
|
||||||
|
auto suivant = gaps_.rend();
|
||||||
|
for (auto g = gaps_.rbegin(); g != gaps_.rend(); suivant = g++) {
|
||||||
|
if ( g->end < t)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (join_extremity) {
|
||||||
|
if (suivant != gaps_.rend()) {
|
||||||
|
for (auto g = gaps_.find(*suivant); g != gaps_.end(); ) {
|
||||||
|
g = gaps_.erase(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = addGap( TimeInterval(t, timing_.end) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (suivant == gaps_.rend())
|
||||||
|
ret = addGap( TimeInterval(t, timing_.end) );
|
||||||
|
else {
|
||||||
|
GstClockTime e = suivant->end;
|
||||||
|
gaps_.erase( gaps_.find(*suivant));
|
||||||
|
ret = addGap( TimeInterval(t, e) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,9 +118,11 @@ public:
|
|||||||
void setGaps(const TimeIntervalSet &g);
|
void setGaps(const TimeIntervalSet &g);
|
||||||
bool addGap(TimeInterval s);
|
bool addGap(TimeInterval s);
|
||||||
bool addGap(GstClockTime begin, GstClockTime end);
|
bool addGap(GstClockTime begin, GstClockTime end);
|
||||||
bool cut(GstClockTime t);
|
bool cut(GstClockTime t, bool left, bool join_extremity);
|
||||||
bool removeGaptAt(GstClockTime t);
|
bool removeGaptAt(GstClockTime t);
|
||||||
bool gapAt(const GstClockTime t, TimeInterval &gap) const;
|
|
||||||
|
bool gapAt(const GstClockTime t) const;
|
||||||
|
bool getGapAt(const GstClockTime t, TimeInterval &gap) const;
|
||||||
|
|
||||||
// Manipulation of Fading
|
// Manipulation of Fading
|
||||||
float fadingAt(const GstClockTime t) const;
|
float fadingAt(const GstClockTime t) const;
|
||||||
|
|||||||
@@ -1986,6 +1986,7 @@ void ToolBox::Render()
|
|||||||
SourceController::SourceController() : _min_width(0.f), _h_space(0.f), _v_space(0.f), _buttons_height(0.f),
|
SourceController::SourceController() : _min_width(0.f), _h_space(0.f), _v_space(0.f), _buttons_height(0.f),
|
||||||
_timeline_height(0.f), _scrollbar(0.f), _mediaplayer_height(0.f), _buttons_width(0.f),
|
_timeline_height(0.f), _scrollbar(0.f), _mediaplayer_height(0.f), _buttons_width(0.f),
|
||||||
active_label_(LABEL_AUTO_MEDIA_PLAYER), active_selection_(-1),
|
active_label_(LABEL_AUTO_MEDIA_PLAYER), active_selection_(-1),
|
||||||
|
_selection_context_menu(false), _selection_mediaplayer(nullptr), _selection_target_slower(0), _selection_target_faster(0),
|
||||||
media_playing_mode_(false), slider_pressed_(false)
|
media_playing_mode_(false), slider_pressed_(false)
|
||||||
{
|
{
|
||||||
info_.setExtendedStringMode();
|
info_.setExtendedStringMode();
|
||||||
@@ -2275,18 +2276,24 @@ void SourceController::RenderSelection(size_t i)
|
|||||||
|
|
||||||
// get max duration and max frame width
|
// get max duration and max frame width
|
||||||
GstClockTime maxduration = 0;
|
GstClockTime maxduration = 0;
|
||||||
|
std::list<guint64> durations;
|
||||||
float maxframewidth = 0.f;
|
float maxframewidth = 0.f;
|
||||||
for (auto source = selection_.begin(); source != selection_.end(); ++source) {
|
for (auto source = selection_.begin(); source != selection_.end(); ++source) {
|
||||||
|
// collect durations of all media sources
|
||||||
MediaSource *ms = dynamic_cast<MediaSource *>(*source);
|
MediaSource *ms = dynamic_cast<MediaSource *>(*source);
|
||||||
if (ms != nullptr) {
|
if (ms != nullptr)
|
||||||
GstClockTime d = (static_cast<double>(ms->mediaplayer()->timeline()->sectionsDuration()) / ms->mediaplayer()->playSpeed());
|
durations.push_back(static_cast<guint64>(static_cast<double>(ms->mediaplayer()->timeline()->sectionsDuration()) / ms->mediaplayer()->playSpeed()));
|
||||||
if ( d > maxduration )
|
// compute the displayed width of frames of this source, and keep the max to align afterwards
|
||||||
maxduration = d;
|
|
||||||
}
|
|
||||||
float w = 1.5f * _timeline_height * (*source)->frame()->aspectRatio();
|
float w = 1.5f * _timeline_height * (*source)->frame()->aspectRatio();
|
||||||
if ( w > maxframewidth)
|
if ( w > maxframewidth)
|
||||||
maxframewidth = w;
|
maxframewidth = w;
|
||||||
}
|
}
|
||||||
|
if (durations.size()>0) {
|
||||||
|
durations.sort();
|
||||||
|
durations.unique();
|
||||||
|
maxduration = durations.back();
|
||||||
|
}
|
||||||
|
|
||||||
// compute the ratio for timeline rendering : width (pixel) per time unit (ms)
|
// compute the ratio for timeline rendering : width (pixel) per time unit (ms)
|
||||||
const float w = rendersize.x -maxframewidth - 3.f * _h_space - _scrollbar;
|
const float w = rendersize.x -maxframewidth - 3.f * _h_space - _scrollbar;
|
||||||
const double width_ratio = static_cast<double>(w) / static_cast<double>(maxduration);
|
const double width_ratio = static_cast<double>(w) / static_cast<double>(maxduration);
|
||||||
@@ -2294,6 +2301,7 @@ void SourceController::RenderSelection(size_t i)
|
|||||||
// draw list in a scroll area
|
// draw list in a scroll area
|
||||||
ImGui::BeginChild("##v_scroll2", rendersize, false);
|
ImGui::BeginChild("##v_scroll2", rendersize, false);
|
||||||
{
|
{
|
||||||
|
|
||||||
// draw play time scale if a duration is set
|
// draw play time scale if a duration is set
|
||||||
if (maxduration > 0) {
|
if (maxduration > 0) {
|
||||||
ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2( maxframewidth + _h_space, 0));
|
ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2( maxframewidth + _h_space, 0));
|
||||||
@@ -2311,7 +2319,9 @@ void SourceController::RenderSelection(size_t i)
|
|||||||
UserInterface::manager().showSourceEditor(*source);
|
UserInterface::manager().showSourceEditor(*source);
|
||||||
|
|
||||||
// text below thumbnail to show status
|
// text below thumbnail to show status
|
||||||
ImGui::Text(" %s %s", SourcePlayIcon(*source), GstToolkit::time_to_string((*source)->playtime()).c_str() );
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||||
|
ImGui::Text("%s %s", SourcePlayIcon(*source), GstToolkit::time_to_string((*source)->playtime()).c_str() );
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
// get media source
|
// get media source
|
||||||
MediaSource *ms = dynamic_cast<MediaSource *>(*source);
|
MediaSource *ms = dynamic_cast<MediaSource *>(*source);
|
||||||
@@ -2320,21 +2330,124 @@ void SourceController::RenderSelection(size_t i)
|
|||||||
MediaPlayer *mp = ms->mediaplayer();
|
MediaPlayer *mp = ms->mediaplayer();
|
||||||
|
|
||||||
// start to draw timeline aligned at maximum frame width + horizontal space
|
// start to draw timeline aligned at maximum frame width + horizontal space
|
||||||
ImGui::SetCursorPos(image_top + ImVec2( maxframewidth + _h_space, 0));
|
ImVec2 pos = image_top + ImVec2( maxframewidth + _h_space, 0);
|
||||||
|
ImGui::SetCursorPos(pos);
|
||||||
|
|
||||||
// draw the mediaplayer's timeline, with the indication of cursor position
|
// draw the mediaplayer's timeline, with the indication of cursor position
|
||||||
// NB: use the same width/time ratio for all to ensure timing vertical correspondance
|
// NB: use the same width/time ratio for all to ensure timing vertical correspondance
|
||||||
DrawTimeline("##timeline_mediaplayer", mp->timeline(), mp->position(), width_ratio / fabs(mp->playSpeed()), framesize.y);
|
DrawTimeline("##timeline_mediaplayer", mp->timeline(), mp->position(), width_ratio / fabs(mp->playSpeed()), framesize.y);
|
||||||
|
|
||||||
ImGui::SetCursorPos(image_top + ImVec2( maxframewidth + _h_space, framesize.y + _v_space));
|
// next icon buttons are small
|
||||||
ImGui::Text("%s play time @ %.2f speed / %s (max duration)",
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(3.f, 3.f));
|
||||||
GstToolkit::time_to_string(mp->timeline()->sectionsDuration()).c_str(),
|
|
||||||
mp->playSpeed(), GstToolkit::time_to_string(maxduration).c_str());
|
// next buttons sub id
|
||||||
|
ImGui::PushID( static_cast<int>(mp->id()));
|
||||||
|
|
||||||
|
// display play speed
|
||||||
|
ImGui::SetCursorPos(pos + ImVec2( 0.f, framesize.y + _v_space));
|
||||||
|
ImGui::Text(UNICODE_MULTIPLY " %.2f", mp->playSpeed());
|
||||||
|
// if not 1x speed, offer to reset
|
||||||
|
if ( fabs( fabs(mp->playSpeed()) - 1.0 ) > EPSILON ) {
|
||||||
|
ImGui::SameLine(0,_h_space);
|
||||||
|
if (ImGuiToolkit::ButtonIcon(19, 15, "Reset speed"))
|
||||||
|
mp->setPlaySpeed( 1.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// if more than one duration of media players, add buttons to adjust
|
||||||
|
if (durations.size() > 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (auto d = durations.crbegin(); d != durations.crend(); ++d) {
|
||||||
|
|
||||||
|
ImGui::PushID( static_cast<int>(*d));
|
||||||
|
|
||||||
|
// calculate position of icons
|
||||||
|
double x = static_cast<double>(*d) * width_ratio;
|
||||||
|
ImGui::SetCursorPos(pos + ImVec2( static_cast<float>(x) - 2.f, framesize.y + _v_space) );
|
||||||
|
// depending on position relative to media play duration, offer corresponding action
|
||||||
|
double secdur = static_cast<double>(mp->timeline()->sectionsDuration());
|
||||||
|
guint64 playdur = static_cast<guint64>( secdur / fabs(mp->playSpeed()) );
|
||||||
|
// last icon in the timeline
|
||||||
|
if ( playdur == (*d) ) {
|
||||||
|
// not the minimum duration :
|
||||||
|
if (playdur > durations.front() ) {
|
||||||
|
// offer to speed up or slow down [<>]
|
||||||
|
if (playdur < durations.back() ) {
|
||||||
|
if ( ImGuiToolkit::ButtonIcon(0, 12, "Adjust duration") ) {
|
||||||
|
auto prev = d;
|
||||||
|
prev--;
|
||||||
|
_selection_target_slower = SIGN(mp->playSpeed()) * secdur / static_cast<double>(*prev);
|
||||||
|
auto next = d;
|
||||||
|
next++;
|
||||||
|
_selection_target_faster = SIGN(mp->playSpeed()) * secdur / static_cast<double>(*next);
|
||||||
|
_selection_mediaplayer = mp;
|
||||||
|
_selection_context_menu = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// offer to speed up [< ]
|
||||||
|
else if ( ImGuiToolkit::ButtonIcon(8, 12, "Adjust duration") ) {
|
||||||
|
auto next = d;
|
||||||
|
next++;
|
||||||
|
_selection_target_faster = SIGN(mp->playSpeed()) * secdur / static_cast<double>(*next);
|
||||||
|
_selection_target_slower = 0.0;
|
||||||
|
_selection_mediaplayer = mp;
|
||||||
|
_selection_context_menu = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// minimum duration : offer to slow down [ >]
|
||||||
|
else if ( ImGuiToolkit::ButtonIcon(9, 12, "Adjust duration") ) {
|
||||||
|
_selection_target_faster = 0.0;
|
||||||
|
auto prev = d;
|
||||||
|
prev--;
|
||||||
|
_selection_target_slower = SIGN(mp->playSpeed()) * secdur / static_cast<double>(*prev);
|
||||||
|
_selection_mediaplayer = mp;
|
||||||
|
_selection_context_menu = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// middle buttons : offer to cut at this position
|
||||||
|
else if ( playdur > (*d) ) {
|
||||||
|
char text_buf[256];
|
||||||
|
GstClockTime cutposition = (*d) * fabs(mp->playSpeed());
|
||||||
|
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "Cut at %s",
|
||||||
|
GstToolkit::time_to_string(cutposition, GstToolkit::TIME_STRING_MINIMAL).c_str());
|
||||||
|
|
||||||
|
if ( ImGuiToolkit::ButtonIcon(9, 3, text_buf) ) {
|
||||||
|
if ( mp->timeline()->cut(cutposition, false, true) ) {
|
||||||
|
std::ostringstream info;
|
||||||
|
info << SystemToolkit::base_filename( mp->filename() ) << ": Timeline " <<text_buf;
|
||||||
|
Action::manager().store(info.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// special case when all media players are (cut to) the same size
|
||||||
|
else if ( durations.size() > 0) {
|
||||||
|
|
||||||
|
// calculate position of icon
|
||||||
|
double x = static_cast<double>(durations.front()) * width_ratio;
|
||||||
|
ImGui::SetCursorPos(pos + ImVec2( static_cast<float>(x) - 2.f, framesize.y + _v_space) );
|
||||||
|
|
||||||
|
// offer only to adjust size by removing ending gap
|
||||||
|
if ( mp->timeline()->gapAt( mp->timeline()->end() ) ) {
|
||||||
|
if ( ImGuiToolkit::ButtonIcon(7, 0, "Remove end gap" )){
|
||||||
|
if ( mp->timeline()->removeGaptAt(mp->timeline()->end()) ) {
|
||||||
|
std::ostringstream info;
|
||||||
|
info << SystemToolkit::base_filename( mp->filename() ) << ": Timeline Remove end gap";
|
||||||
|
Action::manager().store(info.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
// next line position
|
// next line position
|
||||||
ImGui::SetCursorPos(image_top + ImVec2(0, 2.0f * _timeline_height + _v_space));
|
ImGui::SetCursorPos(image_top + ImVec2(0, 2.0f * _timeline_height + 2.f * _v_space));
|
||||||
// ImGui::Spacing();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2342,6 +2455,11 @@ void SourceController::RenderSelection(size_t i)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// context menu from actions above
|
||||||
|
///
|
||||||
|
RenderSelectionContextMenu();
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Play button bar
|
/// Play button bar
|
||||||
///
|
///
|
||||||
@@ -2391,6 +2509,45 @@ void SourceController::RenderSelection(size_t i)
|
|||||||
ImGui::PopStyleColor(4);
|
ImGui::PopStyleColor(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SourceController::RenderSelectionContextMenu()
|
||||||
|
{
|
||||||
|
if (_selection_mediaplayer == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_selection_context_menu) {
|
||||||
|
ImGui::OpenPopup("_speedchange_context_menu");
|
||||||
|
_selection_context_menu = false;
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("_speedchange_context_menu"))
|
||||||
|
{
|
||||||
|
std::ostringstream info;
|
||||||
|
info << SystemToolkit::base_filename( _selection_mediaplayer->filename() );
|
||||||
|
|
||||||
|
if ( ImGuiToolkit::MenuItemIcon(14, 16, ICON_FA_CARET_LEFT " Accelerate", false, fabs(_selection_target_faster) > 0 )){
|
||||||
|
_selection_mediaplayer->setPlaySpeed( _selection_target_faster );
|
||||||
|
info << ": Speed x" << std::setprecision(3) << _selection_target_faster;
|
||||||
|
Action::manager().store(info.str());
|
||||||
|
}
|
||||||
|
if ( ImGuiToolkit::MenuItemIcon(15, 16, ICON_FA_CARET_RIGHT " Slow down", false, fabs(_selection_target_slower) > 0 )){
|
||||||
|
_selection_mediaplayer->setPlaySpeed( _selection_target_slower );
|
||||||
|
info << ": Speed x" << std::setprecision(3) << _selection_target_slower;
|
||||||
|
Action::manager().store(info.str());
|
||||||
|
}
|
||||||
|
if ( _selection_mediaplayer->timeline()->gapAt( _selection_mediaplayer->timeline()->end()) ) {
|
||||||
|
|
||||||
|
if ( ImGuiToolkit::MenuItemIcon(7, 0, "Remove end gap" )){
|
||||||
|
info << ": Remove end gap ";
|
||||||
|
if ( _selection_mediaplayer->timeline()->removeGaptAt(_selection_mediaplayer->timeline()->end()) )
|
||||||
|
Action::manager().store(info.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SourceController::SourceButton(Source *s, ImVec2 framesize)
|
bool SourceController::SourceButton(Source *s, ImVec2 framesize)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
@@ -2722,13 +2879,22 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp)
|
|||||||
if ( ImGuiToolkit::ButtonIconMultistate(iconsloop, ¤t_loop) )
|
if ( ImGuiToolkit::ButtonIconMultistate(iconsloop, ¤t_loop) )
|
||||||
mp->setLoop( (MediaPlayer::LoopMode) current_loop );
|
mp->setLoop( (MediaPlayer::LoopMode) current_loop );
|
||||||
|
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << SystemToolkit::base_filename( mp->filename() );
|
||||||
|
|
||||||
// speed slider (if enough space)
|
// speed slider (if enough space)
|
||||||
float speed = static_cast<float>(mp->playSpeed());
|
float speed = static_cast<float>(mp->playSpeed());
|
||||||
if ( rendersize.x > _min_width * 1.4f ) {
|
if ( rendersize.x > _min_width * 1.4f ) {
|
||||||
ImGui::SameLine(0, MAX(_h_space * 2.f, rendersize.x - _min_width * 1.6f) );
|
ImGui::SameLine(0, MAX(_h_space * 2.f, rendersize.x - _min_width * 1.6f) );
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - _buttons_height );
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - _buttons_height );
|
||||||
if (ImGui::DragFloat( "##Speed", &speed, 0.01f, -10.f, 10.f, "Speed x %.1f", 2.f))
|
if (ImGui::DragFloat( "##Speed", &speed, 0.01f, -10.f, 10.f, "Speed " UNICODE_MULTIPLY " %.1f", 2.f))
|
||||||
mp->setPlaySpeed( static_cast<double>(speed) );
|
mp->setPlaySpeed( static_cast<double>(speed) );
|
||||||
|
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||||
|
oss << ": Speed x" << std::setprecision(3) << speed;
|
||||||
|
Action::manager().store(oss.str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -2742,15 +2908,18 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp)
|
|||||||
ImGui::OpenPopup( "MenuTimeline" );
|
ImGui::OpenPopup( "MenuTimeline" );
|
||||||
if (ImGui::BeginPopup( "MenuTimeline" ))
|
if (ImGui::BeginPopup( "MenuTimeline" ))
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem(UNICODE_MULTIPLY " " ICON_FA_CARET_RIGHT " Reset speed" )){
|
if (ImGuiToolkit::MenuItemIcon(19,15,"Reset speed" )){
|
||||||
speed = 1.f;
|
speed = 1.f;
|
||||||
mp->setPlaySpeed( static_cast<double>(speed) );
|
mp->setPlaySpeed( static_cast<double>(speed) );
|
||||||
|
oss << ": Speed x1";
|
||||||
|
Action::manager().store(oss.str());
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem(ICON_FA_WINDOW_CLOSE " Reset timeline" )){
|
if (ImGui::MenuItem(ICON_FA_WINDOW_CLOSE " Reset timeline" )){
|
||||||
timeline_zoom = 1.f;
|
timeline_zoom = 1.f;
|
||||||
mp->timeline()->clearFading();
|
mp->timeline()->clearFading();
|
||||||
mp->timeline()->clearGaps();
|
mp->timeline()->clearGaps();
|
||||||
Action::manager().store("Timeline Reset");
|
oss << ": Reset timeline";
|
||||||
|
Action::manager().store(oss.str());
|
||||||
}
|
}
|
||||||
if (ImGui::BeginMenu(ICON_FA_RANDOM " Auto fading"))
|
if (ImGui::BeginMenu(ICON_FA_RANDOM " Auto fading"))
|
||||||
{
|
{
|
||||||
@@ -2759,15 +2928,19 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp)
|
|||||||
if (ImGui::MenuItem(names[i])) {
|
if (ImGui::MenuItem(names[i])) {
|
||||||
mp->timeline()->autoFading( 250 * (int ) pow(2, i) );
|
mp->timeline()->autoFading( 250 * (int ) pow(2, i) );
|
||||||
mp->timeline()->smoothFading( 2 * (i + 1) );
|
mp->timeline()->smoothFading( 2 * (i + 1) );
|
||||||
Action::manager().store("Timeline Auto fading");
|
oss << ": Timeline Auto fading " << 250 * (int ) pow(2, i);
|
||||||
|
Action::manager().store(oss.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginMenu(ICON_FA_CUT " Auto cut" )){
|
if (ImGui::BeginMenu(ICON_FA_CUT " Auto cut" )){
|
||||||
if (ImGui::MenuItem("Cut faded areas"))
|
|
||||||
if (mp->timeline()->autoCut())
|
if (ImGuiToolkit::MenuItemIcon(14, 12, "Cut faded areas" ))
|
||||||
Action::manager().store("Timeline Auto cut");
|
if (mp->timeline()->autoCut()){
|
||||||
|
oss << ": Cut faded areas";
|
||||||
|
Action::manager().store(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
@@ -2820,7 +2993,11 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp)
|
|||||||
else if (released)
|
else if (released)
|
||||||
{
|
{
|
||||||
tl->refresh();
|
tl->refresh();
|
||||||
Action::manager().store("Timeline change");
|
if (Settings::application.widget.timeline_editmode)
|
||||||
|
oss << ": Timeline cut";
|
||||||
|
else
|
||||||
|
oss << ": Timeline opacity";
|
||||||
|
Action::manager().store(oss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom timeline slider
|
// custom timeline slider
|
||||||
@@ -2840,10 +3017,27 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp)
|
|||||||
|
|
||||||
ImGui::SetCursorScreenPos(bottom + ImVec2(1.f, 0.5f * _timeline_height));
|
ImGui::SetCursorScreenPos(bottom + ImVec2(1.f, 0.5f * _timeline_height));
|
||||||
if (Settings::application.widget.timeline_editmode) {
|
if (Settings::application.widget.timeline_editmode) {
|
||||||
// action auto cut
|
// action cut
|
||||||
if (ImGuiToolkit::IconButton(9, 3, "Cut at cursor")) {
|
if (mp->isPlaying()) {
|
||||||
if (mp->timeline()->cut(mp->position()))
|
ImGuiToolkit::HelpIcon("Pause video to enable cut options", 9, 3);
|
||||||
Action::manager().store("Timeline Cut");
|
}
|
||||||
|
else if (ImGuiToolkit::IconButton(9, 3, "Cut at cursor")) {
|
||||||
|
ImGui::OpenPopup("timeline_cut_context_menu");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("timeline_cut_context_menu")){
|
||||||
|
if (ImGuiToolkit::MenuItemIcon(1,0,"Cut left")){
|
||||||
|
if (mp->timeline()->cut(mp->position(), true, false)) {
|
||||||
|
oss << ": Timeline cut";
|
||||||
|
Action::manager().store(oss.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGuiToolkit::MenuItemIcon(2,0,"Cut right")){
|
||||||
|
if (mp->timeline()->cut(mp->position(), false, false)){
|
||||||
|
oss << ": Timeline cut";
|
||||||
|
Action::manager().store(oss.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2858,7 +3052,8 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp)
|
|||||||
ImGui::PopButtonRepeat();
|
ImGui::PopButtonRepeat();
|
||||||
|
|
||||||
if (_actionsmooth > 0 && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
if (_actionsmooth > 0 && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||||
Action::manager().store("Timeline Smooth");
|
oss << ": Timeline opacity smooth";
|
||||||
|
Action::manager().store(oss.str());
|
||||||
_actionsmooth = 0;
|
_actionsmooth = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,14 +120,24 @@ class SourceController
|
|||||||
InfoVisitor info_;
|
InfoVisitor info_;
|
||||||
SourceList selection_;
|
SourceList selection_;
|
||||||
|
|
||||||
|
bool _selection_context_menu;
|
||||||
|
MediaPlayer *_selection_mediaplayer;
|
||||||
|
double _selection_target_slower;
|
||||||
|
double _selection_target_faster;
|
||||||
|
void RenderSelectionContextMenu();
|
||||||
|
|
||||||
|
// re-usable ui parts
|
||||||
void DrawButtonBar(ImVec2 bottom, float width);
|
void DrawButtonBar(ImVec2 bottom, float width);
|
||||||
const char *SourcePlayIcon(Source *s);
|
const char *SourcePlayIcon(Source *s);
|
||||||
|
|
||||||
bool SourceButton(Source *s, ImVec2 framesize);
|
bool SourceButton(Source *s, ImVec2 framesize);
|
||||||
void RenderSelectedSources();
|
|
||||||
void RenderSelection(size_t i);
|
|
||||||
void RenderSingleSource(Source *s);
|
|
||||||
|
|
||||||
|
// Render the sources dynamically selected
|
||||||
|
void RenderSelectedSources();
|
||||||
|
// Render a stored selection
|
||||||
|
void RenderSelection(size_t i);
|
||||||
|
// Render a single source
|
||||||
|
void RenderSingleSource(Source *s);
|
||||||
|
// Render a single media player
|
||||||
bool media_playing_mode_;
|
bool media_playing_mode_;
|
||||||
bool slider_pressed_;
|
bool slider_pressed_;
|
||||||
void RenderMediaPlayer(MediaPlayer *mp);
|
void RenderMediaPlayer(MediaPlayer *mp);
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user