Defcon/hook_lib/imgui/imgui_tabs.cpp

391 lines
15 KiB
C++
Raw Normal View History

2023-11-26 08:54:06 -05:00
/* **************************************************************************************
* Author: Scott Mudge
* MAIL@SCOTTMUDGE.COM
*
* MIT License
* -----------
* MAIL@SCOTTMUDGE.COM
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* **************************************************************************************/
// File Name: imgui_tabs.cpp
// File Description: Custom ImGui tab system module
#include "imgui_tabs.h"
ImGui::ImGuiUserStyle::ImGuiUserStyle() {
Colors[ImGuiUserCol_TabNormal] = ImVec4(0.65f, 0.65f, 0.68f, 1.00f);
Colors[ImGuiUserCol_TabTitleTextNormal] = ImVec4(0.0f, 0.0f, 0.0f, 0.5f);
Colors[ImGuiUserCol_TabTitleTextSelected] = ImVec4(1.0f, 1.0f, 1.0f, 1.00f);
Colors[ImGuiUserCol_TabBorder] = Colors[ImGuiUserCol_TabNormal] * ImVec4(1.15f, 1.15f, 1.15f, 1.0f);
Colors[ImGuiUserCol_TabBorderShadow] = Colors[ImGuiUserCol_TabNormal] * ImVec4(0.65f, 0.65f, 0.65f, 1.0f);
Colors[ImGuiUserCol_TabHover] = Colors[ImGuiUserCol_TabNormal] * ImVec4(1.15f, 1.15f, 1.15f, 1.0f);
}
const ImU32 ImGui::GetColorU32(ImGuiUserCol_ idx, float alpha_mul) {
ImVec4 c = UserStyle.Colors[idx];
if (alpha_mul > 0.0f)
c.w *= GImGui->Style.Alpha * alpha_mul;
return ColorConvertFloat4ToU32(c);
}
void ImGui::_drawPartialRect(const ImVec2 a, const ImVec2 b, const float rounding, const int rounding_corners,
ImDrawList* dl, const _EdgeType edges, const ImU32 color, const bool shadow,
const _EdgeType shadow_edges, const float shadow_offset, const float shadow_alpha) {
float r = rounding;
r = ImMin(r, fabsf(b.x - a.x) * (((rounding_corners & (1 | 2)) == (1 | 2)) || ((rounding_corners & (4 | 8)) == (4 | 8)) ? 0.5f : 1.0f) - 1.0f);
r = ImMin(r, fabsf(b.y - a.y) * (((rounding_corners & (1 | 8)) == (1 | 8)) || ((rounding_corners & (2 | 4)) == (2 | 4)) ? 0.5f : 1.0f) - 1.0f);
if (r <= 0.0f || rounding_corners == 0)
{
if (edges & EDGE_TOP) {
dl->PathLineTo(a);
dl->PathLineTo(ImVec2(b.x, a.y));
dl->PathStroke(color, false, 1.0f);
} if (edges & EDGE_RIGHT) {
dl->PathLineTo(ImVec2(b.x, a.y));
dl->PathStroke(color, false, 1.0f);
dl->PathLineTo(b);
} if (edges & EDGE_BOTTOM) {
dl->PathLineTo(b);
dl->PathLineTo(ImVec2(a.x, b.y));
dl->PathStroke(color, false, 1.0f);
} if (edges & EDGE_LEFT) {
dl->PathLineTo(ImVec2(a.x, b.y));
dl->PathLineTo(a);
dl->PathStroke(color, false, 1.0f);
}
}
else
{
const float r0 = (rounding_corners & 1) ? r : 0.0f;
const float r1 = (rounding_corners & 2) ? r : 0.0f;
const float r2 = (rounding_corners & 4) ? r : 0.0f;
const float r3 = (rounding_corners & 8) ? r : 0.0f;
if (edges & EDGE_TOP) {
dl->PathArcToFast(ImVec2(a.x + r0, a.y + r0), r0, 6, 9);
dl->PathArcToFast(ImVec2(b.x - r1, a.y + r1), r1, 9, 12);
dl->PathStroke(color, false, 1.0f);
} if (edges & EDGE_RIGHT) {
dl->PathArcToFast(ImVec2(b.x - r1, a.y + r1), r1, 9, 12);
dl->PathArcToFast(ImVec2(b.x - r2, b.y - r2), r2, 0, 3);
dl->PathStroke(color, false, 1.0f);
} if (edges & EDGE_BOTTOM) {
dl->PathArcToFast(ImVec2(b.x - r2, b.y - r2), r2, 0, 3);
dl->PathArcToFast(ImVec2(a.x + r3, b.y - r3), r3, 3, 6);
dl->PathStroke(color, false, 1.0f);
} if (edges & EDGE_LEFT) {
dl->PathArcToFast(ImVec2(a.x + r3, b.y - r3), r3, 3, 6);
dl->PathArcToFast(ImVec2(a.x + r0, a.y + r0), r0, 6, 9);
dl->PathStroke(color, false, 1.0f);
}
if (shadow) {
const ImColor col = ImColor(0.0f, 0.0f, 0.0f, shadow_alpha);
if (shadow_edges & EDGE_LEFT) {
dl->PathArcToFast(ImVec2(a.x + r3 - shadow_offset, b.y - r3), r3, 3, 6);
dl->PathArcToFast(ImVec2(a.x + r0 - shadow_offset, a.y + r0), r0, 6, 9);
dl->PathLineTo(ImVec2(a.x, b.y));
dl->PathFill(col);
} if (shadow_edges & EDGE_RIGHT) {
dl->PathArcToFast(ImVec2(b.x - r1 + shadow_offset, a.y + r1), r1, 9, 12);
dl->PathArcToFast(ImVec2(b.x - r2 + shadow_offset, b.y - r2), r2, 0, 3);
dl->PathLineTo(ImVec2(b.x, b.y));
dl->PathFill(col);
}
}
}
}
ImGui::TabBar::TabBar(const char* label, const ImVec2 tab_bar_size) {
hash = ImHash(label, 0);
size = tab_bar_size;
}
void ImGui::TabBar::setActiveTab(const unsigned idx) {
activeTab = (int)idx; // This get's checked at the beginning of
}
const int ImGui::TabBar::getActiveTab() {
return activeTab;
}
void ImGui::TabBar::_drawTabBarTop(const char* label) {
if (!hasBeenInitialized)
return;
// Gather pointers and references
ImGuiWindow* wind = ImGui::GetCurrentWindow();
ImDrawList* dl = wind->DrawList;
ImGuiDrawContext& dc = wind->DC;
ImGuiStyle& style = ImGui::GetStyle();
std::string str = label;
barTitle = str.substr(0, str.find_first_of("#"));
if (barTitle.length() > 0)
ImGui::Text(std::string("\t" + barTitle).c_str(), "%s");
// Get some geometric data
const ImVec2 padding = style.WindowPadding;
const ImVec2 frame_padding = style.FramePadding;
ImVec2 pos = dc.CursorPos;
const float division = ((wind->Size.x - 2 * padding.x) / (float)tabCount);
// Compile-time constants
static constexpr const float rounding = 6.0f;
// This is the border shrink in px. Not sure why it needs to be 1, but it works. Adjust to your border size
static constexpr const float shrink = 1.0f;
const float tab_height = CalcTextSize(tabTitles[0]).y + (frame_padding.y * 2);
float selected_offset = 0;
_TabType selected_tab_type;
ImVec2 selected_expands;
_EdgeType selected_shadow_edges = EDGE_NONE;
int selected_idx = 0;
// Store maximum/minimum x value for clipping rect.
const float max_x = wind->Pos.x + wind->Size.x - padding.x; // Used for clipping rect adjustment.
const float min_x = wind->Pos.x + padding.x;
if (activeTab > tabCount - 1)
activeTab = tabCount - 1;
// Idx of the selected tab
newSelected = -1;
// Draw the tabs
for (int i = 0; i < tabCount; i++) {
// Calculate offset
const float offs = i * division;
const bool selected = (activeTab == i);
ImVec2 expands;
if (!selected) {
if (i == 0) {
expands = ImVec2(0, 2);
}
else if (i == tabCount - 1) {
expands = ImVec2(1, 0);
}
else {
expands = ImVec2(1, 2);
}
const float xl = offs - expands.x + shrink;
const float xr = offs + division - shrink + expands.y;
const ImRect bb = ImRect(ImVec2(pos + ImVec2(xl, 0)), ImVec2(pos + ImVec2(xr, tab_height)));
bool hovered, held;
bool pressed = ButtonBehavior(bb, tabHashes[i], &hovered, &held);
if (held)
newSelected = i;
ImU32 col;
hovered ? col = GetColorU32(ImGuiUserCol_TabHover) : col = GetColorU32(ImGuiUserCol_TabNormal);
// Draw background rect
dl->AddRectFilled(bb.Min, bb.Max, col, rounding,
ImGuiCorner_TopLeft | ImGuiCorner_TopRight);
// Draw the border
_drawPartialRect(bb.Min, bb.Max, rounding, ImGuiCorner_TopLeft | ImGuiCorner_TopRight, dl,
(_EdgeType)(EDGE_LEFT | EDGE_RIGHT | EDGE_TOP), GetColorU32(ImGuiUserCol_TabBorderShadow));
// Draw the text
const ImVec2 text_size = CalcTextSize(tabTitles[i]);
const ImVec2 text_pos = pos + ImVec2(offs + ((xr - xl) - text_size.x) / 2.0f, ((text_size.y - frame_padding.y * 2.0f) / 2.0f));
dl->AddText(text_pos, GetColorU32(ImGuiUserCol_TabTitleTextNormal), tabTitles[i]);
}
else {
selected_offset = offs;
selected_idx = i;
if (i == 0) {
selected_expands = ImVec2(0, rounding);
selected_shadow_edges = EDGE_RIGHT;
}
else if (i == tabCount - 1) {
selected_expands = ImVec2(rounding, 0);
selected_shadow_edges = EDGE_LEFT;
}
else {
selected_expands = ImVec2(rounding, rounding);
selected_shadow_edges = (_EdgeType)(EDGE_RIGHT | EDGE_LEFT);
}
}
}
const float xl = selected_offset - selected_expands.x + shrink;
const float xr = selected_offset + division - shrink + selected_expands.y;
const ImRect bb = ImRect(ImVec2(pos + ImVec2(xl, 0)), ImVec2(pos + ImVec2(xr, tab_height)));
// Draw the selected tab on top of everything else
dl->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiUserCol_TabNormal), rounding, ImGuiCorner_TopLeft | ImGuiCorner_TopRight);
dl->AddRectFilled(bb.Min, bb.Max, ImColor(1.0f, 1.0f, 1.0f, 0.35f), rounding, ImGuiCorner_TopLeft | ImGuiCorner_TopRight);
// Draw the border
_drawPartialRect(bb.Min, bb.Max, rounding, ImGuiCorner_TopLeft | ImGuiCorner_TopRight, dl,
(_EdgeType)(EDGE_LEFT | EDGE_RIGHT | EDGE_TOP), GetColorU32(ImGuiUserCol_TabBorderShadow),
true, selected_shadow_edges);
// Draw the text
const ImVec2 text_size = CalcTextSize(tabTitles[selected_idx]);
const ImVec2 text_pos = pos + ImVec2(selected_offset + ((xr - xl) - text_size.x) / 2.0f - selected_expands.x, ((text_size.y - frame_padding.y * 2.0f) / 2.0f));
dl->AddText(text_pos + ImVec2(1, 1), GetColorU32(ImGuiUserCol_TabTitleTextNormal), tabTitles[selected_idx]); // Shadow
dl->AddText(text_pos, GetColorU32(ImGuiUserCol_TabTitleTextSelected), tabTitles[selected_idx]);
dc.CursorPos += ImVec2(0, tab_height + padding.y); // Add all the extra height used above.
upperLeft = dc.CursorPos - ImVec2(0, padding.y);
Indent(padding.x);
corner_rounding = rounding;
ImGui::PushClipRect(ImVec2(min_x, bb.Max.y), ImVec2(max_x, wind->ClipRect.Max.y), false);
}
void ImGui::TabBar::_drawTabBarBottom() {
ImGuiWindow* wind = ImGui::GetCurrentWindow();
ImDrawList* dl = wind->DrawList;
ImGuiStyle& style = ImGui::GetStyle();
ImGuiDrawContext& dc = wind->DC;
const ImVec2 padding = style.WindowPadding;
// dc.CursorPos.y = bottom of tabs + padding.y
// wind->Size.y gives the full window height
// the zero below the tabs is dc.CursorPos.y - padding.y
// and if we wnt to add the tab height, we'll have to remove a constant
// (= all the additional offsets used for the layout). FIXME : use the real name(s) instead
const float height = dc.CursorPos.y - padding.y + wind->Size.y - 92.0f;
const ImVec2 pos = ImVec2(wind->Pos.x + wind->Size.x - padding.x, height);
// Draw the background in a given color + alpha
dl->AddRectFilled(upperLeft, pos, ImColor(1.0f, 1.0f, 1.0f, 0.15f), corner_rounding, ImGuiCorner_BottomLeft | ImGuiCorner_BottomRight);
// Draw the border in a given color + alpha
dl->AddRect(upperLeft, pos, ImColor(1.0f, 1.0f, 1.0f, 0.35f), corner_rounding, ImGuiCorner_BottomLeft | ImGuiCorner_TopRight);
dc.CursorPos += ImVec2(0, corner_rounding + 2.0f); // Add all the extra height used above.
ImGui::PopClipRect();
}
void ImGui::TabBar::_setTabCount() {
if (newSelected >= 0) {
activeTab = newSelected;
}
// If the tab count changes, reinitialize the list.
if (hasBeenInitialized && tabCount != idxCounter) {
hasBeenInitialized = false;
tabTitles.clear();
tabHashes.clear();
return;
}
tabCount = idxCounter;
hasBeenInitialized = true;
}
ImGui::TabBar* ImGui::TabBarStack::getCurrentTabBar() {
if (CurrentTabBar > TabBarCount) {
std::cerr << "TabBar (Error @getCurrentTabBar()) -- CurrentTabBar > TabBarCount" << std::endl;
return NULL;
}
return &TabBars[CurrentTabBar];
}
void ImGui::TabBarStack::clearIdxCountCurrentTabBar() {
TabBars[CurrentTabBar].idxCounter = 0;
}
const bool ImGui::TabBarStack::doesTabBarExist(const ImU32 hash, unsigned int* const idx) {
for (unsigned i = 0; i < TabBarCount; i++)
if (hash == TabBars[i].hash) {
if (idx)
*idx = i;
return true;
}
return false;
}
const bool ImGui::TabBarStack::doesTabBarExist(const char* id, unsigned int* const idx) {
const ImU32 hash = ImHash(id, 0);
for (unsigned i = 0; i < TabBarCount; i++)
if (hash == TabBars[i].hash) {
if (idx)
*idx = i;
return true;
}
*idx = 0;
return false;
}
void ImGui::BeginTabBar(const char* label, const ImVec2 size) {
// Pass this to doesTabBarExist() to get the index of the started TabBar
unsigned idx;
// If a tab bar doesn't exist, let's create one.
if (!TabStack.doesTabBarExist(label, &idx)) {
TabBar bar = TabBar(label, size);
TabStack.TabBars.push_back(bar);
TabStack.CurrentTabBar = TabStack.TabBarCount;
TabStack.TabBarCount++;
}
else { // Set the current active tab bar
TabStack.CurrentTabBar = idx;
TabStack.clearIdxCountCurrentTabBar();
}
TabStack.getCurrentTabBar()->_drawTabBarTop(label);
}
const bool ImGui::AddTab(const char* title) {
// Get current TabBar;
TabBar* bar = TabStack.getCurrentTabBar();
// Check to make sure there aren't any null pointers
assert(bar != NULL);
bar->idxCounter++; // Always increase the counter;
if (bar->idxCounter > bar->tabCount)
bar->hasBeenInitialized = false;
if (!bar->hasBeenInitialized) {
bar->tabTitles.push_back(title);
bar->tabHashes.push_back(ImHash(std::string(title + std::to_string(bar->idxCounter)).c_str(), 0));
}
if (bar->activeTab != (bar->idxCounter - 1))
return false;
return bar->hasBeenInitialized;
}
void ImGui::DrawTabsBackground()
{
TabBar* bar = TabStack.getCurrentTabBar();
if (bar->hasBeenInitialized)
bar->_drawTabBarBottom();
}
void ImGui::EndTabBar() {
TabBar* bar = TabStack.getCurrentTabBar();
bar->_setTabCount();
}
/* FIXME : unused ?
void ImGui::SetActiveTabOfCurrentTabBar(const unsigned idx) {
TabStack.getCurrentTabBar()->setActiveTab(idx);
}
*/