Merge remote-tracking branch 'origin/beta'

This commit is contained in:
Bruno Herbelin
2025-11-14 09:01:16 +01:00
11 changed files with 292 additions and 130 deletions

1
.gitignore vendored
View File

@@ -35,3 +35,4 @@ flatpak/build/
build/
.vscode/
.cache/

View File

@@ -36,22 +36,18 @@ Install the runtime environments:
flatpak install org.gnome.Platform
_Select version **47** in the list of proposed versions_
_Select version **49** in the list of proposed versions_
### 2. Build vimix flatpak
These settings of git are needed to enable clone of local repos during build:
These settings of git are needed to enable clone of local repos during build (done only once):
git config --global --add protocol.file.allow always
Get the flatpak manifest for vimix:
curl -O https://raw.githubusercontent.com/brunoherbelin/vimix/beta/flatpak/io.github.brunoherbelin.Vimix.json
Launch the build of the flatpak:
flatpak-builder --user --install --force-clean build io.github.brunoherbelin.Vimix.json
flatpak-builder --user --install --from-git=https://github.com/brunoherbelin/vimix.git --from-git-branch=beta --delete-build-dirs --force-clean build flatpak/io.github.brunoherbelin.Vimix.json
The build will be quite long as some dependencies are also re-build from source. However, the build of dependencies is kept in cache; rebuilding vimix will subsequently be much faster.
@@ -85,7 +81,7 @@ To build the vimix flatpak with code from local folder (debugging), change the f
"sources": [
{
"type":"dir",
"path": "/home/bhbn/Development/vimix",
"path": "[your_development_dir]/vimix",
}
]
}

View File

