use std::collections::HashMap;
use std::ffi::{CString, c_void};
use std::ptr;
use noesis_runtime::classes::{ClassBuilder, Instance, PropertyChangeHandler, PropertyValue};
use noesis_runtime::ffi::{
ClassBase, noesis_base_component_release, noesis_framework_element_find_name,
noesis_gui_load_component,
};
use noesis_runtime::gui::load_component;
use noesis_runtime::view::{FrameworkElement, View};
use noesis_runtime::xaml_provider::XamlProvider;
const GRID_XAML: &str = r##"<?xml version="1.0" encoding="utf-8"?>
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="#FF202020" Width="200" Height="200">
<Button x:Name="X" Content="Hi" Width="100" Height="40"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>"##;
const DICT_XAML: &str = r##"<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Accent" Color="#FF00FF00"/>
</ResourceDictionary>"##;
const BROKEN_XAML: &str = "this is definitely not xaml @@@ <<< >>>";
const COMPONENT_XAML: &str = r##"<?xml version="1.0" encoding="utf-8"?>
<ContentControl x:Class="Nz.LoadTarget"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button x:Name="GRAFTED" Content="grafted"/>
</ContentControl>"##;
struct InMem(HashMap<String, Vec<u8>>);
impl XamlProvider for InMem {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn load_xaml(&mut self, uri: &str) -> Option<&[u8]> {
self.0.get(uri).map(Vec::as_slice)
}
}
struct NoopHandler;
impl PropertyChangeHandler for NoopHandler {
fn on_changed(&self, _instance: Instance, _prop_index: u32, _value: PropertyValue<'_>) {}
}
fn instance_has_named_child(raw: *mut c_void, name: &str) -> bool {
let c = CString::new(name).expect("name contained NUL");
let found = unsafe { noesis_framework_element_find_name(raw, c.as_ptr()) };
if found.is_null() {
false
} else {
unsafe { noesis_base_component_release(found) };
true
}
}
#[test]
fn parse_xaml_and_load_component() {
if let (Ok(name), Ok(key)) = (
std::env::var("NOESIS_LICENSE_NAME"),
std::env::var("NOESIS_LICENSE_KEY"),
) {
noesis_runtime::set_license(&name, &key);
}
noesis_runtime::init();
{
let element = FrameworkElement::parse(GRID_XAML)
.expect("parse() returned None for a well-formed Grid");
let named = element
.find_name("X")
.expect("find_name(\"X\") returned None on a parsed tree");
assert_eq!(
named.name().as_deref(),
Some("X"),
"named element's x:Name did not round-trip"
);
drop(named);
let mut view = View::create(element);
view.set_size(200, 200);
view.activate();
assert!(
view.update(0.0),
"first Update on parsed tree reported no change"
);
let _ = view.update(0.016);
let content = view.content().expect("View::content returned None");
assert!(
content.find_name("X").is_some(),
"named button not reachable through live view content"
);
drop(content);
view.deactivate();
drop(view);
assert!(
FrameworkElement::parse(DICT_XAML).is_none(),
"parse() must reject a ResourceDictionary root (not a FrameworkElement)"
);
assert!(
FrameworkElement::parse(BROKEN_XAML).is_none(),
"parse() must reject malformed XAML"
);
assert!(
!unsafe { load_component(ptr::null_mut(), "component.xaml") },
"load_component(null, ...) must be false"
);
let registration =
ClassBuilder::new("Nz.LoadTarget", ClassBase::ContentControl, NoopHandler)
.register()
.expect("class registration failed");
let instance = registration
.create_instance()
.expect("create_instance returned None");
assert!(
!unsafe { noesis_gui_load_component(instance.raw(), ptr::null()) },
"noesis_gui_load_component(instance, null uri) must be false"
);
let mut provider = HashMap::new();
provider.insert(
"component.xaml".to_string(),
COMPONENT_XAML.as_bytes().to_vec(),
);
let _registered_provider =
noesis_runtime::xaml_provider::set_xaml_provider(InMem(provider));
assert!(
!instance_has_named_child(instance.raw(), "GRAFTED"),
"instance must not contain the named child before LoadComponent"
);
let ran = unsafe { load_component(instance.raw(), "component.xaml") };
assert!(
ran,
"load_component on a live instance + valid URI returned false"
);
assert!(
instance_has_named_child(instance.raw(), "GRAFTED"),
"LoadComponent did not graft the named child onto the instance"
);
drop(instance);
drop(registration);
}
noesis_runtime::shutdown();
}