tui: Support container focus

This commit is contained in:
Erik Kundt 2023-06-09 09:55:52 +02:00 committed by Alexis Sellier
parent dcfd481424
commit 3a7cef8cea
No known key found for this signature in database
7 changed files with 95 additions and 25 deletions

View File

@ -111,8 +111,8 @@ impl ViewPage for HomeView {
) -> Result<()> { ) -> Result<()> {
if let Message::NavigationChanged(index) = message { if let Message::NavigationChanged(index) = message {
self.active_component = Cid::Home(HomeCid::from(index as usize)); self.active_component = Cid::Home(HomeCid::from(index as usize));
app.active(&self.active_component)?;
} }
app.active(&self.active_component)?;
Ok(()) Ok(())
} }
@ -148,16 +148,12 @@ impl ViewPage for HomeView {
/// Issue detail page /// Issue detail page
/// ///
pub struct IssuePage { pub struct IssuePage {
active_component: Cid,
issue: (IssueId, Issue), issue: (IssueId, Issue),
} }
impl IssuePage { impl IssuePage {
pub fn new(issue: (IssueId, Issue)) -> Self { pub fn new(issue: (IssueId, Issue)) -> Self {
IssuePage { IssuePage { issue }
active_component: Cid::Issue(IssueCid::List),
issue,
}
} }
} }
@ -186,7 +182,7 @@ impl ViewPage for IssuePage {
app.remount(Cid::Issue(IssueCid::Details), details, vec![])?; app.remount(Cid::Issue(IssueCid::Details), details, vec![])?;
app.remount(Cid::Issue(IssueCid::Shortcuts), shortcuts, vec![])?; app.remount(Cid::Issue(IssueCid::Shortcuts), shortcuts, vec![])?;
app.active(&self.active_component)?; app.active(&Cid::Issue(IssueCid::List))?;
Ok(()) Ok(())
} }
@ -212,8 +208,14 @@ impl ViewPage for IssuePage {
let details = widget::issue::details(context, theme, (id, issue)).to_boxed(); let details = widget::issue::details(context, theme, (id, issue)).to_boxed();
app.remount(Cid::Issue(IssueCid::Details), details, vec![])?; app.remount(Cid::Issue(IssueCid::Details), details, vec![])?;
} }
Message::Issue(IssueMessage::FocusList) => {
app.active(&Cid::Issue(IssueCid::List))?;
}
Message::Issue(IssueMessage::FocusDiscussion) => {
app.active(&Cid::Issue(IssueCid::Discussion))?;
}
_ => {}
} }
app.active(&self.active_component)?;
Ok(()) Ok(())
} }

View File

@ -36,6 +36,8 @@ pub struct Colors {
pub context_id_fg: Color, pub context_id_fg: Color,
pub context_id_bg: Color, pub context_id_bg: Color,
pub context_id_author_fg: Color, pub context_id_author_fg: Color,
pub container_border_fg: Color,
pub container_border_focus_fg: Color,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -105,6 +107,8 @@ pub fn default_dark() -> Theme {
context_id_fg: Color::Cyan, context_id_fg: Color::Cyan,
context_id_bg: COLOR_DEFAULT_DARKEST, context_id_bg: COLOR_DEFAULT_DARKEST,
context_id_author_fg: Color::Gray, context_id_author_fg: Color::Gray,
container_border_fg: COLOR_DEFAULT_DARKEST,
container_border_focus_fg: COLOR_DEFAULT_DARK,
}, },
icons: Icons { icons: Icons {
property_divider: '', property_divider: '',

View File

@ -45,8 +45,8 @@ pub fn container_header(theme: &Theme, label: Widget<Label>) -> Widget<Header<1>
Widget::new(header) Widget::new(header)
} }
pub fn container(_theme: &Theme, component: Box<dyn MockComponent>) -> Widget<Container> { pub fn container(theme: &Theme, component: Box<dyn MockComponent>) -> Widget<Container> {
let container = Container::new(component); let container = Container::new(component, theme.clone());
Widget::new(container) Widget::new(container)
} }
@ -59,7 +59,7 @@ pub fn labeled_container(
theme, theme,
label(&format!(" {title} ")).foreground(theme.colors.default_fg), label(&format!(" {title} ")).foreground(theme.colors.default_fg),
); );
let container = LabeledContainer::new(header, component); let container = LabeledContainer::new(header, component, theme.clone());
Widget::new(container) Widget::new(container)
} }

View File

