live-preview: Explicitly set live-data values on reload

Set all values that are changed compared to the initial
set of values right after loading a new component.

This also includes a small change to the printerdemo,
so that it exposes more data to the business logic,
so that we have something to demonstrate;-)
This commit is contained in:
Tobias Hunger 2025-04-04 11:49:38 +00:00 committed by Tobias Hunger
parent e1b85bbb93
commit 081452bee9
4 changed files with 390 additions and 228 deletions

View file

@ -49,7 +49,8 @@ export component MainWindow inherits Window {
{color: #f0f, level: 70%}, {color: #f0f, level: 70%},
{color: #000, level: 30%}, {color: #000, level: 30%},
]; ];
out property <int> active-page: 0; in-out property <int> active-page: 0;
in-out property <bool> dark-mode <=> DemoPalette.dark-mode;
callback quit(); callback quit();

View file

@ -126,6 +126,8 @@ struct PreviewState {
workspace_edit_sent: bool, workspace_edit_sent: bool,
known_components: Vec<ComponentInformation>, known_components: Vec<ComponentInformation>,
preview_loading_delay_timer: Option<slint::Timer>, preview_loading_delay_timer: Option<slint::Timer>,
initial_live_data: preview_data::PreviewDataMap,
current_live_data: preview_data::PreviewDataMap,
} }
impl PreviewState { impl PreviewState {
@ -182,6 +184,49 @@ fn delete_document(url: &lsp_types::Url) {
} }
} }
fn set_current_live_data(mut result: preview_data::PreviewDataMap) {
PREVIEW_STATE.with(|preview_state| {
let mut preview_state = preview_state.borrow_mut();
preview_state.current_live_data.append(&mut result);
})
}
fn apply_live_preview_data() {
let Some(instance) = component_instance() else {
return;
};
let new_initial_data = preview_data::query_preview_data_properties_and_callbacks(&instance);
let (mut previous_initial, mut previous_current) = PREVIEW_STATE.with(|preview_state| {
let mut preview_state = preview_state.borrow_mut();
(
std::mem::replace(&mut preview_state.initial_live_data, new_initial_data),
std::mem::take(&mut preview_state.current_live_data),
)
});
while let Some((kc, vc)) = previous_current.pop_last() {
let prev = previous_initial.pop_last();
let vc = vc.value.unwrap_or_default();
if matches!(vc, slint_interpreter::Value::Void) {
continue;
}
if let Some((ki, vi)) = prev {
let vi = vi.value.unwrap_or_default();
if ki == kc && vi == vc {
continue;
}
}
let _ = preview_data::set_preview_data(&instance, &kc.container, &kc.property_name, vc);
}
}
fn set_contents(url: &common::VersionedUrl, content: String) { fn set_contents(url: &common::VersionedUrl, content: String) {
let mut cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap(); let mut cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap();
let old = cache.source_code.insert( let old = cache.source_code.insert(
@ -932,9 +977,14 @@ fn extract_resources(
result result
} }
fn finish_parsing(preview_url: &Url, previewed_component: Option<String>) { fn finish_parsing(preview_url: &Url, previewed_component: Option<String>, success: bool) {
set_status_text(""); set_status_text("");
if !success {
// No need to update everything...
return;
}
let (previewed_url, component, source_code) = { let (previewed_url, component, source_code) = {
let cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap(); let cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap();
let pc = cache.current_component(); let pc = cache.current_component();
@ -996,6 +1046,8 @@ fn finish_parsing(preview_url: &Url, previewed_component: Option<String>) {
usize::MAX usize::MAX
}; };
apply_live_preview_data();
PREVIEW_STATE.with(|preview_state| { PREVIEW_STATE.with(|preview_state| {
let mut preview_state = preview_state.borrow_mut(); let mut preview_state = preview_state.borrow_mut();
preview_state.known_components = components; preview_state.known_components = components;
@ -1301,6 +1353,12 @@ async fn reload_preview_impl(
) -> Result<(), PlatformError> { ) -> Result<(), PlatformError> {
start_parsing(); start_parsing();
if let Some(component_instance) = component_instance() {
let live_preview_data =
preview_data::query_preview_data_properties_and_callbacks(&component_instance);
set_current_live_data(live_preview_data);
}
let path = component.url.to_file_path().unwrap_or(PathBuf::from(&component.url.to_string())); let path = component.url.to_file_path().unwrap_or(PathBuf::from(&component.url.to_string()));
let (version, source) = get_url_from_cache(&component.url); let (version, source) = get_url_from_cache(&component.url);
@ -1324,6 +1382,8 @@ async fn reload_preview_impl(
) )
.await; .await;
let success = compiled.is_some();
let loaded_component_name = compiled.as_ref().map(|c| c.name().to_string()); let loaded_component_name = compiled.as_ref().map(|c| c.name().to_string());
{ {
@ -1340,7 +1400,7 @@ async fn reload_preview_impl(
update_preview_area(compiled, behavior, open_import_fallback, source_file_versions)?; update_preview_area(compiled, behavior, open_import_fallback, source_file_versions)?;
finish_parsing(&component.url, loaded_component_name); finish_parsing(&component.url, loaded_component_name, success);
Ok(()) Ok(())
} }
@ -1745,7 +1805,6 @@ fn update_preview_area(
native::open_ui_impl(&mut preview_state)?; native::open_ui_impl(&mut preview_state)?;
let ui = preview_state.ui.as_ref().unwrap(); let ui = preview_state.ui.as_ref().unwrap();
let shared_handle = preview_state.handle.clone(); let shared_handle = preview_state.handle.clone();
let shared_document_cache = preview_state.document_cache.clone(); let shared_document_cache = preview_state.document_cache.clone();
@ -1766,6 +1825,7 @@ fn update_preview_area(
), ),
))); )));
} }
shared_handle.replace(Some(instance)); shared_handle.replace(Some(instance));
}), }),
behavior, behavior,
@ -1786,6 +1846,7 @@ fn update_preview_area(
Ok(()) Ok(())
}) })
})?; })?;
element_selection::reselect_element(); element_selection::reselect_element();
Ok(()) Ok(())
} }

