Studio: extract TimelineScrubberGui and add it to the toolbar.
The code for the timeline scrubber widget has been moved from `App::ModelOptionsGui` into a new method `App::TimelineScrubberGui`. This new method is now called in both `App::ModelOptionsGui` and `App::ToolBarGui`, replacing the thread count slider in the toolbar. PiperOrigin-RevId: 945565640 Change-Id: I6d5cab0b2397e8b32a2b335e8a709bb9e19bdb48
This commit is contained in:
committed by
Copybara-Service
parent
dc7581acfa
commit
2f650a7beb
+233
-236
@@ -1233,6 +1233,236 @@ static std::string FormatTimelineTime(double time_in_s) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
void App::TimelineScrubberGui() {
|
||||
// Timeline scrubber: spine + sliding knob widget.
|
||||
const double max_time = tmp_.sim_head_time_;
|
||||
const int hist_size = sim_history_.Size();
|
||||
const int hist_min = 1 - hist_size; // most negative index (oldest)
|
||||
const int hist_max = 0; // index 0 = most recent
|
||||
int current_index = sim_history_.GetIndex();
|
||||
const bool locked = (hist_size <= 1);
|
||||
|
||||
// Compute current timestamp for LH label.
|
||||
double curr_time =
|
||||
(has_data() && current_index == sim_history_.GetIndex())
|
||||
? data()->time
|
||||
: (max_time +
|
||||
current_index *
|
||||
(has_model() ? model()->opt.timestep : 0.002));
|
||||
if (curr_time < 0.0) curr_time = 0.0;
|
||||
|
||||
const int curr_step = (has_model() && model()->opt.timestep > 0)
|
||||
? static_cast<int>(std::round(
|
||||
curr_time / model()->opt.timestep))
|
||||
: 0;
|
||||
const int max_step =
|
||||
(has_model() && model()->opt.timestep > 0)
|
||||
? static_cast<int>(std::round(max_time / model()->opt.timestep))
|
||||
: 0;
|
||||
|
||||
// Both LH and RH labels aim to be at 3 digits, switching units
|
||||
// independently.
|
||||
std::string lh_top_str = FormatTimelineTime(curr_time);
|
||||
std::string rh_top_str = FormatTimelineTime(max_time);
|
||||
std::string lh_bot_str = "(" + std::to_string(curr_step) + ")";
|
||||
std::string rh_bot_str = "(" + std::to_string(max_step) + ")";
|
||||
const char* lh_top_label = lh_top_str.c_str();
|
||||
const char* rh_top_label = rh_top_str.c_str();
|
||||
const char* lh_bot_label = lh_bot_str.c_str();
|
||||
const char* rh_bot_label = rh_bot_str.c_str();
|
||||
|
||||
// Use slightly smaller text for timeline labels (e.g. 90% scale).
|
||||
ImGui::SetWindowFontScale(0.9f);
|
||||
|
||||
const ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||
const float spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
const float base_label_w =
|
||||
std::max(ImGui::CalcTextSize("88.8 ms").x,
|
||||
ImGui::CalcTextSize("88.8 \xC2\xB5s").x);
|
||||
const float curr_lh_w =
|
||||
std::max({base_label_w, ImGui::CalcTextSize(lh_top_label).x,
|
||||
ImGui::CalcTextSize(lh_bot_label).x});
|
||||
if (curr_lh_w > tmp_.timeline_lh_width) {
|
||||
tmp_.timeline_lh_width = curr_lh_w;
|
||||
}
|
||||
const float curr_rh_w =
|
||||
std::max({base_label_w, ImGui::CalcTextSize(rh_top_label).x,
|
||||
ImGui::CalcTextSize(rh_bot_label).x});
|
||||
if (curr_rh_w > tmp_.timeline_rh_width) {
|
||||
tmp_.timeline_rh_width = curr_rh_w;
|
||||
}
|
||||
const float lh_box_w = tmp_.timeline_lh_width;
|
||||
const float rh_box_w = tmp_.timeline_rh_width;
|
||||
const float track_w =
|
||||
std::max(10.0f, ImGui::GetContentRegionAvail().x - lh_box_w -
|
||||
rh_box_w - spacing * 2.0f);
|
||||
|
||||
const float spine_h = 3.0f;
|
||||
const float knob_w = 12.0f;
|
||||
const float knob_h = 27.0f; // 1.5x taller than 18.0f
|
||||
const float text_line_h = ImGui::GetTextLineHeight();
|
||||
const float custom_line_spacing =
|
||||
1.0f; // Decreased spacing between the two lines
|
||||
const float text_2lines_h = text_line_h * 2.0f + custom_line_spacing;
|
||||
const float total_h = std::max(knob_h, text_2lines_h);
|
||||
|
||||
// Vertical center of the track row.
|
||||
const float row_center_y = cursor.y + total_h * 0.5f;
|
||||
const float track_x0 = cursor.x + lh_box_w + spacing;
|
||||
const float spine_y0 = row_center_y - spine_h * 0.5f;
|
||||
const float spine_y1 = row_center_y + spine_h * 0.5f;
|
||||
const float spine_x0 = track_x0;
|
||||
const float spine_x1 = track_x0 + track_w;
|
||||
|
||||
// Compute knob position [0,1] along the spine.
|
||||
float t = 1.0f; // Default: pinned to right end.
|
||||
if (!locked) {
|
||||
t = static_cast<float>(current_index - hist_min) /
|
||||
static_cast<float>(hist_max - hist_min);
|
||||
}
|
||||
const float knob_cx = spine_x0 + t * (track_w - knob_w) + knob_w * 0.5f;
|
||||
|
||||
// Invisible interaction button over the full track area.
|
||||
ImGui::SetCursorScreenPos(
|
||||
ImVec2(track_x0, row_center_y - total_h * 0.5f));
|
||||
ImGui::InvisibleButton("##Scrubber", ImVec2(track_w, total_h));
|
||||
const bool hovered = ImGui::IsItemHovered();
|
||||
const bool active = ImGui::IsItemActive();
|
||||
|
||||
// Handle dragging.
|
||||
if (!locked && (active || (hovered && ImGui::IsMouseDown(
|
||||
ImGuiMouseButton_Left)))) {
|
||||
const float mouse_x = ImGui::GetIO().MousePos.x;
|
||||
if (!tmp_.scrubber_active) {
|
||||
tmp_.scrubber_active = true;
|
||||
if (std::abs(mouse_x - knob_cx) <= knob_w) {
|
||||
tmp_.scrubber_grab_offset = mouse_x - knob_cx;
|
||||
} else {
|
||||
tmp_.scrubber_grab_offset = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
const float effective_mouse_x = mouse_x - tmp_.scrubber_grab_offset;
|
||||
const float raw_t = (effective_mouse_x - spine_x0 - knob_w * 0.5f) /
|
||||
std::max(1.0f, track_w - knob_w);
|
||||
const float clamped_t = std::clamp(raw_t, 0.0f, 1.0f);
|
||||
|
||||
int new_index = current_index;
|
||||
const float snap_eps = std::max(
|
||||
0.02f, std::min(0.05f, 8.0f / std::max(1.0f, track_w - knob_w)));
|
||||
if (clamped_t <= snap_eps ||
|
||||
effective_mouse_x <= spine_x0 + knob_w * 0.5f + 6.0f) {
|
||||
new_index = hist_min;
|
||||
} else if (clamped_t >= 1.0f - snap_eps ||
|
||||
effective_mouse_x >= spine_x1 - knob_w * 0.5f - 6.0f) {
|
||||
new_index = hist_max;
|
||||
} else {
|
||||
const float inner_t =
|
||||
(clamped_t - snap_eps) / (1.0f - 2.0f * snap_eps);
|
||||
new_index =
|
||||
hist_min +
|
||||
static_cast<int>(std::round(inner_t * (hist_max - hist_min)));
|
||||
}
|
||||
|
||||
if (new_index != current_index) {
|
||||
LoadHistory(new_index);
|
||||
current_index = new_index;
|
||||
t = static_cast<float>(current_index - hist_min) /
|
||||
static_cast<float>(hist_max - hist_min);
|
||||
if (has_data() && current_index == sim_history_.GetIndex()) {
|
||||
curr_time = data()->time;
|
||||
if (curr_time < 0.0) curr_time = 0.0;
|
||||
lh_top_str = FormatTimelineTime(curr_time);
|
||||
lh_top_label = lh_top_str.c_str();
|
||||
const int updated_curr_step =
|
||||
(has_model() && model()->opt.timestep > 0)
|
||||
? static_cast<int>(
|
||||
std::round(curr_time / model()->opt.timestep))
|
||||
: 0;
|
||||
lh_bot_str = "(" + std::to_string(updated_curr_step) + ")";
|
||||
lh_bot_label = lh_bot_str.c_str();
|
||||
const float updated_lh_w =
|
||||
std::max({base_label_w, ImGui::CalcTextSize(lh_top_label).x,
|
||||
ImGui::CalcTextSize(lh_bot_label).x});
|
||||
if (updated_lh_w > tmp_.timeline_lh_width) {
|
||||
tmp_.timeline_lh_width = updated_lh_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tmp_.scrubber_active = false;
|
||||
}
|
||||
|
||||
// Draw LH labels (two lines, both right-aligned).
|
||||
const float text_y0 = row_center_y - text_2lines_h * 0.5f;
|
||||
const float text_y1 = text_y0 + text_line_h + custom_line_spacing;
|
||||
|
||||
const float lh_top_x =
|
||||
cursor.x + lh_box_w - ImGui::CalcTextSize(lh_top_label).x;
|
||||
ImGui::SetCursorScreenPos(ImVec2(lh_top_x, text_y0));
|
||||
ImGui::TextUnformatted(lh_top_label);
|
||||
|
||||
const float lh_bot_x =
|
||||
cursor.x + lh_box_w - ImGui::CalcTextSize(lh_bot_label).x;
|
||||
ImGui::SetCursorScreenPos(ImVec2(lh_bot_x, text_y1));
|
||||
ImGui::TextUnformatted(lh_bot_label);
|
||||
|
||||
// Draw RH labels (two lines, both left-aligned).
|
||||
const float rh_x0 = track_x0 + track_w + spacing;
|
||||
ImGui::SetCursorScreenPos(ImVec2(rh_x0, text_y0));
|
||||
ImGui::TextUnformatted(rh_top_label);
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(rh_x0, text_y1));
|
||||
ImGui::TextUnformatted(rh_bot_label);
|
||||
|
||||
// Restore window font scale.
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
// Draw spine and knob on the draw list.
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
// Spine.
|
||||
const ImVec4 scrollbar_bg =
|
||||
ImGui::GetStyle().Colors[ImGuiCol_ScrollbarBg];
|
||||
ImGuiCol bg_col_idx;
|
||||
if (active) {
|
||||
bg_col_idx = ImGuiCol_FrameBgActive;
|
||||
} else if (hovered) {
|
||||
bg_col_idx = ImGuiCol_FrameBgHovered;
|
||||
} else {
|
||||
bg_col_idx = (scrollbar_bg.w > 0.01f) ? ImGuiCol_ScrollbarBg
|
||||
: ImGuiCol_FrameBg;
|
||||
}
|
||||
const ImU32 spine_col = ImGui::GetColorU32(bg_col_idx);
|
||||
dl->AddRectFilled(ImVec2(spine_x0, spine_y0),
|
||||
ImVec2(spine_x1, spine_y1), spine_col,
|
||||
spine_h * 0.5f);
|
||||
|
||||
// Knob rectangle.
|
||||
const float knob_x0 = knob_cx - knob_w * 0.5f;
|
||||
const float knob_x1 = knob_cx + knob_w * 0.5f;
|
||||
const float knob_y0 = row_center_y - knob_h * 0.5f;
|
||||
const float knob_y1 = row_center_y + knob_h * 0.5f;
|
||||
|
||||
ImU32 knob_col;
|
||||
if (active) {
|
||||
knob_col = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
|
||||
} else if (hovered) {
|
||||
knob_col = ImGui::GetColorU32(ImGuiCol_SliderGrab);
|
||||
} else {
|
||||
knob_col = ImGui::GetColorU32(ImGuiCol_SliderGrab);
|
||||
}
|
||||
dl->AddRectFilled(ImVec2(knob_x0, knob_y0), ImVec2(knob_x1, knob_y1),
|
||||
knob_col, style.GrabRounding);
|
||||
// Knob border.
|
||||
dl->AddRect(ImVec2(knob_x0, knob_y0), ImVec2(knob_x1, knob_y1),
|
||||
ImGui::GetColorU32(ImGuiCol_Border), style.GrabRounding);
|
||||
|
||||
// Advance layout cursor past this row.
|
||||
ImGui::SetCursorScreenPos(ImVec2(cursor.x, cursor.y + total_h));
|
||||
}
|
||||
|
||||
void App::ModelOptionsGui() {
|
||||
const float min_width = platform::GetExpectedLabelWidth();
|
||||
const ImGuiChildFlags child_flags =
|
||||
@@ -1378,235 +1608,8 @@ void App::ModelOptionsGui() {
|
||||
}
|
||||
ImGui::SetItemTooltip("%s", "Load next frame from history / Single step");
|
||||
|
||||
// Timeline scrubber: spine + sliding knob widget.
|
||||
{
|
||||
const double max_time = tmp_.sim_head_time_;
|
||||
const int hist_size = sim_history_.Size();
|
||||
const int hist_min = 1 - hist_size; // most negative index (oldest)
|
||||
const int hist_max = 0; // index 0 = most recent
|
||||
int current_index = sim_history_.GetIndex();
|
||||
const bool locked = (hist_size <= 1);
|
||||
|
||||
// Compute current timestamp for LH label.
|
||||
double curr_time =
|
||||
(has_data() && current_index == sim_history_.GetIndex())
|
||||
? data()->time
|
||||
: (max_time +
|
||||
current_index *
|
||||
(has_model() ? model()->opt.timestep : 0.002));
|
||||
if (curr_time < 0.0) curr_time = 0.0;
|
||||
|
||||
const int curr_step = (has_model() && model()->opt.timestep > 0)
|
||||
? static_cast<int>(std::round(
|
||||
curr_time / model()->opt.timestep))
|
||||
: 0;
|
||||
const int max_step =
|
||||
(has_model() && model()->opt.timestep > 0)
|
||||
? static_cast<int>(std::round(max_time / model()->opt.timestep))
|
||||
: 0;
|
||||
|
||||
// Both LH and RH labels aim to be at 3 digits, switching units
|
||||
// independently.
|
||||
std::string lh_top_str = FormatTimelineTime(curr_time);
|
||||
std::string rh_top_str = FormatTimelineTime(max_time);
|
||||
std::string lh_bot_str = "(" + std::to_string(curr_step) + ")";
|
||||
std::string rh_bot_str = "(" + std::to_string(max_step) + ")";
|
||||
const char* lh_top_label = lh_top_str.c_str();
|
||||
const char* rh_top_label = rh_top_str.c_str();
|
||||
const char* lh_bot_label = lh_bot_str.c_str();
|
||||
const char* rh_bot_label = rh_bot_str.c_str();
|
||||
|
||||
// Use slightly smaller text for timeline labels (e.g. 90% scale).
|
||||
ImGui::SetWindowFontScale(0.9f);
|
||||
|
||||
const ImVec2 cursor = ImGui::GetCursorScreenPos();
|
||||
const float spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
const float base_label_w =
|
||||
std::max(ImGui::CalcTextSize("88.8 ms").x,
|
||||
ImGui::CalcTextSize("88.8 \xC2\xB5s").x);
|
||||
const float curr_lh_w =
|
||||
std::max({base_label_w, ImGui::CalcTextSize(lh_top_label).x,
|
||||
ImGui::CalcTextSize(lh_bot_label).x});
|
||||
if (curr_lh_w > tmp_.timeline_lh_width) {
|
||||
tmp_.timeline_lh_width = curr_lh_w;
|
||||
}
|
||||
const float curr_rh_w =
|
||||
std::max({base_label_w, ImGui::CalcTextSize(rh_top_label).x,
|
||||
ImGui::CalcTextSize(rh_bot_label).x});
|
||||
if (curr_rh_w > tmp_.timeline_rh_width) {
|
||||
tmp_.timeline_rh_width = curr_rh_w;
|
||||
}
|
||||
const float lh_box_w = tmp_.timeline_lh_width;
|
||||
const float rh_box_w = tmp_.timeline_rh_width;
|
||||
const float track_w =
|
||||
std::max(10.0f, ImGui::GetContentRegionAvail().x - lh_box_w -
|
||||
rh_box_w - spacing * 2.0f);
|
||||
|
||||
const float spine_h = 3.0f;
|
||||
const float knob_w = 12.0f;
|
||||
const float knob_h = 27.0f; // 1.5x taller than 18.0f
|
||||
const float text_line_h = ImGui::GetTextLineHeight();
|
||||
const float custom_line_spacing =
|
||||
1.0f; // Decreased spacing between the two lines
|
||||
const float text_2lines_h = text_line_h * 2.0f + custom_line_spacing;
|
||||
const float total_h = std::max(knob_h, text_2lines_h);
|
||||
|
||||
// Vertical center of the track row.
|
||||
const float row_center_y = cursor.y + total_h * 0.5f;
|
||||
const float track_x0 = cursor.x + lh_box_w + spacing;
|
||||
const float spine_y0 = row_center_y - spine_h * 0.5f;
|
||||
const float spine_y1 = row_center_y + spine_h * 0.5f;
|
||||
const float spine_x0 = track_x0;
|
||||
const float spine_x1 = track_x0 + track_w;
|
||||
|
||||
// Compute knob position [0,1] along the spine.
|
||||
float t = 1.0f; // Default: pinned to right end.
|
||||
if (!locked) {
|
||||
t = static_cast<float>(current_index - hist_min) /
|
||||
static_cast<float>(hist_max - hist_min);
|
||||
}
|
||||
const float knob_cx = spine_x0 + t * (track_w - knob_w) + knob_w * 0.5f;
|
||||
|
||||
// Invisible interaction button over the full track area.
|
||||
ImGui::SetCursorScreenPos(
|
||||
ImVec2(track_x0, row_center_y - total_h * 0.5f));
|
||||
ImGui::InvisibleButton("##TimelineScrubber", ImVec2(track_w, total_h));
|
||||
const bool hovered = ImGui::IsItemHovered();
|
||||
const bool active = ImGui::IsItemActive();
|
||||
|
||||
// Handle dragging.
|
||||
if (!locked && (active || (hovered && ImGui::IsMouseDown(
|
||||
ImGuiMouseButton_Left)))) {
|
||||
const float mouse_x = ImGui::GetIO().MousePos.x;
|
||||
if (!tmp_.scrubber_active) {
|
||||
tmp_.scrubber_active = true;
|
||||
if (std::abs(mouse_x - knob_cx) <= knob_w) {
|
||||
tmp_.scrubber_grab_offset = mouse_x - knob_cx;
|
||||
} else {
|
||||
tmp_.scrubber_grab_offset = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
const float effective_mouse_x = mouse_x - tmp_.scrubber_grab_offset;
|
||||
const float raw_t = (effective_mouse_x - spine_x0 - knob_w * 0.5f) /
|
||||
std::max(1.0f, track_w - knob_w);
|
||||
const float clamped_t = std::clamp(raw_t, 0.0f, 1.0f);
|
||||
|
||||
int new_index = current_index;
|
||||
const float snap_eps = std::max(
|
||||
0.02f, std::min(0.05f, 8.0f / std::max(1.0f, track_w - knob_w)));
|
||||
if (clamped_t <= snap_eps ||
|
||||
effective_mouse_x <= spine_x0 + knob_w * 0.5f + 6.0f) {
|
||||
new_index = hist_min;
|
||||
} else if (clamped_t >= 1.0f - snap_eps ||
|
||||
effective_mouse_x >= spine_x1 - knob_w * 0.5f - 6.0f) {
|
||||
new_index = hist_max;
|
||||
} else {
|
||||
const float inner_t =
|
||||
(clamped_t - snap_eps) / (1.0f - 2.0f * snap_eps);
|
||||
new_index =
|
||||
hist_min +
|
||||
static_cast<int>(std::round(inner_t * (hist_max - hist_min)));
|
||||
}
|
||||
|
||||
if (new_index != current_index) {
|
||||
LoadHistory(new_index);
|
||||
current_index = new_index;
|
||||
t = static_cast<float>(current_index - hist_min) /
|
||||
static_cast<float>(hist_max - hist_min);
|
||||
if (has_data() && current_index == sim_history_.GetIndex()) {
|
||||
curr_time = data()->time;
|
||||
if (curr_time < 0.0) curr_time = 0.0;
|
||||
lh_top_str = FormatTimelineTime(curr_time);
|
||||
lh_top_label = lh_top_str.c_str();
|
||||
const int updated_curr_step =
|
||||
(has_model() && model()->opt.timestep > 0)
|
||||
? static_cast<int>(
|
||||
std::round(curr_time / model()->opt.timestep))
|
||||
: 0;
|
||||
lh_bot_str = "(" + std::to_string(updated_curr_step) + ")";
|
||||
lh_bot_label = lh_bot_str.c_str();
|
||||
const float updated_lh_w =
|
||||
std::max({base_label_w, ImGui::CalcTextSize(lh_top_label).x,
|
||||
ImGui::CalcTextSize(lh_bot_label).x});
|
||||
if (updated_lh_w > tmp_.timeline_lh_width) {
|
||||
tmp_.timeline_lh_width = updated_lh_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tmp_.scrubber_active = false;
|
||||
}
|
||||
|
||||
// Draw LH labels (two lines, both right-aligned).
|
||||
const float text_y0 = row_center_y - text_2lines_h * 0.5f;
|
||||
const float text_y1 = text_y0 + text_line_h + custom_line_spacing;
|
||||
|
||||
const float lh_top_x =
|
||||
cursor.x + lh_box_w - ImGui::CalcTextSize(lh_top_label).x;
|
||||
ImGui::SetCursorScreenPos(ImVec2(lh_top_x, text_y0));
|
||||
ImGui::TextUnformatted(lh_top_label);
|
||||
|
||||
const float lh_bot_x =
|
||||
cursor.x + lh_box_w - ImGui::CalcTextSize(lh_bot_label).x;
|
||||
ImGui::SetCursorScreenPos(ImVec2(lh_bot_x, text_y1));
|
||||
ImGui::TextUnformatted(lh_bot_label);
|
||||
|
||||
// Draw RH labels (two lines, both left-aligned).
|
||||
const float rh_x0 = track_x0 + track_w + spacing;
|
||||
ImGui::SetCursorScreenPos(ImVec2(rh_x0, text_y0));
|
||||
ImGui::TextUnformatted(rh_top_label);
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(rh_x0, text_y1));
|
||||
ImGui::TextUnformatted(rh_bot_label);
|
||||
|
||||
// Restore window font scale.
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
// Draw spine and knob on the draw list.
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
// Spine.
|
||||
const ImVec4 scrollbar_bg =
|
||||
ImGui::GetStyle().Colors[ImGuiCol_ScrollbarBg];
|
||||
ImGuiCol bg_col_idx;
|
||||
if (active) {
|
||||
bg_col_idx = ImGuiCol_FrameBgActive;
|
||||
} else if (hovered) {
|
||||
bg_col_idx = ImGuiCol_FrameBgHovered;
|
||||
} else {
|
||||
bg_col_idx = (scrollbar_bg.w > 0.01f) ? ImGuiCol_ScrollbarBg
|
||||
: ImGuiCol_FrameBg;
|
||||
}
|
||||
const ImU32 spine_col = ImGui::GetColorU32(bg_col_idx);
|
||||
dl->AddRectFilled(ImVec2(spine_x0, spine_y0),
|
||||
ImVec2(spine_x1, spine_y1), spine_col,
|
||||
spine_h * 0.5f);
|
||||
|
||||
// Knob rectangle.
|
||||
const float knob_x0 = knob_cx - knob_w * 0.5f;
|
||||
const float knob_x1 = knob_cx + knob_w * 0.5f;
|
||||
const float knob_y0 = row_center_y - knob_h * 0.5f;
|
||||
const float knob_y1 = row_center_y + knob_h * 0.5f;
|
||||
|
||||
ImU32 knob_col;
|
||||
if (active) {
|
||||
knob_col = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
|
||||
} else if (hovered) {
|
||||
knob_col = ImGui::GetColorU32(ImGuiCol_SliderGrab);
|
||||
} else {
|
||||
knob_col = ImGui::GetColorU32(ImGuiCol_SliderGrab);
|
||||
}
|
||||
dl->AddRectFilled(ImVec2(knob_x0, knob_y0), ImVec2(knob_x1, knob_y1),
|
||||
knob_col, style.GrabRounding);
|
||||
// Knob border.
|
||||
dl->AddRect(ImVec2(knob_x0, knob_y0), ImVec2(knob_x1, knob_y1),
|
||||
ImGui::GetColorU32(ImGuiCol_Border), style.GrabRounding);
|
||||
|
||||
// Advance layout cursor past this row.
|
||||
ImGui::SetCursorScreenPos(ImVec2(cursor.x, cursor.y + total_h));
|
||||
}
|
||||
// Timeline scrubber.
|
||||
TimelineScrubberGui();
|
||||
}
|
||||
|
||||
// Keyframe controls.
|
||||
@@ -2202,13 +2205,7 @@ void App::ToolBarGui() {
|
||||
platform::StepControlGui(&step_control_, tmp_.speed_index);
|
||||
|
||||
ImGui::SameLine(0, separator_width);
|
||||
ImGui::SetNextItemWidth(120);
|
||||
ImGui::BeginDisabled(std::thread::hardware_concurrency() <= 1);
|
||||
if (ImGui::SliderInt("##NumThreads", &ui_.nthread, 0, 8, "%d threads")) {
|
||||
tmp_.update_threadpool = true;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SetItemTooltip("%s", "Number of threads in threadpool");
|
||||
TimelineScrubberGui();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ class App {
|
||||
|
||||
void MainMenuGui();
|
||||
void ToolBarGui();
|
||||
void TimelineScrubberGui();
|
||||
void StatusBarGui();
|
||||
void HelpGui();
|
||||
void FileDialogGui();
|
||||
|
||||
Reference in New Issue
Block a user