Первый фикс

Пачки некоторых позиций увеличены
This commit is contained in:
2024-03-01 20:53:32 +03:00
commit 7c9c708c92
23653 changed files with 767936 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
local train_util = require("__flib__.train")
local on_tick_n = require("__flib__.on-tick-n")
local constants = require("constants")
local util = require("scripts.util")
local actions = {}
local function toggle_fab(elem, sprite, state)
if state then
elem.style = "flib_selected_frame_action_button"
elem.sprite = sprite .. "_black"
else
elem.style = "frame_action_button"
elem.sprite = sprite .. "_white"
end
end
function actions.close(Gui) Gui:close() end
function actions.recenter(Gui) Gui.refs.window.force_auto_center() end
function actions.toggle_auto_refresh(Gui)
Gui.state.auto_refresh = not Gui.state.auto_refresh
toggle_fab(Gui.refs.titlebar.refresh_button, "ltnm_refresh", Gui.state.auto_refresh)
end
function actions.toggle_pinned(Gui)
Gui.state.pinned = not Gui.state.pinned
toggle_fab(Gui.refs.titlebar.pin_button, "ltnm_pin", Gui.state.pinned)
if Gui.state.pinned then
Gui.state.pinning = true
Gui.player.opened = nil
Gui.state.pinning = false
else
Gui.player.opened = Gui.refs.window
Gui.refs.window.force_auto_center()
end
end
function actions.update_text_search_query(Gui, _, e)
local query = e.text
-- Input sanitization
for pattern, replacement in pairs(constants.input_sanitizers) do
query = string.gsub(query, pattern, replacement)
end
Gui.state.search_query = query
if Gui.state.search_job then on_tick_n.remove(Gui.state.search_job) end
if #query == 0 then
Gui:schedule_update()
else
Gui.state.search_job =
on_tick_n.add(game.tick + 30, { gui = "main", action = "update", player_index = Gui.player.index })
end
end
function actions.update_network_id_query(Gui)
Gui.state.network_id = tonumber(Gui.refs.toolbar.network_id_field.text) or -1
Gui:schedule_update()
end
function actions.open_train_gui(Gui, msg)
local train_id = msg.train_id
local train_data = Gui.state.ltn_data.trains[train_id]
if not train_data or not train_data.train.valid then
util.error_flying_text(Gui.player, { "message.ltnm-error-train-is-invalid" })
return
end
train_util.open_gui(Gui.player.index, train_data.train)
end
function actions.open_station_gui(Gui, msg, e)
local station_id = msg.station_id
local station_data = Gui.state.ltn_data.stations[station_id]
if not station_data or not station_data.entity.valid then
util.error_flying_text(Gui.player, { "message.ltnm-error-station-is-invalid" })
return
end
--- @type LuaPlayer
local player = Gui.player
if e.shift then
if station_data.surface_index ~= player.surface_index then
if
remote.interfaces["space-exploration"]
and remote.call("space-exploration", "remote_view_is_unlocked", { player = player })
then
local zone = remote.call("space-exploration", "get_zone_from_surface_index",
{ surface_index = station_data.surface_index })
if zone then
remote.call("space-exploration", "remote_view_start", {
player = player,
zone_name = zone.name,
position = station_data.entity.position,
location_name = station_data.name,
freeze_history = true,
})
else
util.error_flying_text(player, { "message.ltnm-error-station-on-different-surface-unknown-zone" })
return
end
else
util.error_flying_text(player, { "message.ltnm-error-station-on-different-surface" })
return
end
else
player.zoom_to_world(station_data.entity.position, 1, station_data.entity)
end
rendering.draw_circle({
color = constants.colors.red.tbl,
target = station_data.entity.position,
surface = station_data.entity.surface,
radius = 0.5,
filled = false,
width = 5,
time_to_live = 60 * 3,
players = { player },
})
if not Gui.state.pinned then Gui:close() end
elseif e.control and remote.interfaces["ltn-combinator"] then
if
not remote.call("ltn-combinator", "open_ltn_combinator", e.player_index, station_data.lamp_control, true)
then
util.error_flying_text(player, { "message.ltnm-error-ltn-combinator-not-found" })
end
else
player.opened = station_data.entity
end
end
function actions.toggle_sort(Gui, msg, e)
local tab = msg.tab
local column = msg.column
local sorts = Gui.state.sorts[tab]
local active_column = sorts._active
if active_column == column then
sorts[column] = e.element.state
else
sorts._active = column
e.element.state = sorts[column]
local widths = Gui.widths[tab]
local old_checkbox = Gui.refs[tab].toolbar[active_column .. "_checkbox"]
old_checkbox.style = "ltnm_sort_checkbox"
if widths[active_column .. "_checkbox_stretchy"] then
old_checkbox.style.horizontally_stretchable = true
else
old_checkbox.style.width = widths[active_column]
end
e.element.style = "ltnm_selected_sort_checkbox"
if widths[column .. "_checkbox_stretchy"] then
e.element.style.horizontally_stretchable = true
else
e.element.style.width = widths[column]
end
end
Gui:schedule_update()
end
function actions.update(Gui) Gui:schedule_update() end
function actions.change_tab(Gui, msg)
Gui.state.active_tab = msg.tab
Gui:schedule_update()
end
function actions.change_surface(Gui, _, e)
local selected_index = e.element.selected_index
local selected_surface_index = Gui.state.ltn_data.surfaces.selected_to_index[selected_index]
if selected_surface_index then
Gui.state.surface = selected_surface_index
Gui:schedule_update()
end
end
function actions.clear_history(Gui)
global.flags.deleted_history = true
Gui:schedule_update()
end
function actions.delete_alert(Gui, msg)
global.active_data.alerts_to_delete[msg.alert_id] = true
Gui:schedule_update()
end
function actions.delete_all_alerts(Gui)
global.flags.deleted_all_alerts = true
Gui:schedule_update()
end
function actions.focus_search(Gui)
if not Gui.pinned then
Gui.refs.toolbar.text_search_field.select_all()
Gui.refs.toolbar.text_search_field.focus()
end
end
return actions

