add language selection menu to main menu screen

This commit is contained in:
Yusuf Bera Ertan 2020-07-20 17:07:08 +03:00 committed by Imbris
parent c247263258
commit 5af6757de0
4 changed files with 181 additions and 11 deletions

View File

@ -114,6 +114,7 @@ Is the client up to date?"#,
"main.connecting": "Connecting",
"main.creating_world": "Creating world",
"main.tip": "Tip:",
"main.select_language": "Select a language",
// Welcome notice that appears the first time Veloren is started
"main.notice": r#"Welcome to the alpha version of Veloren!

View File

@ -5,8 +5,11 @@ use super::char_selection::CharSelectionState;
#[cfg(feature = "singleplayer")]
use crate::singleplayer::Singleplayer;
use crate::{
render::Renderer, settings::Settings, window::Event, Direction, GlobalState, PlayState,
PlayStateResult,
i18n::{i18n_asset_key, Localization},
render::Renderer,
settings::Settings,
window::Event,
Direction, GlobalState, PlayState, PlayStateResult,
};
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
use common::{assets::Asset, comp, span};
@ -47,7 +50,7 @@ impl PlayState for MainMenuState {
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
span!(_guard, "tick", "<MainMenuState as PlayState>::tick");
let localized_strings = crate::i18n::Localization::load_expect(
let mut localized_strings = crate::i18n::Localization::load_expect(
&crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language),
);
@ -248,6 +251,15 @@ impl PlayState for MainMenuState {
self.client_init = None;
self.main_menu_ui.cancel_connection();
},
MainMenuEvent::ChangeLanguage(new_language) => {
global_state.settings.language.selected_language =
new_language.language_identifier;
localized_strings = Localization::load_expect(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
localized_strings.log_missing_entries();
self.main_menu_ui.update_language(localized_strings.clone());
},
#[cfg(feature = "singleplayer")]
MainMenuEvent::StartSingleplayer => {
let singleplayer = Singleplayer::new(None); // TODO: Make client and server use the same thread pool

View File

@ -14,7 +14,10 @@ use crate::{
},
},
};
use iced::{button, text_input, Align, Column, Container, Length, Row, Space, Text, TextInput};
use iced::{
button, scrollable, text_input, Align, Button, Column, Container, Length, Row, Scrollable,
Space, Text, TextInput,
};
use vek::*;
const FILL_FRAC_ONE: f32 = 0.77;
@ -27,10 +30,12 @@ pub struct Screen {
quit_button: button::State,
settings_button: button::State,
servers_button: button::State,
language_select_button: button::State,
error_okay_button: button::State,
pub banner: Banner,
pub banner: LoginBanner,
language_selection: LanguageSelectBanner,
}
impl Screen {
@ -39,10 +44,12 @@ impl Screen {
servers_button: Default::default(),
settings_button: Default::default(),
quit_button: Default::default(),
language_select_button: Default::default(),
error_okay_button: Default::default(),
banner: Banner::new(),
banner: LoginBanner::new(),
language_selection: LanguageSelectBanner::new(),
}
}
@ -53,6 +60,9 @@ impl Screen {
login_info: &LoginInfo,
error: Option<&str>,
i18n: &Localization,
is_selecting_language: bool,
selected_language_index: Option<usize>,
language_metadatas: &[crate::i18n::LanguageMetadata],
button_style: style::button::Style,
) -> Element<Message> {
let buttons = Column::with_children(vec![
@ -77,6 +87,13 @@ impl Screen {
button_style,
Some(Message::Quit),
),
neat_button(
&mut self.language_select_button,
i18n.get("common.languages"),
FILL_FRAC_ONE,
button_style,
Some(Message::OpenLanguageMenu),
),
])
.width(Length::Fill)
.max_width(200)
@ -140,8 +157,19 @@ impl Screen {
.padding(20)
.into()
} else {
self.banner
.view(fonts, imgs, login_info, i18n, button_style)
if is_selecting_language {
self.language_selection.view(
fonts,
imgs,
i18n,
language_metadatas,
selected_language_index,
button_style,
)
} else {
self.banner
.view(fonts, imgs, login_info, i18n, button_style)
}
};
let central_column = Container::new(central_content)
@ -164,7 +192,105 @@ impl Screen {
}
}
pub struct Banner {
pub struct LanguageSelectBanner {
okay_button: button::State,
language_buttons: Vec<button::State>,
selection_list: scrollable::State,
}
impl LanguageSelectBanner {
fn new() -> Self {
Self {
okay_button: Default::default(),
language_buttons: Default::default(),
selection_list: Default::default(),
}
}
fn view(
&mut self,
fonts: &Fonts,
imgs: &Imgs,
i18n: &Localization,
language_metadatas: &[crate::i18n::LanguageMetadata],
selected_language_index: Option<usize>,
button_style: style::button::Style,
) -> Element<Message> {
let title =
Container::new(Text::new(i18n.get("main.select_language")).size(fonts.cyri.scale(35)))
.center_x();
let mut list = Scrollable::new(&mut self.selection_list)
.height(Length::Fill)
.width(Length::Fill)
.align_items(Align::Start);
if self.language_buttons.len() != language_metadatas.len() {
self.language_buttons = vec![Default::default(); language_metadatas.len()];
}
for (i, (state, lang)) in self
.language_buttons
.iter_mut()
.zip(language_metadatas)
.enumerate()
{
let text = format!(
"{}{}",
if Some(i) == selected_language_index {
"-> "
} else {
" "
},
lang.language_name,
);
let button = Button::new(
state,
Container::new(Text::new(text).size(fonts.cyri.scale(25)))
.padding(5)
.center_y(),
)
.width(Length::Fill)
.on_press(Message::LangaugeChanged(i));
list = list.push(button);
}
let okay_button = Container::new(neat_button(
&mut self.okay_button,
i18n.get("common.okay"),
FILL_FRAC_TWO,
button_style,
Some(Message::OpenLanguageMenu),
))
.center_x()
.max_width(200);
let content = Column::with_children(vec![title.into(), list.into(), okay_button.into()])
.spacing(8)
.width(Length::Fill)
.height(Length::FillPortion(38))
.align_items(Align::Center);
let selection_menu = BackgroundContainer::new(
CompoundGraphic::from_graphics(vec![
Graphic::image(imgs.banner_top, [138, 17], [0, 0]),
Graphic::rect(Rgba::new(0, 0, 0, 230), [130, 165], [4, 17]),
// TODO: use non image gradient
Graphic::gradient(Rgba::new(0, 0, 0, 230), Rgba::zero(), [130, 50], [4, 182]),
])
.fix_aspect_ratio()
.height(Length::Fill),
content,
)
.padding(Padding::new().horizontal(8).vertical(15).bottom(50))
.max_width(350);
selection_menu.into()
}
}
pub struct LoginBanner {
pub username: text_input::State,
pub password: text_input::State,
pub server: text_input::State,
@ -174,7 +300,7 @@ pub struct Banner {
singleplayer_button: button::State,
}
impl Banner {
impl LoginBanner {
fn new() -> Self {
Self {
username: Default::default(),

View File

@ -4,7 +4,7 @@ mod login;
mod servers;
use crate::{
i18n::{i18n_asset_key, Localization},
i18n::{i18n_asset_key, LanguageMetadata, Localization},
render::Renderer,
ui::{
self,
@ -78,6 +78,7 @@ pub enum Event {
server_address: String,
},
CancelLoginAttempt,
ChangeLanguage(LanguageMetadata),
#[cfg(feature = "singleplayer")]
StartSingleplayer,
Quit,
@ -143,6 +144,9 @@ struct Controls {
selected_server_index: Option<usize>,
login_info: LoginInfo,
is_selecting_language: bool,
selected_language_index: Option<usize>,
time: f32,
screen: Screen,
@ -156,6 +160,8 @@ enum Message {
#[cfg(feature = "singleplayer")]
Singleplayer,
Multiplayer,
LangaugeChanged(usize),
OpenLanguageMenu,
Username(String),
Password(String),
Server(String),
@ -206,6 +212,11 @@ impl Controls {
.iter()
.position(|f| f == &login_info.server);
let language_metadatas = crate::i18n::list_localizations();
let selected_language_index = language_metadatas
.iter()
.position(|f| &f.language_identifier == &settings.language.selected_language);
Self {
fonts,
imgs,
@ -217,6 +228,9 @@ impl Controls {
selected_server_index,
login_info,
is_selecting_language: false,
selected_language_index,
time: 0.0,
screen,
@ -256,6 +270,8 @@ impl Controls {
self.imgs.bg
};
let language_metadatas = crate::i18n::list_localizations();
// TODO: make any large text blocks scrollable so that if the area is to
// small they can still be read
let content = match &mut self.screen {
@ -266,6 +282,9 @@ impl Controls {
&self.login_info,
error.as_deref(),
&self.i18n,
self.is_selecting_language,
self.selected_language_index,
&language_metadatas,
button_style,
),
Screen::Servers { screen } => screen.view(
@ -300,6 +319,7 @@ impl Controls {
fn update(&mut self, message: Message, events: &mut Vec<Event>, settings: &Settings) {
let servers = &settings.networking.servers;
let mut language_metadatas = crate::i18n::list_localizations();
match message {
Message::Quit => events.push(Event::Quit),
@ -344,6 +364,11 @@ impl Controls {
});
},
Message::Username(new_value) => self.login_info.username = new_value,
Message::LangaugeChanged(new_value) => {
self.selected_language_index = Some(new_value);
events.push(Event::ChangeLanguage(language_metadatas.remove(new_value)));
},
Message::OpenLanguageMenu => self.is_selecting_language = !self.is_selecting_language,
Message::Password(new_value) => self.login_info.password = new_value,
Message::Server(new_value) => {
self.login_info.server = new_value;
@ -506,6 +531,12 @@ impl<'a> MainMenuUi {
Self { ui, controls }
}
pub fn update_language(&mut self, i18n: std::sync::Arc<Localization>) {
self.controls.i18n = i18n;
self.controls.fonts = Fonts::load(&self.controls.i18n.fonts, &mut self.ui)
.expect("Impossible to load fonts!");
}
pub fn auth_trust_prompt(&mut self, auth_server: String) {
self.controls.auth_trust_prompt(auth_server);
}