mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-11-16 00:10:38 +08:00
lib: Make widgets mutable in render function
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use ratatui::layout::Constraint;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::{Line, Text};
|
||||
use ratatui::widgets::Row;
|
||||
use ratatui::Frame;
|
||||
|
||||
use radicle_tui as tui;
|
||||
|
||||
use tui::ui::span;
|
||||
use tui::ui::widget::{RenderProps, View, ViewProps};
|
||||
|
||||
use super::items::IssueItem;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct IssueDetailsProps {
|
||||
issue: Option<IssueItem>,
|
||||
}
|
||||
|
||||
impl IssueDetailsProps {
|
||||
pub fn issue(mut self, issue: Option<IssueItem>) -> Self {
|
||||
self.issue = issue;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IssueDetails<S, M> {
|
||||
/// Phantom
|
||||
phantom: PhantomData<(S, M)>,
|
||||
}
|
||||
|
||||
impl<S, M> Default for IssueDetails<S, M> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, M> View for IssueDetails<S, M> {
|
||||
type State = S;
|
||||
type Message = M;
|
||||
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = IssueDetailsProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<IssueDetailsProps>())
|
||||
.unwrap_or(&default);
|
||||
|
||||
if let Some(issue) = props.issue.as_ref() {
|
||||
let author = match &issue.author.alias {
|
||||
Some(alias) => {
|
||||
if issue.author.you {
|
||||
span::alias(&format!("{} (you)", alias))
|
||||
} else {
|
||||
span::alias(alias)
|
||||
}
|
||||
}
|
||||
None => match &issue.author.human_nid {
|
||||
Some(nid) => span::alias(nid).dim(),
|
||||
None => span::blank(),
|
||||
},
|
||||
};
|
||||
let did = match &issue.author.human_nid {
|
||||
Some(nid) => span::alias(nid).dim(),
|
||||
None => span::blank(),
|
||||
};
|
||||
|
||||
let table = ratatui::widgets::Table::new(
|
||||
[
|
||||
Row::new([
|
||||
Text::raw("Title").cyan(),
|
||||
Text::raw(issue.title.clone()).bold(),
|
||||
]),
|
||||
Row::new([
|
||||
Text::raw("Issue").cyan(),
|
||||
Text::raw(issue.id.to_string()).bold(),
|
||||
]),
|
||||
Row::new([
|
||||
Text::raw("Author").cyan(),
|
||||
Line::from([author, " ".into(), did].to_vec()).into(),
|
||||
]),
|
||||
Row::new([Text::raw("Status").cyan(), Text::raw("???").magenta()]),
|
||||
],
|
||||
[Constraint::Length(8), Constraint::Fill(1)],
|
||||
);
|
||||
frame.render_widget(table, render.area);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -177,7 +177,7 @@ pub trait View {
|
||||
fn update(&mut self, _props: Option<&ViewProps>, _state: &Self::State) {}
|
||||
|
||||
/// Should render the view using the given `RenderProps`.
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame);
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame);
|
||||
}
|
||||
|
||||
/// A `View` needs to wrapped into a `Widget` before being able to use with the
|
||||
@@ -243,7 +243,7 @@ impl<S: 'static, M: 'static> Widget<S, M> {
|
||||
}
|
||||
|
||||
/// Renders the wrapped view.
|
||||
pub fn render(&self, render: RenderProps, frame: &mut Frame) {
|
||||
pub fn render(&mut self, render: RenderProps, frame: &mut Frame) {
|
||||
self.view.render(self.props.as_ref(), render.clone(), frame);
|
||||
|
||||
if let Some(on_render) = self.on_render {
|
||||
|
||||
+11
-11
@@ -80,7 +80,7 @@ impl<'a: 'static, S, M> View for Header<S, M> {
|
||||
type Message = M;
|
||||
type State = S;
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = HeaderProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<HeaderProps>())
|
||||
@@ -210,7 +210,7 @@ impl<'a: 'static, S, M> View for Footer<S, M> {
|
||||
type Message = M;
|
||||
type State = S;
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = FooterProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<FooterProps>())
|
||||
@@ -326,7 +326,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = ContainerProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<ContainerProps>())
|
||||
@@ -362,18 +362,18 @@ where
|
||||
.borders(borders);
|
||||
frame.render_widget(block.clone(), content_area);
|
||||
|
||||
if let Some(header) = &self.header {
|
||||
if let Some(header) = self.header.as_mut() {
|
||||
header.render(RenderProps::from(header_area).focus(render.focus), frame);
|
||||
}
|
||||
|
||||
if let Some(content) = &self.content {
|
||||
if let Some(content) = self.content.as_mut() {
|
||||
content.render(
|
||||
RenderProps::from(block.inner(content_area)).focus(render.focus),
|
||||
frame,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(footer) = &self.footer {
|
||||
if let Some(footer) = self.footer.as_mut() {
|
||||
footer.render(RenderProps::from(footer_area).focus(render.focus), frame);
|
||||
}
|
||||
}
|
||||
@@ -472,7 +472,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = SplitContainerProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<SplitContainerProps>())
|
||||
@@ -492,7 +492,7 @@ where
|
||||
|
||||
let [top_area, bottom_area] = Layout::vertical(heights).areas(render.area);
|
||||
|
||||
if let Some(top) = self.top.as_ref() {
|
||||
if let Some(top) = self.top.as_mut() {
|
||||
let block = HeaderBlock::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(style::border(render.focus))
|
||||
@@ -509,7 +509,7 @@ where
|
||||
top.render(RenderProps::from(top_area).focus(render.focus), frame)
|
||||
}
|
||||
|
||||
if let Some(bottom) = self.bottom.as_ref() {
|
||||
if let Some(bottom) = self.bottom.as_mut() {
|
||||
let block = Block::default()
|
||||
.borders(Borders::LEFT | Borders::RIGHT | Borders::BOTTOM)
|
||||
.border_style(style::border(render.focus))
|
||||
@@ -637,7 +637,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = SectionGroupProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<SectionGroupProps>())
|
||||
@@ -646,7 +646,7 @@ where
|
||||
let areas = props.layout.split(render.area);
|
||||
|
||||
for (index, area) in areas.iter().enumerate() {
|
||||
if let Some(section) = self.sections.get(index) {
|
||||
if let Some(section) = self.sections.get_mut(index) {
|
||||
let focus = self
|
||||
.state
|
||||
.focus
|
||||
|
||||
@@ -227,7 +227,7 @@ where
|
||||
self.state.text = Some(props.text.clone());
|
||||
}
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = TextFieldProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<TextFieldProps>())
|
||||
|
||||
@@ -216,7 +216,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = TableProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<TableProps<R, W>>())
|
||||
@@ -261,7 +261,7 @@ where
|
||||
.column_spacing(1)
|
||||
.highlight_style(style::highlight());
|
||||
|
||||
frame.render_stateful_widget(rows, render.area, &mut self.state.clone());
|
||||
frame.render_stateful_widget(rows, render.area, &mut self.state);
|
||||
} else {
|
||||
let center = layout::centered_rect(render.area, 50, 10);
|
||||
let hint = Text::from(span::default("Nothing to show"))
|
||||
|
||||
@@ -168,7 +168,7 @@ where
|
||||
None
|
||||
}
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let default = TextAreaProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<TextAreaProps>())
|
||||
|
||||
@@ -98,7 +98,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, _render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, _render: RenderProps, frame: &mut Frame) {
|
||||
let default = WindowProps::default();
|
||||
let props = props
|
||||
.and_then(|props| props.inner_ref::<WindowProps<Id>>())
|
||||
@@ -109,7 +109,7 @@ where
|
||||
let page = props
|
||||
.current_page
|
||||
.as_ref()
|
||||
.and_then(|id| self.pages.get(id));
|
||||
.and_then(|id| self.pages.get_mut(id));
|
||||
|
||||
if let Some(page) = page {
|
||||
page.render(RenderProps::from(area).focus(true), frame);
|
||||
@@ -190,11 +190,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, _props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, _props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
let [content_area, shortcuts_area] =
|
||||
Layout::vertical([Constraint::Min(1), Constraint::Length(1)]).areas(render.area);
|
||||
|
||||
if let Some(content) = self.content.as_ref() {
|
||||
if let Some(content) = self.content.as_mut() {
|
||||
content.render(
|
||||
RenderProps::from(content_area)
|
||||
.layout(Layout::horizontal([Constraint::Min(1)]))
|
||||
@@ -203,7 +203,7 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(shortcuts) = self.shortcuts.as_ref() {
|
||||
if let Some(shortcuts) = self.shortcuts.as_mut() {
|
||||
shortcuts.render(RenderProps::from(shortcuts_area), frame);
|
||||
}
|
||||
}
|
||||
@@ -256,7 +256,7 @@ impl<S, M> View for Shortcuts<S, M> {
|
||||
type Message = M;
|
||||
type State = S;
|
||||
|
||||
fn render(&self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
fn render(&mut self, props: Option<&ViewProps>, render: RenderProps, frame: &mut Frame) {
|
||||
use ratatui::widgets::Table;
|
||||
|
||||
let default = ShortcutsProps::default();
|
||||
|
||||
Reference in New Issue
Block a user