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