Refactor timeline flag handling and enhance media player functionality

- Removed the TimelineSlider function declaration from ImGuiToolkit.h.
- Updated InputTime function signature in ImGuiToolkit.h to include max_time and validity check.
- Modified MediaPlayer to manage flag statuses more effectively, avoiding repeated pauses during flagged sections.
- Added flag_status_ to MediaPlayer.h to track the current flag status.
- Enhanced SessionLoader to query and set flag types when loading media player sessions.
- Updated SessionVisitor to include flag type attributes in XML serialization.
- Extended Settings to save and load timeline flag configurations.
- Adjusted SourceControlWindow to incorporate flag type management and UI updates for flag interactions.
- Implemented new TimelineSlider function to handle timeline interactions with flags.
- Enhanced Timeline class to support flag types, including methods for adding, retrieving, and setting flag types.
- Updated TimeInterval structure to include a type field for flags.
This commit is contained in:
brunoherbelin
2025-11-07 17:08:13 +01:00
parent 7fd8bc5633
commit 9b432f3fc9
12 changed files with 387 additions and 196 deletions
Binary file not shown.
+49 -140
View File
@@ -220,7 +220,7 @@ bool ImGuiToolkit::ButtonIcon(int i, int j, const char *tooltip, bool enabled, b
const int space_num = static_cast<int>( ceil(g.FontSize / space_size.x) );
space_buf[space_num]='\0';
char text_buf[256];
char text_buf[512];
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s %s", space_buf, tooltip);
ImVec2 draw_pos = ImGui::GetCursorScreenPos() + g.Style.FramePadding * 0.5;
@@ -237,7 +237,9 @@ bool ImGuiToolkit::ButtonIcon(int i, int j, const char *tooltip, bool enabled, b
ImVec2 uv0( static_cast<float>(i) * 0.05, static_cast<float>(j) * 0.05 );
ImVec2 uv1( uv0.x + 0.05, uv0.y + 0.05 );
ImGui::PushID( i*20 + j);
char text_buf[512];
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "ButtonIcon %d %d %s", i, j, tooltip);
ImGui::PushID( text_buf );
if (enabled)
ret = ImGui::ImageButton((void*)(intptr_t)textureicons,
ImVec2(g.FontSize, g.FontSize),
@@ -297,7 +299,9 @@ bool ImGuiToolkit::IconButton(int i, int j, const char *tooltip, const char* sho
if (window->SkipItems)
return false;
ImGui::PushID( i * 20 + j + ( tooltip ? window->GetID(tooltip) : 0) );
char text_buf[512];
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "IconButton %d %d %s", i, j, tooltip);
ImGui::PushID( text_buf );
// duplicate of ImGui::InvisibleButton to handle ImGuiButtonFlags_Repeat
const ImGuiID id = window->GetID("##iconijbutton");
@@ -338,7 +342,9 @@ bool ImGuiToolkit::IconButton(int i, int j, const char *tooltip, const char* sho
bool ImGuiToolkit::IconButton(const char* icon, const char *tooltip, const char* shortcut)
{
bool ret = false;
ImGui::PushID( icon );
char text_buf[512];
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "IconButton %s %s", icon, tooltip);
ImGui::PushID( text_buf );
float frame_height = ImGui::GetFrameHeight();
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
@@ -414,7 +420,9 @@ bool ImGuiToolkit::TextButton(const char* text, const char *tooltip, const char*
bool ImGuiToolkit::IconToggle(int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[])
{
bool ret = false;
ImGui::PushID( i * 20 + j + i_toggle * 20 + j_toggle);
char text_buf[512];
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "IconToggle %d %d %d %d ", i, j, i_toggle, j_toggle);
ImGui::PushID( text_buf );
float frame_height = ImGui::GetFrameHeight();
float frame_width = frame_height;
@@ -612,7 +620,9 @@ bool ImGuiToolkit::SelectableIcon(int i, int j, const char* label, bool selected
char text_buf[256];
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), " %s", label);
ImGui::PushID( i * 20 + j + ImGui::GetID(text_buf) );
char id_buf[512];
ImFormatString(id_buf, IM_ARRAYSIZE(id_buf), "SelectableIcon %d %d %s", i, j, label);
ImGui::PushID( id_buf );
// draw menu item
bool ret = ImGui::Selectable(text_buf, selected, ImGuiSelectableFlags_None, size_arg);
@@ -1098,107 +1108,6 @@ void ImGuiToolkit::RenderTimelineBPM (ImVec2 min_bbox, ImVec2 max_bbox, double t
ImGui::PopStyleColor(1);
}
bool ImGuiToolkit::TimelineSlider (const char* label, guint64 *time, guint64 begin, guint64 first,
guint64 end, guint64 step, const float width)
{
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
// get style & id
const ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float fontsize = g.FontSize;
const ImGuiID id = window->GetID(label);
//
// FIRST PREPARE ALL data structures
//
// widget bounding box
const float height = 2.f * (fontsize + style.FramePadding.y);
ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImVec2(width, height);
ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id))
return false;
// cursor size
const float cursor_width = 0.5f * fontsize;
// TIMELINE is inside the bbox, in a slightly smaller bounding box
ImRect timeline_bbox(bbox);
timeline_bbox.Expand( ImVec2() - style.FramePadding );
// SLIDER is inside the timeline
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width + 2.f, cursor_width + 4.f ), timeline_bbox.GetBR() + ImVec2( cursor_width - 2.f, 0.f ) );
// units conversion: from time to float (calculation made with higher precision first)
float time_ = static_cast<float> ( static_cast<double>(*time - begin) / static_cast<double>(end - begin) );
//
// SECOND GET USER INPUT AND PERFORM CHANGES AND DECISIONS
//
// read user input from system
bool left_mouse_press = false;
const bool hovered = ImGui::ItemHoverable(bbox, id);
bool temp_input_is_active = ImGui::TempInputIsActive(id);
if (!temp_input_is_active)
{
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
left_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left);
if (focus_requested || left_mouse_press || g.NavActivateId == id || g.NavInputId == id)
{
ImGui::SetActiveID(id, window);
ImGui::SetFocusID(id, window);
ImGui::FocusWindow(window);
}
}
// time Slider behavior
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 (value_changed){
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(end - begin) );
if (first != GST_CLOCK_TIME_NONE)
*time -= first;
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
}
//
// THIRD RENDER
//
// 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
RenderTimeline(timeline_bbox.Min, timeline_bbox.Max, begin, end, step);
// 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);
}
// draw the cursor
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 left_mouse_press;
}
bool ImGuiToolkit::InvisibleSliderInt (const char* label, uint *index, uint min, uint max, ImVec2 size)
{
// get window
@@ -1680,12 +1589,11 @@ void word_wrap(std::string *str, unsigned per_line)
bool ImGuiToolkit::InputTime(const char *label, guint64 *time, ImGuiInputTextFlags flag)
bool ImGuiToolkit::InputTime(const char *label, guint64 *time, guint64 max_time, bool *valid, ImGuiInputTextFlags flag)
{
bool changed = false;
// filtering for reading MM:SS.MS text entry
static bool valid = false;
static std::regex RegExTime("([0-9]+\\:)?([0-9]+\\:)?([0-5][0-9]|[0-9])((\\.|\\,)[0-9]+)?");
struct TextFilters
{
@@ -1698,7 +1606,8 @@ bool ImGuiToolkit::InputTime(const char *label, guint64 *time, ImGuiInputTextFla
};
// convert gst time to hh mm s.ms
guint64 ms = GST_TIME_AS_MSECONDS(*time);
guint64 tmp_time = *time;
guint64 ms = GST_TIME_AS_MSECONDS(tmp_time);
guint64 hh = ms / 3600000;
guint64 mm = (ms % 3600000) / 60000;
ms -= (hh * 3600000 + mm * 60000);
@@ -1713,7 +1622,7 @@ bool ImGuiToolkit::InputTime(const char *label, guint64 *time, ImGuiInputTextFla
ImGui::PushStyleColor(ImGuiCol_Text,
flag & ImGuiInputTextFlags_ReadOnly
? g.Style.Colors[ImGuiCol_TextDisabled]
: ImVec4(1.0f, valid ? 1.0f : 0.2f, valid ? 1.0f : 0.2f, 1.f));
: ImVec4(1.0f, *valid ? 1.0f : 0.2f, *valid ? 1.0f : 0.2f, 1.f));
ImGui::PushStyleColor(ImGuiCol_FrameBg,
flag & ImGuiInputTextFlags_ReadOnly ? g.Style.Colors[ImGuiCol_WindowBg]
: g.Style.Colors[ImGuiCol_FrameBg]);
@@ -1727,41 +1636,41 @@ bool ImGuiToolkit::InputTime(const char *label, guint64 *time, ImGuiInputTextFla
ImGui::PopStyleColor(2);
// test string format with regular expression
valid = std::regex_match(buf_time_input, RegExTime);
*valid = std::regex_match(buf_time_input, RegExTime);
if (ImGui::IsItemDeactivatedAfterEdit()) {
if (valid) {
ms = 0;
sec = 0.f;
// user confirmed the entry and the input is valid
// split the "HH:MM:SS.ms" string in HH MM SS.ms
std::string timing(buf_time_input);
std::size_t found = timing.find_last_of(':');
// read the right part SS.ms as a value
if (valid) {
ms = 0;
sec = 0.f;
// split the "HH:MM:SS.ms" string in HH MM SS.ms
std::string timing(buf_time_input);
std::size_t found = timing.find_last_of(':');
// read the right part SS.ms as a value
if (std::string::npos != found
&& BaseToolkit::is_a_value(timing.substr(found + 1), &sec)) {
ms = (guint64) (sec * 1000.f);
// read right part MM as a number
timing = timing.substr(0, found);
found = timing.find_last_of(':');
int min = 0;
if (std::string::npos != found
&& BaseToolkit::is_a_value(timing.substr(found + 1), &sec)) {
ms = (guint64) (sec * 1000.f);
// read right part MM as a number
&& BaseToolkit::is_a_number(timing.substr(found + 1), &min)) {
ms += 60000 * (guint64) min;
// read right part HH as a number
timing = timing.substr(0, found);
found = timing.find_last_of(':');
int min = 0;
if (std::string::npos != found
&& BaseToolkit::is_a_number(timing.substr(found + 1), &min)) {
ms += 60000 * (guint64) min;
// read right part HH as a number
timing = timing.substr(0, found);
int hour = 0;
if (std::string::npos != found && BaseToolkit::is_a_number(timing, &hour)) {
ms += 3600000 * (guint64) hour;
}
int hour = 0;
if (std::string::npos != found && BaseToolkit::is_a_number(timing, &hour)) {
ms += 3600000 * (guint64) hour;
}
}
// set time
*time = GST_MSECOND * ms;
changed = true;
}
// force to test validity next frame
valid = false;
// set time
tmp_time = GST_MSECOND * ms;
*valid &= tmp_time < max_time;
}
if (valid && ImGui::IsItemDeactivatedAfterEdit()) {
*time = tmp_time;
changed = true;
}
return changed;
+1 -2
View File
@@ -52,7 +52,6 @@ namespace ImGuiToolkit
// sliders
bool SliderTiming (const char* label, uint *ms, uint v_min, uint v_max, uint v_step, const char* text_max = nullptr);
bool TimelineSlider (const char* label, guint64 *time, guint64 begin, guint64 first, guint64 end, guint64 step, const float width);
void RenderTimeline (ImVec2 min_bbox, ImVec2 max_bbox, guint64 begin, guint64 end, guint64 step, bool verticalflip = false);
void RenderTimelineBPM (ImVec2 min_bbox, ImVec2 max_bbox, double tempo, double quantum, guint64 begin, guint64 end, guint64 step, bool verticalflip = false);
bool InvisibleSliderInt(const char* label, uint *index, uint min, uint max, const ImVec2 size);
@@ -75,7 +74,7 @@ namespace ImGuiToolkit
void Spacing();
// text input
bool InputTime(const char *label, guint64 *time, ImGuiInputTextFlags flag = 0);
bool InputTime(const char *label, guint64 *time, guint64 max_time, bool *valid, ImGuiInputTextFlags flag = 0);
bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flag = ImGuiInputTextFlags_CharsNoBlank);
bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), int *numline = NULL);
void TextMultiline(const char* label, const std::string &str, float width);
+7 -7
View File
@@ -1401,16 +1401,16 @@ void MediaPlayer::update()
}
// test if position is flagged
else if ( isPlaying() ) {
int t = timeline_.flagTypeAt(position_);
if ( t > 0 ) {
// Avoid to pause repeatedly when inside a flagged section
static bool _in_flagged = false;
if (timeline_.isFlagged(position_)) {
// pause at this position
if (!_in_flagged)
if (flag_status_ == LoopStatus::LOOP_STATUS_DEFAULT) {
loop_status_ = flag_status_ = (LoopStatus) t;
play(false);
_in_flagged = true;
}
}
else
_in_flagged = false;
else
flag_status_ = LoopStatus::LOOP_STATUS_DEFAULT;
}
}
+1
View File
@@ -328,6 +328,7 @@ private:
GstClockTime position_;
LoopMode loop_;
LoopStatus loop_status_;
LoopStatus flag_status_;
GstState desired_state_;
GstElement *pipeline_;
GstBus *bus_;
+3 -1
View File
@@ -933,9 +933,11 @@ void SessionLoader::visit(MediaPlayer &n)
{
uint64_t a = GST_CLOCK_TIME_NONE;
uint64_t b = GST_CLOCK_TIME_NONE;
int t = 0;
flag->QueryUnsigned64Attribute("begin", &a);
flag->QueryUnsigned64Attribute("end", &b);
tl.addFlag( TimeInterval( (GstClockTime) a, (GstClockTime) b ) );
flag->QueryIntAttribute("type", &t);
tl.addFlag( TimeInterval( (GstClockTime) a, (GstClockTime) b ), t );
}
}
n.setTimeline(tl);
+1
View File
@@ -477,6 +477,7 @@ void SessionVisitor::visit(MediaPlayer &n)
XMLElement *f = xmlDoc_->NewElement("Interval");
f->SetAttribute("begin", (uint64_t) (*it).begin);
f->SetAttribute("end", (uint64_t) (*it).end);
f->SetAttribute("type", (int) (*it).type);
flagselement->InsertEndChild(f);
}
timelineelement->InsertEndChild(flagselement);
+2
View File
@@ -183,6 +183,7 @@ void Settings::Save(uint64_t runtime, const std::string &filename)
widgetsNode->SetAttribute("media_player", application.widget.media_player);
widgetsNode->SetAttribute("media_player_view", application.widget.media_player_view);
widgetsNode->SetAttribute("timeline_editmode", application.widget.media_player_timeline_editmode);
widgetsNode->SetAttribute("timeline_flag", application.widget.media_player_timeline_flag);
widgetsNode->SetAttribute("media_player_slider", application.widget.media_player_slider);
widgetsNode->SetAttribute("shader_editor", application.widget.shader_editor);
widgetsNode->SetAttribute("shader_editor_view", application.widget.shader_editor_view);
@@ -522,6 +523,7 @@ void Settings::Load(const std::string &filename)
widgetsNode->QueryBoolAttribute("media_player", &application.widget.media_player);
widgetsNode->QueryIntAttribute("media_player_view", &application.widget.media_player_view);
widgetsNode->QueryIntAttribute("timeline_editmode", &application.widget.media_player_timeline_editmode);
widgetsNode->QueryIntAttribute("timeline_flag", &application.widget.media_player_timeline_flag);
widgetsNode->QueryFloatAttribute("media_player_slider", &application.widget.media_player_slider);
widgetsNode->QueryBoolAttribute("shader_editor", &application.widget.shader_editor);
widgetsNode->QueryIntAttribute("shader_editor_view", &application.widget.shader_editor_view);
+2
View File
@@ -24,6 +24,7 @@ struct WidgetsConfig
bool media_player;
int media_player_view;
int media_player_timeline_editmode;
int media_player_timeline_flag;
float media_player_slider;
bool timer;
int timer_view;
@@ -47,6 +48,7 @@ struct WidgetsConfig
media_player = false;
media_player_view = -1;
media_player_timeline_editmode = 0;
media_player_timeline_flag = 1;
media_player_slider = 0.f;
toolbox = false;
help = false;
+266 -36
View File
@@ -579,7 +579,7 @@ bool EditTimeline(const char *label,
const guint64 end = tl->end();
bool cursor_dot = edit_mode == 1;
bool cursor_flag = false;
int cursor_flag = -1;
Timeline _tl;
bool array_changed = false;
float *lines_array = tl->fadingArray();
@@ -635,6 +635,7 @@ bool EditTimeline(const char *label,
char cursor_text[64];
guint64 time = begin + (index * end) / static_cast<guint64>(MAX_TIMELINE_ARRAY);
static guint64 removed_flag_time = 0;
static int removed_flag_type = -1;
// enter edit if widget is active
if (ImGui::GetActiveID() == id) {
@@ -676,17 +677,18 @@ bool EditTimeline(const char *label,
}
else if (edit_mode == 2) {
if (!active) {
// remove flag on mouse press
// remove flag on mouse press
if ( tl->isFlagged(time) ) {
removed_flag_time = time;
removed_flag_type = tl->flagTypeAt(time);
tl->removeFlagAt(time);
}
// add flag on mouse release
else
active = true;
}
else if (! tl->isFlagged(time) ) {
cursor_flag = true;
else if ( !tl->isFlagged(time) ) {
cursor_flag = tl->flagTypeAt(time);;
}
}
@@ -699,9 +701,13 @@ bool EditTimeline(const char *label,
if (edit_mode == 2 && active) {
// exception: if flag was removed at same time, do not add it back
if ( removed_flag_time != time ) {
tl->addFlag(time);
if (removed_flag_type >= 0)
tl->addFlag(time, removed_flag_type);
else
tl->addFlag(time, Settings::application.widget.media_player_timeline_flag);
}
removed_flag_time = 0;
removed_flag_type = -1;
}
active = false;
@@ -763,7 +769,7 @@ bool EditTimeline(const char *label,
break;
case TimelinePayload::FLAG_ADD:
_tl = *tl;
_tl.addFlag(time);
_tl.addFlag(time, pl->argument);
flags_array = _tl.flagsArray();
break;
case TimelinePayload::FLAG_REMOVE:
@@ -787,7 +793,7 @@ bool EditTimeline(const char *label,
}
else {
if ( edit_mode == 2 && tl->isFlagged(time) ) {
cursor_flag = true;
cursor_flag = tl->flagTypeAt(time);;
}
}
@@ -839,10 +845,10 @@ bool EditTimeline(const char *label,
cursor_pos = cursor_pos + mouse_pos_in_canvas;
window->DrawList->AddCircleFilled( cursor_pos, 3.f, cur_color, 8);
}
else if (cursor_flag) {
else if (cursor_flag >= 0) {
cursor_pos = cursor_pos + ImVec2(mouse_pos_in_canvas.x, 12.f);
window->DrawList->AddLine( cursor_pos, cursor_pos + ImVec2(0.f, size.y - 8.f), cur_color);
_drawIcon(cursor_pos - ImVec2(2.f, 1.f), 12, 6, true, window);
_drawIcon(cursor_pos - ImVec2(2.f, 1.f), 11 + cursor_flag, 6, true, window);
}
else {
cursor_pos = cursor_pos + ImVec2(mouse_pos_in_canvas.x, 4.f);
@@ -863,6 +869,139 @@ bool EditTimeline(const char *label,
return array_changed;
}
bool TimelineSlider (const char* label, guint64 *time, Timeline *tl, const float width)
{
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
// get style & id
const ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float fontsize = g.FontSize;
const ImGuiID id = window->GetID(label);
//
// PREPARE data structures
//
// widget bounding box
const float height = 2.f * (fontsize + style.FramePadding.y);
ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImVec2(width, height);
ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id))
return false;
// cursor size
const float cursor_width = 0.5f * fontsize;
// TIMELINE is inside the bbox, in a slightly smaller bounding box
ImRect timeline_bbox(bbox);
timeline_bbox.Expand( ImVec2() - style.FramePadding );
// SLIDER is inside the timeline
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width + 2.f, cursor_width + 4.f ), timeline_bbox.GetBR() + ImVec2( cursor_width - 2.f, 0.f ) );
// 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
//
// read user input from system
bool left_mouse_press = false;
const 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)
{
const bool focus_requested = ImGui::FocusableItemRegister(window, id);
left_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Left);
if (focus_requested || left_mouse_press || g.NavActivateId == id || g.NavInputId == id)
{
ImGui::SetActiveID(id, window);
ImGui::SetFocusID(id, window);
ImGui::FocusWindow(window);
}
}
// time Slider behavior
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 (value_changed){
*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);
}
//
// 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);
}
// draw the cursor
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);
}
std::list< std::pair<float, guint64> > DrawTimeline(const char* label, Timeline *timeline, guint64 time,
double width_ratio, float height)
{
@@ -1893,8 +2032,6 @@ void SourceControlWindow::RenderSingleSource(Source *s)
void DragButtonIcon(int i, int j, const char *tooltip, TimelinePayload payload)
{
ImGuiToolkit::ButtonIcon(i, j, tooltip);
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
// _payload.action = TimelinePayload::FADE_OUT_IN;
@@ -1904,12 +2041,15 @@ void DragButtonIcon(int i, int j, const char *tooltip, TimelinePayload payload)
ImGuiToolkit::Icon(i, j);
ImGui::EndDragDropSource();
}
if (ImGui::IsItemHovered())
ImGui::SetMouseCursor(7); // ImGuiMouseCursor_Hand
}
void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
{
static bool show_overlay_info = false;
static std::vector< std::pair<int,int> > editmode_icon = { {8, 3}, {7, 4}, {12, 6} };
editmode_icon[2] = { Settings::application.widget.media_player_timeline_flag + 11, 6 };
static std::vector< std::string > editmode_tooltip = { "Cutting tool", "Fading tool", "Flag tool" };
mediaplayer_active_ = ms->mediaplayer();
@@ -2022,6 +2162,11 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
///
/// media player timelines
///
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" };
double current_play_speed = mediaplayer_active_->playSpeed();
static uint counter_menu_timeout = 0;
const ImVec2 scrollwindow = ImVec2(ImGui::GetContentRegionAvail().x - slider_zoom_width - 3.0,
@@ -2070,8 +2215,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
}
// custom timeline slider
// TODO : if (mediaplayer_active_->syncToMetronome() > Metronome::SYNC_NONE)
mediaplayer_slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, tl->begin(),
tl->first(), tl->end(), tl->step(), size.x);
mediaplayer_slider_pressed_ = TimelineSlider("##timeline", &seek_t, tl, size.x);
}
}
ImGui::EndChild();
@@ -2125,7 +2269,8 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
ImGui::SameLine(0, h_space_);
ImGui::PushButtonRepeat(true);
if (ImGui::Button( mediaplayer_active_->playSpeed() < 0 ? ICON_FA_BACKWARD :ICON_FA_FORWARD))
if (ImGui::Button( mediaplayer_active_->playSpeed() < 0 ? ICON_FA_BACKWARD :ICON_FA_FORWARD,
ImVec2(ImGui::GetFrameHeightWithSpacing(), 0)) )
mediaplayer_active_->jump ();
ImGui::PopButtonRepeat();
}
@@ -2138,25 +2283,53 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
ImGui::SameLine(0, h_space_);
ImGui::PushButtonRepeat(true);
if (ImGui::Button( mediaplayer_active_->playSpeed() < 0 ? ICON_FA_STEP_BACKWARD : ICON_FA_STEP_FORWARD))
if (ImGui::Button( mediaplayer_active_->playSpeed() < 0 ? ICON_FA_STEP_BACKWARD : ICON_FA_STEP_FORWARD,
ImVec2(ImGui::GetFrameHeightWithSpacing(), 0)))
mediaplayer_active_->step();
ImGui::PopButtonRepeat();
}
// loop modes button
ImGui::SameLine(0, h_space_);
static int current_loop = 0;
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" };
current_loop = (int) mediaplayer_active_->loop();
if ( ImGuiToolkit::IconMultistate(icons_loop, &current_loop, tooltips_loop) )
mediaplayer_active_->setLoop( (MediaPlayer::LoopMode) current_loop );
// flag buttons
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() );
}
// 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());
}
}
}
else {
ImGui::SameLine(0, h_space_);
ImGuiToolkit::ButtonIcon(3, 0, nullptr, false);
}
// right aligned buttons (if enough space)
if ( rendersize.x > min_width_ * 1.5f ) {
// 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) )
mediaplayer_active_->setLoop( (MediaPlayer::LoopMode) current_loop );
// speed slider (if enough space)
if ( rendersize.x > min_width_ * 1.2f ) {
ImGui::SameLine(0, MAX(h_space_ * 2.f, rendersize.x - min_width_ * 1.4f) );
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - buttons_height_ );
// speed slider
ImGui::SameLine(0, h_space_);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - buttons_height_ );
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) );
@@ -2274,7 +2447,8 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
// variables state of panel
static int current_curve = 2;
static uint d = UINT_MAX;
static guint64 target_time = 30000000000;
static guint64 target_time = 1000000000;
static bool target_time_valid = true;
// timeline to edit
Timeline *tl = mediaplayer_active_->timeline();
@@ -2331,19 +2505,18 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
ImGui::Text("|");
/// Enter time value for CUT
target_time = MIN(target_time, tl->duration());
ImGui::SameLine(0, 0);
ImVec2 draw_pos = ImGui::GetCursorPos();
float w = gap_dialog_size.x - 4.f * ImGui::GetTextLineHeightWithSpacing() ;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 11));
ImGui::SetNextItemWidth(w - draw_pos.x - IMGUI_SAME_LINE); // VARIABLE WIDTH
ImGuiToolkit::InputTime("##Time", &target_time);
ImGuiToolkit::InputTime("##TimeCut", &target_time, tl->duration(), &target_time_valid);
ImGui::PopStyleVar();
/// CUT LEFT TIME
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::SetCursorPos( ImVec2(w, draw_pos.y));
if (ImGuiToolkit::ButtonIcon(17, 3, "Cut left at given time")) {
if (ImGuiToolkit::ButtonIcon(17, 3, "Cut left at given time", target_time_valid)) {
tl->cut(target_time, true);
tl->refresh();
oss << ": Timeline cut";
@@ -2351,7 +2524,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
}
/// CUT RIGHT TIME
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::ButtonIcon(18, 3, "Cut right at given time")){
if (ImGuiToolkit::ButtonIcon(18, 3, "Cut right at given time", target_time_valid)){
tl->cut(target_time, false);
tl->refresh();
oss << ": Timeline cut";
@@ -2542,15 +2715,72 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms)
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
///
/// CUT LEFT OF CURSOR
/// DROP ICONS
///
DragButtonIcon(12, 6, "Drop in timeline to\nAdd flag",
DragButtonIcon(11, 6, "Drop in timeline to\nAdd a Bookmark",
TimelinePayload(TimelinePayload::FLAG_ADD, 0, 0) );
ImGui::SameLine(0, IMGUI_SAME_LINE);
DragButtonIcon(12, 6, "Drop in timeline to\nAdd a Stop Flag",
TimelinePayload(TimelinePayload::FLAG_ADD, 0, 1) );
ImGui::SameLine(0, IMGUI_SAME_LINE);
DragButtonIcon(13, 6, "Drop in timeline to\nAdd a Blackout Flag",
TimelinePayload(TimelinePayload::FLAG_ADD, 0, 2) );
DragButtonIcon(6, 0, "Drop in timeline to\nDelete flag",
TimelinePayload(TimelinePayload::FLAG_REMOVE, 0, 1) );
ImGui::SameLine(0, IMGUI_SAME_LINE);
DragButtonIcon(2, 0, "Drop in timeline to\nErase a flag",
TimelinePayload(TimelinePayload::FLAG_REMOVE, 0, 0) );
///
/// SECTION WITH BUTTONS
///
ImGui::SameLine(0, 0);
ImGui::Text("|");
ImGui::SameLine(0, 0);
ImGuiToolkit::IconMultistate(icons_flags,
&Settings::application.widget.media_player_timeline_flag, tooltips_flags);
/// Enter time value for Flag
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImVec2 draw_pos = ImGui::GetCursorPos();
float w = gap_dialog_size.x - 4.f * ImGui::GetTextLineHeightWithSpacing() ;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 11));
ImGui::SetNextItemWidth(w - draw_pos.x - IMGUI_SAME_LINE); // VARIABLE WIDTH
ImGuiToolkit::InputTime("##TimeFlag", &target_time, tl->duration(), &target_time_valid);
ImGui::PopStyleVar();
/// FLAG AT TIME
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
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->refresh();
oss << ": Timeline flag add";
Action::manager().store(oss.str());
}
/// Add
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->refresh();
oss << ": Timeline flag add";
Action::manager().store(oss.str());
}
/// CLEAR
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::ButtonIcon(11, 14, "Clear all flags")) {
tl->clearFlags();
oss << ": Timeline flag clear";
Action::manager().store(oss.str());
}
ImGui::PopStyleColor();
// end icons
ImGui::PopFont();
+41 -7
View File
@@ -957,17 +957,20 @@ float *Timeline::flagsArray()
return flagsArray_;
}
bool Timeline::addFlag(GstClockTime t)
bool Timeline::addFlag(GstClockTime t, int type)
{
if (t > timing_.begin + (step_ * 2)
&& t < timing_.end - (step_ * 2)
&& !isFlagged(t)) {
// compute nearest frame time
GstClockTime t_frame = ( (t - timing_.begin) / step_ ) * step_ + timing_.begin + step_;
GstClockTime t_frame = ( (t - timing_.begin) / step_ ) * step_ + timing_.begin;
if (t - t_frame > (step_ / 2))
t_frame += step_;
// Flag interval centered on t_frame
TimeInterval f(t_frame - step_ - FLAG_MARGIN, t_frame + step_ + FLAG_MARGIN);
f.type = type;
flags_array_need_update_ = true;
return flags_.insert(f).second;
@@ -976,9 +979,10 @@ bool Timeline::addFlag(GstClockTime t)
return false;
}
bool Timeline::addFlag(TimeInterval s)
bool Timeline::addFlag(TimeInterval s, int type)
{
if ( s.is_valid() ) {
s.type = type;
flags_array_need_update_ = true;
return flags_.insert(s).second;
}
@@ -1008,16 +1012,46 @@ bool Timeline::isFlagged(GstClockTime t) const
return ( f != flags_.end() );
}
GstClockTime Timeline::getFlagAt(GstClockTime t) const
int Timeline::flagTypeAt(GstClockTime t) const
{
TimeIntervalSet::const_iterator f = std::find_if(flags_.begin(), flags_.end(), includesTime(t));
if ( f != flags_.end() )
return (*f).type;
else
return -1;
}
void Timeline::setFlagTypeAt(GstClockTime t, int type)
{
TimeIntervalSet::iterator f = std::find_if(flags_.begin(), flags_.end(), includesTime(t));
if ( f != flags_.end() ) {
return ( (*f).begin + step_ + FLAG_MARGIN);
TimeInterval i = (*f);
flags_.erase(f);
i.type = type;
flags_.insert(i);
}
}
TimeInterval Timeline::getNextFlag(GstClockTime t) const
{
if ( !flags_.empty() ) {
// loop over flags
auto f = flags_.begin();
for (; f != flags_.end(); ++f) {
// gap before target?
if ( f->begin > t )
// done
break;
}
if ( f != flags_.end() )
return (*f);
else
return *(flags_.begin());
}
return GST_CLOCK_TIME_NONE;
return TimeInterval();
}
void Timeline::clearFlags()
+14 -3
View File
@@ -14,6 +14,8 @@ struct TimeInterval
{
GstClockTime begin;
GstClockTime end;
int type;
TimeInterval()
{
reset();
@@ -22,23 +24,30 @@ struct TimeInterval
{
begin = b.begin;
end = b.end;
type = b.type;
}
TimeInterval(GstClockTime a, GstClockTime b) : TimeInterval()
{
if ( a != GST_CLOCK_TIME_NONE && b != GST_CLOCK_TIME_NONE) {
begin = MIN(a, b);
end = MAX(a, b);
type = 0;
}
}
inline void reset()
{
begin = GST_CLOCK_TIME_NONE;
end = GST_CLOCK_TIME_NONE;
type = 0;
}
inline GstClockTime duration() const
{
return is_valid() ? (end - begin) : GST_CLOCK_TIME_NONE;
}
inline GstClockTime midpoint() const
{
return is_valid() ? (end - begin) / 2 + begin : GST_CLOCK_TIME_NONE;
}
inline bool is_valid() const
{
return begin != GST_CLOCK_TIME_NONE && end != GST_CLOCK_TIME_NONE && begin < end;
@@ -141,11 +150,13 @@ public:
inline TimeIntervalSet flags() const { return flags_; };
inline size_t numFlags() const { return flags_.size(); };
float *flagsArray();
bool addFlag(GstClockTime t);
bool addFlag(TimeInterval s);
bool addFlag(GstClockTime t, int type = 0);
bool addFlag(TimeInterval s, int type = 0);
bool removeFlagAt(GstClockTime t);
bool isFlagged(GstClockTime t) const;
GstClockTime getFlagAt(GstClockTime t) const;
int flagTypeAt(GstClockTime t) const;
void setFlagTypeAt(GstClockTime t, int type);
TimeInterval getNextFlag(GstClockTime t) const;
void clearFlags();
// inverse of gaps: sections of play areas