mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
122 lines
3.7 KiB
Rust
122 lines
3.7 KiB
Rust
use i_slint_core::accessibility::AccessibleStringProperty;
|
|
use i_slint_core::item_tree::ItemRc;
|
|
use i_slint_core::lengths::LogicalPoint;
|
|
use i_slint_core::window::WindowInner;
|
|
use slint::Rgb8Pixel;
|
|
use slint::platform::{
|
|
Platform, WindowAdapter, WindowEvent,
|
|
software_renderer::{MinimalSoftwareWindow, RepaintBufferType},
|
|
};
|
|
use std::ops::ControlFlow;
|
|
use std::rc::Rc;
|
|
|
|
slint::slint! {
|
|
import { RecipientRow } from "../ui/components/composer-controls.slint";
|
|
|
|
export component RecipientHarness inherits Window {
|
|
preferred-width: 520px;
|
|
preferred-height: 240px;
|
|
in-out property <string> recipients;
|
|
out property <length> row-height: row.height;
|
|
|
|
public function focus-row() {
|
|
row.focus();
|
|
}
|
|
|
|
row := RecipientRow {
|
|
x: 0px;
|
|
y: 0px;
|
|
width: 520px;
|
|
label: "To";
|
|
text <=> root.recipients;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Headless(Rc<MinimalSoftwareWindow>);
|
|
|
|
impl Platform for Headless {
|
|
fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
|
|
Ok(self.0.clone())
|
|
}
|
|
}
|
|
|
|
fn render(window: &MinimalSoftwareWindow) {
|
|
slint::platform::update_timers_and_animations();
|
|
let mut buffer = vec![Rgb8Pixel::default(); 520 * 240];
|
|
window.request_redraw();
|
|
window.draw_if_needed(|renderer| {
|
|
renderer.render(&mut buffer, 520);
|
|
});
|
|
}
|
|
|
|
/// The vertical span of the editable text in window coordinates.
|
|
fn text_span(ui: &RecipientHarness, text: &str) -> (f32, f32) {
|
|
let inner = WindowInner::from_pub(ui.window());
|
|
let field = ItemRc::new_root(inner.component())
|
|
.visit_descendants(|item| {
|
|
if item
|
|
.accessible_string_property(AccessibleStringProperty::Value)
|
|
.as_deref()
|
|
== Some(text)
|
|
{
|
|
ControlFlow::Break(item.clone())
|
|
} else {
|
|
ControlFlow::Continue(())
|
|
}
|
|
})
|
|
.expect("the recipient field");
|
|
let top = field.map_to_window(LogicalPoint::default()).y;
|
|
(top, top + field.geometry().height())
|
|
}
|
|
|
|
#[test]
|
|
fn recipients_wrap_to_five_lines_then_follow_the_caret() {
|
|
let window = MinimalSoftwareWindow::new(RepaintBufferType::NewBuffer);
|
|
slint::platform::set_platform(Box::new(Headless(window.clone()))).unwrap();
|
|
let ui = RecipientHarness::new().unwrap();
|
|
ui.window().set_size(slint::PhysicalSize::new(520, 240));
|
|
ui.show().unwrap();
|
|
render(&window);
|
|
assert_eq!(ui.get_row_height(), 40.0, "an empty row keeps its height");
|
|
|
|
ui.invoke_focus_row();
|
|
let key = |text: &str| {
|
|
ui.window()
|
|
.dispatch_event(WindowEvent::KeyPressed { text: text.into() });
|
|
ui.window()
|
|
.dispatch_event(WindowEvent::KeyReleased { text: text.into() });
|
|
};
|
|
|
|
key("ada@example.com, grace@example.com");
|
|
render(&window);
|
|
assert_eq!(ui.get_row_height(), 40.0, "one line stays one row");
|
|
|
|
let mut recipients = String::from("ada@example.com, grace@example.com");
|
|
for index in 0..60 {
|
|
let next = format!(", person{index}@example.com");
|
|
key(&next);
|
|
recipients.push_str(&next);
|
|
}
|
|
render(&window);
|
|
let capped = ui.get_row_height();
|
|
assert!(
|
|
capped > 80.0 && capped < 160.0,
|
|
"a long list grows the row to its cap: {capped}"
|
|
);
|
|
|
|
let (top, bottom) = text_span(&ui, &recipients);
|
|
assert!(top < 0.0, "the start scrolled out of view: {top}");
|
|
assert!(
|
|
bottom <= capped,
|
|
"the caret at the end stays inside the row: {bottom} > {capped}"
|
|
);
|
|
|
|
key(&slint::SharedString::from(slint::platform::Key::Return));
|
|
assert_eq!(
|
|
ui.get_recipients(),
|
|
recipients,
|
|
"Return must not insert a line break"
|
|
);
|
|
}
|