From 1e274287f65e032e58978129569315d13f63d56c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?joseLu=C3=ADs?= Date: Wed, 7 Apr 2021 14:17:49 +0200 Subject: [PATCH] rust: add NcTree widget. - created all methods except .goto(). - WIP callback types. - minor updates. - rustfmt. --- rust/src/bindings.rs | 21 ++++ rust/src/widgets/menu/mod.rs | 6 +- rust/src/widgets/mod.rs | 2 + rust/src/widgets/tree/methods/mod.rs | 32 ++++++ rust/src/widgets/tree/methods/options.rs | 36 +++++++ rust/src/widgets/tree/methods/tree.rs | 122 +++++++++++++++++++++++ rust/src/widgets/tree/mod.rs | 89 +++++++++++++++++ 7 files changed, 305 insertions(+), 3 deletions(-) create mode 100644 rust/src/widgets/tree/methods/mod.rs create mode 100644 rust/src/widgets/tree/methods/options.rs create mode 100644 rust/src/widgets/tree/methods/tree.rs create mode 100644 rust/src/widgets/tree/mod.rs diff --git a/rust/src/bindings.rs b/rust/src/bindings.rs index dba75c037..f159cd202 100644 --- a/rust/src/bindings.rs +++ b/rust/src/bindings.rs @@ -622,6 +622,27 @@ pub use ffi::{ nctablet_userptr, }; +// nctree ---------------------------------------------------------------------- +// +// // structs +// nctree, +// nctree_item, +// nctree_options, +// +#[doc(inline)] +pub use ffi::{ + // functions + nctree_create, + nctree_destroy, + nctree_focused, + nctree_goto, + nctree_next, + nctree_offer_input, + nctree_plane, + nctree_prev, + nctree_redraw, +}; + // ncvisual -------------------------------------------------------------------- // // already wrapped: diff --git a/rust/src/widgets/menu/mod.rs b/rust/src/widgets/menu/mod.rs index d5096d805..5d249c651 100644 --- a/rust/src/widgets/menu/mod.rs +++ b/rust/src/widgets/menu/mod.rs @@ -42,13 +42,13 @@ mod methods; /// `type in C: ncmenu (struct)` pub type NcMenu = crate::bindings::ffi::ncmenu; -/// Options struct for [`NcMenu`] +/// Options struct for [`NcMenu`]. pub type NcMenuOptions = crate::bindings::ffi::ncmenu_options; -/// Item for [`NcMenu`] +/// Item for [`NcMenu`]. pub type NcMenuItem = crate::bindings::ffi::ncmenu_item; -/// Section for [`NcMenu`] +/// Section for [`NcMenu`]. pub type NcMenuSection = crate::bindings::ffi::ncmenu_section; /// [NcMenuOptions] flag: Bottom row (as opposed to top row). diff --git a/rust/src/widgets/mod.rs b/rust/src/widgets/mod.rs index e994e23d8..4cd736c3a 100644 --- a/rust/src/widgets/mod.rs +++ b/rust/src/widgets/mod.rs @@ -7,6 +7,7 @@ mod progbar; mod reader; mod reel; mod selector; +mod tree; pub use menu::*; pub use multiselector::*; @@ -15,3 +16,4 @@ pub use progbar::*; pub use reader::*; pub use reel::*; pub use selector::*; +pub use tree::*; diff --git a/rust/src/widgets/tree/methods/mod.rs b/rust/src/widgets/tree/methods/mod.rs new file mode 100644 index 000000000..e2bc74460 --- /dev/null +++ b/rust/src/widgets/tree/methods/mod.rs @@ -0,0 +1,32 @@ +//! `NcTree*` methods and associated functions. + +mod options; +mod tree; + +pub use options::*; +pub use tree::*; + +use core::ptr::null_mut; +use std::ffi::{c_void, CString}; + +use crate::NcTreeItem; + +/// # `NcTreeItem` constructor +impl NcTreeItem { + /// Creates an [NcTreeItem]. + pub fn new(curry: &str, subs: Option<&mut [NcTreeItem]>, subcount: usize) -> Self { + if let Some(subs) = subs { + return Self { + curry: CString::new(curry).unwrap().into_raw() as *mut _ as *mut c_void, + subs: subs.as_mut_ptr(), + subcount: subcount as u32, + }; + } else { + return Self { + curry: CString::new(curry).unwrap().into_raw() as *mut _ as *mut c_void, + subs: null_mut(), + subcount: subcount as u32, + }; + } + } +} diff --git a/rust/src/widgets/tree/methods/options.rs b/rust/src/widgets/tree/methods/options.rs new file mode 100644 index 000000000..b495554f1 --- /dev/null +++ b/rust/src/widgets/tree/methods/options.rs @@ -0,0 +1,36 @@ +use crate::{NcDim, NcTreeItem, NcTreeItemCbUnsafe, NcTreeOptions}; + +/// # `NcTreeOptions` constructors +impl NcTreeOptions { + /// New NcTreeOptions for [NcTree][crate::NcTree]. + pub fn new(items: &[NcTreeItem], indentcols: NcDim) -> Self { + Self::with_all_args(items, items.len(), None, indentcols, 0) + } + + /// New NcTreeOptions for [NcTree][crate::NcTree], with all args. + pub fn with_all_args( + // top-level nctree_item array + items: &[NcTreeItem], + + // size of |items| + count: usize, + + // item callback function + // TODO: use NcTreeItemCb and convert to NcTreeItemCbUnsafe + nctreecb: Option, + + // columns to indent per level of hierarchy + indentcols: NcDim, + + // bitfield of `NCTREE_OPTION_*` (there's none for now) + flags: u64, + ) -> Self { + Self { + items: items as *const _ as *const NcTreeItem, + count: count as u32, + nctreecb, + indentcols: indentcols as i32, + flags, + } + } +} diff --git a/rust/src/widgets/tree/methods/tree.rs b/rust/src/widgets/tree/methods/tree.rs new file mode 100644 index 000000000..42cde83e9 --- /dev/null +++ b/rust/src/widgets/tree/methods/tree.rs @@ -0,0 +1,122 @@ +use core::ptr::null_mut; + +use crate::{ + error, error_ref_mut, nctree_create, NcError, NcInput, NcPlane, NcResult, NcTree, NcTreeItem, + NcTreeOptions, NCRESULT_ERR, +}; + +/// # `NcTree` constructors & destructors +impl NcTree { + /// Creates an [NcTree] with the specified options. + /// + /// *C style function: [nctree_create()][crate::nctree_create].* + pub fn new<'a>(plane: &mut NcPlane, options: NcTreeOptions) -> NcResult<&'a mut Self> { + error_ref_mut![unsafe { nctree_create(plane, &options) }, "Creating NcTree"] + } + + /// Destroys an NcTree created with [new()][NcTree#method.new]. + /// + /// *C style function: [nctree_destroy()][crate::nctree_destroy].* + pub fn destroy(&mut self) { + unsafe { crate::nctree_destroy(self) }; + } +} + +/// # `NcTree` methods +impl NcTree { + // NOTE: not implemented yet in C API + // + // /// Goes to the item specified by the array |spec|, terminated by UINT_MAX. + // /// + // /// If the spec is invalid, returns an error and the depth of the first + // /// invalid spec is written to *|failspec|. + // /// + // /// Otherwise, the true depth is written to *|failspec|, + // /// and the curry is returned (|failspec| is necessary because the + // /// curry could itself be NULL). + // /// + // /// *C style function: [nctree_goto()][crate::nctree_goto].* + // pub fn goto(&mut self, spec: ... , failspec: ...) -> NcResult<&mut NcTreeItem> { + // let res = unsafe { crate::nctree_goto(self) }; + // if res != null_mut() { + // return Ok(unsafe { &mut *(res as *mut NcTreeItem) }); + // } else { + // Err(NcError::with_msg(NCRESULT_ERR, "NcTree.goto()")) + // } + // } + + /// Returns the focused item, if any items are present. + /// + /// This is not a copy; be careful to use it only for the duration of a + /// critical section. + /// + /// *C style function: [nctree_focused()][crate::nctree_focused].* + pub fn focused(&mut self) -> NcResult<&mut NcTreeItem> { + let res = unsafe { crate::nctree_focused(self) }; + if res != null_mut() { + return Ok(unsafe { &mut *(res as *mut NcTreeItem) }); + } else { + Err(NcError::with_msg(NCRESULT_ERR, "NcTree.focused()")) + } + } + + /// Changes the focus to the next item, and returns it. + /// + /// *C style function: [nctree_next()][crate::nctree_next].* + pub fn next(&mut self) -> NcResult<&mut NcTreeItem> { + let res = unsafe { crate::nctree_next(self) }; + if res != null_mut() { + return Ok(unsafe { &mut *(res as *mut NcTreeItem) }); + } else { + Err(NcError::with_msg(NCRESULT_ERR, "NcTree.next()")) + } + } + + /// Changes the focus to the previous item, and returns it. + /// + /// *C style function: [nctree_prev()][crate::nctree_prev].* + pub fn prev(&mut self) -> NcResult<&mut NcTreeItem> { + let res = unsafe { crate::nctree_prev(self) }; + if res != null_mut() { + return Ok(unsafe { &mut *(res as *mut NcTreeItem) }); + } else { + Err(NcError::with_msg(NCRESULT_ERR, "NcTree.prev()")) + } + } + + /// Offers the `input` to this NcTree. + /// + /// If it's relevant, this function returns true, + /// and the input ought not be processed further. + /// If it's irrelevant to the tree, false is returned. + /// + /// Relevant inputs include: + /// + /// - a mouse click on an item (focuses item) + /// - a mouse scrollwheel event (srolls tree) + /// - up, down, pgup, or pgdown (navigates among items) + /// + /// *C style function: [nctree_offer_input()][crate::nctree_offer_input].* + pub fn offer_input(&mut self, input: NcInput) -> bool { + unsafe { crate::nctree_offer_input(self, &input) } + } + + /// Returns the [NcPlane] backing this NcTree. + /// + /// *C style function: [nctree_plane()][crate::nctree_plane].* + pub fn plane(&mut self) -> NcResult<&NcPlane> { + error_ref_mut![unsafe { crate::nctree_plane(self) }, "NcTree.plane()"] + } + + /// Redraws the NcTree in its entirety. + /// + /// The tree will be cleared, and items will be laid out, using the focused + /// item as a fulcrum. + /// + /// Item-drawing callbacks will be invoked for each visible item. + /// + /// *C style function: [nctree_redraw()][crate::nctree_redraw].* + pub fn redraw(&mut self) -> NcResult<()> { + error![unsafe { crate::nctree_redraw(self) }, "NcTree.redraw()"] + } +} diff --git a/rust/src/widgets/tree/mod.rs b/rust/src/widgets/tree/mod.rs new file mode 100644 index 000000000..ab507c582 --- /dev/null +++ b/rust/src/widgets/tree/mod.rs @@ -0,0 +1,89 @@ +//! `NcTree` widget + +// functions already exported by bindgen : 13 +// ------------------------------------------ +// (#) test: 0 +// (W) wrap: 13 +// ------------------------------------------ +//W nctree_create, +//W nctree_destroy, +//W nctree_focused, +//~ nctree_goto, +//W nctree_next, +//W nctree_offer_input, +//W nctree_plane, +//W nctree_prev, +//W nctree_redraw, + +use cty::c_int; +use std::ffi::c_void; + +use crate::NcPlane; + +mod methods; + +/// High-level hierarchical line-based data. +/// +/// `NcTree`s organize static hierarchical items, and allow them to be browsed. +/// +/// An NcTree cannot be empty, count must be non-zero. +/// +/// - Each item can have arbitrary subitems. +/// - Items can be collapsed and expanded. +/// - The display supports scrolling and searching. +/// - Items cannot be added or removed, however; they must be provided in their +/// entirety at creation time. +/// +/// NOTE: `NcTree` shares many properties with `NcReel`. Unlike the latter, +/// `NcTree`s support arbitrary hierarchical levels, but they do not allow +/// elements to come and go across the lifetime of the widget. +/// +/// `type in C: nctree (struct)` +pub type NcTree = crate::bindings::ffi::nctree; + +/// Item for [`NcTree`]. +/// +/// each item has a curry, and zero or more subitems. +pub type NcTreeItem = crate::bindings::ffi::nctree_item; + +/// Options struct for [`NcTree`]. +pub type NcTreeOptions = crate::bindings::ffi::nctree_options; + +// e.g.: +// +// ```c +// int treecb(struct ncplane* n, void* curry, int pos){ +// ncplane_printf_yx(n, 0, 0, "item: %s pos: %d", +// static_cast(curry), pos); +// return 0; +// } +// ``` + +/// An [NcTreeItem] callback function (unsafe). +pub type NcTreeItemCbUnsafe = unsafe extern "C" fn(*mut NcPlane, *mut c_void, c_int) -> c_int; + +/// An [NcTreeItem] callback function. +pub type NcTreeItemCb = fn(&mut NcPlane, &str, i32); + +// WIP TODO: create callback type and conversion functions +// +// /// Converts [NcTreeItemCbUnsafe] to [NcTreeItemCb]. +// pub fn nctreeitemcb_to_rust(resizecb: Option) -> Option { +// if let Some(cb) = resizecb { +// return Some(unsafe { core::mem::transmute(cb) }); +// } else { +// None +// } +// } +// +// /// Converts [NcTreeItemCb] to [NcTreeItemCbUnsafe]. +// /// +// // waiting for https://github.com/rust-lang/rust/issues/53605 +// // to make this function const, and then NcPlaneOptions constructors. +// pub fn nctreeitemcb_to_c(resizecb: Option) -> Option { +// if let Some(cb) = resizecb { +// return Some(unsafe { core::mem::transmute(cb) }); +// } else { +// None +// } +// }