rubygoo 0.0.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.
- data/History.txt +10 -0
- data/Manifest.txt +37 -0
- data/README.txt +81 -0
- data/Rakefile +35 -0
- data/TODO +10 -0
- data/docs/.DS_Store +0 -0
- data/docs/Picture 1.png +0 -0
- data/docs/ui/.DS_Store +0 -0
- data/docs/ui/UI.hpp +425 -0
- data/docs/ui/abutton.png +0 -0
- data/docs/ui/background.png +0 -0
- data/docs/ui/button.png +0 -0
- data/docs/ui/moveable.png +0 -0
- data/docs/ui/textbutton.png +0 -0
- data/lib/code_statistics.rb +107 -0
- data/lib/platform.rb +15 -0
- data/lib/rubygoo.rb +19 -0
- data/lib/rubygoo/adapters/adapter_factory.rb +14 -0
- data/lib/rubygoo/adapters/gosu_app_adapter.rb +50 -0
- data/lib/rubygoo/adapters/gosu_render_adapter.rb +53 -0
- data/lib/rubygoo/adapters/rubygame_app_adapter.rb +37 -0
- data/lib/rubygoo/adapters/rubygame_render_adapter.rb +51 -0
- data/lib/rubygoo/app.rb +80 -0
- data/lib/rubygoo/button.rb +53 -0
- data/lib/rubygoo/check_box.rb +74 -0
- data/lib/rubygoo/container.rb +130 -0
- data/lib/rubygoo/css_colors.rb +151 -0
- data/lib/rubygoo/dialog.rb +40 -0
- data/lib/rubygoo/goo_color.rb +8 -0
- data/lib/rubygoo/goo_event.rb +11 -0
- data/lib/rubygoo/label.rb +42 -0
- data/lib/rubygoo/text_field.rb +301 -0
- data/lib/rubygoo/widget.rb +144 -0
- data/samples/gosu_app.rb +102 -0
- data/samples/rubygame_app.rb +102 -0
- data/themes/default/Vera.ttf +0 -0
- data/themes/default/config.yml +20 -0
- metadata +121 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
class Label < Widget
|
2
|
+
def initialize(text, opts={})
|
3
|
+
super opts
|
4
|
+
@text = text
|
5
|
+
end
|
6
|
+
|
7
|
+
def added()
|
8
|
+
font = theme_property :font
|
9
|
+
@font_size = theme_property :font_size
|
10
|
+
@color = theme_property :color
|
11
|
+
@bg_color = theme_property :bg_color
|
12
|
+
@focus_color = theme_property :focus_color
|
13
|
+
@border_color = theme_property :border_color
|
14
|
+
@font_file = File.join(@app.theme_dir,font)
|
15
|
+
|
16
|
+
set_text @text
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_text(text)
|
20
|
+
@text = text
|
21
|
+
@rendered_text = @app.renderer.render_text @text, @font_file, @font_size, @color
|
22
|
+
@rect = Rect.new [@x,@y,@rendered_text.width+@x_pad,@rendered_text.height+@y_pad]
|
23
|
+
end
|
24
|
+
|
25
|
+
def draw(screen)
|
26
|
+
if @focussed
|
27
|
+
screen.fill @focus_color, @rect
|
28
|
+
elsif @bg_color
|
29
|
+
screen.fill @bg_color, @rect
|
30
|
+
end
|
31
|
+
|
32
|
+
if @border_color
|
33
|
+
x1 = @rect[0]
|
34
|
+
y1 = @rect[1]
|
35
|
+
x2 = @rect[2] + x1
|
36
|
+
y2 = @rect[3] + y1
|
37
|
+
screen.draw_box x1, y1, x2, y2, @border_color
|
38
|
+
end
|
39
|
+
|
40
|
+
screen.draw_image @rendered_text, @x, @y
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
class TextField < Widget
|
2
|
+
SPACE = " "
|
3
|
+
|
4
|
+
def initialize(text, opts={})
|
5
|
+
super opts
|
6
|
+
@text = text
|
7
|
+
|
8
|
+
# TODO make widget respect these?
|
9
|
+
@max_length = opts[:max_length]
|
10
|
+
@min_length = opts[:min_length]
|
11
|
+
|
12
|
+
@key_handlers = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Allows for registering callbacks for various key presses.
|
16
|
+
# note: this will override default behavior such as K_RIGHT and
|
17
|
+
# such
|
18
|
+
# Example:
|
19
|
+
# text_field.on_key K_RETURN, K_KP_ENTER do |evt|
|
20
|
+
# puts "BOO-YAH"
|
21
|
+
# end
|
22
|
+
def on_key(*keys, &block)
|
23
|
+
for key in keys
|
24
|
+
@key_handlers[key] ||= []
|
25
|
+
@key_handlers[key] << block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def added()
|
30
|
+
font = theme_property :font
|
31
|
+
@font_size = theme_property :font_size
|
32
|
+
@color = theme_property :color
|
33
|
+
@fg_color = theme_property :fg_color
|
34
|
+
@bg_color = theme_property :bg_color
|
35
|
+
@bg_select_color = theme_property :bg_select_color
|
36
|
+
@fg_select_color = theme_property :fg_select_color
|
37
|
+
@focus_color = theme_property :focus_color
|
38
|
+
@border_color = theme_property :border_color
|
39
|
+
|
40
|
+
@font_file = File.join(@app.theme_dir,font)
|
41
|
+
|
42
|
+
if @w == 1
|
43
|
+
# we have the default from widget, we want to set the
|
44
|
+
# default to 6 chars wide and 1 char tall
|
45
|
+
size = @app.renderer.size_text "MMMMMM", @font_file, @font_size
|
46
|
+
@min_w = size[0]
|
47
|
+
@min_h = size[1]
|
48
|
+
end
|
49
|
+
|
50
|
+
set_text @text
|
51
|
+
end
|
52
|
+
|
53
|
+
def render()
|
54
|
+
if @text.empty?
|
55
|
+
w = [@w,@min_w].max + @x_pad
|
56
|
+
h = [@h,@min_h].max + @y_pad
|
57
|
+
@rendered_text = nil
|
58
|
+
@rect = Rect.new [@x,@y,w,h]
|
59
|
+
else
|
60
|
+
@rendered_text = @app.renderer.render_text @text, @font_file, @font_size, @color
|
61
|
+
w = [@rendered_text.width,@min_w].max + @x_pad
|
62
|
+
h = [@rendered_text.height,@min_h].max + @y_pad
|
63
|
+
@rect = Rect.new [@x,@y,w,h]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_text(text)
|
68
|
+
if text.nil?
|
69
|
+
@text = ""
|
70
|
+
@caret_pos = 0
|
71
|
+
@select_pos = 0
|
72
|
+
else
|
73
|
+
@text = text
|
74
|
+
@caret_pos = text.length
|
75
|
+
@select_pos = @caret_pos
|
76
|
+
end
|
77
|
+
|
78
|
+
render
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def draw(screen)
|
83
|
+
screen.fill @fg_color, @rect
|
84
|
+
screen.fill @bg_color, [@rect[0]-2,@rect[1]-2,@rect[2]+4,@rect[3]+4]
|
85
|
+
if @border_color
|
86
|
+
x1 = @rect[0] - 2
|
87
|
+
y1 = @rect[1] - 2
|
88
|
+
x2 = @rect[2] + x1 + 4
|
89
|
+
y2 = @rect[3] + y1 + 4
|
90
|
+
screen.draw_box x1, y1, x2, y2, @border_color
|
91
|
+
end
|
92
|
+
defaultY = @app.renderer.size_text(@text.slice(0,1),@font_file,@font_size)[1]
|
93
|
+
x,y,w,h = @rect
|
94
|
+
if @focussed
|
95
|
+
caretX = @app.renderer.size_text(@text.slice(0,@caret_pos),@font_file,@font_size)[0]
|
96
|
+
unless @select_pos.nil?
|
97
|
+
# draw selection highlight
|
98
|
+
selectX = @app.renderer.size_text(@text.slice(0,@select_pos),@font_file,@font_size)[0]
|
99
|
+
selectX0 = [caretX, selectX].min
|
100
|
+
selectX1 = [caretX, selectX].max
|
101
|
+
if selectX0 < selectX1
|
102
|
+
# TODO cache this height
|
103
|
+
screen.fill(@bg_select_color, [x+1+selectX0, y+1, selectX1-selectX0, defaultY])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
unless @text.nil? or @text.empty?
|
109
|
+
@rendered_text = @app.renderer.render_text @text, @font_file, @font_size, @color
|
110
|
+
screen.draw_image @rendered_text, x+1, y+1
|
111
|
+
end
|
112
|
+
|
113
|
+
# draw caret
|
114
|
+
if @focussed
|
115
|
+
screen.fill(@fg_select_color, [x+1+caretX, y+1, 1, defaultY])
|
116
|
+
end
|
117
|
+
|
118
|
+
# don't really need this??
|
119
|
+
return @rect
|
120
|
+
end
|
121
|
+
|
122
|
+
def on_unfocus()
|
123
|
+
@caret_pos = 0
|
124
|
+
@select_pos = 0
|
125
|
+
end
|
126
|
+
|
127
|
+
def on_focus()
|
128
|
+
@select_pos = @text.length
|
129
|
+
@caret_pos = @text.length
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_mouse_position(pos)
|
133
|
+
# put hit position in window relative coords
|
134
|
+
x = pos[0] - @rect[0]
|
135
|
+
y = pos[1] - @rect[1]
|
136
|
+
|
137
|
+
# find the horizontal position within the text by binary search
|
138
|
+
l,r = 0, @text.length
|
139
|
+
c = 0
|
140
|
+
while l < r
|
141
|
+
c = (l + r + 1) / 2
|
142
|
+
w = @app.renderer.size_text(@text.slice(l,c-l), @font_file, @font_size)[0]
|
143
|
+
if x >= w
|
144
|
+
l = c
|
145
|
+
x -= w
|
146
|
+
else
|
147
|
+
if r == c
|
148
|
+
if x > w / 2
|
149
|
+
l = l + 1
|
150
|
+
end
|
151
|
+
break
|
152
|
+
end
|
153
|
+
|
154
|
+
r = c
|
155
|
+
end
|
156
|
+
end
|
157
|
+
return l
|
158
|
+
end
|
159
|
+
|
160
|
+
def mouse_down(event)
|
161
|
+
x = event.data[:x]
|
162
|
+
y = event.data[:y]
|
163
|
+
return 0 unless contains?([x,y])
|
164
|
+
|
165
|
+
# TODO rework focus and mouse events
|
166
|
+
# getFocus()
|
167
|
+
@caret_pos = find_mouse_position([x,y])
|
168
|
+
@select_pos = @caret_pos
|
169
|
+
@dragging = true
|
170
|
+
render
|
171
|
+
end
|
172
|
+
|
173
|
+
def mouse_motion(event)
|
174
|
+
return 0 unless @dragging
|
175
|
+
x = event.data[:x]
|
176
|
+
y = event.data[:y]
|
177
|
+
@caret_pos = find_mouse_position([x,y])
|
178
|
+
render
|
179
|
+
end
|
180
|
+
|
181
|
+
def mouse_up(event)
|
182
|
+
if not @dragging
|
183
|
+
return 0
|
184
|
+
end
|
185
|
+
x = event.data[:x]
|
186
|
+
y = event.data[:y]
|
187
|
+
@caret_pos = find_mouse_position([x,y])
|
188
|
+
@dragging = false
|
189
|
+
render
|
190
|
+
end
|
191
|
+
|
192
|
+
def delete_selected()
|
193
|
+
return if @select_pos == @caret_pos
|
194
|
+
if @caret_pos > @select_pos
|
195
|
+
@caret_pos, @select_pos = @select_pos, @caret_pos
|
196
|
+
end
|
197
|
+
|
198
|
+
@text = @text.slice(0, @caret_pos) +
|
199
|
+
@text.slice(@select_pos,@text.length-@select_pos)
|
200
|
+
render
|
201
|
+
@select_pos = @caret_pos
|
202
|
+
end
|
203
|
+
|
204
|
+
def key_pressed(event)
|
205
|
+
return if not @focussed
|
206
|
+
@dragging = false
|
207
|
+
|
208
|
+
mods = event.data[:mods]
|
209
|
+
handlers = @key_handlers[event.data[:key]]
|
210
|
+
unless handlers.nil?
|
211
|
+
for handler in handlers
|
212
|
+
handler.call event
|
213
|
+
end
|
214
|
+
|
215
|
+
render
|
216
|
+
return
|
217
|
+
end
|
218
|
+
|
219
|
+
case event.data[:key]
|
220
|
+
when K_LEFT
|
221
|
+
if @caret_pos > 0
|
222
|
+
@caret_pos -= 1
|
223
|
+
end
|
224
|
+
if mods.include? K_LCTRL or mods.include? K_RCTRL
|
225
|
+
while @caret_pos > 0 and @text.slice(@caret_pos - 1,1) == SPACE
|
226
|
+
@caret_pos -= 1
|
227
|
+
end
|
228
|
+
while @caret_pos > 0 and not @text.slice(@caret_pos - 1,1) == SPACE
|
229
|
+
@caret_pos -= 1
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
unless mods.include? K_LSHIFT or mods.include? K_RSHIFT
|
234
|
+
@select_pos = @caret_pos
|
235
|
+
end
|
236
|
+
|
237
|
+
when K_RIGHT
|
238
|
+
if @caret_pos < @text.length
|
239
|
+
@caret_pos += 1
|
240
|
+
end
|
241
|
+
if mods.include? K_LCTRL or mods.include? K_RCTRL
|
242
|
+
while @caret_pos < @text.length and not @text.slice(@caret_pos,1) == SPACE
|
243
|
+
@caret_pos += 1
|
244
|
+
end
|
245
|
+
while @caret_pos < @text.length and @text.slice(@caret_pos,1) == SPACE
|
246
|
+
@caret_pos += 1
|
247
|
+
end
|
248
|
+
end
|
249
|
+
unless mods.include? K_LSHIFT or mods.include? K_RSHIFT
|
250
|
+
@select_pos = @caret_pos
|
251
|
+
end
|
252
|
+
|
253
|
+
when K_HOME
|
254
|
+
@caret_pos = 0
|
255
|
+
unless mods.include? K_LSHIFT or mods.include? K_RSHIFT
|
256
|
+
@select_pos = @caret_pos
|
257
|
+
end
|
258
|
+
|
259
|
+
when K_END
|
260
|
+
@caret_pos = @text.length
|
261
|
+
unless mods.include? K_LSHIFT or mods.include? K_RSHIFT
|
262
|
+
@select_pos = @caret_pos
|
263
|
+
end
|
264
|
+
|
265
|
+
when K_BACKSPACE
|
266
|
+
if @select_pos != @caret_pos
|
267
|
+
delete_selected()
|
268
|
+
elsif @caret_pos > 0
|
269
|
+
@text = @text.slice(0,@caret_pos-1) + @text.slice(@caret_pos,@text.length-@caret_pos)
|
270
|
+
@caret_pos -= 1
|
271
|
+
@select_pos = @caret_pos
|
272
|
+
end
|
273
|
+
|
274
|
+
when K_DELETE
|
275
|
+
if @select_pos != @caret_pos:
|
276
|
+
delete_selected()
|
277
|
+
elsif @caret_pos < @text.length:
|
278
|
+
@text = @text.slice(0,@caret_pos) + @text.slice(@caret_pos+1,@text.length-@caret_pos-1)
|
279
|
+
@select_pos = @caret_pos
|
280
|
+
end
|
281
|
+
|
282
|
+
# TODO these key numbers are Rubygame specific
|
283
|
+
when (32..127)
|
284
|
+
# add regular text to the box
|
285
|
+
if @caret_pos == @select_pos
|
286
|
+
@text = @text.slice(0,@caret_pos) + event.data[:string] + @text.slice(@caret_pos,@text.length-@caret_pos)
|
287
|
+
@caret_pos += 1
|
288
|
+
else
|
289
|
+
positions = [@caret_pos,@select_pos]
|
290
|
+
min = positions.min
|
291
|
+
max = positions.max
|
292
|
+
@text = @text.slice(0,min) + event.data[:string] + @text.slice(max,@text.length-max)
|
293
|
+
@caret_pos = min + 1
|
294
|
+
end
|
295
|
+
|
296
|
+
@select_pos = @caret_pos
|
297
|
+
end
|
298
|
+
|
299
|
+
render
|
300
|
+
end
|
301
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'publisher'
|
2
|
+
require 'css_colors'
|
3
|
+
require 'goo_color'
|
4
|
+
class Widget
|
5
|
+
extend Publisher
|
6
|
+
can_fire :clicked
|
7
|
+
attr_accessor :enabled, :parent, :container,
|
8
|
+
:x, :y, :w, :h, :app, :x_pad, :y_pad, :focussed,
|
9
|
+
:focus_priority
|
10
|
+
|
11
|
+
DEFAULT_PARAMS = {
|
12
|
+
:x => 0,
|
13
|
+
:y => 0,
|
14
|
+
:w => 1,
|
15
|
+
:h => 1,
|
16
|
+
:x_pad => 2,
|
17
|
+
:y_pad => 2,
|
18
|
+
}
|
19
|
+
def initialize(opts={})
|
20
|
+
merged_opts = DEFAULT_PARAMS.merge opts
|
21
|
+
@x = merged_opts[:x]
|
22
|
+
@y = merged_opts[:y]
|
23
|
+
@x_pad = merged_opts[:x_pad]
|
24
|
+
@y_pad = merged_opts[:y_pad]
|
25
|
+
@w = merged_opts[:w]
|
26
|
+
@h = merged_opts[:h]
|
27
|
+
update_rect
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_rect()
|
31
|
+
@rect = Rect.new [@x,@y,@w+@x_pad,@h+@y_pad]
|
32
|
+
end
|
33
|
+
|
34
|
+
# called when the widget is added to a container
|
35
|
+
def added()
|
36
|
+
end
|
37
|
+
|
38
|
+
# called when the widget is removed from a container
|
39
|
+
def removed()
|
40
|
+
end
|
41
|
+
|
42
|
+
def contains?(pos)
|
43
|
+
@rect.collide_point? pos[0], pos[1]
|
44
|
+
end
|
45
|
+
|
46
|
+
def enabled?()
|
47
|
+
@enabled
|
48
|
+
end
|
49
|
+
|
50
|
+
def update(millis)
|
51
|
+
end
|
52
|
+
|
53
|
+
# draw ourself on the screen
|
54
|
+
def draw(screen)
|
55
|
+
end
|
56
|
+
|
57
|
+
# called when there is a mouse click
|
58
|
+
def mouse_down(event)
|
59
|
+
end
|
60
|
+
|
61
|
+
# called when there is a mouse motion
|
62
|
+
def mouse_motion(event)
|
63
|
+
end
|
64
|
+
|
65
|
+
# called when there is a mouse release
|
66
|
+
def mouse_up(event)
|
67
|
+
end
|
68
|
+
|
69
|
+
# called when a key press is sent to us
|
70
|
+
def key_pressed(event)
|
71
|
+
end
|
72
|
+
|
73
|
+
# called when a key release is sent to us
|
74
|
+
def key_released(event)
|
75
|
+
end
|
76
|
+
|
77
|
+
# called when the widget receives focus
|
78
|
+
def on_focus()
|
79
|
+
end
|
80
|
+
|
81
|
+
def focus()
|
82
|
+
@focussed = true
|
83
|
+
on_focus
|
84
|
+
end
|
85
|
+
|
86
|
+
# called when the widget loses focus
|
87
|
+
def on_unfocus()
|
88
|
+
end
|
89
|
+
|
90
|
+
def unfocus()
|
91
|
+
@focussed = false
|
92
|
+
on_unfocus
|
93
|
+
end
|
94
|
+
|
95
|
+
def focussed?()
|
96
|
+
@focussed
|
97
|
+
end
|
98
|
+
|
99
|
+
# gets the property for the class asking for it.
|
100
|
+
# it will walk up the object hierarchy if needed
|
101
|
+
def theme_property(prop_key)
|
102
|
+
prop = nil
|
103
|
+
parent = self.class
|
104
|
+
until parent == Object
|
105
|
+
class_theme = app.theme[parent.to_s]
|
106
|
+
if class_theme
|
107
|
+
class_prop = class_theme[prop_key]
|
108
|
+
if class_prop
|
109
|
+
prop = class_prop
|
110
|
+
break
|
111
|
+
end
|
112
|
+
end
|
113
|
+
parent = parent.superclass
|
114
|
+
end
|
115
|
+
if prop_key.to_s.match /color/i
|
116
|
+
get_color prop
|
117
|
+
else
|
118
|
+
prop
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_color(color)
|
123
|
+
new_color = nil
|
124
|
+
if color.is_a? Array
|
125
|
+
if color.size > 3
|
126
|
+
new_color = color
|
127
|
+
else
|
128
|
+
# fill in zeros for all other colors
|
129
|
+
3-color.size.times do
|
130
|
+
color << 0
|
131
|
+
end
|
132
|
+
# fill in alpha as 255
|
133
|
+
color << 255
|
134
|
+
end
|
135
|
+
elsif color.is_a? Symbol
|
136
|
+
new_color = CSS_COLORS[color]
|
137
|
+
else
|
138
|
+
raise "invalid color"
|
139
|
+
end
|
140
|
+
|
141
|
+
GooColor.new *new_color
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|