1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Drawing APIs — draw interface

use super::{color::Rgba, AnimationState};
#[allow(unused)] use super::{DrawRounded, DrawRoundedImpl};
use super::{DrawShared, DrawSharedImpl, ImageId, PassId, PassType, SharedState, WindowCommon};
use crate::geom::{Offset, Quad, Rect};
#[allow(unused)] use crate::text::TextApi;
use crate::text::{Effect, TextDisplay};
use std::any::Any;
use std::time::Instant;

/// Draw interface object
///
/// [`Draw`] and extension traits such as [`DrawRounded`] provide draw
/// functionality over this object.
///
/// This type is used to present a unified mid-level draw interface, as
/// available from [`crate::theme::DrawCx::draw_device`].
/// A concrete `DrawIface` object may be obtained via downcast, e.g.:
/// ```ignore
/// # use kas::draw::{DrawIface, DrawRoundedImpl, DrawSharedImpl, DrawCx, DrawRounded, color::Rgba};
/// # use kas::geom::Rect;
/// # struct CircleWidget<DS> {
/// #     rect: Rect,
/// #     _pd: std::marker::PhantomData<DS>,
/// # }
/// impl CircleWidget {
///     fn draw(&mut self, mut draw: DrawCx) {
///         // This type assumes usage of kas_wgpu without a custom draw pipe:
///         type DrawIface = DrawIface<kas_wgpu::draw::DrawPipe<()>>;
///         if let Some(mut draw) = DrawIface::downcast_from(draw.draw_device()) {
///             draw.circle(self.rect.into(), 0.9, Rgba::BLACK);
///         }
///     }
/// }
/// ```
///
/// Note that this object is little more than a mutable reference to application
/// shared draw state. As such, it is normal to pass *a new copy* created
/// via [`DrawIface::re`] as a method argument. (Note that Rust automatically
/// "reborrows" reference types passed as method arguments, but cannot do so
/// automatically for structs containing references.)
pub struct DrawIface<'a, DS: DrawSharedImpl> {
    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
    #[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
    pub draw: &'a mut DS::Draw,
    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
    #[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
    pub shared: &'a mut SharedState<DS>,
    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
    #[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
    pub pass: PassId,
}

impl<'a, DS: DrawSharedImpl> DrawIface<'a, DS> {
    /// Construct a new instance
    ///
    /// For usage by graphics backends.
    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
    #[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
    pub fn new(draw: &'a mut DS::Draw, shared: &'a mut SharedState<DS>) -> Self {
        DrawIface {
            draw,
            shared,
            pass: PassId::new(0),
        }
    }

    /// Attempt to downcast a `&mut dyn Draw` to a concrete [`DrawIface`] object
    ///
    /// Note: Rust does not (yet) support trait-object-downcast: it not possible
    /// to cast from `&mut dyn Draw` to (for example) `&mut dyn DrawRounded`.
    /// Instead, the target type must be the implementing object, which is
    /// provided by the graphics backend (e.g. `kas_wgpu`).
    /// See documentation on this type for an example, or examine
    /// [`clock.rs`](https://github.com/kas-gui/kas/blob/master/examples/clock.rs).
    pub fn downcast_from(obj: &'a mut dyn Draw) -> Option<Self> {
        let pass = obj.get_pass();
        let (draw, shared) = obj.get_fields_as_any_mut();
        let draw = draw.downcast_mut()?;
        let shared = shared.downcast_mut()?;
        Some(DrawIface { draw, shared, pass })
    }

    /// Reborrow with a new lifetime
    pub fn re<'b>(&'b mut self) -> DrawIface<'b, DS>
    where
        'a: 'b,
    {
        DrawIface {
            draw: &mut *self.draw,
            shared: &mut *self.shared,
            pass: self.pass,
        }
    }

    /// Add a draw pass
    ///
    /// Adds a new draw pass. Passes affect draw order (operations in new passes
    /// happen after their parent pass), may clip drawing to a "clip rect"
    /// (see [`Draw::get_clip_rect`]) and may offset (translate) draw
    /// operations.
    ///
    /// Case `class == PassType::Clip`: the new pass is derived from
    /// `parent_pass`; `rect` and `offset` are specified relative to this parent
    /// and the intersecton of `rect` and the parent's "clip rect" is used.
    /// be clipped to `rect` (expressed in the parent's coordinate system).
    ///
    /// Case `class == PassType::Overlay`: the new pass is derived from the
    /// base pass (i.e. the window). Draw operations still happen after those in
    /// `parent_pass`.
    pub fn new_pass(&mut self, rect: Rect, offset: Offset, class: PassType) -> DrawIface<DS> {
        let pass = self.draw.new_pass(self.pass, rect, offset, class);
        DrawIface {
            draw: &mut *self.draw,
            shared: &mut *self.shared,
            pass,
        }
    }
}

