mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
Merge remote-tracking branch 'origin/beta'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,3 +35,4 @@ flatpak/build/
|
||||
|
||||
build/
|
||||
.vscode/
|
||||
.cache/
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ¤t_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, ¤t_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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user