use crate::ui::theme::Theme; use iris::prelude::*; const MAX_LINES: f32 = 6.0; const APPROX_LINE_HEIGHT_DP: f32 = 24.0; const FIELD_PAD_DP: f32 = 12.0; /// `field` is exposed so the caller can read its content on submit /// (`field(rsc).text()`) and clear it afterward (`field(rsc).set("")`). pub struct Composer { pub field: WeakWidget, /// The bar's own outer padding -- only `bottom` is ever changed, by /// [`Self::set_bottom_inset`]. A `Pad` around the whole bar rather than /// a rebuilt tree, because `field` lives inside it and cannot be /// re-added to a new wrapper once it is strongly owned here. outer_pad: WeakWidget, } pub struct BuiltComposer { pub composer: Composer, pub widget: WeakWidget, } impl Composer { /// Called by the platform shell (Android's `on_insets_changed`, e.g.) /// whenever the space below the bar changes: the IME's own inset while /// it is open, the navigation-bar inset otherwise. Takes a plain /// `f32` in the caller's own physical-pixel units rather than an /// Android-specific insets type, so this crate stays usable from the /// winit backend too, which has no navigation bar to report. /// Rewrites the existing `Pad` in place (marking it dirty through the /// ordinary `Widgets::get_mut` path) instead of swapping in a new one, /// so the field's focus, selection and in-progress text are untouched. pub fn set_bottom_inset(&self, rsc: &mut impl UiRsc, inset: f32) { if let Some(pad) = rsc.ui_mut().widgets.get_mut(&self.outer_pad) { pad.padding.bottom = Len::abs(inset); pad.exact_region = true; } } } /// Returns the composer plus its own bar as a **weak** id -- the caller /// (`lib.rs::build`) embeds it in the screen's own top-level tuple, whose /// `set_root` performs the one real strong registration. Calling /// `.add_strong`/`.upgrade` a second time on an id already strong-owned /// panics ("was already added", `core/src/widget/like.rs:12`) -- the same /// mistake this box's `row.rs` first made with its sender-label header, see /// that file's comment for the fuller account. pub fn build_composer(rsc: &mut Rsc, theme: &Theme) -> BuiltComposer where Rsc::State: FocusHost, { let field = wtext("") .editable(EditMode::MultiLine) .text_align(Align::LEFT) .wrap(true) .size(18) .color(theme.text.clone()) .attr::(()) .label("Message") .add(rsc); // Without any mask at all the overflow paints *above* the bar, over // the transcript: measured at 58px of stray text for a 475px message // in a 417px box. let content = field .width(rest(1)) .scrollable(Axis::Y, Pin::End) .pad(dp(FIELD_PAD_DP)) .max_height(dp(APPROX_LINE_HEIGHT_DP * MAX_LINES + FIELD_PAD_DP * 2.0)) .width(rest(1)) .masked_by(rect(theme.composer_surface.clone())) .add(rsc); let outer_pad: WeakWidget = content.pad(Padding::ZERO).add(rsc); BuiltComposer { composer: Composer { field, outer_pad }, widget: outer_pad, } }