use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::rc::Rc;
use std::time::Duration;
use rich::{Segment, Style};
use crate::component::{Component, Context, Flow, View};
use crate::event::{Event, Key, Mouse, MouseKind};
use crate::keymap::{Binding, Keymap, keys};
use crate::kit::{self, Divider, Theme};
use crate::policy::{LineIo, NotInteractive};
pub const FOCUS_NEXT: &str = "focus-next";
pub const FOCUS_PREVIOUS: &str = "focus-previous";
pub type Child<'a, M> = Box<dyn Component<Output = M> + 'a>;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Rect {
pub x: usize,
pub y: usize,
pub width: usize,
pub height: usize,
}
impl Rect {
pub const fn new(x: usize, y: usize, width: usize, height: usize) -> Rect {
Rect {
x,
y,
width,
height,
}
}
pub fn contains(&self, column: usize, row: usize) -> bool {
column >= self.x
&& column < self.x + self.width
&& row >= self.y
&& row < self.y + self.height
}
pub fn local(&self, mouse: Mouse) -> Mouse {
let clamp = |value: usize| u16::try_from(value).unwrap_or(u16::MAX);
Mouse {
column: clamp((mouse.column as usize).saturating_sub(self.x)),
row: clamp((mouse.row as usize).saturating_sub(self.y)),
..mouse
}
}
pub fn context<'c>(&self, context: &Context<'c>) -> Context<'c> {
Context {
console: context.console,
width: self.width.max(1),
height: self.height.max(1),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Axis {
Horizontal,
#[default]
Vertical,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Size {
Fixed(usize),
Flex(u16),
#[default]
Auto,
}
fn share(total: usize, weights: &[u16]) -> Vec<usize> {
let sum: usize = weights.iter().map(|w| usize::from(*w)).sum();
if sum == 0 {
return vec![0; weights.len()];
}
let mut out: Vec<usize> = weights
.iter()
.map(|w| total * usize::from(*w) / sum)
.collect();
let mut left = total - out.iter().sum::<usize>();
for (index, weight) in weights.iter().enumerate() {
if left == 0 {
break;
}
if *weight > 0 {
out[index] += 1;
left -= 1;
}
}
out
}
pub struct Map<'a, C: Component, M> {
inner: C,
done: Box<dyn FnMut(C::Output) -> Flow<M> + 'a>,
cancel: Box<dyn FnMut() -> Flow<M> + 'a>,
finished: bool,
}
impl<'a, C: Component, M> Map<'a, C, M> {
#[must_use]
pub fn on_cancel(mut self, cancel: impl FnMut() -> Flow<M> + 'a) -> Self {
self.cancel = Box::new(cancel);
self
}
pub fn inner(&self) -> &C {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut C {
&mut self.inner
}
pub fn finished(&self) -> bool {
self.finished
}
fn convert(&mut self, flow: Flow<C::Output>) -> Flow<M> {
match flow {
Flow::Continue => Flow::Continue,
Flow::Ignored => Flow::Ignored,
Flow::Handoff(command) => Flow::Handoff(command),
Flow::Done(value) => {
self.finished = true;
(self.done)(value)
}
Flow::Cancel => {
self.finished = true;
(self.cancel)()
}
}
}
}
impl<C: Component, M> Component for Map<'_, C, M> {
type Output = M;
fn start(&mut self, context: &Context<'_>) -> Flow<M> {
let flow = self.inner.start(context);
self.convert(flow)
}
fn handle(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
if self.finished {
return Flow::Ignored;
}
let flow = self.inner.handle(event, context);
self.convert(flow)
}
fn render(&self, context: &Context<'_>) -> View {
self.inner.render(context)
}
fn tick(&self) -> Option<Duration> {
if self.finished {
None
} else {
self.inner.tick()
}
}
fn mouse(&self) -> bool {
!self.finished && self.inner.mouse()
}
fn keymap(&self) -> Keymap {
if self.finished {
Keymap::default()
} else {
self.inner.keymap()
}
}
fn focusable(&self) -> bool {
!self.finished && self.inner.focusable()
}
fn focus_step(&mut self, forward: bool) -> bool {
!self.finished && self.inner.focus_step(forward)
}
fn focus_enter(&mut self, forward: bool) -> bool {
!self.finished && self.inner.focus_enter(forward)
}
}
pub trait ComponentExt: Component + Sized {
fn map<'a, M>(self, done: impl FnMut(Self::Output) -> Flow<M> + 'a) -> Map<'a, Self, M>
where
Self: 'a,
{
Map {
inner: self,
done: Box::new(done),
cancel: Box::new(|| Flow::Cancel),
finished: false,
}
}
fn with_overlays<'a>(self) -> crate::overlay::Overlays<'a, Self::Output>
where
Self: 'a,
Self::Output: 'a,
{
crate::overlay::Overlays::new(self)
}
fn boxed<'a>(self) -> Child<'a, Self::Output>
where
Self: 'a,
{
Box::new(self)
}
}
impl<C: Component> ComponentExt for C {}
pub struct Label<M> {
markup: String,
_output: PhantomData<fn() -> M>,
}
impl<M> Label<M> {
pub fn new(markup: impl Into<String>) -> Label<M> {
Label {
markup: markup.into(),
_output: PhantomData,
}
}
pub fn set(&mut self, markup: impl Into<String>) {
self.markup = markup.into();
}
}
impl<M> Component for Label<M> {
type Output = M;
fn handle(&mut self, _: &Event, _: &Context<'_>) -> Flow<M> {
Flow::Ignored
}
fn render(&self, context: &Context<'_>) -> View {
View::new(context.markup(&self.markup))
}
fn focusable(&self) -> bool {
false
}
}
pub struct Painted<F, M> {
paint: F,
_output: PhantomData<fn() -> M>,
}
impl<F: Fn(&Context<'_>) -> View, M> Painted<F, M> {
pub fn new(paint: F) -> Painted<F, M> {
Painted {
paint,
_output: PhantomData,
}
}
}
impl<F: Fn(&Context<'_>) -> View, M> Component for Painted<F, M> {
type Output = M;
fn handle(&mut self, _: &Event, _: &Context<'_>) -> Flow<M> {
Flow::Ignored
}
fn render(&self, context: &Context<'_>) -> View {
(self.paint)(context)
}
fn focusable(&self) -> bool {
false
}
}
fn decisive<M>(flow: &Flow<M>) -> bool {
!matches!(flow, Flow::Continue | Flow::Ignored)
}
struct Group<'a, M> {
children: Vec<Child<'a, M>>,
focus: Option<usize>,
grab: Option<usize>,
handoff: Option<usize>,
}
impl<'a, M> Group<'a, M> {
fn new() -> Group<'a, M> {
Group {
children: Vec::new(),
focus: None,
grab: None,
handoff: None,
}
}
fn focused(&self) -> Option<usize> {
let takes = |index: usize| self.children[index].focusable();
self.focus
.filter(|&index| index < self.children.len() && takes(index))
.or_else(|| (0..self.children.len()).find(|&index| takes(index)))
}
fn settle(&mut self) {
self.focus = self.focused();
}
fn focus_step(&mut self, forward: bool) -> bool {
let current = self.focused();
if let Some(index) = current
&& self.children[index].focus_step(forward)
{
return true;
}
let count = self.children.len();
let order: Vec<usize> = if forward {
(current.map_or(0, |index| index + 1)..count).collect()
} else {
(0..current.unwrap_or(count)).rev().collect()
};
for index in order {
if self.children[index].focus_enter(forward) {
self.focus = Some(index);
return true;
}
}
false
}
fn focus_enter(&mut self, forward: bool) -> bool {
let count = self.children.len();
let order: Vec<usize> = if forward {
(0..count).collect()
} else {
(0..count).rev().collect()
};
for index in order {
if self.children[index].focus_enter(forward) {
self.focus = Some(index);
return true;
}
}
false
}
fn deliver(&mut self, index: usize, event: &Event, context: &Context<'_>) -> Flow<M> {
let flow = self.children[index].handle(event, context);
if let Flow::Handoff(_) = flow {
self.handoff = Some(index);
}
flow
}
fn start(&mut self, context: &Context<'_>, rect: impl Fn(usize) -> Rect) -> Flow<M> {
for index in 0..self.children.len() {
let flow = self.children[index].start(&rect(index).context(context));
if decisive(&flow) {
return flow;
}
}
self.settle();
Flow::Continue
}
fn route(
&mut self,
event: &Event,
context: &Context<'_>,
rects: &[(usize, Rect)],
hidden: Rect,
) -> Flow<M> {
let rect_of = |index: usize| {
rects
.iter()
.find(|(i, _)| *i == index)
.map_or(hidden, |(_, rect)| *rect)
};
match event {
Event::Mouse(mouse) => self.route_mouse(*mouse, context, rects),
Event::Tick | Event::Resize { .. } => {
for index in 0..self.children.len() {
if matches!(event, Event::Tick) && self.children[index].tick().is_none() {
continue;
}
let flow = self.deliver(index, event, &rect_of(index).context(context));
if decisive(&flow) {
return flow;
}
}
Flow::Continue
}
Event::Returned(_) => match self.handoff.take().or_else(|| self.focused()) {
Some(index) => self.deliver(index, event, &rect_of(index).context(context)),
None => Flow::Ignored,
},
_ => match self.focused() {
Some(index) => {
self.focus = Some(index);
self.deliver(index, event, &rect_of(index).context(context))
}
None => Flow::Ignored,
},
}
}
fn route_mouse(
&mut self,
mouse: Mouse,
context: &Context<'_>,
rects: &[(usize, Rect)],
) -> Flow<M> {
let (column, row) = (mouse.column as usize, mouse.row as usize);
let under = rects
.iter()
.find(|(index, rect)| *index < self.children.len() && rect.contains(column, row))
.map(|(index, _)| *index);
let mut focused = false;
let target = match mouse.kind {
MouseKind::Down(_) => {
self.grab = under;
if let Some(index) = under.filter(|&i| self.children[i].focusable()) {
focused = self.focused() != Some(index);
self.focus = Some(index);
}
under
}
MouseKind::Drag(_) => self.grab.or(under),
MouseKind::Up(_) => self.grab.take().or(under),
_ => under,
};
let Some(index) = target else {
return Flow::Ignored;
};
let Some((_, rect)) = rects.iter().find(|(i, _)| *i == index).copied() else {
return Flow::Ignored;
};
if !self.children[index].mouse() {
return if focused {
Flow::Continue
} else {
Flow::Ignored
};
}
let flow = self.deliver(
index,
&Event::Mouse(rect.local(mouse)),
&rect.context(context),
);
match flow {
Flow::Ignored if focused => Flow::Continue,
flow => flow,
}
}
fn tick(&self) -> Option<Duration> {
self.children.iter().filter_map(|child| child.tick()).min()
}
fn mouse(&self) -> bool {
self.children.iter().any(|child| child.mouse())
}
fn focusable(&self) -> bool {
self.children.iter().any(|child| child.focusable())
}
}
type Handler<'a, M> = Box<dyn FnMut() -> Flow<M> + 'a>;
struct Bindings<'a, M> {
keymap: Keymap,
priority: Vec<String>,
handlers: Vec<(String, Handler<'a, M>)>,
}
impl<'a, M> Bindings<'a, M> {
fn new(context: &str) -> Bindings<'a, M> {
Bindings {
keymap: Keymap::new(context),
priority: Vec::new(),
handlers: Vec::new(),
}
}
fn declare(&mut self, action: &str, keys: Vec<Key>, description: &str, priority: bool) {
let binding = Binding::new(self.keymap.context(), action, keys, description);
self.keymap.add(binding);
self.priority.retain(|p| p != action);
if priority {
self.priority.push(action.to_string());
}
}
fn focus_keys(mut self) -> Self {
self.declare(FOCUS_NEXT, keys("tab"), "next field", false);
self.declare(FOCUS_PREVIOUS, keys("shift+tab"), "previous field", false);
self
}
fn on(
&mut self,
action: &str,
keys: Vec<Key>,
description: &str,
priority: bool,
handler: impl FnMut() -> Flow<M> + 'a,
) {
self.declare(action, keys, description, priority);
self.handlers.retain(|(name, _)| name != action);
self.handlers.push((action.to_string(), Box::new(handler)));
}
fn action(&self, key: Key, priority: bool) -> Option<String> {
let action = self.keymap.action(key)?;
(self.priority.iter().any(|p| p == action) == priority).then(|| action.to_string())
}
fn run(&mut self, action: &str) -> Option<Flow<M>> {
self.handlers
.iter_mut()
.find(|(name, _)| name == action)
.map(|(_, handler)| handler())
}
}
macro_rules! binding_builders {
() => {
#[must_use]
pub fn on(
mut self,
action: &str,
keys: impl IntoIterator<Item = Key>,
description: &str,
handler: impl FnMut() -> Flow<M> + 'a,
) -> Self {
let keys = keys.into_iter().collect();
self.bindings.on(action, keys, description, false, handler);
self
}
#[must_use]
pub fn shortcut(
mut self,
action: &str,
keys: impl IntoIterator<Item = Key>,
description: &str,
handler: impl FnMut() -> Flow<M> + 'a,
) -> Self {
let keys = keys.into_iter().collect();
self.bindings.on(action, keys, description, true, handler);
self
}
#[must_use]
pub fn rebind(mut self, action: &str, keys: impl IntoIterator<Item = Key>) -> Self {
self.bindings.keymap.rebind(action, keys);
self
}
pub fn own_keymap(&self) -> &Keymap {
&self.bindings.keymap
}
#[must_use]
pub fn with_mouse(mut self, on: bool) -> Self {
self.mouse = on;
self
}
};
}
pub struct Stack<'a, M> {
axis: Axis,
group: Group<'a, M>,
sizes: Vec<Size>,
gap: usize,
bindings: Bindings<'a, M>,
mouse: bool,
}
impl<'a, M> Stack<'a, M> {
pub fn new(axis: Axis) -> Stack<'a, M> {
Stack {
axis,
group: Group::new(),
sizes: Vec::new(),
gap: 0,
bindings: Bindings::new("stack").focus_keys(),
mouse: false,
}
}
#[must_use]
pub fn child(self, child: impl Component<Output = M> + 'a) -> Self {
self.sized(Size::Auto, child)
}
#[must_use]
pub fn sized(mut self, size: Size, child: impl Component<Output = M> + 'a) -> Self {
self.push(size, Box::new(child));
self
}
pub fn push(&mut self, size: Size, child: Child<'a, M>) {
self.group.children.push(child);
self.sizes.push(size);
}
#[must_use]
pub fn gap(mut self, cells: usize) -> Self {
self.gap = cells;
self
}
binding_builders!();
pub fn axis(&self) -> Axis {
self.axis
}
pub fn len(&self) -> usize {
self.group.children.len()
}
pub fn is_empty(&self) -> bool {
self.group.children.is_empty()
}
pub fn get(&self, index: usize) -> Option<&(dyn Component<Output = M> + 'a)> {
self.group.children.get(index).map(|child| &**child)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut (dyn Component<Output = M> + 'a)> {
self.group.children.get_mut(index).map(|child| &mut **child)
}
pub fn focused(&self) -> Option<usize> {
self.group.focused()
}
pub fn focus(&mut self, index: usize) -> bool {
let takes = self
.group
.children
.get_mut(index)
.is_some_and(|child| child.focus_enter(true));
if takes {
self.group.focus = Some(index);
}
takes
}
pub fn layout(&self, context: &Context<'_>) -> Vec<Rect> {
let count = self.len();
let gaps = self.gap * count.saturating_sub(1);
let vertical = self.axis == Axis::Vertical;
let total = if vertical {
context.height
} else {
context.width
};
let mut left = total.saturating_sub(gaps);
let mut extent = vec![0; count];
for (index, size) in self.sizes.iter().enumerate() {
if let Size::Fixed(cells) = size {
extent[index] = (*cells).min(left);
left -= extent[index];
}
}
if vertical {
for (index, size) in self.sizes.iter().enumerate() {
if *size == Size::Auto {
let probe = Rect::new(0, 0, context.width, left).context(context);
extent[index] = self.group.children[index]
.render(&probe)
.lines
.len()
.min(left);
left -= extent[index];
}
}
}
let weights: Vec<u16> = self
.sizes
.iter()
.map(|size| match size {
Size::Flex(weight) => *weight,
Size::Auto if !vertical => 1,
_ => 0,
})
.collect();
for (index, cells) in share(left, &weights).into_iter().enumerate() {
if weights[index] > 0 {
extent[index] = cells;
}
}
let mut at = 0;
extent
.into_iter()
.map(|cells| {
let rect = if vertical {
Rect::new(0, at, context.width, cells)
} else {
Rect::new(at, 0, cells, context.height)
};
at += cells + self.gap;
rect
})
.collect()
}
fn indexed(&self, context: &Context<'_>) -> Vec<(usize, Rect)> {
self.layout(context).into_iter().enumerate().collect()
}
}
fn rows_of(view: &View, rect: Rect, fill: bool) -> Vec<Vec<Segment>> {
let mut lines: Vec<Vec<Segment>> = view
.lines
.iter()
.take(rect.height)
.map(|line| kit::fit(line.clone(), rect.width))
.collect();
if fill {
lines.resize_with(rect.height, Vec::new);
}
lines
}
fn side_by_side(
views: &[(View, Rect)],
rows: usize,
focus: Option<usize>,
between: &[Vec<Segment>],
) -> View {
let mut lines = Vec::with_capacity(rows);
for row in 0..rows {
let mut line = Vec::new();
for (index, (view, rect)) in views.iter().enumerate() {
let cells = view.lines.get(row).cloned().unwrap_or_default();
if index + 1 == views.len() {
line.extend(kit::fit(cells, rect.width));
} else {
line.extend(kit::pad(cells, rect.width));
line.extend(between.get(index).cloned().unwrap_or_default());
}
}
lines.push(line);
}
let mut out = View::new(lines);
if let Some((view, rect)) = focus.and_then(|index| views.get(index))
&& let Some((row, column)) = view.cursor
&& row < rows
&& column < rect.width.max(1)
{
out.cursor = Some((rect.y + row, rect.x + column));
}
out
}
fn keymap_with<M>(focused: Option<&Child<'_, M>>, own: &Keymap) -> Keymap {
let mut keymap = focused.map(|child| child.keymap()).unwrap_or_default();
keymap.extend(own.clone());
keymap
}
impl<M> Component for Stack<'_, M> {
type Output = M;
fn start(&mut self, context: &Context<'_>) -> Flow<M> {
let rects = self.layout(context);
self.group.start(context, |index| rects[index])
}
fn handle(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
if let Some(key) = event.key()
&& let Some(action) = self.bindings.action(key, true)
&& let Some(flow) = self.bindings.run(&action)
{
return flow;
}
let rects = self.indexed(context);
let flow = self.group.route(event, context, &rects, Rect::default());
self.group.settle();
if !matches!(flow, Flow::Ignored) {
return flow;
}
let Some(action) = event.key().and_then(|key| self.bindings.action(key, false)) else {
return Flow::Ignored;
};
match action.as_str() {
FOCUS_NEXT | FOCUS_PREVIOUS => {
if self.group.focus_step(action == FOCUS_NEXT) {
Flow::Continue
} else {
Flow::Ignored
}
}
_ => self.bindings.run(&action).unwrap_or(Flow::Ignored),
}
}
fn render(&self, context: &Context<'_>) -> View {
let rects = self.layout(context);
let focus = self.group.focused();
let views: Vec<(View, Rect)> = rects
.iter()
.enumerate()
.map(|(index, rect)| {
(
self.group.children[index].render(&rect.context(context)),
*rect,
)
})
.collect();
match self.axis {
Axis::Vertical => {
let mut out = View::default();
for (index, (view, rect)) in views.iter().enumerate() {
if index > 0 {
out.lines.extend(std::iter::repeat_n(Vec::new(), self.gap));
}
let fill = self.sizes[index] != Size::Auto;
let top = out.lines.len();
out.lines.extend(rows_of(view, *rect, fill));
if Some(index) == focus
&& let Some((row, column)) = view.cursor
&& row < rect.height
{
out.cursor = Some((top + row, column));
}
}
out
}
Axis::Horizontal => {
let rows = views
.iter()
.map(|(view, _)| view.lines.len())
.max()
.unwrap_or(0)
.min(context.height);
let gap = vec![kit::plain(" ".repeat(self.gap))];
let between = vec![gap; views.len()];
side_by_side(&views, rows, focus, &between)
}
}
}
fn tick(&self) -> Option<Duration> {
self.group.tick()
}
fn mouse(&self) -> bool {
self.mouse || self.group.mouse()
}
fn keymap(&self) -> Keymap {
let focused = self
.group
.focused()
.map(|index| &self.group.children[index]);
keymap_with(focused, &self.bindings.keymap)
}
fn focusable(&self) -> bool {
self.group.focusable()
}
fn focus_step(&mut self, forward: bool) -> bool {
self.group.focus_step(forward)
}
fn focus_enter(&mut self, forward: bool) -> bool {
self.group.focus_enter(forward)
}
}
macro_rules! axis_stack {
($(#[$doc:meta])* $name:ident, $axis:expr) => {
$(#[$doc])*
pub struct $name<'a, M>(Stack<'a, M>);
impl<'a, M> Default for $name<'a, M> {
fn default() -> Self {
Self::new()
}
}
impl<'a, M> $name<'a, M> {
pub fn new() -> Self {
$name(Stack::new($axis))
}
#[must_use]
pub fn child(self, child: impl Component<Output = M> + 'a) -> Self {
$name(self.0.child(child))
}
#[must_use]
pub fn sized(self, size: Size, child: impl Component<Output = M> + 'a) -> Self {
$name(self.0.sized(size, child))
}
#[must_use]
pub fn gap(self, cells: usize) -> Self {
$name(self.0.gap(cells))
}
#[must_use]
pub fn on(
self,
action: &str,
keys: impl IntoIterator<Item = Key>,
description: &str,
handler: impl FnMut() -> Flow<M> + 'a,
) -> Self {
$name(self.0.on(action, keys, description, handler))
}
#[must_use]
pub fn shortcut(
self,
action: &str,
keys: impl IntoIterator<Item = Key>,
description: &str,
handler: impl FnMut() -> Flow<M> + 'a,
) -> Self {
$name(self.0.shortcut(action, keys, description, handler))
}
#[must_use]
pub fn rebind(self, action: &str, keys: impl IntoIterator<Item = Key>) -> Self {
$name(self.0.rebind(action, keys))
}
#[must_use]
pub fn with_mouse(self, on: bool) -> Self {
$name(self.0.with_mouse(on))
}
pub fn stack(&self) -> &Stack<'a, M> {
&self.0
}
pub fn stack_mut(&mut self) -> &mut Stack<'a, M> {
&mut self.0
}
}
impl<'a, M> Component for $name<'a, M> {
type Output = M;
fn start(&mut self, context: &Context<'_>) -> Flow<M> {
self.0.start(context)
}
fn handle(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
self.0.handle(event, context)
}
fn render(&self, context: &Context<'_>) -> View {
self.0.render(context)
}
fn tick(&self) -> Option<Duration> {
self.0.tick()
}
fn mouse(&self) -> bool {
Component::mouse(&self.0)
}
fn keymap(&self) -> Keymap {
self.0.keymap()
}
fn focusable(&self) -> bool {
self.0.focusable()
}
fn focus_step(&mut self, forward: bool) -> bool {
self.0.focus_step(forward)
}
fn focus_enter(&mut self, forward: bool) -> bool {
self.0.focus_enter(forward)
}
}
};
}
axis_stack!(
Column,
Axis::Vertical
);
axis_stack!(
Row,
Axis::Horizontal
);
pub struct Split<'a, M> {
axis: Axis,
group: Group<'a, M>,
divider: Divider,
ratio: u8,
style: Style,
bindings: Bindings<'a, M>,
mouse: bool,
}
impl<'a, M> Split<'a, M> {
fn new(
axis: Axis,
first: impl Component<Output = M> + 'a,
second: impl Component<Output = M> + 'a,
) -> Split<'a, M> {
let mut group = Group::new();
group.children.push(Box::new(first) as Child<'a, M>);
group.children.push(Box::new(second) as Child<'a, M>);
let mut bindings = Bindings::new("split").focus_keys();
let (shrink, grow) = match axis {
Axis::Horizontal => ("alt+h", "alt+l"),
Axis::Vertical => ("alt+k", "alt+j"),
};
bindings.declare("shrink", keys(shrink), "move the border back", true);
bindings.declare("grow", keys(grow), "move the border on", true);
Split {
axis,
group,
divider: Divider::new(1).min(1),
ratio: 50,
style: Theme::default().border,
bindings,
mouse: false,
}
}
pub fn horizontal(
left: impl Component<Output = M> + 'a,
right: impl Component<Output = M> + 'a,
) -> Split<'a, M> {
Split::new(Axis::Horizontal, left, right)
}
pub fn vertical(
top: impl Component<Output = M> + 'a,
bottom: impl Component<Output = M> + 'a,
) -> Split<'a, M> {
Split::new(Axis::Vertical, top, bottom)
}
#[must_use]
pub fn ratio(mut self, percent: u8) -> Self {
self.ratio = percent.min(100);
self
}
#[must_use]
pub fn at(mut self, cells: usize) -> Self {
self.divider.set_position(Some(cells));
self
}
#[must_use]
pub fn min(mut self, cells: usize) -> Self {
self.divider.min_before = cells;
self.divider.min_after = cells;
self
}
#[must_use]
pub fn mins(mut self, first: usize, second: usize) -> Self {
self.divider.min_before = first;
self.divider.min_after = second;
self
}
#[must_use]
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
binding_builders!();
pub fn axis(&self) -> Axis {
self.axis
}
pub fn divider(&self) -> &Divider {
&self.divider
}
pub fn first(&self) -> &(dyn Component<Output = M> + 'a) {
&*self.group.children[0]
}
pub fn second(&self) -> &(dyn Component<Output = M> + 'a) {
&*self.group.children[1]
}
pub fn first_mut(&mut self) -> &mut (dyn Component<Output = M> + 'a) {
&mut *self.group.children[0]
}
pub fn second_mut(&mut self) -> &mut (dyn Component<Output = M> + 'a) {
&mut *self.group.children[1]
}
pub fn focused(&self) -> Option<usize> {
self.group.focused()
}
pub fn focus(&mut self, index: usize) -> bool {
let takes = self
.group
.children
.get_mut(index)
.is_some_and(|child| child.focus_enter(true));
if takes {
self.group.focus = Some(index);
}
takes
}
fn extent(&self, context: &Context<'_>) -> (usize, usize) {
let total = match self.axis {
Axis::Horizontal => context.width,
Axis::Vertical => context.height,
};
let default = self
.divider
.clamp(total * usize::from(self.ratio) / 100, total);
(total, self.divider.resolve(total, default))
}
pub fn layout(&self, context: &Context<'_>) -> [Rect; 2] {
let (total, first) = self.extent(context);
let second = total.saturating_sub(first + self.divider.thickness);
match self.axis {
Axis::Horizontal => [
Rect::new(0, 0, first, context.height),
Rect::new(first + self.divider.thickness, 0, second, context.height),
],
Axis::Vertical => [
Rect::new(0, 0, context.width, first),
Rect::new(0, first + self.divider.thickness, context.width, second),
],
}
}
}
impl<M> Component for Split<'_, M> {
type Output = M;
fn start(&mut self, context: &Context<'_>) -> Flow<M> {
let rects = self.layout(context);
self.group.start(context, |index| rects[index])
}
fn handle(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
let (total, first) = self.extent(context);
if let Some(key) = event.key()
&& let Some(action) = self.bindings.action(key, true)
{
let step = match self.axis {
Axis::Horizontal => 2,
Axis::Vertical => 1,
};
match action.as_str() {
"shrink" => self.divider.nudge(-step, first, total),
"grow" => self.divider.nudge(step, first, total),
_ => {
if let Some(flow) = self.bindings.run(&action) {
return flow;
}
}
}
return Flow::Continue;
}
if let Event::Mouse(mouse) = event {
let along = match self.axis {
Axis::Horizontal => mouse.column as usize,
Axis::Vertical => mouse.row as usize,
};
if self.divider.handle_mouse(mouse, along, first, total) {
self.group.grab = None;
return Flow::Continue;
}
}
let [a, b] = self.layout(context);
let flow = self
.group
.route(event, context, &[(0, a), (1, b)], Rect::default());
self.group.settle();
if !matches!(flow, Flow::Ignored) {
return flow;
}
let Some(action) = event.key().and_then(|key| self.bindings.action(key, false)) else {
return Flow::Ignored;
};
match action.as_str() {
FOCUS_NEXT | FOCUS_PREVIOUS => {
if self.group.focus_step(action == FOCUS_NEXT) {
Flow::Continue
} else {
Flow::Ignored
}
}
_ => self.bindings.run(&action).unwrap_or(Flow::Ignored),
}
}
fn render(&self, context: &Context<'_>) -> View {
let [a, b] = self.layout(context);
let focus = self.group.focused();
let first = self.group.children[0].render(&a.context(context));
let second = self.group.children[1].render(&b.context(context));
match self.axis {
Axis::Horizontal => {
let rows = first
.lines
.len()
.max(second.lines.len())
.min(context.height)
.max(1);
let border = vec![vec![kit::text("│", &self.style)]];
side_by_side(&[(first, a), (second, b)], rows, focus, &border)
}
Axis::Vertical => {
let mut out = View::new(rows_of(&first, a, true));
out.lines
.push(vec![kit::text("─".repeat(context.width), &self.style)]);
out.lines.extend(rows_of(&second, b, true));
let (view, rect) = match focus {
Some(0) => (&first, a),
_ => (&second, b),
};
if let Some((row, column)) = view.cursor.filter(|(row, _)| *row < rect.height) {
out.cursor = Some((rect.y + row, column));
}
out
}
}
}
fn tick(&self) -> Option<Duration> {
self.group.tick()
}
fn mouse(&self) -> bool {
self.mouse || self.group.mouse()
}
fn keymap(&self) -> Keymap {
let focused = self
.group
.focused()
.map(|index| &self.group.children[index]);
keymap_with(focused, &self.bindings.keymap)
}
fn focusable(&self) -> bool {
self.group.focusable()
}
fn focus_step(&mut self, forward: bool) -> bool {
self.group.focus_step(forward)
}
fn focus_enter(&mut self, forward: bool) -> bool {
self.group.focus_enter(forward)
}
}
pub struct Tabs<'a, M> {
titles: Vec<String>,
group: Group<'a, M>,
active: usize,
selected: Style,
unselected: Style,
bindings: Bindings<'a, M>,
mouse: bool,
}
impl<'a, M> Default for Tabs<'a, M> {
fn default() -> Self {
Self::new()
}
}
impl<'a, M> Tabs<'a, M> {
pub fn new() -> Tabs<'a, M> {
let mut bindings = Bindings::new("tabs").focus_keys();
bindings.declare("next", keys("alt+right ctrl+pagedown"), "next tab", true);
bindings.declare(
"previous",
keys("alt+left ctrl+pageup"),
"previous tab",
true,
);
Tabs {
titles: Vec::new(),
group: Group::new(),
active: 0,
selected: Style::parse("bold reverse").expect("a built-in style"),
unselected: Theme::default().hint,
bindings,
mouse: false,
}
}
#[must_use]
pub fn tab(mut self, title: impl Into<String>, child: impl Component<Output = M> + 'a) -> Self {
self.push(title, Box::new(child));
self
}
pub fn push(&mut self, title: impl Into<String>, child: Child<'a, M>) {
self.titles.push(title.into());
self.group.children.push(child);
let number = self.titles.len();
if number <= 9 {
self.bindings.declare(
&format!("tab-{number}"),
keys(&format!("alt+{number}")),
&format!("show tab {number}"),
true,
);
}
}
#[must_use]
pub fn styles(mut self, selected: Style, unselected: Style) -> Self {
self.selected = selected;
self.unselected = unselected;
self
}
#[must_use]
pub fn active(mut self, index: usize) -> Self {
self.select(index);
self
}
binding_builders!();
pub fn select(&mut self, index: usize) {
if index < self.titles.len() {
self.active = index;
self.group.focus = Some(index);
}
}
pub fn selected(&self) -> usize {
self.active
}
pub fn titles(&self) -> &[String] {
&self.titles
}
pub fn len(&self) -> usize {
self.titles.len()
}
pub fn is_empty(&self) -> bool {
self.titles.is_empty()
}
pub fn get(&self, index: usize) -> Option<&(dyn Component<Output = M> + 'a)> {
self.group.children.get(index).map(|child| &**child)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut (dyn Component<Output = M> + 'a)> {
self.group.children.get_mut(index).map(|child| &mut **child)
}
fn bar(&self, width: usize) -> (Vec<Segment>, Vec<(usize, usize)>) {
let mut line = Vec::new();
let mut spans = Vec::new();
let mut at = 0;
for (index, title) in self.titles.iter().enumerate() {
if index > 0 {
line.push(kit::plain(" "));
at += 1;
}
let label = format!(" {} ", kit::shown(title));
let cells = rich::cells::cell_len(&label);
let style = if index == self.active {
&self.selected
} else {
&self.unselected
};
line.push(kit::text(label, style));
spans.push((at, at + cells));
at += cells;
}
(kit::fit(line, width), spans)
}
pub fn content(&self, context: &Context<'_>) -> Rect {
Rect::new(0, 1, context.width, context.height.saturating_sub(1))
}
}
impl<M> Component for Tabs<'_, M> {
type Output = M;
fn start(&mut self, context: &Context<'_>) -> Flow<M> {
let rect = self.content(context);
let flow = self.group.start(context, |_| rect);
self.group.focus = Some(self.active);
flow
}
fn handle(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
let count = self.titles.len();
if let Some(key) = event.key()
&& let Some(action) = self.bindings.action(key, true)
{
match action.as_str() {
"next" if count > 0 => self.select((self.active + 1) % count),
"previous" if count > 0 => self.select((self.active + count - 1) % count),
number if number.starts_with("tab-") => {
if let Ok(n) = number[4..].parse::<usize>() {
self.select(n.saturating_sub(1));
}
}
_ => {
if let Some(flow) = self.bindings.run(&action) {
return flow;
}
}
}
return Flow::Continue;
}
if let Event::Mouse(mouse) = event
&& mouse.row == 0
&& self.group.grab.is_none()
{
if mouse.is_click() {
let (_, spans) = self.bar(context.width);
let column = mouse.column as usize;
if let Some(index) = spans.iter().position(|(a, b)| (*a..*b).contains(&column)) {
self.select(index);
return Flow::Continue;
}
}
return Flow::Ignored;
}
let rect = self.content(context);
self.group.focus = Some(self.active);
let flow = self
.group
.route(event, context, &[(self.active, rect)], rect);
if !matches!(flow, Flow::Ignored) {
return flow;
}
let Some(action) = event.key().and_then(|key| self.bindings.action(key, false)) else {
return Flow::Ignored;
};
match action.as_str() {
FOCUS_NEXT | FOCUS_PREVIOUS => {
let forward = action == FOCUS_NEXT;
if self
.group
.children
.get_mut(self.active)
.is_some_and(|child| child.focus_step(forward))
{
Flow::Continue
} else {
Flow::Ignored
}
}
_ => self.bindings.run(&action).unwrap_or(Flow::Ignored),
}
}
fn render(&self, context: &Context<'_>) -> View {
let (bar, _) = self.bar(context.width);
let mut out = View::new(vec![bar]);
if let Some(child) = self.group.children.get(self.active) {
let rect = self.content(context);
let view = child.render(&rect.context(context));
out.lines.extend(rows_of(&view, rect, false));
if let Some((row, column)) = view.cursor.filter(|(row, _)| *row < rect.height) {
out.cursor = Some((rect.y + row, column));
}
}
out
}
fn tick(&self) -> Option<Duration> {
self.group.tick()
}
fn mouse(&self) -> bool {
self.mouse || self.group.mouse()
}
fn keymap(&self) -> Keymap {
keymap_with(self.group.children.get(self.active), &self.bindings.keymap)
}
fn focusable(&self) -> bool {
self.group
.children
.get(self.active)
.is_some_and(|child| child.focusable())
}
fn focus_step(&mut self, forward: bool) -> bool {
let active = self.active;
self.group
.children
.get_mut(active)
.is_some_and(|child| child.focus_step(forward))
}
fn focus_enter(&mut self, forward: bool) -> bool {
let active = self.active;
self.group
.children
.get_mut(active)
.is_some_and(|child| child.focus_enter(forward))
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum LayerKind {
#[default]
Modal,
Popover,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Placement {
Center { width: usize, height: usize },
At(Rect),
}
impl Default for Placement {
fn default() -> Self {
Placement::Center {
width: 40,
height: 10,
}
}
}
pub struct Layer<'a, M> {
child: Child<'a, M>,
kind: LayerKind,
placement: Placement,
title: Option<String>,
border: bool,
backdrop: bool,
dismissable: bool,
took_focus: bool,
}
impl<'a, M> Layer<'a, M> {
fn new(kind: LayerKind, child: impl Component<Output = M> + 'a) -> Layer<'a, M> {
Layer {
child: Box::new(child),
kind,
placement: Placement::default(),
title: None,
border: true,
backdrop: kind == LayerKind::Modal,
dismissable: true,
took_focus: false,
}
}
pub fn modal(child: impl Component<Output = M> + 'a) -> Layer<'a, M> {
Layer::new(LayerKind::Modal, child)
}
pub fn popover(child: impl Component<Output = M> + 'a) -> Layer<'a, M> {
Layer::new(LayerKind::Popover, child)
}
#[must_use]
pub fn size(mut self, width: usize, height: usize) -> Self {
self.placement = Placement::Center { width, height };
self
}
#[must_use]
pub fn placement(mut self, placement: Placement) -> Self {
self.placement = placement;
self
}
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub fn border(mut self, on: bool) -> Self {
self.border = on;
self
}
#[must_use]
pub fn backdrop(mut self, on: bool) -> Self {
self.backdrop = on;
self
}
#[must_use]
pub fn dismissable(mut self, on: bool) -> Self {
self.dismissable = on;
self
}
pub fn kind(&self) -> LayerKind {
self.kind
}
}
enum Request<'a, M> {
Open(Layer<'a, M>),
Close,
}
pub struct LayerHandle<'a, M> {
requests: Rc<RefCell<Vec<Request<'a, M>>>>,
open: Rc<Cell<usize>>,
}
impl<M> Clone for LayerHandle<'_, M> {
fn clone(&self) -> Self {
LayerHandle {
requests: Rc::clone(&self.requests),
open: Rc::clone(&self.open),
}
}
}
impl<'a, M> LayerHandle<'a, M> {
pub fn open(&self, layer: Layer<'a, M>) {
self.requests.borrow_mut().push(Request::Open(layer));
}
pub fn close(&self) {
self.requests.borrow_mut().push(Request::Close);
}
pub fn open_count(&self) -> usize {
self.open.get()
}
}
pub struct Layers<'a, M> {
base: Child<'a, M>,
layers: Vec<Layer<'a, M>>,
requests: Rc<RefCell<Vec<Request<'a, M>>>>,
open: Rc<Cell<usize>>,
backdrop: Style,
border: Style,
bindings: Bindings<'a, M>,
mouse: bool,
grab: Option<Option<usize>>,
handoff: Option<Option<usize>>,
}
impl<'a, M> Layers<'a, M> {
pub fn new(base: impl Component<Output = M> + 'a) -> Layers<'a, M> {
let mut bindings = Bindings::new("layers").focus_keys();
bindings.declare("dismiss", keys("escape"), "close the dialog", false);
Layers {
base: Box::new(base),
layers: Vec::new(),
requests: Rc::new(RefCell::new(Vec::new())),
open: Rc::new(Cell::new(0)),
backdrop: Style::parse("dim").expect("a built-in style"),
border: Style::default(),
bindings,
mouse: false,
grab: None,
handoff: None,
}
}
pub fn handle(&self) -> LayerHandle<'a, M> {
LayerHandle {
requests: Rc::clone(&self.requests),
open: Rc::clone(&self.open),
}
}
pub fn open(&mut self, mut layer: Layer<'a, M>) {
layer.took_focus = layer.child.focusable();
self.layers.push(layer);
self.open.set(self.layers.len());
}
pub fn close(&mut self) -> bool {
let closed = self.layers.pop().is_some();
self.open.set(self.layers.len());
closed
}
#[must_use]
pub fn open_on(
mut self,
action: &str,
keys: impl IntoIterator<Item = Key>,
description: &str,
mut layer: impl FnMut() -> Layer<'a, M> + 'a,
) -> Self
where
M: 'a,
{
let handle = self.handle();
let keys = keys.into_iter().collect();
self.bindings.on(action, keys, description, false, move || {
handle.open(layer());
Flow::Continue
});
self
}
#[must_use]
pub fn backdrop_style(mut self, style: Style) -> Self {
self.backdrop = style;
self
}
#[must_use]
pub fn border_style(mut self, style: Style) -> Self {
self.border = style;
self
}
binding_builders!();
pub fn depth(&self) -> usize {
self.layers.len()
}
pub fn base(&self) -> &(dyn Component<Output = M> + 'a) {
&*self.base
}
pub fn base_mut(&mut self) -> &mut (dyn Component<Output = M> + 'a) {
&mut *self.base
}
fn apply(&mut self, context: &Context<'_>) -> Flow<M> {
let requests: Vec<Request<'a, M>> = self.requests.borrow_mut().drain(..).collect();
let mut result = Flow::Continue;
for request in requests {
match request {
Request::Open(mut layer) => {
layer.took_focus = layer.child.focusable();
self.layers.push(layer);
let index = self.layers.len() - 1;
let inner = self.placements(context)[index].1;
let flow = self.layers[index].child.start(&inner.context(context));
if let Some(flow) = self.after_layer(index, flow) {
result = flow;
}
}
Request::Close => {
self.layers.pop();
}
}
}
self.open.set(self.layers.len());
result
}
pub fn placements(&self, context: &Context<'_>) -> Vec<(Rect, Rect)> {
if self.layers.is_empty() {
return Vec::new();
}
let rows = self.base.render(context).lines.len();
self.layers
.iter()
.map(|layer| {
let outer = match layer.placement {
Placement::Center { width, height } => {
let width = width.min(context.width);
let height = height.min(context.height.max(1));
let area = rows.max(height).min(context.height.max(height));
Rect::new(
(context.width - width) / 2,
(area - height) / 2,
width,
height,
)
}
Placement::At(rect) => {
let x = rect.x.min(context.width);
Rect::new(x, rect.y, rect.width.min(context.width - x), rect.height)
}
};
let inner = if layer.border && outer.width >= 2 && outer.height >= 2 {
Rect::new(outer.x + 1, outer.y + 1, outer.width - 2, outer.height - 2)
} else {
outer
};
(outer, inner)
})
.collect()
}
fn after_layer(&mut self, index: usize, flow: Flow<M>) -> Option<Flow<M>> {
let layer = &self.layers[index];
let finished = layer.took_focus && !layer.child.focusable();
match flow {
Flow::Cancel => {
self.layers.remove(index);
Some(Flow::Continue)
}
Flow::Done(value) => {
self.layers.remove(index);
Some(Flow::Done(value))
}
Flow::Handoff(command) => {
self.handoff = Some(Some(index));
Some(Flow::Handoff(command))
}
flow if finished => {
self.layers.remove(index);
Some(match flow {
Flow::Ignored => Flow::Continue,
flow => flow,
})
}
_ => None,
}
}
fn handle_top(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
let top = self.layers.len() - 1;
let places = self.placements(context);
let (outer, inner) = places[top];
match event {
Event::Tick | Event::Resize { .. } => {
let flow = self.broadcast(event, context, &places);
return flow;
}
Event::Returned(_) => {
return match self.handoff.take() {
Some(None) => self.base.handle(event, context),
Some(Some(index)) if index < self.layers.len() => {
let inner = places[index].1;
let flow = self.layers[index]
.child
.handle(event, &inner.context(context));
self.after_layer(index, flow).unwrap_or(Flow::Continue)
}
_ => Flow::Continue,
};
}
Event::Mouse(mouse) => {
let (column, row) = (mouse.column as usize, mouse.row as usize);
let inside = outer.contains(column, row);
let held = matches!(mouse.kind, MouseKind::Drag(_) | MouseKind::Up(_))
&& self.grab == Some(Some(top));
if let MouseKind::Down(_) = mouse.kind {
self.grab = Some(Some(if inside { top } else { usize::MAX }));
}
if !inside && !held {
if mouse.is_click() && self.layers[top].kind == LayerKind::Popover {
self.layers.pop();
}
if let MouseKind::Up(_) = mouse.kind {
self.grab = None;
}
return Flow::Continue;
}
if matches!(mouse.kind, MouseKind::Up(_)) {
self.grab = None;
}
if !self.layers[top].child.mouse() || !(inner.contains(column, row) || held) {
return Flow::Continue;
}
let event = Event::Mouse(inner.local(*mouse));
let flow = self.layers[top]
.child
.handle(&event, &inner.context(context));
return self.after_layer(top, flow).unwrap_or(Flow::Continue);
}
_ => {}
}
let flow = self.layers[top]
.child
.handle(event, &inner.context(context));
let ignored = matches!(flow, Flow::Ignored);
if let Some(flow) = self.after_layer(top, flow) {
return flow;
}
if !ignored {
return Flow::Continue;
}
let Some(action) = event.key().and_then(|key| self.bindings.action(key, false)) else {
return Flow::Continue;
};
match action.as_str() {
"dismiss" if self.layers[top].dismissable => {
self.layers.pop();
}
FOCUS_NEXT | FOCUS_PREVIOUS => {
let forward = action == FOCUS_NEXT;
let child = &mut self.layers[top].child;
if !child.focus_step(forward) {
child.focus_enter(forward);
}
}
_ => {}
}
Flow::Continue
}
fn broadcast(
&mut self,
event: &Event,
context: &Context<'_>,
places: &[(Rect, Rect)],
) -> Flow<M> {
if !matches!(event, Event::Tick) || self.base.tick().is_some() {
let flow = self.base.handle(event, context);
if decisive(&flow) {
return flow;
}
}
let mut index = 0;
while index < self.layers.len() {
if matches!(event, Event::Tick) && self.layers[index].child.tick().is_none() {
index += 1;
continue;
}
let inner = places
.get(index)
.map_or(Rect::default(), |(_, inner)| *inner);
let flow = self.layers[index]
.child
.handle(event, &inner.context(context));
let count = self.layers.len();
if let Some(flow) = self.after_layer(index, flow)
&& decisive(&flow)
{
return flow;
}
if self.layers.len() == count {
index += 1;
}
}
Flow::Continue
}
fn boxed(
&self,
layer: &Layer<'a, M>,
view: &View,
outer: Rect,
inner: Rect,
) -> Vec<Vec<Segment>> {
let lines = rows_of(view, inner, true)
.into_iter()
.map(|line| kit::pad(line, inner.width))
.collect::<Vec<_>>();
if !(layer.border && inner != outer) {
return lines;
}
kit::frame(lines, inner.width, layer.title.as_deref(), &self.border)
}
}
impl<M> Component for Layers<'_, M> {
type Output = M;
fn start(&mut self, context: &Context<'_>) -> Flow<M> {
let flow = self.base.start(context);
if decisive(&flow) {
return flow;
}
let places = self.placements(context);
let mut index = 0;
while index < self.layers.len() {
let inner = places
.get(index)
.map_or(Rect::default(), |(_, inner)| *inner);
let flow = self.layers[index].child.start(&inner.context(context));
let count = self.layers.len();
if let Some(flow) = self.after_layer(index, flow)
&& decisive(&flow)
{
return flow;
}
if self.layers.len() == count {
index += 1;
}
}
self.apply(context)
}
fn handle(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
let before = self.apply(context);
if decisive(&before) {
return before;
}
let flow = if self.layers.is_empty() {
self.handle_base(event, context)
} else {
self.handle_top(event, context)
};
self.open.set(self.layers.len());
if decisive(&flow) {
return flow;
}
let after = self.apply(context);
if decisive(&after) {
return after;
}
flow
}
fn render(&self, context: &Context<'_>) -> View {
let base = self.base.render(context);
if self.layers.is_empty() {
return base;
}
let places = self.placements(context);
let mut lines = base.lines;
let mut cursor = None;
for (index, layer) in self.layers.iter().enumerate() {
let (outer, inner) = places[index];
let view = layer.child.render(&inner.context(context));
let boxed = self.boxed(layer, &view, outer, inner);
let backdrop = layer.backdrop.then_some(&self.backdrop);
kit::place(&mut lines, outer.x, outer.y, &boxed, backdrop);
cursor = view
.cursor
.filter(|(row, column)| *row < inner.height && *column < inner.width.max(1))
.map(|(row, column)| (inner.y + row, inner.x + column));
}
View { lines, cursor }
}
fn tick(&self) -> Option<Duration> {
self.layers
.iter()
.filter_map(|layer| layer.child.tick())
.chain(self.base.tick())
.min()
}
fn mouse(&self) -> bool {
self.mouse || self.base.mouse() || self.layers.iter().any(|layer| layer.child.mouse())
}
fn keymap(&self) -> Keymap {
match self.layers.last() {
Some(layer) => keymap_with(Some(&layer.child), &self.bindings.keymap),
None => keymap_with(Some(&self.base), &self.bindings.keymap),
}
}
fn focusable(&self) -> bool {
match self.layers.last() {
Some(_) => true,
None => self.base.focusable(),
}
}
fn focus_step(&mut self, forward: bool) -> bool {
match self.layers.last_mut() {
Some(layer) => {
if !layer.child.focus_step(forward) {
layer.child.focus_enter(forward);
}
true
}
None => self.base.focus_step(forward),
}
}
fn focus_enter(&mut self, forward: bool) -> bool {
match self.layers.last_mut() {
Some(layer) => {
layer.child.focus_enter(forward);
true
}
None => self.base.focus_enter(forward),
}
}
fn default_value(&self) -> Option<M> {
self.base.default_value()
}
fn prompt(&mut self, io: &mut dyn LineIo) -> Result<Option<M>, NotInteractive> {
self.base.prompt(io)
}
}
impl<M> Layers<'_, M> {
fn handle_base(&mut self, event: &Event, context: &Context<'_>) -> Flow<M> {
if let Some(key) = event.key()
&& let Some(action) = self.bindings.action(key, true)
&& let Some(flow) = self.bindings.run(&action)
{
return flow;
}
if let Event::Mouse(mouse) = event {
if !self.base.mouse() {
return Flow::Ignored;
}
match mouse.kind {
MouseKind::Down(_) => self.grab = Some(None),
MouseKind::Drag(_) | MouseKind::Up(_) if matches!(self.grab, Some(Some(_))) => {
if matches!(mouse.kind, MouseKind::Up(_)) {
self.grab = None;
}
return Flow::Continue;
}
MouseKind::Up(_) => self.grab = None,
_ => {}
}
}
if let Event::Returned(_) = event {
self.handoff = None;
}
let flow = self.base.handle(event, context);
if let Flow::Handoff(_) = flow {
self.handoff = Some(None);
}
if !matches!(flow, Flow::Ignored) {
return flow;
}
let Some(action) = event.key().and_then(|key| self.bindings.action(key, false)) else {
return Flow::Ignored;
};
match action.as_str() {
FOCUS_NEXT | FOCUS_PREVIOUS | "dismiss" => Flow::Ignored,
_ => self.bindings.run(&action).unwrap_or(Flow::Ignored),
}
}
}