mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-09 06:04:59 +00:00
Refactored scrollbar into its own control
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
# TODO: Do I need to split self's bitmap into two (one for highlights and one
|
# TODO: Do I need to split self's bitmap into two (one for highlights and one
|
||||||
# for text/slider)? This would be to reduce lag caused by redrawing text
|
# for text)? This would be to reduce lag caused by redrawing text even if
|
||||||
# and the slider even if you're just waving the mouse over the control.
|
# you're just waving the mouse over the control. There doesn't seem to be
|
||||||
# There doesn't seem to be any lag at the moment with a tall list.
|
# any lag at the moment with a tall list.
|
||||||
|
# TODO: Make a viewport for the list, and allow scrolling positions halfway
|
||||||
|
# through a line? Nah.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
class UIControls::List < UIControls::BaseControl
|
class UIControls::List < UIControls::BaseControl
|
||||||
LIST_X = 0
|
LIST_X = 0
|
||||||
@@ -10,26 +12,44 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
ROW_HEIGHT = 24
|
ROW_HEIGHT = 24
|
||||||
TEXT_PADDING_X = 4
|
TEXT_PADDING_X = 4
|
||||||
TEXT_OFFSET_Y = 3
|
TEXT_OFFSET_Y = 3
|
||||||
SLIDER_WIDTH = 16
|
|
||||||
|
|
||||||
SELECTED_ROW_COLOR = Color.green
|
SELECTED_ROW_COLOR = Color.green
|
||||||
|
|
||||||
def initialize(width, height, viewport, values = [])
|
def initialize(width, height, viewport, values = [])
|
||||||
super(width, height, viewport)
|
super(width, height, viewport)
|
||||||
|
@slider = UIControls::Scrollbar.new(LIST_X + width - UIControls::Scrollbar::SLIDER_WIDTH, LIST_Y, height, viewport)
|
||||||
|
@slider.set_interactive_rects
|
||||||
|
@slider.range = ROW_HEIGHT
|
||||||
|
@slider.z = self.z + 1
|
||||||
@rows_count = (height / ROW_HEIGHT).floor # Number of rows visible at once
|
@rows_count = (height / ROW_HEIGHT).floor # Number of rows visible at once
|
||||||
@top_row = 0
|
@top_row = 0
|
||||||
@selected = -1
|
@selected = -1
|
||||||
@show_slider = false
|
|
||||||
self.values = values
|
self.values = values
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def dispose
|
||||||
|
@slider.dispose
|
||||||
|
@slider = nil
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def x=(new_val)
|
||||||
|
super(new_val)
|
||||||
|
@slider.x = new_val + LIST_X + width - UIControls::Scrollbar::SLIDER_WIDTH
|
||||||
|
end
|
||||||
|
|
||||||
|
def y=(new_val)
|
||||||
|
super(new_val)
|
||||||
|
@slider.y = new_val + LIST_Y
|
||||||
|
end
|
||||||
|
|
||||||
# Each value in @values is an array: [id, text].
|
# Each value in @values is an array: [id, text].
|
||||||
def values=(new_vals)
|
def values=(new_vals)
|
||||||
@values = new_vals
|
@values = new_vals
|
||||||
@show_slider = (@values.length > @rows_count)
|
|
||||||
set_interactive_rects
|
set_interactive_rects
|
||||||
if @show_slider
|
@slider.range = @values.length * ROW_HEIGHT
|
||||||
self.top_row = @top_row
|
if @slider.visible
|
||||||
|
self.top_row = (@slider.position.to_f / ROW_HEIGHT).round
|
||||||
else
|
else
|
||||||
self.top_row = 0
|
self.top_row = 0
|
||||||
end
|
end
|
||||||
@@ -40,9 +60,8 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
def top_row=(val)
|
def top_row=(val)
|
||||||
old_val = @top_row
|
old_val = @top_row
|
||||||
@top_row = val
|
@top_row = val
|
||||||
if @show_slider
|
if @slider.visible
|
||||||
@top_row = @top_row.clamp(0, @values.length - @rows_count)
|
@top_row = @top_row.clamp(0, @values.length - @rows_count)
|
||||||
@slider.y = lerp(0, height - @slider.height, @values.length - @rows_count, 0, @top_row).round
|
|
||||||
else
|
else
|
||||||
@top_row = 0
|
@top_row = 0
|
||||||
end
|
end
|
||||||
@@ -63,14 +82,6 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
|
|
||||||
def set_interactive_rects
|
def set_interactive_rects
|
||||||
@interactions = {}
|
@interactions = {}
|
||||||
@slider = nil
|
|
||||||
if @show_slider
|
|
||||||
@slider = Rect.new(LIST_X + width - SLIDER_WIDTH, LIST_Y,
|
|
||||||
SLIDER_WIDTH, height * @rows_count / @values.length)
|
|
||||||
@interactions[:slider] = @slider
|
|
||||||
@slider_tray = Rect.new(LIST_X + width - SLIDER_WIDTH, LIST_Y, SLIDER_WIDTH, height)
|
|
||||||
@interactions[:slider_tray] = @slider_tray
|
|
||||||
end
|
|
||||||
@values.length.times do |i|
|
@values.length.times do |i|
|
||||||
@interactions[i] = Rect.new(LIST_X, LIST_Y + (ROW_HEIGHT * i), width - LIST_X, ROW_HEIGHT)
|
@interactions[i] = Rect.new(LIST_X, LIST_Y + (ROW_HEIGHT * i), width - LIST_X, ROW_HEIGHT)
|
||||||
end
|
end
|
||||||
@@ -86,14 +97,9 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
|
|
||||||
def draw_area_highlight
|
def draw_area_highlight
|
||||||
# If a row is captured, it will automatically be selected and the selection
|
# If a row is captured, it will automatically be selected and the selection
|
||||||
# colour will be drawn over the highlight. The slider tray background
|
# colour will be drawn over the highlight. There's no point drawing a
|
||||||
# (white) is drawn over the slider/slider tray's highlight. Either way,
|
# highlight at all if anything is captured.
|
||||||
# there's no point drawing a highlight at all if anything is captured.
|
|
||||||
return if @captured_area
|
return if @captured_area
|
||||||
# The slider tray background (white) is drawn over the slider/slider tray's
|
|
||||||
# highlight. There's no point drawing any highlight for the slider now; this
|
|
||||||
# is done in def refresh instead.
|
|
||||||
return if [:slider, :slider_tray].include?(@hover_area)
|
|
||||||
# Draw mouse hover over row highlight
|
# Draw mouse hover over row highlight
|
||||||
rect = @interactions[@hover_area]
|
rect = @interactions[@hover_area]
|
||||||
if rect
|
if rect
|
||||||
@@ -103,6 +109,11 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def repaint
|
||||||
|
@slider.repaint if @slider.invalid?
|
||||||
|
super if invalid?
|
||||||
|
end
|
||||||
|
|
||||||
def refresh
|
def refresh
|
||||||
super
|
super
|
||||||
# Draw text options
|
# Draw text options
|
||||||
@@ -121,15 +132,6 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
@interactions[i].y + TEXT_OFFSET_Y - (@top_row * ROW_HEIGHT),
|
@interactions[i].y + TEXT_OFFSET_Y - (@top_row * ROW_HEIGHT),
|
||||||
val[1])
|
val[1])
|
||||||
end
|
end
|
||||||
# Draw vertical slider
|
|
||||||
if @show_slider
|
|
||||||
self.bitmap.fill_rect(@slider_tray.x, @slider_tray.y, @slider_tray.width, @slider_tray.height, Color.white)
|
|
||||||
bar_color = self.bitmap.font.color
|
|
||||||
if @captured_area == :slider || (!@captured_area && @hover_area == :slider)
|
|
||||||
bar_color = HOVER_COLOR
|
|
||||||
end
|
|
||||||
self.bitmap.fill_rect(@slider.x + 1, @slider.y, @slider.width - 1, @slider.height, bar_color)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
@@ -138,16 +140,7 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
@captured_area = nil
|
@captured_area = nil
|
||||||
mouse_x, mouse_y = mouse_pos
|
mouse_x, mouse_y = mouse_pos
|
||||||
return if !mouse_x || !mouse_y
|
return if !mouse_x || !mouse_y
|
||||||
# Check for mouse presses on slider/slider tray
|
return if @slider.visible && (@slider.busy? || mouse_x >= @slider.x - self.x)
|
||||||
@interactions.each_pair do |area, rect|
|
|
||||||
next if area.is_a?(Integer)
|
|
||||||
next if !rect.contains?(mouse_x, mouse_y)
|
|
||||||
@captured_area = area
|
|
||||||
@slider_mouse_offset = mouse_y - rect.y if area == :slider
|
|
||||||
invalidate
|
|
||||||
break
|
|
||||||
end
|
|
||||||
return if @captured_area
|
|
||||||
# Check for mouse presses on rows
|
# Check for mouse presses on rows
|
||||||
mouse_y += @top_row * ROW_HEIGHT
|
mouse_y += @top_row * ROW_HEIGHT
|
||||||
@interactions.each_pair do |area, rect|
|
@interactions.each_pair do |area, rect|
|
||||||
@@ -161,7 +154,7 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
|
|
||||||
def on_mouse_release
|
def on_mouse_release
|
||||||
return if !@captured_area # Wasn't captured to begin with
|
return if !@captured_area # Wasn't captured to begin with
|
||||||
set_changed if @captured_area != :slider
|
set_changed
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -174,28 +167,24 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
@hover_area = nil
|
@hover_area = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
# Don't update the highlight if the mouse is using the scrollbar
|
||||||
|
if @slider.visible && (@slider.busy? || mouse_x >= @slider.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
|
# Check each interactive area for whether the mouse is hovering over it, and
|
||||||
# set @hover_area accordingly
|
# set @hover_area accordingly
|
||||||
in_area = false
|
in_area = false
|
||||||
|
mouse_y += @top_row * ROW_HEIGHT
|
||||||
@interactions.each_pair do |area, rect|
|
@interactions.each_pair do |area, rect|
|
||||||
next if area.is_a?(Integer)
|
next if !area.is_a?(Integer) || area < @top_row || area >= @top_row + @rows_count
|
||||||
next if !rect.contains?(mouse_x, mouse_y)
|
next if !rect.contains?(mouse_x, mouse_y)
|
||||||
invalidate if @hover_area != area
|
invalidate if @hover_area != area
|
||||||
@hover_area = area
|
@hover_area = area
|
||||||
in_area = true
|
in_area = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if !in_area
|
|
||||||
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
|
|
||||||
end
|
|
||||||
if !in_area
|
if !in_area
|
||||||
invalidate if @hover_area
|
invalidate if @hover_area
|
||||||
@hover_area = nil
|
@hover_area = nil
|
||||||
@@ -204,26 +193,14 @@ class UIControls::List < UIControls::BaseControl
|
|||||||
|
|
||||||
def update
|
def update
|
||||||
return if !self.visible
|
return if !self.visible
|
||||||
|
@slider.update
|
||||||
super
|
super
|
||||||
# TODO: Disabled control stuff.
|
# TODO: Disabled control stuff.
|
||||||
# return if self.disabled
|
# return if self.disabled
|
||||||
if @captured_area == :slider
|
# Refresh the list's position if changed by moving the slider
|
||||||
# TODO: Have a display y position for the slider bar which is in pixels,
|
self.top_row = (@slider.position.to_f / ROW_HEIGHT).round
|
||||||
# and round it to the nearest row when setting @top_row? This is
|
# Set the selected row to the row the mouse is over, if clicked on
|
||||||
# just to make the slider bar movement smoother.
|
if @captured_area
|
||||||
mouse_x, mouse_y = mouse_pos
|
|
||||||
return if !mouse_x || !mouse_y
|
|
||||||
self.top_row = lerp(0, @values.length - @rows_count, height - @slider.height, 0, mouse_y - @slider_mouse_offset).round
|
|
||||||
elsif @captured_area == :slider_tray
|
|
||||||
if Input.repeat?(Input::MOUSELEFT) && @hover_area == :slider_tray
|
|
||||||
if mouse_y < @slider.y
|
|
||||||
self.top_row = @top_row - (@rows_count / 2)
|
|
||||||
else
|
|
||||||
self.top_row = @top_row + (@rows_count / 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif @captured_area
|
|
||||||
# Have clicked on a row; set the selected row to the row themouse is over
|
|
||||||
@selected = @hover_area if @hover_area.is_a?(Integer)
|
@selected = @hover_area if @hover_area.is_a?(Integer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
143
Data/Scripts/905_New controls/101_scrollbar.rb
Normal file
143
Data/Scripts/905_New controls/101_scrollbar.rb
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#===============================================================================
|
||||||
|
# TODO: Make the slider a separate sprite that moves, instead of redrawing this
|
||||||
|
# sprite's bitmap whenever it moves? Intended to reduce lag. There doesn't
|
||||||
|
# seem to be any lag at the moment with a tall scrollbar.
|
||||||
|
#===============================================================================
|
||||||
|
class UIControls::Scrollbar < UIControls::BaseControl
|
||||||
|
SLIDER_WIDTH = 16
|
||||||
|
WIDTH_PADDING = 0
|
||||||
|
TRAY_COLOR = Color.white
|
||||||
|
SLIDER_COLOR = Color.black
|
||||||
|
GRAB_COLOR = HOVER_COLOR # Cyan
|
||||||
|
|
||||||
|
def initialize(x, y, size, viewport, horizontal = false, always_visible = false)
|
||||||
|
if horizontal
|
||||||
|
super(size, SLIDER_WIDTH, viewport)
|
||||||
|
else
|
||||||
|
super(SLIDER_WIDTH, size, viewport)
|
||||||
|
end
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
@horizontal = horizontal # Is vertical if not horizontal
|
||||||
|
@tray_size = size # Number of pixels the scrollbar can move around in
|
||||||
|
@slider_size = size
|
||||||
|
@range = size # Total distance of the area this scrollbar is for
|
||||||
|
@slider_top = 0 # Top pixel within @size of the scrollbar
|
||||||
|
@visible = @always_visible
|
||||||
|
@always_visible = always_visible
|
||||||
|
end
|
||||||
|
|
||||||
|
def position
|
||||||
|
return 0 if @range <= @tray_size
|
||||||
|
return (@range - @tray_size) * @slider_top / (@tray_size - @slider_size)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Range is the total size of the large area that the scrollbar is able to
|
||||||
|
# show part of.
|
||||||
|
def range=(new_val)
|
||||||
|
raise "Can't set a scrollbar's range to 0!" if new_val == 0
|
||||||
|
@range = new_val
|
||||||
|
@slider_size = (@tray_size * [@tray_size.to_f / @range, 1].min).round
|
||||||
|
if @horizontal
|
||||||
|
@slider.width = @slider_size
|
||||||
|
else # Vertical
|
||||||
|
@slider.height = @slider_size
|
||||||
|
end
|
||||||
|
self.slider_top = @slider_top
|
||||||
|
self.visible = (@always_visible || @range > @tray_size)
|
||||||
|
invalidate
|
||||||
|
end
|
||||||
|
|
||||||
|
def slider_top=(new_val)
|
||||||
|
old_val = @slider_top
|
||||||
|
@slider_top = new_val.clamp(0, @tray_size - @slider_size)
|
||||||
|
if @horizontal
|
||||||
|
@slider.x = @slider_top
|
||||||
|
else # Vertical
|
||||||
|
@slider.y = @slider_top
|
||||||
|
end
|
||||||
|
invalidate if @slider_top != old_val
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_interactive_rects
|
||||||
|
@interactions = {}
|
||||||
|
if @horizontal
|
||||||
|
@slider = Rect.new(@slider_top, WIDTH_PADDING, @slider_size, height - (WIDTH_PADDING * 2))
|
||||||
|
else # Vertical
|
||||||
|
@slider = Rect.new(WIDTH_PADDING, @slider_top, width - (WIDTH_PADDING * 2), @slider_size)
|
||||||
|
end
|
||||||
|
@interactions[:slider] = @slider
|
||||||
|
@slider_tray = Rect.new(0, 0, width, height)
|
||||||
|
@interactions[:slider_tray] = @slider_tray
|
||||||
|
end
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def refresh
|
||||||
|
super
|
||||||
|
return if !self.visible
|
||||||
|
# Draw the tray
|
||||||
|
self.bitmap.fill_rect(@slider_tray.x, @slider_tray.y, @slider_tray.width, @slider_tray.height, TRAY_COLOR)
|
||||||
|
# Draw the slider
|
||||||
|
if @slider_size < @tray_size
|
||||||
|
bar_color = SLIDER_COLOR
|
||||||
|
if @captured_area == :slider || (!@captured_area && @hover_area == :slider)
|
||||||
|
bar_color = GRAB_COLOR
|
||||||
|
end
|
||||||
|
self.bitmap.fill_rect(@slider.x, @slider.y, @slider.width, @slider.height, bar_color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def on_mouse_press
|
||||||
|
@captured_area = nil
|
||||||
|
mouse_x, mouse_y = mouse_pos
|
||||||
|
return if !mouse_x || !mouse_y
|
||||||
|
# Check for mouse presses on slider/slider tray
|
||||||
|
@interactions.each_pair do |area, rect|
|
||||||
|
next if !rect.contains?(mouse_x, mouse_y)
|
||||||
|
@captured_area = area
|
||||||
|
if area == :slider
|
||||||
|
if @horizontal
|
||||||
|
@slider_mouse_offset = mouse_x - rect.x
|
||||||
|
else
|
||||||
|
@slider_mouse_offset = mouse_y - rect.y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
invalidate
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_mouse_release
|
||||||
|
super if @captured_area
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
return if !self.visible
|
||||||
|
super
|
||||||
|
# TODO: Disabled control stuff.
|
||||||
|
# return if self.disabled
|
||||||
|
if @captured_area == :slider
|
||||||
|
# TODO: Have a display y position for the slider bar which is in pixels,
|
||||||
|
# and round it to the nearest row when setting @top_row? This is
|
||||||
|
# just to make the slider bar movement smoother.
|
||||||
|
mouse_x, mouse_y = mouse_pos
|
||||||
|
return if !mouse_x || !mouse_y
|
||||||
|
long_coord = (@horizontal) ? mouse_x : mouse_y
|
||||||
|
self.slider_top = long_coord - @slider_mouse_offset
|
||||||
|
elsif @captured_area == :slider_tray
|
||||||
|
if Input.repeat?(Input::MOUSELEFT) && @hover_area == :slider_tray
|
||||||
|
mouse_x, mouse_y = mouse_pos
|
||||||
|
return if !mouse_x || !mouse_y
|
||||||
|
long_coord = (@horizontal) ? mouse_x : mouse_y
|
||||||
|
if long_coord < @slider_top
|
||||||
|
self.slider_top = @slider_top - ((@tray_size - @slider_size) / 4.0).ceil
|
||||||
|
else
|
||||||
|
self.slider_top = @slider_top + ((@tray_size - @slider_size) / 4.0).ceil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -45,6 +45,10 @@ class AnimationEditorLoadScreen
|
|||||||
end
|
end
|
||||||
@animations.push([id, name])
|
@animations.push([id, name])
|
||||||
end
|
end
|
||||||
|
# TODO: For slider testing purposes.
|
||||||
|
rand(400).times do |i|
|
||||||
|
@animations.push([42 + i, "Extra animation #{i + 1}"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def draw_editor_background
|
def draw_editor_background
|
||||||
|
|||||||
Reference in New Issue
Block a user