Base of server list
This commit is contained in:
parent
876e88ec95
commit
93edfa3828
103
src/format.rs
103
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<Vec<Component>>,
|
||||
pub bold: Option<bool>,
|
||||
|
@ -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),
|
||||
|
|
16
src/main.rs
16
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);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ const ATLAS_SIZE: usize = 1024;
|
|||
|
||||
pub struct Renderer {
|
||||
resource_version: usize,
|
||||
resources: Arc<RwLock<resources::Manager>>,
|
||||
pub resources: Arc<RwLock<resources::Manager>>,
|
||||
textures: Arc<RwLock<TextureManager>>,
|
||||
glsl: glsl::Registry,
|
||||
pub ui: ui::UIState,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Screen>,
|
||||
init: bool,
|
||||
active: bool,
|
||||
}
|
||||
|
||||
pub struct ScreenSystem {
|
||||
screens: Vec<ScreenInfo>,
|
||||
remove_queue: Vec<ScreenInfo>,
|
||||
}
|
||||
|
||||
impl ScreenSystem {
|
||||
pub fn new() -> ScreenSystem {
|
||||
ScreenSystem {
|
||||
screens: Vec::new(),
|
||||
remove_queue: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_screen(&mut self, screen: Box<Screen>) {
|
||||
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)
|
||||
}
|
|
@ -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<UIElements>,
|
||||
disconnect_reason: Option<format::Component>,
|
||||
}
|
||||
|
||||
struct UIElements {
|
||||
logo: ui::logo::Logo,
|
||||
elements: ui::Collection,
|
||||
servers: Vec<Server>,
|
||||
}
|
||||
|
||||
struct Server {
|
||||
collection: ui::Collection,
|
||||
back: ui::ElementRef<ui::Image>,
|
||||
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<format::Component>) -> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
|
||||
pub struct Formatted {
|
||||
dirty: bool,
|
||||
data: Vec<u8>,
|
||||
|
||||
parent: Option<ElementRefInner>,
|
||||
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<Element>,
|
||||
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<u8> {
|
||||
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<T: UIElement>(&mut self, other: &ElementRef<T>) {
|
||||
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<Element>,
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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<T> {
|
||||
inner: ElementRefInner,
|
||||
ty: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl <T> Clone for ElementRef<T> {
|
||||
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 <T> Default for ElementRef<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Allows for easy cleanup
|
||||
pub struct Collection {
|
||||
elements: Vec<ElementRefInner>,
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn new() -> Collection {
|
||||
Collection {
|
||||
elements: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add<T: UIElement>(&mut self, element: ElementRef<T>) {
|
||||
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<T: UIElement>(&mut self, r: &ElementRef<T>) {
|
||||
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");
|
Loading…
Reference in New Issue