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
4import { Preview } from "./color-basics.slint";
5
6import { Api, BrushKind, GradientStop } from "../../api.slint";
7
8component GradientDot inherits Rectangle {
9 in-out property <bool> selected: false;
10 out property <bool> has-hover <=> ta.has-hover;
11
12 in property <length> parent-width;
13 in-out property <[GradientStop]> gradient-stops;
14 in property <int> index;
15
16 in property <int> apply-update;
17
18 x: parent-width * self.gradient-stops[self.index].position - self.width / 2.0;
19
20 callback select-gradient-stop();
21 callback unselect-gradient-stop();
22 callback update-brush();
23
24 width: self.selected ? 20px : 10px;
25 height: self.selected ? 20px : 10px;
26 border-radius: self.width;
27 background: root.gradient-stops[root.index].color;
28 border-width: root.selected ? 2px : 1px;
29 border-color: root.selected ? black : gray;
30 Rectangle {
31 border-radius: self.width;
32 border-color: root.selected ? white : white.transparentize(0.5);
33 border-width: 1px;
34 }
35
36 ta := TouchArea {
37 private property <length> x-pos: self.mouse-x + root.x;
38
39 changed has-hover => {
40 if !root.selected && self.has-hover {
41 root.select-gradient-stop();
42 }
43 }
44
45 double-clicked => {
46 Api.remove-gradient-stop(root.gradient-stops, index);
47 root.update-brush();
48 }
49
50 moved => {
51 root.gradient-stops[root.index].position = Math.clamp(self.x-pos, 0, root.parent-width) / root.parent-width;
52 root.update-brush();
53 }
54
55 scroll-event(event) => {
56 if event.delta-y != 0 {
57 root.gradient-stops[root.index].position = Math.clamp(
58 root.gradient-stops[root.index].position + Math.clamp((event.delta-y / 1px), -0.01, +0.01),
59 0.0, 1.0);
60 root.update-brush();
61
62 return EventResult.accept;
63 }
64 return EventResult.reject;
65 }
66 }
67}
68
69export component GradientMainContent inherits HorizontalLayout {
70 in property <brush> current-brush;
71 in property <BrushKind> current-brush-kind;
72
73 in-out property <[GradientStop]> gradient-stops;
74 in-out property <float> current-position;
75 private property <float> current-position_: index-position(self.selected-index);
76
77 in-out property <color> current-color;
78 private property <color> current-color_: index-color(self.selected-index);
79
80 out property <bool> has-focus: ta.has-hover || self.dot-hover-count > 0;
81 out property <int> selected-index: -1;
82
83 callback update-brush();
84
85 private property <int> apply-update-to: -1;
86
87 changed current-color_ => {
88 self.current-color = current-color_;
89 }
90
91 changed current-color => {
92 if self.selected-index >= 0 && self.selected-index < self.gradient-stops.length {
93 self.gradient-stops[self.selected-index].color = current-color;
94 }
95 apply-model-change-to-ui();
96 }
97
98 changed current-position_ => {
99 self.current-position = current-position_;
100 }
101
102 changed current-position => {
103 if self.selected-index >= 0 && self.selected-index < self.gradient-stops.length {
104 self.gradient-stops[self.selected-index].position = current-position;
105 }
106 apply-model-change-to-ui();
107 }
108
109 pure function index-color(index: int) -> color {
110 if index >= 0 && index < self.gradient-stops.length {
111 return self.gradient-stops[index].color;
112 }
113 return Colors.transparent;
114 }
115
116 pure function index-position(index: int) -> float {
117 if index >= 0 && index < self.gradient-stops.length {
118 return self.gradient-stops[index].position;
119 }
120 return 0.0;
121 }
122
123 function apply-model-change-to-ui() {
124 if self.selected-index >= 0 {
125 self.apply-update-to = self.selected-index;
126 }
127 }
128
129 function apply-gradient-stops() {
130 if (self.selected-index < 0) || (self.selected-index >= self.gradient-stops.length) {
131 if self.gradient-stops.length > 0 {
132 self.selected-index = 0;
133 } else {
134 self.selected-index = -1;
135 }
136 }
137 }
138
139 init => {
140 apply-gradient-stops();
141 }
142
143 changed gradient-stops => {
144 apply-gradient-stops();
145 }
146
147 private property <int> dot-hover-count: 0;
148
149 grad := Preview {
150 width: 100%;
151 height: 40px;
152
153 if (current-brush-kind == BrushKind.radial): Rectangle {
154 width: parent.width * 2;
155 x: 0 - parent.width;
156
157 background: root.current-brush;
158 }
159
160 background: current-brush-kind == BrushKind.radial ? transparent : root.current-brush;
161
162 ta := TouchArea {
163 double-clicked => {
164 root.selected-index = Api.add-gradient-stop(root.gradient-stops, Api.suggest-gradient-stop-at-position(root.gradient-stops, self.mouse-x / self.width));
165
166 root.update-brush();
167 }
168
169 scroll-event(event) => {
170 if event.delta-y != 0 && root.selected-index >= 0 {
171 root.gradient-stops[root.selected-index].position = Math.clamp(
172 root.index-position(root.selected-index) + Math.clamp((event.delta-y / 1px), -0.01, +0.01),
173 0.0, 1.0);
174 root.update-brush();
175
176 root.apply-update-to = root.selected-index;
177
178 return EventResult.accept;
179 }
180 return EventResult.reject;
181 }
182 }
183
184 Rectangle {
185 height: 1px;
186 background: grey;
187
188 for i[index] in root.gradient-stops: GradientDot {
189 selected: root.selected-index == index;
190 gradient-stops <=> root.gradient-stops;
191 index: index;
192
193 changed has-hover => {
194 if self.has-hover {
195 root.dot-hover-count += 1;
196 } else {
197 root.dot-hover-count -= 1;
198 }
199 }
200
201 parent-width: grad.width;
202
203 select-gradient-stop() => {
204 root.selected-index = index;
205 }
206
207 update-brush() => {
208 root.update-brush();
209 }
210 }
211 }
212 }
213}
214