@ -1,7 +1,5 @@
use tuirealm::command::{Cmd, CmdResult}; use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::{ use tuirealm::props::{AttrValue, Attribute, BorderSides, BorderType, Props, Style, TextModifiers};
AttrValue, Attribute, BorderSides, BorderType, Color, Props, Style, TextModifiers,
};
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect}; use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::tui::widgets::{Block, Cell, Row}; use tuirealm::tui::widgets::{Block, Cell, Row};
use tuirealm::{Frame, MockComponent, State, StateValue}; use tuirealm::{Frame, MockComponent, State, StateValue};
@ -276,10 +274,20 @@ impl<const W: usize> WidgetComponent for Header<W> {
let display = properties let display = properties
.get_or(Attribute::Display, AttrValue::Flag(true)) .get_or(Attribute::Display, AttrValue::Flag(true))
.unwrap_flag(); .unwrap_flag();
let focus = properties
.get_or(Attribute::Focus, AttrValue::Flag(false))
.unwrap_flag();
let color = if focus {
self.theme.colors.container_border_focus_fg
} else {
self.theme.colors.container_border_fg
};
if display { if display {
let block = HeaderBlock::default() let block = HeaderBlock::default()
.borders(BorderSides::all()) .borders(BorderSides::all())
.border_style(Style::default().fg(Color::Rgb(48, 48, 48))) .border_style(Style::default().fg(color))
.border_type(BorderType::Rounded); .border_type(BorderType::Rounded);
frame.render_widget(block, area); frame.render_widget(block, area);
@ -322,11 +330,12 @@ impl<const W: usize> WidgetComponent for Header<W> {
pub struct Container { pub struct Container {
component: Box<dyn MockComponent>, component: Box<dyn MockComponent>,
theme: Theme,
} }
impl Container { impl Container {
pub fn new(component: Box<dyn MockComponent>) -> Self { pub fn new(component: Box<dyn MockComponent>, theme: Theme) -> Self {
Self { component } Self { component, theme }
} }
} }
@ -335,6 +344,15 @@ impl WidgetComponent for Container {
let display = properties let display = properties
.get_or(Attribute::Display, AttrValue::Flag(true)) .get_or(Attribute::Display, AttrValue::Flag(true))
.unwrap_flag(); .unwrap_flag();
let focus = properties
.get_or(Attribute::Focus, AttrValue::Flag(false))
.unwrap_flag();
let color = if focus {
self.theme.colors.container_border_focus_fg
} else {
self.theme.colors.container_border_fg
};
if display { if display {
// Make some space on the left // Make some space on the left
@ -349,7 +367,7 @@ impl WidgetComponent for Container {
let block = Block::default() let block = Block::default()
.borders(BorderSides::ALL) .borders(BorderSides::ALL)
.border_style(Style::default().fg(Color::Rgb(48, 48, 48))) .border_style(Style::default().fg(color))
.border_type(BorderType::Rounded); .border_type(BorderType::Rounded);
frame.render_widget(block, area); frame.render_widget(block, area);
} }
@ -367,11 +385,16 @@ impl WidgetComponent for Container {
pub struct LabeledContainer { pub struct LabeledContainer {
header: Widget<Header<1>>, header: Widget<Header<1>>,
component: Box<dyn MockComponent>, component: Box<dyn MockComponent>,
theme: Theme,
} }
impl LabeledContainer { impl LabeledContainer {
pub fn new(header: Widget<Header<1>>, component: Box<dyn MockComponent>) -> Self { pub fn new(header: Widget<Header<1>>, component: Box<dyn MockComponent>, theme: Theme) -> Self {
Self { header, component } Self {
header,
component,
theme,
}
} }
} }
@ -380,6 +403,16 @@ impl WidgetComponent for LabeledContainer {
let display = properties let display = properties
.get_or(Attribute::Display, AttrValue::Flag(true)) .get_or(Attribute::Display, AttrValue::Flag(true))
.unwrap_flag(); .unwrap_flag();
let focus = properties
.get_or(Attribute::Focus, AttrValue::Flag(false))
.unwrap_flag();
let color = if focus {
self.theme.colors.container_border_focus_fg
} else {
self.theme.colors.container_border_fg
};
let header_height = self let header_height = self
.header .header
.query(Attribute::Height) .query(Attribute::Height)
@ -399,14 +432,17 @@ impl WidgetComponent for LabeledContainer {
.constraints(vec![Constraint::Length(1), Constraint::Min(0)].as_ref()) .constraints(vec![Constraint::Length(1), Constraint::Min(0)].as_ref())
.split(layout[1]); .split(layout[1]);
// reverse draw order: child needs to be drawn first? // reverse draw order: child needs to be drawn first?
self.component.attr(Attribute::Focus, AttrValue::Flag(focus));
self.component.view(frame, inner_layout[1]); self.component.view(frame, inner_layout[1]);
let block = Block::default() let block = Block::default()
.borders(BorderSides::BOTTOM | BorderSides::LEFT | BorderSides::RIGHT) .borders(BorderSides::BOTTOM | BorderSides::LEFT | BorderSides::RIGHT)
.border_style(Style::default().fg(Color::Rgb(48, 48, 48))) .border_style(Style::default().fg(color))
.border_type(BorderType::Rounded); .border_type(BorderType::Rounded);
frame.render_widget(block, layout[1]); frame.render_widget(block, layout[1]);
self.header.attr(Attribute::Focus, AttrValue::Flag(focus));
self.header.view(frame, layout[0]); self.header.view(frame, layout[0]);
} }
} }

View File

@ -225,6 +225,16 @@ where
.get_or(Attribute::HighlightedColor, AttrValue::Color(Color::Reset)) .get_or(Attribute::HighlightedColor, AttrValue::Color(Color::Reset))
.unwrap_color(); .unwrap_color();
let focus = properties
.get_or(Attribute::Focus, AttrValue::Flag(false))
.unwrap_flag();
let color = if focus {
self.theme.colors.container_border_focus_fg
} else {
self.theme.colors.container_border_fg
};
let layout = Layout::default() let layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(vec![Constraint::Length(3), Constraint::Min(1)]) .constraints(vec![Constraint::Length(3), Constraint::Min(1)])
@ -241,7 +251,7 @@ where
.block( .block(
Block::default() Block::default()
.borders(BorderSides::BOTTOM | BorderSides::LEFT | BorderSides::RIGHT) .borders(BorderSides::BOTTOM | BorderSides::LEFT | BorderSides::RIGHT)
.border_style(Style::default().fg(Color::Rgb(48, 48, 48))) .border_style(Style::default().fg(color))
.border_type(BorderType::Rounded), .border_type(BorderType::Rounded),
) )
.highlight_style(Style::default().bg(highlight)) .highlight_style(Style::default().bg(highlight))
@ -253,7 +263,10 @@ where
self.widths, self.widths,
self.theme.clone(), self.theme.clone(),
)); ));
header.attr(Attribute::Focus, AttrValue::Flag(focus));
header.view(frame, layout[0]); header.view(frame, layout[0]);
frame.render_stateful_widget(table, layout[1], &mut TableState::from(&self.state)); frame.render_stateful_widget(table, layout[1], &mut TableState::from(&self.state));
} }

