Add display config modal

This commit is contained in:
Adrian Woźniak 2019-06-04 22:46:39 +02:00
parent ae78642ca9
commit 33715a38c7
26 changed files with 696 additions and 141 deletions

View File

@ -13,6 +13,15 @@ rustup run nightly cargo build --all -rr
rustup run nightly cargo run -p rider-editor rustup run nightly cargo run -p rider-editor
``` ```
## Keyboard mapping
* `DELETE` - delete next character
* `BACKSPACE` - delete prev character
* `SHIFT + DELETE` - delete line
* `CTRL + O` - open file
* `CTRL + S` - save current file
* `ESCAPE` - close current modal
## Road map ## Road map
### v1.0 ### v1.0
@ -25,10 +34,9 @@ rustup run nightly cargo run -p rider-editor
* [x] Open file menu * [x] Open file menu
* [x] `Save file` with button * [x] `Save file` with button
* [x] `Save file` with shortcut * [x] `Save file` with shortcut
* [ ] `Save file as...` with shortcut
* [x] Theme based menu UI * [x] Theme based menu UI
* [x] Lock scroll when no available content * [x] Lock scroll when no available content
* [ ] Config edit menu * [x] Config edit menu
* [x] Project tree * [x] Project tree
* [x] Cover `rider` with tests at least 50% * [x] Cover `rider` with tests at least 50%
* [x] Handle resize window * [x] Handle resize window

View File

@ -205,7 +205,7 @@ mod test_getters {
fn assert_menu_height() { fn assert_menu_height() {
let config = Config::new(); let config = Config::new();
let result = config.menu_height(); let result = config.menu_height();
let expected = 60; let expected = 40;
assert_eq!(result, expected); assert_eq!(result, expected);
} }

View File

@ -16,7 +16,7 @@ pub struct AppState {
files: Vec<EditorFile>, files: Vec<EditorFile>,
config: Arc<RwLock<Config>>, config: Arc<RwLock<Config>>,
file_editor: FileEditor, file_editor: FileEditor,
open_file_modal: Option<OpenFile>, modal: Option<ModalType>,
} }
impl AppState { impl AppState {
@ -29,7 +29,7 @@ impl AppState {
), ),
files: vec![], files: vec![],
file_editor: FileEditor::new(Arc::clone(&config)), file_editor: FileEditor::new(Arc::clone(&config)),
open_file_modal: None, modal: None,
config, config,
} }
} }
@ -65,13 +65,35 @@ impl AppState {
Ok(()) Ok(())
} }
pub fn open_settings<R>(&mut self, renderer: &mut R) -> Result<(), String>
where
R: Renderer + CharacterSizeManager,
{
println!("Open Settings...");
match self.modal {
None => {
let mut settings = Settings::new(self.config.clone());
settings.prepare_ui(renderer);
self.modal = Some(ModalType::Settings(settings));
}
_ => return Ok(()),
}
Ok(())
}
pub fn close_modal(&mut self) -> Result<(), String> {
self.modal = None;
Ok(())
}
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R) pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R)
where where
R: Renderer + CharacterSizeManager, R: Renderer + CharacterSizeManager,
{ {
match self.open_file_modal.as_mut() { match self.modal.as_mut() {
Some(modal) => modal.open_directory(dir_path, renderer), Some(ModalType::OpenFile(modal)) => modal.open_directory(dir_path, renderer),
None => self.project_tree.open_directory(dir_path, renderer), None => self.project_tree.open_directory(dir_path, renderer),
_ => (),
}; };
} }
@ -86,19 +108,33 @@ impl AppState {
} }
pub fn set_open_file_modal(&mut self, modal: Option<OpenFile>) { pub fn set_open_file_modal(&mut self, modal: Option<OpenFile>) {
self.open_file_modal = modal; self.modal = if let Some(modal) = modal {
Some(ModalType::OpenFile(modal))
} else {
None
};
} }
pub fn scroll_by(&mut self, x: i32, y: i32) { pub fn scroll_by(&mut self, x: i32, y: i32) {
if let Some(modal) = self.open_file_modal.as_mut() { match self.modal.as_mut() {
modal.scroll_by(x, y); Some(ModalType::OpenFile(modal)) => modal.scroll_by(x, y),
} else { Some(ModalType::Settings(modal)) => modal.scroll_by(x, y),
self.file_editor_mut().scroll_by(x, y); _ => self.file_editor_mut().scroll_by(x, y),
} };
} }
pub fn open_file_modal(&self) -> Option<&OpenFile> { pub fn open_file_modal(&self) -> Option<&OpenFile> {
self.open_file_modal.as_ref() match self.modal {
Some(ModalType::OpenFile(ref m)) => Some(m),
_ => None,
}
}
pub fn settings_modal(&self) -> Option<&Settings> {
match self.modal {
Some(ModalType::Settings(ref m)) => Some(m),
_ => None,
}
} }
} }
@ -119,9 +155,14 @@ impl AppState {
// project tree // project tree
self.project_tree.render(canvas, renderer); self.project_tree.render(canvas, renderer);
// open file modal // settings modal
match self.open_file_modal.as_ref() { match self.modal.as_ref() {
Some(modal) => modal.render(canvas, renderer, &RenderContext::Nothing), Some(ModalType::OpenFile(modal)) => {
return modal.render(canvas, renderer, &RenderContext::Nothing)
}
Some(ModalType::Settings(modal)) => {
return modal.render(canvas, renderer, &RenderContext::Nothing)
}
_ => (), _ => (),
}; };
} }
@ -136,10 +177,10 @@ impl AppState {
} }
pub fn update(&mut self, ticks: i32, context: &UpdateContext) -> UpdateResult { pub fn update(&mut self, ticks: i32, context: &UpdateContext) -> UpdateResult {
// open file modal let res = match self.modal.as_mut() {
let res = match self.open_file_modal.as_mut() { Some(ModalType::OpenFile(modal)) => modal.update(ticks, context.clone()),
Some(modal) => modal.update(ticks, &UpdateContext::Nothing), Some(ModalType::Settings(modal)) => modal.update(ticks, context.clone()),
_ => UpdateResult::NoOp, None => UpdateResult::NoOp,
}; };
if res != UpdateResult::NoOp { if res != UpdateResult::NoOp {
return res; return res;
@ -171,8 +212,13 @@ impl AppState {
.project_tree .project_tree
.on_left_click(point, &UpdateContext::Nothing); .on_left_click(point, &UpdateContext::Nothing);
} }
match self.open_file_modal.as_mut() { match self.modal.as_mut() {
Some(modal) => return modal.on_left_click(point, &UpdateContext::Nothing), Some(ModalType::OpenFile(modal)) => {
return modal.on_left_click(point, &UpdateContext::Nothing)
}
Some(ModalType::Settings(modal)) => {
return modal.on_left_click(point, &UpdateContext::Nothing)
}
_ => (), _ => (),
}; };
if self if self
@ -188,10 +234,10 @@ impl AppState {
return UpdateResult::NoOp; return UpdateResult::NoOp;
} else { } else {
video_subsystem.text_input().start(); video_subsystem.text_input().start();
self.file_editor return self
.file_editor
.on_left_click(point, &UpdateContext::Nothing); .on_left_click(point, &UpdateContext::Nothing);
} }
UpdateResult::NoOp
} }
pub fn is_left_click_target(&self, _point: &Point) -> bool { pub fn is_left_click_target(&self, _point: &Point) -> bool {

View File

@ -3,6 +3,7 @@ pub use crate::renderer::CanvasRenderer;
use crate::ui::caret::{CaretPosition, MoveDirection}; use crate::ui::caret::{CaretPosition, MoveDirection};
use crate::ui::*; use crate::ui::*;
pub use rider_config::{Config, ConfigAccess, ConfigHolder}; pub use rider_config::{Config, ConfigAccess, ConfigHolder};
use sdl2::event::*; use sdl2::event::*;
use sdl2::hint; use sdl2::hint;
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
@ -24,7 +25,7 @@ use std::time::Duration;
pub type WindowCanvas = Canvas<Window>; pub type WindowCanvas = Canvas<Window>;
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Debug)]
pub enum UpdateResult { pub enum UpdateResult {
NoOp, NoOp,
Stop, Stop,
@ -35,6 +36,7 @@ pub enum UpdateResult {
MoveCaret(Rect, CaretPosition), MoveCaret(Rect, CaretPosition),
DeleteFront, DeleteFront,
DeleteBack, DeleteBack,
DeleteLine,
Input(String), Input(String),
InsertNewLine, InsertNewLine,
MoveCaretLeft, MoveCaretLeft,
@ -49,6 +51,8 @@ pub enum UpdateResult {
OpenFileModal, OpenFileModal,
FileDropped(String), FileDropped(String),
SaveCurrentFile, SaveCurrentFile,
OpenSettings,
CloseModal,
} }
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
@ -137,6 +141,7 @@ impl Application {
let res = app_state.on_left_click(&point, &mut self.video_subsystem); let res = app_state.on_left_click(&point, &mut self.video_subsystem);
match res { match res {
UpdateResult::OpenDirectory(_) => new_tasks.push(res), UpdateResult::OpenDirectory(_) => new_tasks.push(res),
UpdateResult::OpenSettings => new_tasks.push(res),
UpdateResult::OpenFile(_) => { UpdateResult::OpenFile(_) => {
new_tasks.push(res); new_tasks.push(res);
app_state.set_open_file_modal(None); app_state.set_open_file_modal(None);
@ -154,9 +159,14 @@ impl Application {
UpdateResult::Input(text) => app_state UpdateResult::Input(text) => app_state
.file_editor_mut() .file_editor_mut()
.insert_text(text.clone(), &mut renderer), .insert_text(text.clone(), &mut renderer),
UpdateResult::InsertNewLine => { UpdateResult::InsertNewLine => app_state
app_state.file_editor_mut().insert_new_line(&mut renderer); .file_editor_mut()
} .insert_new_line(&mut renderer)
.unwrap_or_else(|e| eprintln!("Failed to delete line {:?}", e)),
UpdateResult::DeleteLine => app_state
.file_editor_mut()
.delete_current_line(&mut renderer)
.unwrap_or_else(|e| eprintln!("Failed to delete line {:?}", e)),
UpdateResult::MoveCaretLeft => { UpdateResult::MoveCaretLeft => {
app_state.file_editor_mut().move_caret(MoveDirection::Left); app_state.file_editor_mut().move_caret(MoveDirection::Left);
} }
@ -205,6 +215,12 @@ impl Application {
UpdateResult::SaveCurrentFile => app_state UpdateResult::SaveCurrentFile => app_state
.save_file() .save_file()
.unwrap_or_else(|e| eprintln!("Failed to save {:?}", e)), .unwrap_or_else(|e| eprintln!("Failed to save {:?}", e)),
UpdateResult::OpenSettings => app_state
.open_settings(&mut renderer)
.unwrap_or_else(|e| eprintln!("Failed to open settings {:?}", e)),
UpdateResult::CloseModal => app_state
.close_modal()
.unwrap_or_else(|e| eprintln!("Failed to close modal {:?}", e)),
} }
} }
self.tasks = new_tasks; self.tasks = new_tasks;
@ -266,12 +282,18 @@ impl Application {
.tasks .tasks
.push(UpdateResult::MouseDragStart(Point::new(x, y))), .push(UpdateResult::MouseDragStart(Point::new(x, y))),
Event::KeyDown { keycode, .. } if keycode.is_some() => match keycode.unwrap() { Event::KeyDown { keycode, .. } if keycode.is_some() => match keycode.unwrap() {
Keycode::Escape => {
self.tasks.push(UpdateResult::CloseModal);
}
Keycode::Backspace => { Keycode::Backspace => {
self.tasks.push(UpdateResult::DeleteFront); self.tasks.push(UpdateResult::DeleteFront);
} }
Keycode::Delete => { Keycode::Delete if !shift_pressed => {
self.tasks.push(UpdateResult::DeleteBack); self.tasks.push(UpdateResult::DeleteBack);
} }
Keycode::Delete if shift_pressed => {
self.tasks.push(UpdateResult::DeleteLine);
}
Keycode::KpEnter | Keycode::Return => { Keycode::KpEnter | Keycode::Return => {
self.tasks.push(UpdateResult::InsertNewLine); self.tasks.push(UpdateResult::InsertNewLine);
} }
@ -307,9 +329,7 @@ impl Application {
MouseWheelDirection::Flipped => { MouseWheelDirection::Flipped => {
self.tasks.push(UpdateResult::Scroll { x, y: -y }); self.tasks.push(UpdateResult::Scroll { x, y: -y });
} }
_ => { _ => (),
// ignore
}
}, },
Event::Window { Event::Window {
win_event: WindowEvent::Resized(w, h), win_event: WindowEvent::Resized(w, h),
@ -318,7 +338,7 @@ impl Application {
width: w, width: w,
height: h, height: h,
}), }),
_ => {} _ => (),
} }
} }
} }

View File

