feat(ui): migrate Validate-address, Address-label & Contacts modals to reference design

Presentation-only reference-design migration of three more modals:
- Validate Address: struct-form overlay (idSuffix validateaddr), 3 StyledButton ->
  TactileButton, drop the description divider, and TR() the hardcoded "Error: %s"
  (reuse the existing error_format key). The validateaddress RPC/worker path is untouched.
- Address label: struct-form overlay (idSuffix addrlabel); drop the footer divider and
  zero its layout-reserve term (separatorH) so the fixed-height icon-picker card math
  stays consistent. Buttons were already TactileButton.
- Contacts add/edit: struct-form overlay (keeps its idSuffix + BeginOverlayDialogFooter);
  StyledButton -> TactileButton across the file (3 modal + 4 contact-list buttons) for a
  consistent Contacts tab. The address-book add/update/notify logic is untouched.

Build + hygiene clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 16:24:28 -05:00
parent 04a5078133
commit 304f65c3b4
3 changed files with 30 additions and 20 deletions

View File

@@ -61,7 +61,12 @@ public:
constexpr float cardTopViewportRatio = 0.15f;
constexpr float cardBottomViewportRatio = 0.80f;
if (BeginOverlayDialog(TR("set_label"), &s_open, 660.0f, 0.92f, cardBottomViewportRatio)) {
OverlayDialogSpec ov;
ov.title = TR("set_label"); ov.p_open = &s_open;
ov.style = OverlayStyle::BlurFloat;
ov.cardWidth = 660.0f; ov.idSuffix = "addrlabel";
ov.cardBottomViewportRatio = cardBottomViewportRatio;
if (BeginOverlayDialog(ov)) {
float dp = Layout::dpiScale();
// Address preview
@@ -108,7 +113,7 @@ public:
const bool showClearIcon = (s_selectedIcon >= 0);
const float clearRowH = showClearIcon ? ImGui::GetTextLineHeight() : 0.0f;
const float buttonRowH = ImGui::GetFrameHeight();
const float separatorH = 1.0f;
const float separatorH = 0.0f; // footer divider removed (reference design)
const float preButtonReserve =
(showClearIcon ? (style.ItemSpacing.y + clearRowH) : 0.0f) +
style.ItemSpacing.y * 3.0f + separatorH;
@@ -196,7 +201,6 @@ public:
ImGui::Spacing();
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::GetCursorPosY() < buttonY) {

View File

@@ -129,8 +129,12 @@ void RenderContactsTab(App* app)
float actionGap = actionBtn.gap > 0 ? actionBtn.gap : Layout::kDialogActionGap();
float notesH = notesInput.height > 0 ? notesInput.height : 60.0f;
if (material::BeginOverlayDialog(title, open, dialogW, 0.94f,
Layout::kDialogCompactBottomRatio(), id)) {
material::OverlayDialogSpec ov;
ov.title = title; ov.p_open = open;
ov.style = material::OverlayStyle::BlurFloat;
ov.cardWidth = dialogW; ov.idSuffix = id;
ov.cardBottomViewportRatio = Layout::kDialogCompactBottomRatio();
if (material::BeginOverlayDialog(ov)) {
// Focus the first field the frame the dialog opens so it's keyboard-ready.
if (s_focus_edit_field) {
ImGui::SetKeyboardFocusHere();
@@ -145,7 +149,7 @@ void RenderContactsTab(App* app)
s_edit_address, sizeof(s_edit_address), formW);
if (!isEdit) {
ImGui::SameLine();
if (material::StyledButton(TR("paste"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
if (material::TactileButton(TR("paste"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
const char* clipboard = ImGui::GetClipboardText();
if (clipboard) copyEditField(s_edit_address, sizeof(s_edit_address), clipboard);
}
@@ -167,7 +171,7 @@ void RenderContactsTab(App* app)
if (!canSubmit) ImGui::BeginDisabled();
const char* primaryLabel = isEdit ? TR("save") : TR("add");
if (material::StyledButton(primaryLabel, ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
if (material::TactileButton(primaryLabel, ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
// Trim the label/address (a pasted address often carries a trailing newline); keep notes as-is.
auto trimAB = [](std::string s) {
while (!s.empty() && (s.front()==' '||s.front()=='\t'||s.front()=='\n'||s.front()=='\r')) s.erase(s.begin());
@@ -198,7 +202,7 @@ void RenderContactsTab(App* app)
if (!canSubmit) ImGui::EndDisabled();
ImGui::SameLine(0, actionGap);
if (material::StyledButton(TR("cancel"), ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
if (material::TactileButton(TR("cancel"), ImVec2(actionW, 0), S.resolveFont(actionBtn.font))) {
*open = false;
}
@@ -212,7 +216,7 @@ void RenderContactsTab(App* app)
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);
// Toolbar
if (material::StyledButton(TR("address_book_add_new"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
if (material::TactileButton(TR("address_book_add_new"), ImVec2(0,0), S.resolveFont(actionBtn.font))) {
s_show_add_dialog = true;
s_focus_edit_field = true;
clearEditFields();
@@ -222,7 +226,7 @@ void RenderContactsTab(App* app)
if (!has_selection) ImGui::BeginDisabled();
if (material::StyledButton(TR("edit"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
if (material::TactileButton(TR("edit"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
openEdit();
ImGui::SameLine();
@@ -230,12 +234,12 @@ void RenderContactsTab(App* app)
// Delete — relabel to a visible confirm prompt while armed (not just a toast).
bool armed = has_selection && s_confirm_delete_idx == s_selected_index;
const char* delLabel = armed ? TR("address_book_confirm_delete") : TR("delete");
if (material::StyledButton(delLabel, ImVec2(0,0), S.resolveFont(actionBtn.font)))
if (material::TactileButton(delLabel, ImVec2(0,0), S.resolveFont(actionBtn.font)))
doDelete();
ImGui::SameLine();
if (material::StyledButton(TR("copy_address"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
if (material::TactileButton(TR("copy_address"), ImVec2(0,0), S.resolveFont(actionBtn.font)))
doCopy();
if (!has_selection) ImGui::EndDisabled();

View File

@@ -53,11 +53,13 @@ void ValidateAddressDialog::render(App* app)
auto lbl = S.label("dialogs.validate-address", "label");
auto closeBtn = S.button("dialogs.validate-address", "close-button");
if (material::BeginOverlayDialog(TR("validate_title"), &s_open, win.width, 0.94f)) {
material::OverlayDialogSpec ov;
ov.title = TR("validate_title"); ov.p_open = &s_open;
ov.style = material::OverlayStyle::BlurFloat;
ov.cardWidth = win.width; ov.idSuffix = "validateaddr";
if (material::BeginOverlayDialog(ov)) {
ImGui::TextWrapped("%s", TR("validate_description"));
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// Address input
@@ -74,7 +76,7 @@ void ValidateAddressDialog::render(App* app)
ImGui::BeginDisabled();
}
if (material::StyledButton(TR("validate_btn"), ImVec2(valBtn.width, 0), S.resolveFont(valBtn.font)) || (enter_pressed && can_validate)) {
if (material::TactileButton(TR("validate_btn"), ImVec2(valBtn.width, 0), S.resolveFont(valBtn.font)) || (enter_pressed && can_validate)) {
s_validating = true;
s_validated = false;
s_error_message.clear();
@@ -150,7 +152,7 @@ void ValidateAddressDialog::render(App* app)
ImGui::SameLine();
if (material::StyledButton(TR("paste"), ImVec2(pasteBtn.width, 0), S.resolveFont(pasteBtn.font))) {
if (material::TactileButton(TR("paste"), ImVec2(pasteBtn.width, 0), S.resolveFont(pasteBtn.font))) {
const char* clipboard = ImGui::GetClipboardText();
if (clipboard) {
strncpy(s_address_input, clipboard, sizeof(s_address_input) - 1);
@@ -174,7 +176,7 @@ void ValidateAddressDialog::render(App* app)
ImGui::Spacing();
if (!s_error_message.empty()) {
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Error: %s", s_error_message.c_str());
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), TR("error_format"), s_error_message.c_str());
} else {
// Valid/Invalid indicator
ImGui::Text("%s", TR("validate_status"));
@@ -210,7 +212,7 @@ void ValidateAddressDialog::render(App* app)
// Close button at bottom
float button_width = closeBtn.width;
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - button_width) / 2.0f);
if (material::StyledButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
if (material::TactileButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
s_open = false;
}
material::EndOverlayDialog();