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