mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
add language selection menu to main menu screen
This commit is contained in:
parent
c247263258
commit
5af6757de0
@ -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!
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user