#![warn(missing_docs)]
use crate::{
core::{
pool::Handle, reflect::prelude::*, type_traits::prelude::*, variable::InheritableVariable,
visitor::prelude::*,
},
message::{KeyCode, MessageDirection, UiMessage},
scroll_viewer::{ScrollViewer, ScrollViewerMessage},
widget::{Widget, WidgetBuilder, WidgetMessage},
BuildContext, Control, UiNode, UserInterface,
};
use fyrox_graph::SceneGraph;
use std::ops::{Deref, DerefMut};
#[derive(Default, Clone, Visit, Reflect, Debug, TypeUuidProvider, ComponentProvider)]
#[type_uuid(id = "135d347b-5019-4743-906c-6df5c295a3be")]
pub struct NavigationLayer {
pub widget: Widget,
pub bring_into_view: InheritableVariable<bool>,
}
crate::define_widget_deref!(NavigationLayer);
#[derive(Debug)]
struct OrderedHandle {
tab_index: usize,
handle: Handle<UiNode>,
}
impl Control for NavigationLayer {
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.widget.handle_routed_message(ui, message);
if let Some(WidgetMessage::KeyDown(KeyCode::Tab)) = message.data() {
let mut tab_list = Vec::new();
for &child in self.children() {
for (descendant_handle, descendant_ref) in ui.traverse_iter(child) {
if !*descendant_ref.tab_stop && descendant_ref.is_globally_visible() {
if let Some(tab_index) = *descendant_ref.tab_index {
tab_list.push(OrderedHandle {
tab_index,
handle: descendant_handle,
});
}
}
}
}
if !tab_list.is_empty() {
tab_list.sort_by_key(|entry| entry.tab_index);
let focused_index = tab_list
.iter()
.position(|entry| entry.handle == ui.keyboard_focus_node)
.unwrap_or_default();
let next_focused_node_index = if ui.keyboard_modifiers.shift {
let count = tab_list.len() as isize;
let mut prev = (focused_index as isize).saturating_sub(1);
if prev < 0 {
prev += count;
}
(prev % count) as usize
} else {
focused_index.saturating_add(1) % tab_list.len()
};
if let Some(entry) = tab_list.get(next_focused_node_index) {
ui.send_message(WidgetMessage::focus(
entry.handle,
MessageDirection::ToWidget,
));
if *self.bring_into_view {
if let Some((scroll_viewer, _)) =
ui.find_component_up::<ScrollViewer>(entry.handle)
{
ui.send_message(ScrollViewerMessage::bring_into_view(
scroll_viewer,
MessageDirection::ToWidget,
entry.handle,
));
}
}
}
}
}
}
}
pub struct NavigationLayerBuilder {
widget_builder: WidgetBuilder,
bring_into_view: bool,
}
impl NavigationLayerBuilder {
pub fn new(widget_builder: WidgetBuilder) -> Self {
Self {
widget_builder,
bring_into_view: true,
}
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
let navigation_layer = NavigationLayer {
widget: self.widget_builder.build(ctx),
bring_into_view: self.bring_into_view.into(),
};
ctx.add_node(UiNode::new(navigation_layer))
}
}
#[cfg(test)]
mod test {
use crate::navigation::NavigationLayerBuilder;
use crate::{test::test_widget_deletion, widget::WidgetBuilder};
#[test]
fn test_deletion() {
test_widget_deletion(|ctx| NavigationLayerBuilder::new(WidgetBuilder::new()).build(ctx));
}
}