1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
76
77
78
79
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
116
117
118
119
120
121
122
123
124
125
126
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
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
|
extends Control
var vscrollbar: VScrollBar
var labels: Array
var positions: Array
var items: Array = [
"item 1",
"item 22",
"item 333",
"item 4444",
"item 55555",
"item 666666",
"item 7777777",
"item 88888888",
"item 999999999",
"This is the longest item of all, but eventually stops.",
"item 1",
"item 22",
"item 333",
"item 4444",
"item 55555",
"item 666666",
"item 7777777",
"item 88888888",
"item 999999999",
"This is the longest item of all, but eventually stops.",
"item 1",
"item 22",
"item 333",
"item 4444",
"item 55555",
"item 666666",
"item 7777777",
"item 88888888",
"item 999999999",
"This is the longest item of all, but eventually stops.",
]
var limit := 60
var labels_pool: Array
onready var font: Font = get_font("font")
func _init():
vscrollbar = VScrollBar.new()
vscrollbar.anchor_left = 1.0
vscrollbar.anchor_bottom = 1.0
vscrollbar.grow_horizontal = Control.GROW_DIRECTION_BEGIN
vscrollbar.name = "vscrollbar"
add_child(vscrollbar)
func _ready():
var num_labels := max_required_labels()
var labels_pool_size = int(ceil(num_labels * 1.10))
for idx in range(labels_pool_size):
var label := Label.new()
label.autowrap = true
label.anchor_left = 0.0
label.anchor_right = 1.0
label.valign = Label.VALIGN_CENTER
labels_pool.push_back(label)
for idx in range(min(num_labels, items.size())):
var label = labels_pool.pop_back()
label.text = items[idx]
labels.append(label)
add_child(label)
# for it in items:
# var label := Label.new()
# label.text = it
# label.autowrap = true
# label.anchor_left = 0.0
# label.anchor_right = 1.0
# label.valign = Label.VALIGN_CENTER
# labels.push_back(label)
# add_child(label)
build_positions()
var pos: Array
func build_positions():
pos.clear()
positions.clear()
var label = labels[0]
# var label := Label.new()
# label.autowrap = true
# label.anchor_left = 0.0
# label.anchor_right = 1.0
# label.valign = Label.VALIGN_CENTER
# add_child(label)
var position := 0.0
var limit := rect_size.x
# positions.append(0)
for it in items:
var size := font.get_wordwrap_string_size(it, limit).y
var lines = size / font.get_height()
var height = size + font.get_descent() * lines
# label.text = it
# label.rect_position = Vector2(0.0, 500.0)
# label.rect_size.y = 0.0
# position += label.rect_size.y
pos.append(position)
position += height
positions.append(position)
# if label.rect_size.y != 14:
# breakpoint
# positions.append(position)
# remove_child(label)
func _process(delta):
var num_labels = max_required_labels()
var difference = num_labels - labels.size()
if difference > 0:
for idx in range(difference):
var label = labels_pool.pop_back()
labels.append(label)
add_child(label)
elif difference < 0:
for idx in range(abs(difference)):
var label = labels.pop_back()
labels_pool.append(label)
remove_child(label)
# @DAM Since the items may have different heights, we must look at the edge items
# in order to update the offsets.
build_positions()
vscrollbar.min_value = 0
vscrollbar.max_value = rect_size.y
var ratio := rect_size.y / float(positions[positions.size()-1])
vscrollbar.visible = ratio < 1.0
vscrollbar.page = ratio * (vscrollbar.max_value - vscrollbar.min_value)
var offset := vscrollbar.value
var bs_value := offset / ratio
var offset_idx := positions.bsearch(bs_value)
# offset_idx = 0
for idx in range(min(num_labels, items.size())):
if idx + offset_idx >= items.size():
labels[idx].text = ""
break
labels[idx].text = items[idx + offset_idx]
# labels[idx].text = items[min(idx + offset_idx, items.size()-1)]
var it = labels[idx].text
var limit = rect_size.x
var size := font.get_wordwrap_string_size(it, limit).y
var lines = size / font.get_height()
var height = size + font.get_descent() * lines
labels[idx].rect_size.y = height
# labels[idx].rect_size.y = font.get_wordwrap_string_size(labels[idx].text, rect_size.x).y
# labels[idx].rect_size.y = 0.0
# labels[idx].rect_position.y = positions[idx + offset_idx] - bs_value - positions[0]
labels[idx].rect_position.y = pos[idx + offset_idx] - bs_value
# if difference != 0:
# for idx in range(min(num_labels, items.size())):
# labels[idx].text = items[idx]
# if Engine.get_idle_frames() % 30 == 1:
# print_debug("> %s | %5.3f > %s > %d | lastPos: %s" % [labels.size(), offset, bs_value, offset_idx, positions[-1]])
# Adapt scrollbar according to options and drawable size.
# @DAM This does not take into consideration the items that are wrapped.
func max_required_labels() -> int:
var max_labels := ceil(rect_size.y / font.get_height()) + 1
return int(min(items.size(), max_labels))
#func _draw():
# var position := Vector2(0.0, font.get_height())
# for it in items:
# var p = font.get_wordwrap_string_size(it, limit)
# print_debug("> %s : %s" % [p, it])
# draw_string(font, position, it, Color.white, limit)
# font.get_string_size(it)
# position.y += p.y
|