Add save button
This commit is contained in:
parent
6aed1047f1
commit
794f47a601
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,4 +6,3 @@ log
|
|||||||
.codecov
|
.codecov
|
||||||
cobertura.xml
|
cobertura.xml
|
||||||
cov
|
cov
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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),
|
||||||
|
@ -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::*;
|
||||||
|
|
||||||
|
106
rider-editor/src/ui/save_button.rs
Normal file
106
rider-editor/src/ui/save_button.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
BIN
rider-generator/assets/themes/default/images/save-48x84.png
Normal file
BIN
rider-generator/assets/themes/default/images/save-48x84.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
rider-generator/assets/themes/default/images/save-512x512.png
Normal file
BIN
rider-generator/assets/themes/default/images/save-512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
rider-generator/assets/themes/default/images/save-64x64.png
Normal file
BIN
rider-generator/assets/themes/default/images/save-64x64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
rider-generator/assets/themes/railscasts/images/save-48x84.png
Normal file
BIN
rider-generator/assets/themes/railscasts/images/save-48x84.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
rider-generator/assets/themes/railscasts/images/save-512x512.png
Normal file
BIN
rider-generator/assets/themes/railscasts/images/save-512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
rider-generator/assets/themes/railscasts/images/save-64x64.png
Normal file
BIN
rider-generator/assets/themes/railscasts/images/save-64x64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user