1
use std::collections::HashSet;
2
use std::sync::Arc;
3
use std::time::{Duration, Instant};
4

            
5
use figures::{DisplayScale, Displayable, One, Pixels, Points, Rectlike, SizedRect};
6
use winit::event::VirtualKeyCode;
7
use winit::window::Theme;
8

            
9
use crate::math::{Point, Scale, Scaled, Size, Vector};
10
use crate::shape::Shape;
11
use crate::sprite::RenderedSprite;
12
use crate::text::prepared::PreparedSpan;
13

            
14
/// An individual render instruction.
15
#[derive(Debug)]
16
pub enum Element {
17
    /// A rendered sprite.
18
    Sprite {
19
        /// The sprite being rendered.
20
        sprite: RenderedSprite,
21
        /// The current clipping rect.
22
        clip: Option<SizedRect<u32, Pixels>>,
23
    },
24
    /// A rendered span of text.
25
    Text {
26
        /// The span being rendered.
27
        span: PreparedSpan,
28
        /// The current clipping rect.
29
        clip: Option<SizedRect<u32, Pixels>>,
30
    },
31
    /// A rendered shape.
32
    Shape(Shape<Pixels>),
33
}
34

            
35
/// An event instructing how to render frames.
36
pub enum SceneEvent {
37
    /// Begin a new frame with the given size.
38
    BeginFrame {
39
        /// The frame size to render.
40
        size: Size<u32, Pixels>,
41
    },
42
    /// Render an element.
43
    Render(Element),
44
    /// Finish the current frame.
45
    EndFrame,
46
}
47

            
48
/// The main rendering destination, usually interacted with through [`Target`].
49
#[derive(Debug)]
50
pub struct Scene {
51
    /// The virtual key codes curently depressed.
52
    pub keys_pressed: HashSet<VirtualKeyCode>,
53
    scale_factor: DisplayScale<f32>,
54
    size: Size<u32, Pixels>,
55
    event_sender: flume::Sender<SceneEvent>,
56
    now: Option<Instant>,
57
    elapsed: Option<Duration>,
58
    system_theme: Theme,
59
}
60

            
61
impl From<Arc<Scene>> for Target {
62
1
    fn from(scene: Arc<Scene>) -> Self {
63
1
        Self {
64
1
            scene,
65
1
            clip: None,
66
1
            offset: None,
67
1
        }
68
1
    }
69
}
70

            
71
impl From<Scene> for Target {
72
1
    fn from(scene: Scene) -> Self {
73
1
        Self::from(Arc::new(scene))
74
1
    }
75
}
76

            
77
/// A render target
78
#[derive(Clone, Debug)]
79
pub struct Target {
80
    /// The scene to draw into.
81
    pub scene: Arc<Scene>,
82
    /// The curent clipping rect. All drawing calls will be clipped to this
83
    /// area.
84
    pub clip: Option<SizedRect<u32, Pixels>>,
85
    /// The current offset (translation) of drawing calls.
86
    pub offset: Option<Vector<f32, Pixels>>,
87
}
88

            
89
impl Target {
90
    /// Returns a new [`Target`] with the intersection of `new_clip` an the
91
    /// current `clip`, if any. The scene and offset are cloned.
92
    #[must_use]
93
    pub fn clipped_to(&self, new_clip: SizedRect<u32, Pixels>) -> Self {
94
        Self {
95
            scene: self.scene.clone(),
96
            clip: Some(match &self.clip {
97
                Some(existing_clip) => existing_clip
98
                    .intersection(&new_clip)
99
                    .unwrap_or_default()
100
                    .as_sized(),
101
                None => new_clip
102
                    .intersection(&SizedRect::from(self.size_in_pixels()))
103
                    .unwrap_or_default()
104
                    .as_sized(),
105
            }),
106
            offset: self.offset,
107
        }
108
    }
109

            
110
    /// Returns a new [`Target`] offset by `delta` from the current `offset`, if
111
    /// any. The scene and clipping rect are cloned.
112
    #[must_use]
113
    pub fn offset_by(&self, delta: Vector<f32, Pixels>) -> Self {
114
        Self {
115
            scene: self.scene.clone(),
116
            clip: self.clip,
117
            offset: Some(match self.offset {
118
                Some(offset) => offset + delta,
119
                None => delta,
120
            }),
121
        }
122
    }
123

            
124
    /// Translates `point` by the current `offset`, if any.
125
    #[must_use]
126
    pub fn offset_point(&self, point: Point<f32, Scaled>) -> Point<f32, Scaled> {
127
        match self.offset {
128
            Some(offset) => point + offset.to_scaled(self.scale()),
129
            None => point,
130
        }
131
    }
132

            
133
    /// Translates `point` by the current `offset`, if any.
134
    #[must_use]
135
    pub fn offset_point_raw(&self, point: Point<f32, Pixels>) -> Point<f32, Pixels> {
136
        match self.offset {
137
            Some(offset) => point + offset,
138
            None => point,
139
        }
140
    }
141

            
142
    /// Returns the scene as a mutable reference. Will only succeed if no other
143
    /// references exist. Not intended for use inside of `kludgine-app`.
144
    #[must_use]
145
3
    pub fn scene_mut(&mut self) -> Option<&mut Scene> {
146
3
        Arc::get_mut(&mut self.scene)
147
3
    }
148
}
149

            
150
impl std::ops::Deref for Target {
151
    type Target = Scene;
152

            
153
12
    fn deref(&self) -> &Self::Target {
154
12
        self.scene.as_ref()
155
12
    }
156
}
157

            
158
/// The state of keyboard modifier keys.
159
#[allow(clippy::struct_excessive_bools)]
160
pub struct Modifiers {
161
    /// If true, a control key is currently depressed.
162
    pub control: bool,
163
    /// If true, an alt key is currently depressed.
164
    pub alt: bool,
165
    /// If true, an "Operating System key" is currently depressed. For most
166
    /// keyboards, this is the Windows key or the Command/Apple key.
167
    pub operating_system: bool,
168
    /// If true, a shift key is currently depressed.
169
    pub shift: bool,
170
}
171

            
172
impl Modifiers {
173
    /// Returns true if the primary modifier of the current OS is depressed. For
174
    /// Mac and iOS, this returns `operating_system`. For all other OSes, this
175
    /// returns `control`.
176
    #[must_use]
177
    pub const fn primary_modifier(&self) -> bool {
178
        #[cfg(any(target_os = "macos", target_os = "ios"))]
179
        {
180
            self.operating_system
181
        }
182

            
183
        #[cfg(not(any(target_os = "macos", target_os = "ios")))]
184
        {
185
            self.control
186
        }
187
    }
188

            
189
    /// Returns true if the command key/Apple key is pressed. This only returns
190
    /// true if `operating_system` key is true and the current operating system
191
    /// is Mac or iOS.
192
    #[must_use]
193
    #[allow(clippy::unused_self)]
194
    pub const fn command_key(&self) -> bool {
195
        #[cfg(any(target_os = "macos", target_os = "ios"))]
196
        {
197
            self.operating_system
198
        }
199

            
200
        #[cfg(not(any(target_os = "macos", target_os = "ios")))]
201
        {
202
            false
203
        }
204
    }
205
}
206

            
207
impl Scene {
208
    /// Returns a new Scene that emits [`SceneEvent`]s to `event_sender`.
209
    #[must_use]
210
1
    pub fn new(event_sender: flume::Sender<SceneEvent>, default_system_theme: Theme) -> Self {
211
1
        Self {
212
1
            event_sender,
213
1
            scale_factor: DisplayScale::one(),
214
1
            size: Size::default(),
215
1
            keys_pressed: HashSet::new(),
216
1
            now: None,
217
1
            elapsed: None,
218
1
            system_theme: default_system_theme,
219
1
        }
220
1
    }
221

            
222
    /// Returns the currently set [`Theme`].
223
    #[must_use]
224
    pub const fn system_theme(&self) -> Theme {
225
        self.system_theme
226
    }
227

            
228
    /// Sets the [`Theme`].
229
    pub fn set_system_theme(&mut self, system_theme: Theme) {
230
        self.system_theme = system_theme;
231
    }
232

            
233
3
    pub(crate) fn push_element(&self, element: Element) {
234
3
        drop(self.event_sender.send(SceneEvent::Render(element)));
235
3
    }
236

            
237
    /// Sets the size of the scene.
238
1
    pub fn set_size(&mut self, size: Size<u32, Pixels>) {
239
1
        self.size = size;
240
1
    }
241

            
242
    /// Sets the DPI scale.
243
    pub fn set_dpi_scale(&mut self, scale_factor: Scale<f32, Points, Pixels>) {
244
        self.scale_factor = DisplayScale::new(scale_factor, Scale::one());
245
    }
246

            
247
    /// Sets the additional scaling factor.
248
    pub fn set_additional_scale(&mut self, scale: Scale<f32, Scaled, Points>) {
249
        self.scale_factor.set_additional_scale(scale);
250
    }
251

            
252
    /// Returns the current [`DisplayScale`].
253
    #[must_use]
254
9
    pub const fn scale(&self) -> &DisplayScale<f32> {
255
9
        &self.scale_factor
256
9
    }
257

            
258
    /// Returns true if any of `keys` are currently pressed.
259
    #[must_use]
260
    pub fn any_key_pressed(&self, keys: &[VirtualKeyCode]) -> bool {
261
        for key in keys {
262
            if self.keys_pressed.contains(key) {
263
                return true;
264
            }
265
        }
266
        false
267
    }
268

            
269
    /// Returns the currently depressed modifier keys.
270
    #[must_use]
271
    pub fn modifiers_pressed(&self) -> Modifiers {
272
        Modifiers {
273
            control: self.any_key_pressed(&[VirtualKeyCode::RControl, VirtualKeyCode::LControl]),
274
            alt: self.any_key_pressed(&[VirtualKeyCode::RAlt, VirtualKeyCode::LAlt]),
275
            shift: self.any_key_pressed(&[VirtualKeyCode::LShift, VirtualKeyCode::RShift]),
276
            operating_system: self.any_key_pressed(&[VirtualKeyCode::RWin, VirtualKeyCode::LWin]),
277
        }
278
    }
279

            
280
    /// Begins a new frame with the current size.
281
1
    pub fn start_frame(&mut self) {
282
1
        let last_start = self.now;
283
1
        self.now = Some(Instant::now());
284
1
        self.elapsed = match last_start {
285
            Some(last_start) => self.now.unwrap().checked_duration_since(last_start),
286
1
            None => None,
287
        };
288
1
        drop(
289
1
            self.event_sender
290
1
                .send(SceneEvent::BeginFrame { size: self.size }),
291
1
        );
292
1
    }
293

            
294
    /// Ends the current frame, allowing it to be rendered.
295
1
    pub fn end_frame(&self) {
296
1
        drop(self.event_sender.send(SceneEvent::EndFrame));
297
1
    }
298

            
299
    /// Returns the current size of the scene in [`Scaled`] units.
300
    #[must_use]
301
    pub fn size(&self) -> Size<f32, Scaled> {
302
        self.size.cast::<f32>().to_scaled(self.scale())
303
    }
304

            
305
    /// Returns the current size of the scene in [`Pixels`] units.
306
    #[must_use]
307
    pub const fn size_in_pixels(&self) -> Size<u32, Pixels> {
308
        self.size
309
    }
310

            
311
    /// Returns the [`Instant`] when the frame began.
312
    #[must_use]
313
    pub fn now(&self) -> Instant {
314
        self.now.expect("now() called without starting a frame")
315
    }
316

            
317
    /// Returns the elapsed [`Duration`] since the scene was created.
318
    #[must_use]
319
    pub const fn elapsed(&self) -> Option<Duration> {
320
        self.elapsed
321
    }
322

            
323
    /// Returns true if this is the first frame being rendered.
324
    #[must_use]
325
    pub const fn is_initial_frame(&self) -> bool {
326
        self.elapsed.is_none()
327
    }
328

            
329
    // pub fn register_font(&mut self, font: &Font) {
330
    //     let family = font.family().expect("Unable to register VecFonts");
331
    //     self.fonts
332
    //         .entry(family)
333
    //         .and_modify(|fonts| fonts.push(font.clone()))
334
    //         .or_insert_with(|| vec![font.clone()]);
335
    // }
336

            
337
    // pub fn lookup_font(
338
    //     &self,
339
    //     family: &str,
340
    //     weight: Weight,
341
    //     style: FontStyle,
342
    // ) -> kludgine::Result<Font> {
343
    //     let fonts = self.fonts.get(family);
344

            
345
    //     match fonts {
346
    //         Some(fonts) => {
347
    //             let mut closest_font = None;
348
    //             let mut closest_weight = None;
349

            
350
    //             for font in fonts.iter() {
351
    //                 let font_weight = font.weight();
352
    //                 let font_style = font.style();
353

            
354
    //                 if font_weight == weight && font_style == style {
355
    //                     return Ok(font.clone());
356
    //                 } else {
357
    //                     // If it's not the right style, we want to heavily
358
    // penalize the score                     // But if no font matches the
359
    // style, we should pick the weight that matches                     // best
360
    // in another style.                     let style_multiplier = if
361
    // font_style == style { 1 } else { 10 };                     let delta =
362
    // (font.weight().to_number() as i32 - weight.to_number() as i32)
363
    //                         .abs()
364
    //                         * style_multiplier;
365

            
366
    //                     if closest_weight.is_none() || closest_weight.unwrap() >
367
    // delta {                         closest_weight = Some(delta);
368
    //                         closest_font = Some(font.clone());
369
    //                     }
370
    //                 }
371
    //             }
372

            
373
    //             closest_font.ok_or_else(||
374
    // KludgineError::FontFamilyNotFound(family.to_owned()))         }
375
    //         None => Err(KludgineError::FontFamilyNotFound(family.to_owned())),
376
    //     }
377
    // }
378
}