Skip to content

fix(ui): block click pass-through on modal overlays#732

Open
thepastaclaw wants to merge 4 commits intodashpay:v1.0-devfrom
thepastaclaw:fix/modal-overlay-click-through
Open

fix(ui): block click pass-through on modal overlays#732
thepastaclaw wants to merge 4 commits intodashpay:v1.0-devfrom
thepastaclaw:fix/modal-overlay-click-through

Conversation

@thepastaclaw
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw commented Mar 11, 2026

Issue

Closes #704

Summary

All modal overlays used painter.rect_filled() via layer_painter(Order::Background) which is purely visual — clicks pass through the overlay to background widgets in the same frame.

Changes

  • New src/ui/components/modal_overlay.rs — centralized draw_modal_overlay(ctx, id) helper that uses egui::Area with allocate_response(Sense::click_and_drag()) to both paint the semi-transparent overlay and consume all pointer events
  • Updated 11 call sites across confirmation dialogs, info popups, wallet unlock, profile screen, identities screen, wallet screens, token screens, and asset lock detail to use the shared helper
  • Removed the duplicate WalletsBalancesScreen::draw_modal_overlay method from dialogs.rs

Net: -29 lines (59 added, 88 removed across 13 files).

Validation

  • cargo check — compiles clean
  • cargo clippy -- -D warnings — no warnings
  • cargo fmt --all -- --check — formatted
  • Verified all 17 modal_overlay() references now go through the centralized helper (grep confirms no remaining inline rect_filled + modal_overlay patterns)

Summary by CodeRabbit

  • Refactor
    • Standardized modal backdrops across dialogs, popups, and modals so overlays look and behave consistently (including blocking clicks behind them).
  • New Features
    • Introduced a centralized overlay renderer and migrated existing modals to use it for uniform visuals and interaction.
  • Bug Fixes
    • Wallet unlock flow updated to respect feature availability when initializing optional wallet components.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a250c4bc-a147-4918-a8d7-b713d5c0a7d7

📥 Commits

Reviewing files that changed from the base of the PR and between 7957db7 and 547e854.

📒 Files selected for processing (13)
  • src/context/wallet_lifecycle.rs
  • src/ui/components/confirmation_dialog.rs
  • src/ui/components/info_popup.rs
  • src/ui/components/selection_dialog.rs
  • src/ui/components/wallet_unlock_popup.rs
  • src/ui/dashpay/profile_screen.rs
  • src/ui/helpers.rs
  • src/ui/identities/identities_screen.rs
  • src/ui/tokens/tokens_screen/data_contract_json_pop_up.rs
  • src/ui/tokens/tokens_screen/my_tokens.rs
  • src/ui/wallets/add_new_wallet_screen.rs
  • src/ui/wallets/asset_lock_detail_screen.rs
  • src/ui/wallets/wallets_screen/dialogs.rs
✅ Files skipped from review due to trivial changes (2)
  • src/ui/components/selection_dialog.rs
  • src/ui/wallets/wallets_screen/dialogs.rs
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/ui/tokens/tokens_screen/my_tokens.rs
  • src/ui/components/confirmation_dialog.rs
  • src/ui/identities/identities_screen.rs
  • src/ui/tokens/tokens_screen/data_contract_json_pop_up.rs
  • src/ui/components/info_popup.rs
  • src/ui/wallets/add_new_wallet_screen.rs
  • src/ui/components/wallet_unlock_popup.rs

📝 Walkthrough

Walkthrough

Centralizes modal overlay rendering by adding pub fn draw_modal_overlay(ctx, id) in src/ui/helpers.rs and replaces inline background-layer rect_filled overlays across many dialogs/popups so the overlay both paints and consumes pointer events. Also switches shielded-wallet availability check to FeatureGate::Shielded.is_available(self) in src/context/wallet_lifecycle.rs.

Changes

