RUIC 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/HISTORY +23 -0
  3. data/README.md +2 -2
  4. data/lib/ruic.rb +11 -3
  5. data/lib/ruic/application.rb +28 -22
  6. data/lib/ruic/attributes.rb +10 -2
  7. data/lib/ruic/behaviors.rb +2 -15
  8. data/lib/ruic/effect.rb +32 -0
  9. data/lib/ruic/interfaces.rb +51 -13
  10. data/lib/ruic/presentation.rb +46 -20
  11. data/lib/ruic/renderplugin.rb +18 -0
  12. data/lib/ruic/ripl.rb +45 -0
  13. data/lib/ruic/statemachine.rb +142 -25
  14. data/lib/ruic/version.rb +1 -1
  15. data/test/customclasses.ruic +0 -1
  16. data/test/filtering.ruic +0 -1
  17. data/test/futureassets.ruic +0 -1
  18. data/test/nonmaster.ruic +0 -1
  19. data/test/paths.ruic +0 -2
  20. data/test/projects/MissingAssets/Existing.uip +87 -0
  21. data/test/projects/MissingAssets/MissingAssets.uia +21 -0
  22. data/test/projects/MissingAssets/MissingAssets.uia-user +14 -0
  23. data/test/projects/MissingAssets/RoundedPlane-1/RoundedPlane-1.import +18 -0
  24. data/test/projects/MissingAssets/RoundedPlane-1/meshes/RoundedPlane.mesh +0 -0
  25. data/test/projects/MissingAssets/effects/effects.txt +3 -0
  26. data/test/projects/MissingAssets/effects/existing.effect +38 -0
  27. data/test/projects/MissingAssets/fonts/Arimo-Regular.ttf +0 -0
  28. data/test/projects/MissingAssets/fonts/Chivo-Black.ttf +0 -0
  29. data/test/projects/MissingAssets/fonts/OFL.txt +92 -0
  30. data/test/projects/MissingAssets/maps/effects/brushnoise.dds +0 -0
  31. data/test/projects/MissingAssets/maps/existing.png +0 -0
  32. data/test/projects/MissingAssets/maps/existing2.png +0 -0
  33. data/test/projects/MissingAssets/maps/maps.txt +2 -0
  34. data/test/projects/MissingAssets/maps/materials/concrete_plain.png +0 -0
  35. data/test/projects/MissingAssets/maps/materials/concrete_plain_bump.png +0 -0
  36. data/test/projects/MissingAssets/maps/materials/spherical_checker.png +0 -0
  37. data/test/projects/MissingAssets/maps/unused.png +0 -0
  38. data/test/projects/MissingAssets/materials/concrete.material +251 -0
  39. data/test/projects/MissingAssets/materials/materials.txt +3 -0
  40. data/test/projects/MissingAssets/scripts/existing1.lua +2 -0
  41. data/test/projects/MissingAssets/scripts/existing2.lua +470 -0
  42. data/test/projects/MissingAssets/states/existing.scxml +4 -0
  43. data/test/projects/MissingAssets/tests/interface-navigation.scxml-test +3 -0
  44. data/test/properties.ruic +0 -2
  45. data/test/referencematerials.ruic +0 -2
  46. data/test/usage.ruic +38 -4
  47. metadata +53 -3
  48. data/lib/ruic/ripl-after-result.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 105e49557e0d92a463d1630cd6fa42d078048e44
4
- data.tar.gz: 21db139d873cbcfa3ad7f9c66c85a2ffc151f9ac
3
+ metadata.gz: 2a99bfd85a630d1804e5c38b33296883a2f43342
4
+ data.tar.gz: f3233b7830899344bcee0c969fdb130e88143b1d
5
5
  SHA512:
