refactor(ui): delete dead button code (buttons.h + ApplyTactile)
UI-standardization audit found two dead, contradictory "how to draw a button" surfaces with zero call sites: - src/ui/material/components/buttons.h (353 lines: TextButton/Outlined/Contained/ Button(spec)/IconButton/FAB) — included by 3 mining files but never used. - ApplyTactile (draw_helpers.h) — a retrofit-tactile-onto-plain-button wrapper, never called. Remove the file, the 3 dead includes, and ApplyTactile. TactileButton/StyledButton remain the canonical button helpers. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,352 +0,0 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../colors.h"
|
||||
#include "../typography.h"
|
||||
#include "../layout.h"
|
||||
#include "../tooltip_style.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
namespace material {
|
||||
|
||||
// ============================================================================
|
||||
// Material Design Button Components
|
||||
// ============================================================================
|
||||
// Based on https://m2.material.io/components/buttons
|
||||
//
|
||||
// Three button variants:
|
||||
// - Text Button: Low emphasis, no container
|
||||
// - Outlined Button: Medium emphasis, border only
|
||||
// - Contained Button: High emphasis, filled background
|
||||
|
||||
enum class ButtonStyle {
|
||||
Text, // Low emphasis - text only
|
||||
Outlined, // Medium emphasis - border
|
||||
Contained // High emphasis - filled (default)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Button configuration
|
||||
*/
|
||||
struct ButtonSpec {
|
||||
ButtonStyle style = ButtonStyle::Contained;
|
||||
bool enabled = true;
|
||||
bool fullWidth = false;
|
||||
const char* icon = nullptr; // Leading icon (text glyph)
|
||||
ImU32 color = 0; // 0 = use primary color
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Render a Material Design button
|
||||
*
|
||||
* @param label Button text (will be uppercased per Material spec)
|
||||
* @param spec Button configuration
|
||||
* @return true if clicked
|
||||
*/
|
||||
bool Button(const char* label, const ButtonSpec& spec = ButtonSpec());
|
||||
|
||||
/**
|
||||
* @brief Render a text-only button (low emphasis)
|
||||
*/
|
||||
inline bool TextButton(const char* label, bool enabled = true) {
|
||||
ButtonSpec spec;
|
||||
spec.style = ButtonStyle::Text;
|
||||
spec.enabled = enabled;
|
||||
return Button(label, spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Render an outlined button (medium emphasis)
|
||||
*/
|
||||
inline bool OutlinedButton(const char* label, bool enabled = true) {
|
||||
ButtonSpec spec;
|
||||
spec.style = ButtonStyle::Outlined;
|
||||
spec.enabled = enabled;
|
||||
return Button(label, spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Render a contained/filled button (high emphasis)
|
||||
*/
|
||||
inline bool ContainedButton(const char* label, bool enabled = true) {
|
||||
ButtonSpec spec;
|
||||
spec.style = ButtonStyle::Contained;
|
||||
spec.enabled = enabled;
|
||||
return Button(label, spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Render an icon button (circular touch target)
|
||||
*
|
||||
* @param icon Icon glyph/character
|
||||
* @param tooltip Hover tooltip text
|
||||
* @param enabled Whether button is enabled
|
||||
* @return true if clicked
|
||||
*/
|
||||
bool IconButton(const char* icon, const char* tooltip = nullptr, bool enabled = true);
|
||||
|
||||
/**
|
||||
* @brief Render a Floating Action Button (FAB)
|
||||
*
|
||||
* @param icon Icon glyph
|
||||
* @param label Optional label for extended FAB
|
||||
* @param mini Use mini size (40dp instead of 56dp)
|
||||
* @return true if clicked
|
||||
*/
|
||||
bool FAB(const char* icon, const char* label = nullptr, bool mini = false);
|
||||
|
||||
// ============================================================================
|
||||
// Implementation
|
||||
// ============================================================================
|
||||
|
||||
inline bool Button(const char* label, const ButtonSpec& spec) {
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
|
||||
// Convert label to uppercase for Material Design
|
||||
char upperLabel[256];
|
||||
{
|
||||
const char* src = label;
|
||||
char* dst = upperLabel;
|
||||
char* end = upperLabel + sizeof(upperLabel) - 1;
|
||||
while (*src && dst < end) {
|
||||
*dst++ = (char)toupper((unsigned char)*src++);
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
// Get button font
|
||||
ImFont* buttonFont = Typography::instance().button();
|
||||
ImGui::PushFont(buttonFont);
|
||||
|
||||
// Calculate button size
|
||||
ImVec2 labelSize = ImGui::CalcTextSize(upperLabel);
|
||||
float iconWidth = spec.icon ? (size::IconSize + size::ButtonIconGap) : 0.0f;
|
||||
|
||||
ImVec2 buttonSize;
|
||||
buttonSize.x = spec.fullWidth ? ImGui::GetContentRegionAvail().x
|
||||
: ImMax(labelSize.x + iconWidth + size::ButtonPaddingH * 2, size::ButtonMinWidth);
|
||||
buttonSize.y = size::ButtonHeight;
|
||||
|
||||
// Get position
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImRect bb(pos, ImVec2(pos.x + buttonSize.x, pos.y + buttonSize.y));
|
||||
|
||||
ImGui::ItemSize(buttonSize, style.FramePadding.y);
|
||||
if (!ImGui::ItemAdd(bb, id)) {
|
||||
ImGui::PopFont();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle interaction
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, 0);
|
||||
|
||||
if (!spec.enabled) {
|
||||
hovered = held = false;
|
||||
}
|
||||
|
||||
if (hovered) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
|
||||
// Get colors
|
||||
ImU32 primaryColor = spec.color ? spec.color : Primary();
|
||||
ImU32 textColor;
|
||||
ImU32 bgColor;
|
||||
ImU32 borderColor = 0;
|
||||
|
||||
switch (spec.style) {
|
||||
case ButtonStyle::Text:
|
||||
bgColor = 0; // No background
|
||||
textColor = spec.enabled ? primaryColor : OnSurfaceDisabled();
|
||||
if (hovered) bgColor = BlendOverlay(Surface(), StateHover(), 1.0f);
|
||||
if (held) bgColor = BlendOverlay(Surface(), StatePressed(), 1.0f);
|
||||
break;
|
||||
|
||||
case ButtonStyle::Outlined:
|
||||
bgColor = 0; // No background
|
||||
textColor = spec.enabled ? primaryColor : OnSurfaceDisabled();
|
||||
borderColor = spec.enabled ? primaryColor : OnSurfaceDisabled();
|
||||
if (hovered) bgColor = BlendOverlay(Surface(), StateHover(), 1.0f);
|
||||
if (held) bgColor = BlendOverlay(Surface(), StatePressed(), 1.0f);
|
||||
break;
|
||||
|
||||
case ButtonStyle::Contained:
|
||||
default:
|
||||
if (spec.enabled) {
|
||||
bgColor = primaryColor;
|
||||
textColor = OnPrimary();
|
||||
if (hovered) bgColor = BlendOverlay(primaryColor, StateHover(), 1.0f);
|
||||
if (held) bgColor = BlendOverlay(primaryColor, StatePressed(), 1.0f);
|
||||
} else {
|
||||
bgColor = WithAlphaF(OnSurface(), 0.12f);
|
||||
textColor = OnSurfaceDisabled();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Render
|
||||
ImDrawList* drawList = window->DrawList;
|
||||
|
||||
// Background
|
||||
if (bgColor) {
|
||||
drawList->AddRectFilled(bb.Min, bb.Max, bgColor, size::ButtonCornerRadius);
|
||||
}
|
||||
|
||||
// Border (for outlined)
|
||||
if (borderColor) {
|
||||
drawList->AddRect(bb.Min, bb.Max, borderColor, size::ButtonCornerRadius, 0, 1.0f);
|
||||
}
|
||||
|
||||
// Icon
|
||||
float contentX = bb.Min.x + size::ButtonPaddingH;
|
||||
float contentY = bb.Min.y + (buttonSize.y - labelSize.y) * 0.5f;
|
||||
|
||||
if (spec.icon) {
|
||||
ImFont* iconFont = Type().iconMed();
|
||||
ImVec2 iconSize = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, spec.icon);
|
||||
ImVec2 iconPos(contentX, bb.Min.y + (buttonSize.y - iconSize.y) * 0.5f);
|
||||
drawList->AddText(iconFont, iconFont->LegacySize, iconPos, textColor, spec.icon);
|
||||
contentX += iconSize.x + size::ButtonIconGap;
|
||||
}
|
||||
|
||||
// Label
|
||||
drawList->AddText(ImVec2(contentX, contentY), textColor, upperLabel);
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
return pressed && spec.enabled;
|
||||
}
|
||||
|
||||
inline bool IconButton(const char* icon, const char* tooltip, bool enabled) {
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiID id = window->GetID(icon);
|
||||
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImVec2 size(size::IconButtonSize, size::IconButtonSize);
|
||||
ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y));
|
||||
|
||||
ImGui::ItemSize(size);
|
||||
if (!ImGui::ItemAdd(bb, id))
|
||||
return false;
|
||||
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, 0);
|
||||
|
||||
if (!enabled) {
|
||||
hovered = held = false;
|
||||
}
|
||||
|
||||
if (hovered) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
|
||||
// Render ripple/hover circle
|
||||
ImDrawList* drawList = window->DrawList;
|
||||
ImVec2 center = bb.GetCenter();
|
||||
float radius = size::IconButtonSize * 0.5f;
|
||||
|
||||
if (hovered || held) {
|
||||
ImU32 overlayColor = held ? StatePressed() : StateHover();
|
||||
drawList->AddCircleFilled(center, radius, overlayColor);
|
||||
}
|
||||
|
||||
// Render icon (use icon font)
|
||||
ImFont* iconFont = Type().iconMed();
|
||||
ImU32 iconColor = enabled ? OnSurface() : OnSurfaceDisabled();
|
||||
ImVec2 iconSize = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, icon);
|
||||
ImVec2 iconPos(center.x - iconSize.x * 0.5f, center.y - iconSize.y * 0.5f);
|
||||
drawList->AddText(iconFont, iconFont->LegacySize, iconPos, iconColor, icon);
|
||||
|
||||
// Tooltip
|
||||
if (tooltip && hovered) {
|
||||
material::Tooltip("%s", tooltip);
|
||||
}
|
||||
|
||||
return pressed && enabled;
|
||||
}
|
||||
|
||||
inline bool FAB(const char* icon, const char* label, bool mini) {
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiID id = window->GetID(icon);
|
||||
|
||||
bool extended = (label != nullptr);
|
||||
float fabSize = mini ? size::FabMiniSize : size::FabSize;
|
||||
|
||||
ImVec2 buttonSize;
|
||||
if (extended) {
|
||||
ImVec2 labelSize = ImGui::CalcTextSize(label);
|
||||
buttonSize.x = size::FabExtendedPadding * 2 + size::IconSize + spacing::Sm + labelSize.x;
|
||||
buttonSize.y = size::FabExtendedHeight;
|
||||
} else {
|
||||
buttonSize.x = fabSize;
|
||||
buttonSize.y = fabSize;
|
||||
}
|
||||
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImRect bb(pos, ImVec2(pos.x + buttonSize.x, pos.y + buttonSize.y));
|
||||
|
||||
ImGui::ItemSize(buttonSize);
|
||||
if (!ImGui::ItemAdd(bb, id))
|
||||
return false;
|
||||
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, 0);
|
||||
|
||||
if (hovered) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
|
||||
// Colors
|
||||
ImU32 bgColor = Secondary();
|
||||
ImU32 textColor = OnSecondary();
|
||||
|
||||
if (hovered) bgColor = BlendOverlay(bgColor, StateHover(), 1.0f);
|
||||
if (held) bgColor = BlendOverlay(bgColor, StatePressed(), 1.0f);
|
||||
|
||||
// Render
|
||||
ImDrawList* drawList = window->DrawList;
|
||||
float radius = extended ? size::FabCornerRadius : (fabSize * 0.5f);
|
||||
drawList->AddRectFilled(bb.Min, bb.Max, bgColor, radius);
|
||||
|
||||
// Icon
|
||||
ImVec2 center = bb.GetCenter();
|
||||
ImFont* iconFont = Type().iconMed();
|
||||
if (extended) {
|
||||
ImVec2 iconSize = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, icon);
|
||||
float iconX = bb.Min.x + size::FabExtendedPadding;
|
||||
float iconY = center.y - iconSize.y * 0.5f;
|
||||
drawList->AddText(iconFont, iconFont->LegacySize, ImVec2(iconX, iconY), textColor, icon);
|
||||
|
||||
// Label
|
||||
Typography::instance().pushFont(TypeStyle::Button);
|
||||
float labelX = iconX + iconSize.x + spacing::Sm;
|
||||
float labelY = center.y - ImGui::GetFontSize() * 0.5f;
|
||||
drawList->AddText(ImVec2(labelX, labelY), textColor, label);
|
||||
Typography::instance().popFont();
|
||||
} else {
|
||||
ImVec2 iconSize = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, icon);
|
||||
ImVec2 iconPos(center.x - iconSize.x * 0.5f, center.y - iconSize.y * 0.5f);
|
||||
drawList->AddText(iconFont, iconFont->LegacySize, iconPos, textColor, icon);
|
||||
}
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
} // namespace material
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
@@ -260,18 +260,6 @@ inline void DrawButtonGlassOverlay(ImDrawList* dl, const ImVec2& bMin,
|
||||
DrawTactileOverlay(dl, bMin, bMax, rounding, active);
|
||||
}
|
||||
|
||||
// Convenience: call right after any ImGui::Button() / SmallButton() call
|
||||
// to add tactile depth. Uses the last item rect automatically.
|
||||
inline void ApplyTactile(ImDrawList* dl = nullptr)
|
||||
{
|
||||
if (!dl) dl = ImGui::GetWindowDrawList();
|
||||
ImVec2 bMin = ImGui::GetItemRectMin();
|
||||
ImVec2 bMax = ImGui::GetItemRectMax();
|
||||
float rounding = ImGui::GetStyle().FrameRounding;
|
||||
bool active = ImGui::IsItemActive();
|
||||
DrawTactileOverlay(dl, bMin, bMax, rounding, active);
|
||||
}
|
||||
|
||||
// ── Button font tier helper ─────────────────────────────────────────────
|
||||
// Resolves an int tier (0=sm, 1=md/default, 2=lg) to the matching ImFont*.
|
||||
// Passing -1 or any out-of-range value returns the default button font.
|
||||
|
||||
Reference in New Issue
Block a user