@ -104,14 +104,14 @@ where
file_editor.replace_current_file(new_file); file_editor.replace_current_file(new_file);
} }
pub fn insert_new_line<R>(file_editor: &mut FileEditor, renderer: &mut R) pub fn insert_new_line<R>(file_editor: &mut FileEditor, renderer: &mut R) -> Result<(), String>
where where
R: ConfigHolder + CharacterSizeManager + Renderer, R: ConfigHolder + CharacterSizeManager + Renderer,
{ {
let mut buffer: String = match file_editor.file() { let mut buffer: String = file_editor
Some(file) => file.buffer(), .file()
None => return, .map(|file| file.buffer())
}; .ok_or_else(|| "No file is open".to_string())?;
let maybe_character = file_editor let maybe_character = file_editor
.file() .file()
@ -134,6 +134,36 @@ where
); );
new_file.prepare_ui(renderer); new_file.prepare_ui(renderer);
file_editor.replace_current_file(new_file); file_editor.replace_current_file(new_file);
Ok(())
}
pub fn delete_current_line<R>(file_editor: &mut FileEditor, renderer: &mut R) -> Result<(), String>
where
R: ConfigHolder + CharacterSizeManager + Renderer,
{
let file: &EditorFile = file_editor
.file()
.ok_or_else(|| "No file is open".to_string())?;
let mut new_buffer = String::new();
let target_line = file_editor.caret().line_number();
let mut current_line = 0;
for c in file.buffer_ref().chars() {
match c {
'\n' if current_line == target_line => {
current_line += 1;
}
'\n' => {
current_line += 1;
new_buffer.push(c);
}
_ if current_line == target_line => (),
_ => new_buffer.push(c),
}
}
let mut new_file = EditorFile::new(file.path(), new_buffer, file_editor.config().clone());
new_file.prepare_ui(renderer);
file_editor.replace_current_file(new_file);
Ok(())
} }
#[cfg(test)] #[cfg(test)]
@ -309,7 +339,8 @@ mod tests {
let mut renderer = RendererMock::new(config.clone()); let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone()); let mut widget = FileEditor::new(config.clone());
widget.prepare_ui(&mut renderer); widget.prepare_ui(&mut renderer);
widget.insert_new_line(&mut renderer); let res: Result<(), String> = widget.insert_new_line(&mut renderer);
assert_eq!(res.is_ok(), false);
let expected = CaretPosition::new(0, 0, 0); let expected = CaretPosition::new(0, 0, 0);
assert_eq!(widget.caret().position(), &expected); assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 0, 6, 15); let expected = Rect::new(0, 0, 6, 15);
@ -324,7 +355,8 @@ mod tests {
let file = EditorFile::new("".to_owned(), "".to_owned(), config.clone()); let file = EditorFile::new("".to_owned(), "".to_owned(), config.clone());
widget.open_file(file); widget.open_file(file);
widget.prepare_ui(&mut renderer); widget.prepare_ui(&mut renderer);
widget.insert_new_line(&mut renderer); let res: Result<(), String> = widget.insert_new_line(&mut renderer);
assert_eq!(res.is_ok(), true);
let expected = CaretPosition::new(1, 1, 0); let expected = CaretPosition::new(1, 1, 0);
assert_eq!(widget.caret().position(), &expected); assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 13, 6, 15); let expected = Rect::new(0, 13, 6, 15);
@ -339,7 +371,8 @@ mod tests {
let file = EditorFile::new("".to_owned(), "foo".to_owned(), config.clone()); let file = EditorFile::new("".to_owned(), "foo".to_owned(), config.clone());
widget.open_file(file); widget.open_file(file);
widget.prepare_ui(&mut renderer); widget.prepare_ui(&mut renderer);
widget.insert_new_line(&mut renderer); let res: Result<(), String> = widget.insert_new_line(&mut renderer);
assert_eq!(res.is_ok(), true);
let expected = CaretPosition::new(1, 1, 0); let expected = CaretPosition::new(1, 1, 0);
assert_eq!(widget.caret().position(), &expected); assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 13, 6, 15); let expected = Rect::new(0, 13, 6, 15);
@ -359,7 +392,8 @@ mod tests {
widget.prepare_ui(&mut renderer); widget.prepare_ui(&mut renderer);
widget.move_caret(MoveDirection::Right); widget.move_caret(MoveDirection::Right);
widget.move_caret(MoveDirection::Right); widget.move_caret(MoveDirection::Right);
widget.insert_new_line(&mut renderer); let res: Result<(), String> = widget.insert_new_line(&mut renderer);
assert_eq!(res.is_ok(), true);
let expected = CaretPosition::new(3, 1, 0); let expected = CaretPosition::new(3, 1, 0);
assert_eq!(widget.caret().position(), &expected); assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 13, 6, 15); let expected = Rect::new(0, 13, 6, 15);

View File

@ -0,0 +1,5 @@
pub mod save_button;
pub mod settings_button;
pub use self::save_button::*;
pub use self::settings_button::*;

View File

