RUIC 0.4.6 → 0.5.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.
- checksums.yaml +4 -4
- data/.yardopts +3 -3
- data/HISTORY +63 -52
- data/README.md +220 -220
- data/bin/ruic +12 -11
- data/gui/TODO +2 -2
- data/gui/appattributesmodel.rb +51 -51
- data/gui/appelementsmodel.rb +126 -126
- data/gui/launch.rb +19 -19
- data/gui/makefile +14 -14
- data/gui/resources/style/dark.qss +459 -459
- data/gui/window.rb +90 -90
- data/gui/window.ui +753 -753
- data/lib/ruic.rb +182 -175
- data/lib/ruic/application.rb +2 -2
- data/lib/ruic/assets.rb +436 -421
- data/lib/ruic/attributes.rb +170 -165
- data/lib/ruic/behaviors.rb +1 -1
- data/lib/ruic/interfaces.rb +23 -1
- data/lib/ruic/presentation.rb +100 -34
- data/lib/ruic/ripl-after-result.rb +24 -24
- data/lib/ruic/statemachine.rb +1 -1
- data/lib/ruic/version.rb +3 -3
- data/ruic.gemspec +25 -25
- data/test/MetaData-simple.xml +28 -28
- data/test/MetaData.xml +435 -435
- data/test/customclasses.ruic +31 -21
- data/test/filtering.ruic +43 -43
- data/test/futureassets.ruic +8 -8
- data/test/nonmaster.ruic +0 -0
- data/test/paths.ruic +18 -18
- data/test/projects/CustomClasses/CustomClasses.uia +7 -7
- data/test/projects/CustomClasses/CustomClasses.uip +7 -1
- data/test/projects/CustomClasses/FutureAsset.uip +17 -17
- data/test/projects/CustomClasses/scripts/DataDrivenTime.lua +58 -0
- data/test/projects/CustomClasses/scripts/TimeDrivenAttribute.lua +49 -0
- data/test/projects/Paths/Paths.uia +4 -4
- data/test/projects/Paths/Paths.uip +98 -98
- data/test/projects/SimpleScene/SimpleScene.uia +4 -4
- data/test/projects/SimpleScene/SimpleScene.uip +35 -35
- data/test/properties.ruic +82 -82
- data/test/referencematerials.ruic +52 -52
- data/test/usage.ruic +20 -20
- metadata +32 -4
data/lib/ruic.rb
CHANGED
@@ -1,176 +1,183 @@
|
|
1
|
-
#encoding:utf-8
|
2
|
-
|
3
|
-
class RUIC; end
|
4
|
-
module UIC; end
|
5
|
-
|
6
|
-
require 'nokogiri'
|
7
|
-
require_relative 'ruic/version'
|
8
|
-
require_relative 'ruic/attributes'
|
9
|
-
require_relative 'ruic/assets'
|
10
|
-
require_relative 'ruic/interfaces'
|
11
|
-
require_relative 'ruic/application'
|
12
|
-
require_relative 'ruic/behaviors'
|
13
|
-
require_relative 'ruic/statemachine'
|
14
|
-
require_relative 'ruic/presentation'
|
15
|
-
|
16
|
-
# The `RUIC` class provides the interface for running scripts using the special DSL,
|
17
|
-
# and for running the interactive REPL.
|
18
|
-
# See the {file:README.md README} file for description of the DSL.
|
19
|
-
class RUIC
|
20
|
-
DEFAULTMETADATA = 'C:/Program Files (x86)/NVIDIA Corporation/UI Composer 8.0/res/DataModelMetadata/en-us/MetaData.xml'
|
21
|
-
|
22
|
-
# Execute a script and/or launch the interactive REPL.
|
23
|
-
#
|
24
|
-
# If you both run a `:script` and then enter the `:repl` all local variables created
|
25
|
-
# by the script will be available in the REPL.
|
26
|
-
#
|
27
|
-
# # Just run a script
|
28
|
-
# RUIC.run script:'my.ruic'
|
29
|
-
#
|
30
|
-
# # Load an application and then enter the REPL
|
31
|
-
# RUIC.run uia:'my.uia', repl:true
|
32
|
-
#
|
33
|
-
# # Run a script and then drop into the REPL
|
34
|
-
# RUIC.run script:'my.ruic', repl:true
|
35
|
-
#
|
36
|
-
# The working directory for scripts is set to the directory of the script.
|
37
|
-
#
|
38
|
-
# The working directory for the repl is set to the directory of the first
|
39
|
-
# loaded application (if any);
|
40
|
-
# failing that, the directory of the script (if any);
|
41
|
-
# failing that, the current directory.
|
42
|
-
#
|
43
|
-
# @option opts [String] :script A path to a `.ruic` script to run _(optional)_.
|
44
|
-
# @option opts [String] :uia An `.uia` file to load (before running the script and/or REPL) _(optional)_.
|
45
|
-
# @option opts [Boolean] :repl Pass `true` to enter the command-line REPL after executing the script (if any).
|
46
|
-
# @return [nil]
|
47
|
-
def self.run(opts={})
|
48
|
-
opts = opts
|
49
|
-
ruic = nil
|
50
|
-
if opts[:script]
|
51
|
-
script = File.read(opts[:script],encoding:'utf-8')
|
52
|
-
Dir.chdir(File.dirname(opts[:script])) do
|
53
|
-
ruic = self.new
|
54
|
-
ruic.
|
55
|
-
ruic.
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# @
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
# # Provides a
|
124
|
-
# assert a==b
|
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
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
#
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
1
|
+
#encoding:utf-8
|
2
|
+
|
3
|
+
class RUIC; end
|
4
|
+
module UIC; end
|
5
|
+
|
6
|
+
require 'nokogiri'
|
7
|
+
require_relative 'ruic/version'
|
8
|
+
require_relative 'ruic/attributes'
|
9
|
+
require_relative 'ruic/assets'
|
10
|
+
require_relative 'ruic/interfaces'
|
11
|
+
require_relative 'ruic/application'
|
12
|
+
require_relative 'ruic/behaviors'
|
13
|
+
require_relative 'ruic/statemachine'
|
14
|
+
require_relative 'ruic/presentation'
|
15
|
+
|
16
|
+
# The `RUIC` class provides the interface for running scripts using the special DSL,
|
17
|
+
# and for running the interactive REPL.
|
18
|
+
# See the {file:README.md README} file for description of the DSL.
|
19
|
+
class RUIC
|
20
|
+
DEFAULTMETADATA = 'C:/Program Files (x86)/NVIDIA Corporation/UI Composer 8.0/res/DataModelMetadata/en-us/MetaData.xml'
|
21
|
+
|
22
|
+
# Execute a script and/or launch the interactive REPL.
|
23
|
+
#
|
24
|
+
# If you both run a `:script` and then enter the `:repl` all local variables created
|
25
|
+
# by the script will be available in the REPL.
|
26
|
+
#
|
27
|
+
# # Just run a script
|
28
|
+
# RUIC.run script:'my.ruic'
|
29
|
+
#
|
30
|
+
# # Load an application and then enter the REPL
|
31
|
+
# RUIC.run uia:'my.uia', repl:true
|
32
|
+
#
|
33
|
+
# # Run a script and then drop into the REPL
|
34
|
+
# RUIC.run script:'my.ruic', repl:true
|
35
|
+
#
|
36
|
+
# The working directory for scripts is set to the directory of the script.
|
37
|
+
#
|
38
|
+
# The working directory for the repl is set to the directory of the first
|
39
|
+
# loaded application (if any);
|
40
|
+
# failing that, the directory of the script (if any);
|
41
|
+
# failing that, the current directory.
|
42
|
+
#
|
43
|
+
# @option opts [String] :script A path to a `.ruic` script to run _(optional)_.
|
44
|
+
# @option opts [String] :uia An `.uia` file to load (before running the script and/or REPL) _(optional)_.
|
45
|
+
# @option opts [Boolean] :repl Pass `true` to enter the command-line REPL after executing the script (if any).
|
46
|
+
# @return [nil]
|
47
|
+
def self.run(opts={})
|
48
|
+
opts = opts
|
49
|
+
ruic = nil
|
50
|
+
if opts[:script]
|
51
|
+
script = File.read(opts[:script],encoding:'utf-8')
|
52
|
+
Dir.chdir(File.dirname(opts[:script])) do
|
53
|
+
ruic = self.new
|
54
|
+
ruic.metadata opts[:metadata] if opts[:metadata]
|
55
|
+
ruic.uia opts[:uia] if opts[:uia]
|
56
|
+
ruic.env.eval(script,opts[:script])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if opts[:repl]
|
61
|
+
location = (ruic && ruic.app && ruic.app.respond_to?(:file) && ruic.app.file) || opts[:uia] || opts[:script] || '.'
|
62
|
+
Dir.chdir( File.dirname(location) ) do
|
63
|
+
ruic ||= self.new.tap do |r|
|
64
|
+
r.metadata opts[:metadata] if opts[:metadata]
|
65
|
+
r.uia opts[:uia] if opts[:uia]
|
66
|
+
end
|
67
|
+
require 'ripl/irb'
|
68
|
+
require 'ripl/multi_line'
|
69
|
+
require 'ripl/multi_line/live_error.rb'
|
70
|
+
require_relative 'ruic/ripl-after-result'
|
71
|
+
Ripl::MultiLine.engine = Ripl::MultiLine::LiveError
|
72
|
+
Ripl::Shell.include Ripl::MultiLine.engine
|
73
|
+
Ripl::Shell.include Ripl::AfterResult
|
74
|
+
Ripl.config.merge! prompt:"", result_prompt:'#=> ', multi_line_prompt:' ', irb_verbose:false, after_result:"\n"
|
75
|
+
ARGV.clear # So that RIPL doesn't try to interpret the options
|
76
|
+
puts "(RUIC v#{RUIC::VERSION} interactive session; 'quit' or ctrl-d to end)"
|
77
|
+
ruic.instance_eval{ puts @apps.map{ |n,app| "(#{n} is #{app.inspect})" } }
|
78
|
+
puts "" # blank line before first input
|
79
|
+
Ripl.start binding:ruic.env
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Creates a new environment for executing a RUIC script.
|
85
|
+
# @param metadata [String] Path to the `MetaData.xml` file to use.
|
86
|
+
def initialize( metadata=DEFAULTMETADATA )
|
87
|
+
@metadata = metadata
|
88
|
+
@apps = {}
|
89
|
+
end
|
90
|
+
|
91
|
+
# Set the metadata to use; generally called from the RUIC DSL.
|
92
|
+
# @param path [String] Path to the `MetaData.xml` file, either absolute or relative to the working directory.
|
93
|
+
def metadata(path)
|
94
|
+
@metadata = path
|
95
|
+
end
|
96
|
+
|
97
|
+
# Load an application, making it available as `app`, `app2`, etc.
|
98
|
+
# @param path [String] Path to the `*.uia` application file.
|
99
|
+
# @return [UIC::Application] The new application loaded.
|
100
|
+
def uia(path)
|
101
|
+
meta = UIC.MetaData @metadata
|
102
|
+
name = @apps.empty? ? :app : :"app#{@apps.length+1}"
|
103
|
+
@apps[name] = UIC.Application(meta,path)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [Binding] the shared binding used for evaluating the script and REPL
|
107
|
+
def env
|
108
|
+
@env ||= binding
|
109
|
+
end
|
110
|
+
|
111
|
+
# @private used as a one-off
|
112
|
+
module SelfInspecting; def inspect; to_s; end; end
|
113
|
+
|
114
|
+
# Used to resolve bare `app` and `app2` calls to a loaded {UIC::Application Application}.
|
115
|
+
# @return [UIC::Application] the new application loaded.
|
116
|
+
def method_missing(name,*a)
|
117
|
+
@apps[name] || (name=~/^app\d*/ ? "(no #{name} loaded)".extend(SelfInspecting) : super)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Simple assertion mechanism to be used within scripts.
|
121
|
+
#
|
122
|
+
# @example 1) simple call syntax
|
123
|
+
# # Provides a generic failure message
|
124
|
+
# assert a==b
|
125
|
+
# #=> assertion failed (my.ruic line 17)
|
126
|
+
#
|
127
|
+
# # Provides a custom failure message
|
128
|
+
# assert a==b, "a should equal b"
|
129
|
+
# #=> a should equal b : assertion failed (my.ruic line 17)
|
130
|
+
#
|
131
|
+
# @example 2) block with string syntax
|
132
|
+
# # The code in the string to eval is also the failure message
|
133
|
+
# assert{ "a==b" }
|
134
|
+
# #=> a==b : assertion failed (my.ruic line 17)
|
135
|
+
#
|
136
|
+
# @param condition [Boolean] the value to evaluate.
|
137
|
+
# @param msg [String] the nice error message to display.
|
138
|
+
# @yieldreturn [String] the code to evaluate as a condition.
|
139
|
+
def assert(condition=:CONDITIONNOTSUPPLIED,msg=nil,&block)
|
140
|
+
if block && condition==:CONDITIONNOTSUPPLIED
|
141
|
+
msg = yield
|
142
|
+
condition = msg.is_a?(String) ? eval(msg,block.binding) : msg
|
143
|
+
end
|
144
|
+
condition || begin
|
145
|
+
file, line, _ = caller.first.split(':')
|
146
|
+
puts "#{"#{msg} : " unless msg.nil?}assertion failed (#{file} line #{line})"
|
147
|
+
exit 1
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Nicer name for `puts` to be used in the DSL, printing the
|
152
|
+
# 'nice' string equivalent for all supplied arguments.
|
153
|
+
def show(*a); puts *a.map(&:to_s); end
|
154
|
+
|
155
|
+
def inspect
|
156
|
+
"<RUIC #{@apps.empty? ? "(no app loaded)" : Hash[ @apps.map{ |id,app| [id,File.basename(app.file)] } ]}>"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Run a series of commands inside the RUIC DSL.
|
161
|
+
#
|
162
|
+
# @example
|
163
|
+
# require 'ruic'
|
164
|
+
# RUIC do
|
165
|
+
# uia 'test/MyProject/MyProject.uia'
|
166
|
+
# show app
|
167
|
+
# #=>UIC::Application 'MyProject.uia'>
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# If no block is supplied, this is the same as {RUIC.run RUIC.run(opts)}.
|
171
|
+
# @option opts [String] :uia Optionally load an application before running the script.
|
172
|
+
def RUIC(opts={},&block)
|
173
|
+
if block
|
174
|
+
Dir.chdir(File.dirname($0)) do
|
175
|
+
RUIC.new.tap do |r|
|
176
|
+
r.metadata opts[:metadata] if opts[:metadata]
|
177
|
+
r.uia opts[:uia] if opts[:uia]
|
178
|
+
end.instance_eval(&block)
|
179
|
+
end
|
180
|
+
else
|
181
|
+
RUIC.run(opts)
|
182
|
+
end
|
176
183
|
end
|
data/lib/ruic/application.rb
CHANGED
@@ -80,8 +80,8 @@ class UIC::Application
|
|
80
80
|
def referenced_files
|
81
81
|
# TODO: state machines can reference external scripts
|
82
82
|
# TODO: behaviors can reference external scripts
|
83
|
-
assets.map{ |asset|
|
84
|
-
+ presentations.flat_map{ |pres| pres.
|
83
|
+
assets.map{ |asset| resolve_file_path(asset.src) }
|
84
|
+
+ presentations.flat_map{ |pres| pres.referenced_files }
|
85
85
|
end
|
86
86
|
|
87
87
|
# @return [Array] all assets referenced by the application. Ordered by the order they appear in the `.uia`.
|
data/lib/ruic/assets.rb
CHANGED
@@ -1,422 +1,437 @@
|
|
1
|
-
#encoding: utf-8
|
2
|
-
class UIC::MetaData
|
3
|
-
|
4
|
-
# The base class for all assets. All other classes are dynamically created when a `MetaData.xml` file is loaded.
|
5
|
-
class AssetBase
|
6
|
-
@properties = {}
|
7
|
-
@name = "AssetBase"
|
8
|
-
|
9
|
-
class << self
|
10
|
-
# @return [String] The scene graph name of the asset.
|
11
|
-
attr_reader :name
|
12
|
-
|
13
|
-
# @return [Hash] a hash mapping attribute names to {Property} instances.
|
14
|
-
def properties
|
15
|
-
(ancestors[1].respond_to?(:properties) ? ancestors[1].properties : {}).merge(@properties)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @private
|
19
|
-
def inspect
|
20
|
-
"<#{@name}>"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# @return [Hash] a hash mapping attribute names to {Property} instances.
|
25
|
-
def properties
|
26
|
-
self.class.properties
|
27
|
-
end
|
28
|
-
|
29
|
-
# Find an asset by relative scripting path.
|
30
|
-
#
|
31
|
-
# @example
|
32
|
-
# preso = app.main
|
33
|
-
# layer = preso/"Scene.Layer"
|
34
|
-
# cam1 = app/"main:Scene.Layer.Camera"
|
35
|
-
# cam2 = preso/"Scene.Layer.Camera"
|
36
|
-
# cam3 = layer/"Camera"
|
37
|
-
# cam4 = cam1/"parent.Camera"
|
38
|
-
#
|
39
|
-
# assert cam1==cam2 && cam2==cam3 && cam3==cam4
|
40
|
-
#
|
41
|
-
# @return [MetaData::AssetBase] The found asset, or `nil` if it cannot be found.
|
42
|
-
#
|
43
|
-
# @see Application#at
|
44
|
-
# @see Presentation#at
|
45
|
-
def at(sub_path)
|
46
|
-
presentation.at(sub_path,@el)
|
47
|
-
end
|
48
|
-
alias_method :/, :at
|
49
|
-
|
50
|
-
# @return [Presentation] the presentation that this asset is part of.
|
51
|
-
attr_accessor :presentation
|
52
|
-
|
53
|
-
# @return [Nokogiri::XML::Element] the internal XML element in the scene graph of the presentation.
|
54
|
-
attr_accessor :el
|
55
|
-
|
56
|
-
# Create a new asset. This is called for you automatically; you likely should not be using it.
|
57
|
-
# @param presentation [Presentation] the presentation owning this asset.
|
58
|
-
# @param element [Nokogiri::XML::Element] the internal XML element in the scene graph of the presentation.
|
59
|
-
def initialize( presentation, element )
|
60
|
-
@presentation = presentation
|
61
|
-
@el = element
|
62
|
-
end
|
63
|
-
|
64
|
-
# @return [String] the type of this asset. For example: `"Model"`, `"Material"`, `"ReferencedMaterial"`, `"PathAnchorPoint"`, etc.
|
65
|
-
def type
|
66
|
-
self.class.name
|
67
|
-
end
|
68
|
-
|
69
|
-
# @return [AssetBase] the parent of this asset in the scene graph.
|
70
|
-
# @see Presentation#parent_asset
|
71
|
-
def parent
|
72
|
-
presentation.parent_asset(self)
|
73
|
-
end
|
74
|
-
|
75
|
-
# @return [Array<AssetBase>] array of child assets in the scene graph. Children are in scene graph order.
|
76
|
-
# @see Presentation#child_assets
|
77
|
-
def children
|
78
|
-
presentation.child_assets(self)
|
79
|
-
end
|
80
|
-
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
# @
|
109
|
-
def component
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
# @return [Boolean] `true` if this asset is
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
# @
|
120
|
-
def
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
# @
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
# @
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
# @
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
# show
|
147
|
-
# show
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
# show
|
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
|
-
def
|
178
|
-
|
179
|
-
end
|
180
|
-
|
181
|
-
#
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
# show logo
|
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
|
-
# logo
|
225
|
-
# show logo
|
226
|
-
#
|
227
|
-
# logo['endtime']
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
|
246
|
-
# @
|
247
|
-
def
|
248
|
-
|
249
|
-
end
|
250
|
-
|
251
|
-
# @private no need to document this
|
252
|
-
def to_s
|
253
|
-
"<#{type} #{path}>"
|
254
|
-
end
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
%w[
|
269
|
-
%w[
|
270
|
-
%w[
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
@by_name = {'AssetBase'=>AssetBase}
|
275
|
-
|
276
|
-
doc = Nokogiri.XML(xml)
|
277
|
-
hack_in_slide_names!(doc)
|
278
|
-
|
279
|
-
HIER.each do |class_name,parent_class_name|
|
280
|
-
parent_class = @by_name[parent_class_name]
|
281
|
-
el = doc.root.at(class_name)
|
282
|
-
@by_name[class_name] = create_class(el,parent_class,el.name)
|
283
|
-
end
|
284
|
-
|
285
|
-
# Extend well-known classes with script interfaces after they are created
|
286
|
-
@by_name['State'] = @by_name['Slide']
|
287
|
-
@by_name['Slide'].instance_eval do
|
288
|
-
attr_accessor :index, :name
|
289
|
-
define_method :inspect do
|
290
|
-
"<slide ##{index} of #{@el['component'] || @el.parent['component']}>"
|
291
|
-
end
|
292
|
-
define_method(:slide?){ true }
|
293
|
-
end
|
294
|
-
|
295
|
-
refmat = @by_name['ReferencedMaterial']
|
296
|
-
@by_name['MaterialBase'].instance_eval do
|
297
|
-
define_method :replace_with_referenced_material do
|
298
|
-
type=='ReferencedMaterial' ? self : presentation.replace_asset( self, 'ReferencedMaterial', name:name )
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
@by_name['Path'].instance_eval do
|
303
|
-
define_method(:anchors){ find _type:'PathAnchorPoint' }
|
304
|
-
end
|
305
|
-
|
306
|
-
end
|
307
|
-
|
308
|
-
# Creates a class from MetaData.xml with accessors for the <Property> listed.
|
309
|
-
# Instances of the class are associated with a presentation and know how to
|
310
|
-
# get/set values in that XML based on value types, slides, defaults.
|
311
|
-
# Also used to create classes from effects, materials, and behavior preambles.
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
end
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
@
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
@
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
end
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
@
|
409
|
-
end
|
410
|
-
def
|
411
|
-
@asset[
|
412
|
-
end
|
413
|
-
def
|
414
|
-
@asset
|
415
|
-
end
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
1
|
+
#encoding: utf-8
|
2
|
+
class UIC::MetaData
|
3
|
+
|
4
|
+
# The base class for all assets. All other classes are dynamically created when a `MetaData.xml` file is loaded.
|
5
|
+
class AssetBase
|
6
|
+
@properties = {}
|
7
|
+
@name = "AssetBase"
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# @return [String] The scene graph name of the asset.
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# @return [Hash] a hash mapping attribute names to {Property} instances.
|
14
|
+
def properties
|
15
|
+
(ancestors[1].respond_to?(:properties) ? ancestors[1].properties : {}).merge(@properties)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @private
|
19
|
+
def inspect
|
20
|
+
"<#{@name}>"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Hash] a hash mapping attribute names to {Property} instances.
|
25
|
+
def properties
|
26
|
+
self.class.properties
|
27
|
+
end
|
28
|
+
|
29
|
+
# Find an asset by relative scripting path.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# preso = app.main
|
33
|
+
# layer = preso/"Scene.Layer"
|
34
|
+
# cam1 = app/"main:Scene.Layer.Camera"
|
35
|
+
# cam2 = preso/"Scene.Layer.Camera"
|
36
|
+
# cam3 = layer/"Camera"
|
37
|
+
# cam4 = cam1/"parent.Camera"
|
38
|
+
#
|
39
|
+
# assert cam1==cam2 && cam2==cam3 && cam3==cam4
|
40
|
+
#
|
41
|
+
# @return [MetaData::AssetBase] The found asset, or `nil` if it cannot be found.
|
42
|
+
#
|
43
|
+
# @see Application#at
|
44
|
+
# @see Presentation#at
|
45
|
+
def at(sub_path)
|
46
|
+
presentation.at(sub_path,@el)
|
47
|
+
end
|
48
|
+
alias_method :/, :at
|
49
|
+
|
50
|
+
# @return [Presentation] the presentation that this asset is part of.
|
51
|
+
attr_accessor :presentation
|
52
|
+
|
53
|
+
# @return [Nokogiri::XML::Element] the internal XML element in the scene graph of the presentation.
|
54
|
+
attr_accessor :el
|
55
|
+
|
56
|
+
# Create a new asset. This is called for you automatically; you likely should not be using it.
|
57
|
+
# @param presentation [Presentation] the presentation owning this asset.
|
58
|
+
# @param element [Nokogiri::XML::Element] the internal XML element in the scene graph of the presentation.
|
59
|
+
def initialize( presentation, element )
|
60
|
+
@presentation = presentation
|
61
|
+
@el = element
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [String] the type of this asset. For example: `"Model"`, `"Material"`, `"ReferencedMaterial"`, `"PathAnchorPoint"`, etc.
|
65
|
+
def type
|
66
|
+
self.class.name
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [AssetBase] the parent of this asset in the scene graph.
|
70
|
+
# @see Presentation#parent_asset
|
71
|
+
def parent
|
72
|
+
presentation.parent_asset(self)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Array<AssetBase>] array of child assets in the scene graph. Children are in scene graph order.
|
76
|
+
# @see Presentation#child_assets
|
77
|
+
def children
|
78
|
+
presentation.child_assets(self)
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [String] the hierarchy under the element (for debugging purposes).
|
82
|
+
def hierarchy
|
83
|
+
presentation.hierarchy(self)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Find descendant assets matching criteria.
|
87
|
+
# This method is the same as (but more convenient than) {Presentation#find} using the `:_under` criteria.
|
88
|
+
# See that method for documentation of the `criteria`.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# preso = app.main
|
92
|
+
# group = preso/"Scene.Layer.Vehicle"
|
93
|
+
# tires = group.find name:/^Tire/
|
94
|
+
#
|
95
|
+
# # alternative
|
96
|
+
# tires = preso.find name:/^Tire/, _under:group
|
97
|
+
#
|
98
|
+
# @return [Array<AssetBase>]
|
99
|
+
#
|
100
|
+
# @see Presentation#find
|
101
|
+
def find(criteria={},&block)
|
102
|
+
criteria[:_under] ||= self
|
103
|
+
presentation.find(criteria,&block)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [AssetBase] the component or scene that owns this asset.
|
107
|
+
# If this asset is a component, does not return itself.
|
108
|
+
# @see Presentation#owning_component
|
109
|
+
def component
|
110
|
+
presentation.owning_component(self)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @return [Boolean] `true` if this asset is a component or Scene.
|
114
|
+
def component?
|
115
|
+
@el.name=='Component' || @el.name=='Scene'
|
116
|
+
end
|
117
|
+
|
118
|
+
# @return [Boolean] `true` if this asset is on the master slide.
|
119
|
+
# @see Presentation#master?
|
120
|
+
def master?
|
121
|
+
presentation.master?(self)
|
122
|
+
end
|
123
|
+
|
124
|
+
# @return [Boolean] `true` if this asset is a Slide.
|
125
|
+
def slide?
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
# @param slide_name_or_index [Integer,String] the slide number of name to check for presence on.
|
130
|
+
# @return [Boolean] `true` if this asset is present on the specified slide.
|
131
|
+
# @see Presentation#has_slide?
|
132
|
+
def has_slide?(slide_name_or_index)
|
133
|
+
presentation.has_slide?(self,slide_name_or_index)
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [SlideCollection] an array-like collection of all slides that the asset is available on.
|
137
|
+
# @see Presentation#slides_for
|
138
|
+
def slides
|
139
|
+
presentation.slides_for(self)
|
140
|
+
end
|
141
|
+
|
142
|
+
# @example
|
143
|
+
# logo = app/"main:Scene.UI.Logo"
|
144
|
+
# assert logo.master? # It's a master object
|
145
|
+
#
|
146
|
+
# show logo['endtime'].values #=> [10000,500,1000,750]
|
147
|
+
# show logo['opacity'].values #=> [100,0,0,100]
|
148
|
+
# logo1 = logo.on_slide(1)
|
149
|
+
# logo2 = logo.on_slide(2)
|
150
|
+
# show logo1['endtime'] #=> 500
|
151
|
+
# show logo2['endtime'] #=> 1000
|
152
|
+
# show logo1['opacity'] #=> 0
|
153
|
+
# show logo2['opacity'] #=> 0
|
154
|
+
#
|
155
|
+
# logo2['opacity'] = 66
|
156
|
+
# show logo['opacity'].values #=> [100,0,66,100]
|
157
|
+
#
|
158
|
+
# @param slide_name_or_index [Integer,String] the slide number or name to create the proxy for.
|
159
|
+
# @return [SlideValues] a proxy that yields attribute values for a specific slide.
|
160
|
+
def on_slide(slide_name_or_index)
|
161
|
+
if has_slide?(slide_name_or_index)
|
162
|
+
UIC::SlideValues.new( self, slide_name_or_index )
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# @return [String] the script path to this asset.
|
167
|
+
# @see #path_to
|
168
|
+
# @see Presentation#path_to
|
169
|
+
def path
|
170
|
+
@path ||= @presentation.path_to(self)
|
171
|
+
end
|
172
|
+
|
173
|
+
# @param other_asset [AssetBase] the asset to find the relative path to.
|
174
|
+
# @return [String] the script path to another asset, relative to this one.
|
175
|
+
# @see #path
|
176
|
+
# @see Presentation#path_to
|
177
|
+
def path_to(other_asset)
|
178
|
+
@presentation.path_to(other_asset,self)
|
179
|
+
end
|
180
|
+
|
181
|
+
# @return [String] the name of this asset in the scene graph.
|
182
|
+
def name
|
183
|
+
properties['name'] ? properties['name'].get( self, presentation.slide_index(self) ) : type
|
184
|
+
end
|
185
|
+
|
186
|
+
# Change the name of the asset in the scene graph.
|
187
|
+
# @param new_name [String] the new name for this asset.
|
188
|
+
# @return [String] the new name.
|
189
|
+
def name=( new_name )
|
190
|
+
@path = nil # invalidate the memoization
|
191
|
+
properties['name'].set( self, new_name, presentation.slide_index(self) )
|
192
|
+
end
|
193
|
+
|
194
|
+
# Get the value(s) of an attribute.
|
195
|
+
# If `slide_name_or_index` is omitted, creates a ValuesPerSlide proxy for the specified attribute.
|
196
|
+
# @example
|
197
|
+
# logo = app/"main:Scene.UI.Logo"
|
198
|
+
# show logo.master? #=> true (it's a master object)
|
199
|
+
# show logo['endtime'].linked? #=> false (the endtime property is unlinked)
|
200
|
+
# show logo['endtime'].values #=> [10000,500,1000,750]
|
201
|
+
# show logo['endtime',0] #=> 10000 (the master slide value)
|
202
|
+
# show logo['endtime',"Slide 1"] #=> 500
|
203
|
+
# show logo['endtime',2] #=> 1000
|
204
|
+
#
|
205
|
+
# @param attribute_name [String,Symbol] the name of the attribute.
|
206
|
+
# @param slide_name_or_index [Integer,String] the slide number or name to find the value on.
|
207
|
+
# @return [Object] the value of the property on the given slide.
|
208
|
+
# @return [ValuesPerSlide] if no slide is specified.
|
209
|
+
# @see #on_slide
|
210
|
+
# @see Presentation#get_attribute
|
211
|
+
def [](attribute_name, slide_name_or_index=nil)
|
212
|
+
if property = properties[attribute_name.to_s]
|
213
|
+
if slide_name_or_index
|
214
|
+
property.get( self, slide_name_or_index ) if has_slide?(slide_name_or_index)
|
215
|
+
else
|
216
|
+
UIC::ValuesPerSlide.new(@presentation,self,property)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Set the value of an attribute, either across all slides, or on a particular slide.
|
222
|
+
#
|
223
|
+
# @example
|
224
|
+
# logo = app/"main:Scene.UI.Logo"
|
225
|
+
# show logo.master? #=> true (it's a master object)
|
226
|
+
# show logo['endtime'].linked? #=> false (the endtime property is unlinked)
|
227
|
+
# show logo['endtime'].values #=> [10000,500,1000,750]
|
228
|
+
#
|
229
|
+
# logo['endtime',1] = 99
|
230
|
+
# show logo['endtime'].values #=> [10000,99,1000,750]
|
231
|
+
#
|
232
|
+
# logo['endtime'] = 42
|
233
|
+
# show logo['endtime'].values #=> [42,42,42,42]
|
234
|
+
# show logo['endtime'].linked? #=> false (the endtime property is still unlinked)
|
235
|
+
#
|
236
|
+
# @param attribute_name [String,Symbol] the name of the attribute.
|
237
|
+
# @param slide_name_or_index [Integer,String] the slide number or name to set the value on.
|
238
|
+
# @param new_value [Numeric,String] the new value for the attribute.
|
239
|
+
# @see Presentation#set_attribute
|
240
|
+
def []=( attribute_name, slide_name_or_index=nil, new_value )
|
241
|
+
if property = properties[attribute_name.to_s] then
|
242
|
+
property.set(self,new_value,slide_name_or_index)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# @return [String] the XML representation of the scene graph element.
|
247
|
+
def to_xml
|
248
|
+
@el.to_xml
|
249
|
+
end
|
250
|
+
|
251
|
+
# @private no need to document this
|
252
|
+
def to_s
|
253
|
+
"<#{type} #{path}>"
|
254
|
+
end
|
255
|
+
alias_method :inspect, :to_s
|
256
|
+
|
257
|
+
# @private no need to document this
|
258
|
+
def ==(other)
|
259
|
+
(self.class==other.class) && (el==other.el)
|
260
|
+
end
|
261
|
+
alias_method :eql?, :==
|
262
|
+
end
|
263
|
+
|
264
|
+
attr_reader :by_name
|
265
|
+
|
266
|
+
HIER = {}
|
267
|
+
|
268
|
+
%w[Asset Slide Scene].each{ |s| HIER[s] = 'AssetBase' }
|
269
|
+
%w[Node Behavior Effect Image Layer MaterialBase PathAnchorPoint RenderPlugin].each{ |s| HIER[s]='Asset' }
|
270
|
+
%w[Alias Camera Component Group Light Model Text Path].each{ |s| HIER[s]='Node' }
|
271
|
+
%w[Material ReferencedMaterial].each{ |s| HIER[s]='MaterialBase' }
|
272
|
+
|
273
|
+
def initialize(xml)
|
274
|
+
@by_name = {'AssetBase'=>AssetBase}
|
275
|
+
|
276
|
+
doc = Nokogiri.XML(xml)
|
277
|
+
hack_in_slide_names!(doc)
|
278
|
+
|
279
|
+
HIER.each do |class_name,parent_class_name|
|
280
|
+
parent_class = @by_name[parent_class_name]
|
281
|
+
el = doc.root.at(class_name)
|
282
|
+
@by_name[class_name] = create_class(el,parent_class,el.name)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Extend well-known classes with script interfaces after they are created
|
286
|
+
@by_name['State'] = @by_name['Slide']
|
287
|
+
@by_name['Slide'].instance_eval do
|
288
|
+
attr_accessor :index, :name
|
289
|
+
define_method :inspect do
|
290
|
+
"<slide ##{index} of #{@el['component'] || @el.parent['component']}>"
|
291
|
+
end
|
292
|
+
define_method(:slide?){ true }
|
293
|
+
end
|
294
|
+
|
295
|
+
refmat = @by_name['ReferencedMaterial']
|
296
|
+
@by_name['MaterialBase'].instance_eval do
|
297
|
+
define_method :replace_with_referenced_material do
|
298
|
+
type=='ReferencedMaterial' ? self : presentation.replace_asset( self, 'ReferencedMaterial', name:name )
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
@by_name['Path'].instance_eval do
|
303
|
+
define_method(:anchors){ find _type:'PathAnchorPoint' }
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
# Creates a class from MetaData.xml with accessors for the <Property> listed.
|
309
|
+
# Instances of the class are associated with a presentation and know how to
|
310
|
+
# get/set values in that XML based on value types, slides, defaults.
|
311
|
+
# Also used to create classes from effects, materials, and behavior preambles.
|
312
|
+
# @param el [Nokogiri::XML::Element] the element in MetaData.xml representing this class.
|
313
|
+
# @param parent_class [Class] the asset class to inherit from.
|
314
|
+
# @param name [String] the name of this class.
|
315
|
+
# @param new_defaults [Hash] hash mapping attribute name to a custom default value (as string) for this class.
|
316
|
+
def create_class(el,parent_class,name,new_defaults={})
|
317
|
+
Class.new(parent_class) do
|
318
|
+
@name = name.to_s
|
319
|
+
@properties = Hash[ el.css("Property").map do |e|
|
320
|
+
type = e['type'] || (e['list'] ? 'String' : 'Float')
|
321
|
+
type = "Float" if type=="float"
|
322
|
+
property = UIC::Property.const_get(type).new(e)
|
323
|
+
new_defaults.delete(property.name)
|
324
|
+
[ property.name, property ]
|
325
|
+
end ]
|
326
|
+
|
327
|
+
new_defaults.each do |name,value|
|
328
|
+
if prop=properties[name] # look in ancestor classes
|
329
|
+
@properties[name] = prop.dup
|
330
|
+
@properties[name].default = value
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def self.inspect
|
335
|
+
@name
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def new_instance(presentation,el)
|
341
|
+
klass = @by_name[el.name] || create_class(el,@by_name['Asset'],el.name)
|
342
|
+
klass.new(presentation,el)
|
343
|
+
end
|
344
|
+
|
345
|
+
def hack_in_slide_names!(doc)
|
346
|
+
doc.at('Slide') << '<Property name="name" formalName="Name" type="String" default="Slide" hidden="True" />'
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def UIC.MetaData(metadata_path)
|
351
|
+
raise %Q{Cannot find MetaData.xml at "#{metadata_path}"} unless File.exist?(metadata_path)
|
352
|
+
UIC::MetaData.new(File.read(metadata_path,encoding:'utf-8'))
|
353
|
+
end
|
354
|
+
|
355
|
+
class UIC::SlideCollection
|
356
|
+
include Enumerable
|
357
|
+
attr_reader :length
|
358
|
+
def initialize(slides)
|
359
|
+
@length = slides.length-1
|
360
|
+
@slides = slides
|
361
|
+
@lookup = {}
|
362
|
+
slides.each do |s|
|
363
|
+
@lookup[s.index] = s
|
364
|
+
@lookup[s.name] = s
|
365
|
+
end
|
366
|
+
end
|
367
|
+
def each
|
368
|
+
@slides.each{ |s| yield(s) }
|
369
|
+
end
|
370
|
+
def [](index_or_name)
|
371
|
+
@lookup[ index_or_name ]
|
372
|
+
end
|
373
|
+
def inspect
|
374
|
+
"[ #{@slides.map(&:inspect).join ', '} ]"
|
375
|
+
end
|
376
|
+
def to_ary
|
377
|
+
@slides
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
class UIC::ValuesPerSlide
|
382
|
+
def initialize(presentation,asset,property)
|
383
|
+
raise unless presentation.is_a?(UIC::Presentation)
|
384
|
+
|
385
|
+
raise unless asset.is_a?(UIC::MetaData::AssetBase)
|
386
|
+
raise unless property.is_a?(UIC::Property)
|
387
|
+
@preso = presentation
|
388
|
+
@asset = asset
|
389
|
+
@el = asset.el
|
390
|
+
@property = property
|
391
|
+
end
|
392
|
+
def value
|
393
|
+
values.first
|
394
|
+
end
|
395
|
+
def [](slide_name_or_index)
|
396
|
+
@property.get( @asset, slide_name_or_index )
|
397
|
+
end
|
398
|
+
def []=(slide_name_or_index,new_value)
|
399
|
+
@property.set( @asset, new_value, slide_name_or_index )
|
400
|
+
end
|
401
|
+
def linked?
|
402
|
+
@preso.attribute_linked?( @asset, @property.name )
|
403
|
+
end
|
404
|
+
def unlink
|
405
|
+
@preso.unlink_attribute( @asset, @property.name )
|
406
|
+
end
|
407
|
+
def link
|
408
|
+
@preso.link_attribute( @asset, @property.name )
|
409
|
+
end
|
410
|
+
def values
|
411
|
+
@asset.slides.map{ |s| self[s.name] }
|
412
|
+
end
|
413
|
+
def inspect
|
414
|
+
"<Values of '#{@asset.name}.#{@property.name}' across slides>"
|
415
|
+
end
|
416
|
+
alias_method :to_s, :inspect
|
417
|
+
end
|
418
|
+
|
419
|
+
class UIC::SlideValues
|
420
|
+
attr_reader :asset
|
421
|
+
def initialize( asset, slide )
|
422
|
+
@asset = asset
|
423
|
+
@slide = slide
|
424
|
+
end
|
425
|
+
def [](attribute_name)
|
426
|
+
@asset[attribute_name,@slide]
|
427
|
+
end
|
428
|
+
def []=( attribute_name, new_value )
|
429
|
+
@asset[attribute_name,@slide] = new_value
|
430
|
+
end
|
431
|
+
def method_missing( name, *args, &blk )
|
432
|
+
asset.send(name,*args,&blk)
|
433
|
+
end
|
434
|
+
def inspect
|
435
|
+
"<#{@asset.inspect} on slide #{@slide.inspect}>"
|
436
|
+
end
|
422
437
|
end
|