stevenarella/src/console/mod.rs

216 lines
5.6 KiB
Rust

// Copyright 2015 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.
use std::marker::PhantomData;
use std::collections::HashMap;
use std::any::Any;
use std::sync::{Arc, Mutex};
use log;
use ui;
use render;
use format::{Component, TextComponent, Color};
pub struct CVar<T: Sized + Any + 'static> {
pub name: &'static str,
pub ty: PhantomData<T>,
pub description: &'static str,
pub mutable: bool,
pub serializable: bool,
pub default: &'static Fn() -> T,
}
impl Var for CVar<String> {}
pub trait Var {
}
pub struct Console {
vars: HashMap<&'static str, Box<Var>>,
var_values: HashMap<&'static str, Box<Any>>,
history: Vec<Component>,
collection: ui::Collection,
active: bool,
position: f64,
}
impl Console {
pub fn new() -> Console {
let mut con = Console {
vars: HashMap::new(),
var_values: HashMap::new(),
history: Vec::with_capacity(200),
collection: ui::Collection::new(),
active: false,
position: -220.0,
};
for _ in 0 .. 200 {
con.history.push(Component::Text(
TextComponent::new("")
));
}
con
}
pub fn register<T: Sized + Any>(&mut self, var: CVar<T>) where CVar<T> : Var {
if self.vars.contains_key(var.name) {
panic!("Key registered twice {}", var.name);
}
self.var_values.insert(var.name, Box::new((var.default)()));
self.vars.insert(var.name, Box::new(var));
}
pub fn get<T: Sized + Any>(&self, var: CVar<T>) -> &T where CVar<T> : Var {
// Should never fail
self.var_values.get(var.name).unwrap().downcast_ref::<T>().unwrap()
}
pub fn set<T: Sized + Any>(&mut self, var: CVar<T>, val: T) where CVar<T> : Var {
self.var_values.insert(var.name, Box::new(val));
}
pub fn is_active(&self) -> bool {
self.active
}
pub fn toggle(&mut self) {
self.active = !self.active;
}
pub fn tick(&mut self, ui_container: &mut ui::Container, renderer: &mut 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 {
return;
}
if self.active {
if self.position < 0.0 {
self.position += delta * 4.0;
} else {
self.position = 0.0;
}
} else {
if self.position > -220.0 {
self.position -= delta * 4.0;
} 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));
}
for fmt in lines {
self.collection.add(fmt);
}
}
fn log(&mut self, record: &log::LogRecord) {
let mut file = record.location().file();
if let Some(pos) = file.rfind("src/") {
file = &file[pos + 4..];
}
println!("[{}:{}][{}] {}", file, record.location().line(), record.level(), record.args());
self.history.remove(0);
let mut msg = TextComponent::new("");
msg.modifier.extra = Some(vec![
Component::Text(TextComponent::new("[")),
{
let mut msg = TextComponent::new(file);
msg.modifier.color = Some(Color::Green);
Component::Text(msg)
},
Component::Text(TextComponent::new(":")),
{
let mut msg = TextComponent::new(&format!("{}", record.location().line()));
msg.modifier.color = Some(Color::Aqua);
Component::Text(msg)
},
Component::Text(TextComponent::new("]")),
Component::Text(TextComponent::new("[")),
{
let mut msg = TextComponent::new(&format!("{}", record.level()));
msg.modifier.color = Some(match record.level() {
log::LogLevel::Debug => Color::Green,
log::LogLevel::Error => Color::Red,
log::LogLevel::Warn => Color::Yellow,
log::LogLevel::Info => Color::Aqua,
log::LogLevel::Trace => Color::Blue,
});
Component::Text(msg)
},
Component::Text(TextComponent::new("] ")),
Component::Text(TextComponent::new(&format!("{}", record.args())))
]);
self.history.push(Component::Text(
msg
));
}
}
pub struct ConsoleProxy {
console: Arc<Mutex<Console>>,
}
impl ConsoleProxy {
pub fn new(con: Arc<Mutex<Console>>) -> ConsoleProxy {
ConsoleProxy {
console: con,
}
}
}
impl log::Log for ConsoleProxy {
fn enabled(&self, metadata: &log::LogMetadata) -> bool {
metadata.level() <= log::LogLevel::Trace
}
fn log(&self, record: &log::LogRecord) {
if self.enabled(record.metadata()) {
self.console.lock().unwrap().log(record);
}
}
}
unsafe impl Send for ConsoleProxy {}
unsafe impl Sync for ConsoleProxy {}