Prevent constant refresh scrollbars, add tests functions, fix caret location on empty line
This commit is contained in:
parent
f06cb0fc1d
commit
60d4c0be88
@ -56,8 +56,6 @@ pub fn move_caret_down(file_editor: &mut FileEditor) -> Option<TextCharacter> {
|
||||
}
|
||||
}
|
||||
let text_character = text_character?;
|
||||
// let character_destination = text_character.dest().clone();
|
||||
// let pos = text_character.position().clone();
|
||||
Some(text_character.clone())
|
||||
})??;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![feature(clamp)]
|
||||
|
||||
extern crate dirs;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
@ -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<RendererRect>,
|
||||
pub borders: Vec<RendererRect>,
|
||||
pub lines: Vec<RendererRect>,
|
||||
pub clippings: Vec<Rect>,
|
||||
pub clippings: Vec<Option<Rect>>,
|
||||
pub character_sizes: HashMap<char, sdl2::rect::Rect>,
|
||||
}
|
||||
|
||||
#[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<Texture>,
|
||||
_src: Rect,
|
||||
_dest: Rect,
|
||||
) -> Result<(), String> {
|
||||
unimplemented!()
|
||||
fn render_image(&mut self, _tex: Rc<Texture>, 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<Texture>")),
|
||||
));
|
||||
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<Rect>) {
|
||||
self.clippings.push(rect);
|
||||
}
|
||||
|
||||
fn clip_rect(&self) -> Option<Rect> {
|
||||
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)]
|
||||
|
@ -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<Texture>,
|
||||
_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);
|
||||
|
@ -111,14 +111,38 @@ impl TextCollection for EditorFile {
|
||||
}
|
||||
|
||||
fn get_last_at_line(&self, line: usize) -> Option<TextCharacter> {
|
||||
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<TextCharacter> = 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,13 +170,20 @@ impl FileAccess for FileEditor {
|
||||
}
|
||||
|
||||
fn open_file(&mut self, file: EditorFile) -> Option<EditorFile> {
|
||||
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();
|
||||
}
|
||||
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()
|
||||
};
|
||||
|
@ -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<Texture>,
|
||||
_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
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,8 @@ pub trait CanvasAccess {
|
||||
) -> Result<(), String>;
|
||||
|
||||
fn set_clipping(&mut self, rect: Rect);
|
||||
fn set_clip_rect(&mut self, rect: Option<Rect>);
|
||||
fn clip_rect(&self) -> Option<Rect>;
|
||||
}
|
||||
|
||||
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<Rect>) {
|
||||
self.set_clip_rect(rect);
|
||||
}
|
||||
|
||||
fn clip_rect(&self) -> Option<Rect> {
|
||||
self.clip_rect()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
Loading…
Reference in New Issue
Block a user