Class: TT::GUI::Window

Inherits:
WebDialogPatch show all
Includes:
ContainerElement
Defined in:
TT_Lib2/window.rb

Overview

Note:

Alpha stage. Very likely to be subject to change!

Examples:

w = TT::GUI::Window.new
w.show_window

Since:

  • 2.4.0

Direct Known Subclasses

ToolWindow

Constant Summary

THEME_DEFAULT =

Since:

  • 2.4.0

'window.html'.freeze
THEME_GRAPHITE =

Since:

  • 2.4.0

'window_graphite.html'.freeze
EVENT_WINDOW_READY =

Called when the HTML DOM is ready.

Since:

  • 2.4.0

proc { |window, params|
  TT.debug( '>> Dialog Ready' )
  window.add_controls_to_webdialog()
  window.trigger_DOM_ready() # 2.7.0
}.freeze
EVENT_CALLBACK =

Called when a control triggers an event. params possibilities:

"<ui_id>||<event>"
"<ui_id>||<event>||arg1,arg2,arg3"

Since:

  • 2.5.0

proc { |window, params|
  TT.debug( '>> Event Callback' )
  TT.debug( params )
  begin
    ui_id, event_str, args_str = params.split('||')
    event = event_str.intern
    # Catch Debug Console callbacks
    return TT.debug( args_str ) if ui_id == 'Console'
    # Process Control
    control = window.get_control_by_ui_id(ui_id)
    if control
      if args_str
        args = args_str.split(',')
        control.call_event( event, args )
      else
        control.call_event( event )
      end
    end
  ensure
    # Pump next message.
    window.call_script( 'Bridge.pump_message' )
  end
}.freeze
EVENT_OPEN_URL =

Called when a URL link is clicked.

Since:

  • 2.7.0

proc { |window, params|
  TT.debug( '>> Open URL' )
  UI.openURL( params )
}.freeze

Instance Attribute Summary collapse

Attributes included from ContainerElement

#controls

Instance Method Summary collapse

Methods included from ContainerElement

#add_control, #get_control_by_name, #get_control_by_ui_id, #release!, #remove_control

Methods inherited from WebDialogPatch

#set_file, #set_html, #set_url

Constructor Details

#initialize(title, scrollable, pref_key, width, height, left, top, resizable) ⇒ Window #initialize(hash) ⇒ Window

Note:

This method is currently not compatible with SketchUp 6 and older.

In addition to the hash keys supported by WebDialog.new, there are additional keys availible:

  • :title alias for :dialog_title

  • :pref_key alias for :preferences_key

Overloads:

  • #initialize(title, scrollable, pref_key, width, height, left, top, resizable) ⇒ Window

    Parameters:

    • title (optional, String)
    • scrollable (optional, Boolean)
    • pref_key (optional, String)
    • width (optional, Integer)
    • height (optional, Integer)
    • left (optional, Integer)
    • top (optional, Integer)
    • resizable (optional, Boolean)
  • #initialize(hash) ⇒ Window

    Parameters:

    • hash (optional, Hash)

    Options Hash (hash):

    • :dialog_title (String)
    • :scrollable (Boolean)
    • :preferences_key (String)
    • :width (Integer)
    • :height (Integer)
    • :left (Integer)
    • :top (Integer)
    • :resizable (Boolean)
    • :min_width (Boolean)
    • :min_height (Boolean)
    • :max_width (Boolean)
    • :max_height (Boolean)
    • :mac_only_use_nswindow (Boolean)

Since:

  • 2.4.0



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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'TT_Lib2/window.rb', line 116

