Class: WinGui::Window

Inherits:
Object
  • Object
show all
Defined in:
lib/win_gui/window.rb

Overview

This class is a wrapper around window handle

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(handle) ⇒ Window

Returns a new instance of Window.



6
7
8
# File 'lib/win_gui/window.rb', line 6

def initialize(handle)
  @handle = handle
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

Since Window instances wrap actual window handles, they should directly support Win32 API functions manipulating these handles. Therefore, when unsupported instance method is invoked, we check if WinGui responds to such method, and if yes, call it with our window handle as a first argument. This gives us all handle-related WinGui functions as instance methods for Window instances, like so:

window.visible?

This API is much more Ruby-like compared to:

visible?(window.handle)

Of course, if we invoke WinGui function that DOESN’T accept handle as a first arg this way, we are screwed. Call such functions only like this:

WinGui.function(*args)

TODO: Such setup is problematic if WinGui is included into Window ancestor chain. TODO: In this case, all WinGui functions become available as instance methods, and method_missing never fires. TODO: It may be a better solution to explicitly define all needed instance methods, TODO: instead of showing off cool meta-programming skillz. ;-)



198
199
200
201
202
203
204
205
# File 'lib/win_gui/window.rb', line 198

def method_missing(name, *args, &block)
  if WinGui.respond_to? name
#        puts "Window #{@handle} calling: #{name} #{@handle} #{args} &#{block}"
    WinGui.send(name, @handle, *args, &block)
  else
    super
  end
end

Instance Attribute Details

#handleObject (readonly)

Returns the value of attribute handle.



10
11
12
# File 'lib/win_gui/window.rb', line 10

def handle
  @handle
end

Class Method Details

.lookup_window(opts) ⇒ Object

Looks up window handle using code specified in attached block (either with or without :timeout). Returns either Window instance (for a found handle) or nil if nothing found. Private method to dry up other window lookup methods



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/win_gui/window.rb', line 17

def lookup_window opts # :yields: index, position
  # Need this to avoid handle considered local var in begin..end block
  handle = yield
  if opts[:timeout]
    begin
      timeout(opts[:timeout]) do
        sleep SLEEP_DELAY until handle = yield
      end
    rescue Timeout::Error
      nil
    end
  end
  raise opts[:raise] if opts[:raise] && !handle
  Window.new(handle) if handle
end

.lookup_window_in_collection(opts, &collection_proc) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/win_gui/window.rb', line 33

def lookup_window_in_collection opts, &collection_proc
  class_name   = opts[:class]
  title        = opts[:title]
  id           = opts[:id]
  class_regexp = class_name.is_a? Regexp
  title_regexp = title.is_a? Regexp

  lookup_window(opts) do
    collection_proc.call.each do |handle|
      win         = Window.new handle

      id_match    = !id || win.id == id
      title_match = !title || win.title == title ||
          title_regexp && win.title =~ title
      class_match = !class_name || win.class_name == class_name ||
          class_regexp && win.class_name =~ class_name
      return win if class_match && title_match && id_match
    end
    nil
  end
end

.top_level(opts = {}) ⇒ Object Also known as: find

Finds top level window by title/class, returns wrapped Window object or nil (raises exception if asked to). If timeout option given, waits for window to appear within timeout, returns nil if it didn’t. Options:

:title

window title (String or Regexp)

:class

window class (String or Regexp)

:timeout

timeout (seconds)

:raise

raise this exception instead of returning nil if nothing found



63
64
65
66
67
68
69
# File 'lib/win_gui/window.rb', line 63

def top_level opts={}
  if opts[:class].is_a?(Regexp) || opts[:title].is_a?(Regexp)
    lookup_window_in_collection(opts) { WinGui.enum_windows }
  else
    lookup_window(opts) { WinGui.find_window opts[:class], opts[:title] }
  end
end

Instance Method Details

#child(opts = {}) ⇒ Object

Finds child window (control) by either control ID or window class/title. By default, only direct children are searched. Options:

