Merge branch 'hqurve/settings-ui' into 'master'

Cleanup of settings window: split each tab into its own file

See merge request veloren/veloren!2108
This commit is contained in:
Monty Marz 2021-04-12 04:11:06 +00:00
commit f7c8389f77
8 changed files with 3550 additions and 3076 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,204 @@
use super::{Event, RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH};
use crate::{
hud::{img_ids::Imgs, ERROR_COLOR, TEXT_BIND_CONFLICT_COLOR, TEXT_COLOR},
i18n::Localization,
ui::fonts::Fonts,
window::GameInput,
GlobalState,
};
use conrod_core::{
color,
position::Relative,
widget::{self, Button, Rectangle, Scrollbar, Text},
widget_ids, Borderable, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
};
widget_ids! {
struct Ids {
window,
window_r,
window_scrollbar,
reset_controls_button,
controls_alignment_rectangle,
controls_texts[],
controls_buttons[],
}
}
#[derive(WidgetCommon)]
pub struct Controls<'a> {
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Controls<'a> {
pub fn new(
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
) -> Self {
Self {
global_state,
imgs,
fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
}
pub struct State {
ids: Ids,
}
impl<'a> Widget for Controls<'a> {
type Event = Vec<Event>;
type State = State;
type Style = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
}
}
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
fn style(&self) -> Self::Style { () }
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let mut events = Vec::new();
Rectangle::fill_with(args.rect.dim(), color::TRANSPARENT)
.xy(args.rect.xy())
.graphics_for(args.id)
.scroll_kids()
.scroll_kids_vertically()
.set(state.ids.window, ui);
Rectangle::fill_with([args.rect.w() / 2.0, args.rect.h()], color::TRANSPARENT)
.top_right()
.parent(state.ids.window)
.set(state.ids.window_r, ui);
Scrollbar::y_axis(state.ids.window)
.thickness(5.0)
.rgba(0.33, 0.33, 0.33, 1.0)
.set(state.ids.window_scrollbar, ui);
// Used for sequential placement in a flow-down pattern
let mut previous_element_id = None;
let mut keybindings_vec: Vec<GameInput> = GameInput::iterator().collect();
keybindings_vec.sort();
let controls = &self.global_state.settings.controls;
if keybindings_vec.len() > state.ids.controls_texts.len()
|| keybindings_vec.len() > state.ids.controls_buttons.len()
{
state.update(|s| {
s.ids
.controls_texts
.resize(keybindings_vec.len(), &mut ui.widget_id_generator());
s.ids
.controls_buttons
.resize(keybindings_vec.len(), &mut ui.widget_id_generator());
});
}
// Loop all existing keybindings and the ids for text and button widgets
for (game_input, (&text_id, &button_id)) in keybindings_vec.into_iter().zip(
state
.ids
.controls_texts
.iter()
.zip(state.ids.controls_buttons.iter()),
) {
let (key_string, key_color) =
if self.global_state.window.remapping_keybindings == Some(game_input) {
(
String::from(self.localized_strings.get("hud.settings.awaitingkey")),
TEXT_COLOR,
)
} else if let Some(key) = controls.get_binding(game_input) {
(
key.to_string(),
if controls.has_conflicting_bindings(key) {
TEXT_BIND_CONFLICT_COLOR
} else {
TEXT_COLOR
},
)
} else {
(
String::from(self.localized_strings.get("hud.settings.unbound")),
ERROR_COLOR,
)
};
let loc_key = self
.localized_strings
.get(game_input.get_localization_key());
let text_widget = Text::new(loc_key)
.color(TEXT_COLOR)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(18));
let button_widget = Button::new()
.label(&key_string)
.label_color(key_color)
.label_font_id(self.fonts.cyri.conrod_id)
.label_font_size(self.fonts.cyri.scale(15))
.w(150.0)
.rgba(0.0, 0.0, 0.0, 0.0)
.border_rgba(0.0, 0.0, 0.0, 255.0)
.label_y(Relative::Scalar(3.0));
// Place top-left if it's the first text, else under the previous one
let text_widget = match previous_element_id {
None => text_widget.top_left_with_margins_on(state.ids.window, 10.0, 5.0),
Some(prev_id) => text_widget.down_from(prev_id, 10.0),
};
let text_width = text_widget.get_w(ui).unwrap_or(0.0);
text_widget.set(text_id, ui);
if button_widget
.right_from(text_id, 350.0 - text_width)
.set(button_id, ui)
.was_clicked()
{
events.push(Event::ChangeBinding(game_input));
}
// Set the previous id to the current one for the next cycle
previous_element_id = Some(text_id);
}
// Reset the KeyBindings settings to the default settings
if let Some(prev_id) = previous_element_id {
if Button::image(self.imgs.button)
.w_h(RESET_BUTTONS_WIDTH, RESET_BUTTONS_HEIGHT)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.down_from(prev_id, 20.0)
.label(&self.localized_strings.get("hud.settings.reset_keybinds"))
.label_font_size(self.fonts.cyri.scale(14))
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.label_y(Relative::Scalar(2.0))
.set(state.ids.reset_controls_button, ui)
.was_clicked()
{
events.push(Event::ResetKeyBindings);
}
previous_element_id = Some(state.ids.reset_controls_button)
}
// Add an empty text widget to simulate some bottom margin, because conrod sucks
if let Some(prev_id) = previous_element_id {
Rectangle::fill_with([1.0, 1.0], color::TRANSPARENT)
.down_from(prev_id, 10.0)
.set(state.ids.controls_alignment_rectangle, ui);
}
events
}
}

View File

@ -0,0 +1,517 @@
use super::{Event, RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH};
use crate::{
hud::{img_ids::Imgs, PressBehavior, MENU_BG, TEXT_COLOR},
i18n::Localization,
ui::{fonts::Fonts, ImageSlider, ToggleButton},
GlobalState,
};
use conrod_core::{
color,
position::Relative,
widget::{self, Button, DropDownList, Rectangle, Scrollbar, Text},
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
};
widget_ids! {
struct Ids {
window,
window_r,
window_scrollbar,
reset_gameplay_button,
mouse_pan_slider,
mouse_pan_label,
mouse_pan_value,
mouse_zoom_slider,
mouse_zoom_label,
mouse_zoom_value,
mouse_zoom_invert_button,
mouse_zoom_invert_label,
camera_clamp_slider,
camera_clamp_label,
camera_clamp_value,
mouse_y_invert_button,
mouse_y_invert_label,
controller_y_invert_button,
controller_y_invert_label,
smooth_pan_toggle_button,
smooth_pan_toggle_label,
//
free_look_behavior_text,
free_look_behavior_list,
auto_walk_behavior_text,
auto_walk_behavior_list,
camera_clamp_behavior_text,
camera_clamp_behavior_list,
stop_auto_walk_on_input_button,
stop_auto_walk_on_input_label,
auto_camera_button,
auto_camera_label,
}
}
#[derive(WidgetCommon)]
pub struct Gameplay<'a> {
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Gameplay<'a> {
pub fn new(
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
) -> Self {
Self {
global_state,
imgs,
fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
}
pub struct State {
ids: Ids,
}
impl<'a> Widget for Gameplay<'a> {
type Event = Vec<Event>;
type State = State;
type Style = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
}
}
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
fn style(&self) -> Self::Style { () }
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let mut events = Vec::new();
Rectangle::fill_with(args.rect.dim(), color::TRANSPARENT)
.xy(args.rect.xy())
.graphics_for(args.id)
.scroll_kids()
.scroll_kids_vertically()
.set(state.ids.window, ui);
Rectangle::fill_with([args.rect.w() / 2.0, args.rect.h()], color::TRANSPARENT)
.top_right()
.parent(state.ids.window)
.set(state.ids.window_r, ui);
Scrollbar::y_axis(state.ids.window)
.thickness(5.0)
.rgba(0.33, 0.33, 0.33, 1.0)
.set(state.ids.window_scrollbar, ui);
let display_pan = self.global_state.settings.gameplay.pan_sensitivity;
let display_zoom = self.global_state.settings.gameplay.zoom_sensitivity;
let display_clamp = self.global_state.settings.gameplay.camera_clamp_angle;
// Mouse Pan Sensitivity
Text::new(&self.localized_strings.get("hud.settings.pan_sensitivity"))
.top_left_with_margins_on(state.ids.window, 10.0, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.mouse_pan_label, ui);
if let Some(new_val) = ImageSlider::discrete(
display_pan,
1,
200,
self.imgs.slider_indicator,
self.imgs.slider,
)
.w_h(550.0, 22.0)
.down_from(state.ids.mouse_pan_label, 10.0)
.track_breadth(30.0)
.slider_length(10.0)
.pad_track((5.0, 5.0))
.set(state.ids.mouse_pan_slider, ui)
{
events.push(Event::AdjustMousePan(new_val));
}
Text::new(&format!("{}", display_pan))
.right_from(state.ids.mouse_pan_slider, 8.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.mouse_pan_value, ui);
// Mouse Zoom Sensitivity
Text::new(&self.localized_strings.get("hud.settings.zoom_sensitivity"))
.down_from(state.ids.mouse_pan_slider, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.mouse_zoom_label, ui);
if let Some(new_val) = ImageSlider::discrete(
display_zoom,
1,
300,
self.imgs.slider_indicator,
self.imgs.slider,
)
.w_h(550.0, 22.0)
.down_from(state.ids.mouse_zoom_label, 10.0)
.track_breadth(30.0)
.slider_length(10.0)
.pad_track((5.0, 5.0))
.set(state.ids.mouse_zoom_slider, ui)
{
events.push(Event::AdjustMouseZoom(new_val));
}
Text::new(&format!("{}", display_zoom))
.right_from(state.ids.mouse_zoom_slider, 8.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.mouse_zoom_value, ui);
// Camera clamp angle
Text::new(
&self
.localized_strings
.get("hud.settings.camera_clamp_angle"),
)
.down_from(state.ids.mouse_zoom_slider, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.camera_clamp_label, ui);
if let Some(new_val) = ImageSlider::discrete(
display_clamp,
1,
90,
self.imgs.slider_indicator,
self.imgs.slider,
)
.w_h(550.0, 22.0)
.down_from(state.ids.camera_clamp_label, 10.0)
.track_breadth(30.0)
.slider_length(10.0)
.pad_track((5.0, 5.0))
.set(state.ids.camera_clamp_slider, ui)
{
events.push(Event::AdjustCameraClamp(new_val));
}
Text::new(&format!("{}", display_clamp))
.right_from(state.ids.camera_clamp_slider, 8.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.camera_clamp_value, ui);
// Zoom Inversion
let zoom_inverted = ToggleButton::new(
self.global_state.settings.gameplay.zoom_inversion,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.down_from(state.ids.camera_clamp_slider, 20.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.mouse_zoom_invert_button, ui);
if self.global_state.settings.gameplay.zoom_inversion != zoom_inverted {
events.push(Event::ToggleZoomInvert(
!self.global_state.settings.gameplay.zoom_inversion,
));
}
Text::new(
&self
.localized_strings
.get("hud.settings.invert_scroll_zoom"),
)
.right_from(state.ids.mouse_zoom_invert_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.mouse_zoom_invert_button)
.color(TEXT_COLOR)
.set(state.ids.mouse_zoom_invert_label, ui);
// Mouse Y Inversion
let mouse_y_inverted = ToggleButton::new(
self.global_state.settings.gameplay.mouse_y_inversion,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.right_from(state.ids.mouse_zoom_invert_label, 10.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.mouse_y_invert_button, ui);
if self.global_state.settings.gameplay.mouse_y_inversion != mouse_y_inverted {
events.push(Event::ToggleMouseYInvert(
!self.global_state.settings.gameplay.mouse_y_inversion,
));
}
Text::new(
&self
.localized_strings
.get("hud.settings.invert_mouse_y_axis"),
)
.right_from(state.ids.mouse_y_invert_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.mouse_y_invert_button)
.color(TEXT_COLOR)
.set(state.ids.mouse_y_invert_label, ui);
// Controller Y Pan Inversion
let controller_y_inverted = ToggleButton::new(
self.global_state.settings.controller.pan_invert_y,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.right_from(state.ids.mouse_y_invert_label, 10.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.controller_y_invert_button, ui);
if self.global_state.settings.controller.pan_invert_y != controller_y_inverted {
events.push(Event::ToggleControllerYInvert(
!self.global_state.settings.controller.pan_invert_y,
));
}
Text::new(
&self
.localized_strings
.get("hud.settings.invert_controller_y_axis"),
)
.right_from(state.ids.controller_y_invert_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.controller_y_invert_button)
.color(TEXT_COLOR)
.set(state.ids.controller_y_invert_label, ui);
// Mouse Smoothing Toggle
let smooth_pan_enabled = ToggleButton::new(
self.global_state.settings.gameplay.smooth_pan_enable,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.right_from(state.ids.controller_y_invert_label, 10.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.smooth_pan_toggle_button, ui);
if self.global_state.settings.gameplay.smooth_pan_enable != smooth_pan_enabled {
events.push(Event::ToggleSmoothPan(
!self.global_state.settings.gameplay.smooth_pan_enable,
));
}
Text::new(
&self
.localized_strings
.get("hud.settings.enable_mouse_smoothing"),
)
.right_from(state.ids.smooth_pan_toggle_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.smooth_pan_toggle_button)
.color(TEXT_COLOR)
.set(state.ids.smooth_pan_toggle_label, ui);
// Free look behaviour
Text::new(
&self
.localized_strings
.get("hud.settings.free_look_behavior"),
)
.down_from(state.ids.mouse_zoom_invert_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.free_look_behavior_text, ui);
let mode_label_list = [
&self
.localized_strings
.get("hud.settings.press_behavior.toggle"),
&self
.localized_strings
.get("hud.settings.press_behavior.hold"),
];
// Get which free look behavior is currently active
let selected = self.global_state.settings.gameplay.free_look_behavior as usize;
if let Some(clicked) = DropDownList::new(&mode_label_list, Some(selected))
.w_h(200.0, 30.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.free_look_behavior_text, 8.0)
.set(state.ids.free_look_behavior_list, ui)
{
match clicked {
0 => events.push(Event::ChangeFreeLookBehavior(PressBehavior::Toggle)),
1 => events.push(Event::ChangeFreeLookBehavior(PressBehavior::Hold)),
_ => unreachable!(),
}
}
// Auto walk behavior
Text::new(
&self
.localized_strings
.get("hud.settings.auto_walk_behavior"),
)
.down_from(state.ids.mouse_zoom_invert_button, 10.0)
.right_from(state.ids.free_look_behavior_text, 150.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.auto_walk_behavior_text, ui);
let auto_walk_selected = self.global_state.settings.gameplay.auto_walk_behavior as usize;
if let Some(clicked) = DropDownList::new(&mode_label_list, Some(auto_walk_selected))
.w_h(200.0, 30.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.auto_walk_behavior_text, 8.0)
.set(state.ids.auto_walk_behavior_list, ui)
{
match clicked {
0 => events.push(Event::ChangeAutoWalkBehavior(PressBehavior::Toggle)),
1 => events.push(Event::ChangeAutoWalkBehavior(PressBehavior::Hold)),
_ => unreachable!(),
}
}
// Camera clamp behavior
Text::new(
&self
.localized_strings
.get("hud.settings.camera_clamp_behavior"),
)
.down_from(state.ids.free_look_behavior_list, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.camera_clamp_behavior_text, ui);
let camera_clamp_selected =
self.global_state.settings.gameplay.camera_clamp_behavior as usize;
if let Some(clicked) = DropDownList::new(&mode_label_list, Some(camera_clamp_selected))
.w_h(200.0, 30.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.camera_clamp_behavior_text, 8.0)
.set(state.ids.camera_clamp_behavior_list, ui)
{
match clicked {
0 => events.push(Event::ChangeCameraClampBehavior(PressBehavior::Toggle)),
1 => events.push(Event::ChangeCameraClampBehavior(PressBehavior::Hold)),
_ => unreachable!(),
}
}
// Stop autowalk on input toggle
let stop_auto_walk_on_input_toggle = ToggleButton::new(
self.global_state.settings.gameplay.stop_auto_walk_on_input,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.down_from(state.ids.smooth_pan_toggle_button, 8.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.stop_auto_walk_on_input_button, ui);
if self.global_state.settings.gameplay.stop_auto_walk_on_input
!= stop_auto_walk_on_input_toggle
{
events.push(Event::ChangeStopAutoWalkOnInput(
!self.global_state.settings.gameplay.stop_auto_walk_on_input,
));
}
Text::new(
&self
.localized_strings
.get("hud.settings.stop_auto_walk_on_input"),
)
.right_from(state.ids.stop_auto_walk_on_input_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.stop_auto_walk_on_input_button)
.color(TEXT_COLOR)
.set(state.ids.stop_auto_walk_on_input_label, ui);
// Auto-camera toggle
let auto_camera_toggle = ToggleButton::new(
self.global_state.settings.gameplay.auto_camera,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.down_from(state.ids.stop_auto_walk_on_input_button, 8.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.auto_camera_button, ui);
if self.global_state.settings.gameplay.auto_camera != auto_camera_toggle {
events.push(Event::ChangeAutoCamera(
!self.global_state.settings.gameplay.auto_camera,
));
}
Text::new(&self.localized_strings.get("hud.settings.auto_camera"))
.right_from(state.ids.auto_camera_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.auto_camera_button)
.color(TEXT_COLOR)
.set(state.ids.auto_camera_label, ui);
// Reset the gameplay settings to the default settings
if Button::image(self.imgs.button)
.w_h(RESET_BUTTONS_WIDTH, RESET_BUTTONS_HEIGHT)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.down_from(state.ids.camera_clamp_behavior_list, 12.0)
.label(&self.localized_strings.get("hud.settings.reset_gameplay"))
.label_font_size(self.fonts.cyri.scale(14))
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.label_y(Relative::Scalar(2.0))
.set(state.ids.reset_gameplay_button, ui)
.was_clicked()
{
events.push(Event::ResetGameplaySettings);
}
events
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
use super::Event;
use crate::{
hud::{img_ids::Imgs, TEXT_COLOR},
i18n::list_localizations,
ui::fonts::Fonts,
GlobalState,
};
use conrod_core::{
color,
widget::{self, Button, Rectangle, Scrollbar},
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
};
widget_ids! {
struct Ids {
window,
window_r,
window_scrollbar,
language_list[],
}
}
#[derive(WidgetCommon)]
pub struct Language<'a> {
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a Fonts,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Language<'a> {
pub fn new(global_state: &'a GlobalState, imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
Self {
global_state,
imgs,
fonts,
common: widget::CommonBuilder::default(),
}
}
}
pub struct State {
ids: Ids,
}
impl<'a> Widget for Language<'a> {
type Event = Vec<Event>;
type State = State;
type Style = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
}
}
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
fn style(&self) -> Self::Style { () }
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let mut events = Vec::new();
Rectangle::fill_with(args.rect.dim(), color::TRANSPARENT)
.xy(args.rect.xy())
.graphics_for(args.id)
.scroll_kids()
.scroll_kids_vertically()
.set(state.ids.window, ui);
Rectangle::fill_with([args.rect.w() / 2.0, args.rect.h()], color::TRANSPARENT)
.top_right()
.parent(state.ids.window)
.set(state.ids.window_r, ui);
Scrollbar::y_axis(state.ids.window)
.thickness(5.0)
.rgba(0.33, 0.33, 0.33, 1.0)
.set(state.ids.window_scrollbar, ui);
// List available languages
let selected_language = &self.global_state.settings.language.selected_language;
let language_list = list_localizations();
if state.ids.language_list.len() < language_list.len() {
state.update(|state| {
state
.ids
.language_list
.resize(language_list.len(), &mut ui.widget_id_generator())
});
};
for (i, language) in language_list.iter().enumerate() {
let button_w = 400.0;
let button_h = 50.0;
let button = Button::image(if selected_language == &language.language_identifier {
self.imgs.selection
} else {
self.imgs.nothing
});
let button = if i == 0 {
button.mid_top_with_margin_on(state.ids.window, 20.0)
} else {
button.mid_bottom_with_margin_on(state.ids.language_list[i - 1], -button_h)
};
if button
.label(&language.language_name)
.w_h(button_w, button_h)
.hover_image(self.imgs.selection_hover)
.press_image(self.imgs.selection_press)
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(22))
.label_font_id(self.fonts.cyri.conrod_id)
.label_y(conrod_core::position::Relative::Scalar(2.0))
.set(state.ids.language_list[i], ui)
.was_clicked()
{
events.push(Event::ChangeLanguage(Box::new(language.to_owned())));
}
}
events
}
}

View File

@ -0,0 +1,336 @@
mod controls;
mod gameplay;
mod interface;
mod language;
mod sound;
mod video;
use crate::{
hud::{
img_ids::Imgs, BarNumbers, BuffPosition, CrosshairType, PressBehavior, ShortcutNumbers,
Show, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
},
i18n::{LanguageMetadata, Localization},
render::RenderMode,
settings::Fps,
ui::fonts::Fonts,
window::{FullScreenSettings, GameInput},
GlobalState,
};
use conrod_core::{
color,
widget::{self, Button, Image, Rectangle, Text},
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
};
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
widget_ids! {
struct Ids {
frame,
settings_bg,
tabs_align,
icon,
settings_close,
settings_title,
settings_content_align,
tabs[],
interface,
gameplay,
controls,
video,
sound,
language,
}
}
const RESET_BUTTONS_HEIGHT: f64 = 34.0;
const RESET_BUTTONS_WIDTH: f64 = 155.0;
#[derive(Debug, EnumIter, PartialEq)]
pub enum SettingsTab {
Interface,
Video,
Sound,
Gameplay,
Controls,
Lang,
}
impl SettingsTab {
fn name_key(&self) -> &str {
match self {
SettingsTab::Interface => "common.interface",
SettingsTab::Gameplay => "common.gameplay",
SettingsTab::Controls => "common.controls",
SettingsTab::Video => "common.video",
SettingsTab::Sound => "common.sound",
SettingsTab::Lang => "common.languages",
}
}
fn title_key(&self) -> &str {
match self {
SettingsTab::Interface => "common.interface_settings",
SettingsTab::Gameplay => "common.gameplay_settings",
SettingsTab::Controls => "common.controls_settings",
SettingsTab::Video => "common.video_settings",
SettingsTab::Sound => "common.sound_settings",
SettingsTab::Lang => "common.language_settings",
}
}
}
#[derive(WidgetCommon)]
pub struct SettingsWindow<'a> {
global_state: &'a GlobalState,
show: &'a Show,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
fps: f32,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> SettingsWindow<'a> {
pub fn new(
global_state: &'a GlobalState,
show: &'a Show,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
fps: f32,
) -> Self {
Self {
global_state,
show,
imgs,
fonts,
localized_strings,
fps,
common: widget::CommonBuilder::default(),
}
}
}
pub struct State {
ids: Ids,
}
pub enum Event {
ToggleHelp,
ToggleDebug,
ToggleTips(bool),
ToggleBarNumbers(BarNumbers),
ToggleShortcutNumbers(ShortcutNumbers),
BuffPosition(BuffPosition),
ChangeTab(SettingsTab),
Close,
AdjustMousePan(u32),
AdjustMouseZoom(u32),
AdjustCameraClamp(u32),
ToggleZoomInvert(bool),
ToggleMouseYInvert(bool),
ToggleControllerYInvert(bool),
ToggleSmoothPan(bool),
AdjustViewDistance(u32),
AdjustSpriteRenderDistance(u32),
AdjustFigureLoDRenderDistance(u32),
AdjustFOV(u16),
AdjustLodDetail(u32),
AdjustGamma(f32),
AdjustExposure(f32),
AdjustAmbiance(f32),
AdjustWindowSize([u16; 2]),
ChangeFullscreenMode(FullScreenSettings),
ToggleParticlesEnabled(bool),
ChangeRenderMode(Box<RenderMode>),
AdjustMusicVolume(f32),
AdjustSfxVolume(f32),
//ChangeAudioDevice(String),
MaximumFPS(Fps),
CrosshairTransp(f32),
CrosshairType(CrosshairType),
UiScale(ScaleChange),
ChatTransp(f32),
ChatCharName(bool),
Sct(bool),
SctPlayerBatch(bool),
SctDamageBatch(bool),
SpeechBubbleDarkMode(bool),
SpeechBubbleIcon(bool),
ChangeLanguage(Box<LanguageMetadata>),
ChangeBinding(GameInput),
ResetInterfaceSettings,
ResetGameplaySettings,
ResetKeyBindings,
ResetGraphicsSettings,
ResetAudioSettings,
ChangeFreeLookBehavior(PressBehavior),
ChangeAutoWalkBehavior(PressBehavior),
ChangeCameraClampBehavior(PressBehavior),
ChangeStopAutoWalkOnInput(bool),
ChangeAutoCamera(bool),
}
#[derive(Clone)]
pub enum ScaleChange {
ToAbsolute,
ToRelative,
Adjust(f64),
}
impl<'a> Widget for SettingsWindow<'a> {
type Event = Vec<Event>;
type State = State;
type Style = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
}
}
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
fn style(&self) -> Self::Style { () }
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let mut events = Vec::new();
let tab_font_scale = 18;
// Frame
Image::new(self.imgs.settings_bg)
.w_h(1052.0, 886.0)
.mid_top_with_margin_on(ui.window, 5.0)
.color(Some(UI_MAIN))
.set(state.ids.settings_bg, ui);
Image::new(self.imgs.settings_frame)
.w_h(1052.0, 886.0)
.middle_of(state.ids.settings_bg)
.color(Some(UI_HIGHLIGHT_0))
.set(state.ids.frame, ui);
// Content Alignment
Rectangle::fill_with([814.0, 834.0], color::TRANSPARENT)
.top_right_with_margins_on(state.ids.frame, 46.0, 2.0)
.set(state.ids.settings_content_align, ui);
// Tabs Content Alignment
Rectangle::fill_with([232.0, 814.0], color::TRANSPARENT)
.top_left_with_margins_on(state.ids.frame, 44.0, 2.0)
.scroll_kids()
.scroll_kids_vertically()
.set(state.ids.tabs_align, ui);
// Icon
Image::new(self.imgs.settings)
.w_h(29.0 * 1.5, 25.0 * 1.5)
.top_left_with_margins_on(state.ids.frame, 2.0, 1.0)
.set(state.ids.icon, ui);
// Title
Text::new(
self.localized_strings
.get(self.show.settings_tab.title_key()),
)
.mid_top_with_margin_on(state.ids.frame, 3.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(29))
.color(TEXT_COLOR)
.set(state.ids.settings_title, ui);
// X-Button
if Button::image(self.imgs.close_button)
.w_h(24.0, 25.0)
.hover_image(self.imgs.close_btn_hover)
.press_image(self.imgs.close_btn_press)
.top_right_with_margins_on(state.ids.frame, 0.0, 0.0)
.set(state.ids.settings_close, ui)
.was_clicked()
{
events.push(Event::Close);
}
// Tabs
if state.ids.tabs.len() < SettingsTab::iter().len() {
state.update(|s| {
s.ids
.tabs
.resize(SettingsTab::iter().len(), &mut ui.widget_id_generator())
});
}
for (i, settings_tab) in SettingsTab::iter().enumerate() {
let mut button = Button::image(if self.show.settings_tab == settings_tab {
self.imgs.selection
} else {
self.imgs.nothing
})
.w_h(230.0, 48.0)
.hover_image(self.imgs.selection_hover)
.press_image(self.imgs.selection_press)
.label(self.localized_strings.get(settings_tab.name_key()))
.label_font_size(self.fonts.cyri.scale(tab_font_scale))
.label_font_id(self.fonts.cyri.conrod_id)
.label_color(TEXT_COLOR);
button = if i == 0 {
button.mid_top_with_margin_on(state.ids.tabs_align, 28.0)
} else {
button.down_from(state.ids.tabs[i - 1], 0.0)
};
if button.set(state.ids.tabs[i], ui).was_clicked() {
events.push(Event::ChangeTab(settings_tab));
}
}
// Content Area
let global_state = self.global_state;
let show = self.show;
let imgs = self.imgs;
let fonts = self.fonts;
let localized_strings = self.localized_strings;
for event in match self.show.settings_tab {
SettingsTab::Interface => {
interface::Interface::new(global_state, show, imgs, fonts, localized_strings)
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
.wh_of(state.ids.settings_content_align)
.set(state.ids.interface, ui)
},
SettingsTab::Gameplay => {
gameplay::Gameplay::new(global_state, imgs, fonts, localized_strings)
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
.wh_of(state.ids.settings_content_align)
.set(state.ids.gameplay, ui)
},
SettingsTab::Controls => {
controls::Controls::new(global_state, imgs, fonts, localized_strings)
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
.wh_of(state.ids.settings_content_align)
.set(state.ids.controls, ui)
},
SettingsTab::Video => {
video::Video::new(global_state, imgs, fonts, localized_strings, self.fps)
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
.wh_of(state.ids.settings_content_align)
.set(state.ids.video, ui)
},
SettingsTab::Sound => sound::Sound::new(global_state, imgs, fonts, localized_strings)
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
.wh_of(state.ids.settings_content_align)
.set(state.ids.sound, ui),
SettingsTab::Lang => language::Language::new(global_state, imgs, fonts)
.top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
.wh_of(state.ids.settings_content_align)
.set(state.ids.language, ui),
} {
events.push(event);
}
events
}
}