6
- metadata.gz: 2e4f62d1164c63510bb3cc931870b3b353e28439fecf38076b6ac8ad3dca09a528c5fedb32c6b613115b1d248fda324a11fac912d873da83e57fd16bd1a82586
7
- data.tar.gz: 0eeb2a2ab41c848feef5859a7f0810584de7f89beb785c9bb29a1c7f55a72495a7234ecc5514e8e45b765ba13aea650a0e7685f7d9a903fcd08baa45527fa19e
6
+ metadata.gz: 474bf2cda9e565b3a050fadb9b266980f3b36075f0a953eabf69106695e623575e980fab75c007e78e47f6de6f5e58f0fad217261008392616b47eeca66207d7
7
+ data.tar.gz: 5a3066ad987ce4597b0ef47d935c8db28bc477ffb281300406184d1119539f84d6130478e3f7ca6dbb3b025d0603567cea0c208fa89867c75d81031832d1a8a0
data/HISTORY CHANGED
@@ -1,3 +1,26 @@
1
+ ## v0.6.0
2
+
3
+ ### Backwards Incompatible Changes
4
+
5
+ * Changed `FileBacked#resolve_file_path` to `FileBacked#absolute_path`, and added `FileBacked#relative_path`.
6
+ * Removed `.errors?` and `.errors` for application and assets.
7
+
8
+ ### New/Changed Features
9
+
10
+ * State machines properly report on visual actions.
11
+ * _Only `<set-attribute>` properly implemented so far._
12
+ * Added much improved `Presentation#referenced_files`, `Application#missing_files`, `Application#unused_files`.
13
+ * Tweaked REPL output.
14
+ * `nil` results no longer print `#=> nil` in the REPL.
15
+ * `show` prefixes the result with `#=>`.
16
+ * Multi-line results are now wrapped, with each line prefixed with `#=>`.
17
+ * Added beginnings of support for effects and renderplugins.
18
+ * Presentations do not cause a runtime error if a custom class (e.g. behavior, effect, custom material) cannot be found.
19
+
20
+ ### Bug Fixes
21
+ * Fix bug that caused some properties to report their type as `nil` or `float` instead of `Float`.
22
+ * Presentations are slightly more capable if they are created without an existing file.
23
+
1
24
  ## v0.5.0 - 2014-Nov-26
2
25
 
3
26
  * Fix bug that prevented loading an application from the command line in another directory.
data/README.md CHANGED
@@ -167,7 +167,7 @@ There are two ways to enter interactive mode:
167
167
  the application and enter the REPL:
168
168
 
169
169
  $ ruic myapp.uia
170
- (RUIC v0.2.2 interactive session; 'quit' or ctrl-d to end)
170
+ (RUIC v0.6.0 interactive session; 'quit' or ctrl-d to end)
171
171
 
172
172
  uia "test/projects/SimpleScene/SimpleScene.uia"
173
173
  #=> <UIC::Application 'SimpleScene.uia'>
@@ -176,7 +176,7 @@ There are two ways to enter interactive mode:
176
176
  by supplying the `-i` command-line switch:
177
177
 
178
178
  $ ruic -i test/referencematerials.ruic
179
- (RUIC v0.2.2 interactive session; 'quit' or ctrl-d to end)
179
+ (RUIC v0.6.0 interactive session; 'quit' or ctrl-d to end)
180
180
 
181
181
  app
182
182
  #=> <UIC::Application 'ReferencedMaterials.uia'>
@@ -10,8 +10,11 @@ require_relative 'ruic/assets'
10
10
  require_relative 'ruic/interfaces'
11
11
  require_relative 'ruic/application'
12
12
  require_relative 'ruic/behaviors'
13
+ require_relative 'ruic/effect'
14
+ require_relative 'ruic/renderplugin'
13
15
  require_relative 'ruic/statemachine'
14
16
  require_relative 'ruic/presentation'
17
+ require_relative 'ruic/ripl'
15
18
 
16
19
  # The `RUIC` class provides the interface for running scripts using the special DSL,
17
20
  # and for running the interactive REPL.
@@ -67,11 +70,11 @@ class RUIC
67
70
  require 'ripl/irb'
68
71
  require 'ripl/multi_line'
69
72
  require 'ripl/multi_line/live_error.rb'
70
- require_relative 'ruic/ripl-after-result'
71
73
  Ripl::MultiLine.engine = Ripl::MultiLine::LiveError
72
74
  Ripl::Shell.include Ripl::MultiLine.engine
73
75
  Ripl::Shell.include Ripl::AfterResult
74
- Ripl.config.merge! prompt:"", result_prompt:'#=> ', multi_line_prompt:' ', irb_verbose:false, after_result:"\n"
76
+ Ripl::Shell.include Ripl::FormatResult
77
+ Ripl.config.merge! prompt:"", result_prompt:'#=> ', multi_line_prompt:' ', irb_verbose:false, after_result:"\n", result_line_limit:120, prefix_result_lines:true, skip_nil_results:true
75
78
  ARGV.clear # So that RIPL doesn't try to interpret the options
76
79
  puts "(RUIC v#{RUIC::VERSION} interactive session; 'quit' or ctrl-d to end)"
77
80
  ruic.instance_eval{ puts @apps.map{ |n,app| "(#{n} is #{app.inspect})" } }
@@ -150,7 +153,12 @@ class RUIC
150
153
 
151
154
  # Nicer name for `puts` to be used in the DSL, printing the
152
155
  # 'nice' string equivalent for all supplied arguments.
153
- def show(*a); puts *a.map(&:to_s); end
156
+ def show(*a)
157
+ a=a.first if a.length==1 && a.first.is_a?(Array)
158
+ opts = { result_prompt:'# ', result_line_limit:120, prefix_result_lines:true, to_s:true }
159
+ a.each{ |x| puts Ripl::FormatResult.format_result(x,opts) }
160
+ nil # so that Ripl won't show the result
161
+ end
154
162
 
155
163
  def inspect
156
164
  "<RUIC #{@apps.empty? ? "(no app loaded)" : Hash[ @apps.map{ |id,app| [id,File.basename(app.file)] } ]}>"
@@ -1,6 +1,6 @@
1
1
  # The `Application` represents the root of your UIC application, corresponding to a `.uia` file.
2
2
  class UIC::Application
3
- include UIC::FileBacked
3
+ include UIC::XMLFileBacked
4
4
 
5
5
  def inspect
6
6
  "<UIC::Application '#{File.basename(file)}'#{:' FILENOTFOUND' unless file_found?}>"
@@ -14,18 +14,16 @@ class UIC::Application
14
14
  # If omitted you will need to later set the `.file = ` for the
15
15
  # instance and then call {#load_from_file}.
16
16
  def initialize(metadata,uia_path=nil)
17
+ @assets = {}
17
18
  @metadata = metadata
18
19
  self.file = uia_path
19
- @assets = {}
20
- load_from_file if file_found?
21
20
  end
22
21
 
23
22
  # Loads the application from the file. If you pass the path to your `.uia`
24
23
  # to {#initialize} then this method is called automatically.
25
24
  #
26
25
  # @return [nil]
27
- def load_from_file
28
- self.doc = Nokogiri.XML(File.read(file,encoding:'utf-8'))
26
+ def on_doc_loaded
29
27
  @assets = @doc.search('assets *').map do |el|
30
28
  case el.name
31
29
  when 'behavior' then UIC::Application::Behavior
@@ -37,18 +35,6 @@ class UIC::Application
37
35
  nil
38
36
  end
39
37
 
40
- # @return [Boolean] true if there are any errors with the application.
41
- def errors?
42
- !errors.empty?
43
- end
44
-
45
- # @example
46
- # show app.errors if app.errors?
47
- # @return [Array<String>] an array (possibly empty) of all errors in this application (including referenced assets).
48
- def errors
49
- file_found? ? assets.flat_map(&:errors) : ["File not found: '#{file}'"]
50
- end
51
-
52
38
  # Find an asset by `.uia` identifier or path to the asset.
53
39
  # @example
54
40
  # main1 = app['#main']
@@ -71,17 +57,37 @@ class UIC::Application
71
57
  end
72
58
  end
73
59
 
74
- # @private do not document until working
60
+ # Files in the application directory not used by the application.
61
+ #
62
+ # @return [Array<String>] absolute paths of files in the directory not used by the application.
75
63
  def unused_files
76
- referenced_files - directory_files
64
+ (directory_files - referenced_files).sort
77
65
  end
78
66
 
