shell: implement minimize
This commit is contained in:
parent
fffae1491d
commit
3eb7e5f82e
20 changed files with 1185 additions and 307 deletions
|
|
@ -1068,7 +1068,12 @@ fn render_node_for_output(
|
|||
let nodes = workspace
|
||||
.get_fullscreen()
|
||||
.map(|w| vec![w.clone()])
|
||||
.unwrap_or_else(|| workspace.windows().collect::<Vec<_>>())
|
||||
.unwrap_or_else(|| {
|
||||
workspace
|
||||
.mapped()
|
||||
.map(|mapped| mapped.active_window())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.into_iter()
|
||||
.flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s)))
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
|||
|
|
@ -2031,7 +2031,7 @@ impl State {
|
|||
let focus_stack = workspace.focus_stack.get(seat);
|
||||
let focused_window = focus_stack.last().cloned();
|
||||
if let Some(window) = focused_window {
|
||||
self.common.shell.maximize_toggle(&window);
|
||||
self.common.shell.maximize_toggle(&window, seat);
|
||||
}
|
||||
}
|
||||
Action::Resizing(direction) => self.common.shell.set_resize_mode(
|
||||
|
|
|
|||
|
|
@ -379,6 +379,16 @@ impl CosmicMapped {
|
|||
window.is_activated(pending)
|
||||
}
|
||||
|
||||
pub fn is_minimized(&self) -> bool {
|
||||
self.active_window().is_minimized()
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
for (w, _) in self.windows() {
|
||||
w.set_minimized(minimized);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_size(&self) -> Option<Size<i32, Logical>> {
|
||||
match &self.element {
|
||||
CosmicMappedInternal::Stack(s) => s.pending_size(),
|
||||
|
|
|
|||
|
|
@ -725,11 +725,10 @@ impl Program for CosmicStackInternal {
|
|||
} else if let Some(workspace) =
|
||||
state.common.shell.space_for_mut(&mapped)
|
||||
{
|
||||
workspace
|
||||
.element_geometry(&mapped)
|
||||
.unwrap()
|
||||
.loc
|
||||
.to_global(&workspace.output)
|
||||
let Some(elem_geo) = workspace.element_geometry(&mapped) else {
|
||||
return;
|
||||
};
|
||||
elem_geo.loc.to_global(&workspace.output)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -761,11 +760,10 @@ impl Program for CosmicStackInternal {
|
|||
state.common.shell.element_for_wl_surface(&surface).cloned()
|
||||
{
|
||||
if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
|
||||
let position = workspace
|
||||
.element_geometry(&mapped)
|
||||
.unwrap()
|
||||
.loc
|
||||
.to_global(&workspace.output);
|
||||
let Some(elem_geo) = workspace.element_geometry(&mapped) else {
|
||||
return;
|
||||
};
|
||||
let position = elem_geo.loc.to_global(&workspace.output);
|
||||
let mut cursor = seat
|
||||
.get_pointer()
|
||||
.unwrap()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
use std::time::Duration;
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::{
|
||||
|
|
@ -70,6 +73,9 @@ impl From<X11Surface> for CosmicSurface {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Minimized(AtomicBool);
|
||||
|
||||
pub const SSD_HEIGHT: i32 = 48;
|
||||
pub const RESIZE_BORDER: i32 = 10;
|
||||
|
||||
|
|
@ -351,6 +357,45 @@ impl CosmicSurface {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_minimized(&self) -> bool {
|
||||
match self.0.underlying_surface() {
|
||||
WindowSurface::Wayland(_) => self
|
||||
.0
|
||||
.user_data()
|
||||
.get_or_insert_threadsafe(Minimized::default)
|
||||
.0
|
||||
.load(Ordering::SeqCst),
|
||||
WindowSurface::X11(surface) => surface.is_minimized(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, minimized: bool) {
|
||||
match self.0.underlying_surface() {
|
||||
WindowSurface::Wayland(_) => self
|
||||
.0
|
||||
.user_data()
|
||||
.get_or_insert_threadsafe(Minimized::default)
|
||||
.0
|
||||
.store(minimized, Ordering::SeqCst),
|
||||
WindowSurface::X11(surface) => {
|
||||
let _ = surface.set_minimized(minimized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_suspended(&self, suspended: bool) {
|
||||
match self.0.underlying_surface() {
|
||||
WindowSurface::Wayland(window) => window.with_pending_state(|state| {
|
||||
if suspended {
|
||||
state.states.set(ToplevelState::Suspended);
|
||||
} else {
|
||||
state.states.unset(ToplevelState::Suspended);
|
||||
}
|
||||
}),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_size(&self) -> Option<Size<i32, Logical>> {
|
||||
match self.0.underlying_surface() {
|
||||
WindowSurface::Wayland(toplevel) => {
|
||||
|
|
|
|||
|
|
@ -290,7 +290,8 @@ impl Program for CosmicWindowInternal {
|
|||
if let Some(mapped) =
|
||||
state.common.shell.element_for_wl_surface(&surface).cloned()
|
||||
{
|
||||
state.common.shell.maximize_toggle(&mapped)
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
state.common.shell.maximize_toggle(&mapped, &seat)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -315,11 +316,10 @@ impl Program for CosmicWindowInternal {
|
|||
} else if let Some(workspace) =
|
||||
state.common.shell.space_for_mut(&mapped)
|
||||
{
|
||||
workspace
|
||||
.element_geometry(&mapped)
|
||||
.unwrap()
|
||||
.loc
|
||||
.to_global(&workspace.output)
|
||||
let Some(elem_geo) = workspace.element_geometry(&mapped) else {
|
||||
return;
|
||||
};
|
||||
elem_geo.loc.to_global(&workspace.output)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,13 +41,13 @@ impl<'a> FocusStack<'a> {
|
|||
pub fn last(&self) -> Option<&CosmicMapped> {
|
||||
self.0
|
||||
.as_ref()
|
||||
.and_then(|set| set.iter().rev().find(|w| w.alive()))
|
||||
.and_then(|set| set.iter().rev().find(|w| w.alive() && !w.is_minimized()))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &'_ CosmicMapped> {
|
||||
self.0
|
||||
.iter()
|
||||
.flat_map(|set| set.iter().rev().filter(|w| w.alive()))
|
||||
.flat_map(|set| set.iter().rev().filter(|w| w.alive() && !w.is_minimized()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,11 +63,14 @@ impl<'a> FocusStackMut<'a> {
|
|||
}
|
||||
|
||||
pub fn last(&self) -> Option<&CosmicMapped> {
|
||||
self.0.iter().rev().find(|w| w.alive())
|
||||
self.0.iter().rev().find(|w| w.alive() && !w.is_minimized())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &'_ CosmicMapped> {
|
||||
self.0.iter().rev().filter(|w| w.alive())
|
||||
self.0
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|w| w.alive() && !w.is_minimized())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,44 +99,34 @@ impl ActiveFocus {
|
|||
}
|
||||
|
||||
impl Shell {
|
||||
pub fn append_focus_stack(
|
||||
state: &mut State,
|
||||
target: Option<&KeyboardFocusTarget>,
|
||||
active_seat: &Seat<State>,
|
||||
) {
|
||||
pub fn append_focus_stack(state: &mut State, mapped: &CosmicMapped, active_seat: &Seat<State>) {
|
||||
if mapped.is_minimized() {
|
||||
return;
|
||||
}
|
||||
|
||||
// update FocusStack and notify layouts about new focus (if any window)
|
||||
let element = match target {
|
||||
Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()),
|
||||
Some(KeyboardFocusTarget::Fullscreen(window)) => {
|
||||
state.common.shell.element_for_surface(window).cloned()
|
||||
}
|
||||
_ => None,
|
||||
let workspace = state.common.shell.space_for_mut(&mapped);
|
||||
let workspace = if workspace.is_none() {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.active_space_mut(&active_seat.active_output())
|
||||
} else {
|
||||
workspace.unwrap()
|
||||
};
|
||||
|
||||
if let Some(mapped) = element {
|
||||
let workspace = state.common.shell.space_for_mut(&mapped);
|
||||
let workspace = if workspace.is_none() {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.active_space_mut(&active_seat.active_output())
|
||||
} else {
|
||||
workspace.unwrap()
|
||||
};
|
||||
|
||||
let mut focus_stack = workspace.focus_stack.get_mut(active_seat);
|
||||
if Some(&mapped) != focus_stack.last() {
|
||||
trace!(?mapped, "Focusing window.");
|
||||
focus_stack.append(&mapped);
|
||||
// also remove popup grabs, if we are switching focus
|
||||
if let Some(mut popup_grab) = active_seat
|
||||
.user_data()
|
||||
.get::<PopupGrabData>()
|
||||
.and_then(|x| x.take())
|
||||
{
|
||||
if !popup_grab.has_ended() {
|
||||
popup_grab.ungrab(PopupUngrabStrategy::All);
|
||||
}
|
||||
let mut focus_stack = workspace.focus_stack.get_mut(active_seat);
|
||||
if Some(mapped) != focus_stack.last() {
|
||||
trace!(?mapped, "Focusing window.");
|
||||
focus_stack.append(&mapped);
|
||||
// also remove popup grabs, if we are switching focus
|
||||
if let Some(mut popup_grab) = active_seat
|
||||
.user_data()
|
||||
.get::<PopupGrabData>()
|
||||
.and_then(|x| x.take())
|
||||
{
|
||||
if !popup_grab.has_ended() {
|
||||
popup_grab.ungrab(PopupUngrabStrategy::All);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -145,7 +138,20 @@ impl Shell {
|
|||
active_seat: &Seat<State>,
|
||||
serial: Option<Serial>,
|
||||
) {
|
||||
Self::append_focus_stack(state, target, active_seat);
|
||||
let element = match target {
|
||||
Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()),
|
||||
Some(KeyboardFocusTarget::Fullscreen(window)) => {
|
||||
state.common.shell.element_for_surface(window).cloned()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(mapped) = element {
|
||||
if mapped.is_minimized() {
|
||||
return;
|
||||
}
|
||||
Self::append_focus_stack(state, &mapped, active_seat);
|
||||
}
|
||||
|
||||
// update keyboard focus
|
||||
if let Some(keyboard) = active_seat.get_keyboard() {
|
||||
|
|
@ -360,7 +366,7 @@ fn focus_target_is_valid(
|
|||
let has_fullscreen = workspace.get_fullscreen().is_some();
|
||||
|
||||
if is_sticky && !is_in_focus_stack {
|
||||
Shell::append_focus_stack(state, Some(&KeyboardFocusTarget::Element(mapped)), seat);
|
||||
Shell::append_focus_stack(state, &mapped, seat);
|
||||
}
|
||||
|
||||
(is_sticky || is_in_focus_stack) && !has_fullscreen
|
||||
|
|
|
|||
|
|
@ -194,8 +194,10 @@ pub fn window_items(
|
|||
Some(
|
||||
Item::new(fl!("window-menu-maximize"), move |handle| {
|
||||
let mapped = maximize_clone.clone();
|
||||
let _ =
|
||||
handle.insert_idle(move |state| state.common.shell.maximize_toggle(&mapped));
|
||||
let _ = handle.insert_idle(move |state| {
|
||||
let seat = state.common.last_active_seat().clone();
|
||||
state.common.shell.maximize_toggle(&mapped, &seat);
|
||||
});
|
||||
})
|
||||
.shortcut(config.get_shortcut_for_action(&Action::Maximize))
|
||||
.toggled(window.is_maximized(false)),
|
||||
|
|
|
|||
|
|
@ -52,12 +52,116 @@ pub const ANIMATION_DURATION: Duration = Duration::from_millis(200);
|
|||
pub struct FloatingLayout {
|
||||
pub(crate) space: Space<CosmicMapped>,
|
||||
spawn_order: Vec<CosmicMapped>,
|
||||
tiling_animations: HashMap<CosmicMapped, (Instant, Rectangle<i32, Local>)>,
|
||||
animations: HashMap<CosmicMapped, Animation>,
|
||||
hovered_stack: Option<(CosmicMapped, Rectangle<i32, Local>)>,
|
||||
dirty: AtomicBool,
|
||||
pub theme: cosmic::Theme,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Animation {
|
||||
Tiled {
|
||||
start: Instant,
|
||||
previous_geometry: Rectangle<i32, Local>,
|
||||
},
|
||||
Minimize {
|
||||
start: Instant,
|
||||
previous_geometry: Rectangle<i32, Local>,
|
||||
target_geometry: Rectangle<i32, Local>,
|
||||
},
|
||||
Unminimize {
|
||||
start: Instant,
|
||||
previous_geometry: Rectangle<i32, Local>,
|
||||
target_geometry: Rectangle<i32, Local>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Animation {
|
||||
fn start(&self) -> &Instant {
|
||||
match self {
|
||||
Animation::Tiled { start, .. } => start,
|
||||
Animation::Minimize { start, .. } => start,
|
||||
Animation::Unminimize { start, .. } => start,
|
||||
}
|
||||
}
|
||||
|
||||
fn alpha(&self) -> f32 {
|
||||
match self {
|
||||
Animation::Tiled { .. } => 1.0,
|
||||
Animation::Minimize { start, .. } => {
|
||||
1.0 - (Instant::now()
|
||||
.duration_since(*start)
|
||||
.min(ANIMATION_DURATION)
|
||||
.as_secs_f32()
|
||||
/ ANIMATION_DURATION.as_secs_f32())
|
||||
}
|
||||
Animation::Unminimize { start, .. } => {
|
||||
Instant::now()
|
||||
.duration_since(*start)
|
||||
.min(ANIMATION_DURATION)
|
||||
.as_secs_f32()
|
||||
/ ANIMATION_DURATION.as_secs_f32()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn previous_geometry(&self) -> &Rectangle<i32, Local> {
|
||||
match self {
|
||||
Animation::Tiled {
|
||||
previous_geometry, ..
|
||||
} => previous_geometry,
|
||||
Animation::Minimize {
|
||||
previous_geometry, ..
|
||||
} => previous_geometry,
|
||||
Animation::Unminimize {
|
||||
previous_geometry, ..
|
||||
} => previous_geometry,
|
||||
}
|
||||
}
|
||||
|
||||
fn geometry(
|
||||
&self,
|
||||
output_geometry: Rectangle<i32, Logical>,
|
||||
tiled_state: Option<&TiledCorners>,
|
||||
) -> Rectangle<i32, Local> {
|
||||
let target_rect = match self {
|
||||
Animation::Minimize {
|
||||
target_geometry, ..
|
||||
}
|
||||
| Animation::Unminimize {
|
||||
target_geometry, ..
|
||||
} => target_geometry.clone(),
|
||||
Animation::Tiled {
|
||||
previous_geometry, ..
|
||||
} => {
|
||||
if let Some(target_rect) =
|
||||
tiled_state.map(|state| state.relative_geometry(output_geometry))
|
||||
{
|
||||
target_rect
|
||||
} else {
|
||||
previous_geometry.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
let previous_rect = self.previous_geometry().clone();
|
||||
let start = *self.start();
|
||||
let now = Instant::now();
|
||||
let progress = now
|
||||
.duration_since(start)
|
||||
.min(ANIMATION_DURATION)
|
||||
.as_secs_f64()
|
||||
/ ANIMATION_DURATION.as_secs_f64();
|
||||
|
||||
ease(
|
||||
EaseInOutCubic,
|
||||
EaseRectangle(previous_rect),
|
||||
EaseRectangle(target_rect),
|
||||
progress,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum TiledCorners {
|
||||
Top,
|
||||
|
|
@ -220,8 +324,13 @@ impl FloatingLayout {
|
|||
|
||||
mapped.moved_since_mapped.store(true, Ordering::SeqCst);
|
||||
|
||||
self.tiling_animations
|
||||
.insert(mapped.clone(), (Instant::now(), previous_geometry));
|
||||
self.animations.insert(
|
||||
mapped.clone(),
|
||||
Animation::Tiled {
|
||||
start: Instant::now(),
|
||||
previous_geometry,
|
||||
},
|
||||
);
|
||||
if mapped.floating_tiled.lock().unwrap().take().is_some() {
|
||||
if let Some(state) = mapped.maximized_state.lock().unwrap().as_mut() {
|
||||
if let Some(real_old_geo) = mapped.last_geometry.lock().unwrap().clone() {
|
||||
|
|
@ -433,13 +542,55 @@ impl FloatingLayout {
|
|||
.set_geometry(Rectangle::from_loc_and_size(position, win_geo.size).to_global(&output));
|
||||
mapped.configure();
|
||||
|
||||
if let Some(previous_geo) = prev.or(already_mapped) {
|
||||
self.tiling_animations
|
||||
.insert(mapped.clone(), (Instant::now(), previous_geo));
|
||||
if let Some(previous_geometry) = prev.or(already_mapped) {
|
||||
self.animations.insert(
|
||||
mapped.clone(),
|
||||
Animation::Tiled {
|
||||
start: Instant::now(),
|
||||
previous_geometry,
|
||||
},
|
||||
);
|
||||
}
|
||||
self.space.map_element(mapped, position.as_logical(), false);
|
||||
}
|
||||
|
||||
pub fn remap_minimized(
|
||||
&mut self,
|
||||
mapped: CosmicMapped,
|
||||
from: Rectangle<i32, Local>,
|
||||
position: Point<i32, Local>,
|
||||
) {
|
||||
let output = self.space.outputs().next().unwrap().clone();
|
||||
let layers = layer_map_for_output(&output);
|
||||
let geometry = layers.non_exclusive_zone().as_local();
|
||||
mapped.set_bounds(geometry.size.as_logical());
|
||||
let window_size = mapped.geometry().size;
|
||||
|
||||
if mapped.is_maximized(false) {
|
||||
mapped.set_geometry(geometry.to_global(&output));
|
||||
mapped.configure();
|
||||
} else {
|
||||
mapped.set_geometry(Rectangle::from_loc_and_size(
|
||||
position.to_global(&output),
|
||||
window_size.as_global(),
|
||||
));
|
||||
}
|
||||
|
||||
mapped.set_minimized(false);
|
||||
self.space
|
||||
.map_element(mapped.clone(), position.as_logical(), true);
|
||||
let target_geometry = self.space.element_geometry(&mapped).unwrap().as_local();
|
||||
|
||||
self.animations.insert(
|
||||
mapped,
|
||||
Animation::Unminimize {
|
||||
start: Instant::now(),
|
||||
previous_geometry: from,
|
||||
target_geometry,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, window: &CosmicMapped) -> bool {
|
||||
if let Some(_) = window.floating_tiled.lock().unwrap().take() {
|
||||
if let Some(last_size) = window.last_geometry.lock().unwrap().map(|geo| geo.size) {
|
||||
|
|
@ -467,7 +618,7 @@ impl FloatingLayout {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = self.tiling_animations.remove(window);
|
||||
let _ = self.animations.remove(window);
|
||||
|
||||
let was_unmaped = self.space.elements().any(|e| e == window);
|
||||
self.space.unmap_elem(&window);
|
||||
|
|
@ -480,6 +631,34 @@ impl FloatingLayout {
|
|||
was_unmaped
|
||||
}
|
||||
|
||||
pub fn unmap_minimize(
|
||||
&mut self,
|
||||
window: &CosmicMapped,
|
||||
to: Rectangle<i32, Local>,
|
||||
) -> Option<(CosmicMapped, Point<i32, Local>)> {
|
||||
let previous_geometry = self.space.element_geometry(window);
|
||||
self.space.unmap_elem(&window);
|
||||
if let Some(previous_geometry) = previous_geometry {
|
||||
if let Some(pos) = self.spawn_order.iter().position(|w| w == window) {
|
||||
self.spawn_order.truncate(pos);
|
||||
}
|
||||
window.moved_since_mapped.store(true, Ordering::SeqCst);
|
||||
self.animations.insert(
|
||||
window.clone(),
|
||||
Animation::Minimize {
|
||||
start: Instant::now(),
|
||||
previous_geometry: previous_geometry.as_local(),
|
||||
target_geometry: to,
|
||||
},
|
||||
);
|
||||
|
||||
window.set_minimized(true);
|
||||
Some((window.clone(), previous_geometry.loc.as_local()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drop_window(
|
||||
&mut self,
|
||||
window: CosmicMapped,
|
||||
|
|
@ -772,30 +951,8 @@ impl FloatingLayout {
|
|||
let output_geometry = layers.non_exclusive_zone();
|
||||
std::mem::drop(layers);
|
||||
|
||||
let start_rectangle = if let Some((previous_start, previous_rect)) =
|
||||
self.tiling_animations.remove(focused)
|
||||
{
|
||||
if let Some(target_rect) = tiled_state
|
||||
.as_ref()
|
||||
.map(|state| state.relative_geometry(output_geometry))
|
||||
{
|
||||
ease(
|
||||
EaseInOutCubic,
|
||||
EaseRectangle(previous_rect),
|
||||
EaseRectangle(target_rect),
|
||||
Instant::now()
|
||||
.duration_since(previous_start)
|
||||
.max(ANIMATION_DURATION)
|
||||
.as_secs_f64()
|
||||
/ ANIMATION_DURATION.as_secs_f64(),
|
||||
)
|
||||
.unwrap()
|
||||
} else {
|
||||
self.space
|
||||
.element_geometry(focused)
|
||||
.map(RectExt::as_local)
|
||||
.unwrap()
|
||||
}
|
||||
let start_rectangle = if let Some(anim) = self.animations.remove(focused) {
|
||||
anim.geometry(output_geometry, tiled_state.as_ref())
|
||||
} else {
|
||||
self.space
|
||||
.element_geometry(focused)
|
||||
|
|
@ -944,13 +1101,14 @@ impl FloatingLayout {
|
|||
}
|
||||
|
||||
pub fn animations_going(&self) -> bool {
|
||||
self.dirty.swap(false, Ordering::SeqCst) || !self.tiling_animations.is_empty()
|
||||
self.dirty.swap(false, Ordering::SeqCst) || !self.animations.is_empty()
|
||||
}
|
||||
|
||||
pub fn update_animation_state(&mut self) {
|
||||
self.tiling_animations
|
||||
.retain(|_, (start, _)| Instant::now().duration_since(*start) < ANIMATION_DURATION);
|
||||
if self.tiling_animations.is_empty() {
|
||||
let was_empty = self.animations.is_empty();
|
||||
self.animations
|
||||
.retain(|_, anim| Instant::now().duration_since(*anim.start()) < ANIMATION_DURATION);
|
||||
if self.animations.is_empty() != was_empty {
|
||||
self.dirty.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
|
@ -1000,12 +1158,18 @@ impl FloatingLayout {
|
|||
let mut window_elements = Vec::new();
|
||||
let mut popup_elements = Vec::new();
|
||||
|
||||
self.space.elements().rev().for_each(|elem| {
|
||||
let mut geometry = self
|
||||
.tiling_animations
|
||||
for elem in self
|
||||
.animations
|
||||
.iter()
|
||||
.filter(|(_, anim)| matches!(anim, Animation::Minimize { .. }))
|
||||
.map(|(elem, _)| elem)
|
||||
.chain(self.space.elements().rev())
|
||||
{
|
||||
let (mut geometry, alpha) = self
|
||||
.animations
|
||||
.get(elem)
|
||||
.map(|(_, rect)| *rect)
|
||||
.unwrap_or_else(|| self.space.element_geometry(elem).unwrap().as_local());
|
||||
.map(|anim| (*anim.previous_geometry(), alpha * anim.alpha()))
|
||||
.unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha));
|
||||
|
||||
let render_location = geometry.loc - elem.geometry().loc.as_local();
|
||||
let (mut w_elements, p_elements) = elem.split_render_elements(
|
||||
|
|
@ -1017,30 +1181,12 @@ impl FloatingLayout {
|
|||
alpha,
|
||||
);
|
||||
|
||||
if let Some((start, original_geo)) = self.tiling_animations.get(elem) {
|
||||
let target_rect = elem
|
||||
.floating_tiled
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|state| state.relative_geometry(output_geometry))
|
||||
.or_else(|| {
|
||||
elem.is_maximized(true)
|
||||
.then_some(output_geometry.as_local())
|
||||
})
|
||||
.unwrap_or_else(|| self.space.element_geometry(elem).unwrap().as_local());
|
||||
|
||||
geometry = ease(
|
||||
EaseInOutCubic,
|
||||
EaseRectangle(original_geo.clone()),
|
||||
EaseRectangle(target_rect),
|
||||
Instant::now()
|
||||
.duration_since(*start)
|
||||
.min(ANIMATION_DURATION)
|
||||
.as_millis() as f32
|
||||
/ ANIMATION_DURATION.as_millis() as f32,
|
||||
)
|
||||
.unwrap();
|
||||
if let Some(anim) = self.animations.get(elem) {
|
||||
let original_geo = anim.previous_geometry();
|
||||
geometry = anim.geometry(
|
||||
output_geometry,
|
||||
elem.floating_tiled.lock().unwrap().as_ref(),
|
||||
);
|
||||
|
||||
let buffer_size = elem.geometry().size;
|
||||
let scale = Scale {
|
||||
|
|
@ -1142,7 +1288,7 @@ impl FloatingLayout {
|
|||
|
||||
window_elements.extend(w_elements);
|
||||
popup_elements.extend(p_elements);
|
||||
});
|
||||
}
|
||||
|
||||
(window_elements, popup_elements)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ pub enum Data {
|
|||
Mapped {
|
||||
mapped: CosmicMapped,
|
||||
last_geometry: Rectangle<i32, Local>,
|
||||
minimize_to: Option<Rectangle<i32, Local>>,
|
||||
},
|
||||
Placeholder {
|
||||
last_geometry: Rectangle<i32, Local>,
|
||||
|
|
@ -317,6 +318,14 @@ enum FocusedNodeData {
|
|||
Window(CosmicMapped),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MinimizedTilingState {
|
||||
pub parent: id_tree::NodeId,
|
||||
pub orientation: Orientation,
|
||||
pub idx: usize,
|
||||
pub sizes: Vec<i32>,
|
||||
}
|
||||
|
||||
impl TilingLayout {
|
||||
pub fn new(theme: cosmic::Theme, output: &Output) -> TilingLayout {
|
||||
TilingLayout {
|
||||
|
|
@ -347,11 +356,7 @@ impl TilingLayout {
|
|||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if let Data::Mapped {
|
||||
mapped,
|
||||
last_geometry: _,
|
||||
} = node.data()
|
||||
{
|
||||
if let Data::Mapped { mapped, .. } = node.data() {
|
||||
mapped.output_leave(&self.output);
|
||||
mapped.output_enter(output, mapped.bbox());
|
||||
}
|
||||
|
|
@ -390,6 +395,75 @@ impl TilingLayout {
|
|||
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
|
||||
}
|
||||
|
||||
pub fn remap_minimized<'a>(
|
||||
&mut self,
|
||||
window: CosmicMapped,
|
||||
from: Rectangle<i32, Local>,
|
||||
tiling_state: Option<MinimizedTilingState>,
|
||||
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
|
||||
) {
|
||||
let gaps = self.gaps();
|
||||
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
|
||||
|
||||
if let Some(MinimizedTilingState {
|
||||
parent,
|
||||
orientation,
|
||||
idx,
|
||||
mut sizes,
|
||||
}) = tiling_state
|
||||
{
|
||||
if let Ok(node) = tree.get_mut(&parent) {
|
||||
if let Data::Group {
|
||||
orientation: current_orientation,
|
||||
sizes: current_sizes,
|
||||
..
|
||||
} = node.data_mut()
|
||||
{
|
||||
if *current_orientation == orientation && sizes.len() == current_sizes.len() + 1
|
||||
{
|
||||
let previous_length: i32 = sizes.iter().copied().sum();
|
||||
let new_length: i32 = current_sizes.iter().copied().sum();
|
||||
if previous_length != new_length {
|
||||
// rescale sizes
|
||||
sizes.iter_mut().for_each(|len| {
|
||||
*len = (((*len as f64) / (previous_length as f64))
|
||||
* (new_length as f64))
|
||||
.round() as i32;
|
||||
});
|
||||
let sum: i32 = sizes.iter().sum();
|
||||
|
||||
// fix rounding issues
|
||||
if sum != new_length {
|
||||
let diff = new_length - sum;
|
||||
*sizes.last_mut().unwrap() += diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*current_sizes = sizes;
|
||||
let new_node = Node::new(Data::Mapped {
|
||||
mapped: window.clone(),
|
||||
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
|
||||
minimize_to: Some(from),
|
||||
});
|
||||
let new_id = tree
|
||||
.insert(new_node, InsertBehavior::UnderNode(&parent))
|
||||
.unwrap();
|
||||
tree.make_nth_sibling(&new_id, idx).unwrap();
|
||||
*window.tiling_node_id.lock().unwrap() = Some(new_id);
|
||||
window.set_minimized(false);
|
||||
|
||||
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
|
||||
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// else add as new_window
|
||||
self.map_internal(window, focus_stack, None);
|
||||
}
|
||||
|
||||
fn map_to_tree<'a>(
|
||||
mut tree: &mut Tree<Data>,
|
||||
window: impl Into<CosmicMapped>,
|
||||
|
|
@ -401,6 +475,7 @@ impl TilingLayout {
|
|||
let new_window = Node::new(Data::Mapped {
|
||||
mapped: window.clone(),
|
||||
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
|
||||
minimize_to: None,
|
||||
});
|
||||
|
||||
let window_id = if let Some(direction) = direction {
|
||||
|
|
@ -934,6 +1009,7 @@ impl TilingLayout {
|
|||
.replace_data(Data::Mapped {
|
||||
mapped,
|
||||
last_geometry: geometry,
|
||||
minimize_to: None,
|
||||
});
|
||||
}
|
||||
(None, Some(other_surface)) => {
|
||||
|
|
@ -1021,6 +1097,7 @@ impl TilingLayout {
|
|||
.replace_data(Data::Mapped {
|
||||
mapped,
|
||||
last_geometry: geometry,
|
||||
minimize_to: None,
|
||||
});
|
||||
}
|
||||
(Some(this_surface), Some(other_surface)) => {
|
||||
|
|
@ -1173,6 +1250,53 @@ impl TilingLayout {
|
|||
Some(node_id)
|
||||
}
|
||||
|
||||
pub fn unmap_minimize(
|
||||
&mut self,
|
||||
window: &CosmicMapped,
|
||||
to: Rectangle<i32, Local>,
|
||||
) -> Option<MinimizedTilingState> {
|
||||
let node_id = window.tiling_node_id.lock().unwrap().clone()?;
|
||||
let state = {
|
||||
let tree = &self.queue.trees.back().unwrap().0;
|
||||
tree.get(&node_id).unwrap().parent().and_then(|parent_id| {
|
||||
let parent = tree.get(&parent_id).unwrap();
|
||||
let idx = parent
|
||||
.children()
|
||||
.iter()
|
||||
.position(|id| id == &node_id)
|
||||
.unwrap();
|
||||
if let Data::Group {
|
||||
orientation, sizes, ..
|
||||
} = parent.data()
|
||||
{
|
||||
Some(MinimizedTilingState {
|
||||
parent: parent_id.clone(),
|
||||
orientation: *orientation,
|
||||
idx,
|
||||
sizes: sizes.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
if self.unmap_window_internal(window) {
|
||||
let tree = &mut self
|
||||
.queue
|
||||
.trees
|
||||
.get_mut(self.queue.trees.len() - 2)
|
||||
.unwrap()
|
||||
.0;
|
||||
if let Data::Mapped { minimize_to, .. } = tree.get_mut(&node_id).unwrap().data_mut() {
|
||||
*minimize_to = Some(to);
|
||||
}
|
||||
window.set_minimized(true);
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
fn unmap_window_internal(&mut self, mapped: &CosmicMapped) -> bool {
|
||||
let tiling_node_id = mapped.tiling_node_id.lock().unwrap().as_ref().cloned();
|
||||
let gaps = self.gaps();
|
||||
|
|
@ -1297,6 +1421,7 @@ impl TilingLayout {
|
|||
let new_node = Node::new(Data::Mapped {
|
||||
mapped: mapped.clone(),
|
||||
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
|
||||
minimize_to: None,
|
||||
});
|
||||
let new_id = tree.insert(new_node, InsertBehavior::AsRoot).unwrap();
|
||||
TilingLayout::new_group(&mut tree, &node_id, &new_id, orientation).unwrap();
|
||||
|
|
@ -2070,6 +2195,7 @@ impl TilingLayout {
|
|||
*data = Data::Mapped {
|
||||
mapped: mapped.clone(),
|
||||
last_geometry: geo,
|
||||
minimize_to: None,
|
||||
};
|
||||
|
||||
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
|
||||
|
|
@ -2406,6 +2532,7 @@ impl TilingLayout {
|
|||
Node::new(Data::Mapped {
|
||||
mapped: window.clone(),
|
||||
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
|
||||
minimize_to: None,
|
||||
}),
|
||||
InsertBehavior::UnderNode(group_id),
|
||||
)
|
||||
|
|
@ -2441,6 +2568,7 @@ impl TilingLayout {
|
|||
Node::new(Data::Mapped {
|
||||
mapped: window.clone(),
|
||||
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
|
||||
minimize_to: None,
|
||||
}),
|
||||
InsertBehavior::UnderNode(group_id),
|
||||
)
|
||||
|
|
@ -2462,6 +2590,7 @@ impl TilingLayout {
|
|||
*data = Data::Mapped {
|
||||
mapped: window.clone(),
|
||||
last_geometry: geo,
|
||||
minimize_to: None,
|
||||
};
|
||||
*window.tiling_node_id.lock().unwrap() = Some(node_id.clone());
|
||||
window
|
||||
|
|
@ -2472,6 +2601,7 @@ impl TilingLayout {
|
|||
Node::new(Data::Mapped {
|
||||
mapped: window.clone(),
|
||||
last_geometry: Rectangle::from_loc_and_size((0, 0), (100, 100)),
|
||||
minimize_to: None,
|
||||
}),
|
||||
InsertBehavior::UnderNode(&window_id),
|
||||
)
|
||||
|
|
@ -2916,6 +3046,7 @@ impl TilingLayout {
|
|||
Data::Mapped {
|
||||
mapped,
|
||||
last_geometry,
|
||||
..
|
||||
},
|
||||
)) => {
|
||||
let test_point = (location.to_f64() - last_geometry.loc.to_f64()
|
||||
|
|
|
|||
270
src/shell/mod.rs
270
src/shell/mod.rs
|
|
@ -50,7 +50,10 @@ use crate::{
|
|||
state::client_should_see_privileged_protocols,
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::{xdg_activation::ActivationContext, xdg_shell::popup::get_popup_toplevel},
|
||||
handlers::{
|
||||
toplevel_management::ToplevelManagementExt, xdg_activation::ActivationContext,
|
||||
xdg_shell::popup::get_popup_toplevel,
|
||||
},
|
||||
protocols::{
|
||||
toplevel_info::ToplevelInfoState,
|
||||
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
|
||||
|
|
@ -289,6 +292,12 @@ fn move_workspace_to_group(
|
|||
toplevel_info_state.toplevel_enter_workspace(&surface, &workspace.handle);
|
||||
}
|
||||
}
|
||||
for window in workspace.minimized_windows.iter() {
|
||||
for (surface, _) in window.window.windows() {
|
||||
toplevel_info_state.toplevel_leave_workspace(&surface, &old_workspace_handle);
|
||||
toplevel_info_state.toplevel_enter_workspace(&surface, &workspace.handle);
|
||||
}
|
||||
}
|
||||
workspace_state.remove_workspace(old_workspace_handle);
|
||||
}
|
||||
|
||||
|
|
@ -311,6 +320,7 @@ fn merge_workspaces(
|
|||
toplevel_info_state.toplevel_enter_workspace(&toplevel, &into.handle);
|
||||
}
|
||||
}
|
||||
// TODO: merge minimized windows
|
||||
into.tiling_layer.merge(workspace.tiling_layer);
|
||||
into.floating_layer.merge(workspace.floating_layer);
|
||||
workspace_state.remove_workspace(workspace.handle);
|
||||
|
|
@ -433,7 +443,7 @@ impl WorkspaceSet {
|
|||
if self
|
||||
.workspaces
|
||||
.last()
|
||||
.map(|last| !last.pending_tokens.is_empty() || last.windows().next().is_some())
|
||||
.map(|last| !last.is_empty())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
self.add_empty_workspace(state);
|
||||
|
|
@ -443,10 +453,7 @@ impl WorkspaceSet {
|
|||
let len = self.workspaces.len();
|
||||
let mut keep = vec![true; len];
|
||||
for (i, workspace) in self.workspaces.iter().enumerate() {
|
||||
let has_windows =
|
||||
!workspace.pending_tokens.is_empty() || workspace.windows().next().is_some();
|
||||
|
||||
if !has_windows && i != self.active && i != len - 1 {
|
||||
if workspace.is_empty() && i != self.active && i != len - 1 {
|
||||
state.remove_workspace(workspace.handle);
|
||||
keep[i] = false;
|
||||
}
|
||||
|
|
@ -609,6 +616,22 @@ impl Workspaces {
|
|||
workspace.refresh(xdg_activation_state);
|
||||
new_set.workspaces.push(workspace);
|
||||
}
|
||||
|
||||
for window in set.sticky_layer.mapped() {
|
||||
for (surface, _) in window.windows() {
|
||||
toplevel_info_state.toplevel_leave_output(&surface, output);
|
||||
toplevel_info_state.toplevel_enter_output(&surface, &new_output);
|
||||
}
|
||||
}
|
||||
new_set.sticky_layer.merge(set.sticky_layer);
|
||||
for window in set.minimized_windows.iter() {
|
||||
for (surface, _) in window.window.windows() {
|
||||
toplevel_info_state.toplevel_leave_output(&surface, output);
|
||||
toplevel_info_state.toplevel_enter_output(&surface, &new_output);
|
||||
}
|
||||
}
|
||||
new_set.minimized_windows.extend(set.minimized_windows);
|
||||
|
||||
if self.mode == WorkspaceMode::OutputBound {
|
||||
workspace_state.remove_workspace_group(set.group);
|
||||
} else {
|
||||
|
|
@ -776,10 +799,7 @@ impl Workspaces {
|
|||
let mut active = self.sets[0].active;
|
||||
let mut keep = vec![true; len];
|
||||
for i in 0..len {
|
||||
let has_windows = self.sets.values().any(|s| {
|
||||
!s.workspaces[i].pending_tokens.is_empty()
|
||||
|| s.workspaces[i].windows().next().is_some()
|
||||
});
|
||||
let has_windows = self.sets.values().any(|s| !s.workspaces[i].is_empty());
|
||||
|
||||
if !has_windows && i != active && i != len - 1 {
|
||||
for workspace in self.sets.values().map(|s| &s.workspaces[i]) {
|
||||
|
|
@ -1231,6 +1251,9 @@ impl Shell {
|
|||
.find(|w| {
|
||||
w.mapped()
|
||||
.any(|e| e.has_surface(surface, WindowSurfaceType::ALL))
|
||||
|| w.minimized_windows
|
||||
.iter()
|
||||
.any(|m| m.window.has_surface(surface, WindowSurfaceType::ALL))
|
||||
})
|
||||
.map(|w| (w.handle.clone(), w.output().clone())),
|
||||
}
|
||||
|
|
@ -1238,8 +1261,10 @@ impl Shell {
|
|||
|
||||
pub fn element_for_surface(&self, surface: &CosmicSurface) -> Option<&CosmicMapped> {
|
||||
self.workspaces.sets.values().find_map(|set| {
|
||||
set.sticky_layer
|
||||
.mapped()
|
||||
set.minimized_windows
|
||||
.iter()
|
||||
.map(|w| &w.window)
|
||||
.chain(set.sticky_layer.mapped())
|
||||
.find(|w| w.windows().any(|(s, _)| &s == surface))
|
||||
.or_else(|| {
|
||||
set.workspaces
|
||||
|
|
@ -1251,8 +1276,10 @@ impl Shell {
|
|||
|
||||
pub fn element_for_wl_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> {
|
||||
self.workspaces.sets.values().find_map(|set| {
|
||||
set.sticky_layer
|
||||
.mapped()
|
||||
set.minimized_windows
|
||||
.iter()
|
||||
.map(|w| &w.window)
|
||||
.chain(set.sticky_layer.mapped())
|
||||
.find(|w| {
|
||||
w.windows()
|
||||
.any(|(s, _)| s.wl_surface().as_ref() == Some(surface))
|
||||
|
|
@ -1267,8 +1294,10 @@ impl Shell {
|
|||
|
||||
pub fn element_for_x11_surface(&self, surface: &X11Surface) -> Option<&CosmicMapped> {
|
||||
self.workspaces.sets.values().find_map(|set| {
|
||||
set.sticky_layer
|
||||
.mapped()
|
||||
set.minimized_windows
|
||||
.iter()
|
||||
.map(|w| &w.window)
|
||||
.chain(set.sticky_layer.mapped())
|
||||
.find(|w| w.windows().any(|(s, _)| s.x11_surface() == Some(surface)))
|
||||
.or_else(|| {
|
||||
set.workspaces
|
||||
|
|
@ -1279,15 +1308,23 @@ impl Shell {
|
|||
}
|
||||
|
||||
pub fn space_for(&self, mapped: &CosmicMapped) -> Option<&Workspace> {
|
||||
self.workspaces
|
||||
.spaces()
|
||||
.find(|workspace| workspace.mapped().any(|m| m == mapped))
|
||||
self.workspaces.spaces().find(|workspace| {
|
||||
workspace.mapped().any(|m| m == mapped)
|
||||
|| workspace
|
||||
.minimized_windows
|
||||
.iter()
|
||||
.any(|m| &m.window == mapped)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn space_for_mut(&mut self, mapped: &CosmicMapped) -> Option<&mut Workspace> {
|
||||
self.workspaces
|
||||
.spaces_mut()
|
||||
.find(|workspace| workspace.mapped().any(|m| m == mapped))
|
||||
self.workspaces.spaces_mut().find(|workspace| {
|
||||
workspace.mapped().any(|m| m == mapped)
|
||||
|| workspace
|
||||
.minimized_windows
|
||||
.iter()
|
||||
.any(|m| &m.window == mapped)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn outputs(&self) -> impl DoubleEndedIterator<Item = &Output> {
|
||||
|
|
@ -1731,7 +1768,13 @@ impl Shell {
|
|||
}
|
||||
|
||||
if !parent_is_sticky && should_be_fullscreen {
|
||||
workspace.fullscreen_request(&mapped.active_window(), None);
|
||||
let from = state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&output, &mapped.active_window());
|
||||
|
||||
workspace.fullscreen_request(&mapped.active_window(), None, from, &seat);
|
||||
}
|
||||
|
||||
if parent_is_sticky {
|
||||
|
|
@ -1753,11 +1796,7 @@ impl Shell {
|
|||
None,
|
||||
);
|
||||
} else if workspace_empty || was_activated || should_be_fullscreen {
|
||||
Shell::append_focus_stack(
|
||||
state,
|
||||
Some(&KeyboardFocusTarget::from(mapped.clone())),
|
||||
&seat,
|
||||
);
|
||||
Shell::append_focus_stack(state, &mapped, &seat);
|
||||
state.common.shell.set_urgent(&workspace_handle);
|
||||
}
|
||||
|
||||
|
|
@ -1891,6 +1930,7 @@ impl Shell {
|
|||
None
|
||||
};
|
||||
|
||||
let any_seat = seat.unwrap_or(state.common.last_active_seat()).clone();
|
||||
let mut to_workspace = state
|
||||
.common
|
||||
.shell
|
||||
|
|
@ -1938,7 +1978,13 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
to_workspace.fullscreen_request(&mapped.active_window(), f.previously);
|
||||
let from = state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&to_output, &mapped.active_window());
|
||||
|
||||
to_workspace.fullscreen_request(&mapped.active_window(), f.previously, from, &any_seat);
|
||||
to_workspace
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
|
|
@ -2027,19 +2073,19 @@ impl Shell {
|
|||
|
||||
pub fn update_reactive_popups(&self, mapped: &CosmicMapped) {
|
||||
if let Some(workspace) = self.space_for(mapped) {
|
||||
let element_loc = workspace
|
||||
if let Some(element_loc) = workspace
|
||||
.element_geometry(mapped)
|
||||
.unwrap()
|
||||
.loc
|
||||
.to_global(&workspace.output);
|
||||
for (window, offset) in mapped.windows() {
|
||||
if let Some(toplevel) = window.0.toplevel() {
|
||||
let window_geo_offset = window.geometry().loc.as_global();
|
||||
update_reactive_popups(
|
||||
toplevel,
|
||||
element_loc + offset.as_global() + window_geo_offset,
|
||||
self.outputs(),
|
||||
);
|
||||
.map(|geo| geo.loc.to_global(&workspace.output))
|
||||
{
|
||||
for (window, offset) in mapped.windows() {
|
||||
if let Some(toplevel) = window.0.toplevel() {
|
||||
let window_geo_offset = window.geometry().loc.as_global();
|
||||
update_reactive_popups(
|
||||
toplevel,
|
||||
element_loc + offset.as_global() + window_geo_offset,
|
||||
self.outputs(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2089,10 +2135,12 @@ impl Shell {
|
|||
} else if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
|
||||
let output = seat.active_output();
|
||||
|
||||
let global_position = (workspace.element_geometry(&mapped).unwrap().loc
|
||||
+ relative_loc.as_local()
|
||||
+ location.as_local())
|
||||
.to_global(&output);
|
||||
let Some(elem_geo) = workspace.element_geometry(&mapped) else {
|
||||
return;
|
||||
};
|
||||
let global_position =
|
||||
(elem_geo.loc + relative_loc.as_local() + location.as_local())
|
||||
.to_global(&output);
|
||||
let is_tiled = workspace.is_tiled(&mapped);
|
||||
let edge = if is_tiled {
|
||||
mapped
|
||||
|
|
@ -2177,6 +2225,10 @@ impl Shell {
|
|||
if let Some(mut old_mapped) =
|
||||
state.common.shell.element_for_wl_surface(surface).cloned()
|
||||
{
|
||||
if old_mapped.is_minimized() {
|
||||
return;
|
||||
}
|
||||
|
||||
let seats = state.common.seats().cloned().collect::<Vec<_>>();
|
||||
for workspace in state.common.shell.workspaces.spaces_mut() {
|
||||
for seat in seats.iter() {
|
||||
|
|
@ -2222,11 +2274,10 @@ impl Shell {
|
|||
let _ = workspace.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace
|
||||
}
|
||||
|
||||
let mut initial_window_location = workspace
|
||||
.element_geometry(&old_mapped)
|
||||
.unwrap()
|
||||
.loc
|
||||
.to_global(&output);
|
||||
let Some(elem_geo) = workspace.element_geometry(&old_mapped) else {
|
||||
return;
|
||||
};
|
||||
let mut initial_window_location = elem_geo.loc.to_global(&output);
|
||||
|
||||
if mapped.maximized_state.lock().unwrap().is_some() {
|
||||
// If surface is maximized then unmaximize it
|
||||
|
|
@ -2616,19 +2667,91 @@ impl Shell {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn maximize_toggle(&mut self, window: &CosmicMapped) {
|
||||
pub fn maximize_toggle(&mut self, window: &CosmicMapped, seat: &Seat<State>) {
|
||||
if window.is_maximized(true) {
|
||||
self.unmaximize_request(window);
|
||||
} else {
|
||||
self.maximize_request(window);
|
||||
self.maximize_request(window, seat);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn minimize_request(&mut self, mapped: &CosmicMapped) {}
|
||||
pub fn minimize_request(&mut self, mapped: &CosmicMapped) {
|
||||
if let Some(set) = self
|
||||
.workspaces
|
||||
.sets
|
||||
.values_mut()
|
||||
.find(|set| set.sticky_layer.mapped().any(|m| m == mapped))
|
||||
{
|
||||
let to = self
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&set.output, &mapped.active_window());
|
||||
let (window, position) = set.sticky_layer.unmap_minimize(mapped, to).unwrap();
|
||||
set.minimized_windows.push(MinimizedWindow {
|
||||
window,
|
||||
previous_state: MinimizedState::Sticky { position },
|
||||
output_geo: set.output.geometry(),
|
||||
fullscreen: None,
|
||||
});
|
||||
} else if let Some(workspace) = self.workspaces.sets.values_mut().find_map(|set| {
|
||||
set.workspaces
|
||||
.iter_mut()
|
||||
.find(|workspace| workspace.mapped().any(|m| m == mapped))
|
||||
}) {
|
||||
let to = self
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(workspace.output(), &mapped.active_window());
|
||||
if let Some(minimized) = workspace.minimize(&mapped, to) {
|
||||
workspace.minimized_windows.push(minimized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unminimize_request(&mut self, mapped: &CosmicMapped) {}
|
||||
pub fn unminimize_request(&mut self, mapped: &CosmicMapped, seat: &Seat<State>) {
|
||||
if let Some((set, window)) = self.workspaces.sets.values_mut().find_map(|set| {
|
||||
set.minimized_windows
|
||||
.iter()
|
||||
.position(|m| &m.window == mapped)
|
||||
.map(|i| set.minimized_windows.swap_remove(i))
|
||||
.map(|window| (set, window))
|
||||
}) {
|
||||
let from = self
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&set.output, &mapped.active_window());
|
||||
|
||||
pub fn maximize_request(&mut self, mapped: &CosmicMapped) {
|
||||
if let MinimizedState::Sticky { mut position } = window.previous_state {
|
||||
let current_output_size = set.output.geometry().size.as_logical();
|
||||
if current_output_size != window.output_geo.size.as_logical() {
|
||||
position = Point::from((
|
||||
(position.x as f64 / window.output_geo.size.w as f64
|
||||
* current_output_size.w as f64)
|
||||
.floor() as i32,
|
||||
(position.y as f64 / window.output_geo.size.h as f64
|
||||
* current_output_size.h as f64)
|
||||
.floor() as i32,
|
||||
))
|
||||
};
|
||||
set.sticky_layer
|
||||
.remap_minimized(window.window, from, position);
|
||||
} else {
|
||||
unreachable!("None sticky window in WorkspaceSet minimized_windows");
|
||||
}
|
||||
} else if let Some((workspace, window)) = self.workspaces.spaces_mut().find_map(|w| {
|
||||
w.minimized_windows
|
||||
.iter()
|
||||
.position(|m| &m.window == mapped)
|
||||
.map(|i| w.minimized_windows.swap_remove(i))
|
||||
.map(|window| (w, window))
|
||||
}) {
|
||||
let from = self
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(workspace.output(), &mapped.active_window());
|
||||
|
||||
workspace.unminimize(window, from, seat);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maximize_request(&mut self, mapped: &CosmicMapped, seat: &Seat<State>) {
|
||||
self.unminimize_request(mapped, seat);
|
||||
let (original_layer, floating_layer, original_geometry) = if let Some(set) = self
|
||||
.workspaces
|
||||
.sets
|
||||
|
|
@ -2661,22 +2784,29 @@ impl Shell {
|
|||
}
|
||||
|
||||
pub fn unmaximize_request(&mut self, mapped: &CosmicMapped) -> Option<Size<i32, Logical>> {
|
||||
if let Some(set) = self
|
||||
.workspaces
|
||||
.sets
|
||||
.values_mut()
|
||||
.find(|set| set.sticky_layer.mapped().any(|m| m == mapped))
|
||||
{
|
||||
if let Some(set) = self.workspaces.sets.values_mut().find(|set| {
|
||||
set.sticky_layer.mapped().any(|m| m == mapped)
|
||||
|| set.minimized_windows.iter().any(|m| &m.window == mapped)
|
||||
}) {
|
||||
let mut state = mapped.maximized_state.lock().unwrap();
|
||||
if let Some(state) = state.take() {
|
||||
assert_eq!(state.original_layer, ManagedLayer::Sticky);
|
||||
mapped.set_maximized(false);
|
||||
set.sticky_layer.map_internal(
|
||||
mapped.clone(),
|
||||
Some(state.original_geometry.loc),
|
||||
Some(state.original_geometry.size.as_logical()),
|
||||
None,
|
||||
);
|
||||
|
||||
if let Some(minimized) = set
|
||||
.minimized_windows
|
||||
.iter_mut()
|
||||
.find(|m| &m.window == mapped)
|
||||
{
|
||||
minimized.unmaximize(state.original_geometry);
|
||||
} else {
|
||||
mapped.set_maximized(false);
|
||||
set.sticky_layer.map_internal(
|
||||
mapped.clone(),
|
||||
Some(state.original_geometry.loc),
|
||||
Some(state.original_geometry.size.as_logical()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
Some(state.original_geometry.size.as_logical())
|
||||
} else {
|
||||
None
|
||||
|
|
@ -2875,7 +3005,9 @@ impl Shell {
|
|||
} else {
|
||||
ManagedLayer::Floating
|
||||
};
|
||||
let geometry = workspace.element_geometry(mapped).unwrap();
|
||||
let Some(geometry) = workspace.element_geometry(mapped) else {
|
||||
return;
|
||||
};
|
||||
workspace.unmap(mapped);
|
||||
|
||||
*mapped.previous_layer.lock().unwrap() = Some(previous_layer);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use smithay::{
|
|||
glow::{GlowFrame, GlowRenderer},
|
||||
ImportAll, ImportMem, Renderer,
|
||||
},
|
||||
desktop::{layer_map_for_output, space::SpaceElement},
|
||||
desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType},
|
||||
input::Seat,
|
||||
output::Output,
|
||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, Resource},
|
||||
|
|
@ -66,7 +66,7 @@ use super::{
|
|||
FocusStack, FocusStackMut,
|
||||
},
|
||||
grabs::ResizeEdge,
|
||||
layout::tiling::{Data, NodeDesc},
|
||||
layout::tiling::{Data, MinimizedTilingState, NodeDesc},
|
||||
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
|
||||
};
|
||||
|
||||
|
|
@ -94,10 +94,46 @@ pub struct Workspace {
|
|||
#[derive(Debug)]
|
||||
pub struct MinimizedWindow {
|
||||
pub window: CosmicMapped,
|
||||
pub previous_layer: ManagedLayer,
|
||||
pub was_fullscreen: Option<FullscreenSurface>,
|
||||
pub was_maximized: bool,
|
||||
pub tiling_state: Option<(id_tree::NodeId, Rectangle<i32, Logical>)>,
|
||||
pub previous_state: MinimizedState,
|
||||
pub fullscreen: Option<FullscreenSurface>,
|
||||
pub output_geo: Rectangle<i32, Global>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MinimizedState {
|
||||
Sticky {
|
||||
position: Point<i32, Local>,
|
||||
},
|
||||
Floating {
|
||||
position: Point<i32, Local>,
|
||||
},
|
||||
Tiling {
|
||||
tiling_state: Option<MinimizedTilingState>,
|
||||
was_maximized: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl MinimizedWindow {
|
||||
pub(super) fn unmaximize(&mut self, original_geometry: Rectangle<i32, Local>) {
|
||||
self.window.set_maximized(false);
|
||||
self.window.configure();
|
||||
|
||||
match &mut self.previous_state {
|
||||
MinimizedState::Sticky { position } | MinimizedState::Floating { position } => {
|
||||
*position = original_geometry.loc;
|
||||
}
|
||||
MinimizedState::Tiling { was_maximized, .. } => {
|
||||
*was_maximized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unfullscreen(&mut self) -> Option<(ManagedLayer, WorkspaceHandle)> {
|
||||
let fullscreen = self.fullscreen.take()?;
|
||||
self.window.set_fullscreen(false);
|
||||
self.window.set_geometry(fullscreen.original_geometry);
|
||||
fullscreen.previously
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -338,6 +374,18 @@ impl Workspace {
|
|||
.0
|
||||
.on_commit();
|
||||
}
|
||||
if let Some(mapped) = self.minimized_windows.iter().find_map(|m| {
|
||||
m.window
|
||||
.has_surface(surface, WindowSurfaceType::TOPLEVEL)
|
||||
.then_some(&m.window)
|
||||
}) {
|
||||
mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface))
|
||||
.unwrap()
|
||||
.0
|
||||
.on_commit();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output(&self) -> &Output {
|
||||
|
|
@ -357,6 +405,12 @@ impl Workspace {
|
|||
toplevel_info.toplevel_enter_output(&surface, output);
|
||||
}
|
||||
}
|
||||
for window in self.minimized_windows.iter() {
|
||||
for (surface, _) in window.window.windows() {
|
||||
toplevel_info.toplevel_leave_output(&surface, &self.output);
|
||||
toplevel_info.toplevel_enter_output(&surface, output);
|
||||
}
|
||||
}
|
||||
let output_name = output.name();
|
||||
if let Some(pos) = self
|
||||
.output_stack
|
||||
|
|
@ -375,7 +429,7 @@ impl Workspace {
|
|||
}
|
||||
|
||||
pub fn unmap(&mut self, mapped: &CosmicMapped) -> Option<ManagedState> {
|
||||
let was_fullscreen = self
|
||||
let mut was_fullscreen = self
|
||||
.fullscreen
|
||||
.as_ref()
|
||||
.filter(|f| f.ended_at.is_none())
|
||||
|
|
@ -388,11 +442,27 @@ impl Workspace {
|
|||
let _ = self.unmaximize_request(mapped);
|
||||
}
|
||||
|
||||
let was_floating = self.floating_layer.unmap(&mapped);
|
||||
let was_tiling = self.tiling_layer.unmap(&mapped);
|
||||
let mut was_floating = self.floating_layer.unmap(&mapped);
|
||||
let mut was_tiling = self.tiling_layer.unmap(&mapped);
|
||||
if was_floating || was_tiling {
|
||||
assert!(was_floating != was_tiling);
|
||||
}
|
||||
if let Some(pos) = self
|
||||
.minimized_windows
|
||||
.iter()
|
||||
.position(|m| &m.window == mapped)
|
||||
{
|
||||
let state = self.minimized_windows.remove(pos);
|
||||
match state.previous_state {
|
||||
MinimizedState::Sticky { .. } | MinimizedState::Floating { .. } => {
|
||||
was_floating = true;
|
||||
}
|
||||
MinimizedState::Tiling { .. } => {
|
||||
was_tiling = true;
|
||||
}
|
||||
}
|
||||
was_fullscreen = state.fullscreen;
|
||||
}
|
||||
|
||||
self.focus_stack
|
||||
.0
|
||||
|
|
@ -417,6 +487,7 @@ impl Workspace {
|
|||
self.floating_layer
|
||||
.mapped()
|
||||
.chain(self.tiling_layer.mapped().map(|(w, _)| w))
|
||||
.chain(self.minimized_windows.iter().map(|w| &w.window))
|
||||
.find(|e| e.windows().any(|(w, _)| &w == surface))
|
||||
}
|
||||
|
||||
|
|
@ -424,6 +495,7 @@ impl Workspace {
|
|||
self.floating_layer
|
||||
.mapped()
|
||||
.chain(self.tiling_layer.mapped().map(|(w, _)| w))
|
||||
.chain(self.minimized_windows.iter().map(|w| &w.window))
|
||||
.find(|e| {
|
||||
e.windows()
|
||||
.any(|(w, _)| w.wl_surface().as_ref() == Some(surface))
|
||||
|
|
@ -434,6 +506,7 @@ impl Workspace {
|
|||
self.floating_layer
|
||||
.mapped()
|
||||
.chain(self.tiling_layer.mapped().map(|(w, _)| w))
|
||||
.chain(self.minimized_windows.iter().map(|w| &w.window))
|
||||
.find(|e| e.windows().any(|(w, _)| w.x11_surface() == Some(surface)))
|
||||
}
|
||||
|
||||
|
|
@ -463,29 +536,38 @@ impl Workspace {
|
|||
pub fn unmaximize_request(&mut self, elem: &CosmicMapped) -> Option<Size<i32, Logical>> {
|
||||
let mut state = elem.maximized_state.lock().unwrap();
|
||||
if let Some(state) = state.take() {
|
||||
match state.original_layer {
|
||||
ManagedLayer::Tiling if self.tiling_enabled => {
|
||||
// should still be mapped in tiling
|
||||
self.floating_layer.unmap(&elem);
|
||||
elem.output_enter(&self.output, elem.bbox());
|
||||
elem.set_maximized(false);
|
||||
elem.set_geometry(state.original_geometry.to_global(&self.output));
|
||||
elem.configure();
|
||||
self.tiling_layer.recalculate();
|
||||
self.tiling_layer
|
||||
.element_geometry(&elem)
|
||||
.map(|geo| geo.size.as_logical())
|
||||
}
|
||||
ManagedLayer::Sticky => unreachable!(),
|
||||
_ => {
|
||||
elem.set_maximized(false);
|
||||
self.floating_layer.map_internal(
|
||||
elem.clone(),
|
||||
Some(state.original_geometry.loc),
|
||||
Some(state.original_geometry.size.as_logical()),
|
||||
None,
|
||||
);
|
||||
Some(state.original_geometry.size.as_logical())
|
||||
if let Some(minimized) = self
|
||||
.minimized_windows
|
||||
.iter_mut()
|
||||
.find(|m| &m.window == elem)
|
||||
{
|
||||
minimized.unmaximize(state.original_geometry);
|
||||
Some(state.original_geometry.size.as_logical())
|
||||
} else {
|
||||
match state.original_layer {
|
||||
ManagedLayer::Tiling if self.tiling_enabled => {
|
||||
// should still be mapped in tiling
|
||||
self.floating_layer.unmap(&elem);
|
||||
elem.output_enter(&self.output, elem.bbox());
|
||||
elem.set_maximized(false);
|
||||
elem.set_geometry(state.original_geometry.to_global(&self.output));
|
||||
elem.configure();
|
||||
self.tiling_layer.recalculate();
|
||||
self.tiling_layer
|
||||
.element_geometry(&elem)
|
||||
.map(|geo| geo.size.as_logical())
|
||||
}
|
||||
ManagedLayer::Sticky => unreachable!(),
|
||||
_ => {
|
||||
elem.set_maximized(false);
|
||||
self.floating_layer.map_internal(
|
||||
elem.clone(),
|
||||
Some(state.original_geometry.loc),
|
||||
Some(state.original_geometry.size.as_logical()),
|
||||
None,
|
||||
);
|
||||
Some(state.original_geometry.size.as_logical())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -493,10 +575,156 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn minimize(
|
||||
&mut self,
|
||||
elem: &CosmicMapped,
|
||||
to: Rectangle<i32, Local>,
|
||||
) -> Option<MinimizedWindow> {
|
||||
let fullscreen = if self
|
||||
.get_fullscreen()
|
||||
.is_some_and(|s| elem.windows().any(|(w, _)| *s == w))
|
||||
{
|
||||
let fullscreen_state = self.fullscreen.clone().unwrap();
|
||||
{
|
||||
let f = self.fullscreen.as_mut().unwrap();
|
||||
f.ended_at = Some(
|
||||
Instant::now()
|
||||
- (FULLSCREEN_ANIMATION_DURATION
|
||||
- f.start_at
|
||||
.take()
|
||||
.map(|earlier| {
|
||||
Instant::now()
|
||||
.duration_since(earlier)
|
||||
.min(FULLSCREEN_ANIMATION_DURATION)
|
||||
})
|
||||
.unwrap_or(FULLSCREEN_ANIMATION_DURATION)),
|
||||
);
|
||||
}
|
||||
Some(fullscreen_state)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if self.is_tiled(elem) {
|
||||
let was_maximized = self.floating_layer.unmap(&elem);
|
||||
let tiling_state = self.tiling_layer.unmap_minimize(elem, to);
|
||||
Some(MinimizedWindow {
|
||||
window: elem.clone(),
|
||||
previous_state: MinimizedState::Tiling {
|
||||
tiling_state,
|
||||
was_maximized,
|
||||
},
|
||||
output_geo: self.output.geometry(),
|
||||
fullscreen,
|
||||
})
|
||||
} else {
|
||||
self.floating_layer
|
||||
.unmap_minimize(elem, to)
|
||||
.map(|(window, position)| MinimizedWindow {
|
||||
window,
|
||||
previous_state: MinimizedState::Floating { position },
|
||||
output_geo: self.output.geometry(),
|
||||
fullscreen,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unminimize(
|
||||
&mut self,
|
||||
window: MinimizedWindow,
|
||||
from: Rectangle<i32, Local>,
|
||||
seat: &Seat<State>,
|
||||
) -> Option<(CosmicMapped, ManagedLayer, WorkspaceHandle)> {
|
||||
match window.previous_state {
|
||||
MinimizedState::Floating { mut position } => {
|
||||
let current_output_size = self.output.geometry().size.as_logical();
|
||||
if current_output_size != window.output_geo.size.as_logical() {
|
||||
position = Point::from((
|
||||
(position.x as f64 / window.output_geo.size.w as f64
|
||||
* current_output_size.w as f64)
|
||||
.floor() as i32,
|
||||
(position.y as f64 / window.output_geo.size.h as f64
|
||||
* current_output_size.h as f64)
|
||||
.floor() as i32,
|
||||
))
|
||||
};
|
||||
self.floating_layer
|
||||
.remap_minimized(window.window, from, position);
|
||||
}
|
||||
MinimizedState::Sticky { .. } => unreachable!(),
|
||||
MinimizedState::Tiling {
|
||||
tiling_state,
|
||||
was_maximized,
|
||||
} => {
|
||||
if self.tiling_enabled {
|
||||
let focus_stack = self.focus_stack.get(seat);
|
||||
self.tiling_layer.remap_minimized(
|
||||
window.window.clone(),
|
||||
from,
|
||||
tiling_state,
|
||||
Some(focus_stack.iter()),
|
||||
);
|
||||
if was_maximized {
|
||||
let previous_geometry =
|
||||
self.tiling_layer.element_geometry(&window.window).unwrap();
|
||||
self.floating_layer
|
||||
.map_maximized(window.window, previous_geometry);
|
||||
}
|
||||
} else {
|
||||
if was_maximized {
|
||||
self.floating_layer.map_maximized(window.window, from);
|
||||
} else {
|
||||
self.floating_layer.map(window.window.clone(), None);
|
||||
// get the right animation
|
||||
let geometry = self
|
||||
.floating_layer
|
||||
.element_geometry(&window.window)
|
||||
.unwrap();
|
||||
self.floating_layer.remap_minimized(
|
||||
window.window.clone(),
|
||||
from,
|
||||
geometry.loc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut fullscreen) = window.fullscreen {
|
||||
let old_fullscreen = self.remove_fullscreen();
|
||||
fullscreen.start_at = Some(Instant::now());
|
||||
let geo = self.output.geometry();
|
||||
if geo != window.output_geo {
|
||||
fullscreen.animation_signal = if let Some(surface) = fullscreen.surface.wl_surface()
|
||||
{
|
||||
let signal = Arc::new(AtomicBool::new(false));
|
||||
add_blocker(
|
||||
&surface,
|
||||
FullscreenBlocker {
|
||||
signal: signal.clone(),
|
||||
},
|
||||
);
|
||||
Some(signal)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
fullscreen.surface.set_geometry(geo);
|
||||
fullscreen.surface.send_configure();
|
||||
}
|
||||
|
||||
self.fullscreen = Some(fullscreen);
|
||||
old_fullscreen
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fullscreen_request(
|
||||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
previously: Option<(ManagedLayer, WorkspaceHandle)>,
|
||||
from: Rectangle<i32, Local>,
|
||||
seat: &Seat<State>,
|
||||
) {
|
||||
if self
|
||||
.fullscreen
|
||||
|
|
@ -507,6 +735,15 @@ impl Workspace {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(pos) = self
|
||||
.minimized_windows
|
||||
.iter()
|
||||
.position(|m| m.window.windows().any(|(w, _)| &w == window))
|
||||
{
|
||||
let minimized = self.minimized_windows.remove(pos);
|
||||
let _ = self.unminimize(minimized, from, seat);
|
||||
}
|
||||
|
||||
window.set_fullscreen(true);
|
||||
let geo = self.output.geometry();
|
||||
let original_geometry = window.geometry().as_global();
|
||||
|
|
@ -540,7 +777,13 @@ impl Workspace {
|
|||
&mut self,
|
||||
window: &CosmicSurface,
|
||||
) -> Option<(ManagedLayer, WorkspaceHandle)> {
|
||||
if let Some(f) = self
|
||||
if let Some(minimized) = self
|
||||
.minimized_windows
|
||||
.iter_mut()
|
||||
.find(|m| m.fullscreen.as_ref().is_some_and(|f| &f.surface == window))
|
||||
{
|
||||
minimized.unfullscreen()
|
||||
} else if let Some(f) = self
|
||||
.fullscreen
|
||||
.as_mut()
|
||||
.filter(|f| &f.surface == window && f.ended_at.is_none())
|
||||
|
|
@ -711,24 +954,41 @@ impl Workspace {
|
|||
self.floating_layer.space.outputs()
|
||||
}
|
||||
|
||||
pub fn windows(&self) -> impl Iterator<Item = CosmicSurface> + '_ {
|
||||
self.floating_layer
|
||||
.windows()
|
||||
.chain(self.tiling_layer.windows().map(|(w, _)| w))
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.floating_layer.mapped().next().is_none()
|
||||
&& self.tiling_layer.mapped().next().is_none()
|
||||
&& self.minimized_windows.is_empty()
|
||||
&& self.pending_tokens.is_empty()
|
||||
}
|
||||
|
||||
pub fn is_fullscreen(&self, mapped: &CosmicMapped) -> bool {
|
||||
self.fullscreen
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.surface == mapped.active_window())
|
||||
|| self
|
||||
.minimized_windows
|
||||
.iter()
|
||||
.any(|m| &m.window == mapped && m.fullscreen.is_some())
|
||||
}
|
||||
|
||||
pub fn is_floating(&self, mapped: &CosmicMapped) -> bool {
|
||||
!self.is_fullscreen(mapped) && self.floating_layer.mapped().any(|m| m == mapped)
|
||||
!self.is_fullscreen(mapped)
|
||||
&& (self.floating_layer.mapped().any(|m| m == mapped)
|
||||
|| self.minimized_windows.iter().any(|m| {
|
||||
&m.window == mapped
|
||||
&& matches!(
|
||||
m.previous_state,
|
||||
MinimizedState::Floating { .. } | MinimizedState::Sticky { .. }
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn is_tiled(&self, mapped: &CosmicMapped) -> bool {
|
||||
!self.is_fullscreen(mapped) && self.tiling_layer.mapped().any(|(m, _)| m == mapped)
|
||||
!self.is_fullscreen(mapped)
|
||||
&& (self.tiling_layer.mapped().any(|(m, _)| m == mapped)
|
||||
|| self.minimized_windows.iter().any(|m| {
|
||||
&m.window == mapped && matches!(m.previous_state, MinimizedState::Tiling { .. })
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option<NodeDesc> {
|
||||
|
|
|
|||
|
|
@ -31,19 +31,19 @@ impl Window for CosmicSurface {
|
|||
}
|
||||
|
||||
fn is_activated(&self) -> bool {
|
||||
CosmicSurface::is_activated(self, false)
|
||||
!self.is_minimized() && CosmicSurface::is_activated(self, false)
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
CosmicSurface::is_maximized(self, false)
|
||||
!self.is_minimized() && CosmicSurface::is_maximized(self, false)
|
||||
}
|
||||
|
||||
fn is_fullscreen(&self) -> bool {
|
||||
CosmicSurface::is_fullscreen(self, false)
|
||||
!self.is_minimized() && CosmicSurface::is_fullscreen(self, false)
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> bool {
|
||||
false // TODO
|
||||
CosmicSurface::is_minimized(self)
|
||||
}
|
||||
|
||||
fn user_data(&self) -> &UserDataMap {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1;
|
||||
use smithay::{input::Seat, output::Output, reexports::wayland_server::DisplayHandle};
|
||||
use smithay::{
|
||||
desktop::{layer_map_for_output, WindowSurfaceType},
|
||||
input::Seat,
|
||||
output::Output,
|
||||
reexports::wayland_server::DisplayHandle,
|
||||
utils::{Point, Rectangle, Size},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
shell::{CosmicSurface, Shell},
|
||||
|
|
@ -22,10 +28,11 @@ impl ToplevelManagementHandler for State {
|
|||
|
||||
fn activate(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
dh: &DisplayHandle,
|
||||
window: &<Self as ToplevelInfoHandler>::Window,
|
||||
seat: Option<Seat<Self>>,
|
||||
) {
|
||||
self.unminimize(dh, window);
|
||||
for output in self
|
||||
.common
|
||||
.shell
|
||||
|
|
@ -40,7 +47,11 @@ impl ToplevelManagementHandler for State {
|
|||
.workspaces
|
||||
.spaces_for_output(output)
|
||||
.enumerate()
|
||||
.find(|(_, w)| w.windows().any(|w| &w == window));
|
||||
.find(|(_, w)| {
|
||||
w.mapped()
|
||||
.flat_map(|m| m.windows().map(|(s, _)| s))
|
||||
.any(|w| &w == window)
|
||||
});
|
||||
if let Some((idx, workspace)) = maybe {
|
||||
let seat = seat.unwrap_or(self.common.last_active_seat().clone());
|
||||
let mapped = workspace
|
||||
|
|
@ -77,12 +88,11 @@ impl ToplevelManagementHandler for State {
|
|||
return;
|
||||
};
|
||||
|
||||
let from_workspace = self
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.spaces()
|
||||
.find(|w| w.windows().any(|w| &w == window));
|
||||
let from_workspace = self.common.shell.workspaces.spaces().find(|w| {
|
||||
w.mapped()
|
||||
.flat_map(|m| m.windows().map(|(s, _)| s))
|
||||
.any(|w| &w == window)
|
||||
});
|
||||
if let Some(from_workspace) = from_workspace {
|
||||
let mapped = from_workspace
|
||||
.mapped()
|
||||
|
|
@ -101,12 +111,33 @@ impl ToplevelManagementHandler for State {
|
|||
window: &<Self as ToplevelInfoHandler>::Window,
|
||||
output: Option<Output>,
|
||||
) {
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() {
|
||||
if let Some(output) = output {
|
||||
let from = self
|
||||
.common
|
||||
.shell
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&output, window);
|
||||
let workspace = self.common.shell.workspaces.active_mut(&output);
|
||||
workspace.fullscreen_request(window, None);
|
||||
} else if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
workspace.fullscreen_request(window, None);
|
||||
workspace.fullscreen_request(window, None, from, &seat);
|
||||
} else if let Some((output, handle)) = self
|
||||
.common
|
||||
.shell
|
||||
.space_for(&mapped)
|
||||
.map(|workspace| (workspace.output.clone(), workspace.handle.clone()))
|
||||
{
|
||||
let from = self
|
||||
.common
|
||||
.shell
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&output, window);
|
||||
self.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle_mut(&handle)
|
||||
.unwrap()
|
||||
.fullscreen_request(window, None, from, &seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,15 +149,32 @@ impl ToplevelManagementHandler for State {
|
|||
) {
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
let previous = workspace.unfullscreen_request(window);
|
||||
assert!(previous.is_none());
|
||||
if let Some((layer, previous_workspace)) = workspace.unfullscreen_request(window) {
|
||||
let old_handle = workspace.handle.clone();
|
||||
let new_workspace_handle = self
|
||||
.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle(&previous_workspace)
|
||||
.is_some()
|
||||
.then_some(previous_workspace)
|
||||
.unwrap_or(old_handle); // if the workspace doesn't exist anymore, we can still remap on the right layer
|
||||
|
||||
self.common.shell.remap_unfullscreened_window(
|
||||
mapped,
|
||||
&old_handle,
|
||||
&new_workspace_handle,
|
||||
layer,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maximize(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() {
|
||||
self.common.shell.maximize_request(&mapped);
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
self.common.shell.maximize_request(&mapped, &seat);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +192,8 @@ impl ToplevelManagementHandler for State {
|
|||
|
||||
fn unminimize(&mut self, _dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(window).cloned() {
|
||||
self.common.shell.unminimize_request(&mapped);
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
self.common.shell.unminimize_request(&mapped, &seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -155,4 +204,43 @@ impl ManagementWindow for CosmicSurface {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ToplevelManagementExt {
|
||||
fn minimize_rectangle(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
window: &CosmicSurface,
|
||||
) -> Rectangle<i32, Local>;
|
||||
}
|
||||
|
||||
impl ToplevelManagementExt for ToplevelManagementState {
|
||||
fn minimize_rectangle(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
window: &CosmicSurface,
|
||||
) -> Rectangle<i32, Local> {
|
||||
self.rectangle_for(window)
|
||||
.find_map(|(surface, relative)| {
|
||||
let map = layer_map_for_output(output);
|
||||
let layer = map.layer_for_surface(&surface, WindowSurfaceType::ALL);
|
||||
layer.and_then(|s| map.layer_geometry(s)).map(|local| {
|
||||
Rectangle::from_loc_and_size(
|
||||
Point::from((local.loc.x + relative.loc.x, local.loc.y + relative.loc.y)),
|
||||
relative.size,
|
||||
)
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let output_size = output.geometry().size;
|
||||
Rectangle::from_loc_and_size(
|
||||
Point::from((
|
||||
(output_size.w / 2) - 100,
|
||||
output_size.h - (output_size.h / 3) - 50,
|
||||
)),
|
||||
Size::from((200, 100)),
|
||||
)
|
||||
})
|
||||
.as_local()
|
||||
}
|
||||
}
|
||||
|
||||
delegate_toplevel_management!(State);
|
||||
|
|
|
|||
|
|
@ -145,14 +145,16 @@ impl XdgActivationHandler for State {
|
|||
}
|
||||
}
|
||||
|
||||
let target = element.into();
|
||||
if workspace == ¤t_workspace.handle && in_current_workspace {
|
||||
let target = element.into();
|
||||
Shell::set_focus(self, Some(&target), &seat, None);
|
||||
} else if let Some((w, _)) = target
|
||||
.toplevel()
|
||||
.and_then(|t| self.common.shell.workspace_for_surface(&t))
|
||||
} else if let Some(w) = self
|
||||
.common
|
||||
.shell
|
||||
.space_for(&element)
|
||||
.map(|w| w.handle.clone())
|
||||
{
|
||||
Shell::append_focus_stack(self, Some(&target), &seat);
|
||||
Shell::append_focus_stack(self, &element, &seat);
|
||||
self.common.shell.set_urgent(&w);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@ use smithay::{
|
|||
use std::cell::Cell;
|
||||
use tracing::warn;
|
||||
|
||||
use super::{compositor::client_compositor_state, screencopy::PendingScreencopyBuffers};
|
||||
use super::{
|
||||
compositor::client_compositor_state, screencopy::PendingScreencopyBuffers,
|
||||
toplevel_management::ToplevelManagementExt,
|
||||
};
|
||||
|
||||
pub mod popup;
|
||||
|
||||
|
|
@ -180,7 +183,8 @@ impl XdgShellHandler for State {
|
|||
.element_for_wl_surface(surface.wl_surface())
|
||||
.cloned()
|
||||
{
|
||||
self.common.shell.maximize_request(&mapped)
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
self.common.shell.maximize_request(&mapped, &seat)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,10 +200,8 @@ impl XdgShellHandler for State {
|
|||
}
|
||||
|
||||
fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {
|
||||
let active_output = {
|
||||
let seat = self.common.last_active_seat();
|
||||
seat.active_output()
|
||||
};
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
let active_output = seat.active_output();
|
||||
let output = output
|
||||
.as_ref()
|
||||
.and_then(Output::from_resource)
|
||||
|
|
@ -211,6 +213,12 @@ impl XdgShellHandler for State {
|
|||
.element_for_wl_surface(surface.wl_surface())
|
||||
.cloned()
|
||||
{
|
||||
let from = self
|
||||
.common
|
||||
.shell
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&output, &mapped.active_window());
|
||||
|
||||
if let Some(set) = self
|
||||
.common
|
||||
.shell
|
||||
|
|
@ -254,9 +262,12 @@ impl XdgShellHandler for State {
|
|||
|
||||
let workspace = self.common.shell.active_space_mut(&output);
|
||||
workspace.floating_layer.map(mapped.clone(), None);
|
||||
|
||||
workspace.fullscreen_request(
|
||||
&mapped.active_window(),
|
||||
Some((ManagedLayer::Sticky, workspace_handle)),
|
||||
from,
|
||||
&seat,
|
||||
);
|
||||
} else if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
if workspace.output != output {
|
||||
|
|
@ -303,13 +314,19 @@ impl XdgShellHandler for State {
|
|||
|
||||
let workspace = self.common.shell.active_space_mut(&output);
|
||||
workspace.floating_layer.map(mapped.clone(), None);
|
||||
workspace.fullscreen_request(&mapped.active_window(), Some((layer, handle)));
|
||||
|
||||
workspace.fullscreen_request(
|
||||
&mapped.active_window(),
|
||||
Some((layer, handle)),
|
||||
from,
|
||||
&seat,
|
||||
);
|
||||
} else {
|
||||
let (window, _) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface()))
|
||||
.unwrap();
|
||||
workspace.fullscreen_request(&window, None)
|
||||
workspace.fullscreen_request(&window, None, from, &seat)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ impl Shell {
|
|||
if let Some(elem) = self.element_for_wl_surface(&parent) {
|
||||
let (mut element_geo, output, is_tiled) =
|
||||
if let Some(workspace) = self.space_for(elem) {
|
||||
let Some(elem_geo) = workspace.element_geometry(elem) else {
|
||||
return;
|
||||
};
|
||||
(
|
||||
workspace
|
||||
.element_geometry(elem)
|
||||
.unwrap()
|
||||
.to_global(workspace.output()),
|
||||
elem_geo.to_global(workspace.output()),
|
||||
workspace.output.clone(),
|
||||
workspace.is_tiled(elem),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use smithay::{
|
||||
output::Output,
|
||||
reexports::wayland_server::{
|
||||
backend::{ClientId, GlobalId},
|
||||
protocol::wl_surface::WlSurface,
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
|
||||
},
|
||||
utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle},
|
||||
};
|
||||
|
|
@ -53,7 +53,7 @@ pub(super) struct ToplevelStateInner {
|
|||
instances: Vec<ZcosmicToplevelHandleV1>,
|
||||
outputs: Vec<Output>,
|
||||
workspaces: Vec<WorkspaceHandle>,
|
||||
pub(super) rectangles: HashMap<ClientId, (WlSurface, Rectangle<i32, Logical>)>,
|
||||
pub(super) rectangles: Vec<(Weak<WlSurface>, Rectangle<i32, Logical>)>,
|
||||
}
|
||||
pub(super) type ToplevelState = Mutex<ToplevelStateInner>;
|
||||
|
||||
|
|
@ -255,7 +255,9 @@ where
|
|||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
state.rectangles.retain(|_, (surface, _)| surface.alive());
|
||||
state
|
||||
.rectangles
|
||||
.retain(|(surface, _)| surface.upgrade().is_ok());
|
||||
if window.alive() {
|
||||
std::mem::drop(state);
|
||||
for instance in &self.instances {
|
||||
|
|
|
|||
|
|
@ -105,12 +105,24 @@ impl ToplevelManagementState {
|
|||
pub fn rectangle_for(
|
||||
&mut self,
|
||||
window: &impl ManagementWindow,
|
||||
client: &ClientId,
|
||||
) -> Option<(WlSurface, Rectangle<i32, Logical>)> {
|
||||
) -> impl Iterator<Item = (WlSurface, Rectangle<i32, Logical>)> {
|
||||
if let Some(state) = window.user_data().get::<ToplevelState>() {
|
||||
state.lock().unwrap().rectangles.get(client).cloned()
|
||||
let mut state = state.lock().unwrap();
|
||||
state
|
||||
.rectangles
|
||||
.retain(|(surface, _)| surface.upgrade().is_ok());
|
||||
Some(
|
||||
state
|
||||
.rectangles
|
||||
.iter()
|
||||
.map(|(surface, rect)| (surface.upgrade().unwrap(), *rect))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter(),
|
||||
)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
} else {
|
||||
None
|
||||
None.into_iter().flatten()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,18 +229,15 @@ where
|
|||
window_from_handle::<<D as ToplevelInfoHandler>::Window>(toplevel).unwrap();
|
||||
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
|
||||
let mut toplevel_state = toplevel_state.lock().unwrap();
|
||||
if let Some(client) = surface.client() {
|
||||
if width == 0 && height == 0 {
|
||||
toplevel_state.rectangles.remove(&client.id());
|
||||
} else {
|
||||
toplevel_state.rectangles.insert(
|
||||
client.id(),
|
||||
(
|
||||
surface,
|
||||
Rectangle::from_loc_and_size((x, y), (width, height)),
|
||||
),
|
||||
);
|
||||
}
|
||||
if width == 0 && height == 0 {
|
||||
toplevel_state
|
||||
.rectangles
|
||||
.retain(|(s, _)| s.id() != surface.id());
|
||||
} else {
|
||||
toplevel_state.rectangles.push((
|
||||
surface.downgrade(),
|
||||
Rectangle::from_loc_and_size((x, y), (width, height)),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -256,7 +265,13 @@ where
|
|||
{
|
||||
for toplevel in state.toplevel_info_state_mut().toplevels.iter() {
|
||||
if let Some(toplevel_state) = toplevel.user_data().get::<ToplevelState>() {
|
||||
toplevel_state.lock().unwrap().rectangles.remove(&client);
|
||||
toplevel_state.lock().unwrap().rectangles.retain(|(s, _)| {
|
||||
s.upgrade()
|
||||
.ok()
|
||||
.and_then(|s| s.client())
|
||||
.map(|c| c.id() != client)
|
||||
.unwrap_or(false)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ use crate::{
|
|||
state::State,
|
||||
utils::prelude::*,
|
||||
wayland::{
|
||||
handlers::{screencopy::PendingScreencopyBuffers, xdg_activation::ActivationContext},
|
||||
handlers::{
|
||||
screencopy::PendingScreencopyBuffers, toplevel_management::ToplevelManagementExt,
|
||||
xdg_activation::ActivationContext,
|
||||
},
|
||||
protocols::screencopy::SessionType,
|
||||
},
|
||||
};
|
||||
|
|
@ -416,7 +419,8 @@ impl XwmHandler for State {
|
|||
|
||||
fn maximize_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||
if let Some(mapped) = self.common.shell.element_for_x11_surface(&window).cloned() {
|
||||
self.common.shell.maximize_request(&mapped);
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
self.common.shell.maximize_request(&mapped, &seat);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -427,27 +431,42 @@ impl XwmHandler for State {
|
|||
}
|
||||
|
||||
fn minimize_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||
let surface = CosmicSurface::X11(window);
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() {
|
||||
if let Some(mapped) = self.common.shell.element_for_x11_surface(&window).cloned() {
|
||||
self.common.shell.minimize_request(&mapped);
|
||||
}
|
||||
}
|
||||
|
||||
fn unminimize_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||
let surface = CosmicSurface::X11(window);
|
||||
if let Some(mapped) = self.common.shell.element_for_surface(&surface).cloned() {
|
||||
self.common.shell.unminimize_request(&mapped);
|
||||
if let Some(mapped) = self.common.shell.element_for_x11_surface(&window).cloned() {
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
self.common.shell.unminimize_request(&mapped, &seat);
|
||||
}
|
||||
}
|
||||
|
||||
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||
let seat = self.common.last_active_seat().clone();
|
||||
if let Some(mapped) = self.common.shell.element_for_x11_surface(&window).cloned() {
|
||||
if let Some(workspace) = self.common.shell.space_for_mut(&mapped) {
|
||||
if let Some((output, handle)) = self
|
||||
.common
|
||||
.shell
|
||||
.space_for(&mapped)
|
||||
.map(|workspace| (workspace.output.clone(), workspace.handle.clone()))
|
||||
{
|
||||
if let Some((surface, _)) = mapped
|
||||
.windows()
|
||||
.find(|(w, _)| w.x11_surface() == Some(&window))
|
||||
{
|
||||
workspace.fullscreen_request(&surface, None)
|
||||
let from = self
|
||||
.common
|
||||
.shell
|
||||
.toplevel_management_state
|
||||
.minimize_rectangle(&output, &surface);
|
||||
self.common
|
||||
.shell
|
||||
.workspaces
|
||||
.space_for_handle_mut(&handle)
|
||||
.unwrap()
|
||||
.fullscreen_request(&surface, None, from, &seat);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue