notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 9033383a290426747963f5566fcb4eaa5f07c167
parent c6045279ddf8a99fe1ea1e7f76fd8f938361dffd
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 10 Jun 2025 07:23:55 -0700

add input context menu helper

We are going to want this in more places

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
MCargo.lock | 3+++
Mcrates/notedeck_columns/src/accounts/mod.rs | 4+++-
Mcrates/notedeck_columns/src/login_manager.rs | 4++++
Mcrates/notedeck_columns/src/nav.rs | 1+
Mcrates/notedeck_columns/src/ui/account_login_view.rs | 19++++++++++---------
Mcrates/notedeck_columns/src/ui/search/mod.rs | 23+++++++----------------
Mcrates/notedeck_ui/Cargo.toml | 6++++--
Acrates/notedeck_ui/src/context_menu.rs | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/notedeck_ui/src/lib.rs | 1+
9 files changed, 82 insertions(+), 28 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -3300,6 +3300,7 @@ dependencies = [ "bitflags 2.9.0", "blurhash", "egui", + "egui-winit", "egui_extras", "ehttp", "enostr", @@ -3309,6 +3310,8 @@ dependencies = [ "notedeck", "poll-promise", "profiling", + "strum", + "strum_macros", "tokio", "tracing", ] diff --git a/crates/notedeck_columns/src/accounts/mod.rs b/crates/notedeck_columns/src/accounts/mod.rs @@ -15,6 +15,7 @@ use crate::{ accounts::{AccountsView, AccountsViewResponse}, }, }; +use egui_winit::clipboard::Clipboard; use tracing::info; mod route; @@ -31,6 +32,7 @@ pub fn render_accounts_route( accounts: &mut Accounts, decks: &mut DecksCache, login_state: &mut AcquireKeyState, + clipboard: &mut Clipboard, route: AccountsRoute, ) -> AddAccountAction { let resp = match route { @@ -39,7 +41,7 @@ pub fn render_accounts_route( .inner .map(AccountsRouteResponse::Accounts), - AccountsRoute::AddAccount => AccountLoginView::new(login_state) + AccountsRoute::AddAccount => AccountLoginView::new(login_state, clipboard) .ui(ui) .inner .map(AccountsRouteResponse::AddAccount), diff --git a/crates/notedeck_columns/src/login_manager.rs b/crates/notedeck_columns/src/login_manager.rs @@ -28,6 +28,10 @@ impl<'a> AcquireKeyState { textedit_closure(&mut self.desired_key) } + pub fn input_buffer(&mut self) -> &mut String { + &mut self.desired_key + } + /// User pressed the 'acquire' button pub fn apply_acquire(&'a mut self) { let new_promise = match &self.promise_query { diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs @@ -423,6 +423,7 @@ fn render_nav_body( ctx.accounts, &mut app.decks_cache, &mut app.view_state.login, + ctx.clipboard, *amr, ); let txn = Transaction::new(ctx.ndb).expect("txn"); diff --git a/crates/notedeck_columns/src/ui/account_login_view.rs b/crates/notedeck_columns/src/ui/account_login_view.rs @@ -4,11 +4,14 @@ use egui::{ Align, Button, Color32, Frame, Image, InnerResponse, Margin, RichText, TextBuffer, Vec2, }; use egui::{Layout, TextEdit}; +use egui_winit::clipboard::Clipboard; use enostr::Keypair; use notedeck::{fonts::get_font_size, AppAction, NotedeckTextStyle}; +use notedeck_ui::context_menu::{input_context, PasteBehavior}; pub struct AccountLoginView<'a> { manager: &'a mut AcquireKeyState, + clipboard: &'a mut Clipboard, } pub enum AccountLoginResponse { @@ -17,8 +20,8 @@ pub enum AccountLoginResponse { } impl<'a> AccountLoginView<'a> { - pub fn new(state: &'a mut AcquireKeyState) -> Self { - AccountLoginView { manager: state } + pub fn new(manager: &'a mut AcquireKeyState, clipboard: &'a mut Clipboard) -> Self { + AccountLoginView { manager, clipboard } } pub fn ui(&mut self, ui: &mut egui::Ui) -> InnerResponse<Option<AccountLoginResponse>> { @@ -42,7 +45,9 @@ impl<'a> AccountLoginView<'a> { let button_width = 32.0; let text_edit_width = available_width - button_width; - ui.add_sized([text_edit_width, 40.0], login_textedit(self.manager)); + let textedit_resp = ui.add_sized([text_edit_width, 40.0], login_textedit(self.manager)); + input_context(&textedit_resp, self.clipboard, self.manager.input_buffer(), PasteBehavior::Clear); + if eye_button(ui, self.manager.password_visible()).clicked() { self.manager.toggle_password_visibility(); } @@ -154,12 +159,8 @@ mod preview { } impl App for AccountLoginPreview { - fn update( - &mut self, - _app_ctx: &mut AppContext<'_>, - ui: &mut egui::Ui, - ) -> Option<AppAction> { - AccountLoginView::new(&mut self.manager).ui(ui); + fn update(&mut self, ctx: &mut AppContext<'_>, ui: &mut egui::Ui) -> Option<AppAction> { + AccountLoginView::new(&mut self.manager, ctx.clipboard).ui(ui); None } diff --git a/crates/notedeck_columns/src/ui/search/mod.rs b/crates/notedeck_columns/src/ui/search/mod.rs @@ -6,7 +6,12 @@ use crate::{timeline::TimelineTab, ui::timeline::TimelineTabView}; use egui_winit::clipboard::Clipboard; use nostrdb::{Filter, Ndb, Transaction}; use notedeck::{MuteFun, NoteAction, NoteContext, NoteRef}; -use notedeck_ui::{icons::search_icon, jobs::JobsCache, padding, NoteOptions}; +use notedeck_ui::{ + context_menu::{input_context, PasteBehavior}, + icons::search_icon, + jobs::JobsCache, + padding, NoteOptions, +}; use std::time::{Duration, Instant}; use tracing::{error, info, warn}; @@ -296,21 +301,7 @@ fn search_box( .frame(false), ); - response.context_menu(|ui| { - if ui.button("paste").clicked() { - if let Some(text) = clipboard.get() { - input.clear(); - input.push_str(&text); - } - } - }); - - if response.middle_clicked() { - if let Some(text) = clipboard.get() { - input.clear(); - input.push_str(&text); - } - } + input_context(&response, clipboard, input, PasteBehavior::Append); let mut requested_focus = false; if focus_state == FocusState::ShouldRequestFocus { diff --git a/crates/notedeck_ui/Cargo.toml b/crates/notedeck_ui/Cargo.toml @@ -6,6 +6,9 @@ version.workspace = true [dependencies] egui = { workspace = true } egui_extras = { workspace = true } +egui-winit = { workspace = true } +strum_macros = { workspace = true } +strum = { workspace = true } ehttp = { workspace = true } nostrdb = { workspace = true } tracing = { workspace = true } @@ -18,4 +21,4 @@ bitflags = { workspace = true } enostr = { workspace = true } hashbrown = { workspace = true } -blurhash = "0.2.3" -\ No newline at end of file +blurhash = "0.2.3" diff --git a/crates/notedeck_ui/src/context_menu.rs b/crates/notedeck_ui/src/context_menu.rs @@ -0,0 +1,49 @@ +/// Context menu helpers (paste, etc) +use egui_winit::clipboard::Clipboard; + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum PasteBehavior { + Clear, + Append, +} + +fn handle_paste(clipboard: &mut Clipboard, input: &mut String, paste_behavior: PasteBehavior) { + if let Some(text) = clipboard.get() { + // if called with clearing_input_context, then we clear before + // we paste. Useful for certain fields like passwords, etc + match paste_behavior { + PasteBehavior::Clear => input.clear(), + PasteBehavior::Append => {} + } + input.push_str(&text); + } +} + +pub fn input_context( + response: &egui::Response, + clipboard: &mut Clipboard, + input: &mut String, + paste_behavior: PasteBehavior, +) { + response.context_menu(|ui| { + if ui.button("Paste").clicked() { + handle_paste(clipboard, input, paste_behavior); + ui.close_menu(); + } + + if ui.button("Copy").clicked() { + clipboard.set_text(input.to_owned()); + ui.close_menu(); + } + + if ui.button("Cut").clicked() { + clipboard.set_text(input.to_owned()); + input.clear(); + ui.close_menu(); + } + }); + + if response.middle_clicked() { + handle_paste(clipboard, input, paste_behavior) + } +} diff --git a/crates/notedeck_ui/src/lib.rs b/crates/notedeck_ui/src/lib.rs @@ -3,6 +3,7 @@ pub mod blur; pub mod colors; pub mod constants; pub mod contacts; +pub mod context_menu; pub mod gif; pub mod icons; pub mod images;