tui: Implement workspaces container
Signed-off-by: Erik Kundt <erik@zirkular.io>
This commit is contained in:
parent
791c378d4e
commit
d331389ada
|
|
@ -2,14 +2,20 @@ use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use tui_realm_stdlib::Phantom;
|
||||||
use tuirealm::application::PollStrategy;
|
use tuirealm::application::PollStrategy;
|
||||||
|
use tuirealm::command::{Cmd, Direction as MoveDirection};
|
||||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers};
|
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers};
|
||||||
use tuirealm::props::{AttrValue, Attribute};
|
use tuirealm::props::{AttrValue, Attribute};
|
||||||
use tuirealm::tui::layout::{Constraint, Direction, Layout};
|
use tuirealm::tui::layout::{Constraint, Direction, Layout};
|
||||||
use tuirealm::{Application, Component, Frame, NoUserEvent, Sub, SubClause, SubEventClause};
|
use tuirealm::{
|
||||||
|
Application, Component, Frame, MockComponent, NoUserEvent, Sub, SubClause, SubEventClause,
|
||||||
|
};
|
||||||
|
|
||||||
use radicle_tui::ui;
|
use radicle_tui::ui;
|
||||||
use radicle_tui::ui::components::{GlobalListener, LabeledContainer, PropertyList, ShortcutBar};
|
use radicle_tui::ui::components::{
|
||||||
|
GlobalListener, LabeledContainer, PropertyList, ShortcutBar, Workspaces,
|
||||||
|
};
|
||||||
use radicle_tui::ui::theme;
|
use radicle_tui::ui::theme;
|
||||||
use radicle_tui::ui::widget::Widget;
|
use radicle_tui::ui::widget::Widget;
|
||||||
|
|
||||||
|
|
@ -34,7 +40,7 @@ pub enum Message {
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
pub enum ComponentId {
|
pub enum ComponentId {
|
||||||
Workspaces,
|
Workspaces,
|
||||||
ShortcutBar,
|
Shortcuts,
|
||||||
GlobalListener,
|
GlobalListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,27 +60,46 @@ impl Tui<ComponentId, Message> for App {
|
||||||
fn init(&mut self, app: &mut Application<ComponentId, Message, NoUserEvent>) -> Result<()> {
|
fn init(&mut self, app: &mut Application<ComponentId, Message, NoUserEvent>) -> Result<()> {
|
||||||
let theme = theme::default_dark();
|
let theme = theme::default_dark();
|
||||||
|
|
||||||
|
let dashboard = 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();
|
||||||
|
|
||||||
app.mount(
|
app.mount(
|
||||||
ComponentId::Workspaces,
|
ComponentId::Workspaces,
|
||||||
ui::labeled_container(
|
ui::workspaces(
|
||||||
&theme,
|
&theme,
|
||||||
"about",
|
self.project.name(),
|
||||||
ui::property_list(
|
ui::tabs(
|
||||||
&theme,
|
&theme,
|
||||||
vec![
|
vec![
|
||||||
ui::property(&theme, "id", &self.id.to_string()),
|
ui::label("dashboard"),
|
||||||
ui::property(&theme, "name", self.project.name()),
|
ui::label("issues"),
|
||||||
ui::property(&theme, "description", self.project.description()),
|
ui::label("patches"),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
.to_boxed(),
|
vec![
|
||||||
|
dashboard,
|
||||||
|
Box::<Phantom>::default(),
|
||||||
|
Box::<Phantom>::default(),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
.to_boxed(),
|
.to_boxed(),
|
||||||
vec![],
|
vec![],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
app.mount(
|
app.mount(
|
||||||
ComponentId::ShortcutBar,
|
ComponentId::Shortcuts,
|
||||||
ui::shortcut_bar(
|
ui::shortcut_bar(
|
||||||
&theme,
|
&theme,
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -100,7 +125,7 @@ impl Tui<ComponentId, Message> for App {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// We need to give focus to a component then
|
// We need to give focus to a component then
|
||||||
app.active(&ComponentId::ShortcutBar)?;
|
app.active(&ComponentId::Workspaces)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +138,7 @@ impl Tui<ComponentId, Message> for App {
|
||||||
let area = frame.size();
|
let area = frame.size();
|
||||||
let margin_h = 1u16;
|
let margin_h = 1u16;
|
||||||
let shortcuts_h = app
|
let shortcuts_h = app
|
||||||
.query(&ComponentId::ShortcutBar, Attribute::Height)
|
.query(&ComponentId::Shortcuts, Attribute::Height)
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.unwrap_or(AttrValue::Size(0))
|
.unwrap_or(AttrValue::Size(0))
|
||||||
|
|
@ -135,7 +160,7 @@ impl Tui<ComponentId, Message> for App {
|
||||||
.split(area);
|
.split(area);
|
||||||
|
|
||||||
app.view(&ComponentId::Workspaces, frame, layout[0]);
|
app.view(&ComponentId::Workspaces, frame, layout[0]);
|
||||||
app.view(&ComponentId::ShortcutBar, frame, layout[1]);
|
app.view(&ComponentId::Shortcuts, frame, layout[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, app: &mut Application<ComponentId, Message, NoUserEvent>, interval: u64) {
|
fn update(&mut self, app: &mut Application<ComponentId, Message, NoUserEvent>, interval: u64) {
|
||||||
|
|
@ -168,6 +193,18 @@ impl Component<Message, NoUserEvent> for Widget<GlobalListener> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Component<Message, NoUserEvent> for Widget<Workspaces> {
|
||||||
|
fn on(&mut self, event: Event<NoUserEvent>) -> Option<Message> {
|
||||||
|
match event {
|
||||||
|
Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => {
|
||||||
|
self.perform(Cmd::Move(MoveDirection::Right));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Component<Message, NoUserEvent> for Widget<LabeledContainer> {
|
impl Component<Message, NoUserEvent> for Widget<LabeledContainer> {
|
||||||
fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
|
fn on(&mut self, _event: Event<NoUserEvent>) -> Option<Message> {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod components;
|
pub mod components;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
mod state;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
|
|
||||||
|
|
@ -8,7 +9,7 @@ use tuirealm::{MockComponent, StateValue};
|
||||||
|
|
||||||
use components::{
|
use components::{
|
||||||
ContainerHeader, GlobalListener, Label, LabeledContainer, Property, PropertyList, Shortcut,
|
ContainerHeader, GlobalListener, Label, LabeledContainer, Property, PropertyList, Shortcut,
|
||||||
ShortcutBar,
|
ShortcutBar, Tabs, Workspaces, WorkspacesHeader,
|
||||||
};
|
};
|
||||||
use widget::Widget;
|
use widget::Widget;
|
||||||
|
|
||||||
|
|
@ -87,3 +88,26 @@ pub fn property_list(
|
||||||
|
|
||||||
Widget::new(property_list)
|
Widget::new(property_list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tabs(theme: &theme::Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
|
||||||
|
let divider = label(&theme.icons.tab_divider.to_string());
|
||||||
|
let tabs = Tabs::new(tabs, divider);
|
||||||
|
|
||||||
|
Widget::new(tabs)
|
||||||
|
.height(1)
|
||||||
|
.foreground(theme.colors.tabs_fg)
|
||||||
|
.highlight(theme.colors.tabs_highlighted_fg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn workspaces(
|
||||||
|
theme: &theme::Theme,
|
||||||
|
info: &str,
|
||||||
|
tabs: Widget<Tabs>,
|
||||||
|
children: Vec<Box<dyn MockComponent>>,
|
||||||
|
) -> Widget<Workspaces> {
|
||||||
|
let info = label(info).foreground(theme.colors.workspaces_info_fg);
|
||||||
|
let header = Widget::new(WorkspacesHeader::new(tabs, info));
|
||||||
|
let workspaces = Workspaces::new(header, children);
|
||||||
|
|
||||||
|
Widget::new(workspaces)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ use tui_realm_stdlib::Phantom;
|
||||||
use tuirealm::command::{Cmd, CmdResult};
|
use tuirealm::command::{Cmd, CmdResult};
|
||||||
use tuirealm::props::{AttrValue, Attribute, Color, Props, Style};
|
use tuirealm::props::{AttrValue, Attribute, Color, Props, Style};
|
||||||
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
|
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
|
||||||
use tuirealm::tui::widgets::Block;
|
use tuirealm::tui::text::{Span, Spans};
|
||||||
|
use tuirealm::tui::widgets::{Block, Tabs as TuiTabs};
|
||||||
use tuirealm::{Frame, MockComponent, State, StateValue};
|
use tuirealm::{Frame, MockComponent, State, StateValue};
|
||||||
|
|
||||||
use super::layout;
|
use super::layout;
|
||||||
|
use super::state::TabState;
|
||||||
use super::widget::{Widget, WidgetComponent};
|
use super::widget::{Widget, WidgetComponent};
|
||||||
|
|
||||||
/// Some user events need to be handled globally (e.g. user presses key `q` to quit
|
/// Some user events need to be handled globally (e.g. user presses key `q` to quit
|
||||||
|
|
@ -88,6 +90,12 @@ impl WidgetComponent for Label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Widget<Label>> for Span<'_> {
|
||||||
|
fn from(label: &Widget<Label>) -> Self {
|
||||||
|
Span::styled(label.content.clone().unwrap_string(), Style::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A labeled container header.
|
/// A labeled container header.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ContainerHeader {
|
pub struct ContainerHeader {
|
||||||
|
|
@ -368,3 +376,193 @@ impl WidgetComponent for PropertyList {
|
||||||
CmdResult::None
|
CmdResult::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A tab header that displays all labels horizontally aligned and separated
|
||||||
|
/// by a divider. Highlights the label defined by the current tab index.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Tabs {
|
||||||
|
tabs: Vec<Widget<Label>>,
|
||||||
|
divider: Widget<Label>,
|
||||||
|
state: TabState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tabs {
|
||||||
|
pub fn new(tabs: Vec<Widget<Label>>, divider: Widget<Label>) -> Self {
|
||||||
|
Self {
|
||||||
|
tabs,
|
||||||
|
divider,
|
||||||
|
state: TabState {
|
||||||
|
selected: 0,
|
||||||
|
len: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetComponent for Tabs {
|
||||||
|
fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
|
||||||
|
let selected = self.state().unwrap_one().unwrap_u16();
|
||||||
|
let display = properties
|
||||||
|
.get_or(Attribute::Display, AttrValue::Flag(true))
|
||||||
|
.unwrap_flag();
|
||||||
|
let foreground = properties
|
||||||
|
.get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
|
||||||
|
.unwrap_color();
|
||||||
|
let highlight = properties
|
||||||
|
.get_or(Attribute::HighlightedColor, AttrValue::Color(Color::Reset))
|
||||||
|
.unwrap_color();
|
||||||
|
|
||||||
|
if display {
|
||||||
|
let spans = self
|
||||||
|
.tabs
|
||||||
|
.iter()
|
||||||
|
.map(|tab| Spans::from(vec![Span::from(tab)]))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let tabs = TuiTabs::new(spans)
|
||||||
|
.style(Style::default().fg(foreground))
|
||||||
|
.highlight_style(Style::default().fg(highlight))
|
||||||
|
.divider(Span::from(&self.divider))
|
||||||
|
.select(selected as usize);
|
||||||
|
|
||||||
|
frame.render_widget(tabs, area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> State {
|
||||||
|
State::One(StateValue::U16(self.state.selected))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||||
|
use tuirealm::command::Direction;
|
||||||
|
|
||||||
|
match cmd {
|
||||||
|
Cmd::Move(Direction::Right) => {
|
||||||
|
let prev = self.state.selected;
|
||||||
|
self.state.incr_tab_index(true);
|
||||||
|
if prev != self.state.selected {
|
||||||
|
CmdResult::Changed(self.state())
|
||||||
|
} else {
|
||||||
|
CmdResult::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => CmdResult::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Workspace header that displays all labels horizontally aligned and separated
|
||||||
|
/// by a divider. Highlights the label defined by the current tab index.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WorkspacesHeader {
|
||||||
|
tabs: Widget<Tabs>,
|
||||||
|
info: Widget<Label>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkspacesHeader {
|
||||||
|
pub fn new(tabs: Widget<Tabs>, info: Widget<Label>) -> Self {
|
||||||
|
Self { tabs, info }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetComponent for WorkspacesHeader {
|
||||||
|
fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
|
||||||
|
let display = properties
|
||||||
|
.get_or(Attribute::Display, AttrValue::Flag(true))
|
||||||
|
.unwrap_flag();
|
||||||
|
let info_width = self
|
||||||
|
.info
|
||||||
|
.query(Attribute::Width)
|
||||||
|
.unwrap_or(AttrValue::Size(1))
|
||||||
|
.unwrap_size();
|
||||||
|
let tabs_width = area.width.saturating_sub(info_width);
|
||||||
|
|
||||||
|
if display {
|
||||||
|
let layout = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Length(tabs_width),
|
||||||
|
Constraint::Length(info_width),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
self.tabs.view(frame, layout[0]);
|
||||||
|
self.info.view(frame, layout[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> State {
|
||||||
|
State::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||||
|
self.tabs.perform(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A container with a tab header. Displays the component selected by the index
|
||||||
|
/// held in the header state.
|
||||||
|
pub struct Workspaces {
|
||||||
|
header: Widget<WorkspacesHeader>,
|
||||||
|
children: Vec<Box<dyn MockComponent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Workspaces {
|
||||||
|
pub fn new(header: Widget<WorkspacesHeader>, children: Vec<Box<dyn MockComponent>>) -> Self {
|
||||||
|
Self { header, children }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetComponent for Workspaces {
|
||||||
|
fn view(&mut self, properties: &Props, frame: &mut Frame, area: Rect) {
|
||||||
|
let display = properties
|
||||||
|
.get_or(Attribute::Display, AttrValue::Flag(true))
|
||||||
|
.unwrap_flag();
|
||||||
|
let header_height = self
|
||||||
|
.header
|
||||||
|
.query(Attribute::Height)
|
||||||
|
.unwrap_or(AttrValue::Size(1))
|
||||||
|
.unwrap_size();
|
||||||
|
let selected = self.header.tabs.state().unwrap_one().unwrap_u16();
|
||||||
|
|
||||||
|
if display {
|
||||||
|
let layout = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Length(header_height),
|
||||||
|
Constraint::Length(1),
|
||||||
|
Constraint::Length(0),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
self.header.view(frame, layout[0]);
|
||||||
|
|
||||||
|
if let Some(child) = self.children.get_mut(selected as usize) {
|
||||||
|
child.view(frame, layout[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> State {
|
||||||
|
State::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||||
|
CmdResult::Batch(
|
||||||
|
[
|
||||||
|
self.children
|
||||||
|
.iter_mut()
|
||||||
|
.map(|child| child.perform(cmd))
|
||||||
|
.collect(),
|
||||||
|
vec![self.header.perform(cmd)],
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
/// State that holds the index of a selected tab item and the count of all tab items.
|
||||||
|
/// The index can be increased and will start at 0, if length was reached.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct TabState {
|
||||||
|
pub selected: u16,
|
||||||
|
pub len: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabState {
|
||||||
|
pub fn incr_tab_index(&mut self, rewind: bool) {
|
||||||
|
if self.selected + 1 < self.len {
|
||||||
|
self.selected += 1;
|
||||||
|
} else if rewind {
|
||||||
|
self.selected = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,9 @@ use tuirealm::props::Color;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Colors {
|
pub struct Colors {
|
||||||
pub default_fg: Color,
|
pub default_fg: Color,
|
||||||
|
pub tabs_fg: Color,
|
||||||
|
pub tabs_highlighted_fg: Color,
|
||||||
|
pub workspaces_info_fg: Color,
|
||||||
pub labeled_container_bg: Color,
|
pub labeled_container_bg: Color,
|
||||||
pub property_name_fg: Color,
|
pub property_name_fg: Color,
|
||||||
pub property_divider_fg: Color,
|
pub property_divider_fg: Color,
|
||||||
|
|
@ -15,6 +18,7 @@ pub struct Colors {
|
||||||
pub struct Icons {
|
pub struct Icons {
|
||||||
pub property_divider: char,
|
pub property_divider: char,
|
||||||
pub shortcutbar_divider: char,
|
pub shortcutbar_divider: char,
|
||||||
|
pub tab_divider: char,
|
||||||
pub whitespace: char,
|
pub whitespace: char,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,6 +48,9 @@ pub fn default_dark() -> Theme {
|
||||||
name: String::from("Radicle Dark"),
|
name: String::from("Radicle Dark"),
|
||||||
colors: Colors {
|
colors: Colors {
|
||||||
default_fg: Color::Rgb(200, 200, 200),
|
default_fg: Color::Rgb(200, 200, 200),
|
||||||
|
tabs_fg: Color::Rgb(100, 100, 100),
|
||||||
|
tabs_highlighted_fg: Color::Rgb(38, 162, 105),
|
||||||
|
workspaces_info_fg: Color::Rgb(220, 140, 40),
|
||||||
labeled_container_bg: Color::Rgb(20, 20, 20),
|
labeled_container_bg: Color::Rgb(20, 20, 20),
|
||||||
property_name_fg: Color::Rgb(85, 85, 255),
|
property_name_fg: Color::Rgb(85, 85, 255),
|
||||||
property_divider_fg: Color::Rgb(10, 206, 209),
|
property_divider_fg: Color::Rgb(10, 206, 209),
|
||||||
|
|
@ -54,6 +61,7 @@ pub fn default_dark() -> Theme {
|
||||||
icons: Icons {
|
icons: Icons {
|
||||||
property_divider: '∙',
|
property_divider: '∙',
|
||||||
shortcutbar_divider: '∙',
|
shortcutbar_divider: '∙',
|
||||||
|
tab_divider: '|',
|
||||||
whitespace: ' ',
|
whitespace: ' ',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use tuirealm::command::{Cmd, CmdResult};
|
use tuirealm::command::{Cmd, CmdResult};
|
||||||
use tuirealm::props::{AttrValue, Attribute, Color, Props};
|
use tuirealm::props::{AttrValue, Attribute, Color, Props};
|
||||||
use tuirealm::tui::layout::Rect;
|
use tuirealm::tui::layout::Rect;
|
||||||
|
|
@ -19,6 +21,14 @@ pub struct Widget<T: WidgetComponent> {
|
||||||
properties: Props,
|
properties: Props,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: WidgetComponent> Deref for Widget<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.component
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: WidgetComponent> Widget<T> {
|
impl<T: WidgetComponent> Widget<T> {
|
||||||
pub fn new(component: T) -> Self {
|
pub fn new(component: T) -> Self {
|
||||||
Widget {
|
Widget {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue