From 7a5247eef9b7aef40c167ae3f0c84d1017757b02 Mon Sep 17 00:00:00 2001 From: Bogdan Condurache Date: Wed, 3 May 2023 12:11:43 +0300 Subject: [PATCH] Implement selectTextRange, setCaretPosition, getCaretLocation --- README.md | 2 +- src/JABWrapper/context_tree.py | 42 ++++++++++++++++++++ src/JABWrapper/jab_wrapper.py | 72 ++++++++++++++++++++++++++++++++-- tests/test.py | 23 ++++++++++- 4 files changed, 133 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6164259..9eefd88 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Python wrapper around the Java Access Bridge / Windows Access Bridge. Enable the Java Access Bridge in windows - C:\path\to\java\bin\jabswitch -enable + C:\path\to\java\bin\jabswitch /enable # Install diff --git a/src/JABWrapper/context_tree.py b/src/JABWrapper/context_tree.py index 8da957f..8618e3a 100644 --- a/src/JABWrapper/context_tree.py +++ b/src/JABWrapper/context_tree.py @@ -240,6 +240,48 @@ def get_visible_children(self) -> List: visible_child = ContextNode(self._jab_wrapper, found_visible_children.children[i], self._lock, self.ancestry + 1, False) visible_children.append(visible_child) return visible_children + + def set_caret_position(self, position): + """ + Set the caret to the given position + + Args: + position: int representing the index where to set the caret. + + Raises: + APIException: Failed to set the caret. + """ + self._jab_wrapper.set_caret_position(self.context, position) + + def get_caret_position(self, index): + """ + Get the coordinates of the current caret location. External method uses `position` for consistency + instead of `location`. + + Args: + context: the element context handle. + index: TODO + + Returns: + An AccessibleTextRectInfo object. + + Raises: + APIException: failed to call the java access bridge API with attributes or failed to get location. + """ + return self._jab_wrapper.get_caret_location(self.context, index) + + def select_text_range(self, start_index: int, end_index: int): + """ + Select text within given range + + Args: + start_index: int for start of selection as index. + end_index: int for end of selection as index. + + Raises: + APIException: failed to call the java access bridge API with attributes or failed to make selection. + """ + return self._jab_wrapper.select_text_range(self.context, start_index, end_index) class ContextTree: diff --git a/src/JABWrapper/jab_wrapper.py b/src/JABWrapper/jab_wrapper.py index 1d5d2b4..785efa0 100644 --- a/src/JABWrapper/jab_wrapper.py +++ b/src/JABWrapper/jab_wrapper.py @@ -395,7 +395,9 @@ def _define_functions(self) -> None: # BOOL requestFocus(long vmID, AccessibleContext context) self._wab.requestFocus.argtypes = [c_long, JavaObject] self._wab.requestFocus.restypes = wintypes.BOOL - # TODO: selectTextRange + # BOOL selectTextRangeFP (long vmID, AccessibleContext accessibleContext, int startIndex, int endIndex) + self._wab.selectTextRange.argtypes = [c_long, JavaObject, c_int, c_int] + self._wab.selectTextRange.restype = wintypes.BOOL # TODO: getTextAttributesInRange # int getVisibleChildrenCount(long vmID, AccessibleContext context) self._wab.getVisibleChildrenCount.argtypes = [c_long, JavaObject] @@ -403,8 +405,13 @@ def _define_functions(self) -> None: # BOOL getVisibleChildren(long vmID, AccessibleContext context, int startIndex, VisibleChildrenInfo *visibleChilderInfo) self._wab.getVisibleChildren.argtypes = [c_long, JavaObject, c_int, POINTER(VisibleChildrenInfo)] self._wab.getVisibleChildren.restype = wintypes.BOOL - # TODO: setCaretPosition - # TODO: getCaretLocation + # BOOL setCaretPositionFP (long vmID, AccessibleContext accessibleContext, int position) + self._wab.setCaretPosition.argtypes = [c_long, JavaObject, c_int] + self._wab.setCaretPosition.restype = wintypes.BOOL + # TODO: Bogdan getCaretLocation + # BOOL getCaretLocationFP (long vmID, AccessibleContext ac, AccessibleTextRectInfo *rectInfo, int index); + self._wab.getCaretLocation.argtypes = [c_long, JavaObject, POINTER(AccessibleTextRectInfo), c_int] + self._wab.getCaretLocation.restype = wintypes.BOOL # TODO: getEventsWaitingFP def _define_callbacks(self) -> None: @@ -1709,6 +1716,26 @@ def get_virtual_accessible_name(self, context: JavaObject) -> str: if not ok: raise APIException("Failed to get virtual accessible name") return buf.value + + def select_text_range(self, context: JavaObject, start_index: int, end_index: int) -> bool: + """ + Select text within given range + + Args: + context: the element context handle. + start_index: int for start of selection as index. + end_index: int for end of selection as index. + + Returns: + bool: should always be True if operation was successful. + + Raises: + APIException: failed to call the java access bridge API with attributes or failed to make selection. + """ + ok = self._wab.selectTextRange(self._vmID, context, start_index, end_index) + if not ok: + raise APIException("Failed to select text") + return ok def get_visible_children_count(self, context: JavaObject) -> int: return self._wab.getVisibleChildrenCount(self._vmID, context) @@ -1719,6 +1746,45 @@ def get_visible_children(self, context: JavaObject, start_index: int) -> Visible if not ok: raise APIException('Failed to get visible children info') return visible_children + + def set_caret_position(self, context: JavaObject, position: int) -> bool: + """ + Set the caret to the given position + + Args: + context: the element context handle. + position: int representing the index where to set the caret. + + Returns: + bool: should always be True if operation was successful. + + Raises: + APIException: failed to call the java access bridge API with attributes or failed to set the caret. + """ + ok = self._wab.setCaretPosition(self._vmID, context, position) + if not ok: + raise APIException("Failed to select text") + return ok + + def get_caret_location(self, context: JavaObject, index: int) -> AccessibleTextRectInfo: + """ + Get the coordinates of the current caret location. + + Args: + context: the element context handle. + index: TODO + + Returns: + An AccessibleTextRectInfo object. + + Raises: + APIException: failed to call the java access bridge API with attributes or failed to get location. + """ + text_rect_info = AccessibleTextRectInfo() + ok = self._wab.getCaretLocation(self._vmID, context, byref(text_rect_info), index) + if not ok: + raise APIException("Failed to select text") + return text_rect_info def register_callback(self, name: str, callback: Callable[[JavaObject], None]) -> None: """ diff --git a/tests/test.py b/tests/test.py index 5330d7c..28c8bd9 100644 --- a/tests/test.py +++ b/tests/test.py @@ -135,6 +135,24 @@ def type_text_into_text_field(context_info_tree) -> ContextNode: wait_until_text_contains(text_area, text) return text_area +def set_caret_position(context_info_tree): + input_area = context_info_tree.get_by_attrs([SearchElement("role", "text")])[0] + logging.info(input_area.get_actions()) + input_area.set_caret_position(3) + logging.info("set_caret_position") + time.sleep(5) + +def get_caret_position(context_info_tree): + input_area = context_info_tree.get_by_attrs([SearchElement("role", "text")])[1] + pos = input_area.get_caret_position(0) + logging.info(f"get_caret_position: {pos.__dict__}") + time.sleep(5) + +def select_text_range(context_info_tree): + input_area = context_info_tree.get_by_attrs([SearchElement("role", "text")])[1] + input_area.select_text_range(1, 3) + logging.info(f"select_text_range") + time.sleep(5) def set_focus(context_info_tree): # Set focus to main frame @@ -227,6 +245,9 @@ def run_app_tests(jab_wrapper, window_id): context_info_tree = parse_elements(jab_wrapper) set_focus(context_info_tree) text_area = type_text_into_text_field(context_info_tree) + set_caret_position(context_info_tree) + get_caret_position(context_info_tree) + select_text_range(context_info_tree) click_send_button(context_info_tree, text_area) click_clear_button(context_info_tree, text_area) verify_table_content(context_info_tree) @@ -257,8 +278,6 @@ def main(): title = windows[0].title assert title == "Foo bar", f"Invalid window found={title}" run_app_tests(jab_wrapper, windows[0].pid) - except Exception as e: - logging.error(f"error={type(e)} - {e}") finally: logging.info("Shutting down JAB wrapper") if jab_wrapper: