Coverage for pyWebLayout/core/query.py: 94%
33 statements
« prev ^ index » next coverage.py v7.11.2, created at 2025-11-12 12:02 +0000
« prev ^ index » next coverage.py v7.11.2, created at 2025-11-12 12:02 +0000
1"""
2Query system for pixel-to-content mapping.
4This module provides data structures for querying rendered content,
5enabling interactive features like link clicking, word definition lookup,
6and text selection.
7"""
9from __future__ import annotations
10from dataclasses import dataclass
11from typing import Optional, Tuple, List, Any, TYPE_CHECKING
13if TYPE_CHECKING: 13 ↛ 14line 13 didn't jump to line 14 because the condition on line 13 was never true
14 from pyWebLayout.core.base import Queriable
17@dataclass
18class QueryResult:
19 """
20 Result of querying a point on a rendered page.
22 This encapsulates all information about what was found at a pixel location,
23 including geometry, content, and interaction capabilities.
24 """
25 # What was found
26 object: 'Queriable' # The object at this point
27 object_type: str # "link", "text", "image", "button", "word", "empty"
29 # Geometry
30 bounds: Tuple[int, int, int, int] # (x, y, width, height) in page coordinates
32 # Content (for text/words)
33 text: Optional[str] = None
34 word_index: Optional[int] = None # Index in abstract document structure
35 block_index: Optional[int] = None # Block index in document
37 # Interaction (for links/buttons)
38 is_interactive: bool = False
39 link_target: Optional[str] = None # URL or internal reference
40 callback: Optional[Any] = None # Interaction callback
42 # Hierarchy (for debugging/traversal)
43 parent_line: Optional[Any] = None
44 parent_page: Optional[Any] = None
46 def to_dict(self) -> dict:
47 """Convert to dictionary for serialization"""
48 return {
49 'object_type': self.object_type,
50 'bounds': self.bounds,
51 'text': self.text,
52 'is_interactive': self.is_interactive,
53 'link_target': self.link_target,
54 'word_index': self.word_index,
55 'block_index': self.block_index
56 }
59@dataclass
60class SelectionRange:
61 """
62 Represents a range of selected text between two points.
63 """
64 start_point: Tuple[int, int]
65 end_point: Tuple[int, int]
66 results: List[QueryResult] # All query results in the range
68 @property
69 def text(self) -> str:
70 """Get concatenated text from all results"""
71 return " ".join(r.text for r in self.results if r.text)
73 @property
74 def bounds_list(self) -> List[Tuple[int, int, int, int]]:
75 """Get list of all bounding boxes for highlighting"""
76 return [r.bounds for r in self.results]
78 def to_dict(self) -> dict:
79 """Convert to dictionary for serialization"""
80 return {
81 'start': self.start_point,
82 'end': self.end_point,
83 'text': self.text,
84 'word_count': len(self.results),
85 'bounds': self.bounds_list
86 }