def initialize(*args)
  # WebDialog.new arguments:
  #
  # title, scrollable, pref_key, width, height, left, top, resizable
  #   0         1          2       3       4      5    6       7
  #
  #
  # WebDialog.new hash keys:
  #
  # :dialog_title
  # :scrollable
  # :preferences_key
  # :width
  # :height
  # :left
  # :top
  # :resizable
  # :mac_only_use_nswindow
  
  @theme = THEME_DEFAULT
  
  @window = self # Required by ContainerElement
  
  @event_DOM_ready = nil
  
  # Default properties
  # (!) Add theme
  @props = {
    :title      => 'Untitled Window',
    :scripts    => [],
    :styles     => [],
    :scrollable => false,
    :resizable  => true,
    :left       => 250,
    :top        => 350,
    :width      => 200,
    :height     => 300
  }
  
  # Process the arguments.
  if args.length == 1 && args[0].is_a?(Hash)
    # Hash arguments
    options = args[0]
    # Syncronize aliased keys. (i) Getting messy. Avoid this.
    options[:dialog_title]    = options[:title] if options.key?(:title)
    options[:title]           = options[:dialog_title] if options.key?(:dialog_title)
    options[:preferences_key] = options[:pref_key] if options.key?(:pref_key)
    options[:pref_key]        = options[:preferences_key] if options.key?(:preferences_key)
    [
      :title, :dialog_title,
      :pref_key, :preferences_key,
      :resizable,
      :scrollable,
      :width,
      :height,
      :left,
      :top
    ].each { |key|
      @props[key] = options[key] if options.key?( key )
    }
  else
    # Classic arguments.
    [
      :title,           # 0
      :scrollable,      # 1
      :preferences_key, # 2
      :width,           # 3
      :height,          # 4
      :left,            # 5
      :top,             # 6
      :resizable        # 7
    ].each_with_index { |key, index|
      break unless args.length > index
      next if args[index].nil?
      @props[key] = args[index] #if args.length > index
    }
  end
  
  # Alias keys.
  @props[:dialog_title]     = @props[:title] if @props.key?(:title)
  @props[:preferences_key]  = @props[:pref_key] if @props.key?(:pref_key)
  
  # Init the real WebDialog
  #
  # (!) It appears that when using a hash to init the webdialog and one 
  # supplies a preference key to store it's position and size only the 
  # registry section is created, but the size and position is not stored.
  # Windows - SU8M1, SU7.1
  #
  # (!) UPDATE: Preferences work if one destroy the WebDialog instance and
  # create a new one before using Webdialog.show.
  #
  # (!) In versions earlier than SU8(M1?), any nil in arguments would stop
  # the processing of the remaining arguments.
  #
  # (!) If left and Top is not spesified the WebDialog will appear in the
  # upper left corner of the screen.
  #
  # In order to work around all these issues it's best to not use a hash,
  # but instead use all arguments with decent default values.
  if @props.key?( :preferences_key )
    # When preferences are saved, used classic arguments as they are not
    # saved when using a hash. ( SU8.0M1, SU7.1 )
    title       = @props[:dialog_title]
    scrollable  = @props[:scrollable]
    pref_key    = @props[:preferences_key]
    width       = @props[:width]
    height      = @props[:height]
    left        = @props[:left]
    top         = @props[:top]
    resizable   = @props[:resizable]
    super( title, scrollable, pref_key, width, height, left, top, resizable )
    min_width  = @props[:min_width]  if @props.key?( :min_width )
    min_height = @props[:min_height] if @props.key?( :min_height )
    max_width  = @props[:max_width]  if @props.key?( :max_width )
    max_height = @props[:max_height] if @props.key?( :max_height )
  else
    # When preferences are not saved, use a hash because in SU prior to
    # SU8.0M1 processing of arguments would stop after a nil. So if one
    # wants to skip the preference argument one need to use the hash.
    # (!) Not compatible with SU6.
    super( @props )
  end
  
  # (!) Remember window positions. SU only remembers them between each session.
  
  # Ensure the size for fixed windows is set - and not read from the last state.
  if @props.key?(:width) && @props.key?(:height) && !@props[:resizable]
    set_size(@props[:width], @props[:height])
  end

  # Turn of the navigation buttons by default.
  if respond_to?( :navigation_buttons_enabled )
    navigation_buttons_enabled = false
  end
  
  # Set HTML file with the core HTML, CSS and JS required.
  # (?) Is this not redundant since self.set_html is used in .show_window?
  # As noted in The Lost Manual, onload will trigger under OSX when .set_file
  # or .set_html is used.
  #self.set_file(TT::Lib::path + '/webdialog/window.html')
  
  # (i) If procs are created in the initalize method for #add_action_callback
  #     then the WebDialog instance will not GC.
  
  add_action_callback( 'Window_Ready', &EVENT_WINDOW_READY )
  add_action_callback( 'Event_Callback', &EVENT_CALLBACK )
  add_action_callback( 'Open_URL', &EVENT_OPEN_URL )

end

Instance Attribute Details

#parentObject

Since:

  • 2.4.0



80
81
82
# File 'TT_Lib2/window.rb', line 80

def parent
  @parent
end

#themeObject

Since:

  • 2.6.0



78
79
80
# File 'TT_Lib2/window.rb', line 78

def theme
  @theme
end