Cohort / File(s) Summary
Dialog & Popup Components
src/ui/components/confirmation_dialog.rs, src/ui/components/info_popup.rs, src/ui/components/selection_dialog.rs, src/ui/components/wallet_unlock_popup.rs
Replaced inline rect_filled overlay painting with draw_modal_overlay(ui.ctx(), "<id>") calls; overlay drawing delegated to helper which now consumes pointer events.
Screen-level Popups
src/ui/dashpay/profile_screen.rs, src/ui/identities/identities_screen.rs, src/ui/tokens/tokens_screen/data_contract_json_pop_up.rs, src/ui/tokens/tokens_screen/my_tokens.rs, src/ui/wallets/add_new_wallet_screen.rs, src/ui/wallets/asset_lock_detail_screen.rs
Removed per-file painter overlay code and delegate overlay rendering + click-blocking to draw_modal_overlay(ctx, "<id>").
Wallets dialogs refactor
src/ui/wallets/wallets_screen/dialogs.rs
Removed WalletsBalancesScreen::draw_modal_overlay(...) method and updated callers to use crate::ui::helpers::draw_modal_overlay.
Shared helper
src/ui/helpers.rs
Added pub fn draw_modal_overlay(ctx: &egui::Context, id: &str) that renders a full-screen semi-transparent overlay and allocates an interactable response (consumes clicks); adjusted theme imports.
Wallet lifecycle feature gating
src/context/wallet_lifecycle.rs
Replaced self.supports_shielded() with FeatureGate::Shielded.is_available(self) when deciding to initialize shielded wallet on unlock.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 I hopped behind each modal pane,

Tucked shadows in and stopped the rain,
No sneaky clicks will prance or dart,
One gentle blanket keeps them apart.
Hooray — a tidier UI heart! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning One change appears out of scope: src/context/wallet_lifecycle.rs replaces supports_shielded() with FeatureGate::Shielded.is_available(), which is unrelated to the modal overlay fix objective. Remove the wallet_lifecycle.rs change or move it to a separate PR focused on feature-gating refactoring, as it is not related to the modal overlay click-through issue #704.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately reflects the main objective: centralizing and fixing modal overlay click-pass-through behavior across 11 UI components.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #704: centralizes overlay painting in draw_modal_overlay helper, uses egui::Area with allocate_response(Sense::click_and_drag()) to consume pointer events, and updates all 11 affected components.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/modal-overlay-click-through

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ui/tokens/tokens_screen/my_tokens.rs (1)

177-216: ⚠️ Potential issue | 🟠 Major

Token Configuration Details still has click-through dismissal.

This popup still closes on outside clicks without drawing the shared overlay first, so the same click can also hit the token list underneath. That leaves the original pass-through bug reproducible on this screen.

