tui: Consolidate widgets and components

This commit is contained in:
Erik Kundt 2023-05-25 11:29:15 +02:00 committed by Alexis Sellier
parent 02f5596c05
commit a4be8f4fa9
No known key found for this signature in database
14 changed files with 274 additions and 291 deletions

View File

@ -4,12 +4,14 @@ use tuirealm::command::{Cmd, CmdResult, Direction as MoveDirection};
use tuirealm::event::{Event, Key, KeyEvent};
use tuirealm::{MockComponent, NoUserEvent, State, StateValue};
use radicle_tui::ui::components::common::container::{GlobalListener, LabeledContainer, Tabs};
use radicle_tui::ui::components::common::context::{ContextBar, Shortcuts};
use radicle_tui::ui::components::common::list::PropertyList;
use radicle_tui::ui::components::common::Browser;
use radicle_tui::ui::components::home::{Dashboard, IssueBrowser};
use radicle_tui::ui::components::patch;
use radicle_tui::ui::widget::common::container::{GlobalListener, LabeledContainer, Tabs};
use radicle_tui::ui::widget::common::context::{ContextBar, Shortcuts};
use radicle_tui::ui::widget::common::list::PropertyList;
use radicle_tui::ui::widget::common::Browser;
use radicle_tui::ui::widget::home::{Dashboard, IssueBrowser};
use radicle_tui::ui::widget::patch;
use radicle_tui::ui::widget::Widget;

View File

@ -1,5 +1,4 @@
pub mod cob;
pub mod components;
pub mod ext;
pub mod layout;
pub mod state;

View File

@ -7,8 +7,8 @@ use radicle::cob::patch::{Patch, PatchId};
use tuirealm::props::{Color, TextSpan};
use crate::ui::components::common::list::List;
use crate::ui::theme::Theme;
use crate::ui::widget::common::list::List;
pub fn format_status(_patch: &Patch) -> String {
String::from("")

View File

@ -1,3 +0,0 @@
pub mod common;
pub mod home;
pub mod patch;

View File

@ -1,55 +0,0 @@
pub mod container;
pub mod context;
pub mod label;
pub mod list;
use std::marker::PhantomData;
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::Props;
use tuirealm::tui::layout::Rect;
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, State};
use crate::ui::layout;
use crate::ui::widget::{Widget, WidgetComponent};
use context::Shortcuts;
use list::{List, Table};
pub struct Browser<T> {
list: Widget<Table>,
shortcuts: Widget<Shortcuts>,
phantom: PhantomData<T>,
}
impl<T: List> Browser<T> {
pub fn new(list: Widget<Table>, shortcuts: Widget<Shortcuts>) -> Self {
Self {
list,
shortcuts,
phantom: PhantomData,
}
}
}
impl<T: List> WidgetComponent for Browser<T> {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.list.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, cmd: Cmd) -> CmdResult {
self.list.perform(cmd)
}
}

View File

