Files
IQ.Pilot/iqpilot/tools/cabana/ui/widgets/scrollabletabbar.cc
2026-09-02 16:46:45 -05:00

88 lines
3.7 KiB
C++

#include "tools/cabana/ui/widgets/scrollabletabbar.h"
#include <algorithm>
#include <cmath>
#include <vector>
#include "imgui_internal.h"
namespace {
float scrollButtonsWidth() {
const ImGuiStyle &style = ImGui::GetStyle();
return ImGui::GetFrameHeight() * 2.0f + style.ItemInnerSpacing.x + style.ItemSpacing.x * 2.0f;
}
void drawScrollButtons(ImGuiTabBar *tab_bar) {
const ImGuiStyle &style = ImGui::GetStyle();
const float size = ImGui::GetFrameHeight();
const float max_scroll = std::max(0.0f, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth());
const float start_x = tab_bar->BarRect.Max.x + style.ItemSpacing.x;
const ImVec2 backup_pos = ImGui::GetCursorScreenPos();
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
for (int i = 0; i < 2; ++i) {
const bool left = i == 0;
ImGui::SetCursorScreenPos(ImVec2(start_x + i * (size + style.ItemInnerSpacing.x), tab_bar->BarRect.Min.y));
ImGui::BeginDisabled(left ? tab_bar->ScrollingTarget <= 0.0f : tab_bar->ScrollingTarget >= max_scroll);
if (ImGui::Button(left ? "###scroll_left" : "###scroll_right", ImVec2(size, size))) {
const float step = (left ? -4.0f : 4.0f) * ImGui::GetFontSize();
tab_bar->ScrollingTarget = std::clamp(tab_bar->ScrollingTarget + step, 0.0f, max_scroll);
tab_bar->ScrollingAnim = tab_bar->ScrollingTarget;
}
const ImVec2 c((ImGui::GetItemRectMin().x + ImGui::GetItemRectMax().x) * 0.5f,
(ImGui::GetItemRectMin().y + ImGui::GetItemRectMax().y) * 0.5f);
const float h = std::round(ImGui::GetFontSize() * 0.25f);
const float dx = left ? h * 0.5f : -h * 0.5f;
ImDrawList *painter = ImGui::GetWindowDrawList();
painter->PathLineTo(ImVec2(c.x + dx, c.y - h));
painter->PathLineTo(ImVec2(c.x - dx, c.y));
painter->PathLineTo(ImVec2(c.x + dx, c.y + h));
painter->PathStroke(ImGui::GetColorU32(ImGuiCol_Text), ImDrawFlags_None, 1.5f);
ImGui::EndDisabled();
}
ImGui::PopItemFlag();
ImGui::SetCursorScreenPos(backup_pos);
}
struct ScrollableTabBar { ImGuiTabBar *tab_bar; bool overflowing; };
std::vector<ScrollableTabBar> scrollable_tab_bars;
}
bool beginScrollableTabBar(const char *str_id, ImGuiTabBarFlags flags) {
ImGuiWindow *window = ImGui::GetCurrentWindow();
ImGuiTabBar *prev_tab_bar = ImGui::TabBarFindByID(window->GetID(str_id));
const bool overflowing = prev_tab_bar && prev_tab_bar->WidthAllTabsIdeal > prev_tab_bar->BarRect.GetWidth() + 1.0f;
const float backup_work_max_x = window->WorkRect.Max.x;
if (overflowing) window->WorkRect.Max.x -= scrollButtonsWidth();
const bool open = ImGui::BeginTabBar(str_id, flags | ImGuiTabBarFlags_FittingPolicyScroll | ImGuiTabBarFlags_NoTabListScrollingButtons);
window->WorkRect.Max.x = backup_work_max_x;
if (open) scrollable_tab_bars.push_back({ImGui::GetCurrentTabBar(), overflowing});
return open;
}
void endScrollableTabBar() {
ImGui::EndTabBar();
const ScrollableTabBar bar = scrollable_tab_bars.back();
scrollable_tab_bars.pop_back();
if (!bar.overflowing) return;
drawScrollButtons(bar.tab_bar);
ImGuiTabBar *tab_bar = bar.tab_bar;
if (ImGui::IsWindowHovered() && ImGui::IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max)) {
ImGui::SetKeyOwner(ImGuiKey_MouseWheelX, tab_bar->ID);
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, tab_bar->ID);
const ImGuiIO &io = ImGui::GetIO();
const float wheel = io.MouseWheelH + io.MouseWheel;
if (wheel != 0.0f) {
const float max_scroll = std::max(0.0f, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth());
const float step = std::floor(ImGui::GetFontSize() * 2.0f);
tab_bar->ScrollingTarget = std::clamp(tab_bar->ScrollingTarget - wheel * step, 0.0f, max_scroll);
tab_bar->ScrollingAnim = tab_bar->ScrollingTarget;
}
}
}