feat: maximize/half tiling drag zones
This commit is contained in:
parent
658a88da8c
commit
a4f3006313
3 changed files with 182 additions and 41 deletions
|
|
@ -108,6 +108,7 @@ pub enum Usage {
|
|||
MoveGrabIndicator,
|
||||
FocusIndicator,
|
||||
PotentialGroupIndicator,
|
||||
SnappingIndicator,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ use crate::{
|
|||
CosmicMappedRenderElement,
|
||||
},
|
||||
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
|
||||
CosmicMapped, CosmicSurface, ManagedLayer,
|
||||
layout::floating::TiledCorners,
|
||||
CosmicMapped, CosmicSurface, Direction, ManagedLayer,
|
||||
},
|
||||
utils::prelude::*,
|
||||
};
|
||||
|
|
@ -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,
|
||||
|
|
@ -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>)>,
|
||||
}
|
||||
|
||||
|
|
@ -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>>(
|
||||
|
|
@ -188,6 +214,7 @@ impl MoveGrabState {
|
|||
}
|
||||
x => x,
|
||||
}))
|
||||
.chain(snapping_indicator)
|
||||
.map(I::from)
|
||||
.collect()
|
||||
}
|
||||
|
|
@ -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>,
|
||||
|
|
@ -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(¤t_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);
|
||||
|
||||
|
|
@ -454,6 +558,7 @@ impl MoveGrab {
|
|||
indicator_thickness,
|
||||
start: Instant::now(),
|
||||
stacking_indicator: None,
|
||||
snapping_zone: None,
|
||||
previous: previous_layer,
|
||||
};
|
||||
|
||||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
@ -995,7 +974,7 @@ impl FloatingLayout {
|
|||
| Some(TiledCorners::BottomRight),
|
||||
) => {
|
||||
return MoveResult::MoveFurther(KeyboardFocusTarget::Element(
|
||||
focused.clone(),
|
||||
element.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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),
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue