Fix mouse position with TextArea

This commit is contained in:
Alexandre Bury 2017-10-13 18:04:41 -07:00
parent a5952d0741
commit ca23a9c10f

View File

@ -1,7 +1,7 @@
use std::cmp::min; use std::cmp::min;
use {Printer, With, XY}; use {Printer, With, XY};
use direction::Direction; use direction::Direction;
use event::{Event, EventResult, Key, MouseEvent}; use event::{Event, EventResult, Key, MouseEvent, MouseButton};
use odds::vec::VecExt; use odds::vec::VecExt;
use theme::{ColorStyle, Effect}; use theme::{ColorStyle, Effect};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@ -28,7 +28,8 @@ pub struct TextArea {
scrollbase: ScrollBase, scrollbase: ScrollBase,
/// Cache to avoid re-computing layout on no-op events /// Cache to avoid re-computing layout on no-op events
last_size: Option<XY<SizeCache>>, size_cache: Option<XY<SizeCache>>,
last_size: Vec2,
/// Byte offset of the currently selected grapheme. /// Byte offset of the currently selected grapheme.
cursor: usize, cursor: usize,
@ -48,7 +49,8 @@ impl TextArea {
rows: Vec::new(), rows: Vec::new(),
enabled: true, enabled: true,
scrollbase: ScrollBase::new().right_padding(0), scrollbase: ScrollBase::new().right_padding(0),
last_size: None, size_cache: None,
last_size: Vec2::zero(),
cursor: 0, cursor: 0,
} }
} }
@ -59,13 +61,13 @@ impl TextArea {
} }
fn invalidate(&mut self) { fn invalidate(&mut self) {
self.last_size = None; self.size_cache = None;
} }
/// Sets the content of the view. /// Sets the content of the view.
pub fn set_content<S: Into<String>>(&mut self, content: S) { pub fn set_content<S: Into<String>>(&mut self, content: S) {
self.content = content.into(); self.content = content.into();
if let Some(size) = self.last_size.map(|s| s.map(|s| s.value)) { if let Some(size) = self.size_cache.map(|s| s.map(|s| s.value)) {
self.compute_rows(size); self.compute_rows(size);
} }
} }
@ -172,7 +174,7 @@ impl TextArea {
} }
fn is_cache_valid(&self, size: Vec2) -> bool { fn is_cache_valid(&self, size: Vec2) -> bool {
match self.last_size { match self.size_cache {
None => false, None => false,
Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y), Some(ref last) => last.x.accept(size.x) && last.y.accept(size.y),
} }
@ -195,7 +197,8 @@ impl TextArea {
} }
} }
fn compute_rows(&mut self, size: Vec2) { fn soft_compute_rows(&mut self, size: Vec2) {
if self.is_cache_valid(size) { if self.is_cache_valid(size) {
return; return;
} }
@ -214,8 +217,12 @@ impl TextArea {
} }
if !self.rows.is_empty() { if !self.rows.is_empty() {
self.last_size = Some(SizeCache::build(size, size)); self.size_cache = Some(SizeCache::build(size, size));
} }
}
fn compute_rows(&mut self, size: Vec2) {
self.soft_compute_rows(size);
self.scrollbase.set_heights(size.y, self.rows.len()); self.scrollbase.set_heights(size.y, self.rows.len());
} }
@ -289,13 +296,13 @@ impl TextArea {
/// ///
/// The only damages are assumed to have occured around the cursor. /// The only damages are assumed to have occured around the cursor.
fn fix_damages(&mut self) { fn fix_damages(&mut self) {
if self.last_size.is_none() { if self.size_cache.is_none() {
// If we don't know our size, it means we'll get a layout command soon. // If we don't know our size, it means we'll get a layout command soon.
// So no need to do that here. // So no need to do that here.
return; return;
} }
let size = self.last_size.unwrap().map(|s| s.value); let size = self.size_cache.unwrap().map(|s| s.value);
// Find affected text. // Find affected text.
// We know the damage started at this row, so it'll need to go. // We know the damage started at this row, so it'll need to go.
@ -362,7 +369,7 @@ impl TextArea {
impl View for TextArea { impl View for TextArea {
fn required_size(&mut self, constraint: Vec2) -> Vec2 { fn required_size(&mut self, constraint: Vec2) -> Vec2 {
// Make sure our structure is up to date // Make sure our structure is up to date
self.compute_rows(constraint); self.soft_compute_rows(constraint);
// Ideally, we'd want x = the longest row + 1 // Ideally, we'd want x = the longest row + 1
// (we always keep a space at the end) // (we always keep a space at the end)
@ -425,6 +432,7 @@ impl View for TextArea {
} }
fn on_event(&mut self, event: Event) -> EventResult { fn on_event(&mut self, event: Event) -> EventResult {
let mut fix_scroll = true;
match event { match event {
Event::Char(ch) => self.insert(ch), Event::Char(ch) => self.insert(ch),
Event::Key(Key::Enter) => self.insert('\n'), Event::Key(Key::Enter) => self.insert('\n'),
@ -459,15 +467,46 @@ impl View for TextArea {
Event::Key(Key::Right) if self.cursor < self.content.len() => { Event::Key(Key::Right) if self.cursor < self.content.len() => {
self.move_right() self.move_right()
} }
Event::Mouse {
event: MouseEvent::WheelUp,
..
} if self.scrollbase.can_scroll_up() => {
fix_scroll = false;
self.scrollbase.scroll_up(5);
}
Event::Mouse {
event: MouseEvent::WheelDown,
..
} if self.scrollbase.can_scroll_down() => {
fix_scroll = false;
self.scrollbase.scroll_down(5);
}
Event::Mouse {
event: MouseEvent::Press(MouseButton::Left),
position,
offset,
} if position.checked_sub(offset).map(|position| {
self.scrollbase.start_drag(position, self.last_size.x)
}).unwrap_or(false) => {
fix_scroll = false;
}
Event::Mouse {
event: MouseEvent::Hold(MouseButton::Left),
position,
offset
} => {
fix_scroll = false;
position
.checked_sub(offset)
.map(|position| self.scrollbase.drag(position));
}
Event::Mouse { Event::Mouse {
event: MouseEvent::Press(_), event: MouseEvent::Press(_),
position, position,
offset, offset,
} if position.fits_in_rect( } if position.fits_in_rect(
offset, offset,
self.last_size self.last_size,
.map(|s| s.map(SizeCache::value))
.unwrap_or_else(Vec2::zero),
) => ) =>
{ {
position.checked_sub(offset).map(|position| { position.checked_sub(offset).map(|position| {
@ -488,8 +527,10 @@ impl View for TextArea {
} }
debug!("Rows: {:?}", self.rows); debug!("Rows: {:?}", self.rows);
if fix_scroll {
let focus = self.selected_row(); let focus = self.selected_row();
self.scrollbase.scroll_to(focus); self.scrollbase.scroll_to(focus);
}
EventResult::Consumed(None) EventResult::Consumed(None)
} }
@ -499,6 +540,7 @@ impl View for TextArea {
} }
fn layout(&mut self, size: Vec2) { fn layout(&mut self, size: Vec2) {
self.last_size = size;
self.compute_rows(size); self.compute_rows(size);
} }
} }