use bevy::app::{App, Plugin, PreUpdate, Update};
use bevy::ecs::component::Component;
use bevy::ecs::entity::Entity;
use bevy::ecs::hierarchy::ChildOf;
use bevy::ecs::lifecycle::Add;
use bevy::ecs::message::MessageReader;
use bevy::ecs::observer::On;
use bevy::ecs::query::{Added, Has, With, Without};
use bevy::ecs::schedule::IntoScheduleConfigs as _;
use bevy::ecs::schedule::common_conditions::{not, resource_exists};
use bevy::ecs::system::{Commands, Local, Populated, Query, Res, Single};
use bevy::time::common_conditions::on_timer;
use std::collections::HashSet;
use std::time::Duration;
use tracing::{Level, debug, error, instrument, warn};
use super::{ActiveDisplayMarker, SpawnWindowTrigger, WMEventTrigger};
use crate::commands::{Direction, MoveFocus, Operation, filter_window_operations};
use crate::ecs::layout::LayoutStrip;
use crate::ecs::params::{ActiveDisplay, Windows};
use crate::ecs::{
ActiveWorkspaceMarker, Bounds, Initializing, NativeFullscreenMarker, Position,
RefreshWindowSizes, SelectedVirtualMarker, Timeout, Unmanaged, flash_message, focus_entity,
reposition_entity, reshuffle_around,
};
use crate::errors::Result;
use crate::events::Event;
use crate::manager::{Application, Display, Origin, Window, WindowManager};
use crate::platform::{WinID, WorkspaceId};
pub struct WorkspaceEventsPlugin;
impl Plugin for WorkspaceEventsPlugin {
fn build(&self, app: &mut App) {
const REFRESH_WINDOW_CHECK_FREQ_MS: u64 = 1000;
const DISPLAY_CHANGE_CHECK_FREQ_MS: u64 = 1000;
app.add_systems(
PreUpdate,
(switch_virtual_workspace_bind, move_virtual_workspace_bind),
);
app.add_systems(
Update,
(
show_active_workspace,
handle_virtual_window_moves,
detect_moved_windows.run_if(not(resource_exists::<Initializing>)),
refresh_workspace_window_sizes.run_if(on_timer(Duration::from_millis(
REFRESH_WINDOW_CHECK_FREQ_MS,
))),
find_orphaned_workspaces
.after(crate::ecs::display::displays_rearranged)
.run_if(on_timer(Duration::from_millis(
DISPLAY_CHANGE_CHECK_FREQ_MS,
))),
),
);
app.add_observer(cleanup_active_workspace_marker)
.add_observer(cleanup_selected_space_marker)
.add_observer(workspace_change_trigger)
.add_observer(workspace_created_trigger)
.add_observer(workspace_destroyed_trigger);
}
}
#[derive(Component)]
struct VirtualMoveMarker {
pub target_virtual_index: u32,
pub move_focus: MoveFocus,
}
#[derive(Component, Debug)]
pub(crate) struct PreviousStripPosition {
pub origin: Origin,
pub focus: Option<Entity>,
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all, fields(trigger))]
fn workspace_change_trigger(
trigger: On<WMEventTrigger>,
windows: Windows,
mut workspaces: Query<(
&mut LayoutStrip,
Entity,
Has<ActiveWorkspaceMarker>,
Has<SelectedVirtualMarker>,
)>,
active_display: Single<(&Display, Entity), With<ActiveDisplayMarker>>,
window_manager: Res<WindowManager>,
mut commands: Commands,
) {
let Event::SpaceChanged = trigger.event().0 else {
return;
};
let (active_display, display_entity) = *active_display;
let Ok(workspace_id) = window_manager.active_display_space(active_display.id()) else {
error!("Unable to get active workspace id!");
return;
};
let mut remove_from = None;
let mut insert_into = None;
for (strip, entity, active, selected) in &workspaces {
if active && strip.id() == workspace_id {
debug!("Workspace id {} already active", strip.id());
return;
}
if active && strip.id() != workspace_id {
debug!("Workspace id {} no longer active", strip.id());
remove_from = Some(entity);
}
if !active && strip.id() == workspace_id && selected {
debug!("Workspace id {} is active", strip.id());
insert_into = Some(entity);
}
}
if insert_into.is_none() {
insert_into = workspaces
.iter()
.filter(|(strip, _, _, _)| strip.id() == workspace_id)
.min_by_key(|(strip, _, _, _)| strip.virtual_index)
.map(|(_, entity, _, _)| entity);
}
if insert_into.is_none()
&& let Some(old_space) = remove_from
&& window_manager.is_fullscreen_space(active_display.id())
&& let Some((_, focused)) = windows.focused()
&& let Ok((mut old_strip, _, _, _)) = workspaces.get_mut(old_space)
{
debug!("workspace_change: space={workspace_id} fullscreen");
let fullscreen_marker = NativeFullscreenMarker {
previous_strip: old_strip.id(),
previous_index: old_strip.index_of(focused).unwrap_or(0),
};
old_strip.remove(focused);
let fullscreen_strip = LayoutStrip::fullscreen(workspace_id, focused);
let entity = commands
.spawn((
Position(active_display.bounds().min),
fullscreen_marker,
fullscreen_strip,
ChildOf(display_entity),
))
.id();
insert_into = Some(entity);
}
if let Some(into) = insert_into
&& let Ok(mut entity_commands) = commands.get_entity(into)
{
entity_commands
.try_insert(ActiveWorkspaceMarker)
.try_insert(SelectedVirtualMarker);
}
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all, fields(trigger))]
fn detect_moved_windows(
activated_workspace: Single<Entity, Added<ActiveWorkspaceMarker>>,
windows: Windows,
mut workspaces: Query<(&mut LayoutStrip, Entity, Has<NativeFullscreenMarker>)>,
apps: Query<&mut Application>,
window_manager: Res<WindowManager>,
mut ignored_windows: Local<HashSet<WinID>>,
mut commands: Commands,
) {
let Ok(workspace_id) = workspaces
.get(*activated_workspace)
.map(|strip| strip.0.id())
else {
return;
};
debug!("workspace {workspace_id}");
let strips = workspaces
.iter()
.filter_map(|strip| (strip.0.id() == workspace_id).then_some(strip.0))
.collect::<Vec<_>>();
let find_window = |window_id| windows.find_managed(window_id).map(|(_, entity)| entity);
let Ok((moved_windows, mut unresolved)) =
windows_not_in_strips(workspace_id, find_window, &strips, &window_manager).inspect_err(
|err| {
warn!("unable to get windows in the current workspace: {err}");
},
)
else {
return;
};
unresolved.retain(|window_id| {
!ignored_windows.contains(window_id) && windows.find(*window_id).is_none()
});
if !unresolved.is_empty() {
let retry_windows = apps
.into_iter()
.flat_map(|app| {
app.window_list()
.into_iter()
.filter(|window| unresolved.contains(&window.id()))
})
.collect::<Vec<_>>();
if retry_windows.is_empty() {
for id in unresolved {
ignored_windows.insert(id);
}
} else {
debug!(
"retrying unresolved windows: {}",
retry_windows
.iter()
.map(|window| format!("{}", window.id()))
.collect::<Vec<_>>()
.join(" ")
);
commands.trigger(SpawnWindowTrigger(retry_windows));
}
}
for entity in moved_windows {
if workspaces
.iter()
.any(|(strip, _, fullscreen)| fullscreen && strip.contains(entity))
{
continue;
}
debug!("Window {entity} moved to workspace {workspace_id}.");
for (mut strip, strip_entity, _) in &mut workspaces {
if strip_entity == *activated_workspace {
strip.append(entity);
} else {
strip.remove(entity);
}
}
}
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all, fields(trigger))]
fn workspace_destroyed_trigger(
trigger: On<WMEventTrigger>,
mut workspaces: Populated<(&mut LayoutStrip, Entity, Option<&NativeFullscreenMarker>)>,
mut commands: Commands,
) {
let Event::SpaceDestroyed { space_id } = trigger.event().0 else {
return;
};
let Some((entity, fullscreen)) = workspaces.iter().find_map(|(strip, entity, fullscreen)| {
let window = strip.first().ok().and_then(|col| col.top());
(strip.id() == space_id).then_some((entity, window.zip(fullscreen.cloned())))
}) else {
return;
};
if let Some((
window,
NativeFullscreenMarker {
previous_strip,
previous_index,
},
)) = fullscreen
&& let Some((mut strip, _, _)) = workspaces
.iter_mut()
.find(|(strip, _, _)| strip.id() == previous_strip)
{
debug!(
"previously fullscreened window {entity} inserted at {}",
previous_index
);
strip.insert_at(previous_index, window);
reshuffle_around(window, &mut commands);
}
if let Ok(mut entity_commands) = commands.get_entity(entity) {
debug!("Workspace destroyed {space_id} {entity}");
entity_commands.try_despawn();
}
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all, fields(trigger))]
fn workspace_created_trigger(
trigger: On<WMEventTrigger>,
active_display: Single<(&Display, Entity), With<ActiveDisplayMarker>>,
workspaces: Query<&LayoutStrip>,
mut commands: Commands,
) {
let Event::SpaceCreated { space_id } = trigger.event().0 else {
return;
};
if workspaces.into_iter().any(|strip| strip.id() == space_id) {
warn!("Workspace {space_id} already exists!");
return;
}
debug!("Workspace create {space_id}");
let (active_display, display_entity) = *active_display;
let strip = LayoutStrip::new(space_id, 0);
let origin = Position(active_display.bounds().min);
commands.spawn((
strip,
origin,
SelectedVirtualMarker,
ChildOf(display_entity),
));
}
fn windows_not_in_strips<F: Fn(WinID) -> Option<Entity>>(
workspace_id: WorkspaceId,
find_window: F,
strips: &[&LayoutStrip],
window_manager: &WindowManager,
) -> Result<(Vec<Entity>, Vec<WinID>)> {
window_manager
.windows_in_workspace(workspace_id)
.map(|ids| {
let mut moved = Vec::new();
let mut unresolved = Vec::new();
for id in ids {
if let Some(entity) = find_window(id) {
if strips.iter().any(|strip| strip.contains(entity)) {
continue;
}
moved.push(entity);
} else {
unresolved.push(id);
}
}
(moved, unresolved)
})
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all)]
fn find_orphaned_workspaces(
orphans: Populated<(&LayoutStrip, Entity, &Timeout, Option<&ChildOf>), With<Timeout>>,
displays: Query<(&Display, Entity)>,
window_manager: Res<WindowManager>,
mut commands: Commands,
) {
let present = window_manager.present_displays();
for (orphan, orphan_entity, timeout, child) in orphans {
if orphan.len() == 0 {
if let Ok(mut cmd) = commands.get_entity(orphan_entity) {
cmd.try_despawn();
}
debug!("despawning empty orphan workspace {}", orphan.id());
continue;
}
if child.is_some() {
if let Ok(mut cmd) = commands.get_entity(orphan_entity) {
cmd.try_remove::<Timeout>();
cmd.insert(RefreshWindowSizes::default());
}
debug!(
"layout strip {} was re-parented, removing timeout.",
orphan.id()
);
continue;
}
if timeout.timer.is_finished() {
debug!("Rescue windows from timed out orphan {}.", orphan.id());
for lost_window in orphan.all_windows() {
if let Ok(mut cmd) = commands.get_entity(lost_window) {
cmd.try_insert(Unmanaged::Floating);
}
}
continue;
}
let target = present.iter().find_map(|(present_display, spaces)| {
if spaces.iter().any(|&id| id == orphan.id()) {
displays
.iter()
.find(|(d, _)| d.id() == present_display.id())
} else {
None
}
});
let Some((target_display, target_entity)) = target else {
continue; };
debug!(
"Re-parenting orphaned strip {} to display {}",
orphan.id(),
target_display.id(),
);
if let Ok(mut cmd) = commands.get_entity(orphan_entity) {
cmd.try_remove::<Timeout>()
.insert(ChildOf(target_entity))
.insert(RefreshWindowSizes::default());
}
}
}
#[allow(clippy::needless_pass_by_value)]
fn refresh_workspace_window_sizes(
layout_strip: Single<(&LayoutStrip, Entity, &RefreshWindowSizes), With<ActiveWorkspaceMarker>>,
mut windows: Query<(Entity, &mut Window, &mut Bounds, Option<&Unmanaged>)>,
active_display: ActiveDisplay,
window_manager: Res<WindowManager>,
mut commands: Commands,
) {
let (strip, strip_entity, marker) = *layout_strip;
if !marker.ready() {
return;
}
debug!("refreshing workspace {} sizes", strip.id());
let mut in_workspace = window_manager
.windows_in_workspace(strip.id())
.inspect_err(|err| {
warn!("getting windows in workspace: {err}");
})
.unwrap_or_default();
for entity in strip.all_windows() {
let Ok((_, ref mut window, ref mut bounds, _)) = windows.get_mut(entity) else {
continue;
};
let Ok(frame) = window.update_frame() else {
continue;
};
bounds.0 = frame.size();
debug!("refreshing window {} frame {:?}", window.id(), frame);
in_workspace.retain(|window_id| *window_id != window.id());
}
let floating = in_workspace
.into_iter()
.filter_map(|window_id| {
windows
.iter()
.find_map(|(entity, window, _, unmanaged)| {
(window_id == window.id()).then_some(unmanaged.zip(Some(entity)))
})
.flatten()
})
.filter_map(|(unmanaged, entity)| {
matches!(unmanaged, Unmanaged::Floating).then_some(entity)
});
for window_entity in floating {
debug!("repositioning floating window {window_entity}");
reposition_entity(window_entity, active_display.bounds().min, &mut commands);
}
if let Ok(mut cmds) = commands.get_entity(strip_entity) {
cmds.try_remove::<RefreshWindowSizes>();
}
}
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn workspace_change_watcher(
active_display: ActiveDisplay,
window_manager: Res<WindowManager>,
mut current_space: Local<WorkspaceId>,
mut commands: Commands,
) {
let Ok(space_id) = window_manager
.0
.active_display_space(active_display.id())
.inspect_err(|err| warn!("{err}"))
else {
return;
};
if *current_space != space_id {
*current_space = space_id;
debug!("workspace changed to {space_id}");
commands.trigger(WMEventTrigger(Event::SpaceChanged));
}
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all, fields(trigger))]
fn cleanup_active_workspace_marker(
trigger: On<Add, ActiveWorkspaceMarker>,
workspaces: Query<(Entity, Has<ActiveWorkspaceMarker>), With<LayoutStrip>>,
mut commands: Commands,
) {
workspaces.iter().for_each(|(entity, marker)| {
if marker
&& entity != trigger.entity
&& let Ok(mut entity_commands) = commands.get_entity(entity)
{
entity_commands.try_remove::<ActiveWorkspaceMarker>();
}
});
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all, fields(trigger))]
fn cleanup_selected_space_marker(
trigger: On<Add, SelectedVirtualMarker>,
workspaces: Query<(Entity, &LayoutStrip, Has<SelectedVirtualMarker>)>,
mut commands: Commands,
) {
let Ok(workspace_id) = workspaces
.get(trigger.entity)
.map(|(_, strip, _)| strip.id())
else {
return;
};
workspaces.iter().for_each(|(entity, strip, marker)| {
if marker
&& entity != trigger.entity
&& strip.id() == workspace_id
&& let Ok(mut entity_commands) = commands.get_entity(entity)
{
entity_commands.try_remove::<SelectedVirtualMarker>();
}
});
}
#[allow(clippy::needless_pass_by_value)]
fn handle_virtual_window_moves(
moved_windows: Populated<(Entity, &VirtualMoveMarker), With<Window>>,
mut workspaces: Query<(
Entity,
&mut LayoutStrip,
&Position,
Has<ActiveWorkspaceMarker>,
)>,
active_display: Single<(Entity, &Display), With<ActiveDisplayMarker>>,
mut commands: Commands,
) {
let Some((workspace_id, source_entity)) = workspaces
.iter()
.find_map(|(entity, strip, _, active)| active.then_some((strip.id(), entity)))
else {
return;
};
let (display_entity, active_display) = *active_display;
for (window_entity, move_marker) in &moved_windows {
commands.entity(window_entity).remove::<VirtualMoveMarker>();
let follow = matches!(move_marker.move_focus, MoveFocus::Follow);
let target_idx = move_marker.target_virtual_index;
let target = workspaces.iter().find_map(|(entity, strip, _, _)| {
(strip.id() == workspace_id && strip.virtual_index == target_idx).then_some(entity)
});
let source_neighbour = workspaces
.get(source_entity)
.ok()
.and_then(|(_, strip, _, _)| {
strip
.left_neighbour(window_entity)
.or_else(|| strip.right_neighbour(window_entity))
});
let stay = !follow && source_neighbour.is_some();
let target_entity = if let Some(entity) = target {
entity
} else {
let visible_origin = active_display.bounds().min;
let origin = if stay {
active_display.bounds().max - 10
} else {
visible_origin
};
debug!(
"Creating new virtual row {target_idx} on workspace {}",
workspace_id
);
let mut new_strip = LayoutStrip::new(workspace_id, target_idx);
new_strip.append(window_entity);
let mut spawned = commands.spawn((
new_strip,
Position(origin),
SelectedVirtualMarker,
ChildOf(display_entity),
));
if stay {
spawned.insert(PreviousStripPosition {
origin: visible_origin,
focus: Some(window_entity),
});
}
spawned.id()
};
if !stay
&& let Ok(mut entity_commands) = commands.get_entity(source_entity)
&& let Ok((_, source_strip, position, _)) = workspaces.get(source_entity)
{
let focus = source_strip
.left_neighbour(window_entity)
.or_else(|| source_strip.right_neighbour(window_entity));
entity_commands.try_insert(PreviousStripPosition {
origin: position.0,
focus,
});
}
for (entity, mut strip, _, _) in &mut workspaces {
if entity == target_entity {
strip.append(window_entity);
} else {
strip.remove(window_entity);
}
}
if let Ok(mut entity_commands) = commands.get_entity(target_entity) {
entity_commands.try_insert(SelectedVirtualMarker);
if !stay {
entity_commands.try_insert(ActiveWorkspaceMarker);
}
}
if stay && let Some(neighbour) = source_neighbour {
focus_entity(neighbour, false, &mut commands);
reshuffle_around(neighbour, &mut commands);
} else {
reshuffle_around(window_entity, &mut commands);
}
debug!(
"Moved window {} to virtual workspace {}",
window_entity, target_idx
);
}
}
#[instrument(level = Level::DEBUG, skip_all)]
#[allow(clippy::needless_pass_by_value)]
fn switch_virtual_workspace_bind(
mut messages: MessageReader<Event>,
active_display: ActiveDisplay,
workspaces: Query<(Entity, &LayoutStrip, Has<ActiveWorkspaceMarker>)>,
mut commands: Commands,
) {
let Some(operation) = filter_window_operations(&mut messages, |op| {
matches!(op, Operation::Virtual(_) | Operation::VirtualNumber(_))
})
.next() else {
return;
};
let workspace_id = active_display.active_strip().id();
let mut rows = workspaces
.iter()
.filter(|(_, strip, _)| strip.id() == workspace_id)
.collect::<Vec<_>>();
if rows.is_empty() {
return;
}
rows.sort_by_key(|(_, strip, _)| strip.virtual_index);
let current_index = rows.iter().position(|(_, _, active)| *active).unwrap_or(0);
let next_index = match operation {
Operation::Virtual(Direction::South) => (current_index + 1).clamp(0, rows.len() - 1),
Operation::Virtual(Direction::North) => current_index.saturating_sub(1),
Operation::VirtualNumber(target_virtual_index) => {
let Some(index) = rows
.iter()
.position(|(_, strip, _)| strip.virtual_index == *target_virtual_index)
else {
if *target_virtual_index == 0 {
return;
}
let strip = LayoutStrip::new(workspace_id, *target_virtual_index);
commands.spawn((
strip,
Position(active_display.bounds().min),
ChildOf(active_display.entity()),
SelectedVirtualMarker,
ActiveWorkspaceMarker,
));
flash_message(format!("{}", *target_virtual_index + 1), 1.0, &mut commands);
return;
};
index
}
_ => return,
};
if next_index == current_index {
return;
}
let new_entity = rows[next_index].0;
let next_virtual_index = rows[next_index].1.virtual_index;
if let Ok(mut entity_commands) = commands.get_entity(new_entity) {
entity_commands
.try_insert(SelectedVirtualMarker)
.try_insert(ActiveWorkspaceMarker);
flash_message(format!("{}", next_virtual_index + 1), 1.0, &mut commands);
}
debug!(
"Switched virtual workspace on display {} from {} to {}",
active_display.id(),
rows[current_index].1.virtual_index,
rows[next_index].1.virtual_index
);
}
#[instrument(level = Level::DEBUG, skip_all)]
#[allow(clippy::needless_pass_by_value)]
fn move_virtual_workspace_bind(
mut messages: MessageReader<Event>,
windows: Windows,
active_display: ActiveDisplay,
mut commands: Commands,
) {
let Some(operation) = filter_window_operations(&mut messages, |op| {
matches!(
op,
Operation::VirtualMove(_, _) | Operation::VirtualMoveNumber(_, _)
)
})
.next() else {
return;
};
let Some((_, focused_entity)) = windows.focused() else {
return;
};
let current_virtual_index = active_display.active_strip().virtual_index;
let (target_virtual_index, move_focus) = match operation {
Operation::VirtualMove(Direction::South, move_focus)
if active_display.active_strip().len() > 1 =>
{
(current_virtual_index + 1, *move_focus)
}
Operation::VirtualMove(Direction::North, move_focus) => {
if current_virtual_index == 0 {
return;
}
(current_virtual_index - 1, *move_focus)
}
Operation::VirtualMoveNumber(target_virtual_index, move_focus) => {
if *target_virtual_index == current_virtual_index {
return;
}
(*target_virtual_index, *move_focus)
}
_ => return,
};
commands.entity(focused_entity).insert(VirtualMoveMarker {
target_virtual_index,
move_focus,
});
if move_focus == MoveFocus::Follow {
flash_message(format!("{}", target_virtual_index + 1), 1.0, &mut commands);
}
debug!("Moving {focused_entity} to new virtual space {target_virtual_index}");
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(level = Level::DEBUG, skip_all)]
fn show_active_workspace(
activated: Single<Entity, Added<ActiveWorkspaceMarker>>,
windows: Windows,
mut workspaces: Query<
(
Entity,
&mut Position,
&LayoutStrip,
Option<&PreviousStripPosition>,
),
Without<Window>,
>,
active_display: Single<&Display, With<ActiveDisplayMarker>>,
mut commands: Commands,
) {
let Some(workspace_id) = workspaces
.iter()
.find_map(|(entity, _, strip, _)| (entity == *activated).then_some(strip.id()))
else {
return;
};
let current_focus = windows.focused();
let current_workspace = workspaces
.iter_mut()
.filter(|(entity, _, strip, _)| strip.id() == workspace_id && *entity != *activated);
for (entity, mut position, strip, previous) in current_workspace {
if previous.is_none() {
let mut focus =
current_focus.and_then(|(_, entity)| strip.contains(entity).then_some(entity));
if focus.is_none() {
let display_center = active_display.bounds().center().x;
let closest = strip
.all_columns()
.into_iter()
.filter_map(|candidate| {
let center = windows.moving_frame(candidate)?.center().x;
let distance = (center - display_center).abs();
Some((candidate, distance))
})
.min_by_key(|(_, dist)| *dist)
.map(|min| min.0);
focus = closest;
debug!("No previous focus, taking centered one {focus:?}.");
}
if let Ok(mut entity_commands) = commands.get_entity(entity) {
entity_commands.try_insert(PreviousStripPosition {
origin: position.0,
focus,
});
}
}
let bounds = active_display.bounds();
position.0 = bounds.max - 10;
}
let Ok((_, mut position, strip, previous_position)) = workspaces.get_mut(*activated) else {
return;
};
debug!("showing virtual workspace {} ({})", strip.id(), *activated);
if let Some(PreviousStripPosition { origin, focus }) = previous_position {
if let Ok(mut entity_commands) = commands.get_entity(*activated) {
entity_commands.try_remove::<PreviousStripPosition>();
}
position.0 = *origin;
if let Some((_, current_focus)) = windows.focused()
&& strip.contains(current_focus)
{
return;
}
if let Some(entity) = focus
&& strip.contains(*entity)
{
focus_entity(*entity, false, &mut commands);
}
}
}