commit 173972f920c130351455a9abe0252c9d5349f021
parent 31ec21ea02f21c546cb1ba55359363408003911e
Author: kernelkind <kernelkind@gmail.com>
Date: Sun, 27 Jul 2025 10:10:24 -0400
mentions: don't lose focus after select mention
Closes: https://github.com/damus-io/notedeck/issues/728
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
2 files changed, 62 insertions(+), 14 deletions(-)
diff --git a/crates/notedeck_columns/src/post.rs b/crates/notedeck_columns/src/post.rs
@@ -1,4 +1,8 @@
-use egui::{text::LayoutJob, TextBuffer, TextFormat};
+use egui::{
+ text::{CCursor, CCursorRange, LayoutJob},
+ text_edit::TextEditOutput,
+ TextBuffer, TextEdit, TextFormat,
+};
use enostr::{FullKeypair, Pubkey};
use nostrdb::{Note, NoteBuilder, NoteReply};
use std::{
@@ -270,6 +274,36 @@ impl Default for PostBuffer {
}
}
+/// New cursor index (indexed by characters) after operation is performed
+#[must_use = "must call MentionSelectedResponse::process"]
+pub struct MentionSelectedResponse {
+ pub next_cursor_index: usize,
+}
+
+impl MentionSelectedResponse {
+ pub fn process(&self, ctx: &egui::Context, text_edit_output: &TextEditOutput) {
+ let text_edit_id = text_edit_output.response.id;
+ let Some(mut before_state) = TextEdit::load_state(ctx, text_edit_id) else {
+ return;
+ };
+
+ let mut new_cursor = text_edit_output
+ .galley
+ .from_ccursor(CCursor::new(self.next_cursor_index));
+ new_cursor.ccursor.prefer_next_row = true;
+
+ before_state
+ .cursor
+ .set_char_range(Some(CCursorRange::one(CCursor::new(
+ self.next_cursor_index,
+ ))));
+
+ ctx.memory_mut(|mem| mem.request_focus(text_edit_id));
+
+ TextEdit::store_state(ctx, text_edit_id, before_state);
+ }
+}
+
impl PostBuffer {
pub fn get_new_mentions_key(&mut self) -> usize {
let prev = self.mentions_key;
@@ -319,15 +353,19 @@ impl PostBuffer {
mention_key: usize,
full_name: &str,
pk: Pubkey,
- ) {
- if let Some(info) = self.mentions.get(&mention_key) {
- let text_start_index = info.start_index + 1;
- self.delete_char_range(text_start_index..info.end_index);
- self.insert_text(full_name, text_start_index);
- self.select_full_mention(mention_key, pk);
- } else {
+ ) -> Option<MentionSelectedResponse> {
+ let Some(info) = self.mentions.get(&mention_key) else {
error!("Error selecting mention for index: {mention_key}. Have the following mentions: {:?}", self.mentions);
- }
+ return None;
+ };
+ let text_start_index = info.start_index + 1; // increment by one to exclude the mention indicator, '@'
+ self.delete_char_range(text_start_index..info.end_index);
+ let text_chars_inserted = self.insert_text(full_name, text_start_index);
+ self.select_full_mention(mention_key, pk);
+
+ Some(MentionSelectedResponse {
+ next_cursor_index: text_start_index + text_chars_inserted,
+ })
}
pub fn delete_mention(&mut self, mention_key: usize) {
diff --git a/crates/notedeck_columns/src/ui/note/post.rs b/crates/notedeck_columns/src/ui/note/post.rs
@@ -218,6 +218,7 @@ impl<'a, 'd> PostView<'a, 'd> {
out.response
}
+ // Displays the mention picker and handles when one is selected.
fn show_mention_hints(
&mut self,
txn: &nostrdb::Transaction,
@@ -281,17 +282,22 @@ impl<'a, 'd> PostView<'a, 'd> {
)
.show_in_rect(hint_rect, ui);
+ let mut selection_made = None;
match resp {
ui::mentions_picker::MentionPickerResponse::SelectResult(selection) => {
if let Some(hint_index) = selection {
if let Some(pk) = res.get(hint_index) {
let record = self.note_context.ndb.get_profile_by_pubkey(txn, pk);
- self.draft.buffer.select_mention_and_replace_name(
- mention.index,
- get_display_name(record.ok().as_ref()).name(),
- Pubkey::new(**pk),
- );
+ if let Some(made_selection) =
+ self.draft.buffer.select_mention_and_replace_name(
+ mention.index,
+ get_display_name(record.ok().as_ref()).name(),
+ Pubkey::new(**pk),
+ )
+ {
+ selection_made = Some(made_selection);
+ }
self.draft.cur_mention_hint = None;
}
}
@@ -301,6 +307,10 @@ impl<'a, 'd> PostView<'a, 'd> {
self.draft.buffer.delete_mention(mention.index)
}
}
+
+ if let Some(selection) = selection_made {
+ selection.process(ui.ctx(), textedit_output);
+ }
}
fn focused(&self, ui: &egui::Ui) -> bool {