@ -32,7 +32,7 @@ impl SaveButton {
{ {
use std::borrow::*; use std::borrow::*;
let mut dest = match context { let mut dest = match context {
&RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.dest), &RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(), _ => self.dest.clone(),
}; };
@ -78,8 +78,6 @@ impl SaveButton {
impl Update for SaveButton { impl Update for SaveButton {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR { fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR {
let config = self.config.read().unwrap();
self.dest.set_width(config.width());
UR::NoOp UR::NoOp
} }
} }
@ -92,7 +90,7 @@ impl ClickHandler for SaveButton {
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool { fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match *context { match *context {
UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest), UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest(), _ => self.dest,
} }
.contains_point(point.clone()) .contains_point(point.clone())
} }

View File

@ -32,7 +32,7 @@ impl SettingsButton {
{ {
use std::borrow::*; use std::borrow::*;
let mut dest = match context { let mut dest = match context {
&RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.dest), &RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(), _ => self.dest.clone(),
}; };
@ -78,21 +78,19 @@ impl SettingsButton {
impl Update for SettingsButton { impl Update for SettingsButton {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR { fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR {
let config = self.config.read().unwrap();
self.dest.set_width(config.width());
UR::NoOp UR::NoOp
} }
} }
impl ClickHandler for SettingsButton { impl ClickHandler for SettingsButton {
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR { fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::NoOp UR::OpenSettings
} }
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool { fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match *context { match *context {
UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest), UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest(), _ => self.dest,
} }
.contains_point(point.clone()) .contains_point(point.clone())
} }

View File

@ -72,7 +72,7 @@ impl Caret {
use std::borrow::*; use std::borrow::*;
let dest = match context.borrow() { let dest = match context.borrow() {
RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.dest), RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest().clone(), _ => self.dest().clone(),
}; };
let start = Point::new(dest.x(), dest.y()); let start = Point::new(dest.x(), dest.y());
@ -368,7 +368,7 @@ mod test_render {
#[test] #[test]
fn assert_render_line() { fn assert_render_line() {
let config = build_config(); let config = build_config();
let context = RenderContext::RelativePosition(Point::new(10, 14)); let context = RenderContext::ParentPosition(Point::new(10, 14));
let mut canvas = CanvasMock { let mut canvas = CanvasMock {
start: Point::new(0, 0), start: Point::new(0, 0),
end: Point::new(0, 0), end: Point::new(0, 0),

View File

@ -47,6 +47,10 @@ impl EditorFile {
self.buffer.clone() self.buffer.clone()
} }
pub fn buffer_ref(&self) -> &String {
&self.buffer
}
pub fn path(&self) -> String { pub fn path(&self) -> String {
self.path.clone() self.path.clone()
} }

View File

@ -80,7 +80,8 @@ impl EditorFileSection {
} }
} }
fn iter_char(&self) -> EditorFileSectionIterator { #[inline]
pub fn iter_char(&self) -> EditorFileSectionIterator {
EditorFileSectionIterator::new(self) EditorFileSectionIterator::new(self)
} }

View File

@ -40,6 +40,7 @@ impl EditorFileToken {
} }
} }
#[inline]
pub fn characters(&self) -> &Vec<TextCharacter> { pub fn characters(&self) -> &Vec<TextCharacter> {
&self.characters &self.characters
} }
@ -59,7 +60,8 @@ impl EditorFileToken {
} }
} }
fn iter_char(&self) -> EditorFileTokenIterator { #[inline]
pub fn iter_char(&self) -> EditorFileTokenIterator {
EditorFileTokenIterator::new(self) EditorFileTokenIterator::new(self)
} }
} }
@ -82,12 +84,10 @@ impl TextWidget for EditorFileToken {
impl TextCollection for EditorFileToken { impl TextCollection for EditorFileToken {
fn get_character_at(&self, index: usize) -> Option<TextCharacter> { fn get_character_at(&self, index: usize) -> Option<TextCharacter> {
for character in self.characters.iter() { self.characters
if character.position() == index { .iter()
return Some(character.clone()); .find(|character| character.position() == index)
} .cloned()
}
None
} }
fn get_line(&self, line: &usize) -> Option<Vec<&TextCharacter>> { fn get_line(&self, line: &usize) -> Option<Vec<&TextCharacter>> {

View File

@ -106,11 +106,18 @@ impl FileEditor {
file_content_manager::insert_text(self, text, renderer); file_content_manager::insert_text(self, text, renderer);
} }
pub fn insert_new_line<R>(&mut self, renderer: &mut R) pub fn insert_new_line<R>(&mut self, renderer: &mut R) -> Result<(), String>
where where
R: ConfigHolder + CharacterSizeManager + Renderer, R: ConfigHolder + CharacterSizeManager + Renderer,
{ {
file_content_manager::insert_new_line(self, renderer); file_content_manager::insert_new_line(self, renderer)
}
pub fn delete_current_line<R>(&mut self, renderer: &mut R) -> Result<(), String>
where
R: ConfigHolder + CharacterSizeManager + Renderer,
{
file_content_manager::delete_current_line(self, renderer)
} }
fn is_text_character_clicked(&self, point: &Point) -> bool { fn is_text_character_clicked(&self, point: &Point) -> bool {
@ -264,22 +271,18 @@ impl FileEditor {
Some(file) => file.render( Some(file) => file.render(
canvas, canvas,
renderer, renderer,
&RenderContext::RelativePosition(self.render_start_point()), &RenderContext::ParentPosition(self.render_start_point()),
), ),
_ => (), _ => (),
}; };
self.caret.render( self.caret.render(
canvas, canvas,
&RenderContext::RelativePosition(self.render_start_point()), &RenderContext::ParentPosition(self.render_start_point()),
);
self.vertical_scroll_bar.render(
canvas,
&RenderContext::RelativePosition(self.dest.top_left()),
);
self.horizontal_scroll_bar.render(
canvas,
&RenderContext::RelativePosition(self.dest.top_left()),
); );
self.vertical_scroll_bar
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
self.horizontal_scroll_bar
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
} }
pub fn prepare_ui<T>(&mut self, renderer: &mut T) pub fn prepare_ui<T>(&mut self, renderer: &mut T)

View File

@ -252,12 +252,12 @@ impl DirectoryView {
self.icon_height as i32 + CHILD_MARGIN, self.icon_height as i32 + CHILD_MARGIN,
); );
for dir in self.directories.iter() { for dir in self.directories.iter() {
let context = RenderContext::RelativePosition(point.clone()); let context = RenderContext::ParentPosition(point.clone());
dir.render(canvas, renderer, &context); dir.render(canvas, renderer, &context);
point = point + Point::new(0, dir.height() as i32 + CHILD_MARGIN as i32); point = point + Point::new(0, dir.height() as i32 + CHILD_MARGIN as i32);
} }
for file in self.files.iter() { for file in self.files.iter() {
let context = RenderContext::RelativePosition(point.clone()); let context = RenderContext::ParentPosition(point.clone());
file.render(canvas, renderer, &context); file.render(canvas, renderer, &context);
point = point + Point::new(0, file.height() as i32 + CHILD_MARGIN as i32); point = point + Point::new(0, file.height() as i32 + CHILD_MARGIN as i32);
} }
@ -302,7 +302,7 @@ impl DirectoryView {
{ {
let dest = self.dest(); let dest = self.dest();
let move_point = match context { let move_point = match context {
&RenderContext::RelativePosition(p) => p.clone(), &RenderContext::ParentPosition(p) => p.clone(),
_ => Point::new(0, 0), _ => Point::new(0, 0),
}; };
let mut dest = move_render_point(move_point, &dest); let mut dest = move_render_point(move_point, &dest);

View File

@ -139,7 +139,7 @@ impl FileEntry {
R: Renderer, R: Renderer,
{ {
let mut dest = match context { let mut dest = match context {
&RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.dest), &RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(), _ => self.dest.clone(),
}; };
self.render_icon(canvas, renderer, &mut dest); self.render_icon(canvas, renderer, &mut dest);

View File

@ -0,0 +1,121 @@
use crate::app::*;
use crate::renderer::*;
use crate::ui::*;
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
use std::collections::HashMap;
const DEST_WIDTH: u32 = 16;
const DEST_HEIGHT: u32 = 16;
const SRC_WIDTH: u32 = 64;
const SRC_HEIGHT: u32 = 64;
pub struct Label {
name_width: u32,
height: u32,
name: String,
source: Rect,
dest: Rect,
char_sizes: HashMap<char, Rect>,
config: ConfigAccess,
}
impl Label {
pub fn new(name: String, config: ConfigAccess) -> Self {
Self {
name,
name_width: 0,
height: 0,
dest: Rect::new(0, 0, DEST_WIDTH, DEST_HEIGHT),
source: Rect::new(0, 0, SRC_WIDTH, SRC_HEIGHT),
config,
char_sizes: HashMap::new(),
}
}
pub fn name_width(&self) -> u32 {
self.name_width
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer,
{
let dest = match context {
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(),
};
let mut d = dest.clone();
d.set_x(dest.x() + NAME_MARGIN);
let font_details = build_font_details(self);
for c in self.name.chars() {
let size = self
.char_sizes
.get(&c)
.unwrap_or(&Rect::new(0, 0, 0, 0))
.clone();
let mut text_details = TextDetails {
color: Color::RGBA(255, 255, 255, 0),
text: c.to_string(),
font: font_details.clone(),
};
let maybe_texture = renderer.load_text_tex(&mut text_details, font_details.clone());
if let Ok(texture) = maybe_texture {
d.set_width(size.width());
d.set_height(size.height());
canvas
.render_image(texture, self.source.clone(), d.clone())
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
d.set_x(d.x() + size.width() as i32);
}
}
}
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
{
let w_rect = renderer.load_character_size('W');
self.char_sizes.insert('W', w_rect.clone());
self.height = w_rect.height();
self.name_width = 0;
for c in self.name().chars() {
let size = { renderer.load_character_size(c.clone()) };
self.char_sizes.insert(c, size);
self.name_width += size.width();
}
self.dest.set_width(w_rect.height());
self.dest.set_height(w_rect.height());
}
}
impl RenderBox for Label {
fn render_start_point(&self) -> Point {
self.dest.top_left()
}
fn dest(&self) -> Rect {
self.dest.clone()
}
}
impl Update for Label {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UpdateResult {
UpdateResult::NoOp
}
}
impl ConfigHolder for Label {
fn config(&self) -> &ConfigAccess {
&self.config
}
}

View File

@ -50,7 +50,7 @@ impl MenuBar {
use std::borrow::*; use std::borrow::*;
let relative_position = match context.borrow() { let relative_position = match context.borrow() {
RenderContext::RelativePosition(p) => p.clone(), RenderContext::ParentPosition(p) => p.clone(),
_ => Point::new(0, 0), _ => Point::new(0, 0),
}; };
@ -64,21 +64,19 @@ impl MenuBar {
canvas canvas
.render_border( .render_border(
match context.borrow() { match context.borrow() {
RenderContext::RelativePosition(p) => { RenderContext::ParentPosition(p) => move_render_point((*p).clone(), &self.dest),
move_render_point((*p).clone(), &self.dest)
}
_ => self.dest(), _ => self.dest(),
}, },
self.border_color.clone(), self.border_color.clone(),
) )
.unwrap_or_else(|_| panic!("Failed to draw main menu background")); .unwrap_or_else(|_| panic!("Failed to draw main menu background"));
let context = RenderContext::RelativePosition( let context = RenderContext::ParentPosition(
relative_position.offset(SAVE_BUTTON_OFFSET_LEFT, SAVE_BUTTON_OFFSET_TOP), relative_position.offset(SAVE_BUTTON_OFFSET_LEFT, SAVE_BUTTON_OFFSET_TOP),
); );
self.save_button.render(canvas, renderer, &context); self.save_button.render(canvas, renderer, &context);
let context = RenderContext::RelativePosition( let context = RenderContext::ParentPosition(
relative_position.offset(SAVE_BUTTON_OFFSET_LEFT * 2, SAVE_BUTTON_OFFSET_TOP), relative_position.offset(SAVE_BUTTON_OFFSET_LEFT * 2, SAVE_BUTTON_OFFSET_TOP),
); );
self.settings_button.render(canvas, renderer, &context); self.settings_button.render(canvas, renderer, &context);
@ -324,12 +322,12 @@ mod test_render {
let mut widget = MenuBar::new(Arc::clone(&config)); let mut widget = MenuBar::new(Arc::clone(&config));
widget.prepare_ui(); widget.prepare_ui();
widget.render(&mut canvas, &mut renderer, &context); widget.render(&mut canvas, &mut renderer, &context);
assert_eq!(widget.dest(), Rect::new(0, 0, 1024, 60)); assert_eq!(widget.dest(), Rect::new(0, 0, 1024, 40));
let expected = CanvasMock { let expected = CanvasMock {
clipping: Rect::new(0, 0, 1024, 60), clipping: Rect::new(32, 10, 32, 32),
background_rect: Rect::new(0, 0, 1024, 60), background_rect: Rect::new(0, 0, 1024, 40),
background_color: Color::RGBA(18, 18, 18, 0), background_color: Color::RGBA(18, 18, 18, 0),
border_rect: Rect::new(0, 0, 1024, 60), border_rect: Rect::new(0, 0, 1024, 40),
border_color: Color::RGBA(200, 200, 200, 0), border_color: Color::RGBA(200, 200, 200, 0),
}; };
assert_eq!(canvas, expected); assert_eq!(canvas, expected);
@ -340,6 +338,6 @@ mod test_render {
let config = support::build_config(); let config = support::build_config();
let mut widget = MenuBar::new(Arc::clone(&config)); let mut widget = MenuBar::new(Arc::clone(&config));
widget.prepare_ui(); widget.prepare_ui();
assert_eq!(widget.dest(), Rect::new(0, 0, 1024, 60)); assert_eq!(widget.dest(), Rect::new(0, 0, 1024, 40));
} }
} }

View File

@ -8,29 +8,29 @@ use crate::app::UpdateResult as UR;
use crate::renderer::managers::*; use crate::renderer::managers::*;
use rider_config::*; use rider_config::*;
pub mod buttons;
pub mod caret; pub mod caret;
pub mod file; pub mod file;
pub mod file_editor; pub mod file_editor;
pub mod filesystem; pub mod filesystem;
pub mod label;
pub mod menu_bar; pub mod menu_bar;
pub mod modal; pub mod modal;
pub mod project_tree; pub mod project_tree;
pub mod save_button;
pub mod scroll_bar; pub mod scroll_bar;
pub mod settings_button;
pub mod text_character; pub mod text_character;
pub use crate::ui::caret::*; pub use self::buttons::*;
pub use crate::ui::file::*; pub use self::caret::*;
pub use crate::ui::file_editor::*; pub use self::file::*;
pub use crate::ui::filesystem::*; pub use self::file_editor::*;
pub use crate::ui::menu_bar::*; pub use self::filesystem::*;
pub use crate::ui::modal::*; pub use self::label::*;
pub use crate::ui::project_tree::*; pub use self::menu_bar::*;
pub use crate::ui::save_button::*; pub use self::modal::*;
pub use crate::ui::scroll_bar::*; pub use self::project_tree::*;
pub use crate::ui::settings_button::*; pub use self::scroll_bar::*;
pub use crate::ui::text_character::*; pub use self::text_character::*;
#[derive(Debug)] #[derive(Debug)]
pub enum UpdateContext<'l> { pub enum UpdateContext<'l> {
@ -42,7 +42,7 @@ pub enum UpdateContext<'l> {
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum RenderContext { pub enum RenderContext {
Nothing, Nothing,
RelativePosition(Point), ParentPosition(Point),
} }
pub trait CanvasAccess { pub trait CanvasAccess {

View File

@ -1,3 +1,30 @@
pub mod open_file; pub mod open_file;
pub mod settings;
pub use crate::ui::modal::open_file::OpenFile; pub use self::open_file::*;
pub use self::settings::*;
pub enum ModalType {
OpenFile(OpenFile),
Settings(Settings),
}
impl PartialEq for ModalType {
fn eq(&self, other: &ModalType) -> bool {
match (self, other) {
(ModalType::OpenFile { .. }, ModalType::OpenFile { .. }) => true,
(ModalType::Settings { .. }, ModalType::Settings { .. }) => true,
_ => false,
}
}
}
impl std::fmt::Debug for ModalType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let name = match self {
ModalType::OpenFile(_) => "OpenFile",
ModalType::Settings(_) => "Settings",
};
write!(f, "<Modal::{:?} {{}}", name)
}
}

View File

@ -1,3 +1,4 @@
use crate::app::UpdateResult as UR;
use crate::renderer::renderer::Renderer; use crate::renderer::renderer::Renderer;
use crate::ui::*; use crate::ui::*;
use crate::ui::{RenderContext as RC, UpdateContext as UC}; use crate::ui::{RenderContext as RC, UpdateContext as UC};
@ -151,7 +152,7 @@ impl OpenFile {
R: Renderer + CharacterSizeManager + ConfigHolder, R: Renderer + CharacterSizeManager + ConfigHolder,
{ {
let dest = match context { let dest = match context {
RC::RelativePosition(p) => move_render_point(p.clone(), &self.dest), RC::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(), _ => self.dest.clone(),
}; };
@ -164,7 +165,7 @@ impl OpenFile {
.render_border(dest, self.border_color) .render_border(dest, self.border_color)
.unwrap_or_else(|_| panic!("Failed to render open file modal border!")); .unwrap_or_else(|_| panic!("Failed to render open file modal border!"));
let context = RC::RelativePosition( let context = RC::ParentPosition(
dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP) + self.scroll(), dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP) + self.scroll(),
); );
@ -172,14 +173,10 @@ impl OpenFile {
self.directory_view.render(canvas, renderer, &context); self.directory_view.render(canvas, renderer, &context);
// Scroll bars // Scroll bars
self.vertical_scroll_bar.render( self.vertical_scroll_bar
canvas, .render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
&RenderContext::RelativePosition(self.dest.top_left()), self.horizontal_scroll_bar
); .render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
self.horizontal_scroll_bar.render(
canvas,
&RenderContext::RelativePosition(self.dest.top_left()),
);
} }
pub fn prepare_ui<R>(&mut self, renderer: &mut R) pub fn prepare_ui<R>(&mut self, renderer: &mut R)
@ -397,7 +394,7 @@ mod tests {
let mut canvas = CanvasMock::new(); let mut canvas = CanvasMock::new();
let widget = OpenFile::new(path.to_owned(), 100, 100, config); let widget = OpenFile::new(path.to_owned(), 100, 100, config);
let p = Point::new(100, 100); let p = Point::new(100, 100);
let context = RenderContext::RelativePosition(p); let context = RenderContext::ParentPosition(p);
widget.render(&mut canvas, &mut renderer, &context); widget.render(&mut canvas, &mut renderer, &context);
} }

View File

@ -0,0 +1,238 @@
use crate::app::UpdateResult as UR;
use crate::renderer::renderer::Renderer;
use crate::ui::*;
use crate::ui::{RenderContext as RC, UpdateContext as UC};
use rider_config::ConfigAccess;
use rider_config::ConfigHolder;
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
use std::sync::Arc;
const CONTENT_MARGIN_LEFT: i32 = 16;
const CONTENT_MARGIN_TOP: i32 = 24;
const DEFAULT_ICON_SIZE: u32 = 16;
pub struct Settings {
vertical_scroll_bar: VerticalScrollBar,
horizontal_scroll_bar: HorizontalScrollBar,
dest: Rect,
full_dest: Rect,
background_color: Color,
border_color: Color,
config: ConfigAccess,
font_label: Label,
font_value: Label,
}
impl Settings {
pub fn new(config: ConfigAccess) -> Self {
// let (window_width, window_height, background_color, border_color) = {
let c = config
.read()
.unwrap_or_else(|_| panic!("Failed to read config"));
let theme = c.theme();
let window_width = c.width();
let window_height = c.height();
let background_color = theme.background().into();
let border_color = theme.border_color().into();
// };
Self {
vertical_scroll_bar: VerticalScrollBar::new(Arc::clone(&config)),
horizontal_scroll_bar: HorizontalScrollBar::new(Arc::clone(&config)),
dest: Rect::new(
CONTENT_MARGIN_LEFT,
CONTENT_MARGIN_TOP,
window_width - (CONTENT_MARGIN_LEFT * 2) as u32,
window_height - (CONTENT_MARGIN_TOP * 2) as u32,
),
full_dest: Rect::new(0, 0, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE),
background_color,
border_color,
font_label: Label::new("Font path".into(), config.clone()),
font_value: Label::new(c.editor_config().font_path().clone(), config.clone()),
config: config.clone(),
}
}
pub fn full_rect(&self) -> &Rect {
&self.full_dest
}
pub fn scroll_by(&mut self, x: i32, y: i32) {
let read_config = self.config.read().unwrap();
let value_x = read_config.scroll().speed() * x;
let value_y = read_config.scroll().speed() * y;
let old_x = self.horizontal_scroll_bar.scroll_value();
let old_y = self.vertical_scroll_bar.scroll_value();
if value_x + old_x >= 0 {
self.horizontal_scroll_bar.scroll_to(value_x + old_x);
if self.horizontal_scroll_bar.scrolled_part() > 1.0 {
self.horizontal_scroll_bar.scroll_to(old_x);
}
}
if value_y + old_y >= 0 {
self.vertical_scroll_bar.scroll_to(value_y + old_y);
if self.vertical_scroll_bar.scrolled_part() > 1.0 {
self.vertical_scroll_bar.scroll_to(old_y);
}
}
}
pub fn scroll(&self) -> Point {
Point::new(
-self.horizontal_scroll_bar.scroll_value(),
-self.vertical_scroll_bar.scroll_value(),
)
}
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RC)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let dest = match context {
RC::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(),
};
// Background
canvas.set_clipping(dest.clone());
canvas
.render_rect(dest, self.background_color)
.unwrap_or_else(|_| panic!("Failed to render open file modal background!"));
canvas
.render_border(dest, self.border_color)
.unwrap_or_else(|_| panic!("Failed to render open file modal border!"));
self.font_label.render(
canvas,
renderer,
&RC::ParentPosition(
self.render_start_point()
+ Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP)
+ self.scroll(),
),
);
self.font_value.render(
canvas,
renderer,
&RC::ParentPosition(
self.render_start_point()
+ Point::new(
(CONTENT_MARGIN_LEFT * 2) + self.font_label.name_width() as i32,
CONTENT_MARGIN_TOP,
)
+ self.scroll(),
),
);
// Scroll bars
self.vertical_scroll_bar
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
self.horizontal_scroll_bar
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
}
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
{
self.font_label.prepare_ui(renderer);
self.font_value.prepare_ui(renderer);
}
}
impl ClickHandler for Settings {
fn on_left_click(&mut self, _point: &Point, context: &UpdateContext) -> UR {
let dest = match context {
UC::ParentPosition(p) => move_render_point(*p, &self.dest),
_ => self.dest,
};
let _context = UC::ParentPosition(
dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP) + self.scroll(),
);
// let res = self.directory_view.on_left_click(point, &context);
// {
// let dest = self.directory_view.dest();
// let full_dest = Rect::new(
// dest.x(),
// dest.y(),
// dest.width() + (2 * CONTENT_MARGIN_LEFT as u32),
// dest.height() + (2 * CONTENT_MARGIN_TOP as u32),
// );
// self.full_dest = full_dest;
// }
// res
UR::NoOp
}
fn is_left_click_target(&self, _point: &Point, context: &UpdateContext) -> bool {
let dest = match *context {
UC::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(),
};
let p =
dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP) + self.scroll();
let _context = UC::ParentPosition(p);
// if self.directory_view.is_left_click_target(point, &context) {
// true
// } else {
// Rect::new(p.x(), p.y(), dest.width(), dest.height()).contains_point(point.clone())
// }
false
}
}
impl RenderBox for Settings {
fn render_start_point(&self) -> Point {
self.dest.top_left()
}
fn dest(&self) -> Rect {
self.dest.clone()
}
}
impl Update for Settings {
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR {
let (window_width, window_height, color, scroll_width, scroll_margin) = {
let c = self.config.read().unwrap();
(
c.width(),
c.height(),
c.theme().background().into(),
c.scroll().width(),
c.scroll().margin_right(),
)
};
self.dest.set_x(CONTENT_MARGIN_LEFT);
self.dest
.set_width(window_width - (CONTENT_MARGIN_LEFT * 2) as u32);
self.dest.set_y(CONTENT_MARGIN_TOP);
self.dest
.set_height(window_height - (CONTENT_MARGIN_TOP * 2) as u32);
self.background_color = color;
// Scroll bars
self.vertical_scroll_bar
.set_full_size(self.full_dest.height()); // full dest
self.vertical_scroll_bar.set_viewport(self.dest.height());
self.vertical_scroll_bar
.set_location(self.dest.width() as i32 - (scroll_width as i32 + scroll_margin));
self.vertical_scroll_bar.update(ticks, context);
self.horizontal_scroll_bar
.set_full_size(self.full_dest.width()); // full dest
self.horizontal_scroll_bar.set_viewport(self.dest.width());
self.horizontal_scroll_bar
.set_location(self.dest.height() as i32 - (scroll_width as i32 + scroll_margin));
self.horizontal_scroll_bar.update(ticks, context);
// End
UR::NoOp
}
}

View File

@ -98,7 +98,7 @@ impl ProjectTreeSidebar {
.unwrap(); .unwrap();
// dir view // dir view
let context = RenderContext::RelativePosition( let context = RenderContext::ParentPosition(
self.dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP), self.dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP),
); );
self.dir_view.render(canvas, renderer, &context); self.dir_view.render(canvas, renderer, &context);
@ -271,7 +271,7 @@ mod tests {
let mut renderer = RendererMock::new(config.clone()); let mut renderer = RendererMock::new(config.clone());
let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config); let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config);
widget.prepare_ui(&mut renderer); widget.prepare_ui(&mut renderer);
assert_eq!(widget.full_rect(), Rect::new(0, 60, 200, 860)); assert_eq!(widget.full_rect(), Rect::new(0, 40, 200, 860));
} }
#[test] #[test]
@ -279,7 +279,7 @@ mod tests {
let config = build_config(); let config = build_config();
let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config); let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config);
widget.update(0); widget.update(0);
assert_eq!(widget.full_rect(), Rect::new(0, 60, 200, 800)); assert_eq!(widget.full_rect(), Rect::new(0, 40, 200, 820));
} }
#[test] #[test]
@ -288,7 +288,7 @@ mod tests {
let mut renderer = RendererMock::new(config.clone()); let mut renderer = RendererMock::new(config.clone());
let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config); let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config);
widget.prepare_ui(&mut renderer); widget.prepare_ui(&mut renderer);
assert_eq!(widget.full_rect(), Rect::new(0, 60, 200, 860)); assert_eq!(widget.full_rect(), Rect::new(0, 40, 200, 860));
} }
#[test] #[test]

