Files
infinitefusion-e18/Data/Scripts/801_UI controls/Control elements/008_List.rb
2024-01-01 18:12:53 +00:00

238 lines
6.7 KiB
Ruby

#===============================================================================
#
#===============================================================================
class UIControls::List < UIControls::BaseControl
LIST_X = 0
LIST_Y = 0
ROW_HEIGHT = 24
TEXT_PADDING_X = 4
TEXT_OFFSET_Y = 3
SELECTED_ROW_COLOR = Color.green
def initialize(width, height, viewport, values = [])
super(width, height, viewport)
@scrollbar = UIControls::Scrollbar.new(LIST_X + width - UIControls::Scrollbar::SLIDER_WIDTH, LIST_Y, height, viewport)
@scrollbar.set_interactive_rects
@scrollbar.range = ROW_HEIGHT
@scrollbar.z = self.z + 1
@rows_count = (height / ROW_HEIGHT).floor # Number of rows visible at once
@top_row = 0
@selected = -1
self.values = values
end
def dispose
@scrollbar.dispose
@scrollbar = nil
super
end
def x=(new_val)
super(new_val)
@scrollbar.x = new_val + LIST_X + width - UIControls::Scrollbar::SLIDER_WIDTH
end
def y=(new_val)
super(new_val)
@scrollbar.y = new_val + LIST_Y
end
def z=(new_val)
super(new_val)
@scrollbar.z = new_val + 1
end
def visible=(new_val)
super
@scrollbar.visible = new_val
end
# Each value in @values is an array: [id, text].
def values=(new_vals)
@values = new_vals
set_interactive_rects
@scrollbar.range = [@values.length, 1].max * ROW_HEIGHT
if @scrollbar.visible
self.top_row = (@scrollbar.position.to_f / ROW_HEIGHT).round
else
self.top_row = 0
end
self.selected = -1 if @selected >= @values.length
invalidate
end
def top_row=(val)
old_val = @top_row
@top_row = val
if @scrollbar.visible
@top_row = @top_row.clamp(0, @values.length - @rows_count)
else
@top_row = 0
end
invalidate if @top_row != old_val
end
def selected=(val)
return if @selected == val
@selected = val
invalidate
end
# Returns the ID of the selected row.
def value
return nil if @selected < 0
if @values.is_a?(Array)
return (@values[@selected].is_a?(Array)) ? @values[@selected][0] : @selected
elsif @values.is_a?(Hash)
return @values.keys[@selected]
end
return nil
end
def mouse_in_control?
mouse_x, mouse_y = mouse_pos
return false if !mouse_x || !mouse_y
return true if Rect.new(0, 0, width, height).contains?(mouse_x, mouse_y)
return true if @scrollbar.mouse_in_control?
return false
end
def set_interactive_rects
@interactions = {}
@values.length.times do |i|
@interactions[i] = Rect.new(LIST_X, LIST_Y + (ROW_HEIGHT * i), width - LIST_X, ROW_HEIGHT)
end
end
#-----------------------------------------------------------------------------
def busy?
return !@captured_area.nil?
end
#-----------------------------------------------------------------------------
def draw_area_highlight
# If a row is captured, it will automatically be selected and the selection
# colour will be drawn over the highlight. There's no point drawing a
# highlight at all if anything is captured.
return if @captured_area
# Draw mouse hover over row highlight
rect = @interactions[@hover_area]
if rect
rect_y = rect.y
rect_y -= @top_row * ROW_HEIGHT if @hover_area.is_a?(Integer)
self.bitmap.fill_rect(rect.x, rect_y, rect.width, rect.height, HOVER_COLOR)
end
end
def repaint
@scrollbar.repaint if @scrollbar.invalid?
super if invalid?
end
def refresh
super
# Draw text options
@values.each_with_index do |val, i|
next if i < @top_row || i >= @top_row + @rows_count
if @selected == i
self.bitmap.fill_rect(
@interactions[i].x,
@interactions[i].y - (@top_row * ROW_HEIGHT),
@interactions[i].width, @interactions[i].height,
SELECTED_ROW_COLOR
)
end
txt = (val.is_a?(Array)) ? val[1] : val
draw_text(self.bitmap,
@interactions[i].x + TEXT_PADDING_X,
@interactions[i].y + TEXT_OFFSET_Y - (@top_row * ROW_HEIGHT),
txt)
end
end
#-----------------------------------------------------------------------------
def on_mouse_press
@captured_area = nil
mouse_x, mouse_y = mouse_pos
return if !mouse_x || !mouse_y
return if @scrollbar.visible && (@scrollbar.busy? || mouse_x >= @scrollbar.x - self.x)
# Check for mouse presses on rows
mouse_y += @top_row * ROW_HEIGHT
@interactions.each_pair do |area, rect|
next if !area.is_a?(Integer) || area < @top_row || area >= @top_row + @rows_count
next if !rect.contains?(mouse_x, mouse_y)
@captured_area = area
invalidate
break
end
end
def on_mouse_release
return if !@captured_area # Wasn't captured to begin with
set_changed
super
end
def update_hover_highlight
# Remove the hover highlight if there are no interactions for this control
# or if the mouse is off-screen
mouse_x, mouse_y = mouse_pos
if !@interactions || @interactions.empty? || !mouse_x || !mouse_y
invalidate if @hover_area
@hover_area = nil
return
end
# Don't update the highlight if the mouse is using the scrollbar
if @scrollbar.visible && (@scrollbar.busy? || mouse_x >= @scrollbar.x - self.x)
invalidate if @hover_area
@hover_area = nil
return
end
# Check each interactive area for whether the mouse is hovering over it, and
# set @hover_area accordingly
in_area = false
mouse_y += @top_row * ROW_HEIGHT
@interactions.each_pair do |area, rect|
next if !area.is_a?(Integer) || area < @top_row || area >= @top_row + @rows_count
next if !rect.contains?(mouse_x, mouse_y)
invalidate if @hover_area != area
@hover_area = area
in_area = true
break
end
if !in_area
invalidate if @hover_area
@hover_area = nil
end
end
def update
return if !self.visible
@scrollbar.update
super
# Refresh the list's position if changed by moving the scrollbar
self.top_row = (@scrollbar.position.to_f / ROW_HEIGHT).round
# Set the selected row to the row the mouse is over, if clicked on
if @captured_area
@selected = @hover_area if @hover_area.is_a?(Integer)
elsif @hover_area
wheel_v = Input.scroll_v
scroll_dist = UIControls::Scrollbar::SCROLL_DISTANCE
scroll_dist /= 2 if @values.length / @rows_count > 20 # Arbitrary 20
if wheel_v > 0 # Scroll up
@scrollbar.slider_top -= scroll_dist
elsif wheel_v < 0 # Scroll down
@scrollbar.slider_top += scroll_dist
end
if wheel_v != 0
self.top_row = (@scrollbar.position.to_f / ROW_HEIGHT).round
update_hover_highlight
end
end
end
end