use super::app_data::AppData;
use super::surface::SurfaceFocus;
use crate::app::App;
use crate::platform::Platform;
use crate::types::{ CursorShape, Point };
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub( crate ) enum ResizeEdge
{
Top,
Bottom,
Left,
Right,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
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,
}
}
impl<A: App, P: Platform> AppData<A, P>
{
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 ).frame.widget_rects, idx ) )
.map( |w| if self.over_text_scrollbar( focus, w ) { CursorShape::Default } else { w.cursor } )
.unwrap_or( CursorShape::Default );
let resize_cursor = self.resize_edge_under_pointer( focus ).map( resize_edge_to_cursor );
let area_cursor = matches!( focus, SurfaceFocus::Main )
.then( || self.app.cursor_at( self.pointer_pos.x, self.pointer_pos.y ) )
.flatten();
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 {
area_cursor.unwrap_or( hover_cursor )
}
};
if self.current_cursor_shape == Some( target ) { return; }
if self.platform.set_cursor_shape( target ) { self.current_cursor_shape = Some( target ); }
}
fn over_text_scrollbar( &self, focus: SurfaceFocus, widget: &crate::widget::LaidOutWidget<A::Message> ) -> bool
{
if !widget.handlers.is_multiline_text_input() { return false; }
let Some( canvas ) = self.surface( focus ).canvas.as_ref() else { return false };
let band = canvas.geom_px( crate::widget::text_edit::theme::SCROLLBAR_HIT );
if self.pointer_pos.x < widget.rect.x + widget.rect.width - band { return false; }
self.text_scrollbar( focus, widget.flat_idx ).is_some_and( |( _, bar, _ )| bar.hit.contains( self.pointer_pos ) )
}
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 !self.main_is_toplevel() { return None; }
let sf = self.surface( focus ).scale_factor;
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 ) );
}
}