slint/tools/lsp/ui/components/selection-popup.slint

350 lines
14 KiB
Text

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { ListView, Palette } from "std-widgets.slint";
import { EditorFontSettings, EditorSizeSettings, EditorSpaceSettings, EditorPalette } from "./styling.slint";
import { Api, SelectionStackFrame } from "../api.slint";
component PopupInner inherits Rectangle {
in property <length> preview-width: 500px;
in property <length> preview-height: 900px;
private property <float> aspect-ratio: preview-width / preview-height;
in property <length> selection-x: 0px;
in property <length> selection-y: 0px;
in property <[SelectionStackFrame]> selection-stack: [
{
width: 100%,
height: 40%,
x: 10%,
y: 0%,
is-in-root-component: true,
is-layout: false,
is-interactive: true,
is-selected: false,
type-name: "TypeA",
file-name: "thumb.slint",
id: "some_a",
},
{
width: 50%,
height: 50%,
x: 0%,
y: 0%,
is-in-root-component: false,
is-layout: true,
is-interactive: false,
is-selected: false,
file-name: "thumb.slint",
type-name: "HorizontalLayout",
},
{
width: 50%,
height: 10%,
x: 20%,
y: 40%,
is-in-root-component: false,
is-layout: false,
is-interactive: false,
is-selected: false,
type-name: "Rectangle",
file-name: "tiling.slint",
id: "",
},
{
width: 50%,
height: 50%,
x: 50%,
y: 50%,
is-in-root-component: false,
is-interactive: false,
is-selected: true,
type-name: "TypeA",
file-name: "finger.slint",
id: "some_a",
},
{
width: 250%,
height: 50%,
x: 50%,
y: 50%,
is-in-root-component: false,
is-layout: false,
is-interactive: true,
is-selected: false,
type-name: "Button",
file-name: "finger.slint",
id: "alsoInteractive",
},
{
width: 50%,
height: 50%,
x: 0%,
y: 50%,
is-in-root-component: false,
is-layout: true,
is-interactive: false,
is-selected: false,
file-name: "finger.slint",
type-name: "VerticalLayout",
},
{
width: 5%,
height: 5%,
x: 25%,
y: 25%,
is-in-root-component: true,
is-layout: false,
is-interactive: false,
is-selected: false,
id: "words",
type-name: "Text",
},
{
width: 50%,
height: 100%,
x: 0%,
y: 0%,
is-in-root-component: true,
is-layout: false,
is-interactive: false,
is-selected: false,
type-name: "Window",
id: "",
}
];
private property <length> max-rect-size: 40px;
private property <length> frame-height: self.max-rect-size + EditorSpaceSettings.default-padding * 2;
private property <int> max-visible-frames: Math.floor(((900px / 1px) * 0.8) / (self.frame-height / 1px));
private property <int> visible-frames: Math.min(self.max-visible-frames, root.selection-stack.length);
border-radius: EditorSizeSettings.radius;
border-width: 0.5px;
background: Palette.background;
drop-shadow-blur: 10px;
drop-shadow-color: black;
border-color: lightgray;
width: 250px;
height: visible-frames * frame-height;
function select-frame(index: int) {
if index >= 0 {
list-view.viewport-y = (index - (self.visible-frames / 2)) * self.frame-height;
} else {
list-view.viewport-x = 0px;
}
}
list-view := ListView {
for frame[index] in root.selection-stack: frame-rect := Rectangle {
width: 100%;
if frame.is-selected: Rectangle {
background: Palette.accent-background;
init() => {
root.select-frame(index);
}
}
function frame-color(frame: SelectionStackFrame) -> brush {
return EditorPalette.interactive-element-selection-secondary;
}
function frame-background(frame: SelectionStackFrame) -> brush {
if frame.is-interactive {
return EditorPalette.interactive-element-selection-primary;
} else if frame.is-layout {
return transparent;
} else if frame.is-selected {
EditorPalette.general-element-selection-selected;
} else {
return EditorPalette.general-element-selection-primary;
}
}
function calculate_pos(p: length, percent: float) -> length {
return Math.round((p / 1px) * percent) * 1px;
}
function calculate_size(p: length, percent: float) -> length {
return Math.max(Math.round((p / 1px) * percent), 2) * 1px;
}
VerticalLayout {
padding-top: EditorSpaceSettings.default-padding / 2;
if !frame.is-in-root-component: Text {
x: EditorSpaceSettings.default-padding;
text: frame.file-name;
color: frame.is-selected ? Palette.accent-foreground : Palette.foreground;
font-size: EditorFontSettings.label-sub.font-size - 3px;
font-italic: true;
}
HorizontalLayout {
visible: (frame.type-name == "Window") ? false : true;
padding-left: EditorSpaceSettings.default-padding / 2.0;
spacing: EditorSpaceSettings.default-spacing / 2.0;
alignment: start;
VerticalLayout {
alignment: center;
padding-bottom: EditorSpaceSettings.default-padding / 2;
Rectangle {
function calculate_aspect_ratio_box(aspect-ratio: float, max-rect-length: length) -> length {
return Math.min(aspect-ratio, 1.0) * max-rect-length;
}
clip: true;
width: calculate_aspect_ratio_box(root.aspect-ratio, root.max-rect-size) + EditorSpaceSettings.default-padding;
height: calculate_aspect_ratio_box(1.0 / root.aspect-ratio, root.max-rect-size) + EditorSpaceSettings.default-padding / 2;
measure-rect := Rectangle {
width: calculate_aspect_ratio_box(root.aspect-ratio, root.max-rect-size);
height: calculate_aspect_ratio_box(1.0 / root.aspect-ratio, root.max-rect-size);
border-color: frame.is-selected ? EditorPalette.general-element-selection-selected.transparentize(0.5) : Palette.foreground.transparentize(0.65);
border-width: 1px;
placeholder-rect := Rectangle {
x: calculate_pos(measure-rect.width, frame.x);
y: calculate_pos(measure-rect.height, frame.y);
width: calculate_size(measure-rect.width, frame.width);
height: calculate_size(measure-rect.height, frame.height);
border-color: frame-rect.frame-color(frame);
border-width: 0.5px;
background: frame-rect.frame-background(frame);
if frame.is-layout: Rectangle {
border-color: EditorPalette.layout-element-selection-secondary;
border-width: 0.5px;
if (frame.type-name == "HorizontalLayout" || frame.type-name == "HorizontalBox"): HorizontalLayout {
spacing: 1px;
padding: 1px;
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
}
if (frame.type-name == "VerticalLayout" || frame.type-name == "VerticalBox"): VerticalLayout {
spacing: 1.5px;
padding: 1.5px;
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
}
if (frame.type-name == "GridLayout" || frame.type-name == "GridBox"): VerticalLayout {
spacing: 1px;
HorizontalLayout {
spacing: 1.5px;
padding: 1.5px;
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
}
HorizontalLayout {
spacing: 1.5px;
padding: 1.5px;
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
Rectangle {
background: EditorPalette.layout-element-selection-primary;
}
}
}
}
}
}
}
}
VerticalLayout {
padding-left: EditorSpaceSettings.default-padding;
spacing: EditorSpaceSettings.default-spacing / 2;
alignment: center;
if frame.id != "": Text {
text: frame.id;
color: frame.is-selected ? Palette.accent-foreground : Palette.foreground;
font-weight: EditorFontSettings.bold-font-weight;
font-size: EditorFontSettings.label.font-size;
}
Text {
text: frame.type-name + (frame.is-interactive ? " (TouchArea)" : "");
color: frame.is-selected ? Palette.accent-foreground : Palette.foreground;
font-size: EditorFontSettings.label-sub.font-size;
font-italic: true;
}
}
}
Rectangle {
height: 1px;
background: Palette.foreground.transparentize(0.9);
}
}
TouchArea {
clicked() => {
Api.select-element(frame.element-path, frame.element-offset, frame.x * root.preview-width, frame.y * root.preview-height);
}
}
}
}
}
export component SelectionPopup {
public function show-selection-stack(x: length, y: length) {
self.selection-x = x;
self.selection-y = y;
self.selection-stack = Api.selection-stack-at(self.selection-x, self.selection-y);
popup.show();
}
public function close-selection-stack() {
self.selection-x = 0;
self.selection-y = 0;
self.selection-stack = [];
popup.close();
}
in property <length> preview-width;
in property <length> preview-height;
private property <[SelectionStackFrame]> selection-stack;
private property <length> selection-x;
private property <length> selection-y;
popup := PopupWindow {
x: 20px;
y: 20px;
in property <length> preview-width: root.preview-width;
in property <length> preview-height: root.preview-height;
in property <[SelectionStackFrame]> selection-stack: root.selection-stack;
in property <length> selection-x: root.selection-x;
in property <length> selection-y: root.selection-y;
VerticalLayout {
inner := PopupInner {
preview-width <=> popup.preview-width;
preview-height <=> popup.preview-height;
selection-stack <=> popup.selection-stack;
selection-x <=> popup.selection-x;
selection-y <=> popup.selection-y;
}
}
}
}