slint/tools/lsp/ui/components/property-widgets.slint
Tobias Hunger b706e7a019 live-preview: Add EditMultiValueWidget
back in. It should open a spreadsheet view for
table-like data in the live data preview.
2025-03-06 09:02:33 +01:00

1363 lines
40 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 { Button, CheckBox, ComboBox, LineEdit, Palette, Slider, TextEdit } from "std-widgets.slint";
import { Api, ColorData, ElementInformation, PreviewData, PreviewDataKind, PropertyContainer, PropertyInformation, PropertyValue, PropertyValueKind } from "../api.slint";
import { BodyStrongText } from "../components/body-strong-text.slint";
import { BodyText } from "../components/body-text.slint";
import { StateLayer } from "../components/state-layer.slint";
import { StatusLineApi } from "../components/status-line.slint";
import { EditorAnimationSettings, EditorFontSettings, EditorSizeSettings, EditorSpaceSettings, Icons } from "../components/styling.slint";
component CodeButton inherits Button {
text: @tr("Code");
}
component ResetButton inherits Button {
text: @tr("Reset");
}
component NameLabel inherits HorizontalLayout {
in property <string> property-name;
in property <PropertyValue> property-value;
horizontal-stretch: 0;
BodyText {
min-width: EditorSizeSettings.min-prefix-text-width;
height: root.property-name != "" ? self.preferred-height : 0;
text: root.property-name;
font-size: 1rem;
font-weight: root.property-value.code != "" ? EditorFontSettings.bold-font-weight : EditorFontSettings.light-font-weight;
font-italic: root.property-value.code == "";
overflow: elide;
}
}
component ResettingLineEdit {
in property <string> default-text;
in-out property <bool> can-compile: true;
in property <bool> enabled;
in property <InputType> input-type <=> le.input-type;
in property <TextHorizontalAlignment> horizontal-alignment <=> le.horizontal-alignment;
in property <string> placeholder-text <=> le.placeholder-text;
out property <bool> has-focus <=> le.has-focus;
in-out property <string> text <=> le.text;
property <length> border: 3px;
callback accepted <=> le.accepted;
callback edited <=> le.edited;
changed default-text => {
if !le.has-focus {
le.text = root.default-text;
}
}
VerticalLayout {
le := LineEdit {
enabled: root.enabled;
text: root.default-text;
font-size: 1rem;
// Reset on focus loss:
changed has-focus => {
if !self.has_focus && text != default-text {
if root.can-compile {
root.accepted(self.text);
}
} else {
// self.text = root.default-text;
root.can-compile = true;
}
}
}
}
Rectangle {
visible: !root.can-compile;
background: Colors.red.transparentize(0.94);
x: root.border;
y: root.border;
width: root.width - 2 * root.border;
height: root.height - 2 * root.border;
border-radius: root.border;
}
}
component ChildIndicator inherits Rectangle {
out property <bool> open: false;
in property <bool> control-hover: false;
width: 16px;
indicator := Image {
vertical-alignment: center;
colorize: Palette.foreground;
visible: expand.has-hover || root.control-hover;
source: Icons.chevron-down;
rotation-angle: root.open ? 0deg : -90deg;
}
expand := TouchArea {
width: 1cm;
height: 1cm;
clicked => {
root.open = !root.open;
}
}
}
component CodeWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
callback code-action();
callback reset-action();
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
visible: false;
}
if property-value.code == "": Text {
horizontal-alignment: left;
vertical-alignment: center;
text: @tr("Not Set");
font-italic: true;
}
if property-value.code != "": HorizontalLayout {
spacing: EditorSpaceSettings.default-spacing;
ResetButton {
enabled: root.enabled;
clicked() => {
root.reset-action();
}
}
CodeButton {
enabled: root.enabled;
clicked() => {
root.code-action();
}
}
}
}
}
component SecondaryContent inherits Rectangle {
in property <bool> enabled;
in property <bool> open: false;
in property <bool> has-code-action: true;
in property <bool> has-reset-action: true;
callback reset();
callback code-action();
callback reset-action();
background: Palette.background;
clip: true;
height: open ? content.preferred-height : 0px;
animate height { duration: EditorAnimationSettings.rotation-duration; }
content := HorizontalLayout {
Rectangle {
height: 100%;
width: 1px;
background: Palette.border;
}
VerticalLayout {
padding: EditorSpaceSettings.default-padding;
spacing: EditorSpaceSettings.default-padding;
@children
HorizontalLayout {
spacing: EditorSpaceSettings.default-spacing;
ResetButton {
height: root.has-reset-action ? self.preferred-height : 0px;
enabled: root.enabled;
clicked() => {
root.reset-action();
root.reset();
}
}
CodeButton {
height: root.has-code-action ? self.preferred-height : 0px;
enabled: root.enabled;
clicked() => {
root.code-action();
}
}
}
}
}
}
component FloatWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
callback test-float-binding(text: string, unit: string) -> bool;
callback set-float-binding(text: string, unit: string);
private property <string> current-unit;
private property <PropertyValue> dummy: self.property-value;
pure function find_current_unit(value: PropertyValue) -> string {
if value.visual-items.length == 0 {
return "";
}
return value.visual-items[self.find-current-index(value)];
}
pure function find_current_index(value: PropertyValue) -> int {
return value.code == "" ? value.default-selection : value.value-int;
}
function set-binding() {
root.set-float-binding(number.text == "" ? "" : number.text, self.current-unit);
}
function test-binding() -> bool {
return root.test-float-binding(number.text == "" ? "" : number.text, self.current-unit);
}
function apply-value() {
number.default-text = self.property-value.value-float;
current-unit = find_current_unit(property-value);
}
changed dummy => {
if !number.has-focus && self.property-value.kind == PropertyValueKind.float {
apply-value();
}
}
init => {
apply-value();
}
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
horizontal-stretch: 0;
visible: false;
}
HorizontalLayout {
spacing: EditorSpaceSettings.default-spacing;
alignment: stretch;
number := ResettingLineEdit {
enabled: root.enabled;
horizontal-alignment: right;
min-width: EditorSizeSettings.float-size;
horizontal-stretch: 0;
default-text: property-value.value-float;
edited(text) => {
self.can-compile = root.test-binding();
}
accepted(text) => {
root.set-binding();
}
changed has-focus => {
if !self.has-focus {
root.apply-value();
}
}
}
if property-value.visual-items.length > 1: ComboBox {
enabled: root.enabled;
horizontal-stretch: 0;
min-width: EditorSizeSettings.length-combo;
model: property-value.visual-items;
current-index: root.find_current_index(root.property-value);
selected(unit) => {
root.current-unit = unit;
root.set-binding();
}
}
if property-value.visual-items.length == 1: Text {
text: property-value.visual-items[0];
vertical-alignment: center;
changed text => {
root.current-unit = self.text;
}
}
}
}
}
component StringWidget inherits VerticalLayout {
// FIXME: @ogoffart says the plural support is not working at this time, so
// we do not offer it in the UI for the time being...
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
in property <bool> has-code-action;
in property <bool> has-reset-action;
in property <bool> is-translatable: true;
out property <length> i-am-a-hack-remove-me: 0px;
property <bool> open: false;
private property <PropertyValue> dummy: self.property-value;
private property <bool> is-translated;
private property <string> tr-context-value;
callback code-action();
callback reset-action();
callback test-string-binding(text: string, is-translated: bool) -> bool;
callback set-string-binding(text: string, is-translated: bool);
function tsb() -> bool {
return test-string-binding(Api.string-to-code(text-rle.text, self.is-translated, self.tr-context-value, "", ""), self.is-translated);
}
function ssb() {
set-string-binding(Api.string-to-code(text-rle.text, self.is-translated, self.tr-context-value, "", ""), self.is-translated);
}
function apply-value() {
text_rle.default-text = property-value.value-string;
self.is-translated = root.property-value.is-translatable;
self.tr-context-value = root.property-value.tr-context;
}
init => {
self.i-am-a-hack-remove-me = self.preferred-height;
apply-value();
}
private property <bool> child-focus: false;
private property <bool> has-focus: text_rle.has-focus || self.child-focus;
changed has-focus => {
if !has-focus {
apply-value();
}
}
changed dummy => {
if !has-focus {
apply-value();
}
}
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
visible: root.is-translatable;
horizontal-stretch: 0;
control-hover: text_rle.has-focus;
}
content := HorizontalLayout {
spacing: EditorSpaceSettings.default-spacing;
text_rle := ResettingLineEdit {
enabled: root.enabled;
edited(text) => {
self.can-compile = root.tsb();
}
accepted(text) => {
root.ssb();
}
}
}
}
if root.is-translatable: HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
sub := SecondaryContent {
enabled: root.enabled;
open: childIndicator.open;
has-code-action <=> root.has-code-action;
has-reset-action <=> root.has-reset-action;
code-action() => {
root.code-action();
}
reset-action => {
root.reset-action();
}
VerticalLayout {
spacing: EditorSpaceSettings.default-spacing;
tr-cb := CheckBox {
checked: root.is-translated;
toggled => {
root.is-translated = self.checked;
root.ssb();
}
enabled: root.enabled;
text: "Translatable";
}
HorizontalLayout {
spacing: EditorSpaceSettings.default-spacing;
Text {
vertical-alignment: center;
horizontal-alignment: right;
text: "Context";
}
tr-context := ResettingLineEdit {
enabled: root.enabled && tr-cb.checked;
default-text: root.tr-context-value;
edited(text) => {
root.tr-context-value = text;
self.can-compile = root.tsb();
}
accepted(text) => {
root.tr-context-value = text;
root.ssb();
}
changed has-focus => {
root.child-focus = self.has-focus;
}
}
}
}
}
}
}
component ColorLineEdit inherits HorizontalLayout {
in property <bool> enabled;
in property <string> channel: "R";
in-out property <float> value;
callback accepted();
changed value => {
num-value.default-text = Math.floor(root.value);
num-value.text = Math.floor(root.value);
}
init => {
num-value.default-text = Math.floor(root.value);
num-value.text = Math.floor(root.value);
}
out property <bool> has-focus: slide-value.has-focus || num-value.has-focus;
spacing: EditorSpaceSettings.default-spacing;
Text {
text: channel;
vertical-alignment: center;
color: Palette.foreground;
}
slide-value := Slider {
enabled: root.enabled;
minimum: 0;
maximum: 255;
step: 1;
value <=> root.value;
horizontal-stretch: 100;
}
num-value := ResettingLineEdit {
enabled: root.enabled;
input-type: number;
width: 5rem;
private property <float> test-value;
edited() => {
self.test-value = self.text.to-float();
if Math.clamp(Math.floor(self.test-value), 0.0, 255.0) == self.text.to-float() {
root.value = Math.floor(self.test-value);
} else {
self.text = self.test-value
}
}
accepted() => {
root.accepted();
}
}
}
component ColorWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
in property <bool> has-code-action <=> sub.has-code-action;
in property <bool> has-reset-action <=> sub.has-reset-action;
private property <bool> open: false;
private property <PropertyValue> dummy: property-value; // Needed to make changed signal work...
callback code-action();
callback reset-action();
callback test-color-binding(text: string) -> bool;
callback set-color-binding(text: string);
private property <color> current-color;
private property <ColorData> current-color-data: Api.color-to-data(self.current-color);
changed current-color-data => {
root.current-color-data = Api.color-to-data(self.current-color);
if self.has-focus {
root.test-color-binding(current-color-data.text);
}
rle.default-text = current-color-data.text;
r.value = self.current-color-data.r;
g.value = self.current-color-data.g;
b.value = self.current-color-data.b;
a.value = self.current-color-data.a;
}
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
function apply-value() {
root.current-color = self.property-value.value-brush;
root.current-color-data = Api.color-to-data(self.property-value.value-brush);
}
private property <bool> has-focus: rle.has-focus || r.has-focus || g.has-focus || b.has-focus || a.has-focus;
init => {
apply-value();
}
changed has-focus => {
if !self.has-focus {
apply-value();
}
}
changed dummy => {
if self.property-value.kind != PropertyValueKind.color {
return;
}
if !self.has-focus {
apply-value();
}
}
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
horizontal-stretch: 0;
control-hover: rle.has-focus;
}
content := HorizontalLayout {
spacing: EditorSpaceSettings.default-spacing;
alignment: stretch;
rle := ResettingLineEdit {
enabled: root.enabled;
default-text: root.current-color-data.text;
edited(text) => {
if text == "" {
// allow empty text -- which will delete the property
self.can-compile = true;
root.current-color = Colors.transparent;
root.current-color-data = Api.color-to-data(root.current-color);
} else {
if self.can-compile {
root.current-color = Api.string-to-color(text);
root.current-color-data = Api.color-to-data(root.current-color);
}
}
}
accepted(text) => {
root.set-color-binding(text);
}
}
color-preview := Rectangle {
background: root.current-color;
border-width: 1px;
border-color: Palette.foreground;
}
}
}
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
sub := SecondaryContent {
private property <color> slider-color: Api.rgba-to-color(r.value, g.value, b.value, a.value);
enabled: root.enabled;
changed slider-color => {
if r.has-focus || g.has-focus || b.has-focus || a.has-focus {
root.current-color = slider-color;
root.current-color-data = Api.color-to-data(self.slider-color);
}
}
open: childIndicator.open;
r := ColorLineEdit {
enabled: root.enabled;
value: root.current-color-data.r;
channel: "R";
accepted() => {
root.set-color-binding(rle.text);
}
}
g := ColorLineEdit {
enabled: root.enabled;
value: root.current-color-data.g;
channel: "G";
accepted() => {
root.set-color-binding(rle.text);
}
}
b := ColorLineEdit {
enabled: root.enabled;
value: root.current-color-data.b;
channel: "B";
accepted() => {
root.set-color-binding(rle.text);
}
}
a := ColorLineEdit {
enabled: root.enabled;
value: root.current-color-data.a;
channel: "A";
accepted() => {
root.set-color-binding(rle.text);
}
}
code-action() => {
root.code-action();
}
reset-action() => {
root.reset-action();
}
}
}
}
component IntegerWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
callback test-integer-binding(text: string) -> bool;
callback set-integer-binding(text: string);
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
horizontal-stretch: 0;
visible: false;
}
ResettingLineEdit {
enabled: root.enabled;
horizontal-alignment: right;
input-type: number;
default-text: property-value.value-int;
edited(text) => {
self.can-compile = test-integer-binding(text);
}
accepted(text) => {
set-integer-binding(text);
}
}
}
}
component EnumWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
private property <PropertyValue> dummy: self.property-value;
changed dummy => {
if self.property-value.kind == PropertyValueKind.enum {
cb.current-index = root.property-value.value-int;
}
}
callback set-enum-binding(text: string);
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
horizontal-stretch: 0;
visible: false;
}
HorizontalLayout {
cb := ComboBox {
enabled: root.enabled;
current-index: root.property-value.value-int;
model: root.property-value.visual-items;
selected(value) => {
root.set-enum-binding(property-value.value-string + "." + value);
}
}
}
}
}
component BooleanWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
callback set-bool-binding(value: bool);
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
horizontal-stretch: 0;
visible: false;
}
checkbox := CheckBox {
enabled: root.enabled;
checked: root.property-value.value-bool;
text: self.checked ? "True" : "False";
toggled() => {
root.set-bool-binding(self.checked);
}
}
}
}
component EditMultiValueWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
in property <PreviewData> preview-data;
in property <string> property-group-name;
callback edit-in-spreadsheet(property-group-name: string, data: PreviewData, values: [[PropertyValue]]);
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
Button {
text: preview-data.has-setter ? @tr("Edit") : @tr("View");
clicked => {
root.edit-in-spreadsheet(root.property-group-name, root.preview-data, [[ ]]);
}
}
}
component EditJsonWidget inherits VerticalLayout {
in property <bool> enabled;
in property <string> property-name;
in property <PropertyValue> property-value;
property <bool> can-compile: true;
property <length> border: 3px;
callback set-code-binding(text: string) -> bool;
spacing: EditorSpaceSettings.default-spacing;
width: 100%;
function apply-value() {
edit.text = root.property-value.code;
}
init => {
apply-value();
}
private property <bool> has-focus: edit.has-focus;
changed has-focus => {
if !has-focus {
apply-value();
}
}
changed property-value => {
if !has-focus {
apply-value();
}
}
HorizontalLayout {
Rectangle {
width: childIndicator.width;
}
NameLabel {
property-name: root.property-name;
property-value: root.property-value;
}
}
HorizontalLayout {
childIndicator := ChildIndicator {
horizontal-stretch: 0;
visible: false;
}
Rectangle {
VerticalLayout {
edit := TextEdit {
min-height: 200px;
enabled: root.enabled;
edited(text) => {
root.can-compile = root.set-code-binding(text);
}
changed has-focus => {
self.text = root.property-value.code;
root.can-compile = true;
}
}
}
Rectangle {
visible: !root.can-compile;
background: Colors.red.transparentize(0.94);
x: edit.x + root.border;
y: edit.y + root.border;
width: edit.width - 2 * root.border;
height: edit.height - 2 * root.border;
border-radius: root.border;
}
}
}
}
export component PropertyValueWidget inherits VerticalLayout {
in property <PropertyValue> property-value;
in property <string> property-name;
in property <bool> enabled;
in property <bool> has-code-action: true;
in property <bool> has-reset-action: true;
in property <bool> strings-are-translatable: true;
callback set-bool-binding(value: bool);
callback set-color-binding(text: string);
callback test-color-binding(text: string) -> bool;
callback set-float-binding(text: string, unit: string);
callback test-float-binding(text: string, unit: string) -> bool;
callback set-code-binding(text: string);
callback test-code-binding(text: string) -> bool;
callback set-string-binding(text: string, is_translated: bool);
callback test-string-binding(text: string, is_translated: bool) -> bool;
callback set-enum-binding(text: string);
callback reset-action();
callback code-action();
VerticalLayout {
Rectangle {
clip: true;
height: root.property-value.kind == PropertyValueKind.boolean ? self.preferred-height : 0px;
BooleanWidget {
enabled <=> root.enabled;
property-name <=> root.property-name;
property-value <=> root.property-value;
set-bool-binding(value) => {
root.set-bool-binding(value);
}
}
}
Rectangle {
clip: true;
height: (root.property-value.kind == PropertyValueKind.color) || (root.property-value.kind == PropertyValueKind.brush) ? self.preferred-height : 0px;
ColorWidget {
enabled <=> root.enabled;
property-name <=> root.property-name;
property-value <=> root.property-value;
has-code-action: root.has-code-action;
has-reset-action: root.has-reset-action;
test-color-binding(text) => {
return (root.test-color-binding(text));
}
set-color-binding(text) => {
root.set-color-binding(text);
}
reset-action() => {
root.reset-action();
}
code-action() => {
root.code-action();
}
}
}
Rectangle {
clip: true;
height: root.property-value.kind == PropertyValueKind.code ? self.preferred-height : 0px;
CodeWidget {
enabled <=> root.enabled;
property-name <=> root.property-name;
property-value <=> root.property-value;
reset-action() => {
root.reset-action();
}
code-action() => {
root.code-action();
}
}
}
Rectangle {
clip: true;
height: root.property-value.kind == PropertyValueKind.enum ? self.preferred-height : 0px;
EnumWidget {
enabled <=> root.enabled;
property-name <=> root.property-name;
property-value <=> root.property-value;
set-enum-binding(text) => {
root.set-enum-binding(text);
}
}
}
Rectangle {
clip: true;
height: root.property-value.kind == PropertyValueKind.float ? self.preferred-height : 0px;
FloatWidget {
enabled <=> root.enabled;
property-name <=> root.property-name;
property-value <=> root.property-value;
test-float-binding(text, unit) => {
return (root.test-float-binding(text, unit));
}
set-float-binding(text, unit) => {
root.set-float-binding(text, unit);
}
}
}
Rectangle {
clip: true;
height: root.property-value.kind == PropertyValueKind.integer ? self.preferred-height : 0px;
IntegerWidget {
enabled <=> root.enabled;
property-name <=> root.property-name;
property-value <=> root.property-value;
test-integer-binding(text) => {
return (root.test-code-binding(text));
}
set-integer-binding(text) => {
root.set-code-binding(text);
}
}
}
Rectangle {
clip: true;
height: root.property-value.kind == PropertyValueKind.string ? self.preferred-height : 0px;
StringWidget {
enabled <=> root.enabled;
property-name <=> root.property-name;
property-value: root.property-value;
has-code-action: root.has-code-action;
has-reset-action: root.has-reset-action;
is-translatable <=> root.strings-are-translatable;
reset-action() => {
root.reset-action();
}
code-action() => {
root.code-action();
}
test-string-binding(text, is_translated) => {
return root.test-string-binding(text, is_translated);
}
set-string-binding(text, is_translated) => {
root.set-string-binding(text, is_translated);
}
}
}
}
}
export component PropertyInformationWidget inherits VerticalLayout {
in property <PropertyInformation> property-information;
in property <ElementInformation> element-information;
in property <bool> enabled;
PropertyValueWidget {
property-value: root.property-information.value;
property-name: root.property-information.name;
enabled: root.enabled;
set-bool-binding(value) => {
self.set-code-binding(value ? "true" : "false");
}
set-color-binding(text) => {
self.set-code-binding(text);
}
test-color-binding(text) => {
return (Api.string-is-color(text));
}
set-enum-binding(text) => {
self.set-code-binding(text);
}
test-float-binding(text, unit) => {
return (self.test-code-binding(text + unit));
}
set-float-binding(text, unit) => {
self.set-code-binding(text + unit);
}
set-code-binding(text) => {
Api.set-code-binding(
element-information.source-uri,
element-information.source-version,
element-information.range.start,
property-information.name,
text,
);
}
test-code-binding(text) => {
return (Api.test-code-binding(
root.element-information.source-uri,
root.element-information.source-version,
root.element-information.range.start,
root.property-information.name,
text,
));
}
set-string-binding(text, is-translated) => {
Api.set-code-binding(
element-information.source-uri,
element-information.source-version,
element-information.range.start,
property-information.name,
text);
}
test-string-binding(text, is-translated) => {
return (Api.test-code-binding(
root.element-information.source-uri,
root.element-information.source-version,
root.element-information.range.start,
root.property-information.name,
text));
}
reset-action() => {
Api.set-code-binding(
element-information.source-uri,
element-information.source-version,
element-information.range.start,
property-information.name,
"",
);
}
code-action() => {
Api.show-document-offset-range(
element-information.source-uri,
Api.property-declaration-ranges(property-information.name).defined-at.expression-range.start,
Api.property-declaration-ranges(property-information.name).defined-at.expression-range.start,
true,
);
}
}
}
export component PreviewDataPropertyValueWidget inherits VerticalLayout {
in property <PreviewData> preview-data;
in property <string> property-container-id;
private property <PropertyValue> value: Api.get-property-value(root.property-container-id, root.preview-data.name);
private property <string> possible-error;
callback edit-in-spreadsheet(rp: PropertyContainer);
function reset-action() {
self.set-code-binding(self.value.code);
}
function set-code-binding(text: string) -> bool {
self.possible_error = Api.set-json-preview-data(root.property-container-id, root.preview-data.name, text);
StatusLineApi.help-text = self.possible-error;
return (self.possible-error == "");
}
if root.preview-data.kind == PreviewDataKind.Value: PropertyValueWidget {
property-value <=> root.value;
property-name: root.preview-data.name;
enabled: root.preview-data.has-setter;
strings-are-translatable: false;
has-code-action: false;
has-reset-action: false;
set-bool-binding(value) => {
self.set-code-binding(value ? "true" : "false");
}
set-color-binding(text) => {
self.set-code-binding("\"\{text}\"");
}
test-color-binding(text) => {
return (root.set-code-binding("\"\{text}\""));
}
set-float-binding(text, _unit) => {
self.set-code-binding("\{text}");
}
test-float-binding(text, _unit) => {
return (root.set-code-binding("\{text}"));
}
set-enum-binding(text) => {
self.set-code-binding("\"\{text}\"");
}
set-code-binding(text) => {
root.set-code-binding(text);
}
test-code-binding(text) => {
return (root.set-code-binding(text));
}
set-string-binding(text, is_translated) => {
self.test-string-binding(text, is_translated);
}
test-string-binding(text, is_translated) => {
return(root.set-code-binding(is_translated ? "\"\{text}\"" : text));
}
}
if root.preview-data.kind == PreviewDataKind.Table: EditMultiValueWidget {
edit-in-spreadsheet(property-group-name, data, values) => {
debug("edit-in-spreadsheet TRIGGERED: ", property-group-name, data, values);
}
}
if root.preview-data.kind == PreviewDataKind.Json: EditJsonWidget {
enabled: root.preview-data.has-setter;
property-name: root.preview-data.name;
property-value <=> root.value;
set-code-binding(text) => {
return (root.set-code-binding(text));
}
}
}
export component ExpandableGroup {
in property <bool> enabled;
in property <string> text;
in property <length> panel-width;
property <bool> open: true;
group-layer := Rectangle {
content-layer := VerticalLayout {
if text != "": Rectangle {
touch-area := TouchArea {
clicked => {
root.open = !root.open;
}
}
state-layer := StateLayer {
width: panel-width;
height: group-layer.height;
y: group-layer.y;
has-hover: touch-area.has-hover;
pressed: touch-area.pressed;
}
HorizontalLayout {
spacing: EditorSpaceSettings.default-spacing / 2;
height: t.preferred-height;
icon-image := Image {
width: EditorSizeSettings.default-icon-width;
colorize: Palette.alternate-foreground.transparentize(0.7);
source: Icons.chevron-down;
rotation-origin-x: self.width / 2;
rotation-origin-y: self.height / 2;
states [
closed when !root.open: {
rotation-angle: -0.25turn;
}
]
animate rotation-angle { duration: EditorAnimationSettings.rotation-duration; }
}
t := BodyStrongText {
text: root.text;
}
}
}
Rectangle {
height: root.open ? self.preferred-height : 0px;
clip: true;
@children
animate height { duration: 200ms; }
}
}
}
}