Add save button

This commit is contained in:
Adrian Wozniak 2019-05-27 21:52:55 +02:00
parent 6aed1047f1
commit 794f47a601
15 changed files with 174 additions and 14 deletions

1
.gitignore vendored
View File

@ -6,4 +6,3 @@ log
.codecov .codecov
cobertura.xml cobertura.xml
cov cov

View File

@ -23,8 +23,8 @@ rustup run nightly cargo run -p rider-editor
* [x] Handle caret movement with arrow keys * [x] Handle caret movement with arrow keys
* [x] Add text content * [x] Add text content
* [x] Open file menu * [x] Open file menu
* [ ] `Save file` with button * [x] `Save file` with button
* [ ] `Save file` with shortcut * [x] `Save file` with shortcut
* [ ] `Save file as...` 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

View File

@ -113,7 +113,8 @@ impl AppState {
self.file_editor.render(canvas, renderer); self.file_editor.render(canvas, renderer);
// menu bar // menu bar
self.menu_bar.render(canvas, &RenderContext::Nothing); self.menu_bar
.render(canvas, renderer, &RenderContext::Nothing);
// project tree // project tree
self.project_tree.render(canvas, renderer); self.project_tree.render(canvas, renderer);

View File

@ -1,14 +1,19 @@
use crate::app::UpdateResult as UR; use crate::app::UpdateResult as UR;
use crate::renderer::Renderer;
use crate::ui::*; use crate::ui::*;
use rider_config::ConfigAccess; use rider_config::ConfigAccess;
use sdl2::pixels::Color; use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
const SAVE_BUTTON_OFFSET_LEFT: i32 = 16;
const SAVE_BUTTON_OFFSET_TOP: i32 = 16;
pub struct MenuBar { pub struct MenuBar {
border_color: Color, border_color: Color,
background_color: Color, background_color: Color,
dest: Rect, dest: Rect,
config: ConfigAccess, config: ConfigAccess,
save_button: SaveButton,
} }
impl MenuBar { impl MenuBar {
@ -26,6 +31,7 @@ impl MenuBar {
border_color, border_color,
background_color, background_color,
dest: Rect::new(0, 0, w as u32, h as u32), dest: Rect::new(0, 0, w as u32, h as u32),
save_button: SaveButton::new(config.clone()),
config, config,
} }
} }
@ -34,19 +40,22 @@ impl MenuBar {
&self.background_color &self.background_color
} }
pub fn render<C>(&self, canvas: &mut C, context: &RenderContext) pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where where
C: CanvasAccess, C: CanvasAccess,
R: Renderer,
{ {
use std::borrow::*; use std::borrow::*;
let relative_position = match context.borrow() {
RenderContext::RelativePosition(p) => p.clone(),
_ => Point::new(0, 0),
};
canvas.set_clipping(self.dest.clone()); canvas.set_clipping(self.dest.clone());
canvas canvas
.render_rect( .render_rect(
match context.borrow() { move_render_point(relative_position.clone(), &self.dest),
RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest(),
},
self.background_color.clone(), self.background_color.clone(),
) )
.unwrap_or_else(|_| panic!("Failed to draw main menu background")); .unwrap_or_else(|_| panic!("Failed to draw main menu background"));
@ -61,6 +70,11 @@ impl MenuBar {
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(
relative_position.offset(SAVE_BUTTON_OFFSET_LEFT, SAVE_BUTTON_OFFSET_TOP),
);
self.save_button.render(canvas, renderer, &context);
} }
pub fn prepare_ui(&mut self) { pub fn prepare_ui(&mut self) {
@ -79,7 +93,19 @@ impl Update for MenuBar {
} }
impl ClickHandler for MenuBar { impl ClickHandler for MenuBar {
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR { fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UR {
use std::borrow::*;
let relative_position = match context.borrow() {
UpdateContext::ParentPosition(p) => p.clone(),
_ => Point::new(0, 0),
};
let context = UpdateContext::ParentPosition(
relative_position.offset(SAVE_BUTTON_OFFSET_LEFT, SAVE_BUTTON_OFFSET_TOP),
);
if self.save_button.is_left_click_target(point, &context) {
return self.save_button.on_left_click(point, &context);
}
UR::NoOp UR::NoOp
} }
@ -218,6 +244,7 @@ mod test_click_handler {
#[cfg(test)] #[cfg(test)]
mod test_render { mod test_render {
use crate::tests::support::SimpleRendererMock;
use crate::tests::*; use crate::tests::*;
use crate::ui::*; use crate::ui::*;
use sdl2::pixels::Color; use sdl2::pixels::Color;
@ -277,9 +304,10 @@ mod test_render {
border_rect: Rect::new(0, 0, 0, 0), border_rect: Rect::new(0, 0, 0, 0),
border_color: Color::RGB(0, 0, 0), border_color: Color::RGB(0, 0, 0),
}; };
let mut renderer = SimpleRendererMock::new(config.clone());
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, &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, 60));
let expected = CanvasMock { let expected = CanvasMock {
clipping: Rect::new(0, 0, 1024, 60), clipping: Rect::new(0, 0, 1024, 60),

View File

@ -15,6 +15,7 @@ pub mod filesystem;
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 text_character; pub mod text_character;
@ -25,6 +26,7 @@ pub use crate::ui::filesystem::*;
pub use crate::ui::menu_bar::*; pub use crate::ui::menu_bar::*;
pub use crate::ui::modal::*; pub use crate::ui::modal::*;
pub use crate::ui::project_tree::*; pub use crate::ui::project_tree::*;
pub use crate::ui::save_button::*;
pub use crate::ui::scroll_bar::*; pub use crate::ui::scroll_bar::*;
pub use crate::ui::text_character::*; pub use crate::ui::text_character::*;

View File

@ -0,0 +1,106 @@
use crate::app::{ConfigAccess, ConfigHolder, UpdateResult as UR};
use crate::renderer::Renderer;
use crate::ui::{
move_render_point, CanvasAccess, ClickHandler, RenderBox, RenderContext, Update, UpdateContext,
};
use sdl2::rect::{Point, Rect};
const ICON_DEST_WIDTH: u32 = 32;
const ICON_DEST_HEIGHT: u32 = 32;
const ICON_SRC_WIDTH: u32 = 64;
const ICON_SRC_HEIGHT: u32 = 64;
pub struct SaveButton {
source: Rect,
dest: Rect,
config: ConfigAccess,
}
impl SaveButton {
pub fn new(config: ConfigAccess) -> Self {
Self {
dest: Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT),
source: Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
config,
}
}
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer,
{
use std::borrow::*;
let mut dest = match context {
&RenderContext::RelativePosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(),
};
canvas.set_clipping(dest.clone());
let save_texture_path = {
let c = self.config.read().unwrap();
let mut themes_dir = c.directories().themes_dir.clone();
let path = c.theme().images().save_icon();
themes_dir.push(path);
themes_dir.to_str().unwrap().to_owned()
};
let maybe_tex = renderer.load_image(save_texture_path.clone());
if let Ok(texture) = maybe_tex {
dest.set_width(ICON_DEST_WIDTH);
dest.set_height(ICON_DEST_HEIGHT);
canvas
.render_image(texture, self.source.clone(), dest.clone())
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
}
}
pub fn prepare_ui<'l, T>(&mut self, _renderer: &mut T)
where
T: ConfigHolder + Renderer,
{
}
pub fn source(&self) -> &Rect {
&self.source
}
pub fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
pub fn set_source(&mut self, rect: &Rect) {
self.source = rect.clone();
}
}
impl Update for SaveButton {
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 SaveButton {
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::SaveCurrentFile
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match *context {
UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest(),
}
.contains_point(point.clone())
}
}
impl RenderBox for SaveButton {
fn render_start_point(&self) -> Point {
self.dest.top_left()
}
fn dest(&self) -> Rect {
self.dest
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -47,6 +47,12 @@ fn create_railscasts_file_icon(dir: &PathBuf) -> std::io::Result<()> {
Ok(()) Ok(())
} }
fn create_railscasts_save_icon(dir: &PathBuf) -> std::io::Result<()> {
let blob = include_bytes!("../assets/themes/railscasts/images/save-64x64.png");
write_bytes_to(dir, "save-64x64.png", blob)?;
Ok(())
}
fn railscasts_theme(directories: &Directories) -> std::io::Result<()> { fn railscasts_theme(directories: &Directories) -> std::io::Result<()> {
let mut dir = PathBuf::new(); let mut dir = PathBuf::new();
dir.push(directories.themes_dir.clone()); dir.push(directories.themes_dir.clone());
@ -55,6 +61,7 @@ fn railscasts_theme(directories: &Directories) -> std::io::Result<()> {
create_dir_all(&dir)?; create_dir_all(&dir)?;
create_railscasts_directory_icon(&dir)?; create_railscasts_directory_icon(&dir)?;
create_railscasts_file_icon(&dir)?; create_railscasts_file_icon(&dir)?;
create_railscasts_save_icon(&dir)?;
Ok(()) Ok(())
} }

View File

@ -2,13 +2,15 @@
pub struct ThemeImages { pub struct ThemeImages {
directory_icon: String, directory_icon: String,
file_icon: String, file_icon: String,
save_icon: String,
} }
impl ThemeImages { impl ThemeImages {
pub fn new(directory_icon: String, file_icon: String) -> Self { pub fn new(directory_icon: String, file_icon: String, save_icon: String) -> Self {
Self { Self {
file_icon, file_icon,
directory_icon, directory_icon,
save_icon,
} }
} }
@ -19,6 +21,10 @@ impl ThemeImages {
pub fn file_icon(&self) -> String { pub fn file_icon(&self) -> String {
self.file_icon.clone() self.file_icon.clone()
} }
pub fn save_icon(&self) -> String {
self.save_icon.clone()
}
} }
impl Default for ThemeImages { impl Default for ThemeImages {
@ -26,6 +32,7 @@ impl Default for ThemeImages {
Self { Self {
directory_icon: "default/images/directory-64x64.png".to_string(), directory_icon: "default/images/directory-64x64.png".to_string(),
file_icon: "default/images/file-64x64.png".to_string(), file_icon: "default/images/file-64x64.png".to_string(),
save_icon: "default/images/save-64x64.png".to_string(),
} }
} }
} }
@ -36,16 +43,25 @@ mod test {
#[test] #[test]
fn assert_directory_icon() { fn assert_directory_icon() {
let config = ThemeImages::new("foo".to_owned(), "bar".to_owned()); let config = ThemeImages::new("foo".to_owned(), "bar".to_owned(), "baz".to_owned());
let result = config.directory_icon(); let result = config.directory_icon();
let expected = "foo".to_owned(); let expected = "foo".to_owned();
assert_eq!(result, expected); assert_eq!(result, expected);
} }
#[test] #[test]
fn assert_file_icon() { fn assert_file_icon() {
let config = ThemeImages::new("foo".to_owned(), "bar".to_owned()); let config = ThemeImages::new("foo".to_owned(), "bar".to_owned(), "baz".to_owned());
let result = config.file_icon(); let result = config.file_icon();
let expected = "bar".to_owned(); let expected = "bar".to_owned();
assert_eq!(result, expected); assert_eq!(result, expected);
} }
#[test]
fn assert_save_icon() {
let config = ThemeImages::new("foo".to_owned(), "bar".to_owned(), "baz".to_owned());
let result = config.save_icon();
let expected = "baz".to_owned();
assert_eq!(result, expected);
}
} }

View File

@ -44,6 +44,7 @@ pub fn build_theme() -> Theme {
ThemeImages::new( ThemeImages::new(
"railscasts/images/directory-64x64.png".to_owned(), "railscasts/images/directory-64x64.png".to_owned(),
"railscasts/images/file-64x64.png".to_owned(), "railscasts/images/file-64x64.png".to_owned(),
"railscasts/images/save-64x64.png".to_owned(),
), ),
) )
} }