tui-input-0.10.1/.cargo_vcs_info.json0000644000000001360000000000100130510ustar { "git": { "sha1": "0813f9f216ba43f85e60cf8796cb27b535227e16" }, "path_in_vcs": "" }tui-input-0.10.1/.github/FUNDING.yml000064400000000000000000000011411046102023000150130ustar 00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: xplr ko_fi: sayanarijit tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: sayanarijit issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] tui-input-0.10.1/.github/workflows/ci.yml000064400000000000000000000006641046102023000163620ustar 00000000000000name: Continuous Integration on: push: branches: [ main ] pull_request: branches: [ main ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose - name: Run lint run: cargo clippy --verbose - name: Run fmt run: cargo fmt --verbose tui-input-0.10.1/.gitignore000064400000000000000000000000231046102023000136240ustar 00000000000000/target Cargo.lock tui-input-0.10.1/Cargo.lock0000644000000407510000000000100110330ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", "version_check", "zerocopy", ] [[package]] name = "allocator-api2" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "cassowary" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "castaway" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "compact_str" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" dependencies = [ "castaway", "cfg-if", "itoa", "rustversion", "ryu", "static_assertions", ] [[package]] name = "crossterm" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.6.0", "crossterm_winapi", "mio", "parking_lot", "rustix", "signal-hook", "signal-hook-mio", "winapi", ] [[package]] name = "crossterm_winapi" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ "winapi", ] [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", ] [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "instability" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" dependencies = [ "quote", "syn", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libredox" version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ "bitflags 2.6.0", "libc", "redox_syscall 0.4.1", ] [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown", ] [[package]] name = "mio" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", "log", "wasi", "windows-sys", ] [[package]] name = "numtoa" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall 0.5.3", "smallvec", "windows-targets", ] [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "ratatui" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d" dependencies = [ "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", "instability", "itertools", "lru", "paste", "strum", "strum_macros", "unicode-segmentation", "unicode-truncate", "unicode-width", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_syscall" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "redox_termios" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rustversion" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-mio" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio", "signal-hook", ] [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strum" version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", "syn", ] [[package]] name = "syn" version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termion" version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ccce68e518d1173e80876edd54760b60b792750d0cab6444a79101c6ea03848" dependencies = [ "libc", "libredox", "numtoa", "redox_termios", ] [[package]] name = "tui-input" version = "0.10.1" dependencies = [ "ratatui", "serde", "termion", "unicode-width", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-truncate" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ "itertools", "unicode-segmentation", "unicode-width", ] [[package]] name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] tui-input-0.10.1/Cargo.toml0000644000000024600000000000100110510ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "tui-input" version = "0.10.1" authors = ["Arijit Basu "] autoexamples = true description = "TUI input library supporting multiple backends" readme = "README.md" keywords = [ "tui", "terminal", "input", ] license = "MIT" repository = "https://github.com/sayanarijit/tui-input" [[example]] name = "crossterm_input" path = "examples/crossterm_input.rs" required-features = ["crossterm"] [[example]] name = "termion_input" path = "examples/termion_input.rs" required-features = ["termion"] [dependencies.ratatui] version = "0.28" optional = true [dependencies.serde] version = "1.0.209" features = ["derive"] optional = true [dependencies.termion] version = "4.0.2" optional = true [dependencies.unicode-width] version = "0.1.13" [features] crossterm = ["dep:ratatui"] default = ["crossterm"] tui-input-0.10.1/Cargo.toml.orig000064400000000000000000000016221046102023000145310ustar 00000000000000[package] name = "tui-input" version = "0.10.1" edition = "2021" authors = ["Arijit Basu "] description = "TUI input library supporting multiple backends" keywords = ["tui", "terminal", "input"] repository = "https://github.com/sayanarijit/tui-input" readme = "README.md" license = "MIT" autoexamples = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["crossterm"] crossterm = ["dep:ratatui"] [dependencies] ratatui = { version = "0.28", optional = true } serde = { version = "1.0.209", optional = true, features = ["derive"] } termion = { version = "4.0.2", optional = true } unicode-width = "0.1.13" [[example]] name = "crossterm_input" path = "./examples/crossterm_input.rs" required-features = ["crossterm"] [[example]] name = "termion_input" path = "./examples/termion_input.rs" required-features = ["termion"] tui-input-0.10.1/LICENSE000064400000000000000000000020541046102023000126470ustar 00000000000000MIT License Copyright (c) 2021 Arijit Basu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tui-input-0.10.1/README.md000064400000000000000000000022441046102023000131220ustar 00000000000000# tui-input [![Crate Status](https://img.shields.io/crates/v/tui-input.svg)](https://crates.io/crates/tui-input) [![Docs Status](https://docs.rs/tui-input/badge.svg)](https://docs.rs/tui-input/) [![tui-input.gif](https://s10.gifyu.com/images/tui-input.gif)](https://github.com/sayanarijit/tui-input/blob/main/examples/ratatui-input/src/main.rs) A TUI input library supporting multiple backends. This crate can be used with [tui-rs](https://github.com/fdehau/tui-rs) and [ratatui](https://github.com/tui-rs-revival/ratatui). For people using `tui-rs` use version `v0.6.*` for people migrating to `ratatui` use latest version. ## Install Cargo.toml ```toml # crossterm tui-input = "*" # termion tui-input = { version = "*", features = ["termion"], default-features = false } ``` ## Features - crossterm (default) - termion - serde ## Demo See [examples](https://github.com/sayanarijit/tui-input/tree/main/examples). ```bash # Run the example with crossterm as backend. cargo run --example crossterm_input # Run the example with termion as backend. cargo run --example termion_input --features termion # Run the tui-rs example (cd ./examples/ratatui-input/ && cargo run) ``` tui-input-0.10.1/examples/crossterm_input.rs000064400000000000000000000030371046102023000172700ustar 00000000000000use ratatui::crossterm::{ cursor::{Hide, Show}, event::{read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent}, execute, terminal::{ disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, }, }; use std::io::{stdout, Result, Write}; use tui_input::backend::crossterm as backend; use tui_input::backend::crossterm::EventHandler; use tui_input::Input; fn main() -> Result<()> { enable_raw_mode()?; let stdout = stdout(); let mut stdout = stdout.lock(); execute!(stdout, Hide, EnterAlternateScreen, EnableMouseCapture)?; let mut input: Input = "Hello ".into(); backend::write(&mut stdout, input.value(), input.cursor(), (0, 0), 15)?; stdout.flush()?; loop { let event = read()?; if let Event::Key(KeyEvent { code, .. }) = event { match code { KeyCode::Esc | KeyCode::Enter => { break; } _ => { if input.handle_event(&event).is_some() { backend::write( &mut stdout, input.value(), input.cursor(), (0, 0), 15, )?; stdout.flush()?; } } } } } execute!(stdout, Show, LeaveAlternateScreen, DisableMouseCapture)?; disable_raw_mode()?; println!("{}", input); Ok(()) } tui-input-0.10.1/examples/termion_input.rs000064400000000000000000000021531046102023000167220ustar 00000000000000use std::io::{stdin, stdout, Result, Write}; use termion::cursor::{Hide, Show}; use termion::event::{Event, Key}; use termion::input::TermRead; use termion::raw::IntoRawMode; use termion::screen::IntoAlternateScreen; use tui_input::backend::termion as backend; use tui_input::backend::termion::EventHandler; use tui_input::Input; fn main() -> Result<()> { let mut input: Input = "Hello ".into(); { let stdin = stdin(); let mut stdout = stdout().into_raw_mode()?.into_alternate_screen()?; write!(&mut stdout, "{}", Hide)?; backend::write(&mut stdout, input.value(), input.cursor(), (0, 0), 15)?; stdout.flush()?; for evt in stdin.events() { let evt = evt?; if evt == Event::Key(Key::Esc) || evt == Event::Key(Key::Char('\n')) { break; } if input.handle_event(&evt).is_some() { backend::write(&mut stdout, input.value(), input.cursor(), (0, 0), 15)?; stdout.flush()?; } } write!(stdout, "{}", Show)?; } println!("{}", input); Ok(()) } tui-input-0.10.1/rustfmt.toml000064400000000000000000000001171046102023000142410ustar 00000000000000edition = "2021" max_width = 89 tab_spaces = 4 use_field_init_shorthand = true tui-input-0.10.1/src/backend/crossterm.rs000064400000000000000000000102171046102023000164070ustar 00000000000000use crate::{Input, InputRequest, StateChanged}; use ratatui::crossterm::event::{ Event as CrosstermEvent, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, }; use ratatui::crossterm::{ cursor::MoveTo, queue, style::{Attribute as CAttribute, Print, SetAttribute}, }; use std::io::{Result, Write}; /// Converts crossterm event into input requests. pub fn to_input_request(evt: &CrosstermEvent) -> Option { use InputRequest::*; use KeyCode::*; match evt { CrosstermEvent::Key(KeyEvent { code, modifiers, kind, state: _, }) if *kind == KeyEventKind::Press => match (*code, *modifiers) { (Backspace, KeyModifiers::NONE) | (Char('h'), KeyModifiers::CONTROL) => { Some(DeletePrevChar) } (Delete, KeyModifiers::NONE) => Some(DeleteNextChar), (Tab, KeyModifiers::NONE) => None, (Left, KeyModifiers::NONE) | (Char('b'), KeyModifiers::CONTROL) => { Some(GoToPrevChar) } (Left, KeyModifiers::CONTROL) | (Char('b'), KeyModifiers::META) => { Some(GoToPrevWord) } (Right, KeyModifiers::NONE) | (Char('f'), KeyModifiers::CONTROL) => { Some(GoToNextChar) } (Right, KeyModifiers::CONTROL) | (Char('f'), KeyModifiers::META) => { Some(GoToNextWord) } (Char('u'), KeyModifiers::CONTROL) => Some(DeleteLine), (Char('w'), KeyModifiers::CONTROL) | (Char('d'), KeyModifiers::META) | (Backspace, KeyModifiers::META) | (Backspace, KeyModifiers::ALT) => Some(DeletePrevWord), (Delete, KeyModifiers::CONTROL) => Some(DeleteNextWord), (Char('k'), KeyModifiers::CONTROL) => Some(DeleteTillEnd), (Char('a'), KeyModifiers::CONTROL) | (Home, KeyModifiers::NONE) => { Some(GoToStart) } (Char('e'), KeyModifiers::CONTROL) | (End, KeyModifiers::NONE) => { Some(GoToEnd) } (Char(c), KeyModifiers::NONE) => Some(InsertChar(c)), (Char(c), KeyModifiers::SHIFT) => Some(InsertChar(c)), (_, _) => None, }, _ => None, } } /// Renders the input UI at the given position with the given width. pub fn write( stdout: &mut W, value: &str, cursor: usize, (x, y): (u16, u16), width: u16, ) -> Result<()> { queue!(stdout, MoveTo(x, y), SetAttribute(CAttribute::NoReverse))?; let val_width = width.max(1) as usize - 1; let len = value.chars().count(); let start = (len.max(val_width) - val_width).min(cursor); let mut chars = value.chars().skip(start); let mut i = start; // Chars before cursor while i < cursor { i += 1; let c = chars.next().unwrap_or(' '); queue!(stdout, Print(c))?; } // Cursor i += 1; let c = chars.next().unwrap_or(' '); queue!( stdout, SetAttribute(CAttribute::Reverse), Print(c), SetAttribute(CAttribute::NoReverse) )?; // Chars after the cursor while i <= start + val_width { i += 1; let c = chars.next().unwrap_or(' '); queue!(stdout, Print(c))?; } Ok(()) } /// Import this trait to implement `Input::handle_event()` for crossterm. pub trait EventHandler { /// Handle crossterm event. fn handle_event(&mut self, evt: &CrosstermEvent) -> Option; } impl EventHandler for Input { /// Handle crossterm event. fn handle_event(&mut self, evt: &CrosstermEvent) -> Option { to_input_request(evt).and_then(|req| self.handle(req)) } } #[cfg(test)] mod tests { use ratatui::crossterm::event::{KeyEventKind, KeyEventState}; use super::*; #[test] fn handle_tab() { let evt = CrosstermEvent::Key(KeyEvent { code: KeyCode::Tab, modifiers: KeyModifiers::NONE, kind: KeyEventKind::Press, state: KeyEventState::NONE, }); let req = to_input_request(&evt); assert!(req.is_none()); } } tui-input-0.10.1/src/backend/mod.rs000064400000000000000000000001371046102023000151450ustar 00000000000000#[cfg(feature = "crossterm")] pub mod crossterm; #[cfg(feature = "termion")] pub mod termion; tui-input-0.10.1/src/backend/termion.rs000064400000000000000000000054341046102023000160500ustar 00000000000000use crate::input::InputRequest; use crate::Input; use crate::StateChanged; use std::io::{Result, Write}; use termion::cursor::Goto; use termion::event::{Event, Key}; use termion::style::Invert; use termion::style::NoInvert; /// Converts termion event into input requests. pub fn to_input_request(evt: &Event) -> Option { use InputRequest::*; match *evt { Event::Key(Key::Backspace) | Event::Key(Key::Ctrl('h')) => Some(DeletePrevChar), Event::Key(Key::Delete) => Some(DeleteNextChar), Event::Key(Key::Left) | Event::Key(Key::Ctrl('b')) => Some(GoToPrevChar), Event::Key(Key::Right) | Event::Key(Key::Ctrl('f')) => Some(GoToNextChar), // Event::Key(Key::Ctrl(Key::Left)) => Some(GoToPrevWord), // Event::Key(Key::Ctrl(Key::Right)) => Some(GoToNextWord), Event::Key(Key::Ctrl('u')) => Some(DeleteLine), Event::Key(Key::Ctrl('w')) => Some(DeletePrevWord), // Event::Key(Key::Ctrl(Key::Delete)) => Some(DeleteNextWord), Event::Key(Key::Ctrl('a')) | Event::Key(Key::Home) => Some(GoToStart), Event::Key(Key::Ctrl('e')) | Event::Key(Key::End) => Some(GoToEnd), Event::Key(Key::Char('\t')) => None, Event::Key(Key::Char(c)) => Some(InsertChar(c)), _ => None, } } /// Renders the input UI at the given position with the given width. pub fn write( stdout: &mut W, value: &str, cursor: usize, (x, y): (u16, u16), width: u16, ) -> Result<()> { write!(stdout, "{}{}", Goto(x + 1, y + 1), NoInvert)?; let val_width = width.max(1) as usize - 1; let len = value.chars().count(); let start = (len.max(val_width) - val_width).min(cursor); let mut chars = value.chars().skip(start); let mut i = start; // Chars before cursor while i < cursor { i += 1; let c = chars.next().unwrap_or(' '); write!(stdout, "{}", c)?; } // Cursor i += 1; let c = chars.next().unwrap_or(' '); write!(stdout, "{}{}{}", Invert, c, NoInvert,)?; // Chars after the cursor while i <= start + val_width { i += 1; let c = chars.next().unwrap_or(' '); write!(stdout, "{}", c)?; } Ok(()) } /// Import this trait to implement `Input::handle_event()` for termion. pub trait EventHandler { /// Handle termion event. fn handle_event(&mut self, evt: &Event) -> Option; } impl EventHandler for Input { /// Handle termion event. fn handle_event(&mut self, evt: &Event) -> Option { to_input_request(evt).and_then(|req| self.handle(req)) } } #[cfg(test)] mod tests { use super::*; #[test] fn handle_tab() { let evt = Event::Key(Key::Char('\t')); let req = to_input_request(&evt); assert!(req.is_none()); } } tui-input-0.10.1/src/input.rs000064400000000000000000000400171046102023000141370ustar 00000000000000/// Input requests are used to change the input state. /// /// Different backends can be used to convert events into requests. #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InputRequest { SetCursor(usize), InsertChar(char), GoToPrevChar, GoToNextChar, GoToPrevWord, GoToNextWord, GoToStart, GoToEnd, DeletePrevChar, DeleteNextChar, DeletePrevWord, DeleteNextWord, DeleteLine, DeleteTillEnd, } #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct StateChanged { pub value: bool, pub cursor: bool, } pub type InputResponse = Option; /// The input buffer with cursor support. /// /// Example: /// /// ``` /// use tui_input::Input; /// /// let input: Input = "Hello World".into(); /// /// assert_eq!(input.cursor(), 11); /// assert_eq!(input.to_string(), "Hello World"); /// ``` #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Input { value: String, cursor: usize, } impl Input { /// Initialize a new instance with a given value /// Cursor will be set to the given value's length. pub fn new(value: String) -> Self { let len = value.chars().count(); Self { value, cursor: len } } /// Set the value manually. /// Cursor will be set to the given value's length. pub fn with_value(mut self, value: String) -> Self { self.cursor = value.chars().count(); self.value = value; self } /// Set the cursor manually. /// If the input is larger than the value length, it'll be auto adjusted. pub fn with_cursor(mut self, cursor: usize) -> Self { self.cursor = cursor.min(self.value.chars().count()); self } // Reset the cursor and value to default pub fn reset(&mut self) { self.cursor = Default::default(); self.value = Default::default(); } /// Handle request and emit response. pub fn handle(&mut self, req: InputRequest) -> InputResponse { use InputRequest::*; match req { SetCursor(pos) => { let pos = pos.min(self.value.chars().count()); if self.cursor == pos { None } else { self.cursor = pos; Some(StateChanged { value: false, cursor: true, }) } } InsertChar(c) => { if self.cursor == self.value.chars().count() { self.value.push(c); } else { self.value = self .value .chars() .take(self.cursor) .chain( std::iter::once(c) .chain(self.value.chars().skip(self.cursor)), ) .collect(); } self.cursor += 1; Some(StateChanged { value: true, cursor: true, }) } DeletePrevChar => { if self.cursor == 0 { None } else { self.cursor -= 1; self.value = self .value .chars() .enumerate() .filter(|(i, _)| i != &self.cursor) .map(|(_, c)| c) .collect(); Some(StateChanged { value: true, cursor: true, }) } } DeleteNextChar => { if self.cursor == self.value.chars().count() { None } else { self.value = self .value .chars() .enumerate() .filter(|(i, _)| i != &self.cursor) .map(|(_, c)| c) .collect(); Some(StateChanged { value: true, cursor: false, }) } } GoToPrevChar => { if self.cursor == 0 { None } else { self.cursor -= 1; Some(StateChanged { value: false, cursor: true, }) } } GoToPrevWord => { if self.cursor == 0 { None } else { self.cursor = self .value .chars() .rev() .skip(self.value.chars().count().max(self.cursor) - self.cursor) .skip_while(|c| !c.is_alphanumeric()) .skip_while(|c| c.is_alphanumeric()) .count(); Some(StateChanged { value: false, cursor: true, }) } } GoToNextChar => { if self.cursor == self.value.chars().count() { None } else { self.cursor += 1; Some(StateChanged { value: false, cursor: true, }) } } GoToNextWord => { if self.cursor == self.value.chars().count() { None } else { self.cursor = self .value .chars() .enumerate() .skip(self.cursor) .skip_while(|(_, c)| c.is_alphanumeric()) .find(|(_, c)| c.is_alphanumeric()) .map(|(i, _)| i) .unwrap_or_else(|| self.value.chars().count()); Some(StateChanged { value: false, cursor: true, }) } } DeleteLine => { if self.value.is_empty() { None } else { let cursor = self.cursor; self.value = "".into(); self.cursor = 0; Some(StateChanged { value: true, cursor: self.cursor == cursor, }) } } DeletePrevWord => { if self.cursor == 0 { None } else { let remaining = self.value.chars().skip(self.cursor); let rev = self .value .chars() .rev() .skip(self.value.chars().count().max(self.cursor) - self.cursor) .skip_while(|c| !c.is_alphanumeric()) .skip_while(|c| c.is_alphanumeric()) .collect::>(); let rev_len = rev.len(); self.value = rev.into_iter().rev().chain(remaining).collect(); self.cursor = rev_len; Some(StateChanged { value: true, cursor: true, }) } } DeleteNextWord => { if self.cursor == self.value.chars().count() { None } else { self.value = self .value .chars() .take(self.cursor) .chain( self.value .chars() .skip(self.cursor) .skip_while(|c| c.is_alphanumeric()) .skip_while(|c| !c.is_alphanumeric()), ) .collect(); Some(StateChanged { value: true, cursor: false, }) } } GoToStart => { if self.cursor == 0 { None } else { self.cursor = 0; Some(StateChanged { value: false, cursor: true, }) } } GoToEnd => { let count = self.value.chars().count(); if self.cursor == count { None } else { self.cursor = count; Some(StateChanged { value: false, cursor: true, }) } } DeleteTillEnd => { self.value = self.value.chars().take(self.cursor).collect(); Some(StateChanged { value: true, cursor: false, }) } } } /// Get a reference to the current value. pub fn value(&self) -> &str { self.value.as_str() } /// Get the currect cursor placement. pub fn cursor(&self) -> usize { self.cursor } /// Get the current cursor position with account for multispace characters. pub fn visual_cursor(&self) -> usize { if self.cursor == 0 { return 0; } // Safe, because the end index will always be within bounds unicode_width::UnicodeWidthStr::width(unsafe { self.value.get_unchecked( 0..self .value .char_indices() .nth(self.cursor) .map_or_else(|| self.value.len(), |(index, _)| index), ) }) } /// Get the scroll position with account for multispace characters. pub fn visual_scroll(&self, width: usize) -> usize { let scroll = (self.visual_cursor()).max(width) - width; let mut uscroll = 0; let mut chars = self.value().chars(); while uscroll < scroll { match chars.next() { Some(c) => { uscroll += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0); } None => break, } } uscroll } } impl From for String { fn from(input: Input) -> Self { input.value } } impl From for Input { fn from(value: String) -> Self { Self::new(value) } } impl From<&str> for Input { fn from(value: &str) -> Self { Self::new(value.into()) } } impl std::fmt::Display for Input { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.value.fmt(f) } } #[cfg(test)] mod tests { const TEXT: &str = "first second, third."; use super::*; #[test] fn format() { let input: Input = TEXT.into(); println!("{}", input); println!("{}", input); } #[test] fn set_cursor() { let mut input: Input = TEXT.into(); let req = InputRequest::SetCursor(3); let resp = input.handle(req); assert_eq!( resp, Some(StateChanged { value: false, cursor: true, }) ); assert_eq!(input.value(), "first second, third."); assert_eq!(input.cursor(), 3); let req = InputRequest::SetCursor(30); let resp = input.handle(req); assert_eq!(input.cursor(), TEXT.chars().count()); assert_eq!( resp, Some(StateChanged { value: false, cursor: true, }) ); let req = InputRequest::SetCursor(TEXT.chars().count()); let resp = input.handle(req); assert_eq!(input.cursor(), TEXT.chars().count()); assert_eq!(resp, None); } #[test] fn insert_char() { let mut input: Input = TEXT.into(); let req = InputRequest::InsertChar('x'); let resp = input.handle(req); assert_eq!( resp, Some(StateChanged { value: true, cursor: true, }) ); assert_eq!(input.value(), "first second, third.x"); assert_eq!(input.cursor(), TEXT.chars().count() + 1); input.handle(req); assert_eq!(input.value(), "first second, third.xx"); assert_eq!(input.cursor(), TEXT.chars().count() + 2); let mut input = input.with_cursor(3); input.handle(req); assert_eq!(input.value(), "firxst second, third.xx"); assert_eq!(input.cursor(), 4); input.handle(req); assert_eq!(input.value(), "firxxst second, third.xx"); assert_eq!(input.cursor(), 5); } #[test] fn go_to_prev_char() { let mut input: Input = TEXT.into(); let req = InputRequest::GoToPrevChar; let resp = input.handle(req); assert_eq!( resp, Some(StateChanged { value: false, cursor: true, }) ); assert_eq!(input.value(), "first second, third."); assert_eq!(input.cursor(), TEXT.chars().count() - 1); let mut input = input.with_cursor(3); input.handle(req); assert_eq!(input.value(), "first second, third."); assert_eq!(input.cursor(), 2); input.handle(req); assert_eq!(input.value(), "first second, third."); assert_eq!(input.cursor(), 1); } #[test] fn remove_unicode_chars() { let mut input: Input = "¡test¡".into(); let req = InputRequest::DeletePrevChar; let resp = input.handle(req); assert_eq!( resp, Some(StateChanged { value: true, cursor: true, }) ); assert_eq!(input.value(), "¡test"); assert_eq!(input.cursor(), 5); input.handle(InputRequest::GoToStart); let req = InputRequest::DeleteNextChar; let resp = input.handle(req); assert_eq!( resp, Some(StateChanged { value: true, cursor: false, }) ); assert_eq!(input.value(), "test"); assert_eq!(input.cursor(), 0); } #[test] fn insert_unicode_chars() { let mut input = Input::from("¡test¡").with_cursor(5); let req = InputRequest::InsertChar('☆'); let resp = input.handle(req); assert_eq!( resp, Some(StateChanged { value: true, cursor: true, }) ); assert_eq!(input.value(), "¡test☆¡"); assert_eq!(input.cursor(), 6); input.handle(InputRequest::GoToStart); input.handle(InputRequest::GoToNextChar); let req = InputRequest::InsertChar('☆'); let resp = input.handle(req); assert_eq!( resp, Some(StateChanged { value: true, cursor: true, }) ); assert_eq!(input.value(), "¡☆test☆¡"); assert_eq!(input.cursor(), 2); } #[test] fn multispace_characters() { let input: Input = "Hello, world!".into(); assert_eq!(input.cursor(), 13); assert_eq!(input.visual_cursor(), 23); assert_eq!(input.visual_scroll(6), 18); } } tui-input-0.10.1/src/lib.rs000064400000000000000000000012371046102023000135470ustar 00000000000000//! TUI input library supporting multiple backends. //! //! # Example: Without any backend //! //! ``` //! use tui_input::{Input, InputRequest, StateChanged}; //! //! let mut input: Input = "Hello Worl".into(); //! //! let req = InputRequest::InsertChar('d'); //! let resp = input.handle(req); //! //! assert_eq!(resp, Some(StateChanged { value: true, cursor: true })); //! assert_eq!(input.cursor(), 11); //! assert_eq!(input.to_string(), "Hello World"); //! ``` //! //! See other examples in the [GitHub repo](https://github.com/sayanarijit/tui-input/tree/main/examples). mod input; pub mod backend; pub use input::{Input, InputRequest, InputResponse, StateChanged};