#windowObject

Since:

  • 2.4.0



80
81
82
# File 'TT_Lib2/window.rb', line 80

def window
  @window
end

Instance Method Details

#add_script(file) ⇒ String

Note:

All local paths should be processed with #local_path to ensure compatibility between platforms.

Parameters:

  • file (String)

Returns:

  • (String)

Since:

  • 2.4.0



282
283
284
285
# File 'TT_Lib2/window.rb', line 282

def add_script( file )
  @props[:scripts] << file
  file
end

#add_style(file) ⇒ String

Note:

All local paths should be processed with #local_path to ensure compatibility between platforms.

Parameters:

  • file (String)

Returns:

  • (String)

Since:

  • 2.4.0



294
295
296
297
# File 'TT_Lib2/window.rb', line 294

def add_style( file )
  @props[:styles] << file
  file
end

#call_script(function, *args) ⇒ Mixed

Wrapper to build a script string and return the return value of the called Javascript.

This method also ensures a that the <SCRIPT> elements which UI::WebDialog.execute_script leaves behind is cleaned up.

return_value = window.call_script('alert', 'Hello World')

Parameters:

  • function (String)

    Name of JavaScript function to call.

Returns:

  • (Mixed)

Since:

  • 2.5.0



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'TT_Lib2/window.rb', line 429

def call_script(function, *args)
  # Ensure that we don't pull old data back from the WebDialog if it should fail.
  execute_script( 'Bridge.reset()' )
  # (!) SU-0415
  # Reports of .execute_script might have a hard limit - possibly under OSX only.
  # Windows does seem unaffected. 
  # Test case:
  #  w.execute_script("alert('#{'x'*10000000}'.length);")
  arguments = args.map { |arg| TT::Javascript.to_js(arg) }.join(',')
  javascript = "#{function}(#{arguments});".inspect
  # If WebDialog is not visible, or no HTML is populated (lacking DOM) then
  # .execute_script returns false.
  #
  # (i) OSX - SU6
  # http://forums.sketchucation.com/viewtopic.php?f=180&t=8316#p49259
  # Indicates that ; might cause the call to fail. Seems to work without,
  # so keeping it like that to be on the safe size.
  # puts "Bridge.execute(#{javascript})" #DEBUG
  if not execute_script( "Bridge.execute(#{javascript})" )
    raise "Script could not be executed. Was window visible? (#{visible?})"
  end
  # (?) Catch JavaScript errors? Or just let the WebDialog display the error?
  raw_data = get_element_value('RUBY_bridge');
  # The JS Bridge converts the JS values into Ruby code strings.
  # (?) Catch exceptions? Re-raise with custom exception?
  eval( raw_data )
end

#get_checkbox_state(ui_id) ⇒ String

Returns the checked state for the given jQuery selector.

Parameters:

  • ui_id (String)

    ID to a Control.ui_id

Returns:

  • (String)

    Returns the checked state for the given jQuery selector.

Since:

  • 2.7.0



318
319
320
# File 'TT_Lib2/window.rb', line 318

def get_checkbox_state( ui_id )
  call_script( 'Webdialog.get_checkbox_state', ui_id )
end

#get_checked_state(selector) ⇒ String

Returns the checked state for the given jQuery selector.

Parameters:

  • selector (String)

    jQuery selector

Returns:

  • (String)

    Returns the checked state for the given jQuery selector.

Since:

  • 2.7.0



326
327
328
# File 'TT_Lib2/window.rb', line 326

def get_checked_state( selector )
  call_script( 'Webdialog.get_checked_state', selector )
end

#get_client_sizeArray<Integer,Integer>

Returns an array with the width and height of the client area.

Returns:

  • (Array<Integer,Integer>)

Since:

  • 2.5.0



369
370
371
# File 'TT_Lib2/window.rb', line 369

def get_client_size
  call_script( 'Webdialog.get_client_size' )
end

#get_control_value(ui_id) ⇒ String

Returns the value for the given Control.

Parameters:

  • ui_id (String)

    Control.ui_id

Returns:

  • (String)

    Returns the value for the given Control.

Since:

  • 2.5.0



361
362
363
# File 'TT_Lib2/window.rb', line 361

def get_control_value( ui_id )
  call_script( 'Webdialog.get_value', "##{ui_id}" )
end

#get_html(selector) ⇒ String

Returns the HTML code for the given jQuery selector.

Parameters:

  • selector (String)

    jQuery selector

