Merge pull request #1778 from zed-industries/trackpad-scroll-snap-lock

Lock trackpad scrolling in buffers to axis until broken free
This commit is contained in:
Julia 2022-10-19 12:02:59 -04:00 committed by GitHub
commit adf7578007
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 11 deletions

View File

@ -35,9 +35,9 @@ use gpui::{
impl_actions, impl_internal_actions,
platform::CursorStyle,
serde_json::json,
text_layout, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox,
Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View,
ViewContext, ViewHandle, WeakViewHandle,
text_layout, AnyViewHandle, AppContext, AsyncAppContext, Axis, ClipboardItem, Element,
ElementBox, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription,
Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@ -84,6 +84,7 @@ const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
const MAX_LINE_LEN: usize = 1024;
const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
const MAX_SELECTION_HISTORY_LEN: usize = 1024;
pub const SCROLL_EVENT_SEPARATION: Duration = Duration::from_millis(28);
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
@ -94,7 +95,10 @@ pub struct SelectNext {
}
#[derive(Clone, PartialEq)]
pub struct Scroll(pub Vector2F);
pub struct Scroll {
pub scroll_position: Vector2F,
pub axis: Option<Axis>,
}
#[derive(Clone, PartialEq)]
pub struct Select(pub SelectPhase);
@ -263,7 +267,7 @@ struct ScrollbarAutoHide(bool);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(Editor::new_file);
cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
cx.add_action(Editor::scroll);
cx.add_action(Editor::select);
cx.add_action(Editor::cancel);
cx.add_action(Editor::newline);
@ -429,6 +433,69 @@ pub type GetFieldEditorTheme = fn(&theme::Theme) -> theme::FieldEditor;
type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
#[derive(Clone, Copy)]
pub struct OngoingScroll {
last_timestamp: Instant,
axis: Option<Axis>,
}
impl OngoingScroll {
fn initial() -> OngoingScroll {
OngoingScroll {
last_timestamp: Instant::now() - SCROLL_EVENT_SEPARATION,
axis: None,
}
}
fn update(&mut self, axis: Option<Axis>) {
self.last_timestamp = Instant::now();
self.axis = axis;
}
pub fn filter(&self, delta: &mut Vector2F) -> Option<Axis> {
const UNLOCK_PERCENT: f32 = 1.9;
const UNLOCK_LOWER_BOUND: f32 = 6.;
let mut axis = self.axis;
let x = delta.x().abs();
let y = delta.y().abs();
let duration = Instant::now().duration_since(self.last_timestamp);
if duration > SCROLL_EVENT_SEPARATION {
//New ongoing scroll will start, determine axis
axis = if x <= y {
Some(Axis::Vertical)
} else {
Some(Axis::Horizontal)
};
} else if x.max(y) >= UNLOCK_LOWER_BOUND {
//Check if the current ongoing will need to unlock
match axis {
Some(Axis::Vertical) => {
if x > y && x >= y * UNLOCK_PERCENT {
axis = None;
}
}
Some(Axis::Horizontal) => {
if y > x && y >= x * UNLOCK_PERCENT {
axis = None;
}
}
None => {}
}
}
match axis {
Some(Axis::Vertical) => *delta = vec2f(0., delta.y()),
Some(Axis::Horizontal) => *delta = vec2f(delta.x(), 0.),
None => {}
}
axis
}
}
pub struct Editor {
handle: WeakViewHandle<Self>,
buffer: ModelHandle<MultiBuffer>,
@ -443,6 +510,7 @@ pub struct Editor {
select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
ime_transaction: Option<TransactionId>,
active_diagnostics: Option<ActiveDiagnosticGroup>,
ongoing_scroll: OngoingScroll,
scroll_position: Vector2F,
scroll_top_anchor: Anchor,
autoscroll_request: Option<(Autoscroll, bool)>,
@ -486,6 +554,7 @@ pub struct EditorSnapshot {
pub display_snapshot: DisplaySnapshot,
pub placeholder_text: Option<Arc<str>>,
is_focused: bool,
ongoing_scroll: OngoingScroll,
scroll_position: Vector2F,
scroll_top_anchor: Anchor,
}
@ -1097,6 +1166,7 @@ impl Editor {
soft_wrap_mode_override: None,
get_field_editor_theme,
project,
ongoing_scroll: OngoingScroll::initial(),
scroll_position: Vector2F::zero(),
scroll_top_anchor: Anchor::min(),
autoscroll_request: None,
@ -1189,6 +1259,7 @@ impl Editor {
EditorSnapshot {
mode: self.mode,
display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
ongoing_scroll: self.ongoing_scroll,
scroll_position: self.scroll_position,
scroll_top_anchor: self.scroll_top_anchor.clone(),
placeholder_text: self.placeholder_text.clone(),
@ -1592,6 +1663,11 @@ impl Editor {
});
}
fn scroll(&mut self, action: &Scroll, cx: &mut ViewContext<Self>) {
self.ongoing_scroll.update(action.axis);
self.set_scroll_position(action.scroll_position, cx);
}
fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
self.hide_context_menu(cx);

View File

@ -423,18 +423,27 @@ impl EditorElement {
return false;
}
let line_height = position_map.line_height;
let max_glyph_width = position_map.em_width;
if !precise {
delta *= vec2f(max_glyph_width, position_map.line_height);
}
let axis = if precise {
//Trackpad
position_map.snapshot.ongoing_scroll.filter(&mut delta)
} else {
//Not trackpad
delta *= vec2f(max_glyph_width, line_height);
None //Resets ongoing scroll
};
let scroll_position = position_map.snapshot.scroll_position();
let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width;
let y =
(scroll_position.y() * position_map.line_height - delta.y()) / position_map.line_height;
let y = (scroll_position.y() * line_height - delta.y()) / line_height;
let scroll_position = vec2f(x, y).clamp(Vector2F::zero(), position_map.scroll_max);
cx.dispatch_action(Scroll(scroll_position));
cx.dispatch_action(Scroll {
scroll_position,
axis,
});
true
}