mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 03:25:23 +09:00
Feature: Toolbar Menu (#268)
* Fix left flow not using uids * Mock Toolbar menu * Fix task list after core gui change * Allow show/hide override * Fix autofill permissions * Copy style from toolbar on change * Open and close automatically * Removed hacky prevent default * Fixed more core issues * Add reset button * Allow for custom draw order on join * Add methods to reorder ui flows * Impliment move buttons * Add locale * Add toolbar to player data * Better player data layout * Picked a suitable datastore id * Update locale for readme * Fix swaping left flow order * Fix datastore updates * Code cleanup * Fix incorrect top flow hashing on load * Fix loading of malformed data * Fixed loading state of left flows * Dont save default data * Dont open menu on join * Lint * Remove incorrect new index metamethod * Revert method used for move_toolbar_button * Fixed missing toolbar button * Fixed desync between visibilty and toggle state * Fix bad gui element path * Fixed enable state of toggle button * Change order of operations * Fix reset not showing top flow
This commit is contained in:
@@ -30,14 +30,12 @@ end
|
||||
local HasEnabledDecon = PlayerData.Settings:combine('HasEnabledDecon')
|
||||
HasEnabledDecon:set_default(false)
|
||||
|
||||
Gui.toolbar_button("entity/tree-01", {'tree-decon.main-tooltip'}, function (player)
|
||||
Gui.toolbar_toggle_button("entity/tree-01", {'tree-decon.main-tooltip'}, function (player)
|
||||
return Roles.player_allowed(player, "fast-tree-decon")
|
||||
end)
|
||||
:on_click(function(player, element)
|
||||
local status = HasEnabledDecon:get(player)
|
||||
HasEnabledDecon:set(player, not status)
|
||||
Gui.toolbar_button_style(element, not status)
|
||||
player.print(status and {'tree-decon.toggle-msg', {'tree-decon.disabled'}} or {'tree-decon.toggle-msg', {'tree-decon.enabled'}})
|
||||
:on_event(Gui.events.on_toolbar_button_toggled, function(player, _, event)
|
||||
HasEnabledDecon:set(player, event.state)
|
||||
player.print{'tree-decon.toggle-msg', event.state and {'tree-decon.enabled'} or {'tree-decon.disabled'}}
|
||||
end)
|
||||
|
||||
|
||||
|
||||
@@ -106,14 +106,12 @@ follow_label =
|
||||
Gui.element(function(definition, parent, target)
|
||||
Gui.destroy_if_valid(parent[definition.name])
|
||||
|
||||
local label = definition:triggers_events(
|
||||
parent.add{
|
||||
type = 'label',
|
||||
style = 'heading_1_label',
|
||||
caption = 'Following '..target.name..'.\nClick here or press esc to stop following.',
|
||||
name = definition.name
|
||||
}
|
||||
)
|
||||
local label = parent.add{
|
||||
type = 'label',
|
||||
style = 'heading_1_label',
|
||||
caption = 'Following '..target.name..'.\nClick here or press esc to stop following.',
|
||||
name = definition.name
|
||||
}
|
||||
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local res = player.display_resolution
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
local Game = require 'utils.game' -- @dep utils.game
|
||||
local Gui = require 'expcore.gui' -- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' -- @dep expcore.gui
|
||||
local Global = require 'utils.global' -- @dep utils.global
|
||||
local config = require 'config.gui.autofill' -- @dep config.gui.autofill
|
||||
local Event = require 'utils.event' -- @dep utils.event
|
||||
@@ -54,13 +55,14 @@ end)
|
||||
--- Toggle enitity button, used for toggling autofill for the specific entity
|
||||
-- All entity autofill settings will be ignored if its disabled
|
||||
-- @element entity_toggle
|
||||
local entity_toggle = Gui.element(function(definition, parent, entity_name)
|
||||
return definition:triggers_events(parent.add{
|
||||
local entity_toggle =
|
||||
Gui.element(function(_, parent, entity_name)
|
||||
return parent.add{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/confirm_slot',
|
||||
tooltip = {'autofill.toggle-entity-tooltip', rich_img('item', entity_name)},
|
||||
style = 'shortcut_bar_button_green'
|
||||
})
|
||||
}
|
||||
end)
|
||||
:style(Gui.sprite_style(22))
|
||||
:on_click(function(player, element, _)
|
||||
@@ -112,11 +114,11 @@ Gui.element(function(definition, parent, section_name, table_size)
|
||||
|
||||
section_table.visible = false
|
||||
|
||||
return section_table
|
||||
return definition:no_events(section_table)
|
||||
end)
|
||||
:on_click(function(_, element, event)
|
||||
event.element = element.parent.alignment[toggle_section.name]
|
||||
toggle_section:raise_custom_event(event)
|
||||
toggle_section:raise_event(event)
|
||||
end)
|
||||
|
||||
--- Toggle item button, used for toggling autofill for the specific item
|
||||
@@ -294,7 +296,9 @@ end)
|
||||
|
||||
--- Button on the top flow used to toggle autofill container
|
||||
-- @element autofill_toggle
|
||||
Gui.left_toolbar_button(config.icon, {'autofill.main-tooltip'}, autofill_container)
|
||||
Gui.left_toolbar_button(config.icon, {'autofill.main-tooltip'}, autofill_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/autofill')
|
||||
end)
|
||||
|
||||
--- When a player is created make sure they have the default autofill settings
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
|
||||
@@ -86,7 +86,7 @@ end)
|
||||
--- Set of elements that are used to make up a row of the player table
|
||||
-- @element add_player_base
|
||||
local add_player_base =
|
||||
Gui.element(function(definition, parent, player_data)
|
||||
Gui.element(function(_, parent, player_data)
|
||||
-- Add the button to open the action bar
|
||||
local toggle_action_bar_flow = parent.add{ type = 'flow', name = player_data.name }
|
||||
open_action_bar(toggle_action_bar_flow)
|
||||
@@ -100,7 +100,6 @@ Gui.element(function(definition, parent, player_data)
|
||||
}
|
||||
player_name.style.padding = {0, 2,0, 0}
|
||||
player_name.style.font_color = player_data.chat_color
|
||||
definition:triggers_events(player_name)
|
||||
|
||||
-- Add the time played label
|
||||
local alignment = Gui.alignment(parent, 'player-time-'..player_data.index)
|
||||
@@ -112,7 +111,7 @@ Gui.element(function(definition, parent, player_data)
|
||||
}
|
||||
time_label.style.padding = 0
|
||||
|
||||
return time_label
|
||||
return player_name
|
||||
end)
|
||||
:on_click(function(player, element, event)
|
||||
local selected_player_name = element.caption
|
||||
|
||||
@@ -75,16 +75,16 @@ Gui.element{
|
||||
--- Used to connect to servers in server list
|
||||
-- @element join_server
|
||||
local join_server =
|
||||
Gui.element(function(definition, parent, server_id, wrong_version)
|
||||
Gui.element(function(_, parent, server_id, wrong_version)
|
||||
local status = External.get_server_status(server_id) or 'Offline'
|
||||
if wrong_version then status = 'Version' end
|
||||
local flow = parent.add{ name = server_id, type = 'flow' }
|
||||
local button = definition:triggers_events(flow.add{
|
||||
local button = flow.add{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/circuit_network_panel_white', --- network panel white, warning white, download white
|
||||
hovered_sprite = 'utility/circuit_network_panel_black', --- network panel black, warning black, download black
|
||||
tooltip = {'readme.servers-connect-'..status, wrong_version}
|
||||
})
|
||||
}
|
||||
|
||||
if status == 'Offline' or status == 'Current' then
|
||||
button.enabled = false
|
||||
@@ -438,12 +438,10 @@ Gui.element(function(definition, parent)
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:on_open(function(player)
|
||||
local toggle_button = Gui.get_top_element(player, readme_toggle)
|
||||
Gui.toolbar_button_style(toggle_button, true)
|
||||
Gui.toggle_toolbar_button(player, readme_toggle, true)
|
||||
end)
|
||||
:on_close(function(player, element)
|
||||
local toggle_button = Gui.get_top_element(player, readme_toggle)
|
||||
Gui.toolbar_button_style(toggle_button, false)
|
||||
Gui.toggle_toolbar_button(player, readme_toggle, false)
|
||||
Gui.destroy_if_valid(element)
|
||||
end)
|
||||
|
||||
|
||||
@@ -467,11 +467,11 @@ Gui.element(function(definition, parent, section_name, table_size)
|
||||
scroll_table.parent.visible = false
|
||||
|
||||
-- Return the flow table
|
||||
return scroll_table
|
||||
return definition:no_events(scroll_table)
|
||||
end)
|
||||
:on_click(function(_, element, event)
|
||||
event.element = element.parent.alignment[toggle_section.name]
|
||||
toggle_section:raise_custom_event(event)
|
||||
toggle_section:raise_event(event)
|
||||
end)
|
||||
|
||||
--- Main gui container for the left flow
|
||||
|
||||
@@ -175,24 +175,26 @@ local subfooter_actions =
|
||||
local task_list_item =
|
||||
Gui.element(
|
||||
function(definition, parent, task)
|
||||
local flow =
|
||||
parent.add {
|
||||
local flow = parent.add {
|
||||
type = "flow",
|
||||
name = "task-" .. task.task_id,
|
||||
caption = task.task_id
|
||||
}
|
||||
|
||||
flow.style.horizontally_stretchable = true
|
||||
local button =
|
||||
flow.add {
|
||||
|
||||
local button = flow.add {
|
||||
name = definition.name,
|
||||
type = "button",
|
||||
style = "list_box_item",
|
||||
caption = task.title
|
||||
caption = task.title,
|
||||
tooltip = { "task-list.last-edit", task.last_edit_name, format_time(task.last_edit_time) }
|
||||
}
|
||||
definition:triggers_events(button)
|
||||
|
||||
button.style.horizontally_stretchable = true
|
||||
button.style.horizontally_squashable = true
|
||||
return flow
|
||||
|
||||
return button
|
||||
end
|
||||
):on_click(
|
||||
function(player, element, _)
|
||||
@@ -487,11 +489,7 @@ local repopulate_task_list = function(task_list_element)
|
||||
for _, task_id in ipairs(task_ids) do
|
||||
-- Add the task
|
||||
local task = Tasks.get_task(task_id)
|
||||
local element = task_list_item(task_list_element, task)
|
||||
-- Set tooltip
|
||||
local last_edit_name = task.last_edit_name
|
||||
local last_edit_time = task.last_edit_time
|
||||
element[task_list_item.name].tooltip = {"task-list.last-edit", last_edit_name, format_time(last_edit_time)}
|
||||
task_list_item(task_list_element, task)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -560,19 +558,16 @@ local update_task = function(player, task_list_element, task_id)
|
||||
return
|
||||
end
|
||||
|
||||
local element
|
||||
-- If task does not exist yet add it to the list
|
||||
if not task_list_element["task-" .. task_id] then
|
||||
element = task_list_item(task_list_element, task)
|
||||
local flow = task_list_element["task-" .. task_id]
|
||||
if not flow then
|
||||
-- If task does not exist yet add it to the list
|
||||
task_list_item(task_list_element, task)
|
||||
else
|
||||
-- If the task exists update the caption
|
||||
element = task_list_element["task-" .. task_id]
|
||||
element[task_list_item.name].caption = task.title
|
||||
-- If the task exists update the caption and tooltip
|
||||
local button = flow[task_list_item.name]
|
||||
button.caption = task.title
|
||||
button.tooltip = {"task-list.last-edit", task.last_edit_name, format_time(task.last_edit_time)}
|
||||
end
|
||||
-- Set tooltip
|
||||
local last_edit_name = task.last_edit_name
|
||||
local last_edit_time = task.last_edit_time
|
||||
element[task_list_item.name].tooltip = {"task-list.last-edit", last_edit_name, format_time(last_edit_time)}
|
||||
end
|
||||
|
||||
-- Update the footer task edit view
|
||||
|
||||
523
modules/gui/toolbar.lua
Normal file
523
modules/gui/toolbar.lua
Normal file
@@ -0,0 +1,523 @@
|
||||
local Gui = require "expcore.gui" --- @dep expcore.gui
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
|
||||
-- Used to store the state of the toolbar when a player leaves
|
||||
local ToolbarState = PlayerData.Settings:combine('ToolbarState')
|
||||
ToolbarState:set_metadata{
|
||||
stringify = function(value)
|
||||
local buttons, favourites = 0, 0
|
||||
for _, state in ipairs(value) do
|
||||
buttons = buttons + 1
|
||||
if state.favourite then
|
||||
favourites = favourites + 1
|
||||
end
|
||||
end
|
||||
return string.format("Buttons: %d, Favourites: %d", buttons, favourites)
|
||||
end
|
||||
}
|
||||
|
||||
-- Styles used for sprite buttons
|
||||
local button_size = 20
|
||||
local Styles = {
|
||||
header = Gui.sprite_style(22),
|
||||
item = Gui.sprite_style(button_size)
|
||||
}
|
||||
|
||||
--- Set the style of the fake toolbar element
|
||||
local function copy_style(src, dst)
|
||||
dst.style = src.style.name
|
||||
dst.style.height = button_size
|
||||
dst.style.width = button_size
|
||||
dst.style.padding = -2
|
||||
end
|
||||
|
||||
local toolbar_container, move_up, move_down, toggle_toolbar
|
||||
|
||||
--- Reorder the buttons relative to each other, this will update the datastore
|
||||
local function move_toolbar_button(player, item, offset)
|
||||
local old_index = item.get_index_in_parent()
|
||||
local new_index = old_index + offset
|
||||
|
||||
-- Ideally the following would all happen in on_update but this had too much latency
|
||||
-- Swap the position in the list
|
||||
local list = item.parent
|
||||
local other_item = list.children[new_index]
|
||||
list.swap_children(old_index, new_index)
|
||||
|
||||
-- Swap the position in the top flow, offset by 1 because of settings button
|
||||
local top_flow = Gui.get_top_flow(player)
|
||||
top_flow.swap_children(old_index+1, new_index+1)
|
||||
|
||||
-- Check if the element has a left element to move
|
||||
local element_define = Gui.defines[item.tags.top_element_uid]
|
||||
local other_define = Gui.defines[other_item.tags.top_element_uid]
|
||||
if element_define.left_flow_element and other_define.left_flow_element then
|
||||
local left_element = Gui.get_left_element(player, element_define.left_flow_element)
|
||||
local other_left_element = Gui.get_left_element(player, other_define.left_flow_element)
|
||||
local left_index = left_element.get_index_in_parent()
|
||||
local other_index = other_left_element.get_index_in_parent()
|
||||
left_element.parent.swap_children(left_index, other_index)
|
||||
end
|
||||
|
||||
-- If we are moving in/out of first/last place we need to update the move buttons
|
||||
local last_index = #list.children
|
||||
if old_index == 1 then -- Moving out of index 1
|
||||
other_item.move[move_up.name].enabled = false
|
||||
item.move[move_up.name].enabled = true
|
||||
elseif new_index == 1 then -- Moving into index 1
|
||||
other_item.move[move_up.name].enabled = true
|
||||
item.move[move_up.name].enabled = false
|
||||
elseif old_index == last_index then -- Moving out of the last index
|
||||
other_item.move[move_down.name].enabled = false
|
||||
item.move[move_down.name].enabled = true
|
||||
elseif new_index == last_index then -- Moving into the last index
|
||||
other_item.move[move_down.name].enabled = true
|
||||
item.move[move_down.name].enabled = false
|
||||
end
|
||||
|
||||
-- Update the datastore state
|
||||
ToolbarState:update(player, function(_, order)
|
||||
local tmp = order[old_index]
|
||||
order[old_index] = order[new_index]
|
||||
order[new_index] = tmp
|
||||
end)
|
||||
end
|
||||
|
||||
--- Reorder the toolbar buttons
|
||||
local function reorder_toolbar_menu(player)
|
||||
local frame = Gui.get_left_element(player, toolbar_container)
|
||||
local list = frame.container.scroll.list
|
||||
local order = ToolbarState:get(player)
|
||||
local last_index = #order
|
||||
|
||||
-- Reorder the buttons
|
||||
for index, state in ipairs(order) do
|
||||
local element_define = Gui.defines[state.element_uid]
|
||||
|
||||
-- Switch item order
|
||||
local item = list[element_define.name]
|
||||
list.swap_children(index, item.get_index_in_parent())
|
||||
|
||||
-- Check if the player is allowed to see the button
|
||||
local allowed = element_define.authenticator
|
||||
if type(allowed) == 'function' then allowed = allowed(player) end
|
||||
|
||||
-- Update the checkbox state and item visibility
|
||||
local toolbar_button = Gui.get_top_element(player, element_define)
|
||||
toolbar_button.visible = allowed and state.favourite or false
|
||||
item.checkbox.state = state.favourite
|
||||
|
||||
-- Update the state if the move buttons
|
||||
item.move[move_up.name].enabled = index ~= 1
|
||||
item.move[move_down.name].enabled = index ~= last_index
|
||||
end
|
||||
|
||||
-- Update the state of the toggle button
|
||||
local button = frame.container.header.alignment[toggle_toolbar.name]
|
||||
button.enabled = Gui.top_flow_has_visible_elements(player)
|
||||
button.toggled = Gui.get_top_flow(player).parent.visible
|
||||
end
|
||||
|
||||
--- Resets the toolbar to its default state when pressed
|
||||
-- @element reset_toolbar
|
||||
local reset_toolbar =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/reset",
|
||||
style = "shortcut_bar_button_red",
|
||||
tooltip = {"toolbar.reset"},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Gui.sprite_style(Styles.header.width, -1))
|
||||
:on_click(function(player)
|
||||
ToolbarState:set(player, nil)
|
||||
Gui.toggle_top_flow(player, true)
|
||||
reorder_toolbar_menu(player)
|
||||
end)
|
||||
|
||||
--- Replaces the default method for opening and closing the toolbar
|
||||
-- @element toggle_toolbar
|
||||
toggle_toolbar =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/bookmark",
|
||||
tooltip = {"toolbar.toggle"},
|
||||
style = "tool_button",
|
||||
auto_toggle = true,
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Styles.header)
|
||||
:on_click(function(player, element)
|
||||
Gui.toggle_top_flow(player, element.toggled)
|
||||
end)
|
||||
|
||||
--- Move an element up the list
|
||||
-- @element move_up
|
||||
move_up =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/speed_up",
|
||||
tooltip = {"toolbar.move-up"},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Styles.item)
|
||||
:on_click(function(player, element)
|
||||
local item = element.parent.parent
|
||||
move_toolbar_button(player, item, -1)
|
||||
end)
|
||||
|
||||
--- Move an element down the list
|
||||
-- @element move_down
|
||||
move_down =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/speed_down",
|
||||
tooltip = {"toolbar.move-down"},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Styles.item)
|
||||
:on_click(function(player, element)
|
||||
local item = element.parent.parent
|
||||
move_toolbar_button(player, item, 1)
|
||||
end)
|
||||
|
||||
--- A flow which represents one item in the toolbar list
|
||||
-- @element toolbar_list_item
|
||||
local toolbar_list_item =
|
||||
Gui.element(function(definition, parent, element_define)
|
||||
local flow = parent.add {
|
||||
type = "frame",
|
||||
style = "shortcut_selection_row",
|
||||
name = element_define.name,
|
||||
tags = {
|
||||
top_element_uid = element_define.uid
|
||||
}
|
||||
}
|
||||
flow.style.horizontally_stretchable = true
|
||||
flow.style.vertical_align = "center"
|
||||
|
||||
-- Add the button and the icon edit button
|
||||
local element = element_define(flow)
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local top_element = Gui.get_top_element(player, element_define)
|
||||
copy_style(top_element, element)
|
||||
|
||||
-- Add the checkbox that can toggle the visibility
|
||||
local checkbox = flow.add{
|
||||
type = "checkbox",
|
||||
name = "checkbox",
|
||||
caption = element_define.tooltip or element_define.caption or "None",
|
||||
state = top_element.visible or false,
|
||||
tags = {
|
||||
top_element_name = element_define.name
|
||||
}
|
||||
}
|
||||
definition:triggers_events(checkbox)
|
||||
checkbox.style.width = 180
|
||||
|
||||
-- Add the buttons used to move the flow up and down
|
||||
local move_flow = flow.add{ type = "flow", name = "move" }
|
||||
move_flow.style.horizontal_spacing = 0
|
||||
move_up(move_flow)
|
||||
move_down(move_flow)
|
||||
|
||||
return definition:no_events(flow)
|
||||
end)
|
||||
:on_checked_changed(function(player, element)
|
||||
local top_flow = Gui.get_top_flow(player)
|
||||
local top_element = top_flow[element.tags.top_element_name]
|
||||
local had_visible = Gui.top_flow_has_visible_elements(player)
|
||||
top_element.visible = element.state
|
||||
|
||||
-- Check if we are on the edge case between 0 and 1 visible elements
|
||||
if element.state and not had_visible then
|
||||
Gui.toggle_top_flow(player, true)
|
||||
local container = element.parent.parent.parent.parent
|
||||
local button = container.header.alignment[toggle_toolbar.name]
|
||||
button.toggled = true
|
||||
button.enabled = true
|
||||
elseif not element.state and not Gui.top_flow_has_visible_elements(player) then
|
||||
Gui.toggle_top_flow(player, false)
|
||||
local container = element.parent.parent.parent.parent
|
||||
local button = container.header.alignment[toggle_toolbar.name]
|
||||
button.toggled = false
|
||||
button.enabled = false
|
||||
end
|
||||
|
||||
-- Update the datastore state
|
||||
ToolbarState:update(player, function(_, order)
|
||||
local index = element.parent.get_index_in_parent()
|
||||
order[index].favourite = element.state
|
||||
end)
|
||||
end)
|
||||
|
||||
--- Scrollable list of all toolbar buttons
|
||||
-- @element toolbar_list
|
||||
local toolbar_list =
|
||||
Gui.element(function(_, parent)
|
||||
-- This is a scroll pane for the list
|
||||
local scroll_pane = parent.add {
|
||||
name = "scroll",
|
||||
type = "scroll-pane",
|
||||
direction = "vertical",
|
||||
horizontal_scroll_policy = "never",
|
||||
vertical_scroll_policy = "auto",
|
||||
style = "scroll_pane_under_subheader"
|
||||
}
|
||||
scroll_pane.style.horizontally_stretchable = true
|
||||
scroll_pane.style.padding = 0
|
||||
scroll_pane.style.maximal_height = 224
|
||||
|
||||
-- This flow is the list, we need a linear list because of get_index_in_parent
|
||||
local flow = scroll_pane.add {
|
||||
name = "list",
|
||||
type = "flow",
|
||||
direction = "vertical"
|
||||
}
|
||||
flow.style.vertical_spacing = 0
|
||||
flow.style.horizontally_stretchable = true
|
||||
|
||||
return flow
|
||||
end)
|
||||
|
||||
--- Main toolbar container for the left flow
|
||||
-- @element toolbar_container
|
||||
toolbar_container =
|
||||
Gui.element(function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name, 268)
|
||||
container.style.maximal_width = 268
|
||||
container.style.minimal_width = 268
|
||||
|
||||
-- Draw the header
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local header = Gui.header(container, {"toolbar.main-caption"}, {"toolbar.main-tooltip"}, true)
|
||||
|
||||
-- Draw the toolbar control buttons
|
||||
local toggle_element = toggle_toolbar(header)
|
||||
toggle_element.toggled = Gui.get_top_flow(player).visible
|
||||
reset_toolbar(header)
|
||||
|
||||
-- Draw toolbar list element
|
||||
local list_element = toolbar_list(container)
|
||||
local flow_order = Gui.get_top_flow_order(player)
|
||||
|
||||
for _, element_define in ipairs(flow_order) do
|
||||
-- Ensure the element exists
|
||||
local element = list_element[element_define.name]
|
||||
if not element then
|
||||
element = toolbar_list_item(list_element, element_define)
|
||||
end
|
||||
|
||||
-- Set the visible state
|
||||
local allowed = element_define.authenticator
|
||||
if type(allowed) == 'function' then allowed = allowed(player) end
|
||||
element.visible = allowed or false
|
||||
end
|
||||
|
||||
-- Set the state of the move buttons for the first and last element
|
||||
local children = list_element.children
|
||||
children[1].move[move_up.name].enabled = false
|
||||
children[#children].move[move_down.name].enabled = false
|
||||
|
||||
-- Return the external container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow(false)
|
||||
|
||||
--- Set the default value for the datastore
|
||||
local datastore_id_map = {}
|
||||
local toolbar_default_state = {}
|
||||
ToolbarState:set_default(toolbar_default_state)
|
||||
|
||||
--- Get the datastore id for this element define, to best of ability it should be unique between versions
|
||||
local function to_datastore_id(element_define)
|
||||
-- First try to use the tooltip locale string
|
||||
local tooltip = element_define.tooltip
|
||||
if type(tooltip) == "table" then
|
||||
return tooltip[1]:gsub("%.(.+)", "")
|
||||
end
|
||||
|
||||
-- Then try to use the caption or sprite
|
||||
return element_define.caption or element_define.sprite
|
||||
end
|
||||
|
||||
--- For all top element, register an on click which will copy their style
|
||||
for index, element_define in ipairs(Gui.top_elements) do
|
||||
-- This is a bit hacky, the gui system cant have multiple handlers registered
|
||||
local prev_handler = element_define[Gui.events.on_toolbar_button_toggled]
|
||||
|
||||
-- Add the handler for when the button is toggled
|
||||
element_define:on_event(Gui.events.on_toolbar_button_toggled, function(player, element, event)
|
||||
if prev_handler then prev_handler(player, element, event) end -- Kind of hacky but works
|
||||
local frame = Gui.get_left_element(player, toolbar_container)
|
||||
if not frame then return end -- Gui might not be loaded yet
|
||||
local button = frame.container.scroll.list[element_define.name][element_define.name]
|
||||
local toolbar_button = Gui.get_top_element(player, element_define)
|
||||
copy_style(toolbar_button, button)
|
||||
end)
|
||||
|
||||
-- Insert the element into the id map
|
||||
local id = to_datastore_id(element_define)
|
||||
if datastore_id_map[id] then
|
||||
error(string.format("All toolbar elements need a unique id to be saved correctly, %d (%s) and %d (%s) share the id %s",
|
||||
datastore_id_map[id].uid, datastore_id_map[id].defined_at, element_define.uid, element_define.defined_at, id
|
||||
))
|
||||
end
|
||||
datastore_id_map[id] = element_define
|
||||
|
||||
-- Add the element to the default state
|
||||
table.insert(toolbar_default_state, {
|
||||
element_uid = element_define.uid,
|
||||
favourite = true,
|
||||
})
|
||||
end
|
||||
|
||||
--- Get the top order based on the players settings
|
||||
Gui.inject_top_flow_order(function(player)
|
||||
local order = ToolbarState:get(player)
|
||||
|
||||
local elements = {}
|
||||
for index, state in ipairs(order) do
|
||||
elements[index] = Gui.defines[state.element_uid]
|
||||
end
|
||||
|
||||
return elements
|
||||
end)
|
||||
|
||||
--- Get the left order based on the player settings, with toolbar menu first, and all remaining after
|
||||
Gui.inject_left_flow_order(function(player)
|
||||
local order = Gui.get_top_flow_order(player)
|
||||
local elements, element_map = { toolbar_container }, { [toolbar_container] = true }
|
||||
|
||||
-- Add the flows that have a top element
|
||||
for _, element_define in ipairs(order) do
|
||||
if element_define.left_flow_element then
|
||||
table.insert(elements, element_define.left_flow_element)
|
||||
element_map[element_define.left_flow_element] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Add the flows that dont have a top element
|
||||
for _, element_define in ipairs(Gui.left_elements) do
|
||||
if not element_map[element_define] then
|
||||
table.insert(elements, element_define)
|
||||
end
|
||||
end
|
||||
|
||||
return elements
|
||||
end)
|
||||
|
||||
--- Overwrite the default toggle behaviour and instead toggle this menu
|
||||
Gui.core_defines.hide_top_flow:on_click(function(player, _, _)
|
||||
Gui.toggle_left_element(player, toolbar_container)
|
||||
end)
|
||||
|
||||
--- Overwrite the default toggle behaviour and instead toggle this menu
|
||||
Gui.core_defines.show_top_flow:on_click(function(player, _, _)
|
||||
Gui.toggle_left_element(player, toolbar_container)
|
||||
end)
|
||||
|
||||
--- Overwrite the default update top flow
|
||||
local _update_top_flow = Gui.update_top_flow
|
||||
function Gui.update_top_flow(player)
|
||||
_update_top_flow(player) -- Call the original
|
||||
|
||||
local order = ToolbarState:get(player)
|
||||
for index, state in ipairs(order) do
|
||||
local element_define = Gui.defines[state.element_uid]
|
||||
local top_element = Gui.get_top_element(player, element_define)
|
||||
top_element.visible = top_element.visible and state.favourite or false
|
||||
end
|
||||
end
|
||||
|
||||
--- Uncompress the data to be more useable
|
||||
ToolbarState:on_load(function(player_name, value)
|
||||
-- If there is no value, do nothing
|
||||
if value == nil then return end
|
||||
|
||||
-- Create a hash map of the favourites
|
||||
local favourites = {}
|
||||
for _, id in ipairs(value[2]) do
|
||||
favourites[id] = true
|
||||
end
|
||||
|
||||
-- Read the order from the value
|
||||
local elements = {}
|
||||
local element_hash = {}
|
||||
for index, id in ipairs(value[1]) do
|
||||
local element = datastore_id_map[id]
|
||||
if element and not element_hash[element.uid] then
|
||||
element_hash[element.uid] = true
|
||||
elements[index] = {
|
||||
element_uid = element.uid,
|
||||
favourite = favourites[id] or false,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Add any in the default state that are missing
|
||||
for _, state in ipairs(toolbar_default_state) do
|
||||
if not element_hash[state.element_uid] then
|
||||
table.insert(elements, table.deep_copy(state))
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a hash map of the open left flows
|
||||
local left_flows = {}
|
||||
for _, id in ipairs(value[3]) do
|
||||
local element = datastore_id_map[id]
|
||||
if element.left_flow_element then
|
||||
left_flows[element.left_flow_element] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Set the visible state of all left flows
|
||||
local player = game.get_player(player_name)
|
||||
for _, left_element in ipairs(Gui.left_elements) do
|
||||
Gui.toggle_left_element(player, left_element, left_flows[left_element] or false)
|
||||
end
|
||||
|
||||
-- Set the toolbar visible state
|
||||
Gui.toggle_top_flow(player, value[4])
|
||||
|
||||
-- Set the data now and update now, ideally this would be on_update but that had too large of a latency
|
||||
ToolbarState:raw_set(player_name, elements)
|
||||
Gui.reorder_top_flow(player)
|
||||
Gui.reorder_left_flow(player)
|
||||
reorder_toolbar_menu(player)
|
||||
|
||||
return elements
|
||||
end)
|
||||
|
||||
--- Save the current state of the players toolbar menu
|
||||
ToolbarState:on_save(function(player_name, value)
|
||||
if value == nil then return nil end -- Don't save default
|
||||
local order, favourites, left_flows = {}, {}, {}
|
||||
|
||||
local player = game.get_player(player_name)
|
||||
local top_flow_open = Gui.get_top_flow(player).parent.visible
|
||||
|
||||
for index, state in ipairs(value) do
|
||||
-- Add the element to the order array
|
||||
local element_define = Gui.defines[state.element_uid]
|
||||
local id = to_datastore_id(element_define)
|
||||
order[index] = id
|
||||
|
||||
-- If its a favourite then insert it
|
||||
if state.favourite then
|
||||
table.insert(favourites, id)
|
||||
end
|
||||
|
||||
-- If it has a left flow and its open then insert it
|
||||
if element_define.left_flow_element then
|
||||
local left_element = Gui.get_left_element(player, element_define.left_flow_element)
|
||||
if left_element.visible then
|
||||
table.insert(left_flows, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return { order, favourites, left_flows, top_flow_open }
|
||||
end)
|
||||
@@ -159,13 +159,13 @@ Gui.element(function(definition, parent, warp)
|
||||
end
|
||||
|
||||
-- Draw the element
|
||||
return definition:triggers_events(parent.add{
|
||||
return parent.add{
|
||||
type = 'sprite-button',
|
||||
sprite = sprite,
|
||||
name = definition.name,
|
||||
tooltip = {'warp-list.goto-tooltip', warp_position.x, warp_position.y},
|
||||
style = 'slot_button'
|
||||
})
|
||||
}
|
||||
end)
|
||||
:style(Styles.sprite32)
|
||||
:static_name(Gui.unique_static_name)
|
||||
@@ -186,13 +186,13 @@ end)
|
||||
-- @element warp_icon_editing
|
||||
local warp_icon_editing =
|
||||
Gui.element(function(definition, parent, warp)
|
||||
return definition:triggers_events(parent.add{
|
||||
return parent.add{
|
||||
name = definition.name,
|
||||
type = 'choose-elem-button',
|
||||
elem_type = 'signal',
|
||||
signal = {type = warp.icon.type, name = warp.icon.name},
|
||||
tooltip = {'warp-list.goto-edit'}
|
||||
})
|
||||
}
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:style(Styles.sprite32)
|
||||
@@ -204,12 +204,12 @@ Gui.element(function(definition, parent, warp)
|
||||
local last_edit_name = warp.last_edit_name
|
||||
local last_edit_time = warp.last_edit_time
|
||||
-- Draw the element
|
||||
return definition:triggers_events(parent.add{
|
||||
return parent.add{
|
||||
type = 'label',
|
||||
caption = warp.name,
|
||||
tooltip = {'warp-list.last-edit', last_edit_name, format_time(last_edit_time)},
|
||||
name = definition.name
|
||||
})
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
single_line = true,
|
||||
@@ -245,12 +245,12 @@ Gui.element{
|
||||
local warp_textfield =
|
||||
Gui.element(function(definition, parent, warp)
|
||||
-- Draw the element
|
||||
return definition:triggers_events(parent.add{
|
||||
return parent.add{
|
||||
type = 'textfield',
|
||||
text = warp.name,
|
||||
clear_and_focus_on_right_click = true,
|
||||
name = definition.name
|
||||
})
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
-- Required fields to make it squashable and strechable.
|
||||
@@ -697,10 +697,10 @@ end)
|
||||
|
||||
--- Button on the top flow used to toggle the warp list container
|
||||
-- @element toggle_warp_list
|
||||
Gui.left_toolbar_button(config.default_icon.type ..'/'..config.default_icon.name, {'warp-list.main-tooltip', config.standard_proximity_radius}, warp_list_container, function(player)
|
||||
Gui.left_toolbar_button(config.default_icon.type ..'/'..config.default_icon.name, {'warp-list.main-tooltip'}, warp_list_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/warp-list')
|
||||
end)
|
||||
:on_custom_event(Gui.events.on_visibility_changed_by_click, function(player, _,event)
|
||||
:on_event(Gui.events.on_visibility_changed_by_click, function(player, _,event)
|
||||
-- Set gui keep open state for player that clicked the button: true if visible, false if invisible
|
||||
keep_gui_open[player.name] = event.state
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user