@@ -142,8 +142,8 @@
"sources": [
{
"type": "git",
"tag": "1.26.7",
"commit": "c5a5c302f5e7218182c0633decec16b25de82add",
"tag": "1.26.8",
"commit": "16d77e12ad213ef24e76a8cc34d347b8221c9975",
"url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git",
"disable-submodules": false
}

Binary file not shown.

View File

@@ -243,7 +243,9 @@ bool ImGuiToolkit::ButtonIcon(int i, int j, const char *tooltip, bool enabled, b
if (enabled)
ret = ImGui::ImageButton((void*)(intptr_t)textureicons,
ImVec2(g.FontSize, g.FontSize),
uv0, uv1, g.Style.FramePadding.y);
uv0, uv1, g.Style.FramePadding.y,
ImVec4(0.f, 0.f, 0.f, 0.f),
g.Style.Colors[ImGuiCol_Text]);
else
ImGui::ImageButton((void*)(intptr_t)textureicons,
ImVec2(g.FontSize, g.FontSize),
@@ -804,12 +806,10 @@ bool ImGuiToolkit::SliderTiming (const char* label, uint* ms, uint v_min, uint v
// *ms = val * v_step;
// quadratic scale
float val = *ms / v_step;
bool ret = ImGui::SliderFloat(label, &val, v_min / v_step, v_max / v_step, text_buf, 2.f);
float val = (float) (*ms / v_step);
bool ret = ImGui::SliderFloat(label, &val, (float) (v_min / v_step), (float) (v_max / v_step), text_buf, 2.f);
*ms = int(floor(val)) * v_step;
return ret;
}
@@ -853,23 +853,24 @@ void ImGuiToolkit::RenderTimeline (ImVec2 min_bbox, ImVec2 max_bbox, guint64 beg
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
// large space
if (tick_step_pixels > 5.f && step > 0)
if (tick_step_pixels > 10.f && step > 0)
{
// try to put a label ticks every second
label_tick_step = (SECOND / step) * step;
large_tick_step = label_tick_step % 5 ? (label_tick_step % 2 ? label_tick_step : label_tick_step / 2 ) : label_tick_step / 5;
tick_delta = SECOND - label_tick_step;
// round to nearest
if (tick_delta > step / 2) {
label_tick_step += step;
large_tick_step += step;
tick_delta = SECOND - label_tick_step;
}
else {
// try to put a label ticks every second
label_tick_step = (SECOND / step) * step;
large_tick_step = label_tick_step % 5 ? (label_tick_step % 2 ? label_tick_step : label_tick_step / 2 ) : label_tick_step / 5;
tick_delta = SECOND - label_tick_step;
}
}
else {
// while there is less than 5 pixels between two tick marks (or at last optimal tick mark)
for ( int i=0; i<10 && tick_step_pixels < 5.f; ++i )
for ( int i=0; i<10 && tick_step_pixels < 10.f; ++i )
{
// try to use the optimal tick marks pre-defined
tick_step = optimal_tick_marks[i];
@@ -1327,7 +1328,7 @@ void ImGuiToolkit::SetFont (ImGuiToolkit::font_style style, const std::string &t
static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
font_config.MergeMode = true; // Merging glyphs into current font
font_config.PixelSnapH = true;
font_config.GlyphOffset.y = pointsize/20;
font_config.GlyphOffset.y = (float) (pointsize/20);
fontname = "icons" + fontname;
fontname.copy(font_config.Name, 40);
// load FontAwesome only once

View File

@@ -22,6 +22,7 @@
// Desktop OpenGL function loader
#include <glad/glad.h>
#include "Timeline.h"
#include "defines.h"
#include "Log.h"
#include "Resource.h"
@@ -1121,6 +1122,7 @@ bool MediaPlayer::go_to(GstClockTime pos)
GstClockTime jumpPts = pos;
// jump in a gap
if (timeline_.getGapAt(pos, gap)) {
// if in a gap, find closest seek target
if (gap.is_valid()) {
@@ -1399,18 +1401,23 @@ void MediaPlayer::update()
need_loop = true;
}
}
// test if position is flagged
// detect flags if playing
else if ( isPlaying() ) {
int t = timeline_.flagTypeAt(position_);
if ( t > 0 ) {
// Avoid to pause repeatedly when inside a flagged section
if (flag_status_ == LoopStatus::LOOP_STATUS_DEFAULT) {
loop_status_ = flag_status_ = (LoopStatus) t;
play(false);
}
}
else
flag_status_ = LoopStatus::LOOP_STATUS_DEFAULT;
// check if position is flagged
TimeInterval flag = timeline_.getFlagAt(position_);
// if type of flag requires stop and not already current
if ( flag.is_valid() && flag.type > 0 ) {
if (flag != current_flag_) {
// set flag as current
current_flag_ = flag;
// set timeline to temporary state (stop or blackout)
loop_status_ = (LoopStatus)current_flag_.type;
// effectively stop playing
play(false);
}
}
else
current_flag_.reset();
}
}
@@ -1589,6 +1596,33 @@ void MediaPlayer::setTimeline(const Timeline &tl)
timeline_ = tl;
}
bool MediaPlayer::go_to_flag(TimeInterval flag)
{
bool ret = false;
if ( flag.is_valid() ) {
// flag target position
GstClockTime flagPts = flag.midpoint();
if (ABS_DIFF (position_, flagPts) > 2 * timeline_.step() ) {
// seek if valid target position
ret = true;
seek( flagPts );
// will reach that flag
current_flag_ = flag;
// change timeline status accordingly
if ( flag.type == (int) LoopStatus::LOOP_STATUS_BLACKOUT)
loop_status_ = LoopStatus::LOOP_STATUS_BLACKOUT;
else if ( flag.type == (int) LoopStatus::LOOP_STATUS_STOPPED)
loop_status_ = LoopStatus::LOOP_STATUS_DEFAULT;
}
}
return ret;
}
MediaInfo MediaPlayer::media() const
{
return media_;

View File

@@ -225,6 +225,17 @@ public:
* Set fading mode
* */
void setTimelineFadingMode(FadingMode m) { fading_mode_ = m; }
/**
* go to a flag position in media timeline.
* return true if seek is performed
* */
bool go_to_flag(TimeInterval flag);
/**
* Set / Get the flag where media player currently is.
* Flag is invalid if player is not currenlty on a flag.
* */
void setCurrentFlag(TimeInterval flag) { current_flag_ = flag; }
TimeInterval currentFlag() const { return current_flag_; };
/**
* Get framerate of the media
* */
@@ -328,7 +339,7 @@ private:
GstClockTime position_;
LoopMode loop_;
LoopStatus loop_status_;
LoopStatus flag_status_;
TimeInterval current_flag_;
GstState desired_state_;
GstElement *pipeline_;
GstBus *bus_;

View File

@@ -937,7 +937,9 @@ void SessionLoader::visit(MediaPlayer &n)
flag->QueryUnsigned64Attribute("begin", &a);
flag->QueryUnsigned64Attribute("end", &b);
flag->QueryIntAttribute("type", &t);
tl.addFlag( TimeInterval( (GstClockTime) a, (GstClockTime) b ), t );
TimeInterval flag((GstClockTime) a, (GstClockTime) b);
flag.type = t;
tl.addFlag( flag );
}
}
n.setTimeline(tl);

View File

@@ -26,6 +26,9 @@
// ImGui
#include "ImGuiToolkit.h"
#include "Timeline.h"
#include "gst/gstclock.h"
#include "imgui.h"
#include "imgui_internal.h"
#include "defines.h"
@@ -702,9 +705,9 @@ bool EditTimeline(const char *label,
// exception: if flag was removed at same time, do not add it back
if ( removed_flag_time != time ) {
if (removed_flag_type >= 0)
tl->addFlag(time, removed_flag_type);
tl->addFlagAt(time, removed_flag_type);
else
tl->addFlag(time, Settings::application.widget.media_player_timeline_flag);
tl->addFlagAt(time, Settings::application.widget.media_player_timeline_flag);
}
removed_flag_time = 0;
removed_flag_type = -1;
@@ -769,7 +772,7 @@ bool EditTimeline(const char *label,
break;
case TimelinePayload::FLAG_ADD:
_tl = *tl;
_tl.addFlag(time, pl->argument);
_tl.addFlagAt(time, pl->argument);
flags_array = _tl.flagsArray();
break;
case TimelinePayload::FLAG_REMOVE:
@@ -869,7 +872,7 @@ bool EditTimeline(const char *label,
return array_changed;
}
bool TimelineSlider (const char* label, guint64 *time, Timeline *tl, const float width)
bool TimelineSlider (const char* label, guint64 *time, TimeInterval *flag, Timeline *tl, const float width)
{
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
@@ -908,52 +911,16 @@ bool TimelineSlider (const char* label, guint64 *time, Timeline *tl, const float
// units conversion: from time to float (calculation made with higher precision first)
float time_ = static_cast<float> ( static_cast<double>(*time - tl->begin()) / static_cast<double>(tl->duration()) );
// Render the bounding box
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
// render the timeline
ImGuiToolkit::RenderTimeline(timeline_bbox.Min, timeline_bbox.Max, tl->begin(), tl->end(), tl->step());
//
// FLAGS
//
bool flag_clicked = false;
const TimeIntervalSet flags = tl->flags();
for (const auto &flag_Interval : flags) {
GstClockTime flag_time = flag_Interval.midpoint();
float flag_pos_ = static_cast<float> ( static_cast<double>(flag_time - tl->begin()) / static_cast<double>(tl->duration()) );
ImVec2 flag_pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), flag_pos_);
flag_pos -= ImVec2(2.f, -3.f);
bool hovered = false, held = false;
ImRect bb(flag_pos, flag_pos + ImVec2(ImGui::GetTextLineHeightWithSpacing(), ImGui::GetTextLineHeightWithSpacing()));
const ImGuiID fid = window->GetID((void*)(intptr_t)(flag_time));
if ( ImGui::ButtonBehavior(bb, fid, &hovered, &held, ImGuiButtonFlags_PressedOnClick) ) {
*time = flag_time;
flag_clicked = true;
}
// icon depends on flag type
_drawIcon(flag_pos, 11 + flag_Interval.type, 6, hovered, window);
// show time when hovering
if (hovered)
ImGui::SetTooltip(" %s ", GstToolkit::time_to_string(flag_time).c_str());
}
//
// GET SLIDER INPUT AND PERFORM CHANGES AND DECISIONS
// GET INPUT
//
// read user input from system
bool left_mouse_press = false;
const bool hovered = ImGui::ItemHoverable(bbox, id);
bool hovered = ImGui::ItemHoverable(bbox, id);
bool temp_input_is_active = ImGui::TempInputIsActive(id);
// slider only if no flag clicked
if (!flag_clicked && !temp_input_is_active)
if (!temp_input_is_active)
{
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
left_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left);
@@ -965,30 +932,76 @@ bool TimelineSlider (const char* label, guint64 *time, Timeline *tl, const float
}
}
// time Slider behavior
//
// BACKGROUND
//
// Render the bounding box
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
// render the timeline
ImGuiToolkit::RenderTimeline(timeline_bbox.Min, timeline_bbox.Max, tl->begin(), tl->end(), tl->step());
//
// FLAGS
//
bool flag_pressed = false;
const TimeIntervalSet flags = tl->flags();
for (const auto &flag_Interval : flags) {
// set position in screen corresponding to flag time
GstClockTime flag_time = flag_Interval.midpoint();
float flag_pos_ = static_cast<float> ( static_cast<double>(flag_time - tl->begin()) / static_cast<double>(tl->duration()) );
ImVec2 draw_pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), flag_pos_);
draw_pos -= ImVec2(2.f, -3.f);
// simulate Button behavior : if mouse hovering flag and mouse pressed
ImRect bb(draw_pos, draw_pos + ImVec2(ImGui::GetTextLineHeightWithSpacing(), ImGui::GetTextLineHeightWithSpacing()));
bool hovered = ImGui::ItemHoverable(bb, id);
if (hovered && left_mouse_press) {
flag_pressed = true;
*flag = flag_Interval;
ImGui::MarkItemEdited(id);
}
// icon depends on flag type & color on hovered
ImGui::PushStyleColor( ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_NavHighlight) );
_drawIcon(draw_pos, 11 + flag_Interval.type, 6, hovered, window);
ImGui::PopStyleColor();
// show time when hovering
if (hovered)
ImGui::SetTooltip(" %s ", GstToolkit::time_to_string(flag_time).c_str());
}
//
// CURSOR
//
bool cursor_pressed = false;
ImRect grab_slider_bb;
ImU32 grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrab);
float time_slider = time_ * 10.f; // x 10 precision on grab
float time_zero = 0.f;
float time_end = 10.f;
bool value_changed = ImGui::SliderBehavior(slider_bbox, id, ImGuiDataType_Float, &time_slider, &time_zero,
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
if (!flag_pressed)
{
float time_slider = time_ * 10.f; // x 10 precision on grab
float time_zero = 0.f;
float time_end = 10.f;
cursor_pressed = ImGui::SliderBehavior(slider_bbox, id, ImGuiDataType_Float, &time_slider, &time_zero,
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
if (value_changed){
if (cursor_pressed){
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(tl->duration()) );
if (tl->first() != GST_CLOCK_TIME_NONE)
*time -= tl->first();
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(tl->duration()) );
if (tl->first() != GST_CLOCK_TIME_NONE)
*time -= tl->first();
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
ImGui::MarkItemEdited(id);
ImGui::MarkItemEdited(id);
}
}
//
// RENDER CURSOR
//
// draw slider grab handle
if (grab_slider_bb.Max.x > grab_slider_bb.Min.x) {
window->DrawList->AddRectFilled(grab_slider_bb.Min, grab_slider_bb.Max, grab_slider_color, style.GrabRounding);
@@ -998,7 +1011,7 @@ bool TimelineSlider (const char* label, guint64 *time, Timeline *tl, const float
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
ImGui::RenderArrow(window->DrawList, pos, ImGui::GetColorU32(ImGuiCol_SliderGrab), ImGuiDir_Up);
return (flag_clicked || left_mouse_press);
return cursor_pressed;
}
@@ -2165,7 +2178,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
static std::vector< std::pair<int, int> > icons_loop = { {0, 15}, {1, 15}, {19, 14}, {18, 14} };
static std::vector< std::string > tooltips_loop = { "Stop at end", "Loop to start", "Bounce (reverse speed)", "Stop and blackout at end" };
static std::vector< std::pair<int, int> > icons_flags = { {11, 6}, {12, 6}, {13, 6} };
static std::vector< std::string > tooltips_flags = { "Bookmark", "Stop Flag", "Blackout Flag" };
static std::vector< std::string > tooltips_flags = { " Bookmark", " Stop Flag", " Blackout Flag" };
double current_play_speed = mediaplayer_active_->playSpeed();
static uint counter_menu_timeout = 0;
@@ -2180,6 +2193,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
// seek position
guint64 seek_t = mediaplayer_active_->position();
TimeInterval seek_flag;
// scrolling sub-window
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(1.f, 1.f));
@@ -2215,7 +2229,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
}
// custom timeline slider
// TODO : if (mediaplayer_active_->syncToMetronome() > Metronome::SYNC_NONE)
mediaplayer_slider_pressed_ = TimelineSlider("##timeline", &seek_t, tl, size.x);
mediaplayer_slider_pressed_ = TimelineSlider("##timeline", &seek_t, &seek_flag, tl, size.x);
}
}
ImGui::EndChild();
@@ -2290,38 +2304,53 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
}
// flag buttons
if ( !mediaplayer_mode_ && mediaplayer_active_->timeline()->numFlags() > 0 ) {
if (rendersize.x > buttons_height_ * 6.0f) {
if ( !mediaplayer_mode_ && mediaplayer_active_->timeline()->numFlags() > 0 ) {
ImGui::SameLine(0, h_space_);
if( ImGuiToolkit::ButtonIcon(3, 0, "Go to next flag") ){
// find next flag and go to its midpoint
TimeInterval next_flag = mediaplayer_active_->timeline()->getNextFlag( mediaplayer_active_->position() );
if ( next_flag.is_valid() )
mediaplayer_active_->go_to( next_flag.midpoint() );
}
GstClockTime _paused_time = mediaplayer_active_->position();
// if stopped at a flag, show flag type editor
if (mediaplayer_active_->timeline()->isFlagged( mediaplayer_active_->position() )) {
static int current_flag = 0;
current_flag = mediaplayer_active_->timeline()->flagTypeAt( mediaplayer_active_->position() );
ImGui::SameLine(0, h_space_);
if ( ImGuiToolkit::IconMultistate(icons_flags, &current_flag, tooltips_flags) ){
mediaplayer_active_->timeline()->setFlagTypeAt( mediaplayer_active_->position(), current_flag );
oss << ": Flag type changed";
Action::manager().store(oss.str());
if( mediaplayer_active_->playSpeed() < 0 ) {
// Go to previous flag when playing backward
TimeInterval target_flag = mediaplayer_active_->timeline()->getPreviousFlag( _paused_time );
bool has_prev = target_flag.is_valid() &&
!( mediaplayer_active_->currentFlag().is_valid() && mediaplayer_active_->timeline()->numFlags() == 1) &&
( mediaplayer_active_->loop() == MediaPlayer::LOOP_REWIND || (target_flag.end < _paused_time) );
if( ImGuiToolkit::ButtonIcon(6, 0, "Go to previous flag", has_prev) )
mediaplayer_active_->go_to_flag( target_flag );
}
else {
// go to next flag when playing forward
TimeInterval target_flag = mediaplayer_active_->timeline()->getNextFlag( _paused_time );
bool has_next = target_flag.is_valid() &&
!( mediaplayer_active_->currentFlag().is_valid() && mediaplayer_active_->timeline()->numFlags() == 1) &&
( mediaplayer_active_->loop() == MediaPlayer::LOOP_REWIND || (target_flag.begin > _paused_time) );
if( ImGuiToolkit::ButtonIcon(5, 0, "Go to next flag", has_next) )
mediaplayer_active_->go_to_flag( target_flag );
}
// if stopped at a flag, show flag menu
if (mediaplayer_active_->currentFlag().is_valid()) {
ImGui::SameLine(0, h_space_);
if (ImGuiToolkit::IconButton(3, 0) || ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
counter_menu_timeout=0;
ImGui::OpenPopup( "MenuMediaPlayerFlags" );
}
}
}
}
else {
ImGui::SameLine(0, h_space_);
ImGuiToolkit::ButtonIcon(3, 0, nullptr, false);
else {
ImGui::SameLine(0, h_space_);
ImGuiToolkit::ButtonIcon(mediaplayer_active_->playSpeed() < 0 ? 6 : 5, 0, nullptr, false);
}
}
// right aligned buttons (if enough space)
if ( rendersize.x > min_width_ * 1.5f ) {
if ( rendersize.x > buttons_height_ * 9.5f ) {
ImGui::SameLine();
ImGui::SetCursorPosX(rendersize.x - buttons_height_ * 4.f);
// loop modes button
ImGui::SameLine(0, MAX(h_space_ , rendersize.x - min_width_ * 1.55f) );
static int current_loop = 0;
current_loop = (int) mediaplayer_active_->loop();
if ( ImGuiToolkit::IconMultistate(icons_loop, &current_loop, tooltips_loop) )
@@ -2329,7 +2358,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
// speed slider
ImGui::SameLine(0, h_space_);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - buttons_height_ );
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - buttons_height_ - h_space_);
float s = fabs(static_cast<float>(current_play_speed));
if (ImGui::DragFloat( "##Speed", &s, 0.01f, 0.1f, 10.f, UNICODE_MULTIPLY " %.2f"))
mediaplayer_active_->setPlaySpeed( SIGN(current_play_speed) * static_cast<double>(s) );
@@ -2369,6 +2398,16 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
// AND force to stop when the slider is pressed
bool media_play = mediaplayer_mode_ & (!mediaplayer_slider_pressed_);
// Flag pressed in timeline
if (seek_flag.is_valid()) {
// go to the flag position
if ( mediaplayer_active_->go_to_flag(seek_flag) ){
// stop if flag type is 'Stop' (1) or 'Blackout' (2)
if (seek_flag.type > 0)
media_play = false;
}
}
// apply play action to media only if status should change
if ( mediaplayer_active_->isPlaying() != media_play ) {
mediaplayer_active_->play( media_play );
@@ -2430,6 +2469,35 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
ImGui::EndPopup();
}
if (ImGui::BeginPopup( "MenuMediaPlayerFlags" ))
{
int num_flags = static_cast<int>(icons_flags.size());
TimeInterval copy_flag = mediaplayer_active_->currentFlag();
for (int i = 0; i < num_flags; ++i) {
if (ImGuiToolkit::MenuItemIcon(icons_flags[i].first,icons_flags[i].second,
tooltips_flags[i].c_str(), nullptr, copy_flag.type == i )) {
copy_flag.type = i;
mediaplayer_active_->timeline()->replaceFlag( copy_flag );
mediaplayer_active_->setCurrentFlag( copy_flag );
oss << ": Flag changed";
Action::manager().store(oss.str());
}
}
ImGui::Separator();
if (ImGuiToolkit::MenuItemIcon(2,0, "Delete flag")) {
mediaplayer_active_->timeline()->removeFlagAt(mediaplayer_active_->currentFlag().midpoint() );
oss << ": Flag removed";
Action::manager().store(oss.str());
}
if (ImGui::IsWindowHovered())
counter_menu_timeout=0;
else if (++counter_menu_timeout > 10)
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
///
/// Window area to edit gaps or fading
///
@@ -2756,7 +2824,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
ImGui::SetCursorPos( ImVec2(w, draw_pos.y));
if (ImGuiToolkit::ButtonIcon(1, 0, "Add flag at given time", target_time_valid)) {
tl->removeFlagAt(target_time);
tl->addFlag(target_time, Settings::application.widget.media_player_timeline_flag);
tl->addFlagAt(target_time, Settings::application.widget.media_player_timeline_flag);
tl->refresh();
oss << ": Timeline flag add";
Action::manager().store(oss.str());
@@ -2766,7 +2834,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::ButtonIcon(0, 0, "Add flag at cursor position", !mediaplayer_active_->isPlaying()) ) {
tl->removeFlagAt(mediaplayer_active_->position());
tl->addFlag(mediaplayer_active_->position(), Settings::application.widget.media_player_timeline_flag);
tl->addFlagAt(mediaplayer_active_->position(), Settings::application.widget.media_player_timeline_flag);
tl->refresh();
oss << ": Timeline flag add";
Action::manager().store(oss.str());

View File

@@ -957,7 +957,7 @@ float *Timeline::flagsArray()
return flagsArray_;
}
bool Timeline::addFlag(GstClockTime t, int type)
bool Timeline::addFlagAt(GstClockTime t, int type)
{
if (t > timing_.begin + (step_ * 2)
&& t < timing_.end - (step_ * 2)
@@ -979,16 +979,27 @@ bool Timeline::addFlag(GstClockTime t, int type)
return false;
}
bool Timeline::addFlag(TimeInterval s, int type)
bool Timeline::addFlag(TimeInterval f)
{
if ( s.is_valid() ) {
s.type = type;
if ( f.is_valid() ) {
flags_array_need_update_ = true;
return flags_.insert(s).second;
return flags_.insert(f).second;
}
return false;
}
bool Timeline::replaceFlag(TimeInterval f)
{
if ( f.is_valid() ) {
if (!removeFlagAt(f.midpoint()))
return false;
flags_array_need_update_ = true;
return flags_.insert(f).second;
}
return false;
}
bool Timeline::removeFlagAt(GstClockTime t)
{
@@ -1033,6 +1044,19 @@ void Timeline::setFlagTypeAt(GstClockTime t, int type)
}
}
TimeInterval Timeline::getFlagAt(GstClockTime t) const
{
TimeInterval ret;
TimeIntervalSet::iterator f = std::find_if(flags_.begin(), flags_.end(), includesTime(t));
if ( f != flags_.end() ) {
ret = *f;
}
return ret;
}
TimeInterval Timeline::getNextFlag(GstClockTime t) const
{
if ( !flags_.empty() ) {
@@ -1054,6 +1078,27 @@ TimeInterval Timeline::getNextFlag(GstClockTime t) const
return TimeInterval();
}
TimeInterval Timeline::getPreviousFlag(GstClockTime t) const
{
if ( !flags_.empty() ) {
// loop over flags
auto f = flags_.rbegin();
for (; f != flags_.rend(); ++f) {
// gap before target?
if ( f->end < t )
// done
break;
}
if ( f != flags_.rend() )
return (*f);
else
return *(flags_.rbegin());
}
return TimeInterval();
}
void Timeline::clearFlags()
{
flags_.clear();

View File

@@ -69,6 +69,7 @@ struct TimeInterval
if (this != &b) {
this->begin = b.begin;
this->end = b.end;
this->type = b.type;
}
return *this;
}
@@ -150,13 +151,16 @@ public:
inline TimeIntervalSet flags() const { return flags_; };
inline size_t numFlags() const { return flags_.size(); };
float *flagsArray();
bool addFlag(GstClockTime t, int type = 0);
bool addFlag(TimeInterval s, int type = 0);
bool addFlag(TimeInterval f);
bool replaceFlag(TimeInterval f);
bool addFlagAt(GstClockTime t, int type = 0);
bool removeFlagAt(GstClockTime t);
bool isFlagged(GstClockTime t) const;
int flagTypeAt(GstClockTime t) const;
void setFlagTypeAt(GstClockTime t, int type);
TimeInterval getFlagAt(GstClockTime t) const;
TimeInterval getNextFlag(GstClockTime t) const;
TimeInterval getPreviousFlag(GstClockTime t) const;
void clearFlags();
// inverse of gaps: sections of play areas