fix: 修复自绘颜色行安全点击误判 (#270)
This commit is contained in:
@@ -331,9 +331,49 @@ def _non_scrollable_target_region(
|
|||||||
heading: Bounds,
|
heading: Bounds,
|
||||||
screen: Bounds,
|
screen: Bounds,
|
||||||
) -> Optional[Bounds]:
|
) -> Optional[Bounds]:
|
||||||
"""用目标卡片和下一规格标题界定非滚动颜色区域。"""
|
"""用目标卡片和下一规格标题界定非滚动颜色区域。
|
||||||
|
|
||||||
|
PDD 的部分自绘横向颜色行没有 ``scrollable`` 属性,颜色卡片会放在
|
||||||
|
一个整行可点击的父节点下。此时同一行的颜色选项可能刚好叫“款式”,
|
||||||
|
不能把它当作规格标题;优先使用该父级行作为区域,避免标题坐标误导。
|
||||||
|
"""
|
||||||
|
|
||||||
parents = _parent_map(root)
|
parents = _parent_map(root)
|
||||||
|
matching_nodes = [
|
||||||
|
(node, bounds)
|
||||||
|
for node in root.iter("node")
|
||||||
|
if _is_available(node) and _matches_target(node, target)
|
||||||
|
if (bounds := _parse_bounds(node.get("bounds", ""))) is not None
|
||||||
|
if _center_in(bounds, screen)
|
||||||
|
]
|
||||||
|
if matching_nodes:
|
||||||
|
target_node, target_bounds = max(
|
||||||
|
matching_nodes,
|
||||||
|
key=lambda item: (item[1][2] - item[1][0])
|
||||||
|
* (item[1][3] - item[1][1]),
|
||||||
|
)
|
||||||
|
target_height = target_bounds[3] - target_bounds[1]
|
||||||
|
row_candidates: list[Bounds] = []
|
||||||
|
current: Optional[ET.Element] = target_node
|
||||||
|
while current is not None:
|
||||||
|
bounds = _parse_bounds(current.get("bounds", ""))
|
||||||
|
if (
|
||||||
|
bounds is not None
|
||||||
|
and current.get("clickable") == "true"
|
||||||
|
and _is_available(current)
|
||||||
|
and _contains_bounds(bounds, target_bounds)
|
||||||
|
and bounds[2] - bounds[0] >= (screen[2] - screen[0]) * 0.60
|
||||||
|
and bounds[3] - bounds[1]
|
||||||
|
<= max(180, target_height * 3)
|
||||||
|
):
|
||||||
|
row_candidates.append(bounds)
|
||||||
|
current = parents.get(current)
|
||||||
|
if row_candidates:
|
||||||
|
return max(
|
||||||
|
row_candidates,
|
||||||
|
key=lambda item: (item[2] - item[0]) * (item[3] - item[1]),
|
||||||
|
)
|
||||||
|
|
||||||
target_bounds = [
|
target_bounds = [
|
||||||
bounds
|
bounds
|
||||||
for node in root.iter("node")
|
for node in root.iter("node")
|
||||||
|
|||||||
@@ -263,6 +263,43 @@ class StaticColorSelectionTest(unittest.TestCase):
|
|||||||
self.assertTrue(selected)
|
self.assertTrue(selected)
|
||||||
self.assertEqual(device.clicks, [(194, 1389)])
|
self.assertEqual(device.clicks, [(194, 1389)])
|
||||||
|
|
||||||
|
def test_non_scrollable_row_ignores_same_row_style_option_as_heading(self):
|
||||||
|
simplified = "纯欲莹白【透.视蕾丝衣.免脱款】"
|
||||||
|
traditional = "純欲瑩白【透.視蕾絲衣.免脫款】"
|
||||||
|
panel_xml = f'''<hierarchy>
|
||||||
|
<node bounds="[0,0][1080,2340]">
|
||||||
|
<node text="请选择:颜色 尺码" bounds="[36,950][1044,1000]" />
|
||||||
|
<node clickable="true" bounds="[0,1039][1080,1174]">
|
||||||
|
<node bounds="[36,1039][220,1174]">
|
||||||
|
<node text="{simplified}" bounds="[36,1081][216,1134]" />
|
||||||
|
</node>
|
||||||
|
<node bounds="[635,1039][1044,1174]">
|
||||||
|
<node text="款式" bounds="[639,1081][1044,1134]" />
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node text="尺码" bounds="[36,1200][126,1253]" />
|
||||||
|
</node>
|
||||||
|
</hierarchy>'''
|
||||||
|
device = StaticColorDevice(
|
||||||
|
panel_xml,
|
||||||
|
selected_text=simplified,
|
||||||
|
)
|
||||||
|
|
||||||
|
selected = select_color(
|
||||||
|
device,
|
||||||
|
panel_xml,
|
||||||
|
traditional,
|
||||||
|
action_delay=0.001,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(selected)
|
||||||
|
self.assertEqual(device.swipes, [])
|
||||||
|
self.assertEqual(device.clicks, [(126, 1107)])
|
||||||
|
self.assertEqual(
|
||||||
|
color_selection_failure_reason(panel_xml, traditional),
|
||||||
|
"selection_unconfirmed",
|
||||||
|
)
|
||||||
|
|
||||||
def test_traditional_size_matches_unique_simplified_option(self):
|
def test_traditional_size_matches_unique_simplified_option(self):
|
||||||
simplified = "均码【40.0-60.0公斤】"
|
simplified = "均码【40.0-60.0公斤】"
|
||||||
traditional = "均碼【40.0-60.0公斤】"
|
traditional = "均碼【40.0-60.0公斤】"
|
||||||
|
|||||||
Reference in New Issue
Block a user