mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'timo_assets_rebased' into 'master'
New asset system, split hud.rs, macros for img and font id structs (Timo) See merge request veloren/veloren!111 Former-commit-id: 4765e936dd445d651348b2460707faacd5273d61
This commit is contained in:
commit
4d29dbdb18
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2285,6 +2285,8 @@ version = "0.2.0"
|
||||
dependencies = [
|
||||
"bincode 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dot_vox 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -10,6 +10,7 @@ sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"]
|
||||
specs = { version = "0.14", features = ["serde", "nightly"] }
|
||||
vek = { version = "0.9", features = ["serde"] }
|
||||
dot_vox = "4.0"
|
||||
image = "0.21"
|
||||
threadpool = "1.7"
|
||||
mio = "0.6"
|
||||
mio-extras = "2.0"
|
||||
@ -19,3 +20,4 @@ bincode = "1.0"
|
||||
log = "0.4"
|
||||
rand = "0.5"
|
||||
rayon = "1.0"
|
||||
lazy_static = "1.3"
|
||||
|
@ -1,50 +1,116 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use dot_vox::DotVoxData;
|
||||
use image::DynamicImage;
|
||||
use lazy_static::lazy_static;
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::HashMap,
|
||||
fs::File,
|
||||
io::Read,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
fn try_load(name: &str) -> Option<File> {
|
||||
let basepaths = [
|
||||
// if it's stupid and it works..,
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
/// An asset of a different type has already been loaded with this specifier
|
||||
InvalidType,
|
||||
/// Asset does not exist
|
||||
NotFound(String),
|
||||
}
|
||||
|
||||
impl From<Arc<dyn Any + 'static + Sync + Send>> for Error {
|
||||
fn from(_: Arc<dyn Any + 'static + Sync + Send>) -> Self {
|
||||
Error::InvalidType
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Error::NotFound(format!("{:?}", err))
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ASSETS: RwLock<HashMap<String, Arc<dyn Any + 'static + Sync + Send>>> =
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
/// Function used to load assets
|
||||
/// loaded assets are cached in a global singleton hashmap
|
||||
/// Example usage:
|
||||
/// ```no_run
|
||||
/// use image::DynamicImage;
|
||||
/// use veloren_common::assets;
|
||||
///
|
||||
/// let my_image = assets::load::<DynamicImage>("core.ui.backgrounds.city").unwrap();
|
||||
/// ```
|
||||
pub fn load<A: Asset + 'static>(specifier: &str) -> Result<Arc<A>, Error> {
|
||||
Ok(ASSETS
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(specifier.to_string())
|
||||
.or_insert(Arc::new(A::load(specifier)?))
|
||||
.clone()
|
||||
.downcast()?)
|
||||
}
|
||||
|
||||
/// Function used to load assets that will panic if the asset is not found
|
||||
/// Use this to load essential assets
|
||||
/// loaded assets are cached in a global singleton hashmap
|
||||
/// Example usage:
|
||||
/// ```no_run
|
||||
/// use image::DynamicImage;
|
||||
/// use veloren_common::assets;
|
||||
///
|
||||
/// let my_image = assets::load_expect::<DynamicImage>("core.ui.backgrounds.city");
|
||||
/// ```
|
||||
pub fn load_expect<A: Asset + 'static>(specifier: &str) -> Arc<A> {
|
||||
load(specifier).expect(&format!("Failed loading essential asset: {}", specifier))
|
||||
}
|
||||
|
||||
/// Asset Trait
|
||||
pub trait Asset: Send + Sync + Sized {
|
||||
fn load(specifier: &str) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
impl Asset for DynamicImage {
|
||||
fn load(specifier: &str) -> Result<Self, Error> {
|
||||
Ok(image::load_from_memory(load_from_path(specifier)?.as_slice()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Asset for DotVoxData {
|
||||
fn load(specifier: &str) -> Result<Self, Error> {
|
||||
Ok(dot_vox::load_bytes(load_from_path(specifier)?.as_slice()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: System to load file from specifiers (eg "core.ui.backgrounds.city")
|
||||
fn try_open_with_path(name: &str) -> Option<File> {
|
||||
debug!("Trying to access \"{}\"", name);
|
||||
// TODO: don't do this?
|
||||
// if it's stupid and it works..,
|
||||
[
|
||||
"assets".to_string(),
|
||||
"../../assets".to_string(),
|
||||
"../assets".to_string(), /* optimizations */
|
||||
"../../assets".to_string(),
|
||||
[env!("CARGO_MANIFEST_DIR"), "/../assets"].concat(),
|
||||
[env!("CARGO_MANIFEST_DIR"), "/assets"].concat(),
|
||||
[env!("CARGO_MANIFEST_DIR"), "/../../assets"].concat(),
|
||||
[env!("CARGO_MANIFEST_DIR"), "/../assets"].concat(),
|
||||
"../../../assets".to_string(),
|
||||
[env!("CARGO_MANIFEST_DIR"), "/../../../assets"].concat(),
|
||||
];
|
||||
for bp in &basepaths {
|
||||
let filename = [bp, name].concat();
|
||||
match File::open(&filename) {
|
||||
Ok(f) => {
|
||||
debug!("loading {} succedeed", filename);
|
||||
return Some(f);
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("loading {} did not work with error: {}", filename, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
return None;
|
||||
]
|
||||
.into_iter()
|
||||
.map(|bp| [bp, name].concat())
|
||||
.find_map(|ref filename| File::open(filename).ok())
|
||||
}
|
||||
|
||||
pub fn load(name: &str) -> Result<Vec<u8>, ()> {
|
||||
return match try_load(name) {
|
||||
pub fn load_from_path(name: &str) -> Result<Vec<u8>, Error> {
|
||||
match try_open_with_path(name) {
|
||||
Some(mut f) => {
|
||||
let mut content: Vec<u8> = vec![];
|
||||
f.read_to_end(&mut content);
|
||||
info!("loaded asset successful: {}", name);
|
||||
let mut content = Vec::<u8>::new();
|
||||
f.read_to_end(&mut content)?;
|
||||
Ok(content)
|
||||
}
|
||||
None => {
|
||||
warn!(
|
||||
"Loading asset failed, wanted to load {} but could not load it, check debug log!",
|
||||
name
|
||||
);
|
||||
Err(())
|
||||
}
|
||||
};
|
||||
None => Err(Error::NotFound(name.to_owned())),
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ use self::cell::Cell;
|
||||
/// Figures are used to represent things like characters, NPCs, mobs, etc.
|
||||
pub type Segment = Dyna<Cell, ()>;
|
||||
|
||||
impl From<DotVoxData> for Segment {
|
||||
fn from(dot_vox_data: DotVoxData) -> Self {
|
||||
impl From<&DotVoxData> for Segment {
|
||||
fn from(dot_vox_data: &DotVoxData) -> Self {
|
||||
if let Some(model) = dot_vox_data.models.get(0) {
|
||||
let palette = dot_vox_data
|
||||
.palette
|
||||
|
117
voxygen/src/hud/bag.rs
Normal file
117
voxygen/src/hud/bag.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use super::{font_ids::Fonts, img_ids::Imgs};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Scrollbar},
|
||||
widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
bag_close,
|
||||
bag_contents,
|
||||
inv_alignment,
|
||||
inv_grid_1,
|
||||
inv_grid_2,
|
||||
inv_scrollbar,
|
||||
inv_slot_0,
|
||||
map_title,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Bag<'a> {
|
||||
inventory_space: u32,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> Bag<'a> {
|
||||
pub fn new(inventory_space: u32, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
inventory_space,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
impl<'a> Widget for Bag<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
// Contents
|
||||
Image::new(self.imgs.bag_contents)
|
||||
.w_h(68.0 * 4.0, 123.0 * 4.0)
|
||||
.bottom_right_with_margins_on(ui.window, 60.0, 5.0)
|
||||
.set(state.ids.bag_contents, ui);
|
||||
|
||||
// Alignment for Grid
|
||||
Rectangle::fill_with([58.0 * 4.0 - 5.0, 100.0 * 4.0], color::TRANSPARENT)
|
||||
.top_left_with_margins_on(state.ids.bag_contents, 11.0 * 4.0, 5.0 * 4.0)
|
||||
.scroll_kids()
|
||||
.scroll_kids_vertically()
|
||||
.set(state.ids.inv_alignment, ui);
|
||||
// Grid
|
||||
Image::new(self.imgs.inv_grid)
|
||||
.w_h(58.0 * 4.0, 111.0 * 4.0)
|
||||
.mid_top_with_margin_on(state.ids.inv_alignment, 0.0)
|
||||
.set(state.ids.inv_grid_1, ui);
|
||||
Image::new(self.imgs.inv_grid)
|
||||
.w_h(58.0 * 4.0, 111.0 * 4.0)
|
||||
.mid_top_with_margin_on(state.ids.inv_alignment, 110.0 * 4.0)
|
||||
.set(state.ids.inv_grid_2, ui);
|
||||
Scrollbar::y_axis(state.ids.inv_alignment)
|
||||
.thickness(5.0)
|
||||
.rgba(0.33, 0.33, 0.33, 1.0)
|
||||
.set(state.ids.inv_scrollbar, ui);
|
||||
|
||||
if self.inventory_space > 0 {
|
||||
// First Slot
|
||||
Button::image(self.imgs.inv_slot)
|
||||
.top_left_with_margins_on(state.ids.inv_grid_1, 4.0, 4.0)
|
||||
.w_h(10.0 * 4.0, 10.0 * 4.0)
|
||||
.set(state.ids.inv_slot_0, ui);
|
||||
}
|
||||
|
||||
// X-button
|
||||
if Button::image(self.imgs.close_button)
|
||||
.w_h(28.0, 28.0)
|
||||
.hover_image(self.imgs.close_button_hover)
|
||||
.press_image(self.imgs.close_button_press)
|
||||
.top_right_with_margins_on(state.ids.bag_contents, 0.0, 0.0)
|
||||
.set(state.ids.bag_close, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
Some(Event::Close)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
245
voxygen/src/hud/buttons.rs
Normal file
245
voxygen/src/hud/buttons.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use conrod_core::{
|
||||
widget::{self, Button, Image, Text},
|
||||
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, small_window::SmallWindowType, Windows, TEXT_COLOR};
|
||||
use crate::ui::ToggleButton;
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
bag,
|
||||
bag_text,
|
||||
bag_show_map,
|
||||
character_button,
|
||||
character_button_bg,
|
||||
map_button,
|
||||
qlog_button,
|
||||
qlog_button_bg,
|
||||
settings_button,
|
||||
social_button,
|
||||
social_button_bg,
|
||||
spellbook_button,
|
||||
spellbook_button_bg,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Buttons<'a> {
|
||||
open_windows: &'a Windows,
|
||||
show_map: bool,
|
||||
show_bag: bool,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> Buttons<'a> {
|
||||
pub fn new(
|
||||
open_windows: &'a Windows,
|
||||
show_map: bool,
|
||||
show_bag: bool,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
) -> Self {
|
||||
Self {
|
||||
open_windows,
|
||||
show_map,
|
||||
show_bag,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
ToggleBag,
|
||||
ToggleSettings,
|
||||
ToggleMap,
|
||||
ToggleSmall(SmallWindowType),
|
||||
ToggleCharacter,
|
||||
}
|
||||
|
||||
impl<'a> Widget for Buttons<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
// Bag
|
||||
if !self.show_map {
|
||||
if self.show_bag
|
||||
!= ToggleButton::new(self.show_bag, self.imgs.bag, self.imgs.bag_open)
|
||||
.bottom_right_with_margins_on(ui.window, 5.0, 5.0)
|
||||
.hover_images(self.imgs.bag_hover, self.imgs.bag_open_hover)
|
||||
.press_images(self.imgs.bag_press, self.imgs.bag_open_press)
|
||||
.w_h(420.0 / 10.0, 480.0 / 10.0)
|
||||
.set(state.ids.bag, ui)
|
||||
{
|
||||
return Some(Event::ToggleBag);
|
||||
}
|
||||
|
||||
Text::new("B")
|
||||
.bottom_right_with_margins_on(state.ids.bag, 0.0, 0.0)
|
||||
.font_size(10)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.bag_text, ui);
|
||||
} else {
|
||||
Image::new(self.imgs.bag)
|
||||
.bottom_right_with_margins_on(ui.window, 5.0, 5.0)
|
||||
.w_h(420.0 / 10.0, 480.0 / 10.0)
|
||||
.set(state.ids.bag_show_map, ui);
|
||||
Text::new("B")
|
||||
.bottom_right_with_margins_on(state.ids.bag, 0.0, 0.0)
|
||||
.font_size(10)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.bag_text, ui);
|
||||
}
|
||||
|
||||
// 0 Settings
|
||||
if Button::image(self.imgs.settings)
|
||||
.w_h(29.0, 25.0)
|
||||
.bottom_right_with_margins_on(ui.window, 5.0, 57.0)
|
||||
.hover_image(self.imgs.settings_hover)
|
||||
.press_image(self.imgs.settings_press)
|
||||
.label("N")
|
||||
.label_font_size(10)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_y(conrod_core::position::Relative::Scalar(-7.0))
|
||||
.label_x(conrod_core::position::Relative::Scalar(10.0))
|
||||
.set(state.ids.settings_button, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::ToggleSettings);
|
||||
};
|
||||
|
||||
// 2 Map
|
||||
if Button::image(self.imgs.map_button)
|
||||
.w_h(22.0, 25.0)
|
||||
.left_from(state.ids.social_button, 10.0)
|
||||
.hover_image(self.imgs.map_hover)
|
||||
.press_image(self.imgs.map_press)
|
||||
.label("M")
|
||||
.label_font_size(10)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_y(conrod_core::position::Relative::Scalar(-7.0))
|
||||
.label_x(conrod_core::position::Relative::Scalar(10.0))
|
||||
.set(state.ids.map_button, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::ToggleMap);
|
||||
};
|
||||
|
||||
// Other Windows can only be accessed, when Settings are closed.
|
||||
// Opening Settings will close all other Windows including the Bag.
|
||||
// Opening the Map won't close the windows displayed before.
|
||||
Image::new(self.imgs.social_button)
|
||||
.w_h(25.0, 25.0)
|
||||
.left_from(state.ids.settings_button, 10.0)
|
||||
.set(state.ids.social_button_bg, ui);
|
||||
Image::new(self.imgs.spellbook_button)
|
||||
.w_h(28.0, 25.0)
|
||||
.left_from(state.ids.map_button, 10.0)
|
||||
.set(state.ids.spellbook_button_bg, ui);
|
||||
Image::new(self.imgs.character_button)
|
||||
.w_h(27.0, 25.0)
|
||||
.left_from(state.ids.spellbook_button, 10.0)
|
||||
.set(state.ids.character_button_bg, ui);
|
||||
Image::new(self.imgs.qlog_button)
|
||||
.w_h(23.0, 25.0)
|
||||
.left_from(state.ids.character_button, 10.0)
|
||||
.set(state.ids.qlog_button_bg, ui);
|
||||
|
||||
if !(*self.open_windows == Windows::Settings) && self.show_map == false {
|
||||
// 1 Social
|
||||
if Button::image(self.imgs.social_button)
|
||||
.w_h(25.0, 25.0)
|
||||
.left_from(state.ids.settings_button, 10.0)
|
||||
.hover_image(self.imgs.social_hover)
|
||||
.press_image(self.imgs.social_press)
|
||||
.label("O")
|
||||
.label_font_size(10)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_y(conrod_core::position::Relative::Scalar(-7.0))
|
||||
.label_x(conrod_core::position::Relative::Scalar(10.0))
|
||||
.set(state.ids.social_button, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::ToggleSmall(SmallWindowType::Social));
|
||||
}
|
||||
|
||||
// 3 Spellbook
|
||||
if Button::image(self.imgs.spellbook_button)
|
||||
.w_h(28.0, 25.0)
|
||||
.left_from(state.ids.map_button, 10.0)
|
||||
.hover_image(self.imgs.spellbook_hover)
|
||||
.press_image(self.imgs.spellbook_press)
|
||||
.label("P")
|
||||
.label_font_size(10)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_y(conrod_core::position::Relative::Scalar(-7.0))
|
||||
.label_x(conrod_core::position::Relative::Scalar(10.0))
|
||||
.set(state.ids.spellbook_button, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::ToggleSmall(SmallWindowType::Spellbook));
|
||||
}
|
||||
|
||||
// 4 Char-Window
|
||||
if Button::image(self.imgs.character_button)
|
||||
.w_h(27.0, 25.0)
|
||||
.left_from(state.ids.spellbook_button, 10.0)
|
||||
.hover_image(self.imgs.character_hover)
|
||||
.press_image(self.imgs.character_press)
|
||||
.label("C")
|
||||
.label_font_size(10)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_y(conrod_core::position::Relative::Scalar(-7.0))
|
||||
.label_x(conrod_core::position::Relative::Scalar(10.0))
|
||||
.set(state.ids.character_button, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::ToggleCharacter);
|
||||
}
|
||||
|
||||
// 5 Quest-Log
|
||||
if Button::image(self.imgs.qlog_button)
|
||||
.w_h(23.0, 25.0)
|
||||
.left_from(state.ids.character_button, 10.0)
|
||||
.hover_image(self.imgs.qlog_hover)
|
||||
.press_image(self.imgs.qlog_press)
|
||||
.label("L")
|
||||
.label_font_size(10)
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_y(conrod_core::position::Relative::Scalar(-7.0))
|
||||
.label_x(conrod_core::position::Relative::Scalar(10.0))
|
||||
.set(state.ids.qlog_button, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::ToggleSmall(SmallWindowType::QuestLog));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
188
voxygen/src/hud/character_window.rs
Normal file
188
voxygen/src/hud/character_window.rs
Normal file
@ -0,0 +1,188 @@
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, TEXT_COLOR, XP_COLOR};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
widget_ids! {
|
||||
pub struct Ids {
|
||||
charwindow,
|
||||
charwindow_bg,
|
||||
charwindow_close,
|
||||
charwindow_exp_progress_rectangle,
|
||||
charwindow_exp_rectangle,
|
||||
charwindow_frame,
|
||||
charwindow_icon,
|
||||
charwindow_rectangle,
|
||||
charwindow_tab1,
|
||||
charwindow_tab1_exp,
|
||||
charwindow_tab1_expbar,
|
||||
charwindow_tab1_level,
|
||||
charwindow_tab1_statnames,
|
||||
charwindow_tab1_stats,
|
||||
charwindow_tab_bg,
|
||||
charwindow_title,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct CharacterWindow<'a> {
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> CharacterWindow<'a> {
|
||||
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
impl<'a> Widget for CharacterWindow<'a> {
|
||||
type State = Ids;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
Ids::new(id_gen)
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
||||
|
||||
// TODO: Read from parameter / character struct
|
||||
let xp_percentage = 0.4;
|
||||
|
||||
// Frame
|
||||
Image::new(self.imgs.window_frame)
|
||||
.middle_of(id)
|
||||
.top_left_with_margins_on(ui.window, 200.0, 215.0)
|
||||
.w_h(107.0 * 4.0, 125.0 * 4.0)
|
||||
.set(state.charwindow_frame, ui);
|
||||
|
||||
// Icon
|
||||
Image::new(self.imgs.charwindow_icon)
|
||||
.w_h(40.0, 40.0)
|
||||
.top_left_with_margins_on(state.charwindow_frame, 4.0, 4.0)
|
||||
.set(state.charwindow_icon, ui);
|
||||
|
||||
// X-Button
|
||||
if Button::image(self.imgs.close_button)
|
||||
.w_h(28.0, 28.0)
|
||||
.hover_image(self.imgs.close_button_hover)
|
||||
.press_image(self.imgs.close_button_press)
|
||||
.top_right_with_margins_on(state.charwindow_frame, 12.0, 0.0)
|
||||
.set(state.charwindow_close, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::Close);
|
||||
}
|
||||
|
||||
// Title
|
||||
Text::new("Character Name") // Add in actual Character Name
|
||||
.mid_top_with_margin_on(state.charwindow_frame, 17.0)
|
||||
.font_id(self.fonts.metamorph)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.charwindow_title, ui);
|
||||
|
||||
// Tab BG
|
||||
Image::new(self.imgs.charwindow_tab_bg)
|
||||
.w_h(205.0, 412.0)
|
||||
.mid_left_with_margin_on(state.charwindow_frame, -205.0)
|
||||
.set(state.charwindow_tab_bg, ui);
|
||||
|
||||
// Tab Rectangle
|
||||
Rectangle::fill_with([192.0, 371.0], color::rgba(0.0, 0.0, 0.0, 0.8))
|
||||
.top_right_with_margins_on(state.charwindow_tab_bg, 20.0, 0.0)
|
||||
.set(state.charwindow_rectangle, ui);
|
||||
|
||||
// Tab Button
|
||||
Button::image(self.imgs.charwindow_tab)
|
||||
.w_h(65.0, 23.0)
|
||||
.top_left_with_margins_on(state.charwindow_tab_bg, -18.0, 2.0)
|
||||
.label("Stats")
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(14)
|
||||
.set(state.charwindow_tab1, ui);
|
||||
|
||||
Text::new("1") //Add in actual Character Level
|
||||
.mid_top_with_margin_on(state.charwindow_rectangle, 10.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(30)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.charwindow_tab1_level, ui);
|
||||
|
||||
// Exp-Bar Background
|
||||
Rectangle::fill_with([170.0, 10.0], color::BLACK)
|
||||
.mid_top_with_margin_on(state.charwindow_rectangle, 50.0)
|
||||
.set(state.charwindow_exp_rectangle, ui);
|
||||
|
||||
// Exp-Bar Progress
|
||||
Rectangle::fill_with([170.0 * (xp_percentage), 6.0], XP_COLOR) // 0.8 = Experience percentage
|
||||
.mid_left_with_margin_on(state.charwindow_tab1_expbar, 1.0)
|
||||
.set(state.charwindow_exp_progress_rectangle, ui);
|
||||
|
||||
// Exp-Bar Foreground Frame
|
||||
Image::new(self.imgs.progress_frame)
|
||||
.w_h(170.0, 10.0)
|
||||
.middle_of(state.charwindow_exp_rectangle)
|
||||
.set(state.charwindow_tab1_expbar, ui);
|
||||
|
||||
// Exp-Text
|
||||
Text::new("120/170") // Shows the Exp / Exp to reach the next level
|
||||
.mid_top_with_margin_on(state.charwindow_tab1_expbar, 10.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(15)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.charwindow_tab1_exp, ui);
|
||||
|
||||
// Stats
|
||||
Text::new(
|
||||
"Stamina\n\
|
||||
\n\
|
||||
Strength\n\
|
||||
\n\
|
||||
Dexterity\n\
|
||||
\n\
|
||||
Intelligence",
|
||||
)
|
||||
.top_left_with_margins_on(state.charwindow_rectangle, 100.0, 20.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(16)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.charwindow_tab1_statnames, ui);
|
||||
|
||||
Text::new(
|
||||
"1234\n\
|
||||
\n\
|
||||
12312\n\
|
||||
\n\
|
||||
12414\n\
|
||||
\n\
|
||||
124124",
|
||||
)
|
||||
.right_from(state.charwindow_tab1_statnames, 10.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(16)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.charwindow_tab1_stats, ui);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
use crate::ui::Ui;
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, TEXT_COLOR};
|
||||
use conrod_core::{
|
||||
color,
|
||||
input::Key,
|
||||
position::Dimension,
|
||||
text::font::Id as FontId,
|
||||
widget::{Button, Id, List, Rectangle, Text, TextEdit},
|
||||
widget_ids, Colorable, Positionable, Sizeable, UiCell, Widget,
|
||||
widget::{self, Button, Id, List, Rectangle, Text, TextEdit},
|
||||
widget_ids, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
@ -18,49 +16,33 @@ widget_ids! {
|
||||
chat_arrow,
|
||||
}
|
||||
}
|
||||
// Chat Behaviour:
|
||||
// Input Window is only shown when the player presses Enter (graphical overlay to make it look better?)
|
||||
// Instead of a Scrollbar it could have 3 Arrows at it's left side
|
||||
// First two: Scroll the chat up and down
|
||||
// Last one: Gets back to the bottom of the chat
|
||||
|
||||
// Consider making this a custom Widget
|
||||
pub struct Chat {
|
||||
ids: Ids,
|
||||
messages: VecDeque<String>,
|
||||
input: String,
|
||||
new_messages: bool,
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Chat<'a> {
|
||||
new_messages: &'a mut VecDeque<String>,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
impl Chat {
|
||||
pub fn new(ui: &mut Ui) -> Self {
|
||||
Chat {
|
||||
ids: Ids::new(ui.id_generator()),
|
||||
messages: VecDeque::new(),
|
||||
input: String::new(),
|
||||
new_messages: false,
|
||||
|
||||
impl<'a> Chat<'a> {
|
||||
pub fn new(new_messages: &'a mut VecDeque<String>, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
new_messages,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
pub fn input_box_id(&self) -> Id {
|
||||
self.ids.input
|
||||
}
|
||||
pub fn new_message(&mut self, msg: String) {
|
||||
self.messages.push_back(msg);
|
||||
self.new_messages = true;
|
||||
}
|
||||
// Determine if the message box is scrolled to the bottom
|
||||
// (i.e. the player is viewing new messages)
|
||||
// If so scroll down when new messages are added
|
||||
fn scroll_new_messages(&self, ui_widgets: &mut UiCell) {
|
||||
// If previously scrolled to the bottom stay there
|
||||
if self.scrolled_to_bottom(ui_widgets) {
|
||||
self.scroll_to_bottom(ui_widgets);
|
||||
}
|
||||
}
|
||||
fn scrolled_to_bottom(&self, ui_widgets: &UiCell) -> bool {
|
||||
|
||||
fn scrolled_to_bottom(state: &State, ui: &UiCell) -> bool {
|
||||
// could be more efficient to cache result and update it when a scroll event has occurred instead of every frame
|
||||
if let Some(scroll) = ui_widgets
|
||||
if let Some(scroll) = ui
|
||||
.widget_graph()
|
||||
.widget(self.ids.message_box)
|
||||
.widget(state.ids.message_box)
|
||||
.and_then(|widget| widget.maybe_y_scroll_state)
|
||||
{
|
||||
scroll.offset >= scroll.offset_bounds.start
|
||||
@ -68,50 +50,75 @@ impl Chat {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn scroll_to_bottom(&self, ui_widgets: &mut UiCell) {
|
||||
ui_widgets.scroll_widget(self.ids.message_box, [0.0, std::f64::MAX]);
|
||||
}
|
||||
pub(super) fn update_layout(
|
||||
&mut self,
|
||||
ui_widgets: &mut UiCell,
|
||||
font: FontId,
|
||||
imgs: &super::Imgs,
|
||||
) -> Option<String> {
|
||||
// Maintain scrolling
|
||||
if self.new_messages {
|
||||
self.scroll_new_messages(ui_widgets);
|
||||
self.new_messages = false;
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
messages: VecDeque<String>,
|
||||
input: String,
|
||||
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
SendMessage(String),
|
||||
Focus(Id),
|
||||
}
|
||||
|
||||
impl<'a> Widget for Chat<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
messages: VecDeque::new(),
|
||||
input: "".to_owned(),
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
||||
|
||||
// Maintain scrolling
|
||||
if !self.new_messages.is_empty() {
|
||||
state.update(|s| s.messages.extend(self.new_messages.drain(..)));
|
||||
ui.scroll_widget(state.ids.message_box, [0.0, std::f64::MAX]);
|
||||
}
|
||||
|
||||
let keyboard_capturer = ui.global_input().current.widget_capturing_keyboard;
|
||||
let input_focused = keyboard_capturer == Some(state.ids.input);
|
||||
|
||||
// Only show if it has the keyboard captured
|
||||
// Chat input with rectangle as background
|
||||
let keyboard_captured = ui_widgets
|
||||
.global_input()
|
||||
.current
|
||||
.widget_capturing_keyboard
|
||||
.map_or(false, |id| id == self.ids.input);
|
||||
if keyboard_captured {
|
||||
let text_edit = TextEdit::new(&self.input)
|
||||
if input_focused {
|
||||
let text_edit = TextEdit::new(&state.input)
|
||||
.w(460.0)
|
||||
.restrict_to_height(false)
|
||||
.color(TEXT_COLOR)
|
||||
.line_spacing(2.0)
|
||||
.font_size(15)
|
||||
.font_id(font);
|
||||
let y = match text_edit.get_y_dimension(ui_widgets) {
|
||||
.font_id(self.fonts.opensans);
|
||||
let y = match text_edit.get_y_dimension(ui) {
|
||||
Dimension::Absolute(y) => y + 6.0,
|
||||
_ => 0.0,
|
||||
};
|
||||
Rectangle::fill([470.0, y])
|
||||
.rgba(0.0, 0.0, 0.0, 0.8)
|
||||
.bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0)
|
||||
.bottom_left_with_margins_on(ui.window, 10.0, 10.0)
|
||||
.w(470.0)
|
||||
.set(self.ids.input_bg, ui_widgets);
|
||||
.set(state.ids.input_bg, ui);
|
||||
if let Some(str) = text_edit
|
||||
.top_left_with_margins_on(self.ids.input_bg, 1.0, 1.0)
|
||||
.set(self.ids.input, ui_widgets)
|
||||
.top_left_with_margins_on(state.ids.input_bg, 1.0, 1.0)
|
||||
.set(state.ids.input, ui)
|
||||
{
|
||||
self.input = str.to_string();
|
||||
self.input.retain(|c| c != '\n');
|
||||
let mut input = str.to_owned();
|
||||
input.retain(|c| c != '\n');
|
||||
state.update(|s| s.input = input);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,29 +126,29 @@ impl Chat {
|
||||
Rectangle::fill([470.0, 174.0])
|
||||
.rgba(0.0, 0.0, 0.0, 0.4)
|
||||
.and(|r| {
|
||||
if keyboard_captured {
|
||||
r.up_from(self.ids.input_bg, 0.0)
|
||||
if input_focused {
|
||||
r.up_from(state.ids.input_bg, 0.0)
|
||||
} else {
|
||||
r.bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0)
|
||||
r.bottom_left_with_margins_on(ui.window, 10.0, 10.0)
|
||||
}
|
||||
})
|
||||
.set(self.ids.message_box_bg, ui_widgets);
|
||||
let (mut items, _) = List::flow_down(self.messages.len() + 1)
|
||||
.top_left_of(self.ids.message_box_bg)
|
||||
.set(state.ids.message_box_bg, ui);
|
||||
let (mut items, _) = List::flow_down(state.messages.len() + 1)
|
||||
.top_left_of(state.ids.message_box_bg)
|
||||
.w_h(470.0, 174.0)
|
||||
.scroll_kids_vertically()
|
||||
.set(self.ids.message_box, ui_widgets);
|
||||
while let Some(item) = items.next(ui_widgets) {
|
||||
.set(state.ids.message_box, ui);
|
||||
while let Some(item) = items.next(ui) {
|
||||
// This would be easier if conrod used the v-metrics from rusttype
|
||||
let widget = if item.i < self.messages.len() {
|
||||
let text = Text::new(&self.messages[item.i])
|
||||
let widget = if item.i < state.messages.len() {
|
||||
let text = Text::new(&state.messages[item.i])
|
||||
.font_size(15)
|
||||
.font_id(font)
|
||||
.font_id(self.fonts.opensans)
|
||||
.w(470.0)
|
||||
.rgba(1.0, 1.0, 1.0, 1.0)
|
||||
.color(TEXT_COLOR)
|
||||
.line_spacing(2.0);
|
||||
// Add space between messages
|
||||
let y = match text.get_y_dimension(ui_widgets) {
|
||||
let y = match text.get_y_dimension(ui) {
|
||||
Dimension::Absolute(y) => y + 2.0,
|
||||
_ => 0.0,
|
||||
};
|
||||
@ -149,38 +156,46 @@ impl Chat {
|
||||
} else {
|
||||
// Spacer at bottom of the last message so that it is not cut off
|
||||
// Needs to be larger than the space above
|
||||
Text::new("").font_size(6).font_id(font).w(470.0)
|
||||
Text::new("")
|
||||
.font_size(6)
|
||||
.font_id(self.fonts.opensans)
|
||||
.w(470.0)
|
||||
};
|
||||
item.set(widget, ui_widgets);
|
||||
item.set(widget, ui);
|
||||
}
|
||||
|
||||
// Chat Arrow
|
||||
if !self.scrolled_to_bottom(ui_widgets) {
|
||||
if Button::image(imgs.chat_arrow)
|
||||
// Check if already at bottom
|
||||
if !Self::scrolled_to_bottom(state, ui) {
|
||||
if Button::image(self.imgs.chat_arrow)
|
||||
.w_h(20.0, 20.0)
|
||||
.hover_image(imgs.chat_arrow_mo)
|
||||
.press_image(imgs.chat_arrow_press)
|
||||
.bottom_right_with_margins_on(self.ids.message_box_bg, 0.0, -22.0)
|
||||
.set(self.ids.chat_arrow, ui_widgets)
|
||||
.hover_image(self.imgs.chat_arrow_mo)
|
||||
.press_image(self.imgs.chat_arrow_press)
|
||||
.bottom_right_with_margins_on(state.ids.message_box_bg, 0.0, -22.0)
|
||||
.set(state.ids.chat_arrow, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
self.scroll_to_bottom(ui_widgets);
|
||||
ui.scroll_widget(state.ids.message_box, [0.0, std::f64::MAX]);
|
||||
}
|
||||
}
|
||||
|
||||
// If the chat widget is focused return a focus event to pass the focus to the input box
|
||||
if keyboard_capturer == Some(id) {
|
||||
Some(Event::Focus(state.ids.input))
|
||||
}
|
||||
// If enter is pressed and the input box is not empty send the current message
|
||||
if ui_widgets
|
||||
.widget_input(self.ids.input)
|
||||
else if ui
|
||||
.widget_input(state.ids.input)
|
||||
.presses()
|
||||
.key()
|
||||
.any(|key_press| match key_press.key {
|
||||
Key::Return if !self.input.is_empty() => true,
|
||||
Key::Return if !state.input.is_empty() => true,
|
||||
_ => false,
|
||||
})
|
||||
{
|
||||
let new_message = self.input.clone();
|
||||
self.input.clear();
|
||||
Some(new_message)
|
||||
let msg = state.input.clone();
|
||||
state.update(|s| s.input.clear());
|
||||
Some(Event::SendMessage(msg))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
156
voxygen/src/hud/esc_menu.rs
Normal file
156
voxygen/src/hud/esc_menu.rs
Normal file
@ -0,0 +1,156 @@
|
||||
use conrod_core::{
|
||||
widget::{self, Button, Image},
|
||||
widget_ids, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, TEXT_COLOR};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
esc_bg,
|
||||
fireplace,
|
||||
menu_button_1,
|
||||
menu_button_2,
|
||||
menu_button_3,
|
||||
menu_button_4,
|
||||
menu_button_5,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct EscMenu<'a> {
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> EscMenu<'a> {
|
||||
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
OpenSettings,
|
||||
Logout,
|
||||
Quit,
|
||||
Close,
|
||||
}
|
||||
|
||||
impl<'a> Widget for EscMenu<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
Image::new(self.imgs.esc_bg)
|
||||
.w_h(228.0, 450.0)
|
||||
.middle_of(ui.window)
|
||||
.set(state.ids.esc_bg, ui);
|
||||
|
||||
Image::new(self.imgs.fireplace)
|
||||
.w_h(180.0, 60.0)
|
||||
.mid_top_with_margin_on(state.ids.esc_bg, 50.0)
|
||||
.set(state.ids.fireplace, ui);
|
||||
|
||||
// Settings
|
||||
if Button::image(self.imgs.button)
|
||||
.mid_top_with_margin_on(state.ids.esc_bg, 115.0)
|
||||
.w_h(170.0, 50.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
.label("Settings")
|
||||
.label_y(conrod_core::position::Relative::Scalar(2.0))
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(17)
|
||||
.set(state.ids.menu_button_1, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::OpenSettings);
|
||||
};
|
||||
// Controls
|
||||
if Button::image(self.imgs.button)
|
||||
.mid_top_with_margin_on(state.ids.esc_bg, 175.0)
|
||||
.w_h(170.0, 50.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
.label("Controls")
|
||||
.label_y(conrod_core::position::Relative::Scalar(2.0))
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(17)
|
||||
.set(state.ids.menu_button_2, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
// TODO: Show controls window
|
||||
};
|
||||
// Servers
|
||||
if Button::image(self.imgs.button)
|
||||
.mid_top_with_margin_on(state.ids.esc_bg, 235.0)
|
||||
.w_h(170.0, 50.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
.label("Servers")
|
||||
.label_y(conrod_core::position::Relative::Scalar(2.0))
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(17)
|
||||
.set(state.ids.menu_button_3, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
// TODO: Show servers window (is needed in-game?)
|
||||
};
|
||||
// Logout
|
||||
if Button::image(self.imgs.button)
|
||||
.mid_top_with_margin_on(state.ids.esc_bg, 295.0)
|
||||
.w_h(170.0, 50.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
.label("Logout")
|
||||
.label_y(conrod_core::position::Relative::Scalar(2.0))
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(17)
|
||||
.set(state.ids.menu_button_4, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::Logout);
|
||||
};
|
||||
// Quit
|
||||
if Button::image(self.imgs.button)
|
||||
.mid_top_with_margin_on(state.ids.esc_bg, 355.0)
|
||||
.w_h(170.0, 50.0)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
.label("Quit")
|
||||
.label_y(conrod_core::position::Relative::Scalar(2.0))
|
||||
.label_color(TEXT_COLOR)
|
||||
.label_font_size(17)
|
||||
.set(state.ids.menu_button_5, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::Quit);
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
}
|
6
voxygen/src/hud/font_ids.rs
Normal file
6
voxygen/src/hud/font_ids.rs
Normal file
@ -0,0 +1,6 @@
|
||||
font_ids! {
|
||||
pub struct Fonts {
|
||||
opensans: "/voxygen/font/OpenSans-Regular.ttf",
|
||||
metamorph: "/voxygen/font/Metamorphous-Regular.ttf",
|
||||
}
|
||||
}
|
157
voxygen/src/hud/img_ids.rs
Normal file
157
voxygen/src/hud/img_ids.rs
Normal file
@ -0,0 +1,157 @@
|
||||
use crate::ui::{BlankGraphic, ImageGraphic, VoxelGraphic};
|
||||
|
||||
image_ids! {
|
||||
pub struct Imgs {
|
||||
<VoxelGraphic>
|
||||
// Bag
|
||||
bag_contents: "/voxygen/element/frames/bag.vox",
|
||||
inv_grid: "/voxygen/element/frames/inv_grid.vox",
|
||||
inv_slot: "/voxygen/element/buttons/inv_slot.vox",
|
||||
|
||||
// Buttons
|
||||
mmap_closed: "/voxygen/element/buttons/button_mmap_closed.vox",
|
||||
mmap_closed_hover: "/voxygen/element/buttons/button_mmap_closed_hover.vox",
|
||||
mmap_closed_press: "/voxygen/element/buttons/button_mmap_closed_press.vox",
|
||||
mmap_open: "/voxygen/element/buttons/button_mmap_open.vox",
|
||||
mmap_open_hover: "/voxygen/element/buttons/button_mmap_open_hover.vox",
|
||||
mmap_open_press: "/voxygen/element/buttons/button_mmap_open_press.vox",
|
||||
|
||||
settings: "/voxygen/element/buttons/settings.vox",
|
||||
settings_hover: "/voxygen/element/buttons/settings_hover.vox",
|
||||
settings_press: "/voxygen/element/buttons/settings_press.vox",
|
||||
|
||||
social_button: "/voxygen/element/buttons/social.vox",
|
||||
social_hover: "/voxygen/element/buttons/social_hover.vox",
|
||||
social_press: "/voxygen/element/buttons/social_press.vox",
|
||||
|
||||
map_button: "/voxygen/element/buttons/map.vox",
|
||||
map_hover: "/voxygen/element/buttons/map_hover.vox",
|
||||
map_press: "/voxygen/element/buttons/map_press.vox",
|
||||
|
||||
spellbook_button: "/voxygen/element/buttons/spellbook.vox",
|
||||
spellbook_hover: "/voxygen/element/buttons/spellbook_hover.vox",
|
||||
spellbook_press: "/voxygen/element/buttons/spellbook_press.vox",
|
||||
|
||||
character_button: "/voxygen/element/buttons/character.vox",
|
||||
character_hover: "/voxygen/element/buttons/character_hover.vox",
|
||||
character_press: "/voxygen/element/buttons/character_press.vox",
|
||||
|
||||
qlog_button: "/voxygen/element/buttons/qlog.vox",
|
||||
qlog_hover: "/voxygen/element/buttons/qlog_hover.vox",
|
||||
qlog_press: "/voxygen/element/buttons/qlog_press.vox",
|
||||
|
||||
|
||||
// Close button
|
||||
close_button: "/voxygen/element/buttons/x.vox",
|
||||
close_button_hover: "/voxygen/element/buttons/x_hover.vox",
|
||||
close_button_press: "/voxygen/element/buttons/x_press.vox",
|
||||
|
||||
// Esc-Menu
|
||||
fireplace: "/voxygen/element/misc_bg/fireplace.vox",
|
||||
button: "/voxygen/element/buttons/button.vox",
|
||||
button_hover: "/voxygen/element/buttons/button_hover.vox",
|
||||
button_press: "/voxygen/element/buttons/button_press.vox",
|
||||
|
||||
// MiniMap
|
||||
mmap_frame: "/voxygen/element/frames/mmap.vox",
|
||||
mmap_frame_closed: "/voxygen/element/frames/mmap_closed.vox",
|
||||
|
||||
|
||||
// Missing: Buff Frame Animation .gif ?! we could do animation in ui.maintain, or in shader?
|
||||
window_frame: "/voxygen/element/frames/window2.vox",
|
||||
|
||||
// Settings Window
|
||||
settings_frame_r: "/voxygen/element/frames/settings_r.vox",
|
||||
settings_frame_l: "/voxygen/element/frames/settings_l.vox",
|
||||
settings_button: "/voxygen/element/buttons/settings_button.vox",
|
||||
settings_button_pressed: "/voxygen/element/buttons/settings_button_pressed.vox",
|
||||
settings_button_hover: "/voxygen/element/buttons/settings_button_hover.vox",
|
||||
settings_button_press: "/voxygen/element/buttons/settings_button_press.vox",
|
||||
check: "/voxygen/element/buttons/check/no.vox",
|
||||
check_mo: "/voxygen/element/buttons/check/no_mo.vox",
|
||||
check_press: "/voxygen/element/buttons/check/press.vox",
|
||||
check_checked: "/voxygen/element/buttons/check/yes.vox",
|
||||
check_checked_mo: "/voxygen/element/buttons/check/yes_mo.vox",
|
||||
slider: "/voxygen/element/slider/track.vox",
|
||||
slider_indicator: "/voxygen/element/slider/indicator.vox",
|
||||
|
||||
|
||||
// Map Window
|
||||
map_frame_l: "/voxygen/element/frames/map_l.vox",
|
||||
map_frame_r: "/voxygen/element/frames/map_r.vox",
|
||||
map_frame_bl: "/voxygen/element/frames/map_bl.vox",
|
||||
map_frame_br: "/voxygen/element/frames/map_br.vox",
|
||||
|
||||
|
||||
// Chat-Arrows
|
||||
chat_arrow: "/voxygen/element/buttons/arrow_down.vox",
|
||||
chat_arrow_mo: "/voxygen/element/buttons/arrow_down_hover.vox",
|
||||
chat_arrow_press: "/voxygen/element/buttons/arrow_down_press.vox",
|
||||
|
||||
<ImageGraphic>
|
||||
|
||||
// Spell Book Window
|
||||
spellbook_bg: "/voxygen/element/misc_bg/small_bg.png",
|
||||
spellbook_icon: "/voxygen/element/icons/spellbook.png",
|
||||
|
||||
// Bag
|
||||
bag: "/voxygen/element/buttons/bag/closed.png",
|
||||
bag_hover: "/voxygen/element/buttons/bag/closed_hover.png",
|
||||
bag_press: "/voxygen/element/buttons/bag/closed_press.png",
|
||||
bag_open: "/voxygen/element/buttons/bag/open.png",
|
||||
bag_open_hover: "/voxygen/element/buttons/bag/open_hover.png",
|
||||
bag_open_press: "/voxygen/element/buttons/bag/open_press.png",
|
||||
|
||||
map_bg: "/voxygen/element/misc_bg/small_bg.png",
|
||||
map_icon: "/voxygen/element/icons/map.png",
|
||||
|
||||
grid_button: "/voxygen/element/buttons/border.png",
|
||||
grid_button_hover: "/voxygen/element/buttons/border_mo.png",
|
||||
grid_button_press: "/voxygen/element/buttons/border_press.png",
|
||||
grid_button_open: "/voxygen/element/buttons/border_pressed.png",
|
||||
|
||||
// Skillbar Module
|
||||
sb_grid: "/voxygen/element/skill_bar/sbar_grid.png",
|
||||
sb_grid_bg: "/voxygen/element/skill_bar/sbar_grid_bg.png",
|
||||
l_click: "/voxygen/element/skill_bar/l.png",
|
||||
r_click: "/voxygen/element/skill_bar/r.png",
|
||||
mana_bar: "/voxygen/element/skill_bar/mana_bar.png",
|
||||
health_bar: "/voxygen/element/skill_bar/health_bar.png",
|
||||
xp_bar: "/voxygen/element/skill_bar/xp_bar.png",
|
||||
|
||||
esc_bg: "/voxygen/element/frames/menu.png",
|
||||
|
||||
window_frame_2: "/voxygen/element/frames/window_2.png",
|
||||
|
||||
settings_bg: "/voxygen/element/frames/settings.png",
|
||||
settings_icon: "/voxygen/element/icons/settings.png",
|
||||
settings_button_mo: "/voxygen/element/buttons/blue_mo.png",
|
||||
|
||||
// Char Window
|
||||
charwindow: "/voxygen/element/misc_bg/charwindow.png",
|
||||
charwindow_icon: "/voxygen/element/icons/charwindow.png",
|
||||
charwindow_tab_bg: "/voxygen/element/frames/tab.png",
|
||||
charwindow_tab: "/voxygen/element/buttons/tab.png",
|
||||
charwindow_expbar: "/voxygen/element/misc_bg/small_bg.png",
|
||||
progress_frame: "/voxygen/element/frames/progress_bar.png",
|
||||
progress: "/voxygen/element/misc_bg/progress.png",
|
||||
|
||||
// Quest-Log Window
|
||||
questlog_bg: "/voxygen/element/misc_bg/small_bg.png",
|
||||
questlog_icon: "/voxygen/element/icons/questlog.png",
|
||||
|
||||
button_blue_mo: "/voxygen/element/buttons/blue_mo.png",
|
||||
button_blue_press: "/voxygen/element/buttons/blue_press.png",
|
||||
|
||||
// Window BG
|
||||
window_bg: "/voxygen/element/misc_bg/window_bg.png",
|
||||
|
||||
// Social Window
|
||||
social_bg: "/voxygen/element/misc_bg/small_bg.png",
|
||||
social_icon: "/voxygen/element/icons/social.png",
|
||||
|
||||
|
||||
<BlankGraphic>
|
||||
blank: (),
|
||||
}
|
||||
}
|
118
voxygen/src/hud/map.rs
Normal file
118
voxygen/src/hud/map.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle},
|
||||
widget_ids, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
use super::{font_ids::Fonts, img_ids::Imgs};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
map_frame,
|
||||
map_bg,
|
||||
map_icon,
|
||||
map_close,
|
||||
map_title,
|
||||
map_frame_l,
|
||||
map_frame_r,
|
||||
map_frame_bl,
|
||||
map_frame_br,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Map<'a> {
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> Map<'a> {
|
||||
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
impl<'a> Widget for Map<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
// BG
|
||||
Rectangle::fill_with([824.0, 976.0], color::TRANSPARENT)
|
||||
.mid_top_with_margin_on(ui.window, 15.0)
|
||||
.scroll_kids()
|
||||
.scroll_kids_vertically()
|
||||
.set(state.ids.map_bg, ui);
|
||||
// Frame
|
||||
Image::new(self.imgs.map_frame_l)
|
||||
.top_left_with_margins_on(state.ids.map_bg, 0.0, 0.0)
|
||||
.w_h(412.0, 488.0)
|
||||
.set(state.ids.map_frame_l, ui);
|
||||
Image::new(self.imgs.map_frame_r)
|
||||
.right_from(state.ids.map_frame_l, 0.0)
|
||||
.w_h(412.0, 488.0)
|
||||
.set(state.ids.map_frame_r, ui);
|
||||
Image::new(self.imgs.map_frame_br)
|
||||
.down_from(state.ids.map_frame_r, 0.0)
|
||||
.w_h(412.0, 488.0)
|
||||
.set(state.ids.map_frame_br, ui);
|
||||
Image::new(self.imgs.map_frame_bl)
|
||||
.down_from(state.ids.map_frame_l, 0.0)
|
||||
.w_h(412.0, 488.0)
|
||||
.set(state.ids.map_frame_bl, ui);
|
||||
|
||||
// Icon
|
||||
Image::new(self.imgs.map_icon)
|
||||
.w_h(224.0 / 3.0, 224.0 / 3.0)
|
||||
.top_left_with_margins_on(state.ids.map_frame, -10.0, -10.0)
|
||||
.set(state.ids.map_icon, ui);
|
||||
|
||||
// Icon
|
||||
Image::new(self.imgs.map_icon)
|
||||
.w_h(224.0 / 3.0, 224.0 / 3.0)
|
||||
.top_left_with_margins_on(state.ids.map_frame, -10.0, -10.0)
|
||||
.set(state.ids.map_icon, ui);
|
||||
|
||||
// X-Button
|
||||
if Button::image(self.imgs.close_button)
|
||||
.w_h(28.0, 28.0)
|
||||
.hover_image(self.imgs.close_button_hover)
|
||||
.press_image(self.imgs.close_button_press)
|
||||
.top_right_with_margins_on(state.ids.map_frame_r, 0.0, 0.0)
|
||||
.set(state.ids.map_close, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::Close);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
115
voxygen/src/hud/minimap.rs
Normal file
115
voxygen/src/hud/minimap.rs
Normal file
@ -0,0 +1,115 @@
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, Show, TEXT_COLOR};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
mmap_frame,
|
||||
mmap_frame_bg,
|
||||
mmap_location,
|
||||
mmap_button,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct MiniMap<'a> {
|
||||
show: &'a Show,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> MiniMap<'a> {
|
||||
pub fn new(show: &'a Show, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
show,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
Toggle,
|
||||
}
|
||||
|
||||
impl<'a> Widget for MiniMap<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
if self.show.mini_map {
|
||||
Image::new(self.imgs.mmap_frame)
|
||||
.w_h(100.0 * 2.0, 100.0 * 2.0)
|
||||
.top_right_with_margins_on(ui.window, 5.0, 5.0)
|
||||
.set(state.ids.mmap_frame, ui);
|
||||
|
||||
Rectangle::fill_with([92.0 * 2.0, 82.0 * 2.0], color::TRANSPARENT)
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, 13.0 * 2.0 + 2.0)
|
||||
.set(state.ids.mmap_frame_bg, ui);
|
||||
} else {
|
||||
Image::new(self.imgs.mmap_frame_closed)
|
||||
.w_h(100.0 * 2.0, 11.0 * 2.0)
|
||||
.top_right_with_margins_on(ui.window, 5.0, 5.0)
|
||||
.set(state.ids.mmap_frame, ui);
|
||||
}
|
||||
|
||||
if Button::image(if self.show.mini_map {
|
||||
self.imgs.mmap_open
|
||||
} else {
|
||||
self.imgs.mmap_closed
|
||||
})
|
||||
.w_h(100.0 * 0.2, 100.0 * 0.2)
|
||||
.hover_image(if self.show.mini_map {
|
||||
self.imgs.mmap_open_hover
|
||||
} else {
|
||||
self.imgs.mmap_closed_hover
|
||||
})
|
||||
.press_image(if self.show.mini_map {
|
||||
self.imgs.mmap_open_press
|
||||
} else {
|
||||
self.imgs.mmap_closed_press
|
||||
})
|
||||
.top_right_with_margins_on(state.ids.mmap_frame, 0.0, 0.0)
|
||||
.set(state.ids.mmap_button, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::Toggle);
|
||||
}
|
||||
|
||||
// Title
|
||||
// Make it display the actual location
|
||||
Text::new("Uncanny Valley")
|
||||
.mid_top_with_margin_on(state.ids.mmap_frame, 3.0)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.mmap_location, ui);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
484
voxygen/src/hud/settings_window.rs
Normal file
484
voxygen/src/hud/settings_window.rs
Normal file
@ -0,0 +1,484 @@
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, TEXT_COLOR};
|
||||
use crate::{hud::Show, ui::ToggleButton};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Scrollbar, Text},
|
||||
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
|
||||
settings_content,
|
||||
settings_icon,
|
||||
settings_button_mo,
|
||||
settings_close,
|
||||
settings_title,
|
||||
settings_r,
|
||||
settings_l,
|
||||
settings_scrollbar,
|
||||
controls_text,
|
||||
controls_controls,
|
||||
button_help,
|
||||
button_help2,
|
||||
show_help_label,
|
||||
gameplay,
|
||||
controls,
|
||||
rectangle,
|
||||
debug_button,
|
||||
debug_button_label,
|
||||
interface,
|
||||
inventory_test_button,
|
||||
inventory_test_button_label,
|
||||
settings_bg,
|
||||
sound,
|
||||
test,
|
||||
video,
|
||||
}
|
||||
}
|
||||
|
||||
enum SettingsTab {
|
||||
Interface,
|
||||
Video,
|
||||
Sound,
|
||||
Gameplay,
|
||||
Controls,
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct SettingsWindow<'a> {
|
||||
show: &'a Show,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> SettingsWindow<'a> {
|
||||
pub fn new(show: &'a Show, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
show,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
settings_tab: SettingsTab,
|
||||
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
ToggleHelp,
|
||||
ToggleInventoryTestButton,
|
||||
ToggleDebug,
|
||||
Close,
|
||||
}
|
||||
|
||||
impl<'a> Widget for SettingsWindow<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
settings_tab: SettingsTab::Interface,
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
// Frame Alignment
|
||||
Rectangle::fill_with([824.0, 488.0], color::TRANSPARENT)
|
||||
.middle_of(ui.window)
|
||||
.set(state.ids.settings_bg, ui);
|
||||
// Frame
|
||||
Image::new(self.imgs.settings_frame_l)
|
||||
.top_left_with_margins_on(state.ids.settings_bg, 0.0, 0.0)
|
||||
.w_h(412.0, 488.0)
|
||||
.set(state.ids.settings_l, ui);
|
||||
Image::new(self.imgs.settings_frame_r)
|
||||
.right_from(state.ids.settings_l, 0.0)
|
||||
.parent(state.ids.settings_bg)
|
||||
.w_h(412.0, 488.0)
|
||||
.set(state.ids.settings_r, ui);
|
||||
// Content Alignment
|
||||
Rectangle::fill_with([198.0 * 4.0, 97.0 * 4.0], color::TRANSPARENT)
|
||||
.top_right_with_margins_on(state.ids.settings_r, 21.0 * 4.0, 4.0 * 4.0)
|
||||
.scroll_kids()
|
||||
.scroll_kids_vertically()
|
||||
.set(state.ids.settings_content, ui);
|
||||
Scrollbar::y_axis(state.ids.settings_content)
|
||||
.thickness(5.0)
|
||||
.rgba(0.33, 0.33, 0.33, 1.0)
|
||||
.set(state.ids.settings_scrollbar, ui);
|
||||
// X-Button
|
||||
if Button::image(self.imgs.close_button)
|
||||
.w_h(28.0, 28.0)
|
||||
.hover_image(self.imgs.close_button_hover)
|
||||
.press_image(self.imgs.close_button_press)
|
||||
.top_right_with_margins_on(state.ids.settings_r, 0.0, 0.0)
|
||||
.set(state.ids.settings_close, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::Close);
|
||||
}
|
||||
|
||||
// Title
|
||||
Text::new("Settings")
|
||||
.mid_top_with_margin_on(state.ids.settings_bg, 5.0)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.settings_title, ui);
|
||||
|
||||
// Interface
|
||||
if Button::image(if let SettingsTab::Interface = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button
|
||||
})
|
||||
.w_h(31.0 * 4.0, 12.0 * 4.0)
|
||||
.hover_image(if let SettingsTab::Interface = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_hover
|
||||
})
|
||||
.press_image(if let SettingsTab::Interface = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_press
|
||||
})
|
||||
.top_left_with_margins_on(state.ids.settings_l, 8.0 * 4.0, 2.0 * 4.0)
|
||||
.label("Interface")
|
||||
.label_font_size(14)
|
||||
.label_color(TEXT_COLOR)
|
||||
.set(state.ids.interface, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
state.update(|s| s.settings_tab = SettingsTab::Interface);
|
||||
}
|
||||
|
||||
if let SettingsTab::Interface = state.settings_tab {
|
||||
// Help
|
||||
let show_help =
|
||||
ToggleButton::new(self.show.help, self.imgs.check, self.imgs.check_checked)
|
||||
.w_h(288.0 / 24.0, 288.0 / 24.0)
|
||||
.top_left_with_margins_on(state.ids.settings_content, 5.0, 5.0)
|
||||
.hover_images(self.imgs.check_checked_mo, self.imgs.check_mo)
|
||||
.press_images(self.imgs.check_press, self.imgs.check_press)
|
||||
.set(state.ids.button_help, ui);
|
||||
|
||||
if self.show.help != show_help {
|
||||
return Some(Event::ToggleHelp);
|
||||
}
|
||||
|
||||
Text::new("Show Help")
|
||||
.right_from(state.ids.button_help, 10.0)
|
||||
.font_size(12)
|
||||
.font_id(self.fonts.opensans)
|
||||
.graphics_for(state.ids.button_help)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.show_help_label, ui);
|
||||
|
||||
// Inventory test
|
||||
let inventory_test_button = ToggleButton::new(
|
||||
self.show.inventory_test_button,
|
||||
self.imgs.check,
|
||||
self.imgs.check_checked,
|
||||
)
|
||||
.w_h(288.0 / 24.0, 288.0 / 24.0)
|
||||
.down_from(state.ids.button_help, 7.0)
|
||||
.hover_images(self.imgs.check_checked_mo, self.imgs.check_mo)
|
||||
.press_images(self.imgs.check_press, self.imgs.check_press)
|
||||
.set(state.ids.inventory_test_button, ui);
|
||||
|
||||
if self.show.inventory_test_button != inventory_test_button {
|
||||
return Some(Event::ToggleInventoryTestButton);
|
||||
}
|
||||
|
||||
Text::new("Show Inventory Test Button")
|
||||
.right_from(state.ids.inventory_test_button, 10.0)
|
||||
.font_size(14)
|
||||
.font_id(self.fonts.opensans)
|
||||
.graphics_for(state.ids.inventory_test_button)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.inventory_test_button_label, ui);
|
||||
|
||||
// Debug
|
||||
let show_debug =
|
||||
ToggleButton::new(self.show.debug, self.imgs.check, self.imgs.check_checked)
|
||||
.w_h(288.0 / 24.0, 288.0 / 24.0)
|
||||
.down_from(state.ids.inventory_test_button, 7.0)
|
||||
.hover_images(self.imgs.check_checked_mo, self.imgs.check_mo)
|
||||
.press_images(self.imgs.check_press, self.imgs.check_press)
|
||||
.set(state.ids.debug_button, ui);
|
||||
|
||||
if self.show.debug != show_debug {
|
||||
return Some(Event::ToggleDebug);
|
||||
}
|
||||
|
||||
Text::new("Show Debug Window")
|
||||
.right_from(state.ids.debug_button, 10.0)
|
||||
.font_size(14)
|
||||
.font_id(self.fonts.opensans)
|
||||
.graphics_for(state.ids.debug_button)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.debug_button_label, ui);
|
||||
}
|
||||
|
||||
// 2 Gameplay////////////////
|
||||
if Button::image(if let SettingsTab::Gameplay = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button
|
||||
})
|
||||
.w_h(31.0 * 4.0, 12.0 * 4.0)
|
||||
.hover_image(if let SettingsTab::Gameplay = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_hover
|
||||
})
|
||||
.press_image(if let SettingsTab::Gameplay = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_press
|
||||
})
|
||||
.right_from(state.ids.interface, 0.0)
|
||||
.label("Gameplay")
|
||||
.label_font_size(14)
|
||||
.label_color(TEXT_COLOR)
|
||||
.set(state.ids.gameplay, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
state.update(|s| s.settings_tab = SettingsTab::Gameplay);
|
||||
}
|
||||
|
||||
// 3 Controls/////////////////////
|
||||
if Button::image(if let SettingsTab::Controls = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button
|
||||
})
|
||||
.w_h(31.0 * 4.0, 12.0 * 4.0)
|
||||
.hover_image(if let SettingsTab::Controls = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_hover
|
||||
})
|
||||
.press_image(if let SettingsTab::Controls = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_press
|
||||
})
|
||||
.right_from(state.ids.gameplay, 0.0)
|
||||
.label("Controls")
|
||||
.label_font_size(14)
|
||||
.label_color(TEXT_COLOR)
|
||||
.set(state.ids.controls, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
state.update(|s| s.settings_tab = SettingsTab::Controls);
|
||||
}
|
||||
if let SettingsTab::Controls = state.settings_tab {
|
||||
Text::new(
|
||||
"Free Cursor\n\
|
||||
Toggle Help Window\n\
|
||||
Toggle Interface\n\
|
||||
Toggle FPS and Debug Info\n\
|
||||
\n\
|
||||
\n\
|
||||
Move Forward\n\
|
||||
Move Left\n\
|
||||
Move Right\n\
|
||||
Move Backwards\n\
|
||||
\n\
|
||||
Jump\n\
|
||||
\n\
|
||||
Dodge\n\
|
||||
\n\
|
||||
Auto Walk\n\
|
||||
\n\
|
||||
Sheathe/Draw Weapons\n\
|
||||
\n\
|
||||
Put on/Remove Helmet\n\
|
||||
\n\
|
||||
\n\
|
||||
Basic Attack\n\
|
||||
Secondary Attack/Block/Aim\n\
|
||||
\n\
|
||||
\n\
|
||||
Skillbar Slot 1\n\
|
||||
Skillbar Slot 2\n\
|
||||
Skillbar Slot 3\n\
|
||||
Skillbar Slot 4\n\
|
||||
Skillbar Slot 5\n\
|
||||
Skillbar Slot 6\n\
|
||||
Skillbar Slot 7\n\
|
||||
Skillbar Slot 8\n\
|
||||
Skillbar Slot 9\n\
|
||||
Skillbar Slot 10\n\
|
||||
\n\
|
||||
\n\
|
||||
Pause Menu\n\
|
||||
Settings\n\
|
||||
Social\n\
|
||||
Map\n\
|
||||
Spellbook\n\
|
||||
Character\n\
|
||||
Questlog\n\
|
||||
Bag\n\
|
||||
\n\
|
||||
\n\
|
||||
\n\
|
||||
Send Chat Message\n\
|
||||
Scroll Chat\n\
|
||||
\n\
|
||||
\n\
|
||||
Chat commands: \n\
|
||||
\n\
|
||||
/alias [Name] - Change your Chat Name \n\
|
||||
/tp [Name] - Teleports you to another player
|
||||
",
|
||||
)
|
||||
.color(TEXT_COLOR)
|
||||
.top_left_with_margins_on(state.ids.settings_content, 5.0, 5.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(18)
|
||||
.set(state.ids.controls_text, ui);
|
||||
// TODO: Replace with buttons that show the actual keybind and allow the user to change it.
|
||||
Text::new(
|
||||
"TAB\n\
|
||||
F1\n\
|
||||
F2\n\
|
||||
F3\n\
|
||||
\n\
|
||||
\n\
|
||||
W\n\
|
||||
A\n\
|
||||
S\n\
|
||||
D\n\
|
||||
\n\
|
||||
SPACE\n\
|
||||
\n\
|
||||
??\n\
|
||||
\n\
|
||||
??\n\
|
||||
\n\
|
||||
??\n\
|
||||
\n\
|
||||
??\n\
|
||||
\n\
|
||||
\n\
|
||||
L-Click\n\
|
||||
R-Click\n\
|
||||
\n\
|
||||
\n\
|
||||
1\n\
|
||||
2\n\
|
||||
3\n\
|
||||
4\n\
|
||||
5\n\
|
||||
6\n\
|
||||
7\n\
|
||||
8\n\
|
||||
9\n\
|
||||
0\n\
|
||||
\n\
|
||||
\n\
|
||||
ESC\n\
|
||||
N\n\
|
||||
O\n\
|
||||
M\n\
|
||||
P\n\
|
||||
C\n\
|
||||
L\n\
|
||||
B\n\
|
||||
\n\
|
||||
\n\
|
||||
\n\
|
||||
ENTER\n\
|
||||
Mousewheel\n\
|
||||
\n\
|
||||
\n\
|
||||
\n\
|
||||
\n\
|
||||
\n\
|
||||
\n\
|
||||
",
|
||||
)
|
||||
.color(TEXT_COLOR)
|
||||
.right_from(state.ids.controls_text, 0.0)
|
||||
.font_id(self.fonts.opensans)
|
||||
.font_size(18)
|
||||
.set(state.ids.controls_controls, ui);
|
||||
}
|
||||
// 4 Video////////////////////////////////
|
||||
if Button::image(if let SettingsTab::Video = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button
|
||||
})
|
||||
.w_h(31.0 * 4.0, 12.0 * 4.0)
|
||||
.hover_image(if let SettingsTab::Video = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_hover
|
||||
})
|
||||
.press_image(if let SettingsTab::Video = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_press
|
||||
})
|
||||
.right_from(state.ids.controls, 0.0)
|
||||
.label("Video")
|
||||
.parent(state.ids.settings_r)
|
||||
.label_font_size(14)
|
||||
.label_color(TEXT_COLOR)
|
||||
.set(state.ids.video, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
state.update(|s| s.settings_tab = SettingsTab::Video);
|
||||
}
|
||||
|
||||
// 5 Sound///////////////////////////////
|
||||
if Button::image(if let SettingsTab::Sound = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button
|
||||
})
|
||||
.w_h(31.0 * 4.0, 12.0 * 4.0)
|
||||
.hover_image(if let SettingsTab::Sound = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_hover
|
||||
})
|
||||
.press_image(if let SettingsTab::Sound = state.settings_tab {
|
||||
self.imgs.settings_button_pressed
|
||||
} else {
|
||||
self.imgs.settings_button_press
|
||||
})
|
||||
.right_from(state.ids.video, 0.0)
|
||||
.parent(state.ids.settings_r)
|
||||
.label("Sound")
|
||||
.label_font_size(14)
|
||||
.label_color(TEXT_COLOR)
|
||||
.set(state.ids.sound, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
state.update(|s| s.settings_tab = SettingsTab::Sound);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
170
voxygen/src/hud/skillbar.rs
Normal file
170
voxygen/src/hud/skillbar.rs
Normal file
@ -0,0 +1,170 @@
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR};
|
||||
use conrod_core::{
|
||||
widget::{self, Image, Rectangle, Text},
|
||||
widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
health_bar,
|
||||
health_bar_color,
|
||||
l_click,
|
||||
level_text,
|
||||
mana_bar,
|
||||
mana_bar_color,
|
||||
next_level_text,
|
||||
r_click,
|
||||
sb_grid_bg_l,
|
||||
sb_grid_bg_r,
|
||||
sb_grid_l,
|
||||
sb_grid_r,
|
||||
test,
|
||||
xp_bar,
|
||||
xp_bar_progress,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct Skillbar<'a> {
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> Skillbar<'a> {
|
||||
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {}
|
||||
|
||||
impl<'a> Widget for Skillbar<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
// TODO: Read from parameter / character struct
|
||||
let xp_percentage = 0.4;
|
||||
let hp_percentage = 0.4;
|
||||
let mana_percentage = 0.4;
|
||||
|
||||
// Experience-Bar
|
||||
Image::new(self.imgs.xp_bar)
|
||||
.w_h(2688.0 / 6.0, 116.0 / 6.0)
|
||||
.mid_bottom_of(ui.window)
|
||||
.set(state.ids.xp_bar, ui);
|
||||
|
||||
Rectangle::fill_with([406.0 * (xp_percentage), 5.0], XP_COLOR) // "W=406*[Exp. %]"
|
||||
.top_left_with_margins_on(state.ids.xp_bar, 5.0, 21.0)
|
||||
.set(state.ids.xp_bar_progress, ui);
|
||||
|
||||
// Left Grid
|
||||
Image::new(self.imgs.sb_grid)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.up_from(state.ids.xp_bar, 0.0)
|
||||
.align_left_of(state.ids.xp_bar)
|
||||
.set(state.ids.sb_grid_l, ui);
|
||||
|
||||
Image::new(self.imgs.sb_grid_bg)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.middle_of(state.ids.sb_grid_l)
|
||||
.set(state.ids.sb_grid_bg_l, ui);
|
||||
|
||||
// Right Grid
|
||||
Image::new(self.imgs.sb_grid)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.up_from(state.ids.xp_bar, 0.0)
|
||||
.align_right_of(state.ids.xp_bar)
|
||||
.set(state.ids.sb_grid_r, ui);
|
||||
|
||||
Image::new(self.imgs.sb_grid_bg)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.middle_of(state.ids.sb_grid_r)
|
||||
.set(state.ids.sb_grid_bg_r, ui);
|
||||
|
||||
// Right and Left Click
|
||||
Image::new(self.imgs.l_click)
|
||||
.w_h(224.0 / 6.0, 320.0 / 6.0)
|
||||
.right_from(state.ids.sb_grid_bg_l, 0.0)
|
||||
.align_bottom_of(state.ids.sb_grid_bg_l)
|
||||
.set(state.ids.l_click, ui);
|
||||
|
||||
Image::new(self.imgs.r_click)
|
||||
.w_h(224.0 / 6.0, 320.0 / 6.0)
|
||||
.left_from(state.ids.sb_grid_bg_r, 0.0)
|
||||
.align_bottom_of(state.ids.sb_grid_bg_r)
|
||||
.set(state.ids.r_click, ui);
|
||||
|
||||
// Health Bar
|
||||
Image::new(self.imgs.health_bar)
|
||||
.w_h(1120.0 / 6.0, 96.0 / 6.0)
|
||||
.left_from(state.ids.l_click, 0.0)
|
||||
.align_top_of(state.ids.l_click)
|
||||
.set(state.ids.health_bar, ui);
|
||||
|
||||
// Filling
|
||||
Rectangle::fill_with([182.0 * (hp_percentage), 6.0], HP_COLOR) // "W=182.0 * [Health. %]"
|
||||
.top_right_with_margins_on(state.ids.health_bar, 5.0, 0.0)
|
||||
.set(state.ids.health_bar_color, ui);
|
||||
|
||||
// Mana Bar
|
||||
Image::new(self.imgs.mana_bar)
|
||||
.w_h(1120.0 / 6.0, 96.0 / 6.0)
|
||||
.right_from(state.ids.r_click, 0.0)
|
||||
.align_top_of(state.ids.r_click)
|
||||
.set(state.ids.mana_bar, ui);
|
||||
|
||||
// Filling
|
||||
Rectangle::fill_with([182.0 * (mana_percentage), 6.0], MANA_COLOR) // "W=182.0 * [Mana. %]"
|
||||
.top_left_with_margins_on(state.ids.mana_bar, 5.0, 0.0)
|
||||
.set(state.ids.mana_bar_color, ui);
|
||||
|
||||
// Buffs/Debuffs
|
||||
|
||||
// Buffs
|
||||
|
||||
// Debuffs
|
||||
|
||||
// Level Display
|
||||
|
||||
// Insert actual Level here
|
||||
Text::new("1")
|
||||
.left_from(state.ids.xp_bar, -15.0)
|
||||
.font_size(10)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.level_text, ui);
|
||||
|
||||
// Insert next Level here
|
||||
Text::new("2")
|
||||
.right_from(state.ids.xp_bar, -15.0)
|
||||
.font_size(10)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.next_level_text, ui);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
136
voxygen/src/hud/small_window.rs
Normal file
136
voxygen/src/hud/small_window.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use super::{font_ids::Fonts, img_ids::Imgs, Windows, TEXT_COLOR};
|
||||
use crate::hud::Show;
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
frame,
|
||||
bg,
|
||||
title,
|
||||
icon,
|
||||
close,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum SmallWindowType {
|
||||
Spellbook,
|
||||
Social,
|
||||
QuestLog,
|
||||
}
|
||||
|
||||
#[derive(WidgetCommon)]
|
||||
pub struct SmallWindow<'a> {
|
||||
content: SmallWindowType,
|
||||
show: &'a Show,
|
||||
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> SmallWindow<'a> {
|
||||
pub fn new(content: SmallWindowType, show: &'a Show, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
Self {
|
||||
content,
|
||||
show,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
impl<'a> Widget for SmallWindow<'a> {
|
||||
type State = State;
|
||||
type Style = ();
|
||||
type Event = Option<Event>;
|
||||
|
||||
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
ids: Ids::new(id_gen),
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
let (title, icon) = match self.content {
|
||||
SmallWindowType::Social => ("Social", self.imgs.social_icon),
|
||||
SmallWindowType::Spellbook => ("Spellbook", self.imgs.spellbook_icon),
|
||||
SmallWindowType::QuestLog => ("QuestLog", self.imgs.questlog_icon),
|
||||
};
|
||||
|
||||
// Frame
|
||||
// TODO: Relative to Char Window?
|
||||
if let Windows::CharacterAnd(_) = self.show.open_windows {
|
||||
Image::new(self.imgs.window_frame)
|
||||
.top_left_with_margins_on(ui.window, 200.0, 658.0)
|
||||
.w_h(107.0 * 4.0, 125.0 * 4.0)
|
||||
.set(state.ids.frame, ui);
|
||||
} else {
|
||||
Image::new(self.imgs.window_frame)
|
||||
.top_left_with_margins_on(ui.window, 200.0, 10.0)
|
||||
.w_h(107.0 * 4.0, 125.0 * 4.0)
|
||||
.set(state.ids.frame, ui);
|
||||
}
|
||||
|
||||
// Icon
|
||||
Image::new(icon)
|
||||
.w_h(40.0, 40.0)
|
||||
.top_left_with_margins_on(state.ids.frame, 4.0, 4.0)
|
||||
.set(state.ids.icon, ui);
|
||||
|
||||
// Content alignment
|
||||
Rectangle::fill_with([362.0, 418.0], color::TRANSPARENT)
|
||||
.bottom_right_with_margins_on(state.ids.frame, 17.0, 17.0)
|
||||
.scroll_kids()
|
||||
.scroll_kids_vertically()
|
||||
.set(state.ids.bg, ui);
|
||||
|
||||
// X-Button
|
||||
if Button::image(self.imgs.close_button)
|
||||
.w_h(28.0, 28.0)
|
||||
.hover_image(self.imgs.close_button_hover)
|
||||
.press_image(self.imgs.close_button_press)
|
||||
.top_right_with_margins_on(state.ids.frame, 12.0, 0.0)
|
||||
.set(state.ids.close, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
return Some(Event::Close);
|
||||
}
|
||||
// Title
|
||||
Text::new(title)
|
||||
.mid_top_with_margin_on(state.ids.frame, 16.0)
|
||||
.font_id(self.fonts.metamorph)
|
||||
.font_size(14)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.title, ui);
|
||||
|
||||
match self.content {
|
||||
SmallWindowType::Social => {}
|
||||
SmallWindowType::Spellbook => {}
|
||||
SmallWindowType::QuestLog => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
#![feature(drain_filter)]
|
||||
#![recursion_limit = "2048"]
|
||||
|
||||
#[macro_use]
|
||||
pub mod ui;
|
||||
pub mod anim;
|
||||
pub mod error;
|
||||
pub mod hud;
|
||||
@ -12,7 +14,6 @@ pub mod scene;
|
||||
pub mod session;
|
||||
pub mod settings;
|
||||
pub mod singleplayer;
|
||||
pub mod ui;
|
||||
pub mod window;
|
||||
|
||||
// Reexports
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
ui::{self, ScaleMode, Ui},
|
||||
ui::{self, Graphic, ScaleMode, Ui},
|
||||
window::Window,
|
||||
};
|
||||
use common::{
|
||||
assets,
|
||||
comp::character::{Belt, Character, Chest, Foot, Gender, Hand, Head, Pants, Race, Weapon},
|
||||
figure::Segment,
|
||||
};
|
||||
use conrod_core::{
|
||||
color,
|
||||
@ -16,6 +15,7 @@ use conrod_core::{
|
||||
widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, TextBox},
|
||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
@ -231,26 +231,16 @@ struct Imgs {
|
||||
icon_border_pressed: ImgId,
|
||||
}
|
||||
impl Imgs {
|
||||
fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs {
|
||||
fn new(ui: &mut Ui) -> Imgs {
|
||||
let load_img = |filename, ui: &mut Ui| {
|
||||
let fullpath: String = ["/voxygen/", filename].concat();
|
||||
let image = image::load_from_memory(
|
||||
assets::load(fullpath.as_str())
|
||||
.expect("Error loading file")
|
||||
.as_slice(),
|
||||
)
|
||||
.unwrap();
|
||||
ui.new_graphic(ui::Graphic::Image(image))
|
||||
let image = assets::load::<image::DynamicImage>(fullpath.as_str()).unwrap();
|
||||
ui.add_graphic(Graphic::Image(image))
|
||||
};
|
||||
let load_vox = |filename, ui: &mut Ui| {
|
||||
let fullpath: String = ["/voxygen/", filename].concat();
|
||||
let dot_vox = dot_vox::load_bytes(
|
||||
assets::load(fullpath.as_str())
|
||||
.expect("Error loading file")
|
||||
.as_slice(),
|
||||
)
|
||||
.unwrap();
|
||||
ui.new_graphic(ui::Graphic::Voxel(Segment::from(dot_vox)))
|
||||
let dot_vox = assets::load::<dot_vox::DotVoxData>(fullpath.as_str()).unwrap();
|
||||
ui.add_graphic(Graphic::Voxel(dot_vox))
|
||||
};
|
||||
Imgs {
|
||||
v_logo: load_vox("element/v_logo.vox", ui),
|
||||
@ -367,16 +357,11 @@ impl CharSelectionUi {
|
||||
// Generate ids
|
||||
let ids = Ids::new(ui.id_generator());
|
||||
// Load images
|
||||
let imgs = Imgs::new(&mut ui, window.renderer_mut());
|
||||
let imgs = Imgs::new(&mut ui);
|
||||
// Load fonts
|
||||
let load_font = |filename, ui: &mut Ui| {
|
||||
let fullpath: String = ["/voxygen/font", filename].concat();
|
||||
ui.new_font(
|
||||
conrod_core::text::Font::from_bytes(
|
||||
assets::load(fullpath.as_str()).expect("Error loading file"),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
ui.new_font(assets::load(fullpath.as_str()).expect("Error loading file"))
|
||||
};
|
||||
let font_opensans = load_font("/OpenSans-Regular.ttf", &mut ui);
|
||||
let font_metamorph = load_font("/Metamorphous-Regular.ttf", &mut ui);
|
||||
|
@ -1,19 +1,19 @@
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
ui::{self, ScaleMode, Ui},
|
||||
window::Window,
|
||||
ui::{self, Graphic, ScaleMode, Ui},
|
||||
GlobalState, DEFAULT_PUBLIC_SERVER,
|
||||
};
|
||||
use common::{assets, figure::Segment};
|
||||
use common::assets;
|
||||
use conrod_core::{
|
||||
color,
|
||||
color::TRANSPARENT,
|
||||
image::Id as ImgId,
|
||||
position::{Dimension, Relative},
|
||||
position::Relative,
|
||||
text::font::Id as FontId,
|
||||
widget::{text_box::Event as TextBoxEvent, Button, Image, List, Rectangle, Text, TextBox},
|
||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
@ -63,26 +63,16 @@ struct Imgs {
|
||||
button_press: ImgId,
|
||||
}
|
||||
impl Imgs {
|
||||
fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs {
|
||||
fn new(ui: &mut Ui) -> Imgs {
|
||||
let load_img = |filename, ui: &mut Ui| {
|
||||
let fullpath: String = ["/voxygen/", filename].concat();
|
||||
let image = image::load_from_memory(
|
||||
assets::load(fullpath.as_str())
|
||||
.expect("Error loading file")
|
||||
.as_slice(),
|
||||
)
|
||||
.unwrap();
|
||||
ui.new_graphic(ui::Graphic::Image(image))
|
||||
let image = assets::load::<image::DynamicImage>(fullpath.as_str()).unwrap();
|
||||
ui.add_graphic(Graphic::Image(image))
|
||||
};
|
||||
let load_vox = |filename, ui: &mut Ui| {
|
||||
let fullpath: String = ["/voxygen/", filename].concat();
|
||||
let dot_vox = dot_vox::load_bytes(
|
||||
assets::load(fullpath.as_str())
|
||||
.expect("Error loading file")
|
||||
.as_slice(),
|
||||
)
|
||||
.unwrap();
|
||||
ui.new_graphic(ui::Graphic::Voxel(Segment::from(dot_vox)))
|
||||
let dot_vox = assets::load::<dot_vox::DotVoxData>(fullpath.as_str()).unwrap();
|
||||
ui.add_graphic(Graphic::Voxel(dot_vox))
|
||||
};
|
||||
Imgs {
|
||||
bg: load_img("background/bg_main.png", ui),
|
||||
@ -123,7 +113,7 @@ pub struct MainMenuUi {
|
||||
|
||||
impl MainMenuUi {
|
||||
pub fn new(global_state: &mut GlobalState) -> Self {
|
||||
let mut window = &mut global_state.window;
|
||||
let window = &mut global_state.window;
|
||||
let networking = &global_state.settings.networking;
|
||||
let mut ui = Ui::new(window).unwrap();
|
||||
// TODO: adjust/remove this, right now it is used to demonstrate window scaling functionality
|
||||
@ -131,16 +121,11 @@ impl MainMenuUi {
|
||||
// Generate ids
|
||||
let ids = Ids::new(ui.id_generator());
|
||||
// Load images
|
||||
let imgs = Imgs::new(&mut ui, window.renderer_mut());
|
||||
let imgs = Imgs::new(&mut ui);
|
||||
// Load fonts
|
||||
let load_font = |filename, ui: &mut Ui| {
|
||||
let fullpath: String = ["/voxygen/font", filename].concat();
|
||||
ui.new_font(
|
||||
conrod_core::text::Font::from_bytes(
|
||||
assets::load(fullpath.as_str()).expect("Error loading file"),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
ui.new_font(assets::load(fullpath.as_str()).expect("Error loading file"))
|
||||
};
|
||||
let font_opensans = load_font("/OpenSans-Regular.ttf", &mut ui);
|
||||
let font_metamorph = load_font("/Metamorphous-Regular.ttf", &mut ui);
|
||||
|
@ -19,6 +19,7 @@ use common::{
|
||||
figure::Segment,
|
||||
msg,
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use specs::{Component, Entity as EcsEntity, Join, VecStorage};
|
||||
use std::{collections::HashMap, f32};
|
||||
use vek::*;
|
||||
@ -98,17 +99,10 @@ impl FigureCache {
|
||||
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
||||
}
|
||||
|
||||
fn load_mesh(filename: &'static str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||
fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||
let fullpath: String = ["/voxygen/voxel/", filename].concat();
|
||||
Segment::from(
|
||||
dot_vox::load_bytes(
|
||||
assets::load(fullpath.as_str())
|
||||
.expect("Error loading file")
|
||||
.as_slice(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.generate_mesh(position)
|
||||
Segment::from(assets::load_expect::<DotVoxData>(fullpath.as_str()).as_ref())
|
||||
.generate_mesh(position)
|
||||
}
|
||||
|
||||
fn load_head(head: Head) -> Mesh<FigurePipeline> {
|
||||
|
30
voxygen/src/ui/font_ids.rs
Normal file
30
voxygen/src/ui/font_ids.rs
Normal file
@ -0,0 +1,30 @@
|
||||
/// This macro will automatically load all specified assets, get the corresponding FontIds and
|
||||
/// create a struct with all of them
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```
|
||||
/// image_ids! {
|
||||
/// pub struct Imgs {
|
||||
/// font1: "filename1.vox",
|
||||
/// font2: "filename2.vox",
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! font_ids {
|
||||
($($v:vis struct $Ids:ident { $( $name:ident: $specifier:expr $(,)? )* })*) => {
|
||||
$(
|
||||
$v struct $Ids {
|
||||
$( $v $name: conrod_core::text::font::Id, )*
|
||||
}
|
||||
|
||||
impl $Ids {
|
||||
pub fn load(ui: &mut crate::ui::Ui) -> Result<Self, common::assets::Error> {
|
||||
Ok(Self {
|
||||
$( $name: ui.new_font(common::assets::load($specifier)?), )*
|
||||
})
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
use common::figure::Segment;
|
||||
use dot_vox::DotVoxData;
|
||||
use fnv::FnvHashMap;
|
||||
use guillotiere::{size2, Allocation, AtlasAllocator};
|
||||
use image::DynamicImage;
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
pub enum Graphic {
|
||||
Image(DynamicImage),
|
||||
Voxel(Segment),
|
||||
Image(Arc<DynamicImage>),
|
||||
Voxel(Arc<DotVoxData>),
|
||||
Blank,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub struct Id(u32);
|
||||
|
||||
@ -29,7 +31,7 @@ impl GraphicCache {
|
||||
next_id: 0,
|
||||
}
|
||||
}
|
||||
pub fn new_graphic(&mut self, graphic: Graphic) -> Id {
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> Id {
|
||||
let id = self.next_id;
|
||||
self.next_id = id.wrapping_add(1);
|
||||
|
||||
@ -92,8 +94,8 @@ impl GraphicCache {
|
||||
.pixels()
|
||||
.map(|p| p.data)
|
||||
.collect::<Vec<[u8; 4]>>(),
|
||||
Graphic::Voxel(segment) => {
|
||||
super::renderer::draw_vox(&segment, aabr.size().into())
|
||||
Graphic::Voxel(ref vox) => {
|
||||
super::renderer::draw_vox(&vox.as_ref().into(), aabr.size().into())
|
||||
}
|
||||
Graphic::Blank => return None,
|
||||
};
|
||||
|
70
voxygen/src/ui/img_ids.rs
Normal file
70
voxygen/src/ui/img_ids.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use super::Graphic;
|
||||
use common::assets::{load, Error};
|
||||
use dot_vox::DotVoxData;
|
||||
use image::DynamicImage;
|
||||
|
||||
pub struct BlankGraphic;
|
||||
pub struct ImageGraphic;
|
||||
pub struct VoxelGraphic;
|
||||
|
||||
pub trait GraphicCreator<'a> {
|
||||
type Specifier;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error>;
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for BlankGraphic {
|
||||
type Specifier = ();
|
||||
fn new_graphic(_: ()) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Blank)
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for ImageGraphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Image(load::<DynamicImage>(specifier)?))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelGraphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// This macro will automatically load all specified assets, get the corresponding ImgIds and
|
||||
/// create a struct with all of them
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```
|
||||
/// image_ids! {
|
||||
/// pub struct Imgs {
|
||||
/// <VoxelGraphic>
|
||||
/// button1: "filename1.vox",
|
||||
/// button2: "filename2.vox",
|
||||
///
|
||||
/// <ImageGraphic>
|
||||
/// background: "background.png",
|
||||
///
|
||||
/// <BlankGraphic>
|
||||
/// blank: (),
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! image_ids {
|
||||
($($v:vis struct $Ids:ident { $( <$T:ty> $( $name:ident: $specifier:expr ),* $(,)? )* })*) => {
|
||||
$(
|
||||
$v struct $Ids {
|
||||
$($( $v $name: conrod_core::image::Id, )*)*
|
||||
}
|
||||
|
||||
impl $Ids {
|
||||
pub fn load(ui: &mut crate::ui::Ui) -> Result<Self, common::assets::Error> {
|
||||
use crate::ui::GraphicCreator;
|
||||
Ok(Self {
|
||||
$($( $name: ui.add_graphic(<$T>::new_graphic($specifier)?), )*)*
|
||||
})
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
@ -1,8 +1,13 @@
|
||||
mod graphic;
|
||||
mod util;
|
||||
mod widgets;
|
||||
#[macro_use]
|
||||
mod img_ids;
|
||||
#[macro_use]
|
||||
mod font_ids;
|
||||
|
||||
pub use graphic::Graphic;
|
||||
pub use img_ids::{BlankGraphic, GraphicCreator, ImageGraphic, VoxelGraphic};
|
||||
pub(self) use util::{linear_to_srgb, srgb_to_linear};
|
||||
pub use widgets::toggle_button::ToggleButton;
|
||||
|
||||
@ -14,16 +19,19 @@ use crate::{
|
||||
window::Window,
|
||||
Error,
|
||||
};
|
||||
use common::assets;
|
||||
use conrod_core::{
|
||||
event::Input,
|
||||
graph::Graph,
|
||||
image::{Id as ImgId, Map},
|
||||
input::{touch::Touch, Button, Motion, Widget},
|
||||
render::Primitive,
|
||||
text::{font::Id as FontId, Font, GlyphCache},
|
||||
text::{self, GlyphCache},
|
||||
widget::{id::Generator, Id as WidgId},
|
||||
Ui as CrUi, UiBuilder, UiCell,
|
||||
};
|
||||
use graphic::{GraphicCache, Id as GraphicId};
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -113,8 +121,8 @@ impl Cache {
|
||||
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) {
|
||||
(&mut self.graphic_cache, &self.graphic_cache_tex)
|
||||
}
|
||||
pub fn new_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||
self.graphic_cache.new_graphic(graphic)
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||
self.graphic_cache.add_graphic(graphic)
|
||||
}
|
||||
pub fn clear_graphic_cache(&mut self, renderer: &mut Renderer, new_size: Vec2<u16>) {
|
||||
self.graphic_cache.clear_cache(new_size);
|
||||
@ -212,6 +220,15 @@ impl Scale {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Font(text::Font);
|
||||
impl assets::Asset for Font {
|
||||
fn load(specifier: &str) -> Result<Self, assets::Error> {
|
||||
Ok(Font(
|
||||
text::Font::from_bytes(assets::load_from_path(specifier)?).unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ui {
|
||||
ui: CrUi,
|
||||
image_map: Map<GraphicId>,
|
||||
@ -228,6 +245,7 @@ impl Ui {
|
||||
pub fn new(window: &mut Window) -> Result<Self, Error> {
|
||||
let scale = Scale::new(window, ScaleMode::Absolute(1.0));
|
||||
let win_dims = scale.scaled_window_size().into_array();
|
||||
|
||||
Ok(Self {
|
||||
ui: UiBuilder::new(win_dims).build(),
|
||||
image_map: Map::new(),
|
||||
@ -246,12 +264,12 @@ impl Ui {
|
||||
self.ui.handle_event(Input::Resize(w, h));
|
||||
}
|
||||
|
||||
pub fn new_graphic(&mut self, graphic: Graphic) -> ImgId {
|
||||
self.image_map.insert(self.cache.new_graphic(graphic))
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> ImgId {
|
||||
self.image_map.insert(self.cache.add_graphic(graphic))
|
||||
}
|
||||
|
||||
pub fn new_font(&mut self, font: Font) -> FontId {
|
||||
self.ui.fonts.insert(font)
|
||||
pub fn new_font(&mut self, mut font: Arc<Font>) -> text::font::Id {
|
||||
self.ui.fonts.insert(font.as_ref().0.clone())
|
||||
}
|
||||
|
||||
pub fn id_generator(&mut self) -> Generator {
|
||||
@ -285,6 +303,10 @@ impl Ui {
|
||||
.is_none()
|
||||
}
|
||||
|
||||
// Get the widget graph
|
||||
pub fn widget_graph(&self) -> &Graph {
|
||||
self.ui.widget_graph()
|
||||
}
|
||||
pub fn handle_event(&mut self, event: Event) {
|
||||
match event.0 {
|
||||
Input::Resize(w, h) => self.window_resized = Some(Vec2::new(w, h)),
|
||||
|
Loading…
Reference in New Issue
Block a user