veloren_voxygen/ui/ice/
mod.rs

1//    tooltip_manager: TooltipManager,
2mod cache;
3pub mod component;
4mod renderer;
5pub mod widget;
6
7pub use cache::{Font, FontId, RawFont, load_font};
8pub use graphic::{Id, Rotation};
9pub use iced::Event;
10pub use iced_winit::conversion::window_event;
11pub use renderer::{IcedRenderer, style};
12
13use super::{
14    graphic::{self, Graphic},
15    scale::{Scale, ScaleMode},
16};
17use crate::{
18    error::Error,
19    render::{Renderer, UiDrawer},
20    window::Window,
21};
22use common::slowjob::SlowJobPool;
23use common_base::span;
24use iced::{Cache, Size, UserInterface, mouse};
25use iced_winit::Clipboard;
26use vek::*;
27
28pub type Element<'a, M> = iced::Element<'a, M, IcedRenderer>;
29
30pub struct IcedUi {
31    renderer: IcedRenderer,
32    cache: Option<Cache>,
33    events: Vec<Event>,
34    cursor_position: Vec2<f32>,
35    // Scaling of the ui
36    scale: Scale,
37    scale_changed: bool,
38}
39impl IcedUi {
40    pub fn new(
41        window: &mut Window,
42        default_font: Font,
43        scale_mode: ScaleMode,
44    ) -> Result<Self, Error> {
45        let scale_factor = window.scale_factor();
46        let renderer = window.renderer_mut();
47        let physical_resolution = renderer.resolution();
48        let scale = Scale::new(physical_resolution, scale_factor, scale_mode, 1.2);
49
50        let scaled_resolution = scale.scaled_resolution().map(|e| e as f32);
51
52        // TODO: examine how much mem fonts take up and reduce clones if significant
53        Ok(Self {
54            renderer: IcedRenderer::new(
55                renderer,
56                scaled_resolution,
57                physical_resolution,
58                default_font,
59            )?,
60            cache: Some(Cache::new()),
61            events: Vec::new(),
62            // TODO: handle None
63            cursor_position: Vec2::zero(),
64            scale,
65            scale_changed: false,
66        })
67    }
68
69    /// Add a new font that is referncable via the returned Id
70    pub fn add_font(&mut self, font: RawFont) -> FontId { self.renderer.add_font(font) }
71
72    /// Allows clearing out the fonts when switching languages
73    pub fn clear_fonts(&mut self, default_font: Font) { self.renderer.clear_fonts(default_font); }
74
75    /// Add a new graphic that is referencable via the returned Id
76    pub fn add_graphic(&mut self, graphic: Graphic) -> Id { self.renderer.add_graphic(graphic) }
77
78    pub fn replace_graphic(&mut self, id: Id, graphic: Graphic) {
79        self.renderer.replace_graphic(id, graphic);
80    }
81
82    pub fn scale(&self) -> Scale { self.scale }
83
84    pub fn set_scaling_mode(&mut self, mode: ScaleMode) {
85        // Signal that change needs to be handled
86        self.scale_changed |= self.scale.set_scaling_mode(mode);
87    }
88
89    /// Dpi factor changed
90    /// Not to be confused with scaling mode
91    pub fn scale_factor_changed(&mut self, scale_factor: f64) {
92        self.scale_changed |= self.scale.scale_factor_changed(scale_factor);
93    }
94
95    pub fn handle_event(&mut self, event: Event) {
96        use iced::window;
97        match event {
98            // Intercept resizing events
99            // We check if the resolution of the renderer has changed to determine if a resize has
100            // occured
101            Event::Window(window::Event::Resized { .. }) => {},
102            // Scale cursor movement events
103            // Note: in some cases the scaling could be off if a resized event occured in the same
104            // frame, in practice this shouldn't be an issue
105            Event::Mouse(mouse::Event::CursorMoved { position }) => {
106                // TODO: return f32 here
107                let scale = self.scale.scale_factor_logical() as f32;
108                let x = position.x / scale;
109                let y = position.y / scale;
110                // TODO: determine why iced moved cursor position out of the `Cache` and if we
111                // may need to handle this in a different way to address
112                // whatever issue iced was trying to address
113                self.cursor_position = Vec2::new(x, y);
114                self.events.push(Event::Mouse(mouse::Event::CursorMoved {
115                    position: iced::Point::new(x, y),
116                }));
117            },
118            // Scale pixel scrolling events
119            Event::Mouse(mouse::Event::WheelScrolled {
120                delta: mouse::ScrollDelta::Pixels { x, y },
121            }) => {
122                // TODO: return f32 here
123                let scale = self.scale.scale_factor_logical() as f32;
124                self.events.push(Event::Mouse(mouse::Event::WheelScrolled {
125                    delta: mouse::ScrollDelta::Pixels {
126                        x: x / scale,
127                        y: y / scale,
128                    },
129                }));
130            },
131            event => self.events.push(event),
132        }
133    }
134
135    // TODO: produce root internally???
136    // TODO: closure/trait for sending messages back? (take a look at higher level
137    // iced libs)
138    pub fn maintain<'a, M, E: Into<Element<'a, M>>>(
139        &mut self,
140        root: E,
141        renderer: &mut Renderer,
142        pool: Option<&SlowJobPool>,
143        clipboard: &mut Clipboard,
144    ) -> (Vec<M>, mouse::Interaction) {
145        span!(_guard, "maintain", "IcedUi::maintain");
146        // There could have been a series of resizes that put us back at the original
147        // resolution.
148        // Avoid resetting cache if window size didn't actually change.
149        let resolution_changed = self.scale.surface_resized(renderer.resolution());
150
151        // Handle window resizing, dpi factor change, and scale mode changing
152        if self.scale_changed || resolution_changed {
153            self.scale_changed = false;
154
155            let scaled_resolution = self.scale.scaled_resolution().map(|e| e as f32);
156            self.events
157                .push(Event::Window(iced::window::Event::Resized {
158                    width: scaled_resolution.x as u32,
159                    height: scaled_resolution.y as u32,
160                }));
161            // Avoid panic in graphic cache when minimizing.
162            // Somewhat inefficient for elements that won't change size after a window
163            // resize
164            let physical_resolution = renderer.resolution();
165            if physical_resolution.map(|e| e > 0).reduce_and() {
166                self.renderer
167                    .resize(scaled_resolution, physical_resolution, renderer);
168            }
169        }
170
171        let cursor_position = iced::Point {
172            x: self.cursor_position.x,
173            y: self.cursor_position.y,
174        };
175
176        // TODO: convert to f32 at source
177        let window_size = self.scale.scaled_resolution().map(|e| e as f32);
178
179        span!(guard, "build user_interface");
180        let mut user_interface = UserInterface::build(
181            root,
182            Size::new(window_size.x, window_size.y),
183            self.cache.take().unwrap(),
184            &mut self.renderer,
185        );
186        drop(guard);
187
188        let messages = {
189            span!(_guard, "update user_interface");
190            let mut messages = Vec::new();
191            let _event_status_list = user_interface.update(
192                &self.events,
193                cursor_position,
194                &self.renderer,
195                clipboard,
196                &mut messages,
197            );
198            messages
199        };
200        // Clear events
201        self.events.clear();
202
203        span!(guard, "draw user_interface");
204        let (primitive, mouse_interaction) =
205            user_interface.draw(&mut self.renderer, cursor_position);
206        drop(guard);
207
208        self.cache = Some(user_interface.into_cache());
209
210        self.renderer.draw(primitive, renderer, pool);
211
212        (messages, mouse_interaction)
213    }
214
215    pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { self.renderer.render(drawer); }
216}