From 60d4c0be8855e2fc4e679d0ae0a2b27d45388cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Mon, 23 Dec 2019 13:04:06 +0100 Subject: [PATCH] Prevent constant refresh scrollbars, add tests functions, fix caret location on empty line --- rider-editor/src/app/caret_manager.rs | 2 - rider-editor/src/main.rs | 2 + rider-editor/src/tests.rs | 143 ++++++++++++++++++++++-- rider-editor/src/ui/caret/caret.rs | 79 +++---------- rider-editor/src/ui/file/editor_file.rs | 36 +++++- rider-editor/src/ui/file_editor/mod.rs | 21 +++- rider-editor/src/ui/menu_bar.rs | 90 ++++----------- rider-editor/src/ui/mod.rs | 10 ++ 8 files changed, 227 insertions(+), 156 deletions(-) diff --git a/rider-editor/src/app/caret_manager.rs b/rider-editor/src/app/caret_manager.rs index 7988086..3d850fa 100644 --- a/rider-editor/src/app/caret_manager.rs +++ b/rider-editor/src/app/caret_manager.rs @@ -56,8 +56,6 @@ pub fn move_caret_down(file_editor: &mut FileEditor) -> Option { } } let text_character = text_character?; - // let character_destination = text_character.dest().clone(); - // let pos = text_character.position().clone(); Some(text_character.clone()) })??; diff --git a/rider-editor/src/main.rs b/rider-editor/src/main.rs index 21dc7f1..0b5b81b 100644 --- a/rider-editor/src/main.rs +++ b/rider-editor/src/main.rs @@ -1,3 +1,5 @@ +#![feature(clamp)] + extern crate dirs; #[macro_use] extern crate log; diff --git a/rider-editor/src/tests.rs b/rider-editor/src/tests.rs index a23ef96..b9ff82b 100644 --- a/rider-editor/src/tests.rs +++ b/rider-editor/src/tests.rs @@ -13,6 +13,7 @@ pub mod support { use sdl2::rect::Rect; use sdl2::render::Texture; use sdl2::ttf::Font; + use std::collections::HashMap; use std::fmt::Debug; use std::fmt::Error; use std::fmt::Formatter; @@ -35,10 +36,27 @@ pub mod support { Arc::new(RwLock::new(config)) } + #[cfg_attr(tarpaulin, skip)] + #[derive(Debug, PartialEq)] + pub enum CanvasShape { + Line, + Border, + Rectangle, + Image(Rect, Rect, String), + } + #[derive(Debug, PartialEq)] pub struct RendererRect { pub rect: Rect, pub color: Color, + pub shape: CanvasShape, + } + + #[cfg_attr(tarpaulin, skip)] + impl RendererRect { + pub fn new(rect: Rect, color: Color, shape: CanvasShape) -> Self { + Self { rect, color, shape } + } } #[cfg_attr(tarpaulin, skip)] @@ -46,13 +64,18 @@ pub mod support { pub rects: Vec, pub borders: Vec, pub lines: Vec, - pub clippings: Vec, + pub clippings: Vec>, + pub character_sizes: HashMap, } #[cfg_attr(tarpaulin, skip)] impl Debug for CanvasMock { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - write!(f, "CanvasMock {{}}") + write!( + f, + "CanvasMock {{ {:?} {:?} {:?} }}", + self.rects, self.lines, self.clippings + ) } } @@ -74,6 +97,7 @@ pub mod support { borders: vec![], lines: vec![], clippings: vec![], + character_sizes: HashMap::new(), } } } @@ -81,35 +105,132 @@ pub mod support { #[cfg_attr(tarpaulin, skip)] impl CanvasAccess for CanvasMock { fn render_rect(&mut self, rect: Rect, color: Color) -> Result<(), String> { - self.rects.push(RendererRect { rect, color }); + self.rects.push(RendererRect { + rect, + color, + shape: CanvasShape::Rectangle, + }); Ok(()) } fn render_border(&mut self, rect: Rect, color: Color) -> Result<(), String> { - self.borders.push(RendererRect { rect, color }); + self.borders.push(RendererRect { + rect, + color, + shape: CanvasShape::Border, + }); Ok(()) } - fn render_image( - &mut self, - _tex: Rc, - _src: Rect, - _dest: Rect, - ) -> Result<(), String> { - unimplemented!() + fn render_image(&mut self, _tex: Rc, src: Rect, dest: Rect) -> Result<(), String> { + self.rects.push(RendererRect::new( + dest.clone(), + Color::RGBA(0, 0, 0, 255), + CanvasShape::Image(src.clone(), dest.clone(), format!("_tex: Rc")), + )); + Ok(()) } fn render_line(&mut self, start: Point, end: Point, color: Color) -> Result<(), String> { self.lines.push(RendererRect { rect: Rect::new(start.x(), start.y(), end.x() as u32, end.y() as u32), color, + shape: CanvasShape::Line, }); Ok(()) } fn set_clipping(&mut self, rect: Rect) { + self.clippings.push(Some(rect)); + } + + fn set_clip_rect(&mut self, rect: Option) { self.clippings.push(rect); } + + fn clip_rect(&self) -> Option { + self.clippings.last().cloned().unwrap_or_else(|| None) + } + } + + impl CharacterSizeManager for CanvasMock { + fn load_character_size(&mut self, c: char) -> Rect { + match self.character_sizes.get(&c) { + Some(r) => r.clone(), + None => { + self.character_sizes.insert(c, Rect::new(0, 0, 1, 1)); + self.character_sizes.get(&c).cloned().unwrap() + } + } + } + } + + impl CanvasMock { + pub fn set_character_rect(&mut self, c: char, rect: Rect) { + self.character_sizes.insert(c, rect); + } + + pub fn find_pixel_with_color( + &self, + point: sdl2::rect::Point, + color: sdl2::pixels::Color, + ) -> Option<&RendererRect> { + for rect in self.rects.iter() { + if rect.rect.contains_point(point.clone()) && rect.color == color { + return Some(rect.clone()); + } + } + for rect in self.borders.iter() { + if rect.rect.contains_point(point.clone()) && rect.color == color { + return Some(rect.clone()); + } + } + for rect in self.lines.iter() { + if rect.rect.contains_point(point.clone()) && rect.color == color { + return Some(rect.clone()); + } + } + None + } + + pub fn find_rect_with_color( + &self, + subject: sdl2::rect::Rect, + color: sdl2::pixels::Color, + ) -> Option<&RendererRect> { + for rect in self.rects.iter() { + if rect.rect == subject && rect.color == color { + return Some(rect.clone()); + } + } + None + } + + pub fn find_line_with_color( + &self, + subject: sdl2::rect::Rect, + color: sdl2::pixels::Color, + ) -> Option<&RendererRect> { + for rect in self.lines.iter() { + if rect.rect == subject && rect.color == color { + return Some(rect.clone()); + } + } + None + } + + pub fn find_border_with_color( + &self, + subject: sdl2::rect::Rect, + color: sdl2::pixels::Color, + ) -> Option<&RendererRect> { + for rect in self.borders.iter() { + if rect.rect == subject && rect.color == color { + return Some(rect.clone()); + } + } + None + } } #[cfg_attr(tarpaulin, skip)] diff --git a/rider-editor/src/ui/caret/caret.rs b/rider-editor/src/ui/caret/caret.rs index 4915a45..99ab1e8 100644 --- a/rider-editor/src/ui/caret/caret.rs +++ b/rider-editor/src/ui/caret/caret.rs @@ -317,85 +317,38 @@ mod test_click_handler { #[cfg(test)] mod test_render { + use crate::tests::support; use crate::tests::support::build_config; use crate::ui::*; - use sdl2::pixels::Color; use sdl2::rect::{Point, Rect}; - use sdl2::render::Texture; - use std::rc::Rc; - - struct CanvasMock { - pub start: Point, - pub end: Point, - pub color: sdl2::pixels::Color, - } - - impl CanvasAccess for CanvasMock { - fn render_rect(&mut self, _rect: Rect, _color: Color) -> Result<(), String> { - unimplemented!() - } - - fn render_border(&mut self, _rect: Rect, _color: Color) -> Result<(), String> { - unimplemented!() - } - - fn render_image( - &mut self, - _tex: Rc, - _src: Rect, - _dest: Rect, - ) -> Result<(), String> { - unimplemented!() - } - - fn render_line( - &mut self, - start: Point, - end: Point, - color: sdl2::pixels::Color, - ) -> Result<(), String> { - self.start = start; - self.end = end; - self.color = color; - Ok(()) - } - - fn set_clipping(&mut self, _rect: Rect) { - unimplemented!() - } - } - - impl CharacterSizeManager for CanvasMock { - fn load_character_size(&mut self, _c: char) -> Rect { - Rect::new(0, 2, 12, 23) - } - } #[test] fn assert_render_line() { let config = build_config(); let context = RenderContext::ParentPosition(Point::new(10, 14)); - let mut canvas = CanvasMock { - start: Point::new(0, 0), - end: Point::new(0, 0), - color: sdl2::pixels::Color::RGB(0, 0, 0), - }; + let mut canvas = support::CanvasMock::new(); let mut widget = Caret::new(config); + canvas.set_character_rect('I', Rect::new(11, 12, 6, 23)); widget.move_caret(CaretPosition::new(0, 0, 0), Point::new(23, 23)); widget.render(&mut canvas, &context); - assert_eq!(canvas.start, Point::new(33, 37)); - assert_eq!(canvas.end, Point::new(33, 38)); - assert_eq!(canvas.color, sdl2::pixels::Color::RGBA(121, 121, 121, 0)); + assert_eq!( + canvas.find_pixel_with_color( + Point::new(33, 37), + sdl2::pixels::Color::RGBA(121, 121, 121, 0) + ), + Some(&support::RendererRect::new( + Rect::new(33, 37, 33, 38), + sdl2::pixels::Color::RGBA(121, 121, 121, 0), + support::CanvasShape::Line + )) + ); } #[test] fn assert_prepare_ui() { let config = build_config(); - let mut canvas = CanvasMock { - start: Point::new(0, 0), - end: Point::new(0, 0), - color: sdl2::pixels::Color::RGB(0, 0, 0), - }; + let mut canvas = support::CanvasMock::new(); + canvas.set_character_rect('I', Rect::new(11, 12, 6, 23)); let mut widget = Caret::new(config); widget.move_caret(CaretPosition::new(0, 0, 0), Point::new(11, 12)); widget.prepare_ui(&mut canvas); diff --git a/rider-editor/src/ui/file/editor_file.rs b/rider-editor/src/ui/file/editor_file.rs index 823698c..aeb717c 100644 --- a/rider-editor/src/ui/file/editor_file.rs +++ b/rider-editor/src/ui/file/editor_file.rs @@ -111,14 +111,38 @@ impl TextCollection for EditorFile { } fn get_last_at_line(&self, line: usize) -> Option { - let mut current = None; - for section in self.sections.iter() { - let c = section.get_last_at_line(line); - if c.is_some() { - current = c; + let mut current: Option = None; + 'scanning: for section in self.sections.iter().rev() { + for token in section.tokens().iter().rev() { + for text_character in token.characters().iter().rev() { + match text_character.line() { + l if l > line => continue, + l if l < line => break 'scanning, + _ => (), + }; + match ( + current.clone().map(|ref c| c.is_new_line()), + text_character.is_new_line(), + ) { + (None, true) => current = Some(text_character.clone()), + (None, false) => current = Some(text_character.clone()), + (Some(true), false) => current = Some(text_character.clone()), + (Some(false), false) => break, + _ => continue, + }; + } } } - current + match current { + Some(ref tc) => { + // Click on empty new line + if tc.is_new_line() && self.get_character_at(tc.position() - 1).is_some() { + return self.get_character_at(tc.position() - 1); + } + return current; + } + None => None, + } } } diff --git a/rider-editor/src/ui/file_editor/mod.rs b/rider-editor/src/ui/file_editor/mod.rs index c59f398..237db36 100644 --- a/rider-editor/src/ui/file_editor/mod.rs +++ b/rider-editor/src/ui/file_editor/mod.rs @@ -170,13 +170,20 @@ impl FileAccess for FileEditor { } fn open_file(&mut self, file: EditorFile) -> Option { + let new_path = file.path(); let mut file = Some(file); + let old_path = match self.file { + Some(ref f) => f.path(), + _ => format!(""), + }; mem::swap(&mut self.file, &mut file); - if let Some(f) = self.file.as_ref() { + if let Some(ref f) = self.file { self.full_rect = f.full_rect(); } - self.vertical_scroll_bar.reset(); - self.horizontal_scroll_bar.reset(); + if old_path != new_path { + self.vertical_scroll_bar.reset(); + self.horizontal_scroll_bar.reset(); + } file } @@ -225,8 +232,12 @@ impl CaretAccess for FileEditor { let rect = text_character.dest(); let position = CaretPosition::new(text_character.position() + 1, line as usize, 0); - let p = if text_character.is_last_in_line() && text_character.is_new_line() { - rect.top_left() + let p = if text_character.is_new_line() { + file.get_character_at(text_character.position() + 1) + .map_or_else( + || text_character.dest().top_left(), + |tc| tc.dest().top_left(), + ) } else { rect.top_right() }; diff --git a/rider-editor/src/ui/menu_bar.rs b/rider-editor/src/ui/menu_bar.rs index 5824beb..224c703 100644 --- a/rider-editor/src/ui/menu_bar.rs +++ b/rider-editor/src/ui/menu_bar.rs @@ -267,83 +267,35 @@ mod test_render { use crate::tests::support::SimpleRendererMock; use crate::tests::*; use crate::ui::*; - use sdl2::pixels::Color; - use sdl2::rect::{Point, Rect}; - use sdl2::render::Texture; - use std::rc::Rc; - use std::sync::*; - - #[derive(Debug, PartialEq)] - struct CanvasMock { - pub clipping: Rect, - pub background_rect: Rect, - pub background_color: Color, - pub border_rect: Rect, - pub border_color: Color, - } - - impl CanvasAccess for CanvasMock { - fn render_rect(&mut self, rect: Rect, color: Color) -> Result<(), String> { - self.background_color = color; - self.background_rect = rect; - Ok(()) - } - - fn render_border(&mut self, rect: Rect, color: Color) -> Result<(), String> { - self.border_color = color; - self.border_rect = rect; - Ok(()) - } - - fn render_image( - &mut self, - _tex: Rc, - _src: Rect, - _dest: Rect, - ) -> Result<(), String> { - unimplemented!() - } - - fn render_line(&mut self, _start: Point, _end: Point, _color: Color) -> Result<(), String> { - unimplemented!() - } - - fn set_clipping(&mut self, rect: Rect) { - self.clipping = rect; - } - } + use sdl2::rect::Rect; #[test] fn assert_render() { + let rect_color = sdl2::pixels::Color::RGBA(18, 18, 18, 0); + let border_color = sdl2::pixels::Color::RGBA(200, 200, 200, 0); let context = RenderContext::Nothing; let config = support::build_config(); - let mut canvas = CanvasMock { - clipping: Rect::new(0, 0, 0, 0), - background_rect: Rect::new(0, 0, 0, 0), - background_color: Color::RGB(0, 0, 0), - border_rect: Rect::new(0, 0, 0, 0), - border_color: Color::RGB(0, 0, 0), - }; + let mut canvas = support::CanvasMock::new(); let mut renderer = SimpleRendererMock::new(config.clone()); - let mut widget = MenuBar::new(Arc::clone(&config)); + let mut widget = MenuBar::new(config.clone()); widget.prepare_ui(); widget.render(&mut canvas, &mut renderer, &context); assert_eq!(widget.dest(), Rect::new(0, 0, 1024, 40)); - let expected = CanvasMock { - clipping: Rect::new(32, 10, 32, 32), - background_rect: Rect::new(0, 0, 1024, 40), - background_color: Color::RGBA(18, 18, 18, 0), - border_rect: Rect::new(0, 0, 1024, 40), - border_color: Color::RGBA(200, 200, 200, 0), - }; - assert_eq!(canvas, expected); - } - - #[test] - fn assert_prepare_ui() { - let config = support::build_config(); - let mut widget = MenuBar::new(Arc::clone(&config)); - widget.prepare_ui(); - assert_eq!(widget.dest(), Rect::new(0, 0, 1024, 40)); + assert_eq!( + canvas.find_rect_with_color(Rect::new(0, 0, 1024, 40), rect_color.clone()), + Some(&support::RendererRect::new( + Rect::new(0, 0, 1024, 40), + rect_color, + support::CanvasShape::Rectangle + )) + ); + assert_eq!( + canvas.find_border_with_color(Rect::new(0, 0, 1024, 40), border_color.clone()), + Some(&support::RendererRect::new( + Rect::new(0, 0, 1024, 40), + border_color, + support::CanvasShape::Border + )) + ); } } diff --git a/rider-editor/src/ui/mod.rs b/rider-editor/src/ui/mod.rs index 488b1dd..0e8e5a6 100644 --- a/rider-editor/src/ui/mod.rs +++ b/rider-editor/src/ui/mod.rs @@ -59,6 +59,8 @@ pub trait CanvasAccess { ) -> Result<(), String>; fn set_clipping(&mut self, rect: Rect); + fn set_clip_rect(&mut self, rect: Option); + fn clip_rect(&self) -> Option; } impl CanvasAccess for WindowCanvas { @@ -89,6 +91,14 @@ impl CanvasAccess for WindowCanvas { fn set_clipping(&mut self, rect: Rect) { self.set_clip_rect(rect); } + + fn set_clip_rect(&mut self, rect: Option) { + self.set_clip_rect(rect); + } + + fn clip_rect(&self) -> Option { + self.clip_rect() + } } #[inline]