// 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); // 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; in property fill-color: Colors.white; 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; out property has-hover <=> ta.has-hover; 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 clicked(x: length, y: length, modifiers: KeyboardModifiers); callback double-clicked(x: length, y: length, modifiers: KeyboardModifiers); callback pointer-event(x: length, y: length, event: PointerEvent); callback scroll-event(event: PointerScrollEvent) -> EventResult; // 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 ta := TouchArea { enabled: root.is-moveable; 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: false; private property process-moves: false; private property can-move-to: false; mouse-cursor: MouseCursor.move; double-clicked() => { root.double-clicked(self.mouse-x, self.mouse-y, self.modifiers); } clicked() => { root.clicked(self.mouse-x, self.mouse-y, self.modifiers); } moved() => { if self.process-moves { 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 event.button == PointerEventButton.left && event.kind == PointerEventKind.down { self.process-moves = true; } else 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; self.process-moves = false; } else { root.pointer-event(self.mouse-x, self.mouse-y, event); } } scroll-event(event) => { return root.scroll-event(event); } 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; background: root.fill-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; background: root.fill-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; background: root.fill-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; background: root.fill-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); } } }