mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-11-03 00:06:01 +00:00
Release 6.9.0
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
class MultiSelectOptionsScene
|
||||
attr_reader :selected_items
|
||||
attr_reader :canceled
|
||||
|
||||
SWITCH_FOCUS_BUTTONS = [Input::L, Input::R]
|
||||
CURSOR_X_OFFSET = -4
|
||||
ARROW_NORMAL = "Graphics/Pictures/selarrow"
|
||||
ARROW_ACTIVE = "Graphics/Pictures/selarrow_red"
|
||||
|
||||
def initialize(items, preselected = [], title = "Select options",
|
||||
item_name_proc = nil, filter_placeholder = "Shift to search",
|
||||
item_weight_proc = nil)
|
||||
@items = items
|
||||
@item_name_proc = item_name_proc || proc { |item| item.to_s }
|
||||
@item_weight_proc = item_weight_proc || proc { |_item| 1 }
|
||||
@selected_items = preselected.dup
|
||||
@selected_display_items = []
|
||||
@focus = :full # :full or :selected
|
||||
|
||||
@title_text = title
|
||||
@filter_placeholder = filter_placeholder
|
||||
@canceled = false
|
||||
end
|
||||
|
||||
def pbStartScene(window_x = 0, window_y = 0)
|
||||
title_height = 60
|
||||
filter_height = 60
|
||||
|
||||
left_width = Graphics.width / 2
|
||||
right_x = window_x + left_width
|
||||
right_width = Graphics.width - left_width
|
||||
|
||||
reference_text = @items.map { |i| @item_name_proc.call(i) }.max_by(&:length) || @filter_placeholder
|
||||
reference_text = @filter_placeholder if @filter_placeholder.length > reference_text.length
|
||||
|
||||
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
|
||||
@viewport.z = 99999
|
||||
|
||||
@sprites = {}
|
||||
@sprites["title"] = Window_UnformattedTextPokemon.newWithSize(
|
||||
_INTL(@title_text), window_x, window_y, Graphics.width, title_height, @viewport)
|
||||
|
||||
@sprites["filter"] = Window_UnformattedTextPokemon.newWithSize(
|
||||
reference_text, window_x, window_y + title_height, left_width, filter_height, @viewport)
|
||||
@sprites["filter"].setTextToFit(reference_text)
|
||||
|
||||
@sprites["option"] = Window_MultiSelectFilterList.new(
|
||||
[], window_x, window_y + title_height + filter_height,
|
||||
left_width, Graphics.height - (window_y + title_height + filter_height),
|
||||
@viewport
|
||||
)
|
||||
@sprites["option"].active = true
|
||||
pbSetSystemFont(@sprites["option"].contents)
|
||||
|
||||
# Right-side panel: selected items + running total
|
||||
@sprites["selected_title"] = Window_UnformattedTextPokemon.newWithSize(
|
||||
"", right_x, window_y + title_height, right_width, filter_height, @viewport)
|
||||
|
||||
@sprites["selected_list"] = Window_ReadOnlyList.new(
|
||||
[], right_x, window_y + title_height + filter_height,
|
||||
right_width, Graphics.height - (window_y + title_height + filter_height),
|
||||
@viewport
|
||||
)
|
||||
@sprites["selected_list"].active = false
|
||||
@sprites["selected_list"].index = 0
|
||||
|
||||
@arrow_paths = {}
|
||||
@sprites["cursor"] = IconSprite.new(0, 0, @viewport)
|
||||
@sprites["cursor"].setBitmap(ARROW_ACTIVE)
|
||||
@arrow_paths[@sprites["cursor"]] = ARROW_ACTIVE
|
||||
@sprites["cursor"].z = 99999
|
||||
|
||||
@sprites["cursor2"] = IconSprite.new(0, 0, @viewport)
|
||||
@sprites["cursor2"].setBitmap(ARROW_NORMAL)
|
||||
@arrow_paths[@sprites["cursor2"]] = ARROW_NORMAL
|
||||
@sprites["cursor2"].z = 99999
|
||||
|
||||
@filterText = ""
|
||||
@filteredItems = []
|
||||
refreshList
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------- cursors
|
||||
|
||||
def updateCursorPosition
|
||||
positionArrow(@sprites["cursor"], @sprites["option"], @focus == :full)
|
||||
positionArrow(@sprites["cursor2"], @sprites["selected_list"], @focus == :selected)
|
||||
@sprites["cursor2"].visible = false if @selected_items.empty?
|
||||
end
|
||||
|
||||
def positionArrow(arrow, win, focused)
|
||||
rect = win.itemRect(win.index)
|
||||
if rect.width == 0 && rect.height == 0 # index out of visible range
|
||||
arrow.visible = false
|
||||
return
|
||||
end
|
||||
|
||||
# Red arrow for the active list, normal arrow for the other
|
||||
path = focused ? ARROW_ACTIVE : ARROW_NORMAL
|
||||
if @arrow_paths[arrow] != path
|
||||
arrow.setBitmap(path)
|
||||
@arrow_paths[arrow] = path
|
||||
end
|
||||
|
||||
arrow_height = arrow.bitmap ? arrow.bitmap.height : 0
|
||||
y_center_offset = (win.rowHeight - arrow_height) / 2
|
||||
|
||||
arrow.x = win.x + win.startX + rect.x + CURSOR_X_OFFSET
|
||||
arrow.y = win.y + win.startY + rect.y + y_center_offset
|
||||
arrow.opacity = focused ? 255 : 110
|
||||
arrow.visible = true
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------- focus
|
||||
|
||||
def setFocus(focus)
|
||||
focus = :full if focus == :selected && @selected_items.empty?
|
||||
@focus = focus
|
||||
@sprites["option"].active = (@focus == :full)
|
||||
@sprites["selected_list"].active = (@focus == :selected)
|
||||
syncFullListToSelected if @focus == :selected
|
||||
updateCursorPosition
|
||||
end
|
||||
|
||||
def toggleFocus
|
||||
pbSEPlay("GUI naming tab swap start")
|
||||
setFocus(@focus == :full ? :selected : :full)
|
||||
end
|
||||
|
||||
def focusLatestSelected
|
||||
@sprites["selected_list"].index = 0
|
||||
updateCursorPosition
|
||||
end
|
||||
|
||||
def syncFullListToSelected
|
||||
item = @selected_display_items[@sprites["selected_list"].index]
|
||||
return if item.nil?
|
||||
idx = @filteredItems.index(item)
|
||||
return if idx.nil? # item is hidden by the current search filter
|
||||
@sprites["option"].index = itemStartIndex + idx
|
||||
updateCursorPosition
|
||||
end
|
||||
|
||||
def removeFromSelected(list_index)
|
||||
item = @selected_display_items[list_index]
|
||||
return if item.nil?
|
||||
@selected_items.delete(item)
|
||||
pbSEPlay("GUI naming tab swap end")
|
||||
keep_index = @sprites["selected_list"].index
|
||||
refreshList
|
||||
|
||||
max_index = [@selected_items.length - 1, 0].max
|
||||
@sprites["selected_list"].index = [keep_index, max_index].min
|
||||
|
||||
setFocus(:full)
|
||||
idx = @filteredItems.index(item)
|
||||
@sprites["option"].index = itemStartIndex + idx if idx
|
||||
updateCursorPosition
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------- indices
|
||||
|
||||
def cancelIndex; return 0; end
|
||||
def selectAllIndex; return 1; end
|
||||
def confirmIndex; return 2; end
|
||||
def itemStartIndex; return 3; end
|
||||
|
||||
# ---------------------------------------------------------------- refresh
|
||||
|
||||
def refreshList
|
||||
filtered = @filterText.empty? ? @items.dup : @items.select { |i|
|
||||
@item_name_proc.call(i).downcase.include?(@filterText.downcase)
|
||||
}
|
||||
@filteredItems = filtered
|
||||
|
||||
display = []
|
||||
flags = []
|
||||
|
||||
display.push(_INTL("Cancel"))
|
||||
flags.push(false)
|
||||
|
||||
all_selected = filtered.length > 0 && filtered.all? { |i| @selected_items.include?(i) }
|
||||
display.push(all_selected ? _INTL("Unselect All") : _INTL("Select All"))
|
||||
flags.push(false)
|
||||
|
||||
display.push(_INTL("Confirm"))
|
||||
flags.push(false)
|
||||
|
||||
filtered.each do |i|
|
||||
display.push(@item_name_proc.call(i))
|
||||
flags.push(@selected_items.include?(i))
|
||||
end
|
||||
|
||||
old_index = @sprites["option"].index || 0
|
||||
@sprites["option"].commands = display
|
||||
@sprites["option"].selected_flags = flags
|
||||
@sprites["option"].confirm_index = confirmIndex
|
||||
@sprites["option"].selall_index = selectAllIndex
|
||||
@sprites["option"].cancel_index = cancelIndex
|
||||
@sprites["option"].index = [old_index, display.length - 1].min
|
||||
@sprites["option"].refresh
|
||||
|
||||
@sprites["filter"].text = @filterText.empty? ? @filter_placeholder : @filterText
|
||||
refreshSelectedPanel
|
||||
updateCursorPosition
|
||||
end
|
||||
|
||||
def refreshSelectedPanel
|
||||
total_weight = @selected_items.sum { |i| @item_weight_proc.call(i) }
|
||||
@sprites["selected_title"].text = _INTL("{1} selected sprites", total_weight)
|
||||
|
||||
@selected_display_items = @selected_items.reverse
|
||||
lines = @selected_display_items.map { |i| @item_name_proc.call(i) }
|
||||
lines = [_INTL("")] if lines.empty?
|
||||
|
||||
old_index = @sprites["selected_list"].index || 0
|
||||
@sprites["selected_list"].commands = lines
|
||||
@sprites["selected_list"].index = [old_index, lines.length - 1].min
|
||||
@sprites["selected_list"].refresh
|
||||
end
|
||||
|
||||
def pbUpdate
|
||||
pbUpdateSpriteHash(@sprites)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------- main loop
|
||||
|
||||
def pbOptions
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
|
||||
if SWITCH_FOCUS_BUTTONS.any? { |b| Input.trigger?(b) }
|
||||
toggleFocus
|
||||
next
|
||||
end
|
||||
|
||||
if Input.trigger?(Input::ACTION)
|
||||
pbStartSearchEntry
|
||||
next
|
||||
end
|
||||
|
||||
if Input.trigger?(Input::BACK)
|
||||
@canceled = true
|
||||
break
|
||||
end
|
||||
|
||||
if @focus == :selected
|
||||
old_index = @sprites["selected_list"].index
|
||||
@sprites["selected_list"].update
|
||||
syncFullListToSelected if @sprites["selected_list"].index != old_index
|
||||
updateCursorPosition
|
||||
|
||||
if Input.trigger?(Input::USE)
|
||||
removeFromSelected(@sprites["selected_list"].index)
|
||||
end
|
||||
next
|
||||
end
|
||||
|
||||
# ---- full list focused ----
|
||||
old_index = @sprites["option"].index
|
||||
@sprites["option"].update
|
||||
updateCursorPosition if @sprites["option"].index != old_index
|
||||
|
||||
if Input.repeat?(Input::RIGHT)
|
||||
pbSEPlay("GUI party switch")
|
||||
scroll_amount = 8
|
||||
last = @sprites["option"].itemCount - 1
|
||||
if @sprites["option"].index + scroll_amount >= last
|
||||
@sprites["option"].index = last
|
||||
else
|
||||
@sprites["option"].index += scroll_amount
|
||||
end
|
||||
updateCursorPosition
|
||||
next
|
||||
end
|
||||
|
||||
if Input.repeat?(Input::LEFT)
|
||||
pbSEPlay("GUI party switch")
|
||||
scroll_amount = -8
|
||||
if @sprites["option"].index + scroll_amount <= 0
|
||||
scrollToConfirm
|
||||
else
|
||||
@sprites["option"].index += scroll_amount
|
||||
end
|
||||
next
|
||||
end
|
||||
|
||||
if Input.trigger?(Input::USE)
|
||||
index = @sprites["option"].index
|
||||
|
||||
if index == cancelIndex
|
||||
@canceled = true
|
||||
break
|
||||
elsif index == selectAllIndex
|
||||
all_selected = @filteredItems.length > 0 && @filteredItems.all? { |i| @selected_items.include?(i) }
|
||||
if all_selected
|
||||
@selected_items -= @filteredItems
|
||||
else
|
||||
@filteredItems.each do |item|
|
||||
@selected_items << item unless @selected_items.include?(item)
|
||||
end
|
||||
end
|
||||
pbSEPlay("GUI naming confirm")
|
||||
refreshList
|
||||
focusLatestSelected
|
||||
elsif index == confirmIndex
|
||||
break
|
||||
else
|
||||
item = @filteredItems[index - itemStartIndex]
|
||||
if @selected_items.include?(item)
|
||||
@selected_items.delete(item)
|
||||
pbSEPlay("GUI naming tab swap end")
|
||||
else
|
||||
@selected_items << item
|
||||
pbSEPlay("GUI naming confirm")
|
||||
end
|
||||
refreshList
|
||||
focusLatestSelected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def scrollToConfirm
|
||||
@sprites["option"].index = confirmIndex
|
||||
updateCursorPosition
|
||||
end
|
||||
|
||||
def pbStartSearchEntry
|
||||
if $PokemonSystem.textinput == 1 # keyboard
|
||||
scene = PokedexTextEntry.new
|
||||
else
|
||||
scene = PokemonEntryScene2.new
|
||||
end
|
||||
scene.pbStartScene(
|
||||
_INTL("Search:"),
|
||||
0, # min length (0 allows clearing the filter)
|
||||
30, # max length
|
||||
@filterText
|
||||
)
|
||||
query = scene.pbEntry
|
||||
scene.pbEndScene
|
||||
|
||||
if query
|
||||
@filterText = query
|
||||
refreshList
|
||||
syncFullListToSelected if @focus == :selected
|
||||
end
|
||||
end
|
||||
|
||||
def pbEndScene
|
||||
pbPlayCloseMenuSE
|
||||
pbDisposeSpriteHash(@sprites)
|
||||
@viewport.dispose
|
||||
end
|
||||
end
|
||||
|
||||
class MultiSelectOptionScreen
|
||||
def initialize(scene)
|
||||
@scene = scene
|
||||
end
|
||||
|
||||
def pbStartScreen(window_x = 0, window_y = 0)
|
||||
@scene.pbStartScene(window_x, window_y)
|
||||
@scene.pbOptions
|
||||
@scene.pbEndScene
|
||||
return nil if @scene.canceled
|
||||
return @scene.selected_items
|
||||
end
|
||||
end
|
||||
|
||||
class Window_MultiSelectFilterList < Window_DrawableCommand
|
||||
attr_accessor :commands
|
||||
attr_accessor :selected_flags
|
||||
attr_accessor :confirm_index
|
||||
attr_accessor :selall_index
|
||||
attr_accessor :cancel_index
|
||||
|
||||
def initialize(commands, x, y, width, height, viewport = nil)
|
||||
@commands = commands
|
||||
@selected_flags = []
|
||||
@confirm_index = nil
|
||||
@cancel_index = nil
|
||||
@selall_index = nil
|
||||
@selBaseColor = Color.new(48, 96, 216)
|
||||
@selShadowColor = Color.new(32, 32, 32)
|
||||
@confirmBaseColor = Color.new(64, 200, 96)
|
||||
@confirmShadowColor = Color.new(24, 96, 40)
|
||||
@cancelBaseColor = Color.new(216, 64, 64)
|
||||
@cancelShadowColor = Color.new(96, 24, 24)
|
||||
super(x, y, width, height, viewport)
|
||||
end
|
||||
|
||||
def itemCount
|
||||
return @commands.length
|
||||
end
|
||||
|
||||
def drawCursor(index, rect)
|
||||
return Rect.new(rect.x + 16, rect.y, rect.width - 16, rect.height)
|
||||
end
|
||||
|
||||
def drawItem(index, _count, rect)
|
||||
rect = drawCursor(index, rect)
|
||||
text = @commands[index]
|
||||
|
||||
if index == @confirm_index
|
||||
base, shadow = @confirmBaseColor, @confirmShadowColor
|
||||
elsif index == @selall_index
|
||||
base, shadow = @selBaseColor, @selShadowColor
|
||||
elsif index == @cancel_index
|
||||
base, shadow = @cancelBaseColor, @cancelShadowColor
|
||||
elsif @selected_flags[index]
|
||||
base, shadow = @selBaseColor, @selShadowColor
|
||||
else
|
||||
base, shadow = self.baseColor, self.shadowColor
|
||||
end
|
||||
|
||||
pbDrawShadowText(self.contents, rect.x, rect.y, rect.width, rect.height, text, base, shadow)
|
||||
end
|
||||
|
||||
def refresh
|
||||
self.contents.clear if self.contents
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# Used for the "Selected" panel.
|
||||
class Window_ReadOnlyList < Window_DrawableCommand
|
||||
attr_accessor :commands
|
||||
|
||||
def initialize(commands, x, y, width, height, viewport = nil)
|
||||
@commands = commands
|
||||
super(x, y, width, height, viewport)
|
||||
self.active = false
|
||||
end
|
||||
|
||||
def itemCount
|
||||
return @commands.length
|
||||
end
|
||||
|
||||
def drawCursor(index, rect)
|
||||
return Rect.new(rect.x + 16, rect.y, rect.width - 16, rect.height)
|
||||
end
|
||||
|
||||
def drawItem(index, _count, rect)
|
||||
rect = drawCursor(index, rect)
|
||||
pbDrawShadowText(self.contents, rect.x, rect.y, rect.width, rect.height,
|
||||
@commands[index], self.baseColor, self.shadowColor)
|
||||
end
|
||||
|
||||
def refresh
|
||||
self.contents.clear if self.contents
|
||||
super
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user