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 {Printer, With, XY};
use direction::Direction;
use event::{Event, EventResult, Key, MouseEvent};
use event::{Event, EventResult, Key, MouseEvent, MouseButton};
use odds::vec::VecExt;
use theme::{ColorStyle, Effect};
use unicode_segmentation::UnicodeSegmentation;
@ -28,7 +28,8 @@ pub struct TextArea {
scrollbase: ScrollBase,
/// 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.
cursor: usize,
@ -48,7 +49,8 @@ impl TextArea {
rows: Vec::new(),
enabled: true,
scrollbase: ScrollBase::new().right_padding(0),
last_size: None,
size_cache: None,
last_size: Vec2::zero(),
cursor: 0,
}
}
@ -59,13 +61,13 @@ impl TextArea {
}
fn invalidate(&mut self) {
self.last_size = None;
self.size_cache = None;
}
/// Sets the content of the view.
pub fn set_content<S: Into<String>>(&mut self, content: S) {
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);
}
}
@ -172,7 +174,7 @@ impl TextArea {
}
fn is_cache_valid(&self, size: Vec2) -> bool {
match self.last_size {
match self.size_cache {
None => false,
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) {
return;
}
@ -214,8 +217,12 @@ impl TextArea {
}
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());
}
@ -289,13 +296,13 @@ impl TextArea {
///
/// The only damages are assumed to have occured around the cursor.
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.
// So no need to do that here.
return;
}
let size = self.last_size.unwrap().map(|s| s.value);
let size = self.size_cache.unwrap().map(|s| s.value);
// Find affected text.
// We know the damage started at this row, so it'll need to go.
@ -362,7 +369,7 @@ impl TextArea {
impl View for TextArea {
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
// 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
// (we always keep a space at the end)
@ -425,6 +432,7 @@ impl View for TextArea {
}
fn on_event(&mut self, event: Event) -> EventResult {
let mut fix_scroll = true;
match event {
Event::Char(ch) => self.insert(ch),
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() => {
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: MouseEvent::Press(_),
position,
offset,
} if position.fits_in_rect(
offset,
self.last_size
.map(|s| s.map(SizeCache::value))
.unwrap_or_else(Vec2::zero),
self.last_size,
) =>
{
position.checked_sub(offset).map(|position| {
@ -488,8 +527,10 @@ impl View for TextArea {
}
debug!("Rows: {:?}", self.rows);
let focus = self.selected_row();
self.scrollbase.scroll_to(focus);
if fix_scroll {
let focus = self.selected_row();
self.scrollbase.scroll_to(focus);
}
EventResult::Consumed(None)
}
@ -499,6 +540,7 @@ impl View for TextArea {
}
fn layout(&mut self, size: Vec2) {
self.last_size = size;
self.compute_rows(size);
}
}