on master help sign in
helix-term: handle scrolling when mouse is enabled (#554)
27b551dDmitry Sharshakov
helix-term/src/commands.rs+1−1
@@ -871,7 +871,7 @@ fn switch_to_lowercase(cx: &mut Context) {
871 doc.append_changes_to_history(view.id);
871 doc.append_changes_to_history(view.id);
872}
872}
873
873
874−fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
874+pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
875 use Direction::*;
875 use Direction::*;
876 let (view, doc) = current!(cx.editor);
876 let (view, doc) = current!(cx.editor);
877 let cursor = coords_at_pos(
877 let cursor = coords_at_pos(
helix-term/src/ui/editor.rs+111−65
@@ -9,6 +9,7 @@ use crate::{
9use helix_core::{
9use helix_core::{
10 coords_at_pos,
10 coords_at_pos,
11 graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary},
11 graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary},
12+ movement::Direction,
12 syntax::{self, HighlightEvent},
13 syntax::{self, HighlightEvent},
13 unicode::segmentation::UnicodeSegmentation,
14 unicode::segmentation::UnicodeSegmentation,
14 unicode::width::UnicodeWidthStr,
15 unicode::width::UnicodeWidthStr,
@@ -694,8 +695,112 @@ impl EditorView {
694 }
695 }
695}
696}
696
697
698+impl EditorView {
699+ fn handle_mouse_event(
700+ &mut self,
701+ event: MouseEvent,
702+ cxt: &mut commands::Context,
703+ ) -> EventResult {
704+ match event {
705+ MouseEvent {
706+ kind: MouseEventKind::Down(MouseButton::Left),
707+ row,
708+ column,
709+ modifiers,
710+ ..
711+ } => {
712+ let editor = &mut cxt.editor;
713+
714+ let result = editor.tree.views().find_map(|(view, _focus)| {
715+ view.pos_at_screen_coords(&editor.documents[view.doc], row, column)
716+ .map(|pos| (pos, view.id))
717+ });
718+
719+ if let Some((pos, view_id)) = result {
720+ let doc = &mut editor.documents[editor.tree.get(view_id).doc];
721+
722+ if modifiers == crossterm::event::KeyModifiers::ALT {
723+ let selection = doc.selection(view_id).clone();
724+ doc.set_selection(view_id, selection.push(Range::point(pos)));
725+ } else {
726+ doc.set_selection(view_id, Selection::point(pos));
727+ }
728+
729+ editor.tree.focus = view_id;
730+
731+ return EventResult::Consumed(None);
732+ }
733+
734+ EventResult::Ignored
735+ }
736+
737+ MouseEvent {
738+ kind: MouseEventKind::Drag(MouseButton::Left),
739+ row,
740+ column,
741+ ..
742+ } => {
743+ let (view, doc) = current!(cxt.editor);
744+
745+ let pos = match view.pos_at_screen_coords(doc, row, column) {
746+ Some(pos) => pos,
747+ None => return EventResult::Ignored,
748+ };
749+
750+ let mut selection = doc.selection(view.id).clone();
751+ let primary = selection.primary_mut();
752+ *primary = Range::new(primary.anchor, pos);
753+ doc.set_selection(view.id, selection);
754+ EventResult::Consumed(None)
755+ }
756+
757+ MouseEvent {
758+ kind: MouseEventKind::ScrollUp | MouseEventKind::ScrollDown,
759+ row,
760+ column,
761+ ..
762+ } => {
763+ let current_view = cxt.editor.tree.focus;
764+
765+ let direction = match event.kind {
766+ MouseEventKind::ScrollUp => Direction::Backward,
767+ MouseEventKind::ScrollDown => Direction::Forward,
768+ _ => unreachable!(),
769+ };
770+
771+ let result = cxt.editor.tree.views().find_map(|(view, _focus)| {
772+ view.pos_at_screen_coords(&cxt.editor.documents[view.doc], row, column)
773+ .map(|_| view.id)
774+ });
775+
776+ match result {
777+ Some(view_id) => cxt.editor.tree.focus = view_id,
778+ None => return EventResult::Ignored,
779+ }
780+
781+ let offset = cxt.editor.config.scroll_lines.abs() as usize;
782+ commands::scroll(cxt, offset, direction);
783+
784+ cxt.editor.tree.focus = current_view;
785+
786+ EventResult::Consumed(None)
787+ }
788+ _ => EventResult::Ignored,
789+ }
790+ }
791+}
792+
697impl Component for EditorView {
793impl Component for EditorView {
698 fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
794 fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
795+ let mut cxt = commands::Context {
796+ selected_register: helix_view::RegisterSelection::default(),
797+ editor: &mut cx.editor,
798+ count: None,
799+ callback: None,
800+ on_next_key_callback: None,
801+ jobs: cx.jobs,
802+ };
803+
699 match event {
804 match event {
700 Event::Resize(_width, _height) => {
805 Event::Resize(_width, _height) => {
701 // Ignore this event, we handle resizing just before rendering to screen.
806 // Ignore this event, we handle resizing just before rendering to screen.
@@ -706,20 +811,11 @@ impl Component for EditorView {
706 let mut key = KeyEvent::from(key);
811 let mut key = KeyEvent::from(key);
707 canonicalize_key(&mut key);
812 canonicalize_key(&mut key);
708 // clear status
813 // clear status
709− cx.editor.status_msg = None;
814+ cxt.editor.status_msg = None;
710
815
711− let (_, doc) = current!(cx.editor);
816+ let (_, doc) = current!(cxt.editor);
712 let mode = doc.mode();
817 let mode = doc.mode();
713
818
714− let mut cxt = commands::Context {
715− selected_register: helix_view::RegisterSelection::default(),
716− editor: &mut cx.editor,
717− count: None,
718− callback: None,
719− on_next_key_callback: None,
720− jobs: cx.jobs,
721− };
722−
723 if let Some(on_next_key) = self.on_next_key.take() {
819 if let Some(on_next_key) = self.on_next_key.take() {
724 // if there's a command waiting input, do that first
820 // if there's a command waiting input, do that first
725 on_next_key(&mut cxt, key);
821 on_next_key(&mut cxt, key);
@@ -773,12 +869,12 @@ impl Component for EditorView {
773
869
774 // if the command consumed the last view, skip the render.
870 // if the command consumed the last view, skip the render.
775 // on the next loop cycle the Application will then terminate.
871 // on the next loop cycle the Application will then terminate.
776− if cx.editor.should_close() {
872+ if cxt.editor.should_close() {
777 return EventResult::Ignored;
873 return EventResult::Ignored;
778 }
874 }
779
875
780− let (view, doc) = current!(cx.editor);
876+ let (view, doc) = current!(cxt.editor);
781− view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff);
877+ view.ensure_cursor_in_view(doc, cxt.editor.config.scrolloff);
782
878
783 // mode transitions
879 // mode transitions
784 match (mode, doc.mode()) {
880 match (mode, doc.mode()) {
@@ -805,58 +901,8 @@ impl Component for EditorView {
805
901
806 EventResult::Consumed(callback)
902 EventResult::Consumed(callback)
807 }
903 }
808− Event::Mouse(MouseEvent {
809− kind: MouseEventKind::Down(MouseButton::Left),
810− row,
811− column,
812− modifiers,
813− ..
814− }) => {
815− let editor = &mut cx.editor;
816−
817− let result = editor.tree.views().find_map(|(view, _focus)| {
818− view.pos_at_screen_coords(&editor.documents[view.doc], row, column)
819− .map(|pos| (pos, view.id))
820− });
821−
822− if let Some((pos, view_id)) = result {
823− let doc = &mut editor.documents[editor.tree.get(view_id).doc];
824−
825− if modifiers == crossterm::event::KeyModifiers::ALT {
826− let selection = doc.selection(view_id).clone();
827− doc.set_selection(view_id, selection.push(Range::point(pos)));
828− } else {
829− doc.set_selection(view_id, Selection::point(pos));
830− }
831−
832− editor.tree.focus = view_id;
833−
834− return EventResult::Consumed(None);
835− }
836−
837− EventResult::Ignored
838− }
839
904
840− Event::Mouse(MouseEvent {
905+ Event::Mouse(event) => self.handle_mouse_event(event, &mut cxt),
841− kind: MouseEventKind::Drag(MouseButton::Left),
842− row,
843− column,
844− ..
845− }) => {
846− let (view, doc) = current!(cx.editor);
847−
848− let pos = match view.pos_at_screen_coords(doc, row, column) {
849− Some(pos) => pos,
850− None => return EventResult::Ignored,
851− };
852−
853− let mut selection = doc.selection(view.id).clone();
854− let primary = selection.primary_mut();
855− *primary = Range::new(primary.anchor, pos);
856− doc.set_selection(view.id, selection);
857− EventResult::Consumed(None)
858− }
859− Event::Mouse(_) => EventResult::Ignored,
860 }
906 }
861 }
907 }
862
908
helix-view/src/editor.rs+4−1
@@ -21,10 +21,12 @@ use helix_core::Position;
21use serde::Deserialize;
21use serde::Deserialize;
22
22
23#[derive(Debug, Clone, PartialEq, Deserialize)]
23#[derive(Debug, Clone, PartialEq, Deserialize)]
24−#[serde(rename_all = "kebab-case")]
24+#[serde(rename_all = "kebab-case", default)]
25pub struct Config {
25pub struct Config {
26 /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
26 /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
27 pub scrolloff: usize,
27 pub scrolloff: usize,
28+ /// Number of lines to scroll at once. Defaults to 3
29+ pub scroll_lines: isize,
28 /// Mouse support. Defaults to true.
30 /// Mouse support. Defaults to true.
29 pub mouse: bool,
31 pub mouse: bool,
30}
32}
@@ -33,6 +35,7 @@ impl Default for Config {
33 fn default() -> Self {
35 fn default() -> Self {
34 Self {
36 Self {
35 scrolloff: 5,
37 scrolloff: 5,
38+ scroll_lines: 3,
36 mouse: true,
39 mouse: true,
37 }
40 }
38 }
41 }