/// Base drawing interface for [`DrawIface`]
///
/// Most methods draw some feature. Exceptions are those starting with `get_`
/// and [`Self::new_dyn_pass`].
///
/// Additional draw routines are available through extension traits, depending
/// on the graphics backend. Since Rust does not (yet) support trait-object-downcast,
/// accessing these requires reconstruction of the implementing type via
/// [`DrawIface::downcast_from`].
pub trait Draw {
    /// Access shared draw state
    fn shared(&mut self) -> &mut dyn DrawShared;

    /// Request redraw at the next frame time
    ///
    /// Animations should call this each frame until complete.
    fn animate(&mut self);

    /// Request a redraw at a specific time
    ///
    /// This may be used for animations with delays, e.g. flashing. Calling this
    /// method only ensures that the *next* draw happens *no later* than `time`,
    /// thus the method should be called again in each following frame.
    fn animate_at(&mut self, time: Instant);

    /// Get the current draw pass
    fn get_pass(&self) -> PassId;

    /// Cast fields to [`Any`] references
    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
    #[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
    fn get_fields_as_any_mut(&mut self) -> (&mut dyn Any, &mut dyn Any);

    /// Add a draw pass
    ///
    /// Adds a new draw pass. Passes affect draw order (operations in new passes
    /// happen after their parent pass), may clip drawing to a "clip rect"
    /// (see [`Draw::get_clip_rect`]) and may offset (translate) draw
    /// operations.
    ///
    /// Case `class == PassType::Clip`: the new pass is derived from
    /// `parent_pass`; `rect` and `offset` are specified relative to this parent
    /// and the intersecton of `rect` and the parent's "clip rect" is used.
    /// be clipped to `rect` (expressed in the parent's coordinate system).
    ///
    /// Case `class == PassType::Overlay`: the new pass is derived from the
    /// base pass (i.e. the window). Draw operations still happen after those in
    /// `parent_pass`.
    fn new_dyn_pass<'b>(
        &'b mut self,
        rect: Rect,
        offset: Offset,
        class: PassType,
    ) -> Box<dyn Draw + 'b>;

    /// Get drawable rect for a draw `pass`
    ///
    /// The result is in the current target's coordinate system, thus normally
    /// `Rect::pos` is zero (but this is not guaranteed).
    ///
    /// (This is not guaranteed to equal the rect passed to
    /// [`DrawIface::new_pass`].)
    fn get_clip_rect(&self) -> Rect;

    /// Draw a rectangle of uniform colour
    ///
    /// Note: where the implementation batches and/or re-orders draw calls,
    /// this should be one of the first items drawn such that almost anything
    /// else will draw "in front of" a rect.
    fn rect(&mut self, rect: Quad, col: Rgba);

    /// Draw a frame of uniform colour
    ///
    /// The frame is defined by the area inside `outer` and not inside `inner`.
    fn frame(&mut self, outer: Quad, inner: Quad, col: Rgba);

    /// Draw the image in the given `rect`
    fn image(&mut self, id: ImageId, rect: Quad);

    /// Draw text with a colour
    ///
    /// Text is drawn from `rect.pos` and clipped to `rect`. If the text
    /// scrolls, `rect` should be the size of the whole text, not the window.
    ///
    /// It is required to call [`TextApi::prepare`] or equivalent
    /// prior to this method to select a font, font size and perform layout.
    fn text(&mut self, rect: Rect, text: &TextDisplay, col: Rgba);

    /// Draw text with a single color and effects
    ///
    /// Text is drawn from `rect.pos` and clipped to `rect`. If the text
    /// scrolls, `rect` should be the size of the whole text, not the window.
    ///
    /// The effects list does not contain colour information, but may contain
    /// underlining/strikethrough information. It may be empty.
    ///
    /// It is required to call [`TextApi::prepare`] or equivalent
    /// prior to this method to select a font, font size and perform layout.
    fn text_effects(&mut self, rect: Rect, text: &TextDisplay, col: Rgba, effects: &[Effect<()>]);

    /// Draw text with effects (including [`Rgba`] color)
    ///
    /// Text is drawn from `rect.pos` and clipped to `rect`. If the text
    /// scrolls, `rect` should be the size of the whole text, not the window.
    ///
    /// The `effects` list provides both underlining and colour information.
    /// If the `effects` list is empty or the first entry has `start > 0`, a
    /// default entity will be assumed.
    ///
    /// It is required to call [`TextApi::prepare`] or equivalent
    /// prior to this method to select a font, font size and perform layout.
    fn text_effects_rgba(&mut self, rect: Rect, text: &TextDisplay, effects: &[Effect<Rgba>]);
}

