use std::convert::TryFrom;
use std::sync::LazyLock;
use crate::prelude::*;
pub use self::builder::MenuBuilder as Builder;
pub type Menu <'element> = Widget <'element,
Selection, model::Component, view::Component>;
pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
controls::Builder::new()
.buttons (vec![
controls::button::Builtin::MenuNextItem,
controls::button::Builtin::MenuPreviousItem,
controls::button::Builtin::MenuFirstItem,
controls::button::Builtin::MenuLastItem
].into_iter().map (Into::into).collect::<Vec <_>>().into())
.build()
);
pub fn next_item (
_ : &controls::button::Release,
elements : &Tree <Element>,
frame_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
use controller::component::{Kind, Selection};
log::trace!("next_item...");
let frame = elements.get (frame_id).unwrap();
let mut children_ids = frame.children().iter();
let menu_id = children_ids.next().unwrap();
let Widget (selection, _, _) = Menu::try_get (elements, menu_id).unwrap();
if let Some (selected_id) = selection.current.as_ref() {
for child_id in children_ids.by_ref() {
if child_id == selected_id {
debug_assert_eq!(
elements.get_element (child_id).controller.state,
State::Focused);
break
}
}
}
let mut select_id = None;
for next_id in children_ids {
let next = elements.get_element (next_id);
if next.controller.state == State::Enabled && Frame::try_from (next).is_ok() {
select_id = Some (next_id);
break
}
}
if select_id.is_none() && selection.loop_ {
for next_id in frame.children().iter().skip (1) {
let next = elements.get_element (next_id);
if next.controller.state == State::Enabled && Frame::try_from (next).is_ok() {
select_id = Some (next_id);
break
}
}
}
if let Some (next_id) = select_id {
let select_id = next_id.clone();
let select_next = Box::new (move |controller : &mut Controller|{
let selection = Selection::try_ref_mut (&mut controller.component).unwrap();
selection.current = Some (select_id);
});
action_buffer.push ((menu_id.clone(), Action::ModifyController (select_next)));
action_buffer.push ((next_id.clone(), Action::Focus));
}
log::trace!("...next_item");
}
pub fn previous_item (
_ : &controls::button::Release,
elements : &Tree <Element>,
frame_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
use controller::component::{Kind, Selection};
log::trace!("previous_item...");
let frame = elements.get (frame_id).unwrap();
let mut children_ids = frame.children().iter();
let menu_id = children_ids.next().unwrap();
let Widget (selection, _, _) = Menu::try_get (elements, menu_id).unwrap();
let mut children_ids = children_ids.rev();
if let Some (selected_id) = selection.current.as_ref() {
for child_id in children_ids.by_ref() {
if child_id == selected_id {
debug_assert_eq!(
elements.get_element (child_id).controller.state,
State::Focused);
break
}
}
}
let mut select_id = None;
for prev_id in children_ids {
let prev = elements.get_element (prev_id);
if prev.controller.state == State::Enabled && Frame::try_from (prev).is_ok() {
select_id = Some (prev_id);
break
}
}
if select_id.is_none() && selection.loop_ {
for prev_id in frame.children().iter().rev() {
let prev = elements.get_element (prev_id);
if prev.controller.state == State::Enabled && Frame::try_from (prev).is_ok() {
select_id = Some (prev_id);
break
}
}
}
if let Some (prev_id) = select_id {
let select_id = prev_id.clone();
let select_prev = Box::new (move |controller : &mut Controller|{
let selection = Selection::try_ref_mut (&mut controller.component).unwrap();
selection.current = Some (select_id);
});
action_buffer.push ((menu_id.clone(), Action::ModifyController (select_prev)));
action_buffer.push ((prev_id.clone(), Action::Focus));
}
log::trace!("...previous_item");
}
pub fn first_item (
_ : &controls::button::Release,
elements : &Tree <Element>,
frame_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
use controller::component::{Kind, Selection};
log::trace!("first_item...");
let frame = elements.get (frame_id).unwrap();
let mut children_ids = frame.children().iter();
let menu_id = children_ids.next().unwrap();
let mut select_id = None;
for child_id in children_ids {
let next = elements.get_element (child_id);
if next.controller.state == State::Enabled && Frame::try_from (next).is_ok() {
select_id = Some (child_id);
break
}
}
if let Some (first_id) = select_id {
let select_id = first_id.clone();
let select_first = Box::new (move |controller : &mut Controller|{
let selection = Selection::try_ref_mut (&mut controller.component).unwrap();
selection.current = Some (select_id);
});
action_buffer.push ((menu_id.clone(), Action::ModifyController (select_first)));
action_buffer.push ((first_id.clone(), Action::Focus));
}
log::trace!("...first_item");
}
pub fn last_item (
_ : &controls::button::Release,
elements : &Tree <Element>,
frame_id : &NodeId,
action_buffer : &mut Vec <(NodeId, Action)>
) {
use controller::component::{Kind, Selection};
log::trace!("last_item...");
let frame = elements.get (frame_id).unwrap();
let mut children_ids = frame.children().iter();
let menu_id = children_ids.next().unwrap();
let mut select_id = None;
for child_id in children_ids.rev() {
let next = elements.get_element (child_id);
if next.controller.state == State::Enabled && Frame::try_from (next).is_ok() {
select_id = Some (child_id);
break
}
}
if let Some (last_id) = select_id {
let select_id = last_id.clone();
let select_last = Box::new (move |controller : &mut Controller|{
let selection = Selection::try_ref_mut (&mut controller.component).unwrap();
selection.current = Some (select_id);
});
action_buffer.push ((menu_id.clone(), Action::ModifyController (select_last)));
action_buffer.push ((last_id.clone(), Action::Focus));
}
log::trace!("...last_item");
}
pub fn get_selected_id <'a> (elements : &'a Tree <Element>, menu_id : &NodeId)
-> Option <&'a NodeId>
{
let Widget (selection, _, _) = Menu::try_get (elements, menu_id).unwrap();
selection.current.as_ref()
}
pub (crate) fn find_first_item (
elements : &Tree <Element>, node_ids : std::slice::Iter <NodeId>
) -> Option <NodeId> {
let mut first_item = None;
for node_id in node_ids {
let node = elements.get_element (node_id);
if node.controller.state == State::Enabled && Frame::try_from (node).is_ok() {
first_item = Some (node_id.clone());
break
}
}
first_item
}
mod builder {
use derive_builder::Builder;
use crate::prelude::*;
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
struct Menu <'a, A : Application> {
elements : &'a Tree <Element>,
frame_id : &'a NodeId,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
name : Option <String>,
#[builder(default)]
selection : Selection
}
impl <'a, A : Application> MenuBuilder <'a, A> {
pub const fn new (elements : &'a Tree <Element>, frame_id : &'a NodeId) -> Self {
MenuBuilder {
elements: Some (elements),
frame_id: Some (frame_id),
bindings: None,
name: None,
selection: None
}
}
}
impl <A : Application + 'static> BuildActions for MenuBuilder <'_, A> {
fn build_actions (self) -> Vec<(NodeId, Action)> {
log::trace!("build actions...");
let Menu { elements, frame_id, bindings, name, selection } = self.build()
.map_err(|err| log::error!("frame builder error: {err:?}"))
.unwrap();
let bindings_empty = Bindings::empty();
let bindings = bindings.unwrap_or (&bindings_empty)
.get_bindings (&super::CONTROLS);
let mut out = Vec::new();
{ let set_controls = Box::new (move |controller : &mut Controller|
controller.add_bindings (&bindings));
out.push ((frame_id.clone(), Action::ModifyController (set_controls)));
}
{ let set_focus_top_false = Box::new (|controller : &mut Controller|
controller.focus_top = false);
for child_id in elements.children_ids (frame_id).unwrap() {
out.push ((child_id.clone(),
Action::ModifyController (set_focus_top_false.clone())));
}
}
{ let selection = {
let controller = ControllerComponent::from (selection).into();
let name = name.unwrap_or ("Menu".to_string());
Element::new (name, controller, Model::default(), View::default())
};
out.push ((
frame_id.clone(),
Action::create_singleton (selection, CreateOrder::Prepend)));
}
if elements.get_element (frame_id).controller.state == State::Focused
&& let Some (focus_id) = super::find_first_item (
elements, elements.get (frame_id).unwrap().children().iter())
{
out.push ((focus_id, Action::Focus))
}
log::trace!("...build actions");
out
}
}
}