1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial |
3 | |
4 | // cSpell: ignore resizer |
5 | |
6 | global ResizeState { |
7 | out property <length> handle-size: 8px; |
8 | } |
9 | |
10 | component ResizeHandle inherits Rectangle { |
11 | in property <bool> show-handle: true; |
12 | in property <color> my-color: Colors.black; |
13 | in property <MouseCursor> mouse-cursor; |
14 | out property <bool> resizing <=> ta.pressed; |
15 | |
16 | callback resize(/* width */ length, /* height */ length, /* done? */ bool); |
17 | callback resize-done(/* width */ length, /* height */ length); |
18 | |
19 | // Internal! |
20 | in-out property <length> new-width; |
21 | in-out property <length> new-height; |
22 | in-out property <length> new-x; |
23 | in-out property <length> new-y; |
24 | |
25 | width: ResizeState.handle-size; |
26 | height: ResizeState.handle-size; |
27 | |
28 | background: root.show-handle ? Colors.white : Colors.transparent; |
29 | border-color: root.show-handle ? root.my-color : Colors.transparent; |
30 | |
31 | border-width: 1px; |
32 | |
33 | ta := TouchArea { |
34 | private property <length> x-offset: self.mouse-x - self.pressed-x; |
35 | private property <length> y-offset: self.mouse-y - self.pressed-y; |
36 | |
37 | mouse-cursor <=> root.mouse-cursor; |
38 | |
39 | moved() => { |
40 | root.resize(x-offset, y-offset, false); |
41 | } |
42 | |
43 | pointer-event(event) => { |
44 | if event.button == PointerEventButton.left && event.kind == PointerEventKind.up { |
45 | root.resize(x-offset, y-offset, true); |
46 | } |
47 | } |
48 | } |
49 | } |
50 | |
51 | export component Resizer { |
52 | in property <length> x-position; |
53 | in property <length> y-position; |
54 | |
55 | in property <bool> is-resizable: true; |
56 | in property <bool> is-moveable: false; |
57 | in property <color> color: Colors.black; |
58 | out property <bool> resizing: n.resizing || ne.resizing || e.resizing || se.resizing || s.resizing || sw.resizing || w.resizing || nw.resizing; |
59 | out property <bool> moving: resize-area.moving; |
60 | out property <length> handle-size: ResizeState.handle-size; |
61 | |
62 | callback update-geometry(/* x */ length, /* y */ length, /* width */ length, /* height */ length, /* done */ bool); |
63 | callback double-clicked(/* x */ length, /* y */ length, /* modifiers */ KeyboardModifiers); |
64 | |
65 | // Private! |
66 | in-out property <length> top: self.y-position; |
67 | in-out property <length> bottom: self.y-position + self.height; |
68 | in-out property <length> left: self.x-position; |
69 | in-out property <length> right: self.x-position + self.width; |
70 | |
71 | resize-area := Rectangle { |
72 | in-out property <bool> moving: false; |
73 | |
74 | width <=> root.width; |
75 | height <=> root.height; |
76 | |
77 | @children |
78 | |
79 | if root.is-moveable: TouchArea { |
80 | private property <length> x-offset: self.mouse-x - self.pressed-x; |
81 | private property <length> y-offset: self.mouse-y - self.pressed-y; |
82 | private property <KeyboardModifiers> modifiers; |
83 | private property <bool> has-moved; |
84 | |
85 | mouse-cursor: MouseCursor.move; |
86 | |
87 | double-clicked() => { |
88 | root.double-clicked(self.mouse-x, self.mouse-y, self.modifiers); |
89 | } |
90 | |
91 | moved() => { |
92 | root.update-geometry(root.x-position + x-offset, root.y-position + y-offset, root.width, root.height, false); |
93 | self.has-moved = true; |
94 | } |
95 | |
96 | pointer-event(event) => { |
97 | resize-area.moving = self.pressed; |
98 | self.modifiers = event.modifiers; |
99 | if self.has-moved && event.button == PointerEventButton.left && event.kind == PointerEventKind.up { |
100 | root.update-geometry(root.x-position + x-offset, root.y-position + y-offset, root.width, root.height, true); |
101 | self.has-moved = false; |
102 | } |
103 | } |
104 | } |
105 | } |
106 | |
107 | Rectangle { |
108 | visible: is-resizable; |
109 | |
110 | n := ResizeHandle { |
111 | show-handle: false; |
112 | resize(x-offset, y-offset, done) => { |
113 | self.new-width = root.width; |
114 | self.new-height = Math.max(0px, y-offset * -1.0 + root.height); |
115 | self.new-x = root.left; |
116 | self.new-y = root.bottom - self.new-height; |
117 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
118 | } |
119 | |
120 | mouse-cursor: MouseCursor.n-resize; |
121 | x: (root.handle-size / 2.0); |
122 | y: -(root.handle-size / 2.0); |
123 | width: root.width - root.handle-size; |
124 | } |
125 | |
126 | ne := ResizeHandle { |
127 | my-color: root.color; |
128 | resize(x-offset, y-offset, done) => { |
129 | self.new-width = Math.max(0px, x-offset + root.width); |
130 | self.new-height = Math.max(0px, y-offset * -1.0 + root.height); |
131 | self.new-x = root.left; |
132 | self.new-y = root.bottom - self.new-height; |
133 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
134 | } |
135 | mouse-cursor: MouseCursor.ne-resize; |
136 | x: root.width - (root.handle-size / 2.0); |
137 | y: -(root.handle-size / 2.0); |
138 | } |
139 | |
140 | e := ResizeHandle { |
141 | show-handle: false; |
142 | resize(x-offset, y-offset, done) => { |
143 | self.new-width = Math.max(0px, x-offset + root.width); |
144 | self.new-height = root.height; |
145 | self.new-x = root.left; |
146 | self.new-y = root.top; |
147 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
148 | } |
149 | mouse-cursor: MouseCursor.e-resize; |
150 | x: root.width - (root.handle-size / 2.0); |
151 | y: root.handle-size / 2.0; |
152 | height: root.height - root.handle-size; |
153 | } |
154 | |
155 | se := ResizeHandle { |
156 | my-color: root.color; |
157 | resize(x-offset, y-offset, done) => { |
158 | self.new-width = Math.max(0px, x-offset + root.width); |
159 | self.new-height = Math.max(0px, y-offset + root.height); |
160 | self.new-x = root.left; |
161 | self.new-y = root.top; |
162 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
163 | } |
164 | mouse-cursor: MouseCursor.se-resize; |
165 | x: root.width - (root.handle-size / 2.0); |
166 | y: root.height - (root.handle-size / 2.0); |
167 | } |
168 | |
169 | s := ResizeHandle { |
170 | show-handle: false; |
171 | resize(x-offset, y-offset, done) => { |
172 | self.new-width = root.width; |
173 | self.new-height = Math.max(0px, y-offset + root.height); |
174 | self.new-x = root.left; |
175 | self.new-y = root.top; |
176 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
177 | } |
178 | mouse-cursor: MouseCursor.s-resize; |
179 | x: root.handle-size / 2.0; |
180 | y: root.height - (root.handle-size / 2.0); |
181 | width: root.width - root.handle-size; |
182 | } |
183 | |
184 | sw := ResizeHandle { |
185 | my-color: root.color; |
186 | resize(x-offset, y-offset, done) => { |
187 | self.new-width = Math.max(0px, x-offset * -1.0 + root.width); |
188 | self.new-height = Math.max(0px, y-offset + root.height); |
189 | self.new-x = root.right - self.new-width; |
190 | self.new-y = root.top; |
191 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
192 | } |
193 | mouse-cursor: MouseCursor.sw-resize; |
194 | x: -(root.handle-size / 2.0); |
195 | y: root.height - (root.handle-size / 2.0); |
196 | } |
197 | |
198 | w := ResizeHandle { |
199 | show-handle: false; |
200 | resize(x-offset, y-offset, done) => { |
201 | self.new-width = Math.max(0px, x-offset * -1.0 + root.width); |
202 | self.new-height = root.height; |
203 | self.new-x = root.right - self.new-width; |
204 | self.new-y = root.top; |
205 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
206 | } |
207 | mouse-cursor: MouseCursor.w-resize; |
208 | x: -(root.handle-size / 2.0); |
209 | y: (root.handle-size / 2.0); |
210 | height: root.height - root.handle-size; |
211 | } |
212 | |
213 | nw := ResizeHandle { |
214 | my-color: root.color; |
215 | resize(x-offset, y-offset, done) => { |
216 | self.new-width = Math.max(0px, x-offset * -1.0 + root.width); |
217 | self.new-height = Math.max(0px, y-offset * -1.0 + root.height); |
218 | self.new-x = root.right - self.new-width; |
219 | self.new-y = root.bottom - self.new-height; |
220 | root.update-geometry(self.new-x, self.new-y, self.new-width, self.new-height, done); |
221 | } |
222 | mouse-cursor: MouseCursor.nw-resize; |
223 | x: -(root.handle-size / 2.0); |
224 | y: -(root.handle-size / 2.0); |
225 | } |
226 | } |
227 | } |
228 | |