mirror of
https://github.com/slint-ui/slint.git
synced 2025-11-01 04:18:14 +00:00
1363 lines
40 KiB
Text
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; }
|
|
}
|
|
}
|
|
}
|
|
}
|