79
- # @private do not document until working
67
+ # Files referenced by the application but not present in the directory.
68
+ #
69
+ # @return [Array<String>] absolute paths of files referenced but gone.
70
+ def missing_files
71
+ (referenced_files - directory_files).sort
72
+ end
73
+
74
+
75
+ # @return [Array<String>] absolute paths of files referenced by the application.
80
76
  def referenced_files
81
77
  # TODO: state machines can reference external scripts
82
78
  # TODO: behaviors can reference external scripts
83
- assets.map{ |asset| resolve_file_path(asset.src) }
84
- + presentations.flat_map{ |pres| pres.referenced_files }
79
+ (
80
+ [file] +
81
+ assets.map{ |asset| absolute_path(asset.src) } +
82
+ statemachines.flat_map(&:referenced_files) +
83
+ presentations.flat_map(&:referenced_files)
84
+ ).uniq
85
+ end
86
+
87
+ # @return [Array<String>] absolute paths of all files present in the application directory (used or not).
88
+ def directory_files
89
+ dir = File.dirname(file)
90
+ Dir.chdir(dir){ Dir['**/*.*'] }.map{ |f| File.expand_path(f,dir) }
85
91
  end
86
92
 
87
93
  # @return [Array] all assets referenced by the application. Ordered by the order they appear in the `.uia`.
@@ -7,7 +7,7 @@ class UIC::Property
7
7
  attr_accessor :default
8
8
  def initialize(el); @el = el; end
9
9
  def name; @name||=@el['name']; end
10
- def type; @type||=@el['type']; end
10
+ def type; @type||=@el['type'] ? (@el['type']=='float' ? 'Float' : @el['type']) : 'Float'; end
11
11
  def formal; @formal||=@el['formalName'] || @el['name']; end
12
12
  def min; @el['min']; end
13
13
  def max; @el['max']; end
@@ -129,10 +129,18 @@ class UIC::Property
129
129
  end
130
130
  end
131
131
 
132
+ class Font < self
133
+ self.default = 'Arimo-Regular'
134
+ def get(asset,slide); "fonts/#{super}.ttf"; end # TODO: how to support non-ttf fonts?
135
+ def set(asset,new_value,slide_name_or_index)
136
+ # TODO: how to support non-ttf fonts?
137
+ super( asset, new_value.sub(%r{^\.[/\\]},'').sub(%r{^fonts[/\\]},'').sub(%r{\.ttf$},''), slide_name_or_index )
138
+ end
139
+ end
140
+
132
141
  Import = String #TODO: a real class
133
142
  Mesh = String #TODO: a real class
134
143
  Renderable = String #TODO: a real class
135
- Font = String #TODO: a real class
136
144
  FontSize = Long
137
145
 
138
146
  StringListOrInt = String #TODO: a real class
@@ -1,22 +1,9 @@
1
1
  class UIC::Behavior
2
2
  include UIC::FileBacked
3
- attr_reader :lua
4
3
  def initialize( lua_path )
5
4
  self.file = lua_path
6
- load_from_file if file_found?
7
5
  end
8
- def load_from_file
9
- @lua = File.read(file,encoding:'utf-8')
10
- end
11
-
12
- def errors?
13
- !errors.empty?
14
- end
15
-
16
- def errors
17
- file_found? ? [] : ["File not found: '#{file}'"]
18
- end
19
-
6
+ alias_method :lua, :file_content
20
7
  end
21
8
 
22
9
  class UIC::Application::Behavior < UIC::Behavior
@@ -27,6 +14,6 @@ class UIC::Application::Behavior < UIC::Behavior
27
14
  def initialize(application,el)
28
15
  self.owner = application
29
16
  self.el = el
30
- super( application.resolve_file_path(src) )
17
+ super( application.absolute_path(src) )
31
18
  end
32
19
  end
