@@ -21,6 +21,8 @@ pub struct Config {
2121 pub speed : usize ,
2222 pub grid_size : usize ,
2323 pub block_interval_ms : u64 ,
24+ pub auto_select_enabled : bool ,
25+ pub auto_select_delay_ms : u64 ,
2426 pub select_key : String ,
2527 pub next_key : String ,
2628 pub back_key : String ,
@@ -35,6 +37,8 @@ impl Default for Config {
3537 speed : 2 ,
3638 grid_size : 4 ,
3739 block_interval_ms : 1000 ,
40+ auto_select_enabled : false ,
41+ auto_select_delay_ms : 1000 ,
3842 select_key : "Space" . into ( ) ,
3943 next_key : "Enter" . into ( ) ,
4044 back_key : "Backspace" . into ( ) ,
@@ -59,6 +63,8 @@ impl Config {
5963 speed : self . speed ,
6064 grid_size : self . grid_size ,
6165 block_interval_ms : self . block_interval_ms ,
66+ auto_select_enabled : self . auto_select_enabled ,
67+ auto_select_delay_ms : self . auto_select_delay_ms ,
6268 }
6369 }
6470 pub fn validate ( & self ) -> Result < ( ) , String > {
@@ -73,12 +79,16 @@ pub struct PointSettings {
7379 pub speed : usize ,
7480 pub grid_size : usize ,
7581 pub block_interval_ms : u64 ,
82+ pub auto_select_enabled : bool ,
83+ pub auto_select_delay_ms : u64 ,
7684}
7785impl PointSettings {
7886 fn validate ( & self ) -> Result < ( ) , String > {
7987 if self . speed > 4
8088 || !( 2 ..=10 ) . contains ( & self . grid_size )
8189 || !( 250 ..=5000 ) . contains ( & self . block_interval_ms )
90+ || !( 100 ..=100_000 ) . contains ( & self . auto_select_delay_ms )
91+ || !self . auto_select_delay_ms . is_multiple_of ( 100 )
8292 {
8393 return Err ( "Point scan speed, grid size, or interval is invalid." . into ( ) ) ;
8494 }
@@ -412,6 +422,7 @@ impl Technique for Engine {
412422 grid,
413423 strips,
414424 tiles : vec ! [ ] ,
425+ countdown : None ,
415426 label : ( self . phase == Phase :: RowEscape ) . then ( || {
416427 let scale = self . units_per_logical_pixel ;
417428 let width = ( 360.0 * scale) . min ( self . screen . width ) ;
@@ -471,6 +482,28 @@ mod tests {
471482 } )
472483 }
473484 #[ test]
485+ fn auto_select_delays_are_bounded_and_round_trip ( ) {
486+ for delay in [ 100 , 500 , 1000 , 100_000 ] {
487+ let config = Config {
488+ auto_select_enabled : true ,
489+ auto_select_delay_ms : delay,
490+ ..Config :: default ( )
491+ } ;
492+ config. validate ( ) . unwrap ( ) ;
493+ let restored: Config =
494+ serde_json:: from_value ( serde_json:: to_value ( & config) . unwrap ( ) ) . unwrap ( ) ;
495+ assert_eq ! ( restored, config) ;
496+ }
497+ for delay in [ 0 , 99 , 101 , 100_100 , u64:: MAX ] {
498+ assert ! ( Config {
499+ auto_select_delay_ms: delay,
500+ ..Config :: default ( )
501+ }
502+ . validate( )
503+ . is_err( ) ) ;
504+ }
505+ }
506+ #[ test]
474507 fn grid_highlights_fill_only_the_current_target_then_clear_for_lines ( ) {
475508 for scale in [ 1.0 , 2.0 ] {
476509 let config = Config {
@@ -835,11 +868,15 @@ mod tests {
835868 assert ! ( e. technique. x > -1000.0 ) ;
836869 }
837870 #[ test]
838- fn existing_flat_settings_round_trip_without_schema_changes ( ) {
871+ fn existing_flat_settings_gain_safe_auto_select_defaults ( ) {
839872 let mut json = serde_json:: json!( { "mode" : "grid" , "automatic" : false , "speed" : 4 , "gridSize" : 7 , "blockIntervalMs" : 1500 , "selectKey" : "F1" , "nextKey" : "F2" , "backKey" : "F3" , "pauseKey" : "F4" } ) ;
840873 let config: Config = serde_json:: from_value ( json. clone ( ) ) . unwrap ( ) ;
841874 config. validate ( ) . unwrap ( ) ;
842875 assert_eq ! ( config. scanner_color, crate :: scanning:: ScannerColor :: Blue ) ;
876+ assert ! ( !config. auto_select_enabled) ;
877+ assert_eq ! ( config. auto_select_delay_ms, 1000 ) ;
878+ json[ "autoSelectEnabled" ] = serde_json:: json!( false ) ;
879+ json[ "autoSelectDelayMs" ] = serde_json:: json!( 1000 ) ;
843880 json[ "scannerColor" ] = serde_json:: json!( "blue" ) ;
844881 assert_eq ! ( serde_json:: to_value( & config) . unwrap( ) , json) ;
845882 assert ! ( !config. switches( ) . automatic) ;
0 commit comments