428 lines
14 KiB
Lua
428 lines
14 KiB
Lua
local gui = require("__flib__.gui")
|
|
local on_tick_n = require("__flib__.on-tick-n")
|
|
local table = require("__flib__.table")
|
|
|
|
local constants = require("constants")
|
|
|
|
local database = require("scripts.database")
|
|
local formatter = require("scripts.formatter")
|
|
local gui_util = require("scripts.gui.util")
|
|
local util = require("scripts.util")
|
|
|
|
local actions = {}
|
|
|
|
--- @param Gui SearchGui
|
|
--- @param e on_gui_click
|
|
function actions.reset_location(Gui, _, e)
|
|
if e.button ~= defines.mouse_button_type.middle then
|
|
return
|
|
end
|
|
|
|
if Gui.player_table.settings.general.interface.search_gui_location == "top_left" then
|
|
local scale = Gui.player.display_scale
|
|
Gui.refs.window.location = table.map(constants.search_gui_top_left_location, function(pos)
|
|
return pos * scale
|
|
end)
|
|
Gui.refs.window.auto_center = false
|
|
else
|
|
Gui.refs.window.force_auto_center()
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.close(Gui, _, _)
|
|
if not Gui.state.ignore_closed and not Gui.player_table.flags.technology_gui_open then
|
|
Gui:close()
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.toggle_pinned(Gui, _, _)
|
|
local player = Gui.player
|
|
local refs = Gui.refs
|
|
local state = Gui.state
|
|
|
|
local pin_button = refs.titlebar.pin_button
|
|
|
|
state.pinned = not state.pinned
|
|
if state.pinned then
|
|
pin_button.style = "flib_selected_frame_action_button"
|
|
pin_button.sprite = "rb_pin_black"
|
|
if player.opened == refs.window then
|
|
state.ignore_closed = true
|
|
player.opened = nil
|
|
state.ignore_closed = false
|
|
end
|
|
else
|
|
pin_button.style = "frame_action_button"
|
|
pin_button.sprite = "rb_pin_white"
|
|
player.opened = refs.window
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.toggle_settings(Gui, _, _)
|
|
local state = Gui.state
|
|
local player = Gui.player
|
|
|
|
state.ignore_closed = true
|
|
local SettingsGui = util.get_gui(Gui.player.index, "settings")
|
|
if SettingsGui then
|
|
SettingsGui:destroy()
|
|
else
|
|
SETTINGS_GUI.build(player, Gui.player_table)
|
|
end
|
|
state.ignore_closed = false
|
|
local settings_button = Gui.refs.titlebar.settings_button
|
|
if Gui.player_table.guis.settings then
|
|
settings_button.style = "flib_selected_frame_action_button"
|
|
settings_button.sprite = "rb_settings_black"
|
|
else
|
|
settings_button.style = "frame_action_button"
|
|
settings_button.sprite = "rb_settings_white"
|
|
if not state.pinned then
|
|
player.opened = Gui.refs.window
|
|
end
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.deselect_settings_button(Gui, _, _)
|
|
local settings_button = Gui.refs.titlebar.settings_button
|
|
settings_button.style = "frame_action_button"
|
|
settings_button.sprite = "rb_settings_white"
|
|
if not Gui.state.pinned and Gui.refs.window.visible then
|
|
Gui.player.opened = Gui.refs.window
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
--- @param e on_gui_text_changed
|
|
function actions.update_search_query(Gui, _, e)
|
|
local player_table = Gui.player_table
|
|
local state = Gui.state
|
|
local refs = Gui.refs
|
|
|
|
local class_filter
|
|
local query = string.lower(e.element.text)
|
|
if string.find(query, "/") then
|
|
-- The `_`s here are technically globals, but whatever
|
|
_, _, class_filter, query = string.find(query, "^/(.-)/(.-)$")
|
|
if class_filter then
|
|
class_filter = string.lower(class_filter)
|
|
end
|
|
-- Check translations of each class filter
|
|
local matched = false
|
|
if class_filter then
|
|
local gui_translations = player_table.translations.gui
|
|
for _, class in pairs(constants.classes) do
|
|
if class_filter == string.lower(gui_translations[class]) then
|
|
matched = true
|
|
class_filter = class
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- Invalidate textfield
|
|
if not class_filter or not query or not matched then
|
|
class_filter = false
|
|
query = nil
|
|
end
|
|
end
|
|
-- Remove results update action if there is one
|
|
if state.update_results_ident then
|
|
on_tick_n.remove(state.update_results_ident)
|
|
state.update_results_ident = nil
|
|
end
|
|
if query then
|
|
-- Fuzzy search
|
|
if player_table.settings.general.search.fuzzy_search then
|
|
query = string.gsub(query, ".", "%1.*")
|
|
end
|
|
-- Input sanitization
|
|
for pattern, replacement in pairs(constants.input_sanitizers) do
|
|
query = string.gsub(query, pattern, replacement)
|
|
end
|
|
-- Save query
|
|
state.search_query = query
|
|
state.class_filter = class_filter
|
|
-- Reset textfield style
|
|
refs.search_textfield.style = "rb_search_textfield"
|
|
if #query == 0 and not class_filter then
|
|
-- Update immediately
|
|
actions.update_search_results(Gui)
|
|
else
|
|
-- Update in a while
|
|
state.update_results_ident = on_tick_n.add(
|
|
game.tick + constants.search_timeout,
|
|
{ gui = "search", action = "update_search_results", player_index = e.player_index }
|
|
)
|
|
end
|
|
else
|
|
state.search_query = ""
|
|
refs.search_textfield.style = "rb_search_invalid_textfield"
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.update_search_results(Gui, _, _)
|
|
local player = Gui.player
|
|
local player_table = Gui.player_table
|
|
local state = Gui.state
|
|
local refs = Gui.refs
|
|
|
|
-- Data
|
|
local player_data = formatter.build_player_data(player, player_table)
|
|
local show_fluid_temperatures = player_table.settings.general.search.show_fluid_temperatures
|
|
local search_type = player_table.settings.general.search.search_type
|
|
local class_filter = state.class_filter
|
|
local query = state.search_query
|
|
|
|
if state.search_type == "textual" then
|
|
-- Update results based on query
|
|
local i = 0
|
|
local pane = refs.textual_results_pane
|
|
local children = pane.children
|
|
local max = constants.search_results_limit
|
|
if class_filter ~= false and (class_filter or #query >= 2) then
|
|
for class in pairs(constants.pages) do
|
|
if not class_filter or class_filter == class then
|
|
for internal, translation in pairs(player_table.translations[class]) do
|
|
-- Match against search string
|
|
local matched
|
|
if search_type == "both" then
|
|
matched = string.find(string.lower(internal), query) or string.find(string.lower(translation), query)
|
|
elseif search_type == "internal" then
|
|
matched = string.find(string.lower(internal), query)
|
|
elseif search_type == "localised" then
|
|
matched = string.find(string.lower(translation), query)
|
|
end
|
|
|
|
if matched then
|
|
local obj_data = database[class][internal]
|
|
|
|
-- Check temperature settings
|
|
local passed = true
|
|
if obj_data.class == "fluid" then
|
|
local temperature_ident = obj_data.temperature_ident
|
|
if temperature_ident then
|
|
local is_range = temperature_ident.min ~= temperature_ident.max
|
|
if is_range then
|
|
if show_fluid_temperatures ~= "all" then
|
|
passed = false
|
|
end
|
|
else
|
|
if show_fluid_temperatures == "off" then
|
|
passed = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if passed then
|
|
local blueprint_result = class == "entity" and obj_data.blueprintable and { name = internal } or nil
|
|
local info = formatter(obj_data, player_data, { blueprint_result = blueprint_result })
|
|
if info then
|
|
i = i + 1
|
|
local style = info.researched and "rb_list_box_item" or "rb_unresearched_list_box_item"
|
|
local item = children[i]
|
|
if item then
|
|
item.style = style
|
|
item.caption = info.caption
|
|
item.tooltip = info.tooltip
|
|
item.enabled = info.num_interactions > 0
|
|
gui.update_tags(
|
|
item,
|
|
{ blueprint_result = blueprint_result, context = { class = class, name = internal } }
|
|
)
|
|
else
|
|
gui.add(pane, {
|
|
type = "button",
|
|
style = style,
|
|
caption = info.caption,
|
|
tooltip = info.tooltip,
|
|
enabled = info.num_interactions > 0,
|
|
mouse_button_filter = { "left", "middle" },
|
|
tags = {
|
|
blueprint_result = blueprint_result,
|
|
context = { class = class, name = internal },
|
|
},
|
|
actions = {
|
|
on_click = { gui = "search", action = "open_object" },
|
|
},
|
|
})
|
|
if i >= max then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if i >= max then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- Destroy extraneous items
|
|
for j = i + 1, #children do
|
|
children[j].destroy()
|
|
end
|
|
elseif state.search_type == "visual" then
|
|
refs.objects_frame.visible = true
|
|
refs.warning_frame.visible = false
|
|
|
|
--- @type LuaGuiElement
|
|
local group_table = refs.group_table
|
|
|
|
for _, group_scroll in pairs(refs.objects_frame.children) do
|
|
local group_has_results = false
|
|
for _, subgroup_table in pairs(group_scroll.children) do
|
|
local visible_count = 0
|
|
for _, obj_button in pairs(subgroup_table.children) do
|
|
local context = gui.get_tags(obj_button).context
|
|
|
|
local matched
|
|
-- Match against class filter
|
|
if not class_filter or class_filter == context.class then
|
|
local translation = player_data.translations[context.class][context.name]
|
|
-- Match against search string
|
|
if search_type == "both" then
|
|
matched = string.find(string.lower(context.name), query) or string.find(string.lower(translation), query)
|
|
elseif search_type == "internal" then
|
|
matched = string.find(string.lower(context.name), query)
|
|
elseif search_type == "localised" then
|
|
matched = string.find(string.lower(translation), query)
|
|
end
|
|
end
|
|
|
|
if matched then
|
|
obj_button.visible = true
|
|
visible_count = visible_count + 1
|
|
else
|
|
obj_button.visible = false
|
|
end
|
|
end
|
|
|
|
if visible_count > 0 then
|
|
group_has_results = true
|
|
subgroup_table.visible = true
|
|
else
|
|
subgroup_table.visible = false
|
|
end
|
|
end
|
|
|
|
local group_name = group_scroll.name
|
|
local group_button = group_table[group_name]
|
|
if group_has_results then
|
|
group_button.style = "rb_filter_group_button_tab"
|
|
group_button.enabled = state.active_group ~= group_scroll.name
|
|
if state.active_group == group_name then
|
|
group_scroll.visible = true
|
|
else
|
|
group_scroll.visible = false
|
|
end
|
|
else
|
|
group_scroll.visible = false
|
|
group_button.style = "rb_disabled_filter_group_button_tab"
|
|
group_button.enabled = false
|
|
if state.active_group == group_name then
|
|
local matched = false
|
|
for _, group_button in pairs(group_table.children) do
|
|
if group_button.enabled then
|
|
matched = true
|
|
actions.change_group(Gui, { group = group_button.name, ignore_last_button = true })
|
|
break
|
|
end
|
|
end
|
|
if not matched then
|
|
refs.objects_frame.visible = false
|
|
refs.warning_frame.visible = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
--- @param e on_gui_click
|
|
function actions.open_object(Gui, _, e)
|
|
local context = gui_util.navigate_to(e)
|
|
if context then
|
|
local attach = Gui.player_table.settings.general.interface.attach_search_results
|
|
local sticky = attach and e.button == defines.mouse_button_type.left
|
|
local id = sticky and Gui.state.id and Gui.player_table.guis.info[Gui.state.id] and Gui.state.id or nil
|
|
local parent = sticky and Gui.refs.window or nil
|
|
OPEN_PAGE(Gui.player, Gui.player_table, context, { id = id, parent = parent })
|
|
if sticky and not id then
|
|
Gui.state.id = Gui.player_table.guis.info._active_id
|
|
end
|
|
if not sticky and Gui.player_table.settings.general.interface.close_search_gui_after_selection then
|
|
actions.close(Gui)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.change_search_type(Gui)
|
|
local state = Gui.state
|
|
local refs = Gui.refs
|
|
if state.search_type == "textual" then
|
|
state.search_type = "visual"
|
|
refs.textual_results_pane.visible = false
|
|
refs.visual_results_flow.visible = true
|
|
if state.needs_visual_update then
|
|
state.needs_visual_update = false
|
|
Gui:update_visual_contents()
|
|
end
|
|
elseif state.search_type == "visual" then
|
|
state.search_type = "textual"
|
|
refs.textual_results_pane.visible = true
|
|
refs.visual_results_flow.visible = false
|
|
end
|
|
actions.update_search_results(Gui)
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
--- @param msg table
|
|
function actions.change_group(Gui, msg)
|
|
local last_group = Gui.state.active_group
|
|
|
|
if not msg.ignore_last_button then
|
|
Gui.refs.group_table[last_group].enabled = true
|
|
end
|
|
Gui.refs.objects_frame[last_group].visible = false
|
|
|
|
local new_group = msg.group
|
|
Gui.refs.group_table[new_group].enabled = false
|
|
Gui.refs.objects_frame[new_group].visible = true
|
|
|
|
Gui.state.active_group = msg.group
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.update_favorites(Gui, _, _)
|
|
Gui:update_favorites()
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.update_history(Gui, _, _)
|
|
Gui:update_history()
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.delete_favorites(Gui, _, _)
|
|
Gui.player_table.favorites = {}
|
|
Gui:update_favorites()
|
|
end
|
|
|
|
--- @param Gui SearchGui
|
|
function actions.delete_history(Gui, _, _)
|
|
Gui.player_table.global_history = {}
|
|
Gui:update_history()
|
|
end
|
|
|
|
return actions
|