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