use bevy::{
ecs::{
entity::Entity,
hierarchy::ChildOf,
query::{With, Without},
system::{Query, Res, ResMut, Single, SystemParam},
world::Mut,
},
math::IRect,
};
use objc2_core_graphics::CGDirectDisplayID;
use tracing::warn;
use super::{ActiveDisplayMarker, FocusFollowsMouse, MissionControlActive, SkipReshuffle};
use crate::{
config::Config,
ecs::{
ActiveWorkspaceMarker, Bounds, DockPosition, FocusedMarker, FullWidthMarker, Initializing,
LayoutPosition, Position, RepositionMarker, ResizeMarker, Unmanaged, WidthRatio,
layout::LayoutStrip,
},
manager::{Application, Display, Origin, Size, Window},
platform::{ProcessSerialNumber, WinID},
};
#[derive(SystemParam)]
pub struct Configuration<'w> {
config: Res<'w, Config>,
focus_follows_mouse_id: ResMut<'w, FocusFollowsMouse>,
skip_reshuffle: ResMut<'w, SkipReshuffle>,
mission_control_active: Res<'w, MissionControlActive>,
initializing: Option<Res<'w, Initializing>>,
}
impl Configuration<'_> {
pub fn focus_follows_mouse(&self) -> bool {
self.config
.options()
.focus_follows_mouse
.is_none_or(|ffm| ffm)
}
pub fn mouse_follows_focus(&self) -> bool {
self.config
.options()
.mouse_follows_focus
.is_none_or(|mff| mff)
}
pub fn auto_center(&self) -> bool {
self.config.auto_center()
}
pub fn window_hidden_ratio(&self) -> f64 {
self.config.window_hidden_ratio()
}
pub fn swipe_gesture_fingers(&self) -> Option<usize> {
self.config.swipe_gesture_fingers()
}
pub fn ffm_flag(&self) -> Option<WinID> {
self.focus_follows_mouse_id.0
}
pub fn set_ffm_flag(&mut self, flag: Option<WinID>) {
self.focus_follows_mouse_id.as_mut().0 = flag;
}
pub fn set_skip_reshuffle(&mut self, to: bool) {
self.skip_reshuffle.as_mut().0 = to;
}
pub fn skip_reshuffle(&self) -> bool {
self.skip_reshuffle.0
}
pub fn edge_padding(&self) -> (i32, i32, i32, i32) {
self.config.edge_padding()
}
pub fn mission_control_active(&self) -> bool {
self.mission_control_active.0
}
pub fn initializing(&self) -> bool {
self.initializing.is_some()
}
pub fn config(&self) -> &Config {
&self.config
}
}
#[derive(SystemParam)]
pub struct ActiveDisplay<'w, 's> {
strip: Single<'w, 's, (&'static LayoutStrip, Entity), With<ActiveWorkspaceMarker>>,
display: Single<
'w,
's,
(&'static Display, Entity, Option<&'static DockPosition>),
With<ActiveDisplayMarker>,
>,
other_displays: Query<'w, 's, &'static Display, Without<ActiveDisplayMarker>>,
}
impl ActiveDisplay<'_, '_> {
pub fn display(&self) -> &Display {
self.display.0
}
pub fn id(&self) -> CGDirectDisplayID {
self.display.0.id()
}
pub fn other(&self) -> impl Iterator<Item = &Display> {
self.other_displays.iter()
}
pub fn active_strip(&self) -> &LayoutStrip {
self.strip.0
}
pub fn active_strip_entity(&self) -> Entity {
self.strip.1
}
pub fn bounds(&self) -> IRect {
self.display.0.bounds()
}
pub fn dock(&self) -> Option<&DockPosition> {
self.display.2
}
}
#[derive(SystemParam)]
pub struct ActiveDisplayMut<'w, 's> {
strip: Single<'w, 's, &'static mut LayoutStrip, With<ActiveWorkspaceMarker>>,
display: Single<
'w,
's,
(&'static mut Display, Entity, Option<&'static DockPosition>),
With<ActiveDisplayMarker>,
>,
other_displays: Query<'w, 's, &'static mut Display, Without<ActiveDisplayMarker>>,
}
impl ActiveDisplayMut<'_, '_> {
pub fn display(&self) -> &Display {
&self.display.0
}
pub fn dock(&self) -> Option<&DockPosition> {
self.display.2
}
pub fn other(&mut self) -> impl Iterator<Item = Mut<'_, Display>> {
self.other_displays.iter_mut()
}
pub fn active_strip(&mut self) -> &mut LayoutStrip {
&mut self.strip
}
pub fn bounds(&self) -> IRect {
self.display().bounds()
}
}
#[derive(SystemParam)]
#[allow(clippy::type_complexity)]
pub struct Windows<'w, 's> {
all: Query<
'w,
's,
(
&'static Window,
Entity,
&'static ChildOf,
Option<&'static Unmanaged>,
),
>,
focus: Query<'w, 's, (&'static Window, Entity), With<FocusedMarker>>,
previous_size: Query<
'w,
's,
(
&'static Window,
Entity,
&'static WidthRatio,
&'static FullWidthMarker,
),
With<FullWidthMarker>,
>,
positions: Query<
'w,
's,
(
&'static LayoutPosition,
&'static Position,
&'static Bounds,
&'static WidthRatio,
Option<&'static RepositionMarker>,
Option<&'static ResizeMarker>,
),
With<Window>,
>,
}
impl Windows<'_, '_> {
#[allow(clippy::type_complexity)]
fn get_all(&self, entity: Entity) -> Option<(&Window, Entity, &ChildOf, Option<&Unmanaged>)> {
self.all
.get(entity)
.inspect_err(|err| warn!("unable to find window: {err}"))
.ok()
}
pub fn get_managed(&self, entity: Entity) -> Option<(&Window, Entity, Option<&Unmanaged>)> {
self.get_all(entity)
.map(|(window, entity, _, unmanaged)| (window, entity, unmanaged))
}
pub fn get(&self, entity: Entity) -> Option<&Window> {
self.get_all(entity).map(|(window, _, _, _)| window)
}
pub fn find(&self, window_id: WinID) -> Option<(&Window, Entity)> {
self.all
.into_iter()
.find(|(window, _, _, _)| window.id() == window_id)
.map(|(window, entity, _, _)| (window, entity))
}
pub fn find_parent(&self, window_id: WinID) -> Option<(&Window, Entity, Entity)> {
self.all.iter().find_map(|(window, entity, childof, _)| {
(window.id() == window_id).then_some((window, entity, childof.parent()))
})
}
pub fn find_managed(&self, window_id: WinID) -> Option<(&Window, Entity)> {
self.all.iter().find_map(|(window, entity, _, unmanaged)| {
(unmanaged.is_none() && window.id() == window_id).then_some((window, entity))
})
}
pub fn focused(&self) -> Option<(&Window, Entity)> {
self.focus.single().ok()
}
pub fn iter(&self) -> impl Iterator<Item = (&Window, Entity)> {
self.all
.iter()
.map(|(window, entity, _, _)| (window, entity))
}
pub fn all_iter(&self) -> impl Iterator<Item = (&Window, Entity, &ChildOf)> {
self.all
.iter()
.map(|(window, entity, childof, _)| (window, entity, childof))
}
pub fn full_width(&self, entity: Entity) -> Option<&FullWidthMarker> {
self.previous_size
.get(entity)
.map(|(_, _, _, marker)| marker)
.ok()
}
pub fn psn(&self, window_id: WinID, apps: &Query<&Application>) -> Option<ProcessSerialNumber> {
self.find_parent(window_id)
.and_then(|(_, _, parent)| apps.get(parent).ok())
.map(|app| app.psn())
}
pub fn origin(&self, entity: Entity) -> Option<Origin> {
self.positions
.get(entity)
.ok()
.map(|(_, origin, _, _, _, _)| origin.0)
}
pub fn size(&self, entity: Entity) -> Option<Size> {
self.positions
.get(entity)
.ok()
.map(|(_, _, size, _, _, _)| size.0)
}
pub fn width_ratio(&self, entity: Entity) -> Option<f64> {
self.positions
.get(entity)
.ok()
.map(|(_, _, _, ratio, _, _)| ratio.0)
}
pub fn frame(&self, entity: Entity) -> Option<IRect> {
self.positions
.get(entity)
.ok()
.map(|(_, origin, size, _, _, _)| IRect::from_corners(origin.0, origin.0 + size.0))
}
pub fn moving_frame(&self, entity: Entity) -> Option<IRect> {
self.positions
.get(entity)
.ok()
.map(|(_, origin, size, _, reposition, resize)| {
let size = size.0;
let mut frame = IRect::from_corners(origin.0, origin.0 + size);
if let Some(reposition) = reposition {
frame.min = reposition.0;
frame.max = frame.min + size;
}
if let Some(resize) = resize {
frame.max = frame.min + resize.0;
}
frame
})
}
pub fn layout_position(&self, entity: Entity) -> Option<&LayoutPosition> {
self.positions
.get(entity)
.ok()
.map(|(layout_position, _, _, _, _, _)| layout_position)
}
}