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