feat(lite): network-tab polish, reworked key-export/QR dialogs
Network tab: glow only the active node, drop the left accent bar. Key-export dialog: fix the lite-wallet "Not connected" failure by exporting the key locally via the SDXL backend when there's no daemon; rework the layout to wrapping click-to-copy fields with a side QR (empty placeholder when hidden), 85% modal width, HRP-preserving key chunking, and a centered, emphasized warning. QR popup matched to the same sizing and click-to-copy address. Shared field rendering extracted to widgets/copy_field.h so both dialogs stay in sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
113
src/ui/widgets/copy_field.h
Normal file
113
src/ui/widgets/copy_field.h
Normal file
@@ -0,0 +1,113 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
//
|
||||
// Shared "click-to-copy field" widgets used by the key-export and QR-popup dialogs, so an address
|
||||
// renders identically in both. A field is a bordered box of wrapping text whose whole area copies
|
||||
// to the clipboard on click; long secrets/addresses are grouped into space-separated blocks.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "../material/colors.h"
|
||||
#include "../material/tooltip_style.h"
|
||||
#include "../../util/i18n.h"
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
namespace widgets {
|
||||
|
||||
// Draw `text` wrapped within wrapW with every visual line horizontally centered (returns the height
|
||||
// used). ImGui has no center+wrap, so we walk the wrap points the way it would.
|
||||
inline float DrawCenteredWrapped(ImDrawList* dl, ImFont* font, float fs, ImVec2 origin,
|
||||
float wrapW, ImU32 col, const char* text)
|
||||
{
|
||||
const float scale = fs / font->LegacySize;
|
||||
const float lineH = ImGui::GetTextLineHeight();
|
||||
const char* s = text;
|
||||
const char* e = text + std::strlen(text);
|
||||
float y = origin.y;
|
||||
while (s < e) {
|
||||
const char* wrap = font->CalcWordWrapPositionA(scale, s, e, wrapW);
|
||||
if (wrap <= s) wrap = s + 1;
|
||||
const ImVec2 lw = font->CalcTextSizeA(fs, 1.0e30f, 0.0f, s, wrap);
|
||||
float lx = origin.x + (wrapW - lw.x) * 0.5f;
|
||||
if (lx < origin.x) lx = origin.x;
|
||||
dl->AddText(font, fs, ImVec2(lx, y), col, s, wrap);
|
||||
y += lineH;
|
||||
s = wrap;
|
||||
while (s < e && (*s == ' ' || *s == '\n')) s++; // ImGui consumes the break char
|
||||
}
|
||||
return y - origin.y;
|
||||
}
|
||||
|
||||
// Group a long string into space-separated blocks so it reads in scannable chunks and wraps on
|
||||
// block boundaries. Callers copy the ORIGINAL (un-grouped) string.
|
||||
inline std::string ChunkString(const std::string& s, size_t group)
|
||||
{
|
||||
if (group == 0 || s.size() <= group) return s;
|
||||
std::string out;
|
||||
out.reserve(s.size() + s.size() / group + 1);
|
||||
for (size_t i = 0; i < s.size(); ++i) {
|
||||
if (i && (i % group) == 0) out.push_back(' ');
|
||||
out.push_back(s[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// A wrapping, click-to-copy "field": draws `display` in a bordered box (the whole box is the copy
|
||||
// affordance) and returns true when clicked. `center` centers each wrapped line. The caller is
|
||||
// responsible for the actual clipboard write (so secrets can use an auto-clearing clipboard).
|
||||
inline bool CopyField(const char* id, const std::string& display, float width, float minH, bool center)
|
||||
{
|
||||
namespace m = material;
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
ImGuiStyle& st = ImGui::GetStyle();
|
||||
ImFont* font = ImGui::GetFont();
|
||||
const float fs = ImGui::GetFontSize();
|
||||
const float padX = st.FramePadding.x + 4.0f;
|
||||
const float padY = st.FramePadding.y + 4.0f;
|
||||
const float wrapW = (width - padX * 2.0f) > 1.0f ? (width - padX * 2.0f) : 1.0f;
|
||||
const ImVec2 ts = ImGui::CalcTextSize(display.c_str(), nullptr, false, wrapW);
|
||||
float h = ts.y + padY * 2.0f;
|
||||
if (h < minH) h = minH;
|
||||
const ImVec2 p0 = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 p1 = ImVec2(p0.x + width, p0.y + h);
|
||||
ImGui::InvisibleButton(id, ImVec2(width, h));
|
||||
const bool hovered = ImGui::IsItemHovered();
|
||||
const bool clicked = ImGui::IsItemClicked();
|
||||
dl->AddRectFilled(p0, p1, m::WithAlpha(m::OnSurface(), hovered ? 18 : 8), 6.0f);
|
||||
dl->AddRect(p0, p1, m::WithAlpha(m::OnSurface(), hovered ? 70 : 30), 6.0f, 0, 1.0f);
|
||||
const float textY = p0.y + (h - ts.y) * 0.5f; // vertical center
|
||||
if (center)
|
||||
DrawCenteredWrapped(dl, font, fs, ImVec2(p0.x + padX, textY), wrapW, m::OnSurface(), display.c_str());
|
||||
else
|
||||
dl->AddText(font, fs, ImVec2(p0.x + padX, textY), m::OnSurface(), display.c_str(), nullptr, wrapW);
|
||||
if (hovered) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", TR("copy"));
|
||||
}
|
||||
return clicked;
|
||||
}
|
||||
|
||||
// An address field: chunked by 4 for readability, width clamped to its text, box centered in the
|
||||
// available region. Copies the raw address to the clipboard on click; returns true when clicked.
|
||||
inline bool AddressCopyField(const char* id, const std::string& address)
|
||||
{
|
||||
const std::string at = ChunkString(address, 4);
|
||||
const float avail = ImGui::GetContentRegionAvail().x;
|
||||
const float padX = ImGui::GetStyle().FramePadding.x + 4.0f;
|
||||
float boxW = ImGui::CalcTextSize(at.c_str()).x + padX * 2.0f + 2.0f; // clamp to text width
|
||||
if (boxW > avail) boxW = avail;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (avail > boxW ? (avail - boxW) * 0.5f : 0.0f));
|
||||
const bool clicked = CopyField(id, at, boxW, 0.0f, /*center=*/true);
|
||||
if (clicked) ImGui::SetClipboardText(address.c_str());
|
||||
return clicked;
|
||||
}
|
||||
|
||||
} // namespace widgets
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user