#[derive(Debug, Clone, Default)]
pub struct SelectState {
pub items: Vec<String>,
pub selected: usize,
pub open: bool,
pub placeholder: String,
cursor: usize,
}
impl SelectState {
pub fn new(items: Vec<impl Into<String>>) -> Self {
Self {
items: items.into_iter().map(Into::into).collect(),
selected: 0,
open: false,
placeholder: String::new(),
cursor: 0,
}
}
pub fn placeholder(mut self, p: impl Into<String>) -> Self {
self.placeholder = p.into();
self
}
pub fn selected_item(&self) -> Option<&str> {
self.items.get(self.selected).map(String::as_str)
}
pub(crate) fn cursor(&self) -> usize {
self.cursor
}
pub(crate) fn set_cursor(&mut self, c: usize) {
self.cursor = c;
}
}
#[derive(Debug, Clone, Default)]
pub struct RadioState {
pub items: Vec<String>,
pub selected: usize,
}
impl RadioState {
pub fn new(items: Vec<impl Into<String>>) -> Self {
Self {
items: items.into_iter().map(Into::into).collect(),
selected: 0,
}
}
pub fn selected_item(&self) -> Option<&str> {
self.items.get(self.selected).map(String::as_str)
}
}
#[derive(Debug, Clone)]
pub struct MultiSelectState {
pub items: Vec<String>,
pub cursor: usize,
pub selected: HashSet<usize>,
}
impl MultiSelectState {
pub fn new(items: Vec<impl Into<String>>) -> Self {
Self {
items: items.into_iter().map(Into::into).collect(),
cursor: 0,
selected: HashSet::new(),
}
}
pub fn selected_items(&self) -> Vec<&str> {
let mut indices: Vec<usize> = self.selected.iter().copied().collect();
indices.sort();
indices
.iter()
.filter_map(|&i| self.items.get(i).map(String::as_str))
.collect()
}
pub fn toggle(&mut self, index: usize) {
if self.selected.contains(&index) {
self.selected.remove(&index);
} else {
self.selected.insert(index);
}
}
}
#[derive(Debug, Clone)]
pub struct TreeNode {
pub label: String,
pub children: Vec<TreeNode>,
pub expanded: bool,
}
impl TreeNode {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
children: Vec::new(),
expanded: false,
}
}
pub fn expanded(mut self) -> Self {
self.expanded = true;
self
}
pub fn children(mut self, children: Vec<TreeNode>) -> Self {
self.children = children;
self
}
pub fn is_leaf(&self) -> bool {
self.children.is_empty()
}
fn flatten(&self, depth: usize, out: &mut Vec<FlatTreeEntry>) {
out.push(FlatTreeEntry {
depth,
label: self.label.clone(),
is_leaf: self.is_leaf(),
expanded: self.expanded,
});
if self.expanded {
for child in &self.children {
child.flatten(depth + 1, out);
}
}
}
}
pub(crate) struct FlatTreeEntry {
pub depth: usize,
pub label: String,
pub is_leaf: bool,
pub expanded: bool,
}
#[derive(Debug, Clone)]
pub struct TreeState {
pub nodes: Vec<TreeNode>,
pub selected: usize,
}
impl TreeState {
pub fn new(nodes: Vec<TreeNode>) -> Self {
Self { nodes, selected: 0 }
}
pub(crate) fn flatten(&self) -> Vec<FlatTreeEntry> {
let mut entries = Vec::new();
for node in &self.nodes {
node.flatten(0, &mut entries);
}
entries
}
pub(crate) fn toggle_at(&mut self, flat_index: usize) {
let mut counter = 0usize;
Self::toggle_recursive(&mut self.nodes, flat_index, &mut counter);
}
fn toggle_recursive(nodes: &mut [TreeNode], target: usize, counter: &mut usize) -> bool {
for node in nodes.iter_mut() {
if *counter == target {
if !node.is_leaf() {
node.expanded = !node.expanded;
}
return true;
}
*counter += 1;
if node.expanded && Self::toggle_recursive(&mut node.children, target, counter) {
return true;
}
}
false
}
}
#[derive(Debug, Clone)]
pub struct DirectoryTreeState {
pub tree: TreeState,
pub show_icons: bool,
}
impl DirectoryTreeState {
pub fn new(nodes: Vec<TreeNode>) -> Self {
Self {
tree: TreeState::new(nodes),
show_icons: true,
}
}
pub fn from_paths(paths: &[&str]) -> Self {
let mut roots: Vec<TreeNode> = Vec::new();
for raw_path in paths {
let parts: Vec<&str> = raw_path
.split('/')
.filter(|part| !part.is_empty())
.collect();
if parts.is_empty() {
continue;
}
insert_path(&mut roots, &parts, 0);
}
Self::new(roots)
}
pub fn selected_label(&self) -> Option<&str> {
let mut cursor = 0usize;
selected_label_in_nodes(&self.tree.nodes, self.tree.selected, &mut cursor)
}
}
impl Default for DirectoryTreeState {
fn default() -> Self {
Self::new(Vec::<TreeNode>::new())
}
}
fn insert_path(nodes: &mut Vec<TreeNode>, parts: &[&str], depth: usize) {
let Some(label) = parts.get(depth) else {
return;
};
let is_last = depth + 1 == parts.len();
let idx = nodes
.iter()
.position(|node| node.label == *label)
.unwrap_or_else(|| {
let mut node = TreeNode::new(*label);
if !is_last {
node.expanded = true;
}
nodes.push(node);
nodes.len() - 1
});
if is_last {
return;
}
nodes[idx].expanded = true;
insert_path(&mut nodes[idx].children, parts, depth + 1);
}
fn selected_label_in_nodes<'a>(
nodes: &'a [TreeNode],
target: usize,
cursor: &mut usize,
) -> Option<&'a str> {
for node in nodes {
if *cursor == target {
return Some(node.label.as_str());
}
*cursor += 1;
if node.expanded {
if let Some(found) = selected_label_in_nodes(&node.children, target, cursor) {
return Some(found);
}
}
}
None
}
#[derive(Debug, Clone)]
pub struct PaletteCommand {
pub label: String,
pub description: String,
pub shortcut: Option<String>,
}
impl PaletteCommand {
pub fn new(label: impl Into<String>, description: impl Into<String>) -> Self {
Self {
label: label.into(),
description: description.into(),
shortcut: None,
}
}
pub fn shortcut(mut self, s: impl Into<String>) -> Self {
self.shortcut = Some(s.into());
self
}
}