Kinda functional server list
This commit is contained in:
parent
93edfa3828
commit
3bcfc6aa4c
|
@ -5,7 +5,6 @@ authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>" ]
|
|||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
rustc-serialize = "0.3"
|
||||
glfw = "0.1.0"
|
||||
byteorder = "0.3.13"
|
||||
hyper = "0.6.13"
|
||||
|
@ -16,6 +15,7 @@ zip = "0.1.12"
|
|||
image = "0.3.12"
|
||||
time = "0.1.32"
|
||||
rand = "0.3.11"
|
||||
rustc-serialize = "0.3"
|
||||
|
||||
[dependencies.steven_gl]
|
||||
path = "./gl"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
use serde_json;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Component {
|
||||
|
@ -272,3 +273,85 @@ fn test_color_from() {
|
|||
_ => panic!("Wrong type"),
|
||||
}
|
||||
}
|
||||
|
||||
const LEGACY_CHAR: char = '§';
|
||||
|
||||
pub fn convert_legacy(c: &mut Component) {
|
||||
match c {
|
||||
&mut Component::Text(ref mut txt) => {
|
||||
if let Some(ref mut extra) = txt.modifier.extra.as_mut() {
|
||||
for e in extra.iter_mut() {
|
||||
convert_legacy(e);
|
||||
}
|
||||
}
|
||||
if txt.text.contains(LEGACY_CHAR) {
|
||||
let mut parts = Vec::new();
|
||||
let mut last = 0;
|
||||
let mut current = TextComponent::new("");
|
||||
{
|
||||
let mut iter = txt.text.char_indices();
|
||||
while let Some((i, c)) = iter.next() {
|
||||
if c == LEGACY_CHAR {
|
||||
let next = match iter.next() {
|
||||
Some(val) => val,
|
||||
None => break,
|
||||
};
|
||||
let color_char = next.1.to_lowercase().next().unwrap();
|
||||
current.text = txt.text[last .. i].to_owned();
|
||||
last = next.0 + 1;
|
||||
|
||||
let mut modifier = if (color_char >= 'a' && color_char <= 'f') || (color_char >= '0' && color_char <= '9') {
|
||||
Default::default()
|
||||
} else {
|
||||
current.modifier.clone()
|
||||
};
|
||||
|
||||
let new = TextComponent::new("");
|
||||
parts.push(Component::Text(mem::replace(&mut current, new)));
|
||||
|
||||
match color_char {
|
||||
'0' => modifier.color = Some(Color::Black),
|
||||
'1' => modifier.color = Some(Color::DarkBlue),
|
||||
'2' => modifier.color = Some(Color::DarkGreen),
|
||||
'3' => modifier.color = Some(Color::DarkAqua),
|
||||
'4' => modifier.color = Some(Color::DarkRed),
|
||||
'5' => modifier.color = Some(Color::DarkPurple),
|
||||
'6' => modifier.color = Some(Color::Gold),
|
||||
'7' => modifier.color = Some(Color::Gray),
|
||||
'8' => modifier.color = Some(Color::DarkGray),
|
||||
'9' => modifier.color = Some(Color::Blue),
|
||||
'a' => modifier.color = Some(Color::Green),
|
||||
'b' => modifier.color = Some(Color::Aqua),
|
||||
'c' => modifier.color = Some(Color::Red),
|
||||
'd' => modifier.color = Some(Color::LightPurple),
|
||||
'e' => modifier.color = Some(Color::Yellow),
|
||||
'f' => modifier.color = Some(Color::White),
|
||||
'k' => modifier.obfuscated = Some(true),
|
||||
'l' => modifier.bold = Some(true),
|
||||
'm' => modifier.strikethrough = Some(true),
|
||||
'n' => modifier.underlined = Some(true),
|
||||
'o' => modifier.italic = Some(true),
|
||||
'r' => {},
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
||||
current.modifier = modifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
if last < txt.text.len() {
|
||||
current.text = txt.text[last..].to_owned();
|
||||
parts.push(Component::Text(current));
|
||||
}
|
||||
|
||||
let old = mem::replace(&mut txt.modifier.extra, Some(parts));
|
||||
if let Some(old_extra) = old {
|
||||
if let Some(ref mut extra) = txt.modifier.extra.as_mut() {
|
||||
extra.extend(old_extra);
|
||||
}
|
||||
}
|
||||
txt.text = "".to_owned();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
24
src/main.rs
24
src/main.rs
|
@ -32,6 +32,7 @@ extern crate steven_openssl as openssl;
|
|||
extern crate hyper;
|
||||
extern crate flate2;
|
||||
extern crate rand;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
use glfw::{Action, Context, Key};
|
||||
|
@ -55,6 +56,8 @@ fn main() {
|
|||
|
||||
window.set_key_polling(true);
|
||||
window.set_scroll_polling(true);
|
||||
window.set_mouse_button_polling(true);
|
||||
window.set_cursor_pos_polling(true);
|
||||
window.make_current();
|
||||
glfw.set_swap_interval(1);
|
||||
|
||||
|
@ -83,12 +86,18 @@ fn main() {
|
|||
window.swap_buffers();
|
||||
glfw.poll_events();
|
||||
for (_, event) in glfw::flush_messages(&events) {
|
||||
handle_window_event(&mut window, &mut screen_sys, event);
|
||||
handle_window_event(&mut window, &mut renderer, &mut screen_sys, &mut ui_container, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_window_event(window: &mut glfw::Window, screen_sys: &mut screen::ScreenSystem, event: glfw::WindowEvent) {
|
||||
fn handle_window_event(
|
||||
window: &mut glfw::Window,
|
||||
renderer: &mut render::Renderer,
|
||||
screen_sys: &mut screen::ScreenSystem,
|
||||
ui_container: &mut ui::Container,
|
||||
event: glfw::WindowEvent
|
||||
) {
|
||||
match event {
|
||||
glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
|
||||
window.set_should_close(true)
|
||||
|
@ -96,6 +105,17 @@ fn handle_window_event(window: &mut glfw::Window, screen_sys: &mut screen::Scree
|
|||
glfw::WindowEvent::Scroll(x, y) => {
|
||||
screen_sys.on_scroll(x, y);
|
||||
},
|
||||
glfw::WindowEvent::MouseButton(glfw::MouseButton::Button1, Action::Press, _) => {
|
||||
let (width, height) = window.get_size();
|
||||
let (xpos, ypos) = window.get_cursor_pos();
|
||||
let (fw, fh) = window.get_framebuffer_size();
|
||||
ui_container.click_at(renderer, xpos*((fw as f64)/(width as f64)), ypos*((fh as f64)/(height as f64)), fw as f64, fh as f64)
|
||||
},
|
||||
glfw::WindowEvent::CursorPos(xpos, ypos) => {
|
||||
let (width, height) = window.get_size();
|
||||
let (fw, fh) = window.get_framebuffer_size();
|
||||
ui_container.hover_at(renderer, xpos*((fw as f64)/(width as f64)), ypos*((fh as f64)/(height as f64)), fw as f64, fh as f64)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ use std::convert;
|
|||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
||||
use flate2::read::{ZlibDecoder, ZlibEncoder};
|
||||
use flate2;
|
||||
use time;
|
||||
|
||||
pub const SUPPORTED_PROTOCOL: i32 = 73;
|
||||
|
||||
/// Helper macro for defining packets
|
||||
#[macro_export]
|
||||
|
@ -130,7 +133,7 @@ macro_rules! state_packets {
|
|||
|
||||
pub mod packet;
|
||||
|
||||
pub trait Serializable {
|
||||
pub trait Serializable: Sized {
|
||||
fn read_from(buf: &mut io::Read) -> Result<Self, io::Error>;
|
||||
fn write_to(&self, buf: &mut io::Write) -> Result<(), io::Error>;
|
||||
}
|
||||
|
@ -651,11 +654,14 @@ pub struct Conn {
|
|||
impl Conn {
|
||||
pub fn new(target: &str) -> Result<Conn, Error>{
|
||||
// TODO SRV record support
|
||||
let stream = match TcpStream::connect(target) {
|
||||
Ok(val) => val,
|
||||
Err(err) => return Result::Err(Error::IOError(err))
|
||||
let mut parts = target.split(":").collect::<Vec<&str>>();
|
||||
let address = if parts.len() == 1 {
|
||||
parts.push("25565");
|
||||
format!("{}:25565", parts[0])
|
||||
} else {
|
||||
format!("{}:{}", parts[0], parts[1])
|
||||
};
|
||||
let parts = target.split(":").collect::<Vec<&str>>();
|
||||
let stream = try!(TcpStream::connect(&*address));
|
||||
Result::Ok(Conn {
|
||||
stream: stream,
|
||||
host: parts[0].to_owned(),
|
||||
|
@ -748,6 +754,91 @@ impl Conn {
|
|||
self.compression_read = Some(ZlibDecoder::new(io::Cursor::new(Vec::new())));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_status(mut self) -> Result<(Status, time::Duration), Error>{
|
||||
use serde_json::Value;
|
||||
use self::packet::status::serverbound::*;
|
||||
use self::packet::handshake::serverbound::*;
|
||||
use self::packet::Packet;
|
||||
let host = self.host.clone();
|
||||
let port = self.port;
|
||||
try!(self.write_packet(Handshake{
|
||||
protocol_version: VarInt(SUPPORTED_PROTOCOL),
|
||||
host: host,
|
||||
port: port,
|
||||
next: VarInt(1),
|
||||
}));
|
||||
self.state = State::Status;
|
||||
|
||||
try!(self.write_packet(StatusRequest{empty: ()}));
|
||||
|
||||
let status = if let Packet::StatusResponse(res) = try!(self.read_packet()) {
|
||||
res.status
|
||||
} else {
|
||||
return Err(Error::Err("Wrong packet".to_owned()));
|
||||
};
|
||||
|
||||
let start = time::now();
|
||||
try!(self.write_packet(StatusPing{ping: 42}));
|
||||
|
||||
if let Packet::StatusPong(_) = try!(self.read_packet()) {
|
||||
} else {
|
||||
return Err(Error::Err("Wrong packet".to_owned()));
|
||||
};
|
||||
|
||||
let ping = time::now() - start;
|
||||
|
||||
let val: Value = match serde_json::from_str(&status) {
|
||||
Ok(val) => val,
|
||||
Err(_) => return Err(Error::Err("Json parse error".to_owned())),
|
||||
};
|
||||
|
||||
let invalid_status = || Error::Err("Invalid status".to_owned());
|
||||
|
||||
let version = try!(val.find("version").ok_or(invalid_status()));
|
||||
let players = try!(val.find("players").ok_or(invalid_status()));
|
||||
|
||||
Ok((Status {
|
||||
version: StatusVersion {
|
||||
name: try!(version.find("name").and_then(Value::as_string).ok_or(invalid_status())).to_owned(),
|
||||
protocol: try!(version.find("protocol").and_then(Value::as_i64).ok_or(invalid_status())) as i32,
|
||||
},
|
||||
players: StatusPlayers {
|
||||
max: try!(players.find("max").and_then(Value::as_i64).ok_or(invalid_status())) as i32,
|
||||
online: try!(players.find("online").and_then(Value::as_i64).ok_or(invalid_status())) as i32,
|
||||
sample: Vec::new(), // TODO
|
||||
},
|
||||
description: format::Component::from_value(try!(val.find("description").ok_or(invalid_status()))),
|
||||
favicon: val.find("favicon").and_then(Value::as_string).map(|v| v.to_owned()),
|
||||
}, ping))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Status {
|
||||
pub version: StatusVersion,
|
||||
pub players: StatusPlayers,
|
||||
pub description: format::Component,
|
||||
pub favicon: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StatusVersion {
|
||||
pub name: String,
|
||||
pub protocol: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StatusPlayers {
|
||||
pub max: i32,
|
||||
pub online: i32,
|
||||
pub sample: Vec<StatusPlayer>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StatusPlayer {
|
||||
name: String,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl Read for Conn {
|
||||
|
@ -805,6 +896,7 @@ pub trait PacketType {
|
|||
fn write(self, buf: &mut io::Write) -> Result<(), io::Error>;
|
||||
}
|
||||
|
||||
// REMOVE ME
|
||||
// #[test]
|
||||
pub fn test() {
|
||||
let mut c = Conn::new("localhost:25565").unwrap();
|
||||
|
|
|
@ -193,6 +193,9 @@ pub struct TextureManager {
|
|||
|
||||
animated_textures: Vec<AnimatedTexture>,
|
||||
pending_uploads: Vec<(i32, atlas::Rect, Vec<u8>)>,
|
||||
|
||||
dynamic_textures: HashMap<String, (i32, atlas::Rect)>,
|
||||
free_dynamics: Vec<(i32, atlas::Rect)>,
|
||||
}
|
||||
|
||||
impl TextureManager {
|
||||
|
@ -204,6 +207,9 @@ impl TextureManager {
|
|||
atlases: Vec::new(),
|
||||
animated_textures: Vec::new(),
|
||||
pending_uploads: Vec::new(),
|
||||
|
||||
dynamic_textures: HashMap::new(),
|
||||
free_dynamics: Vec::new(),
|
||||
};
|
||||
tm.add_defaults();
|
||||
tm
|
||||
|
@ -222,6 +228,8 @@ impl TextureManager {
|
|||
}
|
||||
|
||||
fn update_textures(&mut self, version: usize) {
|
||||
self.dynamic_textures.clear();
|
||||
self.free_dynamics.clear();
|
||||
self.pending_uploads.clear();
|
||||
self.atlases.clear();
|
||||
self.animated_textures.clear();
|
||||
|
@ -391,6 +399,74 @@ impl TextureManager {
|
|||
self.textures.insert(full_name.to_owned(), t.clone());
|
||||
t
|
||||
}
|
||||
|
||||
pub fn put_dynamic(&mut self, plugin: &str, name: &str, img: image::DynamicImage) -> Texture {
|
||||
let (width, height) = img.dimensions();
|
||||
let (width, height) = (width as usize, height as usize);
|
||||
let mut rect = None;
|
||||
let mut rect_pos = 0;
|
||||
for (i, r) in self.free_dynamics.iter().enumerate() {
|
||||
let (atlas, r) = *r;
|
||||
if r.width == width && r.height == height {
|
||||
rect_pos = i;
|
||||
rect = Some((atlas, r));
|
||||
break;
|
||||
} else if r.width >= width && r.height >= height {
|
||||
rect_pos = i;
|
||||
rect = Some((atlas, r));
|
||||
}
|
||||
}
|
||||
let data = img.to_rgba().into_vec();
|
||||
let mut new = false;
|
||||
let (atlas, rect) = if let Some(r) = rect {
|
||||
self.free_dynamics.remove(rect_pos);
|
||||
r
|
||||
} else {
|
||||
new = true;
|
||||
self.find_free(width as usize, height as usize)
|
||||
};
|
||||
|
||||
let mut full_name = String::new();
|
||||
if plugin != "minecraft" {
|
||||
full_name.push_str(plugin);
|
||||
full_name.push_str(":");
|
||||
}
|
||||
full_name.push_str(name);
|
||||
|
||||
self.dynamic_textures.insert(full_name.clone(), (atlas, rect));
|
||||
if new {
|
||||
self.put_texture(plugin, name, width as u32, height as u32, data)
|
||||
} else {
|
||||
let t = Texture {
|
||||
name: full_name.clone(),
|
||||
version: self.version,
|
||||
atlas: atlas,
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
rel_x: 0.0,
|
||||
rel_y: 0.0,
|
||||
rel_width: 1.0,
|
||||
rel_height: 1.0,
|
||||
is_rel: false,
|
||||
};
|
||||
self.textures.insert(full_name.to_owned(), t.clone());
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_dynamic(&mut self, plugin: &str, name: &str) {
|
||||
let mut full_name = String::new();
|
||||
if plugin != "minecraft" {
|
||||
full_name.push_str(plugin);
|
||||
full_name.push_str(":");
|
||||
}
|
||||
full_name.push_str(name);
|
||||
|
||||
let desc = self.dynamic_textures.remove(&full_name).unwrap();
|
||||
self.free_dynamics.push(desc);
|
||||
}
|
||||
}
|
||||
|
||||
struct AnimatedTexture {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
mod server_list;
|
||||
pub use self::server_list::*;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use render;
|
||||
use ui;
|
||||
|
||||
|
@ -141,4 +143,50 @@ pub fn new_button_text(renderer: &mut render::Renderer, val: &str, x: f64, y: f6
|
|||
text.set_v_attach(ui::VAttach::Middle);
|
||||
text.set_h_attach(ui::HAttach::Center);
|
||||
(batch, text)
|
||||
}
|
||||
|
||||
pub fn button_action(ui_container: &mut ui::Container,
|
||||
btn: ui::ElementRef<ui::Batch>, txt: Option<ui::ElementRef<ui::Text>>,
|
||||
click: Option<Rc<Fn(&mut render::Renderer, &mut ui::Container)>>
|
||||
) {
|
||||
let batch = ui_container.get_mut(&btn);
|
||||
batch.add_hover_func(Rc::new(move |over, renderer, ui_container| {
|
||||
let texture = render::Renderer::get_texture(renderer.get_textures_ref(), "gui/widgets").relative(
|
||||
0.0, (if over { 86.0 } else { 66.0 }) / 256.0, 200.0 / 256.0, 20.0 / 256.0
|
||||
);
|
||||
|
||||
{
|
||||
let batch = ui_container.get_mut(&btn);
|
||||
for i in 0 .. batch.len() {
|
||||
let img = batch.get_mut_at::<ui::Image>(i);
|
||||
match i {
|
||||
_i @ 0 ...3 => img.set_texture(texture.clone()),
|
||||
4 => img.set_texture(texture.clone().relative(
|
||||
2.0 / 200.0, 0.0, 196.0 / 200.0, 2.0 / 20.0
|
||||
)),
|
||||
5 => img.set_texture(texture.clone().relative(
|
||||
2.0 / 200.0, 17.0 / 20.0, 196.0 / 200.0, 3.0 / 20.0
|
||||
)),
|
||||
6 => img.set_texture(texture.clone().relative(
|
||||
0.0, 2.0 / 20.0, 2.0 / 200.0, 15.0 / 20.0
|
||||
)),
|
||||
7 => img.set_texture(texture.clone().relative(
|
||||
198.0 / 200.0, 2.0 / 20.0, 2.0 / 200.0, 15.0 / 20.0
|
||||
)),
|
||||
8 => img.set_texture(texture.clone().relative(
|
||||
2.0 / 200.0, 2.0 / 20.0, 196.0 / 200.0, 15.0 / 20.0
|
||||
)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
let txt = txt.clone();
|
||||
if let Some(txt) = txt {
|
||||
let text = ui_container.get_mut(&txt);
|
||||
text.set_b(if over { 160 } else { 255 });
|
||||
}
|
||||
}));
|
||||
if let Some(click) = click {
|
||||
batch.add_click_func(click);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,25 @@
|
|||
|
||||
use std::fs;
|
||||
use std::thread;
|
||||
use std::sync::mpsc;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ui;
|
||||
use render;
|
||||
use format;
|
||||
use format::{Component, TextComponent};
|
||||
use protocol;
|
||||
|
||||
use serde_json;
|
||||
use std::cmp::max;
|
||||
use time;
|
||||
use image;
|
||||
use rustc_serialize::base64::FromBase64;
|
||||
use rand;
|
||||
use rand::{Rng};
|
||||
|
||||
pub struct ServerList {
|
||||
elements: Option<UIElements>,
|
||||
disconnect_reason: Option<format::Component>,
|
||||
disconnect_reason: Option<Component>,
|
||||
}
|
||||
|
||||
struct UIElements {
|
||||
|
@ -22,6 +33,28 @@ struct Server {
|
|||
back: ui::ElementRef<ui::Image>,
|
||||
offset: f64,
|
||||
y: f64,
|
||||
|
||||
motd: ui::ElementRef<ui::Formatted>,
|
||||
ping: ui::ElementRef<ui::Image>,
|
||||
players: ui::ElementRef<ui::Text>,
|
||||
version: ui::ElementRef<ui::Formatted>,
|
||||
|
||||
icon: ui::ElementRef<ui::Image>,
|
||||
icon_texture: Option<String>,
|
||||
|
||||
done_ping: bool,
|
||||
recv: mpsc::Receiver<PingInfo>,
|
||||
}
|
||||
|
||||
struct PingInfo {
|
||||
motd: format::Component,
|
||||
ping: time::Duration,
|
||||
exists: bool,
|
||||
online: i32,
|
||||
max: i32,
|
||||
protocol_version: i32,
|
||||
protocol_name: String,
|
||||
favicon: Option<image::DynamicImage>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
|
@ -35,7 +68,7 @@ impl Server {
|
|||
}
|
||||
|
||||
impl ServerList {
|
||||
pub fn new(disconnect_reason: Option<format::Component>) -> ServerList {
|
||||
pub fn new(disconnect_reason: Option<Component>) -> ServerList {
|
||||
ServerList {
|
||||
elements: None,
|
||||
disconnect_reason: disconnect_reason,
|
||||
|
@ -44,14 +77,20 @@ impl ServerList {
|
|||
|
||||
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);
|
||||
{
|
||||
let mut tex = renderer.get_textures_ref().write().unwrap();
|
||||
for server in &mut elements.servers {
|
||||
server.collection.remove_all(ui_container);
|
||||
if let Some(ref icon) = server.icon_texture {
|
||||
tex.remove_dynamic("steven_icon", &icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
elements.servers.clear();
|
||||
|
||||
let file = match fs::File::open("servers.json") {
|
||||
Ok(val) => val,
|
||||
Err(e) => return,
|
||||
Err(_) => return,
|
||||
};
|
||||
let servers_info: serde_json::Value = serde_json::from_reader(file).unwrap();
|
||||
let servers = servers_info.find("servers").unwrap().as_array().unwrap();
|
||||
|
@ -62,7 +101,7 @@ impl ServerList {
|
|||
|
||||
for svr in servers {
|
||||
let name = svr.find("name").unwrap().as_string().unwrap();
|
||||
let address = svr.find("address").unwrap().as_string().unwrap();
|
||||
let address = svr.find("address").unwrap().as_string().unwrap().to_owned();
|
||||
|
||||
let solid = render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid");
|
||||
|
||||
|
@ -71,14 +110,38 @@ impl ServerList {
|
|||
back.set_v_attach(ui::VAttach::Middle);
|
||||
back.set_h_attach(ui::HAttach::Center);
|
||||
|
||||
let (send, recv) = mpsc::channel::<PingInfo>();
|
||||
let mut server = Server {
|
||||
collection: ui::Collection::new(),
|
||||
back: ui_container.add(back),
|
||||
offset: offset,
|
||||
y: 0.0,
|
||||
done_ping: false,
|
||||
recv: recv,
|
||||
|
||||
motd: Default::default(),
|
||||
ping: Default::default(),
|
||||
players: Default::default(),
|
||||
version: Default::default(),
|
||||
|
||||
icon: Default::default(),
|
||||
icon_texture: None,
|
||||
};
|
||||
server.collection.add(server.back.clone());
|
||||
server.update_position();
|
||||
{
|
||||
let back = ui_container.get_mut(&server.back);
|
||||
let back_ref = server.back.clone();
|
||||
let address = address.clone();
|
||||
back.add_hover_func(Rc::new(move |over, renderer, ui_container| {
|
||||
let back = ui_container.get_mut(&back_ref);
|
||||
back.set_a(if over { 200 } else { 100 });
|
||||
}));
|
||||
|
||||
back.add_click_func(Rc::new(move |renderer, ui_container| {
|
||||
println!("Connecting to {}", address);
|
||||
}));
|
||||
}
|
||||
|
||||
let mut text = ui::Text::new(renderer, &name, 100.0, 5.0, 255, 255, 255);
|
||||
text.set_parent(&server.back);
|
||||
|
@ -86,20 +149,94 @@ impl ServerList {
|
|||
|
||||
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));
|
||||
server.icon = 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));
|
||||
server.ping = 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));
|
||||
server.players = server.collection.add(ui_container.add(players));
|
||||
|
||||
let mut motd = ui::Formatted::with_width_limit(renderer, Component::Text(TextComponent::new("Connecting...")), 100.0, 23.0, 700.0 - (90.0 + 10.0 + 5.0));
|
||||
motd.set_parent(&server.back);
|
||||
server.motd = server.collection.add(ui_container.add(motd));
|
||||
|
||||
let mut version = ui::Formatted::with_width_limit(renderer, Component::Text(TextComponent::new("")), 100.0, 5.0, 700.0 - (90.0 + 10.0 + 5.0));
|
||||
version.set_v_attach(ui::VAttach::Bottom);
|
||||
version.set_parent(&server.back);
|
||||
server.version = server.collection.add(ui_container.add(version));
|
||||
|
||||
|
||||
let (mut del, mut txt) = super::new_button_text(renderer, "X", 0.0, 0.0, 25.0, 25.0);
|
||||
del.set_v_attach(ui::VAttach::Bottom);
|
||||
del.set_h_attach(ui::HAttach::Right);
|
||||
del.set_parent(&server.back);
|
||||
let re = ui_container.add(del);
|
||||
txt.set_parent(&re);
|
||||
let tre = ui_container.add(txt);
|
||||
super::button_action(ui_container, re.clone(), Some(tre.clone()), None);
|
||||
server.collection.add(re);
|
||||
server.collection.add(tre);
|
||||
|
||||
let (mut edit, mut txt) = super::new_button_text(renderer, "E", 25.0, 0.0, 25.0, 25.0);
|
||||
edit.set_v_attach(ui::VAttach::Bottom);
|
||||
edit.set_h_attach(ui::HAttach::Right);
|
||||
edit.set_parent(&server.back);
|
||||
let re = ui_container.add(edit);
|
||||
txt.set_parent(&re);
|
||||
let tre = ui_container.add(txt);
|
||||
super::button_action(ui_container, re.clone(), Some(tre.clone()), None);
|
||||
server.collection.add(re);
|
||||
server.collection.add(tre);
|
||||
|
||||
elements.servers.push(server);
|
||||
offset += 1.0;
|
||||
|
||||
thread::spawn(move || {
|
||||
match protocol::Conn::new(&address).and_then(|conn| conn.do_status()) {
|
||||
Ok(res) => {
|
||||
let mut desc = res.0.description;
|
||||
format::convert_legacy(&mut desc);
|
||||
let favicon = if let Some(icon) = res.0.favicon {
|
||||
let data = icon["data:image/png;base64,".len()..].from_base64().unwrap();
|
||||
Some(image::load_from_memory(
|
||||
&data
|
||||
).unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
send.send(PingInfo {
|
||||
motd: desc,
|
||||
ping: res.1,
|
||||
exists: true,
|
||||
online: res.0.players.online,
|
||||
max: res.0.players.max,
|
||||
protocol_version: res.0.version.protocol,
|
||||
protocol_name: res.0.version.name,
|
||||
favicon: favicon,
|
||||
}).unwrap();
|
||||
},
|
||||
Err(err) => {
|
||||
let e = format!("{}", err);
|
||||
let mut msg = TextComponent::new(&e);
|
||||
msg.modifier.color = Some(format::Color::Red);
|
||||
send.send(PingInfo {
|
||||
motd: Component::Text(msg),
|
||||
ping: time::Duration::seconds(99999),
|
||||
exists: false,
|
||||
online: 0,
|
||||
max: 0,
|
||||
protocol_version: 0,
|
||||
protocol_name: "".to_owned(),
|
||||
favicon: None,
|
||||
}).unwrap();
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,16 +254,22 @@ impl super::Screen for ServerList {
|
|||
refresh.set_h_attach(ui::HAttach::Center);
|
||||
let re = ui_container.add(refresh);
|
||||
txt.set_parent(&re);
|
||||
let tre = ui_container.add(txt);
|
||||
super::button_action(ui_container, re.clone(), Some(tre.clone()), None);
|
||||
elements.add(re);
|
||||
elements.add(ui_container.add(txt));
|
||||
elements.add(tre);
|
||||
|
||||
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);
|
||||
let tre = ui_container.add(txt);
|
||||
super::button_action(ui_container, re.clone(), Some(tre.clone()), Some(Rc::new(|renderer, ui_container| {
|
||||
|
||||
})));
|
||||
elements.add(re);
|
||||
elements.add(ui_container.add(txt));
|
||||
elements.add(tre);
|
||||
|
||||
let mut options = super::new_button(renderer, 5.0, 25.0, 40.0, 40.0);
|
||||
options.set_v_attach(ui::VAttach::Bottom);
|
||||
|
@ -136,6 +279,7 @@ impl super::Screen for ServerList {
|
|||
cog.set_parent(&re);
|
||||
cog.set_v_attach(ui::VAttach::Middle);
|
||||
cog.set_h_attach(ui::HAttach::Center);
|
||||
super::button_action(ui_container, re.clone(), None, None);
|
||||
elements.add(re);
|
||||
elements.add(ui_container.add(cog));
|
||||
|
||||
|
@ -188,13 +332,78 @@ impl super::Screen for ServerList {
|
|||
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);
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if !s.done_ping {
|
||||
match s.recv.try_recv() {
|
||||
Ok(res) => {
|
||||
s.done_ping = true;
|
||||
{
|
||||
let motd = ui_container.get_mut(&s.motd);
|
||||
motd.set_component(renderer, res.motd);
|
||||
}
|
||||
{
|
||||
let ping = ui_container.get_mut(&s.ping);
|
||||
let y = match res.ping.num_milliseconds() {
|
||||
_x @ 0 ... 75 => 16.0 / 256.0,
|
||||
_x @ 76 ... 150 => 24.0 / 256.0,
|
||||
_x @ 151 ... 225 => 32.0 / 256.0,
|
||||
_x @ 226 ... 350 => 40.0 / 256.0,
|
||||
_x @ 351 ... 999 => 48.0 / 256.0,
|
||||
_ => 56.0 / 256.0,
|
||||
};
|
||||
ping.set_t_y(y);
|
||||
}
|
||||
if res.exists {
|
||||
{
|
||||
let players = ui_container.get_mut(&s.players);
|
||||
let txt = if res.protocol_version == protocol::SUPPORTED_PROTOCOL {
|
||||
players.set_g(255);
|
||||
players.set_b(255);
|
||||
format!("{}/{}", res.online, res.max)
|
||||
} else {
|
||||
players.set_g(85);
|
||||
players.set_b(85);
|
||||
format!("Out of date {}/{}", res.online, res.max)
|
||||
};
|
||||
players.set_text(renderer, &txt);
|
||||
}
|
||||
{
|
||||
let version = ui_container.get_mut(&s.version);
|
||||
let mut txt = TextComponent::new(&res.protocol_name);
|
||||
txt.modifier.color = Some(format::Color::Yellow);
|
||||
let mut msg = Component::Text(txt);
|
||||
format::convert_legacy(&mut msg);
|
||||
version.set_component(renderer, msg);
|
||||
}
|
||||
}
|
||||
if let Some(favicon) = res.favicon {
|
||||
let name: String = rand::thread_rng().gen_ascii_chars().take(30).collect();
|
||||
let tex = renderer.get_textures_ref();
|
||||
s.icon_texture = Some(name.clone());
|
||||
let icon_tex = tex.write().unwrap().put_dynamic("steven_icon", &name, favicon);
|
||||
let icon = ui_container.get_mut(&s.icon);
|
||||
icon.set_texture(icon_tex);
|
||||
}
|
||||
},
|
||||
Err(mpsc::TryRecvError::Disconnected) => {
|
||||
s.done_ping = true;
|
||||
let motd = ui_container.get_mut(&s.motd);
|
||||
let mut txt = TextComponent::new("Channel dropped");
|
||||
txt.modifier.color = Some(format::Color::Red);
|
||||
motd.set_component(renderer, Component::Text(txt));
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,80 +1,61 @@
|
|||
|
||||
pub struct Formatted {
|
||||
dirty: bool,
|
||||
data: Vec<u8>,
|
||||
|
||||
parent: Option<ElementRefInner>,
|
||||
should_draw: bool,
|
||||
layer: isize,
|
||||
ui_element!(Formatted {
|
||||
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,
|
||||
}
|
||||
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(),
|
||||
base_impl!();
|
||||
|
||||
parent: None,
|
||||
should_draw: true,
|
||||
layer: 0,
|
||||
pub fn new(renderer: &mut render::Renderer, val: format::Component, x: f64, y: f64) -> Formatted {
|
||||
let mut f = ui_create!(Formatted {
|
||||
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);
|
||||
lines: 0
|
||||
});
|
||||
f.init_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,
|
||||
let mut f = ui_create!(Formatted {
|
||||
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);
|
||||
lines: 0
|
||||
});
|
||||
f.init_component(renderer);
|
||||
f
|
||||
}
|
||||
|
||||
pub fn set_component(&mut self, renderer: &mut render::Renderer, val: format::Component) {
|
||||
self.val = val;
|
||||
self.init_component(renderer);
|
||||
}
|
||||
|
||||
fn set_component(&mut self, renderer: &mut render::Renderer) {
|
||||
fn init_component(&mut self, renderer: &mut render::Renderer) {
|
||||
self.text.clear();
|
||||
let mut state = FormatState {
|
||||
lines: 0,
|
||||
|
@ -93,7 +74,7 @@ impl Formatted {
|
|||
}
|
||||
|
||||
fn update(&mut self, renderer: &mut render::Renderer) {
|
||||
self.set_component(renderer);
|
||||
self.init_component(renderer);
|
||||
}
|
||||
|
||||
fn draw(&mut self, renderer: &mut render::Renderer, r: &Region, width: f64, height: f64, delta: f64) -> &Vec<u8> {
|
||||
|
@ -116,18 +97,8 @@ impl Formatted {
|
|||
((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);
|
||||
|
||||
|
@ -183,7 +154,7 @@ impl <'a> FormatState<'a> {
|
|||
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() {
|
||||
for (i, c) in txt.char_indices() {
|
||||
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();
|
||||
|
|
330
src/ui/mod.rs
330
src/ui/mod.rs
|
@ -16,6 +16,7 @@ pub mod logo;
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use rand;
|
||||
use render;
|
||||
use format;
|
||||
|
@ -34,6 +35,37 @@ pub enum Element {
|
|||
macro_rules! element_impl {
|
||||
($($name:ident),+) => (
|
||||
impl Element {
|
||||
fn get_click_funcs(&self) -> Vec<Rc<Fn(&mut render::Renderer, &mut Container)>> {
|
||||
match self {
|
||||
$(
|
||||
&Element::$name(ref val) => val.click_funcs.clone(),
|
||||
)+
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_hover_funcs(&self) -> Vec<Rc<Fn(bool, &mut render::Renderer, &mut Container)>> {
|
||||
match self {
|
||||
$(
|
||||
&Element::$name(ref val) => val.hover_funcs.clone(),
|
||||
)+
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_call_hover(&mut self, new: bool) -> bool{
|
||||
match self {
|
||||
$(
|
||||
&mut Element::$name(ref mut val) => {
|
||||
let ret = val.hovered != new;
|
||||
val.hovered = new;
|
||||
ret
|
||||
},
|
||||
)+
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_draw(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
|
@ -204,8 +236,9 @@ impl Collection {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add<T: UIElement>(&mut self, element: ElementRef<T>) {
|
||||
pub fn add<T: UIElement>(&mut self, element: ElementRef<T>) -> ElementRef<T> {
|
||||
self.elements.push(element.inner);
|
||||
element
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self, container: &mut Container) {
|
||||
|
@ -296,10 +329,10 @@ impl Container {
|
|||
self.version = renderer.ui.version;
|
||||
}
|
||||
|
||||
// Borrow rules seem to prevent us from doing this in the first pass
|
||||
// Borrow rules seems to prevent us from doing this in the first pass
|
||||
// so we split it.
|
||||
let regions = self.collect_elements(sw, sh);
|
||||
for re in &mut self.elements_list {
|
||||
for re in &self.elements_list {
|
||||
let mut e = self.elements.get_mut(re).unwrap();
|
||||
if !e.should_draw() {
|
||||
continue;
|
||||
|
@ -335,6 +368,61 @@ impl Container {
|
|||
map
|
||||
}
|
||||
|
||||
pub fn click_at(&mut self, renderer: &mut render::Renderer, x: f64, y: f64, width: f64, height: f64) {
|
||||
let (sw, sh) = match self.mode {
|
||||
Mode::Scaled => (SCALED_WIDTH / width, SCALED_HEIGHT / height),
|
||||
Mode::Unscaled(scale) => (scale, scale),
|
||||
};
|
||||
let mx = (x / width) * SCALED_WIDTH;
|
||||
let my = (y / height) * SCALED_HEIGHT;
|
||||
let mut click = None;
|
||||
for re in self.elements_list.iter().rev() {
|
||||
let e = self.elements.get(re).unwrap();
|
||||
let funcs = e.get_click_funcs();
|
||||
if !funcs.is_empty() {
|
||||
let r = self.get_draw_region(e, sw, sh);
|
||||
if mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h {
|
||||
click = Some(funcs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(click) = click {
|
||||
for c in &click {
|
||||
c(renderer, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hover_at(&mut self, renderer: &mut render::Renderer, x: f64, y: f64, width: f64, height: f64) {
|
||||
let (sw, sh) = match self.mode {
|
||||
Mode::Scaled => (SCALED_WIDTH / width, SCALED_HEIGHT / height),
|
||||
Mode::Unscaled(scale) => (scale, scale),
|
||||
};
|
||||
let mx = (x / width) * SCALED_WIDTH;
|
||||
let my = (y / height) * SCALED_HEIGHT;
|
||||
let mut hovers = Vec::new();
|
||||
for re in self.elements_list.iter().rev() {
|
||||
let e = self.elements.get(re).unwrap();
|
||||
let funcs = e.get_hover_funcs();
|
||||
if !funcs.is_empty() {
|
||||
let r = self.get_draw_region(e, sw, sh);
|
||||
hovers.push((*re, funcs, mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h));
|
||||
}
|
||||
}
|
||||
for hover in &hovers {
|
||||
let call = {
|
||||
let e = self.elements.get_mut(&hover.0).unwrap();
|
||||
e.should_call_hover(hover.2)
|
||||
};
|
||||
if call {
|
||||
for f in &hover.1 {
|
||||
f(hover.2, renderer, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_draw_region(&self, e: &Element, sw: f64, sh: f64) -> Region {
|
||||
let super_region = match e.get_parent() {
|
||||
Some(ref p) => self.get_draw_region(self.elements.get(p).unwrap(), sw, sh),
|
||||
|
@ -387,20 +475,82 @@ macro_rules! lazy_field {
|
|||
)
|
||||
}
|
||||
|
||||
pub struct Image {
|
||||
dirty: bool,
|
||||
data: Vec<u8>,
|
||||
macro_rules! ui_element {
|
||||
(
|
||||
$name:ident {
|
||||
$(
|
||||
$field:ident : $field_ty:ty
|
||||
),+
|
||||
}
|
||||
) => (
|
||||
pub struct $name {
|
||||
dirty: bool,
|
||||
data: Vec<u8>,
|
||||
parent: Option<ElementRefInner>,
|
||||
should_draw: bool,
|
||||
layer: isize,
|
||||
x: f64,
|
||||
y: f64,
|
||||
v_attach: VAttach,
|
||||
h_attach: HAttach,
|
||||
click_funcs: Vec<Rc<Fn(&mut render::Renderer, &mut Container)>>,
|
||||
hover_funcs: Vec<Rc<Fn(bool, &mut render::Renderer, &mut Container)>>,
|
||||
hovered: bool,
|
||||
$(
|
||||
$field: $field_ty
|
||||
),+
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
parent: Option<ElementRefInner>,
|
||||
should_draw: bool,
|
||||
macro_rules! base_impl {
|
||||
() => (
|
||||
pub fn set_parent<T: UIElement>(&mut self, other: &ElementRef<T>) {
|
||||
self.parent = Some(other.inner);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn add_click_func(&mut self, f: Rc<Fn(&mut render::Renderer, &mut Container)>) {
|
||||
self.click_funcs.push(f);
|
||||
}
|
||||
|
||||
pub fn add_hover_func(&mut self, f: Rc<Fn(bool, &mut render::Renderer, &mut Container)>) {
|
||||
self.hover_funcs.push(f);
|
||||
}
|
||||
|
||||
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!(v_attach, VAttach, get_v_attach, set_v_attach);
|
||||
lazy_field!(h_attach, HAttach, get_h_attach, set_h_attach);
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! ui_create {
|
||||
($name:ident {
|
||||
$($field:ident: $e:expr),+
|
||||
}) => (
|
||||
$name {
|
||||
dirty: true,
|
||||
data: Vec::new(),
|
||||
|
||||
parent: None,
|
||||
should_draw: true,
|
||||
layer: 0,
|
||||
v_attach: VAttach::Top,
|
||||
h_attach: HAttach::Left,
|
||||
click_funcs: Vec::new(),
|
||||
hover_funcs: Vec::new(),
|
||||
hovered: false,
|
||||
$($field: $e),+
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
ui_element!(Image {
|
||||
texture: render::Texture,
|
||||
layer: isize,
|
||||
x: f64,
|
||||
y: f64,
|
||||
width: f64,
|
||||
height: f64,
|
||||
v_attach: VAttach,
|
||||
h_attach: HAttach,
|
||||
|
||||
t_x: f64,
|
||||
t_y: f64,
|
||||
|
@ -410,25 +560,19 @@ pub struct Image {
|
|||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
}
|
||||
a: u8
|
||||
});
|
||||
|
||||
impl Image {
|
||||
pub fn new(texture: render::Texture, x: f64, y: f64, w: f64, h: f64, t_x: f64, t_y: f64, t_width: f64, t_height: f64, r: u8, g: u8, b: u8) -> Image {
|
||||
Image {
|
||||
dirty: true,
|
||||
data: Vec::new(),
|
||||
base_impl!();
|
||||
|
||||
parent: None,
|
||||
should_draw: true,
|
||||
pub fn new(texture: render::Texture, x: f64, y: f64, w: f64, h: f64, t_x: f64, t_y: f64, t_width: f64, t_height: f64, r: u8, g: u8, b: u8) -> Image {
|
||||
ui_create!(Image {
|
||||
texture: texture,
|
||||
layer: 0,
|
||||
x: x,
|
||||
y: y,
|
||||
width: w,
|
||||
height: h,
|
||||
v_attach: VAttach::Top,
|
||||
h_attach: HAttach::Left,
|
||||
|
||||
t_x: t_x,
|
||||
t_y: t_y,
|
||||
|
@ -438,8 +582,8 @@ impl Image {
|
|||
r: r,
|
||||
g: g,
|
||||
b: b,
|
||||
a: 255,
|
||||
}
|
||||
a: 255
|
||||
})
|
||||
}
|
||||
|
||||
fn update(&mut self, renderer: &mut render::Renderer) {}
|
||||
|
@ -463,11 +607,6 @@ impl Image {
|
|||
(self.width, self.height)
|
||||
}
|
||||
|
||||
pub fn set_parent<T: UIElement>(&mut self, other: &ElementRef<T>) {
|
||||
self.parent = Some(other.inner);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn get_texture(&self) -> render::Texture {
|
||||
self.texture.clone()
|
||||
}
|
||||
|
@ -477,13 +616,8 @@ impl Image {
|
|||
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!(t_x, f64, get_t_x, set_t_x);
|
||||
lazy_field!(t_y, f64, get_t_y, set_t_y);
|
||||
|
@ -516,48 +650,34 @@ impl UIElement for Image {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Getting values out?
|
||||
|
||||
pub struct Batch {
|
||||
dirty: bool,
|
||||
data: Vec<u8>,
|
||||
|
||||
parent: Option<ElementRefInner>,
|
||||
should_draw: bool,
|
||||
layer: isize,
|
||||
x: f64,
|
||||
y: f64,
|
||||
width: f64,
|
||||
height: f64,
|
||||
v_attach: VAttach,
|
||||
h_attach: HAttach,
|
||||
|
||||
elements: Vec<Element>,
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct BatchRef<T: UIElement> {
|
||||
index: usize,
|
||||
ty: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl Batch {
|
||||
pub fn new(x: f64, y: f64, w: f64, h: f64) -> Batch {
|
||||
Batch {
|
||||
dirty: true,
|
||||
data: Vec::new(),
|
||||
ui_element!(Batch {
|
||||
width: f64,
|
||||
height: f64,
|
||||
|
||||
parent: None,
|
||||
should_draw: true,
|
||||
layer: 0,
|
||||
elements: Vec<Element>
|
||||
});
|
||||
|
||||
impl Batch {
|
||||
base_impl!();
|
||||
|
||||
pub fn new(x: f64, y: f64, w: f64, h: f64) -> Batch {
|
||||
ui_create!(Batch {
|
||||
x: x,
|
||||
y: y,
|
||||
width: w,
|
||||
height: h,
|
||||
v_attach: VAttach::Top,
|
||||
h_attach: HAttach::Left,
|
||||
|
||||
elements: Vec::new(),
|
||||
}
|
||||
elements: Vec::new()
|
||||
})
|
||||
}
|
||||
|
||||
fn update(&mut self, renderer: &mut render::Renderer) {
|
||||
|
||||
}
|
||||
fn update(&mut self, renderer: &mut render::Renderer) {}
|
||||
|
||||
fn draw(&mut self, renderer: &mut render::Renderer, r: &Region, width: f64, height: f64, delta: f64) -> &Vec<u8> {
|
||||
if self.dirty {
|
||||
|
@ -580,23 +700,31 @@ impl Batch {
|
|||
(self.width, self.height)
|
||||
}
|
||||
|
||||
pub fn set_parent<T: UIElement>(&mut self, other: &ElementRef<T>) {
|
||||
self.parent = Some(other.inner);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn add<T: UIElement>(&mut self, e: T) {
|
||||
pub fn add<T: UIElement>(&mut self, e: T) -> BatchRef<T> {
|
||||
self.elements.push(e.wrap());
|
||||
BatchRef { index: self.elements.len() - 1, ty: PhantomData }
|
||||
}
|
||||
|
||||
pub fn get<T: UIElement>(&self, r: BatchRef<T>) -> &T {
|
||||
T::unwrap_ref(&self.elements[r.index])
|
||||
}
|
||||
|
||||
pub fn get_mut<T: UIElement>(&mut self, r: BatchRef<T>) -> &mut T {
|
||||
self.dirty = true;
|
||||
T::unwrap_ref_mut(&mut self.elements[r.index])
|
||||
}
|
||||
|
||||
pub fn get_mut_at<T: UIElement>(&mut self, index: usize) -> &mut T {
|
||||
self.dirty = true;
|
||||
T::unwrap_ref_mut(&mut self.elements[index])
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
impl UIElement for Batch {
|
||||
|
@ -619,53 +747,37 @@ impl UIElement for Batch {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Text {
|
||||
dirty: bool,
|
||||
data: Vec<u8>,
|
||||
|
||||
parent: Option<ElementRefInner>,
|
||||
should_draw: bool,
|
||||
layer: isize,
|
||||
ui_element!(Text {
|
||||
val: String,
|
||||
x: f64,
|
||||
y: f64,
|
||||
width: f64,
|
||||
height: f64,
|
||||
v_attach: VAttach,
|
||||
h_attach: HAttach,
|
||||
scale_x: f64,
|
||||
scale_y: f64,
|
||||
rotation: f64,
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
}
|
||||
a: u8
|
||||
});
|
||||
|
||||
impl Text {
|
||||
pub fn new(renderer: &render::Renderer, val: &str, x: f64, y: f64, r: u8, g: u8, b: u8) -> Text {
|
||||
Text {
|
||||
dirty: true,
|
||||
data: Vec::new(),
|
||||
base_impl!();
|
||||
|
||||
parent: None,
|
||||
should_draw: true,
|
||||
layer: 0,
|
||||
pub fn new(renderer: &render::Renderer, val: &str, x: f64, y: f64, r: u8, g: u8, b: u8) -> Text {
|
||||
ui_create!(Text {
|
||||
val: val.to_owned(),
|
||||
x: x,
|
||||
y: y,
|
||||
width: renderer.ui.size_of_string(val),
|
||||
height: 18.0,
|
||||
v_attach: VAttach::Top,
|
||||
h_attach: HAttach::Left,
|
||||
scale_x: 1.0,
|
||||
scale_y: 1.0,
|
||||
rotation: 0.0,
|
||||
r: r,
|
||||
g: g,
|
||||
b: b,
|
||||
a: 255,
|
||||
}
|
||||
a: 255
|
||||
})
|
||||
}
|
||||
|
||||
fn update(&mut self, renderer: &mut render::Renderer) {
|
||||
|
@ -701,11 +813,6 @@ impl Text {
|
|||
((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;
|
||||
}
|
||||
|
||||
pub fn get_text(&self) -> &str {
|
||||
&self.val
|
||||
}
|
||||
|
@ -716,13 +823,8 @@ impl Text {
|
|||
self.width = renderer.ui.size_of_string(val);
|
||||
}
|
||||
|
||||
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);
|
||||
lazy_field!(rotation, f64, get_rotation, set_rotation);
|
||||
|
|
Loading…
Reference in New Issue