wip #3
@ -1,2 +0,0 @@
|
|||||||
[profile.dev.package."*"]
|
|
||||||
opt-level = 2
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
.cargo
|
15
Cargo.toml
15
Cargo.toml
@ -7,5 +7,18 @@ edition = "2021"
|
|||||||
version = "0.15"
|
version = "0.15"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
opt-level = 2
|
||||||
|
|
||||||
[dependencies.bevy_terminal_display]
|
[dependencies.bevy_terminal_display]
|
||||||
git = "https://git.soaos.dev/soaos/bevy_terminal_display"
|
version = "0.7"
|
||||||
|
|
||||||
|
[dependencies.ratatui]
|
||||||
|
version = "0.29"
|
||||||
|
features = ["unstable-widget-ref"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tui-input = "0.11.1"
|
496
src/lib.rs
496
src/lib.rs
@ -1,24 +1,96 @@
|
|||||||
use std::sync::{
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_terminal_display::{
|
use bevy_terminal_display::{
|
||||||
crossterm::event::{Event, KeyCode, KeyEventKind},
|
crossterm::event::{Event, KeyCode, KeyEventKind},
|
||||||
input::events::TerminalInputEvent,
|
input::events::TerminalInputEvent,
|
||||||
ratatui::{
|
ratatui::{
|
||||||
layout::Rect, style::{Color, Modifier, Style}, widgets::{Block, Borders, Clear, HighlightSpacing, List, ListItem, ListState}
|
layout::Rect,
|
||||||
|
style::{Color, Modifier, Style, Stylize},
|
||||||
|
widgets::{Block, Clear, ListState, Paragraph, Widget},
|
||||||
},
|
},
|
||||||
widgets::TerminalWidget,
|
widgets::TerminalWidget,
|
||||||
};
|
};
|
||||||
|
use ratatui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::{Constraint, Direction, Layout},
|
||||||
|
widgets::{Gauge, WidgetRef},
|
||||||
|
};
|
||||||
|
use tui_input::backend::crossterm::EventHandler;
|
||||||
|
pub use tui_input::Input;
|
||||||
|
|
||||||
pub struct MenuWidget<E: bevy::prelude::Event> {
|
pub enum MenuControlData<E: bevy::prelude::Event> {
|
||||||
pub items: Vec<MenuOption<E>>,
|
Button {
|
||||||
pub state: ListState,
|
label: String,
|
||||||
|
event: E,
|
||||||
|
},
|
||||||
|
FSlider {
|
||||||
|
label: String,
|
||||||
|
min: f32,
|
||||||
|
max: f32,
|
||||||
|
step: f32,
|
||||||
|
value: f32,
|
||||||
|
unit: Option<String>,
|
||||||
|
event: fn(f32) -> E,
|
||||||
|
},
|
||||||
|
ISlider {
|
||||||
|
label: String,
|
||||||
|
min: i32,
|
||||||
|
max: i32,
|
||||||
|
step: i32,
|
||||||
|
value: i32,
|
||||||
|
unit: Option<String>,
|
||||||
|
event: fn(i32) -> E,
|
||||||
|
},
|
||||||
|
TextField {
|
||||||
|
label: String,
|
||||||
|
value: Input,
|
||||||
|
event: fn(String) -> E,
|
||||||
|
validate: Option<fn(&Input) -> bool>,
|
||||||
|
},
|
||||||
|
CheckBox {
|
||||||
|
label: String,
|
||||||
|
value: bool,
|
||||||
|
event: fn(bool) -> E,
|
||||||
|
},
|
||||||
|
Header(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MenuControlStyle {
|
||||||
pub enabled_color: Color,
|
pub enabled_color: Color,
|
||||||
pub disabled_color: Color,
|
pub disabled_color: Color,
|
||||||
pub selected_style: Style,
|
pub selected_style: Style,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MenuControlStyle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled_color: Color::White,
|
||||||
|
disabled_color: Color::DarkGray,
|
||||||
|
selected_style: Style::new().fg(Color::Yellow).add_modifier(Modifier::BOLD),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MenuControl<E: bevy::prelude::Event> {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub selected: bool,
|
||||||
|
pub data: MenuControlData<E>,
|
||||||
|
pub style: MenuControlStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: bevy::prelude::Event> Default for MenuControl<E> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enabled: true,
|
||||||
|
selected: false,
|
||||||
|
data: MenuControlData::Header("".into()),
|
||||||
|
style: MenuControlStyle::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MenuWidget<E: bevy::prelude::Event> {
|
||||||
|
pub items: Vec<MenuControl<E>>,
|
||||||
|
pub state: ListState,
|
||||||
pub area_function: Option<Box<dyn Fn(Rect) -> Rect + Send + Sync>>,
|
pub area_function: Option<Box<dyn Fn(Rect) -> Rect + Send + Sync>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,56 +99,34 @@ impl<E: bevy::prelude::Event> Default for MenuWidget<E> {
|
|||||||
Self {
|
Self {
|
||||||
items: vec![],
|
items: vec![],
|
||||||
state: ListState::default().with_selected(Some(0)),
|
state: ListState::default().with_selected(Some(0)),
|
||||||
enabled_color: Color::White,
|
|
||||||
disabled_color: Color::Gray,
|
|
||||||
selected_style: Style::new()
|
|
||||||
.add_modifier(Modifier::UNDERLINED)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
area_function: None,
|
area_function: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MenuOption<E: bevy::prelude::Event> {
|
|
||||||
pub enabled: Arc<AtomicBool>,
|
|
||||||
pub label: String,
|
|
||||||
pub event: E,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: bevy::prelude::Event + Clone> TerminalWidget for MenuWidget<E> {
|
impl<E: bevy::prelude::Event + Clone> TerminalWidget for MenuWidget<E> {
|
||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
frame: &mut bevy_terminal_display::ratatui::Frame,
|
frame: &mut bevy_terminal_display::ratatui::Frame,
|
||||||
rect: bevy_terminal_display::ratatui::prelude::Rect,
|
rect: bevy_terminal_display::ratatui::prelude::Rect,
|
||||||
) {
|
) {
|
||||||
let block = Block::new().borders(Borders::all());
|
|
||||||
let items: Vec<ListItem> = self
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.map(|item| {
|
|
||||||
ListItem::from(item).style(Style::new().fg(
|
|
||||||
if item.enabled.load(Ordering::Relaxed) {
|
|
||||||
self.enabled_color
|
|
||||||
} else {
|
|
||||||
self.disabled_color
|
|
||||||
},
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let list = List::new(items)
|
|
||||||
.block(block)
|
|
||||||
.highlight_style(self.selected_style)
|
|
||||||
.highlight_symbol(">")
|
|
||||||
.highlight_spacing(HighlightSpacing::Always);
|
|
||||||
|
|
||||||
let area = if let Some(area) = &self.area_function {
|
let area = if let Some(area) = &self.area_function {
|
||||||
area(rect)
|
area(rect)
|
||||||
} else {
|
} else {
|
||||||
rect
|
rect
|
||||||
};
|
};
|
||||||
frame.render_widget(Clear, area);
|
frame.render_widget(Clear, area);
|
||||||
frame.render_stateful_widget(list, area, &mut self.state);
|
let outer_block = Block::bordered();
|
||||||
|
let inner_area = outer_block.inner(area);
|
||||||
|
frame.render_widget(outer_block, area);
|
||||||
|
let mut next_area = inner_area;
|
||||||
|
for (i, item) in self.items.iter_mut().enumerate() {
|
||||||
|
item.selected = self.state.selected() == Some(i);
|
||||||
|
let height = item.height();
|
||||||
|
next_area.height = height;
|
||||||
|
frame.render_widget_ref(item, next_area);
|
||||||
|
next_area.y += height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(
|
fn handle_events(
|
||||||
@ -89,41 +139,357 @@ impl<E: bevy::prelude::Event + Clone> TerminalWidget for MenuWidget<E> {
|
|||||||
match key_event.code {
|
match key_event.code {
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
if let Some(selected) = self.state.selected() {
|
if let Some(selected) = self.state.selected() {
|
||||||
if let Some(item) = self.items.get(selected) {
|
if let Some(item) = self.items.get_mut(selected) {
|
||||||
if item.enabled.load(Ordering::Relaxed) {
|
if item.enabled {
|
||||||
commands.send_event(item.event.clone());
|
match &mut item.data {
|
||||||
|
MenuControlData::Button { event, .. } => {
|
||||||
|
commands.send_event(event.clone());
|
||||||
|
}
|
||||||
|
MenuControlData::CheckBox {
|
||||||
|
ref mut value,
|
||||||
|
event,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
*value = !*value;
|
||||||
|
commands.send_event(event(*value));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
KeyCode::Up => self.state.select_previous(),
|
KeyCode::Down => {
|
||||||
KeyCode::Down => self.state.select_next(),
|
self.state.select_next();
|
||||||
_ => (),
|
if self.state.selected().unwrap() > self.items.len() - 1 {
|
||||||
|
self.state.select_previous();
|
||||||
|
}
|
||||||
|
'outer: {
|
||||||
|
let selected = self.state.selected().unwrap().min(self.items.len() - 1);
|
||||||
|
if let MenuControlData::Header(_) =
|
||||||
|
self.items.get(selected).unwrap().data
|
||||||
|
{
|
||||||
|
for (i, item) in self.items[selected + 1..self.items.len()]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let MenuControlData::Header(_) = item.data else {
|
||||||
|
self.state.select(Some(selected + 1 + i));
|
||||||
|
break 'outer;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self.state.select_previous();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Up => {
|
||||||
|
self.state.select_previous();
|
||||||
|
'outer: {
|
||||||
|
let selected = self.state.selected().unwrap();
|
||||||
|
if let MenuControlData::Header(_) =
|
||||||
|
self.items.get(selected).unwrap().data
|
||||||
|
{
|
||||||
|
for (i, item) in self.items[0..selected].iter().enumerate().rev() {
|
||||||
|
let MenuControlData::Header(_) = item.data else {
|
||||||
|
self.state.select(Some(i));
|
||||||
|
break 'outer;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self.state.select_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Left => {
|
||||||
|
if let Some(selected) = self.state.selected() {
|
||||||
|
if let Some(item) = self.items.get_mut(selected) {
|
||||||
|
if item.enabled {
|
||||||
|
match &mut item.data {
|
||||||
|
MenuControlData::FSlider {
|
||||||
|
min,
|
||||||
|
step,
|
||||||
|
value,
|
||||||
|
event,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
*value = (*value - *step).max(*min);
|
||||||
|
commands.send_event(event(*value));
|
||||||
|
}
|
||||||
|
MenuControlData::ISlider {
|
||||||
|
max,
|
||||||
|
step,
|
||||||
|
value,
|
||||||
|
event,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
*value = (*value + *step).min(*max);
|
||||||
|
commands.send_event(event(*value));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Right => {
|
||||||
|
if let Some(selected) = self.state.selected() {
|
||||||
|
if let Some(item) = self.items.get_mut(selected) {
|
||||||
|
if item.enabled {
|
||||||
|
match &mut item.data {
|
||||||
|
MenuControlData::FSlider {
|
||||||
|
max,
|
||||||
|
step,
|
||||||
|
value,
|
||||||
|
event,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
*value = (*value + *step).min(*max);
|
||||||
|
commands.send_event(event(*value));
|
||||||
|
}
|
||||||
|
MenuControlData::ISlider {
|
||||||
|
max,
|
||||||
|
step,
|
||||||
|
value,
|
||||||
|
event,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
*value = (*value + *step).min(*max);
|
||||||
|
commands.send_event(event(*value));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(selected) = self.state.selected() {
|
||||||
|
if let Some(item) = self.items.get_mut(selected) {
|
||||||
|
if item.enabled {
|
||||||
|
if let MenuControlData::TextField {
|
||||||
|
value,
|
||||||
|
event: e,
|
||||||
|
validate,
|
||||||
|
..
|
||||||
|
} = &mut item.data
|
||||||
|
{
|
||||||
|
value.handle_event(&event.0);
|
||||||
|
if let Some(validate) = validate {
|
||||||
|
if validate(value) {
|
||||||
|
commands.send_event(e(value.value().into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: bevy::prelude::Event> FromIterator<(&'static str, E)> for MenuWidget<E> {
|
impl<E: bevy::prelude::Event> MenuControl<E> {
|
||||||
fn from_iter<I: IntoIterator<Item = (&'static str, E)>>(iter: I) -> Self {
|
fn height(&self) -> u16 {
|
||||||
let items = iter
|
match &self.data {
|
||||||
.into_iter()
|
MenuControlData::Button { .. } => 1,
|
||||||
.map(|(label, event)| MenuOption {
|
MenuControlData::FSlider { .. } => 2,
|
||||||
enabled: Arc::new(true.into()),
|
MenuControlData::ISlider { .. } => 2,
|
||||||
label: label.into(),
|
MenuControlData::TextField { .. } => 2,
|
||||||
event,
|
MenuControlData::CheckBox { .. } => 1,
|
||||||
})
|
MenuControlData::Header(_) => 1,
|
||||||
.collect();
|
|
||||||
Self {
|
|
||||||
items,
|
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: bevy::prelude::Event> From<&MenuOption<E>> for ListItem<'_> {
|
pub enum MenuControlState {
|
||||||
fn from(value: &MenuOption<E>) -> Self {
|
None,
|
||||||
ListItem::new(value.label.clone())
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: bevy::prelude::Event> WidgetRef for &mut MenuControl<E> {
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
match &self.data {
|
||||||
|
MenuControlData::Button { label, .. } => {
|
||||||
|
let mut paragraph = Paragraph::new(format!(
|
||||||
|
"{}{}",
|
||||||
|
if self.selected { "> " } else { "" },
|
||||||
|
label.clone()
|
||||||
|
));
|
||||||
|
let enabled = self.enabled;
|
||||||
|
if enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.enabled_color);
|
||||||
|
}
|
||||||
|
if self.selected {
|
||||||
|
paragraph = paragraph.style(self.style.selected_style);
|
||||||
|
}
|
||||||
|
if !enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.disabled_color);
|
||||||
|
}
|
||||||
|
paragraph.render(area, buf);
|
||||||
|
}
|
||||||
|
MenuControlData::FSlider {
|
||||||
|
label,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
value,
|
||||||
|
unit,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut paragraph = Paragraph::new(format!(
|
||||||
|
"{}{}",
|
||||||
|
if self.selected { "> " } else { "" },
|
||||||
|
label.clone()
|
||||||
|
));
|
||||||
|
let enabled = self.enabled;
|
||||||
|
if enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.enabled_color);
|
||||||
|
}
|
||||||
|
if self.selected {
|
||||||
|
paragraph = paragraph.style(self.style.selected_style);
|
||||||
|
}
|
||||||
|
if !enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.disabled_color);
|
||||||
|
}
|
||||||
|
let mut area = area;
|
||||||
|
area.height = 1;
|
||||||
|
paragraph.render(area, buf);
|
||||||
|
area.y += 1;
|
||||||
|
let [l, m, r] = Layout::new(
|
||||||
|
Direction::Horizontal,
|
||||||
|
[
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Fill(0),
|
||||||
|
Constraint::Length(3),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.areas(area);
|
||||||
|
let gauge = Gauge::default()
|
||||||
|
.ratio(f32::inverse_lerp(*min, *max, *value) as f64)
|
||||||
|
.label(format!("{}{}", *value, unit.clone().unwrap_or_default()));
|
||||||
|
gauge.render(m, buf);
|
||||||
|
|
||||||
|
let lspan = Paragraph::new(if self.selected { " < " } else { "" });
|
||||||
|
lspan.render(l, buf);
|
||||||
|
|
||||||
|
let rspan = Paragraph::new(if self.selected { " > " } else { "" });
|
||||||
|
rspan.render(r, buf);
|
||||||
|
}
|
||||||
|
MenuControlData::ISlider {
|
||||||
|
label,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
value,
|
||||||
|
unit,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut paragraph = Paragraph::new(format!(
|
||||||
|
"{}{}",
|
||||||
|
if self.selected { "> " } else { "" },
|
||||||
|
label.clone()
|
||||||
|
));
|
||||||
|
let enabled = self.enabled;
|
||||||
|
if enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.enabled_color);
|
||||||
|
}
|
||||||
|
if self.selected {
|
||||||
|
paragraph = paragraph.style(self.style.selected_style);
|
||||||
|
}
|
||||||
|
if !enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.disabled_color);
|
||||||
|
}
|
||||||
|
let mut area = area;
|
||||||
|
area.height = 1;
|
||||||
|
paragraph.render(area, buf);
|
||||||
|
area.y += 1;
|
||||||
|
let [l, m, r] = Layout::new(
|
||||||
|
Direction::Horizontal,
|
||||||
|
[
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Fill(0),
|
||||||
|
Constraint::Length(3),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.areas(area);
|
||||||
|
let gauge = Gauge::default()
|
||||||
|
.ratio(f32::inverse_lerp(*min as f32, *max as f32, *value as f32) as f64)
|
||||||
|
.label(format!("{}{}", *value, unit.clone().unwrap_or_default()));
|
||||||
|
gauge.render(m, buf);
|
||||||
|
|
||||||
|
let lspan = Paragraph::new(if self.selected { " < " } else { "" });
|
||||||
|
lspan.render(l, buf);
|
||||||
|
|
||||||
|
let rspan = Paragraph::new(if self.selected { " > " } else { "" });
|
||||||
|
rspan.render(r, buf);
|
||||||
|
}
|
||||||
|
MenuControlData::TextField {
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
validate,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut paragraph = Paragraph::new(format!(
|
||||||
|
"{}{}",
|
||||||
|
if self.selected { "> " } else { "" },
|
||||||
|
label.clone()
|
||||||
|
));
|
||||||
|
let enabled = self.enabled;
|
||||||
|
if enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.enabled_color);
|
||||||
|
}
|
||||||
|
if self.selected {
|
||||||
|
paragraph = paragraph.style(self.style.selected_style);
|
||||||
|
}
|
||||||
|
if !enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.disabled_color);
|
||||||
|
}
|
||||||
|
let mut area = area;
|
||||||
|
area.height = 1;
|
||||||
|
paragraph.render(area, buf);
|
||||||
|
area.y += 1;
|
||||||
|
|
||||||
|
let width = area.width;
|
||||||
|
let scroll = value.visual_scroll(width as usize);
|
||||||
|
let mut input = Paragraph::new(value.value())
|
||||||
|
.scroll((0, scroll as u16))
|
||||||
|
.add_modifier(Modifier::REVERSED);
|
||||||
|
if !enabled {
|
||||||
|
input = input.fg(self.style.disabled_color);
|
||||||
|
}
|
||||||
|
if let Some(validate) = validate {
|
||||||
|
if !validate(value) {
|
||||||
|
input = input.on_red();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.render(area, buf);
|
||||||
|
}
|
||||||
|
MenuControlData::CheckBox { label, value, .. } => {
|
||||||
|
let mut paragraph = Paragraph::new(format!(
|
||||||
|
"{}[{}] {label}",
|
||||||
|
if self.selected { "> " } else { "" },
|
||||||
|
if *value { "x" } else { " " }
|
||||||
|
));
|
||||||
|
let enabled = self.enabled;
|
||||||
|
if enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.enabled_color);
|
||||||
|
}
|
||||||
|
if self.selected {
|
||||||
|
paragraph = paragraph.style(self.style.selected_style);
|
||||||
|
}
|
||||||
|
if !enabled {
|
||||||
|
paragraph = paragraph.fg(self.style.disabled_color);
|
||||||
|
}
|
||||||
|
paragraph.render(area, buf);
|
||||||
|
}
|
||||||
|
MenuControlData::Header(label) => {
|
||||||
|
if label.is_empty() {
|
||||||
|
Clear.render(area, buf);
|
||||||
|
} else {
|
||||||
|
Paragraph::new(label.to_string())
|
||||||
|
.underlined()
|
||||||
|
.render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user