Rewrite the UI system (Closes #12)

This commit is contained in:
Thinkofname 2016-04-24 12:22:04 +01:00
parent b0c54a3561
commit a7caa50b6f
18 changed files with 2016 additions and 2371 deletions

View File

@ -181,18 +181,25 @@ impl Vars {
pub struct Console {
history: Vec<Component>,
dirty: bool,
collection: ui::Collection,
elements: Option<ConsoleElements>,
active: bool,
position: f64,
}
struct ConsoleElements {
background: ui::ImageRef,
lines: Vec<ui::FormattedRef>,
}
impl Console {
pub fn new() -> Console {
Console {
history: vec![Component::Text(TextComponent::new("")); 200],
dirty: false,
collection: ui::Collection::new(),
elements: None,
active: false,
position: -220.0,
}
@ -208,14 +215,11 @@ impl Console {
pub fn tick(&mut self,
ui_container: &mut ui::Container,
renderer: &mut render::Renderer,
renderer: &render::Renderer,
delta: f64,
width: f64) {
// To make sure the console is always on top it constant removes and readds itself.
// Its hacky but the console should never appear for normal users so its not really
// a major issue.
self.collection.remove_all(ui_container);
if !self.active && self.position <= -220.0 {
self.elements = None;
return;
}
if self.active {
@ -229,39 +233,48 @@ impl Console {
} else {
self.position = -220.0;
}
let w = match ui_container.mode {
ui::Mode::Scaled => width,
ui::Mode::Unscaled(scale) => 854.0 / scale,
};
let mut background = ui::Image::new(
render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid"),
0.0, self.position, w, 220.0,
0.0, 0.0, 1.0, 1.0,
0, 0, 0
);
background.set_a(180);
let background = self.collection.add(ui_container.add(background));
let mut lines = Vec::new();
let mut offset = 0.0;
for line in self.history.iter().rev() {
if offset >= 210.0 {
break;
}
let mut fmt = ui::Formatted::with_width_limit(
renderer,
line.clone(),
5.0, 5.0 + offset,
w - 1.0
);
fmt.set_parent(&background);
fmt.set_v_attach(ui::VAttach::Bottom);
offset += fmt.get_height();
lines.push(ui_container.add(fmt));
if self.elements.is_none() {
let background = ui::ImageBuilder::new()
.texture("steven:solid")
.position(0.0, self.position)
.size(w, 220.0)
.colour((0, 0, 0, 180))
.draw_index(500)
.create(ui_container);
self.elements = Some(ConsoleElements {
background: background,
lines: vec![],
});
self.dirty = true;
}
for fmt in lines {
self.collection.add(fmt);
let elements = self.elements.as_mut().unwrap();
let mut background = elements.background.borrow_mut();
background.y = self.position;
background.width = w;
if self.dirty {
self.dirty = false;
elements.lines.clear();
let mut offset = 0.0;
for line in self.history.iter().rev() {
if offset >= 210.0 {
break;
}
let (_, height) = ui::Formatted::compute_size(renderer, line, w - 10.0);
elements.lines.push(ui::FormattedBuilder::new()
.text(line.clone())
.position(5.0, 5.0 + offset)
.max_width(w - 10.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Left)
.create(&mut *background));
offset += height;
}
}
}
@ -314,6 +327,7 @@ impl Console {
Component::Text(TextComponent::new(&format!("{}", record.args())))
]);
self.history.push(Component::Text(msg));
self.dirty = true;
}
}

View File

@ -494,7 +494,6 @@ struct MovementHandler {
gamemode: ecs::Key<Gamemode>,
position: ecs::Key<TargetPosition>,
velocity: ecs::Key<Velocity>,
game_info: ecs::Key<GameInfo>,
bounds: ecs::Key<Bounds>,
rotation: ecs::Key<Rotation>,
}
@ -518,7 +517,6 @@ impl MovementHandler {
gamemode: m.get_key(),
position: position,
velocity: velocity,
game_info: m.get_key(),
bounds: bounds,
rotation: rotation,
}

View File

@ -14,6 +14,7 @@
#![recursion_limit="300"]
#![feature(const_fn)]
#![feature(rc_would_unwrap)]
extern crate sdl2;
extern crate image;
@ -243,7 +244,6 @@ fn main() {
let delta = (diff.num_nanoseconds().unwrap() as f64) / frame_time;
let (width, height) = window.drawable_size();
let vsync_changed = *game.vars.get(settings::R_VSYNC);
if vsync != vsync_changed {
vsync = vsync_changed;
@ -262,7 +262,7 @@ fn main() {
game.console
.lock()
.unwrap()
.tick(&mut ui_container, &mut game.renderer, delta, width as f64);
.tick(&mut ui_container, &game.renderer, delta, width as f64);
ui_container.tick(&mut game.renderer, delta, width as f64, height as f64);
game.renderer.tick(&mut game.server.world, delta, width, height);

View File

@ -1200,13 +1200,14 @@ impl TextureManager {
height: height,
};
self.pending_uploads.push((tex.atlas, rect, data));
let t = tex.relative(0.0, 0.0, (width as f32) / (tex.width as f32), (height as f32) / (tex.height as f32));
let mut t = tex.relative(0.0, 0.0, (width as f32) / (tex.width as f32), (height as f32) / (tex.height as f32));
let old_name = mem::replace(&mut tex.name, format!("steven-dynamic:{}", name));
self.dynamic_textures.insert(name.to_owned(), (tex.clone(), img));
// We need to rename the texture itself so that get_texture calls
// work with the new name
let mut old = self.textures.remove(&old_name).unwrap();
old.name = format!("steven-dynamic:{}", name);
t.name = old.name.clone();
self.textures.insert(format!("steven-dynamic:{}", name), old);
t
} else {
@ -1239,7 +1240,7 @@ struct AnimationFrame {
#[derive(Clone, Debug)]
pub struct Texture {
name: String,
pub name: String,
version: usize,
pub atlas: i32,
x: usize,

View File

@ -14,7 +14,6 @@
use ui;
use render;
use format::{self, Component, TextComponent};
pub struct Connecting {
elements: Option<UIElements>,
@ -23,7 +22,9 @@ pub struct Connecting {
struct UIElements {
logo: ui::logo::Logo,
elements: ui::Collection,
_connect_msg: ui::TextRef,
_msg: ui::TextRef,
_disclaimer: ui::TextRef,
}
@ -38,63 +39,48 @@ impl Connecting {
impl super::Screen for Connecting {
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 logo = ui::logo::Logo::new(renderer.resources.clone(), ui_container);
let mut connect_msg = ui::Formatted::new(
renderer,
Component::Text(TextComponent::new("Connecting to")),
0.0, -16.0
);
connect_msg.set_v_attach(ui::VAttach::Middle);
connect_msg.set_h_attach(ui::HAttach::Center);
elements.add(ui_container.add(connect_msg));
let connect_msg = ui::TextBuilder::new()
.text("Connecting to")
.position(0.0, -16.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
let mut msg = TextComponent::new(&self.target);
msg.modifier.color = Some(format::Color::Yellow);
let mut server_msg = ui::Formatted::new(
renderer,
Component::Text(msg),
0.0, 16.0
);
server_msg.set_v_attach(ui::VAttach::Middle);
server_msg.set_h_attach(ui::HAttach::Center);
elements.add(ui_container.add(server_msg));
let msg = ui::TextBuilder::new()
.text(self.target.clone())
.position(0.0, 16.0)
.colour((255, 255, 85, 255))
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
// Disclaimer
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));
let disclaimer = ui::TextBuilder::new()
.text("Not affiliated with Mojang/Minecraft")
.position(5.0, 5.0)
.colour((255, 200, 200, 255))
.alignment(ui::VAttach::Bottom, ui::HAttach::Right)
.create(ui_container);
self.elements = Some(UIElements {
logo: logo,
elements: elements,
_disclaimer: disclaimer,
_msg: msg,
_connect_msg: connect_msg,
});
}
fn on_deactive(&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) {
// Clean up
{
let elements = self.elements.as_mut().unwrap();
elements.logo.remove(ui_container);
elements.elements.remove_all(ui_container);
}
self.elements = None
}
fn tick(&mut self,
_delta: f64,
renderer: &mut render::Renderer,
ui_container: &mut ui::Container) -> Option<Box<super::Screen>>{
_ui_container: &mut ui::Container) -> Option<Box<super::Screen>>{
let elements = self.elements.as_mut().unwrap();
elements.logo.tick(renderer, ui_container);
elements.logo.tick(renderer);
None
}
}

View File

@ -27,7 +27,11 @@ pub struct EditServerEntry {
struct UIElements {
logo: ui::logo::Logo,
elements: ui::Collection,
_name: ui::TextBoxRef,
_address: ui::TextBoxRef,
_done: ui::ButtonRef,
_cancel: ui::ButtonRef,
}
impl EditServerEntry {
@ -77,104 +81,99 @@ impl EditServerEntry {
impl super::Screen for EditServerEntry {
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 logo = ui::logo::Logo::new(renderer.resources.clone(), ui_container);
// Name
let mut server_name = ui::TextBox::new(
renderer, self.entry_info.as_ref().map_or("", |v| &v.1),
0.0, -20.0, 400.0, 40.0
);
server_name.set_v_attach(ui::VAttach::Middle);
server_name.set_h_attach(ui::HAttach::Center);
server_name.add_submit_func(|_, ui| {
ui.cycle_focus();
});
let ure = ui_container.add(server_name);
let mut server_name_label = ui::Text::new(renderer, "Name:", 0.0, -18.0, 255, 255, 255);
server_name_label.set_parent(&ure);
let server_name_txt = elements.add(ure);
elements.add(ui_container.add(server_name_label));
let server_name = ui::TextBoxBuilder::new()
.input(self.entry_info.as_ref().map_or("", |v| &v.1))
.position(0.0, -20.0)
.size(400.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
ui::TextBuilder::new()
.text("Name:")
.position(0.0, -18.0)
.attach(&mut *server_name.borrow_mut());
// Name
let mut server_address = ui::TextBox::new(
renderer, self.entry_info.as_ref().map_or("", |v| &v.2),
0.0, 40.0, 400.0, 40.0
);
server_address.set_v_attach(ui::VAttach::Middle);
server_address.set_h_attach(ui::HAttach::Center);
server_address.add_submit_func(|_, ui| {
ui.cycle_focus();
});
let ure = ui_container.add(server_address);
let mut server_address_label = ui::Text::new(renderer, "Address:", 0.0, -18.0, 255, 255, 255);
server_address_label.set_parent(&ure);
let server_address_txt = elements.add(ure);
elements.add(ui_container.add(server_address_label));
// Address
let server_address = ui::TextBoxBuilder::new()
.input(self.entry_info.as_ref().map_or("", |v| &v.2))
.position(0.0, 40.0)
.size(400.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
ui::TextBuilder::new()
.text("Address")
.position(0.0, -18.0)
.attach(&mut *server_address.borrow_mut());
// Done
let (mut done, mut txt) = super::new_button_text(
renderer, "Done",
110.0, 100.0, 200.0, 40.0
);
done.set_v_attach(ui::VAttach::Middle);
done.set_h_attach(ui::HAttach::Center);
let re = ui_container.add(done);
txt.set_parent(&re);
let tre = ui_container.add(txt);
let index = self.entry_info.as_ref().map(|v| v.0);
super::button_action(ui_container, re.clone(), Some(tre.clone()), move |game, uic| {
Self::save_servers(
index,
&uic.get(&server_name_txt).get_input(),
&uic.get(&server_address_txt).get_input()
);
game.screen_sys.replace_screen(Box::new(super::ServerList::new(None)));
});
elements.add(re);
elements.add(tre);
let done = ui::ButtonBuilder::new()
.position(110.0, 100.0)
.size(200.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut done = done.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Done")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *done);
done.add_text(txt);
let index = self.entry_info.as_ref().map(|v| v.0);
let server_name = server_name.clone();
let server_address = server_address.clone();
done.add_click_func(move |_, game| {
Self::save_servers(
index,
&server_name.borrow().input,
&server_address.borrow().input,
);
game.screen_sys.replace_screen(Box::new(super::ServerList::new(None)));
true
});
}
// Cancel
let (mut cancel, mut txt) = super::new_button_text(
renderer, "Cancel",
-110.0, 100.0, 200.0, 40.0
);
cancel.set_v_attach(ui::VAttach::Middle);
cancel.set_h_attach(ui::HAttach::Center);
let re = ui_container.add(cancel);
txt.set_parent(&re);
let tre = ui_container.add(txt);
super::button_action(ui_container, re.clone(), Some(tre.clone()), |game, _| {
game.screen_sys.replace_screen(Box::new(super::ServerList::new(None)));
});
elements.add(re);
elements.add(tre);
let cancel = ui::ButtonBuilder::new()
.position(-110.0, 100.0)
.size(200.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut cancel = cancel.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Cancel")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *cancel);
cancel.add_text(txt);
cancel.add_click_func(|_, game| {
game.screen_sys.replace_screen(Box::new(super::ServerList::new(None)));
true
});
}
self.elements = Some(UIElements {
logo: logo,
elements: elements,
_name: server_name,
_address: server_address,
_done: done,
_cancel: cancel,
});
}
fn on_deactive(&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) {
// Clean up
{
let elements = self.elements.as_mut().unwrap();
elements.logo.remove(ui_container);
elements.elements.remove_all(ui_container);
}
self.elements = None
}
fn tick(&mut self,
_delta: f64,
renderer: &mut render::Renderer,
ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
_ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
let elements = self.elements.as_mut().unwrap();
elements.logo.tick(renderer, ui_container);
elements.logo.tick(renderer);
None
}

View File

@ -33,13 +33,13 @@ pub struct Login {
struct UIElements {
logo: ui::logo::Logo,
elements: ui::Collection,
login_btn: ui::ElementRef<ui::Button>,
login_btn_text: ui::ElementRef<ui::Text>,
login_error: ui::ElementRef<ui::Text>,
username_txt: ui::ElementRef<ui::TextBox>,
password_txt: ui::ElementRef<ui::TextBox>,
login_btn: ui::ButtonRef,
login_btn_text: ui::TextRef,
login_error: ui::TextRef,
username_txt: ui::TextBoxRef,
password_txt: ui::TextBoxRef,
_disclaimer: ui::TextRef,
try_login: Rc<Cell<bool>>,
refresh: bool,
login_res: Option<mpsc::Receiver<Result<mojang::Profile, protocol::Error>>>,
@ -56,73 +56,76 @@ impl Login {
impl super::Screen for Login {
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 logo = ui::logo::Logo::new(renderer.resources.clone(), ui_container);
let try_login = Rc::new(Cell::new(false));
// Login
let (mut login, mut txt) = super::new_button_text(renderer, "Login", 0.0, 100.0, 400.0, 40.0);
login.set_v_attach(ui::VAttach::Middle);
login.set_h_attach(ui::HAttach::Center);
let re = ui_container.add(login);
txt.set_parent(&re);
let tre = ui_container.add(txt);
let tl = try_login.clone();
super::button_action(ui_container,
re.clone(),
Some(tre.clone()),
move |_, _| {
tl.set(true);
});
let login_btn = elements.add(re);
let login_btn_text = elements.add(tre);
let login_btn = ui::ButtonBuilder::new()
.position(0.0, 100.0)
.size(400.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
let login_btn_text = ui::TextBuilder::new()
.text("Login")
.position(0.0, 0.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *login_btn.borrow_mut());
{
let mut btn = login_btn.borrow_mut();
btn.add_text(login_btn_text.clone());
let tl = try_login.clone();
btn.add_click_func(move |_, _| {
tl.set(true);
true
});
}
// Login Error
let mut login_error = ui::Text::new(renderer, "", 0.0, 150.0, 255, 50, 50);
login_error.set_v_attach(ui::VAttach::Middle);
login_error.set_h_attach(ui::HAttach::Center);
let login_error = elements.add(ui_container.add(login_error));
let login_error = ui::TextBuilder::new()
.text("")
.position(0.0, 150.0)
.colour((255, 50, 50, 255))
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
// Username
let mut username = ui::TextBox::new(renderer, "", 0.0, -20.0, 400.0, 40.0);
username.set_v_attach(ui::VAttach::Middle);
username.set_h_attach(ui::HAttach::Center);
username.add_submit_func(|_, ui| {
ui.cycle_focus();
});
let ure = ui_container.add(username);
let mut username_label = ui::Text::new(renderer, "Username/Email:", 0.0, -18.0, 255, 255, 255);
username_label.set_parent(&ure);
let username_txt = elements.add(ure);
elements.add(ui_container.add(username_label));
let username_txt = ui::TextBoxBuilder::new()
.position(0.0, -20.0)
.size(400.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
ui::TextBox::make_focusable(&username_txt, ui_container);
ui::TextBuilder::new()
.text("Username/Email:")
.position(0.0, -18.0)
.attach(&mut *username_txt.borrow_mut());
// Password
let mut password = ui::TextBox::new(renderer, "", 0.0, 40.0, 400.0, 40.0);
password.set_v_attach(ui::VAttach::Middle);
password.set_h_attach(ui::HAttach::Center);
password.set_password(true);
let password_txt = ui::TextBoxBuilder::new()
.position(0.0, 40.0)
.size(400.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.password(true)
.create(ui_container);
ui::TextBox::make_focusable(&password_txt, ui_container);
ui::TextBuilder::new()
.text("Password:")
.position(0.0, -18.0)
.attach(&mut *password_txt.borrow_mut());
let tl = try_login.clone();
password.add_submit_func(move |_, _| {
password_txt.borrow_mut().add_submit_func(move |_, _| {
tl.set(true);
});
let pre = ui_container.add(password);
let mut password_label = ui::Text::new(renderer, "Password:", 0.0, -18.0, 255, 255, 255);
password_label.set_parent(&pre);
let password_txt = elements.add(pre);
elements.add(ui_container.add(password_label));
// Disclaimer
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));
let disclaimer = ui::TextBuilder::new()
.text("Not affiliated with Mojang/Minecraft")
.position(5.0, 5.0)
.colour((255, 200, 200, 255))
.alignment(ui::VAttach::Bottom, ui::HAttach::Right)
.create(ui_container);
let profile = mojang::Profile {
username: self.vars.get(auth::CL_USERNAME).clone(),
@ -134,7 +137,6 @@ impl super::Screen for Login {
self.elements = Some(UIElements {
logo: logo,
elements: elements,
profile: profile,
login_btn: login_btn,
login_btn_text: login_btn_text,
@ -143,52 +145,37 @@ impl super::Screen for Login {
refresh: refresh,
login_res: None,
_disclaimer: disclaimer,
username_txt: username_txt,
password_txt: password_txt,
});
}
fn on_deactive(&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) {
// Clean up
{
let elements = self.elements.as_mut().unwrap();
elements.logo.remove(ui_container);
elements.elements.remove_all(ui_container);
}
self.elements = None
}
fn tick(&mut self,
_delta: f64,
renderer: &mut render::Renderer,
ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
_ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
let elements = self.elements.as_mut().unwrap();
if elements.try_login.get() && elements.login_res.is_none() {
elements.try_login.set(false);
let (tx, rx) = mpsc::channel();
elements.login_res = Some(rx);
{
let btn = ui_container.get_mut(&elements.login_btn);
btn.set_disabled(true);
}
{
let txt = ui_container.get_mut(&elements.login_btn_text);
txt.set_text(renderer, "Logging in...");
}
elements.login_btn.borrow_mut().disabled = true;
elements.login_btn_text.borrow_mut().text = "Logging in...".into();
let mut client_token = self.vars.get(auth::AUTH_CLIENT_TOKEN).clone();
if client_token.is_empty() {
client_token = rand::thread_rng().gen_ascii_chars().take(20).collect::<String>();
self.vars.set(auth::AUTH_CLIENT_TOKEN, client_token);
}
let client_token = self.vars.get(auth::AUTH_CLIENT_TOKEN).clone();
let username = {
let txt = ui_container.get(&elements.username_txt);
txt.get_input()
};
let password = {
let txt = ui_container.get(&elements.password_txt);
txt.get_input()
};
let username = elements.username_txt.borrow().input.clone();
let password = elements.password_txt.borrow().input.clone();
let refresh = elements.refresh;
let profile = elements.profile.clone();
thread::spawn(move || {
@ -203,14 +190,8 @@ impl super::Screen for Login {
if let Some(rx) = elements.login_res.as_ref() {
if let Ok(res) = rx.try_recv() {
done = true;
{
let btn = ui_container.get_mut(&elements.login_btn);
btn.set_disabled(false);
}
{
let txt = ui_container.get_mut(&elements.login_btn_text);
txt.set_text(renderer, "Login");
}
elements.login_btn.borrow_mut().disabled = false;
elements.login_btn_text.borrow_mut().text = "Login".into();
match res {
Ok(val) => {
self.vars.set(auth::CL_USERNAME, val.username.clone());
@ -220,8 +201,7 @@ impl super::Screen for Login {
return Some(Box::new(super::ServerList::new(None)));
},
Err(err) => {
let login_error = ui_container.get_mut(&elements.login_error);
login_error.set_text(renderer, &format!("{}", err));
elements.login_error.borrow_mut().text = format!("{}", err);
},
}
}
@ -230,7 +210,7 @@ impl super::Screen for Login {
elements.login_res = None;
}
elements.logo.tick(renderer, ui_container);
elements.logo.tick(renderer);
None
}
}

View File

@ -15,11 +15,12 @@
mod server_list;
pub use self::server_list::*;
mod login;
pub mod settings_menu;
pub use self::login::*;
pub mod connecting;
pub mod edit_server;
pub mod settings_menu;
pub use self::settings_menu::{SettingsMenu, VideoSettingsMenu, AudioSettingsMenu};
use render;
@ -148,40 +149,3 @@ impl ScreenSystem {
current.screen.on_scroll(x, y);
}
}
pub fn new_button_text(renderer: &mut render::Renderer,
val: &str,
x: f64,
y: f64,
w: f64,
h: f64)
-> (ui::Button, ui::Text) {
let btn = ui::Button::new(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);
(btn, text)
}
pub fn button_action<F: Fn(&mut ::Game, &mut ui::Container) + 'static>(ui_container: &mut ui::Container,
btn: ui::ElementRef<ui::Button>,
txt: Option<ui::ElementRef<ui::Text>>,
click: F) {
let button = ui_container.get_mut(&btn);
button.add_hover_func(move |over, _, ui_container| {
let disabled = {
let button = ui_container.get_mut(&btn);
button.is_disabled()
};
let txt = txt.clone();
if let Some(txt) = txt {
let text = ui_container.get_mut(&txt);
text.set_b(if over && !disabled {
160
} else {
255
});
}
});
button.add_click_func(click);
}

View File

@ -40,22 +40,27 @@ pub struct ServerList {
struct UIElements {
logo: ui::logo::Logo,
elements: ui::Collection,
servers: Vec<Server>,
_add_btn: ui::ButtonRef,
_refresh_btn: ui::ButtonRef,
_options_btn: ui::ButtonRef,
_disclaimer: ui::TextRef,
_disconnected: Option<ui::ImageRef>,
}
struct Server {
collection: ui::Collection,
back: ui::ElementRef<ui::Image>,
back: ui::ImageRef,
offset: f64,
y: f64,
motd: ui::ElementRef<ui::Formatted>,
ping: ui::ElementRef<ui::Image>,
players: ui::ElementRef<ui::Text>,
version: ui::ElementRef<ui::Formatted>,
motd: ui::FormattedRef,
ping: ui::ImageRef,
players: ui::TextRef,
version: ui::FormattedRef,
icon: ui::ElementRef<ui::Image>,
icon: ui::ImageRef,
icon_texture: Option<String>,
done_ping: bool,
@ -98,10 +103,9 @@ impl ServerList {
let elements = self.elements.as_mut().unwrap();
*self.needs_reload.borrow_mut() = false;
{
// Clean up previous list entries and icons.
// Clean up previous list icons.
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(&icon);
}
@ -117,161 +121,139 @@ impl ServerList {
let servers = servers_info.find("servers").unwrap().as_array().unwrap();
let mut offset = 0.0;
// Default icon whilst we ping the servers or if the server doesn't provide one
let default_icon = render::Renderer::get_texture(renderer.get_textures_ref(),
"misc/unknown_server");
// General gui icons
let icons = render::Renderer::get_texture(renderer.get_textures_ref(), "gui/icons");
for (index, svr) in servers.iter().enumerate() {
let name = svr.find("name").unwrap().as_string().unwrap().to_owned();
let address = svr.find("address").unwrap().as_string().unwrap().to_owned();
let solid = render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid");
// Everything is attached to this
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 back = ui::ImageBuilder::new()
.texture("steven:solid")
.position(0.0, offset * 100.0)
.size(700.0, 100.0)
.colour((0, 0, 0, 100))
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
let (send, recv) = mpsc::channel::<PingInfo>();
// Make whole entry interactable
{
let mut backr = back.borrow_mut();
let address = address.clone();
backr.add_hover_func(move |this, over, _| {
this.colour.3 = if over {
200
} else {
100
};
false
});
backr.add_click_func(move |_, game| {
game.screen_sys.replace_screen(Box::new(super::connecting::Connecting::new(&address)));
game.connect_to(&address);
true
});
}
// Server name
ui::TextBuilder::new()
.text(name.clone())
.position(100.0, 5.0)
.attach(&mut *back.borrow_mut());
// Server icon
let icon = ui::ImageBuilder::new()
.texture("misc/unknown_server")
.position(5.0, 5.0)
.size(90.0, 90.0)
.attach(&mut *back.borrow_mut());
// Ping indicator
let ping = ui::ImageBuilder::new()
.texture("gui/icons")
.position(5.0, 5.0)
.size(20.0, 16.0)
.texture_coords((0.0, 56.0 / 256.0, 10.0 / 256.0, 8.0 / 256.0))
.alignment(ui::VAttach::Top, ui::HAttach::Right)
.attach(&mut *back.borrow_mut());
// Player count
let players = ui::TextBuilder::new()
.text("???")
.position(30.0, 5.0)
.alignment(ui::VAttach::Top, ui::HAttach::Right)
.attach(&mut *back.borrow_mut());
// Server's message of the day
let motd = ui::FormattedBuilder::new()
.text(Component::Text(TextComponent::new("Connecting...")))
.position(100.0, 23.0)
.max_width(700.0 - (90.0 + 10.0 + 5.0))
.attach(&mut *back.borrow_mut());
// Version information
let version = ui::FormattedBuilder::new()
.text(Component::Text(TextComponent::new("")))
.position(100.0, 5.0)
.max_width(700.0 - (90.0 + 10.0 + 5.0))
.alignment(ui::VAttach::Bottom, ui::HAttach::Left)
.attach(&mut *back.borrow_mut());
// Delete entry button
let delete_entry = ui::ButtonBuilder::new()
.position(0.0, 0.0)
.size(25.0, 25.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Right)
.attach(&mut *back.borrow_mut());
{
let mut btn = delete_entry.borrow_mut();
let txt = ui::TextBuilder::new()
.text("X")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *btn);
btn.add_text(txt);
}
// Edit entry button
let edit_entry = ui::ButtonBuilder::new()
.position(25.0, 0.0)
.size(25.0, 25.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Right)
.attach(&mut *back.borrow_mut());
{
let mut btn = edit_entry.borrow_mut();
let txt = ui::TextBuilder::new()
.text("E")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *btn);
btn.add_text(txt);
let index = index;
let sname = name.clone();
let saddr = address.clone();
btn.add_click_func(move |_, game| {
game.screen_sys.replace_screen(Box::new(super::edit_server::EditServerEntry::new(
Some((index, sname.clone(), saddr.clone()))
)));
true
})
}
let mut server = Server {
collection: ui::Collection::new(),
back: ui_container.add(back),
back: back,
offset: offset,
y: 0.0,
done_ping: false,
recv: recv,
motd: Default::default(),
ping: Default::default(),
players: Default::default(),
version: Default::default(),
motd: motd,
ping: ping,
players: players,
version: version,
icon: Default::default(),
icon: icon,
icon_texture: None,
};
server.collection.add(server.back.clone());
server.update_position();
// Make whole entry interactable
{
let back = ui_container.get_mut(&server.back);
let back_ref = server.back.clone();
let address = address.clone();
back.add_hover_func(move |over, _, ui_container| {
let back = ui_container.get_mut(&back_ref);
back.set_a(if over {
200
} else {
100
});
});
back.add_click_func(move |game, _| {
game.screen_sys.replace_screen(Box::new(super::connecting::Connecting::new(&address)));
game.connect_to(&address);
});
}
// Server name
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));
// Server icon
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.icon = server.collection.add(ui_container.add(icon));
// Ping indicator
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.ping = server.collection.add(ui_container.add(ping));
// Player count
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.players = server.collection.add(ui_container.add(players));
// Server's message of the day
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));
// Version information
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));
// Delete entry button
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()), |_,_| {}); // TOOO: delete entry
server.collection.add(re);
server.collection.add(tre);
// Edit entry button
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);
let index = index;
let sname = name.clone();
let saddr = address.clone();
super::button_action(ui_container, re.clone(), Some(tre.clone()), move |game,_|{
let sname = sname.clone();
let saddr = saddr.clone();
game.screen_sys.replace_screen(Box::new(super::edit_server::EditServerEntry::new(
Some((index, sname, saddr))
)));
});
server.collection.add(re);
server.collection.add(tre);
elements.servers.push(server);
offset += 1.0;
@ -304,7 +286,7 @@ impl ServerList {
let e = format!("{}", err);
let mut msg = TextComponent::new(&e);
msg.modifier.color = Some(format::Color::Red);
drop(send.send(PingInfo {
let _ = send.send(PingInfo {
motd: Component::Text(msg),
ping: time::Duration::seconds(99999),
exists: false,
@ -313,7 +295,7 @@ impl ServerList {
protocol_version: 0,
protocol_name: "".to_owned(),
favicon: None,
}));
});
}
}
});
@ -323,140 +305,129 @@ impl ServerList {
impl super::Screen for ServerList {
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 logo = ui::logo::Logo::new(renderer.resources.clone(), ui_container);
// Refresh the server list
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);
let tre = ui_container.add(txt);
let nr = self.needs_reload.clone();
super::button_action(ui_container,
re.clone(),
Some(tre.clone()),
move |_, _| {
*nr.borrow_mut() = true;
});
elements.add(re);
elements.add(tre);
let refresh = ui::ButtonBuilder::new()
.position(300.0, -50.0 - 15.0)
.size(100.0, 30.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.draw_index(2)
.create(ui_container);
{
let mut refresh = refresh.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Refresh")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *refresh);
refresh.add_text(txt);
let nr = self.needs_reload.clone();
refresh.add_click_func(move |_, _| {
*nr.borrow_mut() = true;
true
})
}
// Add a new server to the list
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()), |game, _|{
game.screen_sys.replace_screen(Box::new(super::edit_server::EditServerEntry::new(
None
)));
});
elements.add(re);
elements.add(tre);
let add = ui::ButtonBuilder::new()
.position(200.0, -50.0 - 15.0)
.size(100.0, 30.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.draw_index(2)
.create(ui_container);
{
let mut add = add.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Add")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *add);
add.add_text(txt);
add.add_click_func(move |_, game| {
game.screen_sys.replace_screen(Box::new(super::edit_server::EditServerEntry::new(
None
)));
true
})
}
// Options menu
let mut options = ui::Button::new(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);
super::button_action(ui_container, re.clone(), None, | game, _ | {
game.screen_sys.add_screen(Box::new(super::SettingsMenu::new(game.vars.clone(), false)));
});
elements.add(re);
elements.add(ui_container.add(cog));
let options = ui::ButtonBuilder::new()
.position(5.0, 25.0)
.size(40.0, 40.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Right)
.create(ui_container);
{
let mut options = options.borrow_mut();
ui::ImageBuilder::new()
.texture("steven:gui/cog")
.position(0.0, 0.0)
.size(40.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *options);
options.add_click_func(|_, game| {
game.screen_sys.add_screen(Box::new(super::SettingsMenu::new(game.vars.clone(), false)));
true
});
}
// Disclaimer
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));
let disclaimer = ui::TextBuilder::new()
.text("Not affiliated with Mojang/Minecraft")
.position(5.0, 5.0)
.colour((255, 200, 200, 255))
.alignment(ui::VAttach::Bottom, ui::HAttach::Right)
.create(ui_container);
// If we are kicked from a server display the reason
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));
}
let disconnected = if let Some(ref disconnect_reason) = self.disconnect_reason {
let (width, height) = ui::Formatted::compute_size(renderer, disconnect_reason, 600.0);
let background = ui::ImageBuilder::new()
.texture("steven:solid")
.position(0.0, 3.0)
.size(width.max(renderer.ui.size_of_string("Disconnected")) + 4.0, height + 4.0 + 16.0)
.colour((0, 0, 0, 100))
.alignment(ui::VAttach::Top, ui::HAttach::Center)
.create(ui_container);
ui::TextBuilder::new()
.text("Disconnected")
.position(0.0, 2.0)
.colour((255, 0, 0, 255))
.alignment(ui::VAttach::Top, ui::HAttach::Center)
.attach(&mut *background.borrow_mut());
ui::FormattedBuilder::new()
.text(disconnect_reason.clone())
.position(0.0, 18.0)
.max_width(600.0)
.alignment(ui::VAttach::Top, ui::HAttach::Center)
.attach(&mut *background.borrow_mut());
Some(background)
} else {
None
};
self.elements = Some(UIElements {
logo: logo,
elements: elements,
servers: Vec::new(),
_add_btn: add,
_refresh_btn: refresh,
_options_btn: options,
_disclaimer: disclaimer,
_disconnected: disconnected,
});
self.reload_server_list(renderer, ui_container);
}
fn on_deactive(&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) {
// Clean up
{
let elements = self.elements.as_mut().unwrap();
elements.logo.remove(ui_container);
elements.elements.remove_all(ui_container);
let mut tex = renderer.get_textures_ref().write().unwrap();
for server in &mut elements.servers {
if let Some(ref icon) = server.icon_texture {
tex.remove_dynamic(&icon);
}
server.collection.remove_all(ui_container);
}
elements.servers.clear();
}
self.elements = None
}
@ -470,18 +441,18 @@ impl super::Screen for ServerList {
}
let elements = self.elements.as_mut().unwrap();
elements.logo.tick(renderer, ui_container);
elements.logo.tick(renderer);
for s in &mut elements.servers {
// Animate the entries
{
let back = ui_container.get_mut(&s.back);
let dy = s.y - back.get_y();
let mut back = s.back.borrow_mut();
let dy = s.y - back.y;
if dy * dy > 1.0 {
let y = back.get_y();
back.set_y(y + delta * dy * 0.1);
let y = back.y;
back.y = y + delta * dy * 0.1;
} else {
back.set_y(s.y);
back.y = s.y;
}
}
@ -491,45 +462,36 @@ impl super::Screen for ServerList {
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);
// Selects the icon for the given ping range
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);
}
s.motd.borrow_mut().set_text(res.motd);
// Selects the icon for the given ping range
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,
};
s.ping.borrow_mut().texture_coords.1 = y;
if res.exists {
{
let players = ui_container.get_mut(&s.players);
let mut players = s.players.borrow_mut();
let txt = if res.protocol_version == protocol::SUPPORTED_PROTOCOL {
players.set_g(255);
players.set_b(255);
players.colour.1 = 255;
players.colour.2 = 255;
format!("{}/{}", res.online, res.max)
} else {
players.set_g(85);
players.set_b(85);
players.colour.1 = 85;
players.colour.2 = 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);
players.text = txt;
}
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);
s.version.borrow_mut().set_text(msg);
}
if let Some(favicon) = res.favicon {
let name: String = rand::thread_rng()
@ -541,16 +503,14 @@ impl super::Screen for ServerList {
let icon_tex = tex.write()
.unwrap()
.put_dynamic(&name, favicon);
let icon = ui_container.get_mut(&s.icon);
icon.set_texture(icon_tex);
s.icon.borrow_mut().texture = icon_tex.name;
}
}
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));
s.motd.borrow_mut().set_text(Component::Text(txt));
}
_ => {}
}

View File

@ -5,49 +5,13 @@ use settings;
use std::rc::Rc;
pub fn new_submenu_button(text: &str, renderer: &mut render::Renderer, ui_container: &mut ui::Container, x: f64, y: f64) -> (ui::ElementRef<ui::Button>, ui::ElementRef<ui::Text>) {
let (mut btn, mut txt) = super::new_button_text(renderer, text, x, y, 300.0, 40.0);
btn.set_v_attach(ui::VAttach::Middle);
btn.set_h_attach(ui::HAttach::Center);
let ui_btn = ui_container.add(btn);
txt.set_parent(&ui_btn);
(ui_btn, ui_container.add(txt))
}
pub fn new_centered_button(text: &str, renderer: &mut render::Renderer, ui_container: &mut ui::Container, y: f64, vertical_attach: ui::VAttach) -> (ui::ElementRef<ui::Button>, ui::ElementRef<ui::Text>) {
let (mut btn, mut txt) = super::new_button_text(renderer, text, 0.0, y, 400.0, 40.0);
btn.set_v_attach(vertical_attach);
btn.set_h_attach(ui::HAttach::Center);
let ui_btn = ui_container.add(btn);
txt.set_parent(&ui_btn);
(ui_btn, ui_container.add(txt))
}
macro_rules! get_bool_str {
($fmt:expr, $val:expr, $val_true:expr, $val_false:expr) => (format!($fmt, if $val {
$val_true
} else {
$val_false
}).as_ref());
($fmt:expr, $val:expr) => (get_bool_string!($fmt, $val, "true", "false"));
}
macro_rules! get_matched_str {
($fmt:expr, $val:expr, $($to_match:expr => $result:expr),*) => (
format!($fmt, match $val {
$($to_match => $result.to_owned(), )*
_ => $val.to_string(),
}).as_ref()
)
}
pub struct UIElements {
elements: ui::Collection,
background: ui::ElementRef<ui::Image>,
background: ui::ImageRef,
_buttons: Vec<ui::ButtonRef>,
}
pub struct SettingsMenu {
vars: Rc<console::Vars>,
_vars: Rc<console::Vars>,
elements: Option<UIElements>,
show_disconnect_button: bool
}
@ -55,7 +19,7 @@ pub struct SettingsMenu {
impl SettingsMenu {
pub fn new(vars: Rc<console::Vars>, show_disconnect_button: bool) -> SettingsMenu {
SettingsMenu {
vars: vars,
_vars: vars,
elements: None,
show_disconnect_button: show_disconnect_button
}
@ -63,77 +27,135 @@ impl SettingsMenu {
}
impl super::Screen for SettingsMenu {
fn on_active(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
let mut elements = ui::Collection::new();
fn on_active(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
let background = ui::ImageBuilder::new()
.texture("steven:solid")
.position(0.0, 0.0)
.size(854.0, 480.0)
.colour((0, 0, 0, 100))
.create(ui_container);
let mut background = ui::Image::new(
render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid"),
0.0, 0.0, 854.0, 480.0,
0.0, 0.0, 1.0, 1.0,
0, 0, 0
);
background.set_a(100);
let background = elements.add(ui_container.add(background));
let mut buttons = vec![];
// From top and down
let (btn_audio_settings, txt_audio_settings) = new_submenu_button("Audio settings...", renderer, ui_container, -160.0, -50.0);
super::button_action(ui_container, btn_audio_settings.clone(), Some(txt_audio_settings.clone()), move |game, _| {
game.screen_sys.add_screen(Box::new(AudioSettingsMenu::new(game.vars.clone())));
});
elements.add(btn_audio_settings);
elements.add(txt_audio_settings);
let audio_settings = ui::ButtonBuilder::new()
.position(-160.0, -50.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut audio_settings = audio_settings.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Audio settings...")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *audio_settings);
audio_settings.add_text(txt);
audio_settings.add_click_func(|_, game| {
game.screen_sys.add_screen(Box::new(AudioSettingsMenu::new(game.vars.clone())));
true
});
}
buttons.push(audio_settings);
let (btn_video_settings, txt_video_settings) = new_submenu_button("Video settings...", renderer, ui_container, 160.0, -50.0);
super::button_action(ui_container, btn_video_settings.clone(), Some(txt_video_settings.clone()), move |game, _| {
game.screen_sys.add_screen(Box::new(VideoSettingsMenu::new(game.vars.clone())));
});
elements.add(btn_video_settings);
elements.add(txt_video_settings);
let video_settings = ui::ButtonBuilder::new()
.position(160.0, -50.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut video_settings = video_settings.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Video settings...")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *video_settings);
video_settings.add_text(txt);
video_settings.add_click_func(|_, game| {
game.screen_sys.add_screen(Box::new(VideoSettingsMenu::new(game.vars.clone())));
true
});
}
buttons.push(video_settings);
let (btn_controls_settings, txt_controls_settings) = new_submenu_button("Controls...", renderer, ui_container, 160.0, 0.0);
super::button_action(ui_container, btn_controls_settings.clone(), Some(txt_controls_settings.clone()), move |_, _| {
// TODO: Implement this...
});
elements.add(btn_controls_settings);
elements.add(txt_controls_settings);
let controls_settings = ui::ButtonBuilder::new()
.position(160.0, 0.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut controls_settings = controls_settings.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Controls...")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *controls_settings);
controls_settings.add_text(txt);
}
buttons.push(controls_settings);
let (btn_locale_settings, txt_locale_settings) = new_submenu_button("Language...", renderer, ui_container, -160.0, 0.0);
super::button_action(ui_container, btn_locale_settings.clone(), Some(txt_locale_settings.clone()), move |_, _| {
// TODO: Implement this...
});
elements.add(btn_locale_settings);
elements.add(txt_locale_settings);
let lang_settings = ui::ButtonBuilder::new()
.position(-160.0, 0.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut lang_settings = lang_settings.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Language...")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *lang_settings);
lang_settings.add_text(txt);
}
buttons.push(lang_settings);
// Center bottom items
let (btn_back_to_game, txt_back_to_game) = new_centered_button("Done", renderer, ui_container, 50.0, ui::VAttach::Bottom);
super::button_action(ui_container, btn_back_to_game.clone(), Some(txt_back_to_game.clone()), move |game, _| {
game.screen_sys.pop_screen();
game.focused = true;
});
elements.add(btn_back_to_game);
elements.add(txt_back_to_game);
let done_button = ui::ButtonBuilder::new()
.position(0.0, 50.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
.create(ui_container);
{
let mut done_button = done_button.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Done")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *done_button);
done_button.add_text(txt);
done_button.add_click_func(|_, game| {
game.screen_sys.pop_screen();
game.focused = true;
true
});
}
buttons.push(done_button);
if self.show_disconnect_button {
let (btn_exit_game, txt_exit_game) = new_centered_button("Disconnect", renderer, ui_container, 100.0, ui::VAttach::Bottom);
super::button_action(ui_container, btn_exit_game.clone(), Some(txt_exit_game.clone()), move |game, _| {
game.server.disconnect(None);
game.screen_sys.replace_screen(Box::new(super::ServerList::new(None)));
});
elements.add(btn_exit_game);
elements.add(txt_exit_game);
let disconnect_button = ui::ButtonBuilder::new()
.position(0.0, 100.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
.create(ui_container);
{
let mut disconnect_button = disconnect_button.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Disconnect")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *disconnect_button);
disconnect_button.add_text(txt);
disconnect_button.add_click_func(|_, game| {
game.server.disconnect(None);
game.screen_sys.replace_screen(Box::new(super::ServerList::new(None)));
true
});
}
buttons.push(disconnect_button);
}
self.elements = Some(UIElements {
elements: elements,
background: background,
_buttons: buttons,
});
}
fn on_deactive(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
{
let elements = self.elements.as_mut().unwrap();
elements.elements.remove_all(ui_container);
}
fn on_deactive(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
self.elements = None;
}
@ -142,15 +164,15 @@ impl super::Screen for SettingsMenu {
let elements = self.elements.as_mut().unwrap();
{
let mode = ui_container.mode;
let background = ui_container.get_mut(&elements.background);
background.set_width(match mode {
let mut background = elements.background.borrow_mut();
background.width = match mode {
ui::Mode::Unscaled(scale) => 854.0 / scale,
ui::Mode::Scaled => renderer.width as f64,
});
background.set_height(match mode {
};
background.height = match mode {
ui::Mode::Unscaled(scale) => 480.0 / scale,
ui::Mode::Scaled => renderer.height as f64,
});
};
}
None
}
@ -180,17 +202,15 @@ impl VideoSettingsMenu {
}
impl super::Screen for VideoSettingsMenu {
fn on_active(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
let mut elements = ui::Collection::new();
fn on_active(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
let background = ui::ImageBuilder::new()
.texture("steven:solid")
.position(0.0, 0.0)
.size(854.0, 480.0)
.colour((0, 0, 0, 100))
.create(ui_container);
let mut background = ui::Image::new(
render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid"),
0.0, 0.0, 854.0, 480.0,
0.0, 0.0, 1.0, 1.0,
0, 0, 0
);
background.set_a(100);
let background = elements.add(ui_container.add(background));
let mut buttons = vec![];
// Load defaults
let r_max_fps = *self.vars.get(settings::R_MAX_FPS);
@ -199,42 +219,92 @@ impl super::Screen for VideoSettingsMenu {
// Setting buttons
// TODO: Slider
let (btn_fov, txt_fov) = new_submenu_button(get_matched_str!("FOV: {}", r_fov, 90 => "Normal", 110 => "Quake pro"), renderer, ui_container, -160.0, -50.0);
elements.add(btn_fov);
elements.add(txt_fov);
let fov_setting = ui::ButtonBuilder::new()
.position(160.0, -50.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut fov_setting = fov_setting.borrow_mut();
let txt = ui::TextBuilder::new()
.text(format!("FOV: {}", match r_fov {
90 => "Normal".into(),
110 => "Quake pro".into(),
val => val.to_string(),
}))
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *fov_setting);
fov_setting.add_text(txt);
}
buttons.push(fov_setting);
let (btn_vsync, txt_vsync) = new_submenu_button(get_bool_str!("VSync: {}", r_vsync, "Enabled", "Disabled"), renderer, ui_container, -160.0, 0.0);
elements.add(txt_vsync.clone());
super::button_action(ui_container, btn_vsync.clone(), Some(txt_vsync.clone()), move | game, ui_container | {
let r_vsync = !*game.vars.get(settings::R_VSYNC);
let txt_vsync = ui_container.get_mut(&txt_vsync);
txt_vsync.set_text(&game.renderer, get_bool_str!("VSync: {}", r_vsync, "Enabled", "Disabled"));
game.vars.set(settings::R_VSYNC, r_vsync);
});
elements.add(btn_vsync);
let vsync_setting = ui::ButtonBuilder::new()
.position(-160.0, 0.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut vsync_setting = vsync_setting.borrow_mut();
let txt = ui::TextBuilder::new()
.text(format!("VSync: {}", if r_vsync { "Enabled" } else { "Disabled" }))
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *vsync_setting);
let txt_vsync = txt.clone();
vsync_setting.add_text(txt);
vsync_setting.add_click_func(move |_, game| {
let r_vsync = !*game.vars.get(settings::R_VSYNC);
txt_vsync.borrow_mut().text = format!("VSync: {}", if r_vsync { "Enabled" } else { "Disabled" });
game.vars.set(settings::R_VSYNC, r_vsync);
true
});
}
buttons.push(vsync_setting);
// TODO: Slider
let (btn_fps_cap, txt_fps_cap) = new_submenu_button(get_matched_str!("FPS cap: {}", r_max_fps, 0 => "Unlimited", 15 => "Potato"), renderer, ui_container, 160.0, 0.0);
elements.add(btn_fps_cap);
elements.add(txt_fps_cap);
let fps_setting = ui::ButtonBuilder::new()
.position(160.0, 0.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.create(ui_container);
{
let mut fps_setting = fps_setting.borrow_mut();
let txt = ui::TextBuilder::new()
.text(format!("FPS cap: {}", match r_max_fps {
0 => "Unlimited".into(),
val => val.to_string(),
}))
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *fps_setting);
fps_setting.add_text(txt);
}
buttons.push(fps_setting);
let (btn_done, txt_done) = new_centered_button("Done", renderer, ui_container, 50.0, ui::VAttach::Bottom);
super::button_action(ui_container, btn_done.clone(), Some(txt_done.clone()), move | game, _ | {
game.screen_sys.pop_screen();
});
elements.add(btn_done);
elements.add(txt_done);
let done_button = ui::ButtonBuilder::new()
.position(0.0, 50.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
.create(ui_container);
{
let mut done_button = done_button.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Done")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *done_button);
done_button.add_text(txt);
done_button.add_click_func(|_, game| {
game.screen_sys.pop_screen();
game.focused = true;
true
});
}
buttons.push(done_button);
self.elements = Some(UIElements {
elements: elements,
background: background,
_buttons: buttons,
});
}
fn on_deactive(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
{
let elements = self.elements.as_mut().unwrap();
elements.elements.remove_all(ui_container);
}
fn on_deactive(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
self.elements = None;
}
@ -243,15 +313,15 @@ impl super::Screen for VideoSettingsMenu {
let elements = self.elements.as_mut().unwrap();
{
let mode = ui_container.mode;
let background = ui_container.get_mut(&elements.background);
background.set_width(match mode {
let mut background = elements.background.borrow_mut();
background.width = match mode {
ui::Mode::Unscaled(scale) => 854.0 / scale,
ui::Mode::Scaled => renderer.width as f64,
});
background.set_height(match mode {
};
background.height = match mode {
ui::Mode::Unscaled(scale) => 480.0 / scale,
ui::Mode::Scaled => renderer.height as f64,
});
};
}
None
}
@ -267,56 +337,59 @@ impl super::Screen for VideoSettingsMenu {
}
pub struct AudioSettingsMenu {
vars: Rc<console::Vars>,
_vars: Rc<console::Vars>,
elements: Option<UIElements>
}
impl AudioSettingsMenu {
pub fn new(vars: Rc<console::Vars>) -> AudioSettingsMenu {
AudioSettingsMenu {
vars: vars,
_vars: vars,
elements: None
}
}
}
impl super::Screen for AudioSettingsMenu {
fn on_active(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
let mut elements = ui::Collection::new();
fn on_active(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
let background = ui::ImageBuilder::new()
.texture("steven:solid")
.position(0.0, 0.0)
.size(854.0, 480.0)
.colour((0, 0, 0, 100))
.create(ui_container);
let mut background = ui::Image::new(
render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid"),
0.0, 0.0, 854.0, 480.0,
0.0, 0.0, 1.0, 1.0,
0, 0, 0
);
background.set_a(100);
let background = elements.add(ui_container.add(background));
let mut buttons = vec![];
let master_volume = *self.vars.get(settings::CL_MASTER_VOLUME);
// TODO
let (btn_master_volume, txt_master_volume) = new_centered_button(&master_volume.to_string(), renderer, ui_container, -150.0, ui::VAttach::Middle);
elements.add(btn_master_volume);
elements.add(txt_master_volume);
let (btn_done, txt_done) = new_centered_button("Done", renderer, ui_container, 50.0, ui::VAttach::Bottom);
super::button_action(ui_container, btn_done.clone(), Some(txt_done.clone()), move | game, _ | {
game.screen_sys.pop_screen();
});
elements.add(btn_done);
elements.add(txt_done);
let done_button = ui::ButtonBuilder::new()
.position(0.0, 50.0)
.size(300.0, 40.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
.create(ui_container);
{
let mut done_button = done_button.borrow_mut();
let txt = ui::TextBuilder::new()
.text("Done")
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
.attach(&mut *done_button);
done_button.add_text(txt);
done_button.add_click_func(|_, game| {
game.screen_sys.pop_screen();
game.focused = true;
true
});
}
buttons.push(done_button);
self.elements = Some(UIElements {
elements: elements,
background: background,
_buttons: buttons,
});
}
fn on_deactive(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
{
let elements = self.elements.as_mut().unwrap();
elements.elements.remove_all(ui_container);
}
fn on_deactive(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
self.elements = None;
}
@ -325,15 +398,15 @@ impl super::Screen for AudioSettingsMenu {
let elements = self.elements.as_mut().unwrap();
{
let mode = ui_container.mode;
let background = ui_container.get_mut(&elements.background);
background.set_width(match mode {
let mut background = elements.background.borrow_mut();
background.width = match mode {
ui::Mode::Unscaled(scale) => 854.0 / scale,
ui::Mode::Scaled => renderer.width as f64,
});
background.set_height(match mode {
};
background.height = match mode {
ui::Mode::Unscaled(scale) => 480.0 / scale,
ui::Mode::Scaled => renderer.height as f64,
});
};
}
None
}

View File

@ -1,128 +0,0 @@
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[derive(Clone, Copy)]
pub struct BatchRef<T: UIElement> {
index: usize,
ty: PhantomData<T>,
}
ui_element!(Batch {
width: f64,
height: f64,
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,
elements: Vec::new(),
})
}
fn update(&mut self, _: &mut render::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.elements {
let reg = e.get_draw_region(sx, sy, r);
e.set_dirty(true);
self.data.extend(e.draw(renderer, &reg, width, height, delta));
}
}
&self.data
}
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!(width, f64, get_width, set_width);
lazy_field!(height, f64, get_height, set_height);
}
impl UIElement for Batch {
fn wrap(self) -> Element {
Element::Batch(self)
}
fn unwrap_ref<'a>(e: &'a Element) -> &'a Batch {
match e {
&Element::Batch(ref val) => val,
_ => panic!("Incorrect type"),
}
}
fn unwrap_ref_mut<'a>(e: &'a mut Element) -> &'a mut Batch {
match e {
&mut Element::Batch(ref mut val) => val,
_ => panic!("Incorrect type"),
}
}
fn get_attachment(&self) -> (VAttach, HAttach) {
(self.v_attach, self.h_attach)
}
fn get_offset(&self) -> (f64, f64) {
(self.x, self.y)
}
fn get_size(&self) -> (f64, f64) {
(self.width, self.height)
}
}

View File

@ -1,144 +0,0 @@
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
ui_element!(Button {
width: f64,
height: f64,
disabled: bool,
});
impl Button {
base_impl!();
pub fn new(x: f64, y: f64, w: f64, h: f64) -> Button {
let mut btn = ui_create!(Button {
x: x,
y: y,
width: w,
height: h,
disabled: false,
});
btn.add_hover_func(|_,_,_|{}); // Force hover events to be called
btn
}
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 {
self.dirty = false;
let sx = r.w / self.width;
let sy = r.h / self.height;
let offset = match (self.disabled, self.hovered) {
(true, _) => 46.0,
(false, true) => 86.0,
(false, false) => 66.0,
};
let texture = render::Renderer::get_texture(renderer.get_textures_ref(), "gui/widgets")
.relative(0.0, offset / 256.0, 200.0 / 256.0, 20.0 / 256.0);
self.data.clear();
self.data.extend(render::ui::UIElement::new(&texture, r.x, r.y, 4.0 * sx, 4.0 * sy, 0.0, 0.0, 2.0/200.0, 2.0/20.0).bytes(width, height));
self.data.extend(render::ui::UIElement::new(&texture, r.x + r.w - 4.0 * sx, r.y, 4.0 * sx, 4.0 * sy, 198.0/200.0, 0.0, 2.0/200.0, 2.0/20.0).bytes(width, height));
self.data.extend(render::ui::UIElement::new(&texture, r.x, r.y + r.h - 6.0 * sy, 4.0 * sx, 6.0 * sy, 0.0, 17.0/20.0, 2.0/200.0, 3.0/20.0).bytes(width, height));
self.data.extend(render::ui::UIElement::new(&texture, r.x + r.w - 4.0 * sx, r.y + r.h - 6.0 * sy, 4.0 * sx, 6.0 * sy, 198.0/200.0, 17.0/20.0, 2.0/200.0, 3.0/20.0).bytes(width, height));
let w = ((r.w / sx)/2.0) - 4.0;
self.data.extend(render::ui::UIElement::new(
&texture.relative(2.0/200.0, 0.0, 196.0/200.0, 2.0/20.0),
r.x+4.0*sx, r.y, r.w - 8.0 * sx, 4.0 * sy, 0.0, 0.0, w/196.0, 1.0).bytes(width, height)
);
self.data.extend(render::ui::UIElement::new(
&texture.relative(2.0/200.0, 17.0/20.0, 196.0/200.0, 3.0/20.0),
r.x+4.0*sx, r.y+r.h-6.0*sy, r.w - 8.0 * sx, 6.0 * sy, 0.0, 0.0, w/196.0, 1.0).bytes(width, height)
);
let h = ((r.h / sy)/2.0) - 5.0;
self.data.extend(render::ui::UIElement::new(
&texture.relative(0.0/200.0, 2.0/20.0, 2.0/200.0, 15.0/20.0),
r.x, r.y + 4.0*sy, 4.0 * sx, r.h - 10.0*sy, 0.0, 0.0, 1.0, h/16.0).bytes(width, height)
);
self.data.extend(render::ui::UIElement::new(
&texture.relative(198.0/200.0, 2.0/20.0, 2.0/200.0, 15.0/20.0),
r.x+r.w - 4.0 * sx, r.y + 4.0*sy, 4.0 * sx, r.h - 10.0*sy, 0.0, 0.0, 1.0, h/16.0).bytes(width, height)
);
self.data.extend(render::ui::UIElement::new(
&texture.relative(2.0/200.0, 2.0/20.0, 196.0/200.0, 15.0/20.0),
r.x+4.0*sx, r.y+4.0*sy, r.w - 8.0 * sx, r.h - 10.0 * sy, 0.0, 0.0, w/196.0, h/16.0).bytes(width, height)
);
}
&self.data
}
lazy_field!(width, f64, get_width, set_width);
lazy_field!(height, f64, get_height, set_height);
lazy_field!(disabled, bool, is_disabled, set_disabled);
}
impl UIElement for Button {
fn wrap(self) -> Element {
Element::Button(self)
}
fn unwrap_ref<'a>(e: &'a Element) -> &'a Button {
match e {
&Element::Button(ref val) => val,
_ => panic!("Incorrect type"),
}
}
fn unwrap_ref_mut<'a>(e: &'a mut Element) -> &'a mut Button {
match e {
&mut Element::Button(ref mut val) => val,
_ => panic!("Incorrect type"),
}
}
fn get_attachment(&self) -> (VAttach, HAttach) {
(self.v_attach, self.h_attach)
}
fn get_offset(&self) -> (f64, f64) {
(self.x, self.y)
}
fn get_size(&self) -> (f64, f64) {
(self.width, self.height)
}
}

View File

@ -1,237 +0,0 @@
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
ui_element!(Formatted {
val: format::Component,
width: f64,
height: f64,
scale_x: f64,
scale_y: f64,
text: Vec<Element>,
max_width: f64,
lines: usize,
});
impl Formatted {
base_impl!();
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,
scale_x: 1.0,
scale_y: 1.0,
text: Vec::new(),
max_width: -1.0,
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 = ui_create!(Formatted {
val: val,
x: x,
y: y,
width: 0.0,
height: 18.0,
scale_x: 1.0,
scale_y: 1.0,
text: Vec::new(),
max_width: max_width,
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 init_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.init_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 = e.get_draw_region(sx, sy, r);
e.set_dirty(true);
self.data.extend(e.draw(renderer, &reg, width, height, delta));
}
}
&self.data
}
lazy_field!(width, f64, get_width, set_width);
lazy_field!(height, f64, get_height, set_height);
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"),
}
}
fn get_attachment(&self) -> (VAttach, HAttach) {
(self.v_attach, self.h_attach)
}
fn get_offset(&self) -> (f64, f64) {
(self.x, self.y)
}
fn get_size(&self) -> (f64, f64) {
((self.width + 2.0) * self.scale_x, self.height * self.scale_y)
}
}
struct FormatState<'a> {
max_width: f64,
lines: usize,
offset: f64,
width: f64,
text: Vec<Element>,
renderer: &'a render::Renderer,
}
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.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();
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)
}
}

View File

@ -1,151 +0,0 @@
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
ui_element!(Image {
texture: render::Texture,
width: f64,
height: f64,
t_x: f64,
t_y: f64,
t_width: f64,
t_height: f64,
r: u8,
g: u8,
b: u8,
a: u8,
});
impl Image {
base_impl!();
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,
x: x,
y: y,
width: w,
height: h,
t_x: t_x,
t_y: t_y,
t_width: t_width,
t_height: t_height,
r: r,
g: g,
b: b,
a: 255,
})
}
fn update(&mut self, _: &mut render::Renderer) {
}
fn draw(&mut self,
renderer: &mut render::Renderer,
r: &Region,
width: f64,
height: f64,
_: f64)
-> &Vec<u8> {
if self.dirty {
self.dirty = false;
self.texture = renderer.check_texture(self.texture.clone());
let mut e = render::ui::UIElement::new(&self.texture,
r.x,
r.y,
r.w,
r.h,
self.t_x,
self.t_y,
self.t_width,
self.t_height);
e.r = self.r;
e.g = self.g;
e.b = self.b;
e.a = self.a;
e.layer = self.layer;
self.data = e.bytes(width, height);
}
&self.data
}
pub fn get_texture(&self) -> render::Texture {
self.texture.clone()
}
pub fn set_texture(&mut self, val: render::Texture) {
self.texture = val;
self.dirty = true;
}
lazy_field!(width, f64, get_width, set_width);
lazy_field!(height, f64, get_height, set_height);
lazy_field!(t_x, f64, get_t_x, set_t_x);
lazy_field!(t_y, f64, get_t_y, set_t_y);
lazy_field!(t_width, f64, get_t_width, set_t_width);
lazy_field!(t_height, f64, get_t_height, set_t_height);
lazy_field!(r, u8, get_r, set_r);
lazy_field!(g, u8, get_g, set_g);
lazy_field!(b, u8, get_b, set_b);
lazy_field!(a, u8, get_a, set_a);
}
impl UIElement for Image {
fn wrap(self) -> Element {
Element::Image(self)
}
fn unwrap_ref<'a>(e: &'a Element) -> &'a Image {
match e {
&Element::Image(ref val) => val,
_ => panic!("Incorrect type"),
}
}
fn unwrap_ref_mut<'a>(e: &'a mut Element) -> &'a mut Image {
match e {
&mut Element::Image(ref mut val) => val,
_ => panic!("Incorrect type"),
}
}
fn get_attachment(&self) -> (VAttach, HAttach) {
(self.v_attach, self.h_attach)
}
fn get_offset(&self) -> (f64, f64) {
(self.x, self.y)
}
fn get_size(&self) -> (f64, f64) {
(self.width, self.height)
}
}

View File

@ -9,12 +9,10 @@ use rand;
use rand::Rng;
pub struct Logo {
resources: Arc<RwLock<resources::Manager>>,
_shadow: ui::BatchRef,
_layer0: ui::BatchRef,
shadow: ui::ElementRef<ui::Batch>,
layer0: ui::ElementRef<ui::Batch>,
text: ui::ElementRef<ui::Text>,
text: ui::TextRef,
text_base_scale: f64,
text_orig_x: f64,
text_index: isize,
@ -23,40 +21,27 @@ pub struct Logo {
impl Logo {
pub fn new(resources: Arc<RwLock<resources::Manager>>,
renderer: &mut render::Renderer,
ui_container: &mut ui::Container)
-> Logo {
let mut l = Logo {
resources: resources,
shadow: Default::default(),
layer0: Default::default(),
text: Default::default(),
text_base_scale: 0.0,
text_orig_x: 0.0,
text_index: -1,
text_strings: Vec::new(),
};
l.init(renderer, ui_container);
l
}
fn init(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
let logo_str = {
let res = self.resources.read().unwrap();
let res = resources.read().unwrap();
let mut logo = res.open("steven", "logo/logo.txt").unwrap();
let mut logo_str = String::new();
logo.read_to_string(&mut logo_str).unwrap();
logo_str
};
let solid = render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid");
let front = render::Renderer::get_texture(renderer.get_textures_ref(), "blocks/planks_oak");
let mut shadow_batch = ui::Batch::new(0.0, 8.0, 100.0, 100.0);
let mut layer0 = ui::Batch::new(0.0, 8.0, 100.0, 100.0);
shadow_batch.set_h_attach(ui::HAttach::Center);
layer0.set_h_attach(ui::HAttach::Center);
let shadow_batch = ui::BatchBuilder::new()
.position(0.0, 8.0)
.size(100.0, 100.0)
.alignment(ui::VAttach::Top, ui::HAttach::Center)
.create(ui_container);
let layer0 = ui::BatchBuilder::new()
.position(0.0, 8.0)
.size(100.0, 100.0)
.draw_index(1)
.alignment(ui::VAttach::Top, ui::HAttach::Center)
.create(ui_container);
let mut row = 0;
for line in logo_str.lines() {
@ -74,96 +59,96 @@ impl Logo {
} else {
(170, 170, 170)
};
let mut shadow = ui::Image::new(solid.clone(),
(x + 2) as f64,
(y + 4) as f64,
4.0,
8.0,
0.0,
0.0,
1.0,
1.0,
0,
0,
0);
shadow.set_a(100);
shadow_batch.add(shadow);
ui::ImageBuilder::new()
.texture("steven:solid")
.position((x + 2) as f64, (y + 4) as f64)
.size(4.0, 8.0)
.colour((0, 0, 0, 100))
.attach(&mut *shadow_batch.borrow_mut());
let img = ui::Image::new(front.clone(),
x as f64,
y as f64,
4.0,
8.0,
(x % 16) as f64 / 16.0,
(y % 16) as f64 / 16.0,
4.0 / 16.0,
8.0 / 16.0,
r,
g,
b);
layer0.add(img);
ui::ImageBuilder::new()
.texture("minecraft:blocks/planks_oak")
.position(x as f64, y as f64)
.size(4.0, 8.0)
.texture_coords((
(x % 16) as f64 / 16.0, (y % 16) as f64 / 16.0,
4.0 / 16.0, 8.0 / 16.0
))
.colour((r, g, b, 255))
.attach(&mut *layer0.borrow_mut());
let width = (x + 4) as f64;
if shadow_batch.get_width() < width {
shadow_batch.set_width(width);
layer0.set_width(width);
if shadow_batch.borrow().width < width {
shadow_batch.borrow_mut().width = width;
layer0.borrow_mut().width = width;
}
}
row += 1;
}
shadow_batch.borrow_mut().height = row as f64 * 8.0;
layer0.borrow_mut().height = row as f64 * 8.0;
let mut text_strings = vec![];
{
let res = self.resources.read().unwrap();
let res = resources.read().unwrap();
let mut splashes = res.open_all("minecraft", "texts/splashes.txt");
for file in &mut splashes {
let mut texts = String::new();
file.read_to_string(&mut texts).unwrap();
for line in texts.lines() {
self.text_strings.push(line.to_owned().replace("\r", ""));
text_strings.push(line.to_owned().replace("\r", ""));
}
}
let mut r: rand::XorShiftRng = rand::SeedableRng::from_seed([45, 64, 32, 12]);
r.shuffle(&mut self.text_strings[..]);
r.shuffle(&mut text_strings[..]);
}
shadow_batch.set_height(row as f64 * 8.0);
layer0.set_height(row as f64 * 8.0);
let txt = ui::TextBuilder::new()
.text("")
.position(0.0, -8.0)
.colour((255, 255, 0, 255))
.rotation(-consts::PI / 8.0)
.alignment(ui::VAttach::Bottom, ui::HAttach::Right)
.draw_index(1)
.create(&mut *layer0.borrow_mut());
self.shadow = ui_container.add(shadow_batch);
self.layer0 = ui_container.add(layer0);
let mut txt = ui::Text::new(renderer, "", 0.0, -8.0, 255, 255, 0);
txt.set_h_attach(ui::HAttach::Right);
txt.set_v_attach(ui::VAttach::Bottom);
txt.set_parent(&self.shadow);
txt.set_rotation(-consts::PI / 8.0);
let width = txt.get_width();
self.text_base_scale = 300.0 / width;
if self.text_base_scale > 1.0 {
self.text_base_scale = 1.0;
let width = txt.borrow().width;
let mut text_base_scale = 300.0 / width;
if text_base_scale > 1.0 {
text_base_scale = 1.0;
}
txt.borrow_mut().x = (-width / 2.0) * text_base_scale;
let text_orig_x = txt.borrow().x;
Logo {
_shadow: shadow_batch,
_layer0: layer0,
text: txt,
text_base_scale: text_base_scale,
text_orig_x: text_orig_x,
text_index: -1,
text_strings: text_strings,
}
txt.set_x((-width / 2.0) * self.text_base_scale);
self.text_orig_x = txt.get_x();
self.text = ui_container.add(txt);
}
pub fn tick(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
pub fn tick(&mut self, renderer: &mut render::Renderer) {
let now = time::now().to_timespec();
// Splash text
let text = ui_container.get_mut(&self.text);
let text_index = (now.sec / 15) as isize % self.text_strings.len() as isize;
let mut text = self.text.borrow_mut();
if self.text_index != text_index {
self.text_index = text_index;
text.set_text(renderer, &self.text_strings[text_index as usize]);
let width = text.get_width();
text.text = self.text_strings[text_index as usize].clone();
let width = (renderer.ui.size_of_string(&text.text) + 2.0) * text.scale_x;
self.text_base_scale = 300.0 / width;
if self.text_base_scale > 1.0 {
self.text_base_scale = 1.0;
}
text.set_x((-width / 2.0) * self.text_base_scale);
self.text_orig_x = text.get_x();
text.x =(-width / 2.0) * self.text_base_scale;
self.text_orig_x = text.x;
}
let timer = now.nsec as f64 / 1000000000.0;
@ -172,15 +157,9 @@ impl Logo {
offset = 2.0 - offset;
}
offset = ((offset * consts::PI).cos() + 1.0) / 2.0;
text.set_scale_x((0.7 + (offset / 3.0)) * self.text_base_scale);
text.set_scale_y((0.7 + (offset / 3.0)) * self.text_base_scale);
let scale = text.get_scale_x();
text.set_x(self.text_orig_x * scale * self.text_base_scale);
}
pub fn remove(&self, ui_container: &mut ui::Container) {
ui_container.remove(&self.shadow);
ui_container.remove(&self.layer0);
ui_container.remove(&self.text);
text.scale_x = (0.7 + (offset / 3.0)) * self.text_base_scale;
text.scale_y = (0.7 + (offset / 3.0)) * self.text_base_scale;
let scale = text.scale_x;
text.x =self.text_orig_x * scale * self.text_base_scale;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,156 +0,0 @@
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
ui_element!(Text {
val: String,
width: f64,
height: f64,
scale_x: f64,
scale_y: f64,
rotation: f64,
r: u8,
g: u8,
b: u8,
a: u8,
});
impl Text {
base_impl!();
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,
scale_x: 1.0,
scale_y: 1.0,
rotation: 0.0,
r: r,
g: g,
b: b,
a: 255,
})
}
fn update(&mut self, renderer: &mut render::Renderer) {
self.width = renderer.ui.size_of_string(&self.val);
}
fn draw(&mut self,
renderer: &mut render::Renderer,
r: &Region,
width: f64,
height: f64,
_: f64)
-> &Vec<u8> {
if self.dirty {
self.dirty = false;
let sx = r.w / self.width;
let sy = r.h / self.height;
let mut text = if self.rotation == 0.0 {
renderer.ui.new_text_scaled(&self.val,
r.x,
r.y,
sx * self.scale_x,
sy * self.scale_y,
self.r,
self.g,
self.b)
} else {
let c = self.rotation.cos();
let s = self.rotation.sin();
let tmpx = r.w / 2.0;
let tmpy = r.h / 2.0;
let w = (tmpx * c - tmpy * s).abs();
let h = (tmpy * c + tmpx * s).abs();
renderer.ui.new_text_rotated(&self.val,
r.x + w - (r.w / 2.0),
r.y + h - (r.h / 2.0),
sx * self.scale_x,
sy * self.scale_y,
self.rotation,
self.r,
self.g,
self.b)
};
for e in &mut text.elements {
e.a = self.a;
e.layer = self.layer;
}
self.data = text.bytes(width, height);
}
&self.data
}
pub fn get_text(&self) -> &str {
&self.val
}
pub fn set_text(&mut self, renderer: &render::Renderer, val: &str) {
self.dirty = true;
self.val = val.to_owned();
self.width = renderer.ui.size_of_string(val);
}
lazy_field!(width, f64, get_width, set_width);
lazy_field!(height, f64, get_height, set_height);
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);
lazy_field!(r, u8, get_r, set_r);
lazy_field!(g, u8, get_g, set_g);
lazy_field!(b, u8, get_b, set_b);
}
impl UIElement for Text {
fn wrap(self) -> Element {
Element::Text(self)
}
fn unwrap_ref<'a>(e: &'a Element) -> &'a Text {
match e {
&Element::Text(ref val) => val,
_ => panic!("Incorrect type"),
}
}
fn unwrap_ref_mut<'a>(e: &'a mut Element) -> &'a mut Text {
match e {
&mut Element::Text(ref mut val) => val,
_ => panic!("Incorrect type"),
}
}
fn get_attachment(&self) -> (VAttach, HAttach) {
(self.v_attach, self.h_attach)
}
fn get_offset(&self) -> (f64, f64) {
(self.x, self.y)
}
fn get_size(&self) -> (f64, f64) {
((self.width + 2.0) * self.scale_x, self.height * self.scale_y)
}
}