View File

@ -0,0 +1,195 @@
use super::{Event, RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH};
use crate::{
hud::{img_ids::Imgs, TEXT_COLOR},
i18n::Localization,
ui::{fonts::Fonts, ImageSlider},
GlobalState,
};
use conrod_core::{
color,
position::Relative,
widget::{self, Button, Rectangle, Scrollbar, Text},
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
};
widget_ids! {
struct Ids {
window,
window_r,
window_scrollbar,
reset_sound_button,
audio_volume_slider,
audio_volume_text,
sfx_volume_slider,
sfx_volume_text,
audio_device_list,
audio_device_text,
}
}
#[derive(WidgetCommon)]
pub struct Sound<'a> {
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Sound<'a> {
pub fn new(
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a Fonts,
localized_strings: &'a Localization,
) -> Self {
Self {
global_state,
imgs,
fonts,
localized_strings,
common: widget::CommonBuilder::default(),
}
}
}
pub struct State {
ids: Ids,
}
impl<'a> Widget for Sound<'a> {
type Event = Vec<Event>;
type State = State;
type Style = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
}
}
#[allow(clippy::unused_unit)] // TODO: Pending review in #587
fn style(&self) -> Self::Style { () }
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args;
let mut events = Vec::new();
Rectangle::fill_with(args.rect.dim(), color::TRANSPARENT)
.xy(args.rect.xy())
.graphics_for(args.id)
.scroll_kids()
.scroll_kids_vertically()
.set(state.ids.window, ui);
Rectangle::fill_with([args.rect.w() / 2.0, args.rect.h()], color::TRANSPARENT)
.top_right()
.parent(state.ids.window)
.set(state.ids.window_r, ui);
Scrollbar::y_axis(state.ids.window)
.thickness(5.0)
.rgba(0.33, 0.33, 0.33, 1.0)
.set(state.ids.window_scrollbar, ui);
// Music Volume -----------------------------------------------------
Text::new(&self.localized_strings.get("hud.settings.music_volume"))
.top_left_with_margins_on(state.ids.window, 10.0, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.audio_volume_text, ui);
if let Some(new_val) = ImageSlider::continuous(
self.global_state.settings.audio.music_volume,
0.0,
1.0,
self.imgs.slider_indicator,
self.imgs.slider,
)
.w_h(104.0, 22.0)
.down_from(state.ids.audio_volume_text, 10.0)
.track_breadth(12.0)
.slider_length(10.0)
.pad_track((5.0, 5.0))
.set(state.ids.audio_volume_slider, ui)
{
events.push(Event::AdjustMusicVolume(new_val));
}
// SFX Volume -------------------------------------------------------
Text::new(
&self
.localized_strings
.get("hud.settings.sound_effect_volume"),
)
.down_from(state.ids.audio_volume_slider, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.sfx_volume_text, ui);
if let Some(new_val) = ImageSlider::continuous(
self.global_state.settings.audio.sfx_volume,
0.0,
1.0,
self.imgs.slider_indicator,
self.imgs.slider,
)
.w_h(104.0, 22.0)
.down_from(state.ids.sfx_volume_text, 10.0)
.track_breadth(12.0)
.slider_length(10.0)
.pad_track((5.0, 5.0))
.set(state.ids.sfx_volume_slider, ui)
{
events.push(Event::AdjustSfxVolume(new_val));
}
// Audio Device Selector
// --------------------------------------------
// let device = &self.global_state.audio.device;
//let device_list = &self.global_state.audio.device_list;
//Text::new(&self.localized_strings.get("hud.settings.audio_device"
// )) .down_from(state.ids.sfx_volume_slider, 10.0)
// .font_size(self.fonts.cyri.scale(14))
// .font_id(self.fonts.cyri.conrod_id)
// .color(TEXT_COLOR)
// .set(state.ids.audio_device_text, ui);
//// Get which device is currently selected
//let selected = device_list.iter().position(|x|
// x.contains(device));
//if let Some(clicked) = DropDownList::new(&device_list, selected)
// .w_h(400.0, 22.0)
// .color(MENU_BG)
// .label_color(TEXT_COLOR)
// .label_font_id(self.fonts.opensans.conrod_id)
// .down_from(state.ids.audio_device_text, 10.0)
// .set(state.ids.audio_device_list, ui)
//{
// let new_val = device_list[clicked].clone();
// events.push(Event::ChangeAudioDevice(new_val));
//}
// Reset the sound settings to the default settings
if Button::image(self.imgs.button)
.w_h(RESET_BUTTONS_WIDTH, RESET_BUTTONS_HEIGHT)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.down_from(state.ids.sfx_volume_slider, 12.0)
.label(&self.localized_strings.get("hud.settings.reset_sound"))
.label_font_size(self.fonts.cyri.scale(14))
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.label_y(Relative::Scalar(2.0))
.set(state.ids.reset_sound_button, ui)
.was_clicked()
{
events.push(Event::ResetAudioSettings);
}
events
}
}

File diff suppressed because it is too large Load Diff