tui: Update navigation design
This commit is contained in:
parent
4a99e18562
commit
362f26a0c8
|
|
@ -81,8 +81,7 @@ impl ViewPage for Home {
|
|||
|
||||
fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
|
||||
let area = frame.size();
|
||||
let navigation_h = 1u16;
|
||||
let layout = layout::default_page(area, navigation_h);
|
||||
let layout = layout::default_page(area);
|
||||
|
||||
app.view(&Cid::Home(HomeCid::Navigation), frame, layout[0]);
|
||||
app.view(&self.active_component, frame, layout[1]);
|
||||
|
|
@ -145,9 +144,7 @@ impl ViewPage for PatchView {
|
|||
|
||||
fn view(&mut self, app: &mut Application<Cid, Message, NoUserEvent>, frame: &mut Frame) {
|
||||
let area = frame.size();
|
||||
let navigation_h = 2u16;
|
||||
|
||||
let layout = layout::default_page(area, navigation_h);
|
||||
let layout = layout::default_page(area);
|
||||
|
||||
app.view(&Cid::Patch(PatchCid::Navigation), frame, layout[0]);
|
||||
app.view(&self.active_component, frame, layout[1]);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
use tuirealm::command::{Cmd, CmdResult};
|
||||
use tuirealm::props::{
|
||||
AttrValue, Attribute, BorderSides, BorderType, Color, PropPayload, PropValue, Props, Style,
|
||||
TextModifiers,
|
||||
};
|
||||
use tuirealm::tui::layout::{Constraint, Direction, Layout, Rect};
|
||||
use tuirealm::tui::text::{Span, Spans};
|
||||
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};
|
||||
|
||||
|
|
@ -35,16 +36,16 @@ impl WidgetComponent for GlobalListener {
|
|||
#[derive(Clone)]
|
||||
pub struct Tabs {
|
||||
tabs: Vec<Widget<Label>>,
|
||||
divider: Widget<Label>,
|
||||
line: Widget<Label>,
|
||||
state: TabState,
|
||||
}
|
||||
|
||||
impl Tabs {
|
||||
pub fn new(tabs: Vec<Widget<Label>>, divider: Widget<Label>) -> Self {
|
||||
pub fn new(tabs: Vec<Widget<Label>>, line: Widget<Label>) -> Self {
|
||||
let count = &tabs.len();
|
||||
Self {
|
||||
tabs,
|
||||
divider,
|
||||
line,
|
||||
state: TabState {
|
||||
selected: 0,
|
||||
len: *count as u16,
|
||||
|
|
@ -59,27 +60,46 @@ impl WidgetComponent for Tabs {
|
|||
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
|
||||
let layout = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(vec![
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.split(area);
|
||||
|
||||
// Render tabs on first row, highlighting the selected tab.
|
||||
let mut tabs = vec![];
|
||||
for (index, tab) in self.tabs.iter().enumerate() {
|
||||
let mut tab = tab.clone().to_boxed();
|
||||
if index == selected as usize {
|
||||
tab.attr(
|
||||
Attribute::TextProps,
|
||||
AttrValue::TextModifiers(TextModifiers::REVERSED),
|
||||
);
|
||||
}
|
||||
tabs.push(tab.clone().to_boxed() as Box<dyn MockComponent>);
|
||||
}
|
||||
tabs.push(Widget::new(Label::default()).to_boxed());
|
||||
|
||||
let tab_layout = layout::h_stack(tabs, layout[1]);
|
||||
for (mut tab, area) in tab_layout {
|
||||
tab.view(frame, area);
|
||||
}
|
||||
|
||||
// Repeat and render line on second row.
|
||||
let overlines = vec![self.line.clone(); area.width as usize];
|
||||
let overlines = overlines
|
||||
.iter()
|
||||
.map(|tab| Spans::from(vec![Span::from(tab)]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let tabs = tuirealm::tui::widgets::Tabs::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);
|
||||
.map(|l| l.clone().to_boxed() as Box<dyn MockComponent>)
|
||||
.collect();
|
||||
let line_layout = layout::h_stack(overlines, layout[2]);
|
||||
for (mut line, area) in line_layout {
|
||||
line.view(frame, area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ pub fn h_stack(
|
|||
widgets.into_iter().zip(layout.into_iter()).collect()
|
||||
}
|
||||
|
||||
pub fn default_page(area: Rect, nav_h: u16) -> Vec<Rect> {
|
||||
pub fn default_page(area: Rect) -> Vec<Rect> {
|
||||
let nav_h = 3u16;
|
||||
let margin_h = 1u16;
|
||||
let content_h = area.height.saturating_sub(nav_h.saturating_add(margin_h));
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub struct Icons {
|
|||
pub property_divider: char,
|
||||
pub shortcutbar_divider: char,
|
||||
pub tab_divider: char,
|
||||
pub tab_overline: char,
|
||||
pub whitespace: char,
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ pub fn default_dark() -> Theme {
|
|||
colors: Colors {
|
||||
default_fg: Color::Rgb(200, 200, 200),
|
||||
tabs_fg: Color::Rgb(100, 100, 100),
|
||||
tabs_highlighted_fg: Color::Rgb(38, 162, 105),
|
||||
tabs_highlighted_fg: Color::Rgb(85, 85, 255),
|
||||
workspaces_info_fg: Color::Rgb(220, 140, 40),
|
||||
labeled_container_bg: Color::Rgb(20, 20, 20),
|
||||
item_list_highlighted_bg: Color::Rgb(40, 40, 40),
|
||||
|
|
@ -74,6 +75,7 @@ pub fn default_dark() -> Theme {
|
|||
property_divider: '∙',
|
||||
shortcutbar_divider: '∙',
|
||||
tab_divider: '|',
|
||||
tab_overline: '▔',
|
||||
whitespace: ' ',
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ pub fn label(content: &str) -> Widget<Label> {
|
|||
.width(width)
|
||||
}
|
||||
|
||||
pub fn reversable_label(content: &str) -> Widget<Label> {
|
||||
let content = &format!(" {content} ");
|
||||
label(content)
|
||||
}
|
||||
|
||||
pub fn container_header(theme: &Theme, label: &str) -> Widget<Header> {
|
||||
let content = AttrValue::Payload(PropPayload::Vec(vec![PropValue::TextSpan(
|
||||
TextSpan::from(&format!(" {label} ")).fg(theme.colors.default_fg),
|
||||
|
|
@ -112,13 +117,11 @@ pub fn property_list(_theme: &Theme, properties: Vec<Widget<Property>>) -> Widge
|
|||
}
|
||||
|
||||
pub fn tabs(theme: &Theme, tabs: Vec<Widget<Label>>) -> Widget<Tabs> {
|
||||
let divider = label(&theme.icons.tab_divider.to_string());
|
||||
let tabs = Tabs::new(tabs, divider);
|
||||
let line =
|
||||
label(&theme.icons.tab_overline.to_string()).foreground(theme.colors.tabs_highlighted_fg);
|
||||
let tabs = Tabs::new(tabs, line);
|
||||
|
||||
Widget::new(tabs)
|
||||
.height(1)
|
||||
.foreground(theme.colors.tabs_fg)
|
||||
.highlight(theme.colors.tabs_highlighted_fg)
|
||||
Widget::new(tabs).height(2)
|
||||
}
|
||||
|
||||
pub fn table(
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ pub fn navigation(theme: &Theme) -> Widget<Tabs> {
|
|||
common::tabs(
|
||||
theme,
|
||||
vec![
|
||||
common::label("dashboard"),
|
||||
common::label("issues"),
|
||||
common::label("patches"),
|
||||
common::reversable_label("dashboard").foreground(theme.colors.tabs_highlighted_fg),
|
||||
common::reversable_label("issues").foreground(theme.colors.tabs_highlighted_fg),
|
||||
common::reversable_label("patches").foreground(theme.colors.tabs_highlighted_fg),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ use crate::ui::theme::Theme;
|
|||
pub fn navigation(theme: &Theme) -> Widget<Tabs> {
|
||||
common::tabs(
|
||||
theme,
|
||||
vec![common::label("activity"), common::label("files")],
|
||||
vec![
|
||||
common::reversable_label("activity").foreground(theme.colors.tabs_highlighted_fg),
|
||||
common::reversable_label("files").foreground(theme.colors.tabs_highlighted_fg),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue