| 1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
| 2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
| 3 | |
| 4 | export component SliderBase { |
| 5 | in property <bool> enabled <=> touch-area.enabled; |
| 6 | in property <float> minimum: 0; |
| 7 | in property <float> maximum: 100; |
| 8 | in property <float> step: 1; |
| 9 | in property <Orientation> orientation; |
| 10 | in property <length> handle-x; |
| 11 | in property <length> handle-y; |
| 12 | in property <length> handle-width; |
| 13 | in property <length> handle-height; |
| 14 | out property <bool> pressed <=> touch-area.enabled; |
| 15 | out property <bool> has-hover <=> touch-area.has-hover; |
| 16 | out property <bool> vertical: root.orientation == Orientation.vertical; |
| 17 | out property <bool> has-focus <=> focus-scope.has-focus; |
| 18 | out property <bool> handle-has-hover: touch-area.mouse-x >= root.handle-x && touch-area.mouse-x <= root.handle-x + root.handle-width && |
| 19 | touch-area.mouse-y >= root.handle-y && touch-area.mouse-y <= root.handle-y + root.handle-height; |
| 20 | out property <bool> handle-pressed; |
| 21 | in-out property <float> value: minimum; |
| 22 | |
| 23 | callback changed(value: float); |
| 24 | callback released(value: float); |
| 25 | |
| 26 | private property <length> ref-size: !root.vertical ? root.handle-width : root.handle-height; |
| 27 | |
| 28 | forward-focus: focus-scope; |
| 29 | |
| 30 | touch-area := TouchArea { |
| 31 | property <float> pressed-value; |
| 32 | |
| 33 | width: 100%; |
| 34 | height: 100%; |
| 35 | |
| 36 | pointer-event(event) => { |
| 37 | if (event.button != PointerEventButton.left) { |
| 38 | return; |
| 39 | } |
| 40 | |
| 41 | if (event.kind == PointerEventKind.up) { |
| 42 | if (root.handle-pressed) { |
| 43 | root.released(root.value); |
| 44 | } |
| 45 | root.handle-pressed = false; |
| 46 | return; |
| 47 | } |
| 48 | |
| 49 | |
| 50 | if (!root.handle-has-hover) { |
| 51 | root.set-value((!root.vertical ? root.size-to-value(touch-area.mouse-x, root.width) : |
| 52 | root.size-to-value(touch-area.mouse-y, root.height)) + root.minimum); |
| 53 | } |
| 54 | |
| 55 | self.pressed-value = value; |
| 56 | focus-scope.focus(); |
| 57 | root.handle-pressed = true; |
| 58 | } |
| 59 | |
| 60 | moved => { |
| 61 | if (!self.enabled) { |
| 62 | return; |
| 63 | } |
| 64 | |
| 65 | root.set-value(self.pressed-value + (!vertical ? root.size-to-value(touch-area.mouse-x - touch-area.pressed-x, root.width - root.ref-size) : |
| 66 | root.size-to-value(touch-area.mouse-y - touch-area.pressed-y, root.height - root.ref-size)) |
| 67 | ); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | focus-scope := FocusScope { |
| 72 | x: 0; |
| 73 | y: 0; |
| 74 | width: 0; |
| 75 | height: 0; |
| 76 | enabled: root.enabled; |
| 77 | |
| 78 | key-pressed(event) => { |
| 79 | if (!self.enabled || root.step <= 0) { |
| 80 | return reject; |
| 81 | } |
| 82 | |
| 83 | if ((!vertical && event.text == Key.RightArrow) || (vertical && event.text == Key.DownArrow)) { |
| 84 | root.set-value(root.value + root.step); |
| 85 | return accept; |
| 86 | } else if ((!vertical && event.text == Key.LeftArrow) || (vertical && event.text == Key.UpArrow)) { |
| 87 | root.set-value(root.value - root.step); |
| 88 | return accept; |
| 89 | } else if (event.text == Key.Home) { |
| 90 | root.set-value(root.minimum); |
| 91 | return accept; |
| 92 | } else if (event.text == Key.End) { |
| 93 | root.set-value(root.maximum); |
| 94 | return accept; |
| 95 | } |
| 96 | |
| 97 | reject |
| 98 | } |
| 99 | |
| 100 | key-released(event) => { |
| 101 | if (!self.enabled) { |
| 102 | return reject; |
| 103 | } |
| 104 | if (!vertical && event.text == Key.RightArrow) || (vertical && event.text == Key.DownArrow) |
| 105 | || (!vertical && event.text == Key.LeftArrow) || (vertical && event.text == Key.UpArrow) |
| 106 | || event.text == Key.Home || event.text == Key.End { |
| 107 | root.released(root.value); |
| 108 | } |
| 109 | return accept; |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | pure function size-to-value(size: length, range: length) -> float { |
| 114 | size * (root.maximum - root.minimum) / range; |
| 115 | } |
| 116 | |
| 117 | function set-value(value: float) { |
| 118 | if (root.value == value) { |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | root.value = max(root.minimum, min(root.maximum, value)); |
| 123 | root.changed(root.value); |
| 124 | } |
| 125 | } |
| 126 | |