veloren_voxygen/hud/settings_window/
controls.rs1use super::{RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH};
2
3use crate::{
4 GlobalState,
5 game_input::GameInput,
6 hud::{ERROR_COLOR, TEXT_BIND_CONFLICT_COLOR, TEXT_COLOR, img_ids::Imgs},
7 session::settings_change::{Control as ControlChange, Control::*},
8 ui::fonts::Fonts,
9};
10use conrod_core::{
11 Borderable, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, color,
12 position::Relative,
13 widget::{self, Button, Rectangle, Scrollbar, Text},
14 widget_ids,
15};
16use i18n::Localization;
17use strum::IntoEnumIterator;
18
19widget_ids! {
20 struct Ids {
21 window,
22 window_r,
23 window_scrollbar,
24 reset_controls_button,
25 keybind_helper,
26 controls_alignment_rectangle,
27 controls_texts[],
28 controls_buttons[],
29 }
30}
31
32#[derive(WidgetCommon)]
33pub struct Controls<'a> {
34 global_state: &'a GlobalState,
35 imgs: &'a Imgs,
36 fonts: &'a Fonts,
37 localized_strings: &'a Localization,
38 #[conrod(common_builder)]
39 common: widget::CommonBuilder,
40}
41impl<'a> Controls<'a> {
42 pub fn new(
43 global_state: &'a GlobalState,
44 imgs: &'a Imgs,
45 fonts: &'a Fonts,
46 localized_strings: &'a Localization,
47 ) -> Self {
48 Self {
49 global_state,
50 imgs,
51 fonts,
52 localized_strings,
53 common: widget::CommonBuilder::default(),
54 }
55 }
56}
57
58pub struct State {
59 ids: Ids,
60}
61
62impl Widget for Controls<'_> {
63 type Event = Vec<ControlChange>;
64 type State = State;
65 type Style = ();
66
67 fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
68 State {
69 ids: Ids::new(id_gen),
70 }
71 }
72
73 fn style(&self) -> Self::Style {}
74
75 fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
76 common_base::prof_span!("Controls::update");
77 let widget::UpdateArgs { state, ui, .. } = args;
78
79 let mut events = Vec::new();
80 let key_layout = &self.global_state.window.key_layout;
81
82 Rectangle::fill_with(args.rect.dim(), color::TRANSPARENT)
83 .xy(args.rect.xy())
84 .graphics_for(args.id)
85 .scroll_kids()
86 .scroll_kids_vertically()
87 .set(state.ids.window, ui);
88 Rectangle::fill_with([args.rect.w() / 2.0, args.rect.h()], color::TRANSPARENT)
89 .top_right()
90 .parent(state.ids.window)
91 .set(state.ids.window_r, ui);
92 Scrollbar::y_axis(state.ids.window)
93 .thickness(5.0)
94 .rgba(0.33, 0.33, 0.33, 1.0)
95 .set(state.ids.window_scrollbar, ui);
96
97 let mut previous_element_id = None;
99 let mut keybindings_vec: Vec<GameInput> = GameInput::iter().collect();
100 keybindings_vec.sort();
101
102 let controls = &self.global_state.settings.controls;
103 if keybindings_vec.len() > state.ids.controls_texts.len()
104 || keybindings_vec.len() > state.ids.controls_buttons.len()
105 {
106 state.update(|s| {
107 s.ids
108 .controls_texts
109 .resize(keybindings_vec.len(), &mut ui.widget_id_generator());
110 s.ids
111 .controls_buttons
112 .resize(keybindings_vec.len(), &mut ui.widget_id_generator());
113 });
114 }
115
116 for (game_input, (&text_id, &button_id)) in keybindings_vec.into_iter().zip(
118 state
119 .ids
120 .controls_texts
121 .iter()
122 .zip(state.ids.controls_buttons.iter()),
123 ) {
124 let (key_string, key_color) =
125 if self.global_state.window.remapping_keybindings == Some(game_input) {
126 (
127 self.localized_strings
128 .get_msg("hud-settings-awaitingkey")
129 .into_owned(),
130 TEXT_COLOR,
131 )
132 } else if let Some(key) = controls.get_binding(game_input) {
133 (
134 format!(
135 "{} {}",
136 key.display_string(key_layout),
137 key.try_shortened(key_layout)
138 .map_or("".to_owned(), |short| format!("({})", short))
139 ),
140 if controls.has_conflicting_bindings(key) {
141 TEXT_BIND_CONFLICT_COLOR
142 } else {
143 TEXT_COLOR
144 },
145 )
146 } else {
147 (
148 self.localized_strings
149 .get_msg("hud-settings-unbound")
150 .into_owned(),
151 ERROR_COLOR,
152 )
153 };
154 let loc_key = self
155 .localized_strings
156 .get_msg(game_input.get_localization_key());
157 let text_widget = Text::new(&loc_key)
158 .color(TEXT_COLOR)
159 .font_id(self.fonts.cyri.conrod_id)
160 .font_size(self.fonts.cyri.scale(18));
161 let button_widget = Button::new()
162 .label(&key_string)
163 .label_color(key_color)
164 .label_font_id(self.fonts.cyri.conrod_id)
165 .label_font_size(self.fonts.cyri.scale(15))
166 .w(150.0)
167 .rgba(0.0, 0.0, 0.0, 0.0)
168 .border_rgba(0.0, 0.0, 0.0, 255.0)
169 .label_y(Relative::Scalar(3.0));
170 let text_widget = match previous_element_id {
172 None => text_widget.top_left_with_margins_on(state.ids.window, 10.0, 5.0),
173 Some(prev_id) => text_widget.down_from(prev_id, 10.0),
174 };
175 let text_width = text_widget.get_w(ui).unwrap_or(0.0);
176 text_widget.set(text_id, ui);
177 button_widget
178 .right_from(text_id, 350.0 - text_width)
179 .set(button_id, ui);
180
181 for _ in ui.widget_input(button_id).clicks().left() {
182 events.push(ChangeBinding(game_input));
183 }
184 for _ in ui.widget_input(button_id).clicks().right() {
185 events.push(RemoveBinding(game_input));
186 }
187 previous_element_id = Some(text_id);
189 }
190
191 if let Some(prev_id) = previous_element_id {
193 if Button::image(self.imgs.button)
194 .w_h(RESET_BUTTONS_WIDTH, RESET_BUTTONS_HEIGHT)
195 .hover_image(self.imgs.button_hover)
196 .press_image(self.imgs.button_press)
197 .down_from(prev_id, 20.0)
198 .label(
199 &self
200 .localized_strings
201 .get_msg("hud-settings-reset_keybinds"),
202 )
203 .label_font_size(self.fonts.cyri.scale(14))
204 .label_color(TEXT_COLOR)
205 .label_font_id(self.fonts.cyri.conrod_id)
206 .label_y(Relative::Scalar(2.0))
207 .set(state.ids.reset_controls_button, ui)
208 .was_clicked()
209 {
210 events.push(ResetKeyBindings);
211 }
212 previous_element_id = Some(state.ids.reset_controls_button)
213 }
214
215 let offset = ui
216 .widget_graph()
217 .widget(state.ids.window)
218 .and_then(|widget| {
219 widget
220 .maybe_y_scroll_state
221 .as_ref()
222 .map(|scroll| scroll.offset)
223 })
224 .unwrap_or(0.0);
225
226 let keybind_helper_text = self
227 .localized_strings
228 .get_msg("hud-settings-keybind-helper");
229 let keybind_helper = Text::new(&keybind_helper_text)
230 .color(TEXT_COLOR)
231 .font_id(self.fonts.cyri.conrod_id)
232 .font_size(self.fonts.cyri.scale(18));
233 keybind_helper
234 .top_right_with_margins_on(state.ids.window, offset + 5.0, 10.0)
235 .set(state.ids.keybind_helper, ui);
236
237 if let Some(prev_id) = previous_element_id {
239 Rectangle::fill_with([1.0, 1.0], color::TRANSPARENT)
240 .down_from(prev_id, 10.0)
241 .set(state.ids.controls_alignment_rectangle, ui);
242 }
243
244 events
245 }
246}