// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // cSpell: ignore resizer global ResizeState { out property handle-size: 8px; } component ResizeHandle inherits Rectangle { in property show-handle: true; in property my-color: Colors.black; in property mouse-cursor; out property resizing <=> ta.pressed; callback resize(/* width */ length, /* height */ length, /* done? */ bool); callback resize-done(/* width */ length, /* height */ length); // Internal! in-out property new-width; in-out property new-height; in-out property new-x; in-out property new-y; width: ResizeState.handle-size; height: ResizeState.handle-size; background: root.show-handle ? Colors.white : Colors.transparent; border-color: root.show-handle ? root.my-color : Colors.transparent; border-width: 1px; ta := TouchArea { private property x-offset: self.mouse-x - self.pressed-x; private property y-offset: self.mouse-y - self.pressed-y; mouse-cursor <=> root.mouse-cursor; moved() => { root.resize(x-offset, y-offset, false); } pointer-event(event) => { if event.button == PointerEventButton.left && event.kind == PointerEventKind.up { root.resize(x-offset, y-offset, true); } } } } export component Resizer { in property x-position; in property y-position; in property is-resizable: true; in property is-moveable: false; in property color: Colors.black; out property resizing: n.resizing || ne.resizing || e.resizing || se.resizing || s.resizing || sw.resizing || w.resizing || nw.resizing; out property moving: resize-area.moving; out property handle-size: ResizeState.handle-size; callback resize(/* x */ length, /* y */ length, /* width */ length, /* height */ length, /* done */ bool); callback can-move-to(/* x */ length, /* y */ length, /* mouse_x */ length, /* mouse_y */ length) -> bool; callback move-to(/* x */ length, /* y */ length, /* mouse_x */ length, /* mouse_y */ length); callback double-clicked(/* x */ length, /* y */ length, /* modifiers */ KeyboardModifiers); // Private! in-out property top: self.y-position; in-out property bottom: self.y-position + self.height; in-out property left: self.x-position; in-out property right: self.x-position + self.width; resize-area := Rectangle { in-out property moving: false; width <=> root.width; height <=> root.height; @children if root.is-moveable: TouchArea { private property x-offset: self.mouse-x - self.pressed-x; private property y-offset: self.mouse-y - self.pressed-y; private property modifiers; private property has-moved; private property can-move-to; mouse-cursor: MouseCursor.move; double-clicked() => { root.double-clicked(self.mouse-x, self.mouse-y, self.modifiers); } moved() => { self.can-move-to = root.can-move-to(root.x-position + x-offset, root.y-position + y-offset, root.x-position + self.mouse-x, root.y-position + self.mouse-y); self.has-moved = true; } pointer-event(event) => { resize-area.moving = self.pressed; self.modifiers = event.modifiers; if self.has-moved && event.button == PointerEventButton.left && event.kind == PointerEventKind.up { root.move-to(root.x-position + x-offset, root.y-position + y-offset, root.x-position + self.mouse-x, root.y-position + self.mouse-y); self.has-moved = false; } } states [ dragging-no-drop when self.has-moved && !self.can-move-to: { mouse-cursor: MouseCursor.no-drop; } dragging-can-drop when self.has-moved && self.can-move-to: { mouse-cursor: MouseCursor.copy; } normal when !self.has-moved: { mouse-cursor: MouseCursor.default; } ] } } Rectangle { visible: is-resizable; n := ResizeHandle { show-handle: false; resize(x-offset, y-offset, done) => { self.new-width = root.width; self.new-height = Math.max(0px, y-offset * -1.0 + root.height); self.new-x = root.left; self.new-y = root.bottom - self.new-height; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.n-resize; x: (root.handle-size / 2.0); y: -(root.handle-size / 2.0); width: root.width - root.handle-size; } ne := ResizeHandle { my-color: root.color; resize(x-offset, y-offset, done) => { self.new-width = Math.max(0px, x-offset + root.width); self.new-height = Math.max(0px, y-offset * -1.0 + root.height); self.new-x = root.left; self.new-y = root.bottom - self.new-height; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.ne-resize; x: root.width - (root.handle-size / 2.0); y: -(root.handle-size / 2.0); } e := ResizeHandle { show-handle: false; resize(x-offset, y-offset, done) => { self.new-width = Math.max(0px, x-offset + root.width); self.new-height = root.height; self.new-x = root.left; self.new-y = root.top; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.e-resize; x: root.width - (root.handle-size / 2.0); y: root.handle-size / 2.0; height: root.height - root.handle-size; } se := ResizeHandle { my-color: root.color; resize(x-offset, y-offset, done) => { self.new-width = Math.max(0px, x-offset + root.width); self.new-height = Math.max(0px, y-offset + root.height); self.new-x = root.left; self.new-y = root.top; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.se-resize; x: root.width - (root.handle-size / 2.0); y: root.height - (root.handle-size / 2.0); } s := ResizeHandle { show-handle: false; resize(x-offset, y-offset, done) => { self.new-width = root.width; self.new-height = Math.max(0px, y-offset + root.height); self.new-x = root.left; self.new-y = root.top; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.s-resize; x: root.handle-size / 2.0; y: root.height - (root.handle-size / 2.0); width: root.width - root.handle-size; } sw := ResizeHandle { my-color: root.color; resize(x-offset, y-offset, done) => { self.new-width = Math.max(0px, x-offset * -1.0 + root.width); self.new-height = Math.max(0px, y-offset + root.height); self.new-x = root.right - self.new-width; self.new-y = root.top; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.sw-resize; x: -(root.handle-size / 2.0); y: root.height - (root.handle-size / 2.0); } w := ResizeHandle { show-handle: false; resize(x-offset, y-offset, done) => { self.new-width = Math.max(0px, x-offset * -1.0 + root.width); self.new-height = root.height; self.new-x = root.right - self.new-width; self.new-y = root.top; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.w-resize; x: -(root.handle-size / 2.0); y: (root.handle-size / 2.0); height: root.height - root.handle-size; } nw := ResizeHandle { my-color: root.color; resize(x-offset, y-offset, done) => { self.new-width = Math.max(0px, x-offset * -1.0 + root.width); self.new-height = Math.max(0px, y-offset * -1.0 + root.height); self.new-x = root.right - self.new-width; self.new-y = root.bottom - self.new-height; root.resize(self.new-x, self.new-y, self.new-width, self.new-height, done); } mouse-cursor: MouseCursor.nw-resize; x: -(root.handle-size / 2.0); y: -(root.handle-size / 2.0); } } }