@@ -0,0 +1,32 @@
1
+ class UIC::Effect
2
+ include UIC::FileBacked
3
+ attr_reader :lua
4
+ def initialize( lua_path )
5
+ self.file = lua_path
6
+ load_from_file if file_found?
7
+ end
8
+ def load_from_file
9
+ @lua = File.read(file,encoding:'utf-8')
10
+ end
11
+
12
+ def errors?
13
+ !errors.empty?
14
+ end
15
+
16
+ def errors
17
+ file_found? ? [] : ["File not found: '#{file}'"]
18
+ end
19
+
20
+ end
21
+
22
+ class UIC::Application::Effect < UIC::Effect
23
+ include UIC::ElementBacked
24
+ # @!parse extend UIC::ElementBacked::ClassMethods
25
+ xmlattribute :id
26
+ xmlattribute :src
27
+ def initialize(application,el)
28
+ self.owner = application
29
+ self.el = el
30
+ super( application.absolute_path(src) )
31
+ end
32
+ end
@@ -1,17 +1,23 @@
1
- # Supports classes that represent an XML file on disk (e.g. `.uia` and `.uip`).
1
+ # Supports classes that represent a file on disk (e.g. `.uia` and `.uip`).
2
2
  module UIC::FileBacked
3
- # @return [Nokogiri::XML::Document] the Nokogiri document representing the instance.
4
- attr_accessor :doc
5
-
6
3
  # @return [String] the absolute path to the underlying file.
7
4
  attr_accessor :file
8
5
 
6
+ # @return [String] the content of the file.
7
+ attr_accessor :file_content
8
+
9
9
  # @param relative [String] a file path relative to this file.
10
10
  # @return [String] the full path resolved relative to this file.
11
- def resolve_file_path( relative )
11
+ def absolute_path( relative )
12
12
  File.expand_path( relative.gsub('\\','/'), File.dirname(file) )
13
13
  end
14
14
 
15
+ def relative_path(path)
16
+ my_dir = File.dirname(file) << "/"
17
+ absolute_path(path).tap{ |s| s.slice!(my_dir) }
18
+
19
+ end
20
+
15
21
  # @return [String] the name of the file (without any directories).
16
22
  def filename
17
23
  File.basename(file)
@@ -27,6 +33,39 @@ module UIC::FileBacked
27
33
  # @return [String]
28
34
  def file=( new_path )
29
35
  @file = File.expand_path(new_path)
36
+ if file_found?
37
+ @file_content = File.read(@file,encoding:'utf-8')
38
+ on_file_loaded if respond_to?(:on_file_loaded)
39
+ end
40
+ end
41
+
42
+ # Overwrite the associated file on disk with the `@file_content` of this class.
43
+ # @return [true]
44
+ def save!
45
+ File.open(file,'w:utf-8'){ |f| f << @file_content }
46
+ true
47
+ end
48
+
49
+ # Save to the supplied file path. Subsequent calls to {#save!} will save to the new file, not the original file name.
50
+ def save_as(new_file)
51
+ File.open(new_file,'w:utf-8'){ |f| f << @file_content }
52
+ self.file = new_file
53
+ end
54
+ end
55
+
56
+ # Supports classes that represent an XML file on disk.
57
+ module UIC::XMLFileBacked
58
+ include UIC::FileBacked
59
+
60
+ # @return [Nokogiri::XML::Document] the Nokogiri document representing the instance.
61
+ attr_accessor :doc
62
+
63
+ def file=( new_path )
64
+ super
65
+ if file_found?
66
+ @doc = Nokogiri.XML(file_content,&:noblanks)
67
+ on_doc_loaded if respond_to?(:on_doc_loaded)
68
+ end
30
69
  end
31
70
 
32
71
  # @return [String] the XML representation of the document.
@@ -37,20 +76,19 @@ module UIC::FileBacked
37
76
  # Overwrite the associated file on disk with the {#to_xml} representation of this class.
38
77
  # @return [true]
39
78
  def save!
40
- File.open(file,'w:utf-8'){ |f| f << to_xml }
41
- true
79
+ self.file_content = to_xml
80
+ super
42
81
  end
43
82
 
44
83
  # Save to the supplied file path. Subsequent calls to {#save!} will save to the new file, not the original file name.
45
84
  def save_as(new_file)
46
- File.open(new_file,'w:utf-8'){ |f| f << to_xml }
47
- self.file = new_file
85
+ self.file_content = to_xml
86
+ super
48
87
  end
49
88
  end
50
89
 
