diff --git a/tools/lsp/preview.rs b/tools/lsp/preview.rs index f475ee75c27..5c26b5a7c35 100644 --- a/tools/lsp/preview.rs +++ b/tools/lsp/preview.rs @@ -495,11 +495,7 @@ pub fn send_status_notification(sender: &crate::ServerNotifier, message: &str, h .unwrap_or_else(|e| eprintln!("Error sending notification: {:?}", e)); } -pub fn reset_selections(ui: Option<&ui::PreviewUi>) { - let Some(ui) = ui else { - return; - }; - +pub fn reset_selections(ui: &ui::PreviewUi) { let model = Rc::new(slint::VecModel::from(Vec::new())); ui.set_selections(slint::ModelRc::from(model)); } diff --git a/tools/lsp/preview/native.rs b/tools/lsp/preview/native.rs index 59677fd40c3..fe28a2f8bfc 100644 --- a/tools/lsp/preview/native.rs +++ b/tools/lsp/preview/native.rs @@ -178,7 +178,6 @@ fn open_ui_impl(preview_state: &mut PreviewState) { slint::CloseRequestResponse::HideWindow }); - ui.show().unwrap(); } pub fn close_ui() { @@ -352,14 +351,17 @@ pub fn update_preview_area(compiled: ComponentDefinition) { let shared_handle = preview_state.handle.clone(); + let ui = preview_state.ui.as_ref().unwrap(); super::set_preview_factory( - preview_state.ui.as_ref().unwrap(), + ui, compiled, Box::new(move |instance| { shared_handle.replace(Some(instance)); }), ); - super::reset_selections(preview_state.ui.as_ref()); + super::reset_selections(ui); + + ui.show().unwrap(); }); } diff --git a/tools/lsp/preview/wasm.rs b/tools/lsp/preview/wasm.rs index 01f8ab3e653..c8cb2500077 100644 --- a/tools/lsp/preview/wasm.rs +++ b/tools/lsp/preview/wasm.rs @@ -353,14 +353,15 @@ pub fn update_preview_area(compiled: slint_interpreter::ComponentDefinition) { let shared_handle = preview_state.handle.clone(); + let ui = preview_state.ui.as_ref().unwrap(); super::set_preview_factory( - preview_state.ui.as_ref().unwrap(), + ui, compiled, Box::new(move |instance| { shared_handle.replace(Some(instance)); }), ); - super::reset_selections(preview_state.ui.as_ref()); + super::reset_selections(ui); }) } diff --git a/tools/lsp/ui/main.slint b/tools/lsp/ui/main.slint index 7785191b84e..9106a974733 100644 --- a/tools/lsp/ui/main.slint +++ b/tools/lsp/ui/main.slint @@ -38,113 +38,131 @@ export component PreviewUi inherits Window { title: "Slint Live-Preview"; icon: @image-url("assets/slint-logo-small-light.png"); - if (!show-preview-ui): VerticalLayout { - no-ui-drawing-rect := Rectangle { - ComponentContainer { - component-factory <=> root.preview-area; - width: clamp(no-ui-drawing-rect.width, self.min-width, self.max-width); - height: clamp(no-ui-drawing-rect.height, self.min-height, self.max-height); + VerticalLayout { + if (!show-preview-ui): no-ui-drawing-rect := Rectangle { + VerticalLayout { + ComponentContainer { + component-factory <=> root.preview-area; + } } // Diagnostics overlay: DiagnosticsOverlay { + width: 100%; + height: 100%; diagnostics <=> root.diagnostics; show-document(url, line, column) => { root.show-document(url, line, column); } } } - } - if (show-preview-ui): VerticalLayout { - HeaderBar { - vertical-stretch: 0.0; + if (show-preview-ui): VerticalLayout { + HeaderBar { + vertical-stretch: 0.0; - height: self.preferred-height; + height: self.preferred-height; - i-pick-button := Button { - text: "Pick Mode"; - checkable: true; - checked <=> root.design-mode; - } + i-pick-button := Button { + text: "Pick Mode"; + checkable: true; + checked <=> root.design-mode; + } - Text { - text: "Style:"; - vertical-alignment: center; - } - i-style-select := ComboBox { - model: root.known-styles; - current-value <=> current-style; - selected(value) => { - root.style-changed(); + Text { + text: "Style:"; + vertical-alignment: center; + } + i-style-select := ComboBox { + model: root.known-styles; + current-value <=> current-style; + selected(value) => { + root.style-changed(); + } } - } - Text { - text: root.status-text; - vertical-alignment: center; + Text { + text: root.status-text; + vertical-alignment: center; + } } - } - i-scroll-view := ScrollView { - property border: 60px; + i-scroll-view := ScrollView { + preferred-height: max(i-preview-area-container.preferred-height, i-preview-area-container.min-height) + 2 * i-scroll-view.border; + preferred-width: max(i-preview-area-container.preferred-width, i-preview-area-container.min-width) + 2 * i-scroll-view.border; - viewport-width: i-drawing-rect.width; - viewport-height: i-drawing-rect.height; - i-drawing-rect := Rectangle { - background: Colors.white; + property border: 60px; - width: max(i-scroll-view.visible-width, i-resizer.width + i-scroll-view.border); - height: max(i-scroll-view.visible-height, i-resizer.height + i-scroll-view.border); + viewport-width: i-drawing-rect.width; + viewport-height: i-drawing-rect.height; - i-resizer := Resizer { - is-resizable <=> i-preview-area-container.is-resizable; + i-drawing-rect := Rectangle { + background: Colors.white; - resize(w, h) => { - i-preview-area-container.width = clamp(w, i-preview-area-container.min-width, i-preview-area-container.max-width); - i-preview-area-container.height = clamp(h, i-preview-area-container.min-height, i-preview-area-container.max-height); - } + width: max(i-scroll-view.visible-width, i-resizer.width + i-scroll-view.border); + height: max(i-scroll-view.visible-height, i-resizer.height + i-scroll-view.border); - width <=> i-preview-area-container.width; - height <=> i-preview-area-container.height; - - i-preview-area-container := ComponentContainer { - property is-resizable: (self.min-width != self.max-width && self.min-height != self.max-height) && self.has-component; - - component-factory <=> root.preview-area; - } + i-resizer := Resizer { + is-resizable <=> i-preview-area-container.is-resizable; - i-selection-area := TouchArea { - clicked => { root.select-at(self.pressed-x, self.pressed-y); } - double-clicked => { root.select-into(self.pressed-x, self.pressed-y); } - mouse-cursor: crosshair; - enabled <=> root.design-mode; + resize(w, h) => { + i-preview-area-container.width = clamp(w, i-preview-area-container.min-width, i-preview-area-container.max-width); + i-preview-area-container.height = clamp(h, i-preview-area-container.min-height, i-preview-area-container.max-height); + } - x: i-preview-area-container.x; - y: i-preview-area-container.y; width: i-preview-area-container.width; height: i-preview-area-container.height; - } - i-selection-display-area := Rectangle { - x: i-preview-area-container.x; - y: i-preview-area-container.y; - width: i-preview-area-container.width; - height: i-preview-area-container.height; + i-preview-area-container := ComponentContainer { + + property is-resizable: (self.min-width != self.max-width && self.min-height != self.max-height) && self.has-component; + + component-factory <=> root.preview-area; + + // The width and the height can't depend on the layout info of the inner item otherwise this would + // cause a recursion if this happens (#3989) + // Instead, we use a init function to initialize + width: 0px; + height: 0px; + init => { + self.width = max(self.preferred-width, self.min-width); + self.height = max(self.preferred-height, self.min-height); + } + } + + // Also make a condition that abuses the fact that the init callback + // is called everytime the condition is dirty, to make sure that the size + // is within the bounds. + // Querty the preview-area to make sure this is evaluated when it changes + if i-preview-area-container.has-component && root.preview-area == i-preview-area-container.component-factory : Rectangle { + init => { + i-preview-area-container.width = clamp(i-preview-area-container.width, i-preview-area-container.min-width, i-preview-area-container.max-width); + i-preview-area-container.height = clamp(i-preview-area-container.height, i-preview-area-container.min-height, i-preview-area-container.max-height); + } + } - for s in root.selections: Rectangle { - x: s.x; - y: s.y; - width: s.width; - height: s.height; - border-color: s.border-color; - border-width: 1px; + i-selection-area := TouchArea { + clicked => { root.select-at(self.pressed-x, self.pressed-y); } + double-clicked => { root.select-into(self.pressed-x, self.pressed-y); } + mouse-cursor: crosshair; + enabled <=> root.design-mode; + } + + i-selection-display-area := Rectangle { + for s in root.selections: Rectangle { + x: s.x; + y: s.y; + width: s.width; + height: s.height; + border-color: s.border-color; + border-width: 1px; + } } } - } - // Diagnostics overlay: - DiagnosticsOverlay { - diagnostics <=> root.diagnostics; - show-document(url, line, column) => { root.show-document(url, line, column); } + // Diagnostics overlay: + DiagnosticsOverlay { + diagnostics <=> root.diagnostics; + show-document(url, line, column) => { root.show-document(url, line, column); } + } } } }