View File

@@ -0,0 +1,227 @@
local gui = require("__flib__.gui")
local misc = require("__flib__.misc")
local constants = require("constants")
local util = require("scripts.util")
local templates = require("templates")
local alerts_tab = {}
function alerts_tab.build(widths)
return {
tab = {
type = "tab",
caption = { "gui.ltnm-alerts" },
ref = { "alerts", "tab" },
actions = {
on_click = { gui = "main", action = "change_tab", tab = "alerts" },
},
},
content = {
type = "frame",
style = "ltnm_main_content_frame",
direction = "vertical",
ref = { "alerts", "content_frame" },
{
type = "frame",
style = "ltnm_table_toolbar_frame",
style_mods = { right_padding = 4 },
templates.sort_checkbox(widths, "alerts", "time", true, nil, true),
templates.sort_checkbox(widths, "alerts", "train_id", false),
templates.sort_checkbox(widths, "alerts", "route", false),
templates.sort_checkbox(widths, "alerts", "network_id", false),
templates.sort_checkbox(nil, "alerts", "type", false),
{
type = "sprite-button",
style = "tool_button_red",
sprite = "utility/trash",
tooltip = { "gui.ltnm-delete-all-alerts" },
ref = { "alerts", "delete_all_button" },
actions = {
on_click = { gui = "main", action = "delete_all_alerts" },
},
},
},
{ type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "alerts", "scroll_pane" } },
{
type = "flow",
style = "ltnm_warning_flow",
visible = false,
ref = { "alerts", "warning_flow" },
{
type = "label",
style = "ltnm_semibold_label",
caption = { "gui.ltnm-no-alerts" },
ref = { "alerts", "warning_label" },
},
},
},
}
end
function alerts_tab.update(self)
local dictionaries = self.player_table.dictionaries
local state = self.state
local refs = self.refs.alerts
local widths = self.widths
local search_query = state.search_query
local search_network_id = state.network_id
local search_surface = state.surface
local ltn_alerts = state.ltn_data.alerts
local alerts_to_delete = global.active_data.alerts_to_delete
local scroll_pane = refs.scroll_pane
local children = scroll_pane.children
local sorts = state.sorts[state.active_tab]
local active_sort = sorts._active
local sorted_alerts = state.ltn_data.sorted_alerts[active_sort]
local table_index = 0
-- False = ascending (arrow down), True = descending (arrow up)
local start, finish, step
if sorts[active_sort] then
start = #sorted_alerts
finish = 1
step = -1
else
start = 1
finish = #sorted_alerts
step = 1
end
if not global.flags.deleted_all_alerts then
for sorted_index = start, finish, step do
local alert_id = sorted_alerts[sorted_index]
local alerts_entry = ltn_alerts[alert_id]
if
(search_surface == -1 or (alerts_entry.train.surface_index == search_surface))
and bit32.btest(alerts_entry.train.network_id, search_network_id)
and (#search_query == 0 or string.find(
alerts_entry.search_strings[self.player.index] or "",
string.lower(search_query)
))
and not alerts_to_delete[alert_id]
then
table_index = table_index + 1
local row = children[table_index]
local color = table_index % 2 == 0 and "dark" or "light"
if not row then
row = gui.add(scroll_pane, {
type = "frame",
style = "ltnm_table_row_frame_" .. color,
{ type = "label", style_mods = { width = widths.alerts.time } },
{
type = "label",
style = "ltnm_clickable_semibold_label",
style_mods = { width = widths.alerts.train_id, horizontal_align = "center" },
tooltip = { "gui.ltnm-open-train-gui" },
},
{
type = "flow",
style_mods = { vertical_spacing = 0 },
direction = "vertical",
{
type = "label",
style = "ltnm_clickable_semibold_label",
style_mods = { width = widths.alerts.route },
tooltip = constants.open_station_gui_tooltip,
},
{
type = "label",
style = "ltnm_clickable_semibold_label",
style_mods = { width = widths.alerts.route },
tooltip = constants.open_station_gui_tooltip,
},
},
{ type = "label", style_mods = { width = widths.alerts.network_id, horizontal_align = "center" } },
{ type = "label", style_mods = { width = widths.alerts.type } },
{
type = "frame",
name = "contents_frame",
style = "ltnm_small_slot_table_frame_" .. color,
style_mods = { width = widths.alerts.contents },
{ type = "table", name = "contents_table", style = "slot_table", column_count = 4 },
},
{
type = "sprite-button",
style = "tool_button_red",
sprite = "utility/trash",
tooltip = { "gui.ltnm-delete-alert" },
},
})
end
gui.update(row, {
{ elem_mods = { caption = misc.ticks_to_timestring(alerts_entry.time) } },
{
elem_mods = { caption = alerts_entry.train_id },
actions = {
on_click = { gui = "main", action = "open_train_gui", train_id = alerts_entry.train_id },
},
},
{
{
elem_mods = { caption = alerts_entry.train.from },
actions = {
on_click = { gui = "main", action = "open_station_gui", station_id = alerts_entry.train.from_id },
},
},
{
elem_mods = {
caption = "[color=" .. constants.colors.caption.str .. "]->[/color] " .. alerts_entry.train.to,
},
actions = {
on_click = { gui = "main", action = "open_station_gui", station_id = alerts_entry.train.to_id },
},
},
},
{ elem_mods = { caption = util.signed_int32(alerts_entry.train.network_id) } },
{
elem_mods = {
caption = { "gui.ltnm-alert-" .. string.gsub(alerts_entry.type, "_", "-") },
tooltip = { "gui.ltnm-alert-" .. string.gsub(alerts_entry.type, "_", "-") .. "-description" },
},
},
{},
{
actions = {
on_click = { gui = "main", action = "delete_alert", alert_id = alert_id },
},
},
})
util.slot_table_update(row.contents_frame.contents_table, {
{ color = "green", entries = alerts_entry.planned_shipment or {}, translations = dictionaries.materials },
{ color = "red", entries = alerts_entry.actual_shipment or {}, translations = dictionaries.materials },
{ color = "red", entries = alerts_entry.unscheduled_load or {}, translations = dictionaries.materials },
{ color = "red", entries = alerts_entry.remaining_load or {}, translations = dictionaries.materials },
})
end
end
end
for child_index = table_index + 1, #children do
children[child_index].destroy()
end
if table_index == 0 then
refs.warning_flow.visible = true
scroll_pane.visible = false
refs.content_frame.style = "ltnm_main_warning_frame"
refs.delete_all_button.enabled = false
else
refs.warning_flow.visible = false
scroll_pane.visible = true
refs.content_frame.style = "ltnm_main_content_frame"
refs.delete_all_button.enabled = true
end
end
return alerts_tab

