fix(balance): disambiguate address drag — edge to reorder, centre to transfer
The address list supported two drag gestures that collided: dragging a row onto another transferred funds, dragging into a gap reordered. Since rows are contiguous, a reorder-drag was almost always over another row, so it triggered a fund transfer instead of reordering. Disambiguate by WHERE on the target row the drag is released (user's suggestion): the top/bottom ~30% edge bands = reorder (an insertion line is shown), the centre = transfer (the row highlights). A zero-balance row or an off-row drop always reorders. Tooltip and i18n hint updated to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -139,6 +139,9 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
static float s_dragStartY = 0.0f; // mouse Y at drag start
|
static float s_dragStartY = 0.0f; // mouse Y at drag start
|
||||||
static bool s_dragActive = false; // drag distance threshold passed
|
static bool s_dragActive = false; // drag distance threshold passed
|
||||||
static int s_dropTargetIdx = -1; // row hovered during drag
|
static int s_dropTargetIdx = -1; // row hovered during drag
|
||||||
|
static int s_dropMode = 0; // 0 = transfer (released over a row's CENTER), 1 = reorder
|
||||||
|
// (released over a row's top/bottom EDGE). Disambiguates the
|
||||||
|
// two drag gestures by WHERE on the target row you release.
|
||||||
|
|
||||||
// Copy feedback
|
// Copy feedback
|
||||||
static int s_copiedRow = -1;
|
static int s_copiedRow = -1;
|
||||||
@@ -314,38 +317,45 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
s_copiedRow = s_dragIdx;
|
s_copiedRow = s_dragIdx;
|
||||||
s_copiedTime = (float)ImGui::GetTime();
|
s_copiedTime = (float)ImGui::GetTime();
|
||||||
}
|
}
|
||||||
// Drop
|
// Drop. Released over a row's CENTER (transfer mode) and that row has funds -> transfer;
|
||||||
if (s_dragActive && s_dropTargetIdx >= 0 && s_dropTargetIdx != s_dragIdx) {
|
// released over an EDGE, in a gap, or on a zero-balance row -> reorder.
|
||||||
const auto& srcRow = rows[s_dragIdx];
|
if (s_dragActive) {
|
||||||
const auto& dstRow = rows[s_dropTargetIdx];
|
bool didTransfer = false;
|
||||||
// Transfer: if dropped on another row and drag was active
|
if (s_dropMode == 0 && s_dropTargetIdx >= 0 && s_dropTargetIdx != s_dragIdx &&
|
||||||
if (srcRow.info->balance > 1e-9) {
|
s_dragIdx < (int)rows.size()) {
|
||||||
AddressTransferDialog::TransferInfo ti;
|
const auto& srcRow = rows[s_dragIdx];
|
||||||
ti.fromAddr = srcRow.info->address;
|
const auto& dstRow = rows[s_dropTargetIdx];
|
||||||
ti.toAddr = dstRow.info->address;
|
if (srcRow.info->balance > 1e-9) {
|
||||||
ti.fromBalance = srcRow.info->balance;
|
AddressTransferDialog::TransferInfo ti;
|
||||||
ti.toBalance = dstRow.info->balance;
|
ti.fromAddr = srcRow.info->address;
|
||||||
ti.fromIsZ = srcRow.isZ;
|
ti.toAddr = dstRow.info->address;
|
||||||
ti.toIsZ = dstRow.isZ;
|
ti.fromBalance = srcRow.info->balance;
|
||||||
AddressTransferDialog::show(app, ti);
|
ti.toBalance = dstRow.info->balance;
|
||||||
|
ti.fromIsZ = srcRow.isZ;
|
||||||
|
ti.toIsZ = dstRow.isZ;
|
||||||
|
AddressTransferDialog::show(app, ti);
|
||||||
|
didTransfer = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (s_dragActive && s_dropTargetIdx < 0) {
|
if (!didTransfer) {
|
||||||
// Reorder: dropped in gap — compute insert position from mouseY
|
// Reorder — compute insert position from mouseY.
|
||||||
int insertIdx = 0;
|
int insertIdx = 0;
|
||||||
for (int i = 0; i < (int)rows.size(); ++i) {
|
for (int i = 0; i < (int)rows.size(); ++i) {
|
||||||
if (mousePos.y > rowY[i] + rowH * 0.5f) insertIdx = i + 1;
|
if (mousePos.y > rowY[i] + rowH * 0.5f) insertIdx = i + 1;
|
||||||
}
|
}
|
||||||
if (insertIdx != s_dragIdx && insertIdx != s_dragIdx + 1) {
|
if (insertIdx != s_dragIdx && insertIdx != s_dragIdx + 1) {
|
||||||
int targetIdx = (insertIdx > s_dragIdx) ? insertIdx - 1 : insertIdx;
|
int targetIdx = (insertIdx > s_dragIdx) ? insertIdx - 1 : insertIdx;
|
||||||
if (targetIdx >= 0 && targetIdx < (int)rows.size()) {
|
if (targetIdx >= 0 && targetIdx < (int)rows.size() && s_dragIdx < (int)rows.size()) {
|
||||||
app->swapAddressOrder(rows[s_dragIdx].info->address,
|
app->swapAddressOrder(rows[s_dragIdx].info->address,
|
||||||
rows[targetIdx].info->address);
|
rows[targetIdx].info->address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s_dragIdx = -1;
|
s_dragIdx = -1;
|
||||||
s_dragActive = false;
|
s_dragActive = false;
|
||||||
s_dropTargetIdx = -1;
|
s_dropTargetIdx = -1;
|
||||||
|
s_dropMode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- PASS 2: Render rows ----
|
// ---- PASS 2: Render rows ----
|
||||||
@@ -371,8 +381,9 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
ImVec2(rowEnd.x + 2*dp, rowEnd.y + 2*dp),
|
ImVec2(rowEnd.x + 2*dp, rowEnd.y + 2*dp),
|
||||||
IM_COL32(0, 0, 0, 30), 6.0f * dp);
|
IM_COL32(0, 0, 0, 30), 6.0f * dp);
|
||||||
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(30, 30, 40, 120), 4.0f * dp);
|
dl->AddRectFilled(rowPos, rowEnd, IM_COL32(30, 30, 40, 120), 4.0f * dp);
|
||||||
// Tooltip following cursor — show transfer intent if over a target row
|
// Tooltip following cursor — reflect the gesture: centre of a row = transfer to it,
|
||||||
if (s_dropTargetIdx >= 0 && s_dropTargetIdx < (int)rows.size()) {
|
// edge of a row = reorder (move here).
|
||||||
|
if (s_dropTargetIdx >= 0 && s_dropTargetIdx < (int)rows.size() && s_dropMode == 0) {
|
||||||
const auto& target = rows[s_dropTargetIdx];
|
const auto& target = rows[s_dropTargetIdx];
|
||||||
ImGui::SetTooltip("%s\n%s\n\n%s %s",
|
ImGui::SetTooltip("%s\n%s\n\n%s %s",
|
||||||
truncateAddress(addr.address, 32).c_str(),
|
truncateAddress(addr.address, 32).c_str(),
|
||||||
@@ -380,9 +391,10 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
TR("transfer_to"),
|
TR("transfer_to"),
|
||||||
truncateAddress(target.info->address, 32).c_str());
|
truncateAddress(target.info->address, 32).c_str());
|
||||||
} else {
|
} else {
|
||||||
ImGui::SetTooltip("%s\n%s",
|
ImGui::SetTooltip("%s\n%s\n\n%s",
|
||||||
truncateAddress(addr.address, 32).c_str(),
|
truncateAddress(addr.address, 32).c_str(),
|
||||||
row.isZ ? TR("shielded") : TR("transparent"));
|
row.isZ ? TR("shielded") : TR("transparent"),
|
||||||
|
TR("address_reorder_hint"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,12 +419,24 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
// Hover effects
|
// Hover effects
|
||||||
bool hovered = !isDragged && material::IsRectHovered(rowPos, rowEnd);
|
bool hovered = !isDragged && material::IsRectHovered(rowPos, rowEnd);
|
||||||
|
|
||||||
// Drop target highlight when dragging another row over this one
|
// Drop target feedback when dragging another row over this one. The cursor's vertical
|
||||||
|
// position WITHIN the row picks the gesture: top/bottom edge = reorder (show an insertion
|
||||||
|
// line), centre = transfer (highlight the whole row).
|
||||||
if (s_dragActive && s_dragIdx != row_idx && material::IsRectHovered(rowPos, rowEnd)) {
|
if (s_dragActive && s_dragIdx != row_idx && material::IsRectHovered(rowPos, rowEnd)) {
|
||||||
s_dropTargetIdx = row_idx;
|
s_dropTargetIdx = row_idx;
|
||||||
ImU32 dropCol = WithAlpha(Primary(), 40);
|
const float relY = rowH > 0.0f ? (mousePos.y - rowPos.y) / rowH : 0.5f;
|
||||||
dl->AddRectFilled(rowPos, rowEnd, dropCol, 4.0f * dp);
|
s_dropMode = (relY < 0.30f || relY > 0.70f) ? 1 : 0;
|
||||||
dl->AddRect(rowPos, rowEnd, WithAlpha(Primary(), 140), 4.0f * dp, 0, 2.0f * dp);
|
if (s_dropMode == 0) {
|
||||||
|
// Transfer: highlight the whole target row.
|
||||||
|
dl->AddRectFilled(rowPos, rowEnd, WithAlpha(Primary(), 40), 4.0f * dp);
|
||||||
|
dl->AddRect(rowPos, rowEnd, WithAlpha(Primary(), 140), 4.0f * dp, 0, 2.0f * dp);
|
||||||
|
} else {
|
||||||
|
// Reorder: draw an insertion line at the near (top or bottom) edge.
|
||||||
|
const float lineY = (relY < 0.5f) ? rowPos.y : rowEnd.y;
|
||||||
|
dl->AddLine(ImVec2(rowPos.x, lineY), ImVec2(rowEnd.x, lineY),
|
||||||
|
WithAlpha(Primary(), 230), 2.5f * dp);
|
||||||
|
dl->AddCircleFilled(ImVec2(rowPos.x, lineY), 3.0f * dp, WithAlpha(Primary(), 230));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hovered && selected_row != row_idx && !s_dragActive) {
|
if (hovered && selected_row != row_idx && !s_dragActive) {
|
||||||
|
|||||||
@@ -484,6 +484,7 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["no_icons_found"] = "No icons match your search.";
|
strings_["no_icons_found"] = "No icons match your search.";
|
||||||
strings_["transfer_funds"] = "Transfer Funds";
|
strings_["transfer_funds"] = "Transfer Funds";
|
||||||
strings_["transfer_to"] = "Transfer to:";
|
strings_["transfer_to"] = "Transfer to:";
|
||||||
|
strings_["address_reorder_hint"] = "Drop on a row's edge to reorder, or its centre to transfer";
|
||||||
strings_["deshielding_warning"] = "Warning: This will de-shield funds from a private (Z) address to a transparent (T) address.";
|
strings_["deshielding_warning"] = "Warning: This will de-shield funds from a private (Z) address to a transparent (T) address.";
|
||||||
strings_["shielding_notice"] = "Note: This will shield funds from a transparent (T) address to a private (Z) address.";
|
strings_["shielding_notice"] = "Note: This will shield funds from a transparent (T) address to a private (Z) address.";
|
||||||
strings_["result_preview"] = "Result Preview";
|
strings_["result_preview"] = "Result Preview";
|
||||||
|
|||||||
Reference in New Issue
Block a user