View File

@ -106,14 +106,19 @@ impl IssueBrowser {
} }
impl WidgetComponent for IssueBrowser { impl WidgetComponent for IssueBrowser {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) { fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
let shortcuts_h = self let shortcuts_h = self
.shortcuts .shortcuts
.query(Attribute::Height) .query(Attribute::Height)
.unwrap_or(AttrValue::Size(0)) .unwrap_or(AttrValue::Size(0))
.unwrap_size(); .unwrap_size();
let focus = properties
.get_or(Attribute::Focus, AttrValue::Flag(false))
.unwrap_flag();
let layout = layout::root_component(area, shortcuts_h); let layout = layout::root_component(area, shortcuts_h);
self.table.attr(Attribute::Focus, AttrValue::Flag(focus));
self.table.view(frame, layout[0]); self.table.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]) self.shortcuts.view(frame, layout[1])
} }
@ -187,14 +192,19 @@ impl PatchBrowser {
} }
impl WidgetComponent for PatchBrowser { impl WidgetComponent for PatchBrowser {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) { fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
let shortcuts_h = self let shortcuts_h = self
.shortcuts .shortcuts
.query(Attribute::Height) .query(Attribute::Height)
.unwrap_or(AttrValue::Size(0)) .unwrap_or(AttrValue::Size(0))
.unwrap_size(); .unwrap_size();
let focus = properties
.get_or(Attribute::Focus, AttrValue::Flag(false))
.unwrap_flag();
let layout = layout::root_component(area, shortcuts_h); let layout = layout::root_component(area, shortcuts_h);
self.table.attr(Attribute::Focus, AttrValue::Flag(focus));
self.table.view(frame, layout[0]); self.table.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]); self.shortcuts.view(frame, layout[1]);
} }

View File

@ -56,7 +56,12 @@ impl LargeList {
} }
impl WidgetComponent for LargeList { impl WidgetComponent for LargeList {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) { fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
let focus = properties
.get_or(Attribute::Focus, AttrValue::Flag(false))
.unwrap_flag();
self.list.attr(Attribute::Focus, AttrValue::Flag(focus));
self.list.view(frame, area); self.list.view(frame, area);
} }