@@ -24,6 +24,20 @@ use std::rc::Rc;
2424const TOOLBAR_RISE : f32 = 14. ;
2525const TOOLBAR_REACH : f32 = 320. ;
2626
27+ /// Hover ownership is geometric: the toolbar occludes the pane's hitbox, but
28+ /// crossing onto its buttons must keep the current hover owner.
29+ fn pointed_row ( position : Point < Pixels > , viewport : Bounds < Pixels > , seen : & [ ( usize , Bounds < Pixels > ) ] , held : Option < usize > ) -> Option < usize > {
30+ if !viewport. contains ( & position) { return None ; }
31+ let toolbar = held. and_then ( |held| seen. iter ( ) . find ( |( ix, bounds) | {
32+ let rise = Bounds :: from_corners (
33+ point ( bounds. right ( ) - px ( TOOLBAR_REACH ) , bounds. top ( ) - px ( TOOLBAR_RISE ) ) ,
34+ point ( bounds. right ( ) , bounds. top ( ) + px ( TOOLBAR_RISE + 8. ) ) ,
35+ ) ;
36+ * ix == held && rise. contains ( & position)
37+ } ) ) ;
38+ toolbar. or_else ( || seen. iter ( ) . find ( |( _, bounds) | bounds. contains ( & position) ) ) . map ( |( ix, _) | * ix)
39+ }
40+
2741/// The context menu of a message, where the pointer was.
2842pub struct Menu {
2943 pub message : Rc < Message > ,
@@ -197,36 +211,19 @@ impl Pane {
197211 if self . script . holds_hover ( ) {
198212 return ;
199213 }
200- let viewport = self . list . viewport_bounds ( ) ;
201- let over = viewport. contains ( & position) . then ( || {
214+ let over = {
202215 let seen = self . seen . borrow ( ) ;
203- let row = |bounds : & Bounds < Pixels > | bounds. contains ( & position) ;
204- // The toolbar rises over the row above; the pointer on it is still on its own row.
205- let held = self . hover . as_ref ( ) . and_then ( |id| {
206- seen. iter ( ) . find ( |( ix, bounds) | {
207- let rise = Bounds :: from_corners (
208- point ( bounds. right ( ) - px ( TOOLBAR_REACH ) , bounds. top ( ) - px ( TOOLBAR_RISE ) ) ,
209- point ( bounds. right ( ) , bounds. top ( ) + px ( TOOLBAR_RISE + 8. ) ) ,
210- ) ;
211- rise. contains ( & position) && self . items . get ( * ix) . and_then ( Item :: message) . is_some_and ( |m| m. id == * id)
212- } )
213- } ) ;
214- let found = held. or_else ( || seen. iter ( ) . find ( |( _, bounds) | row ( bounds) ) ) ;
215- found. and_then ( |( ix, _) | self . items . get ( * ix) ) . and_then ( Item :: message) . filter ( |m| self . can ( m) . act ) . map ( |m| m. id . clone ( ) )
216- } ) ;
217- let over = over. flatten ( ) ;
216+ let held = self . hover . as_ref ( ) . and_then ( |id| seen. iter ( ) . find ( |( ix, _) | self . items . get ( * ix) . and_then ( Item :: message) . is_some_and ( |m| m. id == * id) ) ) . map ( |( ix, _) | * ix) ;
217+ pointed_row ( position, self . list . viewport_bounds ( ) , & seen, held)
218+ . and_then ( |ix| self . items . get ( ix) ) . and_then ( Item :: message)
219+ . filter ( |m| self . can ( m) . act ) . map ( |m| m. id . clone ( ) )
220+ } ;
218221 if over != self . hover {
219222 self . hover = over;
220223 cx. notify ( ) ;
221224 }
222225 }
223226
224- pub ( super ) fn unpoint ( & mut self , cx : & mut Context < Self > ) {
225- if self . menu . is_none ( ) && self . picker . is_none ( ) && self . hover . take ( ) . is_some ( ) {
226- cx. notify ( ) ;
227- }
228- }
229-
230227 // The toolbar.
231228
232229 /// A menu, picker, dialog, viewer or a window-level layer is up: no toolbar shows.
@@ -731,6 +728,26 @@ impl Pane {
731728mod tests {
732729 use super :: * ;
733730
731+ #[ test]
732+ fn crossing_onto_toolbar_buttons_keeps_the_same_message ( ) {
733+ let viewport = Bounds :: from_corners ( point ( px ( 0. ) , px ( 0. ) ) , point ( px ( 800. ) , px ( 600. ) ) ) ;
734+ let seen = [
735+ ( 0 , Bounds :: from_corners ( point ( px ( 0. ) , px ( 100. ) ) , point ( px ( 800. ) , px ( 150. ) ) ) ) ,
736+ ( 1 , Bounds :: from_corners ( point ( px ( 0. ) , px ( 150. ) ) , point ( px ( 800. ) , px ( 200. ) ) ) ) ,
737+ ] ;
738+ let mut held = pointed_row ( point ( px ( 400. ) , px ( 175. ) ) , viewport, & seen, None ) ;
739+ assert_eq ! ( held, Some ( 1 ) ) ;
740+ for position in [ point ( px ( 700. ) , px ( 151. ) ) , point ( px ( 700. ) , px ( 140. ) ) , point ( px ( 760. ) , px ( 140. ) ) ] {
741+ // The latter points are above the message, in the occluding toolbar.
742+ held = pointed_row ( position, viewport, & seen, held) ;
743+ assert_eq ! ( held, Some ( 1 ) ) ;
744+ assert_eq ! ( pointed_row( position, viewport, & seen, held) , held, "stationary redraw/press keeps the button's owner" ) ;
745+ }
746+ assert_eq ! ( pointed_row( point( px( 100. ) , px( 140. ) ) , viewport, & seen, held) , Some ( 0 ) ) ;
747+ assert_eq ! ( pointed_row( point( px( 801. ) , px( 140. ) ) , viewport, & seen, held) , None ) ;
748+ assert_eq ! ( pointed_row( point( px( 700. ) , px( -1. ) ) , viewport, & seen, held) , None ) ;
749+ }
750+
734751 #[ test]
735752 fn a_message_link_is_built_from_ids_where_the_network_has_one ( ) {
736753 let mut key = protocol:: Key { network : Network :: Discord , account : "1" . into ( ) , id : "C9" . into ( ) } ;
0 commit comments