mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 12:54:45 +00:00

These are two different concept, and it is confusing to keep them in the same enum We want to support component without any base element, and Void is already used for global component, so do this refactoring before
216 lines
8 KiB
Rust
216 lines
8 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
|
|
|
// cSpell: ignore tabwidget
|
|
|
|
//! Passe lower the TabWidget to create the tabbar.
|
|
//!
|
|
//! Must be done before inlining and many other passes because the lowered code must
|
|
//! be further inlined as it may expends to native widget that needs inlining
|
|
|
|
use crate::diagnostics::BuildDiagnostics;
|
|
use crate::expression_tree::{BindingExpression, Expression, NamedReference, Unit};
|
|
use crate::langtype::{ElementType, Type};
|
|
use crate::object_tree::*;
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
pub async fn lower_tabwidget(
|
|
component: &Rc<Component>,
|
|
type_loader: &mut crate::typeloader::TypeLoader,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
// Ignore import errors
|
|
let mut build_diags_to_ignore = BuildDiagnostics::default();
|
|
let tabwidget_impl = type_loader
|
|
.import_component("std-widgets.slint", "TabWidgetImpl", &mut build_diags_to_ignore)
|
|
.await
|
|
.expect("can't load TabWidgetImpl from std-widgets.slint");
|
|
let tab_impl = type_loader
|
|
.import_component("std-widgets.slint", "TabImpl", &mut build_diags_to_ignore)
|
|
.await
|
|
.expect("can't load TabImpl from std-widgets.slint");
|
|
let tabbar_impl = type_loader
|
|
.import_component("std-widgets.slint", "TabBarImpl", &mut build_diags_to_ignore)
|
|
.await
|
|
.expect("can't load TabBarImpl from std-widgets.slint");
|
|
let rectangle_type =
|
|
type_loader.global_type_registry.borrow().lookup_element("Rectangle").unwrap();
|
|
|
|
recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
|
|
if elem.borrow().base_type.to_string() == "TabWidget" {
|
|
process_tabwidget(
|
|
elem,
|
|
ElementType::Component(tabwidget_impl.clone()),
|
|
ElementType::Component(tab_impl.clone()),
|
|
ElementType::Component(tabbar_impl.clone()),
|
|
&rectangle_type,
|
|
diag,
|
|
);
|
|
}
|
|
})
|
|
}
|
|
|
|
fn process_tabwidget(
|
|
elem: &ElementRc,
|
|
tabwidget_impl: ElementType,
|
|
tab_impl: ElementType,
|
|
tabbar_impl: ElementType,
|
|
rectangle_type: &ElementType,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
elem.borrow_mut().base_type = tabwidget_impl;
|
|
let mut children = std::mem::take(&mut elem.borrow_mut().children);
|
|
let num_tabs = children.len();
|
|
let mut tabs = Vec::new();
|
|
for child in &mut children {
|
|
if child.borrow().repeated.is_some() {
|
|
diag.push_error(
|
|
"dynamic tabs ('if' or 'for') are currently not supported".into(),
|
|
&*child.borrow(),
|
|
);
|
|
continue;
|
|
}
|
|
if child.borrow().base_type.to_string() != "Tab" {
|
|
assert!(diag.has_error());
|
|
continue;
|
|
}
|
|
let index = tabs.len();
|
|
child.borrow_mut().base_type = rectangle_type.clone();
|
|
child.borrow_mut().property_declarations.insert("title".to_owned(), Type::String.into());
|
|
set_geometry_prop(elem, child, "x", diag);
|
|
set_geometry_prop(elem, child, "y", diag);
|
|
set_geometry_prop(elem, child, "width", diag);
|
|
set_geometry_prop(elem, child, "height", diag);
|
|
let condition = Expression::BinaryExpression {
|
|
lhs: Expression::PropertyReference(NamedReference::new(elem, "current-index")).into(),
|
|
rhs: Expression::NumberLiteral(index as _, Unit::None).into(),
|
|
op: '=',
|
|
};
|
|
let old = child
|
|
.borrow_mut()
|
|
.bindings
|
|
.insert("visible".to_owned(), RefCell::new(condition.into()));
|
|
if let Some(old) = old {
|
|
diag.push_error(
|
|
"The property 'visible' cannot be set for Tabs inside a TabWidget".to_owned(),
|
|
&old.into_inner(),
|
|
);
|
|
}
|
|
|
|
let mut tab = Element {
|
|
id: format!("{}-tab{}", elem.borrow().id, index),
|
|
base_type: tab_impl.clone(),
|
|
enclosing_component: elem.borrow().enclosing_component.clone(),
|
|
..Default::default()
|
|
};
|
|
tab.bindings.insert(
|
|
"title".to_owned(),
|
|
BindingExpression::new_two_way(NamedReference::new(child, "title")).into(),
|
|
);
|
|
tab.bindings.insert(
|
|
"current".to_owned(),
|
|
BindingExpression::new_two_way(NamedReference::new(elem, "current-index")).into(),
|
|
);
|
|
tab.bindings.insert(
|
|
"current-focused".to_owned(),
|
|
BindingExpression::new_two_way(NamedReference::new(elem, "current-focused")).into(),
|
|
);
|
|
tab.bindings.insert(
|
|
"tab-index".to_owned(),
|
|
RefCell::new(Expression::NumberLiteral(index as _, Unit::None).into()),
|
|
);
|
|
tab.bindings.insert(
|
|
"num-tabs".to_owned(),
|
|
RefCell::new(Expression::NumberLiteral(num_tabs as _, Unit::None).into()),
|
|
);
|
|
tabs.push(Rc::new(RefCell::new(tab)));
|
|
}
|
|
|
|
let tabbar = Element {
|
|
id: format!("{}-tabbar", elem.borrow().id),
|
|
base_type: tabbar_impl.clone(),
|
|
enclosing_component: elem.borrow().enclosing_component.clone(),
|
|
children: tabs,
|
|
..Default::default()
|
|
};
|
|
let tabbar = Rc::new(RefCell::new(tabbar));
|
|
set_tabbar_geometry_prop(elem, &tabbar, "x");
|
|
set_tabbar_geometry_prop(elem, &tabbar, "y");
|
|
set_tabbar_geometry_prop(elem, &tabbar, "width");
|
|
set_tabbar_geometry_prop(elem, &tabbar, "height");
|
|
tabbar.borrow_mut().bindings.insert(
|
|
"num-tabs".to_owned(),
|
|
RefCell::new(Expression::NumberLiteral(num_tabs as _, Unit::None).into()),
|
|
);
|
|
elem.borrow_mut().bindings.insert(
|
|
"current-index".to_owned(),
|
|
BindingExpression::new_two_way(NamedReference::new(&tabbar, "current")).into(),
|
|
);
|
|
elem.borrow_mut().bindings.insert(
|
|
"current-focused".to_owned(),
|
|
BindingExpression::new_two_way(NamedReference::new(&tabbar, "current-focused")).into(),
|
|
);
|
|
elem.borrow_mut().bindings.insert(
|
|
"tabbar-preferred-width".to_owned(),
|
|
BindingExpression::new_two_way(NamedReference::new(&tabbar, "preferred-width")).into(),
|
|
);
|
|
elem.borrow_mut().bindings.insert(
|
|
"tabbar-preferred-height".to_owned(),
|
|
BindingExpression::new_two_way(NamedReference::new(&tabbar, "preferred-height")).into(),
|
|
);
|
|
|
|
if let Some(expr) = children
|
|
.iter()
|
|
.map(|x| Expression::PropertyReference(NamedReference::new(x, "min-width")))
|
|
.reduce(|lhs, rhs| crate::builtin_macros::min_max_expression(lhs, rhs, '>'))
|
|
{
|
|
elem.borrow_mut().bindings.insert("content-min-width".into(), RefCell::new(expr.into()));
|
|
};
|
|
if let Some(expr) = children
|
|
.iter()
|
|
.map(|x| Expression::PropertyReference(NamedReference::new(x, "min-height")))
|
|
.reduce(|lhs, rhs| crate::builtin_macros::min_max_expression(lhs, rhs, '>'))
|
|
{
|
|
elem.borrow_mut().bindings.insert("content-min-height".into(), RefCell::new(expr.into()));
|
|
};
|
|
|
|
elem.borrow_mut().children = std::iter::once(tabbar).chain(children.into_iter()).collect();
|
|
}
|
|
|
|
fn set_geometry_prop(
|
|
tab_widget: &ElementRc,
|
|
content: &ElementRc,
|
|
prop: &str,
|
|
diag: &mut BuildDiagnostics,
|
|
) {
|
|
let old = content.borrow_mut().bindings.insert(
|
|
prop.into(),
|
|
RefCell::new(
|
|
Expression::PropertyReference(NamedReference::new(
|
|
tab_widget,
|
|
&format!("content-{}", prop),
|
|
))
|
|
.into(),
|
|
),
|
|
);
|
|
if let Some(old) = old.map(RefCell::into_inner) {
|
|
diag.push_error(
|
|
format!("The property '{}' cannot be set for Tabs inside a TabWidget", prop),
|
|
&old,
|
|
);
|
|
}
|
|
}
|
|
|
|
fn set_tabbar_geometry_prop(tab_widget: &ElementRc, tabbar: &ElementRc, prop: &str) {
|
|
tabbar.borrow_mut().bindings.insert(
|
|
prop.into(),
|
|
RefCell::new(
|
|
Expression::PropertyReference(NamedReference::new(
|
|
tab_widget,
|
|
&format!("tabbar-{}", prop),
|
|
))
|
|
.into(),
|
|
),
|
|
);
|
|
}
|