use std::{fmt::Debug, hash::Hash, sync::Arc};
use crate::*;
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct State {
pub pos: Pos2,
pub size: Vec2,
pub interactable: bool,
#[cfg_attr(feature = "with_serde", serde(skip))]
pub vel: Vec2,
}
impl State {
pub fn rect(&self) -> Rect {
Rect::from_min_size(self.pos, self.size)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Area {
id: Id,
movable: bool,
interactable: bool,
order: Order,
default_pos: Option<Pos2>,
fixed_pos: Option<Pos2>,
}
impl Area {
pub fn new(id_source: impl Hash) -> Self {
Self {
id: Id::new(id_source),
movable: true,
interactable: true,
order: Order::Middle,
default_pos: None,
fixed_pos: None,
}
}
pub fn layer(&self) -> Layer {
Layer {
order: self.order,
id: self.id,
}
}
pub fn movable(mut self, movable: bool) -> Self {
self.movable = movable;
self.interactable |= movable;
self
}
pub fn is_movable(&self) -> bool {
self.movable
}
pub fn interactable(mut self, interactable: bool) -> Self {
self.interactable = interactable;
self.movable &= interactable;
self
}
pub fn order(mut self, order: Order) -> Self {
self.order = order;
self
}
pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {
self.default_pos = Some(default_pos.into());
self
}
pub fn fixed_pos(mut self, fixed_pos: impl Into<Pos2>) -> Self {
let fixed_pos = fixed_pos.into();
self.default_pos = Some(fixed_pos);
self.fixed_pos = Some(fixed_pos);
self.movable = false;
self
}
}
pub(crate) struct Prepared {
layer: Layer,
state: State,
movable: bool,
}
impl Area {
pub(crate) fn begin(self, ctx: &Arc<Context>) -> Prepared {
let Area {
id,
movable,
order,
interactable,
default_pos,
fixed_pos,
} = self;
let layer = Layer { order, id };
let state = ctx.memory().areas.get(id).cloned();
let mut state = state.unwrap_or_else(|| State {
pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
size: Vec2::zero(),
interactable,
vel: Vec2::zero(),
});
state.pos = fixed_pos.unwrap_or(state.pos);
state.pos = state.pos.round();
Prepared {
layer,
state,
movable,
}
}
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) -> InteractInfo {
let prepared = self.begin(ctx);
let mut content_ui = prepared.content_ui(ctx);
add_contents(&mut content_ui);
prepared.end(ctx, content_ui)
}
}
impl Prepared {
pub(crate) fn state(&self) -> &State {
&self.state
}
pub(crate) fn state_mut(&mut self) -> &mut State {
&mut self.state
}
pub(crate) fn content_ui(&self, ctx: &Arc<Context>) -> Ui {
Ui::new(
ctx.clone(),
self.layer,
self.layer.id,
Rect::from_min_size(self.state.pos, Vec2::infinity()),
)
}
pub(crate) fn end(self, ctx: &Arc<Context>, content_ui: Ui) -> InteractInfo {
let Prepared {
layer,
mut state,
movable,
} = self;
state.size = (content_ui.child_bounds().max - state.pos).ceil();
let rect = Rect::from_min_size(state.pos, state.size);
let clip_rect = Rect::everything();
let interact_id = if movable {
Some(layer.id.with("move"))
} else {
None
};
let move_interact =
ctx.interact(layer, clip_rect, rect, interact_id, Sense::click_and_drag());
let input = ctx.input();
if move_interact.active {
state.pos += input.mouse.delta;
state.vel = input.mouse.velocity;
} else {
let stop_speed = 20.0; let friction_coeff = 1000.0;
let friction = friction_coeff * input.dt;
if friction > state.vel.length() || state.vel.length() < stop_speed {
state.vel = Vec2::zero();
} else {
state.vel -= friction * state.vel.normalized();
state.pos += state.vel * input.dt;
}
}
let margin = 32.0;
state.pos = state.pos.max(pos2(margin - state.size.x, 0.0));
state.pos = state.pos.min(pos2(
ctx.input().screen_size.x - margin,
ctx.input().screen_size.y - margin,
));
state.pos = state.pos.round();
if move_interact.active
|| mouse_pressed_on_area(ctx, layer)
|| !ctx.memory().areas.visible_last_frame(&layer)
{
ctx.memory().areas.move_to_top(layer);
}
ctx.memory().areas.set_state(layer, state);
move_interact
}
}
fn mouse_pressed_on_area(ctx: &Context, layer: Layer) -> bool {
if let Some(mouse_pos) = ctx.input().mouse.pos {
ctx.input().mouse.pressed && ctx.layer_at(mouse_pos) == Some(layer)
} else {
false
}
}
fn automatic_area_position(ctx: &Context) -> Pos2 {
let mut existing: Vec<Rect> = ctx
.memory()
.areas
.visible_windows()
.into_iter()
.map(State::rect)
.collect();
existing.sort_by_key(|r| r.left().round() as i32);
let left = 16.0;
let top = 32.0; let spacing = 16.0;
if existing.is_empty() {
return pos2(left, top);
}
let mut column_bbs = vec![existing[0]];
for &rect in &existing {
let current_column_bb = column_bbs.last_mut().unwrap();
if rect.left() < current_column_bb.right() {
*current_column_bb = current_column_bb.union(rect);
} else {
column_bbs.push(rect);
}
}
{
let mut x = left;
for col_bb in &column_bbs {
let available = col_bb.left() - x;
if available >= 300.0 {
return pos2(x, top);
}
x = col_bb.right() + spacing;
}
}
for col_bb in &column_bbs {
if col_bb.bottom() < ctx.input().screen_size.y * 0.5 {
return pos2(col_bb.left(), col_bb.bottom() + spacing);
}
}
let rightmost = column_bbs.last().unwrap().right();
if rightmost < ctx.input().screen_size.x - 200.0 {
return pos2(rightmost + spacing, top);
}
let mut best_pos = pos2(left, column_bbs[0].bottom() + spacing);
for col_bb in &column_bbs {
let col_pos = pos2(col_bb.left(), col_bb.bottom() + spacing);
if col_pos.y < best_pos.y {
best_pos = col_pos;
}
}
best_pos
}