├── .github └── workflows │ ├── mdbook.yml │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── publish.cmd ├── rat-cursor ├── Cargo.toml ├── changes.md ├── readme.md └── src │ └── lib.rs ├── rat-dialog ├── Cargo.toml ├── changes.md ├── examples │ └── turbo2.rs ├── readme.md └── src │ ├── lib.rs │ └── widgets │ ├── file_dialog.rs │ ├── mod.rs │ └── msg_dialog.rs ├── rat-event ├── Cargo.toml ├── changes.md ├── readme.md ├── src │ ├── crossterm.rs │ ├── lib.rs │ └── util.rs └── tests │ └── test_util.rs ├── rat-focus ├── Cargo.toml ├── changes.md ├── examples │ ├── adapter │ │ ├── mod.rs │ │ └── textinputf.rs │ ├── focus_input1.rs │ ├── focus_recursive.rs │ ├── focus_recursive2.rs │ └── mini_salsa │ │ ├── mod.rs │ │ ├── text_input_mock.rs │ │ └── theme.rs ├── readme.md ├── src │ ├── focus.rs │ └── lib.rs └── todo.md ├── rat-ftable ├── Cargo.toml ├── changes.md ├── examples │ ├── data │ │ ├── mod.rs │ │ └── render_tablestate.rs │ ├── mini_salsa │ │ ├── mod.rs │ │ ├── text_input_mock.rs │ │ └── theme.rs │ ├── table_0_height.rs │ ├── table_cell_selection.rs │ ├── table_empty.rs │ ├── table_insane_offset.rs │ ├── table_iter.rs │ ├── table_iter_endless.rs │ ├── table_iter_ref.rs │ ├── table_layoutwidth.rs │ ├── table_row_selection.rs │ ├── table_rowrange_selection.rs │ ├── table_slice.rs │ └── table_text.rs ├── ftable.gif ├── readme.md ├── src │ ├── cellselection.rs │ ├── edit.rs │ ├── edit │ │ ├── table.rs │ │ └── vec.rs │ ├── lib.rs │ ├── noselection.rs │ ├── rowselection.rs │ ├── rowsetselection.rs │ ├── table.rs │ ├── textdata.rs │ └── util.rs └── tests │ └── test1.rs ├── rat-markdown ├── Cargo.toml ├── changes.md ├── readme.md └── src │ ├── dump.rs │ ├── format.rs │ ├── lib.rs │ ├── operations.rs │ ├── parser.rs │ ├── styles.rs │ └── util.rs ├── rat-menu ├── Cargo.toml ├── changes.md ├── readme.md └── src │ ├── lib.rs │ ├── menubar.rs │ ├── menuitem.rs │ ├── menuline.rs │ ├── popup_menu.rs │ └── util.rs ├── rat-popup ├── Cargo.toml ├── changes.md ├── examples │ ├── adapter │ │ ├── blue.rs │ │ └── mod.rs │ ├── mini_salsa │ │ ├── mod.rs │ │ ├── text_input_mock.rs │ │ └── theme.rs │ ├── popup1.rs │ └── variants │ │ ├── mod.rs │ │ ├── popup_edit.rs │ │ ├── popup_focus.rs │ │ ├── popup_lock_edit.rs │ │ └── popup_nonfocus.rs ├── readme.md └── src │ ├── lib.rs │ └── popup.rs ├── rat-reloc ├── Cargo.toml ├── changes.md ├── readme.md └── src │ └── lib.rs ├── rat-salsa ├── .github │ └── workflows │ │ ├── mdbook.yml │ │ ├── mdbook22.yml │ │ └── rust.yml ├── Cargo.toml ├── changes.md ├── examples │ ├── async1.rs │ ├── block.life │ ├── cheat.md │ ├── files.rs │ ├── life.life │ ├── life.rs │ ├── mdedit.md │ ├── minimal.rs │ ├── queen_bee_shuttle.life │ ├── rat.life │ ├── theme_sample.rs │ ├── trap.life │ ├── tumbler.life │ ├── turbo.rs │ └── ultra.rs ├── files.gif ├── mdedit.gif ├── readme.md ├── rsbook │ ├── book.toml │ └── src │ │ ├── SUMMARY.md │ │ ├── appctx.md │ │ ├── chapter_1.md │ │ ├── concepts.md │ │ ├── events.md │ │ ├── events_control_flow.md │ │ ├── events_control_flow2.md │ │ ├── events_match.md │ │ ├── events_widget.md │ │ ├── events_widget2.md │ │ ├── examples.md │ │ ├── focus.md │ │ ├── focus_builder.md │ │ ├── focus_container.md │ │ ├── focus_deeper.md │ │ ├── focus_widget.md │ │ ├── intro.md │ │ ├── minimal.md │ │ ├── render_overlay.md │ │ ├── widget-extensions.md │ │ └── widgets.md └── src │ ├── framework.rs │ ├── framework │ ├── control_queue.rs │ └── poll_queue.rs │ ├── lib.rs │ ├── poll │ ├── crossterm.rs │ ├── rendered.rs │ ├── thread_pool.rs │ ├── timer.rs │ └── tokio_tasks.rs │ ├── poll_events.rs │ ├── rendered.rs │ ├── run_config.rs │ ├── terminal │ └── mod.rs │ ├── thread_pool │ └── mod.rs │ ├── timer │ └── mod.rs │ └── tokio_tasks │ └── mod.rs ├── rat-scrolled ├── Cargo.toml ├── changes.md ├── examples │ ├── adapter │ │ ├── list.rs │ │ ├── mod.rs │ │ ├── paragraph.rs │ │ ├── table.rs │ │ └── tree.rs │ ├── mini_salsa │ │ ├── mod.rs │ │ └── theme.rs │ ├── slist.rs │ ├── sparagraph.rs │ ├── stable.rs │ └── stree.rs ├── readme.md └── src │ ├── lib.rs │ ├── scroll.rs │ └── scroll_area.rs ├── rat-text ├── Cargo.toml ├── changes.md ├── examples │ ├── mini_salsa │ │ ├── mod.rs │ │ ├── text_input_mock.rs │ │ └── theme.rs │ ├── textarea1.rs │ ├── textarea2.rs │ ├── textinput1.rs │ └── textmask1.rs ├── readme.md ├── src │ ├── clipboard.rs │ ├── date_input.rs │ ├── grapheme.rs │ ├── lib.rs │ ├── line_number.rs │ ├── number_input.rs │ ├── range_map.rs │ ├── text_area.rs │ ├── text_core.rs │ ├── text_input.rs │ ├── text_input_mask.rs │ ├── text_mask_core.rs │ ├── text_store.rs │ └── undo_buffer.rs └── tests │ ├── test_core.rs │ ├── test_masked.rs │ ├── test_textrope.rs │ └── test_textstring.rs ├── rat-theme ├── Cargo.toml ├── changes.md ├── readme.md ├── src │ ├── base16.rs │ ├── base16r.rs │ ├── dark_theme.rs │ ├── imperial.rs │ ├── lib.rs │ ├── monekai.rs │ ├── monochrome.rs │ ├── ocean.rs │ ├── oxocarbon.rs │ ├── radium.rs │ ├── tundra.rs │ └── vscode_dark.rs └── svg │ ├── Theme.svg │ ├── base16.svg │ ├── monekai.svg │ ├── monochrome.svg │ ├── ocean.svg │ ├── oxocarbon.svg │ └── vscode_dark.svg ├── rat-theme2 ├── Cargo.toml ├── changes.md ├── readme.md ├── src │ ├── base16.rs │ ├── base16r.rs │ ├── blackwhite.rs │ ├── dark_theme.rs │ ├── imperial.rs │ ├── lib.rs │ ├── monekai.rs │ ├── monochrome.rs │ ├── ocean.rs │ ├── oxocarbon.rs │ ├── radium.rs │ ├── scheme.rs │ ├── tundra.rs │ └── vscode_dark.rs └── svg │ ├── Theme.svg │ ├── base16.svg │ ├── monekai.svg │ ├── monochrome.svg │ ├── ocean.svg │ ├── oxocarbon.svg │ └── vscode_dark.svg ├── rat-widget ├── Cargo.toml ├── changes.md ├── examples │ ├── button1.rs │ ├── calendar1.rs │ ├── calendar3.rs │ ├── checkbox1.rs │ ├── choice1.rs │ ├── clipper1.rs │ ├── dateinput1.rs │ ├── doubleclick.rs │ ├── file_dialog1.rs │ ├── form1.rs │ ├── keybinding.rs │ ├── menu_status1.rs │ ├── menubar1.rs │ ├── mini_salsa │ │ ├── endless_scroll.rs │ │ ├── mod.rs │ │ ├── text_input_mock.rs │ │ └── theme.rs │ ├── pager1.rs │ ├── pager2.rs │ ├── pager3.rs │ ├── pager4.rs │ ├── paragraph1.rs │ ├── popup_menu1.rs │ ├── radio1.rs │ ├── reset.rs │ ├── rlist1.rs │ ├── slider1.rs │ ├── split1.rs │ ├── tabbed1.rs │ ├── table_edit1.rs │ ├── table_edit2.rs │ └── view1.rs ├── readme.md ├── src │ ├── button.rs │ ├── calendar.rs │ ├── calendar │ │ ├── calendar.rs │ │ ├── calendar3.rs │ │ ├── event.rs │ │ ├── month.rs │ │ ├── no_selection.rs │ │ ├── range_selection.rs │ │ ├── single_selection.rs │ │ └── style.rs │ ├── checkbox.rs │ ├── choice.rs │ ├── clipper │ │ ├── clipper.rs │ │ ├── clipper_style.rs │ │ └── mod.rs │ ├── file_dialog.rs │ ├── hover.rs │ ├── layout │ │ ├── generic_layout.rs │ │ ├── layout_dialog.rs │ │ ├── layout_edit.rs │ │ ├── layout_form.rs │ │ ├── layout_grid.rs │ │ ├── layout_middle.rs │ │ └── mod.rs │ ├── lib.rs │ ├── list.rs │ ├── list │ │ └── edit.rs │ ├── msgdialog.rs │ ├── pager │ │ ├── dual_pager.rs │ │ ├── form.rs │ │ ├── mod.rs │ │ ├── pager.rs │ │ ├── pager_nav.rs │ │ ├── pager_style.rs │ │ └── single_pager.rs │ ├── paired.rs │ ├── paragraph.rs │ ├── radio.rs │ ├── range_op.rs │ ├── shadow.rs │ ├── slider.rs │ ├── splitter.rs │ ├── statusline.rs │ ├── tabbed.rs │ ├── tabbed │ │ ├── attached.rs │ │ └── glued.rs │ ├── util.rs │ └── view.rs ├── tests │ ├── test_layout_form.rs │ ├── test_splitter.rs │ └── test_splitter2.rs └── todo.md ├── readme.md └── rsbook ├── .gitignore ├── book.toml └── src ├── SUMMARY.md ├── chapter_1.md ├── guidelines.md ├── introduction.md ├── rat-salsa.md ├── rat-salsa ├── appcontext.md ├── appstate.md ├── appwidget.md ├── control.md ├── event-loop.md ├── poll_event.md ├── rendercontext.md └── run_tui.md └── walkthrough.md /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a mdBook site to GitHub Pages 2 | # 3 | # To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html 4 | # 5 | name: Deploy mdBook site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: [ "master" ] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 16 | permissions: 17 | contents: read 18 | pages: write 19 | id-token: write 20 | 21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: false 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | env: 32 | MDBOOK_VERSION: 0.4.36 33 | steps: 34 | - uses: actions/checkout@v4 35 | - name: Install mdBook 36 | run: | 37 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh 38 | rustup update 39 | cargo install --version ${MDBOOK_VERSION} mdbook 40 | - name: Setup Pages 41 | id: pages 42 | uses: actions/configure-pages@v5 43 | - name: Build with mdBook 44 | run: mdbook build rat-salsa/rsbook 45 | - name: Upload artifact 46 | uses: actions/upload-pages-artifact@v3 47 | with: 48 | path: ./rat-salsa/rsbook/book 49 | 50 | # Deployment job 51 | deploy: 52 | environment: 53 | name: github-pages 54 | url: ${{ steps.deployment.outputs.page_url }} 55 | runs-on: ubuntu-latest 56 | needs: build 57 | steps: 58 | - name: Deploy to GitHub Pages 59 | id: deployment 60 | uses: actions/deploy-pages@v4 61 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build 21 | - name: Run tests 22 | run: cargo test 23 | - name: Run test examples 24 | run: cargo build --features async --examples 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /docs 3 | /Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "rat-cursor", 6 | "rat-event", 7 | "rat-focus", 8 | "rat-ftable", 9 | "rat-menu", 10 | "rat-markdown", 11 | "rat-popup", 12 | "rat-reloc", 13 | "rat-salsa", 14 | "rat-scrolled", 15 | "rat-text", 16 | "rat-theme", 17 | "rat-theme2", 18 | "rat-widget", 19 | "rat-dialog" 20 | ] 21 | -------------------------------------------------------------------------------- /publish.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cargo publish -p rat-cursor 4 | cargo publish -p rat-event 5 | cargo publish -p rat-reloc 6 | cargo publish -p rat-focus 7 | cargo publish -p rat-scrolled 8 | 9 | cargo publish -p rat-popup 10 | 11 | cargo publish -p rat-menu 12 | cargo publish -p rat-text 13 | cargo publish -p rat-markdown 14 | cargo publish -p rat-ftable 15 | cargo publish -p rat-widget 16 | 17 | cargo publish -p rat-theme 18 | cargo publish -p rat-theme2 19 | 20 | cargo publish -p rat-salsa 21 | cargo publish -p rat-dialog 22 | 23 | -------------------------------------------------------------------------------- /rat-cursor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rat-cursor" 3 | version = "1.2.0" 4 | edition = "2021" 5 | description = "ratatui trait to communicate the cursor position across widgets" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/thscharler/rat-salsa" 8 | readme = "readme.md" 9 | keywords = ["ratatui", "input", "cursor"] 10 | categories = ["command-line-interface"] 11 | exclude = [".idea/*", ".gitignore"] 12 | 13 | [dependencies] 14 | -------------------------------------------------------------------------------- /rat-cursor/changes.md: -------------------------------------------------------------------------------- 1 | # 1.2.0 2 | 3 | * add impl_screen_cursor! for quick impl 4 | 5 | # 1.1.1 6 | 7 | * moved all rat-crates to one repo 8 | 9 | # 1.1.0 10 | 11 | * feature: add screen_cursor() which acts on an array to evaluate the screencursor. 12 | 13 | # 1.0.0 14 | 15 | stabilization 16 | 17 | # 0.25.1 18 | 19 | * docs 20 | 21 | # 0.25.0 22 | 23 | Sync version for beta. 24 | 25 | # 0.1.0 26 | 27 | Copy the design from rat-text as this is not the only place 28 | where this concept is needed. A separate tiny crate avoids 29 | criss-cross dependencies on crates otherwise not needed. 30 | -------------------------------------------------------------------------------- /rat-cursor/readme.md: -------------------------------------------------------------------------------- 1 | ![semver](https://img.shields.io/badge/semver-☑-FFD700) 2 | ![stable](https://img.shields.io/badge/stability-stable-8A2BE2) 3 | [![crates.io](https://img.shields.io/crates/v/rat-cursor.svg)](https://crates.io/crates/rat-cursor) 4 | [![Documentation](https://docs.rs/rat-cursor/badge.svg)](https://docs.rs/rat-cursor) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 6 | [![License](https://img.shields.io/badge/license-APACHE-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) 7 | ![](https://tokei.rs/b1/github/thscharler/rat-salsa) 8 | 9 | This crate is a part of [rat-salsa][refRatSalsa]. 10 | 11 | * [Changes](https://github.com/thscharler/rat-salsa/blob/master/rat-cursor/changes.md) 12 | 13 | # Rat-Cursor 14 | 15 | ## Why? 16 | 17 | This crate defines just the trait [HasScreenCursor](HasScreenCursor) for use in 18 | other crates. This aims to overcome the shortcomings of ratatui 19 | to handle cursor positioning by widgets. 20 | 21 | > In the long run I hope there will be a solution within ratatui 22 | > which will make this obsolete, but for now ... 23 | 24 | ```rust 25 | pub trait HasScreenCursor { 26 | fn screen_cursor(&self) -> Option<(u16, u16)>; 27 | } 28 | ``` 29 | 30 | ## Use 31 | 32 | ### Widget 33 | 34 | This trait is implemented for the widget-state struct. 35 | 36 | > It's implemented for the state struct because the widget 37 | > might need to run the full layout process to know the cursor 38 | > position. Which would approximately double the rendering 39 | > process. 40 | 41 | Instead of setting the cursor position during rendering somehow, 42 | the rendering process stores the cursor position in the state 43 | struct, where it can be retrieved later on. 44 | 45 | The trait returns a screen-position, but only if it actually 46 | needs the cursor to be displayed: 47 | 48 | * The cursor is not scrolled off-screen. 49 | * The widget has some kind of input-focus. 50 | 51 | ### Container widget 52 | 53 | A container widget can cascade down to its components. 54 | 55 | ```rust ignore 56 | fn screen_cursor(&self) -> Option<(u16, u16)> { 57 | self.widget1.screen_cursor() 58 | .or(self.widget2.screen_cursor()) 59 | .or(self.widget3.screen_cursor()) 60 | } 61 | ``` 62 | 63 | [refRatSalsa]: https://docs.rs/rat-salsa/latest/rat_salsa/ 64 | 65 | -------------------------------------------------------------------------------- /rat-cursor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../readme.md")] 2 | 3 | /// Trait for accessing the screen-cursor. 4 | /// 5 | /// In ratatui the screen-cursor can't be set during rendering, instead 6 | /// it must be set with the Frame at some point. 7 | /// 8 | /// This trait provides a method to get the screen cursor (if any) 9 | /// for a widget. 10 | pub trait HasScreenCursor { 11 | /// This returns the cursor position if 12 | /// - The cursor is displayed at all, and not scrolled off-screen. 13 | /// - The widget has some kind of input focus 14 | /// - other reasons 15 | fn screen_cursor(&self) -> Option<(u16, u16)>; 16 | } 17 | 18 | /// Returns the screen_cursor for the first widget that returns one. 19 | #[inline(always)] 20 | pub fn screen_cursor(list: [&dyn HasScreenCursor; N]) -> Option<(u16, u16)> { 21 | for v in list { 22 | if let Some(v) = v.screen_cursor() { 23 | return Some(v); 24 | } 25 | } 26 | None 27 | } 28 | 29 | /// Create the implementation of HasScreenCursor for the 30 | /// given list of struct members. 31 | #[macro_export] 32 | macro_rules! impl_screen_cursor { 33 | ($($n:ident),* for $ty:ty) => { 34 | impl $crate::HasScreenCursor for $ty { 35 | fn screen_cursor(&self) -> Option<(u16, u16)> { 36 | use $crate::screen_cursor; 37 | screen_cursor([ 38 | $(&self.$n),* 39 | ]) 40 | } 41 | } 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /rat-dialog/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rat-dialog" 3 | version = "0.2.0" 4 | authors = ["thscharler "] 5 | edition = "2021" 6 | description = "stacked dialog windows" 7 | license = "MIT/Apache-2.0" 8 | repository = "https://github.com/thscharler/rat-salsa" 9 | readme = "readme.md" 10 | keywords = ["ratatui", "dialog", "window"] 11 | categories = ["command-line-interface"] 12 | exclude = [".idea/*", ".gitignore"] 13 | 14 | [features] 15 | default = [] 16 | 17 | [dependencies] 18 | crossterm = "0.28" 19 | log = "0.4" 20 | ratatui = { version = "0.29" } 21 | 22 | rat-widget = { version = "1.0", path = "../rat-widget" } 23 | rat-salsa = { version = "1.0", path = "../rat-salsa" } 24 | 25 | [dev-dependencies] 26 | anyhow = "1.0" 27 | fern = "0.7" 28 | dirs = "6.0" 29 | 30 | rat-theme2 = { version = "0.28", path = "../rat-theme2" } 31 | 32 | -------------------------------------------------------------------------------- /rat-dialog/changes.md: -------------------------------------------------------------------------------- 1 | # 0.2.0 2 | 3 | * use a render Fn instead of creating and returning an AppWidget. 4 | * intro DialogWidget and DialogState and detach them from 5 | AppWidget/AppState. They have different bounds and somewhat diverging 6 | functionality. And less impls necessary. 7 | 8 | # 0.1.0 9 | 10 | Initial release. 11 | -------------------------------------------------------------------------------- /rat-dialog/src/widgets/mod.rs: -------------------------------------------------------------------------------- 1 | mod file_dialog; 2 | mod msg_dialog; 3 | 4 | pub use file_dialog::*; 5 | pub use msg_dialog::*; 6 | -------------------------------------------------------------------------------- /rat-event/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rat-event" 3 | version = "1.2.3" 4 | edition = "2021" 5 | description = "ratatui event handler trait for widgets" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/thscharler/rat-salsa" 8 | readme = "readme.md" 9 | keywords = ["ratatui", "input", "event"] 10 | categories = ["command-line-interface"] 11 | exclude = [".idea/*", ".gitignore"] 12 | 13 | [dependencies] 14 | crossterm = "0.28" 15 | ratatui = { version = "0.29" } 16 | log = "0.4" -------------------------------------------------------------------------------- /rat-event/readme.md: -------------------------------------------------------------------------------- 1 | ![semver](https://img.shields.io/badge/semver-☑-FFD700) 2 | ![stable](https://img.shields.io/badge/stability-stable-8A2BE2) 3 | [![crates.io](https://img.shields.io/crates/v/rat-event.svg)](https://crates.io/crates/rat-event) 4 | [![Documentation](https://docs.rs/rat-event/badge.svg)](https://docs.rs/rat-event) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 6 | [![License](https://img.shields.io/badge/license-APACHE-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) 7 | ![](https://tokei.rs/b1/github/thscharler/rat-salsa) 8 | 9 | This crate is a part of [rat-salsa][refRatSalsa]. 10 | 11 | * [Changes](https://github.com/thscharler/rat-salsa/blob/master/rat-event/changes.md) 12 | 13 | # Rat-Event 14 | 15 | ## Why? 16 | 17 | This crate defines the trait [HandleEvent](HandleEvent) to help with 18 | composability of event-handling for ratatui widgets. 19 | 20 | Objectives are 21 | 22 | - work for all event-types. 23 | - allow for multiple handlers per widget 24 | - to override the key-bindings 25 | - to have different key-bindings for certain scenarios. 26 | - have a return type to indicate what state change occured. 27 | 28 | ```rust ignore 29 | pub trait HandleEvent 30 | where 31 | Return: ConsumedEvent 32 | { 33 | fn handle( 34 | &mut self, 35 | event: &Event, 36 | qualifier: Qualifier 37 | ) -> Return; 38 | } 39 | ``` 40 | 41 | ## Event 42 | 43 | Can be anything. 44 | 45 | ## Qualifier 46 | 47 | There are predefined qualifiers 48 | 49 | * [Regular](Regular) - Do what is considered 'normal' behaviour. 50 | Can vary depending on the actual state of the widget 51 | (e.g. focus) 52 | 53 | * [MouseOnly](MouseOnly) - Splitting off mouse interaction helps when 54 | you only want to redefine the key bindings. And handling 55 | mouse events is usually more involved/complicated/specific. 56 | 57 | * [DoubleClick](DoubleClick) - Double clicks are a bit special for widgets, 58 | often it requires a distinct return type and it's not 59 | as generally needed as other mouse behaviour. 60 | 61 | * [Popup](Popup) - Popup event-handlers are regular event-handlers, 62 | but they need processing before regular event-handlers. 63 | This is used for widgets that render popups above other widgets, 64 | and must make sure that event-handling for the popup doesn't 65 | interfere with widgets below the popup. By ensuring the order 66 | of event-handling most of the problems can be solved. 67 | 68 | * [Dialog](Dialog) - Specialized event-handler for dialog-like 69 | popups. They want to be called first to be able to consume 70 | **all** events, thus blocking everything else. 71 | 72 | ## Return 73 | 74 | The return type can be anything too. 75 | 76 | To be useful it is required to implement 77 | [ConsumedEvent](ConsumedEvent) to indicate if the event has been 78 | handled by the widget and further event-handling can stop. 79 | 80 | To set a baseline for the return type this crate defines the enum 81 | [Outcome](Outcome) which can indicate if a render is necessary or not. 82 | 83 | > For interop all return types in rat-salsa are convertible 84 | > to/from Outcome. 85 | 86 | > There is one constraint for the return type: It must implement 87 | > Consumed to indicate the fundamental property of an event being 88 | > consumed by a widget. This lib has some control-flow constructs 89 | > that use this property. 90 | 91 | [refRatSalsa]: https://docs.rs/rat-salsa/latest/rat_salsa/ 92 | -------------------------------------------------------------------------------- /rat-focus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rat-focus" 3 | version = "1.0.1" 4 | edition = "2021" 5 | authors = ["thscharler "] 6 | description = "focus handling for ratatui widgets" 7 | license = "MIT/Apache-2.0" 8 | repository = "https://github.com/thscharler/rat-salsa" 9 | readme = "readme.md" 10 | keywords = ["ratatui", "focus"] 11 | categories = ["command-line-interface"] 12 | exclude = [".idea/*", ".gitignore"] 13 | 14 | [dependencies] 15 | ratatui = { version = "0.29" } 16 | crossterm = "0.28" 17 | log = "0.4" 18 | fxhash = "0.2" 19 | 20 | rat-event = { version = "1.0", path = "../rat-event" } 21 | rat-reloc = { version = "1.0", path = "../rat-reloc" } 22 | 23 | [dev-dependencies] 24 | fern = "0.7" 25 | anyhow = "1.0" 26 | unicode-segmentation = "1.11" 27 | rat-cursor = { version = "1.1", path = "../rat-cursor" } -------------------------------------------------------------------------------- /rat-focus/examples/adapter/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(unreachable_pub)] 2 | 3 | pub mod textinputf; 4 | 5 | mod _private { 6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 7 | pub struct NonExhaustive; 8 | } 9 | -------------------------------------------------------------------------------- /rat-focus/examples/adapter/textinputf.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use crate::adapter::_private::NonExhaustive; 4 | use rat_event::{HandleEvent, MouseOnly, Outcome, Regular}; 5 | use rat_focus::{FocusBuilder, FocusFlag, HasFocus}; 6 | use ratatui::buffer::Buffer; 7 | use ratatui::layout::Rect; 8 | use ratatui::style::Style; 9 | use ratatui::text::Line; 10 | use ratatui::widgets::{StatefulWidget, Widget}; 11 | use std::marker::PhantomData; 12 | 13 | #[derive(Debug, Default)] 14 | pub struct TextInputF<'a> { 15 | style: Style, 16 | focus_style: Style, 17 | phantom_data: PhantomData<&'a ()>, 18 | } 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct TextInputFState { 22 | pub focus: FocusFlag, 23 | pub area: Rect, 24 | 25 | pub non_exhaustive: NonExhaustive, 26 | } 27 | 28 | impl<'a> TextInputF<'a> { 29 | /// Base text style. 30 | pub fn style(mut self, style: impl Into