livesplit_core/rendering/resource/
handles.rs

1use core::{
2    hash::{Hash, Hasher},
3    ops::{Deref, DerefMut},
4};
5
6use crate::settings::Font;
7
8use super::{Label, PathBuilder, ResourceAllocator, SharedOwnership};
9
10pub struct Handles<A> {
11    next_id: usize,
12    allocator: A,
13}
14
15impl<A> Handles<A> {
16    pub const fn new(next_id: usize, allocator: A) -> Self {
17        Self { next_id, allocator }
18    }
19
20    pub fn next<T>(&mut self, element: T) -> Handle<T> {
21        let id = self.next_id;
22        self.next_id = self.next_id.wrapping_add(1);
23        Handle { id, inner: element }
24    }
25
26    /// Get the handles's next ID.
27    #[allow(clippy::missing_const_for_fn)] // FIXME: Drop is unsupported.
28    pub fn into_next_id(self) -> usize {
29        self.next_id
30    }
31}
32
33pub struct HandlePathBuilder<PB: PathBuilder>(PB, usize);
34
35impl<PB: PathBuilder> PathBuilder for HandlePathBuilder<PB> {
36    type Path = Handle<PB::Path>;
37
38    fn move_to(&mut self, x: f32, y: f32) {
39        self.0.move_to(x, y);
40    }
41
42    fn line_to(&mut self, x: f32, y: f32) {
43        self.0.line_to(x, y)
44    }
45
46    fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
47        self.0.quad_to(x1, y1, x, y)
48    }
49
50    fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
51        self.0.curve_to(x1, y1, x2, y2, x, y)
52    }
53
54    fn close(&mut self) {
55        self.0.close()
56    }
57
58    fn finish(self) -> Self::Path {
59        Handle::new(self.1, self.0.finish())
60    }
61}
62
63impl<A: ResourceAllocator> ResourceAllocator for Handles<A> {
64    type PathBuilder = HandlePathBuilder<A::PathBuilder>;
65    type Path = Handle<A::Path>;
66    type Image = Handle<A::Image>;
67    type Font = Handle<A::Font>;
68    type Label = LabelHandle<A::Label>;
69
70    fn path_builder(&mut self) -> Self::PathBuilder {
71        let id = self.next_id;
72        self.next_id = self.next_id.wrapping_add(1);
73        HandlePathBuilder(self.allocator.path_builder(), id)
74    }
75
76    fn build_circle(&mut self, x: f32, y: f32, r: f32) -> Self::Path {
77        let circle = self.allocator.build_circle(x, y, r);
78        self.next(circle)
79    }
80
81    fn create_image(&mut self, data: &[u8]) -> Option<(Self::Image, f32)> {
82        let (image, aspect_ratio) = self.allocator.create_image(data)?;
83        Some((self.next(image), aspect_ratio))
84    }
85
86    fn create_font(&mut self, font: Option<&Font>, kind: super::FontKind) -> Self::Font {
87        let font = self.allocator.create_font(font, kind);
88        self.next(font)
89    }
90
91    fn create_label(
92        &mut self,
93        text: &str,
94        font: &mut Self::Font,
95        max_width: Option<f32>,
96    ) -> Self::Label {
97        let label = self.allocator.create_label(text, font, max_width);
98        LabelHandle {
99            update_counter: 0,
100            handle: self.next(label),
101        }
102    }
103
104    fn update_label(
105        &mut self,
106        label: &mut Self::Label,
107        text: &str,
108        font: &mut Self::Font,
109        max_width: Option<f32>,
110    ) {
111        self.allocator
112            .update_label(&mut label.handle, text, font, max_width);
113        label.update_counter += 1;
114    }
115}
116
117/// A special handle meant to be used for text labels that also tracks how often
118/// the label got updated.
119pub struct LabelHandle<L> {
120    update_counter: usize,
121    handle: Handle<L>,
122}
123
124impl<T> Deref for LabelHandle<T> {
125    type Target = T;
126
127    fn deref(&self) -> &Self::Target {
128        &self.handle
129    }
130}
131
132impl<T> Hash for LabelHandle<T> {
133    fn hash<H: Hasher>(&self, state: &mut H) {
134        self.update_counter.hash(state);
135        self.handle.hash(state);
136    }
137}
138
139impl<L: Label> SharedOwnership for LabelHandle<L> {
140    fn share(&self) -> Self {
141        LabelHandle {
142            update_counter: self.update_counter,
143            handle: self.handle.share(),
144        }
145    }
146}
147
148impl<L: Label> Label for LabelHandle<L> {
149    fn width(&self, scale: f32) -> f32 {
150        self.handle.inner.width(scale)
151    }
152
153    fn width_without_max_width(&self, scale: f32) -> f32 {
154        self.handle.inner.width_without_max_width(scale)
155    }
156}
157
158impl<T> Eq for LabelHandle<T> {}
159
160impl<T> PartialEq for LabelHandle<T> {
161    fn eq(&self, other: &Self) -> bool {
162        self.update_counter.eq(&other.update_counter) && self.handle.eq(&other.handle)
163    }
164}
165
166/// A handle can be used to uniquely identify the resource it wraps.
167pub struct Handle<T> {
168    pub(crate) id: usize,
169    inner: T,
170}
171
172impl<T: SharedOwnership> SharedOwnership for Handle<T> {
173    fn share(&self) -> Self {
174        Self {
175            id: self.id,
176            inner: self.inner.share(),
177        }
178    }
179}
180
181impl<T: SharedOwnership> Handle<T> {
182    /// Creates a handle based on some resource that it wraps and a unique ID.
183    pub const fn new(id: usize, resource: T) -> Self {
184        Self {
185            id,
186            inner: resource,
187        }
188    }
189}
190
191impl<T> Deref for Handle<T> {
192    type Target = T;
193
194    fn deref(&self) -> &Self::Target {
195        &self.inner
196    }
197}
198
199impl<T> DerefMut for Handle<T> {
200    fn deref_mut(&mut self) -> &mut Self::Target {
201        &mut self.inner
202    }
203}
204
205impl<T> Hash for Handle<T> {
206    fn hash<H: Hasher>(&self, state: &mut H) {
207        self.id.hash(state)
208    }
209}
210
211impl<T> Eq for Handle<T> {}
212
213impl<T> PartialEq for Handle<T> {
214    fn eq(&self, other: &Self) -> bool {
215        self.id.eq(&other.id)
216    }
217}