gui 0.0.1.pre1

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.
@@ -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