Returns:

  • (String)

    Returns the HTML code for the given jQuery selector.

Since:

  • 2.5.0



342
343
344
# File 'TT_Lib2/window.rb', line 342

def get_html(selector)
  call_script( 'Webdialog.get_html', selector )
end

#get_text(selector) ⇒ String

Returns the text content for the given jQuery selector.

Parameters:

  • selector (String)

    jQuery selector

Returns:

  • (String)

    Returns the text content for the given jQuery selector.

Since:

  • 2.5.0



334
335
336
# File 'TT_Lib2/window.rb', line 334

def get_text(selector)
  call_script( 'Webdialog.get_text', selector )
end

#get_value(selector) ⇒ String

It appear that under OSX UI::WebDialog.get_element_value doesn't work for <TEXTAREA> and <SELECT> elements. Using this instead solves the issue.

Parameters:

  • selector (String)

    jQuery selector

Returns:

  • (String)

    Returns the value for the given jQuery selector.

Since:

  • 2.5.0



353
354
355
# File 'TT_Lib2/window.rb', line 353

def get_value(selector)
  call_script( 'Webdialog.get_value', selector )
end

#inspectObject

Since:

  • 2.6.0



512
513
514
# File 'TT_Lib2/window.rb', line 512

def inspect
  %&<#{self.class}:#{TT.object_id_hex(self)} "#{@props[:title]}">&
end

#local_path(path) ⇒ String

Local paths must be prefixed with file:/// under OSX for set_html to work. It probably is the correct way to do so anyway.

Parameters:

  • path (String)

Returns:

  • (String)

Since:

  • 2.5.1



503
504
505
506
507
508
509
# File 'TT_Lib2/window.rb', line 503

def local_path( path )
  expanded_path = File.expand_path( path )
  match = expanded_path.match(/^(\/*)/)
  size = (match) ? match[1].size : 0
  prefix = '/' * (3 - size)
  "file:#{prefix}#{expanded_path}"
end

#on_ready {|window| ... } ⇒ Object

Event callback for when the HTML DOM is ready.

Yields:

  • (window)

    Return the window object where the DOM is ready.

Since:

  • 2.7.0



378
379
380
381
# File 'TT_Lib2/window.rb', line 378

def on_ready( &block )
  # (?) Allow more than one event handler?
  @event_DOM_ready = block
end

#set_client_size(width, height) ⇒ Boolean

Adjusts the window so the client area fits the given width and height.

Parameters:

  • width (Integer)
  • height (Integer)

Returns:

  • (Boolean)

    Returns false if the size can't be set.

Since:

  • 2.5.0



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'TT_Lib2/window.rb', line 399

def set_client_size(width, height)
  unless visible?
    # (?) Queue up size for when dialog opens.
    return false
  end
  # (!) Cache size difference.
  set_size( width, height )
  client_width, client_height = get_client_size()
  adjust_width  = width  - client_width
  adjust_height = height - client_height
  unless adjust_width == 0 && adjust_height == 0
    new_width  = width  + adjust_width
    new_height = height + adjust_height
    set_size( new_width, new_height )
  end
  true
end

#show_window(modal = false) ⇒ Nil

TODO:

Add black callback for ready/load event.

Open or bring to front the window.

Parameters:

  • modal (Boolean) (defaults to: false)

    Deprecated argument. Doesn't work across platforms.

Returns:

  • (Nil)

Since:

  • 2.4.0



465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'TT_Lib2/window.rb', line 465

def show_window( modal = false )
  if visible?
    bring_to_front()
  else
    # Under Windows, the HTML is populated when the window is shown.
    # Under OSX, the HTML is populated when set_html is called. 
    # This can be seen by attaching a callback to the DOM load event in JS.
    #
    # We use set_html here to prevent Macs loading the whole dialog when the
    # plugin loads. No need to populate the dialog and use extra resources
    # if it will never be used.
    set_html( build_html )
    
    # (!) Use the ModalWrapper for model windows.
    if TT::System.is_osx? || modal
      show_modal()
      if !@props[:resizable] && TT::System.is_windows?
        TT::Win32.window_no_resize( @props[:title] )
      end
    else
      show()
      if !@props[:resizable]
        TT::Win32.window_no_resize( @props[:title] )
      end
    end
  end
  nil
end

#titleString

Returns:

  • (String)

Since:

  • 2.6.0



270
271
272
# File 'TT_Lib2/window.rb', line 270

def title
  @props[:title].dup
end