View file

@ -1,11 +1,11 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // 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 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
use std::{collections::HashMap, fmt::Display}; use std::fmt::Display;
use slint_interpreter::ComponentInstance; use slint_interpreter::ComponentInstance;
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum PropertyContainer { pub enum PropertyContainer {
Main, Main,
Global(String), Global(String),
@ -45,9 +45,14 @@ fn is_property(ty: &i_slint_compiler::langtype::Type) -> bool {
) )
} }
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct PreviewDataKey {
pub container: PropertyContainer,
pub property_name: String,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PreviewData { pub struct PreviewData {
pub name: String,
pub ty: i_slint_compiler::langtype::Type, pub ty: i_slint_compiler::langtype::Type,
pub visibility: i_slint_compiler::object_tree::PropertyVisibility, pub visibility: i_slint_compiler::object_tree::PropertyVisibility,
pub value: Option<slint_interpreter::Value>, pub value: Option<slint_interpreter::Value>,
@ -67,10 +72,12 @@ impl PreviewData {
} }
} }
pub type PreviewDataMap = std::collections::BTreeMap<PreviewDataKey, PreviewData>;
pub fn get_preview_data( pub fn get_preview_data(
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
container: PropertyContainer, container: &PropertyContainer,
property_name: String, property_name: &str,
) -> Option<PreviewData> { ) -> Option<PreviewData> {
fn find_preview_data( fn find_preview_data(
property_name: &str, property_name: &str,
@ -88,19 +95,19 @@ pub fn get_preview_data(
it.find(|(name, (_, _))| name == property_name).map(|(name, (ty, visibility))| { it.find(|(name, (_, _))| name == property_name).map(|(name, (ty, visibility))| {
let value = value_query(&name); let value = value_query(&name);
PreviewData { name, ty, visibility, value } PreviewData { ty, visibility, value }
}) })
} }
let definition = &component_instance.definition(); let definition = &component_instance.definition();
match &container { match &container {
PropertyContainer::Main => { PropertyContainer::Main => {
find_preview_data(&property_name, &mut definition.properties_and_callbacks(), &|name| { find_preview_data(property_name, &mut definition.properties_and_callbacks(), &|name| {
component_instance.get_property(name).ok() component_instance.get_property(name).ok()
}) })
} }
PropertyContainer::Global(g) => find_preview_data( PropertyContainer::Global(g) => find_preview_data(
&property_name, property_name,
&mut definition.global_properties_and_callbacks(g)?, &mut definition.global_properties_and_callbacks(g)?,
&|name| component_instance.get_global_property(g, name).ok(), &|name| component_instance.get_global_property(g, name).ok(),
), ),
@ -109,13 +116,12 @@ pub fn get_preview_data(
pub fn query_preview_data_properties_and_callbacks( pub fn query_preview_data_properties_and_callbacks(
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
) -> HashMap<PropertyContainer, Vec<PreviewData>> { ) -> PreviewDataMap {
let definition = &component_instance.definition(); let definition = &component_instance.definition();
let mut result = HashMap::new(); fn collect_preview_data<'a>(
container: PropertyContainer,
fn collect_preview_data( it: &'a mut dyn Iterator<
it: &mut dyn Iterator<
Item = ( Item = (
String, String,
( (
@ -124,37 +130,33 @@ pub fn query_preview_data_properties_and_callbacks(
), ),
), ),
>, >,
value_query: &dyn Fn(&str) -> Option<slint_interpreter::Value>, value_query: &'a dyn Fn(&str) -> Option<slint_interpreter::Value>,
) -> Vec<PreviewData> { ) -> impl Iterator<Item = (PreviewDataKey, PreviewData)> + use<'a> {
let mut v = it it.map(move |(name, (ty, visibility))| {
.map(|(name, (ty, visibility))| { let value = value_query(&name);
let value = value_query(&name);
PreviewData { name, ty, visibility, value } (
}) PreviewDataKey { container: container.clone(), property_name: name },
.collect::<Vec<_>>(); PreviewData { ty, visibility, value },
)
v.sort_by_key(|p| p.name.clone()); })
v
} }
result.insert( let mut result = collect_preview_data(
PropertyContainer::Main, PropertyContainer::Main,
collect_preview_data(&mut definition.properties_and_callbacks(), &|name| { &mut definition.properties_and_callbacks(),
component_instance.get_property(name).ok() &|name| component_instance.get_property(name).ok(),
}), )
); .collect::<PreviewDataMap>();
for global in definition.globals() { for global in definition.globals() {
result.insert( result.extend(collect_preview_data(
PropertyContainer::Global(global.clone()), PropertyContainer::Global(global.clone()),
collect_preview_data( &mut definition
&mut definition .global_properties_and_callbacks(&global)
.global_properties_and_callbacks(&global) .expect("Global was just valid"),
.expect("Global was just valid"), &|name| component_instance.get_global_property(&global, name).ok(),
&|name| component_instance.get_global_property(&global, name).ok(), ));
),
);
} }
result result
@ -187,15 +189,42 @@ fn find_component_properties_and_callbacks<'a>(
} }
} }
pub fn set_preview_data(
component_instance: &ComponentInstance,
container: &PropertyContainer,
property_name: &str,
value: slint_interpreter::Value,
) -> Result<(PreviewDataKey, slint_interpreter::Value), String> {
let result = match &container {
PropertyContainer::Main => component_instance.set_property(property_name, value.clone()),
PropertyContainer::Global(g) => {
component_instance.set_global_property(g, property_name, value.clone())
}
};
if let Err(msg) = result {
Err(format!("Could not set property {property_name}: {msg}"))
} else {
Ok((
PreviewDataKey {
container: container.clone(),
property_name: property_name.to_string(),
},
value,
))
}
}
pub fn set_json_preview_data( pub fn set_json_preview_data(
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
container: PropertyContainer, container: PropertyContainer,
property_name: Option<String>, property_name: Option<String>,
json: serde_json::Value, json: serde_json::Value,
) -> Result<(), Vec<String>> { ) -> Result<PreviewDataMap, Vec<String>> {
let mut result = PreviewDataMap::default();
let definition = &component_instance.definition(); let definition = &component_instance.definition();
let mut properties_set = 0_usize;
let mut failed_properties = vec![]; let mut failed_properties = vec![];
let it = let it =
@ -209,8 +238,8 @@ pub fn set_json_preview_data(
} }
}; };
for (name, (ty, _)) in it { for (name, (ty, visibility)) in it {
let (name, json_value) = if let Some(pn) = &property_name { let (property_name, json_value) = if let Some(pn) = &property_name {
(pn.clone(), Some(&json)) (pn.clone(), Some(&json))
} else { } else {
let json_value = match &json { let json_value = match &json {
@ -239,20 +268,18 @@ pub fn set_json_preview_data(
continue; continue;
}; };
let Ok(value) = slint_interpreter::json::value_from_json(&ty, json_value) else { let Ok(value) = slint_interpreter::json::value_from_json(&ty, json_value) else {
failed_properties.push(format!("Could not convert JSON value for property {name}")); failed_properties
.push(format!("Could not convert JSON value for property {property_name}"));
continue; continue;
}; };
let result = match &container { match set_preview_data(component_instance, &container, &property_name, value) {
PropertyContainer::Main => component_instance.set_property(&name, value), Err(msg) => {
PropertyContainer::Global(g) => component_instance.set_global_property(g, &name, value), failed_properties.push(msg);
}; }
Ok((key, value)) => {
if let Err(msg) = result { result.insert(key, PreviewData { ty, visibility, value: Some(value) });
failed_properties.push(format!("Could not set property {name}: {msg}")); }
continue;
} else {
properties_set += 1;
} }
} }
@ -260,10 +287,10 @@ pub fn set_json_preview_data(
return Err(failed_properties); return Err(failed_properties);
} }
if properties_set == 0 { if result.is_empty() {
Err(vec![format!("No property set")]) Err(vec![format!("No property set")])
} else { } else {
Ok(()) Ok(result)
} }
} }
@ -335,131 +362,203 @@ mod tests {
let properties = query_preview_data_properties_and_callbacks(&component_instance); let properties = query_preview_data_properties_and_callbacks(&component_instance);
assert_eq!(properties.len(), 4); assert_eq!(properties.len(), 12);
let main = properties.get(&PropertyContainer::Main).unwrap(); let main = properties
.iter()
.filter(|(k, _)| k.container == PropertyContainer::Main)
.collect::<Vec<_>>();
assert_eq!(main.len(), 3); assert_eq!(main.len(), 3);
assert_eq!( assert_eq!(
main[0], main[0],
PreviewData { (
name: "main-component-in".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Main,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input, property_name: "main-component-in".into(),
value: Some(slint_interpreter::Value::Number(65.0)), },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input,
value: Some(slint_interpreter::Value::Number(65.0)),
}
)
); );
assert_eq!( assert_eq!(
main[1], main[1],
PreviewData { (
name: "main-component-in-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Main,
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut, property_name: "main-component-in-out".into(),
value: Some(slint_interpreter::Value::Number(260.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut,
value: Some(slint_interpreter::Value::Number(260.0))
}
)
); );
assert_eq!( assert_eq!(
main[2], main[2],
PreviewData { (
name: "main-component-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Main,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output, property_name: "main-component-out".into(),
value: Some(slint_interpreter::Value::Number(130.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output,
value: Some(slint_interpreter::Value::Number(130.0))
}
)
); );
let global = properties.get(&PropertyContainer::Global("MainGlobal".into())).unwrap(); let global = properties
.iter()
.filter(|(k, _)| k.container == PropertyContainer::Global("MainGlobal".into()))
.collect::<Vec<_>>();
assert_eq!(global.len(), 3); assert_eq!(global.len(), 3);
assert_eq!( assert_eq!(
global[0], global[0],
PreviewData { (
name: "main-global-in".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("MainGlobal".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input, property_name: "main-global-in".into(),
value: Some(slint_interpreter::Value::Number(1.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input,
value: Some(slint_interpreter::Value::Number(1.0))
}
)
); );
assert_eq!( assert_eq!(
global[1], global[1],
PreviewData { (
name: "main-global-in-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("MainGlobal".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut, property_name: "main-global-in-out".into(),
value: Some(slint_interpreter::Value::Number(4.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut,
value: Some(slint_interpreter::Value::Number(4.0))
}
)
); );
assert_eq!( assert_eq!(
global[2], global[2],
PreviewData { (
name: "main-global-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("MainGlobal".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output, property_name: "main-global-out".into(),
value: Some(slint_interpreter::Value::Number(2.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output,
value: Some(slint_interpreter::Value::Number(2.0))
}
)
); );
let user1 = properties.get(&PropertyContainer::Global("User1".into())).unwrap(); let user1 = properties
.iter()
.filter(|(k, _)| k.container == PropertyContainer::Global("User1".into()))
.collect::<Vec<_>>();
assert_eq!(user1.len(), 3); assert_eq!(user1.len(), 3);
assert_eq!( assert_eq!(
user1[0], user1[0],
PreviewData { (
name: "user1-in".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("User1".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input, property_name: "user1-in".into(),
value: Some(slint_interpreter::Value::Number(8.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input,
value: Some(slint_interpreter::Value::Number(8.0))
}
)
); );
assert_eq!( assert_eq!(
user1[1], user1[1],
PreviewData { (
name: "user1-in-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("User1".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut, property_name: "user1-in-out".into(),
value: Some(slint_interpreter::Value::Number(32.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut,
value: Some(slint_interpreter::Value::Number(32.0))
}
)
); );
assert_eq!( assert_eq!(
user1[2], user1[2],
PreviewData { (
name: "user1-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("User1".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output, property_name: "user1-out".into(),
value: Some(slint_interpreter::Value::Number(16.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output,
value: Some(slint_interpreter::Value::Number(16.0))
}
)
); );
let user2 = properties.get(&PropertyContainer::Global("User2".into())).unwrap(); let user2 = properties
.iter()
.filter(|(k, _)| k.container == PropertyContainer::Global("User2".into()))
.collect::<Vec<_>>();
assert_eq!(user2.len(), 3); assert_eq!(user2.len(), 3);
assert_eq!( assert_eq!(
user2[0], user2[0],
PreviewData { (
name: "user2-in".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("User2".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input, property_name: "user2-in".into(),
value: Some(slint_interpreter::Value::Number(64.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Input,
value: Some(slint_interpreter::Value::Number(64.0))
}
)
); );
assert_eq!( assert_eq!(
user2[1], user2[1],
PreviewData { (
name: "user2-in-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("User2".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut, property_name: "user2-in-out".into(),
value: Some(slint_interpreter::Value::Number(256.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::InOut,
value: Some(slint_interpreter::Value::Number(256.0))
}
)
); );
assert_eq!( assert_eq!(
user2[2], user2[2],
PreviewData { (
name: "user2-out".into(), &PreviewDataKey {
ty: i_slint_compiler::langtype::Type::Int32, container: PropertyContainer::Global("User2".into()),
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output, property_name: "user2-out".into(),
value: Some(slint_interpreter::Value::Number(128.0)) },
} &PreviewData {
ty: i_slint_compiler::langtype::Type::Int32,
visibility: i_slint_compiler::object_tree::PropertyVisibility::Output,
value: Some(slint_interpreter::Value::Number(128.0))
}
)
); );
} }
} }

View file

@ -7,6 +7,7 @@ use std::{collections::HashMap, iter::once, rc::Rc};
use i_slint_compiler::parser::{syntax_nodes, SyntaxKind, TextRange}; use i_slint_compiler::parser::{syntax_nodes, SyntaxKind, TextRange};
use i_slint_compiler::{expression_tree, langtype, literals}; use i_slint_compiler::{expression_tree, langtype, literals};
use itertools::Itertools; use itertools::Itertools;
use lsp_types::Url; use lsp_types::Url;
use slint::{Model, ModelRc, SharedString, ToSharedString, VecModel}; use slint::{Model, ModelRc, SharedString, ToSharedString, VecModel};
@ -114,7 +115,7 @@ pub fn create_ui(style: String, experimental: bool) -> Result<PreviewUi, Platfor
r: c.red() as i32, r: c.red() as i32,
g: c.green() as i32, g: c.green() as i32,
b: c.blue() as i32, b: c.blue() as i32,
text: color_to_string(c).into(), text: color_to_string(c),
short_text: color_to_short_string(c).into(), short_text: color_to_short_string(c).into(),
}); });
api.on_rgba_to_color(|r, g, b, a| { api.on_rgba_to_color(|r, g, b, a| {
@ -143,7 +144,7 @@ pub fn create_ui(style: String, experimental: bool) -> Result<PreviewUi, Platfor
} }
let row = row as usize; let row = row as usize;
if row < model.row_count() { if row < model.row_count() {
model.as_any().downcast_ref::<VecModel<GradientStop>>().unwrap().remove(row as usize); model.as_any().downcast_ref::<VecModel<GradientStop>>().unwrap().remove(row);
} }
}); });
@ -283,10 +284,10 @@ pub fn ui_set_known_components(
let mut all_components = Vec::with_capacity( let mut all_components = Vec::with_capacity(
builtin_components.len() + library_components.len() + file_components.len(), builtin_components.len() + library_components.len() + file_components.len(),
); );
all_components.extend_from_slice(&builtin_components); all_components.extend_from_slice(&builtin_components[..]);
all_components.extend_from_slice(&std_widgets_components); all_components.extend_from_slice(&std_widgets_components[..]);
all_components.extend_from_slice(&library_components); all_components.extend_from_slice(&library_components[..]);
all_components.extend_from_slice(&file_components); all_components.extend_from_slice(&file_components[..]);
let result = Rc::new(VecModel::from(all_components)); let result = Rc::new(VecModel::from(all_components));
let api = ui.global::<Api>(); let api = ui.global::<Api>();
@ -516,7 +517,7 @@ fn set_default_brush(
} }
value.brush_kind = BrushKind::Solid; value.brush_kind = BrushKind::Solid;
let text = "#00000000"; let text = "#00000000";
let color = string_to_color(&text).unwrap(); let color = string_to_color(text).unwrap();
value.gradient_stops = value.gradient_stops =
Rc::new(VecModel::from(vec![GradientStop { color, position: 0.5 }])).into(); Rc::new(VecModel::from(vec![GradientStop { color, position: 0.5 }])).into();
value.display_string = text.into(); value.display_string = text.into();
@ -926,7 +927,7 @@ fn map_value_and_type(
gradient_stops: Rc::new(VecModel::from(vec![GradientStop { color, position: 0.5 }])) gradient_stops: Rc::new(VecModel::from(vec![GradientStop { color, position: 0.5 }]))
.into(), .into(),
code, code,
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -941,7 +942,7 @@ fn map_value_and_type(
kind: PropertyValueKind::Float, kind: PropertyValueKind::Float,
value_float: get_value::<f32>(value), value_float: get_value::<f32>(value),
code: get_code(value), code: get_code(value),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -953,7 +954,7 @@ fn map_value_and_type(
kind: PropertyValueKind::Integer, kind: PropertyValueKind::Integer,
value_int: get_value::<i32>(value), value_int: get_value::<i32>(value),
code: get_code(value), code: get_code(value),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -967,7 +968,7 @@ fn map_value_and_type(
value_int: 0, value_int: 0,
code: get_code(value), code: get_code(value),
default_selection: 1, default_selection: 1,
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -981,7 +982,7 @@ fn map_value_and_type(
value_int: 0, value_int: 0,
code: get_code(value), code: get_code(value),
default_selection: 0, default_selection: 0,
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -995,7 +996,7 @@ fn map_value_and_type(
value_int: 0, value_int: 0,
code: get_code(value), code: get_code(value),
default_selection: 0, default_selection: 0,
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -1009,7 +1010,7 @@ fn map_value_and_type(
value_int: 0, value_int: 0,
code: get_code(value), code: get_code(value),
default_selection: 0, default_selection: 0,
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -1023,7 +1024,7 @@ fn map_value_and_type(
value_int: 0, value_int: 0,
code: get_code(value), code: get_code(value),
default_selection: 0, default_selection: 0,
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -1037,7 +1038,7 @@ fn map_value_and_type(
value_int: 0, value_int: 0,
code: get_code(value), code: get_code(value),
default_selection: 0, default_selection: 0,
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -1048,7 +1049,7 @@ fn map_value_and_type(
kind: PropertyValueKind::String, kind: PropertyValueKind::String,
value_string: get_value::<SharedString>(value), value_string: get_value::<SharedString>(value),
code: get_code(value), code: get_code(value),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
..Default::default() ..Default::default()
}); });
} }
@ -1080,7 +1081,7 @@ fn map_value_and_type(
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) ))
.into(), .into(),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
code: get_code(value), code: get_code(value),
..Default::default() ..Default::default()
}); });
@ -1098,7 +1099,7 @@ fn map_value_and_type(
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) ))
.into(), .into(),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
code: get_code(value), code: get_code(value),
..Default::default() ..Default::default()
}); });
@ -1109,7 +1110,7 @@ fn map_value_and_type(
display_string: SharedString::from("Unknown Brush"), display_string: SharedString::from("Unknown Brush"),
kind: PropertyValueKind::Code, kind: PropertyValueKind::Code,
value_string: SharedString::from("???"), value_string: SharedString::from("???"),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
code: get_code(value), code: get_code(value),
..Default::default() ..Default::default()
}); });
@ -1122,7 +1123,7 @@ fn map_value_and_type(
display_string: get_value::<bool>(value).to_shared_string(), display_string: get_value::<bool>(value).to_shared_string(),
kind: PropertyValueKind::Boolean, kind: PropertyValueKind::Boolean,
value_bool: get_value::<bool>(value), value_bool: get_value::<bool>(value),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
code: get_code(value), code: get_code(value),
..Default::default() ..Default::default()
}); });
@ -1143,8 +1144,7 @@ fn map_value_and_type(
"{}.{}", "{}.{}",
enumeration.name, enumeration.name,
enumeration.values[selected_value] enumeration.values[selected_value]
) ),
.into(),
kind: PropertyValueKind::Enum, kind: PropertyValueKind::Enum,
value_string: enumeration.name.as_str().into(), value_string: enumeration.name.as_str().into(),
default_selection: i32::try_from(enumeration.default_value).unwrap_or_default(), default_selection: i32::try_from(enumeration.default_value).unwrap_or_default(),
@ -1157,7 +1157,7 @@ fn map_value_and_type(
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) ))
.into(), .into(),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
code: get_code(value), code: get_code(value),
..Default::default() ..Default::default()
}); });
@ -1167,9 +1167,9 @@ fn map_value_and_type(
let model = get_value::<ModelRc<slint_interpreter::Value>>(value); let model = get_value::<ModelRc<slint_interpreter::Value>>(value);
for (idx, sub_value) in model.iter().enumerate() { for (idx, sub_value) in model.iter().enumerate() {
let mut sub_mapping = ValueMapping::default(); let mut sub_mapping =
sub_mapping.name_prefix = mapping.name_prefix.clone(); ValueMapping { name_prefix: mapping.name_prefix.clone(), ..Default::default() };
map_value_and_type(&array_ty, &Some(sub_value), &mut sub_mapping); map_value_and_type(array_ty, &Some(sub_value), &mut sub_mapping);
let sub_mapping_too_complex = sub_mapping.is_array || sub_mapping.is_too_complex; let sub_mapping_too_complex = sub_mapping.is_array || sub_mapping.is_too_complex;
mapping.is_too_complex = mapping.is_too_complex || sub_mapping_too_complex; mapping.is_too_complex = mapping.is_too_complex || sub_mapping_too_complex;
@ -1203,7 +1203,7 @@ fn map_value_and_type(
sub_mapping.name_prefix = header_name.clone(); sub_mapping.name_prefix = header_name.clone();
map_value_and_type( map_value_and_type(
&field_ty, field_ty,
&struct_data.get_field(&field).cloned(), &struct_data.get_field(&field).cloned(),
&mut sub_mapping, &mut sub_mapping,
); );
@ -1231,7 +1231,7 @@ fn map_value_and_type(
display_string: "Unsupported type".into(), display_string: "Unsupported type".into(),
kind: PropertyValueKind::Code, kind: PropertyValueKind::Code,
value_string: "???".into(), value_string: "???".into(),
accessor_path: mapping.name_prefix.clone().into(), accessor_path: mapping.name_prefix.clone(),
code: get_code(value), code: get_code(value),
..Default::default() ..Default::default()
}); });
@ -1265,22 +1265,25 @@ fn map_preview_data_to_property_value(
} }
} }
fn map_preview_data_property(preview_data: &preview_data::PreviewData) -> Option<PreviewData> { fn map_preview_data_property(
if !preview_data.is_property() { key: &preview_data::PreviewDataKey,
value: &preview_data::PreviewData,
) -> Option<PreviewData> {
if !value.is_property() {
return None; return None;
}; };
let has_getter = preview_data.has_getter(); let has_getter = value.has_getter();
let has_setter = preview_data.has_setter(); let has_setter = value.has_setter();
let mut mapping = ValueMapping::default(); let mut mapping = ValueMapping::default();
map_value_and_type(&preview_data.ty, &preview_data.value, &mut mapping); map_value_and_type(&value.ty, &value.value, &mut mapping);
let is_array = mapping.array_values.len() != 1 || mapping.array_values[0].len() != 1; let is_array = mapping.array_values.len() != 1 || mapping.array_values[0].len() != 1;
let is_too_complex = mapping.is_too_complex; let is_too_complex = mapping.is_too_complex;
Some(PreviewData { Some(PreviewData {
name: preview_data.name.clone().into(), name: SharedString::from(&key.property_name),
has_getter, has_getter,
has_setter, has_setter,
kind: match (is_array, is_too_complex) { kind: match (is_array, is_too_complex) {
@ -1293,41 +1296,45 @@ fn map_preview_data_property(preview_data: &preview_data::PreviewData) -> Option
pub fn ui_set_preview_data( pub fn ui_set_preview_data(
ui: &PreviewUi, ui: &PreviewUi,
preview_data: HashMap<preview_data::PropertyContainer, Vec<preview_data::PreviewData>>, preview_data: preview_data::PreviewDataMap,
previewed_component: Option<String>, previewed_component: Option<String>,
) { ) {
fn fill_container( fn create_container(
container_name: String, container_name: String,
container_id: String, it: &mut dyn Iterator<Item = (&preview_data::PreviewDataKey, &preview_data::PreviewData)>,
properties: &[preview_data::PreviewData], ) -> Option<PropertyContainer> {
) -> PropertyContainer { let (id, props) = it.filter_map(|(k, v)| Some((k, map_preview_data_property(k, v)?))).fold(
let properties = (None, vec![]),
properties.iter().filter_map(map_preview_data_property).collect::<Vec<_>>(); move |mut acc, (key, value)| {
acc.0 = Some(acc.0.unwrap_or_else(|| key.container.clone()));
PropertyContainer { acc.1.push(value);
acc
},
);
Some(PropertyContainer {
container_name: container_name.into(), container_name: container_name.into(),
container_id: container_id.into(), container_id: id?.to_string().into(),
properties: Rc::new(VecModel::from(properties)).into(), properties: Rc::new(VecModel::from(props)).into(),
} })
} }
let mut result: Vec<PropertyContainer> = vec![]; let mut result: Vec<PropertyContainer> = vec![];
if let Some(main) = preview_data.get(&preview_data::PropertyContainer::Main) { if let Some(c) = create_container(
let c = fill_container( previewed_component.unwrap_or_else(|| "<MAIN>".to_string()),
previewed_component.unwrap_or_else(|| "<MAIN>".to_string()), &mut preview_data
String::new(), .iter()
main, .filter(|(k, _)| k.container == preview_data::PropertyContainer::Main),
); ) {
result.push(c); result.push(c);
} }
for component_key in for (k, mut chunk) in &preview_data
preview_data.keys().filter(|k| **k != preview_data::PropertyContainer::Main) .iter()
.filter(|(k, _)| k.container != preview_data::PropertyContainer::Main)
.chunk_by(|(k, _)| k.container.clone())
{ {
if let Some(component) = preview_data.get(component_key) { if let Some(c) = create_container(k.to_string(), &mut chunk) {
let component_key = component_key.to_string();
let c = fill_container(component_key.clone(), component_key, component);
result.push(c); result.push(c);
} }
} }
@ -1338,7 +1345,7 @@ pub fn ui_set_preview_data(
} }
fn to_property_container(container: SharedString) -> preview_data::PropertyContainer { fn to_property_container(container: SharedString) -> preview_data::PropertyContainer {
if container.is_empty() { if container.is_empty() || container == "<MAIN>" {
preview_data::PropertyContainer::Main preview_data::PropertyContainer::Main
} else { } else {
preview_data::PropertyContainer::Global(container.to_string()) preview_data::PropertyContainer::Global(container.to_string())
@ -1350,12 +1357,12 @@ fn get_property_value(container: SharedString, property_name: SharedString) -> P
.and_then(|component_instance| { .and_then(|component_instance| {
preview_data::get_preview_data( preview_data::get_preview_data(
&component_instance, &component_instance,
to_property_container(container), &to_property_container(container),
property_name.to_string(), property_name.as_str(),
) )
}) })
.and_then(|pd| map_preview_data_to_property_value(&pd)) .and_then(|pd| map_preview_data_to_property_value(&pd))
.unwrap_or_else(Default::default) .unwrap_or_default()
} }
fn map_preview_data_to_property_value_table( fn map_preview_data_to_property_value_table(
@ -1375,19 +1382,18 @@ fn get_property_value_table(
container: SharedString, container: SharedString,
property_name: SharedString, property_name: SharedString,
) -> PropertyValueTable { ) -> PropertyValueTable {
let (is_array, mut headers, mut values) = preview::component_instance() let (is_array, headers, mut values) = preview::component_instance()
.and_then(|component_instance| { .and_then(|component_instance| {
preview_data::get_preview_data( preview_data::get_preview_data(
&component_instance, &component_instance,
to_property_container(container), &to_property_container(container),
property_name.to_string(), property_name.as_str(),
) )
}) })
.map(|pd| map_preview_data_to_property_value_table(&pd)) .map(|pd| map_preview_data_to_property_value_table(&pd))
.unwrap_or_else(|| (false, Default::default(), Default::default())); .unwrap_or_else(|| (false, Default::default(), Default::default()));
let headers = let headers = Rc::new(VecModel::from(headers)).into();
Rc::new(VecModel::from(headers.drain(..).map(|s| s.into()).collect::<Vec<_>>())).into();
let values = Rc::new(VecModel::from( let values = Rc::new(VecModel::from(
values.drain(..).map(|cv| Rc::new(VecModel::from(cv)).into()).collect::<Vec<_>>(), values.drain(..).map(|cv| Rc::new(VecModel::from(cv)).into()).collect::<Vec<_>>(),
)) ))
@ -1439,12 +1445,12 @@ fn table_row_to_struct(row: ModelRc<PropertyValue>, indent_level: usize) -> Opti
0 => None, 0 => None,
1 => { 1 => {
let prev = map let prev = map
.insert(accessor_path.get(0).unwrap().to_string(), NodeKind::Leaf(value)); .insert(accessor_path.first().unwrap().to_string(), NodeKind::Leaf(value));
prev.is_none().then_some(()) prev.is_none().then_some(())
} }
_ => { _ => {
let n = map let n = map
.entry(accessor_path.get(0).unwrap().to_string()) .entry(accessor_path.first().unwrap().to_string())
.or_insert_with(|| NodeKind::Inner(BTreeMap::default())); .or_insert_with(|| NodeKind::Inner(BTreeMap::default()));
match n { match n {
@ -1624,7 +1630,7 @@ fn remove_row_from_value_table(table: PropertyValueTable, to_remove: i32) {
}; };
if to_remove < vec_model.row_count() { if to_remove < vec_model.row_count() {
vec_model.remove(to_remove as usize); vec_model.remove(to_remove);
} }
} }
@ -1653,7 +1659,7 @@ fn set_json_preview_data(
property_name, property_name,
json, json,
) { ) {
Ok(()) => SharedString::new(), Ok(_) => SharedString::new(),
Err(v) => v.first().cloned().unwrap_or_default().into(), Err(v) => v.first().cloned().unwrap_or_default().into(),
} }
} else { } else {
@ -1754,7 +1760,7 @@ fn as_slint_brush(
} }
match kind { match kind {
BrushKind::Solid => color_to_string(color).into(), BrushKind::Solid => color_to_string(color),
BrushKind::Linear => { BrushKind::Linear => {
format!("@linear-gradient({angle}deg{})", stops_as_string(stops)).into() format!("@linear-gradient({angle}deg{})", stops_as_string(stops)).into()
} }
@ -2382,7 +2388,8 @@ export component X {
type_def: &str, type_def: &str,
type_name: &str, type_name: &str,
code: &str, code: &str,
) -> crate::preview::preview_data::PreviewData { ) -> (crate::preview::preview_data::PreviewDataKey, crate::preview::preview_data::PreviewData)
{
let component_instance = crate::preview::test::interpret_test( let component_instance = crate::preview::test::interpret_test(
"fluent", "fluent",
&format!( &format!(
@ -2394,9 +2401,10 @@ export component Tester {{
"# "#
), ),
); );
let preview_data = let mut data =
preview_data::query_preview_data_properties_and_callbacks(&component_instance); preview_data::query_preview_data_properties_and_callbacks(&component_instance);
return preview_data.get(&preview_data::PropertyContainer::Main).unwrap()[0].clone(); assert_eq!(data.len(), 1);
data.pop_first().unwrap()
} }
fn compare_pv(r: &super::PropertyValue, e: &PropertyValue) { fn compare_pv(r: &super::PropertyValue, e: &PropertyValue) {
@ -2427,10 +2435,9 @@ export component Tester {{
type_name: &str, type_name: &str,
code: &str, code: &str,
expected_data: super::PreviewData, expected_data: super::PreviewData,
) -> preview_data::PreviewData { ) -> (preview_data::PreviewDataKey, preview_data::PreviewData) {
let raw_data = generate_preview_data(visibility, type_def, type_name, code); let (key, value) = generate_preview_data(visibility, type_def, type_name, code);
let rp = super::map_preview_data_property(&key, &value).unwrap();
let rp = super::map_preview_data_property(&raw_data).unwrap();
eprintln!("*** Validating PreviewData: Received: {rp:?}"); eprintln!("*** Validating PreviewData: Received: {rp:?}");
eprintln!("*** Validating PreviewData: Expected: {expected_data:?}"); eprintln!("*** Validating PreviewData: Expected: {expected_data:?}");
@ -2442,7 +2449,7 @@ export component Tester {{
eprintln!("*** PreviewData is as expected..."); eprintln!("*** PreviewData is as expected...");
raw_data (key, value)
} }
fn validate_rp( fn validate_rp(
@ -2453,15 +2460,15 @@ export component Tester {{
expected_data: super::PreviewData, expected_data: super::PreviewData,
expected_value: super::PropertyValue, expected_value: super::PropertyValue,
) { ) {
let rp = validate_rp_impl(visibility, type_def, type_name, code, expected_data); let (_, value) = validate_rp_impl(visibility, type_def, type_name, code, expected_data);
let pv = super::map_preview_data_to_property_value(&rp).unwrap(); let pv = super::map_preview_data_to_property_value(&value).unwrap();
compare_pv(&pv, &expected_value); compare_pv(&pv, &expected_value);
let (is_array, headers, values) = super::map_preview_data_to_property_value_table(&rp); let (is_array, headers, values) = super::map_preview_data_to_property_value_table(&value);
assert!(!is_array); assert!(!is_array);
assert!(headers.len() == 1); assert!(headers.len() == 1);
assert!(headers[0] == ""); assert!(headers[0].is_empty());
assert_eq!(values.len(), 1); assert_eq!(values.len(), 1);
assert_eq!(values.first().unwrap().len(), 1); assert_eq!(values.first().unwrap().len(), 1);
} }
@ -2477,9 +2484,9 @@ export component Tester {{
expected_headers: Vec<String>, expected_headers: Vec<String>,
expected_table: Vec<Vec<super::PropertyValue>>, expected_table: Vec<Vec<super::PropertyValue>>,
) { ) {
let rp = validate_rp_impl(visibility, type_def, type_name, code, expected_data); let (_, value) = validate_rp_impl(visibility, type_def, type_name, code, expected_data);
let pv = super::map_preview_data_to_property_value(&rp).unwrap(); let pv = super::map_preview_data_to_property_value(&value).unwrap();
compare_pv( compare_pv(
&pv, &pv,
&super::PropertyValue { &super::PropertyValue {
@ -2489,7 +2496,7 @@ export component Tester {{
}, },
); );
let (is_array, headers, values) = super::map_preview_data_to_property_value_table(&rp); let (is_array, headers, values) = super::map_preview_data_to_property_value_table(&value);
assert_eq!(is_array, expected_is_array); assert_eq!(is_array, expected_is_array);
@ -2734,7 +2741,6 @@ export component Tester {{
has_getter: true, has_getter: true,
has_setter: true, has_setter: true,
kind: super::PreviewDataKind::Value, kind: super::PreviewDataKind::Value,
..Default::default()
}, },
super::PropertyValue { super::PropertyValue {
display_string: "false".into(), display_string: "false".into(),
@ -2760,7 +2766,6 @@ export component Tester {{
has_getter: true, has_getter: true,
has_setter: true, has_setter: true,
kind: super::PreviewDataKind::Json, kind: super::PreviewDataKind::Json,
..Default::default()
}, },
super::PropertyValue { super::PropertyValue {
kind: super::PropertyValueKind::Code, kind: super::PropertyValueKind::Code,
@ -2784,7 +2789,6 @@ export component Tester {{
has_getter: true, has_getter: true,
has_setter: true, has_setter: true,
kind: super::PreviewDataKind::Table, kind: super::PreviewDataKind::Table,
..Default::default()
}, },
"{\n \"bar\": true,\n \"count\": 23\n}", "{\n \"bar\": true,\n \"count\": 23\n}",
false, false,
@ -2824,7 +2828,6 @@ export component Tester {{
has_getter: true, has_getter: true,
has_setter: true, has_setter: true,
kind: super::PreviewDataKind::Table, kind: super::PreviewDataKind::Table,
..Default::default()
}, },
"{\n \"first\": {\n \"c1-1\": \"first of a kind\",\n \"c1-2\": 23\n },\n \"second\": {\n \"c2-1\": \"second of a kind\",\n \"c2-2\": 42\n }\n}", "{\n \"first\": {\n \"c1-1\": \"first of a kind\",\n \"c1-2\": 23\n },\n \"second\": {\n \"c2-1\": \"second of a kind\",\n \"c2-2\": 42\n }\n}",
false, false,
@ -2884,7 +2887,6 @@ export component Tester {{
has_getter: true, has_getter: true,
has_setter: true, has_setter: true,
kind: super::PreviewDataKind::Table, kind: super::PreviewDataKind::Table,
..Default::default()
}, },
"[\n {\n \"first\": {\n \"c1-1\": \"first of a kind\",\n \"c1-2\": 23\n },\n \"second\": {\n \"c2-1\": \"second of a kind\",\n \"c2-2\": 42\n }\n },\n {\n \"first\": {\n \"c1-1\": \"row 2, 1\",\n \"c1-2\": 3\n },\n \"second\": {\n \"c2-1\": \"row 2, 2\",\n \"c2-2\": 2\n }\n }\n]", "[\n {\n \"first\": {\n \"c1-1\": \"first of a kind\",\n \"c1-2\": 23\n },\n \"second\": {\n \"c2-1\": \"second of a kind\",\n \"c2-2\": 42\n }\n },\n {\n \"first\": {\n \"c1-1\": \"row 2, 1\",\n \"c1-2\": 3\n },\n \"second\": {\n \"c2-1\": \"row 2, 2\",\n \"c2-2\": 2\n }\n }\n]",
true, true,
@ -2969,7 +2971,6 @@ export component Tester {{
has_getter: true, has_getter: true,
has_setter: true, has_setter: true,
kind: super::PreviewDataKind::Table, kind: super::PreviewDataKind::Table,
..Default::default()
}, },
"[\n true,\n false\n]", "[\n true,\n false\n]",
true, true,