Skip to content

Commit 1764cef

Browse files
authored
fix: Only expose the placeholder property on empty text inputs (#607)
1 parent b2c07d6 commit 1764cef

File tree

2 files changed

+157
-3
lines changed

2 files changed

+157
-3
lines changed

consumer/src/node.rs

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,23 @@ impl<'a> Node<'a> {
611611
.map(|description| description.to_string())
612612
}
613613

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+
614627
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())
616631
}
617632

618633
pub fn value(&self) -> Option<String> {
@@ -860,7 +875,10 @@ impl<W: fmt::Write> fmt::Write for SpacePrefixingWriter<W> {
860875

861876
#[cfg(test)]
862877
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+
};
864882
use alloc::vec;
865883

866884
use crate::tests::*;
@@ -1365,4 +1383,140 @@ mod tests {
13651383
tree.state().node_by_id(MENU_ITEM_RADIO_ID).unwrap().label()
13661384
);
13671385
}
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+
}
13681522
}

consumer/src/text.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ fn character_index_at_point(node: &Node, point: Point) -> usize {
868868
}
869869

870870
impl<'a> Node<'a> {
871-
fn text_runs(
871+
pub(crate) fn text_runs(
872872
&self,
873873
) -> impl DoubleEndedIterator<Item = Node<'a>> + FusedIterator<Item = Node<'a>> + 'a {
874874
let id = self.id();

0 commit comments

Comments
 (0)