Skip to content

Commit

Permalink
add WinGetText
Browse files Browse the repository at this point in the history
  • Loading branch information
phil294 committed Nov 12, 2022
1 parent 40c0575 commit e1ea33c
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 16 deletions.
19 changes: 10 additions & 9 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ <h2>Table of contents </h2>
<a href="#WinGetPos.htm">WinGetPos</a>
</li>
<li>
<a class="tbd" href="#WinGetText.htm">WinGetText</a>
<a href="#WinGetText.htm">WinGetText</a>
</li>
<li>
<a class="tbd" href="#WinGetTitle.htm">WinGetTitle</a>
Expand Down Expand Up @@ -2720,7 +2720,7 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
<td height="16" class="calibre4">Retrieves the position and size of a given window.</td>
</tr>
<tr class="calibre3">
<td height="16" class="tbd calibre4"><a href="#WinGetText.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinGetText</a></td>
<td height="16" class="calibre4"><a href="#WinGetText.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinGetText</a></td>
<td height="16" class="calibre4">Retrieves the text from a window.</td>
</tr>
<tr class="calibre3">
Expand Down Expand Up @@ -14284,7 +14284,6 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
</pre>
</div>
</div>
<div class="tbd">
<div class="calibreMain">
<div class="calibreEbookContent">
<a id="WinGetText.htm" href="#WinGetText.htm">#</a> <h2 class="calibre17">WinGetText</h2>
Expand All @@ -14293,7 +14292,7 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
<table cellspacing="5" width="100%" class="calibre28">
<tbody class="calibre2">
<tr class="calibre3">
<td height="48" class="calibre4">WinGetText, OutputVar [, WinTitle, WinText, ExcludeTitle, ExcludeText]</td>
<td height="48" class="calibre4">WinGetText, OutputVar [, WinTitle, <span class="tbd">WinText</span>, ExcludeTitle, <span class="tbd">ExcludeText</span>]</td>
</tr>
</tbody>
</table>
Expand All @@ -14307,7 +14306,7 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
</tr>
<tr class="calibre3">
<td class="calibre4">WinTitle</td>
<td class="calibre4">The title or partial title of the target window (the matching behavior is determined by <a href="#SetTitleMatchMode.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">SetTitleMatchMode</a>). If this and the next 3 parameters are omitted, the <a href="#LastFoundWindow.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Last Found Window</a> will be used. If this is the letter A and the next 3 parameters are omitted, the active window will be used. To use a window class (v1.0.09+), specify ahk_class ExactClassName (shown by Window Spy). To use a window's <a href="#WinGet.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">unique ID number</a>, specify ahk_id IDNumber.</td>
<td class="calibre4">The title or partial title of the target window (the matching behavior is determined by <a href="#SetTitleMatchMode.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">SetTitleMatchMode</a>). If this and the next 3 parameters are omitted, the <a href="#LastFoundWindow.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Last Found Window</a> will be used. If this is the letter A and the next 3 parameters are omitted, the active window will be used. To use a window class, specify ahk_class ExactClassName (shown by Window Spy). To use a window's <a href="#WinGet.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">unique ID number</a>, specify ahk_id IDNumber.</td>
</tr>
<tr class="calibre3">
<td class="calibre4">WinText</td>
Expand All @@ -14329,22 +14328,24 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Remarks</strong></p>
<p class="calibre8">The text retrieved is generally the same as what Window Spy shows for that window.</p>
<p class="calibre8">Each text element ends with a carriage return and linefeed (CR+LF), which can be represented in the script as `r`n</p>
<p class="calibre8">The amount of text retrieved is limited to a variable's maximum capacity (which can be changed via the <a href="#h_MaxMem.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">#MaxMem</a> directive). As a result, this command might use a large amount of RAM if the target window (e.g. an editor with a large document open) contains a large quantity of text. To avoid this, it might be possible to retrieve only portions of the window's text by using <a href="#ControlGetText.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">ControlGetText</a> instead. However, a variable's memory can be freed after use by assigning it to nothing, i.e. OutputVar =</p>
<p class="calibre8">Windows 95/98/ME may be limited to 64K for some text elements of certain windows.</p>
<p class="calibre8">Each text element ends with a <span class="rm">carriage return and</span> linefeed (<span class="rm">CR+</span>LF), which can be represented in the script as <span class="rm">`r</span>`n</p>
<p class="calibre8 tbd">The amount of text retrieved is limited to a variable's maximum capacity (which can be changed via the <a href="#h_MaxMem.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">#MaxMem</a> directive). As a result, this command might use a large amount of RAM if the target window (e.g. an editor with a large document open) contains a large quantity of text. To avoid this, it might be possible to retrieve only portions of the window's text by using <a href="#ControlGetText.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">ControlGetText</a> instead. However, a variable's memory can be freed after use by assigning it to nothing, i.e. OutputVar =</p>
<p class="calibre8 rm">Windows 95/98/ME may be limited to 64K for some text elements of certain windows.</p>
<p class="calibre8">To retrieve a list of all controls in a window, use <a href="#WinGet.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinGet</a>.</p>
<p class="calibre8">Window titles and text are always case sensitive. Hidden windows are not detected unless <a href="#DetectHiddenWindows.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">DetectHiddenWindows</a> has been turned on.</p>
<p class="calibre8 x11">at-spi children are only iterated over 1,000 children per node max (both in/visible) to prevent too long execution time. This means that for windows with lists of many elements, this command may not return all of them, even if the ones visible are but few.</p>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Related</strong></p>
<p class="calibre8"><a href="#WinGetActiveStats.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinGetActiveStats</a>, <a href="#WinGetActiveTitle.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinGetActiveTitle</a>, <a href="#WinGetTitle.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinGetTitle</a>, <a href="#ControlGetText.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">ControlGetText</a>, <a href="#WinGetPos.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinGetPos</a>, <a href="#h_MaxMem.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">#MaxMem</a></p>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Example</strong></p>
<pre class="calibre22">Run, Calc.exe
<pre class="calibre22">Run, calc
WinWait, Calculator
WinGetText, text ; The window found above will be used.
MsgBox, The text is:`n%text%</pre>
</div>
</div>
<div class="tbd">
<div class="calibreMain">
<div class="calibreEbookContent">
<a id="WinGetTitle.htm" href="#WinGetTitle.htm">#</a> <h2 class="calibre17">WinGetTitle</h2>
Expand Down
24 changes: 17 additions & 7 deletions src/run/at-spi.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module Run
return if @is_init
atspi_status = ::Atspi.init
# TODO: test this out, does it show the actual command that failed to the user?
raise Run::RuntimeException.new "Cannot access ATSPI (window control info bus). Maybe you need to install libatspi2. Init error code: #{atspi_status.to_s}" if atspi_status != 0
raise Run::RuntimeException.new "Cannot access ATSPI (window control info bus). Maybe you need to install libatspi2.0. Init error code: #{atspi_status.to_s}" if atspi_status != 0
@is_init = true
end

Expand Down Expand Up @@ -189,8 +189,9 @@ The window '#{window_name} #{app ? " is recognized but has no control children,
yield app
end
end
def each_child(accessible, *, include_hidden = false)
def each_child(accessible, *, max = nil, include_hidden = false)
accessible.child_count.times do |i|
break if max && i > max
child = accessible.child_at_index(i)
if ! include_hidden
next if hidden?(child)
Expand All @@ -203,21 +204,21 @@ The window '#{window_name} #{app ? " is recognized but has no control children,
# `false`: Continue but skip the children of this accessible, so continue on
# to the next sibling or parent;
# `nil`: Stop.
def each_descendant(accessible, *, include_hidden = false, &block : ::Atspi::Accessible, Array(Int32), String, Int32 -> Bool?)
iter_descendants(accessible, include_hidden) do |desc, path, class_NN, nest_level|
def each_descendant(accessible, *, include_hidden = false, max_children = nil, &block : ::Atspi::Accessible, Array(Int32), String, Int32 -> Bool?)
iter_descendants(accessible, max_children, include_hidden) do |desc, path, class_NN, nest_level|
block.call desc, path, class_NN, nest_level
end
end
private def iter_descendants(accessible, include_hidden, nest_level = 0, path = [] of Int32, &block : ::Atspi::Accessible, Array(Int32), String, Int32 -> Bool?)
private def iter_descendants(accessible, max_children, include_hidden, nest_level = 0, path = [] of Int32, &block : ::Atspi::Accessible, Array(Int32), String, Int32 -> Bool?)
# Elements would actually expose a `.accessibility_id` property, but it's
# usually empty :-( So we forge an artificial, unique path for each element and
# just pretend it's an actual ahk-like ClassNN: e.g. `push_button_0_1_0`
class_NN = to_class_NN(path, accessible.role_name)
response = yield accessible, path, class_NN, nest_level
return nil if response == nil
if response
each_child(accessible, include_hidden: include_hidden) do |child, i|
response = iter_descendants(child, include_hidden, nest_level + 1, path + [i], &block)
each_child(accessible, max: max_children, include_hidden: include_hidden) do |child, i|
response = iter_descendants(child, max_children, include_hidden, nest_level + 1, path + [i], &block)
break if response == nil
end
end
Expand Down Expand Up @@ -266,6 +267,15 @@ The window '#{window_name} #{app ? " is recognized but has no control children,
text = text.gsub('', "").strip()
text.empty? ? nil : text
end
def get_all_texts(accessible, *, include_hidden, max_children)
strings = [] of ::String
each_descendant(accessible, include_hidden: include_hidden, max_children: max_children) do |descendant, class_NN|
text = get_text(descendant)
strings << text if text
true
end
strings
end
# Less of an action click, more of a general "interact" in the best possible compatible way.
# Find the best selection or action, going upwards the parent chain if necessary.
# This logic is necessary because apparently an action name can be *anything* but we
Expand Down

0 comments on commit e1ea33c

Please sign in to comment.