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
use super::super::{super::Rotation, style, IcedRenderer, Primitive};
use common::util::srgba_to_linear;
use core::ops::RangeInclusive;
use iced::{mouse, slider, Point, Rectangle};
use style::slider::{Bar, Cursor, Style};

const CURSOR_DRAG_SHIFT: f32 = 0.7;

impl slider::Renderer for IcedRenderer {
    type Style = Style;

    const DEFAULT_HEIGHT: u16 = 25;

    fn draw(
        &mut self,
        bounds: Rectangle,
        cursor_position: Point,
        range: RangeInclusive<f32>,
        value: f32,
        is_dragging: bool,
        style: &Self::Style,
    ) -> Self::Output {
        let bar_bounds = Rectangle {
            height: style.bar_height as f32,
            y: bounds.y + (bounds.height - style.bar_height as f32) / 2.0,
            ..bounds
        };
        let bar = match style.bar {
            Bar::Color(color) => Primitive::Rectangle {
                bounds: bar_bounds,
                linear_color: srgba_to_linear(color),
            },
            // Note: bar_pad adds to the size of the bar currently since the dragging logic wouldn't
            // account for shrinking the area that the cursor is shown in
            Bar::Image(handle, color, bar_pad) => Primitive::Image {
                handle: (handle, Rotation::None),
                bounds: Rectangle {
                    x: bar_bounds.x - bar_pad as f32,
                    width: bar_bounds.width + bar_pad as f32 * 2.0,
                    ..bar_bounds
                },
                color,
                source_rect: None,
            },
        };

        let (cursor_width, cursor_height) = style.cursor_size;
        let (cursor_width, cursor_height) = (f32::from(cursor_width), f32::from(cursor_height));
        let (min, max) = range.into_inner();
        let offset = bounds.width * (value - min) / (max - min);
        let cursor_bounds = Rectangle {
            x: bounds.x + offset - cursor_width / 2.0,
            y: bounds.y
                + if is_dragging { CURSOR_DRAG_SHIFT } else { 0.0 }
                + (bounds.height - cursor_height) / 2.0,
            width: cursor_width,
            height: cursor_height,
        };
        let cursor = match style.cursor {
            Cursor::Color(color) => Primitive::Rectangle {
                bounds: cursor_bounds,
                linear_color: srgba_to_linear(color),
            },
            Cursor::Image(handle, color) => Primitive::Image {
                handle: (handle, Rotation::None),
                bounds: cursor_bounds,
                color,
                source_rect: None,
            },
        };

        let interaction = if is_dragging {
            mouse::Interaction::Grabbing
        } else if cursor_bounds.contains(cursor_position) {
            mouse::Interaction::Grab
        } else if bar_bounds.contains(cursor_position) {
            mouse::Interaction::Pointer
        } else {
            mouse::Interaction::Idle
        };

        #[allow(clippy::if_same_then_else)] // TODO: remove
        let primitives = if style.labels {
            // TODO text label on left and right ends
            vec![bar, cursor]
        } else {
            // TODO Cursor text label
            vec![bar, cursor]
        };
        (Primitive::Group { primitives }, interaction)
    }
}