More tests (#21)

* More tests

* Add some tests, refactor code

* Some additional tests

* Add some tests

* Add some tests

* Add pixel based tests for UI

* Add some tests

* Fix surface format

* Fix surface format
This commit is contained in:
Adrian Woźniak 2020-05-13 20:55:58 +02:00 committed by GitHub
parent 60d4c0be88
commit 7c01076cc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 2927 additions and 1594 deletions

110
Cargo.lock generated
View File

@ -137,6 +137,38 @@ name = "constant_time_eq"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "darling"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "darling_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "darling_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dirs"
version = "1.0.4"
@ -179,6 +211,11 @@ dependencies = [
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fnv"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
@ -201,6 +238,11 @@ dependencies = [
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.4.3"
@ -301,6 +343,14 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quick-error"
version = "1.2.2"
@ -314,6 +364,14 @@ dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.3.22"
@ -440,6 +498,16 @@ dependencies = [
"simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rider-derive"
version = "0.1.0"
dependencies = [
"darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rider-editor"
version = "0.1.0"
@ -449,7 +517,9 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rider-config 0.1.0",
"rider-derive 0.1.0",
"rider-lexers 0.1.0",
"rider-match-widget 0.1.0",
"rider-themes 0.1.0",
"sdl2 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
@ -483,6 +553,16 @@ dependencies = [
"simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rider-match-widget"
version = "0.1.0"
dependencies = [
"darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rider-themes"
version = "0.1.0"
@ -566,6 +646,11 @@ dependencies = [
"term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.14.9"
@ -586,6 +671,16 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.10.1"
@ -652,6 +747,11 @@ name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.2"
@ -731,13 +831,18 @@ dependencies = [
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
"checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
"checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
"checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a"
"checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum lalr 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "106d7548f95adbe3019b4fc4954554d7b72535867aa9ce326d2f766b68958de7"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
@ -752,8 +857,10 @@ dependencies = [
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum plex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "59d79bd74b3d0be2619e58217e8b2b96372e3feca8426e5c560623205d70c146"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
@ -774,8 +881,10 @@ dependencies = [
"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b"
"checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545"
"checksum simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e95345f185d5adeb8ec93459d2dc99654e294cc6ccf5b75414d8ea262de9a13"
"checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
@ -784,6 +893,7 @@ dependencies = [
"checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c"
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
"checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6"
"checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f"

View File

@ -10,14 +10,16 @@ members = [
"rider-config",
"rider-themes",
"rider-lexers",
"rider-editor"
"rider-editor",
"rider-match-widget"
]
default-members = [
"rider-generator",
"rider-config",
"rider-themes",
"rider-lexers",
"rider-editor"
"rider-editor",
"rider-match-widget"
]
[dependencies]

View File

@ -102,20 +102,15 @@ impl Config {
impl Config {
pub fn load_theme(&self, theme_name: String) -> Theme {
let home_dir = dirs::config_dir().unwrap();
let mut config_dir = home_dir;
config_dir.push("rider");
fs::create_dir_all(&config_dir)
#[cfg_attr(tarpaulin, skip)]
fs::create_dir_all(&home_dir.join("rider"))
.unwrap_or_else(|_| panic!("Cannot create config directory"));
self.load_theme_content(format!("{}.json", theme_name).as_str())
}
fn load_theme_content(&self, file_name: &str) -> Theme {
let mut config_file = self.directories.themes_dir.clone();
config_file.push(file_name);
let contents = match fs::read_to_string(&config_file) {
Ok(s) => s,
Err(_) => fs::read_to_string(&config_file).unwrap_or_else(|_| "".to_owned()),
};
let config_file = self.directories.themes_dir.clone();
let contents = fs::read_to_string(&config_file.join(file_name)).unwrap_or_default();
serde_json::from_str(&contents).unwrap_or_default()
}
}
@ -194,21 +189,21 @@ mod test_getters {
assert_eq!(result, expected);
}
// #[test]
// fn assert_editor_config() {
// let config = Config::new();
// let result = config.editor_config();
// let expected = 1;
// assert_eq!(result, expected);
// }
#[test]
fn assert_editor_config() {
let config = Config::new();
let result = config.editor_config();
let expected = EditorConfig::new(&Directories::new(None, None));
assert_eq!(result, &expected);
}
// #[test]
// fn assert_theme() {
// let config = Config::new();
// let result = config.theme();
// let expected = 1;
// assert_eq!(result, expected);
// }
#[test]
fn assert_theme() {
let config = Config::new();
let result = config.theme();
let expected = Theme::default();
assert_eq!(result, &expected);
}
#[test]
fn assert_menu_height() {

View File

@ -21,10 +21,8 @@ impl Directories {
config_dir.push(path);
config_dir.push("rider");
let path = match project_dir {
Some(s) => s,
None => dirs::runtime_dir().unwrap().to_str().unwrap().to_owned(),
};
let path = project_dir
.unwrap_or_else(|| dirs::runtime_dir().unwrap().to_str().unwrap().to_owned());
let mut project_dir = PathBuf::new();
project_dir.push(path);
project_dir.push(".rider");
@ -56,19 +54,12 @@ pub fn themes_dir(config_dir: &PathBuf) -> PathBuf {
}
pub fn fonts_dir(config_dir: &PathBuf) -> PathBuf {
let path = config_dir.to_str().unwrap().to_owned();
let mut path_buf = PathBuf::new();
path_buf.push(path);
path_buf.push("fonts");
path_buf
PathBuf::from(config_dir.to_str().unwrap().to_owned()).join("fonts")
}
#[cfg_attr(tarpaulin, skip)]
pub fn project_dir() -> PathBuf {
let path = dirs::runtime_dir().unwrap().to_str().unwrap().to_owned();
let mut path_buf = PathBuf::new();
path_buf.push(path);
path_buf.push(".rider");
path_buf
PathBuf::from(dirs::runtime_dir().unwrap().to_str().unwrap().to_owned()).join(".rider")
}
#[cfg_attr(tarpaulin, skip)]
@ -110,11 +101,10 @@ pub fn binaries_directory() -> Result<PathBuf, String> {
Err("Cannot find binaries!".to_string())
}
#[cfg_attr(tarpaulin, skip)]
pub fn get_binary_path(name: &str) -> Result<String, String> {
if cfg!(test) {
use std::fs;
println!("#[cfg(test)]");
let mut current_dir = env::current_dir().unwrap();
current_dir.push("target");
current_dir.push("debug");
@ -132,7 +122,6 @@ pub fn get_binary_path(name: &str) -> Result<String, String> {
}
Err(format!("Cannot find {:?}", name))
} else {
println!("#[cfg(not(test))]");
let r = binaries_directory();
let mut binaries: PathBuf = r.unwrap_or_else(|e| panic!(e));
binaries.push(name.to_string());

View File

@ -1,5 +1,6 @@
use crate::directories::Directories;
#[cfg_attr(test, derive(PartialEq))]
#[derive(Debug, Clone)]
pub struct EditorConfig {
character_size: u16,
@ -26,8 +27,15 @@ impl EditorConfig {
self.character_size
}
pub fn font_path(&self) -> &String {
&self.font_path
pub fn font_path(&self) -> &str {
self.font_path.as_str()
}
pub fn set_font_path<S>(&mut self, path: S)
where
S: Into<String>,
{
self.font_path = path.into();
}
pub fn current_theme(&self) -> &String {
@ -49,9 +57,14 @@ mod test {
#[test]
fn assert_font_path() {
let directories = Directories::new(Some("/tmp".to_owned()), None);
let config = EditorConfig::new(&directories);
let path = config.font_path().to_owned();
let expected: String = "/tmp/rider/fonts/DejaVuSansMono.ttf".to_owned();
let mut config = EditorConfig::new(&directories);
let path = config.font_path();
let expected = "/tmp/rider/fonts/DejaVuSansMono.ttf";
assert_eq!(path, expected);
config.set_font_path("/a");
let path = config.font_path();
let expected = "/a";
assert_eq!(path, expected);
}

View File

@ -53,6 +53,14 @@ impl Default for ScrollConfig {
mod tests {
use super::*;
#[test]
fn assert_default() {
let config = ScrollConfig::default();
assert_eq!(config.width, 4);
assert_eq!(config.margin_right, 5);
assert_eq!(config.speed, 10);
}
#[test]
fn assert_width() {
let config = ScrollConfig::new();

21
rider-derive/Cargo.toml Normal file
View File

@ -0,0 +1,21 @@
[package]
name = "rider-derive"
version = "0.1.0"
authors = ["Adrian Woźniak <adrian.wozniak.1986@gmail.com>"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
darling = { version = "*" }
[dependencies.proc-macro2]
version = "1.0"
[dependencies.quote]
version = "1.0"
[dependencies.syn]
version = "1.0"
features = ["full", "extra-traits"]

27
rider-derive/src/lib.rs Normal file
View File

@ -0,0 +1,27 @@
use proc_macro;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
#[proc_macro]
pub fn build_test_renderer(input: TokenStream) -> TokenStream {
let renderer_name = format_ident!("{}", input.to_string());
let exp = quote! {
let mut character_sizes = std::collections::HashMap::new();
let mut canvas = CanvasMock::new();
let config = build_config();
let mut surface =
sdl2::surface::Surface::new(512, 512, sdl2::pixels::PixelFormatEnum::RGB24).unwrap();
let mut surface_canvas = sdl2::render::Canvas::from_surface(surface).unwrap();
let mut texture_creator = surface_canvas.texture_creator();
let mut texture_manager = crate::renderer::managers::TextureManager::new(&texture_creator);
let mut ttf_context = sdl2::ttf::Sdl2TtfContext {};
let mut #renderer_name = SimpleRendererMock {
config: config.clone(),
ttf: ttf_context,
character_sizes,
texture_manager,
};
};
exp.into()
}

View File

@ -8,6 +8,8 @@ edition = "2018"
rider-config = { path = "../rider-config", version = "0.1.0" }
rider-lexers = { path = "../rider-lexers", version = "0.1.0" }
rider-themes = { path = "../rider-themes", version = "0.1.0" }
rider-derive = { path = "../rider-derive", version = "0.1.0" }
rider-match-widget = { path = "../rider-match-widget", version = "0.1.0" }
rand = "0.5"
dirs = "*"
serde = "*"

View File

@ -21,13 +21,13 @@ pub struct AppState {
impl AppState {
pub fn new(config: Arc<RwLock<Config>>) -> Self {
Self {
menu_bar: MenuBar::new(Arc::clone(&config)),
menu_bar: MenuBar::new(config.clone()),
project_tree: ProjectTreeSidebar::new(
Application::current_working_directory(),
config.clone(),
),
files: vec![],
file_editor: FileEditor::new(Arc::clone(&config)),
file_editor: FileEditor::new(config.clone()),
modal: None,
config,
}
@ -67,7 +67,7 @@ impl AppState {
pub fn open_settings<R>(&mut self, renderer: &mut R) -> Result<(), String>
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
match self.modal {
None => {
@ -87,7 +87,7 @@ impl AppState {
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
match self.modal.as_mut() {
Some(ModalType::OpenFile(modal)) => modal.open_directory(dir_path, renderer),
@ -145,14 +145,16 @@ impl AppState {
R: Renderer + ConfigHolder + CharacterSizeManager,
{
// file editor
self.file_editor.render(canvas, renderer);
self.file_editor
.render(canvas, renderer, &RenderContext::Nothing);
// menu bar
self.menu_bar
.render(canvas, renderer, &RenderContext::Nothing);
// project tree
self.project_tree.render(canvas, renderer);
self.project_tree
.render(canvas, renderer, &RenderContext::Nothing);
// settings modal
match self.modal.as_ref() {
@ -168,7 +170,7 @@ impl AppState {
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
self.menu_bar.prepare_ui();
self.project_tree.prepare_ui(renderer);
@ -189,7 +191,7 @@ impl AppState {
self.menu_bar.update(ticks, context);
// sidebar
self.project_tree.update(ticks);
self.project_tree.update(ticks, context);
// file editor
let context = UpdateContext::ParentPosition(
@ -253,21 +255,21 @@ impl ConfigHolder for AppState {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::support;
use std::sync::Arc;
use crate::tests::*;
use rider_derive::*;
#[test]
fn must_return_none_for_default_file() {
let config = support::build_config();
let state = AppState::new(Arc::clone(&config));
let config = build_config();
let state = AppState::new(config.clone());
let file = state.file_editor().file();
assert_eq!(file.is_none(), true);
}
#[test]
fn must_scroll_file_when_no_modal() {
let config = support::build_config();
let mut state = AppState::new(Arc::clone(&config));
let config = build_config();
let mut state = AppState::new(config.clone());
let old_scroll = state.file_editor().scroll();
state.set_open_file_modal(None);
state.scroll_by(10, 10);
@ -276,9 +278,9 @@ mod tests {
#[test]
fn must_scroll_modal_when_modal_was_set() {
let config = support::build_config();
let mut state = AppState::new(Arc::clone(&config));
let modal = OpenFile::new("/".to_owned(), 100, 100, Arc::clone(&config));
let config = build_config();
let mut state = AppState::new(config.clone());
let modal = OpenFile::new("/".to_owned(), 100, 100, config.clone());
let file_scroll = state.file_editor().scroll();
let old_scroll = state.file_editor().scroll();
state.set_open_file_modal(Some(modal));
@ -295,8 +297,8 @@ mod tests {
#[test]
fn must_fail_save_file_when_none_is_open() {
let config = support::build_config();
let state = AppState::new(Arc::clone(&config));
let config = build_config();
let state = AppState::new(config.clone());
let result = state.save_file();
assert_eq!(result, Err(format!("No buffer found")));
}
@ -313,9 +315,8 @@ mod tests {
true
);
let config = support::build_config();
let mut renderer = support::SimpleRendererMock::new(config.clone());
let mut state = AppState::new(Arc::clone(&config));
build_test_renderer!(renderer);
let mut state = AppState::new(config.clone());
let result = state.open_file(
format!("/tmp/must_succeed_save_file_when_file_is_open.md"),
&mut renderer,
@ -337,9 +338,8 @@ mod tests {
true
);
let config = support::build_config();
let mut renderer = support::SimpleRendererMock::new(config.clone());
let mut state = AppState::new(Arc::clone(&config));
build_test_renderer!(renderer);
let mut state = AppState::new(config.clone());
let result = state.open_file(
format!("/tmp/must_succeed_save_file_when_file_does_not_exists.md"),
&mut renderer,
@ -351,34 +351,32 @@ mod tests {
#[test]
fn must_close_modal_when_no_modal_is_open() {
let config = support::build_config();
let mut state = AppState::new(Arc::clone(&config));
let config = build_config();
let mut state = AppState::new(config.clone());
assert_eq!(state.close_modal(), Ok(()));
}
#[test]
fn must_close_modal_when_some_modal_is_open() {
let config = support::build_config();
let mut state = AppState::new(Arc::clone(&config));
let modal = OpenFile::new("/".to_owned(), 100, 100, Arc::clone(&config));
let config = build_config();
let mut state = AppState::new(config.clone());
let modal = OpenFile::new("/".to_owned(), 100, 100, config.clone());
state.set_open_file_modal(Some(modal));
assert_eq!(state.close_modal(), Ok(()));
}
#[test]
fn open_settings_when_there_is_no_other_modal() {
let config = support::build_config();
let mut renderer = support::SimpleRendererMock::new(config.clone());
let mut state = AppState::new(Arc::clone(&config));
build_test_renderer!(renderer);
let mut state = AppState::new(config.clone());
assert_eq!(state.open_settings(&mut renderer), Ok(()));
}
#[test]
fn open_settings_when_other_modal_is_open() {
let config = support::build_config();
let mut renderer = support::SimpleRendererMock::new(config.clone());
let mut state = AppState::new(Arc::clone(&config));
let modal = OpenFile::new("/".to_owned(), 100, 100, Arc::clone(&config));
build_test_renderer!(renderer);
let mut state = AppState::new(config.clone());
let modal = OpenFile::new("/".to_owned(), 100, 100, config.clone());
state.set_open_file_modal(Some(modal));
assert_eq!(state.open_settings(&mut renderer), Ok(()));
}
@ -390,9 +388,8 @@ mod tests {
true
);
let config = support::build_config();
let mut renderer = support::SimpleRendererMock::new(config.clone());
let mut state = AppState::new(Arc::clone(&config));
build_test_renderer!(renderer);
let mut state = AppState::new(config.clone());
state.open_directory("/must_open_directory".to_owned(), &mut renderer);
}
}

View File

@ -1,6 +1,9 @@
use crate::ui::*;
pub fn move_caret_right(file_editor: &mut FileEditor) -> Option<TextCharacter> {
pub fn move_caret_right<C>(file_editor: &mut C) -> Option<TextCharacter>
where
C: CaretAccess + FileAccess + ?Sized,
{
let text_character: TextCharacter = file_editor
.file()
.map(|file| file.get_character_at(file_editor.caret().text_position() + 1))??;
@ -12,7 +15,10 @@ pub fn move_caret_right(file_editor: &mut FileEditor) -> Option<TextCharacter> {
Some(text_character)
}
pub fn move_caret_left(file_editor: &mut FileEditor) -> Option<TextCharacter> {
pub fn move_caret_left<C>(file_editor: &mut C) -> Option<TextCharacter>
where
C: CaretAccess + FileAccess + ?Sized,
{
if file_editor.caret().text_position() == 0 {
return None;
}
@ -28,7 +34,10 @@ pub fn move_caret_left(file_editor: &mut FileEditor) -> Option<TextCharacter> {
Some(text_character)
}
pub fn move_caret_down(file_editor: &mut FileEditor) -> Option<TextCharacter> {
pub fn move_caret_down<C>(file_editor: &mut C) -> Option<TextCharacter>
where
C: CaretAccess + FileAccess + ?Sized,
{
if file_editor.caret().text_position() == 0 {
return None;
}
@ -68,7 +77,10 @@ pub fn move_caret_down(file_editor: &mut FileEditor) -> Option<TextCharacter> {
Some(text_character.clone())
}
pub fn move_caret_up(file_editor: &mut FileEditor) -> Option<TextCharacter> {
pub fn move_caret_up<C>(file_editor: &mut C) -> Option<TextCharacter>
where
C: CaretAccess + FileAccess + ?Sized,
{
if file_editor.caret().text_position() == 0 {
return None;
}
@ -121,12 +133,12 @@ pub fn move_caret_up(file_editor: &mut FileEditor) -> Option<TextCharacter> {
#[cfg(test)]
mod test_move_right {
use super::*;
use crate::tests::support;
use crate::tests::support::SimpleRendererMock;
use crate::tests::*;
use rider_derive::*;
#[test]
fn assert_move_with_no_file() {
let config = support::build_config();
let config = build_config();
let mut editor = FileEditor::new(config);
assert_eq!(move_caret_right(&mut editor).is_some(), false);
@ -134,8 +146,7 @@ mod test_move_right {
#[test]
fn assert_move_caret_with_empty_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "".to_owned(), config);
file.prepare_ui(&mut renderer);
@ -147,8 +158,7 @@ mod test_move_right {
#[test]
fn assert_move_caret_with_filled_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "hello".to_owned(), config);
file.prepare_ui(&mut renderer);
@ -162,12 +172,12 @@ mod test_move_right {
#[cfg(test)]
mod test_move_left {
use super::*;
use crate::tests::support;
use crate::tests::support::SimpleRendererMock;
use crate::tests::*;
use rider_derive::*;
#[test]
fn assert_move_caret_without_file() {
let config = support::build_config();
let config = build_config();
let mut editor = FileEditor::new(config);
assert_eq!(move_caret_left(&mut editor).is_some(), false);
@ -175,8 +185,7 @@ mod test_move_left {
#[test]
fn assert_move_caret_with_empty_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "".to_owned(), config);
file.prepare_ui(&mut renderer);
@ -188,8 +197,7 @@ mod test_move_left {
#[test]
fn assert_move_caret_with_filled_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "hello".to_owned(), config);
file.prepare_ui(&mut renderer);
@ -209,13 +217,12 @@ mod test_move_left {
#[cfg(test)]
mod test_move_up {
use super::*;
use crate::tests::support;
use crate::tests::support::SimpleRendererMock;
use crate::tests::*;
use rider_derive::*;
#[test]
fn assert_move_caret_with_top_of_filled_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "he\nll\no".to_owned(), config);
file.prepare_ui(&mut renderer);
@ -227,8 +234,7 @@ mod test_move_up {
#[test]
fn assert_move_caret_with_filled_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "he\nll\no".to_owned(), config);
file.prepare_ui(&mut renderer);
@ -249,13 +255,12 @@ mod test_move_up {
#[cfg(test)]
mod test_move_down {
use super::*;
use crate::tests::support;
use crate::tests::support::SimpleRendererMock;
use crate::tests::*;
use rider_derive::*;
#[test]
fn assert_move_caret_with_bottom_of_filled_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "he\nll\no".to_owned(), config);
file.prepare_ui(&mut renderer);
@ -270,8 +275,9 @@ mod test_move_down {
#[test]
fn assert_move_caret_with_filled_file() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
use crate::tests::*;
build_test_renderer!(renderer);
let mut editor = FileEditor::new(config.clone());
let mut file = EditorFile::new("test.txt".to_owned(), "he\nll\nod".to_owned(), config);
file.prepare_ui(&mut renderer);

View File

@ -34,7 +34,7 @@ where
let move_to = file_editor
.file()
.and_then(|f| f.get_character_at(position.text_position()))
.map(|character| character.dest())
.map(|character| character.dest().clone())
.map(|dest| (position, Point::new(dest.x(), dest.y())));
match move_to {
Some((position, point)) => file_editor.caret_mut().move_caret(position, point),
@ -171,7 +171,7 @@ mod tests {
use super::*;
use crate::renderer::managers::FontDetails;
use crate::renderer::managers::TextDetails;
use crate::tests::support;
use crate::tests::*;
use sdl2::rect::Rect;
use sdl2::render::Texture;
use sdl2::ttf::Font;
@ -226,7 +226,7 @@ mod tests {
#[test]
fn must_return_empty_string_when_no_file() {
let config = support::build_config();
let config = build_config();
let mut editor = FileEditor::new(config);
let result = current_file_path(&mut editor);
assert_eq!(result, String::new());
@ -234,7 +234,7 @@ mod tests {
#[test]
fn must_return_path_string_when_file_was_set() {
let config = support::build_config();
let config = build_config();
let mut editor = FileEditor::new(Arc::clone(&config));
let file = EditorFile::new(
"/foo/bar".to_owned(),
@ -252,7 +252,7 @@ mod tests {
#[test]
fn assert_insert_text_without_file() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
widget.prepare_ui(&mut renderer);
@ -263,7 +263,7 @@ mod tests {
#[test]
fn assert_insert_text_to_empty_file() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
let file = EditorFile::new("".to_owned(), "".to_owned(), config.clone());
@ -276,7 +276,7 @@ mod tests {
#[test]
fn assert_insert_text_to_file_without_new_line() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
let mut file = EditorFile::new("".to_owned(), "bar".to_owned(), config.clone());
@ -294,7 +294,7 @@ mod tests {
#[test]
fn assert_insert_text_to_file_with_new_line() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
let mut file = EditorFile::new("".to_owned(), "bar\n".to_owned(), config.clone());
@ -312,7 +312,7 @@ mod tests {
#[test]
fn assert_insert_text_to_file_with_new_line_with_caret_at_new_line() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
let mut file = EditorFile::new("".to_owned(), "old content\n".to_owned(), config.clone());
@ -335,7 +335,7 @@ mod tests {
#[test]
fn assert_insert_new_line_without_file() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
widget.prepare_ui(&mut renderer);
@ -344,12 +344,12 @@ mod tests {
let expected = CaretPosition::new(0, 0, 0);
assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 0, 6, 15);
assert_eq!(widget.caret().dest(), expected);
assert_eq!(widget.caret().dest(), &expected);
}
#[test]
fn assert_insert_new_line_to_empty_file() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
let file = EditorFile::new("".to_owned(), "".to_owned(), config.clone());
@ -360,12 +360,12 @@ mod tests {
let expected = CaretPosition::new(1, 1, 0);
assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 13, 6, 15);
assert_eq!(widget.caret().dest(), expected);
assert_eq!(widget.caret().dest(), &expected);
}
#[test]
fn assert_insert_new_line_to_file_at_beginning() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
let file = EditorFile::new("".to_owned(), "foo".to_owned(), config.clone());
@ -376,14 +376,14 @@ mod tests {
let expected = CaretPosition::new(1, 1, 0);
assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 13, 6, 15);
assert_eq!(widget.caret().dest(), expected);
assert_eq!(widget.caret().dest(), &expected);
assert_eq!(widget.file().is_some(), true);
assert_eq!(widget.file().unwrap().buffer(), "\nfoo".to_owned());
}
#[test]
fn assert_insert_new_line_to_file_in_middle() {
let config = support::build_config();
let config = build_config();
let mut renderer = RendererMock::new(config.clone());
let mut widget = FileEditor::new(config.clone());
let mut file = EditorFile::new("hello.txt".to_owned(), "abcd".to_owned(), config.clone());
@ -397,7 +397,7 @@ mod tests {
let expected = CaretPosition::new(3, 1, 0);
assert_eq!(widget.caret().position(), &expected);
let expected = Rect::new(0, 13, 6, 15);
assert_eq!(widget.caret().dest(), expected);
assert_eq!(widget.caret().dest(), &expected);
assert_eq!(widget.file().is_some(), true);
assert_eq!(widget.file().unwrap().buffer(), "ab\ncd".to_owned());
}

View File

@ -1,10 +1,17 @@
#![feature(clamp)]
#![feature(
clamp,
associated_type_bounds,
crate_visibility_modifier,
proc_macro_hygiene
)]
extern crate dirs;
#[macro_use]
extern crate log;
extern crate lazy_static;
extern crate rand;
extern crate rider_config;
extern crate rider_derive;
extern crate rider_lexers;
extern crate rider_themes;
extern crate sdl2;
@ -20,14 +27,12 @@ use std::fs::File;
pub mod app;
pub mod renderer;
#[cfg(test)]
#[macro_use]
pub mod tests;
pub mod ui;
#[cfg_attr(tarpaulin, skip)]
fn init_logger(directories: &Directories) {
// use simplelog::SharedLogger;
let mut log_file_path = directories.log_dir.clone();
log_file_path.push("rider.log");

View File

@ -41,7 +41,13 @@ pub struct FontDetails {
impl From<&EditorConfig> for FontDetails {
fn from(config: &EditorConfig) -> Self {
FontDetails::new(config.font_path().as_str(), config.character_size().clone())
FontDetails::new(config.font_path(), config.character_size().clone())
}
}
impl From<(&str, u16)> for FontDetails {
fn from((path, size): (&str, u16)) -> Self {
FontDetails::new(path, size)
}
}
@ -97,18 +103,10 @@ impl<'a> From<&'a FontDetails> for FontDetails {
#[cfg_attr(tarpaulin, skip)]
//noinspection RsWrongLifetimeParametersNumber
pub type TextureManager<'l> =
ResourceManager<'l, String, Texture<'l>, TextureCreator<sdl2::video::WindowContext>>;
pub type TextureManager<'l, T> = ResourceManager<'l, String, Texture<'l>, TextureCreator<T>>;
#[cfg_attr(tarpaulin, skip)]
pub type FontManager<'l> = ResourceManager<'l, FontDetails, Font<'l, 'static>, Sdl2TtfContext>;
#[cfg_attr(tarpaulin, skip)]
pub trait ManagersHolder<'l> {
fn font_manager(&mut self) -> &mut FontManager<'l>;
fn texture_manager(&mut self) -> &mut TextureManager<'l>;
}
#[cfg_attr(tarpaulin, skip)]
#[derive(Clone)]
pub struct ResourceManager<'l, K, R, L>
@ -160,7 +158,7 @@ impl<'l, T> ResourceLoader<'l, Texture<'l>> for TextureCreator<T> {
type Args = str;
fn load(&'l self, path: &str) -> Result<Texture, String> {
println!("Loading {}...", path);
debug!("Loading {}...", path);
self.load_texture(path)
}
}
@ -170,7 +168,7 @@ impl<'l> ResourceLoader<'l, Font<'l, 'static>> for Sdl2TtfContext {
type Args = FontDetails;
fn load(&'l self, data: &FontDetails) -> Result<Font<'l, 'static>, String> {
info!("Loading font {}...", data.path);
debug!("Loading font {}...", data.path);
self.load_font(&data.path, data.size)
}
}
@ -186,7 +184,7 @@ pub trait TextTextureManager<'l> {
}
#[cfg_attr(tarpaulin, skip)]
impl<'l> TextTextureManager<'l> for TextureManager<'l> {
impl<'l, T> TextTextureManager<'l> for TextureManager<'l, T> {
//noinspection RsWrongLifetimeParametersNumber
fn load_text(
&mut self,
@ -203,9 +201,6 @@ impl<'l> TextTextureManager<'l> for TextureManager<'l> {
let texture = self.loader.create_texture_from_surface(&surface).unwrap();
let resource = Rc::new(texture);
self.cache.insert(key, resource.clone());
// for c in details.text.chars() {
// info!("texture for '{:?}' created", c);
// }
Ok(resource)
},
Ok,

View File

@ -26,7 +26,7 @@ pub trait Renderer {
pub struct CanvasRenderer<'l> {
config: ConfigAccess,
font_manager: FontManager<'l>,
texture_manager: TextureManager<'l>,
texture_manager: TextureManager<'l, sdl2::video::WindowContext>,
character_sizes: HashMap<TextCharacterDetails, Rect>,
}
@ -58,13 +58,13 @@ impl<'l> CharacterSizeManager for CanvasRenderer<'l> {
let (font_path, font_size) = {
let config = self.config().read().unwrap();
(
config.editor_config().font_path().clone(),
config.editor_config().font_path().to_string(),
config.editor_config().character_size().clone(),
)
};
let details = TextCharacterDetails {
c: c.clone(),
font_path,
font_path: font_path.to_string(),
font_size,
};
self.character_sizes
@ -80,17 +80,6 @@ impl<'l> CharacterSizeManager for CanvasRenderer<'l> {
}
}
#[cfg_attr(tarpaulin, skip)]
impl<'l> ManagersHolder<'l> for CanvasRenderer<'l> {
fn font_manager(&mut self) -> &mut FontManager<'l> {
&mut self.font_manager
}
fn texture_manager(&mut self) -> &mut TextureManager<'l> {
&mut self.texture_manager
}
}
#[cfg_attr(tarpaulin, skip)]
impl<'l> ConfigHolder for CanvasRenderer<'l> {
fn config(&self) -> &ConfigAccess {
@ -101,7 +90,7 @@ impl<'l> ConfigHolder for CanvasRenderer<'l> {
#[cfg_attr(tarpaulin, skip)]
impl<'l> Renderer for CanvasRenderer<'l> {
fn load_font(&mut self, details: FontDetails) -> Rc<Font> {
self.font_manager()
self.font_manager
.load(&details)
.unwrap_or_else(|_| panic!("Font not found {:?}", details))
}
@ -113,11 +102,10 @@ impl<'l> Renderer for CanvasRenderer<'l> {
) -> Result<Rc<Texture>, String> {
use crate::renderer::managers::*;
let font = self
.font_manager()
.font_manager
.load(&font_details)
.unwrap_or_else(|_| panic!("Font not found {:?}", details));
let tex_manager = self.texture_manager();
tex_manager.load_text(details, font)
self.texture_manager.load_text(details, font)
}
fn load_image(&mut self, path: String) -> Result<Rc<Texture>, String> {

View File

@ -1,280 +0,0 @@
#[cfg(test)]
pub mod support {
use crate::renderer::managers::FontDetails;
use crate::renderer::managers::TextDetails;
use crate::renderer::renderer::Renderer;
use crate::ui::text_character::CharacterSizeManager;
use crate::ui::CanvasAccess;
use rider_config::Config;
use rider_config::ConfigAccess;
use rider_config::ConfigHolder;
use sdl2::pixels::Color;
use sdl2::rect::Point;
use sdl2::rect::Rect;
use sdl2::render::Texture;
use sdl2::ttf::Font;
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Error;
use std::fmt::Formatter;
use std::rc::Rc;
use std::sync::*;
pub fn build_path(path: String) {
use std::fs;
fs::create_dir_all(path.as_str()).unwrap();
fs::write((path.clone() + &"/file1".to_owned()).as_str(), "foo").unwrap();
fs::write((path.clone() + &"/file2".to_owned()).as_str(), "bar").unwrap();
fs::create_dir_all((path.clone() + &"/dir1".to_owned()).as_str()).unwrap();
fs::create_dir_all((path.clone() + &"/dir2".to_owned()).as_str()).unwrap();
}
pub fn build_config() -> Arc<RwLock<Config>> {
let mut config = Config::new();
config.set_theme(config.editor_config().current_theme().clone());
Arc::new(RwLock::new(config))
}
#[cfg_attr(tarpaulin, skip)]
#[derive(Debug, PartialEq)]
pub enum CanvasShape {
Line,
Border,
Rectangle,
Image(Rect, Rect, String),
}
#[derive(Debug, PartialEq)]
pub struct RendererRect {
pub rect: Rect,
pub color: Color,
pub shape: CanvasShape,
}
#[cfg_attr(tarpaulin, skip)]
impl RendererRect {
pub fn new(rect: Rect, color: Color, shape: CanvasShape) -> Self {
Self { rect, color, shape }
}
}
#[cfg_attr(tarpaulin, skip)]
pub struct CanvasMock {
pub rects: Vec<RendererRect>,
pub borders: Vec<RendererRect>,
pub lines: Vec<RendererRect>,
pub clippings: Vec<Option<Rect>>,
pub character_sizes: HashMap<char, sdl2::rect::Rect>,
}
#[cfg_attr(tarpaulin, skip)]
impl Debug for CanvasMock {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(
f,
"CanvasMock {{ {:?} {:?} {:?} }}",
self.rects, self.lines, self.clippings
)
}
}
#[cfg_attr(tarpaulin, skip)]
impl PartialEq for CanvasMock {
fn eq(&self, other: &CanvasMock) -> bool {
self.rects == other.rects
&& self.borders == other.borders
&& self.clippings == other.clippings
&& self.lines == other.lines
}
}
#[cfg_attr(tarpaulin, skip)]
impl CanvasMock {
pub fn new() -> Self {
Self {
rects: vec![],
borders: vec![],
lines: vec![],
clippings: vec![],
character_sizes: HashMap::new(),
}
}
}
#[cfg_attr(tarpaulin, skip)]
impl CanvasAccess for CanvasMock {
fn render_rect(&mut self, rect: Rect, color: Color) -> Result<(), String> {
self.rects.push(RendererRect {
rect,
color,
shape: CanvasShape::Rectangle,
});
Ok(())
}
fn render_border(&mut self, rect: Rect, color: Color) -> Result<(), String> {
self.borders.push(RendererRect {
rect,
color,
shape: CanvasShape::Border,
});
Ok(())
}
fn render_image(&mut self, _tex: Rc<Texture>, src: Rect, dest: Rect) -> Result<(), String> {
self.rects.push(RendererRect::new(
dest.clone(),
Color::RGBA(0, 0, 0, 255),
CanvasShape::Image(src.clone(), dest.clone(), format!("_tex: Rc<Texture>")),
));
Ok(())
}
fn render_line(&mut self, start: Point, end: Point, color: Color) -> Result<(), String> {
self.lines.push(RendererRect {
rect: Rect::new(start.x(), start.y(), end.x() as u32, end.y() as u32),
color,
shape: CanvasShape::Line,
});
Ok(())
}
fn set_clipping(&mut self, rect: Rect) {
self.clippings.push(Some(rect));
}
fn set_clip_rect(&mut self, rect: Option<Rect>) {
self.clippings.push(rect);
}
fn clip_rect(&self) -> Option<Rect> {
self.clippings.last().cloned().unwrap_or_else(|| None)
}
}
impl CharacterSizeManager for CanvasMock {
fn load_character_size(&mut self, c: char) -> Rect {
match self.character_sizes.get(&c) {
Some(r) => r.clone(),
None => {
self.character_sizes.insert(c, Rect::new(0, 0, 1, 1));
self.character_sizes.get(&c).cloned().unwrap()
}
}
}
}
impl CanvasMock {
pub fn set_character_rect(&mut self, c: char, rect: Rect) {
self.character_sizes.insert(c, rect);
}
pub fn find_pixel_with_color(
&self,
point: sdl2::rect::Point,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.rects.iter() {
if rect.rect.contains_point(point.clone()) && rect.color == color {
return Some(rect.clone());
}
}
for rect in self.borders.iter() {
if rect.rect.contains_point(point.clone()) && rect.color == color {
return Some(rect.clone());
}
}
for rect in self.lines.iter() {
if rect.rect.contains_point(point.clone()) && rect.color == color {
return Some(rect.clone());
}
}
None
}
pub fn find_rect_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.rects.iter() {
if rect.rect == subject && rect.color == color {
return Some(rect.clone());
}
}
None
}
pub fn find_line_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.lines.iter() {
if rect.rect == subject && rect.color == color {
return Some(rect.clone());
}
}
None
}
pub fn find_border_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.borders.iter() {
if rect.rect == subject && rect.color == color {
return Some(rect.clone());
}
}
None
}
}
#[cfg_attr(tarpaulin, skip)]
pub struct SimpleRendererMock {
config: ConfigAccess,
}
#[cfg_attr(tarpaulin, skip)]
impl SimpleRendererMock {
pub fn new(config: ConfigAccess) -> Self {
Self { config }
}
}
#[cfg_attr(tarpaulin, skip)]
impl Renderer for SimpleRendererMock {
fn load_font(&mut self, _details: FontDetails) -> Rc<Font> {
unimplemented!()
}
fn load_text_tex(
&mut self,
_details: &mut TextDetails,
_font_details: FontDetails,
) -> Result<Rc<Texture>, String> {
Err("skip text texture".to_owned())
}
fn load_image(&mut self, _path: String) -> Result<Rc<Texture>, String> {
Err("skip img texture".to_owned())
}
}
#[cfg_attr(tarpaulin, skip)]
impl CharacterSizeManager for SimpleRendererMock {
fn load_character_size(&mut self, _c: char) -> Rect {
Rect::new(0, 0, 13, 14)
}
}
#[cfg_attr(tarpaulin, skip)]
impl ConfigHolder for SimpleRendererMock {
fn config(&self) -> &Arc<RwLock<Config>> {
&self.config
}
}
}

View File

@ -0,0 +1,402 @@
use crate::renderer::managers::FontDetails;
use crate::renderer::managers::TextDetails;
use crate::renderer::renderer::Renderer;
use crate::renderer::TextureManager;
use crate::ui::text_character::CharacterSizeManager;
use crate::ui::CanvasAccess;
use rider_config::Config;
use rider_config::ConfigAccess;
use rider_config::ConfigHolder;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::rect::Point;
use sdl2::rect::Rect;
use sdl2::render::{Texture, TextureCreator};
use sdl2::ttf::{Font, Sdl2TtfContext};
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Error;
use std::fmt::Formatter;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::*;
#[cfg_attr(tarpaulin, skip)]
pub fn build_path(path: String) {
use std::fs;
fs::create_dir_all(path.as_str()).unwrap();
fs::write((path.clone() + &"/file1".to_owned()).as_str(), "foo").unwrap();
fs::write((path.clone() + &"/file2".to_owned()).as_str(), "bar").unwrap();
fs::create_dir_all((path.clone() + &"/dir1".to_owned()).as_str()).unwrap();
fs::create_dir_all((path.clone() + &"/dir2".to_owned()).as_str()).unwrap();
}
#[cfg_attr(tarpaulin, skip)]
pub fn build_config() -> Arc<RwLock<Config>> {
let mut config = Config::new();
config.set_theme(config.editor_config().current_theme().clone());
Arc::new(RwLock::new(config))
}
#[cfg_attr(tarpaulin, skip)]
#[derive(Debug, PartialEq)]
pub enum CanvasShape {
Line,
Border,
Rectangle,
Image(Rect, Rect, String),
}
#[cfg_attr(tarpaulin, skip)]
#[derive(Debug, PartialEq)]
pub struct RendererRect {
pub rect: Rect,
pub color: Color,
pub shape: CanvasShape,
}
#[cfg_attr(tarpaulin, skip)]
impl RendererRect {
pub fn new(rect: Rect, color: Color, shape: CanvasShape) -> Self {
Self { rect, color, shape }
}
}
#[cfg_attr(tarpaulin, skip)]
pub struct CanvasMock {
pub rects: Vec<RendererRect>,
pub borders: Vec<RendererRect>,
pub lines: Vec<RendererRect>,
pub clippings: Vec<Option<Rect>>,
pub character_sizes: HashMap<char, sdl2::rect::Rect>,
}
#[cfg_attr(tarpaulin, skip)]
impl Debug for CanvasMock {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(
f,
"CanvasMock {{ {:?} {:?} {:?} }}",
self.rects, self.lines, self.clippings
)
}
}
#[cfg_attr(tarpaulin, skip)]
impl PartialEq for CanvasMock {
fn eq(&self, other: &CanvasMock) -> bool {
self.rects == other.rects
&& self.borders == other.borders
&& self.clippings == other.clippings
&& self.lines == other.lines
}
}
#[cfg_attr(tarpaulin, skip)]
impl CanvasMock {
pub fn new() -> Self {
Self {
rects: vec![],
borders: vec![],
lines: vec![],
clippings: vec![],
character_sizes: HashMap::new(),
}
}
}
#[cfg_attr(tarpaulin, skip)]
impl CanvasAccess for CanvasMock {
fn render_rect(&mut self, rect: Rect, color: Color) -> Result<(), String> {
self.rects.push(RendererRect {
rect,
color,
shape: CanvasShape::Rectangle,
});
Ok(())
}
fn render_border(&mut self, rect: Rect, color: Color) -> Result<(), String> {
self.borders.push(RendererRect {
rect,
color,
shape: CanvasShape::Border,
});
Ok(())
}
fn render_image(&mut self, _tex: Rc<Texture>, src: Rect, dest: Rect) -> Result<(), String> {
self.rects.push(RendererRect::new(
dest.clone(),
Color::RGBA(0, 0, 0, 255),
CanvasShape::Image(src.clone(), dest.clone(), format!("_tex: Rc<Texture>")),
));
Ok(())
}
fn render_line(&mut self, start: Point, end: Point, color: Color) -> Result<(), String> {
self.lines.push(RendererRect {
rect: Rect::new(start.x(), start.y(), end.x() as u32, end.y() as u32),
color,
shape: CanvasShape::Line,
});
Ok(())
}
fn set_clipping(&mut self, rect: Rect) {
self.clippings.push(Some(rect));
}
fn set_clip_rect(&mut self, rect: Option<Rect>) {
self.clippings.push(rect);
}
fn clip_rect(&self) -> Option<Rect> {
self.clippings.last().cloned().unwrap_or_else(|| None)
}
}
#[cfg_attr(tarpaulin, skip)]
impl CharacterSizeManager for CanvasMock {
fn load_character_size(&mut self, c: char) -> Rect {
match self.character_sizes.get(&c) {
Some(r) => r.clone(),
None => {
self.character_sizes.insert(c, Rect::new(0, 0, 1, 1));
self.character_sizes.get(&c).cloned().unwrap()
}
}
}
}
#[cfg_attr(tarpaulin, skip)]
pub trait CanvasTester {
fn set_character_rect(&mut self, c: char, rect: Rect);
fn find_pixel_with_color(
&self,
point: sdl2::rect::Point,
color: sdl2::pixels::Color,
) -> Option<&RendererRect>;
fn find_rect_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect>;
fn find_line_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect>;
fn find_border_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect>;
}
#[cfg_attr(tarpaulin, skip)]
impl CanvasTester for CanvasMock {
fn set_character_rect(&mut self, c: char, rect: Rect) {
self.character_sizes.insert(c, rect);
}
fn find_pixel_with_color(
&self,
point: sdl2::rect::Point,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.rects.iter() {
if rect.rect.contains_point(point.clone()) && rect.color == color {
return Some(rect.clone());
}
}
for rect in self.borders.iter() {
if rect.rect.contains_point(point.clone()) && rect.color == color {
return Some(rect.clone());
}
}
for rect in self.lines.iter() {
if rect.rect.contains_point(point.clone()) && rect.color == color {
return Some(rect.clone());
}
}
None
}
fn find_rect_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.rects.iter() {
if rect.rect == subject && rect.color == color {
return Some(rect.clone());
}
}
None
}
fn find_line_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.lines.iter() {
if rect.rect == subject && rect.color == color {
return Some(rect.clone());
}
}
None
}
fn find_border_with_color(
&self,
subject: sdl2::rect::Rect,
color: sdl2::pixels::Color,
) -> Option<&RendererRect> {
for rect in self.borders.iter() {
if rect.rect == subject && rect.color == color {
return Some(rect.clone());
}
}
None
}
}
#[cfg_attr(tarpaulin, skip)]
pub struct SimpleRendererMock<'l> {
pub config: ConfigAccess,
pub ttf: Sdl2TtfContext,
pub character_sizes: HashMap<char, Rect>,
pub texture_manager: TextureManager<'l, sdl2::surface::SurfaceContext<'l>>,
}
#[cfg_attr(tarpaulin, skip)]
impl<'l> SimpleRendererMock<'l> {
pub fn set_character_rect(&mut self, c: char, rect: Rect) {
self.character_sizes.insert(c, rect);
}
pub fn texture_creator(&self) -> &TextureCreator<sdl2::surface::SurfaceContext<'l>> {
self.texture_manager.loader()
}
}
#[cfg_attr(tarpaulin, skip)]
impl<'l> Renderer for SimpleRendererMock<'l> {
fn load_font(&mut self, details: FontDetails) -> Rc<Font> {
Rc::new(
self.ttf
.load_font(details.path, details.size)
.unwrap_or_else(|e| panic!("{:?}", e)),
)
}
fn load_text_tex(
&mut self,
_details: &mut TextDetails,
_font_details: FontDetails,
) -> Result<Rc<Texture>, String> {
self.texture_creator()
.create_texture(
PixelFormatEnum::RGB24,
sdl2::render::TextureAccess::Target,
24,
24,
)
.map_err(|e| format!("{:?}", e))
.map(|t| Rc::new(t))
}
fn load_image(&mut self, path: String) -> Result<Rc<Texture>, String> {
self.texture_manager.load(path.as_str())
}
}
#[cfg_attr(tarpaulin, skip)]
impl<'l> CharacterSizeManager for SimpleRendererMock<'l> {
fn load_character_size(&mut self, c: char) -> Rect {
match self.character_sizes.get(&c) {
Some(r) => r.clone(),
_ => {
let rect = Rect::new(0, 0, 13, 14);
self.set_character_rect(c.clone(), rect.clone());
rect
}
}
}
}
#[cfg_attr(tarpaulin, skip)]
impl<'l> ConfigHolder for SimpleRendererMock<'l> {
fn config(&self) -> &Arc<RwLock<Config>> {
&self.config
}
}
#[cfg_attr(tarpaulin, skip)]
pub type TestCanvas<'r> = sdl2::render::Canvas<sdl2::surface::Surface<'r>>;
#[cfg_attr(tarpaulin, skip)]
impl<'r> CanvasAccess for TestCanvas<'r> {
fn render_rect(&mut self, rect: Rect, color: sdl2::pixels::Color) -> Result<(), String> {
self.set_draw_color(color);
self.fill_rect(rect)
}
fn render_border(&mut self, rect: Rect, color: sdl2::pixels::Color) -> Result<(), String> {
self.set_draw_color(color);
self.draw_rect(rect)
}
fn render_image(&mut self, tex: Rc<Texture>, src: Rect, dest: Rect) -> Result<(), String> {
self.copy_ex(&tex, Some(src), Some(dest), 0.0, None, false, false)
}
fn render_line(
&mut self,
start: Point,
end: Point,
color: sdl2::pixels::Color,
) -> Result<(), String> {
self.set_draw_color(color);
self.draw_line(start, end)
}
fn set_clipping(&mut self, rect: Rect) {
self.set_clip_rect(rect);
}
fn set_clip_rect(&mut self, rect: Option<Rect>) {
self.set_clip_rect(rect);
}
fn clip_rect(&self) -> Option<Rect> {
self.clip_rect()
}
}
#[cfg_attr(tarpaulin, skip)]
pub trait DumpImage {
fn dump_ui<S>(&self, path: S)
where
S: Into<String>;
}
#[cfg_attr(tarpaulin, skip)]
impl<'r> DumpImage for TestCanvas<'r> {
fn dump_ui<S>(&self, path: S)
where
S: Into<String>,
{
let p = std::path::PathBuf::from(path.into());
std::fs::create_dir_all(p.parent().unwrap()).unwrap();
self.surface()
.save_bmp(p)
.expect("Failed to save canvas as BMP file");
}
}

View File

@ -11,6 +11,7 @@ pub struct SaveButton {
inner: WidgetInner,
}
#[cfg_attr(tarpaulin, skip)]
impl std::ops::Deref for SaveButton {
type Target = WidgetInner;
@ -19,6 +20,7 @@ impl std::ops::Deref for SaveButton {
}
}
#[cfg_attr(tarpaulin, skip)]
impl std::ops::DerefMut for SaveButton {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
@ -79,11 +81,11 @@ impl SaveButton {
mod test {
use super::*;
use crate::app::UpdateResult;
use crate::tests::support;
use crate::tests::*;
#[test]
fn must_return_save_on_left_click() {
let config = support::build_config();
let config = build_config();
let mut widget = SaveButton::new(config);
assert_eq!(
widget.on_left_click(&Point::new(0, 0), &UpdateContext::Nothing),
@ -93,7 +95,7 @@ mod test {
#[test]
fn must_use_inner() {
let config = support::build_config();
let config = build_config();
let mut widget = SaveButton::new(config);
assert_eq!(
@ -113,7 +115,7 @@ mod test {
#[test]
fn must_have_padding() {
let config = support::build_config();
let config = build_config();
let widget = SaveButton::new(config);
assert_eq!(widget.padding_width(), ICON_DEST_WIDTH);
assert_eq!(widget.padding_height(), ICON_DEST_HEIGHT);

View File

@ -80,11 +80,11 @@ impl SettingsButton {
mod test {
use super::*;
use crate::app::UpdateResult;
use crate::tests::support;
use crate::tests::*;
#[test]
fn must_return_open_settings_on_left_click() {
let config = support::build_config();
let config = build_config();
let mut widget = SettingsButton::new(config);
assert_eq!(
widget.on_left_click(&Point::new(0, 0), &UpdateContext::Nothing),
@ -94,7 +94,7 @@ mod test {
#[test]
fn must_use_inner() {
let config = support::build_config();
let config = build_config();
let mut widget = SettingsButton::new(config);
assert_eq!(
@ -114,7 +114,7 @@ mod test {
#[test]
fn must_have_padding() {
let config = support::build_config();
let config = build_config();
let widget = SettingsButton::new(config);
assert_eq!(widget.padding_width(), ICON_DEST_WIDTH);
assert_eq!(widget.padding_height(), ICON_DEST_HEIGHT);

View File

@ -1,6 +1,7 @@
use crate::app::UpdateResult as UR;
use crate::app::UpdateResult;
use crate::renderer::Renderer;
use crate::ui::*;
use rider_config::ConfigAccess;
use rider_config::{ConfigAccess, ConfigHolder};
use sdl2::rect::{Point, Rect};
use std::ops::{Deref, DerefMut};
@ -69,11 +70,56 @@ impl DerefMut for Caret {
}
}
#[cfg_attr(tarpaulin, skip)]
impl Caret {
pub fn render<T>(&self, canvas: &mut T, context: &RenderContext)
impl Widget for Caret {
fn texture_path(&self) -> Option<String> {
None
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn source(&self) -> &Rect {
&self.dest
}
fn set_source(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UpdateResult {
self.blink_delay += 1;
if self.blink_delay >= 15 {
self.blink_delay = 0;
self.toggle_state();
}
UpdateResult::NoOp
}
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::NoOp
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match context {
&UpdateContext::ParentPosition(p) => move_render_point(p, &self.dest),
_ => self.dest().clone(),
}
.contains_point(point.clone())
}
fn render_start_point(&self) -> Point {
self.dest().top_left()
}
fn render<C, R>(&self, canvas: &mut C, _renderer: &mut R, context: &RenderContext)
where
T: CanvasAccess,
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
use std::borrow::*;
@ -92,54 +138,15 @@ impl Caret {
.render_line(start, end, color)
.unwrap_or_else(|_| panic!("Failed to draw a caret"));
}
pub fn prepare_ui<T>(&mut self, renderer: &mut T)
fn prepare_ui<'l, T>(&mut self, renderer: &mut T)
where
T: CharacterSizeManager,
T: Renderer + CharacterSizeManager + ConfigHolder,
{
let rect = renderer.load_character_size('I');
self.dest.set_height(rect.height());
}
}
impl Caret {
pub fn update(&mut self) -> UR {
self.blink_delay += 1;
if self.blink_delay >= 15 {
self.blink_delay = 0;
self.toggle_state();
}
UR::NoOp
}
}
impl ClickHandler for Caret {
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::NoOp
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
let dest = self.dest();
is_in_rect(
point,
&match context {
&UpdateContext::ParentPosition(p) => move_render_point(p, &dest),
_ => dest,
},
)
}
}
impl RenderBox for Caret {
fn render_start_point(&self) -> Point {
self.dest().top_left()
}
fn dest(&self) -> Rect {
self.dest.clone()
}
}
#[cfg(test)]
mod test_own_methods {
use crate::tests::*;
@ -149,7 +156,7 @@ mod test_own_methods {
#[test]
fn assert_move_caret() {
let config = support::build_config();
let config = build_config();
let mut widget = Caret::new(Arc::clone(&config));
widget.move_caret(widget.moved(10, 21, 34), Point::new(10, 20));
let result = (
@ -164,7 +171,7 @@ mod test_own_methods {
#[test]
fn assert_reset() {
let config = support::build_config();
let config = build_config();
let mut widget = Caret::new(Arc::clone(&config));
widget.reset_caret();
let result = (
@ -179,7 +186,7 @@ mod test_own_methods {
#[test]
fn assert_toggle_state() {
let config = support::build_config();
let config = build_config();
let mut widget = Caret::new(Arc::clone(&config));
let old = widget.state().clone();
@ -208,7 +215,7 @@ mod test_deref {
#[test]
fn must_deref_text_position() {
let config = support::build_config();
let config = build_config();
let mut widget = Caret::new(Arc::clone(&config));
widget.move_caret(widget.moved(10, 21, 34), Point::new(0, 0));
let result = widget.text_position();
@ -218,7 +225,7 @@ mod test_deref {
#[test]
fn must_deref_line_number() {
let config = support::build_config();
let config = build_config();
let mut widget = Caret::new(Arc::clone(&config));
widget.move_caret(widget.moved(10, 21, 34), Point::new(0, 0));
let result = widget.line_number();
@ -228,7 +235,7 @@ mod test_deref {
#[test]
fn must_deref_line_position() {
let config = support::build_config();
let config = build_config();
let mut widget = Caret::new(Arc::clone(&config));
widget.move_caret(widget.moved(10, 21, 34), Point::new(0, 0));
let result = widget.line_position();
@ -246,7 +253,7 @@ mod test_render_box {
#[test]
fn must_return_top_left_point() {
let config = support::build_config();
let config = build_config();
let widget = Caret::new(Arc::clone(&config));
let result = widget.render_start_point();
let expected = Point::new(0, 0);
@ -264,7 +271,7 @@ mod test_click_handler {
#[test]
fn refute_when_not_click_target() {
let config = support::build_config();
let config = build_config();
let widget = Caret::new(Arc::clone(&config));
let point = Point::new(9999, 9999);
let context = UpdateContext::Nothing;
@ -274,7 +281,7 @@ mod test_click_handler {
#[test]
fn assert_when_click_target() {
let config = support::build_config();
let config = build_config();
let widget = Caret::new(Arc::clone(&config));
let point = Point::new(0, 0);
@ -285,7 +292,7 @@ mod test_click_handler {
#[test]
fn refute_when_not_click_target_because_parent() {
let config = support::build_config();
let config = build_config();
let widget = Caret::new(Arc::clone(&config));
let point = Point::new(20, 30);
let context = UpdateContext::ParentPosition(Point::new(9999, 9999));
@ -295,7 +302,7 @@ mod test_click_handler {
#[test]
fn assert_when_click_target_because_parent() {
let config = support::build_config();
let config = build_config();
let widget = Caret::new(Arc::clone(&config));
let point = Point::new(10, 10);
let context = UpdateContext::ParentPosition(Point::new(10, 10));
@ -305,7 +312,7 @@ mod test_click_handler {
#[test]
fn assert_on_click_do_nothing() {
let config = support::build_config();
let config = build_config();
let mut widget = Caret::new(Arc::clone(&config));
let point = Point::new(12, 34);
let context = UpdateContext::ParentPosition(Point::new(678, 293));
@ -317,41 +324,43 @@ mod test_click_handler {
#[cfg(test)]
mod test_render {
use crate::tests::support;
use crate::tests::support::build_config;
use crate::tests::*;
use crate::ui::*;
use rider_derive::*;
use sdl2::rect::{Point, Rect};
#[test]
fn assert_render_line() {
let config = build_config();
let context = RenderContext::ParentPosition(Point::new(10, 14));
let mut canvas = support::CanvasMock::new();
build_test_renderer!(renderer);
let mut widget = Caret::new(config);
canvas.set_character_rect('I', Rect::new(11, 12, 6, 23));
widget.move_caret(CaretPosition::new(0, 0, 0), Point::new(23, 23));
widget.render(&mut canvas, &context);
widget.render(&mut canvas, &mut renderer, &context);
assert_eq!(
canvas.find_pixel_with_color(
Point::new(33, 37),
sdl2::pixels::Color::RGBA(121, 121, 121, 0)
),
Some(&support::RendererRect::new(
Some(&RendererRect::new(
Rect::new(33, 37, 33, 38),
sdl2::pixels::Color::RGBA(121, 121, 121, 0),
support::CanvasShape::Line
CanvasShape::Line
))
);
}
#[test]
fn assert_prepare_ui() {
let config = build_config();
let mut canvas = support::CanvasMock::new();
canvas.set_character_rect('I', Rect::new(11, 12, 6, 23));
build_test_renderer!(renderer);
renderer
.character_sizes
.insert('I', Rect::new(11, 12, 6, 23));
let mut widget = Caret::new(config);
widget.move_caret(CaretPosition::new(0, 0, 0), Point::new(11, 12));
widget.prepare_ui(&mut canvas);
assert_eq!(widget.dest(), Rect::new(11, 12, 6, 23));
widget.prepare_ui(&mut renderer);
assert_eq!(widget.dest(), &Rect::new(11, 12, 6, 23));
}
}

View File

@ -63,10 +63,6 @@ impl EditorFile {
self.line_height
}
pub fn render_position(&self) -> &Rect {
&self.dest
}
pub fn get_section_at_mut(&mut self, index: usize) -> Option<&mut EditorFileSection> {
self.sections.get_mut(index)
}
@ -97,9 +93,8 @@ impl TextCollection for EditorFile {
fn get_line(&self, line: &usize) -> Option<Vec<&TextCharacter>> {
let mut vec: Vec<&TextCharacter> = vec![];
for section in self.sections.iter() {
match section.get_line(line) {
Some(v) => vec.append(&mut v.clone()),
_ => (),
if let Some(v) = section.get_line(line) {
vec.append(&mut v.clone());
}
}
@ -146,32 +141,27 @@ impl TextCollection for EditorFile {
}
}
impl EditorFile {
pub fn render<R, C>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
R: Renderer + ConfigHolder,
C: CanvasAccess,
{
for section in self.sections.iter() {
section.render(canvas, renderer, context);
}
impl Widget for EditorFile {
fn texture_path(&self) -> Option<String> {
None
}
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: ConfigHolder + CharacterSizeManager + Renderer,
{
for section in self.sections.iter_mut() {
section.prepare_ui(renderer);
}
let r = renderer.load_character_size('W');
self.line_height = r.height();
self.refresh_characters_position();
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn source(&self) -> &Rect {
self.dest()
}
fn set_source(&mut self, rect: &Rect) {
self.set_dest(rect);
}
}
impl Update for EditorFile {
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR {
let mut result = UR::NoOp;
for section in self.sections.iter_mut() {
@ -179,9 +169,7 @@ impl Update for EditorFile {
}
result
}
}
impl ClickHandler for EditorFile {
fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UR {
let mut index = -1;
for (i, section) in self.sections.iter().enumerate() {
@ -207,6 +195,29 @@ impl ClickHandler for EditorFile {
}
false
}
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
for section in self.sections.iter() {
section.render(canvas, renderer, context);
}
}
fn prepare_ui<'l, T>(&mut self, renderer: &mut T)
where
T: Renderer + CharacterSizeManager + ConfigHolder,
{
for section in self.sections.iter_mut() {
section.prepare_ui(renderer);
}
let r = renderer.load_character_size('W');
self.line_height = r.height();
self.refresh_characters_position();
}
}
impl TextWidget for EditorFile {
@ -230,16 +241,6 @@ impl TextWidget for EditorFile {
}
}
impl RenderBox for EditorFile {
fn render_start_point(&self) -> Point {
self.dest.top_left()
}
fn dest(&self) -> Rect {
self.dest.clone()
}
}
pub struct EditorFileIterator<'a> {
current_section: usize,
current_token: usize,
@ -298,19 +299,34 @@ impl<'a> Iterator for EditorFileIterator<'a> {
#[cfg(test)]
mod tests {
use crate::tests::support;
use crate::tests::support::SimpleRendererMock;
use crate::tests::*;
use crate::app::UpdateResult;
use crate::ui::*;
use rider_derive::*;
use sdl2::rect::{Point, Rect};
#[test]
fn check_get_line() {
build_test_renderer!(renderer);
let mut file =
EditorFile::new("./foo.txt".to_owned(), "a b c d".to_owned(), config.clone());
file.prepare_ui(&mut renderer);
let result = file.get_line(&0);
assert_eq!(result.is_some(), true);
let result = file.get_line(&1);
assert_eq!(result.is_some(), false);
}
//##################################################
// iterator
//##################################################
#[test]
fn assert_simple_iterations() {
let config = support::build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut file =
EditorFile::new("./foo.txt".to_owned(), "a b c d".to_owned(), config.clone());
file.prepare_ui(&mut renderer);
@ -334,7 +350,7 @@ mod tests {
#[test]
fn assert_path_txt() {
let config = support::build_config();
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config);
@ -343,7 +359,7 @@ mod tests {
#[test]
fn assert_path_rs() {
let config = support::build_config();
let config = build_config();
let buffer = "".to_owned();
let path = "/example.rs".to_owned();
let widget = EditorFile::new(path, buffer, config);
@ -356,7 +372,7 @@ mod tests {
#[test]
fn assert_empty_buffer() {
let config = support::build_config();
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config);
@ -365,7 +381,7 @@ mod tests {
#[test]
fn assert_some_buffer() {
let config = support::build_config();
let config = build_config();
let buffer = "fn main(){}".to_owned();
let path = "some.rs".to_owned();
let widget = EditorFile::new(path, buffer, config);
@ -378,7 +394,7 @@ mod tests {
#[test]
fn assert_initial_line_height() {
let config = support::build_config();
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config);
@ -391,7 +407,7 @@ mod tests {
#[test]
fn assert_dest() {
let config = support::build_config();
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config);
@ -402,7 +418,7 @@ mod tests {
#[test]
fn assert_render_start_point() {
let config = support::build_config();
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config);
@ -410,4 +426,129 @@ mod tests {
let expected = Point::new(0, 0);
assert_eq!(result, expected);
}
#[test]
fn check_get_section_at_mut() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFile::new(path, buffer, config);
let result = widget.get_section_at_mut(12);
assert!(result.is_none());
}
//#######################################################
// widget
//#######################################################
#[test]
fn check_texture_path() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config);
let result = widget.texture_path();
assert!(result.is_none());
}
#[test]
fn check_set_dest() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFile::new(path, buffer, config);
let rect = Rect::new(2, 4, 6, 8);
widget.set_dest(&rect);
assert_eq!(format!("{:?}", widget.dest()), format!("{:?}", rect));
}
#[test]
fn check_source() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFile::new(path, buffer, config);
let rect = Rect::new(2, 4, 6, 8);
widget.set_source(&rect);
assert_eq!(format!("{:?}", widget.source()), format!("{:?}", rect));
assert_eq!(
format!("{:?}", widget.source()),
format!("{:?}", widget.dest())
);
}
#[test]
fn check_update() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFile::new(path, buffer, config);
let result = widget.update(0, &UpdateContext::Nothing);
assert_eq!(result, UpdateResult::NoOp);
}
#[test]
fn check_on_left_click() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFile::new(path, buffer, config);
let result = widget.on_left_click(&Point::new(0, 0), &UpdateContext::Nothing);
assert_eq!(result, UpdateResult::NoOp);
}
#[test]
fn check_on_left_click_with_sections() {
build_test_renderer!(renderer);
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFile::new(path, buffer, config.clone());
widget.sections = vec![EditorFileSection::new(
"a b c d".to_string(),
"".to_string(),
config,
)];
widget.prepare_ui(&mut renderer);
let result = widget.on_left_click(&Point::new(0, 0), &UpdateContext::Nothing);
assert_eq!(
result,
UpdateResult::MoveCaret(Rect::new(0, 0, 13, 14), CaretPosition::new(0, 0, 0))
);
}
#[test]
fn check_is_left_click_target() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config);
let result = widget.is_left_click_target(&Point::new(0, 0), &UpdateContext::Nothing);
assert_eq!(result, false);
}
#[test]
fn check_is_left_click_target_with_sections() {
build_test_renderer!(renderer);
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFile::new(path, buffer, config.clone());
widget.sections = vec![EditorFileSection::new(
"a b c d".to_string(),
"".to_string(),
config,
)];
widget.prepare_ui(&mut renderer);
let result = widget.is_left_click_target(&Point::new(0, 0), &UpdateContext::Nothing);
assert_eq!(result, true);
}
#[test]
fn check_render() {
build_test_renderer!(renderer);
let buffer = "a b c".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFile::new(path, buffer, config.clone());
widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing);
assert!(true);
}
}

View File

@ -16,6 +16,7 @@ pub struct EditorFileSection {
tokens: Vec<EditorFileToken>,
language: Language,
config: Arc<RwLock<Config>>,
dest: Rect,
}
impl EditorFileSection {
@ -48,6 +49,7 @@ impl EditorFileSection {
tokens,
language,
config,
dest: Rect::new(0, 0, 0, 0),
}
}
@ -61,25 +63,6 @@ impl EditorFileSection {
}
}
pub fn render<R, C>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
R: Renderer + ConfigHolder,
C: CanvasAccess,
{
for token in self.tokens.iter() {
token.render(canvas, renderer, context);
}
}
pub fn prepare_ui<'l, T>(&mut self, renderer: &mut T)
where
T: ConfigHolder + CharacterSizeManager + Renderer,
{
for token in self.tokens.iter_mut() {
token.prepare_ui(renderer);
}
}
#[inline]
pub fn iter_char(&self) -> EditorFileSectionIterator {
EditorFileSectionIterator::new(self)
@ -90,6 +73,83 @@ impl EditorFileSection {
}
}
impl Widget for EditorFileSection {
fn texture_path(&self) -> Option<String> {
None
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn source(&self) -> &Rect {
&self.dest
}
fn set_source(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR {
let mut result = UR::NoOp;
for token in self.tokens.iter_mut() {
result = token.update(ticks, context)
}
result
}
fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UR {
for token in self.tokens.iter_mut() {
if token.is_left_click_target(point, context) {
return token.on_left_click(point, context);
}
}
UR::NoOp
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
let mut i = 0;
loop {
if i == self.tokens.len() {
break;
}
match self.tokens.get(i) {
Some(token) => {
if token.is_left_click_target(point, context) {
return true;
}
}
None => break,
}
i += 1;
}
false
}
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
for token in self.tokens.iter() {
token.render(canvas, renderer, context);
}
}
fn prepare_ui<'l, T>(&mut self, renderer: &mut T)
where
T: Renderer + CharacterSizeManager + ConfigHolder,
{
for token in self.tokens.iter_mut() {
token.prepare_ui(renderer);
}
}
}
impl TextWidget for EditorFileSection {
fn full_rect(&self) -> Rect {
let mut current_line_width = 0;
@ -157,46 +217,6 @@ impl TextCollection for EditorFileSection {
}
}
impl Update for EditorFileSection {
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR {
let mut result = UR::NoOp;
for token in self.tokens.iter_mut() {
result = token.update(ticks, context)
}
result
}
}
impl ClickHandler for EditorFileSection {
fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UR {
for token in self.tokens.iter_mut() {
if token.is_left_click_target(point, context) {
return token.on_left_click(point, context);
}
}
UR::NoOp
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
let mut i = 0;
loop {
if i == self.tokens.len() {
break;
}
match self.tokens.get(i) {
Some(token) => {
if token.is_left_click_target(point, context) {
return true;
}
}
None => break,
}
i += 1;
}
false
}
}
pub struct EditorFileSectionIterator<'a> {
section: &'a EditorFileSection,
current_token: usize,
@ -243,7 +263,8 @@ impl<'a> std::iter::Iterator for EditorFileSectionIterator<'a> {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::support::{build_config, SimpleRendererMock};
use crate::tests::*;
use rider_derive::*;
impl EditorFileSection {
pub fn tokens_count(&self) -> usize {
@ -269,8 +290,7 @@ mod tests {
#[test]
fn assert_simple_char_iteration() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut section = EditorFileSection::new("a b c d".to_owned(), ".txt".to_owned(), config);
section.prepare_ui(&mut renderer);
for (index, c) in section.iter_char().enumerate() {
@ -289,8 +309,7 @@ mod tests {
#[test]
fn assert_complex_char_iteration() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut section = EditorFileSection::new("let a = 1".to_owned(), ".rs".to_owned(), config);
section.prepare_ui(&mut renderer);
assert_eq!(section.tokens.len(), 7);
@ -309,4 +328,40 @@ mod tests {
}
}
}
#[test]
fn check_texture_path() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let widget = EditorFileSection::new(path, buffer, config);
let result = widget.texture_path();
assert!(result.is_none());
}
#[test]
fn check_set_dest() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFileSection::new(path, buffer, config);
let rect = Rect::new(2, 4, 6, 8);
widget.set_dest(&rect);
assert_eq!(format!("{:?}", widget.dest()), format!("{:?}", rect));
}
#[test]
fn check_source() {
let config = build_config();
let buffer = "".to_owned();
let path = "/example.txt".to_owned();
let mut widget = EditorFileSection::new(path, buffer, config);
let rect = Rect::new(2, 4, 6, 8);
widget.set_source(&rect);
assert_eq!(format!("{:?}", widget.source()), format!("{:?}", rect));
assert_eq!(
format!("{:?}", widget.source()),
format!("{:?}", widget.dest())
);
}
}

View File

@ -14,6 +14,7 @@ pub struct EditorFileToken {
characters: Vec<TextCharacter>,
token_type: TokenType,
config: Arc<RwLock<Config>>,
dest: Rect,
}
impl EditorFileToken {
@ -23,6 +24,7 @@ impl EditorFileToken {
characters: vec![],
token_type: token_type.clone(),
config,
dest: Rect::new(0, 0, 0, 0),
}
}
@ -132,22 +134,68 @@ impl TextCollection for EditorFileToken {
}
}
impl EditorFileToken {
impl Widget for EditorFileToken {
fn texture_path(&self) -> Option<String> {
None
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn source(&self) -> &Rect {
&self.dest
}
fn set_source(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR {
for text_character in self.characters.iter_mut() {
text_character.update(ticks, context);
}
self.set_dest(&self.full_rect());
UR::NoOp
}
fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UR {
for text_character in self.characters.iter_mut() {
if text_character.is_left_click_target(point, context) {
return text_character.on_left_click(point, context);
}
}
UR::NoOp
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
for text_character in self.characters.iter() {
if text_character.is_left_click_target(point, context) {
return true;
}
}
false
}
/**
* Must first create targets so even if new line appear renderer will know
* where move render starting point
*/
pub fn render<R, C>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
R: Renderer + ConfigHolder,
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
for text_character in self.characters.iter() {
text_character.render(canvas, renderer, context);
}
}
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: ConfigHolder + CharacterSizeManager + Renderer,
{
@ -172,35 +220,6 @@ impl EditorFileToken {
}
}
impl Update for EditorFileToken {
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR {
for text_character in self.characters.iter_mut() {
text_character.update(ticks, context);
}
UR::NoOp
}
}
impl ClickHandler for EditorFileToken {
fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UR {
for text_character in self.characters.iter_mut() {
if text_character.is_left_click_target(point, context) {
return text_character.on_left_click(point, context);
}
}
UR::NoOp
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
for text_character in self.characters.iter() {
if text_character.is_left_click_target(point, context) {
return true;
}
}
false
}
}
#[derive(Clone)]
pub struct EditorFileTokenIterator<'a> {
editor_file_token: &'a EditorFileToken,
@ -230,8 +249,8 @@ impl<'a> std::iter::Iterator for EditorFileTokenIterator<'a> {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::support::build_config;
use crate::tests::support::CanvasMock;
use crate::tests::*;
use rider_lexers::Token;
use sdl2::pixels::PixelFormatEnum;
use sdl2::render::Texture;

View File

@ -1,4 +1,3 @@
use crate::app::UpdateResult as UR;
use crate::app::*;
use crate::renderer::renderer::Renderer;
use crate::ui::caret::caret::Caret;
@ -11,20 +10,19 @@ use crate::ui::scroll_bar::horizontal_scroll_bar::*;
use crate::ui::scroll_bar::vertical_scroll_bar::*;
use crate::ui::scroll_bar::ScrollWidget;
use crate::ui::text_character::CharacterSizeManager;
use crate::ui::CanvasAccess;
use crate::ui::ClickHandler;
use crate::ui::RenderBox;
use crate::ui::RenderContext;
use crate::ui::Update;
use crate::ui::UpdateContext;
use crate::ui::{move_render_point, ScrollView};
use crate::ui::{CanvasAccess, Widget};
use sdl2::rect::Point;
use sdl2::rect::Rect;
use std::mem;
use std::sync::*;
pub trait FileAccess {
fn has_file(&self) -> bool;
fn has_file(&self) -> bool {
self.file().is_some()
}
fn file(&self) -> Option<&EditorFile>;
@ -34,17 +32,55 @@ pub trait FileAccess {
fn drop_file(&mut self) -> Option<EditorFile>;
fn replace_current_file(&mut self, file: EditorFile);
fn replace_current_file(&mut self, file: EditorFile) {
self.open_file(file);
}
}
pub trait CaretAccess {
pub trait CaretAccess: FileAccess {
fn caret(&self) -> &Caret;
fn caret_mut(&mut self) -> &mut Caret;
fn move_caret(&mut self, dir: MoveDirection);
fn move_caret(&mut self, dir: MoveDirection) {
match dir {
MoveDirection::Left => caret_manager::move_caret_left(self),
MoveDirection::Right => caret_manager::move_caret_right(self),
MoveDirection::Up => caret_manager::move_caret_up(self),
MoveDirection::Down => caret_manager::move_caret_down(self),
};
}
fn set_caret_to_end_of_line(&mut self, line: i32);
fn set_caret_to_end_of_line(&mut self, line: i32) {
let file = match self.file_mut() {
Some(f) => f,
_ => return,
};
let mut line = line;
while line >= 0 {
match file.get_last_at_line(line.clone() as usize) {
Some(text_character) => {
let rect = text_character.dest();
let position =
CaretPosition::new(text_character.position() + 1, line as usize, 0);
let p = if text_character.is_new_line() {
file.get_character_at(text_character.position() + 1)
.map_or_else(
|| text_character.dest().top_left(),
|tc| tc.dest().top_left(),
)
} else {
rect.top_right()
};
self.caret_mut().move_caret(position, p);
break;
}
_ => {
line -= 1;
}
}
}
}
}
pub struct FileEditor {
@ -57,19 +93,152 @@ pub struct FileEditor {
horizontal_scroll_bar: HorizontalScrollBar,
}
impl FileEditor {
pub fn new(config: ConfigAccess) -> 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,
impl Widget for FileEditor {
fn texture_path(&self) -> Option<String> {
None
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn source(&self) -> &Rect {
&self.dest
}
fn set_source(&mut self, _rect: &Rect) {}
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UpdateResult {
let (width, height, editor_left_margin, editor_top_margin, scroll_width, scroll_margin) = {
let config: RwLockReadGuard<Config> = self.config.read().unwrap();
(
config.width(),
config.height(),
config.editor_left_margin() as u32,
config.editor_top_margin() as u32,
config.scroll().width(),
config.scroll().margin_right(),
)
};
let editor_left_margin = match context {
UpdateContext::ParentPosition(p) => p.x() as u32,
_ => editor_left_margin as u32,
};
self.dest.set_x(editor_left_margin.clone() as i32);
self.dest.set_width(width - editor_left_margin);
self.dest.set_height(height - editor_top_margin);
self.vertical_scroll_bar
.set_full_size(self.full_rect.height());
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_rect.width());
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);
self.caret.update(ticks, context);
match self.file_mut() {
Some(file) => file.update(ticks, context),
_ => UpdateResult::NoOp,
}
}
fn on_left_click(&mut self, point: &Point, _context: &UpdateContext) -> UpdateResult {
let it = match self.file() {
Some(f) => f.iter_char(),
_ => return UpdateResult::NoOp,
};
let scroll = self.scroll();
let render_point = self.render_start_point();
let moved_by = self.scroll().offset(render_point.x(), render_point.y());
let scroll_context = UpdateContext::ScrolledBy(moved_by.clone());
let mut target: Option<(Point, CaretPosition)> = None;
for char in it {
if char.is_left_click_target(point, &scroll_context) {
let position = CaretPosition::new(char.position(), char.line(), 0);
let point = char.dest().top_left();
target = Some((point, position));
break;
}
}
if let Some((point, position)) = target {
self.caret.move_caret(position, point);
} else {
self.set_caret_to_end_of_line(
self.resolve_line_from_point(&point.offset(-scroll.x(), -scroll.y())),
);
}
UpdateResult::NoOp
}
fn is_left_click_target(&self, point: &Point, _context: &UpdateContext) -> bool {
self.is_text_character_clicked(point) || self.is_editor_clicked(point)
}
fn use_clipping(&self) -> bool {
true
}
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, _context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
if self.use_clipping() {
canvas.set_clipping(self.dest.clone());
}
match self.file() {
Some(file) => file.render(
canvas,
renderer,
&RenderContext::ParentPosition(self.render_start_point() + self.scroll()),
),
_ => (),
};
self.caret.render(
canvas,
renderer,
&RenderContext::ParentPosition(self.render_start_point() + self.scroll()),
);
self.vertical_scroll_bar
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
self.horizontal_scroll_bar
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
}
fn prepare_ui<T>(&mut self, renderer: &mut T)
where
T: Renderer + CharacterSizeManager + ConfigHolder,
{
if let Some(ref mut file) = self.file {
file.prepare_ui(renderer);
}
self.caret.prepare_ui(renderer);
}
}
impl FileEditor {
pub fn new(config: ConfigAccess) -> Self {
Self {
dest,
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,
)
},
full_rect: Rect::new(0, 0, 0, 0),
caret: Caret::new(Arc::clone(&config)),
vertical_scroll_bar: VerticalScrollBar::new(Arc::clone(&config)),
@ -115,14 +284,25 @@ impl FileEditor {
}
fn is_text_character_clicked(&self, point: &Point) -> bool {
let context = UpdateContext::ParentPosition(self.render_start_point());
self.file()
.map_or(false, |file| file.is_left_click_target(point, &context))
let file = match self.file() {
Some(f) => f,
_ => return false,
};
let moved_by = self
.scroll()
.offset(self.render_start_point().x(), self.render_start_point().y());
let scroll_context = UpdateContext::ScrolledBy(moved_by.clone());
for char in file.iter_char() {
if char.is_left_click_target(point, &scroll_context) {
return true;
}
}
false
}
fn is_editor_clicked(&self, point: &Point) -> bool {
self.dest
.contains_point(move_render_point(point.clone(), &self.dest).top_left())
self.dest()
.contains_point(move_render_point(point.clone(), self.dest()).top_left())
}
fn resolve_line_from_point(&self, point: &Point) -> i32 {
@ -130,11 +310,12 @@ impl FileEditor {
Some(f) => f,
_ => return 0,
};
let mut y = point.y() - self.render_start_point().y();
if y < 0 {
y = 0;
let y = point.y() - self.render_start_point().y();
match (y, file.line_height()) {
(y, _) if y <= 0 => 0,
(_, 0) => 0,
(_, line_height) => y / (line_height as i32),
}
y / (file.line_height() as i32)
}
}
@ -157,10 +338,6 @@ impl ScrollView<VerticalScrollBar, HorizontalScrollBar> for FileEditor {
}
impl FileAccess for FileEditor {
fn has_file(&self) -> bool {
self.file.is_some()
}
fn file(&self) -> Option<&EditorFile> {
self.file.as_ref()
}
@ -172,8 +349,8 @@ impl FileAccess for FileEditor {
fn open_file(&mut self, file: EditorFile) -> Option<EditorFile> {
let new_path = file.path();
let mut file = Some(file);
let old_path = match self.file {
Some(ref f) => f.path(),
let old_path = match self.file() {
Some(f) => f.path(),
_ => format!(""),
};
mem::swap(&mut self.file, &mut file);
@ -196,10 +373,6 @@ impl FileAccess for FileEditor {
None
}
}
fn replace_current_file(&mut self, file: EditorFile) {
self.open_file(file);
}
}
impl CaretAccess for FileEditor {
@ -210,160 +383,6 @@ impl CaretAccess for FileEditor {
fn caret_mut(&mut self) -> &mut Caret {
&mut self.caret
}
fn move_caret(&mut self, dir: MoveDirection) {
match dir {
MoveDirection::Left => caret_manager::move_caret_left(self),
MoveDirection::Right => caret_manager::move_caret_right(self),
MoveDirection::Up => caret_manager::move_caret_up(self),
MoveDirection::Down => caret_manager::move_caret_down(self),
};
}
fn set_caret_to_end_of_line(&mut self, line: i32) {
let file = match self.file_mut() {
Some(f) => f,
_ => return,
};
let mut line = line;
while line >= 0 {
match file.get_last_at_line(line.clone() as usize) {
Some(text_character) => {
let rect = text_character.dest();
let position =
CaretPosition::new(text_character.position() + 1, line as usize, 0);
let p = if text_character.is_new_line() {
file.get_character_at(text_character.position() + 1)
.map_or_else(
|| text_character.dest().top_left(),
|tc| tc.dest().top_left(),
)
} else {
rect.top_right()
};
self.caret.move_caret(position, p);
break;
}
_ => {
line -= 1;
}
}
}
}
}
impl FileEditor {
pub fn render<R, C>(&self, canvas: &mut C, renderer: &mut R)
where
R: Renderer + ConfigHolder,
C: CanvasAccess,
{
canvas.set_clipping(self.dest.clone());
match self.file() {
Some(file) => file.render(
canvas,
renderer,
&RenderContext::ParentPosition(self.render_start_point()),
),
_ => (),
};
self.caret.render(
canvas,
&RenderContext::ParentPosition(self.render_start_point()),
);
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)
where
T: CharacterSizeManager,
{
self.caret.prepare_ui(renderer);
}
}
impl Update for FileEditor {
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UR {
let (width, height, editor_left_margin, editor_top_margin, scroll_width, scroll_margin) = {
let config: RwLockReadGuard<Config> = self.config.read().unwrap();
(
config.width(),
config.height(),
config.editor_left_margin() as u32,
config.editor_top_margin() as u32,
config.scroll().width(),
config.scroll().margin_right(),
)
};
let editor_left_margin = match context {
UpdateContext::ParentPosition(p) => p.x() as u32,
_ => editor_left_margin as u32,
};
self.dest.set_x(editor_left_margin.clone() as i32);
self.dest.set_width(width - editor_left_margin);
self.dest.set_height(height - editor_top_margin);
self.vertical_scroll_bar
.set_full_size(self.full_rect.height());
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_rect.width());
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);
self.caret.update();
match self.file_mut() {
Some(file) => file.update(ticks, context),
_ => UR::NoOp,
}
}
}
impl ClickHandler for FileEditor {
fn on_left_click(&mut self, point: &Point, _context: &UpdateContext) -> UR {
let context = UpdateContext::ParentPosition(self.render_start_point());
if self.is_text_character_clicked(point) {
let file = if let Some(file) = self.file_mut() {
file
} else {
return UR::NoOp;
};
match file.on_left_click(point, &context) {
UR::MoveCaret(rect, position) => {
self.caret
.move_caret(position, Point::new(rect.x(), rect.y()));
}
_ => {}
}
} else {
self.set_caret_to_end_of_line(self.resolve_line_from_point(point));
}
UR::NoOp
}
fn is_left_click_target(&self, point: &Point, _context: &UpdateContext) -> bool {
self.is_text_character_clicked(point) || self.is_editor_clicked(point)
}
}
impl RenderBox for FileEditor {
fn render_start_point(&self) -> Point {
self.dest.top_left() + self.scroll()
}
fn dest(&self) -> Rect {
self.dest.clone()
}
}
impl ConfigHolder for FileEditor {
@ -374,57 +393,15 @@ impl ConfigHolder for FileEditor {
#[cfg(test)]
mod tests {
use crate::app::UpdateResult;
use crate::tests::*;
use crate::ui::*;
use rider_config::Config;
use std::sync::*;
#[test]
fn replace_file() {
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 =
EditorFile::new("./bar.txt".to_string(), "bar".to_string(), config.clone());
editor.open_file(first_file.clone());
let result = editor.open_file(second_file.clone());
assert_eq!(result.is_some(), true);
let file = result.as_ref().unwrap();
assert_eq!(file.path(), first_file.path());
assert_eq!(file.buffer(), first_file.buffer());
}
}
#[cfg(test)]
mod test_config_holder {
use crate::app::*;
use crate::tests::support;
use crate::ui::*;
use std::sync::*;
#[test]
fn assert_config() {
let config = support::build_config();
let widget = FileEditor::new(Arc::clone(&config));
let result = widget.config();
{
let mut w = config.write().unwrap();
w.set_height(1240);
w.set_width(1024);
}
let local = config.read().unwrap();
let widget_config = result.read().unwrap();
assert_eq!(widget_config.width(), local.width());
assert_eq!(widget_config.height(), local.height());
}
}
#[cfg(test)]
mod test_render_box {
use crate::tests::support;
use crate::ui::*;
use rider_config::{Config, ConfigHolder};
use rider_derive::*;
use sdl2::rect::{Point, Rect};
use std::sync::*;
#[cfg_attr(tarpaulin, skip)]
impl FileEditor {
pub fn set_full_rect(&mut self, r: Rect) {
self.full_rect = r;
@ -435,9 +412,19 @@ mod test_render_box {
}
}
// Widget
#[test]
fn assert_texture_path() {
let config = build_config();
let widget = FileEditor::new(config);
let result = widget.texture_path();
assert!(result.is_none());
}
#[test]
fn assert_dest() {
let config = support::build_config();
let config = build_config();
let (x, y, mw, mh) = {
let c = config.read().unwrap();
(
@ -454,23 +441,300 @@ mod test_render_box {
}
#[test]
fn assert_render_start_point() {
let config = support::build_config();
let (x, y, ss) = {
let c = config.read().unwrap();
(
c.editor_left_margin(),
c.editor_top_margin(),
c.scroll().speed(),
)
};
fn assert_set_dest() {
let config = build_config();
let mut widget = FileEditor::new(config);
widget.set_dest(Rect::new(x.clone(), y.clone(), 999, 999));
widget.set_full_rect(Rect::new(0, 0, 99999, 99999));
widget.update(1, &UpdateContext::Nothing);
widget.scroll_by(30, 40);
let result = widget.render_start_point().clone();
let expected = Point::new(x - (ss * 30), y - (ss * 40));
assert_eq!(result, expected);
widget.set_dest(Rect::new(100, 200, 300, 400));
assert_eq!(widget.dest(), &Rect::new(100, 200, 300, 400));
}
#[test]
fn assert_source() {
let config = build_config();
let widget = FileEditor::new(config);
let result = widget.source();
assert_eq!(result, &Rect::new(10, 50, 1014, 810));
}
#[test]
fn assert_set_source() {
let config = build_config();
let mut widget = FileEditor::new(config);
widget.set_source(&Rect::new(1, 2, 3, 4));
assert_ne!(widget.source(), &Rect::new(1, 2, 3, 4));
}
#[test]
fn assert_update() {
let config = build_config();
let mut widget = FileEditor::new(config);
let result = widget.update(0, &UpdateContext::Nothing);
assert_eq!(result, UpdateResult::NoOp);
}
#[test]
fn assert_on_left_click() {
let config = build_config();
let mut widget = FileEditor::new(config);
let result = widget.on_left_click(&Point::new(600, 800), &UpdateContext::Nothing);
assert_eq!(result, UpdateResult::NoOp);
}
#[test]
fn assert_is_left_click_target() {
let config = build_config();
let widget = FileEditor::new(config);
let result = widget.is_left_click_target(&Point::new(600, 800), &UpdateContext::Nothing);
assert_eq!(result, true);
}
#[test]
fn assert_use_clipping() {
let config = build_config();
let widget = FileEditor::new(config);
let result = widget.use_clipping();
assert_eq!(result, true);
}
#[test]
fn assert_render() {
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let widget = FileEditor::new(config);
let result = widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing);
assert_eq!(result, ());
}
#[test]
fn assert_prepare_ui() {
build_test_renderer!(renderer);
let mut widget = FileEditor::new(config);
let result = widget.prepare_ui(&mut renderer);
assert_eq!(result, ());
}
// File manipulation
#[test]
fn assert_has_file() {
let config = build_config();
let widget = FileEditor::new(config);
assert_eq!(widget.has_file(), false);
}
#[test]
fn drop_file_when_no_file_is_selected() {
let config = build_config();
let mut widget = FileEditor::new(config);
let result = widget.drop_file();
assert!(result.is_none());
}
#[test]
fn drop_file_when_file_is_selected() {
let config = build_config();
let mut widget = FileEditor::new(config.clone());
let file = EditorFile::new(
"/tmp/drop_file_when_file_is_selected".to_owned(),
"foo bar".to_owned(),
config,
);
widget.open_file(file);
let result = widget.drop_file();
assert!(result.is_some())
}
#[test]
fn mut_file_when_no_file_is_selected() {
let config = build_config();
let mut widget = FileEditor::new(config);
let result = widget.file_mut();
assert!(result.is_none());
}
#[test]
fn mut_file_when_file_is_selected() {
let config = build_config();
let mut widget = FileEditor::new(config.clone());
let file = EditorFile::new(
"/tmp/drop_file_when_file_is_selected".to_owned(),
"foo bar".to_owned(),
config,
);
widget.open_file(file);
let result = widget.file_mut();
assert!(result.is_some())
}
#[test]
fn file_when_no_file_is_selected() {
let config = build_config();
let widget = FileEditor::new(config);
let result = widget.file();
assert!(result.is_none());
}
#[test]
fn file_when_file_is_selected() {
let config = build_config();
let mut widget = FileEditor::new(config.clone());
let file = EditorFile::new(
"/tmp/drop_file_when_file_is_selected".to_owned(),
"foo bar".to_owned(),
config,
);
widget.open_file(file);
let result = widget.file();
assert!(result.is_some())
}
#[test]
fn replace_file() {
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 =
EditorFile::new("./bar.txt".to_string(), "bar".to_string(), config.clone());
editor.open_file(first_file.clone());
let result = editor.open_file(second_file.clone());
assert_eq!(result.is_some(), true);
let file = result.as_ref().unwrap();
assert_eq!(file.path(), first_file.path());
assert_eq!(file.buffer(), first_file.buffer());
}
// config
#[test]
fn assert_config() {
let config = build_config();
let widget = FileEditor::new(config.clone());
assert!(widget
.config()
.write()
.and_then(|ref mut w| {
w.set_height(1240);
w.set_width(1024);
Ok(())
})
.is_ok());
let local: (u32, u32) = config
.read()
.map_or_else(|_| (0, 0), |ref w| (w.width(), w.height()));
let widget_config: (u32, u32) = widget
.config()
.read()
.map_or_else(|_| (0, 0), |ref w| (w.width(), w.height()));
assert_eq!(widget_config, local);
}
}
#[cfg(test)]
mod own_methods_tests {
use crate::tests::*;
use crate::ui::{CaretAccess, EditorFile, FileAccess, FileEditor};
use rider_config::ConfigAccess;
use rider_derive::*;
use sdl2::rect::Point;
fn build_testable_file<S>(buffer: S, config: ConfigAccess) -> EditorFile
where
S: Into<String> + Sized,
{
EditorFile::new(
"/tmp/drop_file_when_file_is_selected".to_owned(),
buffer.into(),
config,
)
}
#[test]
fn delete_front() {
build_test_renderer!(renderer);
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar", config));
widget.caret_mut().set_text_position(1);
widget.delete_front(&mut renderer);
let buffer = widget
.file()
.map_or_else(|| "".to_owned(), |ref f| f.buffer());
assert_eq!(buffer, "oo bar".to_owned());
}
#[test]
fn delete_back() {
build_test_renderer!(renderer);
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar", config));
widget.caret_mut().set_text_position(6);
widget.delete_back(&mut renderer);
let buffer = widget
.file()
.map_or_else(|| "".to_owned(), |ref f| f.buffer());
assert_eq!(buffer, "foo ba".to_owned());
}
#[test]
fn insert_text() {
build_test_renderer!(renderer);
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar", config));
widget.insert_text("hello world ".to_owned(), &mut renderer);
let buffer = widget
.file()
.map_or_else(|| "".to_owned(), |ref f| f.buffer());
assert_eq!(buffer, "hello world foo bar".to_owned());
}
#[test]
fn insert_new_line() {
build_test_renderer!(renderer);
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar", config));
assert!(widget.insert_new_line(&mut renderer).is_ok());
let buffer = widget
.file()
.map_or_else(|| "".to_owned(), |ref f| f.buffer());
assert_eq!(buffer, "\nfoo bar".to_owned());
}
#[test]
fn delete_current_line() {
build_test_renderer!(renderer);
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar\nfoz baz", config));
assert!(widget.delete_current_line(&mut renderer).is_ok());
let buffer = widget
.file()
.map_or_else(|| "".to_owned(), |ref f| f.buffer());
assert_eq!(buffer, "foz baz");
}
#[test]
fn is_text_character_clicked() {
let config = build_config();
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar", config));
assert_eq!(
widget.is_text_character_clicked(&Point::new(1000, 1000)),
false
);
}
#[test]
fn is_editor_clicked() {
let config = build_config();
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar", config));
assert_eq!(widget.is_editor_clicked(&Point::new(1000, 1000)), false);
}
#[test]
fn resolve_line_from_point() {
let config = build_config();
let mut widget = FileEditor::new(config.clone());
widget.open_file(build_testable_file("foo bar", config));
assert_eq!(widget.resolve_line_from_point(&Point::new(100, 100)), 0);
}
}

View File

@ -155,7 +155,7 @@ impl Widget for DirectoryView {
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
C: CanvasAccess,
{
let mut dest = move_render_point(
@ -185,7 +185,7 @@ impl Widget for DirectoryView {
fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let size = renderer.load_character_size('W');
self.icon.prepare_ui(renderer);
@ -271,7 +271,7 @@ impl DirectoryView {
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R) -> bool
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
match dir_path {
_ if dir_path == self.path => {
@ -343,7 +343,7 @@ impl DirectoryView {
fn read_directory<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let entries: fs::ReadDir = match fs::read_dir(self.path.clone()) {
Ok(d) => d,
@ -387,7 +387,7 @@ impl DirectoryView {
fn render_children<C, R>(&self, canvas: &mut C, renderer: &mut R, dest: &mut Rect)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
if !self.expanded {
return;
@ -447,10 +447,9 @@ impl ConfigHolder for DirectoryView {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::support::CanvasMock;
use crate::tests::support::SimpleRendererMock;
use crate::tests::support::{build_config, build_path};
use crate::tests::*;
use crate::ui::Widget;
use rider_derive::*;
//##########################################################
// name_width
@ -465,8 +464,7 @@ mod tests {
#[test]
fn assert_prepared_name_width() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.name_width(), 39);
@ -485,8 +483,7 @@ mod tests {
#[test]
fn assert_prepared_icon_width() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.icon_width(), 14);
@ -505,8 +502,7 @@ mod tests {
#[test]
fn assert_prepared_height() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.height(), 14);
@ -525,8 +521,7 @@ mod tests {
#[test]
fn assert_prepared_name() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.name(), "foo".to_owned());
@ -545,8 +540,7 @@ mod tests {
#[test]
fn assert_prepared_path() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.path(), "/foo".to_owned());
@ -565,8 +559,7 @@ mod tests {
#[test]
fn assert_prepared_source() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.source(), &Rect::new(0, 0, 64, 64));
@ -585,21 +578,37 @@ mod tests {
#[test]
fn assert_prepared_dest() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.dest(), &Rect::new(0, 0, 53, 14));
}
#[test]
fn assert_set_dest() {
let config = build_config();
let mut widget = DirectoryView::new("/foo".to_owned(), config);
let rect = Rect::new(1, 2, 3, 4);
widget.set_dest(&rect);
assert_ne!(widget.dest(), &rect);
}
#[test]
fn assert_set_source() {
let config = build_config();
let mut widget = DirectoryView::new("/foo".to_owned(), config);
let rect = Rect::new(1, 2, 3, 4);
widget.set_source(&rect);
assert_ne!(widget.source(), &rect);
}
//##########################################################
// update
//##########################################################
#[test]
fn assert_update_when_doesnt_exists() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(
@ -610,8 +619,7 @@ mod tests {
#[test]
fn assert_update_when_does_exists() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = DirectoryView::new("/tmp".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(
@ -624,8 +632,7 @@ mod tests {
fn assert_update_expanded() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -648,8 +655,7 @@ mod tests {
fn assert_render_no_expanded() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
@ -661,8 +667,7 @@ mod tests {
fn assert_render_expanded() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
@ -683,8 +688,7 @@ mod tests {
fn assert_is_left_click_target_when_target() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -696,8 +700,7 @@ mod tests {
#[test]
fn assert_is_left_click_target_when_target_with_parent() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -711,8 +714,7 @@ mod tests {
fn assert_is_left_click_target_expanded() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
@ -730,8 +732,7 @@ mod tests {
#[test]
fn refute_is_left_click_target_when_target() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -743,8 +744,7 @@ mod tests {
#[test]
fn refute_is_left_click_target_when_target_with_parent() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = DirectoryView::new("/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -762,8 +762,7 @@ mod tests {
fn assert_on_left_click_when_target() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
@ -781,8 +780,7 @@ mod tests {
fn assert_on_left_click_when_target_with_parent() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
@ -800,8 +798,7 @@ mod tests {
fn assert_on_left_click_expanded() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
@ -812,6 +809,7 @@ mod tests {
);
widget.prepare_ui(&mut renderer);
widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing);
widget.expanded = true;
let p = Point::new(0, 0);
let context = UpdateContext::ParentPosition(Point::new(0, 0));
assert_eq!(
@ -819,4 +817,64 @@ mod tests {
UpdateResult::OpenDirectory("/tmp/rider-editor/directory-view-test".to_owned())
);
}
#[test]
fn assert_on_left_click_not_expanded() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
widget.prepare_ui(&mut renderer);
widget.open_directory("/tmp".to_owned(), &mut renderer);
widget.open_directory("/tmp/rider-editor".to_owned(), &mut renderer);
widget.open_directory(
"/tmp/rider-editor/directory-view-test".to_owned(),
&mut renderer,
);
widget.prepare_ui(&mut renderer);
widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing);
widget.expanded = false;
let p = Point::new(0, 0);
let context = UpdateContext::ParentPosition(Point::new(0, 0));
assert_eq!(
widget.on_left_click(&p, &context),
UpdateResult::OpenDirectory("/tmp/rider-editor/directory-view-test".to_owned())
);
}
#[test]
fn assert_on_left_click_expanded_with_sub() {
build_path("/tmp/rider-editor/directory-view-test/foo/bar".to_owned());
build_test_renderer!(renderer);
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
widget.prepare_ui(&mut renderer);
widget.open_directory("/tmp".to_owned(), &mut renderer);
widget.open_directory("/tmp/rider-editor".to_owned(), &mut renderer);
widget.prepare_ui(&mut renderer);
widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing);
let p = Point::new(0, 0);
let context = UpdateContext::ParentPosition(Point::new(0, 0));
assert_eq!(
widget.on_left_click(&p, &context),
UpdateResult::OpenDirectory("/tmp/rider-editor/directory-view-test".to_owned())
);
}
#[test]
fn check_config() {
build_path("/tmp/rider-editor/directory-view-test".to_owned());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget =
DirectoryView::new("/tmp/rider-editor/directory-view-test".to_owned(), config);
widget.prepare_ui(&mut renderer);
widget.config();
assert!(true)
}
}

View File

@ -62,7 +62,7 @@ impl Widget for FileEntry {
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let dest = match context {
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), self.dest()),
@ -82,7 +82,7 @@ impl Widget for FileEntry {
fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let rect = renderer.load_character_size('W');
self.icon.prepare_ui(renderer);
@ -186,9 +186,9 @@ impl ConfigHolder for FileEntry {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::support::build_config;
use crate::tests::support::CanvasMock;
use crate::tests::support::SimpleRendererMock;
use crate::tests::*;
use rider_derive::*;
use crate::ui::{UpdateContext, Widget};
//##########################################################
@ -204,8 +204,7 @@ mod tests {
#[test]
fn assert_prepared_name_width() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.name_width(), 91);
@ -224,8 +223,7 @@ mod tests {
#[test]
fn assert_prepared_icon_width() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.icon_width(), 14);
@ -244,8 +242,7 @@ mod tests {
#[test]
fn assert_prepared_height() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.height(), 16);
@ -264,8 +261,7 @@ mod tests {
#[test]
fn assert_prepared_name() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.name(), "bar.txt".to_owned());
@ -284,8 +280,7 @@ mod tests {
#[test]
fn assert_prepared_path() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.path(), "/foo".to_owned());
@ -304,8 +299,7 @@ mod tests {
#[test]
fn assert_prepared_source() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.source(), &Rect::new(0, 0, 64, 64));
@ -324,8 +318,7 @@ mod tests {
#[test]
fn assert_prepared_dest() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.dest(), &Rect::new(0, 0, 105, 16));
@ -344,8 +337,7 @@ mod tests {
#[test]
fn assert_prepared_full_dest() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(widget.full_dest(), Rect::new(0, 0, 125, 16));
@ -357,8 +349,7 @@ mod tests {
#[test]
fn assert_update_when_doesnt_exists() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(
@ -369,8 +360,7 @@ mod tests {
#[test]
fn assert_update_when_does_exists() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = FileEntry::new("bar.txt".to_owned(), "/tmp".to_owned(), config);
widget.prepare_ui(&mut renderer);
assert_eq!(
@ -385,8 +375,7 @@ mod tests {
#[test]
fn assert_render() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -400,8 +389,7 @@ mod tests {
#[test]
fn assert_is_left_click_target_when_target() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -413,8 +401,7 @@ mod tests {
#[test]
fn assert_is_left_click_target_when_target_with_parent() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -426,8 +413,7 @@ mod tests {
#[test]
fn refute_is_left_click_target_when_target() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);
@ -439,8 +425,7 @@ mod tests {
#[test]
fn refute_is_left_click_target_when_target_with_parent() {
let config = build_config();
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
widget.prepare_ui(&mut renderer);

View File

@ -70,13 +70,13 @@ impl Widget for Icon {
mod test {
use super::*;
use crate::app::UpdateResult;
use crate::tests::support;
use crate::tests::*;
use crate::ui::UpdateContext;
use sdl2::rect::Point;
#[test]
fn must_return_noop_on_left_click() {
let config = support::build_config();
let config = build_config();
let dest = Rect::new(0, 10, 20, 30);
let src = Rect::new(40, 50, 60, 70);
let path = "/foo/bar.png".to_owned();
@ -89,7 +89,7 @@ mod test {
#[test]
fn must_use_inner() {
let config = support::build_config();
let config = build_config();
let dest = Rect::new(0, 10, 20, 30);
let src = Rect::new(40, 50, 60, 70);
let path = "/foo/bar.png".to_owned();

View File

@ -54,7 +54,7 @@ impl Widget for Label {
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let dest = match context {
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
@ -62,7 +62,9 @@ impl Widget for Label {
};
let mut d = dest.clone();
d.set_x(dest.x() + NAME_MARGIN);
canvas.set_clipping(d.clone());
if self.use_clipping() {
canvas.set_clipping(d.clone());
}
let font_details = build_font_details(self);
for c in self.name.chars() {
@ -147,13 +149,14 @@ impl ConfigHolder for Label {
mod test {
use super::*;
use crate::app::UpdateResult;
use crate::tests::support;
use crate::tests::*;
use crate::ui::{UpdateContext, Widget};
use rider_derive::*;
use sdl2::rect::Point;
#[test]
fn must_return_noop_on_left_click() {
let config = support::build_config();
let config = build_config();
let name = "Hello world".to_owned();
let mut widget = Label::new(name, config);
assert_eq!(
@ -164,8 +167,7 @@ mod test {
#[test]
fn must_use_inner() {
let config = support::build_config();
let mut renderer = support::SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let name = "Hello world".to_owned();
let mut widget = Label::new(name.clone(), config);
let dest = Rect::new(0, 0, DEST_WIDTH, DEST_HEIGHT);

View File

@ -1,12 +1,12 @@
use crate::app::UpdateResult as UR;
use crate::renderer::Renderer;
use crate::ui::*;
use rider_config::ConfigAccess;
use rider_config::{ConfigAccess, ConfigHolder};
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
const SAVE_BUTTON_OFFSET_LEFT: i32 = 16;
const SAVE_BUTTON_OFFSET_TOP: i32 = 10;
pub const SAVE_BUTTON_OFFSET_LEFT: i32 = 16;
pub const SAVE_BUTTON_OFFSET_TOP: i32 = 10;
pub struct MenuBar {
border_color: Color,
@ -45,7 +45,7 @@ impl MenuBar {
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
use std::borrow::*;
@ -60,7 +60,7 @@ impl MenuBar {
move_render_point(relative_position.clone(), &self.dest),
self.background_color.clone(),
)
.unwrap_or_else(|_| panic!("Failed to draw main menu background"));
.expect("Failed to draw main menu background");
canvas
.render_border(
match context.borrow() {
@ -69,7 +69,7 @@ impl MenuBar {
},
self.border_color.clone(),
)
.unwrap_or_else(|_| panic!("Failed to draw main menu background"));
.expect("Failed to draw main menu background");
self.save_button.render(
canvas,
@ -158,7 +158,7 @@ mod test_getters {
#[test]
fn assert_background_color() {
let config = support::build_config();
let config = build_config();
let widget = MenuBar::new(Arc::clone(&config));
let result = widget.background_color().clone();
let expected = Color::RGBA(18, 18, 18, 0);
@ -167,7 +167,7 @@ mod test_getters {
#[test]
fn assert_dest() {
let config = support::build_config();
let config = build_config();
let (w, h) = {
let c = config.read().unwrap();
(c.width() as u32, c.menu_height() as u32)
@ -188,7 +188,7 @@ mod test_render_box {
#[test]
fn must_return_top_left_point() {
let config = support::build_config();
let config = build_config();
let widget = MenuBar::new(Arc::clone(&config));
let result = widget.render_start_point();
let expected = Point::new(0, 0);
@ -206,7 +206,7 @@ mod test_click_handler {
#[test]
fn refute_when_not_click_target() {
let config = support::build_config();
let config = build_config();
let widget = MenuBar::new(Arc::clone(&config));
let point = Point::new(9999, 9999);
let context = UpdateContext::Nothing;
@ -216,7 +216,7 @@ mod test_click_handler {
#[test]
fn assert_when_click_target() {
let config = support::build_config();
let config = build_config();
let widget = MenuBar::new(Arc::clone(&config));
let point = Point::new(20, 30);
let context = UpdateContext::Nothing;
@ -226,7 +226,7 @@ mod test_click_handler {
#[test]
fn refute_when_not_click_target_because_parent() {
let config = support::build_config();
let config = build_config();
let widget = MenuBar::new(Arc::clone(&config));
let point = Point::new(20, 30);
let context = UpdateContext::ParentPosition(Point::new(9999, 9999));
@ -236,7 +236,7 @@ mod test_click_handler {
#[test]
fn assert_when_click_target_because_parent() {
let config = support::build_config();
let config = build_config();
let (w, h) = {
(
config.read().unwrap().width(),
@ -252,7 +252,7 @@ mod test_click_handler {
#[test]
fn assert_on_click_do_nothing() {
let config = support::build_config();
let config = build_config();
let mut widget = MenuBar::new(Arc::clone(&config));
let point = Point::new(12, 34);
let context = UpdateContext::ParentPosition(Point::new(678, 293));
@ -264,38 +264,51 @@ mod test_click_handler {
#[cfg(test)]
mod test_render {
use crate::tests::support::SimpleRendererMock;
use crate::app::UpdateResult;
use crate::tests::*;
use crate::ui::*;
use sdl2::rect::Rect;
use rider_derive::*;
use rider_match_widget::*;
use sdl2::pixels::PixelFormatEnum;
#[test]
fn assert_render() {
let rect_color = sdl2::pixels::Color::RGBA(18, 18, 18, 0);
let border_color = sdl2::pixels::Color::RGBA(200, 200, 200, 0);
let context = RenderContext::Nothing;
let config = support::build_config();
let mut canvas = support::CanvasMock::new();
let mut renderer = SimpleRendererMock::new(config.clone());
#[match_widgets(
fn_name = "assert_save_button_child",
widget = "MenuBar",
contains = "save_button",
x = 16i32,
y = 10i32,
w = 16u32,
h = 16u32,
widget_dump_path = "/tmp/rider-images/MenuBar-saveButton.png",
child_dump_path = "/tmp/rider-images/SaveButton.png",
background_from = "background_color",
widget_variable = "widget"
)]
fn assert_save_button_child() {}
#[test]
#[match_widgets(
fn_name = "assert_settings_button",
widget = "MenuBar",
contains = "settings_button",
x = 32i32,
y = 10i32,
w = 16u32,
h = 16u32,
widget_dump_path = "/tmp/rider-images/MenuBar-settingsButton.png",
child_dump_path = "/tmp/rider-images/SettingsButton.png",
background_from = "background_color",
widget_variable = "widget"
)]
fn assert_settings_button() {}
#[test]
fn assert_update() {
build_test_renderer!(renderer);
let mut widget = MenuBar::new(config.clone());
widget.prepare_ui();
widget.render(&mut canvas, &mut renderer, &context);
assert_eq!(widget.dest(), Rect::new(0, 0, 1024, 40));
assert_eq!(
canvas.find_rect_with_color(Rect::new(0, 0, 1024, 40), rect_color.clone()),
Some(&support::RendererRect::new(
Rect::new(0, 0, 1024, 40),
rect_color,
support::CanvasShape::Rectangle
))
);
assert_eq!(
canvas.find_border_with_color(Rect::new(0, 0, 1024, 40), border_color.clone()),
Some(&support::RendererRect::new(
Rect::new(0, 0, 1024, 40),
border_color,
support::CanvasShape::Border
))
);
let result = widget.update(0, &UpdateContext::Nothing);
assert_eq!(result, UpdateResult::NoOp);
}
}

View File

@ -39,6 +39,7 @@ pub enum UpdateContext<'l> {
Nothing,
ParentPosition(Point),
CurrentFile(&'l mut EditorFile),
ScrolledBy(Point),
}
#[derive(Clone, PartialEq, Debug)]
@ -47,6 +48,7 @@ pub enum RenderContext {
ParentPosition(Point),
}
#[cfg_attr(tarpaulin, skip)]
pub trait CanvasAccess {
fn render_rect(&mut self, rect: Rect, color: sdl2::pixels::Color) -> Result<(), String>;
fn render_border(&mut self, rect: Rect, color: sdl2::pixels::Color) -> Result<(), String>;
@ -63,6 +65,7 @@ pub trait CanvasAccess {
fn clip_rect(&self) -> Option<Rect>;
}
#[cfg_attr(tarpaulin, skip)]
impl CanvasAccess for WindowCanvas {
fn render_rect(&mut self, rect: Rect, color: sdl2::pixels::Color) -> Result<(), String> {
self.set_draw_color(color);
@ -101,33 +104,29 @@ impl CanvasAccess for WindowCanvas {
}
}
#[inline]
pub fn is_in_rect(point: &Point, rect: &Rect) -> bool {
rect.contains_point(point.clone())
}
#[inline]
pub fn build_font_details<T>(config_holder: &T) -> FontDetails
where
T: ConfigHolder,
{
let c = config_holder.config().read().unwrap();
FontDetails::new(
c.editor_config().font_path().as_str(),
(
c.editor_config().font_path(),
c.editor_config().character_size(),
)
.into()
}
#[cfg_attr(tarpaulin, skip)]
pub fn get_text_character_rect<'l, T>(c: char, renderer: &mut T) -> Option<Rect>
where
T: ManagersHolder<'l> + ConfigHolder,
T: Renderer + ConfigHolder,
{
let font_details = renderer.config().read().unwrap().editor_config().into();
renderer
.font_manager()
.load(&font_details)
.load_font(font_details)
.size_of_char(c)
.ok()
.and_then(|font| font.size_of_char(c).ok())
.and_then(|(width, height)| Some(Rect::new(0, 0, width, height)))
}
@ -189,7 +188,9 @@ pub trait Widget {
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match *context {
UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest()),
UpdateContext::ParentPosition(p) | UpdateContext::ScrolledBy(p) => {
move_render_point(p.clone(), &self.dest())
}
_ => self.dest().clone(),
}
.contains_point(point.clone())
@ -216,17 +217,24 @@ pub trait Widget {
0
}
fn use_clipping(&self) -> bool {
false
}
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let mut dest = match context {
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest()),
_ => self.dest().clone(),
};
canvas.set_clipping(self.clipping(&dest));
if self.use_clipping() {
canvas.set_clipping(self.clipping(&dest));
}
self.texture_path()
.and_then(|path| renderer.load_image(path).ok())
.and_then(|texture| {
@ -234,14 +242,14 @@ pub trait Widget {
dest.set_height(self.dest().height());
canvas
.render_image(texture.clone(), self.source().clone(), dest.clone())
.unwrap_or_else(|_| panic!("Failed to draw widget texture"));
.unwrap_or_else(|e| panic!("Failed to draw widget texture. {}", e));
Some(())
});
}
fn prepare_ui<'l, T>(&mut self, _renderer: &mut T)
where
T: Renderer + CharacterSizeManager,
T: Renderer + CharacterSizeManager + ConfigHolder,
{
}
}
@ -249,7 +257,9 @@ pub trait Widget {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::support;
use crate::tests::*;
use rider_derive::*;
use std::ops::{Deref, DerefMut};
struct ConfigWrapper {
pub inner: ConfigAccess,
@ -261,18 +271,52 @@ mod tests {
}
}
#[test]
fn must_return_true_if_inside_rect() {
let rect = Rect::new(10, 10, 30, 30);
let point = Point::new(20, 20);
assert_eq!(is_in_rect(&point, &rect), true);
struct Dummy {
pub inner: WidgetInner,
}
#[test]
fn must_return_not_if_not_inside_rect() {
let rect = Rect::new(10, 10, 30, 30);
let point = Point::new(41, 41);
assert_eq!(is_in_rect(&point, &rect), false);
impl Dummy {
pub fn new(config: ConfigAccess) -> Self {
Self {
inner: WidgetInner::new(config, Rect::new(0, 1, 2, 3), Rect::new(4, 5, 6, 7)),
}
}
}
impl Deref for Dummy {
type Target = WidgetInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for Dummy {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Widget for Dummy {
fn texture_path(&self) -> Option<String> {
None
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn source(&self) -> &Rect {
&self.source
}
fn set_source(&mut self, rect: &Rect) {
self.source = rect.clone();
}
}
#[test]
@ -284,7 +328,7 @@ mod tests {
#[test]
fn must_build_font_details() {
let config = support::build_config();
let config = build_config();
let wrapper = ConfigWrapper {
inner: config.clone(),
};
@ -293,4 +337,137 @@ mod tests {
assert_eq!(details.path, c.editor_config().font_path().to_string());
assert_eq!(details.size, c.editor_config().character_size());
}
// #[test]
// fn mut_return_character_rectangle() {
// let config = build_config();
// let mut renderer = SimpleRendererMock::new(config);
// let result = get_text_character_rect('a', &mut renderer);
// assert_eq!(result, Some(Rect::new(0, 0, 10, 10)));
// }
#[test]
fn check_texture_path() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(widget.texture_path(), None);
}
#[test]
fn check_dest() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(widget.dest(), &Rect::new(4, 5, 6, 7));
}
#[test]
fn check_set_dest() {
let config = build_config();
let mut widget = Dummy::new(config);
assert_eq!(widget.set_dest(&Rect::new(9, 8, 7, 6)), ());
assert_eq!(widget.dest(), &Rect::new(9, 8, 7, 6));
}
#[test]
fn check_source() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(widget.source(), &Rect::new(0, 1, 2, 3));
}
#[test]
fn check_set_source() {
let config = build_config();
let mut widget = Dummy::new(config);
assert_eq!(widget.set_source(&Rect::new(9, 8, 7, 6)), ());
assert_eq!(widget.source(), &Rect::new(9, 8, 7, 6));
}
#[test]
fn check_update() {
let config = build_config();
let mut widget = Dummy::new(config);
assert_eq!(
widget.update(0, &UpdateContext::Nothing),
UpdateResult::NoOp
);
}
#[test]
fn check_on_left_click() {
let config = build_config();
let mut widget = Dummy::new(config);
assert_eq!(
widget.on_left_click(&Point::new(0, 1), &UpdateContext::Nothing),
UpdateResult::NoOp
);
}
#[test]
fn check_is_left_click_target() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(
widget.is_left_click_target(&Point::new(0, 1), &UpdateContext::Nothing),
false
);
}
#[test]
fn check_render_start_point() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(
widget.render_start_point(),
Rect::new(4, 5, 6, 7).top_left()
);
}
#[test]
fn check_clipping() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(
widget.clipping(&Rect::new(0, 0, 1, 1)),
Rect::new(0, 0, 1, 1)
);
}
#[test]
fn check_padding_width() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(widget.padding_width(), 0);
}
#[test]
fn check_padding_height() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(widget.padding_height(), 0);
}
#[test]
fn check_use_clipping() {
let config = build_config();
let widget = Dummy::new(config);
assert_eq!(widget.use_clipping(), false);
}
#[test]
fn check_render() {
build_test_renderer!(renderer);
let widget = Dummy::new(config);
assert_eq!(
widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing),
()
);
}
#[test]
fn check_prepare_ui() {
build_test_renderer!(renderer);
let mut widget = Dummy::new(config);
assert_eq!(widget.prepare_ui(&mut renderer), ());
}
}

View File

@ -9,6 +9,7 @@ pub enum ModalType {
Settings(Settings),
}
#[cfg_attr(tarpaulin, skip)]
impl PartialEq for ModalType {
fn eq(&self, other: &ModalType) -> bool {
match (self, other) {
@ -19,6 +20,7 @@ impl PartialEq for ModalType {
}
}
#[cfg_attr(tarpaulin, skip)]
impl std::fmt::Debug for ModalType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let name = match self {

View File

@ -84,26 +84,46 @@ impl OpenFile {
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
self.directory_view.open_directory(dir_path, renderer);
{
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 = Rect::new(
self.directory_view.dest().x(),
self.directory_view.dest().y(),
self.directory_view.dest().width() + (2 * CONTENT_MARGIN_LEFT as u32),
self.directory_view.dest().height() + (2 * CONTENT_MARGIN_TOP as u32),
);
self.full_dest = full_dest;
}
}
pub fn full_rect(&self) -> &Rect {
&self.full_dest
}
}
pub fn update(&mut self, ticks: i32, context: &UC) -> UR {
impl Widget for OpenFile {
fn texture_path(&self) -> Option<String> {
None
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
fn source(&self) -> &Rect {
self.dest()
}
fn set_source(&mut self, rect: &Rect) {
self.set_dest(rect)
}
fn update(&mut self, ticks: i32, context: &UC) -> UR {
let (window_width, window_height, color, scroll_width, scroll_margin) = {
let c = self.config.read().unwrap();
(
@ -141,7 +161,44 @@ impl OpenFile {
UR::NoOp
}
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RC)
fn on_left_click(&mut self, point: &Point, context: &UC) -> 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
}
fn is_left_click_target(&self, point: &Point, context: &UC) -> 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())
}
}
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
@ -174,64 +231,19 @@ impl OpenFile {
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
}
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
fn prepare_ui<'l, T>(&mut self, renderer: &mut T)
where
R: Renderer + CharacterSizeManager,
T: Renderer + CharacterSizeManager + ConfigHolder,
{
self.directory_view.prepare_ui(renderer);
}
pub fn render_start_point(&self) -> Point {
self.dest.top_left()
}
pub fn dest(&self) -> Rect {
self.dest.clone()
}
pub fn on_left_click(&mut self, point: &Point, context: &UC) -> 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
}
pub fn is_left_click_target(&self, point: &Point, context: &UC) -> 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())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::support::SimpleRendererMock;
use crate::tests::support::{build_config, CanvasMock};
use crate::tests::*;
use rider_derive::*;
use std::fs;
//#######################################################################
@ -254,7 +266,7 @@ mod tests {
fn assert_dest() {
let config = build_config();
let widget = OpenFile::new("/tmp".to_owned(), 120, 130, config);
assert_eq!(widget.dest(), Rect::new(452, 365, 120, 130));
assert_eq!(widget.dest(), &Rect::new(452, 365, 120, 130));
}
//#######################################################################
@ -276,8 +288,7 @@ mod tests {
fn assert_open_directory() {
let path = "/tmp/rider/test-open-file/open-directory";
fs::create_dir_all(path).unwrap();
let config = build_config();
let mut renderer = SimpleRendererMock::new(config);
build_test_renderer!(renderer);
let mut widget = OpenFile::new(path.to_owned(), 120, 130, renderer.config().clone());
widget.open_directory(path.to_owned(), &mut renderer);
}
@ -383,9 +394,8 @@ mod tests {
#[test]
fn assert_render() {
let config = build_config();
let path = "/tmp/rider/test-open-file/open-directory";
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let widget = OpenFile::new(path.to_owned(), 100, 100, config);
let p = Point::new(100, 100);
@ -399,9 +409,8 @@ mod tests {
#[test]
fn assert_prepare_ui() {
let config = build_config();
let path = "/tmp/rider/test-open-file/open-directory";
let mut renderer = SimpleRendererMock::new(config.clone());
build_test_renderer!(renderer);
let mut widget = OpenFile::new(path.to_owned(), 100, 100, config);
widget.prepare_ui(&mut renderer);
}

View File

@ -27,73 +27,74 @@ pub struct Settings {
character_size_value: Label,
}
impl Settings {
pub fn new(config: ConfigAccess) -> Self {
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()),
character_size_label: Label::new("Character size".into(), config.clone()),
character_size_value: Label::new(
format!("{}", c.editor_config().character_size()).to_owned(),
config.clone(),
),
config: config.clone(),
}
impl Widget for Settings {
fn texture_path(&self) -> Option<String> {
None
}
pub fn full_rect(&self) -> &Rect {
&self.full_dest
fn dest(&self) -> &Rect {
&self.dest
}
pub fn scroll_by(&mut self, x: i32, y: i32) {
let read_config = self.config.read().unwrap();
fn set_dest(&mut self, _rect: &Rect) {}
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);
}
}
fn source(&self) -> &Rect {
&self.dest
}
pub fn scroll(&self) -> Point {
Point::new(
-self.horizontal_scroll_bar.scroll_value(),
-self.vertical_scroll_bar.scroll_value(),
)
fn set_source(&mut self, _rect: &Rect) {}
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
}
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RC)
#[inline]
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::NoOp
}
#[inline]
fn is_left_click_target(&self, _point: &Point, _context: &UpdateContext) -> bool {
false
}
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RC)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
@ -165,9 +166,9 @@ impl Settings {
.render(canvas, &RenderContext::ParentPosition(self.dest.top_left()));
}
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
self.font_label.prepare_ui(renderer);
self.font_value.prepare_ui(renderer);
@ -176,66 +177,178 @@ impl Settings {
}
}
impl ClickHandler for Settings {
#[inline]
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::NoOp
impl ScrollView<VerticalScrollBar, HorizontalScrollBar> for Settings {
fn mut_horizontal_scroll_handler(&mut self) -> Option<&mut HorizontalScrollBar> {
Some(&mut self.horizontal_scroll_bar)
}
#[inline]
fn is_left_click_target(&self, _point: &Point, _context: &UpdateContext) -> bool {
false
fn horizontal_scroll_handler(&self) -> Option<&HorizontalScrollBar> {
Some(&self.horizontal_scroll_bar)
}
fn mut_vertical_scroll_handler(&mut self) -> Option<&mut VerticalScrollBar> {
Some(&mut self.vertical_scroll_bar)
}
fn vertical_scroll_handler(&self) -> Option<&VerticalScrollBar> {
Some(&self.vertical_scroll_bar)
}
}
impl RenderBox for Settings {
fn render_start_point(&self) -> Point {
self.dest.top_left()
impl Settings {
pub fn new(config: ConfigAccess) -> Self {
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().to_string(), config.clone()),
character_size_label: Label::new("Character size".into(), config.clone()),
character_size_value: Label::new(
format!("{}", c.editor_config().character_size()).to_owned(),
config.clone(),
),
config: config.clone(),
}
}
fn dest(&self) -> Rect {
self.dest.clone()
pub fn full_rect(&self) -> &Rect {
&self.full_dest
}
}
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
impl ConfigHolder for Settings {
fn config(&self) -> &ConfigAccess {
&self.config
}
}
#[cfg(test)]
mod tests {
use crate::app::UpdateResult;
use crate::tests::*;
use crate::ui::{RenderContext, ScrollView, Settings, UpdateContext, Widget};
use rider_derive::*;
use sdl2::rect::{Point, Rect};
#[test]
fn must_have_vertical_scrollbar() {
let config = build_config();
let mut widget = Settings::new(config);
assert_eq!(widget.mut_vertical_scroll_handler().is_some(), true);
assert_eq!(widget.vertical_scroll_handler().is_some(), true);
}
#[test]
fn must_have_horizontal_scrollbar() {
let config = build_config();
let mut widget = Settings::new(config);
assert_eq!(widget.mut_horizontal_scroll_handler().is_some(), true);
assert_eq!(widget.horizontal_scroll_handler().is_some(), true);
}
// Widget
#[test]
fn assert_texture_path() {
let config = build_config();
let widget = Settings::new(config);
let result = widget.texture_path();
assert!(result.is_none());
}
#[test]
fn assert_dest() {
let config = build_config();
let widget = Settings::new(config);
let result = widget.dest().clone();
let expected = Rect::new(16, 24, 992, 812);
assert_eq!(result, expected);
}
#[test]
fn assert_set_dest() {
let config = build_config();
let mut widget = Settings::new(config);
widget.set_dest(&Rect::new(100, 200, 300, 400));
assert_ne!(widget.dest(), &Rect::new(100, 200, 300, 400));
}
#[test]
fn assert_source() {
let config = build_config();
let widget = Settings::new(config);
let result = widget.source();
assert_eq!(result, &Rect::new(16, 24, 992, 812));
}
#[test]
fn assert_set_source() {
let config = build_config();
let mut widget = Settings::new(config);
widget.set_source(&Rect::new(1, 2, 3, 4));
assert_ne!(widget.source(), &Rect::new(1, 2, 3, 4));
}
#[test]
fn assert_update() {
let config = build_config();
let mut widget = Settings::new(config);
let result = widget.update(0, &UpdateContext::Nothing);
assert_eq!(result, UpdateResult::NoOp);
}
#[test]
fn assert_on_left_click() {
let config = build_config();
let mut widget = Settings::new(config);
let result = widget.on_left_click(&Point::new(600, 800), &UpdateContext::Nothing);
assert_eq!(result, UpdateResult::NoOp);
}
#[test]
fn assert_is_left_click_target() {
let config = build_config();
let widget = Settings::new(config);
let result = widget.is_left_click_target(&Point::new(600, 800), &UpdateContext::Nothing);
assert_eq!(result, false);
}
#[test]
fn assert_use_clipping() {
let config = build_config();
let widget = Settings::new(config);
let result = widget.use_clipping();
assert_eq!(result, false);
}
#[test]
fn assert_render() {
build_test_renderer!(renderer);
let mut canvas = CanvasMock::new();
let widget = Settings::new(config);
let result = widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing);
assert_eq!(result, ());
}
#[test]
fn assert_prepare_ui() {
build_test_renderer!(renderer);
let mut widget = Settings::new(config);
let result = widget.prepare_ui(&mut renderer);
assert_eq!(result, ());
}
}

View File

@ -4,7 +4,6 @@ use crate::ui::filesystem::directory::DirectoryView;
use crate::ui::horizontal_scroll_bar::HorizontalScrollBar;
use crate::ui::text_character::CharacterSizeManager;
use crate::ui::vertical_scroll_bar::VerticalScrollBar;
use crate::ui::ClickHandler;
use crate::ui::RenderContext;
use crate::ui::UpdateContext;
use crate::ui::{move_render_point, ScrollView};
@ -33,31 +32,24 @@ pub struct ProjectTreeSidebar {
horizontal_scroll_bar: HorizontalScrollBar,
}
impl ProjectTreeSidebar {
pub fn new(root: String, config: Arc<RwLock<Config>>) -> Self {
let (background_color, border_color, h): (Color, Color, u32) = {
let c = config.read().unwrap();
(
c.theme().background().into(),
c.theme().border_color().into(),
c.height(),
)
};
Self {
dest: Rect::new(0, 0, 200, h),
full_dest: Rect::new(0, 0, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE),
dir_view: DirectoryView::new(root.clone(), config.clone()),
vertical_scroll_bar: VerticalScrollBar::new(Arc::clone(&config)),
horizontal_scroll_bar: HorizontalScrollBar::new(Arc::clone(&config)),
config,
root,
background_color,
border_color,
}
impl Widget for ProjectTreeSidebar {
fn texture_path(&self) -> Option<String> {
None
}
pub fn update(&mut self, ticks: i32) {
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, _rect: &Rect) {}
fn source(&self) -> &Rect {
&self.dest
}
fn set_source(&mut self, _rect: &Rect) {}
fn update(&mut self, ticks: i32, _context: &UpdateContext) -> UpdateResult {
let config = self.config.read().unwrap();
let height = config.height();
// let left_margin = config.editor_left_margin();
@ -66,70 +58,9 @@ impl ProjectTreeSidebar {
self.dest.set_y(top_margin);
self.dest.set_height(height - top_margin as u32);
self.dir_view.update(ticks, &UpdateContext::Nothing);
UpdateResult::NoOp
}
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
{
let config = self.config.read().unwrap();
let height = config.height();
let left_margin = 0;
let top_margin = config.menu_height() as i32;
self.dest.set_x(left_margin);
self.dest.set_y(top_margin);
self.dest.set_height(height);
self.dir_view.prepare_ui(renderer);
self.dir_view.open_directory(self.root.clone(), renderer);
}
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
C: CanvasAccess,
{
canvas.set_clipping(self.dest.clone());
canvas
.render_rect(self.dest.clone(), self.background_color.clone())
.unwrap();
canvas
.render_border(self.dest.clone(), self.border_color.clone())
.unwrap();
// dir view
let context = RenderContext::ParentPosition(
self.dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP),
);
self.dir_view.render(canvas, renderer, &context);
}
pub fn full_rect(&self) -> Rect {
self.dest.clone()
}
pub fn root(&self) -> String {
self.root.clone()
}
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R)
where
R: Renderer + CharacterSizeManager,
{
self.dir_view.open_directory(dir_path, renderer);
{
let dest = self.dir_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;
}
}
}
impl ClickHandler for ProjectTreeSidebar {
fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UpdateResult {
let dest = match context {
UpdateContext::ParentPosition(p) => move_render_point(*p, &self.dest),
@ -166,6 +97,95 @@ impl ClickHandler for ProjectTreeSidebar {
Rect::new(p.x(), p.y(), dest.width(), dest.height()).contains_point(point.clone())
}
}
fn use_clipping(&self) -> bool {
true
}
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, _context: &RenderContext)
where
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
if self.use_clipping() {
canvas.set_clipping(self.dest.clone());
}
canvas
.render_rect(self.dest.clone(), self.background_color.clone())
.unwrap();
canvas
.render_border(self.dest.clone(), self.border_color.clone())
.unwrap();
// dir view
let context = RenderContext::ParentPosition(
self.dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP),
);
self.dir_view.render(canvas, renderer, &context);
}
fn prepare_ui<R>(&mut self, renderer: &mut R)
where
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let config = self.config.read().unwrap();
let height = config.height();
let left_margin = 0;
let top_margin = config.menu_height() as i32;
self.dest.set_x(left_margin);
self.dest.set_y(top_margin);
self.dest.set_height(height);
self.dir_view.prepare_ui(renderer);
self.dir_view.open_directory(self.root.clone(), renderer);
}
}
impl ProjectTreeSidebar {
pub fn new(root: String, config: Arc<RwLock<Config>>) -> Self {
let (background_color, border_color, h): (Color, Color, u32) = {
let c = config.read().unwrap();
(
c.theme().background().into(),
c.theme().border_color().into(),
c.height(),
)
};
Self {
dest: Rect::new(0, 0, 200, h),
full_dest: Rect::new(0, 0, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE),
dir_view: DirectoryView::new(root.clone(), config.clone()),
vertical_scroll_bar: VerticalScrollBar::new(Arc::clone(&config)),
horizontal_scroll_bar: HorizontalScrollBar::new(Arc::clone(&config)),
config,
root,
background_color,
border_color,
}
}
pub fn full_rect(&self) -> Rect {
self.dest.clone()
}
pub fn root(&self) -> String {
self.root.clone()
}
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R)
where
R: Renderer + CharacterSizeManager + ConfigHolder,
{
self.dir_view.open_directory(dir_path, renderer);
{
self.full_dest = Rect::new(
self.dir_view.dest().x(),
self.dir_view.dest().y(),
self.dir_view.dest().width() + (2 * CONTENT_MARGIN_LEFT as u32),
self.dir_view.dest().height() + (2 * CONTENT_MARGIN_TOP as u32),
);
}
}
}
impl ConfigHolder for ProjectTreeSidebar {
@ -197,13 +217,12 @@ mod tests {
use crate::renderer::managers::FontDetails;
use crate::renderer::managers::TextDetails;
use crate::renderer::renderer::Renderer;
use crate::tests::support::build_config;
use crate::tests::support::CanvasMock;
use crate::tests::*;
use crate::ui::project_tree::ProjectTreeSidebar;
use crate::ui::scroll_bar::ScrollView;
use crate::ui::text_character::CharacterSizeManager;
use crate::ui::ClickHandler;
use crate::ui::UpdateContext;
use crate::ui::Widget;
use crate::ui::{RenderContext, UpdateContext};
use rider_config::ConfigAccess;
use rider_config::ConfigHolder;
use sdl2::rect::Point;
@ -270,7 +289,7 @@ mod tests {
fn assert_update() {
let config = build_config();
let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config);
widget.update(0);
widget.update(0, &UpdateContext::Nothing);
assert_eq!(widget.full_rect(), Rect::new(0, 40, 200, 820));
}
@ -289,7 +308,7 @@ mod tests {
let mut renderer = RendererMock::new(config.clone());
let mut canvas = CanvasMock::new();
let widget = ProjectTreeSidebar::new("/tmp".to_owned(), config);
widget.render(&mut canvas, &mut renderer);
widget.render(&mut canvas, &mut renderer, &RenderContext::Nothing);
}
//#######################################################################

View File

@ -84,7 +84,7 @@ impl ScrollWidget for HorizontalScrollBar {
#[cfg(test)]
mod test_update {
use super::*;
use crate::tests::support;
use crate::tests::*;
use std::sync::*;
impl HorizontalScrollBar {
@ -95,7 +95,7 @@ mod test_update {
#[test]
fn assert_do_nothing_when_small_content() {
let config = support::build_config();
let config = build_config();
let mut widget = HorizontalScrollBar::new(Arc::clone(&config));
widget.set_viewport(100);
widget.set_full_size(20);
@ -108,7 +108,7 @@ mod test_update {
#[test]
fn assert_update_when_huge_content() {
let config = support::build_config();
let config = build_config();
let mut widget = HorizontalScrollBar::new(Arc::clone(&config));
widget.set_viewport(100);
widget.set_full_size(200);
@ -123,12 +123,12 @@ mod test_update {
#[cfg(test)]
mod test_scrollable {
use super::*;
use crate::tests::support;
use crate::tests::*;
use std::sync::*;
#[test]
fn assert_scroll_to() {
let config = support::build_config();
let config = build_config();
let mut widget = HorizontalScrollBar::new(Arc::clone(&config));
let old = widget.scroll_value();
widget.scroll_to(157);
@ -140,14 +140,14 @@ mod test_scrollable {
#[test]
fn assert_scroll_value() {
let config = support::build_config();
let config = build_config();
let widget = HorizontalScrollBar::new(Arc::clone(&config));
assert_eq!(widget.scroll_value(), 0);
}
#[test]
fn assert_set_viewport() {
let config = support::build_config();
let config = build_config();
let mut widget = HorizontalScrollBar::new(Arc::clone(&config));
let old = widget.viewport();
widget.set_viewport(157);
@ -159,7 +159,7 @@ mod test_scrollable {
#[test]
fn assert_set_full_size() {
let config = support::build_config();
let config = build_config();
let mut widget = HorizontalScrollBar::new(Arc::clone(&config));
let old = widget.full;
widget.set_full_size(157);
@ -171,7 +171,7 @@ mod test_scrollable {
#[test]
fn assert_set_location() {
let config = support::build_config();
let config = build_config();
let mut widget = HorizontalScrollBar::new(Arc::clone(&config));
let old = widget.rect().y();
widget.set_location(157);

View File

@ -84,7 +84,7 @@ impl ScrollWidget for VerticalScrollBar {
#[cfg(test)]
mod test_update {
use super::*;
use crate::tests::support;
use crate::tests::*;
use std::sync::*;
impl VerticalScrollBar {
@ -95,7 +95,7 @@ mod test_update {
#[test]
fn assert_do_nothing_when_small_content() {
let config = support::build_config();
let config = build_config();
let mut widget = VerticalScrollBar::new(Arc::clone(&config));
widget.set_viewport(100);
widget.set_full_size(20);
@ -108,7 +108,7 @@ mod test_update {
#[test]
fn assert_update_when_huge_content() {
let config = support::build_config();
let config = build_config();
let mut widget = VerticalScrollBar::new(Arc::clone(&config));
widget.set_viewport(100);
widget.set_full_size(200);
@ -123,12 +123,12 @@ mod test_update {
#[cfg(test)]
mod test_scrollable {
use super::*;
use crate::tests::support;
use crate::tests::*;
use std::sync::*;
#[test]
fn assert_scroll_to() {
let config = support::build_config();
let config = build_config();
let mut widget = VerticalScrollBar::new(Arc::clone(&config));
let old = widget.scroll_value();
widget.scroll_to(157);
@ -140,14 +140,14 @@ mod test_scrollable {
#[test]
fn assert_scroll_value() {
let config = support::build_config();
let config = build_config();
let widget = VerticalScrollBar::new(Arc::clone(&config));
assert_eq!(widget.scroll_value(), 0);
}
#[test]
fn assert_set_viewport() {
let config = support::build_config();
let config = build_config();
let mut widget = VerticalScrollBar::new(Arc::clone(&config));
let old = widget.viewport();
widget.set_viewport(157);
@ -159,7 +159,7 @@ mod test_scrollable {
#[test]
fn assert_set_full_size() {
let config = support::build_config();
let config = build_config();
let mut widget = VerticalScrollBar::new(Arc::clone(&config));
let old = widget.full;
widget.set_full_size(157);
@ -171,7 +171,7 @@ mod test_scrollable {
#[test]
fn assert_set_location() {
let config = support::build_config();
let config = build_config();
let mut widget = VerticalScrollBar::new(Arc::clone(&config));
let old = widget.rect().x();
widget.set_location(157);

View File

@ -51,18 +51,6 @@ impl TextCharacter {
self.last_in_line
}
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();
}
pub fn color(&self) -> &Color {
&self.color
}
@ -101,15 +89,42 @@ impl TextCharacter {
}
}
impl TextCharacter {
impl Widget for TextCharacter {
fn texture_path(&self) -> Option<String> {
None
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone()
}
fn source(&self) -> &Rect {
&self.source
}
fn set_source(&mut self, rect: &Rect) {
self.source = rect.clone();
}
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::MoveCaret(
self.dest.clone(),
CaretPosition::new(self.position(), self.line(), 0),
)
}
/**
* Must first create targets so even if new line appear renderer will know
* where move render starting point
*/
pub fn render<R, C>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
R: Renderer + ConfigHolder,
C: CanvasAccess,
R: Renderer + CharacterSizeManager + ConfigHolder,
{
let font_details: FontDetails = renderer.config().read().unwrap().editor_config().into();
@ -125,7 +140,7 @@ impl TextCharacter {
};
let dest = match context {
RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest(),
_ => self.dest().clone(),
};
if let Ok(texture) = renderer.load_text_tex(&mut details, font_details) {
@ -135,9 +150,9 @@ impl TextCharacter {
}
}
pub fn prepare_ui<'l, T>(&mut self, renderer: &mut T)
fn prepare_ui<'l, T>(&mut self, renderer: &mut T)
where
T: ConfigHolder + CharacterSizeManager + Renderer,
T: Renderer + CharacterSizeManager + ConfigHolder,
{
let font_details: FontDetails = renderer.config().read().unwrap().editor_config().into();
let rect = renderer.load_character_size(self.text_character);
@ -159,39 +174,6 @@ impl TextCharacter {
}
}
impl Update for TextCharacter {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR {
UR::NoOp
}
}
impl ClickHandler for TextCharacter {
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::MoveCaret(
self.dest().clone(),
CaretPosition::new(self.position(), self.line(), 0),
)
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
let rect = match context {
&UpdateContext::ParentPosition(p) => move_render_point(p, &self.dest),
_ => self.dest(),
};
is_in_rect(point, &rect)
}
}
impl RenderBox for TextCharacter {
fn render_start_point(&self) -> Point {
self.dest.top_left()
}
fn dest(&self) -> Rect {
self.dest
}
}
impl PartialEq for TextCharacter {
fn eq(&self, other: &Self) -> bool {
self.line == other.line
@ -229,7 +211,7 @@ mod test_getters {
#[test]
fn must_return_valid_is_last_in_line() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
0,
@ -243,7 +225,7 @@ mod test_getters {
#[test]
fn must_return_true_for_is_new_line_if_new_line() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
0,
@ -257,7 +239,7 @@ mod test_getters {
#[test]
fn must_return_false_for_is_new_line_if_new_line() {
let config = support::build_config();
let config = build_config();
let widget =
TextCharacter::new('W', 0, 0, true, Color::RGB(1, 12, 123), Arc::clone(&config));
assert_eq!(widget.is_new_line(), false);
@ -265,7 +247,7 @@ mod test_getters {
#[test]
fn must_return_valid_position() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
1,
@ -279,7 +261,7 @@ mod test_getters {
#[test]
fn must_return_valid_line() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
1,
@ -293,7 +275,7 @@ mod test_getters {
#[test]
fn must_return_valid_text_character() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
87,
@ -307,7 +289,7 @@ mod test_getters {
#[test]
fn must_return_valid_source() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
0,
@ -321,7 +303,7 @@ mod test_getters {
#[test]
fn must_return_valid_dest() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
0,
@ -330,12 +312,12 @@ mod test_getters {
Color::RGB(1, 12, 123),
Arc::clone(&config),
);
assert_eq!(widget.dest(), Rect::new(0, 0, 0, 0));
assert_eq!(widget.dest(), &Rect::new(0, 0, 0, 0));
}
#[test]
fn must_return_valid_color() {
let config = support::build_config();
let config = build_config();
let widget = TextCharacter::new(
'\n',
0,
@ -357,7 +339,7 @@ mod test_own_methods {
#[test]
fn must_update_position_of_new_line() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'\n',
0,
@ -371,13 +353,13 @@ mod test_own_methods {
let mut current = Rect::new(10, 23, 0, 0);
widget.update_position(&mut current);
assert_eq!(current, Rect::new(0, 103, 1, 1));
assert_eq!(widget.dest(), Rect::new(10, 23, 30, 40));
assert_eq!(widget.dest(), &Rect::new(10, 23, 30, 40));
assert_eq!(widget.source(), &Rect::new(50, 60, 70, 80));
}
#[test]
fn must_update_position_of_non_new_line() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'W',
0,
@ -391,7 +373,7 @@ mod test_own_methods {
let mut current = Rect::new(10, 23, 0, 0);
widget.update_position(&mut current);
assert_eq!(current, Rect::new(80, 23, 1, 1));
assert_eq!(widget.dest(), Rect::new(10, 23, 70, 80));
assert_eq!(widget.dest(), &Rect::new(10, 23, 70, 80));
assert_eq!(widget.source(), &Rect::new(50, 60, 70, 80));
}
}
@ -406,7 +388,7 @@ mod test_click_handler {
#[test]
fn refute_when_not_click_target() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'\n',
0,
@ -425,7 +407,7 @@ mod test_click_handler {
#[test]
fn assert_when_click_target() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'\n',
0,
@ -444,7 +426,7 @@ mod test_click_handler {
#[test]
fn refute_when_not_click_target_because_parent() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'\n',
0,
@ -463,7 +445,7 @@ mod test_click_handler {
#[test]
fn assert_when_click_target_because_parent() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'\n',
0,
@ -482,7 +464,7 @@ mod test_click_handler {
#[test]
fn assert_on_click_return_move_caret() {
let config = support::build_config();
let config = build_config();
let position = 1233;
let line = 2893;
let mut widget = TextCharacter::new(
@ -514,7 +496,7 @@ mod test_render_box {
#[test]
fn must_return_top_left_point() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'\n',
0,
@ -541,7 +523,7 @@ mod test_update {
#[test]
fn assert_do_nothing() {
let config = support::build_config();
let config = build_config();
let mut widget = TextCharacter::new(
'\n',
0,

View File

@ -25,7 +25,7 @@ pub mod lexer {
token: Token::new(text.to_string(), 0, 0, 0, 0)
}, text),
r"(let|fn|type|struct|pub|impl|for|self|Self|mod|use|enum|(iu)(8|16|32)|usize|bool)" => (TokenType::Keyword {
r"(let|fn|type|struct|trait|pub|impl|for|self|Self|mod|use|enum|(iu)(8|16|32)|usize|bool)" => (TokenType::Keyword {
token: Token::new(text.to_string(), 0, 0, 0, 0)
}, text),

View File

@ -0,0 +1,21 @@
[package]
name = "rider-match-widget"
version = "0.1.0"
authors = ["Adrian Woźniak <adrian.wozniak.1986@gmail.com>"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
darling = { version = "*" }
[dependencies.proc-macro2]
version = "1.0"
[dependencies.quote]
version = "1.0"
[dependencies.syn]
version = "1.0"
features = ["full", "extra-traits"]

View File

@ -0,0 +1,176 @@
use darling::{FromMeta, ToTokens};
use proc_macro;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, AttributeArgs};
#[derive(Debug, Clone)]
struct AssertBuilder {
widget: String,
widget_variable: Option<String>,
contains: String,
x: i32,
y: i32,
w: u32,
h: u32,
widget_dump_path: Option<String>,
child_dump_path: Option<String>,
background_from: Option<String>,
}
impl ToTokens for AssertBuilder {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let AssertBuilder {
widget,
contains,
x,
y,
w,
h,
widget_dump_path,
child_dump_path,
widget_variable,
background_from,
} = self.clone();
let var = format_ident!(
"{}",
widget_variable
.as_ref()
.cloned()
.unwrap_or_else(|| "widget".to_string())
);
let widget_name = format_ident!("{}", widget);
let child_name = format_ident!("{}", contains);
let background = match background_from {
Some(name) => {
let ident = format_ident!("{}", name);
quote! {
surface_canvas
.render_rect(size.clone(), widget. #ident .clone())
.unwrap();
}
}
_ => quote! {},
};
let widget_dump_path = match widget_dump_path.as_ref() {
Some(t) => {
quote! { Some( #t ) }
}
None => quote! { None },
};
let child_dump_path = match child_dump_path.as_ref() {
Some(t) => {
quote! { Some( #t ) }
}
None => quote! { None },
};
let generated = quote! {
let mut character_sizes = std::collections::HashMap::new();
let config = build_config();
let mut surface =
sdl2::surface::Surface::new( #x as u32 + #w as u32 , #y as u32 + #h as u32, sdl2::pixels::PixelFormatEnum::RGBA8888).unwrap();
let mut surface_canvas = sdl2::render::Canvas::from_surface(surface).unwrap();
let mut texture_creator = surface_canvas.texture_creator();
let mut texture_manager = crate::renderer::managers::TextureManager::new(&texture_creator);
let mut ttf_context = sdl2::ttf::Sdl2TtfContext {};
let mut renderer = SimpleRendererMock {
config: config.clone(),
ttf: ttf_context,
character_sizes,
texture_manager,
};
let mut #var = #widget_name ::new(config);
#var .prepare_ui();
let size = sdl2::rect::Rect::new(
0i32,
0i32,
#w as u32 + #x as u32,
#h as u32 + #y as u32,
);
let expected_pixels = {
#background
#var . #child_name .render(
&mut surface_canvas,
&mut renderer,
&RenderContext::ParentPosition( size.top_left() .offset( #x , #y ) ),
);
surface_canvas
.read_pixels(sdl2::rect::Rect::new( #x as i32, #y as i32 , #w as u32 , #h as u32 ), PixelFormatEnum::RGBA8888)
.unwrap()
};
if let Some(child_dump_path) = #child_dump_path {
surface_canvas.dump_ui(child_dump_path);
}
#var .render(&mut surface_canvas, &mut renderer, &RenderContext::Nothing);
if let Some(widget_dump_path) = #widget_dump_path {
surface_canvas.dump_ui(widget_dump_path);
}
let result_pixels = surface_canvas
.read_pixels(sdl2::rect::Rect::new( #x as i32, #y as i32, #w as u32, #h as u32 ), PixelFormatEnum::RGBA8888)
.unwrap();
if expected_pixels != result_pixels {
match ( #child_dump_path, #widget_dump_path ) {
(Some(c), Some(w)) => {
assert_eq!(c, w);
}
_ => {
assert_eq!(result_pixels, expected_pixels);
}
}
}
};
tokens.extend(generated);
}
}
#[derive(Debug, Clone, FromMeta)]
struct AssertOptions {
fn_name: String,
widget: String,
contains: String,
x: i32,
y: i32,
w: u32,
h: u32,
widget_dump_path: Option<String>,
child_dump_path: Option<String>,
widget_variable: Option<String>,
background_from: Option<String>,
}
#[proc_macro_attribute]
pub fn match_widgets(args: TokenStream, _input: TokenStream) -> TokenStream {
let attr_args = parse_macro_input!(args as AttributeArgs);
// let _input = parse_macro_input!(input as ItemFn);
let opts = match AssertOptions::from_list(&attr_args) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(e.write_errors());
}
};
let builder = AssertBuilder {
widget: opts.widget,
widget_variable: opts.widget_variable,
contains: opts.contains,
x: opts.x,
y: opts.y,
w: opts.w,
h: opts.h,
widget_dump_path: opts.widget_dump_path,
child_dump_path: opts.child_dump_path,
background_from: opts.background_from,
};
let name = format_ident!("{}", opts.fn_name);
let output = quote! { fn #name () { #builder } };
output.into()
}

View File

@ -1,6 +1,7 @@
extern crate rider_config;
use std::process::Command;
#[cfg_attr(tarpaulin, skip)]
fn main() {
let generator = rider_config::directories::get_binary_path("rider-generator").unwrap();
println!("generator will be {:?}", generator);

View File

@ -1,154 +1,159 @@
use std::ops::Deref;
#[cfg_attr(tarpaulin, skip)]
mod test_files {
use std::ops::Deref;
pub mod plain;
pub mod rust_lang;
pub mod plain;
pub mod rust_lang;
#[derive(Debug, Clone)]
pub enum Language {
PlainText,
}
#[cfg_attr(tarpaulin, skip)]
mod test_files {
#[derive(Debug, Clone)]
pub enum Language {
PlainText,
}
#[derive(Debug, Clone)]
pub enum TokenType {
Whitespace { token: Token },
Keyword { token: Token },
String { token: Token },
Number { token: Token },
Identifier { token: Token },
Literal { token: Token },
Comment { token: Token },
Operator { token: Token },
Separator { token: Token },
}
#[derive(Debug, Clone)]
pub enum TokenType {
Whitespace { token: Token },
Keyword { token: Token },
String { token: Token },
Number { token: Token },
Identifier { token: Token },
Literal { token: Token },
Comment { token: Token },
Operator { token: Token },
Separator { token: Token },
}
impl TokenType {
pub fn move_to(&self, line: usize, character: usize, start: usize, end: usize) -> Self {
match self {
TokenType::Whitespace { token } => TokenType::Whitespace {
token: token.move_to(line, character, start, end),
},
TokenType::Keyword { token } => TokenType::Keyword {
token: token.move_to(line, character, start, end),
},
TokenType::String { token } => TokenType::String {
token: token.move_to(line, character, start, end),
},
TokenType::Number { token } => TokenType::Number {
token: token.move_to(line, character, start, end),
},
TokenType::Identifier { token } => TokenType::Identifier {
token: token.move_to(line, character, start, end),
},
TokenType::Literal { token } => TokenType::Literal {
token: token.move_to(line, character, start, end),
},
TokenType::Comment { token } => TokenType::Comment {
token: token.move_to(line, character, start, end),
},
TokenType::Operator { token } => TokenType::Operator {
token: token.move_to(line, character, start, end),
},
TokenType::Separator { token } => TokenType::Separator {
token: token.move_to(line, character, start, end),
},
impl TokenType {
pub fn move_to(&self, line: usize, character: usize, start: usize, end: usize) -> Self {
match self {
TokenType::Whitespace { token } => TokenType::Whitespace {
token: token.move_to(line, character, start, end),
},
TokenType::Keyword { token } => TokenType::Keyword {
token: token.move_to(line, character, start, end),
},
TokenType::String { token } => TokenType::String {
token: token.move_to(line, character, start, end),
},
TokenType::Number { token } => TokenType::Number {
token: token.move_to(line, character, start, end),
},
TokenType::Identifier { token } => TokenType::Identifier {
token: token.move_to(line, character, start, end),
},
TokenType::Literal { token } => TokenType::Literal {
token: token.move_to(line, character, start, end),
},
TokenType::Comment { token } => TokenType::Comment {
token: token.move_to(line, character, start, end),
},
TokenType::Operator { token } => TokenType::Operator {
token: token.move_to(line, character, start, end),
},
TokenType::Separator { token } => TokenType::Separator {
token: token.move_to(line, character, start, end),
},
}
}
pub fn is_new_line(&self) -> bool {
match self {
TokenType::Whitespace { token } => token.text().as_str() == "\n",
_ => false,
}
}
pub fn is_space(&self) -> bool {
match self {
TokenType::Whitespace { token } => token.text().as_str() == " ",
_ => false,
}
}
}
pub fn is_new_line(&self) -> bool {
match self {
TokenType::Whitespace { token } => token.text().as_str() == "\n",
_ => false,
impl Deref for TokenType {
type Target = Token;
fn deref(&self) -> &<Self as Deref>::Target {
match self {
TokenType::Whitespace { token } => token,
TokenType::Keyword { token } => token,
TokenType::String { token } => token,
TokenType::Number { token } => token,
TokenType::Identifier { token } => token,
TokenType::Literal { token } => token,
TokenType::Comment { token } => token,
TokenType::Operator { token } => token,
TokenType::Separator { token } => token,
}
}
}
pub fn is_space(&self) -> bool {
match self {
TokenType::Whitespace { token } => token.text().as_str() == " ",
_ => false,
}
#[derive(Debug, Clone)]
pub struct Token {
line: usize,
character: usize,
start: usize,
end: usize,
text: String,
}
}
impl Deref for TokenType {
type Target = Token;
fn deref(&self) -> &<Self as Deref>::Target {
match self {
TokenType::Whitespace { token } => token,
TokenType::Keyword { token } => token,
TokenType::String { token } => token,
TokenType::Number { token } => token,
TokenType::Identifier { token } => token,
TokenType::Literal { token } => token,
TokenType::Comment { token } => token,
TokenType::Operator { token } => token,
TokenType::Separator { token } => token,
}
#[derive(Debug, Clone, Copy)]
pub struct Span {
pub lo: usize,
pub hi: usize,
}
}
#[derive(Debug, Clone)]
pub struct Token {
line: usize,
character: usize,
start: usize,
end: usize,
text: String,
}
impl Token {
pub fn new(text: String, line: usize, character: usize, start: usize, end: usize) -> Self {
Self {
text,
line,
character,
start,
end,
}
}
#[derive(Debug, Clone, Copy)]
pub struct Span {
pub lo: usize,
pub hi: usize,
}
pub fn text(&self) -> &String {
&self.text
}
impl Token {
pub fn new(text: String, line: usize, character: usize, start: usize, end: usize) -> Self {
Self {
text,
line,
character,
start,
end,
pub fn line(&self) -> usize {
self.line.clone()
}
pub fn character(&self) -> usize {
self.character.clone()
}
pub fn start(&self) -> usize {
self.start.clone()
}
pub fn end(&self) -> usize {
self.end.clone()
}
pub fn move_to(&self, line: usize, character: usize, start: usize, end: usize) -> Self {
Self {
text: self.text.clone(),
line,
character,
start,
end,
}
}
}
pub fn text(&self) -> &String {
&self.text
}
pub fn line(&self) -> usize {
self.line.clone()
}
pub fn character(&self) -> usize {
self.character.clone()
}
pub fn start(&self) -> usize {
self.start.clone()
}
pub fn end(&self) -> usize {
self.end.clone()
}
pub fn move_to(&self, line: usize, character: usize, start: usize, end: usize) -> Self {
Self {
text: self.text.clone(),
line,
character,
start,
end,
pub fn parse(text: String, language: Language) -> Vec<TokenType> {
match language {
Language::PlainText => plain::lexer::Lexer::new(text.as_str())
// .inspect(|tok| println!("tok: {:?}", tok))
.map(|t| t.0)
.collect(),
}
}
}
pub fn parse(text: String, language: Language) -> Vec<TokenType> {
match language {
Language::PlainText => plain::lexer::Lexer::new(text.as_str())
// .inspect(|tok| println!("tok: {:?}", tok))
.map(|t| t.0)
.collect(),
}
}