From 5c13983631b5e5f4844705209b866f174d3fdbf2 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 1 Jun 2020 04:05:10 -0400 Subject: [PATCH] Add parts of main menu such as version text and info text banner, make connection screen --- voxygen/src/menu/main/ui/connecting.rs | 54 ++++++- voxygen/src/menu/main/ui/login.rs | 62 +++++-- voxygen/src/menu/main/ui/mod.rs | 153 +++++++++++------- voxygen/src/ui/ice/renderer/mod.rs | 1 + .../src/ui/ice/widget/background_container.rs | 4 + 5 files changed, 204 insertions(+), 70 deletions(-) diff --git a/voxygen/src/menu/main/ui/connecting.rs b/voxygen/src/menu/main/ui/connecting.rs index 4297ba4035..59cee400e1 100644 --- a/voxygen/src/menu/main/ui/connecting.rs +++ b/voxygen/src/menu/main/ui/connecting.rs @@ -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 { - 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() diff --git a/voxygen/src/menu/main/ui/login.rs b/voxygen/src/menu/main/ui/login.rs index de21d09597..1d46028638 100644 --- a/voxygen/src/menu/main/ui/login.rs +++ b/voxygen/src/menu/main/ui/login.rs @@ -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 { 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() } diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index 43d363c573..e6774244b6 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -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, + // Voxygen version + version: String, + + login_info: LoginInfo, // TODO: not sure if this should be used for connecting popup: Option, 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, + 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 { + fn view(&mut self, dt: f32) -> Element { + 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 { - - - // 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 { 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 } diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index a7fdd24645..02774e09a5 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -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); diff --git a/voxygen/src/ui/ice/widget/background_container.rs b/voxygen/src/ui/ice/widget/background_container.rs index af94fbdedb..aa8a10583d 100644 --- a/voxygen/src/ui/ice/widget/background_container.rs +++ b/voxygen/src/ui/ice/widget/background_container.rs @@ -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