mirror of
https://github.com/slint-ui/slint.git
synced 2025-11-10 00:06:03 +00:00
live-preview: Refactor Api between backend and UI
Only push basic information over the boundary in a model. The values themselves now need to be queried via `Api`. Pros: * Slint tracks the properties automatically, no more Property change tracker! Cons: * Live preview is less useful now as we can not yet provide function call results via ourr data API.
This commit is contained in:
parent
fd0f6e822a
commit
f96ce07287
5 changed files with 252 additions and 460 deletions
|
|
@ -16,7 +16,7 @@ use lsp_types::Url;
|
|||
use slint::PlatformError;
|
||||
use slint_interpreter::{ComponentDefinition, ComponentHandle, ComponentInstance};
|
||||
use std::borrow::BorrowMut;
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
|
@ -126,9 +126,6 @@ struct PreviewState {
|
|||
workspace_edit_sent: bool,
|
||||
known_components: Vec<ComponentInformation>,
|
||||
preview_loading_delay_timer: Option<slint::Timer>,
|
||||
property_change_tracker: OnceCell<
|
||||
std::pin::Pin<Box<i_slint_core::properties::PropertyTracker<PreviewDataDirtyHandler>>>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl PreviewState {
|
||||
|
|
@ -935,40 +932,6 @@ fn extract_resources(
|
|||
result
|
||||
}
|
||||
|
||||
struct PreviewDataDirtyHandler {}
|
||||
|
||||
impl i_slint_core::properties::PropertyDirtyHandler for PreviewDataDirtyHandler {
|
||||
fn notify(self: std::pin::Pin<&Self>) {
|
||||
PREVIEW_STATE.with(|preview_state| {
|
||||
let mut preview_state = preview_state.borrow_mut();
|
||||
|
||||
let preview_data = &track_preview_data(&mut preview_state);
|
||||
|
||||
if let Some(ui) = &preview_state.ui {
|
||||
ui::ui_set_preview_data(ui, preview_data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn track_preview_data(
|
||||
preview_state: &mut std::cell::RefMut<PreviewState>,
|
||||
) -> HashMap<preview_data::PropertyContainer, Vec<preview_data::PreviewData>> {
|
||||
let Some(component_instance) = preview_state.component_instance() else {
|
||||
return Default::default();
|
||||
};
|
||||
|
||||
let mut tracker = preview_state.property_change_tracker.get_or_init(move || {
|
||||
Box::pin(i_slint_core::properties::PropertyTracker::new_with_dirty_handler(
|
||||
PreviewDataDirtyHandler {},
|
||||
))
|
||||
});
|
||||
|
||||
tracker.borrow_mut().as_ref().evaluate_as_dependency_root(|| {
|
||||
preview_data::query_preview_data_properties_and_callbacks(&component_instance)
|
||||
})
|
||||
}
|
||||
|
||||
fn finish_parsing(preview_url: &Url) {
|
||||
set_status_text("");
|
||||
|
||||
|
|
@ -1039,12 +1002,17 @@ fn finish_parsing(preview_url: &Url) {
|
|||
|
||||
preview_state.document_cache.borrow_mut().replace(Some(Rc::new(document_cache)));
|
||||
|
||||
let preview_data = track_preview_data(&mut preview_state);
|
||||
let preview_data = preview_state
|
||||
.component_instance()
|
||||
.map(|component_instance| {
|
||||
preview_data::query_preview_data_properties_and_callbacks(&component_instance)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(ui) = &preview_state.ui {
|
||||
ui::ui_set_uses_widgets(ui, uses_widgets);
|
||||
ui::ui_set_known_components(ui, &preview_state.known_components, index);
|
||||
ui::ui_set_preview_data(ui, &preview_data);
|
||||
ui::ui_set_preview_data(ui, preview_data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,46 @@ impl PreviewData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_preview_data(
|
||||
component_instance: &ComponentInstance,
|
||||
container: PropertyContainer,
|
||||
property_name: String,
|
||||
) -> Option<PreviewData> {
|
||||
fn find_preview_data(
|
||||
property_name: &str,
|
||||
mut it: impl Iterator<
|
||||
Item = (
|
||||
String,
|
||||
(
|
||||
i_slint_compiler::langtype::Type,
|
||||
i_slint_compiler::object_tree::PropertyVisibility,
|
||||
),
|
||||
),
|
||||
>,
|
||||
value_query: &dyn Fn(&str) -> Option<slint_interpreter::Value>,
|
||||
) -> Option<PreviewData> {
|
||||
it.find(|(name, (_, _))| name == property_name).map(|(name, (ty, visibility))| {
|
||||
let value = value_query(&name);
|
||||
|
||||
PreviewData { name, ty, visibility, value }
|
||||
})
|
||||
}
|
||||
|
||||
let definition = &component_instance.definition();
|
||||
match &container {
|
||||
PropertyContainer::Main => {
|
||||
find_preview_data(&property_name, &mut definition.properties_and_callbacks(), &|name| {
|
||||
component_instance.get_property(name).ok()
|
||||
})
|
||||
}
|
||||
PropertyContainer::Global(g) => find_preview_data(
|
||||
&property_name,
|
||||
&mut definition.global_properties_and_callbacks(g)?,
|
||||
&|name| component_instance.get_global_property(g, name).ok(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_preview_data_properties_and_callbacks(
|
||||
component_instance: &ComponentInstance,
|
||||
) -> HashMap<PropertyContainer, Vec<PreviewData>> {
|
||||
|
|
@ -147,38 +187,6 @@ fn find_component_properties_and_callbacks<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_preview_data(
|
||||
component_instance: &ComponentInstance,
|
||||
container: PropertyContainer,
|
||||
property_name: String,
|
||||
values: Vec<Vec<String>>,
|
||||
) -> Result<(), String> {
|
||||
let definition = &component_instance.definition();
|
||||
|
||||
let (_, (ty, _)) = find_component_properties_and_callbacks(definition, &container)?
|
||||
.find(|(name, (_, _))| name == &property_name)
|
||||
.ok_or_else(|| {
|
||||
format!("Property name {property_name} not found on component {container}")
|
||||
})?;
|
||||
|
||||
if values.len() == 1 && values[0].len() == 1 {
|
||||
let json_value: serde_json::Value = serde_json::from_str(&values[0][0])
|
||||
.map_err(|e| format!("Failed to read value as JSON: {e}"))?;
|
||||
let value = slint_interpreter::json::value_from_json(&ty, &json_value)?;
|
||||
|
||||
match &container {
|
||||
PropertyContainer::Main => component_instance
|
||||
.set_property(&property_name, value)
|
||||
.map_err(|e| format!("Failed to set property: {e}"))?,
|
||||
PropertyContainer::Global(g) => component_instance
|
||||
.set_global_property(g, &property_name, value)
|
||||
.map_err(|e| format!("Failed to set global property: {e}"))?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_json_preview_data(
|
||||
component_instance: &ComponentInstance,
|
||||
container: PropertyContainer,
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ pub fn create_ui(style: String, experimental: bool) -> Result<PreviewUi, Platfor
|
|||
api.on_set_color_binding(super::set_color_binding);
|
||||
api.on_property_declaration_ranges(super::property_declaration_ranges);
|
||||
|
||||
api.on_set_preview_data(set_preview_data);
|
||||
api.on_get_property_value(get_property_value);
|
||||
api.on_set_json_preview_data(set_json_preview_data);
|
||||
|
||||
api.on_string_to_code(string_to_code);
|
||||
|
|
@ -794,16 +794,12 @@ fn update_grouped_properties(
|
|||
to_do.push(Op::Remove(c_index));
|
||||
cp = c_it.next();
|
||||
}
|
||||
(Some(c), Some(n)) => {
|
||||
if c.name < n.name {
|
||||
(Some(c), Some(n)) => match c.name.cmp(&n.name) {
|
||||
std::cmp::Ordering::Less => {
|
||||
to_do.push(Op::Remove(c_index));
|
||||
cp = c_it.next();
|
||||
} else if c.name > n.name {
|
||||
to_do.push(Op::Insert((c_index, n_index)));
|
||||
c_index += 1;
|
||||
n_index += 1;
|
||||
np = n_it.next();
|
||||
} else {
|
||||
}
|
||||
std::cmp::Ordering::Equal => {
|
||||
if !is_equal_property(c, n) {
|
||||
to_do.push(Op::Copy((c_index, n_index)));
|
||||
}
|
||||
|
|
@ -812,7 +808,13 @@ fn update_grouped_properties(
|
|||
cp = c_it.next();
|
||||
np = n_it.next();
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
to_do.push(Op::Insert((c_index, n_index)));
|
||||
c_index += 1;
|
||||
n_index += 1;
|
||||
np = n_it.next();
|
||||
}
|
||||
},
|
||||
(None, Some(_)) => {
|
||||
to_do.push(Op::PushBack(n_index));
|
||||
n_index += 1;
|
||||
|
|
@ -1030,51 +1032,44 @@ fn map_value_and_type(
|
|||
}
|
||||
}
|
||||
|
||||
fn map_preview_data_property(rp: &preview_data::PreviewData) -> Option<PreviewData> {
|
||||
if !rp.is_property() {
|
||||
fn map_preview_data_to_property_value(
|
||||
preview_data: &preview_data::PreviewData,
|
||||
) -> Option<PropertyValue> {
|
||||
let mut mapping = ValueMapping::default();
|
||||
map_value_and_type(&preview_data.ty, &preview_data.value, &mut mapping);
|
||||
mapping.array_values.first().and_then(|av| av.first()).cloned()
|
||||
}
|
||||
|
||||
fn map_preview_data_property(preview_data: &preview_data::PreviewData) -> Option<PreviewData> {
|
||||
if !preview_data.is_property() {
|
||||
return None;
|
||||
};
|
||||
|
||||
let has_getter = rp.has_getter();
|
||||
let has_setter = rp.has_setter();
|
||||
let has_getter = preview_data.has_getter();
|
||||
let has_setter = preview_data.has_setter();
|
||||
|
||||
let mut mapping = ValueMapping::default();
|
||||
map_value_and_type(&rp.ty, &rp.value, &mut mapping);
|
||||
|
||||
let array_values = mapping
|
||||
.array_values
|
||||
.drain(..)
|
||||
.map(|v| Rc::new(slint::VecModel::from(v)).into())
|
||||
.collect::<Vec<slint::ModelRc<_>>>();
|
||||
let array_values = Rc::new(slint::VecModel::from(array_values)).into();
|
||||
map_value_and_type(&preview_data.ty, &preview_data.value, &mut mapping);
|
||||
|
||||
Some(PreviewData {
|
||||
name: rp.name.clone().into(),
|
||||
name: preview_data.name.clone().into(),
|
||||
has_getter,
|
||||
has_setter,
|
||||
prefer_json: mapping.is_too_complex,
|
||||
is_array: mapping.is_array,
|
||||
header: Rc::new(VecModel::from(
|
||||
mapping.header.drain(..).map(slint::SharedString::from).collect::<Vec<_>>(),
|
||||
))
|
||||
.into(),
|
||||
array_values,
|
||||
kind: if mapping.is_too_complex { PreviewDataKind::Json } else { PreviewDataKind::Value },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ui_set_preview_data(
|
||||
ui: &PreviewUi,
|
||||
preview_data: &HashMap<preview_data::PropertyContainer, Vec<preview_data::PreviewData>>,
|
||||
preview_data: HashMap<preview_data::PropertyContainer, Vec<preview_data::PreviewData>>,
|
||||
) {
|
||||
let mut result: Vec<PropertyContainer> = vec![];
|
||||
|
||||
fn fill_container(
|
||||
container_name: String,
|
||||
container_id: String,
|
||||
properties: &[preview_data::PreviewData],
|
||||
) -> Option<PropertyContainer> {
|
||||
let properties =
|
||||
properties.iter().filter_map(|rp| map_preview_data_property(rp)).collect::<Vec<_>>();
|
||||
properties.iter().filter_map(map_preview_data_property).collect::<Vec<_>>();
|
||||
|
||||
(!properties.is_empty()).then(|| PropertyContainer {
|
||||
container_name: container_name.into(),
|
||||
|
|
@ -1083,11 +1078,14 @@ pub fn ui_set_preview_data(
|
|||
})
|
||||
}
|
||||
|
||||
let mut result: Vec<PropertyContainer> = vec![];
|
||||
|
||||
if let Some(main) = preview_data.get(&preview_data::PropertyContainer::Main) {
|
||||
if let Some(c) = fill_container("<MAIN>".to_string(), String::new(), main) {
|
||||
result.push(c)
|
||||
}
|
||||
}
|
||||
|
||||
for component_key in
|
||||
preview_data.keys().filter(|k| **k != preview_data::PropertyContainer::Main)
|
||||
{
|
||||
|
|
@ -1101,85 +1099,31 @@ pub fn ui_set_preview_data(
|
|||
|
||||
let api = ui.global::<Api>();
|
||||
|
||||
let mut old_model = api.get_preview_data();
|
||||
let old_model = api.get_preview_data();
|
||||
|
||||
fn update_model(
|
||||
old_model: &mut slint::ModelRc<PropertyContainer>,
|
||||
new_model: Vec<PropertyContainer>,
|
||||
) -> Option<slint::ModelRc<PropertyContainer>> {
|
||||
// The structure should never change as the API between business logic and UI should be pretty
|
||||
// fixed.
|
||||
|
||||
fn m(model: Vec<PropertyContainer>) -> Option<slint::ModelRc<PropertyContainer>> {
|
||||
Some(Rc::new(VecModel::from(model)).into())
|
||||
}
|
||||
|
||||
fn is_semantic_equal(o: &PreviewData, n: &PreviewData) -> bool {
|
||||
if o.name != n.name
|
||||
|| o.has_getter != n.has_getter
|
||||
|| o.has_setter != n.has_setter
|
||||
|| o.is_array != n.is_array
|
||||
|| o.prefer_json != n.prefer_json
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if o.header.row_count() != n.header.row_count() {
|
||||
return false;
|
||||
}
|
||||
for (oh, nh) in o.header.iter().zip(n.header.iter()) {
|
||||
if oh != nh {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if o.array_values.row_count() != n.array_values.row_count() {
|
||||
return false;
|
||||
}
|
||||
for (oav, nav) in o.array_values.iter().zip(n.array_values.iter()) {
|
||||
if oav.row_count() != nav.row_count() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (oavv, navv) in oav.iter().zip(nav.iter()) {
|
||||
if oavv != navv {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
old_model: &slint::ModelRc<PropertyContainer>,
|
||||
new_model: &[PropertyContainer],
|
||||
) -> bool {
|
||||
if old_model.row_count() != new_model.len() {
|
||||
return m(new_model);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (oc, nc) in old_model.iter().zip(new_model.iter()) {
|
||||
if oc.container_name != nc.container_name
|
||||
|| oc.container_id != nc.container_id
|
||||
|| oc.properties.row_count() != nc.properties.row_count()
|
||||
|| oc.properties.iter().zip(nc.properties.iter()).any(|(o, n)| o != n)
|
||||
{
|
||||
return m(new_model);
|
||||
}
|
||||
|
||||
let mut to_replace = oc
|
||||
.properties
|
||||
.iter()
|
||||
.zip(nc.properties.iter())
|
||||
.enumerate()
|
||||
.filter_map(|(i, (o, n))| (!is_semantic_equal(&o, &n)).then_some((i, n)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, n) in to_replace.drain(..) {
|
||||
oc.properties.set_row_data(i, n);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
false
|
||||
}
|
||||
|
||||
if let Some(m) = update_model(&mut old_model, result) {
|
||||
api.set_preview_data(m);
|
||||
if update_model(&old_model, &result) {
|
||||
api.set_preview_data(Rc::new(VecModel::from(result)).into());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1191,60 +1135,45 @@ fn to_property_container(container: slint::SharedString) -> preview_data::Proper
|
|||
}
|
||||
}
|
||||
|
||||
fn set_preview_data(
|
||||
container: SharedString,
|
||||
property_name: SharedString,
|
||||
model: slint::ModelRc<slint::ModelRc<PropertyValue>>,
|
||||
) -> bool {
|
||||
if model.row_count() == 0 || property_name.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let values = model
|
||||
.iter()
|
||||
.map(|r| {
|
||||
r.iter()
|
||||
.map(|c| if c.was_edited { c.edited_value.to_string() } else { c.code.to_string() })
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Some(component_instance) = preview::component_instance() {
|
||||
preview_data::set_preview_data(
|
||||
fn get_property_value(container: SharedString, property_name: SharedString) -> PropertyValue {
|
||||
preview::component_instance()
|
||||
.and_then(|component_instance| {
|
||||
preview_data::get_preview_data(
|
||||
&component_instance,
|
||||
to_property_container(container),
|
||||
property_name.to_string(),
|
||||
values,
|
||||
)
|
||||
.is_ok()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.and_then(|pd| map_preview_data_to_property_value(&pd))
|
||||
.unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
fn set_json_preview_data(
|
||||
container: SharedString,
|
||||
property_name: SharedString,
|
||||
json_string: SharedString,
|
||||
) {
|
||||
) -> bool {
|
||||
let property_name = (!property_name.is_empty()).then_some(property_name.to_string());
|
||||
|
||||
let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_string.to_string()) else {
|
||||
return;
|
||||
let Ok(json) = serde_json::from_str::<serde_json::Value>(json_string.as_ref()) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if property_name.is_none() && !json.is_object() {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(component_instance) = preview::component_instance() {
|
||||
let _ = preview_data::set_json_preview_data(
|
||||
preview::component_instance()
|
||||
.and_then(|component_instance| {
|
||||
preview_data::set_json_preview_data(
|
||||
&component_instance,
|
||||
to_property_container(container),
|
||||
property_name,
|
||||
json,
|
||||
);
|
||||
};
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn update_properties(
|
||||
|
|
@ -1309,7 +1238,7 @@ mod tests {
|
|||
|
||||
use i_slint_core::model::Model;
|
||||
|
||||
use super::{map_preview_data_property, PropertyInformation, PropertyValue, PropertyValueKind};
|
||||
use super::{PropertyInformation, PropertyValue, PropertyValueKind};
|
||||
|
||||
fn properties_at_position(
|
||||
source: &str,
|
||||
|
|
@ -1945,46 +1874,23 @@ export component Tester {{
|
|||
type_def: &str,
|
||||
type_name: &str,
|
||||
code: &str,
|
||||
expected: super::PreviewData,
|
||||
expected_data: super::PreviewData,
|
||||
expected_value: super::PropertyValue,
|
||||
) {
|
||||
let rp = generate_preview_data(visibility, type_def, type_name, code);
|
||||
let raw_data = generate_preview_data(visibility, type_def, type_name, code);
|
||||
|
||||
let rp = map_preview_data_property(&rp).unwrap();
|
||||
let rp = super::map_preview_data_property(&raw_data).unwrap();
|
||||
|
||||
eprintln!("*** Validating PreviewData: Received: {rp:?}");
|
||||
eprintln!("*** Validating PreviewData: Expected: {expected:?}");
|
||||
eprintln!("*** Validating PreviewData: Expected: {expected_data:?}");
|
||||
|
||||
assert_eq!(rp.name, expected.name);
|
||||
assert_eq!(rp.has_getter, expected.has_getter);
|
||||
assert_eq!(rp.has_setter, expected.has_setter);
|
||||
assert_eq!(rp.is_array, expected.is_array);
|
||||
assert_eq!(rp.prefer_json, expected.prefer_json);
|
||||
assert_eq!(rp.name, expected_data.name);
|
||||
assert_eq!(rp.has_getter, expected_data.has_getter);
|
||||
assert_eq!(rp.has_setter, expected_data.has_setter);
|
||||
assert_eq!(rp.kind, expected_data.kind);
|
||||
|
||||
eprintln!("*** Basic properties all match, looking at values next...");
|
||||
|
||||
eprintln!("*** Headers: Received:");
|
||||
for h in rp.header.iter() {
|
||||
eprintln!("*** {h}.");
|
||||
}
|
||||
eprintln!("*** Headers: Expected:");
|
||||
for h in expected.header.iter() {
|
||||
eprintln!("*** {h}.");
|
||||
}
|
||||
|
||||
assert_eq!(rp.header.row_count(), expected.header.row_count());
|
||||
for (r, e) in rp.header.iter().zip(expected.header.iter()) {
|
||||
assert_eq!(r, e);
|
||||
}
|
||||
|
||||
eprintln!("*** Values all match, looking at array_values next...");
|
||||
|
||||
assert_eq!(rp.array_values.row_count(), expected.array_values.row_count());
|
||||
for (rr, er) in rp.array_values.iter().zip(expected.array_values.iter()) {
|
||||
assert_eq!(rr.row_count(), er.row_count());
|
||||
for (e, r) in rr.iter().zip(er.iter()) {
|
||||
compare_pv(&r, &e);
|
||||
}
|
||||
}
|
||||
let pv = super::map_preview_data_to_property_value(&raw_data).unwrap();
|
||||
compare_pv(&pv, &expected_value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1997,18 +1903,14 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "\"Test\"".into(),
|
||||
kind: super::PropertyValueKind::String,
|
||||
value_string: "Test".into(),
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2023,9 +1925,10 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "100".into(),
|
||||
kind: super::PropertyValueKind::Float,
|
||||
value_float: 100.0,
|
||||
|
|
@ -2041,11 +1944,6 @@ export component Tester {{
|
|||
]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2060,9 +1958,10 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "100000".into(),
|
||||
kind: super::PropertyValueKind::Float,
|
||||
value_float: 100000.0,
|
||||
|
|
@ -2074,11 +1973,6 @@ export component Tester {{
|
|||
.into(),
|
||||
default_selection: 1,
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2093,9 +1987,10 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "36000".into(),
|
||||
kind: super::PropertyValueKind::Float,
|
||||
value_float: 36000.0,
|
||||
|
|
@ -2108,11 +2003,6 @@ export component Tester {{
|
|||
]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2127,20 +2017,15 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "10".into(),
|
||||
kind: super::PropertyValueKind::Float,
|
||||
value_float: 10.0,
|
||||
value_string: "10%".into(),
|
||||
visual_items: std::rc::Rc::new(slint::VecModel::from(vec!["%".into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
visual_items: std::rc::Rc::new(slint::VecModel::from(vec!["%".into()])).into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
|
@ -2156,9 +2041,10 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "\"#aabbccff\"".into(),
|
||||
kind: super::PropertyValueKind::Color,
|
||||
value_string: "#aabbccff".into(),
|
||||
|
|
@ -2166,11 +2052,6 @@ export component Tester {{
|
|||
0xff, 0xaa, 0xbb, 0xcc,
|
||||
)),
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2185,19 +2066,15 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "12".into(),
|
||||
kind: super::PropertyValueKind::Integer,
|
||||
value_string: "12".into(),
|
||||
value_int: 12,
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2212,19 +2089,15 @@ export component Tester {{
|
|||
super::PreviewData {
|
||||
name: "test".into(),
|
||||
has_getter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "true".into(),
|
||||
kind: super::PropertyValueKind::Boolean,
|
||||
value_string: "true".into(),
|
||||
value_bool: true,
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2240,19 +2113,15 @@ export component Tester {{
|
|||
name: "test".into(),
|
||||
has_getter: true,
|
||||
has_setter: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PreviewDataKind::Value,
|
||||
..Default::default()
|
||||
},
|
||||
super::PropertyValue {
|
||||
code: "false".into(),
|
||||
kind: super::PropertyValueKind::Boolean,
|
||||
value_string: "false".into(),
|
||||
value_bool: false,
|
||||
..Default::default()
|
||||
}]),
|
||||
)
|
||||
.into()]))
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -2270,16 +2139,13 @@ export component Tester {{
|
|||
name: "test".into(),
|
||||
has_getter: true,
|
||||
has_setter: true,
|
||||
prefer_json: true,
|
||||
header: std::rc::Rc::new(slint::VecModel::from(vec!["".into()])).into(),
|
||||
array_values: std::rc::Rc::new(slint::VecModel::from(vec![std::rc::Rc::new(
|
||||
slint::VecModel::from(vec![super::PropertyValue {
|
||||
kind: super::PropertyValueKind::Code,
|
||||
code: "{\n \"first\": [\n \"first of a kind\",\n \"second of a kind\"\n ]\n}".into(),
|
||||
kind: super::PreviewDataKind::Json,
|
||||
..Default::default()
|
||||
},
|
||||
])).into(),
|
||||
]))
|
||||
super::PropertyValue {
|
||||
kind: super::PropertyValueKind::Code,
|
||||
code:
|
||||
"{\n \"first\": [\n \"first of a kind\",\n \"second of a kind\"\n ]\n}"
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -166,14 +166,16 @@ export struct PropertyGroup {
|
|||
properties: [PropertyInformation],
|
||||
}
|
||||
|
||||
export enum PreviewDataKind {
|
||||
Value,
|
||||
Json,
|
||||
}
|
||||
|
||||
export struct PreviewData {
|
||||
name: string,
|
||||
has-getter: bool,
|
||||
has-setter: bool,
|
||||
is-array: bool,
|
||||
prefer-json: bool,
|
||||
header: [string], // "Simple" values + table headers
|
||||
array-values: [[PropertyValue]], // The "Table values"
|
||||
kind: PreviewDataKind,
|
||||
}
|
||||
|
||||
/// Information on exported components and their properties
|
||||
|
|
@ -406,54 +408,7 @@ export global Api {
|
|||
|
||||
// ## preview data
|
||||
|
||||
in-out property <[PropertyContainer]> preview-data: [
|
||||
{
|
||||
container-name: "Fruits",
|
||||
properties: [
|
||||
{
|
||||
name: "AppleSetter",
|
||||
has-getter: false,
|
||||
has-setter: true,
|
||||
is-array: false,
|
||||
prefer-json: false,
|
||||
header: [ "" ],
|
||||
array-values: [[{
|
||||
kind: PropertyValueKind.boolean,
|
||||
value-bool: true,
|
||||
}]],
|
||||
},
|
||||
{
|
||||
name: "BananaGetter",
|
||||
has-getter: true,
|
||||
has-setter: true,
|
||||
is-array: false,
|
||||
prefer-json: true,
|
||||
header: [ "fruit" ],
|
||||
array-values: [[{
|
||||
kind: PropertyValueKind.code,
|
||||
code: "{\n json: true\n}"
|
||||
}]]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
container-name: "Vegetables",
|
||||
properties: [
|
||||
{
|
||||
name: "Cucumber",
|
||||
has-getter: true,
|
||||
has-setter: true,
|
||||
is-array: false,
|
||||
prefer-json: false,
|
||||
header: [ "" ],
|
||||
array-values: [[{
|
||||
kind: PropertyValueKind.string,
|
||||
value-string: "a green banana",
|
||||
}]],
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
in-out property <[PropertyContainer]> preview-data;
|
||||
|
||||
// # Callbacks
|
||||
|
||||
|
|
@ -517,8 +472,9 @@ export global Api {
|
|||
pure callback string-to-code(value: string, is_translatable: bool, tr_context: string, tr_plural: string, tr_plural_expression: string) -> string;
|
||||
|
||||
// ## preview data
|
||||
callback set-preview-data(component: string, name: string, raw-json-array-values: [[PropertyValue]]) -> bool;
|
||||
callback set-json-preview-data(component: string, name: string, json-value: string);
|
||||
pure callback get-property-value(component: string, name: string) -> PropertyValue;
|
||||
|
||||
callback set-json-preview-data(component: string, name: string, json-value: string) -> bool;
|
||||
|
||||
// Get the property declaration/definition ranges
|
||||
callback property-declaration-ranges(property-name: string) -> PropertyDeclaration;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { Button, CheckBox, ComboBox, LineEdit, Palette, Slider, TextEdit } from "std-widgets.slint";
|
||||
|
||||
import { Api, ColorData, ElementInformation, PreviewData, PropertyContainer, PropertyInformation, PropertyValue, PropertyValueKind } from "../api.slint";
|
||||
import { Api, ColorData, ElementInformation, PreviewData, PreviewDataKind, PropertyContainer, PropertyInformation, PropertyValue, PropertyValueKind } from "../api.slint";
|
||||
import { BodyStrongText } from "../components/body-strong-text.slint";
|
||||
import { BodyText } from "../components/body-text.slint";
|
||||
import { StateLayer } from "../components/state-layer.slint";
|
||||
|
|
@ -889,6 +889,12 @@ export component PreviewDataPropertyValueWidget inherits VerticalLayout {
|
|||
in property <PreviewData> preview-data;
|
||||
in property <string> property-container-name;
|
||||
|
||||
private property <PropertyValue> value: Api.get-property-value(root.property-container-name, root.preview-data.name);
|
||||
|
||||
changed value => {
|
||||
debug("\{self.property-container-name}.\{self.preview-data.name}: VALUE CHANGED TO \{self.value.code}");
|
||||
}
|
||||
|
||||
callback edit-in-spreadsheet(rp: PropertyContainer);
|
||||
|
||||
function reset-action() {
|
||||
|
|
@ -896,32 +902,20 @@ export component PreviewDataPropertyValueWidget inherits VerticalLayout {
|
|||
}
|
||||
|
||||
function set-code-binding(text: string) -> bool {
|
||||
root.value.was-edited = true;
|
||||
root.value.edited-value = text;
|
||||
|
||||
root.array-values[0][0] = root.value;
|
||||
|
||||
return(Api.set-preview-data(root.property-container-name, root.preview-data.name, self.array-values));
|
||||
return(Api.set-json-preview-data(root.property-container-name, root.preview-data.name, text));
|
||||
}
|
||||
|
||||
private property <bool> is-simple: preview-data.header.length == 1 && !preview-data.is-array && !preview-data.prefer-json;
|
||||
private property <bool> show-json: preview-data.prefer-json;
|
||||
|
||||
private property <[[PropertyValue]]> array-values: preview-data.array-values;
|
||||
|
||||
property <PropertyValue> value: root.preview-data.array-values[0][0];
|
||||
|
||||
if is-simple && value.kind == PropertyValueKind.code: CodeWidget {
|
||||
if root.preview-data.kind == PreviewDataKind.Value && value.kind == PropertyValueKind.code: CodeWidget {
|
||||
enabled: root.preview-data.has-setter;
|
||||
property-name: root.preview-data.name;
|
||||
property-value: value;
|
||||
property-value <=> root.value;
|
||||
has-code-action: false;
|
||||
|
||||
reset-action() => {
|
||||
root.reset-action();
|
||||
}
|
||||
}
|
||||
if is-simple && value.kind != PropertyValueKind.code: PropertyValueWidget {
|
||||
if root.preview-data.kind == PreviewDataKind.Value && value.kind != PropertyValueKind.code: PropertyValueWidget {
|
||||
property-value <=> root.value;
|
||||
property-name: root.preview-data.name;
|
||||
enabled: root.preview-data.has-setter;
|
||||
|
|
@ -951,10 +945,10 @@ export component PreviewDataPropertyValueWidget inherits VerticalLayout {
|
|||
return(root.set-code-binding(is_translated ? "\"\{text}\"" : text));
|
||||
}
|
||||
}
|
||||
if show-json: EditJsonWidget {
|
||||
if root.preview-data.kind == PreviewDataKind.Json: EditJsonWidget {
|
||||
enabled: root.preview-data.has-setter;
|
||||
property-name: root.preview-data.name;
|
||||
property-value: value;
|
||||
property-value <=> root.value;
|
||||
|
||||
set-code-binding(text) => {
|
||||
root.set-code-binding(text);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue