aboutsummaryrefslogtreecommitdiff
path: root/helix-view
diff options
context:
space:
mode:
authorDmitry Sharshakov2021-07-30 07:52:00 +0000
committerGitHub2021-07-30 07:52:00 +0000
commit8361de45dc20e428c538f784898e6c47646b6e8d (patch)
treea76526b6599e99e6152f5a6ac60d045d58a8682e /helix-view
parent0fdb626c2cc5518b10a9bfbedc8b78cff3d360c9 (diff)
Mouse selection support (#509)
* Initial mouse selection support Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Disable mouse event capture if editor crashes Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Translate screen coordinates to view position Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Select full lines by dragging on line numbers Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * editor: don't register dragging as a jump Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Count graphemes correctly Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Do not select lines when dragging on the line number bar Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Split out verify_screen_coords Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Do not iterate over the graphemes twice Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Switch view by clicking on it Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Add disable-mouse config option Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Support multiple selections with mouse Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Remove unnecessary check Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Refactor using match expression Co-authored-by: Gokul Soumya <gokulps15@gmail.com> Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Rename local variable Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Rename mouse option Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Refactor code Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Fix dragging selection Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Fix crash when clicking past last line Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Count characters better Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Remove comparison not needed anymore Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Validate coordinates before resolving position Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Tidy up references to editor tree Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Better way to determine line end and avoid overflow Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Fix for last line Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> * Add unit tests for text_pos_at_screen_coords Signed-off-by: Dmitry Sharshakov <d3dx12.xx@gmail.com> Co-authored-by: Gokul Soumya <gokulps15@gmail.com>
Diffstat (limited to 'helix-view')
-rw-r--r--helix-view/src/view.rs147
1 files changed, 147 insertions, 0 deletions
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index 6b0c3c2a..d61fbe4a 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -4,6 +4,7 @@ use crate::{graphics::Rect, Document, DocumentId, ViewId};
use helix_core::{
coords_at_pos,
graphemes::{grapheme_width, RopeGraphemes},
+ line_ending::line_end_char_index,
Position, RopeSlice, Selection,
};
@@ -165,6 +166,74 @@ impl View {
Some(Position::new(row, col))
}
+ /// Verifies whether a screen position is inside the view
+ /// Returns true when position is inside the view
+ pub fn verify_screen_coords(&self, row: usize, column: usize) -> bool {
+ // 2 for status
+ if row < self.area.y as usize || row > self.area.y as usize + self.area.height as usize - 2
+ {
+ return false;
+ }
+
+ // TODO: not ideal
+ const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
+
+ if column < self.area.x as usize + OFFSET
+ || column > self.area.x as usize + self.area.width as usize
+ {
+ return false;
+ }
+ true
+ }
+
+ pub fn text_pos_at_screen_coords(
+ &self,
+ text: &RopeSlice,
+ row: usize,
+ column: usize,
+ tab_width: usize,
+ ) -> Option<usize> {
+ if !self.verify_screen_coords(row, column) {
+ return None;
+ }
+
+ let line_number = row - self.area.y as usize + self.first_line;
+
+ if line_number > text.len_lines() - 1 {
+ return Some(text.len_chars());
+ }
+
+ let mut pos = text.line_to_char(line_number);
+
+ let current_line = text.line(line_number);
+
+ // TODO: not ideal
+ const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
+
+ let target = column - OFFSET - self.area.x as usize + self.first_col;
+ let mut selected = 0;
+
+ for grapheme in RopeGraphemes::new(current_line) {
+ if selected >= target {
+ break;
+ }
+ if grapheme == "\t" {
+ selected += tab_width;
+ } else {
+ let width = grapheme_width(&Cow::from(grapheme));
+ selected += width;
+ }
+ pos += grapheme.chars().count();
+ }
+
+ Some(pos.min(line_end_char_index(&text.slice(..), line_number)))
+ }
+
+ /// Translates a screen position to position in the text document.
+ /// Returns a usize typed position in bounds of the text if found in this view, None if out of view.
+ pub fn pos_at_screen_coords(&self, doc: &Document, row: usize, column: usize) -> Option<usize> {
+ self.text_pos_at_screen_coords(&doc.text().slice(..), row, column, doc.tab_width())
+ }
// pub fn traverse<F>(&self, text: RopeSlice, start: usize, end: usize, fun: F)
// where
// F: Fn(usize, usize),
@@ -186,3 +255,81 @@ impl View {
// }
// }
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use helix_core::Rope;
+
+ #[test]
+ fn test_text_pos_at_screen_coords() {
+ let mut view = View::new(DocumentId::default());
+ view.area = Rect::new(40, 40, 40, 40);
+ let text = Rope::from_str("abc\n\tdef");
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 40, 2, 4),
+ None
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 40, 41, 4),
+ None
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 0, 2, 4),
+ None
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 0, 49, 4),
+ None
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 0, 41, 4),
+ None
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 40, 81, 4),
+ None
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 78, 41, 4),
+ None
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 40, 40 + 7 + 3, 4),
+ Some(3)
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 40, 80, 4),
+ Some(3)
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 1, 4),
+ Some(5)
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 4, 4),
+ Some(5)
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 7, 4),
+ Some(8)
+ );
+
+ assert_eq!(
+ view.text_pos_at_screen_coords(&text.slice(..), 41, 80, 4),
+ Some(8)
+ );
+ }
+}