Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions app/src/ai/blocklist/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6619,9 +6619,9 @@ impl TypedActionView for AIBlock {
// Clear all other selections within the `AIBlock`'s view sub-hierarchy to ensure
// only one component has a selection at a time.
self.clear_other_selections(None, ctx.window_id(), ctx);
// If we have a selection, we should use the default cursor, even if it's over a link.
ctx.reset_cursor();
if has_selection {
// An empty selection can come from clicking another input; preserve its cursor.
ctx.reset_cursor();
self.dismiss_ai_tooltips(ctx);
}
// Notify the terminal view so it can keep the model's record of which rich
Expand Down
36 changes: 36 additions & 0 deletions app/src/terminal/view_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10255,13 +10255,37 @@ fn copy_selected_text_from_ai_block() {
.expect("an AI block should have been inserted")
});

terminal.update(&mut app, |view, ctx| {
view.input.update(ctx, |_, ctx| {
ctx.set_cursor_shape(Cursor::IBeam);
});
});
ai_block.update(&mut app, |block, ctx| {
block.set_block_level_selected_text_for_test(None);
block.handle_action(&AIBlockAction::SelectText, ctx);
});
app.read(|ctx| {
assert_eq!(
ctx.get_cursor_shape(),
Cursor::IBeam,
"an empty AI block selection must not override the input's hover cursor"
);
});

// Simulate a block-level text selection within the AI block and notify the
// terminal view (mirrors the `SelectableArea` selection callback plus the
// `AIBlockAction::SelectText` dispatch that happens on a real drag).
ai_block.update(&mut app, |block, ctx| {
block.set_block_level_selected_text_for_test(Some("quick brown".to_owned()));
block.handle_action(&AIBlockAction::SelectText, ctx);
});
app.read(|ctx| {
assert_eq!(
ctx.get_cursor_shape(),
Cursor::Arrow,
"a non-empty AI block selection must still reset the cursor"
);
});

// The model must now record that the AI block has an active text
// selection, which is what lets the copy/insert paths (via
Expand All @@ -10284,10 +10308,22 @@ fn copy_selected_text_from_ai_block() {

// Clearing the AI block's selection must clear the tracked model selection
// so stale text isn't returned by later copy/insert operations.
terminal.update(&mut app, |view, ctx| {
view.input.update(ctx, |_, ctx| {
ctx.set_cursor_shape(Cursor::IBeam);
});
});
ai_block.update(&mut app, |block, ctx| {
block.set_block_level_selected_text_for_test(None);
block.handle_action(&AIBlockAction::SelectText, ctx);
});
app.read(|ctx| {
assert_eq!(
ctx.get_cursor_shape(),
Cursor::IBeam,
"clearing an AI block selection must preserve the input's hover cursor"
);
});
terminal.read(&app, |view, ctx| {
let semantic_selection = SemanticSelection::as_ref(ctx);
let model = view.model.lock();
Expand Down
2 changes: 1 addition & 1 deletion crates/warpui_core/src/core/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2354,7 +2354,7 @@ impl AppContext {
self.platform_delegate.set_cursor_shape(cursor)
}

#[cfg(test)]
#[cfg(any(test, feature = "test-util"))]
pub fn get_cursor_shape(&self) -> Cursor {
self.platform_delegate.get_cursor_shape()
}
Expand Down
Loading