mod carry;
mod resolve;
#[cfg(test)]
mod tests;
use super::*;
use crate::compositor::overlap::read::OverlapReadContext;
use crate::frame_loop::anim_style_for;
pub(crate) use crate::compositor::overlap::read::CollisionExtents;
pub(crate) use carry::carry_surface_non_overlap;
pub(crate) use resolve::{
request_toplevel_resize, resolve_landmarks_overlapped_by_active_window, resolve_surface_overlap,
};
fn overlap_read_context(st: &Halley) -> OverlapReadContext<'_> {
OverlapReadContext {
field: &st.model.field,
monitor_state: &st.model.monitor_state,
interaction_state: &st.input.interaction_state,
spawn_state: &st.model.spawn_state,
render_state: &st.ui.render_state,
workspace_state: &st.model.workspace_state,
tuning: &st.runtime.tuning,
viewport: st.model.viewport,
camera_render_scale: st.camera_render_scale(),
}
}
#[inline]
fn clamp_speed(v: Vec2, max_speed: f32) -> Vec2 {
OverlapReadContext::clamp_speed(v, max_speed)
}
#[inline]
fn physics_damping_per_sec(st: &Halley) -> f32 {
overlap_read_context(st).physics_damping_per_sec()
}
#[inline]
fn physics_inv_mass(st: &Halley, id: NodeId, pinned: bool) -> f32 {
overlap_read_context(st).physics_inv_mass(id, pinned)
}
#[inline]
fn node_participates_in_overlap(st: &Halley, id: NodeId) -> bool {
!st.is_fullscreen_active(id) && overlap_read_context(st).node_participates_in_overlap(id)
}
fn node_participates_in_drag_overlap(st: &Halley, id: NodeId) -> bool {
if node_participates_in_overlap(st, id) {
return true;
}
if st.is_fullscreen_active(id) || !st.model.field.is_active_cluster_member(id) {
return false;
}
st.model.field.node(id).is_some_and(|n| {
st.model.field.is_visible(id)
&& matches!(
n.state,
halley_core::field::NodeState::Active
| halley_core::field::NodeState::Node
| halley_core::field::NodeState::Core
| halley_core::field::NodeState::Drifting
)
})
}
pub(crate) fn non_overlap_gap_world(st: &Halley) -> f32 {
overlap_read_context(st).non_overlap_gap_world()
}
#[inline]
pub(crate) fn required_sep_x(
st: &Halley,
a_pos_x: f32,
a_ext: CollisionExtents,
b_pos_x: f32,
b_ext: CollisionExtents,
gap: f32,
) -> f32 {
overlap_read_context(st).required_sep_x(a_pos_x, a_ext, b_pos_x, b_ext, gap)
}
#[inline]
fn nodes_share_overlap_group(st: &Halley, a: NodeId, b: NodeId) -> bool {
overlap_read_context(st).nodes_share_overlap_group(a, b)
}
#[inline]
fn node_is_expanded_window(st: &Halley, id: NodeId) -> bool {
overlap_read_context(st).node_is_expanded_window(id)
}
#[inline]
fn node_is_landmark(st: &Halley, id: NodeId) -> bool {
overlap_read_context(st).node_is_landmark(id)
}
fn mixed_expanded_landmark_locks(
st: &Halley,
a: NodeId,
b: NodeId,
a_locked: bool,
b_locked: bool,
) -> (bool, bool) {
let a_expanded = node_is_expanded_window(st, a);
let b_expanded = node_is_expanded_window(st, b);
let a_landmark = node_is_landmark(st, a);
let b_landmark = node_is_landmark(st, b);
let drag_authority = st.input.interaction_state.drag_authority_node;
let drag_authority_expanded =
drag_authority.is_some_and(|drag_id| node_is_expanded_window(st, drag_id));
if a_expanded && b_landmark {
let b_pinned = st.model.field.node(b).is_some_and(|node| node.pinned);
if st.runtime.tuning.physics_enabled && drag_authority == Some(b) {
return (a_locked, true);
}
if b_pinned {
(a_locked, true)
} else if drag_authority_expanded {
if drag_authority == Some(a) || a_locked {
(true, b_locked)
} else {
(a_locked, true)
}
} else {
(true, b_locked)
}
} else if b_expanded && a_landmark {
let a_pinned = st.model.field.node(a).is_some_and(|node| node.pinned);
if st.runtime.tuning.physics_enabled && drag_authority == Some(a) {
return (true, b_locked);
}
if a_pinned {
(true, b_locked)
} else if drag_authority_expanded {
if drag_authority == Some(b) || b_locked {
(a_locked, true)
} else {
(true, b_locked)
}
} else {
(a_locked, true)
}
} else {
(a_locked, b_locked)
}
}
#[inline]
pub(crate) fn required_sep_y(
st: &Halley,
a_pos_y: f32,
a_ext: CollisionExtents,
b_pos_y: f32,
b_ext: CollisionExtents,
gap: f32,
) -> f32 {
overlap_read_context(st).required_sep_y(a_pos_y, a_ext, b_pos_y, b_ext, gap)
}
pub(super) fn carry_overlap_node_direct(st: &mut Halley, id: NodeId, to: Vec2) -> bool {
if st.model.field.node(id).is_some_and(|node| node.pinned) {
return false;
}
if st
.model
.field
.node(id)
.is_some_and(|node| node.kind == halley_core::field::NodeKind::Core)
{
st.model.field.carry_cluster_by_core(id, to)
} else {
st.model.field.carry(id, to)
}
}
pub(crate) fn surface_window_collision_extents(
st: &Halley,
n: &halley_core::field::Node,
) -> CollisionExtents {
overlap_read_context(st).surface_window_collision_extents(n)
}
pub(crate) fn spawn_obstacle_extents_for_node(
st: &Halley,
n: &halley_core::field::Node,
) -> CollisionExtents {
if n.kind == halley_core::field::NodeKind::Surface {
overlap_read_context(st).spawn_obstacle_extents_for_node(n)
} else {
collision_extents_for_node(st, n)
}
}
pub(crate) fn collision_extents_for_node(
st: &Halley,
n: &halley_core::field::Node,
) -> CollisionExtents {
let anim = anim_style_for(st, n.id, n.state.clone(), Instant::now());
match n.state {
halley_core::field::NodeState::Active => {
let basis = st
.model
.workspace_state
.last_active_size
.get(&n.id)
.copied()
.unwrap_or(n.intrinsic_size);
let s = OverlapReadContext::active_collision_scale(anim.scale, basis.x, basis.y);
let ext = overlap_read_context(st).surface_window_collision_extents(n);
CollisionExtents {
left: ext.left * s,
right: ext.right * s,
top: ext.top * s,
bottom: ext.bottom * s,
}
}
halley_core::field::NodeState::Node => {
overlap_read_context(st).node_collision_extents(n.intrinsic_size, &n.label, anim.scale)
}
halley_core::field::NodeState::Core => {
overlap_read_context(st).core_node_collision_extents()
}
halley_core::field::NodeState::Drifting => CollisionExtents::symmetric(n.footprint),
}
}
pub(crate) fn collision_size_for_node(st: &Halley, n: &halley_core::field::Node) -> Vec2 {
collision_extents_for_node(st, n).size()
}
pub(super) fn layout_collision_extents_for_node(
st: &Halley,
n: &halley_core::field::Node,
) -> CollisionExtents {
match n.state {
halley_core::field::NodeState::Active => surface_window_collision_extents(st, n),
halley_core::field::NodeState::Node | halley_core::field::NodeState::Core => {
collision_extents_for_node(st, n)
}
_ => collision_extents_for_node(st, n),
}
}