View File

@ -1,181 +0,0 @@
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
ui_element!(TextBox {
input: String,
width: f64,
height: f64,
password: bool,
button: Button,
text: Text,
cursor_tick: f64,
was_focused: bool,
submit_funcs: Vec<Rc<ClickFunc>>,
});
impl TextBox {
base_impl!();
pub fn new(renderer: &render::Renderer,
input: &str,
x: f64, y: f64, w: f64, h: f64
) -> TextBox {
let mut btn = Button::new(0.0, 0.0, w, h);
btn.set_disabled(true);
let mut txt = Text::new(renderer, input, 5.0, 0.0, 255, 255, 255);
txt.set_v_attach(VAttach::Middle);
let mut tbox = ui_create!(TextBox {
input: input.to_owned(),
x: x,
y: y,
width: w,
height: h,
password: false,
button: btn,
text: txt,
cursor_tick: 0.0,
was_focused: false,
submit_funcs: vec![],
});
tbox.can_focus = true;
tbox
}
fn update(&mut self, renderer: &mut render::Renderer) {
self.text.update(renderer);
}
fn draw(&mut self,
renderer: &mut render::Renderer,
r: &Region,
width: f64,
height: f64,
delta: f64)
-> &Vec<u8> {
if self.dirty || self.focused || self.was_focused {
self.was_focused = self.focused;
self.data.clear();
self.dirty = false;
self.cursor_tick += delta;
if self.cursor_tick > 3000.0 {
self.cursor_tick -= 3000.0;
}
let mut txt = self.transform_input();
if self.focused && ((self.cursor_tick / 30.0) as i32) % 2 == 0 {
txt.push('|');
}
self.text.set_text(renderer, &txt);
let sx = r.w / self.width;
let sy = r.h / self.height;
let reg = Container::get_draw_region_raw(&self.button, sx, sy, r);
self.button.dirty = true;
self.data.extend(self.button.draw(renderer, &reg, width, height, delta));
let reg = Container::get_draw_region_raw(&self.text, sx, sy, r);
self.text.dirty = true;
self.data.extend(self.text.draw(renderer, &reg, width, height, delta));
}
&self.data
}
pub fn get_input(&self) -> String {
self.input.clone()
}
pub fn set_input(&mut self, renderer: &render::Renderer, input: &str) {
self.dirty = true;
self.input = input.to_owned();
let txt = self.transform_input();
self.text.set_text(renderer, &txt);
}
pub fn add_submit_func<F: Fn(&mut ::Game, &mut Container) + 'static>(&mut self, f: F) {
self.submit_funcs.push(Rc::new(f));
}
fn transform_input(&self) -> String {
if self.password {
::std::iter::repeat('*').take(self.input.len()).collect()
} else {
self.input.clone()
}
}
lazy_field!(width, f64, get_width, set_width);
lazy_field!(height, f64, get_height, set_height);
lazy_field!(password, bool, is_password, set_password);
}
impl UIElement for TextBox {
fn key_press(&mut self, _game: &mut ::Game, key: Keycode, down: bool) -> Vec<Rc<ClickFunc>> {
match (key, down) {
(Keycode::Backspace, false) => {self.input.pop();},
(Keycode::Return, false) => return self.submit_funcs.clone(),
_ => {},
}
vec![]
}
fn key_type(&mut self, _game: &mut ::Game, c: char) -> Vec<Rc<ClickFunc>> {
self.input.push(c);
vec![]
}
fn wrap(self) -> Element {
Element::TextBox(self)
}
fn unwrap_ref<'a>(e: &'a Element) -> &'a TextBox {
match e {
&Element::TextBox(ref val) => val,
_ => panic!("Incorrect type"),
}
}
fn unwrap_ref_mut<'a>(e: &'a mut Element) -> &'a mut TextBox {
match e {
&mut Element::TextBox(ref mut val) => val,
_ => panic!("Incorrect type"),
}
}
fn get_attachment(&self) -> (VAttach, HAttach) {
(self.v_attach, self.h_attach)
}
fn get_offset(&self) -> (f64, f64) {
(self.x, self.y)
}
fn get_size(&self) -> (f64, f64) {
(self.width, self.height)
}
}