use accesskit::{Node, Role};
use tracing::{Span, trace_span};
use vello::Scene;
use vello::kurbo::{Point, Size};
use crate::core::{
AccessCtx, AccessEvent, BoxConstraints, ChildrenIds, EventCtx, LayoutCtx, NewWidget, NoAction,
PaintCtx, PointerEvent, PropertiesMut, PropertiesRef, RegisterCtx, TextEvent, Update,
UpdateCtx, Widget, WidgetId, WidgetMut, WidgetPod,
};
use crate::util::include_screenshot;
use crate::widgets::TextArea;
#[doc = include_screenshot!("prose_alignment_flex.png", "Multiple lines with different alignments.")]
pub struct Prose {
text: WidgetPod<TextArea<false>>,
clip: bool,
}
impl Prose {
pub fn new(text: &str) -> Self {
Self::from_text_area(TextArea::new_immutable(text).with_auto_id())
}
pub fn from_text_area(text: NewWidget<TextArea<false>>) -> Self {
Self {
text: text.to_pod(),
clip: false,
}
}
pub fn with_clip(mut self, clip: bool) -> Self {
self.clip = clip;
self
}
pub fn text_area_pod(&self) -> &WidgetPod<TextArea<false>> {
&self.text
}
}
impl Prose {
pub fn text_mut<'t>(this: &'t mut WidgetMut<'_, Self>) -> WidgetMut<'t, TextArea<false>> {
this.ctx.get_mut(&mut this.widget.text)
}
pub fn set_clip(this: &mut WidgetMut<'_, Self>, clip: bool) {
this.widget.clip = clip;
this.ctx.request_layout();
}
}
impl Widget for Prose {
type Action = NoAction;
fn on_pointer_event(
&mut self,
_: &mut EventCtx<'_>,
_props: &mut PropertiesMut<'_>,
_: &PointerEvent,
) {
}
fn on_text_event(
&mut self,
_ctx: &mut EventCtx<'_>,
_props: &mut PropertiesMut<'_>,
_event: &TextEvent,
) {
}
fn on_access_event(
&mut self,
_ctx: &mut EventCtx<'_>,
_props: &mut PropertiesMut<'_>,
_event: &AccessEvent,
) {
}
fn register_children(&mut self, ctx: &mut RegisterCtx<'_>) {
ctx.register_child(&mut self.text);
}
fn update(
&mut self,
_ctx: &mut UpdateCtx<'_>,
_props: &mut PropertiesMut<'_>,
_event: &Update,
) {
}
fn layout(
&mut self,
ctx: &mut LayoutCtx<'_>,
_props: &mut PropertiesMut<'_>,
bc: &BoxConstraints,
) -> Size {
let size = ctx.run_layout(&mut self.text, bc);
ctx.place_child(&mut self.text, Point::ORIGIN);
if self.clip {
ctx.set_clip_path(size.to_rect());
} else {
ctx.clear_clip_path();
}
size
}
fn paint(&mut self, _ctx: &mut PaintCtx<'_>, _props: &PropertiesRef<'_>, _scene: &mut Scene) {
}
fn accessibility_role(&self) -> Role {
Role::GenericContainer
}
fn accessibility(
&mut self,
_ctx: &mut AccessCtx<'_>,
_props: &PropertiesRef<'_>,
_node: &mut Node,
) {
}
fn children_ids(&self) -> ChildrenIds {
ChildrenIds::from_slice(&[self.text.id()])
}
fn make_trace_span(&self, id: WidgetId) -> Span {
trace_span!("Prose", id = id.trace())
}
fn get_debug_text(&self) -> Option<String> {
self.clip.then(|| "(clip)".into())
}
}
#[cfg(test)]
mod tests {
use parley::StyleProperty;
use vello::kurbo::Size;
use super::*;
use crate::TextAlign;
use crate::properties::types::CrossAxisAlignment;
use crate::properties::types::{AsUnit, Length};
use crate::testing::{TestHarness, assert_render_snapshot};
use crate::theme::default_property_set;
use crate::widgets::{Flex, SizedBox, TextArea};
#[test]
fn prose_clipping() {
let prose = Prose::from_text_area(
TextArea::new_immutable("Truncated text - you should not see this")
.with_style(StyleProperty::FontSize(14.0))
.with_word_wrap(false)
.with_auto_id(),
)
.with_clip(true)
.with_auto_id();
let root_widget = Flex::row()
.with_child(SizedBox::new(prose).width(60.px()).with_auto_id())
.with_auto_id();
let mut harness = TestHarness::create_with_size(
default_property_set(),
root_widget,
Size::new(200.0, 40.0),
);
assert_render_snapshot!(harness, "prose_clipping");
}
#[test]
fn prose_alignment_flex() {
fn base_prose(text_alignment: TextAlign) -> Prose {
Prose::from_text_area(
TextArea::new_immutable("Hello ")
.with_style(StyleProperty::FontSize(14.0))
.with_text_alignment(text_alignment)
.with_word_wrap(true)
.with_auto_id(),
)
}
let prose1 = base_prose(TextAlign::Start);
let prose2 = base_prose(TextAlign::Center);
let prose3 = base_prose(TextAlign::End);
let prose4 = base_prose(TextAlign::Start);
let prose5 = base_prose(TextAlign::Center);
let prose6 = base_prose(TextAlign::End);
let flex = Flex::column()
.with_flex_child(prose1.with_auto_id(), CrossAxisAlignment::Start)
.with_flex_child(prose2.with_auto_id(), CrossAxisAlignment::Start)
.with_flex_child(prose3.with_auto_id(), CrossAxisAlignment::Start)
.with_flex_child(prose4.with_auto_id(), CrossAxisAlignment::Center)
.with_flex_child(prose5.with_auto_id(), CrossAxisAlignment::Center)
.with_flex_child(prose6.with_auto_id(), CrossAxisAlignment::Center)
.with_gap(Length::ZERO)
.with_auto_id();
let mut harness =
TestHarness::create_with_size(default_property_set(), flex, Size::new(200.0, 120.0));
assert_render_snapshot!(harness, "prose_alignment_flex");
}
}