mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-11-16 00:10:38 +08:00
lib: Implement container widget
This commit is contained in:
+136
-1
@@ -11,7 +11,7 @@ use ratatui::widgets::{BorderType, Borders, Row};
|
||||
use crate::ui::ext::{FooterBlock, FooterBlockType, HeaderBlock};
|
||||
use crate::ui::theme::style;
|
||||
|
||||
use super::{Column, EventCallback, Properties, UpdateCallback, View, Widget};
|
||||
use super::{BoxedWidget, Column, EventCallback, Properties, UpdateCallback, View, Widget};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HeaderProps<'a> {
|
||||
@@ -343,3 +343,138 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ContainerProps {
|
||||
_focus: bool,
|
||||
}
|
||||
|
||||
impl Properties for ContainerProps {}
|
||||
|
||||
pub struct Container<B, S, A>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
/// Internal properties
|
||||
props: ContainerProps,
|
||||
/// Message sender
|
||||
_action_tx: UnboundedSender<A>,
|
||||
/// Custom update handler
|
||||
on_update: Option<UpdateCallback<S>>,
|
||||
/// Additional custom event handler
|
||||
on_change: Option<EventCallback<A>>,
|
||||
/// Container header
|
||||
header: Option<BoxedWidget<B, S, A>>,
|
||||
/// Content widget
|
||||
content: Option<BoxedWidget<B, S, A>>,
|
||||
/// Container footer
|
||||
footer: Option<BoxedWidget<B, S, A>>,
|
||||
}
|
||||
|
||||
impl<B, S, A> Container<B, S, A>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
pub fn header(mut self, header: BoxedWidget<B, S, A>) -> Self {
|
||||
self.header = Some(header);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn content(mut self, content: BoxedWidget<B, S, A>) -> Self {
|
||||
self.content = Some(content);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn footer(mut self, footer: BoxedWidget<B, S, A>) -> Self {
|
||||
self.footer = Some(footer);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, S, A> View<S, A> for Container<B, S, A>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
fn new(_state: &S, action_tx: UnboundedSender<A>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self {
|
||||
_action_tx: action_tx.clone(),
|
||||
props: ContainerProps::default(),
|
||||
header: None,
|
||||
content: None,
|
||||
footer: None,
|
||||
on_update: None,
|
||||
on_change: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_update(mut self, callback: UpdateCallback<S>) -> Self {
|
||||
self.on_update = Some(callback);
|
||||
self
|
||||
}
|
||||
|
||||
fn on_change(mut self, callback: EventCallback<A>) -> Self {
|
||||
self.on_change = Some(callback);
|
||||
self
|
||||
}
|
||||
|
||||
fn update(&mut self, state: &S) {
|
||||
self.props = self
|
||||
.on_update
|
||||
.and_then(|on_update| (on_update)(state).downcast_ref::<ContainerProps>().cloned())
|
||||
.unwrap_or(self.props.clone());
|
||||
|
||||
if let Some(header) = &mut self.header {
|
||||
header.update(state);
|
||||
}
|
||||
|
||||
if let Some(content) = &mut self.content {
|
||||
content.update(state);
|
||||
}
|
||||
|
||||
if let Some(footer) = &mut self.footer {
|
||||
footer.update(state);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, key: termion::event::Key) {
|
||||
if let Some(content) = &mut self.content {
|
||||
content.handle_key_event(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a: 'static, B, S, A> Widget<B, S, A> for Container<B, S, A>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
fn render(&self, frame: &mut ratatui::Frame, area: Rect, props: Option<&dyn Any>) {
|
||||
let _props = props
|
||||
.and_then(|props| props.downcast_ref::<ContainerProps>())
|
||||
.unwrap_or(&self.props);
|
||||
|
||||
let header_h = if self.header.is_some() { 3 } else { 0 };
|
||||
let footer_h = if self.footer.is_some() { 3 } else { 0 };
|
||||
|
||||
let [header_area, content_area, footer_area] = Layout::vertical([
|
||||
Constraint::Length(header_h),
|
||||
Constraint::Min(1),
|
||||
Constraint::Length(footer_h),
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
if let Some(header) = &self.header {
|
||||
header.render(frame, header_area, None);
|
||||
}
|
||||
|
||||
if let Some(content) = &self.content {
|
||||
content.render(frame, content_area, None);
|
||||
}
|
||||
|
||||
if let Some(footer) = &self.footer {
|
||||
footer.render(frame, footer_area, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,6 @@ impl<'a: 'static, S, A> View<S, A> for Paragraph<'a, S, A> {
|
||||
}
|
||||
|
||||
if let Some(on_change) = self.on_change {
|
||||
// (on_change)(&self.props, self.action_tx.clone());
|
||||
(on_change)(&self.state, self.action_tx.clone());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user