mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-03 15:14:35 +00:00
Testing: Change ElementReference to be weak
We don't want testing variables to affect the life-cycle of elements in the UI.
This commit is contained in:
parent
cca570549c
commit
d8c1096149
5 changed files with 153 additions and 65 deletions
|
@ -22,7 +22,7 @@ inline void init()
|
|||
/// Use find_by_accessible_label() to obtain all elements matching the given accessible label.
|
||||
class ElementHandle
|
||||
{
|
||||
cbindgen_private::ItemRc inner;
|
||||
cbindgen_private::ItemWeak inner;
|
||||
|
||||
public:
|
||||
/// Find all elements matching the given accessible label.
|
||||
|
@ -38,41 +38,47 @@ public:
|
|||
SharedVector<ElementHandle> result;
|
||||
cbindgen_private::slint_testing_element_find_by_accessible_label(
|
||||
&vrc, &label_view,
|
||||
reinterpret_cast<SharedVector<cbindgen_private::ItemRc> *>(&result));
|
||||
reinterpret_cast<SharedVector<cbindgen_private::ItemWeak> *>(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns true if the underlying element still exists; false otherwise.
|
||||
bool is_valid() const { return private_api::upgrade_item_weak(inner).has_value(); }
|
||||
|
||||
/// Returns the accessible-label of that element, if any.
|
||||
std::optional<SharedString> accessible_label() const
|
||||
{
|
||||
if (auto item = private_api::upgrade_item_weak(inner)) {
|
||||
SharedString result;
|
||||
if (inner.item_tree.vtable()->accessible_string_property(
|
||||
inner.item_tree.borrow(), inner.index,
|
||||
if (item->item_tree.vtable()->accessible_string_property(
|
||||
item->item_tree.borrow(), item->index,
|
||||
cbindgen_private::AccessibleStringProperty::Label, &result)) {
|
||||
return result;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Returns the accessible-value of that element, if any.
|
||||
std::optional<SharedString> accessible_value() const
|
||||
{
|
||||
if (auto item = private_api::upgrade_item_weak(inner)) {
|
||||
SharedString result;
|
||||
if (inner.item_tree.vtable()->accessible_string_property(
|
||||
inner.item_tree.borrow(), inner.index,
|
||||
if (item->item_tree.vtable()->accessible_string_property(
|
||||
item->item_tree.borrow(), item->index,
|
||||
cbindgen_private::AccessibleStringProperty::Value, &result)) {
|
||||
return result;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Sets the accessible-value of that element.
|
||||
///
|
||||
/// Setting the value will invoke the `accessible-action-set-value` callback.
|
||||
void set_accessible_value(SharedString value) const
|
||||
{
|
||||
if (auto item = private_api::upgrade_item_weak(inner)) {
|
||||
union SetValueHelper {
|
||||
cbindgen_private::AccessibilityAction action;
|
||||
SetValueHelper(SharedString value)
|
||||
|
@ -86,13 +92,16 @@ public:
|
|||
~SetValueHelper() { action.set_value.~SetValue_Body(); }
|
||||
|
||||
} action(std::move(value));
|
||||
inner.item_tree.vtable()->accessibility_action(inner.item_tree.borrow(), inner.index,
|
||||
item->item_tree.vtable()->accessibility_action(item->item_tree.borrow(), item->index,
|
||||
&action.action);
|
||||
}
|
||||
}
|
||||
|
||||
/// Invokes the default accessibility action of that element (`accessible-action-default`).
|
||||
/// Invokes the default accessibility action of that element
|
||||
/// (`accessible-action-default`).
|
||||
void invoke_default_action() const
|
||||
{
|
||||
if (auto item = private_api::upgrade_item_weak(inner)) {
|
||||
union DefaultActionHelper {
|
||||
cbindgen_private::AccessibilityAction action;
|
||||
DefaultActionHelper()
|
||||
|
@ -103,26 +112,35 @@ public:
|
|||
~DefaultActionHelper() { }
|
||||
|
||||
} action;
|
||||
inner.item_tree.vtable()->accessibility_action(inner.item_tree.borrow(), inner.index,
|
||||
item->item_tree.vtable()->accessibility_action(item->item_tree.borrow(), item->index,
|
||||
&action.action);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the size of this element
|
||||
LogicalSize size() const
|
||||
{
|
||||
auto rect = inner.item_tree.vtable()->item_geometry(inner.item_tree.borrow(), inner.index);
|
||||
if (auto item = private_api::upgrade_item_weak(inner)) {
|
||||
auto rect =
|
||||
item->item_tree.vtable()->item_geometry(item->item_tree.borrow(), item->index);
|
||||
return LogicalSize({ rect.width, rect.height });
|
||||
}
|
||||
return LogicalSize({ 0, 0 });
|
||||
}
|
||||
|
||||
/// Returns the absolute position of this element
|
||||
LogicalPosition absolute_position() const
|
||||
{
|
||||
if (auto item = private_api::upgrade_item_weak(inner)) {
|
||||
cbindgen_private::LogicalRect rect =
|
||||
inner.item_tree.vtable()->item_geometry(inner.item_tree.borrow(), inner.index);
|
||||
cbindgen_private::LogicalPoint abs = slint::cbindgen_private::slint_item_absolute_position(
|
||||
&inner.item_tree, inner.index);
|
||||
item->item_tree.vtable()->item_geometry(item->item_tree.borrow(), item->index);
|
||||
cbindgen_private::LogicalPoint abs =
|
||||
slint::cbindgen_private::slint_item_absolute_position(&item->item_tree,
|
||||
item->index);
|
||||
return LogicalPosition({ abs.x + rect.x, abs.y + rect.y });
|
||||
}
|
||||
return LogicalPosition({ 0, 0 });
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -114,6 +114,16 @@ inline vtable::Layout drop_in_place(ItemTreeRef item_tree)
|
|||
# endif
|
||||
#endif // !defined(DOXYGEN)
|
||||
|
||||
inline std::optional<cbindgen_private::ItemRc>
|
||||
upgrade_item_weak(const cbindgen_private::ItemWeak &item_weak)
|
||||
{
|
||||
if (auto item_tree_strong = item_weak.item_tree.lock()) {
|
||||
return { { *item_tree_strong, item_weak.index } };
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace private_api
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial
|
||||
|
||||
use i_slint_core::accessibility::AccessibleStringProperty;
|
||||
use i_slint_core::item_tree::ItemTreeRc;
|
||||
use i_slint_core::items::ItemRc;
|
||||
use i_slint_core::item_tree::{ItemTreeRc, ItemWeak};
|
||||
use i_slint_core::slice::Slice;
|
||||
use i_slint_core::SharedVector;
|
||||
|
||||
|
@ -16,7 +15,7 @@ pub extern "C" fn slint_testing_init_backend() {
|
|||
pub extern "C" fn slint_testing_element_find_by_accessible_label(
|
||||
root: &ItemTreeRc,
|
||||
label: &Slice<u8>,
|
||||
out: &mut SharedVector<ItemRc>,
|
||||
out: &mut SharedVector<ItemWeak>,
|
||||
) {
|
||||
let Ok(label) = core::str::from_utf8(label.as_slice()) else { return };
|
||||
*out = crate::search_api::search_item(root, |item| {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial
|
||||
|
||||
use i_slint_core::accessibility::{AccessibilityAction, AccessibleStringProperty};
|
||||
use i_slint_core::item_tree::{ItemTreeRc, ItemVisitorResult, TraversalOrder};
|
||||
use i_slint_core::item_tree::{ItemTreeRc, ItemVisitorResult, ItemWeak, TraversalOrder};
|
||||
use i_slint_core::items::ItemRc;
|
||||
use i_slint_core::window::WindowInner;
|
||||
use i_slint_core::{SharedString, SharedVector};
|
||||
|
@ -10,7 +10,7 @@ use i_slint_core::{SharedString, SharedVector};
|
|||
pub(crate) fn search_item(
|
||||
item_tree: &ItemTreeRc,
|
||||
mut filter: impl FnMut(&ItemRc) -> bool,
|
||||
) -> SharedVector<ItemRc> {
|
||||
) -> SharedVector<ItemWeak> {
|
||||
let mut result = SharedVector::default();
|
||||
i_slint_core::item_tree::visit_items(
|
||||
item_tree,
|
||||
|
@ -18,7 +18,7 @@ pub(crate) fn search_item(
|
|||
|parent_tree, _, index, _| {
|
||||
let item_rc = ItemRc::new(parent_tree.clone(), index);
|
||||
if filter(&item_rc) {
|
||||
result.push(item_rc);
|
||||
result.push(item_rc.downgrade());
|
||||
}
|
||||
ItemVisitorResult::Continue(())
|
||||
},
|
||||
|
@ -27,9 +27,13 @@ pub(crate) fn search_item(
|
|||
result
|
||||
}
|
||||
|
||||
pub struct ElementHandle(ItemRc);
|
||||
pub struct ElementHandle(ItemWeak);
|
||||
|
||||
impl ElementHandle {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.0.upgrade().is_some()
|
||||
}
|
||||
|
||||
pub fn find_by_accessible_label(
|
||||
component: &impl i_slint_core::api::ComponentHandle,
|
||||
label: &str,
|
||||
|
@ -44,29 +48,47 @@ impl ElementHandle {
|
|||
}
|
||||
|
||||
pub fn invoke_default_action(&self) {
|
||||
self.0.accessible_action(&AccessibilityAction::Default)
|
||||
if let Some(item) = self.0.upgrade() {
|
||||
item.accessible_action(&AccessibilityAction::Default)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn accessible_value(&self) -> Option<SharedString> {
|
||||
self.0.accessible_string_property(AccessibleStringProperty::Value)
|
||||
self.0
|
||||
.upgrade()
|
||||
.and_then(|item| item.accessible_string_property(AccessibleStringProperty::Value))
|
||||
}
|
||||
|
||||
pub fn set_accessible_value(&self, value: SharedString) {
|
||||
self.0.accessible_action(&AccessibilityAction::SetValue(value))
|
||||
if let Some(item) = self.0.upgrade() {
|
||||
item.accessible_action(&AccessibilityAction::SetValue(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn accessible_label(&self) -> Option<SharedString> {
|
||||
self.0.accessible_string_property(AccessibleStringProperty::Label)
|
||||
self.0
|
||||
.upgrade()
|
||||
.and_then(|item| item.accessible_string_property(AccessibleStringProperty::Label))
|
||||
}
|
||||
|
||||
pub fn size(&self) -> i_slint_core::api::LogicalSize {
|
||||
let g = self.0.geometry();
|
||||
self.0
|
||||
.upgrade()
|
||||
.map(|item| {
|
||||
let g = item.geometry();
|
||||
i_slint_core::lengths::logical_size_to_api(g.size)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn absolute_position(&self) -> i_slint_core::api::LogicalPosition {
|
||||
let g = self.0.geometry();
|
||||
let p = self.0.map_to_window(g.origin);
|
||||
self.0
|
||||
.upgrade()
|
||||
.map(|item| {
|
||||
let g = item.geometry();
|
||||
let p = item.map_to_window(g.origin);
|
||||
i_slint_core::lengths::logical_position_to_api(p)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
|
39
tests/cases/testing/dynamic_components.slint
Normal file
39
tests/cases/testing/dynamic_components.slint
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial
|
||||
|
||||
export component TestCase {
|
||||
in-out property <bool> condition: true;
|
||||
if condition: Text {
|
||||
accessible-role: text;
|
||||
accessible-label: "testlabel";
|
||||
text: "Ok";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
```rust
|
||||
let instance = TestCase::new().unwrap();
|
||||
let mut label_search = slint_testing::ElementHandle::find_by_accessible_label(&instance, "testlabel");
|
||||
let label = label_search.next().unwrap();
|
||||
assert!(label.is_valid());
|
||||
instance.set_condition(false);
|
||||
// Trigger re-evaluation of the item tree traversal and thus re-evaluation of the repeater
|
||||
slint_testing::send_mouse_click(&instance, 5., 5.);
|
||||
assert!(!label.is_valid());
|
||||
```
|
||||
|
||||
```cpp
|
||||
auto handle = TestCase::create();
|
||||
const TestCase &instance = *handle;
|
||||
|
||||
auto label_search = slint::testing::ElementHandle::find_by_accessible_label(handle, "testlabel");
|
||||
assert_eq(label_search.size(), 1);
|
||||
auto label = label_search[0];
|
||||
|
||||
assert(label.is_valid());
|
||||
instance.set_condition(false);
|
||||
// Trigger re-evaluation of the item tree traversal and thus re-evaluation of the repeater
|
||||
slint_testing::send_mouse_click(&instance, 5., 5.);
|
||||
assert(!label.is_valid());
|
||||
```
|
||||
*/
|
Loading…
Add table
Add a link
Reference in a new issue