51
90
  # Supports classes that represent an XML element (e.g. `<presentation id="main" src="foo.uip"/>`).
52
91
  module UIC::ElementBacked
53
-
54
92
  # @return [Object] the object in charge of this instance.
55
93
  attr_accessor :owner
56
94
 
@@ -65,10 +103,10 @@ module UIC::ElementBacked
65
103
  module ClassMethods
66
104
  # Add methods to instances of the class which gets/sets from an XML attribute.
67
105
  # @param name [String] the name of an XML attribute to expose.
68
- # @param getblock [Proc] a proc to run
106
+ # @param getblock [Proc] a proc to run to fetch the value.
69
107
  def xmlattribute(name,getblock=nil,&setblock)
70
- define_method(name){ getblock ? getblock[@el[name]] : @el[name] }
71
- define_method("#{name}="){ |new_value| @el[name] = (setblock ? setblock[new_value] : new_value).to_s }
108
+ define_method(name){ getblock ? getblock[@el[name],self] : @el[name] }
109
+ define_method("#{name}="){ |new_value| @el[name] = (setblock ? setblock[new_value,self] : new_value).to_s }
72
110
  end
73
111
  end
74
112
  end
@@ -1,22 +1,28 @@
1
1
  # A `Presentation` represents a `.uip` presentation, created and edited by UI Composer Studio.
2
2
  class UIC::Presentation
3
- include UIC::FileBacked
3
+ include UIC::XMLFileBacked
4
4
 
5
5
  # Create a new presentation. If you do not specify the `uip_path` to load from, you must
6
6
  # later set the `.file = `for the presentation, and then call the {#load_from_file} method.
7
7
  # @param uip_path [String] path to the `.uip` to load.
8
8
  def initialize( uip_path=nil )
9
- self.file = uip_path
10
- load_from_file if file_found?
9
+ @doc = Nokogiri.XML('<UIP version="3"><Project><Graph><Scene id="Scene"/></Graph><Logic><State name="Master Slide" component="#Scene"/></Logic></Project></UIP>')
10
+ @graph = @doc.at('Graph')
11
+ @scene = @graph.at('Scene')
12
+ @logic = @doc.at('Logic')
13
+ @missing_classes = []
14
+ @asset_by_el = {} # indexed by asset graph element
15
+ @slides_for = {} # indexed by asset graph element
16
+ @slides_by_el = {} # indexed by slide state element
17
+ self.file = uip_path if uip_path
11
18
  end
12
19
 
13
20
  # Load information for the presentation from disk.
14
21
  # If you supply a path to a `.uip` file when creating the presentation
15
22
  # this method is automatically called.
23
+ #
16
24
  # @return [nil]
17
- def load_from_file
18
- # TODO: this method assumes an application to find the metadata on; the metadata should be part of this class instance instead, shared with the app when present
19
- @doc = Nokogiri.XML( File.read( file, encoding:'utf-8' ), &:noblanks )
25
+ def on_doc_loaded
20
26
  @graph = @doc.at('Graph')
21
27
  @scene = @graph.at('Scene')
22
28
  @logic = @doc.at('Logic')
@@ -24,10 +30,6 @@ class UIC::Presentation
24
30
  generate_custom_classes
25
31
  rebuild_caches_from_document
26
32
 
27
- @asset_by_el = {} # indexed by asset graph element
28
- @slides_for = {} # indexed by asset graph element
29
- @slides_by_el = {} # indexed by slide state element
30
-
31
33
  nil
32
34
  end
33
35
 
@@ -42,6 +44,7 @@ class UIC::Presentation
42
44
  # Called once during initial load of the file.
43
45
  # @return [nil]
44
46
  def generate_custom_classes
47
+ # TODO: this method assumes an application to find the metadata on; the metadata should be part of this class instance instead, shared with the app when present
45
48
  parent_class_name = {
46
49
  'CustomMaterial' => 'MaterialBase',
47
50
  'Effect' => 'Effect',
@@ -49,8 +52,8 @@ class UIC::Presentation
49
52
  }
50
53
  @class_by_ref = {}
51
54
  @doc.xpath('/UIP/Project/Classes/*').each do |reference|
