136 lines
4.0 KiB
GDScript
136 lines
4.0 KiB
GDScript
class_name ScrollBar90
|
|
extends Control
|
|
## Вертикальный скролл: тонкая дорожка, аккуратный скруглённый ползунок,
|
|
## едва заметные шевроны. Ширина 14 px сохранена для раскладки.
|
|
|
|
signal value_changed(value: float)
|
|
|
|
const W := 14.0
|
|
const ARROW := 14.0
|
|
|
|
@export var page := 100.0
|
|
@export var max_value := 100.0
|
|
|
|
var value := 0.0:
|
|
set(v):
|
|
var nv := clampf(v, 0.0, _limit())
|
|
if is_equal_approx(nv, value):
|
|
return
|
|
value = nv
|
|
queue_redraw()
|
|
value_changed.emit(value)
|
|
|
|
var _dragging := false
|
|
var _drag_offset := 0.0
|
|
var _hover_thumb := false
|
|
|
|
|
|
func _ready() -> void:
|
|
custom_minimum_size = Vector2(W, 48)
|
|
mouse_filter = Control.MOUSE_FILTER_STOP
|
|
mouse_default_cursor_shape = Control.CURSOR_ARROW
|
|
mouse_entered.connect(queue_redraw)
|
|
mouse_exited.connect(func() -> void:
|
|
_hover_thumb = false
|
|
queue_redraw())
|
|
|
|
|
|
func _limit() -> float:
|
|
return maxf(0.0, max_value - page)
|
|
|
|
|
|
func set_metrics(p_page: float, p_max: float, p_value: float = -1.0) -> void:
|
|
page = maxf(1.0, p_page)
|
|
max_value = maxf(page, p_max)
|
|
if p_value >= 0.0:
|
|
value = clampf(p_value, 0.0, _limit())
|
|
else:
|
|
value = clampf(value, 0.0, _limit())
|
|
queue_redraw()
|
|
|
|
|
|
func _track_rect() -> Rect2:
|
|
return Rect2(5, ARROW, 4, maxf(0.0, size.y - ARROW * 2.0))
|
|
|
|
|
|
func _thumb_rect() -> Rect2:
|
|
var track_h := size.y - ARROW * 2.0
|
|
var lim := _limit()
|
|
var ratio := clampf(page / maxf(1.0, max_value), 0.0, 1.0)
|
|
var th := clampf(track_h * ratio, 24.0, track_h)
|
|
if lim <= 0.0:
|
|
return Rect2(4, ARROW, 6, track_h)
|
|
var t := (value / lim) * (track_h - th)
|
|
return Rect2(4, ARROW + t, 6, th)
|
|
|
|
|
|
func _draw() -> void:
|
|
if _limit() <= 0.0:
|
|
# нечего прокручивать — не рисуем цветную полосу
|
|
return
|
|
var track := _track_rect()
|
|
draw_style_box(Theme90.fill_box(Color(Theme90.SHADE.r, Theme90.SHADE.g, Theme90.SHADE.b,
|
|
0.12), 2), track)
|
|
# шевроны-подсказки, очень тихие
|
|
Theme90.draw_chevron(self, Vector2(W * 0.5, ARROW * 0.5 - 1), 0,
|
|
Color(Theme90.SHADE.r, Theme90.SHADE.g, Theme90.SHADE.b, 0.55), 2.6, 1.2)
|
|
Theme90.draw_chevron(self, Vector2(W * 0.5, size.y - ARROW * 0.5 + 1), 1,
|
|
Color(Theme90.SHADE.r, Theme90.SHADE.g, Theme90.SHADE.b, 0.55), 2.6, 1.2)
|
|
|
|
var tb := _thumb_rect()
|
|
var active := _hover_thumb or _dragging
|
|
var col := Color(Theme90.ACCENT.r, Theme90.ACCENT.g, Theme90.ACCENT.b, 0.62) \
|
|
if active else Color(Theme90.SHADE.r, Theme90.SHADE.g, Theme90.SHADE.b, 0.30)
|
|
draw_style_box(Theme90.box(col, Color(0, 0, 0, 0), 3, 0), tb)
|
|
if tb.size.y >= 20.0:
|
|
var gy := roundf(tb.get_center().y)
|
|
for i in 3:
|
|
draw_rect(Rect2(tb.get_center().x - 1, gy - 3 + i * 3, 2, 1),
|
|
Color(1, 1, 1, 0.65))
|
|
|
|
|
|
func _gui_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseButton:
|
|
if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
|
|
value -= 32.0
|
|
accept_event()
|
|
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
|
|
value += 32.0
|
|
accept_event()
|
|
elif event.button_index == MOUSE_BUTTON_LEFT:
|
|
if event.pressed:
|
|
if event.position.y < ARROW:
|
|
value -= 24.0
|
|
accept_event()
|
|
return
|
|
if event.position.y > size.y - ARROW:
|
|
value += 24.0
|
|
accept_event()
|
|
return
|
|
var tb := _thumb_rect()
|
|
if tb.has_point(event.position):
|
|
_dragging = true
|
|
_drag_offset = event.position.y - tb.position.y
|
|
accept_event()
|
|
else:
|
|
value += page if event.position.y > tb.position.y else -page
|
|
accept_event()
|
|
else:
|
|
_dragging = false
|
|
accept_event()
|
|
elif event is InputEventMouseMotion:
|
|
if _dragging:
|
|
var track_h := size.y - ARROW * 2.0
|
|
var tb := _thumb_rect()
|
|
var lim := _limit()
|
|
if lim > 0.0 and track_h - tb.size.y > 0.0:
|
|
var t := clampf(event.position.y - _drag_offset - ARROW, 0.0,
|
|
track_h - tb.size.y)
|
|
value = (t / (track_h - tb.size.y)) * lim
|
|
accept_event()
|
|
else:
|
|
var hover := _thumb_rect().grow(3.0).has_point(event.position)
|
|
if hover != _hover_thumb:
|
|
_hover_thumb = hover
|
|
queue_redraw()
|