Skip to content

Commit

Permalink
feat: maximize/half tiling drag zones
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanabx authored and Drakulix committed Mar 20, 2024
1 parent 658a88d commit a4f3006
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/backend/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ pub enum Usage {
MoveGrabIndicator,
FocusIndicator,
PotentialGroupIndicator,
SnappingIndicator,
}

#[derive(Clone)]
Expand Down
133 changes: 131 additions & 2 deletions src/shell/grabs/moving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use crate::{
CosmicMappedRenderElement,
},
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
CosmicMapped, CosmicSurface, ManagedLayer,
layout::floating::TiledCorners,
CosmicMapped, CosmicSurface, Direction, ManagedLayer,
},
utils::prelude::*,
};
Expand All @@ -27,7 +28,7 @@ use smithay::{
ImportAll, ImportMem, Renderer,
},
},
desktop::space::SpaceElement,
desktop::{layer_map_for_output, space::SpaceElement},
input::{
pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
Expand Down Expand Up @@ -55,6 +56,7 @@ pub struct MoveGrabState {
indicator_thickness: u8,
start: Instant,
previous: ManagedLayer,
snapping_zone: Option<SnappingZone>,
stacking_indicator: Option<(StackHover, Point<i32, Logical>)>,
}

Expand Down Expand Up @@ -141,6 +143,30 @@ impl MoveGrabState {
None
};

let non_exclusive_geometry = {
let layers = layer_map_for_output(&output);
layers.non_exclusive_zone()
};

let snapping_indicator = match &self.snapping_zone {
Some(t) => vec![IndicatorShader::element(
renderer,
Key::Window(Usage::SnappingIndicator, self.window.clone()),
t.overlay_geometry(non_exclusive_geometry),
4,
8,
0.5,
output_scale.x,
[
active_window_hint.red,
active_window_hint.green,
active_window_hint.blue,
],
)
.into()],
None => vec![],
};

let (window_elements, popup_elements) = self
.window
.split_render_elements::<R, CosmicMappedRenderElement<R>>(
Expand Down Expand Up @@ -188,6 +214,7 @@ impl MoveGrabState {
}
x => x,
}))
.chain(snapping_indicator)
.map(I::from)
.collect()
}
Expand All @@ -200,6 +227,58 @@ impl MoveGrabState {
struct NotSend<T>(pub T);
unsafe impl<T> Send for NotSend<T> {}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SnappingZone {
TopMaxim,
TopSnap,
Left,
Right,
Bottom,
}

const SNAPPING_RANGE: i32 = 12;

impl SnappingZone {
pub fn contains(
&self,
point: Point<i32, Logical>,
non_exclusive_geometry: Rectangle<i32, Logical>,
output_geometry: Rectangle<i32, Logical>,
) -> bool {
if !output_geometry.contains(point) {
return false;
}
match self {
SnappingZone::TopMaxim => point.y < non_exclusive_geometry.loc.y + SNAPPING_RANGE,
SnappingZone::TopSnap => {
point.y < non_exclusive_geometry.loc.y + SNAPPING_RANGE * 2
&& point.y >= non_exclusive_geometry.loc.y + SNAPPING_RANGE
}
SnappingZone::Bottom => {
point.y
> non_exclusive_geometry.loc.y + non_exclusive_geometry.size.h - SNAPPING_RANGE
}
SnappingZone::Left => point.x < non_exclusive_geometry.loc.x + SNAPPING_RANGE,
SnappingZone::Right => {
point.x
> non_exclusive_geometry.loc.x + non_exclusive_geometry.size.w - SNAPPING_RANGE
}
}
}
pub fn overlay_geometry(
&self,
non_exclusive_geometry: Rectangle<i32, Logical>,
) -> Rectangle<i32, Local> {
match self {
SnappingZone::TopMaxim => non_exclusive_geometry.as_local(),
SnappingZone::TopSnap => TiledCorners::Top.relative_geometry(non_exclusive_geometry),
SnappingZone::Left => TiledCorners::Left.relative_geometry(non_exclusive_geometry),
SnappingZone::Right => TiledCorners::Right.relative_geometry(non_exclusive_geometry),
SnappingZone::Bottom => TiledCorners::Bottom.relative_geometry(non_exclusive_geometry),
}
}
}

pub struct MoveGrab {
window: CosmicMapped,
start_data: PointerGrabStartData<State>,
Expand Down Expand Up @@ -297,6 +376,31 @@ impl PointerGrab<State> for MoveGrab {
(element, geo.loc.as_logical())
});
}

