default is no_std heapless
This commit is contained in:
50
Cargo.lock
generated
50
Cargo.lock
generated
@@ -3,22 +3,19 @@
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
@@ -26,14 +23,35 @@ version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
"rustc-std-workspace-alloc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tui_orchestrator"
|
||||
name = "heapless"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pages-tui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"heapless",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-std-workspace-alloc"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -1,16 +1,17 @@
|
||||
[package]
|
||||
name = "tui_orchestrator"
|
||||
name = "pages-tui"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
alloc = ["hashbrown"]
|
||||
default = []
|
||||
std = ["alloc"]
|
||||
alloc = ["dep:hashbrown"]
|
||||
sequences = ["alloc"]
|
||||
|
||||
[dependencies]
|
||||
hashbrown = { version = "0.15", optional = true }
|
||||
hashbrown = { version = "0.15", optional = true, default-features = false, features = ["alloc"] }
|
||||
heapless = { version = "0.9.2", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
use super::action::{Action, ComponentAction};
|
||||
use super::key::Key;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use hashbrown::HashSet;
|
||||
|
||||
/// Maps keys to actions.
|
||||
///
|
||||
/// When `alloc` feature is enabled, uses HashMap for O(1) lookup.
|
||||
/// Without `alloc`, falls back to Vec with O(n) lookup.
|
||||
/// Without `alloc`, uses heapless::LinearMap with fixed capacity N.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bindings<A: Action> {
|
||||
pub struct Bindings<A: Action, const N: usize = 32> {
|
||||
#[cfg(feature = "alloc")]
|
||||
bindings: hashbrown::HashMap<Key, A>,
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
bindings: alloc::vec::Vec<(Key, A)>,
|
||||
bindings: heapless::LinearMap<Key, A, N>,
|
||||
}
|
||||
|
||||
impl<A: Action> Bindings<A> {
|
||||
impl<A: Action, const N: usize> Bindings<A, N> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "alloc")]
|
||||
bindings: hashbrown::HashMap::new(),
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
bindings: alloc::vec::Vec::new(),
|
||||
bindings: heapless::LinearMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,19 +33,14 @@ impl<A: Action> Bindings<A> {
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
self.bindings.push((key, action));
|
||||
// V heapless verzii ignorujeme chybu pri plnej kapacite,
|
||||
// alebo by ste mohli vrátiť Result.
|
||||
let _ = self.bindings.insert(key, action);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &Key) -> Option<&A> {
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
self.bindings.get(key)
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
self.bindings.iter().find(|(k, _)| k == key).map(|(_, a)| a)
|
||||
}
|
||||
self.bindings.get(key)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: &Key) {
|
||||
@@ -52,113 +50,85 @@ impl<A: Action> Bindings<A> {
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
self.bindings.retain(|(k, _)| k != key);
|
||||
self.bindings.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
self.bindings.is_empty()
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
self.bindings.is_empty()
|
||||
}
|
||||
self.bindings.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
self.bindings.len()
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
self.bindings.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keys(&self) -> alloc::vec::Vec<&Key> {
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
self.bindings.keys().collect()
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
self.bindings.iter().map(|(k, _)| k).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
pub fn iter(&self) -> impl Iterator<Item = &(Key, A)> {
|
||||
self.bindings.iter()
|
||||
self.bindings.len()
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn keys(&self) -> alloc::vec::Vec<&Key> {
|
||||
self.bindings.keys().collect()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
pub fn keys(&self) -> heapless::Vec<&Key, N> {
|
||||
let mut v = heapless::Vec::new();
|
||||
for k in self.bindings.keys() {
|
||||
let _ = v.push(k);
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Key, &A)> {
|
||||
self.bindings.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Action> Default for Bindings<A> {
|
||||
impl<A: Action, const N: usize> Default for Bindings<A, N> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sequences")]
|
||||
impl<A: Action + core::hash::Hash + Eq> Bindings<A> {
|
||||
impl<A: Action + core::hash::Hash + Eq, const N: usize> Bindings<A, N> {
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn bind_sequence(&mut self, keys: alloc::vec::Vec<Key>, action: A) {
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
for key in keys {
|
||||
self.bindings.insert(key, action.clone());
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
for key in keys {
|
||||
self.bindings.push((key, action.clone()));
|
||||
}
|
||||
for key in keys {
|
||||
self.bindings.insert(key, action.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
pub fn bind_sequence<const K: usize>(&mut self, keys: heapless::Vec<Key, K>, action: A) {
|
||||
for key in keys {
|
||||
let _ = self.bindings.insert(key, action.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn get_sequences(&self) -> alloc::vec::Vec<&A> {
|
||||
let mut actions = alloc::vec::Vec::new();
|
||||
let mut seen = HashSet::new();
|
||||
#[cfg(feature = "alloc")]
|
||||
{
|
||||
for (_, action) in &self.bindings {
|
||||
if seen.insert(action) {
|
||||
actions.push(action);
|
||||
}
|
||||
for (_, action) in &self.bindings {
|
||||
if seen.insert(action) {
|
||||
actions.push(action);
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
for (_, action) in &self.bindings {
|
||||
if seen.insert(action) {
|
||||
actions.push(action);
|
||||
}
|
||||
actions
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
pub fn get_sequences(&self) -> heapless::Vec<&A, N> {
|
||||
let mut actions = heapless::Vec::new();
|
||||
// V no_std/no_alloc bez HashSetu robíme O(n^2) kontrolu unikátnosti
|
||||
for (_, action) in &self.bindings {
|
||||
if !actions.iter().any(|&a| a == action) {
|
||||
let _ = actions.push(action);
|
||||
}
|
||||
}
|
||||
actions
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns default key bindings for common TUI patterns.
|
||||
///
|
||||
/// Includes:
|
||||
/// - Tab/Shift+Tab for next/prev navigation
|
||||
/// - Enter for select
|
||||
/// - Esc for cancel
|
||||
/// - Arrow keys for directional movement
|
||||
/// - Home/End/PageUp/PageDown for scrolling
|
||||
/// - Backspace/Delete for editing
|
||||
/// - Ctrl+C as custom quit action
|
||||
///
|
||||
/// Use as-is or extend with your own bindings.
|
||||
pub fn default_bindings() -> Bindings<ComponentAction> {
|
||||
pub fn default_bindings() -> Bindings<ComponentAction, 16> {
|
||||
let mut bindings = Bindings::new();
|
||||
bindings.bind(Key::tab(), ComponentAction::Next);
|
||||
bindings.bind(Key::shift_tab(), ComponentAction::Prev);
|
||||
|
||||
Reference in New Issue
Block a user