// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 import { Palette } from "std-widgets.slint"; import { Api, BrushKind, ElementInformation, GradientStop, PreviewData, PreviewDataKind, PropertyContainer, PropertyInformation, PropertyValue, PropertyValueKind } from "../api.slint"; import { StatusLineApi } from "../components/status-line.slint"; import { EditorSpaceSettings } from "../components/styling.slint"; import { BooleanWidget } from "./widgets/boolean-widget.slint"; import { ConsoleStyles } from "./styling.slint"; import { CodeWidget } from "./widgets/code-widget.slint"; import { EnumWidget } from "./widgets/enum-widget.slint"; import { FloatWidget } from "./widgets/float-widget.slint"; import { IntegerWidget } from "./widgets/integer-widget.slint"; import { JsonWidget } from "./widgets/json-widget.slint"; import { MultiValueWidget } from "./widgets/multi-value-widget.slint"; import { StringWidget } from "./widgets/string-widget.slint"; import { BrushPropertyType, WindowGlobal, WindowManager } from "../windowglobal.slint"; import { InlineBrushWidget } from "widgets/inline-brush-widget.slint"; import { NameLabel, ResettingLineEdit } from "widgets/basics.slint"; export component PropertyValueWidget inherits VerticalLayout { in-out property property-value; in property preview-data; in property property-name; in property enabled; in property has-code-action: true; in property has-reset-action: true; in property strings-are-translatable: true; in property property-container-id; callback update-display-string(value: string); callback set-bool-binding(value: bool); callback set-color-binding(text: string); callback test-color-binding(text: string) -> bool; callback set-brush-binding(kind: BrushKind, angle: float, color: color, stops: [GradientStop]); callback test-brush-binding(kind: BrushKind, angle: float, color: color, stops: [GradientStop]) -> 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 set-current-item(); callback reset-action(); callback code-action(); function update-display-string-impl(value: string) { self.property-value.display-string = value; self.update-display-string(value); } if root.property-value.kind == PropertyValueKind.boolean: BooleanWidget { enabled <=> root.enabled; property-value <=> root.property-value; set-bool-binding(value) => { root.set-bool-binding(value); } update-display-string(value) => { root.update-display-string-impl(value); } } if root.property-value.kind == PropertyValueKind.color || root.property-value.kind == PropertyValueKind.brush: InlineBrushWidget { enabled <=> root.enabled; property-name <=> root.property-name; property-value <=> root.property-value; brush-property-type: root.property-value.kind == PropertyValueKind.color ? BrushPropertyType.color : BrushPropertyType.brush; preview-data <=> root.preview-data; update-floating-editor() => { return root.set-current-item(); } test-color-binding(text) => { return (root.test-color-binding(text)); } set-color-binding(text) => { root.set-color-binding(text); } test-brush-binding(kind, angle, color, stops) => { return root.test-brush-binding(kind, angle, color, stops); } set-brush-binding(kind, angle, color, stops) => { root.set-brush-binding(kind, angle, color, stops); } update-display-string(value) => { root.update-display-string-impl(value); } init => { // Floating editors need to know the current property details. Call the update if something changes. if WindowManager.showing-color-picker && WindowManager.current-property-information.name == root.property-name { self.update-floating-editor(); } } } if root.property-value.kind == PropertyValueKind.code: CodeWidget { enabled <=> root.enabled; property-value <=> root.property-value; update-display-string(value) => { root.update-display-string-impl(value); } reset-action() => { root.reset-action(); } code-action() => { root.code-action(); } } if root.property-value.kind == PropertyValueKind.enum: EnumWidget { enabled <=> root.enabled; property-value <=> root.property-value; set-enum-binding(text) => { root.set-enum-binding(text); } update-display-string(value) => { root.update-display-string-impl(value); } } if root.property-value.kind == PropertyValueKind.float: FloatWidget { enabled <=> root.enabled; 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); } update-display-string(value) => { root.update-display-string-impl(value); } } if root.property-value.kind == PropertyValueKind.integer: IntegerWidget { enabled <=> root.enabled; property-value <=> root.property-value; test-integer-binding(text) => { return (root.test-code-binding(text)); } set-integer-binding(text) => { root.set-code-binding(text); } update-display-string(value) => { root.update-display-string-impl(value); } } if root.property-value.kind == PropertyValueKind.string: StringWidget { enabled <=> root.enabled; 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; 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); } update-display-string(value) => { root.update-display-string-impl(value); } } } component IconButton inherits Rectangle { in property icon; in property checked: false; in property enabled: true; ta := TouchArea { clicked => root.clicked(); mouse-cursor: pointer; enabled: root.enabled; } callback clicked(); Image { height: 1rem; colorize: !enabled ? Palette.foreground.transparentize(0.8) : checked ? Palette.accent-background : ta.has-hover ? Palette.accent-background.transparentize(0.2) : Palette.foreground.transparentize(0.5); source: root.icon; } } enum PropertyWidgetMode { value, expression, bind } export component PropertyInformationWidget inherits TouchArea { in property property-information; in property element-information; private property kind: property-information.value.kind; changed kind => { if kind == PropertyValueKind.code { mode = PropertyWidgetMode.expression; } else { mode = PropertyWidgetMode.value; } } private property mode: kind == PropertyValueKind.code ? PropertyWidgetMode.expression : PropertyWidgetMode.value; function 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, ); } function reset-action() { Api.set-code-binding( element-information.source-uri, element-information.source-version, element-information.offset, property-information.name, "", ); } VerticalLayout { padding-bottom: EditorSpaceSettings.default-padding; padding-right: EditorSpaceSettings.default-padding * 2; spacing: EditorSpaceSettings.default-spacing / 2; HorizontalLayout { alignment: LayoutAlignment.space-between; NameLabel { property-name: property-information.name; property-value: property-information.value; TouchArea { double-clicked => { root.code-action(); } } } HorizontalLayout { opacity: root.has-hover ? 1 : 0; animate opacity { duration: 200ms; easing: ease-in-out-quad; } alignment: end; spacing: 4px; IconButton { icon: @image-url("../assets/constant.svg"); checked: mode == PropertyWidgetMode.value; enabled: kind != PropertyValueKind.code; clicked => { mode = PropertyWidgetMode.value; } } IconButton { checked: mode == PropertyWidgetMode.expression; icon: @image-url("../assets/code.svg"); clicked => { mode = PropertyWidgetMode.expression; } } IconButton { icon: @image-url("../assets/revert.svg"); enabled: property-information.value.code != ""; clicked => { root.reset-action(); } } } } if mode == PropertyWidgetMode.expression : HorizontalLayout { alignment: LayoutAlignment.stretch; spacing: 5px; IconButton { icon: @image-url("../assets/code.svg"); clicked => { root.code-action(); } } ResettingLineEdit { enabled: root.enabled; default-text: property-information.value.code; edited(text) => { self.can-compile = Api.test-code-binding( root.element-information.source-uri, root.element-information.source-version, root.element-information.offset, root.property-information.name, text, ); } accepted(text) => { Api.set-code-binding( root.element-information.source-uri, root.element-information.source-version, root.element-information.offset, root.property-information.name, text, ); } } } if mode == PropertyWidgetMode.value : PropertyValueWidget { property-value: root.property-information.value; property-name: root.property-information.name; enabled: root.enabled; set-current-item() => { WindowManager.show-floating-widget(property-information, element-information); } set-bool-binding(value) => { self.set-code-binding(value ? "true" : "false"); } set-brush-binding(kind, angle, color, stops) => { self.set-code-binding(Api.as-slint-brush(kind, angle, color, stops)); } test-brush-binding(kind, angle, color, stops) => { return self.test-code-binding(Api.as-slint-brush(kind, angle, color, stops)); } 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.offset, 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.offset, 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.offset, 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.offset, root.property-information.name, text)); } reset-action() => { root.reset-action(); } code-action() => { root.code-action(); } } } } export component PreviewDataPropertyValueWidget inherits VerticalLayout { in property preview-data; in property property-container-id; private property value: Api.get-property-value(root.property-container-id, root.preview-data.name); private property possible-error; callback edit-in-table-editor(property-group-id: string, data: PreviewData); 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, check-new-value(root.property-container-id + root.preview-data.name)); StatusLineApi.help-text = self.possible-error; return (self.possible-error == ""); } property container-property-name; function check-new-value(value: string) -> bool { if container-property-name == value { return false; } else { container-property-name = value; return true; } } padding-bottom: EditorSpaceSettings.default-padding; padding-right: EditorSpaceSettings.default-padding * 2; spacing: EditorSpaceSettings.default-spacing / 2; NameLabel { property-name: root.preview-data.name; property-value: root.value; } if root.preview-data.kind == PreviewDataKind.Value: PropertyValueWidget { property-value <=> root.value; property-name: root.preview-data.name; enabled: root.preview-data.has-setter; property-container-id <=> root.property-container-id; preview-data <=> root.preview-data; strings-are-translatable: false; has-code-action: false; has-reset-action: false; set-current-item() => { WindowManager.show-floating-preview-widget(property-container-id, preview-data, root.value); } set-bool-binding(value) => { self.set-code-binding(value ? "true" : "false"); } set-brush-binding(kind, angle, color, stops) => { self.set-code-binding(Api.as-json-brush(kind, angle, color, stops)); } test-brush-binding(kind, angle, color, stops) => { return self.test-code-binding(Api.as-json-brush(kind, angle, color, stops)); } 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: MultiValueWidget { property-value <=> root.value; preview-data: root.preview-data; property-group-id: root.property-container-id; edit-in-table-editor(property-group-id, data) => { root.edit-in-table-editor(property-group-id, data); } } if root.preview-data.kind == PreviewDataKind.Json: JsonWidget { enabled: root.preview-data.has-setter; property-value <=> root.value; set-code-binding(text) => { return (root.set-code-binding(text)); } } }