shell/tiling: Implement window swap mode

This commit is contained in:
Victoria Brekenfeld 2023-08-11 18:15:22 +02:00
parent ac4bf01315
commit 1251b7e9f7
18 changed files with 1305 additions and 197 deletions

View file

@ -59,6 +59,7 @@
(modifiers: [Super], key: "s"): ToggleStacking, (modifiers: [Super], key: "s"): ToggleStacking,
(modifiers: [Super], key: "y"): ToggleTiling, (modifiers: [Super], key: "y"): ToggleTiling,
(modifiers: [Super], key: "g"): ToggleWindowFloating, (modifiers: [Super], key: "g"): ToggleWindowFloating,
(modifiers: [Super], key: "x"): SwapWindow,
(modifiers: [Super], key: "r"): Resizing(Outwards), (modifiers: [Super], key: "r"): Resizing(Outwards),
(modifiers: [Super, Shift], key: "r"): Resizing(Inwards), (modifiers: [Super, Shift], key: "r"): Resizing(Inwards),

View file

@ -1,4 +1,5 @@
grow-window = Vergrößern grow-window = Vergrößern
shrink-window = Verkleinern shrink-window = Verkleinern
unknown-keybinding = <nicht zugewiesen> swap-windows = Fenster tauschen
stack-windows = Fenster stapeln stack-windows = Fenster stapeln
unknown-keybinding = <nicht zugewiesen>

View file

@ -1,4 +1,5 @@
grow-window = Grow grow-window = Grow
shrink-window = Shrink shrink-window = Shrink
unknown-keybinding = <unset> swap-windows = Swap Windows
stack-windows = Stack Windows stack-windows = Stack Windows
unknown-keybinding = <unset>

View file

