Add parts of main menu such as version text and info text banner, make connection screen

This commit is contained in:
Imbris 2020-06-01 04:05:10 -04:00
parent d6b38c8cd2
commit 5c13983631
5 changed files with 204 additions and 70 deletions

View File

@ -4,16 +4,20 @@ use crate::{
ui::ice::{
component::neat_button,
widget::{image, BackgroundContainer, Image},
Element,
ButtonStyle, Element,
},
};
use iced::{button, Length, Space};
use iced::{button, Color, Column, Container, HorizontalAlignment, Length, Row, Space, Text};
/// Connecting screen for the main menu
pub struct Screen {
cancel_button: button::State,
}
// TODO: move to super and unify with identical login consts
const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
impl Screen {
pub fn new() -> Self {
Self {
@ -26,9 +30,53 @@ impl Screen {
imgs: &Imgs,
bg_img: image::Handle,
start: &std::time::Instant,
status_text: &str,
version: &str,
time: f32,
i18n: &Localization,
) -> Element<Message> {
let content = Space::new(Length::Fill, Length::Fill);
let fade_msg = (time * 2.0).sin() * 0.5 + 0.51;
let button_style = ButtonStyle::new(imgs.button)
.hover_image(imgs.button_hover)
.press_image(imgs.button_press)
.text_color(TEXT_COLOR)
.disabled_text_color(DISABLED_TEXT_COLOR);
let version = Text::new(version)
.size(15) // move version text size to const
.width(Length::Fill)
.height(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Right);
let status = Text::new(status_text)
.size(80)
.color(Color::from_rgba(1.0, 1.0, 1.0, fade_msg))
.width(Length::Fill);
let status = Row::with_children(vec![
Space::new(Length::Units(80), Length::Shrink).into(),
status.into(),
]);
let cancel = neat_button(
&mut self.cancel_button,
i18n.get("common.cancel"),
0.7,
button_style,
Some(Message::CancelConnect),
);
let cancel = Container::new(cancel)
.width(Length::Fill)
.height(Length::Units(50))
.center_x()
.padding(3);
let content = Column::with_children(vec![version.into(), status.into(), cancel.into()])
.width(Length::Fill)
.height(Length::Fill)
.padding(3);
// Note: could replace this with styling on iced's container since we aren't
// using fixed aspect ratio
BackgroundContainer::new(Image::new(bg_img), content).into()

View File

@ -1,4 +1,4 @@
use super::{IcedImgs as Imgs, LoginInfo, Message};
use super::{IcedImgs as Imgs, Info, LoginInfo, Message};
use crate::{
i18n::Localization,
ui::ice::{
@ -10,7 +10,10 @@ use crate::{
ButtonStyle, Element,
},
};
use iced::{button, text_input, Align, Column, Container, Length, Row, Space, TextInput};
use iced::{
button, text_input, Align, Column, Container, HorizontalAlignment, Length, Row, Space, Text,
TextInput,
};
use vek::*;
const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
@ -44,6 +47,9 @@ impl Screen {
&mut self,
imgs: &Imgs,
login_info: &LoginInfo,
info: &Info,
version: &str,
show_servers: bool,
i18n: &Localization,
) -> Element<Message> {
let button_style = ButtonStyle::new(imgs.button)
@ -77,14 +83,39 @@ impl Screen {
])
.width(Length::Fill)
.max_width(200)
.spacing(5)
.padding(10);
.spacing(5);
let buttons = Container::new(buttons)
.width(Length::Fill)
.height(Length::Fill)
.align_y(Align::End)
.padding(20);
.align_y(Align::End);
let left_column = if matches!(info, Info::Intro) {
let intro_text = i18n.get("main.login_process");
let info_window = BackgroundContainer::new(
CompoundGraphic::from_graphics(vec![
Graphic::rect(Rgba::new(0, 0, 0, 240), [500, 300], [0, 0]),
// Note: a way to tell it to keep the height of this one piece constant and
// unstreched would be nice, I suppose we could just break this out into a
// column and use Length::Units
Graphic::image(imgs.banner_bottom, [500, 30], [0, 300])
.color(Rgba::new(255, 255, 255, 240)),
])
.height(Length::Shrink),
Text::new(intro_text).size(21),
)
.max_width(450)
.padding(Padding::new().horizontal(20).top(10).bottom(60));
Column::with_children(vec![info_window.into(), buttons.into()])
.width(Length::Fill)
.height(Length::Fill)
.padding(27)
.into()
} else {
buttons.into()
};
let banner = self.banner.view(imgs, login_info, i18n, button_style);
@ -94,13 +125,20 @@ impl Screen {
.align_x(Align::Center)
.align_y(Align::Center);
let image3 = Image::new(imgs.banner_bottom).fix_aspect_ratio();
let right_column = Text::new(version)
.size(15)
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Right);
let content =
Row::with_children(vec![buttons.into(), central_column.into(), image3.into()])
.width(Length::Fill)
.height(Length::Fill)
.spacing(10);
let content = Row::with_children(vec![
left_column,
central_column.into(),
right_column.into(),
])
.width(Length::Fill)
.height(Length::Fill)
.spacing(10)
.padding(3);
BackgroundContainer::new(Image::new(imgs.bg), content).into()
}

View File

@ -14,6 +14,7 @@ use crate::{
GlobalState,
};
//ImageFrame, Tooltip,
use crate::settings::Settings;
use common::assets::Asset;
use conrod_core::{
color,
@ -113,6 +114,11 @@ pub struct LoginInfo {
pub server: String,
}
enum Info {
Disclaimer,
Intro,
}
enum Screen {
Login {
screen: login::Screen,
@ -121,6 +127,7 @@ enum Screen {
screen: connecting::Screen,
// TODO: why instant?
start: std::time::Instant,
status_text: String,
},
}
@ -129,12 +136,16 @@ struct IcedState {
imgs: IcedImgs,
bg_img: widget::image::Handle,
i18n: std::sync::Arc<Localization>,
// Voxygen version
version: String,
login_info: LoginInfo,
// TODO: not sure if this should be used for connecting
popup: Option<PopupData>,
show_servers: bool,
show_disclaimer: bool,
login_info: LoginInfo,
info: Info,
time: f32,
screen: Screen,
}
@ -150,6 +161,7 @@ enum Message {
Password(String),
Server(String),
FocusPassword,
CancelConnect,
}
impl IcedState {
@ -157,32 +169,68 @@ impl IcedState {
imgs: IcedImgs,
bg_img: widget::image::Handle,
i18n: std::sync::Arc<Localization>,
settings: &Settings,
) -> Self {
let version = format!(
"{}-{}",
env!("CARGO_PKG_VERSION"),
common::util::GIT_VERSION.to_string()
);
let info = if settings.show_disclaimer {
Info::Disclaimer
} else {
Info::Intro
};
Self {
imgs,
bg_img,
i18n,
popup: None,
show_servers: false,
show_disclaimer: false,
version,
login_info: LoginInfo {
username: String::new(),
password: String::new(),
server: String::new(),
},
popup: None,
show_servers: false,
info,
time: 0.0,
screen: Screen::Login {
screen: login::Screen::new(),
},
}
}
fn view(&mut self) -> Element<Message> {
fn view(&mut self, dt: f32) -> Element<Message> {
self.time = self.time + dt;
match &mut self.screen {
Screen::Login { screen } => screen.view(&self.imgs, &self.login_info, &self.i18n),
Screen::Connecting { screen, start } => {
screen.view(&self.imgs, self.bg_img, &start, &self.i18n)
},
Screen::Login { screen } => screen.view(
&self.imgs,
&self.login_info,
&self.info,
&self.version,
self.show_servers,
&self.i18n,
),
Screen::Connecting {
screen,
start,
status_text,
} => screen.view(
&self.imgs,
self.bg_img,
&start,
&status_text,
&self.version,
self.time,
&self.i18n,
),
}
}
@ -191,16 +239,21 @@ impl IcedState {
Message::Quit => events.push(Event::Quit),
Message::ShowServers => self.show_servers = true,
#[cfg(feature = "singleplayer")]
Message::Singleplayer => events.push(Event::StartSingleplayer),
Message::Singleplayer => {
self.screen = Screen::Connecting {
screen: connecting::Screen::new(),
start: std::time::Instant::now(),
status_text: [self.i18n.get("main.creating_world"), "..."].concat(),
};
events.push(Event::StartSingleplayer);
},
Message::Multiplayer => {
self.screen = Screen::Connecting {
screen: connecting::Screen::new(),
start: std::time::Instant::now(),
status_text: [self.i18n.get("main.connecting"), "..."].concat(),
};
self.popup = Some(PopupData {
msg: [self.i18n.get("main.connecting"), "..."].concat(),
popup_type: PopupType::ConnectionInfo,
});
events.push(Event::LoginAttempt {
username: self.login_info.username.clone(),
@ -212,11 +265,23 @@ impl IcedState {
Message::Password(new_value) => self.login_info.password = new_value,
Message::Server(new_value) => self.login_info.server = new_value,
Message::FocusPassword => {
if let Screen::Login { screen } = &mut self.screen {
if let Screen::Login { screen, .. } = &mut self.screen {
screen.banner.password = text_input::State::focused();
screen.banner.username = text_input::State::new();
}
},
Message::CancelConnect => {
self.cancel_connection();
events.push(Event::CancelLoginAttempt);
},
}
}
fn cancel_connection(&mut self) {
if matches!(&self.screen, Screen::Connecting {..}) {
self.screen = Screen::Login {
screen: login::Screen::new(),
}
}
}
}
@ -300,23 +365,12 @@ image_ids! {
}
}
rotation_image_ids! {
pub struct ImgsRot {
<VoxelGraphic>
// Tooltip Test
tt_side: "voxygen/element/frames/tt_test_edge",
tt_corner: "voxygen/element/frames/tt_test_corner_tr",
}
}
pub struct MainMenuUi {
ui: Ui,
ice_ui: IcedUi,
ice_state: IcedState,
ids: Ids,
imgs: Imgs,
rot_imgs: ImgsRot,
username: String,
password: String,
server_address: String,
@ -346,7 +400,6 @@ impl<'a> MainMenuUi {
let ids = Ids::new(ui.id_generator());
// Load images
let imgs = Imgs::load(&mut ui).expect("Failed to load images");
let rot_imgs = ImgsRot::load(&mut ui).expect("Failed to load images!");
let bg_img_spec = BG_IMGS.choose(&mut thread_rng()).unwrap();
let bg_img_id = ui.add_graphic(Graphic::Image(DynamicImage::load_expect(bg_img_spec)));
// Load language
@ -372,8 +425,9 @@ impl<'a> MainMenuUi {
let mut ice_ui = IcedUi::new(window, ice_font).unwrap();
let ice_state = IcedState::new(
IcedImgs::load(&mut ice_ui).expect("Failed to load images"),
ice_ui.add_graphic(Graphic::Image(load_expect(bg_img_spec))),
ice_ui.add_graphic(Graphic::Image(DynamicImage::load_expect(bg_img_spec))),
i18n.clone(),
&global_state.settings,
);
Self {
@ -382,7 +436,6 @@ impl<'a> MainMenuUi {
ice_state,
ids,
imgs,
rot_imgs,
username: networking.username.clone(),
password: "".to_owned(),
server_address: networking
@ -429,24 +482,6 @@ impl<'a> MainMenuUi {
let intro_text = &self.i18n.get("main.login_process");
// Tooltip
/*let _tooltip = Tooltip::new({
// Edge images [t, b, r, l]
// Corner images [tr, tl, br, bl]
let edge = &self.rot_imgs.tt_side;
let corner = &self.rot_imgs.tt_corner;
ImageFrame::new(
[edge.cw180, edge.none, edge.cw270, edge.cw90],
[corner.none, corner.cw270, corner.cw90, corner.cw180],
Color::Rgba(0.08, 0.07, 0.04, 1.0),
5.0,
)
})
.title_font_size(self.fonts.cyri.scale(15))
.desc_font_size(self.fonts.cyri.scale(10))
.font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR_2);*/
// Background image, Veloren logo, Alpha-Version Label
Image::new(if self.connect {
self.bg_img_id
@ -1047,6 +1082,7 @@ impl<'a> MainMenuUi {
self.popup = None;
self.connecting = None;
self.connect = false;
self.ice_state.cancel_connection();
}
pub fn handle_event(&mut self, event: ui::Event) {
@ -1055,17 +1091,24 @@ impl<'a> MainMenuUi {
}
}
pub fn handle_iced_event(&mut self, event: ui::ice::Event) { self.ice_ui.handle_event(event); }
pub fn handle_iced_event(&mut self, event: ui::ice::Event) {
if self.show_iced {
self.ice_ui.handle_event(event);
}
}
pub fn maintain(&mut self, global_state: &mut GlobalState, dt: Duration) -> Vec<Event> {
let mut events = self.update_layout(global_state, dt);
self.ui.maintain(global_state.window.renderer_mut(), None);
let (messages, _) = self
.ice_ui
.maintain(self.ice_state.view(), global_state.window.renderer_mut());
messages
.into_iter()
.for_each(|message| self.ice_state.update(message, &mut events));
if self.show_iced {
let (messages, _) = self.ice_ui.maintain(
self.ice_state.view(dt.as_secs_f32()),
global_state.window.renderer_mut(),
);
messages
.into_iter()
.for_each(|message| self.ice_state.update(message, &mut events));
}
events
}

View File

@ -541,6 +541,7 @@ impl IcedRenderer {
// TODO: support nested clips?
// TODO: if last command is a clip changing back to the default replace it with
// this
// TODO: cull primitives outside the current scissor
// Renderer child
self.draw_primitive(*content, renderer);

View File

@ -1,6 +1,10 @@
use iced::{layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, Size, Widget};
use std::{hash::Hash, u32};
// TODO: decouple from image/compound graphic widgets (they could still use
// common types, but the info we want here for the background is a subset of
// what a fullblown widget might need)
// Note: it might be more efficient to make this generic over the content type
// Note: maybe we could just use the container styling for this (not really with