gui 0.0.1.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,66 @@
1
+ # Copyright 2014 Noel Cower
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # -----------------------------------------------------------------------------
16
+ #
17
+ # selector.rb
18
+ # Selector chain class.
19
+
20
+
21
+ module GUI
22
+
23
+ class Selector
24
+
25
+ class << self
26
+ def view_matches_attr(view, name, value)
27
+ view.respond_to?(name) && view.__send__(name) == value
28
+ end
29
+
30
+ def build(selector_str)
31
+
32
+ end
33
+ end # singleton_class
34
+
35
+ # The next
36
+ attr_accessor :succ
37
+ # Array of proc/lambda objects that receive a view and return true if
38
+ # the view matches, otherwise nil/false
39
+ attr_accessor :attributes
40
+ attr_accessor :direct
41
+
42
+ def initialize
43
+ @succ = nil
44
+ @attributes = []
45
+ @direct = false
46
+ end
47
+
48
+ # Whether this selector matches a view.
49
+ def matches?(view)
50
+ attributes.empty? || attributes.all? { |sel_attr| sel_attr[view] }
51
+ end
52
+
53
+ def find_match(view)
54
+ # TODO: Grab leaves and test selectors in reverse order
55
+ further = direct ? nil : self
56
+ if matches?(view)
57
+ return view unless @succ
58
+ further = @succ
59
+ end
60
+
61
+ view.subviews.detect { |subview| further.find_match(subview) }
62
+ end
63
+
64
+ end # Selector
65
+
66
+ end # GUI
@@ -0,0 +1,178 @@
1
+ # Copyright 2014 Noel Cower
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # -----------------------------------------------------------------------------
16
+ #
17
+ # checks.rb
18
+ # Selector checks (class/tag/attribute)
19
+
20
+
21
+ module GUI
22
+
23
+ #
24
+ # All selector checks (held by the attributes array of a selector) expect a
25
+ # call(view) method to be implemented. This returns either true or false values,
26
+ # though it's only necessary that they return things that evaluate to true or
27
+ # false. It is possible, therefore, to hand-write all your selectors rather than
28
+ # compiling them, which can be _much_ faster when performing lookups since
29
+ # attribute checks don't have to depend on reducing something by sending
30
+ # messages over and over.
31
+ #
32
+ # In practice, however, selectors are just slow in all my tests right now and I
33
+ # need to rewrite them to test starting with leaf views anyway (this should
34
+ # result in more specific matches and avoid recursive tests since it's then
35
+ # possible to iterate down through parents and cut off if a minimum depth isn't
36
+ # met [i.e., a selector with N views to match requires at least depth N, but
37
+ # can match views across depths greater than N if it has indirect matches]).
38
+ #
39
+
40
+
41
+ class ViewTagCheck
42
+
43
+ def initialize(tagname)
44
+ @tagname = tagname
45
+ end
46
+
47
+ def call(view)
48
+ view.tag == tagname
49
+ end
50
+
51
+ alias_method :[], :call
52
+
53
+ end # ViewTagCheck
54
+
55
+
56
+ class ViewClassCheck
57
+
58
+ def initialize(classname)
59
+ @classnames =
60
+ case classname
61
+ when Array then classname.dup
62
+ when Symbol then [classname]
63
+ when String then [classname.to_sym]
64
+ else raise ArgumentError, "Invalid class name type: #{classname.class}"
65
+ end
66
+ end
67
+
68
+ def call(view)
69
+ klass = view.class
70
+ name = nil
71
+ while klass
72
+ name = ViewAttrCheck.extract_class_name(klass)
73
+ return true if @classnames.include?(name)
74
+ klass = klass.superclass
75
+ end
76
+ false
77
+ end
78
+
79
+ alias_method :[], :call
80
+
81
+ end # ViewClassCheck
82
+
83
+
84
+ class ViewAttrCheck
85
+
86
+ SCO_MARKER = '::'
87
+ KEYPATH_SEPARATOR = '.'
88
+
89
+ class << self
90
+ attr_accessor :__module_name_cache__
91
+
92
+ def extract_class_name(klass)
93
+ (__module_name_cache__ ||= {})[klass] ||= begin
94
+ # Cache classname symbols because string ops are slow
95
+ name = klass.name
96
+ sco_index = klass.rindex(SCO_MARKER)
97
+ if sco_index
98
+ klass.slice!(0 .. sco_index + 1)
99
+ end
100
+ name.to_sym
101
+ end
102
+ end
103
+ end # singleton_class
104
+
105
+ def initialize(key, operator, operand)
106
+ @key = key.split(KEYPATH_SEPARATOR).map!(&:to_sym)
107
+ @operator = operator
108
+ @operand = operand
109
+ @is_string = @operand.kind_of?(String)
110
+ end
111
+
112
+ # NOTE: Deprecate and remove class checks for ViewAttrCheck? Might be a good
113
+ # idea, but it sort of remains since it's occasionally handy to do something
114
+ # like [content_view.class = Something]. Probably just going to remove this,
115
+ # though.
116
+ def class_check(klass)
117
+ # Cache Symbol for operand so I'm not converting it every time.
118
+ name = (@operand_sym ||= @operand.to_sym)
119
+
120
+ while klass
121
+ case @operator
122
+ when :equal
123
+ return true if self.class.extract_class_name(klass) == name
124
+ when :not_equal
125
+ return true unless self.class.extract_class_name(klass) == name
126
+ when :trueish # Necessarily true for classes.
127
+ true
128
+ else # Otherwise no test passes.
129
+ false
130
+ end
131
+ klass = klass.superclass
132
+ end
133
+ false
134
+ end
135
+
136
+ def call(view)
137
+ view_value = @key.reduce(view) { |value, msg| value.__send__(msg) }
138
+
139
+ if @is_string && !view_value.kind_of?(String)
140
+ view_value =
141
+ case view_value
142
+ when Class then return class_check(view_value) # return early
143
+ when Module then extract_class_name(view_value)
144
+ when Enumerable then
145
+ # There is a case here where doing something like
146
+ # `included_modules <- X` will fail because the values contained by
147
+ # the Enumerable are modules but the check won't know, so it'll just
148
+ # always fail. Thought about working around this by mapping modules
149
+ # to their extracted names, but decided I'll only do that if it turns
150
+ # out to be a problem.
151
+ return false unless @operator == :contains
152
+ view_value
153
+ else view_value.to_s
154
+ end
155
+ end
156
+
157
+ case operator
158
+ when :trueish then !!view_value
159
+ when :falseish then !view_value
160
+ when :equal then view_value == @operand
161
+ when :not_equal then view_value != @operand
162
+ when :greater then view_value > @operand
163
+ when :greater_equal then view_value >= @operand
164
+ when :lesser then view_value < @operand
165
+ when :lesser_equal then view_value <= @operand
166
+ when :contains
167
+ view_value.respond_to?(:include?) && view_value.include?(@operand)
168
+ else
169
+ raise SelectorError, "Invalid operator for ViewAttrCheck: #{operator}"
170
+ end
171
+ end
172
+
173
+ alias_method :[], :call
174
+
175
+ end # ViewAttrCheck
176
+
177
+ end # GUI
178
+
@@ -0,0 +1,103 @@
1
+ # Copyright 2014 Noel Cower
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # -----------------------------------------------------------------------------
16
+ #
17
+ # texture.rb
18
+ # Wrapper around GL texture names.
19
+
20
+
21
+ require 'stb-image'
22
+ require 'snow-data'
23
+
24
+
25
+ module GUI
26
+
27
+ class Texture
28
+
29
+ TextureName = Snow::CStruct.new { uint32_t :name }
30
+
31
+ LOAD_TEXTURE_BLOCK = -> (data, x, y, components) do
32
+ internalFormat =
33
+ case components
34
+ when STBI::COMPONENTS_GREY then Gl::GL_RED
35
+ when STBI::COMPONENTS_GREY_ALPHA then Gl::GL_RG
36
+ when STBI::COMPONENTS_RGB then Gl::GL_RGB
37
+ when STBI::COMPONENTS_RGB_ALPHA then Gl::GL_RGBA
38
+ else raise ArgumentError, "Invalid number of texture components"
39
+ end
40
+
41
+ name = TextureName.new
42
+ raise "Unable to allocate texture name" unless name
43
+
44
+ Gl::glGetIntegerv(Gl::GL_TEXTURE_BINDING_2D, name.address)
45
+ prev_name = name.name
46
+
47
+ Gl::glGenTextures(1, name.address)
48
+ Gl::glBindTexture(Gl::GL_TEXTURE_2D, name.name)
49
+
50
+ Gl::glTexParameteri(Gl::GL_TEXTURE_2D, Gl::GL_TEXTURE_WRAP_S, Gl::GL_CLAMP_TO_EDGE)
51
+ Gl::glTexParameteri(Gl::GL_TEXTURE_2D, Gl::GL_TEXTURE_WRAP_T, Gl::GL_CLAMP_TO_EDGE)
52
+ Gl::glTexParameteri(Gl::GL_TEXTURE_2D, Gl::GL_TEXTURE_MIN_FILTER, Gl::GL_LINEAR)
53
+ Gl::glTexParameteri(Gl::GL_TEXTURE_2D, Gl::GL_TEXTURE_MAG_FILTER, Gl::GL_LINEAR)
54
+
55
+ Gl::glTexImage2D(
56
+ Gl::GL_TEXTURE_2D, # target
57
+ 0, # level
58
+ format, # internal format
59
+ x, y, # width, height
60
+ 0, # border
61
+ format, # format
62
+ Gl::GL_UNSIGNED_BYTE, # typep
63
+ data
64
+ )
65
+
66
+ glBindTexture(Gl::GL_TEXTURE_2D, prev_name)
67
+
68
+ name
69
+ end
70
+
71
+
72
+ # Allocates a new texture using the given IO object.
73
+ # If a block is given, the texture is only valid in the scope of the block
74
+ # unless retained elsewhere.
75
+ def initialize(io, &block)
76
+ @name = STBI.load_image(io, STBI::COMPONENTS_DEFAULT, &LOAD_TEXTURE_BLOCK)
77
+ @refs = 0
78
+ retain(&block)
79
+ end
80
+
81
+ def retain
82
+ @refs += 1
83
+
84
+ if block_given?
85
+ yield self
86
+ release
87
+ end
88
+ end
89
+
90
+ def release
91
+ @refs -= 1
92
+ if @refs == 0
93
+ # If it's necessary to do anything else to release the object, pass it
94
+ # to a block first.
95
+ yield self if block_given?
96
+
97
+ glDeleteTextures(1, @name.address)
98
+ end
99
+ end
100
+
101
+ end # Texture
102
+
103
+ end # GUI
@@ -0,0 +1,36 @@
1
+ # Copyright 2014 Noel Cower
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # -----------------------------------------------------------------------------
16
+ #
17
+ # version.rb
18
+ # Version information for the gui gem.
19
+
20
+
21
+ module GUI
22
+
23
+ GUI_VERSION = '0.0.1.pre1'.freeze
24
+ GUI_LICENSE_BRIEF = 'Apache 2.0 License'.freeze
25
+ GUI_GEM_ROOT = File.expand_path('../../../', __FILE__).freeze
26
+
27
+ # Don't load the license unless it's needed.
28
+ define_singleton_method(:GUI_LICENSE_FULL, &-> do
29
+ File.open("#{GUI_GEM_ROOT}/COPYING") do |io|
30
+ io.read
31
+ end.freeze.tap do |license_txt|
32
+ ::GUI::set_const(:GUI_LICENSE_FULL, license_txt)
33
+ end
34
+ end)
35
+
36
+ end # GUI
@@ -0,0 +1,134 @@
1
+ # Copyright 2014 Noel Cower
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # -----------------------------------------------------------------------------
16
+ #
17
+ # widget.rb
18
+ # Base class for view types.
19
+
20
+
21
+ require 'gui/geom'
22
+
23
+
24
+ module GUI
25
+
26
+ class View
27
+
28
+ # View tag (default: nil)
29
+ attr_accessor :tag
30
+
31
+ # Subviews held by the view. Should not be modified directly. Instead, to
32
+ # add a subview, use add_view.
33
+ attr_reader :subviews
34
+
35
+ # Rectangular portion
36
+ attr_accessor :frame # Rect
37
+
38
+ def initialize(frame = nil)
39
+ @needs_layout = false
40
+ @invalidated = nil
41
+ @subviews = []
42
+ @attributes = []
43
+ @superview = nil
44
+ @tag = nil
45
+ @frame = frame || Rect.new
46
+
47
+ invalidate
48
+ request_layout
49
+ end
50
+
51
+ # Returns the containing superview of the view.
52
+ def superview
53
+ @superview
54
+ end
55
+
56
+ # Sets the containing superview of the view. This invalidates and requests
57
+ # layout on the previous superview, if any.
58
+ def superview=(new_superview)
59
+ old_superview = @superview
60
+ if !old_superview.nil?
61
+ old_superview.delete(self)
62
+ old_superview.invalidate(@frame.dup)
63
+ old_superview.request_layout
64
+ end
65
+
66
+ @superview = new_superview
67
+ if !new_superview.nil?
68
+ new_superview.children << self
69
+ end
70
+ end
71
+
72
+ def add_view(view)
73
+ raise ArgumentError, "View already has a superview" if view.superview
74
+ view.superview = self
75
+ end
76
+
77
+ def bounds
78
+ @frame.with_origin(0, 0)
79
+ end
80
+
81
+ def remove_from_superview
82
+ self.superview = nil
83
+ end
84
+
85
+ def invalidated_region
86
+ @invalidated
87
+ end
88
+
89
+ def invalidate(region = nil)
90
+ if @invalidated
91
+ @invalidated.contains_both!(region || @frame)
92
+ else
93
+ @invalidated = (region || @frame.with_origin(0, 0)).dup
94
+ end.intersection!(bounds)
95
+
96
+ self
97
+ end
98
+
99
+ def request_layout
100
+ @needs_layout = true
101
+ end
102
+
103
+ def needs_layout?
104
+ @needs_layout
105
+ end
106
+
107
+ def perform_layout
108
+ end
109
+
110
+ def view_with_tag(tag)
111
+ if @tag == tag
112
+ self
113
+ else
114
+ @subviews.detect { |subview| subview.view_with_tag(tag) }
115
+ end
116
+ end
117
+
118
+ def view_with_selector(selector)
119
+ nil
120
+ end
121
+
122
+ def [](selector)
123
+ case selector
124
+ when Symbol then view_with_tag(selector)
125
+ when Selector then view_with_selector(selector)
126
+ when String then view_with_selector(Selector.build(selector))
127
+ when Numeric then @subviews[selector]
128
+ else raise ArgumentError, "Invalid selector for View#[]"
129
+ end
130
+ end
131
+
132
+ end # View
133
+
134
+ end # GUI