tui: Show project payload

Signed-off-by: Erik Kundt <erik@zirkular.io>
This commit is contained in:
Erik Kundt 2023-02-22 12:35:56 +01:00 committed by Alexis Sellier
parent 897f4c5250
commit 791c378d4e
No known key found for this signature in database
4 changed files with 161 additions and 4 deletions

View File

@ -9,7 +9,7 @@ use tuirealm::tui::layout::{Constraint, Direction, Layout};
use tuirealm::{Application, Component, Frame, NoUserEvent, Sub, SubClause, SubEventClause};
use radicle_tui::ui;
use radicle_tui::ui::components::{GlobalListener, ShortcutBar};
use radicle_tui::ui::components::{GlobalListener, LabeledContainer, PropertyList, ShortcutBar};
use radicle_tui::ui::theme;
use radicle_tui::ui::widget::Widget;
@ -33,6 +33,7 @@ pub enum Message {
/// All components known to this application.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum ComponentId {
Workspaces,
ShortcutBar,
GlobalListener,
}
@ -53,6 +54,25 @@ impl Tui<ComponentId, Message> for App {
fn init(&mut self, app: &mut Application<ComponentId, Message, NoUserEvent>) -> Result<()> {
let theme = theme::default_dark();
app.mount(
ComponentId::Workspaces,
ui::labeled_container(
&theme,
"about",
ui::property_list(
&theme,
vec![
ui::property(&theme, "id", &self.id.to_string()),
ui::property(&theme, "name", self.project.name()),
ui::property(&theme, "description", self.project.description()),
],
)
.to_boxed(),
)
.to_boxed(),
vec![],
)?;
app.mount(
ComponentId::ShortcutBar,
ui::shortcut_bar(
@ -114,6 +134,7 @@ impl Tui<ComponentId, Message> for App {
)
.split(area);
app.view(&ComponentId::Workspaces, frame, layout[0]);
app.view(&ComponentId::ShortcutBar, frame, layout[1]);
}
@ -147,6 +168,18 @@ impl Component<Message, NoUserEvent> for Widget<GlobalListener> {
}
}
impl Component<Message, NoUserEvent> for Widget<LabeledContainer> {
fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
None
}
}
impl Component<Message, NoUserEvent> for Widget<PropertyList> {
fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
None
}
}
impl Component<Message, NoUserEvent> for Widget<ShortcutBar> {
fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
None

View File

@ -6,7 +6,10 @@ pub mod widget;
use tuirealm::props::Attribute;
use tuirealm::{MockComponent, StateValue};
use components::{GlobalListener, Label, Property, PropertyList, Shortcut, ShortcutBar};
use components::{
ContainerHeader, GlobalListener, Label, LabeledContainer, Property, PropertyList, Shortcut,
ShortcutBar,
};
use widget::Widget;
pub fn global_listener() -> Widget<GlobalListener> {
@ -21,6 +24,21 @@ pub fn label(content: &str) -> Widget<Label> {
Widget::new(label).height(1).width(width)
}
pub fn labeled_container(
theme: &theme::Theme,
title: &str,
component: Box<dyn MockComponent>,
) -> Widget<LabeledContainer> {
let title = label(&format!(" {} ", title))
.foreground(theme.colors.default_fg)
.background(theme.colors.labeled_container_bg);
let spacer = label("");
let header = Widget::new(ContainerHeader::new(title, spacer));
let container = LabeledContainer::new(header, component);
Widget::new(container).background(theme.colors.labeled_container_bg)
}
pub fn shortcut(theme: &theme::Theme, short: &str, long: &str) -> Widget<Shortcut> {
let short = label(short).foreground(theme.colors.shortcut_short_fg);
let divider = label(&theme.icons.whitespace.to_string());

View File

@ -1,8 +1,9 @@
use tui_realm_stdlib::Phantom;
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::{AttrValue, Attribute, Color, Props};
use tuirealm::tui::layout::Rect;
use tuirealm::props::{AttrValue, Attribute, Color, Props, Style};
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::tui::widgets::Block;
use tuirealm::{Frame, MockComponent, State, StateValue};
use super::layout;
@ -57,15 +58,20 @@ impl WidgetComponent for Label {
let foreground = properties
.get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
.unwrap_color();
let background = properties
.get_or(Attribute::Background, AttrValue::Color(Color::Reset))
.unwrap_color();
if display {
let mut label = match properties.get(Attribute::TextProps) {
Some(modifiers) => Label::default()
.foreground(foreground)
.background(background)
.modifiers(modifiers.unwrap_text_modifiers())
.text(self.content.clone().unwrap_string()),
None => Label::default()
.foreground(foreground)
.background(background)
.text(self.content.clone().unwrap_string()),
};
@ -82,6 +88,102 @@ impl WidgetComponent for Label {
}
}
/// A labeled container header.
#[derive(Clone)]
pub struct ContainerHeader {
content: Widget<Label>,
spacer: Widget<Label>,
}
impl ContainerHeader {
pub fn new(content: Widget<Label>, spacer: Widget<Label>) -> Self {
Self { content, spacer }
}
}
impl WidgetComponent for ContainerHeader {
fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
let display = properties
.get_or(Attribute::Display, AttrValue::Flag(true))
.unwrap_flag();
if display {
let labels: Vec<Box<dyn MockComponent>> = vec![
self.content.clone().to_boxed(),
self.spacer.clone().to_boxed(),
];
let layout = layout::h_stack(labels, area);
for (mut shortcut, area) in layout {
shortcut.view(frame, area);
}
}
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
pub struct LabeledContainer {
header: Widget<ContainerHeader>,
component: Box<dyn MockComponent>,
}
impl LabeledContainer {
pub fn new(header: Widget<ContainerHeader>, component: Box<dyn MockComponent>) -> Self {
Self { header, component }
}
}
impl WidgetComponent for LabeledContainer {
fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
let display = properties
.get_or(Attribute::Display, AttrValue::Flag(true))
.unwrap_flag();
let background = properties
.get_or(Attribute::Background, AttrValue::Color(Color::Reset))
.unwrap_color();
let header_height = self
.header
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
if display {
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(header_height), Constraint::Length(0)].as_ref())
.split(area);
self.header.view(frame, layout[0]);
// Make some space on the left
let inner_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints(vec![Constraint::Length(1), Constraint::Min(0)].as_ref())
.split(layout[1]);
// reverse draw order: child needs to be drawn first?
self.component.view(frame, inner_layout[1]);
let block = Block::default().style(Style::default().bg(background));
frame.render_widget(block, layout[1]);
}
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, cmd: Cmd) -> CmdResult {
self.component.perform(cmd)
}
}
/// A shortcut that consists of a label displaying the "hotkey", a label that displays
/// the action and a spacer between them.
#[derive(Clone)]
@ -226,6 +328,8 @@ impl WidgetComponent for Property {
}
/// A component that can display lists of labeled properties
#[derive(Default)]
#[allow(clippy::vec_box)]
pub struct PropertyList {
properties: Vec<Widget<Property>>,
}

View File

@ -3,6 +3,7 @@ use tuirealm::props::Color;
#[derive(Debug)]
pub struct Colors {
pub default_fg: Color,
pub labeled_container_bg: Color,
pub property_name_fg: Color,
pub property_divider_fg: Color,
pub shortcut_short_fg: Color,
@ -43,6 +44,7 @@ pub fn default_dark() -> Theme {
name: String::from("Radicle Dark"),
colors: Colors {
default_fg: Color::Rgb(200, 200, 200),
labeled_container_bg: Color::Rgb(20, 20, 20),
property_name_fg: Color::Rgb(85, 85, 255),
property_divider_fg: Color::Rgb(10, 206, 209),
shortcut_short_fg: Color::Rgb(100, 100, 100),