diff --git a/README.md b/README.md index cd3374e..17d1185 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Text editor in rust * [ ] Config edit menu * [ ] Project tree * [ ] Cover `rider` with tests -* [ ] Handle resize window +* [x] Handle resize window ### v1.1 diff --git a/src/app/app_state.rs b/src/app/app_state.rs index b44b397..b529e92 100644 --- a/src/app/app_state.rs +++ b/src/app/app_state.rs @@ -14,29 +14,21 @@ use sdl2::rect::{Point, Rect}; use sdl2::VideoSubsystem as VS; use std::boxed::Box; use std::rc::Rc; -use std::sync::Arc; +use std::sync::*; pub struct AppState { menu_bar: MenuBar, files: Vec, - config: Rc, + config: Arc>, file_editor: FileEditor, } impl AppState { - pub fn new(config: Rc) -> Self { + pub fn new(config: Arc>) -> Self { Self { - menu_bar: MenuBar::new(config.clone()), + menu_bar: MenuBar::new(Arc::clone(&config)), files: vec![], - file_editor: FileEditor::new( - Rect::new( - config.editor_left_margin(), - config.editor_top_margin(), - config.width() - config.editor_left_margin() as u32, - config.height() - config.editor_top_margin() as u32, - ), - config.clone(), - ), + file_editor: FileEditor::new(Arc::clone(&config)), config, } } @@ -56,7 +48,7 @@ impl AppState { }; } - pub fn config(&self) -> &Rc { + pub fn config(&self) -> &Arc> { &self.config } diff --git a/src/app/application.rs b/src/app/application.rs index e844c2e..69cc43d 100644 --- a/src/app/application.rs +++ b/src/app/application.rs @@ -6,10 +6,11 @@ use crate::ui::caret::{CaretPosition, MoveDirection}; use crate::ui::*; use std::rc::Rc; +use std::sync::*; use std::thread::sleep; use std::time::Duration; -use sdl2::event::Event; +use sdl2::event::*; use sdl2::hint; use sdl2::keyboard::{Keycode, Mod}; use sdl2::mouse::*; @@ -41,6 +42,7 @@ pub enum UpdateResult { MoveCaretUp, MoveCaretDown, Scroll { x: i32, y: i32 }, + WindowResize { width: i32, height: i32 }, } pub enum Task { @@ -48,7 +50,7 @@ pub enum Task { } pub struct Application { - config: Rc, + config: Arc>, clear_color: Color, sdl_context: Sdl, canvas: WindowCanvas, @@ -58,7 +60,7 @@ pub struct Application { impl Application { pub fn new() -> Self { - let config = Rc::new(Config::new()); + let config = Arc::new(RwLock::new(Config::new())); let sdl_context = sdl2::init().unwrap(); hint::set("SDL_GL_MULTISAMPLEBUFFERS", "1"); @@ -69,26 +71,30 @@ impl Application { let video_subsystem = sdl_context.video().unwrap(); - let mut window: Window = video_subsystem - .window("Rider", config.width(), config.height()) - .position_centered() - .resizable() - .opengl() - .build() - .unwrap(); + let mut window: Window = { + let c = config.read().unwrap(); + video_subsystem + .window("Rider", c.width(), c.height()) + .position_centered() + .resizable() + .opengl() + .build() + .unwrap() + }; let icon_bytes = include_bytes!("../../assets/gear-64x64.bmp").clone(); let mut rw = RWops::from_bytes(&icon_bytes).unwrap(); let mut icon = Surface::load_bmp_rw(&mut rw).unwrap(); window.set_icon(&mut icon); let canvas = window.into_canvas().accelerated().build().unwrap(); + let clear_color: Color = { config.read().unwrap().theme().background().into() }; Self { sdl_context, video_subsystem, canvas, tasks: vec![], - clear_color: config.theme().background().into(), + clear_color, config, } } @@ -103,8 +109,8 @@ impl Application { let font_context = sdl2::ttf::init().unwrap(); let texture_creator = self.canvas.texture_creator(); let sleep_time = Duration::new(0, 1_000_000_000u32 / 60); - let mut app_state = AppState::new(self.config.clone()); - let mut renderer = Renderer::new(self.config.clone(), &font_context, &texture_creator); + let mut app_state = AppState::new(Arc::clone(&self.config)); + let mut renderer = Renderer::new(Arc::clone(&self.config), &font_context, &texture_creator); app_state.prepare_ui(&mut renderer); 'running: loop { @@ -143,6 +149,15 @@ impl Application { UpdateResult::Scroll { x, y } => { app_state.file_editor_mut().scroll_to(x, y); } + UpdateResult::WindowResize { width, height } => { + let mut c = app_state.config().write().unwrap(); + if width > 0 { + c.set_width(width as u32); + } + if height > 0 { + c.set_height(height as u32); + } + } } for task in self.tasks.iter() { match task { @@ -222,13 +237,22 @@ impl Application { } }; } + Event::Window { + win_event: WindowEvent::Resized(w, h), + .. + } => { + return UpdateResult::WindowResize { + width: w, + height: h, + }; + } _ => (), } } UpdateResult::NoOp } - pub fn config(&self) -> &Rc { + pub fn config(&self) -> &Arc> { &self.config } } diff --git a/src/app/file_content_manager.rs b/src/app/file_content_manager.rs index 024c2b6..3f5cac7 100644 --- a/src/app/file_content_manager.rs +++ b/src/app/file_content_manager.rs @@ -2,6 +2,7 @@ use crate::app::*; use crate::renderer::Renderer; use crate::ui::*; use sdl2::rect::*; +use std::sync::*; fn current_file_path(file_editor: &mut FileEditor) -> String { file_editor @@ -119,7 +120,7 @@ pub fn insert_new_line(file_editor: &mut FileEditor, renderer: &mut Renderer) { buffer.insert(position.text_position(), '\n'); if let Some(rect) = get_text_character_rect('\n', renderer) { pos = Point::new( - file_editor.config().editor_left_margin(), + file_editor.config().read().unwrap().editor_left_margin(), pos.y() + rect.height() as i32, ); position = position.moved(0, 1, 0); @@ -129,7 +130,7 @@ pub fn insert_new_line(file_editor: &mut FileEditor, renderer: &mut Renderer) { let mut new_file = EditorFile::new( current_file_path(file_editor), buffer, - file_editor.config().clone(), + Arc::clone(file_editor.config()), ); new_file.prepare_ui(renderer); file_editor.replace_current_file(new_file); diff --git a/src/config/mod.rs b/src/config/mod.rs index 318da89..cef019c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -91,10 +91,18 @@ impl Config { self.width } + pub fn set_width(&mut self, w: u32) { + self.width = w; + } + pub fn height(&self) -> u32 { self.height } + pub fn set_height(&mut self, h: u32) { + self.height = h; + } + pub fn editor_config(&self) -> &EditorConfig { &self.editor_config } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index d16dbcf..d4e9daa 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -7,11 +7,12 @@ use sdl2::render::{Texture, TextureCreator}; use sdl2::ttf::Sdl2TtfContext; use sdl2::video::WindowContext; use std::rc::Rc; +use std::sync::*; pub mod managers; pub struct Renderer<'a> { - config: Rc, + config: Arc>, font_manager: FontManager<'a>, texture_manager: TextureManager<'a, WindowContext>, scroll: Point, @@ -19,7 +20,7 @@ pub struct Renderer<'a> { impl<'a> Renderer<'a> { pub fn new( - config: Rc, + config: Arc>, font_context: &'a Sdl2TtfContext, texture_creator: &'a TextureCreator, ) -> Self { @@ -31,7 +32,7 @@ impl<'a> Renderer<'a> { } } - pub fn config(&self) -> &Rc { + pub fn config(&self) -> &Arc> { &self.config } diff --git a/src/ui/caret.rs b/src/ui/caret.rs index 7749dc7..aa73a15 100644 --- a/src/ui/caret.rs +++ b/src/ui/caret.rs @@ -8,6 +8,7 @@ use sdl2::rect::{Point, Rect}; use sdl2::render::Texture; use std::ops::Deref; use std::rc::Rc; +use std::sync::*; #[derive(Clone, Debug, PartialEq)] pub enum CaretState { @@ -126,9 +127,10 @@ pub struct Caret { } impl Caret { - pub fn new(config: Rc) -> Self { - let bright = config.theme().caret().bright().color().into(); - let blur = config.theme().caret().blur().color().into(); + pub fn new(config: Arc>) -> Self { + let read_config = config.read().unwrap(); + let bright = read_config.theme().caret().bright().color().into(); + let blur = read_config.theme().caret().blur().color().into(); Self { state: CaretState::Bright, blink_delay: 0, diff --git a/src/ui/file/editor_file.rs b/src/ui/file/editor_file.rs index 88f8761..d012fb4 100644 --- a/src/ui/file/editor_file.rs +++ b/src/ui/file/editor_file.rs @@ -15,26 +15,34 @@ pub struct EditorFile { sections: Vec, render_position: Rect, buffer: String, - config: Rc, + config: Arc>, line_height: u32, } impl EditorFile { - pub fn new(path: String, buffer: String, config: Rc) -> Self { + pub fn new(path: String, buffer: String, config: Arc>) -> Self { use std::path::Path; let ext = Path::new(&path) .extension() .and_then(|p| p.to_str()) .map_or("txt", |s| s) .to_string(); - let sections = vec![EditorFileSection::new(buffer.clone(), ext, config.clone())]; - let x = config.editor_left_margin(); - let y = config.editor_top_margin(); + let sections = vec![EditorFileSection::new( + buffer.clone(), + ext, + Arc::clone(&config), + )]; + let render_position = { + let c = config.read().unwrap(); + let x = c.editor_left_margin(); + let y = c.editor_top_margin(); + Rect::new(x, y, 0, 0) + }; Self { path, sections, - render_position: Rect::new(x, y, 0, 0), + render_position, buffer, config, line_height: 0, diff --git a/src/ui/file/editor_file_section.rs b/src/ui/file/editor_file_section.rs index 79bddf2..ed616d9 100644 --- a/src/ui/file/editor_file_section.rs +++ b/src/ui/file/editor_file_section.rs @@ -1,6 +1,7 @@ use sdl2::rect::{Point, Rect}; use std::cell::Cell; use std::rc::Rc; +use std::sync::*; use crate::app::{UpdateResult as UR, WindowCanvas as WC}; use crate::config::Config; @@ -14,18 +15,21 @@ use crate::ui::*; pub struct EditorFileSection { tokens: Vec, language: Language, - config: Rc, + config: Arc>, } impl EditorFileSection { - pub fn new(buffer: String, ext: String, config: Rc) -> Self { + pub fn new(buffer: String, ext: String, config: Arc>) -> Self { use crate::lexer; let language = config + .read() + .unwrap() .extensions_mapping() .get(ext.as_str()) - .unwrap_or(&Language::PlainText); - let lexer_tokens = lexer::parse(buffer.clone(), language); + .unwrap_or(&Language::PlainText) + .clone(); + let lexer_tokens = lexer::parse(buffer.clone(), &language); let mut tokens: Vec = vec![]; let mut iterator = lexer_tokens.iter().peekable(); diff --git a/src/ui/file/editor_file_token.rs b/src/ui/file/editor_file_token.rs index eae4e77..01ec080 100644 --- a/src/ui/file/editor_file_token.rs +++ b/src/ui/file/editor_file_token.rs @@ -9,20 +9,22 @@ use sdl2::rect::{Point, Rect}; use sdl2::render::Texture; use sdl2::ttf::Font; use std::rc::Rc; +use std::sync::*; impl TokenType { - pub fn to_color(&self, config: &Rc) -> Color { - let config = config.theme().code_highlighting(); + pub fn to_color(&self, config: &Arc>) -> Color { + let config = config.read().unwrap(); + let ch = config.theme().code_highlighting(); match self { - &TokenType::Whitespace { .. } => config.whitespace().color().into(), - &TokenType::Keyword { .. } => config.keyword().color().into(), - &TokenType::String { .. } => config.string().color().into(), - &TokenType::Number { .. } => config.number().color().into(), - &TokenType::Identifier { .. } => config.identifier().color().into(), - &TokenType::Literal { .. } => config.literal().color().into(), - &TokenType::Comment { .. } => config.comment().color().into(), - &TokenType::Operator { .. } => config.operator().color().into(), - &TokenType::Separator { .. } => config.separator().color().into(), + &TokenType::Whitespace { .. } => ch.whitespace().color().into(), + &TokenType::Keyword { .. } => ch.keyword().color().into(), + &TokenType::String { .. } => ch.string().color().into(), + &TokenType::Number { .. } => ch.number().color().into(), + &TokenType::Identifier { .. } => ch.identifier().color().into(), + &TokenType::Literal { .. } => ch.literal().color().into(), + &TokenType::Comment { .. } => ch.comment().color().into(), + &TokenType::Operator { .. } => ch.operator().color().into(), + &TokenType::Separator { .. } => ch.separator().color().into(), } } } @@ -32,11 +34,11 @@ pub struct EditorFileToken { last_in_line: bool, characters: Vec, token_type: Rc, - config: Rc, + config: Arc>, } impl EditorFileToken { - pub fn new(token_type: &TokenType, last_in_line: bool, config: Rc) -> Self { + pub fn new(token_type: &TokenType, last_in_line: bool, config: Arc>) -> Self { Self { last_in_line, characters: vec![], diff --git a/src/ui/file_editor.rs b/src/ui/file_editor.rs index f30531e..8c9fe98 100644 --- a/src/ui/file_editor.rs +++ b/src/ui/file_editor.rs @@ -2,6 +2,7 @@ use sdl2::rect::*; use std::borrow::*; use std::mem; use std::rc::Rc; +use std::sync::*; use crate::app::*; use crate::app::{UpdateResult as UR, WindowCanvas as WS}; @@ -12,21 +13,30 @@ pub struct FileEditor { scroll: Point, caret: Caret, file: Option, - config: Rc, + config: Arc>, } impl FileEditor { - pub fn new(dest: Rect, config: Rc) -> Self { + pub fn new(config: Arc>) -> Self { + let dest = { + let c = config.read().unwrap(); + Rect::new( + c.editor_left_margin(), + c.editor_top_margin(), + c.width() - c.editor_left_margin() as u32, + c.height() - c.editor_top_margin() as u32, + ) + }; Self { dest, scroll: Point::new(0, 0), - caret: Caret::new(config.clone()), + caret: Caret::new(Arc::clone(&config)), file: None, config, } } - pub fn config(&self) -> &Rc { + pub fn config(&self) -> &Arc> { &self.config } @@ -96,10 +106,11 @@ impl FileEditor { } pub fn scroll_to(&mut self, x: i32, y: i32) { + let read_config = self.config.read().unwrap(); self.scroll = self.scroll + Point::new( - self.config.scroll_speed() * x, - self.config.scroll_speed() * y, + read_config.scroll_speed() * x, + read_config.scroll_speed() * y, ); } @@ -156,6 +167,7 @@ impl FileEditor { impl Render for FileEditor { fn render(&self, canvas: &mut WS, renderer: &mut Renderer, _parent: Parent) -> UR { + canvas.set_clip_rect(self.dest.clone()); match self.file() { Some(file) => file.render(canvas, renderer, Some(self)), _ => UR::NoOp, @@ -170,6 +182,13 @@ impl Render for FileEditor { impl Update for FileEditor { fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR { + { + let config = self.config.read().unwrap(); + self.dest + .set_width(config.width() - config.editor_left_margin() as u32); + self.dest + .set_height(config.height() - config.editor_top_margin() as u32); + } self.caret.update(ticks, context); match self.file_mut() { Some(file) => file.update(ticks, context), @@ -220,11 +239,12 @@ mod tests { use sdl2::*; use std::borrow::*; use std::rc::*; + use std::sync::*; #[test] fn replace_file() { - let config = Rc::new(Config::new()); - let mut editor = FileEditor::new(Rect::new(0, 0, 100, 100), config.clone()); + let config = Arc::new(RwLock::new(Config::new())); + let mut editor = FileEditor::new(Arc::clone(&config)); let first_file = EditorFile::new("./foo.txt".to_string(), "foo".to_string(), config.clone()); let second_file = @@ -239,7 +259,7 @@ mod tests { #[test] fn add_text() { - let config = Rc::new(Config::new()); + let config = Arc::new(RwLock::new(Config::new())); let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); let window = video_subsystem @@ -253,7 +273,7 @@ mod tests { let texture_creator = canvas.texture_creator(); let mut renderer = Renderer::new(config.clone(), &font_context, &texture_creator); - let mut editor = FileEditor::new(Rect::new(0, 0, 100, 100), config.clone()); + let mut editor = FileEditor::new(Arc::clone(&config)); let mut file = EditorFile::new("./foo.txt".to_string(), "foo".to_string(), config.clone()); file.prepare_ui(&mut renderer); assert_eq!(editor.open_file(file).is_none(), true); diff --git a/src/ui/menu_bar.rs b/src/ui/menu_bar.rs index 049627f..fbe120a 100644 --- a/src/ui/menu_bar.rs +++ b/src/ui/menu_bar.rs @@ -1,24 +1,26 @@ -use crate::app::{UpdateResult, WindowCanvas}; +use crate::app::{UpdateResult as UR, WindowCanvas as WC}; use crate::config::Config; use crate::renderer::Renderer; use crate::ui::*; use sdl2::pixels::Color; use sdl2::rect::{Point, Rect}; use std::rc::Rc; +use std::sync::*; pub struct MenuBar { border_color: Color, background_color: Color, dest: Rect, - config: Rc, + config: Arc>, pending: bool, } impl MenuBar { - pub fn new(config: Rc) -> Self { + pub fn new(config: Arc>) -> Self { + let background_color = { config.read().unwrap().theme().background().into() }; Self { border_color: Color::RGB(10, 10, 10), - background_color: config.theme().background().into(), + background_color, dest: Rect::new(0, 0, 0, 0), config, pending: true, @@ -35,12 +37,8 @@ impl MenuBar { } impl Render for MenuBar { - fn render( - &self, - canvas: &mut WindowCanvas, - _renderer: &mut Renderer, - parent: Parent, - ) -> UpdateResult { + fn render(&self, canvas: &mut WC, _renderer: &mut Renderer, parent: Parent) -> UR { + canvas.set_clip_rect(self.dest.clone()); canvas.set_draw_color(self.background_color.clone()); canvas .fill_rect(match parent { @@ -57,28 +55,30 @@ impl Render for MenuBar { }) .unwrap_or_else(|_| panic!("Failed to draw main menu background")); - UpdateResult::NoOp + UR::NoOp } fn prepare_ui(&mut self, _renderer: &mut Renderer) { if !self.pending { return; } - let width = self.config.width(); - let height = self.config.menu_height() as u32; + let width = self.config.read().unwrap().width(); + let height = self.config.read().unwrap().menu_height() as u32; self.dest = Rect::new(0, 0, width, height); self.pending = false; } } impl Update for MenuBar { - fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UpdateResult { - UpdateResult::NoOp + fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR { + let config = self.config.read().unwrap(); + self.dest.set_width(config.width()); + UR::NoOp } } impl ClickHandler for MenuBar { - fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UpdateResult { + fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR { unimplemented!() } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8018419..aae6f32 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -39,9 +39,22 @@ pub fn is_in_rect(point: &Point, rect: &Rect) -> bool { } pub fn get_text_character_rect(c: char, renderer: &mut Renderer) -> Option { - let config = renderer.config().editor_config(); - let font_details = - FontDetails::new(config.font_path().as_str(), config.character_size().clone()); + let font_details = FontDetails::new( + renderer + .config() + .read() + .unwrap() + .editor_config() + .font_path() + .as_str(), + renderer + .config() + .read() + .unwrap() + .editor_config() + .character_size() + .clone(), + ); let font = renderer .font_manager() .load(&font_details) diff --git a/src/ui/text_character.rs b/src/ui/text_character.rs index 4204f36..02290c2 100644 --- a/src/ui/text_character.rs +++ b/src/ui/text_character.rs @@ -11,6 +11,7 @@ use sdl2::rect::{Point, Rect}; use sdl2::render::Texture; use sdl2::ttf::Font; use std::rc::Rc; +use std::sync::*; #[derive(Clone, Debug)] pub struct TextCharacter { @@ -21,7 +22,7 @@ pub struct TextCharacter { source: Rect, dest: Rect, color: Color, - config: Rc, + config: Arc>, } impl TextCharacter { @@ -31,7 +32,7 @@ impl TextCharacter { line: usize, last_in_line: bool, color: Color, - config: Rc, + config: Arc>, ) -> Self { Self { text_character, @@ -105,9 +106,11 @@ impl Render for TextCharacter { return UR::NoOp; } - let config = renderer.config().editor_config(); - let font_details = - FontDetails::new(config.font_path().as_str(), config.character_size().clone()); + let font_details = { + let config = renderer.config().read().unwrap(); + let ec = config.editor_config(); + FontDetails::new(ec.font_path().as_str(), ec.character_size().clone()) + }; let font = renderer .font_manager() .load(&font_details) @@ -133,9 +136,11 @@ impl Render for TextCharacter { } fn prepare_ui(&mut self, renderer: &mut Renderer) { - let config = renderer.config().editor_config(); - let font_details = - FontDetails::new(config.font_path().as_str(), config.character_size().clone()); + let font_details = { + let config = renderer.config().read().unwrap(); + let ec = config.editor_config(); + FontDetails::new(ec.font_path().as_str(), ec.character_size().clone()) + }; let font = renderer .font_manager() .load(&font_details)