:id

integer control id (such as IDOK, IDCANCEL, etc)

:title

window title (String or Regexp)

:class

window class (String or Regexp)

:indirect

search all descendants, not only direct children

:timeout

timeout (seconds)

:raise

raise this exception instead of returning nil if nothing found



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/win_gui/window.rb', line 84

def child(opts={})
  if opts[:indirect]
    self.class.lookup_window_in_collection(opts) { enum_child_windows }
  elsif opts[:class].is_a?(Regexp) || opts[:title].is_a?(Regexp)
    self.class.lookup_window_in_collection(opts) do
      enum_child_windows.select { |handle| child? handle }
    end
  else
    self.class.lookup_window opts do
      opts[:id] ? get_dlg_item(opts[:id]) : find_window_ex(0, opts[:class], opts[:title])
    end
  end
end

#childrenObject

Returns array of Windows that are descendants (not only DIRECT children) of a given Window



100
101
102
# File 'lib/win_gui/window.rb', line 100

def children
  enum_child_windows.map { |child_handle| Window.new child_handle }
end

#click(opts = {}) ⇒ Object

Emulates click of the control identified by opts (:id, :title, :class). Beware of keyboard shortcuts in button titles! So, use “&Yes” instead of just “Yes”. Returns screen coordinates of click point if successful, nil if control was not found

:id

integer control id (such as IDOK, IDCANCEL, etc)

:title

window title

:class

window class

:raise

raise this exception instead of returning nil if nothing found

:position/point/where

location where the click is to be applied - default :center

:mouse_button/button/which

mouse button which to click - default :right



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/win_gui/window.rb', line 114

def click(opts={})
  control = child(opts)
  if control
    left, top, right, bottom = control.get_window_rect

    where = opts[:point] || opts[:where] || opts[:position]
    point = case where
              when Array
                where # Explicit screen coords
              when :random
                [left + rand(right - left), top + rand(bottom - top)] # Random point within control window
              else
                [(left + right) / 2, (top + bottom) / 2] # Center of a control window
            end

    WinGui.set_cursor_pos *point

    button = opts[:mouse_button] || opts[:mouse] || opts[:which]
    down, up = (button == :right) ?
        [WinGui::MOUSEEVENTF_RIGHTDOWN, WinGui::MOUSEEVENTF_RIGHTUP] :
        [WinGui::MOUSEEVENTF_LEFTDOWN, WinGui::MOUSEEVENTF_LEFTUP]

    WinGui.mouse_event down, 0, 0, 0, 0
    WinGui.mouse_event up, 0, 0, 0, 0
    point
  else
    nil
  end
end

#closeObject

We alias convenience method shut_window (from Win::Gui::Window) with even more convenient

window.close

Please keep in mind that Win32 API has another function CloseWindow that merely MINIMIZES window. If you want to invoke this function, you can do it like this:

window.close_window


158
159
160
# File 'lib/win_gui/window.rb', line 158

def close
  shut_window
end

#idObject

Control ID associated with the window (only makes sense for controls)



179
180
181
# File 'lib/win_gui/window.rb', line 179

def id
  get_dlg_ctrl_id
end

#processObject Also known as: pid



172
173
174
# File 'lib/win_gui/window.rb', line 172

def process
  get_window_thread_process_id.last
end

#threadObject



168
169
170
# File 'lib/win_gui/window.rb', line 168

def thread
  get_window_thread_process_id.first
end

#titleObject

Alias for [get_]window_text



164
165
166
# File 'lib/win_gui/window.rb', line 164

def title
  get_window_text
end

#wait_for_close(timeout = CLOSE_TIMEOUT) ⇒ Object

Waits for this window to close with timeout (default CLOSE_TIMEOUT).



146
147
148
149
150
# File 'lib/win_gui/window.rb', line 146

def wait_for_close(timeout=CLOSE_TIMEOUT)
  timeout(timeout) do
    sleep SLEEP_DELAY while window_visible?
  end
end