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. ;-)



173
174
175
176
177
178
179
180
# File 'lib/win_gui/window.rb', line 173

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

.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

:class

window class

:timeout

timeout (seconds)

:raise

raise this exception instead of returning nil if nothing found



41
42
43
# File 'lib/win_gui/window.rb', line 41

def top_level(opts={})
  lookup_window(opts) { WinGui.find_window opts[:class], opts[:title] }
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

:class

window class

:indirect

search all descendants, not only direct children

:timeout

timeout (seconds)

:raise

raise this exception instead of returning nil if nothing found

TODO: add the ability to nail indirect children as well



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/win_gui/window.rb', line 58

def child(opts={})
  if opts[:indirect]
    self.class.lookup_window opts do
      found = children.find do |child|
        (opts[:id] ? child.id == opts[:id] : true) &&
                (opts[:class] ? child.class_name == opts[:class] : true) &&
                (opts[:title] ? child.title == opts[:title] : true)
      end
      found.handle if found
    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



77
78
79
# File 'lib/win_gui/window.rb', line 77

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



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/win_gui/window.rb', line 91

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


135
136
137
# File 'lib/win_gui/window.rb', line 135

def close
  shut_window
end

#idObject

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



154
155
156
# File 'lib/win_gui/window.rb', line 154

def id
  get_dlg_ctrl_id
end

#processObject



149
150
151
# File 'lib/win_gui/window.rb', line 149

def process
  get_window_thread_process_id.last
end

#threadObject



145
146
147
# File 'lib/win_gui/window.rb', line 145

def thread
  get_window_thread_process_id.first
end

#titleObject

Alias for [get_]window_text



141
142
143
# File 'lib/win_gui/window.rb', line 141

def title
  get_window_text
end

#wait_for_close(timeout = CLOSE_TIMEOUT) ⇒ Object

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



123
124
125
126
127
# File 'lib/win_gui/window.rb', line 123

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