use super::InterfaceItem;
use crate::events::Events;
use crate::text_buffer::TextBuffer;
use crate::text_processing::{DefaultProcessor, TextProcessor};
use glutin::VirtualKeyCode;
#[derive(Default)]
pub struct MenuList<'a> {
items_ref: Vec<Box<&'a mut dyn InterfaceItem>>,
positions: Vec<MenuPosition>,
}
impl<'a> MenuList<'a> {
pub fn new() -> MenuList<'a> {
MenuList {
items_ref: Vec::new(),
positions: Vec::new(),
}
}
pub fn with_item<T: 'static + InterfaceItem + Clone, U: Into<Option<MenuPosition>>>(
mut self,
item: &'a mut T,
position: U,
) -> MenuList<'a> {
self.items_ref.push(Box::new(item));
if let Some(position) = position.into() {
self.positions.push(position);
} else {
self.positions.push(MenuPosition::RelativeToLast(0, 0));
}
self
}
pub fn add_item<T: 'static + InterfaceItem + Clone, U: Into<Option<MenuPosition>>>(
&mut self,
item: &'a mut T,
position: U,
) {
self.items_ref.push(Box::new(item));
if let Some(position) = position.into() {
self.positions.push(position);
} else {
self.positions.push(MenuPosition::RelativeToLast(0, 0));
}
}
pub(crate) fn get_cloned_list(&self) -> Vec<Box<dyn InterfaceItem>> {
let mut list = Vec::new();
for item in &self.items_ref {
list.push(item.clone_box());
}
list
}
}
#[derive(Clone, Copy)]
pub enum GrowthDirection {
Down,
Up,
Right,
Left,
}
pub enum MenuPosition {
Relative(i32, i32),
RelativeToLast(i32, i32),
Absolute(u32, u32),
}
pub enum FocusSelection {
Keyboard(Option<VirtualKeyCode>, Option<VirtualKeyCode>),
Mouse(),
MouseAndKeyboard(Option<VirtualKeyCode>, Option<VirtualKeyCode>),
}
pub struct Menu {
x: u32,
y: u32,
focused: bool,
is_dirty: bool,
select_idx: u32,
total_width: u32,
total_height: u32,
cloned_interface_items: Vec<Box<dyn InterfaceItem>>,
growth_direction: GrowthDirection,
focus_selection: FocusSelection,
text_processor: Box<dyn TextProcessor>,
}
impl Default for Menu {
fn default() -> Menu {
Menu {
x: 0,
y: 0,
focused: false,
is_dirty: true,
select_idx: 0,
total_width: 0,
total_height: 0,
cloned_interface_items: Vec::new(),
growth_direction: GrowthDirection::Down,
focus_selection: FocusSelection::Keyboard(None, None),
text_processor: Box::new(DefaultProcessor),
}
}
}
impl Menu {
pub fn new() -> Menu {
Default::default()
}
pub fn with_pos(mut self, position: (u32, u32)) -> Menu {
let (x, y) = position;
self.x = x;
self.y = y;
self
}
pub fn with_focus(mut self, focused: bool) -> Menu {
self.focused = focused;
self
}
pub fn with_growth_direction(mut self, growth_direction: GrowthDirection) -> Menu {
self.growth_direction = growth_direction;
self
}
pub fn with_focus_selection(mut self, focus_selection: FocusSelection) -> Menu {
self.focus_selection = focus_selection;
self
}
pub fn with_text_processor<T: 'static + TextProcessor>(mut self, processor: T) -> Menu {
self.text_processor = Box::new(processor);
self
}
pub fn set_pos(&mut self, pos: (u32, u32)) {
let (x, y) = pos;
self.x = x;
self.y = y;
}
pub fn set_focused(&mut self, focused: bool) {
self.focused = focused;
}
pub fn set_growth_direction(&mut self, growth_direction: GrowthDirection) {
self.growth_direction = growth_direction;
}
pub fn set_focus_selection(&mut self, focus_selection: FocusSelection) {
self.focus_selection = focus_selection;
}
pub fn set_text_processor<T: 'static + TextProcessor>(&mut self, processor: T) {
self.text_processor = Box::new(processor);
}
pub fn get_pos(&self) -> (u32, u32) {
(self.x, self.y)
}
pub fn get_total_width(&self) -> u32 {
self.total_width
}
pub fn get_total_height(&self) -> u32 {
self.total_height
}
pub fn is_focused(&self) -> bool {
self.focused
}
pub fn get_select_idx(&self) -> u32 {
self.select_idx
}
pub fn set_select_idx(&mut self, idx: u32) {
self.select_idx = (idx as i32)
.min(self.cloned_interface_items.len() as i32 - 1)
.max(0) as u32;
}
pub fn get_previous_button(&self) -> VirtualKeyCode {
let previous_button;
match self.focus_selection {
FocusSelection::Keyboard(previous, _) => previous_button = previous,
FocusSelection::Mouse() => previous_button = None,
FocusSelection::MouseAndKeyboard(previous, _) => previous_button = previous,
}
if let Some(button) = previous_button {
button
} else {
match self.growth_direction {
GrowthDirection::Up => VirtualKeyCode::Down,
GrowthDirection::Down => VirtualKeyCode::Up,
GrowthDirection::Left => VirtualKeyCode::Right,
GrowthDirection::Right => VirtualKeyCode::Left,
}
}
}
pub fn get_next_button(&self) -> VirtualKeyCode {
let next_button;
match self.focus_selection {
FocusSelection::Keyboard(_, next) => next_button = next,
FocusSelection::Mouse() => next_button = None,
FocusSelection::MouseAndKeyboard(_, next) => next_button = next,
}
if let Some(button) = next_button {
button
} else {
match self.growth_direction {
GrowthDirection::Up => VirtualKeyCode::Up,
GrowthDirection::Down => VirtualKeyCode::Down,
GrowthDirection::Left => VirtualKeyCode::Left,
GrowthDirection::Right => VirtualKeyCode::Right,
}
}
}
pub fn get_cloned_list(&self) -> &[Box<dyn InterfaceItem>] {
&self.cloned_interface_items
}
pub fn update(
&mut self,
events: &Events,
delta: f32,
text_buffer: &TextBuffer,
list: &mut MenuList,
) -> bool {
let length = list.items_ref.len() as i32;
self.select_idx = (self.select_idx as i32).min(length - 1).max(0) as u32;
if self.focused {
self.handle_events(events, list, text_buffer);
}
let start_idx = (self.select_idx as i32).min(length - 1).max(0) as u32;
while {
!list.items_ref[self.select_idx as usize]
.get_base()
.can_be_focused
} {
self.select_idx = (((self.select_idx as i32) + 1) % length) as u32;
if self.select_idx == start_idx {
break;
}
}
for (idx, item) in (&mut list.items_ref).iter_mut().enumerate() {
item.get_mut_base()
.set_focused((self.select_idx == idx as u32) && self.focused);
item.update(delta, &*self.text_processor);
}
self.is_dirty = self.children_are_dirty(&mut list.items_ref);
if self.is_dirty {
self.set_new_positions(&list);
}
self.is_dirty
}
pub fn draw(&mut self, text_buffer: &mut TextBuffer) {
for item in &mut self.cloned_interface_items {
item.draw(text_buffer);
}
}
fn handle_events(&mut self, events: &Events, list: &mut MenuList, text_buffer: &TextBuffer) {
let length = list.items_ref.len();
if length == 0 {
self.select_idx = 0;
return;
}
let mut focused_handled_input = false;
if let Some(item) = (&mut list.items_ref).get_mut(self.select_idx as usize) {
focused_handled_input = item.handle_events(events);
}
if !focused_handled_input {
let keyboard_focus = match self.focus_selection {
FocusSelection::Keyboard(..) => true,
FocusSelection::MouseAndKeyboard(..) => true,
_ => false,
};
if keyboard_focus {
if events.keyboard.was_just_pressed(self.get_previous_button()) {
self.select_idx =
(((self.select_idx as i32 + length as i32) - 1) % length as i32) as u32;
let start_idx = self.select_idx.min(length as u32 - 1).max(0);
while {
!list.items_ref[self.select_idx as usize]
.get_base()
.can_be_focused
} {
self.select_idx =
(((self.select_idx as i32 + length as i32) - 1) % length as i32) as u32;
if self.select_idx == start_idx {
break;
}
}
}
if events.keyboard.was_just_pressed(self.get_next_button()) {
self.select_idx = (((self.select_idx as i32) + 1) % length as i32) as u32;
}
}
let mouse_focus = match self.focus_selection {
FocusSelection::Mouse() => true,
FocusSelection::MouseAndKeyboard(..) => true,
_ => false,
};
if mouse_focus {
let grow_right = match self.growth_direction {
GrowthDirection::Left => false,
_ => true,
};
let grow_down = match self.growth_direction {
GrowthDirection::Up => false,
_ => true,
};
if let Some(loc) = events.cursor.get_location(&text_buffer) {
for idx in 0..self.cloned_interface_items.len() {
let item = &self.cloned_interface_items[idx];
let base = item.get_base();
let idx = idx as u32;
if !base.can_be_focused {
continue;
}
let (x, y) = (base.get_pos().0, base.get_pos().1);
let width = item.get_total_width();
let height = item.get_total_height();
let (x0, x1);
if grow_right {
x0 = x;
x1 = x + width - 1;
} else {
x0 = x - width + 1;
x1 = x;
}
let (y0, y1);
if grow_down {
y0 = y;
y1 = y + height - 1;
} else {
y0 = y - height + 1;
y1 = y;
}
if loc.0 >= x0 && loc.0 <= x1 && loc.1 >= y0 && loc.1 <= y1 {
self.select_idx = idx;
break;
}
}
}
}
}
}
fn children_are_dirty(&self, children: &mut Vec<Box<&mut dyn InterfaceItem>>) -> bool {
let lengths_equal = self.cloned_interface_items.len() == children.len();
let mut children_are_dirty = !lengths_equal; for item in children {
let mut base = item.get_mut_base();
children_are_dirty = children_are_dirty || base.dirty;
base.dirty = false;
}
children_are_dirty
}
fn set_new_positions(&mut self, list: &MenuList) {
self.cloned_interface_items = list.get_cloned_list();
let mut off: (i32, i32) = (0, 0);
let mut last_off: (i32, i32) = (0, 0);
let mut last_pos: (i32, i32) = (self.x as i32, self.y as i32);
match self.growth_direction {
GrowthDirection::Down => {
for (idx, item) in (&mut self.cloned_interface_items).iter_mut().enumerate() {
let position = &list.positions[idx];
last_pos = Menu::calc_new_pos(
self.x as i32,
self.y as i32,
position,
off,
last_off,
last_pos,
);
item.get_mut_base()
.set_pos((last_pos.0 as u32, last_pos.1 as u32));
last_off = (0, item.get_total_height() as i32);
off.1 += last_off.1;
}
}
GrowthDirection::Up => {
for (idx, item) in (&mut self.cloned_interface_items).iter_mut().enumerate() {
let position = &list.positions[idx];
last_off = (0, -(item.get_total_height() as i32));
last_pos = Menu::calc_new_pos(
self.x as i32,
self.y as i32,
position,
off,
last_off,
last_pos,
);
item.get_mut_base()
.set_pos((last_pos.0 as u32, last_pos.1 as u32));
off.1 += last_off.1;
}
}
GrowthDirection::Right => {
for (idx, item) in (&mut self.cloned_interface_items).iter_mut().enumerate() {
let position = &list.positions[idx];
last_pos = Menu::calc_new_pos(
self.x as i32,
self.y as i32,
position,
off,
last_off,
last_pos,
);
item.get_mut_base()
.set_pos((last_pos.0 as u32, last_pos.1 as u32));
last_off = (item.get_total_width() as i32, 0);
off.0 += last_off.0;
}
}
GrowthDirection::Left => {
for (idx, item) in (&mut self.cloned_interface_items).iter_mut().enumerate() {
let position = &list.positions[idx];
last_off = (-(item.get_total_width() as i32), 0);
last_pos = Menu::calc_new_pos(
self.x as i32,
self.y as i32,
position,
off,
last_off,
last_pos,
);
item.get_mut_base()
.set_pos((last_pos.0 as u32, last_pos.1 as u32));
off.0 += last_off.0;
}
}
}
}
fn calc_new_pos(
root_x: i32,
root_y: i32,
position: &MenuPosition,
off: (i32, i32),
last_off: (i32, i32),
last_pos: (i32, i32),
) -> (i32, i32) {
match position {
MenuPosition::Relative(x, y) => (root_x + x + off.0, root_y + y + off.1),
MenuPosition::RelativeToLast(x, y) => {
(last_pos.0 + x + last_off.0, last_pos.1 + y + last_off.1)
}
MenuPosition::Absolute(x, y) => (*x as i32, *y as i32),
}
}
}