// 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, PreviewData, PreviewDataKind, PropertyValue, PropertyValueKind, PropertyValueTable } from "../api.slint"; import { StatusLineApi } from "../components/status-line.slint"; import { EditorSizeSettings, Icons, EditorSpaceSettings } from "../components/styling.slint"; import { PropertyValueWidget } from "../components/property-widgets.slint"; export struct CellData { property-group-id: string, property-name: string, row: int, col: int, x: length, y: length, width: length, height: length} export component EditWindow inherits PopupWindow { close-policy: close-on-click-outside; callback test(string) -> bool; callback save(string); callback close-editor(); in property current-cell-data; in-out property current-table; content := Rectangle { changed height => { parent.height = self.height; } drop-shadow-blur: 10px; drop-shadow-color: black.transparentize(0.5); drop-shadow-offset-x: 0; drop-shadow-offset-y: 0; border-radius: EditorSizeSettings.radius; width: self.preferred-width; height: self.preferred-height; background: Palette.alternate-background; hl := HorizontalLayout { padding: EditorSpaceSettings.default-padding; padding-left: EditorSpaceSettings.default-padding; spacing: EditorSpaceSettings.default-spacing; PropertyValueWidget { property-value: root.current-table.values[root.current-cell-data.row][root.current-cell-data.col]; property-name: root.current-table.headers[root.current-cell-data.col]; enabled: true; changed property-value => { root.current-table.values[root.current-cell-data.row][root.current-cell-data.col] = self.property-value; } update-display-string(value) => { root.current-table.values[root.current-cell-data.row][root.current-cell-data.col].display-string = value; } test-color-binding(text) => { return root.test("\"\{text}\""); } test-brush-binding(kind, angle, color, stops) => { return root.test(Api.as-json-brush(kind, angle, color, stops)); } test-float-binding(text, unit) => { return root.test(text); } test-code-binding(text) => { return root.test(text); } test-string-binding(text, is_translated) => { return root.test(is_translated ? "\"\{text}\"" : text); } set-bool-binding(value) => { debug("set-bool-binding"); root.save(value ? "true" : "false"); } set-color-binding(text) => { debug("set-color-binding"); root.save("\"\{text}\""); } set-brush-binding(kind, angle, color, stops) => { debug("set-brush-binding"); root.save(Api.as-json-brush(kind, angle, color, stops)); } set-float-binding(text, _unit) => { debug("set-float-binding"); root.save(text); } set-code-binding(text) => { debug("set-code-binding"); root.save(text); } set-string-binding(text, is_translated) => { debug("set-string-binding"); root.save(is_translated ? "\"\{text}\"" : text); } set-enum-binding(text) => { debug("set-enum-binding"); root.save("\"\{text}\""); } } } } } component RowMarker inherits Rectangle { in property value; in property header: false; in property hovered; in property show-menu: true; width: 30px; if (value > 0): Rectangle { height: 30px; background: header ? transparent : Palette.alternate-background; VerticalLayout { HorizontalLayout { Text { vertical-alignment: center; horizontal-alignment: right; text: value; } VerticalLayout { alignment: LayoutAlignment.center; padding: EditorSpaceSettings.default-padding / 4; indicator := Image { visible: hovered && show-menu; colorize: Palette.foreground; source: Icons.chevron-down; width: 10px; height: 10px; } } } Rectangle { height: 1px; background: Palette.border; width: 100%; } } } } component Cell inherits Rectangle { in property property-value; in property is-header: false; in property is-writeable: false; in property text; in-out property bg-color: cell-clicked ? Palette.accent-background.transparentize(0.8) : transparent; private property cell-clicked: false; callback edit-clicked(); width: 100px; height: 30px; border-width: 1px; border-color: Palette.border; background: is-header ? Palette.foreground.transparentize(0.9) : bg-color; HorizontalLayout { Rectangle { width: EditorSpaceSettings.default-padding / 2; } Text { font-weight: is-header ? 700 : 400; width: root.width - EditorSpaceSettings.default-padding; height: root.height; horizontal-alignment: TextHorizontalAlignment.left; vertical-alignment: center; overflow: TextOverflow.elide; text: root.text; } } if is-writeable: TouchArea { clicked => { root.edit-clicked(); } } } export component Spreadsheet { in property property-group-id; in property preview-data: { name: "Addresses", has-getter: true, has-setter: true, kind: PreviewDataKind.Table, }; in-out property current-table: { is-array: true, headers: ["type", "street", "city", "state", "zip", "favorite", "color"], values: [ [ { kind: PropertyValueKind.string, display-string: "home" }, { kind: PropertyValueKind.string, display-string: "123 Oak Lane" }, { kind: PropertyValueKind.string, display-string: "Richmond" }, { kind: PropertyValueKind.string, display-string: "VA" }, { kind: PropertyValueKind.string, display-string: "23226" }, { kind: PropertyValueKind.boolean, value-bool: true, display-string: "true" }, { kind: PropertyValueKind.color, display-string: "#ff0000" } ], [ { kind: PropertyValueKind.string, display-string: "work" }, { kind: PropertyValueKind.string, display-string: "456 Corporate Blvd" }, { kind: PropertyValueKind.string, display-string: "Richmond" }, { kind: PropertyValueKind.string, display-string: "VA" }, { kind: PropertyValueKind.string, display-string: "23219" }, { kind: PropertyValueKind.boolean, value-bool: false, display-string: "false" } ], [ { kind: PropertyValueKind.string, display-string: "world" }, { kind: PropertyValueKind.string, display-string: "456 Corporate Blvd" }, { kind: PropertyValueKind.string, display-string: "Richmond" }, { kind: PropertyValueKind.string, display-string: "VA" }, { kind: PropertyValueKind.string, display-string: "23219" }, { kind: PropertyValueKind.boolean, value-bool: false, display-string: "false" }, { kind: PropertyValueKind.color, display-string: "#ff0000" } ] ], }; private property selection-x; private property selection-y; private property current-cell-data; VerticalLayout { HorizontalLayout { RowMarker { } for header in root.current-table.headers: Cell { is-header: true; text: header; } } for data-row[row] in root.current-table.values: HorizontalLayout { RowMarker { value: row + 1; hovered: ta.has-hover; show-menu: root.current-table.is-array; ta := TouchArea { visible: root.current-table.is-array; clicked => { root.selection-x = self.x; root.selection-y = self.y; row_menu.row-number = row; row_menu.show({ x:self.mouse-x + ta.absolute-position.x - row_menu.absolute-position.x, y:self.mouse-y + ta.absolute-position.y - row_menu.absolute-position.y }); } } } for value[col] in data-row: Cell { text: value.display-string; is-writeable: true; edit-clicked() => { root.current-cell-data = { property-group-id: root.property-group-id, property-name: preview-data.name, property-value: root.current-table.values[row][col], row: row, col: col, x: self.x, y: parent.y, width: self.width, height: self.height }; ew.show(); } } } } ew := EditWindow { x: self.current-cell-data.x > 15px ? self.current-cell-data.x - EditorSpaceSettings.default-padding - 15px : (self.current-cell-data.x + self.current-cell-data.width > root.width ? root.width - ew.width : 0); // TODO the end needs to be windowWidth - current-cell.x < current.cell.width * 2; y: self.current-cell-data.y - EditorSpaceSettings.default-padding; current-cell-data <=> root.current-cell-data; current-table: root.current-table; private property possible-error; function edit(new-value: string) -> bool { debug("EW: edit(...): Model has \{root.current-table.values.length} rows"); self.current-table.values[self.current-cell-data.row][self.current-cell-data.col].was-edited = true; self.current-table.values[self.current-cell-data.row][self.current-cell-data.col].edited-value = new-value; self.possible_error = Api.set-property-value-table(root.property-group-id, root.preview-data.name, root.current-table.values, root.current-table.is-array); StatusLineApi.help-text = self.possible-error; return self.possible-error == ""; } test(new-value) => { return edit(new-value); } save(new-value) => { edit(new-value); self.close-editor(); } close-editor => { debug("EW: Received close-editor"); self.close(); } } row_menu := ContextMenuArea { in-out property row-number; Menu { MenuItem { title: "Add Row Above"; activated() => { Api.insert-row-into-value-table(root.current_table, row_menu.row-number); } } MenuItem { title: "Add Row Below"; activated() => { Api.insert-row-into-value-table(root.current_table, row_menu.row-number + 1); } } MenuItem { title: "Remove Row"; activated() => { Api.remove-row-from-value-table(root.current_table, row_menu.row-number); } } } } }