1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
3
4// cSpell: ignore Heade
5
6import { Button, ComboBox, ListView, ScrollView, VerticalBox } from "std-widgets.slint";
7import { ComponentList, ComponentListItem } from "component-list.slint";
8import { DrawArea, DrawAreaMode, Selection } from "draw-area.slint";
9import { HeaderBar } from "header-bar.slint";
10import { Diagnostics, DiagnosticsOverlay } from "diagnostics-overlay.slint";
11
12export { Diagnostics }
13
14export component PreviewUi inherits Window {
15 in property <[ComponentListItem]> known-components;
16 in property <[Diagnostics]> diagnostics;
17 in property <[Selection]> selections;
18 in property <[string]> known-styles;
19 in property <bool> experimental: false;
20 in property <bool> show-preview-ui: true;
21 in property <component-factory> preview-area;
22 in property <string> status-text;
23 in-out property <string> current-style;
24 out property <bool> design-mode;
25
26 pure callback can-drop(/* component_type */ string, /* x */ length, /* y */ length) -> bool;
27 callback drop(/* component_type */ string, /* x */ length, /* y */ length);
28 callback selected-element-update-geometry(/* x */ length, /* y */ length, /* width */ length, /* height */ length);
29 callback selected-element-delete();
30 callback select-at(/* x */ length, /* y */ length, /* enter_component? */ bool);
31 callback select-behind(/* x */ length, /* y */ length, /* enter_component* */ bool, /* reverse */ bool);
32 callback show-document(/* url */ string, /* line */ int, /* column */ int);
33 callback style-changed();
34 callback reselect();
35 callback unselect();
36
37 property <length> border: 20px;
38 property <length> side-bar-width: 200px;
39
40 title: "Slint Live-Preview";
41 icon: @image-url("assets/slint-logo-small-light.png");
42
43 VerticalLayout {
44 if (!show-preview-ui): no-ui-drawing-rect := Rectangle {
45 VerticalLayout {
46 ComponentContainer {
47 component-factory <=> root.preview-area;
48 }
49 }
50
51 // Diagnostics overlay:
52 DiagnosticsOverlay {
53 width: 100%;
54 height: 100%;
55 diagnostics <=> root.diagnostics;
56 show-document(url, line, column) => {
57 root.show-document(url, line, column);
58 }
59 }
60 }
61 if (show-preview-ui): Rectangle {
62 VerticalLayout {
63 HeaderBar {
64 vertical-stretch: 0.0;
65
66 height: self.preferred-height;
67
68 pick-button := Button {
69 text: @tr("Design Mode");
70 checkable: true;
71 checked <=> root.design-mode;
72 clicked() => {
73 key-handler.focus();
74 }
75 }
76
77 Text {
78 text: @tr("Style:");
79 vertical-alignment: center;
80 }
81
82 style-select := ComboBox {
83 model: root.known-styles;
84 current-value <=> current-style;
85 selected(value) => {
86 root.style-changed();
87 }
88 }
89
90 Text {
91 text: root.status-text;
92 vertical-alignment: center;
93 }
94 }
95
96 HorizontalLayout {
97 left-sidebar := Rectangle {
98 VerticalBox {
99 ComponentList {
100 known-components <=> root.known-components;
101 preview-area-position-x: draw-area.preview-area-position-x;
102 preview-area-position-y: draw-area.preview-area-position-y;
103 preview-area-width: draw-area.preview-area-width;
104 preview-area-height: draw-area.preview-area-height;
105
106 can-drop(c, x, y) => {
107 return root.can-drop(c, x, y);
108 }
109 drop(c, x, y) => {
110 root.drop(c, x, y);
111 }
112 }
113 }
114
115 states [
116 visible when !pick-button.checked: {
117 width: 0px;
118 }
119 hidden when pick-button.checked: {
120 width: root.side-bar-width;
121 }
122 ]
123 }
124
125 draw-area := DrawArea {
126 design-mode <=> root.design-mode;
127 diagnostics <=> root.diagnostics;
128 preview-area <=> root.preview-area;
129 selections <=> root.selections;
130
131 select-at(x, y, enter_component) => {
132 root.select-at(x, y, enter_component);
133 }
134 selected-element-update-geometry(x, y, w, h) => {
135 root.selected-element-update-geometry(x, y, w, h);
136 }
137 selected-element-delete() => {
138 root.selected-element-delete();
139 }
140 select-behind(x, y, stay_in_file, reverse) => {
141 root.select-behind(x, y, stay_in_file, reverse);
142 }
143 show-document(url, line, column) => {
144 root.show-document(url, line, column);
145 }
146 unselect() => {
147 root.unselect();
148 }
149 reselect() => {
150 root.reselect();
151 }
152 }
153
154 preferred-width: draw-area.preferred-width + root.side-bar-width/* for left-side-bar */;
155 }
156 }
157
158 key-handler := FocusScope {
159 enabled: draw-area.mode == DrawAreaMode.designing;
160
161 key-released(event) => {
162 if event.text == Key.Delete {
163 // This `if` should not be necessary, but without it
164 // we do trigger deletion of Elements while errors
165 // are on screen.
166 if draw-area.mode == DrawAreaMode.designing {
167 root.selected-element-delete();
168 }
169 return accept;
170 }
171 reject
172 }
173 }
174 }
175 }
176}
177