Complete refactoring

This commit is contained in:
brunoherbelin
2020-03-22 12:22:36 +01:00
parent a435daa494
commit 0bba7a0a84
24 changed files with 929 additions and 600 deletions

View File

@@ -2,17 +2,22 @@
#include <map>
#include <ctime>
#ifdef _WIN32
# include <windows.h>
# include <shellapi.h>
#endif
#include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui_internal.h"
#define MILISECOND 1000000##L
#define SECOND 1000000000##L
#define MINUTE 60000000000##L
#define MILISECOND 1000000L
#define SECOND 1000000000L
#define MINUTE 60000000000L
#include "ResourceManager.h"
#include "Resource.h"
#include "ImGuiToolkit.h"
#include "GstToolkit.h"
@@ -30,6 +35,22 @@ std::string ImGuiToolkit::DateTime(){
return datetime;
}
void ImGuiToolkit::OpenWebpage( const char* url )
{
#ifdef _WIN32
ShellExecuteA( nullptr, nullptr, url, nullptr, nullptr, 0 );
#elif defined __APPLE__
char buf[1024];
sprintf( buf, "open %s", url );
system( buf );
#else
char buf[1024];
sprintf( buf, "xdg-open %s", url );
system( buf );
#endif
}
void ImGuiToolkit::Icon(int i, int j)
{
// icons.dds is a 20 x 20 grid of icons
@@ -68,8 +89,6 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
ImGui::Begin("Icons", p_open);
// ImGui::Text(" 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19");
ImVec2 pos = ImGui::GetCursorScreenPos();
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
if (ImGui::IsItemHovered())
@@ -95,104 +114,135 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
ImGui::End();
}
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 begin, guint64 end, guint64 duration, guint64 step)
// Draws a timeline showing
// 1) a cursor at position *time in the range [0 duration]
// 2) a line of tick marks indicating time, every step if possible
// 3) a slider handle below the cursor: user can move the slider
// Behavior
// a) Returns TRUE if the left mouse button LMB is pressed over the timeline
// b) the value of *time is changed to the position of the slider handle from user input (LMB)
bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 duration, guint64 step)
{
static guint64 optimal_tick_marks[] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE };
bool value_return = false;
static guint64 optimal_tick_marks[12] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE };
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems) {
return value_return;
}
if (window->SkipItems)
return false;
// get style
// get style & id
const ImGuiStyle& style = ImGui::GetStyle();
const float fontsize = ImGui::GetFontSize();
ImGuiContext& g = *GImGui;
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 = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), (fontsize + style.FramePadding.y)* 2.0f);
ImVec2 size = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), height);
ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id))
return value_return;
return false;
// cursor size
const float cursor_scale = 1.f;
const float cursor_width = window->DrawList->_Data->FontSize * cursor_scale;
const float cursor_width = 0.5f * fontsize * cursor_scale;
// user input
// TIMELINE is inside the bbox, in a slightly smaller bounding box
ImRect timeline_bbox(bbox);
timeline_bbox.Expand( ImVec2(-cursor_width, -style.FramePadding.y) );
// 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) / static_cast<double>(duration) );
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(duration) );
//
// 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);
const bool clicked = (hovered && g.IO.MouseClicked[0]);
if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == 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);
}
value_return = hovered && (clicked || g.IO.MouseDownDuration[0] > 0.f);
}
// Render bounding box
// 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){
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(duration) );
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);
// draw inside a slightly smaller bounding box
ImRect timeline_bbox(bbox);
timeline_bbox.Expand(ImVec2(-cursor_width, 0.f));
// units conversion: from time to float (calculation made with higher precision first)
float time_ = static_cast<float> ( static_cast<double>(*time) / static_cast<double>(duration) );
float begin_ = static_cast<float> ( static_cast<double>(begin) / static_cast<double>(duration) );
float end_ = static_cast<float> ( static_cast<double>(end) / static_cast<double>(duration) );
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(duration) );
// by default, put a tick mark at every frame step and a large mark every second
guint64 tick_step = step;
guint64 large_tick_step = SECOND;
// how many pixels to represent one frame step?
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
// while there is less than 3 pixels between two tick marks (or at last optimal tick mark)
for ( int i=0; i<10 && tick_step_pixels < 3.f; ++i )
{
// try to use the optimal tick marks pre-defined
tick_step = optimal_tick_marks[i];
large_tick_step = optimal_tick_marks[i+1];
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
}
// static guint64 t = 0;
// if (t != *time) {
// t = *time;
// ImGuiToolkit::Log("tic %ld %ld %ld %ld \n", duration, step, tick_step, large_tick_step);
// }
// tick marks along timeline
ImU32 color = ImGui::GetColorU32( style.Colors[ImGuiCol_PlotLines] );
// render the tick marks along TIMELINE
ImU32 color = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] );
pos = timeline_bbox.GetTL();
guint64 tick = 0;
float tick_percent = 0.f;
while ( tick < duration)
{
// large tick mark every large tick
float tick_length = !(tick%large_tick_step) ? fontsize : style.FramePadding.y;
// draw a tick mark each step
window->DrawList->AddLine( pos, pos + ImVec2(0.f, tick_length), color);
// next tick
tick += tick_step;
float tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
}
// end
// tick EOF
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color);
// render text : duration and current time
@@ -210,32 +260,222 @@ bool ImGuiToolkit::TimelineSlider(const char* label, guint64 *time, guint64 begi
if (overlay_size.x > 0.0f)
ImGui::RenderTextClipped( bbox.GetBL() + overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
// Slider behavior
ImRect grab_bb;
ImRect slider_bbox( timeline_bbox.GetTL() + ImVec2(-cursor_width * 0.5f, cursor_width ), timeline_bbox.GetBR() + ImVec2( cursor_width * 0.5f, 0.f ) );
float time_slider = time_;
float time_zero = 0.f;
float time_end = 1.f;
bool value_changed = ImGui::SliderBehavior(slider_bbox, id, ImGuiDataType_Float, &time_slider, &time_zero,
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_bb);
if (value_changed){
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
*time = static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) );
// 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);
}
// Render grab
if (grab_bb.Max.x > grab_bb.Min.x)
window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max,
ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab),
style.GrabRounding);
// last: draw the cursor
// draw the cursor
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width * 0.5f, 0.f);
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
return value_return;
return left_mouse_press;
}
// Draws a timeline showing
// 1) a cursor at position *time in the range [0 duration]
// 2) a line of tick marks indicating time, every step if possible
// 3) a slider handle below the cursor: user can move the slider
// 4) different blocs for each time segment [first second] of the list of segments
// Behavior
// a) Returns TRUE if the left mouse button LMB is pressed over the timeline
// b) the value of *time is changed to the position of the slider handle from user input (LMB)
// c) the list segments is replaced with the list of new segments created by user (empty list otherwise)
bool ImGuiToolkit::TimelineSliderEdit(const char* label, guint64 *time, guint64 duration, guint64 step,
std::list<std::pair<guint64, guint64> >& segments)
{
static guint64 optimal_tick_marks[12] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE };
// get window
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
// get style & id
const ImGuiStyle& style = ImGui::GetStyle();
const float fontsize = ImGui::GetFontSize();
ImGuiContext& g = *GImGui;
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 = ImGui::CalcItemSize(ImVec2(-FLT_MIN, 0.0f), ImGui::CalcItemWidth(), height);
ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id))
return false;
// cursor size
const float cursor_scale = 1.f;
const float cursor_width = 0.5f * fontsize * cursor_scale;
// TIMELINE is inside the bbox, in a slightly smaller bounding box
ImRect timeline_bbox(bbox);
timeline_bbox.Expand( ImVec2(-cursor_width, -style.FramePadding.y) );
// 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) / static_cast<double>(duration) );
float step_ = static_cast<float> ( static_cast<double>(step) / static_cast<double>(duration) );
//
// SECOND GET USER INPUT AND PERFORM CHANGES AND DECISIONS
//
// read user input from system
bool left_mouse_press = false;
bool right_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);
right_mouse_press = hovered && ImGui::IsMouseDown(ImGuiMouseButton_Right);
if (focus_requested || left_mouse_press || right_mouse_press || g.NavActivateId == id || g.NavInputId == id)
{
ImGui::SetActiveID(id, window);
ImGui::SetFocusID(id, window);
ImGui::FocusWindow(window);
}
}
// active slider if inside bb
ImRect grab_slider_bb;
ImU32 grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrab);
ImGuiID slider_id = window->GetID("werwerwsdvcsdgfdghdfsgagewrgsvdfhfdghkjfghsdgsdtgewfszdvgfkjfg"); // FIXME: cleaner way to create a valid but useless id that is never active
if ( slider_bbox.Contains(g.IO.MousePos) ) {
slider_id = id; // active id
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
}
// time Slider behavior
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, slider_id, ImGuiDataType_Float, &time_slider, &time_zero,
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
if (value_changed){
// g_print("slider %f %ld \n", time_slider, static_cast<guint64> ( static_cast<double>(time_slider) * static_cast<double>(duration) ));
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(duration) );
}
// segments behavior
float time_segment_begin = 0.f;
float time_segment_end = 0.f;
if (right_mouse_press) {
time_segment_begin = 0.f;
}
//
// 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);
// by default, put a tick mark at every frame step and a large mark every second
guint64 tick_step = step;
guint64 large_tick_step = SECOND;
// how many pixels to represent one frame step?
float tick_step_pixels = timeline_bbox.GetWidth() * step_;
// while there is less than 3 pixels between two tick marks (or at last optimal tick mark)
for ( int i=0; i<10 && tick_step_pixels < 3.f; ++i )
{
// try to use the optimal tick marks pre-defined
tick_step = optimal_tick_marks[i];
large_tick_step = optimal_tick_marks[i+1];
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
}
// render the tick marks along TIMELINE
ImU32 color_in = ImGui::GetColorU32( style.Colors[ImGuiCol_Text] );
ImU32 color_out = ImGui::GetColorU32( style.Colors[ImGuiCol_TextDisabled] );
ImU32 color = color_in;
pos = timeline_bbox.GetTL();
guint64 tick = 0;
float tick_percent = 0.f;
std::list< std::pair<guint64, guint64> >::iterator it = segments.begin();
while ( tick < duration)
{
// large tick mark every large tick
float tick_length = !(tick%large_tick_step) ? fontsize : style.FramePadding.y;
// different colors for inside and outside [begin end] of segments
if ( it != segments.end() ) {
if ( tick < it->first )
color = color_out;
else if ( tick > it->second ){
color = color_out;
it ++;
}
else
color = color_in;
}
// draw a tick mark each step
window->DrawList->AddLine( pos, pos + ImVec2(0.f, tick_length), color);
// next tick
tick += tick_step;
tick_percent = static_cast<float> ( static_cast<double>(tick) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
}
// loop over segments and EMPTY the list
// ticks for segments begin & end
for (std::list< std::pair<guint64, guint64> >::iterator s = segments.begin(); s != segments.end(); s++){
tick_percent = static_cast<float> ( static_cast<double>(s->first) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
window->DrawList->AddLine( pos, pos + ImVec2(0.f, timeline_bbox.GetHeight()), color_in);
tick_percent = static_cast<float> ( static_cast<double>(s->second) / static_cast<double>(duration) );
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
window->DrawList->AddLine( pos, pos + ImVec2(0.f, timeline_bbox.GetHeight()), color_in);
}
segments.clear();
// tick EOF
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetTR() + ImVec2(0.f, fontsize), color_in);
// render text : duration and current time
char overlay_buf[24];
ImVec2 overlay_size = ImVec2(0.f, 0.f);
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s", GstToolkit::to_string(duration).c_str());
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
overlay_size += ImVec2(3.f, 3.f);
if (overlay_size.x > 0.0f)
ImGui::RenderTextClipped( bbox.GetBR() - overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%s", GstToolkit::to_string(*time).c_str());
overlay_size = ImGui::CalcTextSize(overlay_buf, NULL);
overlay_size = ImVec2(3.f, -3.f - overlay_size.y);
if (overlay_size.x > 0.0f)
ImGui::RenderTextClipped( bbox.GetBL() + overlay_size, bbox.Max, overlay_buf, NULL, &overlay_size);
// 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
color = ImGui::GetColorU32(style.Colors[ImGuiCol_SliderGrab]);
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
ImGui::RenderArrow(window->DrawList, pos, color, ImGuiDir_Up, cursor_scale);
return left_mouse_press;
}