259 lines
11 KiB
Lua
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 }
|