View File

@ -66,7 +66,7 @@ impl HorizontalScrollBar {
canvas canvas
.render_rect( .render_rect(
match context { match context {
RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.rect), RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.rect),
_ => self.rect.clone(), _ => self.rect.clone(),
}, },
Color::RGBA(255, 255, 255, 0), Color::RGBA(255, 255, 255, 0),

View File

@ -66,7 +66,7 @@ impl VerticalScrollBar {
canvas canvas
.render_border( .render_border(
match context { match context {
RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.rect), RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.rect),
_ => self.rect.clone(), _ => self.rect.clone(),
}, },
Color::RGBA(255, 255, 255, 0), Color::RGBA(255, 255, 255, 0),

View File

@ -124,7 +124,7 @@ impl TextCharacter {
font: font_details.clone(), font: font_details.clone(),
}; };
let dest = match context { let dest = match context {
RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.dest), RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest(), _ => self.dest(),
}; };

View File

@ -28,7 +28,7 @@ fn main() -> std::io::Result<()> {
mod tests { mod tests {
use super::*; use super::*;
use std::env::set_var; use std::env::set_var;
use std::fs::create_dir_all; use std::fs::{create_dir_all, remove_dir_all};
use std::path::Path; use std::path::Path;
use uuid::Uuid; use uuid::Uuid;
@ -48,73 +48,130 @@ mod tests {
let uniq = Uuid::new_v4(); let uniq = Uuid::new_v4();
let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string()); let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string());
let test_path = joined.as_str(); let test_path = joined.as_str();
remove_dir_all(joined.clone()).unwrap_or_else(|_| ());
create_dir_all(test_path.to_owned()).unwrap(); create_dir_all(test_path.to_owned()).unwrap();
set_var("XDG_CONFIG_HOME", test_path); set_var("XDG_CONFIG_HOME", test_path);
set_var("XDG_RUNTIME_DIR", test_path); set_var("XDG_RUNTIME_DIR", test_path);
assert_eq!(exists(&test_path.to_owned(), ".rider"), false); debug_assert!(
assert_eq!(main().is_ok(), true); !exists(&test_path.to_owned(), ".rider"),
assert_eq!(exists(&test_path.to_owned(), ".rider"), true); "rider config dir should not exists before generator run"
);
debug_assert!(main().is_ok(), "generator should not failed");
debug_assert!(
exists(&test_path.to_owned(), ".rider"),
"rider config dir should exists after generator run"
);
} }
#[test] #[test]
fn assert_fonts_dir() { fn assert_fonts_dir() {
let uniq = Uuid::new_v4(); let uniq = Uuid::new_v4();
let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string()); let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string());
remove_dir_all(joined.clone()).unwrap_or_else(|_| ());
create_dir_all(joined.clone()).unwrap(); create_dir_all(joined.clone()).unwrap();
set_var("XDG_CONFIG_HOME", joined.as_str().clone()); set_var("XDG_CONFIG_HOME", joined.as_str().clone());
set_var("XDG_RUNTIME_HOME", joined.as_str().clone()); set_var("XDG_RUNTIME_HOME", joined.as_str().clone());
assert_eq!(exists(&joined, "rider/fonts"), false);
assert_eq!(main().is_ok(), true); debug_assert!(
assert_eq!(exists(&joined, "rider/fonts"), true); !exists(&joined, "rider/fonts"),
"fonts director should not exists before run generator"
);
debug_assert!(main().is_ok(), "generator should not failed");
debug_assert!(
exists(&joined, "rider/fonts"),
"fonts director should exists after run generator"
);
} }
#[test] #[test]
fn assert_log_dir() { fn assert_log_dir() {
let uniq = Uuid::new_v4(); let uniq = Uuid::new_v4();
let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string()); let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string());
remove_dir_all(joined.clone()).unwrap_or_else(|_| ());
create_dir_all(joined.clone()).unwrap(); create_dir_all(joined.clone()).unwrap();
set_var("XDG_CONFIG_HOME", joined.as_str().clone()); set_var("XDG_CONFIG_HOME", joined.as_str().clone());
set_var("XDG_RUNTIME_HOME", joined.as_str().clone()); set_var("XDG_RUNTIME_HOME", joined.as_str().clone());
assert_eq!(exists(&joined, "rider/log"), false);
assert_eq!(main().is_ok(), true); debug_assert!(
assert_eq!(exists(&joined, "rider/log"), true); !exists(&joined, "rider/log"),
"log should not exists before run generator"
);
debug_assert!(main().is_ok(), "generator should not failed");
debug_assert!(
exists(&joined, "rider/log"),
"log should exists after run generator"
);
} }
#[test] #[test]
fn assert_themes_dir() { fn assert_themes_dir() {
let uniq = Uuid::new_v4(); let uniq = Uuid::new_v4();
let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string()); let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string());
remove_dir_all(joined.clone()).unwrap_or_else(|_| ());
create_dir_all(joined.clone()).unwrap(); create_dir_all(joined.clone()).unwrap();
set_var("XDG_CONFIG_HOME", joined.as_str().clone()); set_var("XDG_CONFIG_HOME", joined.as_str().clone());
set_var("XDG_RUNTIME_HOME", joined.as_str().clone()); set_var("XDG_RUNTIME_HOME", joined.as_str().clone());
assert_eq!(exists(&joined, "rider/themes"), false);
assert_eq!(main().is_ok(), true); debug_assert!(
assert_eq!(exists(&joined, "rider/themes"), true); !exists(&joined, "rider/themes"),
"themes should not exists before run generator"
);
debug_assert!(main().is_ok(), "generator should not failed");
debug_assert!(
exists(&joined, "rider/themes"),
"themes should exists after run generator"
);
} }
#[test] #[test]
fn assert_default_json() { fn assert_default_json() {
let uniq = Uuid::new_v4(); let uniq = Uuid::new_v4();
let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string()); let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string());
remove_dir_all(joined.clone()).unwrap_or_else(|_| ());
create_dir_all(joined.clone()).unwrap(); create_dir_all(joined.clone()).unwrap();
set_var("XDG_CONFIG_HOME", joined.as_str().clone()); set_var("XDG_CONFIG_HOME", joined.as_str().clone());
set_var("XDG_RUNTIME_HOME", joined.as_str().clone()); set_var("XDG_RUNTIME_HOME", joined.as_str().clone());
assert_eq!(exists(&joined, "rider/themes/default.json"), false);
assert_eq!(main().is_ok(), true); debug_assert!(
assert_eq!(exists(&joined, "rider/themes/default.json"), true); !exists(&joined, "rider/themes/default.json"),
"default theme should not exists before run generator"
);
debug_assert!(main().is_ok(), "generator should not failed");
debug_assert!(
exists(&joined, "rider/themes/default.json"),
"default theme should exists after run generator"
);
} }
#[test] #[test]
fn assert_railscasts_json() { fn assert_railscasts_json() {
let uniq = Uuid::new_v4(); let uniq = Uuid::new_v4();
let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string()); let joined = join("/tmp/rider-tests".to_owned(), uniq.to_string());
remove_dir_all(joined.clone()).unwrap_or_else(|_| ());
create_dir_all(joined.clone()).unwrap(); create_dir_all(joined.clone()).unwrap();
set_var("XDG_CONFIG_HOME", joined.as_str().clone()); set_var("XDG_CONFIG_HOME", joined.as_str().clone());
set_var("XDG_RUNTIME_HOME", joined.as_str().clone()); set_var("XDG_RUNTIME_HOME", joined.as_str().clone());
assert_eq!(exists(&joined, "rider/themes/railscasts.json"), false);
assert_eq!(main().is_ok(), true); debug_assert!(
assert_eq!(exists(&joined, "rider/themes/railscasts.json"), true); !exists(&joined, "rider/themes/railscasts.json"),
"railscasts theme should not exists before run generator"
);
debug_assert!(main().is_ok(), "generator should not failed");
debug_assert!(
exists(&joined, "rider/themes/railscasts.json"),
"railscasts theme should exists after run generator"
);
} }
} }