@@ -611,8 +611,23 @@ impl<'a> Node<'a> {
611
611
. map ( |description| description. to_string ( ) )
612
612
}
613
613
614
+ fn is_empty_text_input ( & self ) -> bool {
615
+ let mut text_runs = self . text_runs ( ) ;
616
+ if let Some ( first_text_run) = text_runs. next ( ) {
617
+ first_text_run
618
+ . data ( )
619
+ . value ( )
620
+ . map_or ( true , |value| value. is_empty ( ) )
621
+ && text_runs. next ( ) . is_none ( )
622
+ } else {
623
+ true
624
+ }
625
+ }
626
+
614
627
pub fn placeholder ( & self ) -> Option < & str > {
615
- self . data ( ) . placeholder ( )
628
+ self . data ( )
629
+ . placeholder ( )
630
+ . filter ( |_| self . is_text_input ( ) && self . is_empty_text_input ( ) )
616
631
}
617
632
618
633
pub fn value ( & self ) -> Option < String > {
@@ -860,7 +875,10 @@ impl<W: fmt::Write> fmt::Write for SpacePrefixingWriter<W> {
860
875
861
876
#[ cfg( test) ]
862
877
mod tests {
863
- use accesskit:: { Node , NodeId , Point , Rect , Role , Tree , TreeUpdate } ;
878
+ use accesskit:: {
879
+ Action , Node , NodeId , Point , Rect , Role , TextDirection , TextPosition , TextSelection , Tree ,
880
+ TreeUpdate ,
881
+ } ;
864
882
use alloc:: vec;
865
883
866
884
use crate :: tests:: * ;
@@ -1365,4 +1383,140 @@ mod tests {
1365
1383
tree. state( ) . node_by_id( MENU_ITEM_RADIO_ID ) . unwrap( ) . label( )
1366
1384
) ;
1367
1385
}
1386
+
1387
+ #[ test]
1388
+ fn placeholder_should_be_exposed_on_empty_text_input ( ) {
1389
+ const ROOT_ID : NodeId = NodeId ( 0 ) ;
1390
+ const TEXT_INPUT_ID : NodeId = NodeId ( 1 ) ;
1391
+ const TEXT_RUN_ID : NodeId = NodeId ( 2 ) ;
1392
+
1393
+ const PLACEHOLDER : & str = "John Doe" ;
1394
+
1395
+ let update = TreeUpdate {
1396
+ nodes : vec ! [
1397
+ ( ROOT_ID , {
1398
+ let mut node = Node :: new( Role :: Window ) ;
1399
+ node. set_children( vec![ TEXT_INPUT_ID ] ) ;
1400
+ node
1401
+ } ) ,
1402
+ ( TEXT_INPUT_ID , {
1403
+ let mut node = Node :: new( Role :: MultilineTextInput ) ;
1404
+ node. set_bounds( Rect {
1405
+ x0: 8.0 ,
1406
+ y0: 8.0 ,
1407
+ x1: 296.0 ,
1408
+ y1: 69.5 ,
1409
+ } ) ;
1410
+ node. push_child( TEXT_RUN_ID ) ;
1411
+ node. set_placeholder( PLACEHOLDER ) ;
1412
+ node. set_text_selection( TextSelection {
1413
+ anchor: TextPosition {
1414
+ node: TEXT_RUN_ID ,
1415
+ character_index: 0 ,
1416
+ } ,
1417
+ focus: TextPosition {
1418
+ node: TEXT_RUN_ID ,
1419
+ character_index: 0 ,
1420
+ } ,
1421
+ } ) ;
1422
+ node. add_action( Action :: Focus ) ;
1423
+ node
1424
+ } ) ,
1425
+ ( TEXT_RUN_ID , {
1426
+ let mut node = Node :: new( Role :: TextRun ) ;
1427
+ node. set_bounds( Rect {
1428
+ x0: 12.0 ,
1429
+ y0: 10.0 ,
1430
+ x1: 12.0 ,
1431
+ y1: 24.0 ,
1432
+ } ) ;
1433
+ node. set_value( "" ) ;
1434
+ node. set_character_lengths( [ ] ) ;
1435
+ node. set_character_positions( [ ] ) ;
1436
+ node. set_character_widths( [ ] ) ;
1437
+ node. set_word_lengths( [ 0 ] ) ;
1438
+ node. set_text_direction( TextDirection :: LeftToRight ) ;
1439
+ node
1440
+ } ) ,
1441
+ ] ,
1442
+ tree : Some ( Tree :: new ( ROOT_ID ) ) ,
1443
+ focus : TEXT_INPUT_ID ,
1444
+ } ;
1445
+ let tree = crate :: Tree :: new ( update, false ) ;
1446
+ assert_eq ! (
1447
+ Some ( PLACEHOLDER ) ,
1448
+ tree. state( )
1449
+ . node_by_id( TEXT_INPUT_ID )
1450
+ . unwrap( )
1451
+ . placeholder( )
1452
+ ) ;
1453
+ }
1454
+
1455
+ #[ test]
1456
+ fn placeholder_should_be_ignored_on_non_empty_text_input ( ) {
1457
+ const ROOT_ID : NodeId = NodeId ( 0 ) ;
1458
+ const TEXT_INPUT_ID : NodeId = NodeId ( 1 ) ;
1459
+ const TEXT_RUN_ID : NodeId = NodeId ( 2 ) ;
1460
+
1461
+ const PLACEHOLDER : & str = "John Doe" ;
1462
+
1463
+ let update = TreeUpdate {
1464
+ nodes : vec ! [
1465
+ ( ROOT_ID , {
1466
+ let mut node = Node :: new( Role :: Window ) ;
1467
+ node. set_children( vec![ TEXT_INPUT_ID ] ) ;
1468
+ node
1469
+ } ) ,
1470
+ ( TEXT_INPUT_ID , {
1471
+ let mut node = Node :: new( Role :: MultilineTextInput ) ;
1472
+ node. set_bounds( Rect {
1473
+ x0: 8.0 ,
1474
+ y0: 8.0 ,
1475
+ x1: 296.0 ,
1476
+ y1: 69.5 ,
1477
+ } ) ;
1478
+ node. push_child( TEXT_RUN_ID ) ;
1479
+ node. set_placeholder( PLACEHOLDER ) ;
1480
+ node. set_text_selection( TextSelection {
1481
+ anchor: TextPosition {
1482
+ node: TEXT_RUN_ID ,
1483
+ character_index: 1 ,
1484
+ } ,
1485
+ focus: TextPosition {
1486
+ node: TEXT_RUN_ID ,
1487
+ character_index: 1 ,
1488
+ } ,
1489
+ } ) ;
1490
+ node. add_action( Action :: Focus ) ;
1491
+ node
1492
+ } ) ,
1493
+ ( TEXT_RUN_ID , {
1494
+ let mut node = Node :: new( Role :: TextRun ) ;
1495
+ node. set_bounds( Rect {
1496
+ x0: 12.0 ,
1497
+ y0: 10.0 ,
1498
+ x1: 20.0 ,
1499
+ y1: 24.0 ,
1500
+ } ) ;
1501
+ node. set_value( "A" ) ;
1502
+ node. set_character_lengths( [ 1 ] ) ;
1503
+ node. set_character_positions( [ 0.0 ] ) ;
1504
+ node. set_character_widths( [ 8.0 ] ) ;
1505
+ node. set_word_lengths( [ 1 ] ) ;
1506
+ node. set_text_direction( TextDirection :: LeftToRight ) ;
1507
+ node
1508
+ } ) ,
1509
+ ] ,
1510
+ tree : Some ( Tree :: new ( ROOT_ID ) ) ,
1511
+ focus : TEXT_INPUT_ID ,
1512
+ } ;
1513
+ let tree = crate :: Tree :: new ( update, false ) ;
1514
+ assert_eq ! (
1515
+ None ,
1516
+ tree. state( )
1517
+ . node_by_id( TEXT_INPUT_ID )
1518
+ . unwrap( )
1519
+ . placeholder( )
1520
+ ) ;
1521
+ }
1368
1522
}
0 commit comments