// Check for overlapping with zones
if grab_state.previous == ManagedLayer::Floating {
let non_exclusive_geometry = {
let layers = layer_map_for_output(&current_output);
layers.non_exclusive_zone()
};
let total_geometry = current_output.geometry().as_logical();
grab_state.snapping_zone = vec![
SnappingZone::TopMaxim,
SnappingZone::TopSnap,
SnappingZone::Left,
SnappingZone::Right,
SnappingZone::Bottom,
]
.iter()
.find(|&x| {
x.contains(
handle.current_location().to_i32_floor(),
non_exclusive_geometry,
total_geometry,
)
})
.cloned();
}
}
drop(borrow);

Expand Down Expand Up @@ -454,6 +558,7 @@ impl MoveGrab {
indicator_thickness,
start: Instant::now(),
stacking_indicator: None,
snapping_zone: None,
previous: previous_layer,
};

Expand Down Expand Up @@ -555,11 +660,35 @@ impl Drop for MoveGrab {
window_location,
grab_state.window.geometry().size.as_global(),
));
let theme = state.common.shell.theme.clone();
let workspace = state.common.shell.active_space_mut(&output);
let (window, location) = workspace.floating_layer.drop_window(
grab_state.window,
window_location.to_local(&workspace.output),
);

if previous == ManagedLayer::Floating {
if let Some(sz) = grab_state.snapping_zone {
if sz == SnappingZone::TopMaxim {
state.common.shell.maximize_toggle(&window, &seat);
} else {
let direction = match sz {
SnappingZone::TopSnap => Direction::Up,
SnappingZone::Bottom => Direction::Down,
SnappingZone::Left => Direction::Left,
SnappingZone::Right => Direction::Right,
_ => Direction::Up,
};
workspace.floating_layer.move_element(
direction,
&seat,
ManagedLayer::Floating,
theme,
&window,
);
}
}
}
Some((window, location.to_global(&output)))
}
}
Expand Down
89 changes: 50 additions & 39 deletions src/shell/layout/floating/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,41 +894,20 @@ impl FloatingLayout {
res
}

pub fn move_current_element<'a>(
pub fn move_element<'a>(
&mut self,
direction: Direction,
seat: &Seat<State>,
layer: ManagedLayer,
theme: cosmic::Theme,
element: &CosmicMapped,
) -> MoveResult {
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return MoveResult::None;
};

let Some(focused) = (match target {
KeyboardFocusTarget::Popup(popup) => {
let Some(toplevel_surface) = (match popup {
PopupKind::Xdg(xdg) => get_popup_toplevel(&xdg),
PopupKind::InputMethod(_) => unreachable!(),
}) else {
return MoveResult::None;
};
self.space
.elements()
.find(|elem| elem.wl_surface().as_ref() == Some(&toplevel_surface))
}
KeyboardFocusTarget::Element(elem) => self.space.elements().find(|e| *e == &elem),
_ => None,
}) else {
return MoveResult::None;
};

