Aleksei-bird 7c9c708c92 Первый фикс
Пачки некоторых позиций увеличены
2024-03-01 20:54:33 +03:00

259 lines
11 KiB
Lua

require("ui.main.subfactory_list")
require("ui.base.view_state")
main_dialog = {}
-- Accepts custom width and height parameters so dimensions can be tried out without needing to change actual settings
local function determine_main_dimensions(player, products_per_row, subfactory_list_rows)
local settings = util.globals.settings(player)
products_per_row = products_per_row or settings.products_per_row
subfactory_list_rows = subfactory_list_rows or settings.subfactory_list_rows
local frame_spacing = MAGIC_NUMBERS.frame_spacing
-- Width of the larger ingredients-box, which has twice the buttons per row
local boxes_width_1 = (products_per_row * 2 * MAGIC_NUMBERS.item_button_size) + (2 * frame_spacing)
-- Width of the two smaller product+byproduct-boxes
local boxes_width_2 = 2 * ((products_per_row * MAGIC_NUMBERS.item_button_size) + (2 * frame_spacing))
local width = MAGIC_NUMBERS.list_width + boxes_width_1 + boxes_width_2 + ((2+3) * frame_spacing)
local subfactory_list_height = (subfactory_list_rows * MAGIC_NUMBERS.list_element_height)
+ MAGIC_NUMBERS.subheader_height
local height = MAGIC_NUMBERS.title_bar_height + subfactory_list_height + MAGIC_NUMBERS.info_height
+ ((2+1) * frame_spacing)
return {width=width, height=height}
end
-- Downscale width and height mod settings until the main interface fits onto the player's screen
function main_dialog.shrinkwrap_interface(player)
local resolution, scale = player.display_resolution, player.display_scale
local actual_resolution = {width=math.ceil(resolution.width / scale), height=math.ceil(resolution.height / scale)}
local mod_settings = util.globals.settings(player)
local products_per_row = mod_settings.products_per_row
local subfactory_list_rows = mod_settings.subfactory_list_rows
local function dimensions() return determine_main_dimensions(player, products_per_row, subfactory_list_rows) end
while (actual_resolution.width * 0.95) < dimensions().width do
products_per_row = products_per_row - 1
end
while (actual_resolution.height * 0.95) < dimensions().height do
subfactory_list_rows = subfactory_list_rows - 2
end
local setting_prototypes = game.mod_setting_prototypes
local width_minimum = setting_prototypes["fp_products_per_row"].allowed_values[1] --[[@as number]]
local height_minimum = setting_prototypes["fp_subfactory_list_rows"].allowed_values[1] --[[@as number]]
local live_settings = settings.get_player_settings(player)
live_settings["fp_products_per_row"] = {value = math.max(products_per_row, width_minimum)}
live_settings["fp_subfactory_list_rows"] = {value = math.max(subfactory_list_rows, height_minimum)}
end
local function interface_toggle(metadata)
local player = game.get_player(metadata.player_index)
local compact_view = util.globals.flags(player).compact_view
if compact_view then compact_dialog.toggle(player)
else main_dialog.toggle(player) end
end
function main_dialog.rebuild(player, default_visibility)
local ui_state = util.globals.ui_state(player)
local main_elements = ui_state.main_elements
local interface_visible = default_visibility
local main_frame = main_elements.main_frame
-- Delete the existing interface if there is one
if main_frame ~= nil then
if main_frame.valid then
interface_visible = main_frame.visible
main_frame.destroy()
end
ui_state.main_elements = {} -- reset all main element references
main_elements = ui_state.main_elements
end
-- Create and configure the top-level frame
local frame_main_dialog = player.gui.screen.add{type="frame", direction="vertical",
visible=interface_visible, tags={mod="fp", on_gui_closed="close_main_dialog"},
name="fp_frame_main_dialog"}
main_elements["main_frame"] = frame_main_dialog
local dimensions = determine_main_dimensions(player)
ui_state.main_dialog_dimensions = dimensions
frame_main_dialog.style.size = dimensions
util.gui.properly_center_frame(player, frame_main_dialog, dimensions)
-- Create the actual dialog structure
local frame_spacing = MAGIC_NUMBERS.frame_spacing
main_elements.flows = {}
local top_horizontal = frame_main_dialog.add{type="flow", direction="horizontal"}
main_elements.flows["top_horizontal"] = top_horizontal
local main_horizontal = frame_main_dialog.add{type="flow", direction="horizontal"}
main_horizontal.style.horizontal_spacing = frame_spacing
main_elements.flows["main_horizontal"] = main_horizontal
local left_vertical = main_horizontal.add{type="flow", direction="vertical"}
left_vertical.style.vertical_spacing = frame_spacing
main_elements.flows["left_vertical"] = left_vertical
local right_vertical = main_horizontal.add{type="flow", direction="vertical"}
right_vertical.style.vertical_spacing = frame_spacing
main_elements.flows["right_vertical"] = right_vertical
view_state.rebuild_state(player) -- initializes the view_state
util.raise.build(player, "main_dialog", nil) -- tells all elements to build themselves
if interface_visible then player.opened = frame_main_dialog end
main_dialog.set_pause_state(player, frame_main_dialog)
end
function main_dialog.toggle(player, skip_opened)
local ui_state = util.globals.ui_state(player)
local frame_main_dialog = ui_state.main_elements.main_frame
if frame_main_dialog == nil or not frame_main_dialog.valid then
main_dialog.rebuild(player, true) -- sets opened and paused-state itself
elseif ui_state.modal_dialog_type == nil then -- don't toggle if modal dialog is open
local new_dialog_visibility = not frame_main_dialog.visible
frame_main_dialog.visible = new_dialog_visibility
if not skip_opened then -- flag used only for hacky internal reasons
player.opened = (new_dialog_visibility) and frame_main_dialog or nil
end
main_dialog.set_pause_state(player, frame_main_dialog)
-- Make sure FP is not behind some vanilla interfaces
if new_dialog_visibility then frame_main_dialog.bring_to_front() end
end
end
-- Returns true when the main dialog is open while no modal dialogs are
function main_dialog.is_in_focus(player)
local frame_main_dialog = util.globals.main_elements(player).main_frame
return (frame_main_dialog ~= nil and frame_main_dialog.valid and frame_main_dialog.visible
and util.globals.ui_state(player).modal_dialog_type == nil)
end
-- Sets the game.paused-state as is appropriate
function main_dialog.set_pause_state(player, frame_main_dialog, force_false)
-- Don't touch paused-state if this is a multiplayer session or the editor is active
if game.is_multiplayer() or player.controller_type == defines.controllers.editor then return end
game.tick_paused = (util.globals.preferences(player).pause_on_interface and not force_false)
and frame_main_dialog.visible or false
end
-- ** EVENTS **
local listeners = {}
listeners.gui = {
on_gui_closed = {
{
name = "close_main_dialog",
handler = (function(player, _, _)
main_dialog.toggle(player)
end)
}
},
on_gui_click = {
{
name = "mod_gui_toggle_interface",
handler = (function(player, _, _)
if DEV_ACTIVE then -- implicit mod reload for easier development
util.gui.reset_player(player) -- destroys all FP GUIs
util.gui.toggle_mod_gui(player) -- fixes the mod gui button after its been destroyed
game.reload_mods() -- toggle needs to be delayed by a tick since the reload is not instant
game.print("Mods reloaded")
util.nth_tick.register((game.tick + 1), "interface_toggle", {player_index=player.index})
else -- call the interface toggle function directly
interface_toggle({player_index=player.index})
end
end)
}
}
}
listeners.misc = {
-- Makes sure that another GUI can open properly while a modal dialog is open.
-- The FP interface can have at most 3 layers of GUI: main interface, modal dialog, selection mode.
-- We need to make sure opening the technology screen (for example) from any of those layers behaves properly.
-- We need to consider that if the technology screen is opened (which is the reason we get this event),
-- the game automtically closes the currently open GUI before calling this one. This means the top layer
-- that's open at that stage is closed already when we get here. So we're at most at the modal dialog
-- layer at this point and need to close the things below, if there are any.
on_gui_opened = (function(player, _)
local ui_state = util.globals.ui_state(player)
-- With that in mind, if there's a modal dialog open, we were in selection mode, and need to close the dialog
if ui_state.modal_dialog_type ~= nil then util.raise.close_dialog(player, "cancel", true) end
-- Then, at this point we're at most at the stage where the main dialog is open, so close it
if main_dialog.is_in_focus(player) then main_dialog.toggle(player, true) end
end),
on_player_display_resolution_changed = (function(player, _)
main_dialog.shrinkwrap_interface(player)
main_dialog.rebuild(player, false)
end),
on_player_display_scale_changed = (function(player, _)
main_dialog.shrinkwrap_interface(player)
main_dialog.rebuild(player, false)
end),
on_lua_shortcut = (function(player, event)
if event.prototype_name == "fp_open_interface" and not util.globals.flags(player).compact_view then
main_dialog.toggle(player)
end
end),
fp_toggle_interface = (function(player, _)
if not util.globals.flags(player).compact_view then main_dialog.toggle(player) end
end),
-- This needs to be in a single place, otherwise the events cancel each other out
fp_toggle_compact_view = (function(player, _)
local ui_state = util.globals.ui_state(player)
local flags = ui_state.flags
local subfactory = ui_state.context.subfactory
local main_focus = main_dialog.is_in_focus(player)
local compact_focus = compact_dialog.is_in_focus(player)
-- Open the compact view if this toggle is pressed when neither dialog
-- is open as that makes the most sense from a user perspective
if not main_focus and not compact_focus then
flags.compact_view = true
compact_dialog.toggle(player)
elseif flags.compact_view and compact_focus then
compact_dialog.toggle(player)
main_dialog.toggle(player)
util.raise.refresh(player, "production", nil)
flags.compact_view = false
elseif main_focus and subfactory ~= nil and subfactory.valid then
main_dialog.toggle(player)
compact_dialog.toggle(player) -- toggle also refreshes
flags.compact_view = true
end
end)
}
listeners.global = {
interface_toggle = interface_toggle
}
return { listeners }