@ -1,83 +0,0 @@
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::Props;
use tuirealm::tui::layout::Rect;
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, State};
use crate::ui::layout;
use crate::ui::widget::{Widget, WidgetComponent};
use super::common::container::LabeledContainer;
use super::common::context::Shortcuts;
use super::common::label::Label;
pub struct Dashboard {
about: Widget<LabeledContainer>,
shortcuts: Widget<Shortcuts>,
}
impl Dashboard {
pub fn new(about: Widget<LabeledContainer>, shortcuts: Widget<Shortcuts>) -> Self {
Self { about, shortcuts }
}
}
impl WidgetComponent for Dashboard {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.about.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
pub struct IssueBrowser {
label: Widget<Label>,
shortcuts: Widget<Shortcuts>,
}
impl IssueBrowser {
pub fn new(label: Widget<Label>, shortcuts: Widget<Shortcuts>) -> Self {
Self { label, shortcuts }
}
}
impl WidgetComponent for IssueBrowser {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let label_w = self
.label
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.shortcuts.view(frame, layout[1])
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}

View File

@ -1,118 +0,0 @@
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::Props;
use tuirealm::tui::layout::Rect;
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, State};
use crate::ui::layout;
use crate::ui::widget::{Widget, WidgetComponent};
use super::common::context::{ContextBar, Shortcuts};
use super::common::label::Label;
pub struct Activity {
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
}
impl Activity {
pub fn new(
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
) -> Self {
Self {
label,
context,
shortcuts,
}
}
}
impl WidgetComponent for Activity {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let label_w = self
.label
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let context_h = self
.context
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component_with_context(area, context_h, shortcuts_h);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.context.view(frame, layout[1]);
self.shortcuts.view(frame, layout[2]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
pub struct Files {
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
}
impl Files {
pub fn new(
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
) -> Self {
Self {
label,
context,
shortcuts,
}
}
}
impl WidgetComponent for Files {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let label_w = self
.label
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let context_h = self
.context
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component_with_context(area, context_h, shortcuts_h);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.context.view(frame, layout[1]);
self.shortcuts.view(frame, layout[2]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}

View File

@ -1,17 +1,64 @@
pub mod container;
pub mod context;
pub mod label;
pub mod list;
use std::marker::PhantomData;
use radicle::Profile;
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::{AttrValue, Attribute, PropPayload, PropValue, TextSpan};
use tuirealm::MockComponent;
use tuirealm::tui::layout::Rect;
use tuirealm::{Frame, MockComponent, Props, State};
use crate::ui;
use crate::ui::components::common::container::Header;
use container::{GlobalListener, Header, LabeledContainer, Tabs};
use context::{Shortcut, Shortcuts};
use label::Label;
use list::{List, Property, PropertyList, Table};
use ui::components::common::container::{GlobalListener, LabeledContainer, Tabs};
use ui::components::common::context::{Shortcut, Shortcuts};
use ui::components::common::label::Label;
use ui::components::common::list::{List, Property, PropertyList, Table};
use ui::theme::Theme;
use super::{Widget, WidgetComponent};
use super::Widget;
use crate::ui::layout;
use crate::ui::theme::Theme;
pub struct Browser<T> {
list: Widget<Table>,
shortcuts: Widget<Shortcuts>,
phantom: PhantomData<T>,
}
impl<T: List> Browser<T> {
pub fn new(list: Widget<Table>, shortcuts: Widget<Shortcuts>) -> Self {
Self {
list,
shortcuts,
phantom: PhantomData,
}
}
}
impl<T: List> WidgetComponent for Browser<T> {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.list.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, cmd: Cmd) -> CmdResult {
self.list.perform(cmd)
}
}
pub fn global_listener() -> Widget<GlobalListener> {
Widget::new(GlobalListener::default())

View File

@ -7,12 +7,13 @@ use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::tui::widgets::{Block, Cell, Row};
use tuirealm::{Frame, MockComponent, State, StateValue};
use crate::ui::components::common::label::Label;
use crate::ui::ext::HeaderBlock;
use crate::ui::layout;
use crate::ui::state::TabState;
use crate::ui::widget::{Widget, WidgetComponent};
use super::label::Label;
/// 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.

View File

@ -3,7 +3,8 @@ use tuirealm::props::{AttrValue, Attribute, Props};
use tuirealm::tui::layout::Rect;
use tuirealm::{Frame, MockComponent, State};
use crate::ui::components::common::label::Label;
use super::label::Label;
use crate::ui::layout;
use crate::ui::widget::{Widget, WidgetComponent};

View File

@ -8,12 +8,12 @@ use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::tui::widgets::{Block, Cell, Row, TableState};
use tuirealm::{Frame, MockComponent, State, StateValue};
use crate::ui::components::common::label::Label;
use crate::ui::layout;
use crate::ui::theme::Theme;
use crate::ui::widget::{Widget, WidgetComponent};
use super::container::Header;
use super::label::Label;
pub trait List {
fn row(&self, theme: &Theme, profile: &Profile) -> Vec<TextSpan>;

View File

@ -1,13 +1,92 @@
use radicle::cob::patch::{Patch, PatchId};
use radicle::identity::{Id, Project};
use radicle::Profile;
use crate::ui::components::common::container::Tabs;
use crate::ui::components::common::Browser;
use crate::ui::components::home::{Dashboard, IssueBrowser};
use radicle::prelude::{Id, Project};
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::tui::layout::Rect;
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State};
use super::{Widget, WidgetComponent};
use super::common::container::{LabeledContainer, Tabs};
use super::common::context::Shortcuts;
use super::common::label::Label;
use super::common::{self, Browser};
use crate::ui::layout;
use crate::ui::theme::Theme;
use super::{common, Widget};
pub struct Dashboard {
about: Widget<LabeledContainer>,
shortcuts: Widget<Shortcuts>,
}
impl Dashboard {
pub fn new(about: Widget<LabeledContainer>, shortcuts: Widget<Shortcuts>) -> Self {
Self { about, shortcuts }
}
}
impl WidgetComponent for Dashboard {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.about.view(frame, layout[0]);
self.shortcuts.view(frame, layout[1]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
pub struct IssueBrowser {
label: Widget<Label>,
shortcuts: Widget<Shortcuts>,
}
impl IssueBrowser {
pub fn new(label: Widget<Label>, shortcuts: Widget<Shortcuts>) -> Self {
Self { label, shortcuts }
}
}
impl WidgetComponent for IssueBrowser {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let label_w = self
.label
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component(area, shortcuts_h);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.shortcuts.view(frame, layout[1])
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
common::tabs(

View File

@ -1,17 +1,130 @@
use radicle::Profile;
use tuirealm::props::Color;
use radicle::cob::patch::{Patch, PatchId};
use radicle::Profile;
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::props::Color;
use tuirealm::tui::layout::Rect;
use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State};
use super::{Widget, WidgetComponent};
use super::common;
use super::Widget;
use super::common::container::Tabs;
use super::common::context::{ContextBar, Shortcuts};
use super::common::label::Label;
use crate::ui::cob::patch;
use crate::ui::components::common::container::Tabs;
use crate::ui::components::common::context::ContextBar;
use crate::ui::components::patch::Activity;
use crate::ui::layout;
use crate::ui::theme::Theme;
pub struct Activity {
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
}
impl Activity {
pub fn new(
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
) -> Self {
Self {
label,
context,
shortcuts,
}
}
}
impl WidgetComponent for Activity {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let label_w = self
.label
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let context_h = self
.context
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component_with_context(area, context_h, shortcuts_h);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.context.view(frame, layout[1]);
self.shortcuts.view(frame, layout[2]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
pub struct Files {
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
}
impl Files {
pub fn new(
label: Widget<Label>,
context: Widget<ContextBar>,
shortcuts: Widget<Shortcuts>,
) -> Self {
Self {
label,
context,
shortcuts,
}
}
}
impl WidgetComponent for Files {
fn view(&mut self, _properties: &Props, frame: &mut Frame, area: Rect) {
let label_w = self
.label
.query(Attribute::Width)
.unwrap_or(AttrValue::Size(1))
.unwrap_size();
let context_h = self
.context
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let shortcuts_h = self
.shortcuts
.query(Attribute::Height)
.unwrap_or(AttrValue::Size(0))
.unwrap_size();
let layout = layout::root_component_with_context(area, context_h, shortcuts_h);
self.label
.view(frame, layout::centered_label(label_w, layout[0]));
self.context.view(frame, layout[1]);
self.shortcuts.view(frame, layout[2]);
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, _properties: &Props, _cmd: Cmd) -> CmdResult {
CmdResult::None
}
}
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
common::tabs(
theme,