├── .gitignore ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── examples ├── files.rs └── basic.rs ├── LICENSE-APACHE └── src ├── lib.rs └── tree_list └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cursive_tree_view" 3 | edition = "2018" 4 | version = "0.9.0" 5 | authors = ["Ivo Wetzel ", "Alexandre Bury "] 6 | description = "A tree view implementation for cursive." 7 | repository = "https://github.com/BonsaiDen/cursive_tree_view.git" 8 | readme = "README.md" 9 | keywords = ["cursive", "ncurses", "TUI", "UI", "tree"] 10 | categories = ["command-line-interface", "gui"] 11 | license = "MIT/Apache-2.0" 12 | 13 | [dependencies] 14 | debug_stub_derive = "0.3.0" 15 | cursive_core = "0.4" 16 | 17 | [dev-dependencies] 18 | cursive = "0.21" 19 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2017 Ivo Wetzel 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cursive-tree-view 2 | 3 | [![cursive-tree-view on crates.io][cratesio-image]][cratesio] 4 | [![cursive-tree-view on docs.rs][docsrs-image]][docsrs] 5 | 6 | [cratesio-image]: https://img.shields.io/crates/v/cursive_tree_view.svg 7 | [cratesio]: https://crates.io/crates/cursive_tree_view 8 | [docsrs-image]: https://docs.rs/cursive_tree_view/badge.svg 9 | [docsrs]: https://docs.rs/cursive_tree_view/ 10 | 11 | A basic tree view implementation for [cursive](https://crates.io/crates/cursive). 12 | 13 | ![Picture of File Viewer Example](https://cloud.githubusercontent.com/assets/124674/25919091/ddd9ac46-35cd-11e7-976a-e461e9b153f0.png) 14 | 15 | ## Usage 16 | 17 | Add this to your `Cargo.toml`: 18 | 19 | ```toml 20 | [dependencies] 21 | cursive_tree_view = "0.8" 22 | ``` 23 | 24 | and this to your crate root: 25 | 26 | ```rust 27 | extern crate cursive_tree_view; 28 | ``` 29 | 30 | ## License 31 | 32 | Licensed under either of 33 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 34 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 35 | at your option. 36 | 37 | 38 | ### Contribution 39 | 40 | Unless you explicitly state otherwise, any contribution intentionally submitted 41 | for inclusion in the work by you shall be dual licensed as above, without any 42 | additional terms or conditions. 43 | 44 | -------------------------------------------------------------------------------- /examples/files.rs: -------------------------------------------------------------------------------- 1 | // Crate Dependencies --------------------------------------------------------- 2 | use cursive; 3 | 4 | 5 | // STD Dependencies ----------------------------------------------------------- 6 | use std::cmp::Ordering; 7 | use std::env; 8 | use std::fmt; 9 | use std::fs; 10 | use std::io; 11 | use std::path::PathBuf; 12 | 13 | // External Dependencies ------------------------------------------------------ 14 | use cursive::traits::*; 15 | use cursive::views::Dialog; 16 | use cursive::Cursive; 17 | 18 | // Modules -------------------------------------------------------------------- 19 | use cursive_tree_view::{Placement, TreeView}; 20 | 21 | // Example -------------------------------------------------------------------- 22 | fn main() { 23 | #[derive(Debug)] 24 | struct TreeEntry { 25 | name: String, 26 | dir: Option, 27 | } 28 | 29 | impl fmt::Display for TreeEntry { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | write!(f, "{}", self.name) 32 | } 33 | } 34 | 35 | fn collect_entries(dir: &PathBuf, entries: &mut Vec) -> io::Result<()> { 36 | if dir.is_dir() { 37 | for entry in fs::read_dir(dir)? { 38 | let entry = entry?; 39 | let path = entry.path(); 40 | 41 | if path.is_dir() { 42 | entries.push(TreeEntry { 43 | name: entry 44 | .file_name() 45 | .into_string() 46 | .unwrap_or_else(|_| "".to_string()), 47 | dir: Some(path), 48 | }); 49 | } else if path.is_file() { 50 | entries.push(TreeEntry { 51 | name: entry 52 | .file_name() 53 | .into_string() 54 | .unwrap_or_else(|_| "".to_string()), 55 | dir: None, 56 | }); 57 | } 58 | } 59 | } 60 | Ok(()) 61 | } 62 | 63 | fn expand_tree(tree: &mut TreeView, parent_row: usize, dir: &PathBuf) { 64 | let mut entries = Vec::new(); 65 | collect_entries(dir, &mut entries).ok(); 66 | 67 | entries.sort_by(|a, b| match (a.dir.is_some(), b.dir.is_some()) { 68 | (true, true) | (false, false) => a.name.cmp(&b.name), 69 | (true, false) => Ordering::Less, 70 | (false, true) => Ordering::Greater, 71 | }); 72 | 73 | for i in entries { 74 | if i.dir.is_some() { 75 | tree.insert_container_item(i, Placement::LastChild, parent_row); 76 | } else { 77 | tree.insert_item(i, Placement::LastChild, parent_row); 78 | } 79 | } 80 | } 81 | 82 | // Create TreeView with initial working directory 83 | let mut tree = TreeView::::new(); 84 | let path = env::current_dir().expect("Working directory missing."); 85 | 86 | tree.insert_item( 87 | TreeEntry { 88 | name: path.file_name().unwrap().to_str().unwrap().to_string(), 89 | dir: Some(path.clone()), 90 | }, 91 | Placement::After, 92 | 0, 93 | ); 94 | 95 | expand_tree(&mut tree, 0, &path); 96 | 97 | // Lazily insert directory listings for sub nodes 98 | tree.set_on_collapse(|siv: &mut Cursive, row, is_collapsed, children| { 99 | if !is_collapsed && children == 0 { 100 | siv.call_on_name("tree", move |tree: &mut TreeView| { 101 | if let Some(dir) = tree.borrow_item(row).unwrap().dir.clone() { 102 | expand_tree(tree, row, &dir); 103 | } 104 | }); 105 | } 106 | }); 107 | 108 | // Setup Cursive 109 | let mut siv = cursive::default(); 110 | siv.add_layer(Dialog::around(tree.with_name("tree").scrollable()).title("File View")); 111 | 112 | siv.run(); 113 | } 114 | -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | // Crate Dependencies --------------------------------------------------------- 2 | use cursive; 3 | 4 | 5 | // External Dependencies ------------------------------------------------------ 6 | use cursive::direction::Orientation; 7 | use cursive::traits::*; 8 | use cursive::views::{Dialog, DummyView, LinearLayout, Panel, ResizedView, TextView}; 9 | use cursive::Cursive; 10 | 11 | // Modules -------------------------------------------------------------------- 12 | use cursive_tree_view::{Placement, TreeView}; 13 | 14 | // Example -------------------------------------------------------------------- 15 | fn main() { 16 | let mut siv = cursive::default(); 17 | 18 | // Tree ------------------------------------------------------------------- 19 | let mut tree = TreeView::new(); 20 | tree.insert_item("tree_view".to_string(), Placement::LastChild, 0); 21 | 22 | tree.insert_item("src".to_string(), Placement::LastChild, 0); 23 | tree.insert_item("tree_list".to_string(), Placement::LastChild, 1); 24 | tree.insert_item("mod.rs".to_string(), Placement::LastChild, 2); 25 | 26 | tree.insert_item("2b".to_string(), Placement::LastChild, 0); 27 | tree.insert_item("3b".to_string(), Placement::LastChild, 4); 28 | tree.insert_item("4b".to_string(), Placement::LastChild, 5); 29 | 30 | tree.insert_item("yet".to_string(), Placement::After, 0); 31 | tree.insert_item("another".to_string(), Placement::After, 0); 32 | tree.insert_item("tree".to_string(), Placement::After, 0); 33 | tree.insert_item("view".to_string(), Placement::After, 0); 34 | tree.insert_item("item".to_string(), Placement::After, 0); 35 | tree.insert_item("last".to_string(), Placement::After, 0); 36 | 37 | // Callbacks -------------------------------------------------------------- 38 | tree.set_on_submit(|siv: &mut Cursive, row| { 39 | let value = siv.call_on_name("tree", move |tree: &mut TreeView| { 40 | tree.borrow_item(row).unwrap().to_string() 41 | }); 42 | 43 | siv.add_layer( 44 | Dialog::around(TextView::new(value.unwrap())) 45 | .title("Item submitted") 46 | .button("Close", |s| { 47 | s.pop_layer(); 48 | }), 49 | ); 50 | 51 | set_status(siv, row, "Submitted"); 52 | }); 53 | 54 | tree.set_on_select(|siv: &mut Cursive, row| { 55 | set_status(siv, row, "Selected"); 56 | }); 57 | 58 | tree.set_on_collapse(|siv: &mut Cursive, row, collpased, _| { 59 | if collpased { 60 | set_status(siv, row, "Collpased"); 61 | } else { 62 | set_status(siv, row, "Unfolded"); 63 | } 64 | }); 65 | 66 | // Controls --------------------------------------------------------------- 67 | fn insert_row(s: &mut Cursive, text: &str, placement: Placement) { 68 | let row = s.call_on_name("tree", move |tree: &mut TreeView| { 69 | let row = tree.row().unwrap_or(0); 70 | tree.insert_item(text.to_string(), placement, row) 71 | .unwrap_or(0) 72 | }); 73 | set_status(s, row.unwrap(), "Row inserted"); 74 | } 75 | 76 | siv.add_global_callback('b', |s| insert_row(s, "Before", Placement::Before)); 77 | siv.add_global_callback('a', |s| insert_row(s, "After", Placement::After)); 78 | siv.add_global_callback('p', |s| insert_row(s, "Parent", Placement::Parent)); 79 | siv.add_global_callback('f', |s| insert_row(s, "FirstChild", Placement::FirstChild)); 80 | siv.add_global_callback('l', |s| insert_row(s, "LastChild", Placement::LastChild)); 81 | 82 | siv.add_global_callback('r', |s| { 83 | s.call_on_name("tree", move |tree: &mut TreeView| { 84 | if let Some(row) = tree.row() { 85 | tree.remove_item(row); 86 | } 87 | }); 88 | }); 89 | 90 | siv.add_global_callback('h', |s| { 91 | s.call_on_name("tree", move |tree: &mut TreeView| { 92 | if let Some(row) = tree.row() { 93 | tree.remove_children(row); 94 | } 95 | }); 96 | }); 97 | 98 | siv.add_global_callback('e', |s| { 99 | s.call_on_name("tree", move |tree: &mut TreeView| { 100 | if let Some(row) = tree.row() { 101 | tree.extract_item(row); 102 | } 103 | }); 104 | }); 105 | 106 | siv.add_global_callback('c', |s| { 107 | s.call_on_name("tree", move |tree: &mut TreeView| { 108 | tree.clear(); 109 | }); 110 | }); 111 | 112 | // UI --------------------------------------------------------------------- 113 | let mut v_split = LinearLayout::new(Orientation::Vertical); 114 | v_split.add_child( 115 | TextView::new( 116 | r#" 117 | -- Controls -- 118 | 119 | Enter - Collapse children or submit row. 120 | 121 | b - Insert before row. 122 | a - Insert after row. 123 | p - Insert parent above row. 124 | f - Insert as first child of row. 125 | l - Insert as last child of row. 126 | e - Extract row without children. 127 | r - Remove row and children. 128 | h - Remove only children. 129 | c - Clear all items. 130 | "#, 131 | ) 132 | .min_height(13), 133 | ); 134 | 135 | v_split.add_child(ResizedView::with_full_height(DummyView)); 136 | v_split.add_child(TextView::new("Last action: None").with_name("status")); 137 | 138 | let mut h_split = LinearLayout::new(Orientation::Horizontal); 139 | h_split.add_child(v_split); 140 | h_split.add_child(ResizedView::with_fixed_size((4, 0), DummyView)); 141 | h_split.add_child(Panel::new(tree.with_name("tree").scrollable())); 142 | 143 | siv.add_layer(Dialog::around(h_split).title("Tree View").max_height(20)); 144 | 145 | fn set_status(siv: &mut Cursive, row: usize, text: &str) { 146 | let value = siv.call_on_name("tree", move |tree: &mut TreeView| { 147 | tree.borrow_item(row) 148 | .map(|s| s.to_string()) 149 | .unwrap_or_else(|| "".to_string()) 150 | }); 151 | 152 | siv.call_on_name("status", move |view: &mut TextView| { 153 | view.set_content(format!( 154 | "Last action: {} row #{} \"{}\"", 155 | text, 156 | row, 157 | value.unwrap() 158 | )); 159 | }); 160 | } 161 | 162 | siv.run(); 163 | } 164 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A tree view implementation for [cursive](https://crates.io/crates/cursive). 2 | #![deny( 3 | missing_docs, 4 | trivial_casts, 5 | trivial_numeric_casts, 6 | unsafe_code, 7 | unused_import_braces, 8 | unused_qualifications 9 | )] 10 | 11 | // Crate Dependencies --------------------------------------------------------- 12 | extern crate cursive_core as cursive; 13 | #[macro_use] 14 | extern crate debug_stub_derive; 15 | 16 | // STD Dependencies ----------------------------------------------------------- 17 | use std::cmp; 18 | use std::fmt::{Debug, Display}; 19 | use std::sync::{Arc, Mutex}; 20 | 21 | // External Dependencies ------------------------------------------------------ 22 | use cursive::direction::Direction; 23 | use cursive::event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent}; 24 | use cursive::theme::ColorStyle; 25 | use cursive::vec::Vec2; 26 | use cursive::view::{CannotFocus, View}; 27 | use cursive::{Cursive, Printer}; 28 | use cursive::{Rect, With}; 29 | 30 | // Internal Dependencies ------------------------------------------------------ 31 | mod tree_list; 32 | pub use tree_list::Placement; 33 | use tree_list::TreeList; 34 | 35 | /// Callback taking an item index as input. 36 | type IndexCallback = Arc; 37 | 38 | /// Callback taking as input the row ID, the collapsed state, and the child ID. 39 | type CollapseCallback = Arc; 40 | 41 | /// A low level tree view. 42 | /// 43 | /// Each view provides a number of low level methods for manipulating its 44 | /// contained items and their structure. 45 | /// 46 | /// All interactions are performed via relative (i.e. visual) `row` indices which 47 | /// makes reasoning about behaviour much easier in the context of interactive 48 | /// user manipulation of the tree. 49 | /// 50 | /// # Examples 51 | /// 52 | /// ```rust 53 | /// # extern crate cursive; 54 | /// # extern crate cursive_tree_view; 55 | /// # use cursive_tree_view::{TreeView, Placement}; 56 | /// # fn main() { 57 | /// let mut tree = TreeView::new(); 58 | /// 59 | /// tree.insert_item("root".to_string(), Placement::LastChild, 0); 60 | /// 61 | /// tree.insert_item("1".to_string(), Placement::LastChild, 0); 62 | /// tree.insert_item("2".to_string(), Placement::LastChild, 1); 63 | /// tree.insert_item("3".to_string(), Placement::LastChild, 2); 64 | /// # } 65 | /// ``` 66 | #[derive(DebugStub)] 67 | pub struct TreeView { 68 | enabled: bool, 69 | 70 | #[debug_stub(some = "Arc, 72 | 73 | #[debug_stub(some = "Arc, 75 | 76 | #[debug_stub(some = "Arc")] 77 | on_collapse: Option, 78 | 79 | last_size: Vec2, 80 | focus: usize, 81 | list: TreeList, 82 | } 83 | 84 | /// One character for the symbol, and one for a space between the sybol and the item 85 | const SYMBOL_WIDTH: usize = 2; 86 | 87 | impl Default for TreeView { 88 | /// Creates a new, empty `TreeView`. 89 | fn default() -> Self { 90 | Self::new() 91 | } 92 | } 93 | impl TreeView { 94 | /// Creates a new, empty `TreeView`. 95 | pub fn new() -> Self { 96 | Self { 97 | enabled: true, 98 | on_submit: None, 99 | on_select: None, 100 | on_collapse: None, 101 | 102 | last_size: (0, 0).into(), 103 | focus: 0, 104 | list: TreeList::new(), 105 | } 106 | } 107 | 108 | /// Disables this view. 109 | /// 110 | /// A disabled view cannot be selected. 111 | pub fn disable(&mut self) { 112 | self.enabled = false; 113 | } 114 | 115 | /// Re-enables this view. 116 | pub fn enable(&mut self) { 117 | self.enabled = true; 118 | } 119 | 120 | /// Enable or disable this view. 121 | pub fn set_enabled(&mut self, enabled: bool) { 122 | self.enabled = enabled; 123 | } 124 | 125 | /// Returns `true` if this view is enabled. 126 | pub fn is_enabled(&self) -> bool { 127 | self.enabled 128 | } 129 | 130 | /// Sets a callback to be used when `` is pressed while an item 131 | /// is selected. 132 | /// 133 | /// # Example 134 | /// 135 | /// ```rust 136 | /// # extern crate cursive; 137 | /// # extern crate cursive_tree_view; 138 | /// # use cursive::Cursive; 139 | /// # use cursive_tree_view::TreeView; 140 | /// # fn main() { 141 | /// # let mut tree = TreeView::::new(); 142 | /// tree.set_on_submit(|siv: &mut Cursive, row: usize| { 143 | /// 144 | /// }); 145 | /// # } 146 | /// ``` 147 | pub fn set_on_submit(&mut self, cb: F) 148 | where 149 | F: Fn(&mut Cursive, usize) + Send + Sync + 'static, 150 | { 151 | self.on_submit = Some(Arc::new(move |s, row| cb(s, row))); 152 | } 153 | 154 | /// Sets a callback to be used when `` is pressed while an item 155 | /// is selected. 156 | /// 157 | /// Chainable variant. 158 | /// 159 | /// # Example 160 | /// 161 | /// ```rust 162 | /// # extern crate cursive; 163 | /// # extern crate cursive_tree_view; 164 | /// # use cursive::Cursive; 165 | /// # use cursive_tree_view::TreeView; 166 | /// # fn main() { 167 | /// # let mut tree = TreeView::::new(); 168 | /// tree.on_submit(|siv: &mut Cursive, row: usize| { 169 | /// 170 | /// }); 171 | /// # } 172 | /// ``` 173 | pub fn on_submit(self, cb: F) -> Self 174 | where 175 | F: Fn(&mut Cursive, usize) + Send + Sync + 'static, 176 | { 177 | self.with(|t| t.set_on_submit(cb)) 178 | } 179 | 180 | /// Sets a callback to be used when an item is selected. 181 | /// 182 | /// # Example 183 | /// 184 | /// ```rust 185 | /// # extern crate cursive; 186 | /// # extern crate cursive_tree_view; 187 | /// # use cursive::Cursive; 188 | /// # use cursive_tree_view::TreeView; 189 | /// # fn main() { 190 | /// # let mut tree = TreeView::::new(); 191 | /// tree.set_on_select(|siv: &mut Cursive, row: usize| { 192 | /// 193 | /// }); 194 | /// # } 195 | /// ``` 196 | pub fn set_on_select(&mut self, cb: F) 197 | where 198 | F: Fn(&mut Cursive, usize) + Send + Sync + 'static, 199 | { 200 | self.on_select = Some(Arc::new(move |s, row| cb(s, row))); 201 | } 202 | 203 | /// Sets a callback to be used when an item is selected. 204 | /// 205 | /// Chainable variant. 206 | /// 207 | /// # Example 208 | /// 209 | /// ```rust 210 | /// # extern crate cursive; 211 | /// # extern crate cursive_tree_view; 212 | /// # use cursive::Cursive; 213 | /// # use cursive_tree_view::TreeView; 214 | /// # fn main() { 215 | /// # let mut tree = TreeView::::new(); 216 | /// tree.on_select(|siv: &mut Cursive, row: usize| { 217 | /// 218 | /// }); 219 | /// # } 220 | /// ``` 221 | pub fn on_select(self, cb: F) -> Self 222 | where 223 | F: Fn(&mut Cursive, usize) + Send + Sync + 'static, 224 | { 225 | self.with(|t| t.set_on_select(cb)) 226 | } 227 | 228 | /// Sets a callback to be used when an item has its children collapsed or expanded. 229 | /// 230 | /// # Example 231 | /// 232 | /// ```rust 233 | /// # extern crate cursive; 234 | /// # extern crate cursive_tree_view; 235 | /// # use cursive::Cursive; 236 | /// # use cursive_tree_view::TreeView; 237 | /// # fn main() { 238 | /// # let mut tree = TreeView::::new(); 239 | /// tree.set_on_collapse(|siv: &mut Cursive, row: usize, is_collapsed: bool, children: usize| { 240 | /// 241 | /// }); 242 | /// # } 243 | /// ``` 244 | pub fn set_on_collapse(&mut self, cb: F) 245 | where 246 | F: Fn(&mut Cursive, usize, bool, usize) + Send + Sync + 'static, 247 | { 248 | self.on_collapse = Some(Arc::new(move |s, row, collapsed, children| { 249 | cb(s, row, collapsed, children) 250 | })); 251 | } 252 | 253 | /// Sets a callback to be used when an item has its children collapsed or expanded. 254 | /// 255 | /// Chainable variant. 256 | /// 257 | /// # Example 258 | /// 259 | /// ```rust 260 | /// # extern crate cursive; 261 | /// # extern crate cursive_tree_view; 262 | /// # use cursive::Cursive; 263 | /// # use cursive_tree_view::TreeView; 264 | /// # fn main() { 265 | /// # let mut tree = TreeView::::new(); 266 | /// tree.on_collapse(|siv: &mut Cursive, row: usize, is_collapsed: bool, children: usize| { 267 | /// 268 | /// }); 269 | /// # } 270 | /// ``` 271 | pub fn on_collapse(self, cb: F) -> Self 272 | where 273 | F: Fn(&mut Cursive, usize, bool, usize) + Send + Sync + 'static, 274 | { 275 | self.with(|t| t.set_on_collapse(cb)) 276 | } 277 | 278 | /// Removes all items from this view. 279 | pub fn clear(&mut self) { 280 | self.list.clear(); 281 | self.focus = 0; 282 | } 283 | 284 | /// Removes all items from this view, returning them. 285 | pub fn take_items(&mut self) -> Vec { 286 | let items = self.list.take_items(); 287 | self.focus = 0; 288 | items 289 | } 290 | 291 | /// Returns the number of items in this tree. 292 | pub fn len(&self) -> usize { 293 | self.list.len() 294 | } 295 | 296 | /// Returns `true` if this tree has no items. 297 | pub fn is_empty(&self) -> bool { 298 | self.list.is_empty() 299 | } 300 | 301 | /// Returns the index of the currently selected tree row. 302 | /// 303 | /// `None` is returned in case of the tree being empty. 304 | pub fn row(&self) -> Option { 305 | if self.is_empty() { 306 | None 307 | } else { 308 | Some(self.focus) 309 | } 310 | } 311 | 312 | /// Returns position on the x axis of the symbol (first character of an item) at the given row. 313 | /// 314 | /// `None` is returned in case the specified `row` does not visually exist. 315 | pub fn first_col(&self, row: usize) -> Option { 316 | let index = self.list.row_to_item_index(row); 317 | self.list.first_col(index) 318 | } 319 | 320 | /// Returns total width (including the symbol) of the item at the given row. 321 | /// 322 | /// `None` is returned in case the specified `row` does not visually exist. 323 | pub fn item_width(&self, row: usize) -> Option { 324 | let index = self.list.row_to_item_index(row); 325 | self.list.width(index).map(|width| width + SYMBOL_WIDTH) 326 | } 327 | 328 | /// Selects the row at the specified index. 329 | pub fn set_selected_row(&mut self, row: usize) { 330 | self.focus = row; 331 | } 332 | 333 | /// Selects the row at the specified index. 334 | /// 335 | /// Chainable variant. 336 | pub fn selected_row(self, row: usize) -> Self { 337 | self.with(|t| t.set_selected_row(row)) 338 | } 339 | 340 | /// Returns a immutable reference to the item at the given row. 341 | /// 342 | /// `None` is returned in case the specified `row` does not visually exist. 343 | pub fn borrow_item(&self, row: usize) -> Option<&T> { 344 | let index = self.list.row_to_item_index(row); 345 | self.list.get(index) 346 | } 347 | 348 | /// Returns a mutable reference to the item at the given row. 349 | /// 350 | /// `None` is returned in case the specified `row` does not visually exist. 351 | pub fn borrow_item_mut(&mut self, row: usize) -> Option<&mut T> { 352 | let index = self.list.row_to_item_index(row); 353 | self.list.get_mut(index) 354 | } 355 | 356 | /// Inserts a new `item` at the given `row` with the specified 357 | /// [`Placement`](enum.Placement.html), returning the visual row of the item 358 | /// occupies after its insertion. 359 | /// 360 | /// 361 | /// `None` will be returned in case the item is not visible after insertion 362 | /// due to one of its parents being in a collapsed state. 363 | pub fn insert_item(&mut self, item: T, placement: Placement, row: usize) -> Option { 364 | let index = self.list.row_to_item_index(row); 365 | self.list.insert_item(placement, index, item) 366 | } 367 | 368 | /// Inserts a new `container` at the given `row` with the specified 369 | /// [`Placement`](enum.Placement.html), returning the visual row of the 370 | /// container occupies after its insertion. 371 | /// 372 | /// A container is identical to a normal item except for the fact that it 373 | /// can always be collapsed even if it does not contain any children. 374 | /// 375 | /// > Note: If the container is not visible because one of its parents is 376 | /// > collapsed `None` will be returned since there is no visible row for 377 | /// > the container to occupy. 378 | pub fn insert_container_item( 379 | &mut self, 380 | item: T, 381 | placement: Placement, 382 | row: usize, 383 | ) -> Option { 384 | let index = self.list.row_to_item_index(row); 385 | self.list.insert_container_item(placement, index, item) 386 | } 387 | 388 | /// Removes the item at the given `row` along with all of its children. 389 | /// 390 | /// The returned vector contains the removed items in top to bottom order. 391 | /// 392 | /// `None` is returned in case the specified `row` does not visually exist. 393 | pub fn remove_item(&mut self, row: usize) -> Option> { 394 | let index = self.list.row_to_item_index(row); 395 | let removed = self.list.remove_with_children(index); 396 | self.focus = cmp::min(self.focus, self.list.height() - 1); 397 | removed 398 | } 399 | 400 | /// Removes all children of the item at the given `row`. 401 | /// 402 | /// The returned vector contains the removed children in top to bottom order. 403 | /// 404 | /// `None` is returned in case the specified `row` does not visually exist. 405 | pub fn remove_children(&mut self, row: usize) -> Option> { 406 | let index = self.list.row_to_item_index(row); 407 | let removed = self.list.remove_children(index); 408 | self.focus = cmp::min(self.focus, self.list.height() - 1); 409 | removed 410 | } 411 | 412 | /// Extracts the item at the given `row` from the tree. 413 | /// 414 | /// All of the items children will be moved up one level within the tree. 415 | /// 416 | /// `None` is returned in case the specified `row` does not visually exist. 417 | pub fn extract_item(&mut self, row: usize) -> Option { 418 | let index = self.list.row_to_item_index(row); 419 | let removed = self.list.remove(index); 420 | self.focus = cmp::min(self.focus, self.list.height() - 1); 421 | removed 422 | } 423 | 424 | /// Collapses the children of the given `row`. 425 | pub fn collapse_item(&mut self, row: usize) { 426 | let index = self.list.row_to_item_index(row); 427 | self.list.set_collapsed(index, true); 428 | } 429 | 430 | /// Expands the children of the given `row`. 431 | pub fn expand_item(&mut self, row: usize) { 432 | let index = self.list.row_to_item_index(row); 433 | self.list.set_collapsed(index, false); 434 | } 435 | 436 | /// Collapses or expands the children of the given `row`. 437 | pub fn set_collapsed(&mut self, row: usize, collapsed: bool) { 438 | let index = self.list.row_to_item_index(row); 439 | self.list.set_collapsed(index, collapsed); 440 | } 441 | 442 | /// Collapses or expands the children of the given `row`. 443 | /// 444 | /// Chained variant. 445 | pub fn collapsed(self, row: usize, collapsed: bool) -> Self { 446 | self.with(|t| t.set_collapsed(row, collapsed)) 447 | } 448 | 449 | /// Select item `n` rows up from the one currently selected. 450 | pub fn focus_up(&mut self, n: usize) { 451 | self.focus -= cmp::min(self.focus, n); 452 | } 453 | 454 | /// Select item `n` rows down from the one currently selected. 455 | pub fn focus_down(&mut self, n: usize) { 456 | self.focus = cmp::min(self.focus + n, self.list.height() - 1); 457 | } 458 | 459 | /// Returns position of the parent of the item located in `row`. 460 | /// 461 | /// `None` is returned if `row` is not currenlty visible or if the item has no ancestors. 462 | pub fn item_parent(&self, row: usize) -> Option { 463 | let item_index = self.list.row_to_item_index(row); 464 | let parent_index = self.list.item_parent_index(item_index)?; 465 | Some(self.list.item_index_to_row(parent_index)) 466 | } 467 | 468 | fn submit(&mut self) -> EventResult { 469 | let row = self.focus; 470 | let index = self.list.row_to_item_index(row); 471 | 472 | if self.list.is_container_item(index) { 473 | let collapsed = self.list.get_collapsed(index); 474 | let children = self.list.get_children(index); 475 | 476 | self.list.set_collapsed(index, !collapsed); 477 | 478 | if self.on_collapse.is_some() { 479 | let cb = self.on_collapse.clone().unwrap(); 480 | return EventResult::Consumed(Some(Callback::from_fn(move |s| { 481 | cb(s, row, !collapsed, children) 482 | }))); 483 | } 484 | } else if self.on_submit.is_some() { 485 | let cb = self.on_submit.clone().unwrap(); 486 | return EventResult::Consumed(Some(Callback::from_fn(move |s| cb(s, row)))); 487 | } 488 | 489 | EventResult::Ignored 490 | } 491 | } 492 | 493 | impl View for TreeView { 494 | fn draw(&self, printer: &Printer<'_, '_>) { 495 | let index = self.list.row_to_item_index(0); 496 | let items = self.list.items(); 497 | let list_index = Arc::new(Mutex::new(index)); 498 | 499 | for i in 0..self.list.height() { 500 | let printer = printer.offset((0, i)); 501 | let mut index = list_index.lock().unwrap(); 502 | 503 | let item = &items[*index]; 504 | *index += item.len(); 505 | 506 | let color = if i == self.focus { 507 | if self.enabled && printer.focused { 508 | ColorStyle::highlight() 509 | } else { 510 | ColorStyle::highlight_inactive() 511 | } 512 | } else { 513 | ColorStyle::primary() 514 | }; 515 | 516 | printer.print((item.offset(), 0), item.symbol()); 517 | 518 | printer.with_color(color, |printer| { 519 | printer.print( 520 | (item.offset() + SYMBOL_WIDTH, 0), 521 | format!("{}", item.value()).as_str(), 522 | ); 523 | }); 524 | } 525 | } 526 | 527 | fn required_size(&mut self, _req: Vec2) -> Vec2 { 528 | let w: usize = self 529 | .list 530 | .items() 531 | .iter() 532 | .map(|item| item.level() * 2 + format!("{}", item.value()).len() + 2) 533 | .max() 534 | .unwrap_or(0); 535 | 536 | let h = self.list.height(); 537 | 538 | (w, h).into() 539 | } 540 | 541 | fn layout(&mut self, size: Vec2) { 542 | self.last_size = size; 543 | } 544 | 545 | fn take_focus(&mut self, _: Direction) -> Result { 546 | (self.enabled && !self.is_empty()) 547 | .then(EventResult::consumed) 548 | .ok_or(CannotFocus) 549 | } 550 | 551 | fn on_event(&mut self, event: Event) -> EventResult { 552 | if !self.enabled { 553 | return EventResult::Ignored; 554 | } 555 | 556 | let last_focus = self.focus; 557 | match event { 558 | Event::Key(Key::Up) if self.focus > 0 => { 559 | self.focus_up(1); 560 | } 561 | Event::Key(Key::Down) if self.focus + 1 < self.list.height() => { 562 | self.focus_down(1); 563 | } 564 | Event::Key(Key::PageUp) => { 565 | self.focus_up(10); 566 | } 567 | Event::Key(Key::PageDown) => { 568 | self.focus_down(10); 569 | } 570 | Event::Key(Key::Home) => { 571 | self.focus = 0; 572 | } 573 | Event::Key(Key::End) => { 574 | self.focus = self.list.height() - 1; 575 | } 576 | Event::Key(Key::Enter) => { 577 | if !self.is_empty() { 578 | return self.submit(); 579 | } 580 | } 581 | Event::Mouse { 582 | position, 583 | offset, 584 | event: MouseEvent::Press(btn), 585 | } => { 586 | if let Some(position) = position.checked_sub(offset) { 587 | match position.y { 588 | y if y == self.focus && btn == MouseButton::Left => return self.submit(), 589 | y if y < self.list.height() => self.focus = position.y, 590 | _ => return EventResult::Ignored, 591 | } 592 | } 593 | } 594 | _ => return EventResult::Ignored, 595 | } 596 | 597 | let focus = self.focus; 598 | 599 | if !self.is_empty() && last_focus != focus { 600 | let row = self.focus; 601 | EventResult::Consumed( 602 | self.on_select 603 | .clone() 604 | .map(|cb| Callback::from_fn(move |s| cb(s, row))), 605 | ) 606 | } else { 607 | EventResult::Ignored 608 | } 609 | } 610 | 611 | fn important_area(&self, size: Vec2) -> Rect { 612 | Rect::from_size((0, self.focus), (size.x, 1)) 613 | } 614 | } 615 | -------------------------------------------------------------------------------- /src/tree_list/mod.rs: -------------------------------------------------------------------------------- 1 | // STD Dependencies ----------------------------------------------------------- 2 | use std::cmp; 3 | use std::fmt::{Debug, Display}; 4 | 5 | #[derive(Debug)] 6 | pub struct TreeNode { 7 | value: T, 8 | level: usize, 9 | is_collapsed: bool, 10 | children: usize, 11 | height: usize, 12 | is_container: bool, 13 | collapsed_height: Option, 14 | } 15 | 16 | impl TreeNode { 17 | pub fn value(&self) -> &T { 18 | &self.value 19 | } 20 | 21 | pub fn level(&self) -> usize { 22 | self.level 23 | } 24 | 25 | pub fn len(&self) -> usize { 26 | if self.is_collapsed { 27 | self.children + 1 28 | } else { 29 | 1 30 | } 31 | } 32 | 33 | pub fn symbol(&self) -> &str { 34 | if self.is_container { 35 | if self.is_collapsed { 36 | "▸" 37 | } else { 38 | "▾" 39 | } 40 | } else { 41 | "◦" 42 | } 43 | } 44 | 45 | /// Returns indentation of the element in the tree 46 | pub fn offset(&self) -> usize { 47 | self.level() * 2 48 | } 49 | 50 | /// Returns length of the string representation of the item 51 | pub fn width(&self) -> usize { 52 | format!("{}", self.value()).len() 53 | } 54 | } 55 | 56 | /// Determines how items are inserted into a [`TreeView`](struct.TreeView.html). 57 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 58 | pub enum Placement { 59 | /// The item is inserted as a sibling after the specified row. 60 | After, 61 | 62 | /// The item is inserted as a sibling before the specified row. 63 | Before, 64 | 65 | /// The item is inserted as new child of the specified row, placed 66 | /// before all other existing children. 67 | FirstChild, 68 | 69 | /// The item is inserted as new child of the specified row, placed 70 | /// after all other existing children. 71 | LastChild, 72 | 73 | /// The item is inserted as the new immediate parent of the specified row. 74 | Parent, 75 | } 76 | 77 | #[derive(Debug)] 78 | pub struct TreeList { 79 | items: Vec>, 80 | height: usize, 81 | } 82 | 83 | impl TreeList { 84 | pub fn new() -> Self { 85 | Self { 86 | items: Vec::new(), 87 | height: 0, 88 | } 89 | } 90 | 91 | pub fn len(&self) -> usize { 92 | self.items.len() 93 | } 94 | 95 | pub fn is_empty(&self) -> bool { 96 | self.items.is_empty() 97 | } 98 | 99 | pub fn height(&self) -> usize { 100 | self.height 101 | } 102 | 103 | pub fn items(&self) -> &[TreeNode] { 104 | &self.items 105 | } 106 | 107 | pub fn get(&self, index: usize) -> Option<&T> { 108 | self.items.get(index).map(|item| &item.value) 109 | } 110 | 111 | pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { 112 | self.items.get_mut(index).map(|item| &mut item.value) 113 | } 114 | 115 | pub fn take_items(&mut self) -> Vec { 116 | self.height = 0; 117 | self.items.drain(0..).map(|item| item.value).collect() 118 | } 119 | 120 | pub fn clear(&mut self) { 121 | self.items.clear(); 122 | self.height = 0; 123 | } 124 | 125 | /// Returns position on the x axis of the item at `index` 126 | /// 127 | /// `None` is returned when no item exists at `index`. 128 | pub fn first_col(&self, index: usize) -> Option { 129 | self.items.get(index).map(|item| item.offset()) 130 | } 131 | 132 | /// Returns width of the string representation of the item at `index` 133 | /// 134 | /// `None` is returned when no item exists at `index`. 135 | pub fn width(&self, index: usize) -> Option { 136 | self.items.get(index).map(|item| item.width()) 137 | } 138 | 139 | pub fn insert_item(&mut self, placement: Placement, index: usize, value: T) -> Option { 140 | self.insert(placement, index, value, false) 141 | } 142 | 143 | pub fn insert_container_item( 144 | &mut self, 145 | placement: Placement, 146 | index: usize, 147 | value: T, 148 | ) -> Option { 149 | self.insert(placement, index, value, true) 150 | } 151 | 152 | pub fn remove(&mut self, index: usize) -> Option { 153 | if index < self.len() { 154 | // Uncollapse to avoid additional height calculation 155 | self.set_collapsed(index, false); 156 | 157 | // Reduce height and children of all parents 158 | self.traverse_up(index, 0, |item| { 159 | item.children -= 1; 160 | item.height -= 1; 161 | }); 162 | 163 | // Remove item 164 | let removed_item = self.items.remove(index); 165 | 166 | // Reduce level of all children 167 | if removed_item.children > 0 { 168 | self.traverse_down(index, true, |item| { 169 | item.level -= 1; 170 | }); 171 | } 172 | 173 | // Reduce tree height 174 | self.height -= 1; 175 | 176 | Some(removed_item.value) 177 | } else { 178 | None 179 | } 180 | } 181 | 182 | pub fn remove_children(&mut self, index: usize) -> Option> { 183 | if index < self.len() { 184 | let (item_height, item_children, was_collapsed) = { 185 | let item = &self.items[index]; 186 | (item.height - 1, item.children, item.is_collapsed) 187 | }; 188 | 189 | // Uncollapse to avoid additional height calculation 190 | self.set_collapsed(index, false); 191 | 192 | // Reduce height and children of all parents 193 | self.traverse_up(index, 1, |item| { 194 | item.children -= item_children; 195 | item.height -= item_height; 196 | }); 197 | 198 | // Reduce tree height 199 | self.height -= item_height; 200 | 201 | // Remove children 202 | let removed_items = if item_children > 0 { 203 | self.items 204 | .drain(index + 1..index + 1 + item_children) 205 | .map(|item| item.value) 206 | .collect() 207 | } else { 208 | Vec::new() 209 | }; 210 | 211 | self.set_collapsed(index, was_collapsed); 212 | 213 | Some(removed_items) 214 | } else { 215 | None 216 | } 217 | } 218 | 219 | pub fn remove_with_children(&mut self, index: usize) -> Option> { 220 | if index < self.len() { 221 | // Uncollapse to avoid additional height calculation 222 | self.set_collapsed(index, false); 223 | 224 | let (item_height, item_children) = { 225 | let item = &self.items[index]; 226 | (item.height, item.children) 227 | }; 228 | 229 | // Reduce height and children of all parents 230 | self.traverse_up(index, 0, |item| { 231 | item.children -= item_children + 1; 232 | item.height -= item_height; 233 | }); 234 | 235 | // Remove item 236 | let item = self.items.remove(index); 237 | 238 | // Reduce tree height 239 | self.height -= item.height; 240 | 241 | // Remove children 242 | let mut removed_items = vec![item.value]; 243 | if item_children > 0 { 244 | removed_items.append( 245 | &mut self 246 | .items 247 | .drain(index..index + item_children) 248 | .map(|item| item.value) 249 | .collect(), 250 | ) 251 | }; 252 | 253 | Some(removed_items) 254 | } else { 255 | None 256 | } 257 | } 258 | 259 | // TODO rename and cleanup the methods below 260 | pub fn is_container_item(&self, index: usize) -> bool { 261 | self.items 262 | .get(index) 263 | .map(|item| item.is_container) 264 | .unwrap_or(false) 265 | } 266 | 267 | pub fn get_children(&self, index: usize) -> usize { 268 | self.items.get(index).map(|item| item.children).unwrap_or(0) 269 | } 270 | 271 | pub fn get_collapsed(&self, index: usize) -> bool { 272 | self.items 273 | .get(index) 274 | .map(|item| item.is_collapsed) 275 | .unwrap_or(false) 276 | } 277 | 278 | pub fn set_collapsed(&mut self, index: usize, collapsed: bool) { 279 | if index < self.len() { 280 | let offset = { 281 | let item = &mut self.items[index]; 282 | if item.is_collapsed != collapsed { 283 | // Uncollapse items early in order to propagate height 284 | // changes to parents correctly 285 | if !collapsed { 286 | item.is_collapsed = false; 287 | } 288 | 289 | // Remove the height if we are collpasing 290 | // This way already collapsed children are not counted in 291 | // We also store the height for later unfolding. 292 | if collapsed { 293 | item.collapsed_height = Some(item.height); 294 | Some(item.height - 1) 295 | } else { 296 | Some(item.collapsed_height.take().unwrap() - 1) 297 | } 298 | } else { 299 | None 300 | } 301 | }; 302 | 303 | if let Some(offset) = offset { 304 | let mut inside_collapsed = false; 305 | self.traverse_up(index, 1, |item| { 306 | inside_collapsed |= item.is_collapsed; 307 | 308 | // Modify the collapsed height of the parent if required 309 | if item.is_collapsed { 310 | if collapsed { 311 | item.collapsed_height = Some(item.collapsed_height.unwrap() - offset); 312 | } else { 313 | item.collapsed_height = Some(item.collapsed_height.unwrap() + offset); 314 | } 315 | 316 | // Ignore all parents beyond the first collapsed one as the 317 | // changes in height cannot visibly propagate any further 318 | } else if !inside_collapsed { 319 | if collapsed { 320 | item.height -= offset; 321 | } else { 322 | item.height += offset; 323 | } 324 | } 325 | }); 326 | 327 | // Collapse items late in order to propagate height changes to 328 | // parents correctly 329 | if collapsed { 330 | let item = &mut self.items[index]; 331 | item.is_collapsed = true; 332 | } 333 | 334 | // Complete tree height is only affected when not contained 335 | // within an already collapsed parent 336 | if !inside_collapsed { 337 | if collapsed { 338 | self.height -= offset; 339 | } else { 340 | self.height += offset; 341 | } 342 | } 343 | } 344 | } 345 | } 346 | 347 | pub fn row_to_item_index(&self, row: usize) -> usize { 348 | let mut i = 0; 349 | let mut item_index = row; 350 | 351 | while i < self.items.len() { 352 | if item_index == i { 353 | return i; 354 | } else if self.get_collapsed(i) { 355 | let children = self.get_children(i); 356 | i += children; 357 | item_index += children; 358 | } 359 | 360 | i += 1; 361 | } 362 | 363 | self.len() 364 | } 365 | 366 | pub fn item_index_to_row(&self, index: usize) -> usize { 367 | let mut i = 0; 368 | let mut row = index; 369 | 370 | while i < index { 371 | if self.get_collapsed(i) { 372 | let children = self.get_children(i); 373 | i += children; 374 | row -= children; 375 | } 376 | 377 | i += 1; 378 | } 379 | 380 | row 381 | } 382 | 383 | /// Returns index of the parent of the node at the input index. 384 | /// 385 | /// `None` is returned if the node at index is a root (has no ancestors). 386 | pub fn item_parent_index(&self, index: usize) -> Option { 387 | let level = self.items[index].level; 388 | for i in 0..=index { 389 | if self.items[index - i].level < level { 390 | return Some(index - i); 391 | } 392 | } 393 | None 394 | } 395 | } 396 | 397 | impl TreeList { 398 | fn insert( 399 | &mut self, 400 | placement: Placement, 401 | index: usize, 402 | value: T, 403 | is_container: bool, 404 | ) -> Option { 405 | // Limit index to the maximum index of the items vec 406 | let index = cmp::min(index, cmp::max(self.len() as isize - 1, 0) as usize); 407 | 408 | let (parent_index, item_index, level, move_children) = if self.items.is_empty() { 409 | (None, 0, 0, false) 410 | } else { 411 | match placement { 412 | Placement::After => { 413 | // General case 414 | if let Some(parent_index) = self.item_parent_index(index) { 415 | // Find the actual parent 416 | let parent = &self.items[parent_index]; 417 | 418 | // How many items to skip due to children of the node 419 | // after which to insert 420 | let before = &self.items[index]; 421 | 422 | ( 423 | Some(parent_index), 424 | index + 1 + before.children, 425 | parent.level + 1, 426 | false, 427 | ) 428 | 429 | // Case where the parent is the root 430 | } else { 431 | let parent = self.items.get(index).expect("Tree should not be empty"); 432 | (None, index + 1 + parent.children, parent.level, false) 433 | } 434 | } 435 | Placement::Before => { 436 | if let Some(parent_index) = self.item_parent_index(index) { 437 | let parent = &self.items[parent_index]; 438 | (Some(parent_index), index, parent.level + 1, false) 439 | } else { 440 | (None, index, 0, false) 441 | } 442 | } 443 | Placement::FirstChild => { 444 | let parent = self.items.get(index).expect("Tree should not be empty"); 445 | (Some(index), index + 1, parent.level + 1, false) 446 | } 447 | Placement::LastChild => { 448 | let parent = self.items.get(index).expect("Tree should not be empty"); 449 | ( 450 | Some(index), 451 | index + 1 + parent.children, 452 | parent.level + 1, 453 | false, 454 | ) 455 | } 456 | Placement::Parent => { 457 | // Get level of first child that we replace 458 | let level = { 459 | self.items 460 | .get(index) 461 | .expect("Tree should not be empty") 462 | .level 463 | }; 464 | 465 | // Also increase height and children count of all upward 466 | // parents 467 | ( 468 | if index > 0 { Some(index - 1) } else { None }, 469 | index, 470 | level, 471 | true, 472 | ) 473 | } 474 | } 475 | }; 476 | 477 | let mut inside_collapsed = false; 478 | if let Some(parent_index) = parent_index { 479 | self.traverse_up(parent_index, 1, |item| { 480 | if item.level < level { 481 | // Automatically convert the item into a container 482 | item.is_container = true; 483 | item.children += 1; 484 | 485 | // In case the parent is collapsed we increment the stored 486 | // collapsed height instead of the actual one and exit early 487 | // to avoid messing up any parents further up the in the tree 488 | if !inside_collapsed { 489 | if item.is_collapsed { 490 | inside_collapsed = true; 491 | item.collapsed_height = Some(item.collapsed_height.unwrap() + 1); 492 | } else { 493 | item.height += 1; 494 | } 495 | } 496 | } 497 | }); 498 | } 499 | 500 | // Move children to a deeper level 501 | let children = if move_children { 502 | self.traverse_down(item_index, false, |item| { 503 | item.level += 1; 504 | }) 505 | } else { 506 | 0 507 | }; 508 | 509 | let initially_collapsed = is_container && children == 0; 510 | self.items.insert( 511 | item_index, 512 | TreeNode { 513 | value, 514 | is_collapsed: initially_collapsed, 515 | level, 516 | children, 517 | height: 1 + children, 518 | is_container, 519 | collapsed_height: if initially_collapsed { Some(1) } else { None }, 520 | }, 521 | ); 522 | 523 | // Only increment the tree height if the item was not inserted within a 524 | // already collapsed parent 525 | if !inside_collapsed { 526 | self.height += 1; 527 | 528 | // We only return the visual row index in case the inserted item is 529 | // visible 530 | Some(self.item_index_to_row(item_index)) 531 | } else { 532 | None 533 | } 534 | } 535 | 536 | fn traverse_up)>(&mut self, index: usize, offset: usize, mut cb: C) { 537 | let mut level = self.items[index].level + offset; 538 | for i in 0..=index { 539 | if self.items[index - i].level < level { 540 | cb(&mut self.items[index - i]); 541 | level -= 1; 542 | } 543 | } 544 | } 545 | 546 | fn traverse_down)>( 547 | &mut self, 548 | index: usize, 549 | same_level: bool, 550 | cb: C, 551 | ) -> usize { 552 | let mut children = 0; 553 | let level = self.items[index].level; 554 | for i in index..self.len() { 555 | if ((same_level || children == 0) && self.items[i].level == level) 556 | || self.items[i].level > level 557 | { 558 | children += 1; 559 | cb(&mut self.items[i]); 560 | } else { 561 | break; 562 | } 563 | } 564 | 565 | children 566 | } 567 | } 568 | 569 | // Tests ---------------------------------------------------------------------- 570 | #[cfg(test)] 571 | mod test { 572 | 573 | use super::TreeList; 574 | use std::fmt; 575 | 576 | // Debug Implementations -------------------------------------------------- 577 | impl TreeList { 578 | fn print(&self) { 579 | let mut i = 0; 580 | while i < self.len() { 581 | let item = &self.items[i]; 582 | if item.is_collapsed { 583 | i += item.children + 1; 584 | println!( 585 | "{: >width$}> {} ({} / {} / {})", 586 | "", 587 | item.value, 588 | item.level, 589 | item.children, 590 | item.height, 591 | width = item.level * 2 592 | ); 593 | } else { 594 | println!( 595 | "{: >width$}- {} ({} / {} / {})", 596 | "", 597 | item.value, 598 | item.level, 599 | item.children, 600 | item.height, 601 | width = item.level * 2 602 | ); 603 | i += 1; 604 | } 605 | } 606 | } 607 | 608 | fn to_vec(&self) -> Vec<(usize, bool, String, usize, usize)> { 609 | let mut list = Vec::new(); 610 | let mut i = 0; 611 | while i < self.len() { 612 | let item = &self.items[i]; 613 | if item.is_collapsed { 614 | i += item.children + 1; 615 | } else { 616 | i += 1; 617 | } 618 | 619 | list.push(( 620 | item.level, 621 | item.is_collapsed, 622 | format!("{}", item.value), 623 | item.children, 624 | item.height, 625 | )); 626 | } 627 | 628 | list 629 | } 630 | } 631 | 632 | #[test] 633 | fn test_create() { 634 | use super::TreeList; 635 | let _ = TreeList::::new(); 636 | } 637 | 638 | #[test] 639 | fn test_insert_out_of_bounds() { 640 | use super::TreeList; 641 | let _ = TreeList::::new(); 642 | } 643 | 644 | #[test] 645 | fn test_insert_after_flat() { 646 | use super::{Placement, TreeList}; 647 | 648 | let mut tree = TreeList::::new(); 649 | assert_eq!( 650 | tree.insert_item(Placement::After, 0, "1".to_string()), 651 | Some(0) 652 | ); 653 | assert_eq!( 654 | tree.insert_item(Placement::After, 0, "2".to_string()), 655 | Some(1) 656 | ); 657 | assert_eq!( 658 | tree.insert_item(Placement::After, 1, "3".to_string()), 659 | Some(2) 660 | ); 661 | assert_eq!( 662 | tree.insert_item(Placement::After, 2, "4".to_string()), 663 | Some(3) 664 | ); 665 | 666 | assert_eq!( 667 | tree.to_vec(), 668 | vec![ 669 | (0, false, "1".to_string(), 0, 1), 670 | (0, false, "2".to_string(), 0, 1), 671 | (0, false, "3".to_string(), 0, 1), 672 | (0, false, "4".to_string(), 0, 1) 673 | ] 674 | ); 675 | 676 | assert_eq!(tree.len(), 4); 677 | assert_eq!(tree.height(), 4); 678 | } 679 | 680 | #[test] 681 | fn test_insert_after_out_of_range() { 682 | use super::{Placement, TreeList}; 683 | 684 | let mut tree = TreeList::::new(); 685 | assert_eq!( 686 | tree.insert_item(Placement::After, 0, "1".to_string()), 687 | Some(0) 688 | ); 689 | assert_eq!( 690 | tree.insert_item(Placement::After, 10, "2".to_string()), 691 | Some(1) 692 | ); 693 | assert_eq!( 694 | tree.insert_item(Placement::After, 20, "3".to_string()), 695 | Some(2) 696 | ); 697 | assert_eq!( 698 | tree.insert_item(Placement::After, 30, "4".to_string()), 699 | Some(3) 700 | ); 701 | 702 | assert_eq!( 703 | tree.to_vec(), 704 | vec![ 705 | (0, false, "1".to_string(), 0, 1), 706 | (0, false, "2".to_string(), 0, 1), 707 | (0, false, "3".to_string(), 0, 1), 708 | (0, false, "4".to_string(), 0, 1) 709 | ] 710 | ); 711 | 712 | assert_eq!(tree.len(), 4); 713 | assert_eq!(tree.height(), 4); 714 | } 715 | 716 | #[test] 717 | fn test_insert_after_nested() { 718 | use super::{Placement, TreeList}; 719 | 720 | let mut tree = TreeList::::new(); 721 | tree.insert_item(Placement::After, 0, "Parent".to_string()); 722 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 723 | tree.insert_item(Placement::After, 1, "2".to_string()); 724 | tree.insert_item(Placement::After, 2, "3".to_string()); 725 | tree.insert_item(Placement::After, 3, "4".to_string()); 726 | 727 | assert_eq!( 728 | tree.to_vec(), 729 | vec![ 730 | (0, false, "Parent".to_string(), 4, 5), 731 | (1, false, "1".to_string(), 0, 1), 732 | (1, false, "2".to_string(), 0, 1), 733 | (1, false, "3".to_string(), 0, 1), 734 | (1, false, "4".to_string(), 0, 1) 735 | ] 736 | ); 737 | 738 | assert_eq!(tree.len(), 5); 739 | assert_eq!(tree.height(), 5); 740 | 741 | tree.insert_item(Placement::After, 0, "12".to_string()); 742 | tree.insert_item(Placement::After, 0, "11".to_string()); 743 | tree.insert_item(Placement::After, 0, "10".to_string()); 744 | 745 | assert_eq!( 746 | tree.to_vec(), 747 | vec![ 748 | (0, false, "Parent".to_string(), 4, 5), 749 | (1, false, "1".to_string(), 0, 1), 750 | (1, false, "2".to_string(), 0, 1), 751 | (1, false, "3".to_string(), 0, 1), 752 | (1, false, "4".to_string(), 0, 1), 753 | (0, false, "10".to_string(), 0, 1), 754 | (0, false, "11".to_string(), 0, 1), 755 | (0, false, "12".to_string(), 0, 1) 756 | ] 757 | ); 758 | 759 | tree.insert_item(Placement::After, 6, "after".to_string()); 760 | 761 | assert_eq!( 762 | tree.to_vec(), 763 | vec![ 764 | (0, false, "Parent".to_string(), 4, 5), 765 | (1, false, "1".to_string(), 0, 1), 766 | (1, false, "2".to_string(), 0, 1), 767 | (1, false, "3".to_string(), 0, 1), 768 | (1, false, "4".to_string(), 0, 1), 769 | (0, false, "10".to_string(), 0, 1), 770 | (0, false, "11".to_string(), 0, 1), 771 | (0, false, "after".to_string(), 0, 1), 772 | (0, false, "12".to_string(), 0, 1) 773 | ] 774 | ); 775 | } 776 | 777 | #[test] 778 | fn test_insert_before_flat() { 779 | use super::{Placement, TreeList}; 780 | 781 | let mut tree = TreeList::::new(); 782 | assert_eq!( 783 | tree.insert_item(Placement::Before, 0, "4".to_string()), 784 | Some(0) 785 | ); 786 | assert_eq!( 787 | tree.insert_item(Placement::Before, 0, "1".to_string()), 788 | Some(0) 789 | ); 790 | assert_eq!( 791 | tree.insert_item(Placement::Before, 1, "2".to_string()), 792 | Some(1) 793 | ); 794 | assert_eq!( 795 | tree.insert_item(Placement::Before, 2, "3".to_string()), 796 | Some(2) 797 | ); 798 | 799 | assert_eq!( 800 | tree.to_vec(), 801 | vec![ 802 | (0, false, "1".to_string(), 0, 1), 803 | (0, false, "2".to_string(), 0, 1), 804 | (0, false, "3".to_string(), 0, 1), 805 | (0, false, "4".to_string(), 0, 1) 806 | ] 807 | ); 808 | 809 | assert_eq!(tree.len(), 4); 810 | assert_eq!(tree.height(), 4); 811 | } 812 | 813 | #[test] 814 | fn test_insert_before_nested() { 815 | use super::{Placement, TreeList}; 816 | 817 | let mut tree = TreeList::::new(); 818 | tree.insert_item(Placement::Before, 0, "Parent".to_string()); 819 | tree.insert_item(Placement::LastChild, 0, "4".to_string()); 820 | tree.insert_item(Placement::Before, 1, "1".to_string()); 821 | tree.insert_item(Placement::Before, 2, "2".to_string()); 822 | tree.insert_item(Placement::Before, 3, "3".to_string()); 823 | 824 | assert_eq!( 825 | tree.to_vec(), 826 | vec![ 827 | (0, false, "Parent".to_string(), 4, 5), 828 | (1, false, "1".to_string(), 0, 1), 829 | (1, false, "2".to_string(), 0, 1), 830 | (1, false, "3".to_string(), 0, 1), 831 | (1, false, "4".to_string(), 0, 1) 832 | ] 833 | ); 834 | 835 | assert_eq!(tree.len(), 5); 836 | assert_eq!(tree.height(), 5); 837 | } 838 | 839 | #[test] 840 | fn test_insert_last_child() { 841 | use super::{Placement, TreeList}; 842 | 843 | let mut tree = TreeList::::new(); 844 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 845 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 846 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 847 | tree.insert_item(Placement::LastChild, 2, "4".to_string()); 848 | tree.insert_item(Placement::LastChild, 3, "5".to_string()); 849 | 850 | assert_eq!( 851 | tree.to_vec(), 852 | vec![ 853 | (0, false, "1".to_string(), 4, 5), 854 | (1, false, "2".to_string(), 3, 4), 855 | (2, false, "3".to_string(), 2, 3), 856 | (3, false, "4".to_string(), 1, 2), 857 | (4, false, "5".to_string(), 0, 1) 858 | ] 859 | ); 860 | 861 | assert_eq!(tree.len(), 5); 862 | assert_eq!(tree.height(), 5); 863 | } 864 | 865 | #[test] 866 | fn test_insert_last_child_double() { 867 | use super::{Placement, TreeList}; 868 | 869 | let mut tree = TreeList::::new(); 870 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 871 | 872 | assert_eq!( 873 | tree.insert_item(Placement::LastChild, 0, "2a".to_string()), 874 | Some(1) 875 | ); 876 | assert_eq!( 877 | tree.insert_item(Placement::LastChild, 1, "3a".to_string()), 878 | Some(2) 879 | ); 880 | assert_eq!( 881 | tree.insert_item(Placement::LastChild, 2, "4a".to_string()), 882 | Some(3) 883 | ); 884 | 885 | assert_eq!( 886 | tree.insert_item(Placement::LastChild, 0, "2b".to_string()), 887 | Some(4) 888 | ); 889 | assert_eq!( 890 | tree.insert_item(Placement::LastChild, 4, "3b".to_string()), 891 | Some(5) 892 | ); 893 | assert_eq!( 894 | tree.insert_item(Placement::LastChild, 5, "4b".to_string()), 895 | Some(6) 896 | ); 897 | 898 | assert_eq!( 899 | tree.to_vec(), 900 | vec![ 901 | (0, false, "1".to_string(), 6, 7), 902 | (1, false, "2a".to_string(), 2, 3), 903 | (2, false, "3a".to_string(), 1, 2), 904 | (3, false, "4a".to_string(), 0, 1), 905 | (1, false, "2b".to_string(), 2, 3), 906 | (2, false, "3b".to_string(), 1, 2), 907 | (3, false, "4b".to_string(), 0, 1) 908 | ] 909 | ); 910 | 911 | assert_eq!(tree.len(), 7); 912 | assert_eq!(tree.height(), 7); 913 | } 914 | 915 | #[test] 916 | fn test_insert_first_child() { 917 | use super::{Placement, TreeList}; 918 | 919 | let mut tree = TreeList::::new(); 920 | tree.insert_item(Placement::FirstChild, 0, "1".to_string()); 921 | tree.insert_item(Placement::FirstChild, 0, "2".to_string()); 922 | tree.insert_item(Placement::FirstChild, 1, "3".to_string()); 923 | tree.insert_item(Placement::FirstChild, 2, "4".to_string()); 924 | tree.insert_item(Placement::FirstChild, 3, "5".to_string()); 925 | 926 | assert_eq!( 927 | tree.to_vec(), 928 | vec![ 929 | (0, false, "1".to_string(), 4, 5), 930 | (1, false, "2".to_string(), 3, 4), 931 | (2, false, "3".to_string(), 2, 3), 932 | (3, false, "4".to_string(), 1, 2), 933 | (4, false, "5".to_string(), 0, 1) 934 | ] 935 | ); 936 | 937 | assert_eq!(tree.len(), 5); 938 | assert_eq!(tree.height(), 5); 939 | } 940 | 941 | #[test] 942 | fn test_insert_first_child_double() { 943 | use super::{Placement, TreeList}; 944 | 945 | let mut tree = TreeList::::new(); 946 | tree.insert_item(Placement::FirstChild, 0, "1".to_string()); 947 | 948 | assert_eq!( 949 | tree.insert_item(Placement::FirstChild, 0, "2a".to_string()), 950 | Some(1) 951 | ); 952 | assert_eq!( 953 | tree.insert_item(Placement::FirstChild, 1, "3a".to_string()), 954 | Some(2) 955 | ); 956 | assert_eq!( 957 | tree.insert_item(Placement::FirstChild, 2, "4a".to_string()), 958 | Some(3) 959 | ); 960 | 961 | assert_eq!( 962 | tree.insert_item(Placement::FirstChild, 0, "2b".to_string()), 963 | Some(1) 964 | ); 965 | assert_eq!( 966 | tree.insert_item(Placement::FirstChild, 1, "3b".to_string()), 967 | Some(2) 968 | ); 969 | assert_eq!( 970 | tree.insert_item(Placement::FirstChild, 2, "4b".to_string()), 971 | Some(3) 972 | ); 973 | 974 | tree.print(); 975 | assert_eq!( 976 | tree.to_vec(), 977 | vec![ 978 | (0, false, "1".to_string(), 6, 7), 979 | (1, false, "2b".to_string(), 2, 3), 980 | (2, false, "3b".to_string(), 1, 2), 981 | (3, false, "4b".to_string(), 0, 1), 982 | (1, false, "2a".to_string(), 2, 3), 983 | (2, false, "3a".to_string(), 1, 2), 984 | (3, false, "4a".to_string(), 0, 1) 985 | ] 986 | ); 987 | 988 | assert_eq!(tree.len(), 7); 989 | assert_eq!(tree.height(), 7); 990 | } 991 | 992 | #[test] 993 | fn test_insert_first_child_multiple() { 994 | use super::{Placement, TreeList}; 995 | 996 | let mut tree = TreeList::::new(); 997 | tree.insert_item(Placement::FirstChild, 0, "1".to_string()); 998 | tree.insert_item(Placement::FirstChild, 0, "2".to_string()); 999 | tree.insert_item(Placement::FirstChild, 1, "5".to_string()); 1000 | tree.insert_item(Placement::FirstChild, 1, "4".to_string()); 1001 | tree.insert_item(Placement::FirstChild, 1, "3".to_string()); 1002 | 1003 | assert_eq!( 1004 | tree.to_vec(), 1005 | vec![ 1006 | (0, false, "1".to_string(), 4, 5), 1007 | (1, false, "2".to_string(), 3, 4), 1008 | (2, false, "3".to_string(), 0, 1), 1009 | (2, false, "4".to_string(), 0, 1), 1010 | (2, false, "5".to_string(), 0, 1) 1011 | ] 1012 | ); 1013 | 1014 | assert_eq!(tree.len(), 5); 1015 | assert_eq!(tree.height(), 5); 1016 | } 1017 | 1018 | #[test] 1019 | fn test_insert_parent() { 1020 | use super::{Placement, TreeList}; 1021 | 1022 | let mut tree = TreeList::::new(); 1023 | assert_eq!( 1024 | tree.insert_item(Placement::Parent, 0, "5".to_string()), 1025 | Some(0) 1026 | ); 1027 | assert_eq!( 1028 | tree.insert_item(Placement::Parent, 0, "4".to_string()), 1029 | Some(0) 1030 | ); 1031 | assert_eq!( 1032 | tree.insert_item(Placement::Parent, 0, "3".to_string()), 1033 | Some(0) 1034 | ); 1035 | assert_eq!( 1036 | tree.insert_item(Placement::Parent, 0, "2".to_string()), 1037 | Some(0) 1038 | ); 1039 | assert_eq!( 1040 | tree.insert_item(Placement::Parent, 0, "1".to_string()), 1041 | Some(0) 1042 | ); 1043 | 1044 | assert_eq!( 1045 | tree.to_vec(), 1046 | vec![ 1047 | (0, false, "1".to_string(), 4, 5), 1048 | (1, false, "2".to_string(), 3, 4), 1049 | (2, false, "3".to_string(), 2, 3), 1050 | (3, false, "4".to_string(), 1, 2), 1051 | (4, false, "5".to_string(), 0, 1) 1052 | ] 1053 | ); 1054 | 1055 | assert_eq!(tree.len(), 5); 1056 | assert_eq!(tree.height(), 5); 1057 | } 1058 | 1059 | #[test] 1060 | fn test_insert_parent_siblings() { 1061 | use super::{Placement, TreeList}; 1062 | 1063 | let mut tree = TreeList::::new(); 1064 | tree.insert_item(Placement::After, 0, "Root".to_string()); 1065 | tree.insert_item(Placement::LastChild, 1, "1".to_string()); 1066 | tree.insert_item(Placement::After, 1, "6".to_string()); 1067 | tree.insert_item(Placement::After, 1, "5".to_string()); 1068 | tree.insert_item(Placement::After, 1, "4".to_string()); 1069 | tree.insert_item(Placement::After, 1, "3".to_string()); 1070 | tree.insert_item(Placement::After, 1, "2".to_string()); 1071 | 1072 | assert_eq!( 1073 | tree.to_vec(), 1074 | vec![ 1075 | (0, false, "Root".to_string(), 6, 7), 1076 | (1, false, "1".to_string(), 0, 1), 1077 | (1, false, "2".to_string(), 0, 1), 1078 | (1, false, "3".to_string(), 0, 1), 1079 | (1, false, "4".to_string(), 0, 1), 1080 | (1, false, "5".to_string(), 0, 1), 1081 | (1, false, "6".to_string(), 0, 1) 1082 | ] 1083 | ); 1084 | 1085 | assert_eq!(tree.len(), 7); 1086 | assert_eq!(tree.height(), 7); 1087 | 1088 | tree.insert_item(Placement::Parent, 3, "Parent".to_string()); 1089 | 1090 | assert_eq!( 1091 | tree.to_vec(), 1092 | vec![ 1093 | (0, false, "Root".to_string(), 7, 8), 1094 | (1, false, "1".to_string(), 0, 1), 1095 | (1, false, "2".to_string(), 0, 1), 1096 | (1, false, "Parent".to_string(), 1, 2), 1097 | (2, false, "3".to_string(), 0, 1), 1098 | (1, false, "4".to_string(), 0, 1), 1099 | (1, false, "5".to_string(), 0, 1), 1100 | (1, false, "6".to_string(), 0, 1) 1101 | ] 1102 | ); 1103 | 1104 | assert_eq!(tree.len(), 8); 1105 | assert_eq!(tree.height(), 8); 1106 | } 1107 | 1108 | #[test] 1109 | fn test_insert_parent_between() { 1110 | use super::{Placement, TreeList}; 1111 | 1112 | let mut tree = TreeList::::new(); 1113 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1114 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1115 | tree.insert_item(Placement::LastChild, 1, "8".to_string()); 1116 | 1117 | tree.insert_item(Placement::Parent, 2, "7".to_string()); 1118 | tree.insert_item(Placement::Parent, 2, "6".to_string()); 1119 | tree.insert_item(Placement::Parent, 2, "5".to_string()); 1120 | tree.insert_item(Placement::Parent, 2, "4".to_string()); 1121 | tree.insert_item(Placement::Parent, 2, "3".to_string()); 1122 | 1123 | assert_eq!( 1124 | tree.to_vec(), 1125 | vec![ 1126 | (0, false, "1".to_string(), 7, 8), 1127 | (1, false, "2".to_string(), 6, 7), 1128 | (2, false, "3".to_string(), 5, 6), 1129 | (3, false, "4".to_string(), 4, 5), 1130 | (4, false, "5".to_string(), 3, 4), 1131 | (5, false, "6".to_string(), 2, 3), 1132 | (6, false, "7".to_string(), 1, 2), 1133 | (7, false, "8".to_string(), 0, 1) 1134 | ] 1135 | ); 1136 | 1137 | assert_eq!(tree.len(), 8); 1138 | assert_eq!(tree.height(), 8); 1139 | } 1140 | 1141 | #[test] 1142 | fn test_insert_before_child() { 1143 | use super::{Placement, TreeList}; 1144 | 1145 | let mut tree = TreeList::::new(); 1146 | tree.insert_item(Placement::Before, 0, "Parent".to_string()); 1147 | tree.insert_item(Placement::LastChild, 0, "LastChild 1".to_string()); 1148 | tree.insert_item(Placement::LastChild, 0, "LastChild 2".to_string()); 1149 | tree.insert_item(Placement::LastChild, 2, "Nested LastChild".to_string()); 1150 | tree.insert_item(Placement::Before, 2, "Before".to_string()); 1151 | 1152 | assert_eq!( 1153 | tree.to_vec(), 1154 | vec![ 1155 | (0, false, "Parent".to_string(), 4, 5), 1156 | (1, false, "LastChild 1".to_string(), 0, 1), 1157 | (1, false, "Before".to_string(), 0, 1), 1158 | (1, false, "LastChild 2".to_string(), 1, 2), 1159 | (2, false, "Nested LastChild".to_string(), 0, 1) 1160 | ] 1161 | ); 1162 | 1163 | assert_eq!(tree.len(), 5); 1164 | assert_eq!(tree.height(), 5); 1165 | } 1166 | 1167 | #[test] 1168 | fn test_insert_after_child() { 1169 | use super::{Placement, TreeList}; 1170 | 1171 | let mut tree = TreeList::::new(); 1172 | tree.insert_item(Placement::After, 0, "Parent".to_string()); 1173 | tree.insert_item(Placement::LastChild, 0, "LastChild 1".to_string()); 1174 | tree.insert_item(Placement::LastChild, 0, "LastChild 2".to_string()); 1175 | tree.insert_item(Placement::LastChild, 2, "Nested LastChild".to_string()); 1176 | tree.insert_item(Placement::After, 1, "After".to_string()); 1177 | 1178 | assert_eq!( 1179 | tree.to_vec(), 1180 | vec![ 1181 | (0, false, "Parent".to_string(), 4, 5), 1182 | (1, false, "LastChild 1".to_string(), 0, 1), 1183 | (1, false, "After".to_string(), 0, 1), 1184 | (1, false, "LastChild 2".to_string(), 1, 2), 1185 | (2, false, "Nested LastChild".to_string(), 0, 1) 1186 | ] 1187 | ); 1188 | 1189 | assert_eq!(tree.len(), 5); 1190 | assert_eq!(tree.height(), 5); 1191 | } 1192 | 1193 | #[test] 1194 | fn test_insert_after_children() { 1195 | use super::{Placement, TreeList}; 1196 | 1197 | let mut tree = TreeList::::new(); 1198 | tree.insert_item(Placement::After, 0, "Parent".to_string()); 1199 | tree.insert_item(Placement::LastChild, 0, "LastChild 1".to_string()); 1200 | tree.insert_item(Placement::LastChild, 0, "LastChild 2".to_string()); 1201 | tree.insert_item(Placement::LastChild, 2, "Nested LastChild".to_string()); 1202 | tree.insert_item(Placement::After, 0, "After Parent".to_string()); 1203 | tree.insert_item(Placement::After, 0, "After Parent 2".to_string()); 1204 | tree.insert_item(Placement::After, 2, "After LastChild 2".to_string()); 1205 | 1206 | assert_eq!( 1207 | tree.to_vec(), 1208 | vec![ 1209 | (0, false, "Parent".to_string(), 4, 5), 1210 | (1, false, "LastChild 1".to_string(), 0, 1), 1211 | (1, false, "LastChild 2".to_string(), 1, 2), 1212 | (2, false, "Nested LastChild".to_string(), 0, 1), 1213 | (1, false, "After LastChild 2".to_string(), 0, 1), 1214 | (0, false, "After Parent 2".to_string(), 0, 1), 1215 | (0, false, "After Parent".to_string(), 0, 1) 1216 | ] 1217 | ); 1218 | 1219 | assert_eq!(tree.len(), 7); 1220 | assert_eq!(tree.height(), 7); 1221 | } 1222 | 1223 | #[test] 1224 | fn test_collapse_and_row_to_item_index() { 1225 | use super::{Placement, TreeList}; 1226 | 1227 | let mut tree = TreeList::::new(); 1228 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1229 | 1230 | tree.insert_item(Placement::LastChild, 0, "2a".to_string()); 1231 | tree.insert_item(Placement::LastChild, 1, "3a".to_string()); 1232 | tree.insert_item(Placement::LastChild, 2, "4a".to_string()); 1233 | 1234 | tree.insert_item(Placement::LastChild, 0, "2b".to_string()); 1235 | tree.insert_item(Placement::LastChild, 4, "3b".to_string()); 1236 | tree.insert_item(Placement::LastChild, 5, "4b".to_string()); 1237 | 1238 | assert_eq!( 1239 | tree.to_vec(), 1240 | vec![ 1241 | (0, false, "1".to_string(), 6, 7), 1242 | (1, false, "2a".to_string(), 2, 3), 1243 | (2, false, "3a".to_string(), 1, 2), 1244 | (3, false, "4a".to_string(), 0, 1), 1245 | (1, false, "2b".to_string(), 2, 3), 1246 | (2, false, "3b".to_string(), 1, 2), 1247 | (3, false, "4b".to_string(), 0, 1) 1248 | ] 1249 | ); 1250 | 1251 | assert_eq!(tree.len(), 7); 1252 | assert_eq!(tree.height(), 7); 1253 | 1254 | assert_eq!(tree.row_to_item_index(0), 0); 1255 | assert_eq!(tree.row_to_item_index(1), 1); 1256 | assert_eq!(tree.row_to_item_index(4), 4); 1257 | 1258 | tree.set_collapsed(1, true); 1259 | 1260 | assert_eq!( 1261 | tree.to_vec(), 1262 | vec![ 1263 | (0, false, "1".to_string(), 6, 5), 1264 | (1, true, "2a".to_string(), 2, 1), 1265 | (1, false, "2b".to_string(), 2, 3), 1266 | (2, false, "3b".to_string(), 1, 2), 1267 | (3, false, "4b".to_string(), 0, 1) 1268 | ] 1269 | ); 1270 | 1271 | assert_eq!(tree.len(), 7); 1272 | assert_eq!(tree.height(), 5); 1273 | 1274 | assert_eq!(tree.row_to_item_index(0), 0); 1275 | assert_eq!(tree.row_to_item_index(1), 1); 1276 | assert_eq!(tree.row_to_item_index(2), 4); 1277 | 1278 | tree.set_collapsed(4, true); 1279 | 1280 | assert_eq!( 1281 | tree.to_vec(), 1282 | vec![ 1283 | (0, false, "1".to_string(), 6, 3), 1284 | (1, true, "2a".to_string(), 2, 1), 1285 | (1, true, "2b".to_string(), 2, 1) 1286 | ] 1287 | ); 1288 | 1289 | assert_eq!(tree.len(), 7); 1290 | assert_eq!(tree.height(), 3); 1291 | 1292 | assert_eq!(tree.row_to_item_index(0), 0); 1293 | assert_eq!(tree.row_to_item_index(1), 1); 1294 | assert_eq!(tree.row_to_item_index(2), 4); 1295 | 1296 | tree.set_collapsed(1, false); 1297 | 1298 | tree.print(); 1299 | assert_eq!( 1300 | tree.to_vec(), 1301 | vec![ 1302 | (0, false, "1".to_string(), 6, 5), 1303 | (1, false, "2a".to_string(), 2, 3), 1304 | (2, false, "3a".to_string(), 1, 2), 1305 | (3, false, "4a".to_string(), 0, 1), 1306 | (1, true, "2b".to_string(), 2, 1) 1307 | ] 1308 | ); 1309 | 1310 | assert_eq!(tree.len(), 7); 1311 | assert_eq!(tree.height(), 5); 1312 | 1313 | assert_eq!(tree.row_to_item_index(4), 4); 1314 | 1315 | tree.set_collapsed(4, false); 1316 | 1317 | assert_eq!( 1318 | tree.to_vec(), 1319 | vec![ 1320 | (0, false, "1".to_string(), 6, 7), 1321 | (1, false, "2a".to_string(), 2, 3), 1322 | (2, false, "3a".to_string(), 1, 2), 1323 | (3, false, "4a".to_string(), 0, 1), 1324 | (1, false, "2b".to_string(), 2, 3), 1325 | (2, false, "3b".to_string(), 1, 2), 1326 | (3, false, "4b".to_string(), 0, 1) 1327 | ] 1328 | ); 1329 | 1330 | assert_eq!(tree.len(), 7); 1331 | assert_eq!(tree.height(), 7); 1332 | } 1333 | 1334 | #[test] 1335 | fn test_collapse_multiple() { 1336 | use super::{Placement, TreeList}; 1337 | 1338 | let mut tree = TreeList::::new(); 1339 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1340 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1341 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 1342 | tree.insert_item(Placement::LastChild, 2, "4".to_string()); 1343 | tree.insert_item(Placement::LastChild, 3, "5".to_string()); 1344 | 1345 | assert_eq!( 1346 | tree.to_vec(), 1347 | vec![ 1348 | (0, false, "1".to_string(), 4, 5), 1349 | (1, false, "2".to_string(), 3, 4), 1350 | (2, false, "3".to_string(), 2, 3), 1351 | (3, false, "4".to_string(), 1, 2), 1352 | (4, false, "5".to_string(), 0, 1) 1353 | ] 1354 | ); 1355 | 1356 | assert_eq!(tree.len(), 5); 1357 | assert_eq!(tree.height(), 5); 1358 | 1359 | tree.set_collapsed(3, true); 1360 | assert_eq!(tree.len(), 5); 1361 | assert_eq!(tree.height(), 4); 1362 | assert_eq!( 1363 | tree.to_vec(), 1364 | vec![ 1365 | (0, false, "1".to_string(), 4, 4), 1366 | (1, false, "2".to_string(), 3, 3), 1367 | (2, false, "3".to_string(), 2, 2), 1368 | (3, true, "4".to_string(), 1, 1) 1369 | ] 1370 | ); 1371 | 1372 | tree.set_collapsed(1, true); 1373 | assert_eq!(tree.len(), 5); 1374 | assert_eq!(tree.height(), 2); 1375 | assert_eq!( 1376 | tree.to_vec(), 1377 | vec![ 1378 | (0, false, "1".to_string(), 4, 2), 1379 | (1, true, "2".to_string(), 3, 1) 1380 | ] 1381 | ); 1382 | 1383 | tree.set_collapsed(1, false); 1384 | assert_eq!(tree.len(), 5); 1385 | assert_eq!(tree.height(), 4); 1386 | assert_eq!( 1387 | tree.to_vec(), 1388 | vec![ 1389 | (0, false, "1".to_string(), 4, 4), 1390 | (1, false, "2".to_string(), 3, 3), 1391 | (2, false, "3".to_string(), 2, 2), 1392 | (3, true, "4".to_string(), 1, 1) 1393 | ] 1394 | ); 1395 | 1396 | tree.set_collapsed(3, false); 1397 | assert_eq!(tree.len(), 5); 1398 | assert_eq!(tree.height(), 5); 1399 | assert_eq!( 1400 | tree.to_vec(), 1401 | vec![ 1402 | (0, false, "1".to_string(), 4, 5), 1403 | (1, false, "2".to_string(), 3, 4), 1404 | (2, false, "3".to_string(), 2, 3), 1405 | (3, false, "4".to_string(), 1, 2), 1406 | (4, false, "5".to_string(), 0, 1) 1407 | ] 1408 | ); 1409 | } 1410 | 1411 | #[test] 1412 | fn test_collapse_multiple_nested() { 1413 | use super::{Placement, TreeList}; 1414 | 1415 | let mut tree = TreeList::::new(); 1416 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1417 | 1418 | tree.insert_item(Placement::LastChild, 0, "2a".to_string()); 1419 | tree.insert_item(Placement::LastChild, 1, "3a".to_string()); 1420 | tree.insert_item(Placement::LastChild, 2, "4a".to_string()); 1421 | 1422 | tree.insert_item(Placement::LastChild, 0, "2b".to_string()); 1423 | tree.insert_item(Placement::LastChild, 4, "3b".to_string()); 1424 | tree.insert_item(Placement::LastChild, 5, "4b".to_string()); 1425 | 1426 | assert_eq!( 1427 | tree.to_vec(), 1428 | vec![ 1429 | (0, false, "1".to_string(), 6, 7), 1430 | (1, false, "2a".to_string(), 2, 3), 1431 | (2, false, "3a".to_string(), 1, 2), 1432 | (3, false, "4a".to_string(), 0, 1), 1433 | (1, false, "2b".to_string(), 2, 3), 1434 | (2, false, "3b".to_string(), 1, 2), 1435 | (3, false, "4b".to_string(), 0, 1) 1436 | ] 1437 | ); 1438 | 1439 | let indicies: Vec = (0..tree.height()) 1440 | .map(|row| tree.row_to_item_index(row)) 1441 | .collect(); 1442 | 1443 | assert_eq!(indicies, vec![0, 1, 2, 3, 4, 5, 6]); 1444 | 1445 | tree.set_collapsed(2, true); 1446 | tree.set_collapsed(1, true); 1447 | 1448 | let indicies: Vec = (0..tree.height()) 1449 | .map(|row| tree.row_to_item_index(row)) 1450 | .collect(); 1451 | 1452 | assert_eq!(indicies, vec![0, 1, 4, 5, 6]); 1453 | } 1454 | 1455 | #[test] 1456 | fn test_insert_after_collapsed() { 1457 | use super::{Placement, TreeList}; 1458 | 1459 | let mut tree = TreeList::::new(); 1460 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1461 | tree.insert_item(Placement::LastChild, 0, "2a".to_string()); 1462 | tree.insert_item(Placement::LastChild, 1, "3a".to_string()); 1463 | tree.insert_item(Placement::LastChild, 2, "4a".to_string()); 1464 | 1465 | tree.set_collapsed(0, true); 1466 | 1467 | assert_eq!(tree.to_vec(), vec![(0, true, "1".to_string(), 3, 1)]); 1468 | 1469 | let i = tree.row_to_item_index(0); 1470 | assert_eq!( 1471 | tree.insert_item(Placement::After, i, "5".to_string()), 1472 | Some(1) 1473 | ); 1474 | 1475 | assert_eq!( 1476 | tree.to_vec(), 1477 | vec![ 1478 | (0, true, "1".to_string(), 3, 1), 1479 | (0, false, "5".to_string(), 0, 1) 1480 | ] 1481 | ); 1482 | 1483 | let i = tree.row_to_item_index(1); 1484 | assert_eq!( 1485 | tree.insert_item(Placement::LastChild, i, "6a".to_string()), 1486 | Some(2) 1487 | ); 1488 | 1489 | let i = tree.row_to_item_index(1); 1490 | assert_eq!( 1491 | tree.insert_item(Placement::LastChild, i, "7".to_string()), 1492 | Some(3) 1493 | ); 1494 | 1495 | let i = tree.row_to_item_index(1); 1496 | tree.set_collapsed(i, true); 1497 | 1498 | assert_eq!( 1499 | tree.insert_item(Placement::After, i, "8".to_string()), 1500 | Some(2) 1501 | ); 1502 | } 1503 | 1504 | #[test] 1505 | fn test_remove_flat() { 1506 | use super::{Placement, TreeList}; 1507 | 1508 | let mut tree = TreeList::::new(); 1509 | tree.insert_item(Placement::Before, 0, "4".to_string()); 1510 | tree.insert_item(Placement::Before, 0, "1".to_string()); 1511 | tree.insert_item(Placement::Before, 1, "2".to_string()); 1512 | tree.insert_item(Placement::Before, 2, "3".to_string()); 1513 | 1514 | assert_eq!( 1515 | tree.to_vec(), 1516 | vec![ 1517 | (0, false, "1".to_string(), 0, 1), 1518 | (0, false, "2".to_string(), 0, 1), 1519 | (0, false, "3".to_string(), 0, 1), 1520 | (0, false, "4".to_string(), 0, 1) 1521 | ] 1522 | ); 1523 | 1524 | assert_eq!(tree.len(), 4); 1525 | assert_eq!(tree.height(), 4); 1526 | 1527 | assert_eq!(tree.remove(1), Some("2".to_string())); 1528 | assert_eq!(tree.len(), 3); 1529 | assert_eq!(tree.height(), 3); 1530 | assert_eq!( 1531 | tree.to_vec(), 1532 | vec![ 1533 | (0, false, "1".to_string(), 0, 1), 1534 | (0, false, "3".to_string(), 0, 1), 1535 | (0, false, "4".to_string(), 0, 1) 1536 | ] 1537 | ); 1538 | 1539 | assert_eq!(tree.remove(1), Some("3".to_string())); 1540 | assert_eq!(tree.len(), 2); 1541 | assert_eq!(tree.height(), 2); 1542 | assert_eq!( 1543 | tree.to_vec(), 1544 | vec![ 1545 | (0, false, "1".to_string(), 0, 1), 1546 | (0, false, "4".to_string(), 0, 1) 1547 | ] 1548 | ); 1549 | 1550 | assert_eq!(tree.remove(0), Some("1".to_string())); 1551 | assert_eq!(tree.len(), 1); 1552 | assert_eq!(tree.height(), 1); 1553 | assert_eq!(tree.to_vec(), vec![(0, false, "4".to_string(), 0, 1)]); 1554 | 1555 | assert_eq!(tree.remove(0), Some("4".to_string())); 1556 | assert_eq!(tree.len(), 0); 1557 | assert_eq!(tree.height(), 0); 1558 | assert_eq!(tree.to_vec(), vec![]); 1559 | } 1560 | 1561 | #[test] 1562 | fn test_remove_children() { 1563 | use super::{Placement, TreeList}; 1564 | 1565 | let mut tree = TreeList::::new(); 1566 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1567 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1568 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 1569 | tree.insert_item(Placement::LastChild, 2, "4".to_string()); 1570 | tree.insert_item(Placement::LastChild, 3, "5".to_string()); 1571 | 1572 | assert_eq!( 1573 | tree.to_vec(), 1574 | vec![ 1575 | (0, false, "1".to_string(), 4, 5), 1576 | (1, false, "2".to_string(), 3, 4), 1577 | (2, false, "3".to_string(), 2, 3), 1578 | (3, false, "4".to_string(), 1, 2), 1579 | (4, false, "5".to_string(), 0, 1) 1580 | ] 1581 | ); 1582 | 1583 | assert_eq!(tree.len(), 5); 1584 | assert_eq!(tree.height(), 5); 1585 | 1586 | assert_eq!(tree.remove(2), Some("3".to_string())); 1587 | assert_eq!(tree.len(), 4); 1588 | assert_eq!(tree.height(), 4); 1589 | assert_eq!( 1590 | tree.to_vec(), 1591 | vec![ 1592 | (0, false, "1".to_string(), 3, 4), 1593 | (1, false, "2".to_string(), 2, 3), 1594 | (2, false, "4".to_string(), 1, 2), 1595 | (3, false, "5".to_string(), 0, 1) 1596 | ] 1597 | ); 1598 | 1599 | assert_eq!(tree.remove(2), Some("4".to_string())); 1600 | assert_eq!(tree.len(), 3); 1601 | assert_eq!(tree.height(), 3); 1602 | assert_eq!( 1603 | tree.to_vec(), 1604 | vec![ 1605 | (0, false, "1".to_string(), 2, 3), 1606 | (1, false, "2".to_string(), 1, 2), 1607 | (2, false, "5".to_string(), 0, 1) 1608 | ] 1609 | ); 1610 | 1611 | assert_eq!(tree.remove(0), Some("1".to_string())); 1612 | assert_eq!(tree.len(), 2); 1613 | assert_eq!(tree.height(), 2); 1614 | assert_eq!( 1615 | tree.to_vec(), 1616 | vec![ 1617 | (0, false, "2".to_string(), 1, 2), 1618 | (1, false, "5".to_string(), 0, 1) 1619 | ] 1620 | ); 1621 | 1622 | assert_eq!(tree.remove(1), Some("5".to_string())); 1623 | assert_eq!(tree.len(), 1); 1624 | assert_eq!(tree.height(), 1); 1625 | assert_eq!(tree.to_vec(), vec![(0, false, "2".to_string(), 0, 1)]); 1626 | 1627 | assert_eq!(tree.remove(0), Some("2".to_string())); 1628 | assert_eq!(tree.len(), 0); 1629 | assert_eq!(tree.height(), 0); 1630 | assert_eq!(tree.to_vec(), vec![]); 1631 | } 1632 | 1633 | #[test] 1634 | fn test_remove_children_collapsed() { 1635 | use super::{Placement, TreeList}; 1636 | 1637 | let mut tree = TreeList::::new(); 1638 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1639 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1640 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 1641 | tree.insert_item(Placement::LastChild, 2, "4".to_string()); 1642 | tree.insert_item(Placement::LastChild, 3, "5".to_string()); 1643 | 1644 | tree.set_collapsed(2, true); 1645 | 1646 | assert_eq!( 1647 | tree.to_vec(), 1648 | vec![ 1649 | (0, false, "1".to_string(), 4, 3), 1650 | (1, false, "2".to_string(), 3, 2), 1651 | (2, true, "3".to_string(), 2, 1) 1652 | ] 1653 | ); 1654 | 1655 | assert_eq!(tree.len(), 5); 1656 | assert_eq!(tree.height(), 3); 1657 | 1658 | assert_eq!(tree.remove(2), Some("3".to_string())); 1659 | 1660 | assert_eq!(tree.len(), 4); 1661 | assert_eq!(tree.height(), 4); 1662 | } 1663 | 1664 | #[test] 1665 | fn test_remove_sibling() { 1666 | use super::{Placement, TreeList}; 1667 | 1668 | let mut tree = TreeList::::new(); 1669 | tree.insert_item(Placement::LastChild, 0, "Parent".to_string()); 1670 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1671 | tree.insert_item(Placement::After, 1, "2".to_string()); 1672 | tree.insert_item(Placement::After, 2, "3".to_string()); 1673 | tree.insert_item(Placement::After, 3, "4".to_string()); 1674 | 1675 | assert_eq!( 1676 | tree.to_vec(), 1677 | vec![ 1678 | (0, false, "Parent".to_string(), 4, 5), 1679 | (1, false, "1".to_string(), 0, 1), 1680 | (1, false, "2".to_string(), 0, 1), 1681 | (1, false, "3".to_string(), 0, 1), 1682 | (1, false, "4".to_string(), 0, 1) 1683 | ] 1684 | ); 1685 | 1686 | assert_eq!(tree.len(), 5); 1687 | assert_eq!(tree.height(), 5); 1688 | 1689 | assert_eq!(tree.remove(2), Some("2".to_string())); 1690 | assert_eq!(tree.len(), 4); 1691 | assert_eq!(tree.height(), 4); 1692 | assert_eq!( 1693 | tree.to_vec(), 1694 | vec![ 1695 | (0, false, "Parent".to_string(), 3, 4), 1696 | (1, false, "1".to_string(), 0, 1), 1697 | (1, false, "3".to_string(), 0, 1), 1698 | (1, false, "4".to_string(), 0, 1) 1699 | ] 1700 | ); 1701 | 1702 | assert_eq!(tree.remove(2), Some("3".to_string())); 1703 | assert_eq!(tree.len(), 3); 1704 | assert_eq!(tree.height(), 3); 1705 | assert_eq!( 1706 | tree.to_vec(), 1707 | vec![ 1708 | (0, false, "Parent".to_string(), 2, 3), 1709 | (1, false, "1".to_string(), 0, 1), 1710 | (1, false, "4".to_string(), 0, 1) 1711 | ] 1712 | ); 1713 | 1714 | assert_eq!(tree.remove(1), Some("1".to_string())); 1715 | assert_eq!(tree.len(), 2); 1716 | assert_eq!(tree.height(), 2); 1717 | assert_eq!( 1718 | tree.to_vec(), 1719 | vec![ 1720 | (0, false, "Parent".to_string(), 1, 2), 1721 | (1, false, "4".to_string(), 0, 1) 1722 | ] 1723 | ); 1724 | 1725 | assert_eq!(tree.remove(1), Some("4".to_string())); 1726 | assert_eq!(tree.len(), 1); 1727 | assert_eq!(tree.height(), 1); 1728 | assert_eq!(tree.to_vec(), vec![(0, false, "Parent".to_string(), 0, 1)]); 1729 | } 1730 | 1731 | #[test] 1732 | fn test_remove_with_children() { 1733 | use super::{Placement, TreeList}; 1734 | 1735 | let mut tree = TreeList::::new(); 1736 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1737 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1738 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 1739 | tree.insert_item(Placement::LastChild, 2, "4".to_string()); 1740 | tree.insert_item(Placement::LastChild, 3, "5".to_string()); 1741 | 1742 | assert_eq!( 1743 | tree.to_vec(), 1744 | vec![ 1745 | (0, false, "1".to_string(), 4, 5), 1746 | (1, false, "2".to_string(), 3, 4), 1747 | (2, false, "3".to_string(), 2, 3), 1748 | (3, false, "4".to_string(), 1, 2), 1749 | (4, false, "5".to_string(), 0, 1) 1750 | ] 1751 | ); 1752 | 1753 | assert_eq!(tree.len(), 5); 1754 | assert_eq!(tree.height(), 5); 1755 | 1756 | assert_eq!( 1757 | tree.remove_with_children(2), 1758 | Some(vec!["3".to_string(), "4".to_string(), "5".to_string()]) 1759 | ); 1760 | 1761 | assert_eq!(tree.len(), 2); 1762 | assert_eq!(tree.height(), 2); 1763 | assert_eq!( 1764 | tree.to_vec(), 1765 | vec![ 1766 | (0, false, "1".to_string(), 1, 2), 1767 | (1, false, "2".to_string(), 0, 1) 1768 | ] 1769 | ); 1770 | 1771 | assert_eq!(tree.remove_with_children(1), Some(vec!["2".to_string()])); 1772 | 1773 | assert_eq!(tree.len(), 1); 1774 | assert_eq!(tree.height(), 1); 1775 | assert_eq!(tree.to_vec(), vec![(0, false, "1".to_string(), 0, 1)]); 1776 | } 1777 | 1778 | #[test] 1779 | fn test_remove_with_children_flat() { 1780 | use super::{Placement, TreeList}; 1781 | 1782 | let mut tree = TreeList::::new(); 1783 | tree.insert_item(Placement::Before, 0, "4".to_string()); 1784 | tree.insert_item(Placement::Before, 0, "1".to_string()); 1785 | tree.insert_item(Placement::Before, 1, "2".to_string()); 1786 | tree.insert_item(Placement::Before, 2, "3".to_string()); 1787 | 1788 | assert_eq!( 1789 | tree.to_vec(), 1790 | vec![ 1791 | (0, false, "1".to_string(), 0, 1), 1792 | (0, false, "2".to_string(), 0, 1), 1793 | (0, false, "3".to_string(), 0, 1), 1794 | (0, false, "4".to_string(), 0, 1) 1795 | ] 1796 | ); 1797 | 1798 | assert_eq!(tree.len(), 4); 1799 | assert_eq!(tree.height(), 4); 1800 | 1801 | assert_eq!(tree.remove_with_children(1), Some(vec!["2".to_string()])); 1802 | assert_eq!(tree.len(), 3); 1803 | assert_eq!(tree.height(), 3); 1804 | 1805 | assert_eq!(tree.remove_with_children(1), Some(vec!["3".to_string()])); 1806 | assert_eq!(tree.len(), 2); 1807 | assert_eq!(tree.height(), 2); 1808 | } 1809 | 1810 | #[test] 1811 | fn test_remove_with_children_collapsed() { 1812 | use super::{Placement, TreeList}; 1813 | 1814 | let mut tree = TreeList::::new(); 1815 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1816 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1817 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 1818 | tree.insert_item(Placement::LastChild, 2, "4".to_string()); 1819 | tree.insert_item(Placement::LastChild, 3, "5".to_string()); 1820 | 1821 | tree.set_collapsed(2, true); 1822 | 1823 | assert_eq!( 1824 | tree.to_vec(), 1825 | vec![ 1826 | (0, false, "1".to_string(), 4, 3), 1827 | (1, false, "2".to_string(), 3, 2), 1828 | (2, true, "3".to_string(), 2, 1) 1829 | ] 1830 | ); 1831 | 1832 | assert_eq!(tree.len(), 5); 1833 | assert_eq!(tree.height(), 3); 1834 | 1835 | assert_eq!( 1836 | tree.remove_with_children(2), 1837 | Some(vec!["3".to_string(), "4".to_string(), "5".to_string()]) 1838 | ); 1839 | 1840 | assert_eq!(tree.len(), 2); 1841 | assert_eq!(tree.height(), 2); 1842 | } 1843 | 1844 | #[test] 1845 | fn test_insert_child_when_collapsed() { 1846 | use super::{Placement, TreeList}; 1847 | 1848 | let mut tree = TreeList::::new(); 1849 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1850 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1851 | 1852 | tree.set_collapsed(1, true); 1853 | 1854 | assert_eq!( 1855 | tree.to_vec(), 1856 | vec![ 1857 | (0, false, "1".to_string(), 1, 2), 1858 | (1, true, "2".to_string(), 0, 1), 1859 | ] 1860 | ); 1861 | 1862 | assert_eq!(tree.len(), 2); 1863 | assert_eq!(tree.height(), 2); 1864 | 1865 | assert_eq!( 1866 | tree.insert_item(Placement::LastChild, 1, "3".to_string()), 1867 | None 1868 | ); 1869 | 1870 | assert_eq!( 1871 | tree.to_vec(), 1872 | vec![ 1873 | (0, false, "1".to_string(), 2, 2), 1874 | (1, true, "2".to_string(), 1, 1) 1875 | ] 1876 | ); 1877 | assert_eq!(tree.len(), 3); 1878 | assert_eq!(tree.height(), 2); 1879 | 1880 | tree.set_collapsed(1, false); 1881 | 1882 | assert_eq!( 1883 | tree.to_vec(), 1884 | vec![ 1885 | (0, false, "1".to_string(), 2, 3), 1886 | (1, false, "2".to_string(), 1, 2), 1887 | (2, false, "3".to_string(), 0, 1) 1888 | ] 1889 | ); 1890 | 1891 | assert_eq!(tree.len(), 3); 1892 | assert_eq!(tree.height(), 3); 1893 | } 1894 | 1895 | #[test] 1896 | fn test_remove_child_when_collapsed() { 1897 | use super::{Placement, TreeList}; 1898 | 1899 | let mut tree = TreeList::::new(); 1900 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1901 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1902 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 1903 | tree.insert_item(Placement::LastChild, 1, "4".to_string()); 1904 | tree.insert_item(Placement::After, 0, "5".to_string()); 1905 | 1906 | tree.set_collapsed(1, true); 1907 | 1908 | assert_eq!( 1909 | tree.to_vec(), 1910 | vec![ 1911 | (0, false, "1".to_string(), 3, 2), 1912 | (1, true, "2".to_string(), 2, 1), 1913 | (0, false, "5".to_string(), 0, 1) 1914 | ] 1915 | ); 1916 | 1917 | assert_eq!(tree.len(), 5); 1918 | assert_eq!(tree.height(), 3); 1919 | 1920 | assert_eq!( 1921 | tree.remove_children(1), 1922 | Some(vec!["3".to_string(), "4".to_string()]) 1923 | ); 1924 | 1925 | assert_eq!( 1926 | tree.to_vec(), 1927 | vec![ 1928 | (0, false, "1".to_string(), 1, 2), 1929 | (1, true, "2".to_string(), 0, 1), 1930 | (0, false, "5".to_string(), 0, 1) 1931 | ] 1932 | ); 1933 | 1934 | assert_eq!(tree.len(), 3); 1935 | assert_eq!(tree.height(), 3); 1936 | } 1937 | 1938 | #[test] 1939 | fn test_collapse_within_collapsed_parent() { 1940 | use super::{Placement, TreeList}; 1941 | 1942 | let mut tree = TreeList::::new(); 1943 | tree.insert_item(Placement::LastChild, 0, "1".to_string()); 1944 | tree.insert_item(Placement::LastChild, 0, "2".to_string()); 1945 | tree.insert_item(Placement::LastChild, 1, "3".to_string()); 1946 | tree.insert_item(Placement::LastChild, 1, "4".to_string()); 1947 | 1948 | tree.set_collapsed(0, true); 1949 | 1950 | assert_eq!(tree.to_vec(), vec![(0, true, "1".to_string(), 3, 1),]); 1951 | 1952 | assert_eq!(tree.len(), 4); 1953 | assert_eq!(tree.height(), 1); 1954 | 1955 | println!("foo"); 1956 | tree.set_collapsed(1, true); 1957 | assert_eq!(tree.to_vec(), vec![(0, true, "1".to_string(), 3, 1),]); 1958 | 1959 | assert_eq!(tree.len(), 4); 1960 | assert_eq!(tree.height(), 1); 1961 | 1962 | println!("bar"); 1963 | tree.set_collapsed(0, false); 1964 | 1965 | tree.print(); 1966 | 1967 | assert_eq!( 1968 | tree.to_vec(), 1969 | vec![ 1970 | (0, false, "1".to_string(), 3, 2), 1971 | (1, true, "2".to_string(), 2, 1) 1972 | ] 1973 | ); 1974 | 1975 | assert_eq!(tree.len(), 4); 1976 | assert_eq!(tree.height(), 2); 1977 | } 1978 | 1979 | #[test] 1980 | fn test_insert_container_collapse() { 1981 | use super::{Placement, TreeList}; 1982 | 1983 | let mut tree = TreeList::::new(); 1984 | tree.insert_container_item(Placement::LastChild, 0, "1".to_string()); 1985 | 1986 | assert_eq!(tree.to_vec(), vec![(0, true, "1".to_string(), 0, 1)]); 1987 | 1988 | assert_eq!(tree.len(), 1); 1989 | assert_eq!(tree.height(), 1); 1990 | 1991 | tree.set_collapsed(0, false); 1992 | 1993 | assert_eq!(tree.to_vec(), vec![(0, false, "1".to_string(), 0, 1)]); 1994 | } 1995 | 1996 | #[test] 1997 | fn test_custom_tree_item() { 1998 | use super::{Placement, TreeList}; 1999 | use std::fmt; 2000 | 2001 | #[derive(Debug, Eq, PartialEq)] 2002 | struct TreeItem { 2003 | value: usize, 2004 | } 2005 | 2006 | impl fmt::Display for TreeItem { 2007 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 2008 | write!(f, "TreeItem<{}>", self.value) 2009 | } 2010 | } 2011 | 2012 | let mut tree = TreeList::::new(); 2013 | tree.insert_item(Placement::After, 0, TreeItem { value: 42 }); 2014 | 2015 | assert_eq!(tree.remove(0).unwrap(), TreeItem { value: 42 }); 2016 | } 2017 | } 2018 | --------------------------------------------------------------------------------