veloren_voxygen/hud/
popup.rs1use super::Show;
2use crate::ui::fonts::Fonts;
3use client::{self, Client};
4use common_net::msg::Notification;
5use conrod_core::{
6 Color, Colorable, Positionable, Widget, WidgetCommon,
7 widget::{self, Text},
8 widget_ids,
9};
10use i18n::Localization;
11use std::{collections::VecDeque, time::Instant};
12
13widget_ids! {
14 struct Ids {
15 error_bg,
16 error_text,
17 info_bg,
18 info_text,
19 message_bg,
20 message_text,
21 }
22}
23
24#[derive(WidgetCommon)]
25pub struct Popup<'a> {
26 i18n: &'a Localization,
27 client: &'a Client,
28 new_notifications: &'a VecDeque<Notification>,
29 fonts: &'a Fonts,
30 #[conrod(common_builder)]
31 common: widget::CommonBuilder,
32 show: &'a Show,
33}
34
35impl<'a> Popup<'a> {
38 pub fn new(
39 i18n: &'a Localization,
40 client: &'a Client,
41 new_notifications: &'a VecDeque<Notification>,
42 fonts: &'a Fonts,
43 show: &'a Show,
44 ) -> Self {
45 Self {
46 i18n,
47 client,
48 new_notifications,
49 fonts,
50 common: widget::CommonBuilder::default(),
51 show,
52 }
53 }
54}
55
56pub struct State {
57 ids: Ids,
58 errors: VecDeque<String>,
59 infos: VecDeque<String>,
60 messages: VecDeque<String>,
61 last_error_update: Instant,
62 last_info_update: Instant,
63 last_message_update: Instant,
64 last_region_name: Option<String>,
65}
66
67impl Widget for Popup<'_> {
68 type Event = ();
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 errors: VecDeque::new(),
76 infos: VecDeque::new(),
77 messages: VecDeque::new(),
78 last_error_update: Instant::now(),
79 last_info_update: Instant::now(),
80 last_message_update: Instant::now(),
81 last_region_name: None,
82 }
83 }
84
85 fn style(&self) -> Self::Style {}
86
87 fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
88 common_base::prof_span!("Popup::update");
89 let widget::UpdateArgs { state, ui, .. } = args;
90
91 const FADE_IN: f32 = 0.5;
92 const FADE_HOLD: f32 = 1.0;
93 const FADE_OUT: f32 = 3.0;
94
95 let bg_color = |fade| Color::Rgba(0.0, 0.0, 0.0, fade);
96 let error_color = |fade| Color::Rgba(1.0, 0.0, 0.0, fade);
97 let info_color = |fade| Color::Rgba(1.0, 1.0, 0.0, fade);
98 let message_color = |fade| Color::Rgba(1.0, 1.0, 1.0, fade);
99
100 if let Some(chunk) = self.client.current_chunk() {
102 if let Some(current) = chunk.meta().name() {
103 if state.messages.is_empty()
105 && state
106 .last_region_name
107 .as_ref()
108 .map(|l| l != current)
109 .unwrap_or(true)
110 {
111 state.update(|s| {
113 if s.messages.is_empty() {
114 s.last_message_update = Instant::now();
115 }
116 s.last_region_name = Some(current.to_owned());
117 s.messages.push_back(current.to_owned());
118 });
119 }
120 }
121 }
122
123 for notification in self.new_notifications {
125 match notification {
126 Notification::WaypointSaved => {
127 state.update(|s| {
128 if s.infos.is_empty() {
129 s.last_info_update = Instant::now();
130 }
131 let text = self.i18n.get_msg("hud-waypoint_saved");
132 s.infos.push_back(text.to_string());
133 });
134 },
135 }
136 }
137
138 if !state.errors.is_empty()
140 && state.last_error_update.elapsed().as_secs_f32() > FADE_IN + FADE_HOLD + FADE_OUT
141 {
142 state.update(|s| {
143 s.errors.pop_front();
144 s.last_error_update = Instant::now();
145 });
146 }
147
148 if let Some(error) = state.errors.front() {
150 let seconds = state.last_error_update.elapsed().as_secs_f32();
151 let fade = if seconds < FADE_IN {
152 seconds / FADE_IN
153 } else if seconds < FADE_IN + FADE_HOLD {
154 1.0
155 } else {
156 (1.0 - (seconds - FADE_IN - FADE_HOLD) / FADE_OUT).max(0.0)
157 };
158 Text::new(error)
159 .mid_top_with_margin_on(ui.window, 50.0)
160 .font_size(self.fonts.cyri.scale(20))
161 .font_id(self.fonts.cyri.conrod_id)
162 .color(bg_color(fade))
163 .set(state.ids.error_bg, ui);
164 Text::new(error)
165 .top_left_with_margins_on(state.ids.error_bg, -1.0, -1.0)
166 .font_size(self.fonts.cyri.scale(20))
167 .font_id(self.fonts.cyri.conrod_id)
168 .color(error_color(fade))
169 .set(state.ids.error_text, ui);
170 }
171
172 if !state.infos.is_empty()
174 && state.last_info_update.elapsed().as_secs_f32() > FADE_IN + FADE_HOLD + FADE_OUT
175 {
176 state.update(|s| {
177 s.infos.pop_front();
178 s.last_info_update = Instant::now();
179 });
180 }
181
182 if !self.show.intro {
184 if let Some(info) = state.infos.front() {
185 let seconds = state.last_info_update.elapsed().as_secs_f32();
186 let fade = if seconds < FADE_IN {
187 seconds / FADE_IN
188 } else if seconds < FADE_IN + FADE_HOLD {
189 1.0
190 } else {
191 (1.0 - (seconds - FADE_IN - FADE_HOLD) / FADE_OUT).max(0.0)
192 };
193
194 Text::new(info)
195 .mid_top_with_margin_on(ui.window, 100.0)
196 .font_size(self.fonts.cyri.scale(20))
197 .font_id(self.fonts.cyri.conrod_id)
198 .color(bg_color(fade))
199 .set(state.ids.info_bg, ui);
200 Text::new(info)
201 .top_left_with_margins_on(state.ids.info_bg, -1.0, -1.0)
202 .font_size(self.fonts.cyri.scale(20))
203 .font_id(self.fonts.cyri.conrod_id)
204 .color(info_color(fade))
205 .set(state.ids.info_text, ui);
206 }
207 }
208
209 if !state.messages.is_empty()
211 && state.last_message_update.elapsed().as_secs_f32() > FADE_IN + FADE_HOLD + FADE_OUT
212 {
213 state.update(|s| {
214 s.messages.pop_front();
215 s.last_message_update = Instant::now();
216 });
217 }
218
219 if !self.show.intro {
221 if let Some(message) = state.messages.front() {
222 let seconds = state.last_message_update.elapsed().as_secs_f32();
223 let fade = if seconds < FADE_IN {
224 seconds / FADE_IN
225 } else if seconds < FADE_IN + FADE_HOLD {
226 1.0
227 } else {
228 (1.0 - (seconds - FADE_IN - FADE_HOLD) / FADE_OUT).max(0.0)
229 };
230 Text::new(message)
231 .mid_top_with_margin_on(ui.window, 200.0)
232 .font_size(self.fonts.alkhemi.scale(70))
233 .font_id(self.fonts.alkhemi.conrod_id)
234 .color(bg_color(fade))
235 .set(state.ids.message_bg, ui);
236 Text::new(message)
237 .top_left_with_margins_on(state.ids.message_bg, -2.5, -2.5)
238 .font_size(self.fonts.alkhemi.scale(70))
239 .font_id(self.fonts.alkhemi.conrod_id)
240 .color(message_color(fade))
241 .set(state.ids.message_text, ui);
242 }
243 }
244 }
245}