use std::collections::{HashMap, HashSet};
use crate::{
area, collapsing_header, menu, resize, scroll_area, util::Cache, widgets::text_edit, window,
Id, InputState, LayerId, Pos2, Rect, Style,
};
use epaint::color::{Color32, Hsva};
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))]
pub struct Memory {
pub(crate) options: Options,
pub(crate) new_pixels_per_point: Option<f32>,
#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) interaction: Interaction,
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
pub(crate) grid: HashMap<Id, crate::grid::State>,
#[cfg_attr(feature = "persistence", serde(skip))]
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 = "persistence", serde(skip))]
pub(crate) window_interaction: Option<window::WindowInteraction>,
#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) drag_value: crate::widgets::drag_value::MonoState,
#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) tooltip: crate::containers::popup::MonoState,
pub(crate) areas: Areas,
#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) color_cache: Cache<Color32, Hsva>,
#[cfg_attr(feature = "persistence", serde(skip))]
popup: Option<Id>,
#[cfg_attr(feature = "persistence", serde(skip))]
everything_is_visible: bool,
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))]
pub(crate) struct Options {
#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) style: std::sync::Arc<Style>,
pub(crate) tessellation_options: epaint::TessellationOptions,
pub(crate) font_definitions: epaint::text::FontDefinitions,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct Interaction {
pub click_id: Option<Id>,
pub drag_id: Option<Id>,
pub kb_focus_id: Option<Id>,
pub kb_focus_id_previous_frame: Option<Id>,
pub kb_focus_give_to_next: bool,
pub kb_focus_last_interested: Option<Id>,
pressed_tab: bool,
pressed_shift_tab: bool,
pub drag_is_window: bool,
pub click_interest: bool,
pub drag_interest: bool,
}
impl Interaction {
pub fn is_using_pointer(&self) -> bool {
self.click_id.is_some() || self.drag_id.is_some()
}
fn begin_frame(
&mut self,
prev_input: &crate::input_state::InputState,
new_input: &crate::data::input::RawInput,
) {
self.kb_focus_id_previous_frame = self.kb_focus_id;
self.click_interest = false;
self.drag_interest = false;
if !prev_input.pointer.could_any_button_be_click() {
self.click_id = None;
}
if !prev_input.pointer.any_down() || prev_input.pointer.latest_pos().is_none() {
self.click_id = None;
self.drag_id = None;
}
self.pressed_tab = false;
self.pressed_shift_tab = false;
for event in &new_input.events {
if let crate::Event::Key {
key: crate::Key::Tab,
pressed: true,
modifiers,
} = event
{
if modifiers.shift {
self.pressed_shift_tab = true;
} else {
self.pressed_tab = true;
}
}
}
}
}
impl Memory {
pub(crate) fn begin_frame(
&mut self,
prev_input: &crate::input_state::InputState,
new_input: &crate::data::input::RawInput,
) {
self.interaction.begin_frame(prev_input, new_input);
if !prev_input.pointer.any_down() {
self.window_interaction = None;
}
}
pub(crate) fn end_frame(
&mut self,
input: &InputState,
used_ids: &epaint::ahash::AHashMap<Id, Pos2>,
) {
self.areas.end_frame();
if let Some(kb_focus_id) = self.interaction.kb_focus_id {
let recently_gained_kb_focus =
self.interaction.kb_focus_id_previous_frame != Some(kb_focus_id);
if !recently_gained_kb_focus && !used_ids.contains_key(&kb_focus_id) {
self.interaction.kb_focus_id = None;
}
}
self.drag_value.end_frame(input);
}
pub fn layer_id_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<LayerId> {
self.areas.layer_id_at(pos, resize_interact_radius_side)
}
pub fn lost_kb_focus(&self, id: Id) -> bool {
self.interaction.kb_focus_id_previous_frame == Some(id) && !self.has_kb_focus(id)
}
pub fn has_kb_focus(&self, id: Id) -> bool {
self.interaction.kb_focus_id == Some(id)
}
pub fn request_kb_focus(&mut self, id: Id) {
self.interaction.kb_focus_id = Some(id);
}
pub fn surrender_kb_focus(&mut self, id: Id) {
if self.interaction.kb_focus_id == Some(id) {
self.interaction.kb_focus_id = None;
}
}
pub fn interested_in_kb_focus(&mut self, id: Id) {
if self.interaction.kb_focus_give_to_next {
self.interaction.kb_focus_id = Some(id);
self.interaction.kb_focus_give_to_next = false;
} else if self.has_kb_focus(id) {
if self.interaction.pressed_tab {
self.interaction.kb_focus_id = None;
self.interaction.kb_focus_give_to_next = true;
self.interaction.pressed_tab = false;
} else if self.interaction.pressed_shift_tab {
self.interaction.kb_focus_id = self.interaction.kb_focus_last_interested;
self.interaction.pressed_shift_tab = false;
}
}
self.interaction.kb_focus_last_interested = Some(id);
}
pub fn stop_text_input(&mut self) {
self.interaction.kb_focus_id = None;
}
pub fn is_anything_being_dragged(&self) -> bool {
self.interaction.drag_id.is_some()
}
pub fn is_being_dragged(&self, id: Id) -> bool {
self.interaction.drag_id == Some(id)
}
pub fn reset_areas(&mut self) {
self.areas = Default::default();
}
}
impl Memory {
pub fn is_popup_open(&mut self, popup_id: Id) -> bool {
self.popup == Some(popup_id) || self.everything_is_visible()
}
pub fn open_popup(&mut self, popup_id: Id) {
self.popup = Some(popup_id);
}
pub fn close_popup(&mut self) {
self.popup = None;
}
pub fn toggle_popup(&mut self, popup_id: Id) {
if self.is_popup_open(popup_id) {
self.close_popup();
} else {
self.open_popup(popup_id);
}
}
pub fn everything_is_visible(&self) -> bool {
self.everything_is_visible
}
pub fn set_everything_is_visible(&mut self, value: bool) {
self.everything_is_visible = value;
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))]
pub struct Areas {
areas: HashMap<Id, area::State>,
order: Vec<LayerId>,
visible_last_frame: HashSet<LayerId>,
visible_current_frame: HashSet<LayerId>,
wants_to_be_on_top: HashSet<LayerId>,
}
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) -> &[LayerId] {
&self.order
}
pub(crate) fn set_state(&mut self, layer_id: LayerId, state: area::State) {
self.visible_current_frame.insert(layer_id);
self.areas.insert(layer_id.id, state);
if self.order.iter().find(|x| **x == layer_id).is_none() {
self.order.push(layer_id);
}
}
pub fn layer_id_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<LayerId> {
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);
let rect = rect.expand(resize_interact_radius_side);
if rect.contains(pos) {
return Some(*layer);
}
}
}
}
}
None
}
pub fn visible_last_frame(&self, layer_id: &LayerId) -> bool {
self.visible_last_frame.contains(layer_id)
}
pub fn is_visible(&self, layer_id: &LayerId) -> bool {
self.visible_last_frame.contains(layer_id) || self.visible_current_frame.contains(layer_id)
}
pub fn visible_layer_ids(&self) -> HashSet<LayerId> {
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_layer_ids()
.iter()
.filter(|layer| layer.order == crate::Order::Middle)
.filter_map(|layer| self.get(layer.id))
.collect()
}
pub fn move_to_top(&mut self, layer_id: LayerId) {
self.visible_current_frame.insert(layer_id);
self.wants_to_be_on_top.insert(layer_id);
if self.order.iter().find(|x| **x == layer_id).is_none() {
self.order.push(layer_id);
}
}
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();
}
}