@ -88,18 +88,30 @@ pub static RECTANGLE_SHADER: &str = include_str!("./shaders/rounded_rectangle.fr
pub struct IndicatorShader(pub GlesPixelProgram); pub struct IndicatorShader(pub GlesPixelProgram);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum Usage {
OverviewBackdrop,
Overlay,
MoveGrabIndicator,
FocusIndicator,
PotentialGroupIndicator,
}
#[derive(Clone)] #[derive(Clone)]
pub enum Key { pub enum Key {
Static(Id), Static(Id),
Group(Weak<()>), Group(Weak<()>),
Window(CosmicMapped), Window(Usage, CosmicMapped),
} }
impl std::hash::Hash for Key { impl std::hash::Hash for Key {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self { match self {
Key::Static(id) => id.hash(state), Key::Static(id) => id.hash(state),
Key::Group(arc) => (arc.as_ptr() as usize).hash(state), Key::Group(arc) => (arc.as_ptr() as usize).hash(state),
Key::Window(window) => window.hash(state), Key::Window(usage, window) => {
usage.hash(state);
window.hash(state);
}
} }
} }
} }
@ -108,17 +120,12 @@ impl PartialEq for Key {
match (self, other) { match (self, other) {
(Key::Static(s1), Key::Static(s2)) => s1 == s2, (Key::Static(s1), Key::Static(s2)) => s1 == s2,
(Key::Group(g1), Key::Group(g2)) => Weak::ptr_eq(g1, g2), (Key::Group(g1), Key::Group(g2)) => Weak::ptr_eq(g1, g2),
(Key::Window(w1), Key::Window(w2)) => w1 == w2, (Key::Window(u1, w1), Key::Window(u2, w2)) => u1 == u2 && w1 == w2,
_ => false, _ => false,
} }
} }
} }
impl Eq for Key {} impl Eq for Key {}
impl From<CosmicMapped> for Key {
fn from(window: CosmicMapped) -> Self {
Key::Window(window)
}
}
impl From<WindowGroup> for Key { impl From<WindowGroup> for Key {
fn from(group: WindowGroup) -> Self { fn from(group: WindowGroup) -> Self {
Key::Group(group.alive.clone()) Key::Group(group.alive.clone())
@ -202,7 +209,7 @@ impl IndicatorShader {
cache.retain(|k, _| match k { cache.retain(|k, _| match k {
Key::Static(_) => true, Key::Static(_) => true,
Key::Group(w) => w.upgrade().is_some(), Key::Group(w) => w.upgrade().is_some(),
Key::Window(w) => w.alive(), Key::Window(_, w) => w.alive(),
}); });
let key = key.into(); let key = key.into();
@ -283,7 +290,7 @@ impl BackdropShader {
cache.retain(|k, _| match k { cache.retain(|k, _| match k {
Key::Static(_) => true, Key::Static(_) => true,
Key::Group(a) => a.upgrade().is_some(), Key::Group(a) => a.upgrade().is_some(),
Key::Window(w) => w.alive(), Key::Window(_, w) => w.alive(),
}); });
let key = key.into(); let key = key.into();

View file

@ -157,9 +157,9 @@ pub enum Action {
Orientation(crate::shell::layout::Orientation), Orientation(crate::shell::layout::Orientation),
ToggleStacking, ToggleStacking,
ToggleTiling, ToggleTiling,
ToggleWindowFloating, ToggleWindowFloating,
SwapWindow,
Resizing(ResizeDirection), Resizing(ResizeDirection),
#[serde(skip)] #[serde(skip)]

View file

@ -6,7 +6,7 @@ use crate::{
shell::{ shell::{
focus::{target::PointerFocusTarget, FocusDirection}, focus::{target::PointerFocusTarget, FocusDirection},
grabs::{ResizeEdge, SeatMoveGrabState}, grabs::{ResizeEdge, SeatMoveGrabState},
layout::tiling::{Direction, FocusResult, MoveResult}, layout::tiling::{Direction, FocusResult, MoveResult, SwapWindowGrab, TilingLayout},
OverviewMode, ResizeDirection, ResizeMode, Trigger, Workspace, OverviewMode, ResizeDirection, ResizeMode, Trigger, Workspace,
}, },
state::Common, state::Common,
@ -250,9 +250,9 @@ impl State {
let serial = SERIAL_COUNTER.next_serial(); let serial = SERIAL_COUNTER.next_serial();
let time = Event::time_msec(&event); let time = Event::time_msec(&event);
if let Some((action, pattern)) = seat let keyboard = seat.get_keyboard().unwrap();
.get_keyboard() let current_focus = keyboard.current_focus();
.unwrap() if let Some((action, pattern)) = keyboard
.input( .input(
self, self,
keycode, keycode,
@ -260,16 +260,58 @@ impl State {
serial, serial,
time, time,
|data, modifiers, handle| { |data, modifiers, handle| {
// Leave overview mode, if any modifier was released // Leave move overview mode, if any modifier was released
if let OverviewMode::Started(Trigger::Keyboard(action_modifiers), _) = if let OverviewMode::Started(Trigger::KeyboardMove(action_modifiers), _) =
data.common.shell.overview_mode() data.common.shell.overview_mode().0
{ {
if (action_modifiers.ctrl && !modifiers.ctrl) if (action_modifiers.ctrl && !modifiers.ctrl)
|| (action_modifiers.alt && !modifiers.alt) || (action_modifiers.alt && !modifiers.alt)
|| (action_modifiers.logo && !modifiers.logo) || (action_modifiers.logo && !modifiers.logo)
|| (action_modifiers.shift && !modifiers.shift) || (action_modifiers.shift && !modifiers.shift)
{ {
data.common.shell.set_overview_mode(None); data.common.shell.set_overview_mode(None, data.common.event_loop_handle.clone());
}
}
// Leave swap overview mode, if any key was released
if let OverviewMode::Started(Trigger::KeyboardSwap(action_pattern, old_descriptor), _) =
data.common.shell.overview_mode().0
{
if (action_pattern.modifiers.ctrl && !modifiers.ctrl)
|| (action_pattern.modifiers.alt && !modifiers.alt)
|| (action_pattern.modifiers.logo && !modifiers.logo)
|| (action_pattern.modifiers.shift && !modifiers.shift)
|| (handle.raw_syms().contains(&action_pattern.key) && state == KeyState::Released)
{
data.common.shell.set_overview_mode(None, data.common.event_loop_handle.clone());
if let Some(focus) = current_focus {
if let Some(new_descriptor) = data.common.shell.workspaces.active(&current_output).1.node_desc(focus) {
let mut spaces = data.common.shell.workspaces.spaces_mut();
if old_descriptor.handle != new_descriptor.handle {
let (mut old_w, mut other_w) = spaces.partition::<Vec<_>, _>(|w| w.handle == old_descriptor.handle);
if let Some(old_tiling_layer) = old_w.get_mut(0).map(|w| &mut w.tiling_layer) {
if let Some(new_tiling_layer) = other_w.iter_mut().find(|w| w.handle == new_descriptor.handle).map(|w| &mut w.tiling_layer) {
if let Some(focus) = TilingLayout::swap_trees(old_tiling_layer, Some(new_tiling_layer), &old_descriptor, &new_descriptor) {
let seat = seat.clone();
data.common.event_loop_handle.insert_idle(move |data| {
Common::set_focus(&mut data.state, Some(&focus), &seat, None);
});
}
}
}
} else {
if let Some(tiling_layer) = spaces.find(|w| w.handle == new_descriptor.handle).map(|w| &mut w.tiling_layer) {
if let Some(focus) = TilingLayout::swap_trees(tiling_layer, None, &old_descriptor, &new_descriptor) {
std::mem::drop(spaces);
let seat = seat.clone();
data.common.event_loop_handle.insert_idle(move |data| {
Common::set_focus(&mut data.state, Some(&focus), &seat, None);
});
}
}
}
}
}
} }
} }
@ -481,7 +523,7 @@ impl State {
&output, &output,
output_geometry, output_geometry,
&self.common.shell.override_redirect_windows, &self.common.shell.override_redirect_windows,
overview, overview.0,
workspace, workspace,
); );
@ -551,7 +593,7 @@ impl State {
&output, &output,
geometry, geometry,
&self.common.shell.override_redirect_windows, &self.common.shell.override_redirect_windows,
overview, overview.0,
workspace, workspace,
); );
@ -670,7 +712,7 @@ impl State {
}; };
if !done { if !done {
if let Some((target, _)) = if let Some((target, _)) =
workspace.element_under(relative_pos, overview) workspace.element_under(relative_pos, overview.0)
{ {
under = Some(target); under = Some(target);
} else { } else {
@ -705,10 +747,12 @@ impl State {
} }
} else { } else {
if let OverviewMode::Started(Trigger::Pointer(action_button), _) = if let OverviewMode::Started(Trigger::Pointer(action_button), _) =
self.common.shell.overview_mode() self.common.shell.overview_mode().0
{ {
if action_button == button { if action_button == button {
self.common.shell.set_overview_mode(None); self.common
.shell
.set_overview_mode(None, self.common.event_loop_handle.clone());
} }
} }
}; };
@ -899,7 +943,7 @@ impl State {
} }
} }
fn handle_action( pub fn handle_action(
&mut self, &mut self,
action: Action, action: Action,
seat: &Seat<State>, seat: &Seat<State>,
@ -1232,11 +1276,18 @@ impl State {
} }
Action::Focus(focus) => { Action::Focus(focus) => {
let current_output = seat.active_output(); let current_output = seat.active_output();
let overview = self.common.shell.overview_mode().0;
let workspace = self.common.shell.active_space_mut(&current_output); let workspace = self.common.shell.active_space_mut(&current_output);
let focus_stack = workspace.focus_stack.get(seat); let focus_stack = workspace.focus_stack.get(seat);
let mut result = workspace let mut result = workspace.tiling_layer.next_focus(
.tiling_layer focus,
.next_focus(focus, seat, focus_stack.iter()); seat,
focus_stack.iter(),
match overview {
OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _) => Some(desc),
_ => None,
},
);
if workspace.get_fullscreen(&current_output).is_some() { if workspace.get_fullscreen(&current_output).is_some() {
result = FocusResult::None; result = FocusResult::None;
} }
@ -1348,14 +1399,34 @@ impl State {
MoveResult::Done => { MoveResult::Done => {
if let Some(focused_window) = workspace.focus_stack.get(seat).last() { if let Some(focused_window) = workspace.focus_stack.get(seat).last() {
if workspace.is_tiled(focused_window) { if workspace.is_tiled(focused_window) {
self.common self.common.shell.set_overview_mode(
.shell Some(Trigger::KeyboardMove(pattern.modifiers)),
.set_overview_mode(Some(Trigger::Keyboard(pattern.modifiers))); self.common.event_loop_handle.clone(),
);
} }
} }
} }
} }
} }
Action::SwapWindow => {
let current_output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&current_output);
if workspace.get_fullscreen(&current_output).is_some() {
return; // TODO, is this what we want? Maybe disengage fullscreen instead?
}
let keyboard_handle = seat.get_keyboard().unwrap();
if let Some(focus) = keyboard_handle.current_focus() {
if let Some(descriptor) = workspace.node_desc(focus) {
let grab = SwapWindowGrab::new(seat.clone(), descriptor.clone());
keyboard_handle.set_grab(grab, serial);
self.common.shell.set_overview_mode(
Some(Trigger::KeyboardSwap(pattern, descriptor)),
self.common.event_loop_handle.clone(),
);
}
}
}
Action::Maximize => { Action::Maximize => {
let current_output = seat.active_output(); let current_output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&current_output); let workspace = self.common.shell.active_space_mut(&current_output);

View file

@ -62,6 +62,7 @@ pub mod window;
pub use self::window::CosmicWindow; pub use self::window::CosmicWindow;
pub mod resize_indicator; pub mod resize_indicator;
pub mod stack_hover; pub mod stack_hover;
pub mod swap_indicator;
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
use egui::plot::{Corner, Legend, Plot, PlotPoints, Polygon}; use egui::plot::{Corner, Legend, Plot, PlotPoints, Polygon};
@ -72,7 +73,10 @@ use tracing::debug;
use super::{ use super::{
focus::FocusDirection, focus::FocusDirection,
layout::{floating::ResizeState, tiling::Direction}, layout::{
floating::ResizeState,
tiling::{Direction, NodeDesc},
},
}; };
space_elements! { space_elements! {
@ -90,7 +94,7 @@ pub struct CosmicMapped {
last_cursor_position: Arc<Mutex<HashMap<usize, Point<f64, Logical>>>>, last_cursor_position: Arc<Mutex<HashMap<usize, Point<f64, Logical>>>>,
//tiling //tiling
pub(super) tiling_node_id: Arc<Mutex<Option<NodeId>>>, pub tiling_node_id: Arc<Mutex<Option<NodeId>>>,
//floating //floating
pub(super) last_geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>, pub(super) last_geometry: Arc<Mutex<Option<Rectangle<i32, Logical>>>>,
pub(super) resize_state: Arc<Mutex<Option<ResizeState>>>, pub(super) resize_state: Arc<Mutex<Option<ResizeState>>>,
@ -167,14 +171,12 @@ impl CosmicMapped {
CosmicMappedInternal::Stack(stack) => { CosmicMappedInternal::Stack(stack) => {
let win = stack.active(); let win = stack.active();
let location = stack.offset(); let location = stack.offset();
let mut size = win.geometry().size; let size = win.geometry().size;
size -= location.to_size();
Rectangle::from_loc_and_size(location, size) Rectangle::from_loc_and_size(location, size)
} }
CosmicMappedInternal::Window(win) => { CosmicMappedInternal::Window(win) => {
let location = win.offset(); let location = win.offset();
let mut size = win.geometry().size; let size = win.geometry().size;
size -= location.to_size();
Rectangle::from_loc_and_size(location, size) Rectangle::from_loc_and_size(location, size)
} }
_ => unreachable!(), _ => unreachable!(),
@ -248,9 +250,9 @@ impl CosmicMapped {
} }
} }
pub fn handle_focus(&self, direction: FocusDirection) -> bool { pub fn handle_focus(&self, direction: FocusDirection, swap: Option<NodeDesc>) -> bool {
if let CosmicMappedInternal::Stack(stack) = &self.element { if let CosmicMappedInternal::Stack(stack) = &self.element {
stack.handle_focus(direction) stack.handle_focus(direction, swap)
} else { } else {
false false
} }
@ -1100,9 +1102,13 @@ where
RescaleRenderElement<CropRenderElement<self::window::CosmicWindowRenderElement<R>>>, RescaleRenderElement<CropRenderElement<self::window::CosmicWindowRenderElement<R>>>,
>, >,
), ),
TiledOverlay(
RelocateRenderElement<RescaleRenderElement<CropRenderElement<PixelShaderElement>>>,
),
GrabbedStack(RescaleRenderElement<self::stack::CosmicStackRenderElement<R>>), GrabbedStack(RescaleRenderElement<self::stack::CosmicStackRenderElement<R>>),
GrabbedWindow(RescaleRenderElement<self::window::CosmicWindowRenderElement<R>>), GrabbedWindow(RescaleRenderElement<self::window::CosmicWindowRenderElement<R>>),
FocusIndicator(PixelShaderElement), FocusIndicator(PixelShaderElement),
Overlay(PixelShaderElement),
StackHoverIndicator(MemoryRenderBufferRenderElement<R>), StackHoverIndicator(MemoryRenderBufferRenderElement<R>),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
Egui(TextureRenderElement<GlesTexture>), Egui(TextureRenderElement<GlesTexture>),
@ -1119,9 +1125,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.id(), CosmicMappedRenderElement::Window(elem) => elem.id(),
CosmicMappedRenderElement::TiledStack(elem) => elem.id(), CosmicMappedRenderElement::TiledStack(elem) => elem.id(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.id(), CosmicMappedRenderElement::TiledWindow(elem) => elem.id(),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.id(),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.id(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.id(),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.id(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.id(),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.id(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.id(),
CosmicMappedRenderElement::Overlay(elem) => elem.id(),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.id(), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.id(),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.id(), CosmicMappedRenderElement::Egui(elem) => elem.id(),
@ -1134,9 +1142,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.current_commit(), CosmicMappedRenderElement::Window(elem) => elem.current_commit(),
CosmicMappedRenderElement::TiledStack(elem) => elem.current_commit(), CosmicMappedRenderElement::TiledStack(elem) => elem.current_commit(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.current_commit(), CosmicMappedRenderElement::TiledWindow(elem) => elem.current_commit(),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.current_commit(),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.current_commit(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.current_commit(),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.current_commit(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.current_commit(),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.current_commit(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.current_commit(),
CosmicMappedRenderElement::Overlay(elem) => elem.current_commit(),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.current_commit(), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.current_commit(),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.current_commit(), CosmicMappedRenderElement::Egui(elem) => elem.current_commit(),
@ -1149,9 +1159,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.src(), CosmicMappedRenderElement::Window(elem) => elem.src(),
CosmicMappedRenderElement::TiledStack(elem) => elem.src(), CosmicMappedRenderElement::TiledStack(elem) => elem.src(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.src(), CosmicMappedRenderElement::TiledWindow(elem) => elem.src(),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.src(),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.src(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.src(),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.src(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.src(),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.src(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.src(),
CosmicMappedRenderElement::Overlay(elem) => elem.src(),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.src(), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.src(),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.src(), CosmicMappedRenderElement::Egui(elem) => elem.src(),
@ -1164,9 +1176,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.geometry(scale), CosmicMappedRenderElement::Window(elem) => elem.geometry(scale),
CosmicMappedRenderElement::TiledStack(elem) => elem.geometry(scale), CosmicMappedRenderElement::TiledStack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::TiledWindow(elem) => elem.geometry(scale), CosmicMappedRenderElement::TiledWindow(elem) => elem.geometry(scale),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.geometry(scale),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.geometry(scale), CosmicMappedRenderElement::GrabbedStack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.geometry(scale), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.geometry(scale),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.geometry(scale), CosmicMappedRenderElement::FocusIndicator(elem) => elem.geometry(scale),
CosmicMappedRenderElement::Overlay(elem) => elem.geometry(scale),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.geometry(scale), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.geometry(scale),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.geometry(scale), CosmicMappedRenderElement::Egui(elem) => elem.geometry(scale),
@ -1179,9 +1193,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.location(scale), CosmicMappedRenderElement::Window(elem) => elem.location(scale),
CosmicMappedRenderElement::TiledStack(elem) => elem.location(scale), CosmicMappedRenderElement::TiledStack(elem) => elem.location(scale),
CosmicMappedRenderElement::TiledWindow(elem) => elem.location(scale), CosmicMappedRenderElement::TiledWindow(elem) => elem.location(scale),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.location(scale),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.location(scale), CosmicMappedRenderElement::GrabbedStack(elem) => elem.location(scale),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.location(scale), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.location(scale),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.location(scale), CosmicMappedRenderElement::FocusIndicator(elem) => elem.location(scale),
CosmicMappedRenderElement::Overlay(elem) => elem.location(scale),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.location(scale), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.location(scale),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.location(scale), CosmicMappedRenderElement::Egui(elem) => elem.location(scale),
@ -1194,9 +1210,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.transform(), CosmicMappedRenderElement::Window(elem) => elem.transform(),
CosmicMappedRenderElement::TiledStack(elem) => elem.transform(), CosmicMappedRenderElement::TiledStack(elem) => elem.transform(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.transform(), CosmicMappedRenderElement::TiledWindow(elem) => elem.transform(),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.transform(),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.transform(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.transform(),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.transform(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.transform(),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.transform(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.transform(),
CosmicMappedRenderElement::Overlay(elem) => elem.transform(),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.transform(), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.transform(),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.transform(), CosmicMappedRenderElement::Egui(elem) => elem.transform(),
@ -1213,9 +1231,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::Window(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::TiledStack(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::TiledStack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::TiledWindow(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::TiledWindow(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::GrabbedStack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::FocusIndicator(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::Overlay(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::StackHoverIndicator(elem) => { CosmicMappedRenderElement::StackHoverIndicator(elem) => {
elem.damage_since(scale, commit) elem.damage_since(scale, commit)
} }
@ -1230,9 +1250,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::Window(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::TiledStack(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::TiledStack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::TiledWindow(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::TiledWindow(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::GrabbedStack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::FocusIndicator(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::Overlay(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.opaque_regions(scale),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::Egui(elem) => elem.opaque_regions(scale),
@ -1245,9 +1267,11 @@ where
CosmicMappedRenderElement::Window(elem) => elem.alpha(), CosmicMappedRenderElement::Window(elem) => elem.alpha(),
CosmicMappedRenderElement::TiledStack(elem) => elem.alpha(), CosmicMappedRenderElement::TiledStack(elem) => elem.alpha(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.alpha(), CosmicMappedRenderElement::TiledWindow(elem) => elem.alpha(),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.alpha(),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.alpha(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.alpha(),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.alpha(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.alpha(),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.alpha(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.alpha(),
CosmicMappedRenderElement::Overlay(elem) => elem.alpha(),
CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.alpha(), CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.alpha(),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.alpha(), CosmicMappedRenderElement::Egui(elem) => elem.alpha(),
@ -1268,11 +1292,17 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledStack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::TiledStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledOverlay(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
}
CosmicMappedRenderElement::GrabbedStack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::FocusIndicator(elem) => { CosmicMappedRenderElement::FocusIndicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage) RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
} }
CosmicMappedRenderElement::Overlay(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
}
CosmicMappedRenderElement::StackHoverIndicator(elem) => { CosmicMappedRenderElement::StackHoverIndicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage) RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
} }
@ -1289,9 +1319,11 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledStack(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::TiledStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::TiledWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledOverlay(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::GrabbedStack(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::FocusIndicator(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::FocusIndicator(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Overlay(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::StackHoverIndicator(elem) => { CosmicMappedRenderElement::StackHoverIndicator(elem) => {
elem.underlying_storage(renderer) elem.underlying_storage(renderer)
} }
@ -1316,12 +1348,20 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledStack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::TiledStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledOverlay(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(|err| GlMultiError::Render(err))
}
CosmicMappedRenderElement::GrabbedStack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::FocusIndicator(elem) => { CosmicMappedRenderElement::FocusIndicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage) RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(|err| GlMultiError::Render(err)) .map_err(|err| GlMultiError::Render(err))
} }
CosmicMappedRenderElement::Overlay(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(|err| GlMultiError::Render(err))
}
CosmicMappedRenderElement::StackHoverIndicator(elem) => { CosmicMappedRenderElement::StackHoverIndicator(elem) => {
elem.draw(frame, src, dst, damage) elem.draw(frame, src, dst, damage)
} }
@ -1343,11 +1383,17 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledStack(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::TiledStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::TiledWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledOverlay(elem) => {
elem.underlying_storage(renderer.glow_renderer_mut())
}
CosmicMappedRenderElement::GrabbedStack(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::GrabbedWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::FocusIndicator(elem) => { CosmicMappedRenderElement::FocusIndicator(elem) => {
elem.underlying_storage(renderer.glow_renderer_mut()) elem.underlying_storage(renderer.glow_renderer_mut())
} }
CosmicMappedRenderElement::Overlay(elem) => {
elem.underlying_storage(renderer.glow_renderer_mut())
}
CosmicMappedRenderElement::StackHoverIndicator(elem) => { CosmicMappedRenderElement::StackHoverIndicator(elem) => {
elem.underlying_storage(renderer) elem.underlying_storage(renderer)
} }

View file

@ -1,6 +1,11 @@
use super::{CosmicMapped, CosmicSurface, CosmicWindow}; use super::{CosmicMapped, CosmicSurface, CosmicWindow};
use crate::{ use crate::{
shell::{focus::FocusDirection, grabs::MoveGrab, layout::tiling::Direction, Shell, Trigger}, shell::{
focus::FocusDirection,
grabs::MoveGrab,
layout::tiling::{Direction, NodeDesc},
Shell, Trigger,
},
state::State, state::State,
utils::iced::{IcedElement, Program}, utils::iced::{IcedElement, Program},
utils::prelude::SeatExt, utils::prelude::SeatExt,
@ -85,6 +90,7 @@ pub struct CosmicStackInternal {
previous_keyboard: Arc<AtomicUsize>, previous_keyboard: Arc<AtomicUsize>,
pointer_entered: Arc<AtomicU8>, pointer_entered: Arc<AtomicU8>,
previous_pointer: Arc<AtomicUsize>, previous_pointer: Arc<AtomicUsize>,
reenter: Arc<AtomicBool>,
potential_drag: Arc<Mutex<Option<usize>>>, potential_drag: Arc<Mutex<Option<usize>>>,
override_alive: Arc<AtomicBool>, override_alive: Arc<AtomicBool>,
last_seat: Arc<Mutex<Option<(Seat<State>, Serial)>>>, last_seat: Arc<Mutex<Option<(Seat<State>, Serial)>>>,
@ -107,7 +113,7 @@ impl CosmicStackInternal {
} }
} }
const TAB_HEIGHT: i32 = 24; pub const TAB_HEIGHT: i32 = 24;
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -148,6 +154,7 @@ impl CosmicStack {
previous_keyboard: Arc::new(AtomicUsize::new(0)), previous_keyboard: Arc::new(AtomicUsize::new(0)),
pointer_entered: Arc::new(AtomicU8::new(Focus::None as u8)), pointer_entered: Arc::new(AtomicU8::new(Focus::None as u8)),
previous_pointer: Arc::new(AtomicUsize::new(0)), previous_pointer: Arc::new(AtomicUsize::new(0)),
reenter: Arc::new(AtomicBool::new(false)),
potential_drag: Arc::new(Mutex::new(None)), potential_drag: Arc::new(Mutex::new(None)),
override_alive: Arc::new(AtomicBool::new(true)), override_alive: Arc::new(AtomicBool::new(true)),
last_seat: Arc::new(Mutex::new(None)), last_seat: Arc::new(Mutex::new(None)),
@ -198,6 +205,9 @@ impl CosmicStack {
let Some(idx) = windows.iter().position(|w| w == window) else { let Some(idx) = windows.iter().position(|w| w == window) else {
return; return;
}; };
if idx == p.active.load(Ordering::SeqCst) {
p.reenter.store(true, Ordering::SeqCst);
}
let window = windows.remove(idx); let window = windows.remove(idx);
window.try_force_undecorated(false); window.try_force_undecorated(false);
window.set_tiled(false); window.set_tiled(false);
@ -220,7 +230,10 @@ impl CosmicStack {
if windows.len() <= idx { if windows.len() <= idx {
return; return;
} }
let window = dbg!(windows.remove(idx)); if idx == p.active.load(Ordering::SeqCst) {
p.reenter.store(true, Ordering::SeqCst);
}
let window = windows.remove(idx);
window.try_force_undecorated(false); window.try_force_undecorated(false);
window.set_tiled(false); window.set_tiled(false);
@ -233,7 +246,7 @@ impl CosmicStack {
self.0.with_program(|p| p.windows.lock().unwrap().len()) self.0.with_program(|p| p.windows.lock().unwrap().len())
} }
pub fn handle_focus(&self, direction: FocusDirection) -> bool { pub fn handle_focus(&self, direction: FocusDirection, swap: Option<NodeDesc>) -> bool {
let result = self.0.with_program(|p| match direction { let result = self.0.with_program(|p| match direction {
FocusDirection::Left => { FocusDirection::Left => {
if !p.group_focused.load(Ordering::SeqCst) { if !p.group_focused.load(Ordering::SeqCst) {
@ -278,7 +291,7 @@ impl CosmicStack {
false false
} }
} }
FocusDirection::Out => { FocusDirection::Out if swap.is_none() => {
if !p.group_focused.swap(true, Ordering::SeqCst) { if !p.group_focused.swap(true, Ordering::SeqCst) {
p.windows.lock().unwrap().iter().for_each(|w| { p.windows.lock().unwrap().iter().for_each(|w| {
w.set_activated(false); w.set_activated(false);
@ -289,7 +302,7 @@ impl CosmicStack {
false false
} }
} }
FocusDirection::In => { FocusDirection::In if swap.is_none() => {
if !p.group_focused.swap(false, Ordering::SeqCst) { if !p.group_focused.swap(false, Ordering::SeqCst) {
p.windows.lock().unwrap().iter().for_each(|w| { p.windows.lock().unwrap().iter().for_each(|w| {
w.set_activated(true); w.set_activated(true);
@ -360,6 +373,11 @@ impl CosmicStack {
.with_program(|p| &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] == window) .with_program(|p| &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] == window)
} }
pub fn whole_stack_focused(&self) -> bool {
self.0
.with_program(|p| p.group_focused.load(Ordering::SeqCst))
}
pub fn set_active(&self, window: &CosmicSurface) { pub fn set_active(&self, window: &CosmicSurface) {
self.0.with_program(|p| { self.0.with_program(|p| {
if let Some(val) = p.windows.lock().unwrap().iter().position(|w| w == window) { if let Some(val) = p.windows.lock().unwrap().iter().position(|w| w == window) {
@ -412,10 +430,12 @@ impl CosmicStack {
self.0.with_program(|p| { self.0.with_program(|p| {
let active = p.active.load(Ordering::SeqCst); let active = p.active.load(Ordering::SeqCst);
let previous = p.previous_keyboard.swap(active, Ordering::SeqCst); let previous = p.previous_keyboard.swap(active, Ordering::SeqCst);
if previous != active { if previous != active || p.reenter.swap(false, Ordering::SeqCst) {
let windows = p.windows.lock().unwrap(); let windows = p.windows.lock().unwrap();
if let Some(previous) = windows.get(previous) { if let Some(previous_surface) = windows.get(previous) {
KeyboardTarget::leave(previous, seat, data, serial); if previous != active {
KeyboardTarget::leave(previous_surface, seat, data, serial);
}
} }
KeyboardTarget::enter( KeyboardTarget::enter(
&windows[active], &windows[active],
@ -476,7 +496,7 @@ impl CosmicStack {
.with_program(|p| p.group_focused.store(true, Ordering::SeqCst)); .with_program(|p| p.group_focused.store(true, Ordering::SeqCst));
} }
pub(super) fn loop_handle(&self) -> LoopHandle<'static, crate::state::Data> { pub(in super::super) fn loop_handle(&self) -> LoopHandle<'static, crate::state::Data> {
self.0.loop_handle() self.0.loop_handle()
} }
@ -1051,9 +1071,10 @@ impl PointerTarget<State> for CosmicStack {
was_tiled, was_tiled,
); );
if grab.is_tiling_grab() { if grab.is_tiling_grab() {
data.common data.common.shell.set_overview_mode(
.shell Some(Trigger::Pointer(button)),
.set_overview_mode(Some(Trigger::Pointer(button))); data.common.event_loop_handle.clone(),
);
} }
let seat = seat.clone(); let seat = seat.clone();

View file

@ -0,0 +1,58 @@
use crate::{
fl,
utils::iced::{IcedElement, Program},
};
use apply::Apply;
use calloop::LoopHandle;
use cosmic::{
iced::widget::{container, horizontal_space, row},
iced_core::{Alignment, Background, Color, Length},
theme,
widget::{icon, text},
};
use smithay::utils::Size;
pub type SwapIndicator = IcedElement<SwapIndicatorInternal>;
pub fn swap_indicator(evlh: LoopHandle<'static, crate::state::Data>) -> SwapIndicator {
SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh)
}
pub struct SwapIndicatorInternal;
impl Program for SwapIndicatorInternal {
type Message = ();
fn view(&self) -> crate::utils::iced::Element<'_, Self::Message> {
row(vec![
icon("window-swap-symbolic", 32).force_svg(true).into(),
horizontal_space(16).into(),
text(fl!("swap-windows"))
.font(cosmic::font::FONT)
.size(24)
.into(),
])
.align_items(Alignment::Center)
.apply(container)
.center_x()
.center_y()
.padding(16)
.apply(container)
.style(theme::Container::custom(|theme| container::Appearance {
text_color: Some(Color::from(theme.cosmic().accent.on)),
background: Some(Background::Color(theme.cosmic().accent_color().into())),
border_radius: 18.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}))
.width(Length::Shrink)
.height(Length::Shrink)
.apply(container)
.height(Length::Fill)
.width(Length::Fill)
.center_x()
.center_y()
.into()
}
}

View file

@ -4,7 +4,7 @@ use crate::{
backend::render::{ backend::render::{
cursor::{CursorShape, CursorState}, cursor::{CursorShape, CursorState},
element::AsGlowRenderer, element::AsGlowRenderer,
IndicatorShader, IndicatorShader, Key, Usage,
}, },
shell::{ shell::{
element::{ element::{
@ -96,7 +96,7 @@ impl MoveGrabState {
Some( Some(
CosmicMappedRenderElement::from(IndicatorShader::focus_element( CosmicMappedRenderElement::from(IndicatorShader::focus_element(
renderer, renderer,
self.window.clone(), Key::Window(Usage::MoveGrabIndicator, self.window.clone()),
Rectangle::from_loc_and_size( Rectangle::from_loc_and_size(
render_location, render_location,
self.window self.window

View file

@ -13,7 +13,7 @@ use smithay::{
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
backend::render::{element::AsGlowRenderer, IndicatorShader}, backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage},
shell::{ shell::{
element::{ element::{
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement,
@ -514,7 +514,7 @@ impl FloatingLayout {
if indicator_thickness > 0 { if indicator_thickness > 0 {
let element = IndicatorShader::focus_element( let element = IndicatorShader::focus_element(
renderer, renderer,
elem.clone(), Key::Window(Usage::FocusIndicator, elem.clone()),
indicator_geometry, indicator_geometry,
indicator_thickness, indicator_thickness,
output_scale, output_scale,

View file

@ -1,3 +1,5 @@
mod resize; mod resize;
mod swap;
pub use self::resize::*; pub use self::resize::*;
pub use self::swap::*;

View file

@ -0,0 +1,93 @@
use smithay::{
backend::input::KeyState,
input::{
keyboard::{
GrabStartData as KeyboardGrabStartData, KeyboardGrab, KeyboardInnerHandle,
ModifiersState,
},
Seat, SeatHandler,
},
utils::Serial,
};
use crate::{
config::{Action, KeyPattern},
shell::{layout::tiling::NodeDesc, OverviewMode, Trigger},
state::State,
};
pub struct SwapWindowGrab {
seat: Seat<State>,
desc: NodeDesc,
}
impl SwapWindowGrab {
pub fn new(seat: Seat<State>, desc: NodeDesc) -> Self {
SwapWindowGrab { seat, desc }
}
}
impl KeyboardGrab<State> for SwapWindowGrab {
fn input(
&mut self,
data: &mut State,
handle: &mut KeyboardInnerHandle<'_, State>,
keycode: u32,
state: KeyState,
modifiers: Option<ModifiersState>,
serial: Serial,
time: u32,
) {
if self.desc.output.upgrade().is_none()
|| !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc)
{
handle.unset_grab(data, serial, false);
return;
}
if state == KeyState::Released {
return;
}
let syms = Vec::from(handle.keysym_handle(keycode).raw_syms());
let focus_bindings = &data
.common
.config
.static_conf
.key_bindings
.iter()
.filter(|(_, action)| matches!(action, Action::Focus(_)))
.map(|(pattern, action)| {
let Action::Focus(direction) = action else { unreachable!() };
(pattern.key, *direction)
})
.collect::<Vec<_>>();
let Some(direction) = syms.iter().find_map(|sym| focus_bindings.iter().find_map(|(key, direction)| (sym == key).then_some(*direction))) else { return };
data.handle_action(
Action::Focus(direction),
&self.seat,
serial,
time,
KeyPattern {
modifiers: modifiers.map(Into::into).unwrap_or_default(),
key: keycode,
},
None,
);
}
fn set_focus(
&mut self,
data: &mut State,
handle: &mut KeyboardInnerHandle<'_, State>,
focus: Option<<State as SeatHandler>::KeyboardFocus>,
serial: Serial,
) {
handle.set_focus(data, focus, serial)
}
fn start_data(&self) -> &KeyboardGrabStartData<State> {
&KeyboardGrabStartData { focus: None }
}
}

File diff suppressed because it is too large Load diff

View file

@ -59,13 +59,14 @@ pub use self::workspace::*;
use self::{ use self::{
element::{ element::{
resize_indicator::{resize_indicator, ResizeIndicator}, resize_indicator::{resize_indicator, ResizeIndicator},
swap_indicator::{swap_indicator, SwapIndicator},
CosmicWindow, CosmicWindow,
}, },
focus::target::KeyboardFocusTarget, focus::target::KeyboardFocusTarget,
grabs::ResizeEdge, grabs::ResizeEdge,
layout::{ layout::{
floating::{FloatingLayout, ResizeState}, floating::{FloatingLayout, ResizeState},
tiling::{Direction, TilingLayout}, tiling::{Direction, NodeDesc, TilingLayout},
}, },
}; };
@ -73,7 +74,8 @@ const ANIMATION_DURATION: Duration = Duration::from_millis(200);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Trigger { pub enum Trigger {
Keyboard(KeyModifiers), KeyboardSwap(KeyPattern, NodeDesc),
KeyboardMove(KeyModifiers),
Pointer(u32), Pointer(u32),
} }
@ -81,7 +83,7 @@ pub enum Trigger {
pub enum OverviewMode { pub enum OverviewMode {
None, None,
Started(Trigger, Instant), Started(Trigger, Instant),
Ended(Instant), Ended(Option<Trigger>, Instant),
} }
impl OverviewMode { impl OverviewMode {
@ -92,7 +94,7 @@ impl OverviewMode {
/ ANIMATION_DURATION.as_millis() as f32; / ANIMATION_DURATION.as_millis() as f32;
Some(ease(EaseInOutCubic, 0.0, 1.0, percentage)) Some(ease(EaseInOutCubic, 0.0, 1.0, percentage))
} }
OverviewMode::Ended(end) => { OverviewMode::Ended(_, end) => {
let percentage = Instant::now().duration_since(*end).as_millis() as f32 let percentage = Instant::now().duration_since(*end).as_millis() as f32
/ ANIMATION_DURATION.as_millis() as f32; / ANIMATION_DURATION.as_millis() as f32;
if percentage < 1.0 { if percentage < 1.0 {
@ -160,6 +162,7 @@ pub struct Shell {
gaps: (u8, u8), gaps: (u8, u8),
overview_mode: OverviewMode, overview_mode: OverviewMode,
swap_indicator: Option<SwapIndicator>,
resize_mode: ResizeMode, resize_mode: ResizeMode,
resize_state: Option<( resize_state: Option<(
KeyboardFocusTarget, KeyboardFocusTarget,
@ -628,6 +631,7 @@ impl Shell {
gaps: config.static_conf.gaps, gaps: config.static_conf.gaps,
overview_mode: OverviewMode::None, overview_mode: OverviewMode::None,
swap_indicator: None,
resize_mode: ResizeMode::None, resize_mode: ResizeMode::None,
resize_state: None, resize_state: None,
resize_indicator: None, resize_indicator: None,
@ -1206,32 +1210,45 @@ impl Shell {
clients clients
} }
pub fn set_overview_mode(&mut self, enabled: Option<Trigger>) { pub fn set_overview_mode(
&mut self,
enabled: Option<Trigger>,
evlh: LoopHandle<'static, crate::state::Data>,
) {
if let Some(trigger) = enabled { if let Some(trigger) = enabled {
if !matches!(self.overview_mode, OverviewMode::Started(_, _)) { if !matches!(self.overview_mode, OverviewMode::Started(_, _)) {
if matches!(trigger, Trigger::KeyboardSwap(_, _)) {
self.swap_indicator = Some(swap_indicator(evlh));
}
self.overview_mode = OverviewMode::Started(trigger, Instant::now()); self.overview_mode = OverviewMode::Started(trigger, Instant::now());
} }
} else { } else {
if !matches!(self.overview_mode, OverviewMode::Ended(_)) { if !matches!(self.overview_mode, OverviewMode::Ended(_, _)) {
let reverse_duration = if let OverviewMode::Started(_, start) = self.overview_mode { let (reverse_duration, trigger) =
ANIMATION_DURATION if let OverviewMode::Started(trigger, start) = self.overview_mode.clone() {
- Instant::now().duration_since(start).min(ANIMATION_DURATION) (
} else { ANIMATION_DURATION
Duration::ZERO - Instant::now().duration_since(start).min(ANIMATION_DURATION),
}; Some(trigger),
self.overview_mode = OverviewMode::Ended(Instant::now() - reverse_duration); )
} else {
(Duration::ZERO, None)
};
self.overview_mode =
OverviewMode::Ended(trigger, Instant::now() - reverse_duration);
} }
} }
} }
pub fn overview_mode(&mut self) -> OverviewMode { pub fn overview_mode(&mut self) -> (OverviewMode, Option<SwapIndicator>) {
if let OverviewMode::Ended(timestamp) = self.overview_mode { if let OverviewMode::Ended(_, timestamp) = self.overview_mode {
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION { if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
self.overview_mode = OverviewMode::None; self.overview_mode = OverviewMode::None;
self.swap_indicator = None;
} }
} }
self.overview_mode.clone() (self.overview_mode.clone(), self.swap_indicator.clone())
} }
pub fn set_resize_mode( pub fn set_resize_mode(
@ -1570,10 +1587,10 @@ impl Shell {
.toplevel_info_state .toplevel_info_state
.toplevel_leave_output(&window, &output); .toplevel_leave_output(&window, &output);
if grab.is_tiling_grab() { if grab.is_tiling_grab() {
state state.common.shell.set_overview_mode(
.common Some(Trigger::Pointer(button)),
.shell state.common.event_loop_handle.clone(),
.set_overview_mode(Some(Trigger::Pointer(button))); );
} }
seat.get_pointer().unwrap().set_grab( seat.get_pointer().unwrap().set_grab(
state, state,

View file

@ -50,13 +50,14 @@ use wayland_backend::server::ClientId;
use super::{ use super::{
element::{ element::{
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement,
window::CosmicWindowRenderElement, CosmicMapped, swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, CosmicMapped,
}, },
focus::{ focus::{
target::{KeyboardFocusTarget, PointerFocusTarget}, target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup},
FocusStack, FocusStackMut, FocusStack, FocusStackMut,
}, },
grabs::{ResizeEdge, ResizeGrab}, grabs::{ResizeEdge, ResizeGrab},
layout::tiling::NodeDesc,
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode, CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
}; };
@ -482,6 +483,43 @@ impl Workspace {
&& self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped)
} }
pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option<NodeDesc> {
match focus {
KeyboardFocusTarget::Element(mapped) => {
self.tiling_layer.mapped().find_map(|(output, m, _)| {
(m == &mapped).then_some(output.clone()).and_then(|output| {
mapped
.tiling_node_id
.lock()
.unwrap()
.clone()
.map(|node_id| NodeDesc {
handle: self.handle.clone(),
output: output.downgrade(),
node: node_id,
stack_window: if mapped
.stack_ref()
.map(|stack| !stack.whole_stack_focused())
.unwrap_or(false)
{
Some(mapped.active_window())
} else {
None
},
})
})
})
}
KeyboardFocusTarget::Group(WindowGroup { output, node, .. }) => Some(NodeDesc {
handle: self.handle.clone(),
output,
node,
stack_window: None,
}),
_ => None,
}
}
pub fn render_output<'a, R>( pub fn render_output<'a, R>(
&self, &self,
renderer: &mut R, renderer: &mut R,
@ -489,7 +527,7 @@ impl Workspace {
override_redirect_windows: &[X11Surface], override_redirect_windows: &[X11Surface],
xwm_state: Option<&'a mut XWaylandState>, xwm_state: Option<&'a mut XWaylandState>,
draw_focus_indicator: Option<&Seat<State>>, draw_focus_indicator: Option<&Seat<State>>,
overview: OverviewMode, overview: (OverviewMode, Option<SwapIndicator>),
resize_indicator: Option<(ResizeMode, ResizeIndicator)>, resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
indicator_thickness: u8, indicator_thickness: u8,
) -> Result< ) -> Result<
@ -582,7 +620,7 @@ impl Workspace {
draw_focus_indicator.and_then(|seat| self.focus_stack.get(seat).last().cloned()); draw_focus_indicator.and_then(|seat| self.focus_stack.get(seat).last().cloned());
// floating surfaces // floating surfaces
let alpha = match &overview { let alpha = match &overview.0 {
OverviewMode::Started(_, started) => { OverviewMode::Started(_, started) => {
(1.0 - (Instant::now().duration_since(*started).as_millis() (1.0 - (Instant::now().duration_since(*started).as_millis()
/ ANIMATION_DURATION.as_millis()) as f32) / ANIMATION_DURATION.as_millis()) as f32)
@ -590,7 +628,7 @@ impl Workspace {
* 0.4 * 0.4
+ 0.6 + 0.6
} }
OverviewMode::Ended(ended) => { OverviewMode::Ended(_, ended) => {
((Instant::now().duration_since(*ended).as_millis() ((Instant::now().duration_since(*ended).as_millis()
/ ANIMATION_DURATION.as_millis()) as f32) / ANIMATION_DURATION.as_millis()) as f32)
* 0.4 * 0.4
@ -614,6 +652,7 @@ impl Workspace {
let (w_elements, p_elements) = self.tiling_layer.render_output::<R>( let (w_elements, p_elements) = self.tiling_layer.render_output::<R>(
renderer, renderer,
output, output,
&self.handle,
draw_focus_indicator, draw_focus_indicator,
layer_map.non_exclusive_zone(), layer_map.non_exclusive_zone(),
overview.clone(), overview.clone(),
@ -635,12 +674,12 @@ impl Workspace {
} }
} }
let alpha = match overview { let alpha = match overview.0 {
OverviewMode::Started(_, start) => Some( OverviewMode::Started(_, start) => Some(
(Instant::now().duration_since(start).as_millis() as f64 / 100.0).min(1.0) (Instant::now().duration_since(start).as_millis() as f64 / 100.0).min(1.0)
as f32, as f32,
), ),
OverviewMode::Ended(ended) => Some( OverviewMode::Ended(_, ended) => Some(
1.0 - (Instant::now().duration_since(ended).as_millis() as f64 / 100.0).min(1.0) 1.0 - (Instant::now().duration_since(ended).as_millis() as f64 / 100.0).min(1.0)
as f32, as f32,
), ),

View file

@ -4,3 +4,4 @@ mod ids;
pub(crate) use self::ids::id_gen; pub(crate) use self::ids::id_gen;
pub mod iced; pub mod iced;
pub mod prelude; pub mod prelude;
pub mod tween;

47
src/utils/tween.rs Normal file
View file

@ -0,0 +1,47 @@
use keyframe::{num_traits::Float, CanTween};
use smithay::utils::{Coordinate, Point, Rectangle, Size};
pub struct EasePoint<N: Coordinate, Kind>(pub Point<N, Kind>);
pub struct EaseSize<N: Coordinate, Kind>(pub Size<N, Kind>);
pub struct EaseRectangle<N: Coordinate, Kind>(pub Rectangle<N, Kind>);
impl<N: Coordinate, Kind> CanTween for EasePoint<N, Kind> {
fn ease(from: Self, to: Self, time: impl Float) -> Self {
let x = N::from_f64(CanTween::ease(from.0.x.to_f64(), to.0.x.to_f64(), time).round());
let y = N::from_f64(CanTween::ease(from.0.y.to_f64(), to.0.y.to_f64(), time).round());
EasePoint((x, y).into())
}
}
impl<N: Coordinate, Kind> CanTween for EaseSize<N, Kind> {
fn ease(from: Self, to: Self, time: impl Float) -> Self {
let w = N::from_f64(CanTween::ease(from.0.w.to_f64(), to.0.w.to_f64(), time).round());
let h = N::from_f64(CanTween::ease(from.0.h.to_f64(), to.0.h.to_f64(), time).round());
EaseSize((w, h).into())
}
}
impl<N: Coordinate, Kind> CanTween for EaseRectangle<N, Kind> {
fn ease(from: Self, to: Self, time: impl Float) -> Self {
EaseRectangle(Rectangle::from_loc_and_size(
CanTween::ease(EasePoint(from.0.loc), EasePoint(to.0.loc), time).unwrap(),
CanTween::ease(EaseSize(from.0.size), EaseSize(to.0.size), time).unwrap(),
))
}
}
impl<N: Coordinate, Kind> EasePoint<N, Kind> {
pub fn unwrap(self) -> Point<N, Kind> {
self.0
}
}
impl<N: Coordinate, Kind> EaseSize<N, Kind> {
pub fn unwrap(self) -> Size<N, Kind> {
self.0
}
}
impl<N: Coordinate, Kind> EaseRectangle<N, Kind> {
pub fn unwrap(self) -> Rectangle<N, Kind> {
self.0
}
}