@@ -5,13 +5,13 @@ use objc2::{
55} ;
66use objc2_core_foundation:: { CFRetained , CGPoint , CGRect , CGSize } ;
77use objc2_core_graphics:: { CGColor , CGMutablePath , CGPath } ;
8- use objc2_foundation:: { NSArray , NSNumber , NSString } ;
8+ use objc2_foundation:: { NSArray , NSNumber , ns_string } ;
99use objc2_quartz_core:: {
10- CAAnimation , CAAnimationGroup , CABasicAnimation , CAGradientLayer , CAMediaTiming ,
10+ CAAnimation , CAAnimationGroup , CABasicAnimation , CAGradientLayer , CALayer , CAMediaTiming ,
1111 CAMediaTimingFunction , CAShapeLayer , CATransaction , kCAMediaTimingFunctionEaseOut,
1212} ;
1313
14- use crate :: user_interface:: { UIDrawer , calibrated_origin} ;
14+ use crate :: user_interface:: { HintBox , UIDrawer , calibrated_origin} ;
1515
1616const RIPPLE_DURATION : f64 = 0.4 ;
1717const RIPPLE_INIT_RADIUS : f64 = 5.0 ;
@@ -22,6 +22,8 @@ const TRAIL_DURATION: f64 = 0.5;
2222const CURSOR_OFFSET_X : f64 = 9.0 ;
2323const CURSOR_OFFSET_Y : f64 = 15.0 ;
2424
25+ const HINT_FADE_OUT_DURATION : f64 = 1.0 ;
26+
2527impl UIDrawer {
2628 /// Triggers a ripple animation at the given (x, y) coordinates inside a parent CALayer.
2729 pub fn draw_ripple ( & self , x : f64 , y : f64 , color : & CFRetained < CGColor > ) {
@@ -65,15 +67,13 @@ impl UIDrawer {
6567 CATransaction :: setCompletionBlock ( Some ( & completion_block) ) ;
6668
6769 // Create the Scale Animation (Expanding)
68- let scale_anim = CABasicAnimation :: animationWithKeyPath ( Some ( & NSString :: from_str (
69- "transform.scale" ,
70- ) ) ) ;
70+ let scale_anim =
71+ CABasicAnimation :: animationWithKeyPath ( Some ( ns_string ! ( "transform.scale" ) ) ) ;
7172 scale_anim. setFromValue ( Some ( & NSNumber :: new_f64 ( 1.0 ) ) ) ;
7273 scale_anim. setToValue ( Some ( & NSNumber :: new_f64 ( RIPPLE_SCALE_FACTOR ) ) ) ; // Grows 6x the initial radius
7374
7475 // Create the Opacity Animation (Fading Out)
75- let fade_anim =
76- CABasicAnimation :: animationWithKeyPath ( Some ( & NSString :: from_str ( "opacity" ) ) ) ;
76+ let fade_anim = CABasicAnimation :: animationWithKeyPath ( Some ( ns_string ! ( "opacity" ) ) ) ;
7777 fade_anim. setFromValue ( Some ( & NSNumber :: new_f64 ( 1.0 ) ) ) ;
7878 fade_anim. setToValue ( Some ( & NSNumber :: new_f64 ( 0.0 ) ) ) ;
7979
@@ -96,7 +96,7 @@ impl UIDrawer {
9696
9797 // Bind animation to layer and add layer to window/view hierarchy
9898 // The key "ripple" can be anything unique
99- ripple_layer. addAnimation_forKey ( & anim_group, Some ( & NSString :: from_str ( "ripple" ) ) ) ;
99+ ripple_layer. addAnimation_forKey ( & anim_group, Some ( ns_string ! ( "ripple" ) ) ) ;
100100
101101 CATransaction :: commit ( ) ;
102102 }
@@ -224,7 +224,7 @@ impl UIDrawer {
224224 // At t=1, locations are [1.0, 2.0], meaning the entire range [0, 1] is transparent.
225225 // This results in the "fade front" moving from the start point to the end point.
226226 let loc_anim =
227- CABasicAnimation :: animationWithKeyPath ( Some ( & NSString :: from_str ( "locations" ) ) ) ;
227+ CABasicAnimation :: animationWithKeyPath ( Some ( ns_string ! ( "locations" ) ) ) ;
228228 let loc_start = NSArray :: from_retained_slice ( & [
229229 NSNumber :: new_f64 ( -0.2 ) ,
230230 NSNumber :: new_f64 ( 0.0 ) ,
@@ -240,7 +240,7 @@ impl UIDrawer {
240240 CAMediaTimingFunction :: functionWithName ( kCAMediaTimingFunctionEaseOut) ;
241241 loc_anim. setTimingFunction ( Some ( & timing_function) ) ;
242242
243- mask_layer. addAnimation_forKey ( & loc_anim, Some ( & NSString :: from_str ( "locations" ) ) ) ;
243+ mask_layer. addAnimation_forKey ( & loc_anim, Some ( ns_string ! ( "locations" ) ) ) ;
244244 // Set the model's locations to the final value to prevent flickering after animation
245245 mask_layer. setLocations ( Some ( & loc_end) ) ;
246246
@@ -249,3 +249,42 @@ impl UIDrawer {
249249 } ) ;
250250 }
251251}
252+
253+ impl HintBox {
254+ /// Fades the hint box out, then hides it.
255+ pub fn fade_out ( & self ) {
256+ let layers: Vec < Retained < CALayer > > = std:: iter:: once ( self . box_layer . clone ( ) )
257+ . chain ( self . frame_layer . iter ( ) . cloned ( ) )
258+ . collect ( ) ;
259+
260+ for layer in layers {
261+ // Set the model opacity to 0 before the animation so the layer
262+ // stays hidden after the animation is removed.
263+ layer. setOpacity ( 0.0 ) ;
264+
265+ CATransaction :: begin ( ) ;
266+
267+ let layer_clone = layer. clone ( ) ;
268+ let completion = RcBlock :: new ( move || {
269+ layer_clone. setHidden ( true ) ;
270+ // Restore opacity so the layer can be shown again later.
271+ layer_clone. setOpacity ( 1.0 ) ;
272+ } ) ;
273+ unsafe {
274+ CATransaction :: setCompletionBlock ( Some ( & completion) ) ;
275+
276+ let anim = CABasicAnimation :: animationWithKeyPath ( Some ( ns_string ! ( "opacity" ) ) ) ;
277+ anim. setFromValue ( Some ( & NSNumber :: new_f64 ( 1.0 ) ) ) ;
278+ anim. setToValue ( Some ( & NSNumber :: new_f64 ( 0.0 ) ) ) ;
279+ anim. setDuration ( HINT_FADE_OUT_DURATION ) ;
280+ anim. setRemovedOnCompletion ( true ) ;
281+
282+ let timing = CAMediaTimingFunction :: functionWithName ( kCAMediaTimingFunctionEaseOut) ;
283+ anim. setTimingFunction ( Some ( & timing) ) ;
284+
285+ layer. addAnimation_forKey ( & anim, Some ( ns_string ! ( "fade_out" ) ) ) ;
286+ }
287+ CATransaction :: commit ( ) ;
288+ }
289+ }
290+ }
0 commit comments