View File

@@ -0,0 +1,144 @@
local gui = require("__flib__.gui")
local templates = require("templates")
local depots_tab = {}
function depots_tab.build(widths)
return {
tab = {
type = "tab",
caption = { "gui.ltnm-depots" },
ref = { "depots", "tab" },
actions = {
on_click = { gui = "main", action = "change_tab", tab = "depots" },
},
},
content = {
type = "frame",
style = "ltnm_main_content_frame",
direction = "vertical",
ref = { "depots", "content_frame" },
{
type = "frame",
style = "ltnm_table_toolbar_frame",
style_mods = { right_padding = 4 },
templates.sort_checkbox(widths, "depots", "name", true, nil, true),
templates.sort_checkbox(widths, "depots", "network_id", false),
templates.sort_checkbox(widths, "depots", "status", false, { "gui.ltnm-status-description" }),
templates.sort_checkbox(widths, "depots", "trains", false),
},
{ type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "depots", "scroll_pane" } },
{
type = "flow",
style = "ltnm_warning_flow",
visible = false,
ref = { "depots", "warning_flow" },
{
type = "label",
style = "ltnm_semibold_label",
caption = { "gui.ltnm-no-depots" },
ref = { "depots", "warning_label" },
},
},
},
}
end
function depots_tab.update(self)
local state = self.state
local refs = self.refs.depots
local widths = self.widths.depots
local search_query = state.search_query
local search_network_id = state.network_id
local search_surface = state.surface
local ltn_depots = state.ltn_data.depots
local scroll_pane = refs.scroll_pane
local children = scroll_pane.children
local sorts = state.sorts.depots
local active_sort = sorts._active
local sorted_depots = state.ltn_data.sorted_depots[active_sort]
local table_index = 0
-- False = ascending (arrow down), True = descending (arrow up)
local start, finish, step
if sorts[active_sort] then
start = #sorted_depots
finish = 1
step = -1
else
start = 1
finish = #sorted_depots
step = 1
end
for sorted_index = start, finish, step do
local depot_name = sorted_depots[sorted_index]
local depot_data = ltn_depots[depot_name]
if
(search_surface == -1 or depot_data.surfaces[search_surface])
and bit32.btest(depot_data.network_id, search_network_id)
and (#search_query == 0 or string.find(depot_data.search_string, string.lower(search_query)))
then
table_index = table_index + 1
local row = children[table_index]
local color = table_index % 2 == 0 and "dark" or "light"
if not row then
row = gui.add(scroll_pane, {
type = "frame",
style = "ltnm_table_row_frame_" .. color,
{ type = "label", style_mods = { width = widths.name } },
{ type = "label", style_mods = { width = widths.network_id, horizontal_align = "center" } },
{ type = "flow", name = "statuses_flow", style_mods = { width = widths.status } },
{ type = "label", style_mods = { width = widths.trains } },
})
end
gui.update(row, {
{ elem_mods = { caption = depot_name } },
{ elem_mods = { caption = depot_data.network_id } },
{},
{ elem_mods = { caption = depot_data.trains_string } },
})
local statuses_flow = row.statuses_flow
local statuses_children = statuses_flow.children
local status_index = 0
for color, count in pairs(depot_data.statuses) do
status_index = status_index + 1
local status_flow = statuses_children[status_index]
if not status_flow then
status_flow = gui.add(statuses_flow, templates.status_indicator())
end
gui.update(status_flow, {
{ elem_mods = { sprite = "flib_indicator_" .. color } },
{ elem_mods = { caption = count } },
})
end
for child_index = status_index + 1, #statuses_children do
statuses_children[child_index].destroy()
end
end
end
for child_index = table_index + 1, #children do
children[child_index].destroy()
end
if table_index == 0 then
refs.warning_flow.visible = true
scroll_pane.visible = false
refs.content_frame.style = "ltnm_main_warning_frame"
else
refs.warning_flow.visible = false
scroll_pane.visible = true
refs.content_frame.style = "ltnm_main_content_frame"
end
end
return depots_tab

View File

@@ -0,0 +1,208 @@
local gui = require("__flib__.gui")
local misc = require("__flib__.misc")
local constants = require("constants")
local util = require("scripts.util")
local templates = require("templates")
local history_tab = {}
function history_tab.build(widths)
return {
tab = {
type = "tab",
caption = { "gui.ltnm-history" },
ref = { "history", "tab" },
actions = {
on_click = { gui = "main", action = "change_tab", tab = "history" },
},
},
content = {
type = "frame",
style = "ltnm_main_content_frame",
direction = "vertical",
ref = { "history", "content_frame" },
{
type = "frame",
style = "ltnm_table_toolbar_frame",
style_mods = { right_padding = 4 },
templates.sort_checkbox(widths, "history", "train_id", false),
templates.sort_checkbox(widths, "history", "route", false),
templates.sort_checkbox(widths, "history", "depot", false),
templates.sort_checkbox(widths, "history", "network_id", false),
templates.sort_checkbox(widths, "history", "runtime", false),
templates.sort_checkbox(widths, "history", "finished", true, nil, true),
templates.sort_checkbox(nil, "history", "shipment", false),
{
type = "sprite-button",
style = "tool_button_red",
sprite = "utility/trash",
tooltip = { "gui.ltnm-clear-history" },
ref = { "history", "clear_button" },
actions = {
on_click = { gui = "main", action = "clear_history" },
},
},
},
{ type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "history", "scroll_pane" } },
{
type = "flow",
style = "ltnm_warning_flow",
visible = false,
ref = { "history", "warning_flow" },
{
type = "label",
style = "ltnm_semibold_label",
caption = { "gui.ltnm-no-history" },
ref = { "history", "warning_label" },
},
},
},
}
end
function history_tab.update(self)
local dictionaries = self.player_table.dictionaries
local state = self.state
local refs = self.refs.history
local widths = self.widths
local search_query = state.search_query
local search_network_id = state.network_id
local search_surface = state.surface
local ltn_history = state.ltn_data.history
local scroll_pane = refs.scroll_pane
local children = scroll_pane.children
local sorts = state.sorts[state.active_tab]
local active_sort = sorts._active
local sorted_history = state.ltn_data.sorted_history[active_sort]
local table_index = 0
-- False = ascending (arrow down), True = descending (arrow up)
local start, finish, step
if sorts[active_sort] then
start = #sorted_history
finish = 1
step = -1
else
start = 1
finish = #sorted_history
step = 1
end
if not global.flags.deleted_history then
for sorted_index = start, finish, step do
local history_id = sorted_history[sorted_index]
local history_entry = ltn_history[history_id]
if
(search_surface == -1 or (history_entry.surface_index == search_surface))
and bit32.btest(history_entry.network_id, search_network_id)
and (
#search_query == 0 or string.find(history_entry.search_strings[self.player.index], string.lower(search_query))
)
then
table_index = table_index + 1
local row = children[table_index]
local color = table_index % 2 == 0 and "dark" or "light"
if not row then
row = gui.add(scroll_pane, {
type = "frame",
style = "ltnm_table_row_frame_" .. color,
{
type = "label",
style = "ltnm_clickable_semibold_label",
style_mods = { width = widths.history.train_id, horizontal_align = "center" },
tooltip = constants.open_station_gui_tooltip,
},
{
type = "flow",
style_mods = { vertical_spacing = 0 },
direction = "vertical",
{
type = "label",
style = "ltnm_clickable_semibold_label",
style_mods = { width = widths.history.route },
tooltip = constants.open_station_gui_tooltip,
},
{
type = "label",
style = "ltnm_clickable_semibold_label",
style_mods = { width = widths.history.route },
tooltip = constants.open_station_gui_tooltip,
},
},
{ type = "label", style_mods = { width = widths.history.depot } },
{ type = "label", style_mods = { width = widths.history.network_id, horizontal_align = "center" } },
{ type = "label", style_mods = { width = widths.history.finished, horizontal_align = "center" } },
{ type = "label", style_mods = { width = widths.history.runtime, horizontal_align = "center" } },
{
type = "frame",
name = "shipment_frame",
style = "ltnm_small_slot_table_frame_" .. color,
style_mods = { width = widths.history.shipment },
{ type = "table", name = "shipment_table", style = "slot_table", column_count = 4 },
},
})
end
gui.update(row, {
{
elem_mods = { caption = history_entry.train_id },
actions = {
on_click = { gui = "main", action = "open_train_gui", train_id = history_entry.train_id },
},
},
{
{
elem_mods = { caption = history_entry.from },
actions = {
on_click = { gui = "main", action = "open_station_gui", station_id = history_entry.from_id },
},
},
{
elem_mods = {
caption = "[color=" .. constants.colors.caption.str .. "]->[/color] " .. history_entry.to,
},
actions = {
on_click = { gui = "main", action = "open_station_gui", station_id = history_entry.to_id },
},
},
},
{ elem_mods = { caption = history_entry.depot } },
{ elem_mods = { caption = util.signed_int32(history_entry.network_id) } },
{ elem_mods = { caption = misc.ticks_to_timestring(history_entry.runtime) } },
{ elem_mods = { caption = misc.ticks_to_timestring(history_entry.finished) } },
})
util.slot_table_update(
row.shipment_frame.shipment_table,
{ { color = "default", entries = history_entry.shipment, translations = dictionaries.materials } }
)
end
end
end
for child_index = table_index + 1, #children do
children[child_index].destroy()
end
if table_index == 0 then
refs.warning_flow.visible = true
scroll_pane.visible = false
refs.content_frame.style = "ltnm_main_warning_frame"
refs.clear_button.enabled = false
else
refs.warning_flow.visible = false
scroll_pane.visible = true
refs.content_frame.style = "ltnm_main_content_frame"
refs.clear_button.enabled = true
end
end
return history_tab

View File

@@ -0,0 +1,330 @@
local gui = require("__flib__.gui")
local misc = require("__flib__.misc")
local queue = require("lib.queue")
local table = require("__flib__.table")
local constants = require("constants")
local actions = require("actions")
local templates = require("templates")
local trains_tab = require("trains")
local depots_tab = require("depots")
local stations_tab = require("stations")
local inventory_tab = require("inventory")
local history_tab = require("history")
local alerts_tab = require("alerts")
-- Object methods
local Index = {}
Index.actions = actions
function Index:destroy()
self.refs.window.destroy()
self.player_table.guis.main = nil
self.player.set_shortcut_toggled("ltnm-toggle-gui", false)
self.player.set_shortcut_available("ltnm-toggle-gui", false)
end
function Index:open()
self.state.ltn_data = global.data
self:update() -- TODO: Do we want to do this every time?
self.refs.window.bring_to_front()
self.refs.window.visible = true
self.state.visible = true
if not self.state.pinned then self.player.opened = self.refs.window end
self.player.set_shortcut_toggled("ltnm-toggle-gui", true)
end
function Index:close()
if self.state.pinning then return end
self.refs.window.visible = false
self.state.visible = false
if self.player.opened == self.refs.window then self.player.opened = nil end
self.player.set_shortcut_toggled("ltnm-toggle-gui", false)
end
function Index:toggle()
if self.state.visible then
Index.close(self)
else
Index.open(self)
end
end
function Index:dispatch(msg, e)
-- "Transform" the action based on criteria
if msg.transform == "handle_refresh_click" then
if e.shift then
msg.action = "toggle_auto_refresh"
else
self.state.ltn_data = global.data
self.do_update = true
end
elseif msg.transform == "handle_titlebar_click" then
if e.button == defines.mouse_button_type.middle then msg.action = "recenter" end
end
-- Dispatch the associated action
if msg.action then
local func = self.actions[msg.action]
if func then
func(self, msg, e)
else
log("Attempted to call action `" .. msg.action .. "` for which there is no handler yet.")
end
end
-- Update if necessary
if self.do_update then
self:update()
self.do_update = false
end
end
function Index:schedule_update() self.do_update = true end
function Index:update()
local state = self.state
local refs = self.refs
local ltn_data = self.state.ltn_data
-- Dispatcher status
refs.titlebar.dispatcher_status_label.visible = not settings.global["ltn-dispatcher-enabled"].value
-- Surface dropdown
local surface_dropdown = refs.toolbar.surface_dropdown
surface_dropdown.items = ltn_data.surfaces.items
-- Validate that the selected index still exist
local selected_index = table.find(ltn_data.surfaces.selected_to_index, state.surface)
-- If the surface was invalidated since last update, reset to all
if not selected_index then
selected_index = 1
state.surface = -1
end
surface_dropdown.selected_index = selected_index
refs.trains.tab.badge_text = misc.delineate_number(#ltn_data.sorted_trains.composition)
refs.depots.tab.badge_text = misc.delineate_number(#ltn_data.sorted_depots.name)
refs.stations.tab.badge_text = misc.delineate_number(#ltn_data.sorted_stations.name)
refs.history.tab.badge_text = misc.delineate_number(queue.length(ltn_data.history))
refs.alerts.tab.badge_text = misc.delineate_number(queue.length(ltn_data.alerts))
if state.active_tab == "trains" then
trains_tab.update(self)
elseif state.active_tab == "depots" then
depots_tab.update(self)
elseif state.active_tab == "stations" then
stations_tab.update(self)
elseif state.active_tab == "inventory" then
inventory_tab.update(self)
elseif state.active_tab == "history" then
history_tab.update(self)
elseif state.active_tab == "alerts" then
alerts_tab.update(self)
end
end
-- Constructor and utilities
local index = {}
function index.build(player, player_table)
local widths = constants.gui[player_table.language] or constants.gui["en"]
local refs = gui.build(player.gui.screen, {
{
type = "frame",
direction = "vertical",
visible = false,
ref = { "window" },
actions = {
on_closed = { gui = "main", action = "close" },
},
{
type = "flow",
style = "flib_titlebar_flow",
ref = { "titlebar", "flow" },
actions = {
on_click = { gui = "main", transform = "handle_titlebar_click" },
},
{
type = "label",
style = "frame_title",
caption = { "mod-name.LtnManager" },
ignored_by_interaction = true,
},
{ type = "empty-widget", style = "flib_titlebar_drag_handle", ignored_by_interaction = true },
{
type = "label",
style = "bold_label",
style_mods = { font_color = constants.colors.red.tbl, left_margin = -4, top_margin = 1 },
caption = { "gui.ltnm-dispatcher-disabled" },
tooltip = { "gui.ltnm-dispatcher-disabled-description" },
ref = { "titlebar", "dispatcher_status_label" },
visible = false,
},
templates.frame_action_button(
"ltnm_pin",
{ "gui.ltnm-keep-open" },
{ "titlebar", "pin_button" },
{ gui = "main", action = "toggle_pinned" }
),
templates.frame_action_button(
"ltnm_refresh",
{ "gui.ltnm-refresh-tooltip" },
{ "titlebar", "refresh_button" },
{ gui = "main", transform = "handle_refresh_click" }
),
templates.frame_action_button(
"utility/close",
{ "gui.close-instruction" },
nil,
{ gui = "main", action = "close" }
),
},
{
type = "frame",
style = "inside_deep_frame",
direction = "vertical",
{
type = "frame",
style = "ltnm_main_toolbar_frame",
{ type = "label", style = "subheader_caption_label", caption = { "gui.ltnm-search-label" } },
{
type = "textfield",
clear_and_focus_on_right_click = true,
ref = { "toolbar", "text_search_field" },
actions = {
on_text_changed = { gui = "main", action = "update_text_search_query" },
},
},
{ type = "empty-widget", style = "flib_horizontal_pusher" },
{ type = "label", style = "caption_label", caption = { "gui.ltnm-network-id-label" } },
{
type = "textfield",
style_mods = { width = 120 },
numeric = true,
allow_negative = true,
clear_and_focus_on_right_click = true,
text = "-1",
ref = { "toolbar", "network_id_field" },
actions = {
on_text_changed = { gui = "main", action = "update_network_id_query" },
},
},
{ type = "label", style = "caption_label", caption = { "gui.ltnm-surface-label" } },
{
type = "drop-down",
ref = { "toolbar", "surface_dropdown" },
actions = {
on_selection_state_changed = { gui = "main", action = "change_surface" },
},
},
},
{
type = "tabbed-pane",
style = "ltnm_tabbed_pane",
trains_tab.build(widths),
depots_tab.build(widths),
stations_tab.build(widths),
inventory_tab.build(),
history_tab.build(widths),
alerts_tab.build(widths),
},
},
},
})
refs.titlebar.flow.drag_target = refs.window
refs.window.force_auto_center()
local Gui = {
player = player,
player_table = player_table,
refs = refs,
state = {
active_tab = "trains",
closing = false,
do_update = false,
ltn_data = global.data,
network_id = -1,
sorts = {
trains = {
_active = "train_id",
train_id = false,
status = false,
composition = false,
depot = false,
shipment = false,
},
depots = {
_active = "name",
name = false,
network_id = false,
status = false,
trains = false,
},
stations = {
_active = "name",
name = false,
status = false,
network_id = false,
provided_requested = false,
shipments = false,
control_signals = false,
},
history = {
_active = "runtime",
train_id = false,
route = false,
depot = false,
network_id = false,
runtime = false,
finished = true,
shipment = false,
},
alerts = {
_active = "time",
time = true,
train_id = false,
route = false,
network_id = false,
type = false,
contents = false,
},
},
surface = -1,
pinned = false,
search_query = "",
visible = false,
},
widths = widths,
}
index.load(Gui)
player_table.guis.main = Gui
player_table.flags.can_open_gui = true
player.set_shortcut_available("ltnm-toggle-gui", true)
end
function index.load(Gui) setmetatable(Gui, { __index = Index }) end
function index.get(player_index)
local Gui = global.players[player_index].guis.main
if Gui and Gui.refs.window.valid then return setmetatable(Gui, { __index = Index }) end
end
return index

View File

@@ -0,0 +1,87 @@
local misc = require("__flib__.misc")
local templates = require("templates")
local inventory_tab = {}
function inventory_tab.build()
return {
tab = {
type = "tab",
caption = { "gui.ltnm-inventory" },
ref = { "inventory", "tab" },
actions = {
on_click = { gui = "main", action = "change_tab", tab = "inventory" },
},
},
content = {
type = "flow",
style_mods = { horizontal_spacing = 12 },
direction = "horizontal",
ref = { "inventory", "content_frame" },
templates.inventory_slot_table("provided", 12),
templates.inventory_slot_table("in_transit", 8),
templates.inventory_slot_table("requested", 6),
},
}
end
local function update_table(self, name, color)
local translations = self.player_table.dictionaries.materials
local state = self.state
local refs = self.refs.inventory
local search_query = state.search_query
local search_network_id = state.network_id
local search_surface = state.surface
local ltn_inventory = state.ltn_data.inventory[name][search_surface]
local i = 0
local table = refs[name].table
local children = table.children
for name, count_by_network_id in pairs(ltn_inventory or {}) do
if
bit32.btest(count_by_network_id.combined_id, search_network_id)
and string.find(string.lower(translations[name]), string.lower(search_query))
then
local running_count = 0
for network_id, count in pairs(count_by_network_id) do
if network_id ~= "combined_id" and bit32.btest(network_id, search_network_id) then
running_count = running_count + count
end
end
if running_count > 0 then
i = i + 1
local button = children[i]
if not button then
button = table.add({ type = "sprite-button", style = "flib_slot_button_" .. color, enabled = false })
end
button.sprite = string.gsub(name, ",", "/")
button.number = running_count
button.tooltip = "[img="
.. string.gsub(name, ",", "/")
.. "] [font=default-semibold]"
.. translations[name]
.. "[/font]\n"
.. misc.delineate_number(running_count)
end
end
end
for j = i + 1, #children do
children[j].destroy()
end
end
function inventory_tab.update(self)
update_table(self, "provided", "green")
update_table(self, "in_transit", "blue")
update_table(self, "requested", "red")
end
return inventory_tab

View File

@@ -0,0 +1,174 @@
local gui = require("__flib__.gui")
local constants = require("constants")
local util = require("scripts.util")
local templates = require("templates")
local stations_tab = {}
function stations_tab.build(widths)
return {
tab = {
type = "tab",
caption = { "gui.ltnm-stations" },
ref = { "stations", "tab" },
actions = {
on_click = { gui = "main", action = "change_tab", tab = "stations" },
},
},
content = {
type = "frame",
style = "ltnm_main_content_frame",
direction = "vertical",
ref = { "stations", "content_frame" },
{
type = "frame",
style = "ltnm_table_toolbar_frame",
templates.sort_checkbox(widths, "stations", "name", true),
templates.sort_checkbox(widths, "stations", "status", false, { "gui.ltnm-status-description" }),
templates.sort_checkbox(widths, "stations", "network_id", false),
templates.sort_checkbox(
widths,
"stations",
"provided_requested",
false,
{ "gui.ltnm-provided-requested-description" }
),
templates.sort_checkbox(widths, "stations", "shipments", false, { "gui.ltnm-shipments-description" }),
templates.sort_checkbox(widths, "stations", "control_signals", false),
},
{ type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "stations", "scroll_pane" } },
{
type = "flow",
style = "ltnm_warning_flow",
visible = false,
ref = { "stations", "warning_flow" },
{
type = "label",
style = "ltnm_semibold_label",
caption = { "gui.ltnm-no-stations" },
ref = { "stations", "warning_label" },
},
},
},
}
end
function stations_tab.update(self)
local dictionaries = self.player_table.dictionaries
local state = self.state
local refs = self.refs.stations
local widths = self.widths.stations
local search_query = state.search_query
local search_network_id = state.network_id
local search_surface = state.surface
local ltn_stations = state.ltn_data.stations
local scroll_pane = refs.scroll_pane
local children = scroll_pane.children
local sorts = state.sorts.stations
local active_sort = sorts._active
local sorted_stations = state.ltn_data.sorted_stations[active_sort]
local table_index = 0
-- False = ascending (arrow down), True = descending (arrow up)
local start, finish, step
if sorts[active_sort] then
start = #sorted_stations
finish = 1
step = -1
else
start = 1
finish = #sorted_stations
step = 1
end
for sorted_index = start, finish, step do
local station_id = sorted_stations[sorted_index]
local station_data = ltn_stations[station_id]
if station_data.entity.valid then
if
(search_surface == -1 or station_data.entity.surface.index == search_surface)
and bit32.btest(station_data.network_id, search_network_id)
and (
#search_query == 0 or string.find(station_data.search_strings[self.player.index], string.lower(search_query))
)
then
table_index = table_index + 1
local row = children[table_index]
local color = table_index % 2 == 0 and "dark" or "light"
if not row then
row = gui.add(scroll_pane, {
type = "frame",
style = "ltnm_table_row_frame_" .. color,
{
type = "label",
style = "ltnm_clickable_semibold_label",
style_mods = { width = widths.name },
tooltip = constants.open_station_gui_tooltip,
},
templates.status_indicator(widths.status, true),
{ type = "label", style_mods = { width = widths.network_id, horizontal_align = "center" } },
templates.small_slot_table(widths, color, "provided_requested"),
templates.small_slot_table(widths, color, "shipments"),
templates.small_slot_table(widths, color, "control_signals"),
})
end
gui.update(row, {
{
elem_mods = { caption = station_data.name },
actions = {
on_click = { gui = "main", action = "open_station_gui", station_id = station_id },
},
},
{
{ elem_mods = { sprite = "flib_indicator_" .. station_data.status.color } },
{ elem_mods = { caption = station_data.status.count } },
},
{ elem_mods = { caption = station_data.network_id } },
})
util.slot_table_update(row.provided_requested_frame.provided_requested_table, {
{ color = "green", entries = station_data.provided, translations = dictionaries.materials },
{ color = "red", entries = station_data.requested, translations = dictionaries.materials },
})
util.slot_table_update(row.shipments_frame.shipments_table, {
{ color = "green", entries = station_data.inbound, translations = dictionaries.materials },
{ color = "blue", entries = station_data.outbound, translations = dictionaries.materials },
})
util.slot_table_update(row.control_signals_frame.control_signals_table, {
{
color = "default",
entries = station_data.control_signals,
translations = dictionaries.virtual_signals,
type = "virtual-signal",
},
})
end
end
end
for child_index = table_index + 1, #children do
children[child_index].destroy()
end
if table_index == 0 then
refs.warning_flow.visible = true
scroll_pane.visible = false
refs.content_frame.style = "ltnm_main_warning_frame"
else
refs.warning_flow.visible = false
scroll_pane.visible = true
refs.content_frame.style = "ltnm_main_content_frame"
end
end
return stations_tab

View File

@@ -0,0 +1,98 @@
local constants = require("constants")
local templates = {}
--- Creates a frame action button, automatically accounting for inverted sprites.
--- @param sprite string|nil
--- @param tooltip string|nil
--- @param ref string[]|nil
--- @param action table|nil
function templates.frame_action_button(sprite, tooltip, ref, action)
return {
type = "sprite-button",
style = "frame_action_button",
sprite = sprite .. "_white",
hovered_sprite = sprite .. "_black",
clicked_sprite = sprite .. "_black",
mouse_button_filter = { "left" },
tooltip = tooltip,
ref = ref,
actions = { on_click = action },
}
end
--- Creates a full-sized scrollable slot table for the inventory tab.
--- @param name string
--- @param columns uint
function templates.inventory_slot_table(name, columns)
return {
type = "flow",
direction = "vertical",
{ type = "label", style = "bold_label", caption = { "gui.ltnm-" .. string.gsub(name, "_", "-") } },
{
type = "frame",
style = "deep_frame_in_shallow_frame",
style_mods = { height = constants.gui_inventory_table_height },
ref = { "inventory", name, "frame" },
{
type = "scroll-pane",
style = "ltnm_slot_table_scroll_pane",
style_mods = { width = 40 * columns + 12, minimal_height = constants.gui_inventory_table_height },
vertical_scroll_policy = "auto-and-reserve-space",
-- vertical_scroll_policy = "always",
ref = { "inventory", name, "scroll_pane" },
{ type = "table", style = "slot_table", column_count = columns, ref = { "inventory", name, "table" } },
},
},
}
end
--- Creates a small non-scrollable slot table.
--- @param widths table
--- @param color string
--- @param name string
function templates.small_slot_table(widths, color, name)
return {
type = "frame",
name = name .. "_frame",
style = "ltnm_small_slot_table_frame_" .. color,
style_mods = { width = widths[name] },
{ type = "table", name = name .. "_table", style = "slot_table", column_count = widths[name .. "_columns"] },
}
end
--- Creates a column header with a sort toggle.
--- @param widths table
--- @param tab string
--- @param column string
--- @param selected boolean
--- @param tooltip string|nil
function templates.sort_checkbox(widths, tab, column, selected, tooltip, state)
if state == nil then
state = false
end
return {
type = "checkbox",
style = selected and "ltnm_selected_sort_checkbox" or "ltnm_sort_checkbox",
style_mods = { width = widths and widths[tab][column] or nil, horizontally_stretchable = not widths },
caption = { "gui.ltnm-" .. string.gsub(column, "_", "-") },
tooltip = tooltip,
state = state,
ref = { tab, "toolbar", column .. "_checkbox" },
actions = {
on_checked_state_changed = { gui = "main", tab = tab, action = "toggle_sort", column = column },
},
}
end
function templates.status_indicator(width, center)
return {
type = "flow",
style = "flib_indicator_flow",
style_mods = { horizontal_align = center and "center" or nil, width = width },
{ type = "sprite", style = "flib_indicator" },
{ type = "label" },
}
end
return templates

View File

@@ -0,0 +1,188 @@
local gui = require("__flib__.gui")
local constants = require("constants")
local util = require("scripts.util")
local templates = require("templates")
local trains_tab = {}
function trains_tab.build(widths)
return {
tab = {
type = "tab",
caption = { "gui.ltnm-trains" },
ref = { "trains", "tab" },
actions = {
on_click = { gui = "main", action = "change_tab", tab = "trains" },
},
},
content = {
type = "frame",
style = "ltnm_main_content_frame",
direction = "vertical",
ref = { "trains", "content_frame" },
{
type = "frame",
style = "ltnm_table_toolbar_frame",
templates.sort_checkbox(widths, "trains", "train_id", true),
templates.sort_checkbox(widths, "trains", "status", false),
templates.sort_checkbox(widths, "trains", "composition", false, { "gui.ltnm-composition-description" }),
templates.sort_checkbox(widths, "trains", "depot", false),
templates.sort_checkbox(widths, "trains", "shipment", false),
},
{ type = "scroll-pane", style = "ltnm_table_scroll_pane", ref = { "trains", "scroll_pane" } },
{
type = "flow",
style = "ltnm_warning_flow",
visible = false,
ref = { "trains", "warning_flow" },
{
type = "label",
style = "ltnm_semibold_label",
caption = { "gui.ltnm-no-trains" },
ref = { "trains", "warning_label" },
},
},
},
}
end
function trains_tab.update(self)
local dictionaries = self.player_table.dictionaries
local state = self.state
local refs = self.refs.trains
local widths = self.widths
local search_query = state.search_query
local search_network_id = state.network_id
local search_surface = state.surface
local ltn_trains = state.ltn_data.trains
local scroll_pane = refs.scroll_pane
local children = scroll_pane.children
local sorts = state.sorts[state.active_tab]
local active_sort = sorts._active
local sorted_trains = state.ltn_data.sorted_trains[active_sort]
if active_sort == "status" then
sorted_trains = sorted_trains[self.player.index]
end
local table_index = 0
-- False = ascending (arrow down), True = descending (arrow up)
local start, finish, step
if sorts[active_sort] then
start = #sorted_trains
finish = 1
step = -1
else
start = 1
finish = #sorted_trains
step = 1
end
for sorted_index = start, finish, step do
local train_id = sorted_trains[sorted_index]
local train_data = ltn_trains[train_id]
if train_data.train.valid and train_data.main_locomotive and train_data.main_locomotive.valid then
if
(search_surface == -1 or (train_data.surface_index == search_surface))
and bit32.btest(train_data.network_id, search_network_id)
and (
#search_query == 0 or string.find(train_data.search_strings[self.player.index], string.lower(search_query))
)
then
table_index = table_index + 1
local row = children[table_index]
local color = table_index % 2 == 0 and "dark" or "light"
if not row then
row = gui.add(scroll_pane, {
type = "frame",
style = "ltnm_table_row_frame_" .. color,
{
type = "frame",
style = "ltnm_table_inset_frame_" .. color,
{
type = "minimap",
style = "ltnm_train_minimap",
{ type = "label", style = "ltnm_minimap_label" },
{ type = "button", style = "ltnm_train_minimap_button", tooltip = { "gui.ltnm-open-train-gui" } },
},
},
{ type = "label", style = "ltnm_clickable_semibold_label" },
{ type = "label", style_mods = { width = widths.trains.composition } },
{ type = "label", style_mods = { width = widths.trains.depot } },
{
type = "frame",
name = "shipment_frame",
style = "ltnm_small_slot_table_frame_" .. color,
style_mods = { width = widths.trains.shipment },
{
type = "table",
name = "shipment_table",
style = "slot_table",
column_count = widths.trains.shipment_columns,
},
},
})
end
local status = train_data.status[self.player.index]
-- TODO: This shouldn't be needed any more
if not status then
status = { color = constants.colors.red.tbl, string = "ERROR" }
end
local station_id = status.station and train_data[status.station .. "_id"] or nil
gui.update(row, {
{
{
elem_mods = { entity = train_data.main_locomotive },
{ elem_mods = { caption = train_id } },
{
actions = {
on_click = { gui = "main", action = "open_train_gui", train_id = train_id },
},
},
},
},
{
actions = {
on_click = station_id and { gui = "main", action = "open_station_gui", station_id = station_id } or false,
},
elem_mods = { caption = status.string, tooltip = station_id and constants.open_station_gui_tooltip or "" },
style = station_id and "ltnm_clickable_semibold_label" or "ltnm_semibold_label",
style_mods = { font_color = status.color or constants.colors.white.tbl, width = widths.trains.status },
},
{ elem_mods = { caption = train_data.composition } },
{ elem_mods = { caption = train_data.depot } },
})
util.slot_table_update(
row.shipment_frame.shipment_table,
{ { color = "default", entries = train_data.shipment, translations = dictionaries.materials } }
)
end
end
end
for child_index = table_index + 1, #children do
children[child_index].destroy()
end
if table_index == 0 then
refs.warning_flow.visible = true
scroll_pane.visible = false
refs.content_frame.style = "ltnm_main_warning_frame"
else
refs.warning_flow.visible = false
scroll_pane.visible = true
refs.content_frame.style = "ltnm_main_content_frame"
end
end
return trains_tab