match focused.handle_move(direction) {
match element.handle_move(direction) {
StackMoveResult::Handled => MoveResult::Done,
StackMoveResult::MoveOut(surface, loop_handle) => {
let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle, theme).into();
let output = seat.active_output();
let pos = self.space.element_geometry(focused).unwrap().loc
let pos = self.space.element_geometry(element).unwrap().loc
+ match direction {
Direction::Up => Point::from((5, -10)),
Direction::Down => Point::from((5, 10)),
Expand All @@ -950,7 +929,7 @@ impl FloatingLayout {
MoveResult::ShiftFocus(KeyboardFocusTarget::Element(mapped))
}
StackMoveResult::Default => {
let mut tiled_state = focused.floating_tiled.lock().unwrap();
let mut tiled_state = element.floating_tiled.lock().unwrap();

let output = self.space.outputs().next().unwrap().clone();
let layers = layer_map_for_output(&output);
Expand All @@ -959,10 +938,10 @@ impl FloatingLayout {

let current_geometry = self
.space
.element_geometry(focused)
.element_geometry(element)
.map(RectExt::as_local)
.unwrap();
let start_rectangle = if let Some(anim) = self.animations.remove(focused) {
let start_rectangle = if let Some(anim) = self.animations.remove(element) {
anim.geometry(output_geometry, current_geometry, tiled_state.as_ref())
} else {
current_geometry
Expand Down Expand Up @@ -995,7 +974,7 @@ impl FloatingLayout {
| Some(TiledCorners::BottomRight),
) => {
return MoveResult::MoveFurther(KeyboardFocusTarget::Element(
focused.clone(),
element.clone(),
));
}

Expand All @@ -1006,14 +985,14 @@ impl FloatingLayout {
| (Direction::Right, Some(TiledCorners::Left)) => {
std::mem::drop(tiled_state);

let mut maximized_state = focused.maximized_state.lock().unwrap();
let mut maximized_state = element.maximized_state.lock().unwrap();
*maximized_state = Some(MaximizedState {
original_geometry: start_rectangle,
original_layer: layer,
});
std::mem::drop(maximized_state);

self.map_maximized(focused.clone(), start_rectangle, true);
self.map_maximized(element.clone(), start_rectangle, true);
return MoveResult::Done;
}

Expand Down Expand Up @@ -1044,28 +1023,28 @@ impl FloatingLayout {

let new_geo = new_state.relative_geometry(output_geometry);
let (new_pos, new_size) = (new_geo.loc, new_geo.size);
focused.set_tiled(true); // TODO: More fine grained?
focused.set_maximized(false);
element.set_tiled(true); // TODO: More fine grained?
element.set_maximized(false);

if tiled_state.is_none() {
let last_geometry = focused
let last_geometry = element
.maximized_state
.lock()
.unwrap()
.take()
.map(|state| state.original_geometry)
.or_else(|| self.space.element_geometry(focused).map(RectExt::as_local));
.or_else(|| self.space.element_geometry(element).map(RectExt::as_local));

*focused.last_geometry.lock().unwrap() = last_geometry;
*element.last_geometry.lock().unwrap() = last_geometry;
}

*tiled_state = Some(new_state);
std::mem::drop(tiled_state);

focused.moved_since_mapped.store(true, Ordering::SeqCst);
let focused = focused.clone();
element.moved_since_mapped.store(true, Ordering::SeqCst);
let element = element.clone();
self.map_internal(
focused,
element,
Some(new_pos),
Some(new_size.as_logical()),
Some(start_rectangle),
Expand All @@ -1076,6 +1055,38 @@ impl FloatingLayout {
}
}

pub fn move_current_element<'a>(
&mut self,
direction: Direction,
seat: &Seat<State>,
layer: ManagedLayer,
theme: cosmic::Theme,
) -> MoveResult {
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
return MoveResult::None;
};

let Some(focused) = (match target {
KeyboardFocusTarget::Popup(popup) => {
let Some(toplevel_surface) = (match popup {
PopupKind::Xdg(xdg) => get_popup_toplevel(&xdg),
PopupKind::InputMethod(_) => unreachable!(),
}) else {
return MoveResult::None;
};
self.space
.elements()
.find(|elem| elem.wl_surface().as_ref() == Some(&toplevel_surface))
}
KeyboardFocusTarget::Element(elem) => self.space.elements().find(|x| *x == &elem),
_ => None,
}) else {
return MoveResult::None;
};

self.move_element(direction, seat, layer, theme, &focused.clone())
}

pub fn mapped(&self) -> impl Iterator<Item = &CosmicMapped> {
self.space.elements().rev()
}
Expand Down

0 comments on commit a4f3006

Please sign in to comment.