Compare commits
1 Commits
codex
...
smooth-cur
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a9fa435f0 |
@@ -38,6 +38,7 @@ mod proposed_changes_editor;
|
|||||||
mod rust_analyzer_ext;
|
mod rust_analyzer_ext;
|
||||||
pub mod scroll;
|
pub mod scroll;
|
||||||
mod selections_collection;
|
mod selections_collection;
|
||||||
|
mod smooth_cursor_manager;
|
||||||
pub mod tasks;
|
pub mod tasks;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -152,6 +153,7 @@ use selections_collection::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
|
use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use smooth_cursor_manager::SmoothCursorManager;
|
||||||
use snippet::Snippet;
|
use snippet::Snippet;
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
@@ -760,6 +762,7 @@ pub struct Editor {
|
|||||||
toggle_fold_multiple_buffers: Task<()>,
|
toggle_fold_multiple_buffers: Task<()>,
|
||||||
_scroll_cursor_center_top_bottom_task: Task<()>,
|
_scroll_cursor_center_top_bottom_task: Task<()>,
|
||||||
serialize_selections: Task<()>,
|
serialize_selections: Task<()>,
|
||||||
|
smooth_cursor_manager: SmoothCursorManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
|
||||||
@@ -1467,6 +1470,7 @@ impl Editor {
|
|||||||
serialize_selections: Task::ready(()),
|
serialize_selections: Task::ready(()),
|
||||||
text_style_refinement: None,
|
text_style_refinement: None,
|
||||||
load_diff_task: load_uncommitted_diff,
|
load_diff_task: load_uncommitted_diff,
|
||||||
|
smooth_cursor_manager: SmoothCursorManager::Inactive,
|
||||||
};
|
};
|
||||||
this.tasks_update_task = Some(this.refresh_runnables(window, cx));
|
this.tasks_update_task = Some(this.refresh_runnables(window, cx));
|
||||||
this._subscriptions.extend(project_subscriptions);
|
this._subscriptions.extend(project_subscriptions);
|
||||||
@@ -2030,6 +2034,7 @@ impl Editor {
|
|||||||
local: bool,
|
local: bool,
|
||||||
old_cursor_position: &Anchor,
|
old_cursor_position: &Anchor,
|
||||||
show_completions: bool,
|
show_completions: bool,
|
||||||
|
pre_edit_pixel_points: HashMap<usize, Option<gpui::Point<Pixels>>>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
@@ -2162,6 +2167,23 @@ impl Editor {
|
|||||||
|
|
||||||
hide_hover(self, cx);
|
hide_hover(self, cx);
|
||||||
|
|
||||||
|
let mut post_edit_pixel_points = HashMap::default();
|
||||||
|
|
||||||
|
for selection in self.selections.disjoint_anchors().iter() {
|
||||||
|
let head_point =
|
||||||
|
self.to_pixel_point(selection.head(), &self.snapshot(window, cx), window);
|
||||||
|
post_edit_pixel_points.insert(selection.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pending) = self.selections.pending_anchor() {
|
||||||
|
let head_point =
|
||||||
|
self.to_pixel_point(pending.head(), &self.snapshot(window, cx), window);
|
||||||
|
post_edit_pixel_points.insert(pending.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.smooth_cursor_manager
|
||||||
|
.update(pre_edit_pixel_points, post_edit_pixel_points);
|
||||||
|
|
||||||
if old_cursor_position.to_display_point(&display_map).row()
|
if old_cursor_position.to_display_point(&display_map).row()
|
||||||
!= new_cursor_position.to_display_point(&display_map).row()
|
!= new_cursor_position.to_display_point(&display_map).row()
|
||||||
{
|
{
|
||||||
@@ -2279,6 +2301,21 @@ impl Editor {
|
|||||||
change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
|
change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
let old_cursor_position = self.selections.newest_anchor().head();
|
let old_cursor_position = self.selections.newest_anchor().head();
|
||||||
|
|
||||||
|
let mut pre_edit_pixel_points = HashMap::default();
|
||||||
|
|
||||||
|
for selection in self.selections.disjoint_anchors().iter() {
|
||||||
|
let head_point =
|
||||||
|
self.to_pixel_point(selection.head(), &self.snapshot(window, cx), window);
|
||||||
|
pre_edit_pixel_points.insert(selection.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pending) = self.selections.pending_anchor() {
|
||||||
|
let head_point =
|
||||||
|
self.to_pixel_point(pending.head(), &self.snapshot(window, cx), window);
|
||||||
|
pre_edit_pixel_points.insert(pending.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
self.push_to_selection_history();
|
self.push_to_selection_history();
|
||||||
|
|
||||||
let (changed, result) = self.selections.change_with(cx, change);
|
let (changed, result) = self.selections.change_with(cx, change);
|
||||||
@@ -2287,7 +2324,14 @@ impl Editor {
|
|||||||
if let Some(autoscroll) = autoscroll {
|
if let Some(autoscroll) = autoscroll {
|
||||||
self.request_autoscroll(autoscroll, cx);
|
self.request_autoscroll(autoscroll, cx);
|
||||||
}
|
}
|
||||||
self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
|
self.selections_did_change(
|
||||||
|
true,
|
||||||
|
&old_cursor_position,
|
||||||
|
request_completions,
|
||||||
|
pre_edit_pixel_points,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
if self.should_open_signature_help_automatically(
|
if self.should_open_signature_help_automatically(
|
||||||
&old_cursor_position,
|
&old_cursor_position,
|
||||||
@@ -3100,6 +3144,20 @@ impl Editor {
|
|||||||
let initial_buffer_versions =
|
let initial_buffer_versions =
|
||||||
jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
|
jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
|
||||||
|
|
||||||
|
let mut pre_edit_pixel_points = HashMap::default();
|
||||||
|
|
||||||
|
for selection in this.selections.disjoint_anchors().iter() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(selection.head(), &this.snapshot(window, cx), window);
|
||||||
|
pre_edit_pixel_points.insert(selection.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pending) = this.selections.pending_anchor() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(pending.head(), &this.snapshot(window, cx), window);
|
||||||
|
pre_edit_pixel_points.insert(pending.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
buffer.edit(edits, this.autoindent_mode.clone(), cx);
|
buffer.edit(edits, this.autoindent_mode.clone(), cx);
|
||||||
});
|
});
|
||||||
@@ -3201,6 +3259,22 @@ impl Editor {
|
|||||||
linked_editing_ranges::refresh_linked_ranges(this, window, cx);
|
linked_editing_ranges::refresh_linked_ranges(this, window, cx);
|
||||||
this.refresh_inline_completion(true, false, window, cx);
|
this.refresh_inline_completion(true, false, window, cx);
|
||||||
jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
|
jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
|
||||||
|
|
||||||
|
let mut post_edit_pixel_points = HashMap::default();
|
||||||
|
|
||||||
|
for selection in this.selections.disjoint_anchors().iter() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(selection.head(), &this.snapshot(window, cx), window);
|
||||||
|
post_edit_pixel_points.insert(selection.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pending) = this.selections.pending_anchor() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(pending.head(), &this.snapshot(window, cx), window);
|
||||||
|
post_edit_pixel_points.insert(pending.id, head_point);
|
||||||
|
}
|
||||||
|
this.smooth_cursor_manager
|
||||||
|
.update(pre_edit_pixel_points, post_edit_pixel_points);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3253,6 +3327,20 @@ impl Editor {
|
|||||||
|
|
||||||
pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
|
pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.transact(window, cx, |this, window, cx| {
|
self.transact(window, cx, |this, window, cx| {
|
||||||
|
let mut pre_edit_pixel_points = HashMap::default();
|
||||||
|
|
||||||
|
for selection in this.selections.disjoint_anchors().iter() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(selection.head(), &this.snapshot(window, cx), window);
|
||||||
|
pre_edit_pixel_points.insert(selection.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pending) = this.selections.pending_anchor() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(pending.head(), &this.snapshot(window, cx), window);
|
||||||
|
pre_edit_pixel_points.insert(pending.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
|
let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
|
||||||
let selections = this.selections.all::<usize>(cx);
|
let selections = this.selections.all::<usize>(cx);
|
||||||
let multi_buffer = this.buffer.read(cx);
|
let multi_buffer = this.buffer.read(cx);
|
||||||
@@ -3363,6 +3451,23 @@ impl Editor {
|
|||||||
s.select(new_selections)
|
s.select(new_selections)
|
||||||
});
|
});
|
||||||
this.refresh_inline_completion(true, false, window, cx);
|
this.refresh_inline_completion(true, false, window, cx);
|
||||||
|
|
||||||
|
let mut post_edit_pixel_points = HashMap::default();
|
||||||
|
|
||||||
|
for selection in this.selections.disjoint_anchors().iter() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(selection.head(), &this.snapshot(window, cx), window);
|
||||||
|
post_edit_pixel_points.insert(selection.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pending) = this.selections.pending_anchor() {
|
||||||
|
let head_point =
|
||||||
|
this.to_pixel_point(pending.head(), &this.snapshot(window, cx), window);
|
||||||
|
post_edit_pixel_points.insert(pending.id, head_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.smooth_cursor_manager
|
||||||
|
.update(pre_edit_pixel_points, post_edit_pixel_points);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13185,7 +13290,14 @@ impl Editor {
|
|||||||
s.clear_pending();
|
s.clear_pending();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.selections_did_change(false, &old_cursor_position, true, window, cx);
|
self.selections_did_change(
|
||||||
|
false,
|
||||||
|
&old_cursor_position,
|
||||||
|
true,
|
||||||
|
HashMap::default(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_to_selection_history(&mut self) {
|
fn push_to_selection_history(&mut self) {
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ use crate::{
|
|||||||
EditorSettings, EditorSnapshot, EditorStyle, FocusedBlock, GoToHunk, GoToPreviousHunk,
|
EditorSettings, EditorSnapshot, EditorStyle, FocusedBlock, GoToHunk, GoToPreviousHunk,
|
||||||
GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, InlayHintRefreshReason,
|
GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, InlayHintRefreshReason,
|
||||||
InlineCompletion, JumpData, LineDown, LineHighlight, LineUp, OpenExcerpts, PageDown, PageUp,
|
InlineCompletion, JumpData, LineDown, LineHighlight, LineUp, OpenExcerpts, PageDown, PageUp,
|
||||||
Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, Selection, SoftWrap,
|
Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, Selection, SmoothCursorManager,
|
||||||
StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS, CURSORS_VISIBLE_FOR,
|
SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS,
|
||||||
FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
|
CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
|
||||||
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
||||||
};
|
};
|
||||||
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
|
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
|
||||||
@@ -83,6 +83,7 @@ const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
|
|||||||
const MIN_SCROLL_THUMB_SIZE: f32 = 25.;
|
const MIN_SCROLL_THUMB_SIZE: f32 = 25.;
|
||||||
|
|
||||||
struct SelectionLayout {
|
struct SelectionLayout {
|
||||||
|
id: usize,
|
||||||
head: DisplayPoint,
|
head: DisplayPoint,
|
||||||
cursor_shape: CursorShape,
|
cursor_shape: CursorShape,
|
||||||
is_newest: bool,
|
is_newest: bool,
|
||||||
@@ -140,6 +141,7 @@ impl SelectionLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
id: selection.id,
|
||||||
head,
|
head,
|
||||||
cursor_shape,
|
cursor_shape,
|
||||||
is_newest,
|
is_newest,
|
||||||
@@ -1151,12 +1153,29 @@ impl EditorElement {
|
|||||||
let cursor_layouts = self.editor.update(cx, |editor, cx| {
|
let cursor_layouts = self.editor.update(cx, |editor, cx| {
|
||||||
let mut cursors = Vec::new();
|
let mut cursors = Vec::new();
|
||||||
|
|
||||||
|
let is_animating =
|
||||||
|
!matches!(editor.smooth_cursor_manager, SmoothCursorManager::Inactive);
|
||||||
|
let animated_selection_ids = if is_animating {
|
||||||
|
match &editor.smooth_cursor_manager {
|
||||||
|
SmoothCursorManager::Active { cursors } => {
|
||||||
|
cursors.keys().copied().collect::<HashSet<_>>()
|
||||||
|
}
|
||||||
|
_ => HashSet::default(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HashSet::default()
|
||||||
|
};
|
||||||
|
|
||||||
let show_local_cursors = editor.show_local_cursors(window, cx);
|
let show_local_cursors = editor.show_local_cursors(window, cx);
|
||||||
|
|
||||||
for (player_color, selections) in selections {
|
for (player_color, selections) in selections {
|
||||||
for selection in selections {
|
for selection in selections {
|
||||||
let cursor_position = selection.head;
|
let cursor_position = selection.head;
|
||||||
|
|
||||||
|
if animated_selection_ids.contains(&selection.id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let in_range = visible_display_row_range.contains(&cursor_position.row());
|
let in_range = visible_display_row_range.contains(&cursor_position.row());
|
||||||
if (selection.is_local && !show_local_cursors)
|
if (selection.is_local && !show_local_cursors)
|
||||||
|| !in_range
|
|| !in_range
|
||||||
@@ -1283,6 +1302,19 @@ impl EditorElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_animating {
|
||||||
|
let animated_cursors = self.layout_animated_cursors(
|
||||||
|
editor,
|
||||||
|
content_origin,
|
||||||
|
line_height,
|
||||||
|
em_advance,
|
||||||
|
selections,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
cursors.extend(animated_cursors);
|
||||||
|
}
|
||||||
|
|
||||||
cursors
|
cursors
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1293,6 +1325,47 @@ impl EditorElement {
|
|||||||
cursor_layouts
|
cursor_layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn layout_animated_cursors(
|
||||||
|
&self,
|
||||||
|
editor: &mut Editor,
|
||||||
|
content_origin: gpui::Point<Pixels>,
|
||||||
|
line_height: Pixels,
|
||||||
|
em_advance: Pixels,
|
||||||
|
selections: &[(PlayerColor, Vec<SelectionLayout>)],
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> Vec<CursorLayout> {
|
||||||
|
let new_positions = editor.smooth_cursor_manager.animate();
|
||||||
|
if !new_positions.is_empty() {
|
||||||
|
window.request_animation_frame();
|
||||||
|
}
|
||||||
|
new_positions
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, position)| {
|
||||||
|
// todo smit: worst way to get cursor shape and player color
|
||||||
|
let (cursor_shape, player_color) = selections
|
||||||
|
.iter()
|
||||||
|
.find_map(|(player_color, sels)| {
|
||||||
|
sels.iter()
|
||||||
|
.find(|sel| sel.id == id)
|
||||||
|
.map(|sel| (sel.cursor_shape, *player_color))
|
||||||
|
})
|
||||||
|
.unwrap_or((CursorShape::Bar, editor.current_user_player_color(cx)));
|
||||||
|
let mut cursor = CursorLayout {
|
||||||
|
color: player_color.cursor,
|
||||||
|
block_width: em_advance,
|
||||||
|
origin: position,
|
||||||
|
line_height,
|
||||||
|
shape: cursor_shape,
|
||||||
|
block_text: None,
|
||||||
|
cursor_name: None,
|
||||||
|
};
|
||||||
|
cursor.layout(content_origin, None, window, cx);
|
||||||
|
cursor
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn layout_scrollbars(
|
fn layout_scrollbars(
|
||||||
&self,
|
&self,
|
||||||
snapshot: &EditorSnapshot,
|
snapshot: &EditorSnapshot,
|
||||||
|
|||||||
117
crates/editor/src/smooth_cursor_manager.rs
Normal file
117
crates/editor/src/smooth_cursor_manager.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
use collections::HashMap;
|
||||||
|
use gpui::Pixels;
|
||||||
|
|
||||||
|
const DELTA_PERCENT_PER_FRAME: f32 = 0.01;
|
||||||
|
|
||||||
|
pub struct Cursor {
|
||||||
|
current_position: gpui::Point<Pixels>,
|
||||||
|
target_position: gpui::Point<Pixels>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SmoothCursorManager {
|
||||||
|
Inactive,
|
||||||
|
Active { cursors: HashMap<usize, Cursor> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SmoothCursorManager {
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
source_positions: HashMap<usize, Option<gpui::Point<Pixels>>>,
|
||||||
|
target_positions: HashMap<usize, Option<gpui::Point<Pixels>>>,
|
||||||
|
) {
|
||||||
|
if source_positions.len() == 1 && target_positions.len() == 1 {
|
||||||
|
let old_id = source_positions.keys().next().unwrap();
|
||||||
|
let new_id = target_positions.keys().next().unwrap();
|
||||||
|
if old_id != new_id {
|
||||||
|
if let (Some(Some(old_pos)), Some(Some(new_pos))) = (
|
||||||
|
source_positions.values().next(),
|
||||||
|
target_positions.values().next(),
|
||||||
|
) {
|
||||||
|
*self = Self::Active {
|
||||||
|
cursors: HashMap::from_iter([(
|
||||||
|
*new_id,
|
||||||
|
Cursor {
|
||||||
|
current_position: *old_pos,
|
||||||
|
target_position: *new_pos,
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Inactive => {
|
||||||
|
let mut cursors = HashMap::default();
|
||||||
|
for (id, target_position) in target_positions.iter() {
|
||||||
|
let Some(target_position) = target_position else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(Some(source_position)) = source_positions.get(id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if source_position == target_position {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cursors.insert(
|
||||||
|
*id,
|
||||||
|
Cursor {
|
||||||
|
current_position: *source_position,
|
||||||
|
target_position: *target_position,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !cursors.is_empty() {
|
||||||
|
*self = Self::Active { cursors };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Active { cursors } => {
|
||||||
|
for (id, target_position) in target_positions.iter() {
|
||||||
|
let Some(target_position) = target_position else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(cursor) = cursors.get_mut(id) {
|
||||||
|
cursor.target_position = *target_position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn animate(&mut self) -> HashMap<usize, gpui::Point<Pixels>> {
|
||||||
|
match self {
|
||||||
|
Self::Inactive => HashMap::default(),
|
||||||
|
Self::Active { cursors } => {
|
||||||
|
let mut new_positions = HashMap::default();
|
||||||
|
let mut completed = Vec::new();
|
||||||
|
|
||||||
|
for (id, cursor) in cursors.iter_mut() {
|
||||||
|
let dx = cursor.target_position.x - cursor.current_position.x;
|
||||||
|
let dy = cursor.target_position.y - cursor.current_position.y;
|
||||||
|
|
||||||
|
let distance = (dx.0.powi(2) + dy.0.powi(2)).sqrt();
|
||||||
|
if distance < 0.2 {
|
||||||
|
new_positions.insert(*id, cursor.target_position);
|
||||||
|
completed.push(*id);
|
||||||
|
} else {
|
||||||
|
cursor.current_position.x =
|
||||||
|
Pixels(cursor.current_position.x.0 + dx.0 * DELTA_PERCENT_PER_FRAME);
|
||||||
|
cursor.current_position.y =
|
||||||
|
Pixels(cursor.current_position.y.0 + dy.0 * DELTA_PERCENT_PER_FRAME);
|
||||||
|
new_positions.insert(*id, cursor.current_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in completed {
|
||||||
|
cursors.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursors.is_empty() {
|
||||||
|
*self = Self::Inactive;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_positions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user