From 93edfa3828aa0e66592fa183caf8017300fed5e7 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Wed, 23 Sep 2015 20:16:25 +0100 Subject: [PATCH] Base of server list --- src/format.rs | 103 ++++++++++-------- src/main.rs | 16 ++- src/render/mod.rs | 2 +- src/render/ui.rs | 14 +-- src/screen/mod.rs | 144 ++++++++++++++++++++++++ src/screen/server_list.rs | 224 ++++++++++++++++++++++++++++++++++++++ src/ui/formatted.rs | 220 +++++++++++++++++++++++++++++++++++++ src/ui/logo.rs | 2 +- src/ui/mod.rs | 51 ++++++++- 9 files changed, 705 insertions(+), 71 deletions(-) create mode 100644 src/screen/mod.rs create mode 100644 src/screen/server_list.rs create mode 100644 src/ui/formatted.rs diff --git a/src/format.rs b/src/format.rs index d68d7a6..6cbe786 100644 --- a/src/format.rs +++ b/src/format.rs @@ -15,26 +15,27 @@ use serde_json; use std::fmt; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Component { Text(TextComponent) } impl Component { - pub fn from_value(v: &serde_json::Value) -> Self { - let modifier = Modifier::from_value(v); - if let Some(val) = v.as_string() { - Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) - } else if v.find("text").is_some(){ - Component::Text(TextComponent::from_value(v, modifier)) - } else { - Component::Text(TextComponent{text: "".to_owned(), modifier: modifier}) - } + pub fn from_value(v: &serde_json::Value) -> Self { + let mut modifier = Modifier::from_value(v); + if let Some(val) = v.as_string() { + Component::Text(TextComponent{text: val.to_owned(), modifier: modifier}) + } else if v.find("text").is_some(){ + Component::Text(TextComponent::from_value(v, modifier)) + } else { + modifier.color = Some(Color::RGB(255, 0, 0)); + Component::Text(TextComponent{text: "UNHANDLED".to_owned(), modifier: modifier}) } + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() - } + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } impl fmt::Display for Component { @@ -51,7 +52,7 @@ impl Default for Component { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Modifier { pub extra: Option>, pub bold: Option, @@ -67,50 +68,59 @@ pub struct Modifier { } impl Modifier { - pub fn from_value(v: &serde_json::Value) -> Self { - let mut m = Modifier { - bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), - italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), - underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), - strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), - obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), - color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), - extra: Option::None, - }; - if let Some(extra) = v.find("extra") { - if let Some(data) = extra.as_array() { - let mut ex = Vec::new(); - for e in data { - ex.push(Component::from_value(e)); - } - m.extra = Some(ex); + pub fn from_value(v: &serde_json::Value) -> Self { + let mut m = Modifier { + bold: v.find("bold").map_or(Option::None, |v| v.as_boolean()), + italic: v.find("italic").map_or(Option::None, |v| v.as_boolean()), + underlined: v.find("underlined").map_or(Option::None, |v| v.as_boolean()), + strikethrough: v.find("strikethrough").map_or(Option::None, |v| v.as_boolean()), + obfuscated: v.find("obfuscated").map_or(Option::None, |v| v.as_boolean()), + color: v.find("color").map_or(Option::None, |v| v.as_string()).map(|v| Color::from_string(&v.to_owned())), + extra: Option::None, + }; + if let Some(extra) = v.find("extra") { + if let Some(data) = extra.as_array() { + let mut ex = Vec::new(); + for e in data { + ex.push(Component::from_value(e)); } + m.extra = Some(ex); } - m } + m + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() - } + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TextComponent { pub text: String, - pub modifier: Modifier, + pub modifier: Modifier, } impl TextComponent { - pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { - TextComponent { - text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), - modifier: modifier, + pub fn new(val: &str) -> TextComponent { + TextComponent { + text: val.to_owned(), + modifier: Modifier { + .. Default::default() } } + } - pub fn to_value(&self) -> serde_json::Value { - unimplemented!() + pub fn from_value(v: &serde_json::Value, modifier: Modifier) -> Self { + TextComponent { + text: v.find("text").unwrap().as_string().unwrap_or("").to_owned(), + modifier: modifier, } + } + + pub fn to_value(&self) -> serde_json::Value { + unimplemented!() + } } impl fmt::Display for TextComponent { @@ -125,7 +135,7 @@ impl fmt::Display for TextComponent { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum Color { Black, DarkBlue, @@ -194,7 +204,7 @@ impl Color { } } - fn to_string(&self) -> String { + pub fn to_string(&self) -> String { match *self { Color::Black => "black".to_owned(), Color::DarkBlue => "dark_blue".to_owned(), @@ -216,8 +226,7 @@ impl Color { } } - #[allow(dead_code)] - fn to_rgb(&self) -> (u8, u8, u8) { + pub fn to_rgb(&self) -> (u8, u8, u8) { match *self { Color::Black =>(0, 0, 0), Color::DarkBlue =>(0, 0, 170), diff --git a/src/main.rs b/src/main.rs index 3552d86..ae8ceb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ pub mod types; pub mod resources; pub mod render; pub mod ui; +pub mod screen; extern crate glfw; extern crate image; @@ -53,6 +54,7 @@ fn main() { gl::init(&mut window); window.set_key_polling(true); + window.set_scroll_polling(true); window.make_current(); glfw.set_swap_interval(1); @@ -62,7 +64,8 @@ fn main() { let mut last_frame = time::now(); let frame_time = (time::Duration::seconds(1).num_nanoseconds().unwrap() as f64) / 60.0; - let mut logo = ui::logo::Logo::new(resource_manager.clone(), &mut renderer, &mut ui_container); + let mut screen_sys = screen::ScreenSystem::new(); + screen_sys.add_screen(Box::new(screen::ServerList::new(None))); while !window.should_close() { { resource_manager.write().unwrap().tick(); } @@ -71,7 +74,7 @@ fn main() { last_frame = now; let delta = (diff.num_nanoseconds().unwrap() as f64) / frame_time; - logo.tick(&mut renderer, &mut ui_container); + screen_sys.tick(delta, &mut renderer, &mut ui_container); let (width, height) = window.get_framebuffer_size(); ui_container.tick(&mut renderer, delta, width as f64, height as f64); @@ -80,16 +83,19 @@ fn main() { window.swap_buffers(); glfw.poll_events(); for (_, event) in glfw::flush_messages(&events) { - handle_window_event(&mut window, event); + handle_window_event(&mut window, &mut screen_sys, event); } } } -fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) { +fn handle_window_event(window: &mut glfw::Window, screen_sys: &mut screen::ScreenSystem, event: glfw::WindowEvent) { match event { glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => { window.set_should_close(true) - } + }, + glfw::WindowEvent::Scroll(x, y) => { + screen_sys.on_scroll(x, y); + }, _ => {} } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 281c1d4..bbab203 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -30,7 +30,7 @@ const ATLAS_SIZE: usize = 1024; pub struct Renderer { resource_version: usize, - resources: Arc>, + pub resources: Arc>, textures: Arc>, glsl: glsl::Registry, pub ui: ui::UIState, diff --git a/src/render/ui.rs b/src/render/ui.rs index e81923a..bdbbc47 100644 --- a/src/render/ui.rs +++ b/src/render/ui.rs @@ -312,19 +312,7 @@ impl UIState { continue; } let texture = self.character_texture(ch); - let mut raw = ch as u32; - let page = raw >> 8; - - if page == 0 { - raw = (*self.char_map.get(&ch).unwrap_or(&ch)) as u32; - } - let info = self.font_character_info[raw as usize]; - let w = if page == 0 { - let sw = self.page_width / 16.0; - ((info.1 - info.0) as f64 / sw) * 16.0 - } else { - (info.1 - info.0) as f64 - }; + let w = self.size_of_char(ch); let mut dsx = offset + 2.0; let mut dsy = 2.0; diff --git a/src/screen/mod.rs b/src/screen/mod.rs new file mode 100644 index 0000000..9450630 --- /dev/null +++ b/src/screen/mod.rs @@ -0,0 +1,144 @@ + +mod server_list; +pub use self::server_list::*; + +use render; +use ui; + +pub trait Screen { + // Called once + fn init(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container); + fn deinit(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container); + + // May be called multiple times + fn on_active(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container); + fn on_deactive(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container); + + // Called every frame the screen is active + fn tick(&mut self, delta: f64, renderer: &mut render::Renderer, ui_container: &mut ui::Container); + + // Events + fn on_scroll(&mut self, x: f64, y: f64) {} +} + +struct ScreenInfo { + screen: Box, + init: bool, + active: bool, +} + +pub struct ScreenSystem { + screens: Vec, + remove_queue: Vec, +} + +impl ScreenSystem { + pub fn new() -> ScreenSystem { + ScreenSystem { + screens: Vec::new(), + remove_queue: Vec::new(), + } + } + + pub fn add_screen(&mut self, screen: Box) { + self.screens.push(ScreenInfo { + screen: screen, + init: false, + active: false, + }); + } + + pub fn pop_screen(&mut self) { + if let Some(screen) = self.screens.pop() { + self.remove_queue.push(screen); + } + } + + pub fn tick(&mut self, delta: f64, renderer: &mut render::Renderer, ui_container: &mut ui::Container) { + for screen in &mut self.remove_queue { + if screen.active { + screen.screen.on_deactive(renderer, ui_container); + } + if screen.init { + screen.screen.deinit(renderer, ui_container); + } + } + self.remove_queue.clear(); + if self.screens.is_empty() { + return; + } + // Update state for screens + let len = self.screens.len(); + for screen in &mut self.screens[..len - 1] { + if screen.active { + screen.active = false; + screen.screen.on_deactive(renderer, ui_container); + } + } + let current = self.screens.last_mut().unwrap(); + if !current.init { + current.init = true; + current.screen.init(renderer, ui_container); + } + if !current.active { + current.active = true; + current.screen.on_active(renderer, ui_container); + } + + // Handle current + current.screen.tick(delta, renderer, ui_container); + } + + pub fn on_scroll(&mut self, x: f64, y: f64) { + if self.screens.is_empty() { + return; + } + let current = self.screens.last_mut().unwrap(); + current.screen.on_scroll(x, y); + } +} + +pub fn new_button(renderer: &mut render::Renderer, x: f64, y: f64, w: f64, h: f64) -> ui::Batch { + let mut batch = ui::Batch::new(x, y, w, h); + + let texture = render::Renderer::get_texture(renderer.get_textures_ref(), "gui/widgets").relative( + 0.0, 66.0 / 256.0, 200.0 / 256.0, 20.0 / 256.0 + ); + + // Corners + batch.add(ui::Image::new(texture.clone(), 0.0, 0.0, 4.0, 4.0, 0.0, 0.0, 2.0 / 200.0, 2.0 / 20.0, 255, 255, 255)); + batch.add(ui::Image::new(texture.clone(), w - 4.0, 0.0, 4.0, 4.0, 198.0 / 200.0, 0.0, 2.0 / 200.0, 2.0 / 20.0, 255, 255, 255)); + batch.add(ui::Image::new(texture.clone(), 0.0, h - 6.0, 4.0, 6.0, 0.0, 17.0 / 20.0, 2.0 / 200.0, 3.0 / 20.0, 255, 255, 255)); + batch.add(ui::Image::new(texture.clone(), w - 4.0, h - 6.0, 4.0, 6.0, 198.0 / 200.0, 17.0 / 20.0, 2.0 / 200.0, 3.0 / 20.0, 255, 255, 255)); + + // Widths + batch.add(ui::Image::new(texture.clone().relative( + 2.0 / 200.0, 0.0, 196.0 / 200.0, 2.0 / 20.0 + ), 4.0, 0.0, w - 8.0, 4.0, 0.0, 0.0, 1.0, 1.0, 255, 255, 255)); + batch.add(ui::Image::new(texture.clone().relative( + 2.0 / 200.0, 17.0 / 20.0, 196.0 / 200.0, 3.0 / 20.0 + ), 4.0, h - 6.0, w - 8.0, 6.0, 0.0, 0.0, 1.0, 1.0, 255, 255, 255)); + + // Heights + batch.add(ui::Image::new(texture.clone().relative( + 0.0, 2.0 / 20.0, 2.0 / 200.0, 15.0 / 20.0 + ), 0.0, 4.0, 4.0, h - 10.0, 0.0, 0.0, 1.0, 1.0, 255, 255, 255)); + batch.add(ui::Image::new(texture.clone().relative( + 198.0 / 200.0, 2.0 / 20.0, 2.0 / 200.0, 15.0 / 20.0 + ), w - 4.0, 4.0, 4.0, h - 10.0, 0.0, 0.0, 1.0, 1.0, 255, 255, 255)); + + // Center + batch.add(ui::Image::new(texture.clone().relative( + 2.0 / 200.0, 2.0 / 20.0, 196.0 / 200.0, 15.0 / 20.0 + ), 4.0, 4.0, w - 8.0, h - 10.0, 0.0, 0.0, 1.0, 1.0, 255, 255, 255)); + + batch +} + +pub fn new_button_text(renderer: &mut render::Renderer, val: &str, x: f64, y: f64, w: f64, h: f64) -> (ui::Batch, ui::Text) { + let batch = new_button(renderer, x, y, w, h); + let mut text = ui::Text::new(renderer, val, 0.0, 0.0, 255, 255, 255); + text.set_v_attach(ui::VAttach::Middle); + text.set_h_attach(ui::HAttach::Center); + (batch, text) +} \ No newline at end of file diff --git a/src/screen/server_list.rs b/src/screen/server_list.rs new file mode 100644 index 0000000..ea8cc45 --- /dev/null +++ b/src/screen/server_list.rs @@ -0,0 +1,224 @@ + +use std::fs; +use ui; +use render; +use format; +use serde_json; +use std::cmp::max; + +pub struct ServerList { + elements: Option, + disconnect_reason: Option, +} + +struct UIElements { + logo: ui::logo::Logo, + elements: ui::Collection, + servers: Vec, +} + +struct Server { + collection: ui::Collection, + back: ui::ElementRef, + offset: f64, + y: f64, +} + +impl Server { + fn update_position(&mut self) { + if self.offset < 0.0 { + self.y = self.offset * 200.0; + } else { + self.y = self.offset * 100.0; + } + } +} + +impl ServerList { + pub fn new(disconnect_reason: Option) -> ServerList { + ServerList { + elements: None, + disconnect_reason: disconnect_reason, + } + } + + fn reload_server_list(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) { + let elements = self.elements.as_mut().unwrap(); + for server in &mut elements.servers { + server.collection.remove_all(ui_container); + } + elements.servers.clear(); + + let file = match fs::File::open("servers.json") { + Ok(val) => val, + Err(e) => return, + }; + let servers_info: serde_json::Value = serde_json::from_reader(file).unwrap(); + let servers = servers_info.find("servers").unwrap().as_array().unwrap(); + let mut offset = 0.0; + + let default_icon = render::Renderer::get_texture(renderer.get_textures_ref(), "misc/unknown_server"); + let icons = render::Renderer::get_texture(renderer.get_textures_ref(), "gui/icons"); + + for svr in servers { + let name = svr.find("name").unwrap().as_string().unwrap(); + let address = svr.find("address").unwrap().as_string().unwrap(); + + let solid = render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid"); + + let mut back = ui::Image::new(solid, 0.0, offset * 100.0, 700.0, 100.0, 0.0, 0.0, 1.0, 1.0, 0, 0, 0); + back.set_a(100); + back.set_v_attach(ui::VAttach::Middle); + back.set_h_attach(ui::HAttach::Center); + + let mut server = Server { + collection: ui::Collection::new(), + back: ui_container.add(back), + offset: offset, + y: 0.0, + }; + server.collection.add(server.back.clone()); + server.update_position(); + + let mut text = ui::Text::new(renderer, &name, 100.0, 5.0, 255, 255, 255); + text.set_parent(&server.back); + server.collection.add(ui_container.add(text)); + + let mut icon = ui::Image::new(default_icon.clone(), 5.0, 5.0, 90.0, 90.0, 0.0, 0.0, 1.0, 1.0, 255, 255, 255); + icon.set_parent(&server.back); + server.collection.add(ui_container.add(icon)); + + let mut ping = ui::Image::new(icons.clone(), 5.0, 5.0, 20.0, 16.0, 0.0, 56.0/256.0, 10.0/256.0, 8.0/256.0, 255, 255, 255); + ping.set_h_attach(ui::HAttach::Right); + ping.set_parent(&server.back); + server.collection.add(ui_container.add(ping)); + + let mut players = ui::Text::new(renderer, "???", 30.0, 5.0, 255, 255, 255); + players.set_h_attach(ui::HAttach::Right); + players.set_parent(&server.back); + server.collection.add(ui_container.add(players)); + + elements.servers.push(server); + offset += 1.0; + } + } +} + +impl super::Screen for ServerList { + fn init(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) {} + fn deinit(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) {} + + fn on_active(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) { + let logo = ui::logo::Logo::new(renderer.resources.clone(), renderer, ui_container); + let mut elements = ui::Collection::new(); + + let (mut refresh, mut txt) = super::new_button_text(renderer, "Refresh", 300.0, -50.0-15.0, 100.0, 30.0); + refresh.set_v_attach(ui::VAttach::Middle); + refresh.set_h_attach(ui::HAttach::Center); + let re = ui_container.add(refresh); + txt.set_parent(&re); + elements.add(re); + elements.add(ui_container.add(txt)); + + let (mut add, mut txt) = super::new_button_text(renderer, "Add", 200.0, -50.0-15.0, 100.0, 30.0); + add.set_v_attach(ui::VAttach::Middle); + add.set_h_attach(ui::HAttach::Center); + let re = ui_container.add(add); + txt.set_parent(&re); + elements.add(re); + elements.add(ui_container.add(txt)); + + let mut options = super::new_button(renderer, 5.0, 25.0, 40.0, 40.0); + options.set_v_attach(ui::VAttach::Bottom); + options.set_h_attach(ui::HAttach::Right); + let re = ui_container.add(options); + let mut cog = ui::Image::new(render::Renderer::get_texture(renderer.get_textures_ref(), "steven:gui/cog"), 0.0, 0.0, 40.0, 40.0, 0.0, 0.0, 1.0, 1.0, 255, 255, 255); + cog.set_parent(&re); + cog.set_v_attach(ui::VAttach::Middle); + cog.set_h_attach(ui::HAttach::Center); + elements.add(re); + elements.add(ui_container.add(cog)); + + let mut warn = ui::Text::new(renderer, "Not affiliated with Mojang/Minecraft", 5.0, 5.0, 255, 200, 200); + warn.set_v_attach(ui::VAttach::Bottom); + warn.set_h_attach(ui::HAttach::Right); + elements.add(ui_container.add(warn)); + + if let Some(ref disconnect_reason) = self.disconnect_reason { + let mut dis_msg = ui::Text::new(renderer, "Disconnected", 0.0, 32.0, 255, 0, 0); + dis_msg.set_h_attach(ui::HAttach::Center); + let mut dis = ui::Formatted::with_width_limit(renderer, disconnect_reason.clone(), 0.0, 48.0, 600.0); + dis.set_h_attach(ui::HAttach::Center); + let mut back = ui::Image::new( + render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid"), + 0.0, 30.0, + dis.get_width().max(dis_msg.get_width()) + 4.0, dis.get_height() + 4.0 + 16.0, + 0.0, 0.0, 1.0, 1.0, + 0, 0, 0 + ); + back.set_a(100); + back.set_h_attach(ui::HAttach::Center); + elements.add(ui_container.add(back)); + elements.add(ui_container.add(dis)); + elements.add(ui_container.add(dis_msg)); + } + + self.elements = Some(UIElements { + logo: logo, + elements: elements, + servers: Vec::new(), + }); + self.reload_server_list(renderer, ui_container); + } + fn on_deactive(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) { + { + let elements = self.elements.as_mut().unwrap(); + elements.logo.remove(ui_container); + elements.elements.remove_all(ui_container); + for server in &mut elements.servers { + server.collection.remove_all(ui_container); + } + elements.servers.clear(); + } + self.elements = None + } + + fn tick(&mut self, delta: f64, renderer: &mut render::Renderer, ui_container: &mut ui::Container) { + let elements = self.elements.as_mut().unwrap(); + elements.logo.tick(renderer, ui_container); + + for s in &mut elements.servers { + let back = ui_container.get_mut(&s.back); + let dy = s.y - back.get_y(); + if dy*dy > 1.0 { + let y = back.get_y(); + back.set_y(y + delta * dy * 0.1); + } else { + back.set_y(s.y); + } + } + } + + fn on_scroll(&mut self, x: f64, y: f64) { + let elements = self.elements.as_mut().unwrap(); + if elements.servers.is_empty() { + return; + } + let mut diff = y / 1.0; + { + let last = elements.servers.last().unwrap(); + if last.offset+diff <= 2.0 { + diff = 2.0 - last.offset; + } + let first = elements.servers.first().unwrap(); + if first.offset + diff >= 0.0 { + diff = -first.offset; + } + } + + for s in &mut elements.servers { + s.offset += diff; + s.update_position(); + } + } +} \ No newline at end of file diff --git a/src/ui/formatted.rs b/src/ui/formatted.rs new file mode 100644 index 0000000..474659e --- /dev/null +++ b/src/ui/formatted.rs @@ -0,0 +1,220 @@ + +pub struct Formatted { + dirty: bool, + data: Vec, + + parent: Option, + should_draw: bool, + layer: isize, + val: format::Component, + x: f64, + y: f64, + width: f64, + height: f64, + v_attach: VAttach, + h_attach: HAttach, + scale_x: f64, + scale_y: f64, + + text: Vec, + max_width: f64, + lines: usize, +} + +impl Formatted { + pub fn new(renderer: &mut render::Renderer, val: format::Component, x: f64, y: f64) -> Formatted { + let mut f = Formatted { + dirty: true, + data: Vec::new(), + + parent: None, + should_draw: true, + layer: 0, + val: val, + x: x, + y: y, + width: 0.0, + height: 18.0, + v_attach: VAttach::Top, + h_attach: HAttach::Left, + scale_x: 1.0, + scale_y: 1.0, + + text: Vec::new(), + max_width: -1.0, + lines: 0, + }; + f.set_component(renderer); + f + } + + pub fn with_width_limit(renderer: &mut render::Renderer, val: format::Component, x: f64, y: f64, max_width: f64) -> Formatted { + let mut f = Formatted { + dirty: true, + data: Vec::new(), + + parent: None, + should_draw: true, + layer: 0, + val: val, + x: x, + y: y, + width: 0.0, + height: 18.0, + v_attach: VAttach::Top, + h_attach: HAttach::Left, + scale_x: 1.0, + scale_y: 1.0, + + text: Vec::new(), + max_width: max_width, + lines: 0, + }; + f.set_component(renderer); + f + } + + fn set_component(&mut self, renderer: &mut render::Renderer) { + self.text.clear(); + let mut state = FormatState { + lines: 0, + width: 0.0, + offset: 0.0, + text: Vec::new(), + max_width: self.max_width, + renderer: &renderer, + }; + state.build(&self.val, format::Color::White); + self.height = (state.lines + 1) as f64 * 18.0; + self.width = state.width; + self.lines = state.lines; + self.text = state.text; + self.dirty = true; + } + + fn update(&mut self, renderer: &mut render::Renderer) { + self.set_component(renderer); + } + + fn draw(&mut self, renderer: &mut render::Renderer, r: &Region, width: f64, height: f64, delta: f64) -> &Vec { + if self.dirty { + self.dirty = false; + self.data.clear(); + let sx = r.w / self.width; + let sy = r.h / self.height; + + for e in &mut self.text { + let reg = Container::get_draw_region_raw(e, sx, sy, r); + e.set_dirty(true); + self.data.extend(e.draw(renderer, ®, width, height, delta)); + } + } + &self.data + } + + pub fn get_size(&self) -> (f64, f64) { + ((self.width + 2.0) * self.scale_x, self.height * self.scale_y) + } + + pub fn set_parent(&mut self, other: &ElementRef) { + self.parent = Some(other.inner); + self.dirty = true; + } + + lazy_field!(layer, isize, get_layer, set_layer); + lazy_field!(x, f64, get_x, set_x); + lazy_field!(y, f64, get_y, set_y); + lazy_field!(width, f64, get_width, set_width); + lazy_field!(height, f64, get_height, set_height); + lazy_field!(v_attach, VAttach, get_v_attach, set_v_attach); + lazy_field!(h_attach, HAttach, get_h_attach, set_h_attach); + lazy_field!(scale_x, f64, get_scale_x, set_scale_x); + lazy_field!(scale_y, f64, get_scale_y, set_scale_y); + +} + +impl UIElement for Formatted { + fn wrap(self) -> Element { + Element::Formatted(self) + } + + fn unwrap_ref<'a>(e: &'a Element) -> &'a Formatted { + match e { + &Element::Formatted(ref val) => val, + _ => panic!("Incorrect type"), + } + } + + fn unwrap_ref_mut<'a>(e: &'a mut Element) -> &'a mut Formatted { + match e { + &mut Element::Formatted(ref mut val) => val, + _ => panic!("Incorrect type"), + } + } +} + +struct FormatState<'a> { + max_width: f64, + lines: usize, + offset: f64, + width: f64, + text: Vec, + renderer: &'a render::Renderer, +} + +type GetColorFn = Fn() -> format::Color; + +impl <'a> FormatState<'a> { + fn build(&mut self, c: &format::Component, color: format::Color) { + match c { + &format::Component::Text(ref txt) => { + let col = FormatState::get_color(&txt.modifier, color); + self.append_text(&txt.text, col); + let modi = &txt.modifier; + if let Some(ref extra) = modi.extra { + for e in extra { + self.build(e, col); + } + } + }, + } + } + + fn append_text(&mut self, txt: &str, color: format::Color) { + let mut width = 0.0; + let mut last = 0; + for (i, c) in txt.chars().enumerate() { + let size = self.renderer.ui.size_of_char(c) + 2.0; + if (self.max_width > 0.0 && self.offset + width + size > self.max_width) || c == '\n' { + let (rr, gg, bb) = color.to_rgb(); + let text = Text::new(self.renderer, &txt[last .. i], self.offset, (self.lines*18 + 1) as f64, rr, gg, bb); + self.text.push(text.wrap()); + last = i; + if c == '\n' { + last += 1; + } + self.offset = 0.0; + self.lines += 1; + width = 0.0; + } + width += size; + if self.offset + width > self.width { + self.width = self.offset + width; + } + } + + if last != txt.len() { + let (rr, gg, bb) = color.to_rgb(); + let text = Text::new(self.renderer, &txt[last..], self.offset, (self.lines*18 + 1) as f64, rr, gg, bb); + self.offset += text.width + 4.0; // TODO Why is this 4 not 2? + self.text.push(text.wrap()); + if self.offset > self.width { + self.width = self.offset; + } + } + } + + fn get_color(modi: &format::Modifier, color: format::Color) -> format::Color { + modi.color.unwrap_or(color) + } +} \ No newline at end of file diff --git a/src/ui/logo.rs b/src/ui/logo.rs index 23bb41b..83f3cdb 100644 --- a/src/ui/logo.rs +++ b/src/ui/logo.rs @@ -162,7 +162,7 @@ impl Logo { text.set_x(self.text_orig_x * scale * self.text_base_scale); } - pub fn remove(self, ui_container: &mut ui::Container) { + pub fn remove(&self, ui_container: &mut ui::Container) { ui_container.remove(&self.shadow); ui_container.remove(&self.layer0); ui_container.remove(&self.text); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 3984b56..7cf33db 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -18,6 +18,7 @@ use std::collections::HashMap; use std::marker::PhantomData; use rand; use render; +use format; const SCALED_WIDTH: f64 = 854.0; const SCALED_HEIGHT: f64 = 480.0; @@ -26,6 +27,7 @@ pub enum Element { Image(Image), Batch(Batch), Text(Text), + Formatted(Formatted), None, } @@ -119,7 +121,8 @@ impl Element { element_impl!( Image, Batch, - Text + Text, + Formatted ); pub enum Mode { @@ -160,12 +163,21 @@ impl Region { /// Reference to an element currently attached to a /// container. -#[derive(Clone, Copy)] +#[derive(Copy)] pub struct ElementRef { inner: ElementRefInner, ty: PhantomData, } +impl Clone for ElementRef { + fn clone(&self) -> Self { + ElementRef { + inner: self.inner, + ty: PhantomData, + } + } +} + #[derive(Hash, PartialEq, Eq, Clone, Copy)] struct ElementRefInner { index: usize, @@ -180,6 +192,29 @@ impl Default for ElementRef { } } +/// Allows for easy cleanup +pub struct Collection { + elements: Vec, +} + +impl Collection { + pub fn new() -> Collection { + Collection { + elements: Vec::new(), + } + } + + pub fn add(&mut self, element: ElementRef) { + self.elements.push(element.inner); + } + + pub fn remove_all(&mut self, container: &mut Container) { + for e in &self.elements { + container.remove_raw(e); + } + } +} + const SCREEN: Region = Region{x: 0.0, y: 0.0, w: SCALED_WIDTH, h: SCALED_HEIGHT}; pub struct Container { @@ -228,9 +263,13 @@ impl Container { } pub fn remove(&mut self, r: &ElementRef) { - self.elements.remove(&r.inner); + self.remove_raw(&r.inner); + } + + fn remove_raw(&mut self, r: &ElementRefInner) { + self.elements.remove(&r); self.elements_list.iter() - .position(|&e| e.index == r.inner.index) + .position(|&e| e.index == r.index) .map(|e| self.elements_list.remove(e)) .unwrap(); } @@ -477,6 +516,7 @@ impl UIElement for Image { } } +// TODO Getting values out? pub struct Batch { dirty: bool, @@ -711,3 +751,6 @@ impl UIElement for Text { } } } + +// Include instead of mod so we can access private parts +include!("formatted.rs"); \ No newline at end of file