glimr 0.1.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.
- data/lib/glimr.rb +2 -0
- data/lib/glimr/configurable.rb +37 -0
- data/lib/glimr/default_theme/button_bg.png +0 -0
- data/lib/glimr/default_theme/button_bg_down.png +0 -0
- data/lib/glimr/default_theme/button_cover.png +0 -0
- data/lib/glimr/default_theme/button_cover_down.png +0 -0
- data/lib/glimr/default_theme/button_focus.png +0 -0
- data/lib/glimr/default_theme/checkbox_bg.png +0 -0
- data/lib/glimr/default_theme/checkbox_checked_bg.png +0 -0
- data/lib/glimr/default_theme/font.ttf +0 -0
- data/lib/glimr/default_theme/hscroller_bg.png +0 -0
- data/lib/glimr/default_theme/radiobutton_bg.png +0 -0
- data/lib/glimr/default_theme/radiobutton_checked_bg.png +0 -0
- data/lib/glimr/default_theme/resizer_down.png +0 -0
- data/lib/glimr/default_theme/resizer_up.png +0 -0
- data/lib/glimr/default_theme/scroll_down_down.png +0 -0
- data/lib/glimr/default_theme/scroll_down_up.png +0 -0
- data/lib/glimr/default_theme/scroll_hknob_down.png +0 -0
- data/lib/glimr/default_theme/scroll_hknob_up.png +0 -0
- data/lib/glimr/default_theme/scroll_left_down.png +0 -0
- data/lib/glimr/default_theme/scroll_left_up.png +0 -0
- data/lib/glimr/default_theme/scroll_right_down.png +0 -0
- data/lib/glimr/default_theme/scroll_right_up.png +0 -0
- data/lib/glimr/default_theme/scroll_up_down.png +0 -0
- data/lib/glimr/default_theme/scroll_up_up.png +0 -0
- data/lib/glimr/default_theme/scroll_vknob_down.png +0 -0
- data/lib/glimr/default_theme/scroll_vknob_up.png +0 -0
- data/lib/glimr/default_theme/text_cursor.png +0 -0
- data/lib/glimr/default_theme/text_cursor_insert.png +0 -0
- data/lib/glimr/default_theme/text_input_bg.png +0 -0
- data/lib/glimr/default_theme/vscroller_bg.png +0 -0
- data/lib/glimr/event.rb +41 -0
- data/lib/glimr/eventlistener.rb +209 -0
- data/lib/glimr/layoutable.rb +520 -0
- data/lib/glimr/renderer.rb +2 -0
- data/lib/glimr/renderer/camera.rb +63 -0
- data/lib/glimr/renderer/geometry.rb +194 -0
- data/lib/glimr/renderer/glutwindow.rb +387 -0
- data/lib/glimr/renderer/light.rb +43 -0
- data/lib/glimr/renderer/material.rb +66 -0
- data/lib/glimr/renderer/model.rb +103 -0
- data/lib/glimr/renderer/orthoprojection.rb +21 -0
- data/lib/glimr/renderer/raw.rb +34 -0
- data/lib/glimr/renderer/sceneobject.rb +279 -0
- data/lib/glimr/renderer/shader.rb +14 -0
- data/lib/glimr/renderer/texture.rb +280 -0
- data/lib/glimr/renderer/transform.rb +322 -0
- data/lib/glimr/renderer/viewport.rb +349 -0
- data/lib/glimr/renderer_core.rb +10 -0
- data/lib/glimr/util.rb +247 -0
- data/lib/glimr/widget.rb +87 -0
- data/lib/glimr/widgets.rb +37 -0
- data/lib/glimr/widgets/button.rb +277 -0
- data/lib/glimr/widgets/checkbox.rb +82 -0
- data/lib/glimr/widgets/container.rb +84 -0
- data/lib/glimr/widgets/image.rb +82 -0
- data/lib/glimr/widgets/label.rb +91 -0
- data/lib/glimr/widgets/layout.rb +227 -0
- data/lib/glimr/widgets/list.rb +28 -0
- data/lib/glimr/widgets/radiogroup.rb +118 -0
- data/lib/glimr/widgets/resizer.rb +31 -0
- data/lib/glimr/widgets/scrollable_container.rb +67 -0
- data/lib/glimr/widgets/scrollbar.rb +496 -0
- data/lib/glimr/widgets/stretchable_image.rb +135 -0
- data/lib/glimr/widgets/text_editor.rb +349 -0
- data/tests/assets/datatowers_crop.jpg +0 -0
- data/tests/assets/download_progress_meter.png +0 -0
- data/tests/assets/metalwing2.png +0 -0
- data/tests/assets/redhairgreeneyes3.jpg +0 -0
- data/tests/demo_apps/spinning_ruby.rb +37 -0
- data/tests/integration_tests/run_all.rb +8 -0
- data/tests/integration_tests/test_button.rb +22 -0
- data/tests/integration_tests/test_checkbox.rb +21 -0
- data/tests/integration_tests/test_container.rb +22 -0
- data/tests/integration_tests/test_label.rb +12 -0
- data/tests/integration_tests/test_layout.rb +43 -0
- data/tests/integration_tests/test_radiogroup.rb +16 -0
- data/tests/integration_tests/test_renderer.rb +44 -0
- data/tests/integration_tests/test_renderer2.rb +36 -0
- data/tests/integration_tests/test_scrollable_container.rb +34 -0
- data/tests/integration_tests/test_scrollbar.rb +20 -0
- data/tests/integration_tests/test_stretchable_image.rb +34 -0
- data/tests/integration_tests/test_text_input.rb +20 -0
- data/tests/integration_tests/test_zsort.rb +18 -0
- data/tests/unit_tests/test_button.rb +93 -0
- data/tests/unit_tests/test_checkbox.rb +35 -0
- data/tests/unit_tests/test_label.rb +36 -0
- data/tests/unit_tests/test_layout.rb +229 -0
- data/tests/unit_tests/test_widget.rb +3 -0
- metadata +139 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'glimr/widget'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module GlimR
|
|
5
|
+
|
|
6
|
+
class Container < Widget
|
|
7
|
+
|
|
8
|
+
touching_accessor :scroll_x, :scroll_y, :content
|
|
9
|
+
attr_reader :content_view
|
|
10
|
+
|
|
11
|
+
def default_config
|
|
12
|
+
super.merge(
|
|
13
|
+
:scroll_x => 0,
|
|
14
|
+
:scroll_y => 0,
|
|
15
|
+
:width => 200,
|
|
16
|
+
:height => 100,
|
|
17
|
+
:fit_to_children => false
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(*a, &b)
|
|
22
|
+
@content_view = Viewport.new(
|
|
23
|
+
:x => 0, :y => 0, :width => 1, :height => 1
|
|
24
|
+
)
|
|
25
|
+
@mirror_event = method(:mirror_event).to_proc
|
|
26
|
+
super
|
|
27
|
+
self.content = @content if @content
|
|
28
|
+
@content_view.on_layout(@mirror_event)
|
|
29
|
+
attach @content_view
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def mirror_event(o,e)
|
|
33
|
+
process_event e
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def content= c
|
|
37
|
+
@content_view.detach @content if @content
|
|
38
|
+
@content.remove_event_listener(:layout, @mirror_event) if @content
|
|
39
|
+
@content = c
|
|
40
|
+
@content_view.attach @content if @content
|
|
41
|
+
@content.add_event_listener(:layout, @mirror_event) if @content
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def scroll_x=(x)
|
|
45
|
+
@scroll_x = x
|
|
46
|
+
if @content
|
|
47
|
+
case x
|
|
48
|
+
when Float
|
|
49
|
+
@content.x = -x*(@content.width-@width).to_i
|
|
50
|
+
else
|
|
51
|
+
@content.x = -x
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def scroll_y=(y)
|
|
57
|
+
@scroll_y = y
|
|
58
|
+
if @content
|
|
59
|
+
case y
|
|
60
|
+
when Float
|
|
61
|
+
@content.y = -y*(@content.height-@height).to_i
|
|
62
|
+
else
|
|
63
|
+
@content.y = -y
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def scroll
|
|
69
|
+
[@scroll_x, @scroll_y]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def scroll= s
|
|
73
|
+
self.scroll_x, self.scroll_y = *s
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def layout
|
|
77
|
+
super
|
|
78
|
+
@content_view.width = @width
|
|
79
|
+
@content_view.height = @height
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'glimr/widget'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module GlimR
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Image is a quad matching a texture's dimensions.
|
|
8
|
+
class Image < Widget
|
|
9
|
+
|
|
10
|
+
def self.load(filename)
|
|
11
|
+
image = new
|
|
12
|
+
image.load(filename)
|
|
13
|
+
image
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.load_theme(filename)
|
|
17
|
+
load(GlimR.theme + filename)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_accessor :image, :bound
|
|
21
|
+
|
|
22
|
+
def default_config
|
|
23
|
+
super.merge(
|
|
24
|
+
:fit_children => false,
|
|
25
|
+
:image => nil
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(*a,&b)
|
|
30
|
+
super
|
|
31
|
+
if @image
|
|
32
|
+
load(@image)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def texture= t
|
|
37
|
+
super
|
|
38
|
+
update_geometry
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def load(filename,update_geo=true)
|
|
42
|
+
@texture.load(filename)
|
|
43
|
+
update_geometry if update_geo
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def load_theme(filename,update_geo=true)
|
|
47
|
+
load(GlimR.theme + filename, update_geo)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def update_geometry
|
|
51
|
+
@geometry.vertices = create_vertices
|
|
52
|
+
@geometry.normals = create_normals
|
|
53
|
+
@geometry.colors = create_colors
|
|
54
|
+
@geometry.texcoords = create_texcoords
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def create_colors
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def create_vertices
|
|
62
|
+
size_changing do
|
|
63
|
+
@min_width = @width = @texture.width
|
|
64
|
+
@min_height = @height = @texture.height
|
|
65
|
+
end
|
|
66
|
+
[0,0,0, @texture.width,0,0, @texture.width,@texture.height,0, 0,@texture.height,0]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def create_normals
|
|
70
|
+
[0,0,1, 0,0,1, 0,0,1, 0,0,1]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def create_texcoords
|
|
74
|
+
hb = @texture.height / @texture.bound.to_f
|
|
75
|
+
wb = @texture.width / @texture.bound.to_f
|
|
76
|
+
[0,0, wb,0, wb,hb, 0,hb]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'glimr/widgets/image'
|
|
2
|
+
require 'sdl'
|
|
3
|
+
SDL::TTF.init
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module GlimR
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :label_font, :label_size, :label_color
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
self.label_font = GlimR.theme + 'font.ttf'
|
|
13
|
+
self.label_size = 12
|
|
14
|
+
self.label_color = [0,0,0,1]
|
|
15
|
+
|
|
16
|
+
class Label < Image
|
|
17
|
+
|
|
18
|
+
touching_accessor :font, :size, :text, :valign, :align, :color
|
|
19
|
+
|
|
20
|
+
def default_config
|
|
21
|
+
super.merge(
|
|
22
|
+
:valign => :center,
|
|
23
|
+
:align => :center,
|
|
24
|
+
:font => GlimR.label_font,
|
|
25
|
+
:size => GlimR.label_size,
|
|
26
|
+
:width => 0, :height => 0,
|
|
27
|
+
:color => GlimR.label_color
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def initialize(a,&b)
|
|
32
|
+
txt = a[:text]
|
|
33
|
+
a.delete(:text)
|
|
34
|
+
super(a, &b)
|
|
35
|
+
self.text = txt if txt
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def text= t
|
|
39
|
+
@text = t.to_s
|
|
40
|
+
sdl_font = SDL::TTF.open(font, size)
|
|
41
|
+
lines = @text.split("\n")
|
|
42
|
+
w,h = 0,0
|
|
43
|
+
lines.each{|line|
|
|
44
|
+
nw,nh = sdl_font.text_size(line)
|
|
45
|
+
w = nw if nw > w
|
|
46
|
+
}
|
|
47
|
+
h = lines.size * (sdl_font.line_skip+1)
|
|
48
|
+
|
|
49
|
+
texture.width = w
|
|
50
|
+
texture.height = h
|
|
51
|
+
rmask = 0xff000000
|
|
52
|
+
gmask = 0x00ff0000
|
|
53
|
+
bmask = 0x0000ff00
|
|
54
|
+
amask = 0x000000ff
|
|
55
|
+
mask = SDL::Surface.new(
|
|
56
|
+
SDL::SWSURFACE|SDL::SRCALPHA,
|
|
57
|
+
w, h, 32,
|
|
58
|
+
amask,bmask,gmask,rmask)
|
|
59
|
+
mask.fill_rect(0,0, w,h, [0,0,0,255])
|
|
60
|
+
lines.each_with_index{|line,i|
|
|
61
|
+
sdl_font.draw_blended_utf8(
|
|
62
|
+
mask, line,
|
|
63
|
+
0, i*(sdl_font.line_skip+1),
|
|
64
|
+
255, 255, 255
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
image = SDL::Surface.new(
|
|
68
|
+
SDL::SWSURFACE|SDL::SRCALPHA,
|
|
69
|
+
w, h, 32,
|
|
70
|
+
amask,bmask,gmask,rmask)
|
|
71
|
+
image.fill_rect(0,0,w,h, color.map{|i|(i*255).to_i})
|
|
72
|
+
texture.mode = TEXTURE_2D
|
|
73
|
+
texture.pixels = as_alpha(image.pixels, mask.pixels)
|
|
74
|
+
@width = texture.width
|
|
75
|
+
@height = texture.height
|
|
76
|
+
@bound = texture.bound
|
|
77
|
+
update_geometry
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def as_alpha(pixels, alpha)
|
|
81
|
+
i = 3
|
|
82
|
+
while i < pixels.size
|
|
83
|
+
pixels[i] = alpha[i-1]
|
|
84
|
+
i += 4
|
|
85
|
+
end
|
|
86
|
+
pixels
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
require 'glimr/widget'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module GlimR
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# A Layout is a Widget that layouts its child widgets.
|
|
8
|
+
#
|
|
9
|
+
# Layouts come in two breeds: the horizontal layout and
|
|
10
|
+
# the vertical layout. A horizontal layout (or HLayout)
|
|
11
|
+
# arranges its children in a row, while a vertical layout
|
|
12
|
+
# (a.k.a. VLayout) arranges them in a column.
|
|
13
|
+
#
|
|
14
|
+
# A layout has two alignment attributes, the #align and the
|
|
15
|
+
# #valign. Align determines where to put the children
|
|
16
|
+
# on the horizontal axis, and valign does the same on the
|
|
17
|
+
# vertical axis.
|
|
18
|
+
#
|
|
19
|
+
# Align value :left aligns the left border of the layouted widgets and
|
|
20
|
+
# pushes them to the left border of the layout.
|
|
21
|
+
# Using :right as align does the same, except for the right border.
|
|
22
|
+
# The :center align aligns the centerline of the widgets with the layout's
|
|
23
|
+
# centerline. The default align is :left.
|
|
24
|
+
#
|
|
25
|
+
# Valign takes three values as well: :top, :bottom, and :middle. The default
|
|
26
|
+
# is :top, and aligns the top border of the widgets with the top border of
|
|
27
|
+
# the layout. To align to the bottom border, use :bottom. The :middle valign
|
|
28
|
+
# aligns the vertical midpoint of the widgets with the layout's vertical
|
|
29
|
+
# midpoint.
|
|
30
|
+
#
|
|
31
|
+
# Item spacing controls the amount of space between the layouted widgets.
|
|
32
|
+
# The default item spacing is 2.
|
|
33
|
+
#
|
|
34
|
+
# A Layout fits its children by default and doesn't expand to
|
|
35
|
+
# maximum width or height.
|
|
36
|
+
#
|
|
37
|
+
class Layout < Widget
|
|
38
|
+
|
|
39
|
+
attr_accessor :item_spacing, :direction
|
|
40
|
+
|
|
41
|
+
def default_config
|
|
42
|
+
super.merge(
|
|
43
|
+
:item_spacing => 2,
|
|
44
|
+
:direction => :horizontal,
|
|
45
|
+
:fit_children => true
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def children_change
|
|
50
|
+
@free_width = @free_height = nil
|
|
51
|
+
layout
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# If direction is vertical, sets width equal to the largest child + padding.
|
|
55
|
+
# If direction is horizontal, sets width to sum of children widths + padding + item spacing.
|
|
56
|
+
def fit_width!
|
|
57
|
+
return super if direction == :vertical
|
|
58
|
+
self.width = layoutable_children.inject(padding_left+padding_right-item_spacing){|s,c|
|
|
59
|
+
s + c.full_width + item_spacing
|
|
60
|
+
} unless layoutable_children.empty?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# If direction is horizontal, sets height equal to the largest child + padding.
|
|
64
|
+
# If direction is vertical, sets height to sum of children heights + padding + item spacing.
|
|
65
|
+
def fit_height!
|
|
66
|
+
return super if direction == :horizontal
|
|
67
|
+
self.height = layoutable_children.inject(padding_top+padding_left-item_spacing){|s,c|
|
|
68
|
+
s + c.full_height + item_spacing
|
|
69
|
+
} unless layoutable_children.empty?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def layout
|
|
73
|
+
return if @layouting
|
|
74
|
+
c = layoutable_children
|
|
75
|
+
return if c.empty?
|
|
76
|
+
@layouting = true
|
|
77
|
+
size_changing do
|
|
78
|
+
@free_width = @free_height = nil
|
|
79
|
+
@id ||= 0
|
|
80
|
+
@id += 1
|
|
81
|
+
c.each{|ch|
|
|
82
|
+
ch.width = free_width-ch.margin_left-ch.margin_right if ch.expand_width
|
|
83
|
+
ch.height = free_height-ch.margin_top-ch.margin_bottom if ch.expand_height
|
|
84
|
+
}
|
|
85
|
+
fit_to_children! if fit_children?
|
|
86
|
+
if @direction == :horizontal
|
|
87
|
+
horiz_tile(c)
|
|
88
|
+
else
|
|
89
|
+
vert_tile(c)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
@layouting = false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def item_spacing= s
|
|
96
|
+
@item_spacing = s
|
|
97
|
+
layout
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def direction= d
|
|
101
|
+
@direction = d
|
|
102
|
+
layout
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Horizontally tile objects inside self.
|
|
106
|
+
def horiz_tile(objs)
|
|
107
|
+
objs_width = objs.inject(-@item_spacing){|s,o|
|
|
108
|
+
s + o.full_width + @item_spacing
|
|
109
|
+
}
|
|
110
|
+
x = case @align
|
|
111
|
+
when :left
|
|
112
|
+
@padding_left
|
|
113
|
+
when :right
|
|
114
|
+
@padding_left + inner_width - objs_width
|
|
115
|
+
when :center, :middle
|
|
116
|
+
@padding_left + (inner_width - objs_width) / 2
|
|
117
|
+
end
|
|
118
|
+
ih = inner_height
|
|
119
|
+
objs.each{|obj|
|
|
120
|
+
obj.x = x + obj.margin_left
|
|
121
|
+
obj.y = case @valign
|
|
122
|
+
when :top
|
|
123
|
+
@padding_top
|
|
124
|
+
when :bottom
|
|
125
|
+
@padding_top + ih - obj.full_height
|
|
126
|
+
when :center, :middle
|
|
127
|
+
@padding_top + (ih - obj.full_height) / 2
|
|
128
|
+
end
|
|
129
|
+
obj.z = 1
|
|
130
|
+
x += obj.full_width + @item_spacing
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Vertically tile objects inside self.
|
|
135
|
+
def vert_tile(objs)
|
|
136
|
+
objs_height = objs.inject(-@item_spacing){|s,o|
|
|
137
|
+
s + o.full_height + @item_spacing
|
|
138
|
+
}
|
|
139
|
+
y = case @align
|
|
140
|
+
when :left
|
|
141
|
+
@padding_top
|
|
142
|
+
when :right
|
|
143
|
+
@padding_top + inner_height - objs_height
|
|
144
|
+
when :center, :middle
|
|
145
|
+
@padding_top + (inner_height - objs_height) / 2
|
|
146
|
+
end
|
|
147
|
+
iw = inner_width
|
|
148
|
+
objs.each{|obj|
|
|
149
|
+
obj.y = y + obj.margin_top
|
|
150
|
+
obj.x = case @align
|
|
151
|
+
when :left
|
|
152
|
+
@padding_left
|
|
153
|
+
when :right
|
|
154
|
+
@padding_left + iw - obj.full_width
|
|
155
|
+
when :center, :middle
|
|
156
|
+
@padding_left + (iw - obj.full_width) / 2
|
|
157
|
+
end
|
|
158
|
+
obj.z = 1
|
|
159
|
+
y += obj.full_height + @item_spacing
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def free_width
|
|
164
|
+
@free_width ||= calculate_free_width
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def free_height
|
|
168
|
+
@free_height ||= calculate_free_height
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def calculate_free_width
|
|
172
|
+
return inner_width if @direction == :vertical
|
|
173
|
+
objs = layoutable_children
|
|
174
|
+
fit_width_objs, constant_width_objs = objs.partition{|o| o.expand_width }
|
|
175
|
+
if fit_width_objs.empty?
|
|
176
|
+
0.0
|
|
177
|
+
else
|
|
178
|
+
const_width = constant_width_objs.inject(0){|cw, o|
|
|
179
|
+
cw + o.full_width
|
|
180
|
+
} + (objs.size - 1) * @item_spacing
|
|
181
|
+
[0.0, (inner_width - const_width) / fit_width_objs.size].max
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def calculate_free_height
|
|
186
|
+
return inner_height if @direction == :horizontal
|
|
187
|
+
objs = layoutable_children
|
|
188
|
+
fit_height_objs, constant_height_objs = objs.partition{|o| o.expand_height}
|
|
189
|
+
if fit_height_objs.empty?
|
|
190
|
+
0.0
|
|
191
|
+
else
|
|
192
|
+
const_height = constant_height_objs.inject(0){|cw, o|
|
|
193
|
+
cw + o.full_height
|
|
194
|
+
} + (objs.size - 1) * @item_spacing
|
|
195
|
+
[0.0, (inner_height - const_height) / fit_height_objs.size].max
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class HLayout < Layout
|
|
203
|
+
|
|
204
|
+
def default_config
|
|
205
|
+
super.merge(:direction => :horizontal)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class VLayout < Layout
|
|
212
|
+
|
|
213
|
+
def default_config
|
|
214
|
+
super.merge(:direction => :vertical)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# class OverLayout < Layout
|
|
221
|
+
# end
|
|
222
|
+
#
|
|
223
|
+
#
|
|
224
|
+
# class FreeLayout < Layout
|
|
225
|
+
# end
|
|
226
|
+
|
|
227
|
+
end
|