tui: Support property lists

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

View File

@ -6,9 +6,13 @@ pub mod widget;
use tuirealm::props::Attribute;
use tuirealm::{MockComponent, StateValue};
use components::{GlobalListener, Label, Shortcut, ShortcutBar};
use components::{GlobalListener, Label, Property, PropertyList, Shortcut, ShortcutBar};
use widget::Widget;
pub fn global_listener() -> Widget<GlobalListener> {
Widget::new(GlobalListener::default())
}
pub fn label(content: &str) -> Widget<Label> {
// TODO: Remove when size constraints are implemented
let width = content.chars().count() as u16;
@ -41,6 +45,27 @@ pub fn shortcut_bar(theme: &theme::Theme, shortcuts: Vec<Widget<Shortcut>>) -> W
Widget::new(shortcut_bar).height(1)
}
pub fn global_listener() -> Widget<GlobalListener> {
Widget::new(GlobalListener::default())
pub fn property(theme: &theme::Theme, name: &str, value: &str) -> Widget<Property> {
let name = label(name).foreground(theme.colors.property_name_fg);
let divider = label(&format!(" {} ", theme.icons.property_divider));
let value = label(value).foreground(theme.colors.default_fg);
// TODO: Remove when size constraints are implemented
let name_w = name.query(Attribute::Width).unwrap().unwrap_size();
let divider_w = divider.query(Attribute::Width).unwrap().unwrap_size();
let value_w = value.query(Attribute::Width).unwrap().unwrap_size();
let width = name_w.saturating_add(divider_w).saturating_add(value_w);
let property = Property::new(name, divider, value);
Widget::new(property).height(1).width(width)
}
pub fn property_list(
_theme: &theme::Theme,
properties: Vec<Widget<Property>>,
) -> Widget<PropertyList> {
let property_list = PropertyList::new(properties);
Widget::new(property_list)
}

View File

@ -1,3 +1,5 @@
use tui_realm_stdlib::Phantom;
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::{AttrValue, Attribute, Color, Props};
use tuirealm::tui::layout::Rect;
@ -24,6 +26,14 @@ impl WidgetComponent for GlobalListener {
}
}
/// Some user events need to be handled globally (e.g. user presses key `q` to quit
/// the application). This component can be used in conjunction with SubEventClause
/// to handle those events.
#[derive(Default, MockComponent)]
pub struct GlobalPhantom {
component: Phantom,
}
/// A label that can be styled using a foreground color and text modifiers.
/// Its height is fixed, its width depends on the length of the text it displays.
#[derive(Clone)]
@ -167,3 +177,90 @@ impl WidgetComponent for ShortcutBar {
CmdResult::None
}
}
/// A component that displays a labeled property.
#[derive(Clone)]
pub struct Property {
label: Widget<Label>,
divider: Widget<Label>,
property: Widget<Label>,
}
impl Property {
pub fn new(label: Widget<Label>, divider: Widget<Label>, property: Widget<Label>) -> Self {
Self {
label,
divider,
property,
}
}
}
impl WidgetComponent for Property {
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.label.clone().to_boxed(),
self.divider.clone().to_boxed(),
self.property.clone().to_boxed(),
];
let layout = layout::h_stack(labels, area);
for (mut label, area) in layout {
label.view(frame, area);
}
}
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
/// A component that can display lists of labeled properties
pub struct PropertyList {
properties: Vec<Widget<Property>>,
}
impl PropertyList {
pub fn new(properties: Vec<Widget<Property>>) -> Self {
Self { properties }
}
}
impl WidgetComponent for PropertyList {
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 properties = self
.properties
.iter()
.map(|property| property.clone().to_boxed() as Box<dyn MockComponent>)
.collect();
let layout = layout::v_stack(properties, area);
for (mut property, area) in layout {
property.view(frame, area);
}
}
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}

View File

@ -1,7 +1,5 @@
use tuirealm::props::{AttrValue, Attribute};
use tuirealm::tui::layout::Rect;
use tuirealm::tui::layout::{Constraint, Direction, Layout};
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::MockComponent;
pub fn v_stack(

View File

@ -2,6 +2,9 @@ use tuirealm::props::Color;
#[derive(Debug)]
pub struct Colors {
pub default_fg: Color,
pub property_name_fg: Color,
pub property_divider_fg: Color,
pub shortcut_short_fg: Color,
pub shortcut_long_fg: Color,
pub shortcutbar_divider_fg: Color,
@ -9,16 +12,18 @@ pub struct Colors {
#[derive(Debug)]
pub struct Icons {
pub whitespace: char,
pub property_divider: char,
pub shortcutbar_divider: char,
pub whitespace: char,
}
/// The Radicle TUI theme. Can be defined in a JSON config file. e.g.:
///
/// The Radicle TUI theme. Will be defined in a JSON config file in the
/// future. e.g.:
/// {
/// "name": "Radicle Dark",
/// "colors": {
/// "foreground": "#ffffff",
/// "propertyForeground": "#ffffff",
/// "highlightedBackground": "#000000",
/// },
/// "icons": {
@ -37,13 +42,17 @@ pub fn default_dark() -> Theme {
Theme {
name: String::from("Radicle Dark"),
colors: Colors {
default_fg: Color::Rgb(200, 200, 200),
property_name_fg: Color::Rgb(85, 85, 255),
property_divider_fg: Color::Rgb(10, 206, 209),
shortcut_short_fg: Color::Rgb(100, 100, 100),
shortcut_long_fg: Color::Rgb(70, 70, 70),
shortcutbar_divider_fg: Color::Rgb(70, 70, 70),
},
icons: Icons {
whitespace: ' ',
property_divider: '',
shortcutbar_divider: '',
whitespace: ' ',
},
}
}