impl<'a, DS: DrawSharedImpl> Draw for DrawIface<'a, DS> {
    fn shared(&mut self) -> &mut dyn DrawShared {
        self.shared
    }

    fn animate(&mut self) {
        self.draw.animate();
    }

    fn animate_at(&mut self, time: Instant) {
        self.draw.animate_at(time);
    }

    fn get_pass(&self) -> PassId {
        self.pass
    }

    fn get_fields_as_any_mut(&mut self) -> (&mut dyn Any, &mut dyn Any) {
        (self.draw, self.shared)
    }

    fn new_dyn_pass<'b>(
        &'b mut self,
        rect: Rect,
        offset: Offset,
        class: PassType,
    ) -> Box<dyn Draw + 'b> {
        Box::new(self.new_pass(rect, offset, class))
    }

    fn get_clip_rect(&self) -> Rect {
        self.draw.get_clip_rect(self.pass)
    }

    fn rect(&mut self, rect: Quad, col: Rgba) {
        self.draw.rect(self.pass, rect, col);
    }
    fn frame(&mut self, outer: Quad, inner: Quad, col: Rgba) {
        self.draw.frame(self.pass, outer, inner, col);
    }

    fn image(&mut self, id: ImageId, rect: Quad) {
        self.shared.draw.draw_image(self.draw, self.pass, id, rect);
    }

    fn text(&mut self, rect: Rect, text: &TextDisplay, col: Rgba) {
        self.shared
            .draw
            .draw_text(self.draw, self.pass, rect, text, col);
    }

    fn text_effects(&mut self, rect: Rect, text: &TextDisplay, col: Rgba, effects: &[Effect<()>]) {
        self.shared
            .draw
            .draw_text_effects(self.draw, self.pass, rect, text, col, effects);
    }

    fn text_effects_rgba(&mut self, rect: Rect, text: &TextDisplay, effects: &[Effect<Rgba>]) {
        self.shared
            .draw
            .draw_text_effects_rgba(self.draw, self.pass, rect, text, effects);
    }
}

/// Base abstraction over drawing
///
/// This trait covers only the bare minimum of functionality which *must* be
/// provided by the graphics backend; extension traits such as [`DrawRoundedImpl`]
/// optionally provide more functionality.
///
/// Coordinates for many primitives are specified using floating-point types
/// allowing fractional precision, deliberately excepting text which must be
/// pixel-aligned for best appearance.
///
/// All draw operations may be batched; when drawn primitives overlap, the
/// results are only loosely defined. Draw operations involving transparency
/// should be ordered after those without transparency.
///
/// Draw operations take place over multiple render passes, identified by a
/// handle of type [`PassId`]. In general the user only needs to pass this value
/// into methods as required. [`DrawImpl::new_pass`] creates a new [`PassId`].
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub trait DrawImpl: Any {
    /// Access common data
    fn common_mut(&mut self) -> &mut WindowCommon;

    /// Request redraw at the next frame time
    ///
    /// Animations should call this each frame until complete.
    fn animate(&mut self) {
        self.common_mut().anim.merge_in(AnimationState::Animate);
    }

    /// Request a redraw at a specific time
    ///
    /// This may be used for animations with delays, e.g. flashing. Calling this
    /// method only ensures that the *next* draw happens *no later* than `time`,
    /// thus the method should be called again in each following frame.
    fn animate_at(&mut self, time: Instant) {
        self.common_mut().anim.merge_in(AnimationState::Timed(time));
    }

    /// Add a draw pass
    ///
    /// Adds a new draw pass. Passes have the following effects:
    ///
    /// -   Draw operations of a pass occur *after* those of the parent pass
    /// -   Drawing is clipped to `rect` (in the base's coordinate space) and
    ///     translated by `offset` (relative to the base's offset)
    ///
    /// The *parent pass* is the one used as the `self` argument of this method.
    /// The *base pass* is dependent on `class`:
    ///
    /// -   `PassType::Clip`: the base is the parent
    /// -   `PassType::Overlay`: the base is the initial pass (i.e. whole window
    ///     with no offset)
    fn new_pass(
        &mut self,
        parent_pass: PassId,
        rect: Rect,
        offset: Offset,
        class: PassType,
    ) -> PassId;

    /// Get drawable rect for a draw `pass`
    ///
    /// The result is in the current target's coordinate system, thus normally
    /// `Rect::pos` is zero (but this is not guaranteed).
    ///
    /// (This is not guaranteed to equal the rect passed to
    /// [`DrawImpl::new_pass`].)
    fn get_clip_rect(&self, pass: PassId) -> Rect;

    /// Draw a rectangle of uniform colour
    fn rect(&mut self, pass: PassId, rect: Quad, col: Rgba);

    /// Draw a frame of uniform colour
    fn frame(&mut self, pass: PassId, outer: Quad, inner: Quad, col: Rgba);
}