use smithay_client_toolkit::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge;
use super::app_data::AppData;
use super::surface::{ SurfaceFocus, SurfaceKind };
use crate::app::App;
use crate::types::{ CursorShape, Point };
pub( crate ) fn resize_edge_for_pos( pos: Point, w: f32, h: f32, border: f32 ) -> Option<ResizeEdge>
{
let on_left = pos.x < border;
let on_right = pos.x > w - border;
let on_top = pos.y < border;
let on_bottom = pos.y > h - border;
Some( match ( on_top, on_bottom, on_left, on_right )
{
( true, _, true, _ ) => ResizeEdge::TopLeft,
( true, _, _, true ) => ResizeEdge::TopRight,
( _, true, true, _ ) => ResizeEdge::BottomLeft,
( _, true, _, true ) => ResizeEdge::BottomRight,
( true, _, _, _ ) => ResizeEdge::Top,
( _, true, _, _ ) => ResizeEdge::Bottom,
( _, _, true, _ ) => ResizeEdge::Left,
( _, _, _, true ) => ResizeEdge::Right,
_ => return None,
} )
}
pub( crate ) fn resize_edge_to_cursor( edge: ResizeEdge ) -> CursorShape
{
use CursorShape::*;
match edge
{
ResizeEdge::Top => NResize,
ResizeEdge::Bottom => SResize,
ResizeEdge::Left => WResize,
ResizeEdge::Right => EResize,
ResizeEdge::TopLeft => NwResize,
ResizeEdge::TopRight => NeResize,
ResizeEdge::BottomLeft => SwResize,
ResizeEdge::BottomRight => SeResize,
_ => Default,
}
}
impl<A: App> AppData<A>
{
fn cursor_shape_to_wp( shape: CursorShape )
-> smithay_client_toolkit::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape
{
use CursorShape as C;
use smithay_client_toolkit::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape as S;
match shape
{
C::Default => S::Default,
C::ContextMenu => S::ContextMenu,
C::Help => S::Help,
C::Pointer => S::Pointer,
C::Progress => S::Progress,
C::Wait => S::Wait,
C::Cell => S::Cell,
C::Crosshair => S::Crosshair,
C::Text => S::Text,
C::VerticalText => S::VerticalText,
C::Alias => S::Alias,
C::Copy => S::Copy,
C::Move => S::Move,
C::NoDrop => S::NoDrop,
C::NotAllowed => S::NotAllowed,
C::Grab => S::Grab,
C::Grabbing => S::Grabbing,
C::EResize => S::EResize,
C::NResize => S::NResize,
C::NeResize => S::NeResize,
C::NwResize => S::NwResize,
C::SResize => S::SResize,
C::SeResize => S::SeResize,
C::SwResize => S::SwResize,
C::WResize => S::WResize,
C::EwResize => S::EwResize,
C::NsResize => S::NsResize,
C::NeswResize => S::NeswResize,
C::NwseResize => S::NwseResize,
C::ColResize => S::ColResize,
C::RowResize => S::RowResize,
C::AllScroll => S::AllScroll,
C::ZoomIn => S::ZoomIn,
C::ZoomOut => S::ZoomOut,
}
}
pub( crate ) fn dispatch_cursor_shape( &mut self, focus: SurfaceFocus )
{
let target = {
let app_override = self.app.cursor_override();
let dragging = self.surface( focus ).gesture.dragging_slider.is_some();
let hover_cursor = self.surface( focus ).hovered_idx
.and_then( |idx| crate::tree::find_widget( &self.surface( focus ).widget_rects, idx ) )
.map( |w| w.cursor )
.unwrap_or( CursorShape::Default );
let resize_cursor = self.resize_edge_under_pointer( focus ).map( resize_edge_to_cursor );
let allow_chrome = app_override.is_none()
|| app_override == Some( CursorShape::Default );
if let Some( c ) = resize_cursor.filter( |_| allow_chrome )
{
c
} else if let Some( o ) = app_override {
o
} else if dragging {
CursorShape::Grabbing
} else {
hover_cursor
}
};
if self.current_cursor_shape == Some( target ) { return; }
if let Some( device ) = self.cursor_shape_device.as_ref()
{
device.set_shape( self.last_pointer_enter_serial, Self::cursor_shape_to_wp( target ) );
self.current_cursor_shape = Some( target );
}
}
pub( crate ) const RESIZE_BORDER_LOGICAL: f32 = 6.0;
pub( crate ) fn resize_edge_under_pointer( &self, focus: SurfaceFocus ) -> Option<ResizeEdge>
{
if !matches!( focus, SurfaceFocus::Main ) { return None; }
if !matches!( self.main.surface, SurfaceKind::Window( _ ) ) { return None; }
let sf = self.surface( focus ).scale_factor.max( 1 ) as f32;
let border = Self::RESIZE_BORDER_LOGICAL * sf;
let w = self.surface( focus ).physical_width() as f32;
let h = self.surface( focus ).physical_height() as f32;
resize_edge_for_pos( self.pointer_pos, w, h, border )
}
}
#[ cfg( test ) ]
mod resize_edge_tests
{
use super::{ resize_edge_for_pos, ResizeEdge };
use crate::types::Point;
const W: f32 = 800.0;
const H: f32 = 600.0;
const B: f32 = 6.0;
fn at( x: f32, y: f32 ) -> Option<ResizeEdge>
{
resize_edge_for_pos( Point { x, y }, W, H, B )
}
#[ test ]
fn middle_is_none()
{
assert!( at( 400.0, 300.0 ).is_none() );
}
#[ test ]
fn just_inside_border_is_none()
{
assert!( at( 6.0, 6.0 ).is_none() );
}
#[ test ]
fn corners_take_precedence_over_single_edges()
{
assert_eq!( at( 1.0, 1.0 ), Some( ResizeEdge::TopLeft ) );
assert_eq!( at( W - 1.0, 1.0 ), Some( ResizeEdge::TopRight ) );
assert_eq!( at( 1.0, H - 1.0 ), Some( ResizeEdge::BottomLeft ) );
assert_eq!( at( W - 1.0, H - 1.0 ), Some( ResizeEdge::BottomRight ) );
}
#[ test ]
fn straight_edges()
{
assert_eq!( at( 400.0, 1.0 ), Some( ResizeEdge::Top ) );
assert_eq!( at( 400.0, H - 1.0 ), Some( ResizeEdge::Bottom ) );
assert_eq!( at( 1.0, 300.0 ), Some( ResizeEdge::Left ) );
assert_eq!( at( W - 1.0, 300.0 ), Some( ResizeEdge::Right ) );
}
}