Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
689b6ee
feat(gui): enable partial selection of message text
jlucaso1 Sep 24, 2026
46f05eb
fix(gui): resolve selection build errors
jlucaso1 Sep 24, 2026
a7f1887
fix(gui): retain link presses across repaint
jlucaso1 Sep 24, 2026
a1463df
fix(gui): clear selection only for identity changes
jlucaso1 Sep 24, 2026
8d9619f
fix(gui): keep message selection tied to virtualized identity
jlucaso1 Sep 24, 2026
a793294
fix(gui): pass selection identity to all rich text paths
jlucaso1 Sep 24, 2026
e9210d6
test(gui): focus the text selection copy shortcut
jlucaso1 Sep 24, 2026
fd5d42d
fix(gui): clear stale selections with timeline visibility
jlucaso1 Sep 24, 2026
7b23149
fix(gui): clear selection when settings hides chat
jlucaso1 Sep 24, 2026
2417c74
style(gui): clarify active selection lookup
jlucaso1 Sep 24, 2026
ded2346
fix(gui): handle bidi selection and pointer slop
jlucaso1 Sep 24, 2026
5d1ba36
fix(gui): clear transient selection on link click
jlucaso1 Sep 24, 2026
dec52c3
fix(gui): derive highlights from visual glyph runs
jlucaso1 Sep 24, 2026
dd3f45a
fix(gui): bound wrapped bidi selection glyphs
jlucaso1 Sep 24, 2026
87e7291
fix(gui): import pixel half trait
jlucaso1 Sep 24, 2026
373592e
fix(gui): retain virtualized selection and refine hits
jlucaso1 Sep 24, 2026
a36bd39
fix(gui): remove unnecessary test context mut
jlucaso1 Sep 24, 2026
3174a19
test(gui): cover link end hit testing
jlucaso1 Sep 24, 2026
6a664d6
fix(gui): preserve retained text and shaped glyph bounds
jlucaso1 Sep 24, 2026
3c834c2
fix(gui): retain virtual selection order efficiently
jlucaso1 Sep 24, 2026
15c6809
fix(gui): preserve shaped caret and receipt selections
jlucaso1 Sep 24, 2026
fa5cd84
fix(gui): address shaped caret clippy feedback
jlucaso1 Sep 24, 2026
32ae2db
fix(gui): keep trailing carets within wrapped segments
jlucaso1 Sep 24, 2026
275f625
fix(gui): measure wrapped clusters from shaped glyph positions
jlucaso1 Sep 24, 2026
c25a0e0
fix(gui): correct shaped pixel delta
jlucaso1 Sep 24, 2026
37ed89f
fix(gui): preserve positioned glyph cluster extents
jlucaso1 Sep 24, 2026
6e47e95
fix(gui): preserve RTL trailing cluster advances
jlucaso1 Sep 24, 2026
10aa0de
fix(gui): close selection lifecycle gaps
jlucaso1 Sep 24, 2026
1615266
test(gui): cover retained selection lifecycle
jlucaso1 Sep 24, 2026
ba04aed
test(gui): import rich text builder helpers
jlucaso1 Sep 24, 2026
6b95703
test(gui): scope selection checks to visual context
jlucaso1 Sep 24, 2026
ee3b154
test(gui): draw before asserting text selection
jlucaso1 Sep 24, 2026
0ae39ec
test(gui): satisfy clippy in selection fixture
jlucaso1 Sep 24, 2026
816f9a2
fix(gui): retain selected virtualized participants
jlucaso1 Sep 24, 2026
4bb8558
fix(gui): use an empty layout child list
jlucaso1 Sep 24, 2026
7a466e6
style(gui): simplify selection keepalive flow
jlucaso1 Sep 24, 2026
fa82463
fix(gui): cache copied selection for virtualized rows
jlucaso1 Sep 24, 2026
80bef13
fix(gui): scale dormant selection geometry
jlucaso1 Sep 24, 2026
a792886
fix(gui): preserve selection geometry while virtualized
jlucaso1 Sep 24, 2026
f725990
style(gui): avoid cloning selection bounds
jlucaso1 Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ chrono = { workspace = true, features = ["std", "clock"] }

# Match GPUI Kit's published GPUI family so entities share one implementation.
gpui = { package = "gpui-pre", version = "0.3.4" }
gpui-base = { git = "https://github.com/longbridge/gpui-kit.git" }
gpui-component = { git = "https://github.com/longbridge/gpui-kit.git" }
gpui-kit-assets = { git = "https://github.com/longbridge/gpui-kit.git" }

Expand Down
3 changes: 3 additions & 0 deletions crates/gui/src/app/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ impl Render for Body {
}
AppState::Syncing => render_syncing_view(cx).into_any_element(),
AppState::Connected | AppState::Offline if app.showing_settings(cx) => {
if gpui_base::TextSelection::has_selection(window, cx) {
super::clear_window_message_selection(window, cx);
}
Comment thread
jlucaso1 marked this conversation as resolved.
render_settings_view(app, window, cx).into_any_element()
}
AppState::Connected | AppState::Offline => {
Expand Down
2 changes: 1 addition & 1 deletion crates/gui/src/app/chats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ impl WhatsAppApp {
// A selection that no longer names a chat is a selection of nothing:
// the conversation pane resolves it every frame and would draw the
// empty state with no way back on a phone.
self.forget_missing_selection();
self.forget_missing_selection(cx);
// The merge above took the store's word for every row, and a merge
// assembled before a view was written does not carry it.
self.restore_watched_status(watched);
Expand Down
9 changes: 8 additions & 1 deletion crates/gui/src/app/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ impl WhatsAppApp {
cx.notify();
}
UiEvent::LoggedOut(message) => {
self.leave_connected_view(cx);
self.leave_connected_view(None, cx);
self.app_state = AppState::LoggedOut { message };
cx.notify();
}
Expand Down Expand Up @@ -318,6 +318,13 @@ impl WhatsAppApp {
self.handle_chat_presence(chat_jid, sender_jid, sender_name, composing, cx);
}
UiEvent::AccountUpdated { name, jid, lid } => {
if (self.account_jid != jid || self.account_lid != lid)
&& let Some(window) = self.modal_window
{
let _ = window.update(cx, |_, window, cx| {
super::clear_window_message_selection(window, cx);
});
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if self.account_name != name || self.account_jid != jid || self.account_lid != lid {
self.account_name = name;
self.account_jid = jid;
Expand Down
14 changes: 14 additions & 0 deletions crates/gui/src/app/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ pub struct BubbleIds {
pub group: SharedString,
/// The bubble itself.
pub bubble: SharedString,
/// Shared selection identity, built with the row cache rather than on each
/// bubble repaint.
pub selection_key: Arc<str>,
/// The three controls in the action bar beside it, which is built on
/// every frame whether or not the pointer is over the row: it is
/// `invisible()` until a hover reveals it, not absent.
Expand All @@ -125,6 +128,7 @@ impl BubbleIds {
row: format!("row-{id}").into(),
group: format!("bubble-{id}").into(),
bubble: format!("msg-{id}").into(),
selection_key: Arc::from(id.as_str()),
react: format!("react-{id}").into(),
reply: format!("reply-{id}").into(),
copy: format!("copy-{id}").into(),
Expand Down Expand Up @@ -279,6 +283,16 @@ mod tests {
msg
}

#[test]
fn bubble_clones_share_the_precomputed_selection_identity() {
let message = message("peer", false, at(13, 9));
let ids = BubbleIds::of(&message);
let cloned_ids = ids.clone();

assert_eq!(ids.selection_key.as_ref(), message.id.as_str());
assert!(Arc::ptr_eq(&ids.selection_key, &cloned_ids.selection_key));
}

fn kinds(items: &[TimelineItem]) -> Vec<&'static str> {
items
.iter()
Expand Down
Loading
Loading