use std::collections::{HashMap, HashSet};
use crate::{
containers::{area, collapsing_header, menu, resize, scroll_area, window},
widgets::text_edit,
Id, Layer, Pos2, Rect,
};
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "with_serde", serde(default))]
pub struct Memory {
#[cfg_attr(feature = "with_serde", serde(skip))]
pub(crate) interaction: Interaction,
#[cfg_attr(feature = "with_serde", serde(skip))]
pub(crate) kb_focus_id: Option<Id>,
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
pub(crate) menu_bar: HashMap<Id, menu::BarState>,
pub(crate) resize: HashMap<Id, resize::State>,
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>,
pub(crate) text_edit: HashMap<Id, text_edit::State>,
#[cfg_attr(feature = "with_serde", serde(skip))]
pub(crate) window_interaction: Option<window::WindowInteraction>,
pub(crate) areas: Areas,
}
#[derive(Clone, Debug, Default)]
pub struct Interaction {
pub click_id: Option<Id>,
pub drag_id: Option<Id>,
pub drag_is_window: bool,
pub click_interest: bool,
pub drag_interest: bool,
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "with_serde", serde(default))]
pub struct Areas {
areas: HashMap<Id, area::State>,
order: Vec<Layer>,
visible_last_frame: HashSet<Layer>,
visible_current_frame: HashSet<Layer>,
wants_to_be_on_top: HashSet<Layer>,
}
impl Memory {
pub(crate) fn begin_frame(&mut self, prev_input: &crate::input::InputState) {
self.interaction.click_interest = false;
self.interaction.drag_interest = false;
if !prev_input.mouse.could_be_click {
self.interaction.click_id = None;
}
if !prev_input.mouse.down || prev_input.mouse.pos.is_none() {
self.interaction.click_id = None;
self.interaction.drag_id = None;
let window_interaction = self.window_interaction.take();
if let Some(window_interaction) = window_interaction {
if window_interaction.is_pure_move() {
let area_layer = window_interaction.area_layer;
let area_state = self.areas.get(area_layer.id).cloned();
if let Some(mut area_state) = area_state {
area_state.vel = prev_input.mouse.velocity;
self.areas.set_state(area_layer, area_state);
}
}
}
}
}
pub(crate) fn end_frame(&mut self) {
self.areas.end_frame()
}
pub fn layer_at(&self, pos: Pos2) -> Option<Layer> {
self.areas.layer_at(pos)
}
}
impl Areas {
pub(crate) fn count(&self) -> usize {
self.areas.len()
}
pub(crate) fn get(&self, id: Id) -> Option<&area::State> {
self.areas.get(&id)
}
pub(crate) fn order(&self) -> &[Layer] {
&self.order
}
pub(crate) fn set_state(&mut self, layer: Layer, state: area::State) {
self.visible_current_frame.insert(layer);
let did_insert = self.areas.insert(layer.id, state).is_none();
if did_insert {
self.order.push(layer);
}
}
pub fn layer_at(&self, pos: Pos2) -> Option<Layer> {
for layer in self.order.iter().rev() {
if self.is_visible(layer) {
if let Some(state) = self.areas.get(&layer.id) {
if state.interactable {
let rect = Rect::from_min_size(state.pos, state.size);
if rect.contains(pos) {
return Some(*layer);
}
}
}
}
}
None
}
pub fn visible_last_frame(&self, layer: &Layer) -> bool {
self.visible_last_frame.contains(layer)
}
pub fn is_visible(&self, layer: &Layer) -> bool {
self.visible_last_frame.contains(layer) || self.visible_current_frame.contains(layer)
}
pub fn visible_layers(&self) -> HashSet<Layer> {
self.visible_last_frame
.iter()
.cloned()
.chain(self.visible_current_frame.iter().cloned())
.collect()
}
pub(crate) fn visible_windows(&self) -> Vec<&area::State> {
self.visible_layers()
.iter()
.filter(|layer| layer.order == crate::layers::Order::Middle)
.filter_map(|layer| self.get(layer.id))
.collect()
}
pub fn move_to_top(&mut self, layer: Layer) {
self.visible_current_frame.insert(layer);
self.wants_to_be_on_top.insert(layer);
if self.order.iter().find(|x| **x == layer).is_none() {
self.order.push(layer);
}
}
pub(crate) fn end_frame(&mut self) {
let Self {
visible_last_frame,
visible_current_frame,
order,
wants_to_be_on_top,
..
} = self;
*visible_last_frame = std::mem::take(visible_current_frame);
order.sort_by_key(|layer| (layer.order, wants_to_be_on_top.contains(layer)));
wants_to_be_on_top.clear();
}
}