🛠️ Proposed fix
         if let Some(token_info) = self.all_known_tokens.get(&token_id).cloned() {
             let mut is_open = true;
             let mut close_popup = false;
             let dark_mode = ui.ctx().style().visuals.dark_mode;
 
+            draw_modal_overlay(ui.ctx(), "token_info_popup_overlay");
+
             let window_response = egui::Window::new("Token Configuration Details")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/tokens/tokens_screen/my_tokens.rs` around lines 177 - 216, The popup
still allows click-through because the shared modal overlay isn't painted before
the window is shown and the outside-click check runs; to fix, when
self.show_token_info_popup.is_some() (the same condition that drives Token
Configuration Details / is_open / close_popup), paint the modal overlay on the
UI context before creating/showing the egui::Window (i.e. before calling
egui::Window::new(...).show) so the overlay consumes the outside click; you can
do this by painting a fullscreen translucent rect via ui.ctx().layer_painter or
an appropriate LayerId/screen rect paint call using ui.ctx().input().screen_rect
(or ctx.screen_rect()) and DashColors::overlay(dark_mode) so
clicked_outside_window(ui.ctx(), wr.response.rect) no longer allows the original
click to pass through to the token list underneath.
🧹 Nitpick comments (1)
src/ui/components/modal_overlay.rs (1)

9-21: Add a regression test for the shared overlay path.

This helper is now the single choke point for the pass-through fix. A UI test that opens a modal, clicks outside it, and asserts the background widget does not fire would protect every call site that now depends on this behavior.

As per coding guidelines, "UI integration tests should be located in tests/kittest/."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/components/modal_overlay.rs` around lines 9 - 21, Add a UI integration
test under tests/kittest that exercises the shared overlay path by opening a
modal that uses draw_modal_overlay (ensure the code path uses
DashColors::modal_overlay and ui.allocate_response with
egui::Sense::click_and_drag), simulate a click outside the modal, and assert the
background widget's click handler was not invoked; the test should construct the
UI to call draw_modal_overlay, trigger a pointer event outside the modal bounds,
and verify that only the modal consumed the event (i.e., the background widget's
state/callback was unchanged) to prevent regressions for the pass-through fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/components/selection_dialog.rs`:
- Around line 180-181: The shared modal overlay used by selection_dialog (called
via draw_modal_overlay) is drawn with egui::Order::Background which doesn't
block hit-testing for higher-order windows; update the modal overlay helper in
modal_overlay.rs to use egui::Order::Foreground (matching egui::Modal) so it
reliably blocks interactions, and ensure the overlay still fills
ctx.content_rect() and uses the same response/hit area logic as before (preserve
the existing draw_modal_overlay function name and its call sites like in
selection_dialog.rs).

---

Outside diff comments:
In `@src/ui/tokens/tokens_screen/my_tokens.rs`:
- Around line 177-216: The popup still allows click-through because the shared
modal overlay isn't painted before the window is shown and the outside-click
check runs; to fix, when self.show_token_info_popup.is_some() (the same
condition that drives Token Configuration Details / is_open / close_popup),
paint the modal overlay on the UI context before creating/showing the
egui::Window (i.e. before calling egui::Window::new(...).show) so the overlay
consumes the outside click; you can do this by painting a fullscreen translucent
rect via ui.ctx().layer_painter or an appropriate LayerId/screen rect paint call
using ui.ctx().input().screen_rect (or ctx.screen_rect()) and
DashColors::overlay(dark_mode) so clicked_outside_window(ui.ctx(),
wr.response.rect) no longer allows the original click to pass through to the
token list underneath.

---

Nitpick comments:
In `@src/ui/components/modal_overlay.rs`:
- Around line 9-21: Add a UI integration test under tests/kittest that exercises
the shared overlay path by opening a modal that uses draw_modal_overlay (ensure
the code path uses DashColors::modal_overlay and ui.allocate_response with
egui::Sense::click_and_drag), simulate a click outside the modal, and assert the
background widget's click handler was not invoked; the test should construct the
UI to call draw_modal_overlay, trigger a pointer event outside the modal bounds,
and verify that only the modal consumed the event (i.e., the background widget's
state/callback was unchanged) to prevent regressions for the pass-through fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 133d707e-3345-422e-a436-1c434a3d5cfa

📥 Commits

Reviewing files that changed from the base of the PR and between 91abb2c and 58eaa7a.

📒 Files selected for processing (13)
  • src/ui/components/confirmation_dialog.rs
  • src/ui/components/info_popup.rs
  • src/ui/components/mod.rs
  • src/ui/components/modal_overlay.rs
  • src/ui/components/selection_dialog.rs
  • src/ui/components/wallet_unlock_popup.rs
  • src/ui/dashpay/profile_screen.rs
  • src/ui/identities/identities_screen.rs
  • src/ui/tokens/tokens_screen/data_contract_json_pop_up.rs
  • src/ui/tokens/tokens_screen/my_tokens.rs
  • src/ui/wallets/add_new_wallet_screen.rs
  • src/ui/wallets/asset_lock_detail_screen.rs
  • src/ui/wallets/wallets_screen/dialogs.rs

Comment thread src/ui/components/selection_dialog.rs
@lklimek lklimek added the human review This PR is ready for human review. label Mar 15, 2026
@lklimek lklimek self-requested a review March 15, 2026 21:20
@lklimek
Copy link
Copy Markdown
Contributor

lklimek commented Mar 16, 2026

it doesn't work, when on Wallet I click "receive", the overlay is above both the main window and dialog. Expected: only below dialog.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@lklimek Good catch — the overlay was using Order::Foreground which sits above the dialog windows (Order::Middle), so it blocked the dialog itself. Fixed in c7ed35e: reverted to Order::Background. The Area with allocate_response(Sense::click_and_drag()) still blocks click-through to background widgets because within the same order, the later-drawn overlay Area wins hit-testing over the earlier-drawn panel layers.

Added doc comments explaining the ordering rationale and warning against Order::Foreground.

Comment thread src/ui/components/modal_overlay.rs Outdated
@lklimek lklimek marked this pull request as ready for review March 16, 2026 12:46
@lklimek lklimek enabled auto-merge (squash) March 16, 2026 12:46
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lklimek
Copy link
Copy Markdown
Contributor

lklimek commented Apr 1, 2026

claw resolve conflicts

Centralize modal overlay painting into a shared helper that both
renders the semi-transparent background and consumes pointer events,
preventing clicks from reaching background widgets.

Uses egui::Area with allocate_response(Sense::click_and_drag()) to
intercept all pointer events on the overlay rect.

Closes dashpay#704
- Use Order::Foreground instead of Order::Background for the modal
  overlay Area, matching egui's own Modal implementation. Higher-order
  layers win hit-testing, so Background couldn't reliably block
  interactions with windows/popups above it.

- Add draw_modal_overlay() call before Token Configuration Details
  popup in my_tokens.rs, which was missing the shared overlay and
  still allowed click-through to the token list underneath.
Order::Foreground places the overlay above egui::Window dialogs
(which default to Order::Middle), blocking interaction with the
dialog itself. This was reported by lklimek: clicking 'receive'
on the Wallet screen showed the overlay above both the background
and the dialog.

Revert to Order::Background — the Area with allocate_response
(Sense::click_and_drag) still blocks click-through to background
panel widgets because, within the same order, this later-drawn
Area wins hit-testing over earlier-drawn panel layers.

Add doc comments explaining the ordering rationale and warning
against Order::Foreground.
auto-merge was automatically disabled April 1, 2026 11:30

Head branch was pushed to by a user without write access

@thepastaclaw thepastaclaw force-pushed the fix/modal-overlay-click-through branch from 5d5b411 to 7957db7 Compare April 1, 2026 11:30
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

thepastaclaw commented Apr 1, 2026

✅ Review complete (commit 547e854)

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@lklimek Conflicts resolved — rebased onto latest v1.0-dev and force-pushed.

Note: upstream v1.0-dev currently has a pre-existing build failure (supports_shielded() method missing in wallet_lifecycle.rs, introduced in the latest upstream commit). Our changes are unaffected by that.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/ui/components/selection_dialog.rs (1)

189-189: Consider consistent window ordering across dialog components.

SelectionDialog uses Order::Foreground for its window, while InfoPopup and other dialogs use the default Order::Middle. Both work correctly with the Order::Background overlay, but the inconsistency could cause unexpected z-ordering if dialogs ever overlap. Consider aligning all dialogs to use the same window order for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/components/selection_dialog.rs` at line 189, SelectionDialog sets its
window order to egui::Order::Foreground which is inconsistent with InfoPopup and
other dialogs that use the default/egui::Order::Middle; update the window
creation in SelectionDialog (where .order(egui::Order::Foreground) is
called—likely in the SelectionDialog show/build method) to use
egui::Order::Middle (or otherwise match the shared ordering used by InfoPopup)
so all dialog components have consistent z-ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/components/selection_dialog.rs`:
- Line 189: SelectionDialog sets its window order to egui::Order::Foreground
which is inconsistent with InfoPopup and other dialogs that use the
default/egui::Order::Middle; update the window creation in SelectionDialog
(where .order(egui::Order::Foreground) is called—likely in the SelectionDialog
show/build method) to use egui::Order::Middle (or otherwise match the shared
ordering used by InfoPopup) so all dialog components have consistent z-ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6784ec3b-4df1-40ac-9f48-5778a84022ee

📥 Commits

Reviewing files that changed from the base of the PR and between 5d5b411 and 7957db7.

📒 Files selected for processing (12)
  • src/ui/components/confirmation_dialog.rs
  • src/ui/components/info_popup.rs
  • src/ui/components/selection_dialog.rs
  • src/ui/components/wallet_unlock_popup.rs
  • src/ui/dashpay/profile_screen.rs
  • src/ui/helpers.rs
  • src/ui/identities/identities_screen.rs
  • src/ui/tokens/tokens_screen/data_contract_json_pop_up.rs
  • src/ui/tokens/tokens_screen/my_tokens.rs
  • src/ui/wallets/add_new_wallet_screen.rs
  • src/ui/wallets/asset_lock_detail_screen.rs
  • src/ui/wallets/wallets_screen/dialogs.rs
✅ Files skipped from review due to trivial changes (3)
  • src/ui/components/confirmation_dialog.rs
  • src/ui/components/wallet_unlock_popup.rs
  • src/ui/wallets/wallets_screen/dialogs.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/ui/identities/identities_screen.rs
  • src/ui/dashpay/profile_screen.rs
  • src/ui/tokens/tokens_screen/my_tokens.rs
  • src/ui/wallets/add_new_wallet_screen.rs
  • src/ui/tokens/tokens_screen/data_contract_json_pop_up.rs

Per review feedback from lklimek: no need for a new file, move the
function to an existing file. helpers.rs is the best fit since it
already contains the related clicked_outside_window() helper.

- Moved draw_modal_overlay() to src/ui/helpers.rs
- Updated 11 import paths across the codebase
- Removed src/ui/components/modal_overlay.rs
- Removed pub mod modal_overlay from components/mod.rs
@thepastaclaw thepastaclaw force-pushed the fix/modal-overlay-click-through branch from 7957db7 to 547e854 Compare April 1, 2026 12:13
Copy link
Copy Markdown
Collaborator Author

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

PR #732 introduces a draw_modal_overlay() helper that creates an egui::Area with Order::Background and Sense::click_and_drag() to block pointer events from reaching background widgets. It replaces inline overlay painting across 11 callsites. 4 commits: initial fix, CodeRabbit feedback, Order::Background fix, and refactor moving the function to helpers.rs. The wallet_lifecycle.rs change (FeatureGate refactor) is unrelated scope creep but harmless.

Reviewed commit: 547e854

💬 2 nitpick(s)

1 additional finding

💬 nitpick: Unrelated FeatureGate change in wallet_lifecycle.rs

src/context/wallet_lifecycle.rs (line 1)

Commit 547e854 (refactor: move draw_modal_overlay) includes a 3-line change in src/context/wallet_lifecycle.rs replacing self.supports_shielded() with FeatureGate::Shielded.is_available(self). This is unrelated to the modal overlay fix — it's a FeatureGate API migration that was bundled into the refactor commit. The change is correct and harmless, but strictly out of PR scope.

Comment thread src/ui/helpers.rs
@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

@lklimek quick status check from my side: the requested changes on this PR were already addressed in code — the overlay ordering fix landed in c7ed35e6, the helper move to src/ui/helpers.rs landed in fdaac4b7, and 547e8545 was the later conflict-resolution rebase onto current v1.0-dev.

I re-checked the current head and reran verification on 547e8545 just now:

  • cargo check
  • cargo clippy --all-features --all-targets -- -D warnings

I am not making additional code changes here because the only remaining open note was a non-blocking self-review nitpick about explicit .interactable(true) in draw_modal_overlay(), and I am intentionally keeping that for clarity since the helper’s whole job is to consume pointer input.

If you still want anything else changed on top of the current head, I can pick it up — otherwise this should be ready for re-review.

@thepastaclaw
Copy link
Copy Markdown
Collaborator Author

Fresh pass on current head 547e8545:

  • I re-read the actual requested-change review from @lklimek. The concrete request there was to move draw_modal_overlay() out of the new standalone src/ui/components/modal_overlay.rs file and into an existing shared module.
  • Current head already does that: src/ui/components/modal_overlay.rs is gone, and draw_modal_overlay() now lives in src/ui/helpers.rs alongside clicked_outside_window().
  • I revalidated the current head in a clean worktree today:
    • cargo check
    • cargo clippy --all-features --all-targets -- -D warnings

So from my side there is not a new code change to make here — the requested code move is already present on the PR branch. If there is still something blocking on the current head, please point me at the specific current-head issue/thread and I'll pick it up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human review This PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ui): modal overlay does not consume click events, allowing pass-through to background widgets

2 participants