Skip to main content

veloren_voxygen/hud/
prompt_dialog.rs

1use super::{TEXT_COLOR, UI_HIGHLIGHT_0, img_ids::Imgs};
2use crate::{
3    game_input::GameInput,
4    hud::{Event, PromptDialogSettings},
5    settings::Settings,
6    ui::fonts::Fonts,
7};
8use conrod_core::{
9    Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
10    widget::{self, Button, Image, Text},
11    widget_ids,
12};
13use i18n::LocalizationHandle;
14
15widget_ids! {
16    struct Ids {
17        top,
18        mid,
19        bot,
20        accept_txt, // optional timer
21        accept_key, //button with label
22        decline_txt,
23        decline_key,
24        prompt_txt,
25    }
26}
27#[derive(WidgetCommon)]
28pub struct PromptDialog<'a> {
29    imgs: &'a Imgs,
30    fonts: &'a Fonts,
31    #[conrod(common_builder)]
32    common: widget::CommonBuilder,
33    localized_strings: &'a LocalizationHandle,
34    settings: &'a Settings,
35    prompt_dialog_settings: &'a PromptDialogSettings,
36}
37
38impl<'a> PromptDialog<'a> {
39    pub fn new(
40        imgs: &'a Imgs,
41        fonts: &'a Fonts,
42        localized_strings: &'a LocalizationHandle,
43        settings: &'a Settings,
44        prompt_dialog_settings: &'a PromptDialogSettings,
45    ) -> Self {
46        Self {
47            imgs,
48            fonts,
49            localized_strings,
50            common: widget::CommonBuilder::default(),
51            settings,
52            prompt_dialog_settings,
53        }
54    }
55}
56
57pub struct State {
58    ids: Ids,
59}
60
61pub enum DialogOutcomeEvent {
62    Affirmative(Event),
63    Negative(Option<Event>),
64}
65
66impl Widget for PromptDialog<'_> {
67    type Event = Option<DialogOutcomeEvent>;
68    type State = State;
69    type Style = ();
70
71    fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
72        State {
73            ids: Ids::new(id_gen),
74        }
75    }
76
77    fn style(&self) -> Self::Style {}
78
79    fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
80        common_base::prof_span!("PromptDialog::update");
81        let widget::UpdateArgs { state, ui, .. } = args;
82        let _localized_strings = &self.localized_strings;
83        let mut event: Option<DialogOutcomeEvent> = None;
84
85        let accept_key = self
86            .settings
87            .controls
88            .get_binding(GameInput::AcceptGroupInvite)
89            .map_or_else(|| "".into(), |key| key.display_string());
90        let decline_key = self
91            .settings
92            .controls
93            .get_binding(GameInput::DeclineGroupInvite)
94            .map_or_else(|| "".into(), |key| key.display_string());
95
96        // Window
97        Image::new(self.imgs.prompt_top)
98            .w_h(276.0, 24.0)
99            .mid_top_with_margin_on(ui.window, 100.0)
100            .color(Some(UI_HIGHLIGHT_0))
101            .set(state.ids.top, ui);
102        if !self.prompt_dialog_settings.message.is_empty() {
103            Image::new(self.imgs.prompt_mid)
104            .w(276.0)
105            .h_of(state.ids.prompt_txt) // height relative to content, max height 150
106            .down_from(state.ids.top, 0.0)
107            .color(Some(UI_HIGHLIGHT_0))
108            .scroll_kids_vertically()
109            .set(state.ids.mid, ui);
110        }
111        Image::new(self.imgs.prompt_bot)
112            .w_h(276.0, 35.0)
113            .down_from(state.ids.mid, 0.0)
114            .color(Some(UI_HIGHLIGHT_0))
115            .set(state.ids.bot, ui);
116
117        // Accept/Decline Buttons
118        if Button::image(self.imgs.key_button)
119            .w_h(20.0, 20.0)
120            .hover_image(self.imgs.close_btn_hover)
121            .press_image(self.imgs.close_btn_press)
122            .label(&accept_key)
123            .image_color(UI_HIGHLIGHT_0)
124            .label_color(TEXT_COLOR)
125            .label_font_size(self.fonts.cyri.scale(16))
126            .label_font_id(self.fonts.cyri.conrod_id)
127            .label_y(conrod_core::position::Relative::Scalar(2.5))
128            .label_x(conrod_core::position::Relative::Scalar(0.5))
129            .bottom_right_with_margins_on(state.ids.bot, 4.0, 6.0)
130            .set(state.ids.accept_key, ui)
131            .was_clicked()
132            || self
133                .prompt_dialog_settings
134                .outcome_via_keypress
135                .is_some_and(|outcome| outcome)
136        {
137            // Primary use should be through pressing the key instead of clicking this
138            event = Some(DialogOutcomeEvent::Affirmative(
139                self.prompt_dialog_settings.affirmative_event.clone(),
140            ));
141        }
142        let accept_txt = if self.prompt_dialog_settings.negative_option {
143            "Accept"
144        } else {
145            "Ok"
146        };
147        Text::new(accept_txt)
148            .bottom_left_with_margins_on(state.ids.accept_key, 4.0, -65.0)
149            .font_id(self.fonts.cyri.conrod_id)
150            .font_size(self.fonts.cyri.scale(18))
151            .color(TEXT_COLOR)
152            .set(state.ids.accept_txt, ui);
153
154        if self.prompt_dialog_settings.negative_option {
155            if Button::image(self.imgs.key_button)
156                .w_h(20.0, 20.0)
157                .hover_image(self.imgs.close_btn_hover)
158                .press_image(self.imgs.close_btn_press)
159                .label(&decline_key)
160                .image_color(UI_HIGHLIGHT_0)
161                .label_color(TEXT_COLOR)
162                .label_font_size(self.fonts.cyri.scale(16))
163                .label_font_id(self.fonts.cyri.conrod_id)
164                .label_y(conrod_core::position::Relative::Scalar(2.5))
165                .label_x(conrod_core::position::Relative::Scalar(0.5))
166                .bottom_left_with_margins_on(state.ids.bot, 4.0, 6.0)
167                .set(state.ids.decline_key, ui)
168                .was_clicked()
169                || self
170                    .prompt_dialog_settings
171                    .outcome_via_keypress
172                    .is_some_and(|outcome| !outcome)
173            {
174                event = Some(DialogOutcomeEvent::Negative(
175                    self.prompt_dialog_settings.negative_event.as_ref().cloned(),
176                ));
177            }
178            Text::new("Decline")
179                .bottom_left_with_margins_on(state.ids.decline_key, 4.0, 28.0)
180                .font_id(self.fonts.cyri.conrod_id)
181                .font_size(self.fonts.cyri.scale(18))
182                .color(TEXT_COLOR)
183                .set(state.ids.decline_txt, ui);
184        }
185
186        // Prompt Description
187        Text::new(&self.prompt_dialog_settings.message)
188        .mid_top_with_margin_on(state.ids.mid,0.0)
189        .font_id(self.fonts.cyri.conrod_id)
190        .font_size(self.fonts.cyri.scale(18))
191        .color(TEXT_COLOR)
192        .w(260.0) // Text stays within frame
193        .set(state.ids.prompt_txt, ui);
194
195        event
196    }
197}