82 lines
3.1 KiB
Rust
82 lines
3.1 KiB
Rust
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<TextEdit>,
|
|
/// 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<Pad>,
|
|
}
|
|
|
|
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: HasEvents>(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::<Selectable>(())
|
|
.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<Pad> = content.pad(Padding::ZERO).add(rsc);
|
|
|
|
BuiltComposer {
|
|
composer: Composer { field, outer_pad },
|
|
widget: outer_pad,
|
|
}
|
|
}
|