RUIC 0.4.2 → 0.4.3
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 -4
- data/README.md +278 -279
- data/bin/ruic +0 -0
- 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 +175 -175
- data/lib/ruic/application.rb +5 -1
- data/lib/ruic/assets.rb +312 -289
- data/lib/ruic/attributes.rb +165 -165
- data/lib/ruic/behaviors.rb +1 -0
- data/lib/ruic/interfaces.rb +45 -7
- data/lib/ruic/presentation.rb +103 -39
- data/lib/ruic/ripl-after-result.rb +24 -11
- data/lib/ruic/statemachine.rb +3 -2
- data/lib/ruic/version.rb +2 -2
- data/ruic.gemspec +25 -25
- data/test/MetaData-simple.xml +28 -28
- data/test/MetaData.xml +435 -435
- data/test/customclasses.ruic +21 -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/FutureAsset.uip +17 -17
- data/test/projects/Paths/Paths.uia +4 -0
- data/test/projects/Paths/Paths.uip +98 -0
- 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 +29 -3
data/lib/ruic.rb
CHANGED
@@ -1,176 +1,176 @@
|
|
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.uia(opts[:uia]) if opts[:uia]
|
55
|
-
ruic.env.eval(script,opts[:script])
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
if opts[:repl]
|
60
|
-
location = (ruic && ruic.app && ruic.app.respond_to?(:file) && ruic.app.file) || opts[:uia] || opts[:script] || '.'
|
61
|
-
Dir.chdir( File.dirname(location) ) do
|
62
|
-
ruic ||= self.new.tap{ |r| r.uia(opts[:uia]) if opts[:uia] }
|
63
|
-
require 'ripl/irb'
|
64
|
-
require 'ripl/multi_line'
|
65
|
-
require 'ripl/multi_line/live_error.rb'
|
66
|
-
require_relative 'ruic/ripl-after-result'
|
67
|
-
Ripl::MultiLine.engine = Ripl::MultiLine::LiveError
|
68
|
-
Ripl::Shell.include Ripl::MultiLine.engine
|
69
|
-
Ripl::Shell.include Ripl::AfterResult
|
70
|
-
Ripl.config.merge! prompt:"", result_prompt:'#=> ', multi_line_prompt:' ', irb_verbose:false, after_result:"\n"
|
71
|
-
ARGV.clear # So that RIPL doesn't try to interpret the options
|
72
|
-
puts "(RUIC v#{RUIC::VERSION} interactive session; 'quit' or ctrl-d to end)"
|
73
|
-
ruic.instance_eval{ puts @apps.map{ |n,app| "(#{n} is #{app.inspect})" } }
|
74
|
-
puts "" # blank line before first input
|
75
|
-
Ripl.start binding:ruic.env
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Creates a new environment for executing a RUIC script.
|
81
|
-
# @param metadata [String] Path to the `MetaData.xml` file to use.
|
82
|
-
def initialize( metadata=DEFAULTMETADATA )
|
83
|
-
@metadata = metadata
|
84
|
-
@apps = {}
|
85
|
-
end
|
86
|
-
|
87
|
-
# Set the metadata to use; generally called from the RUIC DSL.
|
88
|
-
# @param path [String] Path to the `MetaData.xml` file, either absolute or relative to the working directory.
|
89
|
-
def metadata(path)
|
90
|
-
@metadata = path
|
91
|
-
end
|
92
|
-
|
93
|
-
# Load an application, making it available as `app`, `app2`, etc.
|
94
|
-
# @param path [String] Path to the `*.uia` application file.
|
95
|
-
# @return [UIC::Application] The new application loaded.
|
96
|
-
def uia(path)
|
97
|
-
meta = UIC.MetaData @metadata
|
98
|
-
name = @apps.empty? ? :app : :"app#{@apps.length+1}"
|
99
|
-
@apps[name] = UIC.Application(meta,path)
|
100
|
-
end
|
101
|
-
|
102
|
-
# @return [Binding] the shared binding used for evaluating the script and REPL
|
103
|
-
def env
|
104
|
-
@env ||= binding
|
105
|
-
end
|
106
|
-
|
107
|
-
# @private used as a one-off
|
108
|
-
module SelfInspecting; def inspect; to_s; end; end
|
109
|
-
|
110
|
-
# Used to resolve bare `app` and `app2` calls to a loaded {UIC::Application Application}.
|
111
|
-
# @return [UIC::Application] the new application loaded.
|
112
|
-
def method_missing(name,*a)
|
113
|
-
@apps[name] || (name=~/^app\d*/ ? "(no #{name} loaded)".extend(SelfInspecting) : super)
|
114
|
-
end
|
115
|
-
|
116
|
-
# Simple assertion mechanism to be used within scripts.
|
117
|
-
#
|
118
|
-
# @example 1) simple call syntax
|
119
|
-
# # Provides a generic failure message
|
120
|
-
# assert a==b
|
121
|
-
# #=> assertion failed (my.ruic line 17)
|
122
|
-
#
|
123
|
-
# # Provides a custom failure message
|
124
|
-
# assert a==b, "a should equal b"
|
125
|
-
# #=> a should equal b : assertion failed (my.ruic line 17)
|
126
|
-
#
|
127
|
-
# @example 2) block with string syntax
|
128
|
-
# # The code in the string to eval is also the failure message
|
129
|
-
# assert{ "a==b" }
|
130
|
-
# #=> a==b : assertion failed (my.ruic line 17)
|
131
|
-
#
|
132
|
-
# @param condition [Boolean] the value to evaluate.
|
133
|
-
# @param msg [String] the nice error message to display.
|
134
|
-
# @yieldreturn [String] the code to evaluate as a condition.
|
135
|
-
def assert(condition=:CONDITIONNOTSUPPLIED,msg=nil,&block)
|
136
|
-
if block && condition==:CONDITIONNOTSUPPLIED
|
137
|
-
msg = yield
|
138
|
-
condition = msg.is_a?(String) ? eval(msg,block.binding) : msg
|
139
|
-
end
|
140
|
-
condition || begin
|
141
|
-
file, line, _ = caller.first.split(':')
|
142
|
-
puts "#{"#{msg} : " unless msg.nil?}assertion failed (#{file} line #{line})"
|
143
|
-
exit 1
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
# Nicer name for `puts` to be used in the DSL, printing the
|
148
|
-
# 'nice' string equivalent for all supplied arguments.
|
149
|
-
def show(*a); puts *a.map(&:to_s); end
|
150
|
-
|
151
|
-
def inspect
|
152
|
-
"<RUIC #{@apps.empty? ? "(no app loaded)" : Hash[ @apps.map{ |id,app| [id,File.basename(app.file)] } ]}>"
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# Run a series of commands inside the RUIC DSL.
|
157
|
-
#
|
158
|
-
# @example
|
159
|
-
# require 'ruic'
|
160
|
-
# RUIC do
|
161
|
-
# uia 'test/MyProject/MyProject.uia'
|
162
|
-
# show app
|
163
|
-
# #=>UIC::Application 'MyProject.uia'>
|
164
|
-
# end
|
165
|
-
#
|
166
|
-
# If no block is supplied, this is the same as {RUIC.run RUIC.run(opts)}.
|
167
|
-
# @option opts [String] :uia Optionally load an application before running the script.
|
168
|
-
def RUIC(opts={},&block)
|
169
|
-
if block
|
170
|
-
Dir.chdir(File.dirname($0)) do
|
171
|
-
RUIC.new.tap{ |r| r.uia(opts[:uia]) if opts[:uia] }.instance_eval(&block)
|
172
|
-
end
|
173
|
-
else
|
174
|
-
RUIC.run(opts)
|
175
|
-
end
|
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.uia(opts[:uia]) if opts[:uia]
|
55
|
+
ruic.env.eval(script,opts[:script])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if opts[:repl]
|
60
|
+
location = (ruic && ruic.app && ruic.app.respond_to?(:file) && ruic.app.file) || opts[:uia] || opts[:script] || '.'
|
61
|
+
Dir.chdir( File.dirname(location) ) do
|
62
|
+
ruic ||= self.new.tap{ |r| r.uia(opts[:uia]) if opts[:uia] }
|
63
|
+
require 'ripl/irb'
|
64
|
+
require 'ripl/multi_line'
|
65
|
+
require 'ripl/multi_line/live_error.rb'
|
66
|
+
require_relative 'ruic/ripl-after-result'
|
67
|
+
Ripl::MultiLine.engine = Ripl::MultiLine::LiveError
|
68
|
+
Ripl::Shell.include Ripl::MultiLine.engine
|
69
|
+
Ripl::Shell.include Ripl::AfterResult
|
70
|
+
Ripl.config.merge! prompt:"", result_prompt:'#=> ', multi_line_prompt:' ', irb_verbose:false, after_result:"\n"
|
71
|
+
ARGV.clear # So that RIPL doesn't try to interpret the options
|
72
|
+
puts "(RUIC v#{RUIC::VERSION} interactive session; 'quit' or ctrl-d to end)"
|
73
|
+
ruic.instance_eval{ puts @apps.map{ |n,app| "(#{n} is #{app.inspect})" } }
|
74
|
+
puts "" # blank line before first input
|
75
|
+
Ripl.start binding:ruic.env
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a new environment for executing a RUIC script.
|
81
|
+
# @param metadata [String] Path to the `MetaData.xml` file to use.
|
82
|
+
def initialize( metadata=DEFAULTMETADATA )
|
83
|
+
@metadata = metadata
|
84
|
+
@apps = {}
|
85
|
+
end
|
86
|
+
|
87
|
+
# Set the metadata to use; generally called from the RUIC DSL.
|
88
|
+
# @param path [String] Path to the `MetaData.xml` file, either absolute or relative to the working directory.
|
89
|
+
def metadata(path)
|
90
|
+
@metadata = path
|
91
|
+
end
|
92
|
+
|
93
|
+
# Load an application, making it available as `app`, `app2`, etc.
|
94
|
+
# @param path [String] Path to the `*.uia` application file.
|
95
|
+
# @return [UIC::Application] The new application loaded.
|
96
|
+
def uia(path)
|
97
|
+
meta = UIC.MetaData @metadata
|
98
|
+
name = @apps.empty? ? :app : :"app#{@apps.length+1}"
|
99
|
+
@apps[name] = UIC.Application(meta,path)
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [Binding] the shared binding used for evaluating the script and REPL
|
103
|
+
def env
|
104
|
+
@env ||= binding
|
105
|
+
end
|
106
|
+
|
107
|
+
# @private used as a one-off
|
108
|
+
module SelfInspecting; def inspect; to_s; end; end
|
109
|
+
|
110
|
+
# Used to resolve bare `app` and `app2` calls to a loaded {UIC::Application Application}.
|
111
|
+
# @return [UIC::Application] the new application loaded.
|
112
|
+
def method_missing(name,*a)
|
113
|
+
@apps[name] || (name=~/^app\d*/ ? "(no #{name} loaded)".extend(SelfInspecting) : super)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Simple assertion mechanism to be used within scripts.
|
117
|
+
#
|
118
|
+
# @example 1) simple call syntax
|
119
|
+
# # Provides a generic failure message
|
120
|
+
# assert a==b
|
121
|
+
# #=> assertion failed (my.ruic line 17)
|
122
|
+
#
|
123
|
+
# # Provides a custom failure message
|
124
|
+
# assert a==b, "a should equal b"
|
125
|
+
# #=> a should equal b : assertion failed (my.ruic line 17)
|
126
|
+
#
|
127
|
+
# @example 2) block with string syntax
|
128
|
+
# # The code in the string to eval is also the failure message
|
129
|
+
# assert{ "a==b" }
|
130
|
+
# #=> a==b : assertion failed (my.ruic line 17)
|
131
|
+
#
|
132
|
+
# @param condition [Boolean] the value to evaluate.
|
133
|
+
# @param msg [String] the nice error message to display.
|
134
|
+
# @yieldreturn [String] the code to evaluate as a condition.
|
135
|
+
def assert(condition=:CONDITIONNOTSUPPLIED,msg=nil,&block)
|
136
|
+
if block && condition==:CONDITIONNOTSUPPLIED
|
137
|
+
msg = yield
|
138
|
+
condition = msg.is_a?(String) ? eval(msg,block.binding) : msg
|
139
|
+
end
|
140
|
+
condition || begin
|
141
|
+
file, line, _ = caller.first.split(':')
|
142
|
+
puts "#{"#{msg} : " unless msg.nil?}assertion failed (#{file} line #{line})"
|
143
|
+
exit 1
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Nicer name for `puts` to be used in the DSL, printing the
|
148
|
+
# 'nice' string equivalent for all supplied arguments.
|
149
|
+
def show(*a); puts *a.map(&:to_s); end
|
150
|
+
|
151
|
+
def inspect
|
152
|
+
"<RUIC #{@apps.empty? ? "(no app loaded)" : Hash[ @apps.map{ |id,app| [id,File.basename(app.file)] } ]}>"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Run a series of commands inside the RUIC DSL.
|
157
|
+
#
|
158
|
+
# @example
|
159
|
+
# require 'ruic'
|
160
|
+
# RUIC do
|
161
|
+
# uia 'test/MyProject/MyProject.uia'
|
162
|
+
# show app
|
163
|
+
# #=>UIC::Application 'MyProject.uia'>
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# If no block is supplied, this is the same as {RUIC.run RUIC.run(opts)}.
|
167
|
+
# @option opts [String] :uia Optionally load an application before running the script.
|
168
|
+
def RUIC(opts={},&block)
|
169
|
+
if block
|
170
|
+
Dir.chdir(File.dirname($0)) do
|
171
|
+
RUIC.new.tap{ |r| r.uia(opts[:uia]) if opts[:uia] }.instance_eval(&block)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
RUIC.run(opts)
|
175
|
+
end
|
176
176
|
end
|
data/lib/ruic/application.rb
CHANGED
@@ -164,7 +164,11 @@ class UIC::Application
|
|
164
164
|
# layer4 = app.main/"Scene.Layer"
|
165
165
|
#
|
166
166
|
# assert layer1==layer2 && layer2==layer3 && layer3==layer4
|
167
|
-
#
|
167
|
+
#
|
168
|
+
# @return [MetaData::AssetBase] The found asset, or `nil` if it cannot be found.
|
169
|
+
#
|
170
|
+
# @see Presentation#at
|
171
|
+
# @see MetaData::AssetBase#at
|
168
172
|
def at(path)
|
169
173
|
parts = path.split(':')
|
170
174
|
preso = parts.length==2 ? self["##{parts.first}"] : main_presentation
|
data/lib/ruic/assets.rb
CHANGED
@@ -1,290 +1,313 @@
|
|
1
|
-
#encoding: utf-8
|
2
|
-
class UIC::MetaData
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def
|
46
|
-
presentation.
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
def
|
76
|
-
presentation.
|
77
|
-
end
|
78
|
-
|
79
|
-
def
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
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
|
-
@by_name
|
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
|
-
end
|
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
|
-
@asset
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
end
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
@
|
277
|
-
end
|
278
|
-
def
|
279
|
-
@asset
|
280
|
-
end
|
281
|
-
def
|
282
|
-
@asset
|
283
|
-
end
|
284
|
-
def
|
285
|
-
asset.
|
286
|
-
end
|
287
|
-
def
|
288
|
-
|
289
|
-
end
|
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
|
+
attr_accessor :presentation, :el
|
51
|
+
def initialize( presentation, element )
|
52
|
+
@presentation = presentation
|
53
|
+
@el = element
|
54
|
+
end
|
55
|
+
|
56
|
+
def type
|
57
|
+
self.class.name
|
58
|
+
# self.class.name.split('::').last
|
59
|
+
end
|
60
|
+
|
61
|
+
def parent
|
62
|
+
presentation.parent_asset(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def children
|
66
|
+
presentation.child_assets(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
def find(criteria={},&block)
|
70
|
+
criteria[:_under] ||= self
|
71
|
+
presentation.find(criteria,&block)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Find the owning component (even if you are a component)
|
75
|
+
def component
|
76
|
+
presentation.owning_component(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
def component?
|
80
|
+
@el.name == 'Component'
|
81
|
+
end
|
82
|
+
|
83
|
+
def master?
|
84
|
+
presentation.master?(self)
|
85
|
+
end
|
86
|
+
|
87
|
+
def slide?
|
88
|
+
false
|
89
|
+
end
|
90
|
+
|
91
|
+
def has_slide?(slide_name_or_index)
|
92
|
+
presentation.has_slide?(self,slide_name_or_index)
|
93
|
+
end
|
94
|
+
|
95
|
+
def slides
|
96
|
+
presentation.slides_for(self)
|
97
|
+
end
|
98
|
+
|
99
|
+
def on_slide(slide_name_or_index)
|
100
|
+
if has_slide?(slide_name_or_index)
|
101
|
+
UIC::SlideValues.new( self, slide_name_or_index )
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def path
|
106
|
+
@path ||= @presentation.path_to(self)
|
107
|
+
end
|
108
|
+
|
109
|
+
def name
|
110
|
+
properties['name'].get( self, presentation.slide_index(self) )
|
111
|
+
end
|
112
|
+
|
113
|
+
def name=( new_name )
|
114
|
+
properties['name'].set( self, new_name, presentation.slide_index(self) )
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get the value(s) of an attribute
|
118
|
+
def [](attribute_name, slide_name_or_index=nil)
|
119
|
+
if property = properties[attribute_name]
|
120
|
+
if slide_name_or_index
|
121
|
+
property.get( self, slide_name_or_index ) if has_slide?(slide_name_or_index)
|
122
|
+
else
|
123
|
+
UIC::ValuesPerSlide.new(@presentation,self,property)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Set the value of an attribute, either across all slides, or on a particular slide
|
129
|
+
# el['foo'] = 42
|
130
|
+
# el['foo',0] = 42
|
131
|
+
def []=( attribute_name, slide_name_or_index=nil, new_value )
|
132
|
+
if property = properties[attribute_name] then
|
133
|
+
property.set(self,new_value,slide_name_or_index)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_xml
|
138
|
+
@el.to_xml
|
139
|
+
end
|
140
|
+
def inspect
|
141
|
+
"<asset #{@el.name}##{@el['id']}>"
|
142
|
+
end
|
143
|
+
|
144
|
+
def to_s
|
145
|
+
"<#{type} #{path}>"
|
146
|
+
end
|
147
|
+
|
148
|
+
def ==(other)
|
149
|
+
(self.class==other.class) && (el==other.el)
|
150
|
+
end
|
151
|
+
alias_method :eql?, :==
|
152
|
+
end
|
153
|
+
|
154
|
+
attr_reader :by_name
|
155
|
+
|
156
|
+
HIER = {}
|
157
|
+
|
158
|
+
%w[Asset Slide Scene].each{ |s| HIER[s] = 'AssetBase' }
|
159
|
+
%w[Node Behavior Effect Image Layer MaterialBase PathAnchorPoint RenderPlugin].each{ |s| HIER[s]='Asset' }
|
160
|
+
%w[Alias Camera Component Group Light Model Text Path].each{ |s| HIER[s]='Node' }
|
161
|
+
%w[Material ReferencedMaterial].each{ |s| HIER[s]='MaterialBase' }
|
162
|
+
|
163
|
+
def initialize(xml)
|
164
|
+
|
165
|
+
@by_name = {'AssetBase'=>AssetBase}
|
166
|
+
|
167
|
+
doc = Nokogiri.XML(xml)
|
168
|
+
hack_in_slide_names!(doc)
|
169
|
+
|
170
|
+
HIER.each do |class_name,parent_class_name|
|
171
|
+
parent_class = @by_name[parent_class_name]
|
172
|
+
el = doc.root.at(class_name)
|
173
|
+
@by_name[class_name] = create_class(el,parent_class,el.name)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Extend well-known classes with script interfaces after they are created
|
177
|
+
@by_name['State'] = @by_name['Slide']
|
178
|
+
@by_name['Slide'].instance_eval do
|
179
|
+
attr_accessor :index, :name
|
180
|
+
define_method :inspect do
|
181
|
+
"<slide ##{index} of #{@el['component'] || @el.parent['component']}>"
|
182
|
+
end
|
183
|
+
define_method(:slide?){ true }
|
184
|
+
end
|
185
|
+
|
186
|
+
refmat = @by_name['ReferencedMaterial']
|
187
|
+
@by_name['MaterialBase'].instance_eval do
|
188
|
+
define_method :replace_with_referenced_material do
|
189
|
+
type=='ReferencedMaterial' ? self : presentation.replace_asset( self, 'ReferencedMaterial', name:name )
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
@by_name['Path'].instance_eval do
|
194
|
+
define_method(:anchors){ find _type:'PathAnchorPoint' }
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
# Creates a class from MetaData.xml with accessors for the <Property> listed.
|
200
|
+
# Instances of the class are associated with a presentation and know how to
|
201
|
+
# get/set values in that XML based on value types, slides, defaults.
|
202
|
+
# Also used to create classes from effects, materials, and behavior preambles.
|
203
|
+
def create_class(el,parent_class,name='CustomAsset')
|
204
|
+
Class.new(parent_class) do
|
205
|
+
@name = name.to_s
|
206
|
+
@properties = Hash[ el.css("Property").map do |e|
|
207
|
+
type = e['type'] || (e['list'] ? 'String' : 'Float')
|
208
|
+
type = "Float" if type=="float"
|
209
|
+
property = UIC::Property.const_get(type).new(e)
|
210
|
+
[ property.name, UIC::Property.const_get(type).new(e) ]
|
211
|
+
end ]
|
212
|
+
def self.inspect
|
213
|
+
@name
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def new_instance(presentation,el)
|
219
|
+
klass = @by_name[el.name] || create_class(el,@by_name['Asset'],el.name)
|
220
|
+
klass.new(presentation,el)
|
221
|
+
end
|
222
|
+
|
223
|
+
def hack_in_slide_names!(doc)
|
224
|
+
doc.at('Slide') << '<Property name="name" formalName="Name" type="String" default="Slide" hidden="True" />'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def UIC.MetaData(metadata_path)
|
229
|
+
UIC::MetaData.new(File.read(metadata_path,encoding:'utf-8'))
|
230
|
+
end
|
231
|
+
|
232
|
+
class UIC::SlideCollection
|
233
|
+
include Enumerable
|
234
|
+
attr_reader :length
|
235
|
+
def initialize(slides)
|
236
|
+
@length = slides.length-1
|
237
|
+
@slides = slides
|
238
|
+
@lookup = {}
|
239
|
+
slides.each do |s|
|
240
|
+
@lookup[s.index] = s
|
241
|
+
@lookup[s.name] = s
|
242
|
+
end
|
243
|
+
end
|
244
|
+
def each
|
245
|
+
@slides.each{ |s| yield(s) }
|
246
|
+
end
|
247
|
+
def [](index_or_name)
|
248
|
+
@lookup[ index_or_name ]
|
249
|
+
end
|
250
|
+
def inspect
|
251
|
+
"[ #{@slides.map(&:inspect).join ', '} ]"
|
252
|
+
end
|
253
|
+
def to_ary
|
254
|
+
@slides
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
class UIC::ValuesPerSlide
|
259
|
+
def initialize(presentation,asset,property)
|
260
|
+
raise unless presentation.is_a?(UIC::Presentation)
|
261
|
+
|
262
|
+
raise unless asset.is_a?(UIC::MetaData::AssetBase)
|
263
|
+
raise unless property.is_a?(UIC::Property)
|
264
|
+
@preso = presentation
|
265
|
+
@asset = asset
|
266
|
+
@el = asset.el
|
267
|
+
@property = property
|
268
|
+
end
|
269
|
+
def value
|
270
|
+
values.first
|
271
|
+
end
|
272
|
+
def [](slide_name_or_index)
|
273
|
+
@property.get( @asset, slide_name_or_index )
|
274
|
+
end
|
275
|
+
def []=(slide_name_or_index,new_value)
|
276
|
+
@property.set( @asset, new_value, slide_name_or_index )
|
277
|
+
end
|
278
|
+
def linked?
|
279
|
+
@preso.attribute_linked?( @asset, @property.name )
|
280
|
+
end
|
281
|
+
def unlink
|
282
|
+
@preso.unlink_attribute( @asset, @property.name )
|
283
|
+
end
|
284
|
+
def link
|
285
|
+
@preso.link_attribute( @asset, @property.name )
|
286
|
+
end
|
287
|
+
def values
|
288
|
+
@asset.slides.map{ |s| self[s.name] }
|
289
|
+
end
|
290
|
+
def inspect
|
291
|
+
"<Values of '#{@asset.name}.#{@property.name}' across slides>"
|
292
|
+
end
|
293
|
+
alias_method :to_s, :inspect
|
294
|
+
end
|
295
|
+
|
296
|
+
class UIC::SlideValues
|
297
|
+
def initialize( asset, slide )
|
298
|
+
@asset = asset
|
299
|
+
@slide = slide
|
300
|
+
end
|
301
|
+
def [](attribute_name)
|
302
|
+
@asset[attribute_name,@slide]
|
303
|
+
end
|
304
|
+
def []=( attribute_name, new_value )
|
305
|
+
@asset[attribute_name,@slide] = new_value
|
306
|
+
end
|
307
|
+
def method_missing( name, *args, &blk )
|
308
|
+
asset.send(name,*args,&blk)
|
309
|
+
end
|
310
|
+
def inspect
|
311
|
+
"<#{@asset.inspect} on slide #{@slide.inspect}>"
|
312
|
+
end
|
290
313
|
end
|