52
- path = resolve_file_path( reference['sourcepath'] )
53
- raise "Cannot find file '#{path}' referenced by #{self.inspect}" unless File.exist?( path )
55
+ path = absolute_path( reference['sourcepath'] )
56
+ next unless File.exist?( path )
54
57
  parent_class = app.metadata.by_name[ parent_class_name[reference.name] ]
55
58
  parent_props = parent_class.properties
56
59
  new_defaults = Hash[ reference.attributes.map{ |name,attr| [name,attr.value] }.select{ |name,val| parent_props[name] } ]
@@ -169,16 +172,39 @@ class UIC::Presentation
169
172
  # Find or create an asset for a scene graph element.
170
173
  # @param el [Nokogiri::XML::Element] the scene graph element.
171
174
  def asset_for_el(el)
172
- (@asset_by_el[el] ||= el['class'] ? @class_by_ref[el['class']].new(self,el) : app.metadata.new_instance(self,el))
175
+ @asset_by_el[el] ||= begin
176
+ if el['class'] && @class_by_ref[el['class']]
177
+ @class_by_ref[el['class']].new(self,el)
178
+ else
179
+ app.metadata.new_instance(self,el)
180
+ end
181
+ end
173
182
  end
174
183
  private :asset_for_el
175
184
 
185
+ # Which files are used by the presentation.
186
+ # @return [Array<String>] array of absolute file paths referenced by this presentation.
176
187
  def referenced_files
177
188
  (
178
- (images + behaviors + effects + meshes + materials ).map(&:file)
179
- + effects.flat_map(&:images)
180
- + fonts
181
- ).sort_by{ |f| parts = f.split(/[\/\\]/); [parts.length,parts] }
189
+ [file] +
190
+ %w[sourcepath importfile].flat_map do |att|
191
+ find(att=>/./).flat_map do |asset|
192
+ asset[att].values.compact.map do |path|
193
+ path.sub!(/#.+/,'')
194
+ absolute_path(path) unless path.empty?
195
+ end.compact
196
+ end
197
+ end +
198
+ find.flat_map do |asset|
199
+ asset.properties.select{ |name,prop| prop.type=='Texture' }.flat_map do |name,prop|
200
+ asset[name].values.compact.uniq.map{ |path| absolute_path(path) }
201
+ end
202
+ end +
203
+ find(_type:'Text').flat_map do |asset|
204
+ asset['font'].values.compact.map{ |font| absolute_path(font) }
205
+ end +
206
+ @doc.xpath('/UIP/Project/Classes/*/@sourcepath').map{ |att| absolute_path att.value }
207
+ ).uniq
182
208
  end
183
209
 
184
210
  # @return [MetaData::Scene] the root scene asset for the presentation.
@@ -243,7 +269,7 @@ class UIC::Presentation
243
269
 
244
270
  # @return [Array<String>] an array (possibly empty) of all errors in this presentation.
245
271
  def errors
246
- (file_found? ? [] : ["File not found: '#{file}'"])
272
+ @errors
247
273
  end
248
274
 
249
275
  # Find an element or asset in this presentation by scripting path.
@@ -599,7 +625,7 @@ class UIC::Presentation
599
625
  cols = hier.map{ |i,a| a ? [ [i,a.name].join, a.type, elide[a.path] ] : [i,"",""] }
600
626
  maxs = cols.transpose.map{ |col| col.map(&:length).max }
601
627
  tmpl = maxs.map{ |n| "%-#{n}s" }.join(' ')
602
- cols.map{ |a| tmpl % a }.join("\n").prepend("\n").extend(RUIC::SelfInspecting)
628
+ cols.map{ |a| tmpl % a }.join("\n").extend(RUIC::SelfInspecting)
603
629
  end
604
630
 
605
631
  # @private
@@ -643,7 +669,7 @@ class UIC::Application::Presentation < UIC::Presentation
643
669
  def initialize(application,el)
644
670
  self.owner = application
645
671
  self.el = el
646
- super( application.resolve_file_path(src) )
672
+ super( application.absolute_path(src) )
647
673
  end
648
674
  alias_method :app, :owner
649
675