Start handle window resize (#12)

* Start handle window resize

* Save new window size, resize menu and file editor

* Add clipping for performaance, fix editor height and width

* Update readme
This commit is contained in:
Adrian Woźniak 2019-01-06 21:15:31 +01:00 committed by GitHub
parent 7e2218bf82
commit 0cf8629868
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 177 additions and 97 deletions

View File

@ -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

View File

@ -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<EditorFile>,
config: Rc<Config>,
config: Arc<RwLock<Config>>,
file_editor: FileEditor,
}
impl AppState {
pub fn new(config: Rc<Config>) -> Self {
pub fn new(config: Arc<RwLock<Config>>) -> 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<Config> {
pub fn config(&self) -> &Arc<RwLock<Config>> {
&self.config
}

View File

@ -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>,
config: Arc<RwLock<Config>>,
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<Config> {
pub fn config(&self) -> &Arc<RwLock<Config>> {
&self.config
}
}

View File

@ -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);

View File

@ -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
}

View File

@ -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>,
config: Arc<RwLock<Config>>,
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>,
config: Arc<RwLock<Config>>,
font_context: &'a Sdl2TtfContext,
texture_creator: &'a TextureCreator<WindowContext>,
) -> Self {
@ -31,7 +32,7 @@ impl<'a> Renderer<'a> {
}
}
pub fn config(&self) -> &Rc<Config> {
pub fn config(&self) -> &Arc<RwLock<Config>> {
&self.config
}

View File

@ -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<Config>) -> Self {
let bright = config.theme().caret().bright().color().into();
let blur = config.theme().caret().blur().color().into();
pub fn new(config: Arc<RwLock<Config>>) -> 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,

View File

@ -15,26 +15,34 @@ pub struct EditorFile {
sections: Vec<EditorFileSection>,
render_position: Rect,
buffer: String,
config: Rc<Config>,
config: Arc<RwLock<Config>>,
line_height: u32,
}
impl EditorFile {
pub fn new(path: String, buffer: String, config: Rc<Config>) -> Self {
pub fn new(path: String, buffer: String, config: Arc<RwLock<Config>>) -> 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,

View File

@ -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<EditorFileToken>,
language: Language,
config: Rc<Config>,
config: Arc<RwLock<Config>>,
}
impl EditorFileSection {
pub fn new(buffer: String, ext: String, config: Rc<Config>) -> Self {
pub fn new(buffer: String, ext: String, config: Arc<RwLock<Config>>) -> 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<EditorFileToken> = vec![];
let mut iterator = lexer_tokens.iter().peekable();

View File

@ -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<Config>) -> Color {
let config = config.theme().code_highlighting();
pub fn to_color(&self, config: &Arc<RwLock<Config>>) -> 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<TextCharacter>,
token_type: Rc<TokenType>,
config: Rc<Config>,
config: Arc<RwLock<Config>>,
}
impl EditorFileToken {
pub fn new(token_type: &TokenType, last_in_line: bool, config: Rc<Config>) -> Self {
pub fn new(token_type: &TokenType, last_in_line: bool, config: Arc<RwLock<Config>>) -> Self {
Self {
last_in_line,
characters: vec![],

View File

@ -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<EditorFile>,
config: Rc<Config>,
config: Arc<RwLock<Config>>,
}
impl FileEditor {
pub fn new(dest: Rect, config: Rc<Config>) -> Self {
pub fn new(config: Arc<RwLock<Config>>) -> 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<Config> {
pub fn config(&self) -> &Arc<RwLock<Config>> {
&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);

View File

@ -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>,
config: Arc<RwLock<Config>>,
pending: bool,
}
impl MenuBar {
pub fn new(config: Rc<Config>) -> Self {
pub fn new(config: Arc<RwLock<Config>>) -> 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!()
}

View File

@ -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<Rect> {
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)

View File

@ -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>,
config: Arc<RwLock<Config>>,
}
impl TextCharacter {
@ -31,7 +32,7 @@ impl TextCharacter {
line: usize,
last_in_line: bool,
color: Color,
config: Rc<Config>,
config: Arc<RwLock<Config>>,
) -> 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)