diff --git a/files/data/CMakeLists.txt b/files/data/CMakeLists.txt index 4b36254183..e038e9f573 100644 --- a/files/data/CMakeLists.txt +++ b/files/data/CMakeLists.txt @@ -78,10 +78,10 @@ set(BUILTIN_DATA_FILES scripts/omw/console/menu.lua scripts/omw/mechanics/playercontroller.lua scripts/omw/playercontrols.lua + scripts/omw/settings/menu.lua scripts/omw/settings/player.lua scripts/omw/settings/global.lua scripts/omw/settings/common.lua - scripts/omw/settings/render.lua scripts/omw/settings/renderers.lua scripts/omw/mwui/constants.lua scripts/omw/mwui/borders.lua diff --git a/files/data/builtin.omwscripts b/files/data/builtin.omwscripts index e35e86aaaf..3e902f6639 100644 --- a/files/data/builtin.omwscripts +++ b/files/data/builtin.omwscripts @@ -1,11 +1,10 @@ # UI framework -PLAYER: scripts/omw/mwui/init.lua -MENU: scripts/omw/mwui/init.lua +MENU,PLAYER: scripts/omw/mwui/init.lua # Settings framework -GLOBAL: scripts/omw/settings/global.lua -PLAYER: scripts/omw/settings/player.lua MENU: scripts/omw/settings/menu.lua +PLAYER: scripts/omw/settings/player.lua +GLOBAL: scripts/omw/settings/global.lua # Mechanics GLOBAL: scripts/omw/activationhandlers.lua diff --git a/files/data/scripts/omw/settings/common.lua b/files/data/scripts/omw/settings/common.lua index cb83b4223b..9155c64ba7 100644 --- a/files/data/scripts/omw/settings/common.lua +++ b/files/data/scripts/omw/settings/common.lua @@ -6,7 +6,6 @@ local argumentSectionPostfix = 'Arguments' local contextSection = storage.playerSection or storage.globalSection local groupSection = contextSection(groupSectionKey) -groupSection:reset() groupSection:removeOnExit() local function validateSettingOptions(options) @@ -110,9 +109,11 @@ end return { getSection = function(global, key) + if global then error('Getting global section') end return (global and storage.globalSection or storage.playerSection)(key) end, getArgumentSection = function(global, key) + if global then error('Getting global section') end return (global and storage.globalSection or storage.playerSection)(key .. argumentSectionPostfix) end, updateRendererArgument = function(groupKey, settingKey, argument) @@ -120,6 +121,7 @@ return { argumentSection:set(settingKey, argument) end, setGlobalEvent = 'OMWSettingsGlobalSet', + registerPageEvent = 'OmWSettingsRegisterPage', groupSectionKey = groupSectionKey, onLoad = function(saved) if not saved then return end diff --git a/files/data/scripts/omw/settings/global.lua b/files/data/scripts/omw/settings/global.lua index d84794f61d..423c38680b 100644 --- a/files/data/scripts/omw/settings/global.lua +++ b/files/data/scripts/omw/settings/global.lua @@ -5,6 +5,7 @@ local common = require('scripts.omw.settings.common') return { interfaceName = 'Settings', interface = { + version = 1, registerGroup = common.registerGroup, updateRendererArgument = common.updateRendererArgument, }, @@ -17,4 +18,4 @@ return { storage.globalSection(e.groupKey):set(e.settingKey, e.value) end, }, -} \ No newline at end of file +} diff --git a/files/data/scripts/omw/settings/menu.lua b/files/data/scripts/omw/settings/menu.lua index 6fd26d40d2..ff554df768 100644 --- a/files/data/scripts/omw/settings/menu.lua +++ b/files/data/scripts/omw/settings/menu.lua @@ -1,19 +1,498 @@ -local common = require('scripts.omw.settings.common') -local render = require('scripts.omw.settings.render') +local menu = require('openmw.menu') +local ui = require('openmw.ui') +local util = require('openmw.util') +local async = require('openmw.async') +local core = require('openmw.core') +local storage = require('openmw.storage') +local I = require('openmw.interfaces') -require('scripts.omw.settings.renderers')(render.registerRenderer) +local common = require('scripts.omw.settings.common') + +local renderers = {} +local function registerRenderer(name, renderFunction) + renderers[name] = renderFunction +end +require('scripts.omw.settings.renderers')(registerRenderer) + +local interfaceL10n = core.l10n('Interface') + +local pages = {} +local groups = {} +local pageOptions = {} + +local interval = { template = I.MWUI.templates.interval } +local growingIntreval = { + template = I.MWUI.templates.interval, + external = { + grow = 1, + }, +} +local spacer = { + props = { + size = util.vector2(0, 10), + }, +} +local bigSpacer = { + props = { + size = util.vector2(0, 50), + }, +} +local stretchingLine = { + template = I.MWUI.templates.horizontalLine, + external = { + stretch = 1, + }, +} +local spacedLines = function(count) + local content = {} + table.insert(content, spacer) + table.insert(content, stretchingLine) + for i = 2, count do + table.insert(content, interval) + table.insert(content, stretchingLine) + end + table.insert(content, spacer) + return { + type = ui.TYPE.Flex, + external = { + stretch = 1, + }, + content = ui.content(content), + } +end + +local function interlaceSeparator(layouts, separator) + local result = {} + result[1] = layouts[1] + for i = 2, #layouts do + table.insert(result, separator) + table.insert(result, layouts[i]) + end + return result +end + +local function setSettingValue(global, groupKey, settingKey, value) + if global then + core.sendGlobalEvent(common.setGlobalEvent, { + groupKey = groupKey, + settingKey = settingKey, + value = value, + }) + else + storage.playerSection(groupKey):set(settingKey, value) + end +end + +local function renderSetting(group, setting, value, global) + local renderFunction = renderers[setting.renderer] + if not renderFunction then + error(('Setting %s of %s has unknown renderer %s'):format(setting.key, group.key, setting.renderer)) + end + local set = function(value) + setSettingValue(global, group.key, setting.key, value) + end + local l10n = core.l10n(group.l10n) + local titleLayout = { + type = ui.TYPE.Flex, + content = ui.content { + { + template = I.MWUI.templates.textHeader, + props = { + text = l10n(setting.name), + }, + }, + }, + } + if setting.description then + titleLayout.content:add(interval) + titleLayout.content:add { + template = I.MWUI.templates.textParagraph, + props = { + text = l10n(setting.description), + size = util.vector2(300, 0), + }, + } + end + local argument = common.getArgumentSection(global, group.key):get(setting.key) + return { + name = setting.key, + type = ui.TYPE.Flex, + props = { + horizontal = true, + arrange = ui.ALIGNMENT.Center, + }, + external = { + stretch = 1, + }, + content = ui.content { + titleLayout, + growingIntreval, + renderFunction(value, set, argument), + }, + } +end + +local groupLayoutName = function(key, global) + return ('%s%s'):format(global and 'global_' or 'player_', key) +end + +local function renderGroup(group, global) + local l10n = core.l10n(group.l10n) + + local valueSection = common.getSection(global, group.key) + local settingLayouts = {} + local sortedSettings = {} + for _, setting in pairs(group.settings) do + sortedSettings[setting.order] = setting + end + for _, setting in ipairs(sortedSettings) do + table.insert(settingLayouts, renderSetting(group, setting, valueSection:get(setting.key), global)) + end + local settingsContent = ui.content(interlaceSeparator(settingLayouts, spacedLines(1))) + + local resetButtonLayout = { + template = I.MWUI.templates.box, + events = { + mouseClick = async:callback(function() + for _, setting in pairs(group.settings) do + setSettingValue(global, group.key, setting.key, setting.default) + end + end), + }, + content = ui.content { + { + template = I.MWUI.templates.padding, + content = ui.content { + { + template = I.MWUI.templates.textNormal, + props = { + text = interfaceL10n('Reset') + }, + }, + }, + }, + }, + } + + local titleLayout = { + type = ui.TYPE.Flex, + external = { + stretch = 1, + }, + content = ui.content { + { + template = I.MWUI.templates.textHeader, + props = { + text = l10n(group.name), + textSize = 20, + }, + } + }, + } + if group.description then + titleLayout.content:add(interval) + titleLayout.content:add { + template = I.MWUI.templates.textParagraph, + props = { + text = l10n(group.description), + size = util.vector2(300, 0), + }, + } + end + + return { + name = groupLayoutName(group.key, global), + type = ui.TYPE.Flex, + external = { + stretch = 1, + }, + content = ui.content { + { + type = ui.TYPE.Flex, + props = { + horizontal = true, + arrange = ui.ALIGNMENT.Center, + }, + external = { + stretch = 1, + }, + content = ui.content { + titleLayout, + growingIntreval, + resetButtonLayout, + }, + }, + spacedLines(2), + { + name = 'settings', + type = ui.TYPE.Flex, + content = settingsContent, + external = { + stretch = 1, + }, + }, + }, + } +end + +local function pageGroupComparator(a, b) + return a.order < b.order or ( + a.order == b.order and a.key < b.key + ) +end + +local function generateSearchHints(page) + local hints = {} + local l10n = core.l10n(page.l10n) + table.insert(hints, l10n(page.name)) + if page.description then + table.insert(hints, l10n(page.description)) + end + local pageGroups = groups[page.key] + for _, pageGroup in pairs(pageGroups) do + local group = common.getSection(pageGroup.global, common.groupSectionKey):get(pageGroup.key) + local l10n = core.l10n(group.l10n) + table.insert(hints, l10n(group.name)) + if group.description then + table.insert(hints, l10n(group.description)) + end + for _, setting in pairs(group.settings) do + table.insert(hints, l10n(setting.name)) + if setting.description then + table.insert(hints, l10n(setting.description)) + end + end + end + return table.concat(hints, ' ') +end + +local function renderPage(page) + local l10n = core.l10n(page.l10n) + local sortedGroups = {} + for _, group in pairs(groups[page.key]) do + table.insert(sortedGroups, group) + end + table.sort(sortedGroups, pageGroupComparator) + local groupLayouts = {} + for _, pageGroup in ipairs(sortedGroups) do + local group = common.getSection(pageGroup.global, common.groupSectionKey):get(pageGroup.key) + table.insert(groupLayouts, renderGroup(group, pageGroup.global)) + end + local groupsLayout = { + name = 'groups', + type = ui.TYPE.Flex, + external = { + stretch = 1, + }, + content = ui.content(interlaceSeparator(groupLayouts, bigSpacer)), + } + local titleLayout = { + type = ui.TYPE.Flex, + external = { + stretch = 1, + }, + content = ui.content { + { + template = I.MWUI.templates.textHeader, + props = { + text = l10n(page.name), + textSize = 22, + }, + }, + spacedLines(3), + }, + } + if page.description then + titleLayout.content:add { + template = I.MWUI.templates.textParagraph, + props = { + text = l10n(page.description), + size = util.vector2(300, 0), + }, + } + end + local layout = { + name = page.key, + type = ui.TYPE.Flex, + props = { + position = util.vector2(10, 10), + }, + content = ui.content { + titleLayout, + bigSpacer, + groupsLayout, + bigSpacer, + }, + } + return { + name = l10n(page.name), + element = ui.create(layout), + searchHints = generateSearchHints(page), + } +end + +local function onSettingChanged(global) + return async:callback(function(groupKey, settingKey) + local group = common.getSection(global, common.groupSectionKey):get(groupKey) + if not group or not pageOptions[group.page] then return end + + local value = common.getSection(global, group.key):get(settingKey) + + local element = pageOptions[group.page].element + local groupsLayout = element.layout.content.groups + local groupLayout = groupsLayout.content[groupLayoutName(group.key, global)] + local settingsContent = groupLayout.content.settings.content + settingsContent[settingKey] = renderSetting(group, group.settings[settingKey], value, global) + element:update() + end) +end + +local function onGroupRegistered(global, key) + local group = common.getSection(global, common.groupSectionKey):get(key) + groups[group.page] = groups[group.page] or {} + local pageGroup = { + key = group.key, + global = global, + order = group.order, + } + + if not groups[group.page][pageGroup.key] then + common.getSection(global, group.key):subscribe(onSettingChanged(global)) + common.getArgumentSection(global, group.key):subscribe(async:callback(function(_, settingKey) + local group = common.getSection(global, common.groupSectionKey):get(group.key) + if not group or not pageOptions[group.page] then return end + + local value = common.getSection(global, group.key):get(settingKey) + + local element = pageOptions[group.page].element + local groupsLayout = element.layout.content.groups + local groupLayout = groupsLayout.content[groupLayoutName(group.key, global)] + local settingsContent = groupLayout.content.settings.content + settingsContent[settingKey] = renderSetting(group, group.settings[settingKey], value, global) + element:update() + end)) + end + + groups[group.page][pageGroup.key] = pageGroup + + if not pages[group.page] then return end + if pageOptions[group.page] then + pageOptions[group.page].element:destroy() + else + pageOptions[group.page] = {} + end + local renderedOptions = renderPage(pages[group.page]) + for k, v in pairs(renderedOptions) do + pageOptions[group.page][k] = v + end +end + + +local function updatePlayerGroups() + local playerGroups = storage.playerSection(common.groupSectionKey) + for groupKey in pairs(playerGroups:asTable()) do + onGroupRegistered(false, groupKey) + end + playerGroups:subscribe(async:callback(function(_, key) + if key then + onGroupRegistered(false, key) + else + for groupKey in pairs(playerGroups:asTable()) do + onGroupRegistered(false, groupKey) + end + end + end)) +end + +updatePlayerGroups() + +local function updateGlobalGroups() + local globalGroups = storage.globalSection(common.groupSectionKey) + for groupKey in pairs(globalGroups:asTable()) do + onGroupRegistered(true, groupKey) + end + globalGroups:subscribe(async:callback(function(_, key) + if key then + onGroupRegistered(true, key) + else + for groupKey in pairs(globalGroups:asTable()) do + onGroupRegistered(true, groupKey) + end + end + end)) +end + +local function resetGroups() + for pageKey, page in pairs(groups) do + for groupKey in pairs(page) do + page[groupKey] = nil + end + local renderedOptions = renderPage(pages[pageKey]) + for k, v in pairs(renderedOptions) do + pageOptions[pageKey][k] = v + end + end +end + +local function registerPage(options) + if type(options) ~= 'table' then + error('Page options must be a table') + end + if type(options.key) ~= 'string' then + error('Page must have a key') + end + if type(options.l10n) ~= 'string' then + error('Page must have a localization context') + end + if type(options.name) ~= 'string' then + error('Page must have a name') + end + if options.description ~= nil and type(options.description) ~= 'string' then + error('Page description key must be a string') + end + local page = { + key = options.key, + l10n = options.l10n, + name = options.name, + description = options.description, + } + pages[page.key] = page + groups[page.key] = groups[page.key] or {} + if pageOptions[page.key] then + pageOptions[page.key].element:destroy() + end + pageOptions[page.key] = pageOptions[page.key] or {} + local renderedOptions = renderPage(page) + for k, v in pairs(renderedOptions) do + pageOptions[page.key][k] = v + end + ui.registerSettingsPage(pageOptions[page.key]) +end return { interfaceName = 'Settings', interface = { - version = 0, - registerPage = render.registerPage, - registerRenderer = render.registerRenderer, + version = 1, + registerPage = registerPage, + registerRenderer = registerRenderer, registerGroup = common.registerGroup, updateRendererArgument = common.updateRendererArgument, }, engineHandlers = { onLoad = common.onLoad, onSave = common.onSave, + onStateChanged = function() + if menu.getState() == menu.STATE.Running then + updateGlobalGroups() + else + resetGroups() + end + updatePlayerGroups() + end, }, + eventHandlers = { + [common.registerPageEvent] = function(options) + registerPage(options) + end, + } } diff --git a/files/data/scripts/omw/settings/player.lua b/files/data/scripts/omw/settings/player.lua index 3a71f11456..3898e733e1 100644 --- a/files/data/scripts/omw/settings/player.lua +++ b/files/data/scripts/omw/settings/player.lua @@ -1,5 +1,12 @@ +local storage = require('openmw.storage') +local types = require('openmw.types') +local self = require('openmw.self') + local common = require('scripts.omw.settings.common') -local render = require('scripts.omw.settings.render') + +local function registerPage(options) + types.Player.sendMenuEvent(self, common.registerPageEvent, options) +end --- -- @type PageOptions @@ -82,7 +89,7 @@ return { -- name = 'MyModName', -- description = 'MyModDescription', -- })--- - registerPage = render.registerPage, + registerPage = registerPage, --- -- @function [parent=#Settings] registerRenderer Register a renderer, -- only avaialable in menu scripts (DEPRECATED in player scripts) @@ -105,9 +112,9 @@ return { -- }, -- } -- end) - registerRenderer = function() - print( - 'Register setting renderers in player scripts has been deprecated and moved to menu Settings interface') + registerRenderer = function(name) + print(([[Can't register setting renderer "%s". registerRenderer and moved to Menu context Settings interface]]) + :format(name)) end, --- -- @function [parent=#Settings] registerGroup Register a group to be attached to a page, diff --git a/files/data/scripts/omw/settings/render.lua b/files/data/scripts/omw/settings/render.lua deleted file mode 100644 index d4de143b02..0000000000 --- a/files/data/scripts/omw/settings/render.lua +++ /dev/null @@ -1,423 +0,0 @@ -local ui = require('openmw.ui') -local util = require('openmw.util') -local async = require('openmw.async') -local core = require('openmw.core') -local storage = require('openmw.storage') -local I = require('openmw.interfaces') - -local common = require('scripts.omw.settings.common') - -local renderers = {} -local function registerRenderer(name, renderFunction) - renderers[name] = renderFunction -end - -local interfaceL10n = core.l10n('Interface') - -local pages = {} -local groups = {} -local pageOptions = {} - -local interval = { template = I.MWUI.templates.interval } -local growingIntreval = { - template = I.MWUI.templates.interval, - external = { - grow = 1, - }, -} -local spacer = { - props = { - size = util.vector2(0, 10), - }, -} -local bigSpacer = { - props = { - size = util.vector2(0, 50), - }, -} -local stretchingLine = { - template = I.MWUI.templates.horizontalLine, - external = { - stretch = 1, - }, -} -local spacedLines = function(count) - local content = {} - table.insert(content, spacer) - table.insert(content, stretchingLine) - for i = 2, count do - table.insert(content, interval) - table.insert(content, stretchingLine) - end - table.insert(content, spacer) - return { - type = ui.TYPE.Flex, - external = { - stretch = 1, - }, - content = ui.content(content), - } -end - -local function interlaceSeparator(layouts, separator) - local result = {} - result[1] = layouts[1] - for i = 2, #layouts do - table.insert(result, separator) - table.insert(result, layouts[i]) - end - return result -end - -local function setSettingValue(global, groupKey, settingKey, value) - if global then - core.sendGlobalEvent(common.setGlobalEvent, { - groupKey = groupKey, - settingKey = settingKey, - value = value, - }) - else - storage.playerSection(groupKey):set(settingKey, value) - end -end - -local function renderSetting(group, setting, value, global) - local renderFunction = renderers[setting.renderer] - if not renderFunction then - error(('Setting %s of %s has unknown renderer %s'):format(setting.key, group.key, setting.renderer)) - end - local set = function(value) - setSettingValue(global, group.key, setting.key, value) - end - local l10n = core.l10n(group.l10n) - local titleLayout = { - type = ui.TYPE.Flex, - content = ui.content { - { - template = I.MWUI.templates.textHeader, - props = { - text = l10n(setting.name), - }, - }, - }, - } - if setting.description then - titleLayout.content:add(interval) - titleLayout.content:add { - template = I.MWUI.templates.textParagraph, - props = { - text = l10n(setting.description), - size = util.vector2(300, 0), - }, - } - end - local argument = common.getArgumentSection(global, group.key):get(setting.key) - return { - name = setting.key, - type = ui.TYPE.Flex, - props = { - horizontal = true, - arrange = ui.ALIGNMENT.Center, - }, - external = { - stretch = 1, - }, - content = ui.content { - titleLayout, - growingIntreval, - renderFunction(value, set, argument), - }, - } -end - -local groupLayoutName = function(key, global) - return ('%s%s'):format(global and 'global_' or 'player_', key) -end - -local function renderGroup(group, global) - local l10n = core.l10n(group.l10n) - - local valueSection = common.getSection(global, group.key) - local settingLayouts = {} - local sortedSettings = {} - for _, setting in pairs(group.settings) do - sortedSettings[setting.order] = setting - end - for _, setting in ipairs(sortedSettings) do - table.insert(settingLayouts, renderSetting(group, setting, valueSection:get(setting.key), global)) - end - local settingsContent = ui.content(interlaceSeparator(settingLayouts, spacedLines(1))) - - local resetButtonLayout = { - template = I.MWUI.templates.box, - events = { - mouseClick = async:callback(function() - for _, setting in pairs(group.settings) do - setSettingValue(global, group.key, setting.key, setting.default) - end - end), - }, - content = ui.content { - { - template = I.MWUI.templates.padding, - content = ui.content { - { - template = I.MWUI.templates.textNormal, - props = { - text = interfaceL10n('Reset') - }, - }, - }, - }, - }, - } - - local titleLayout = { - type = ui.TYPE.Flex, - external = { - stretch = 1, - }, - content = ui.content { - { - template = I.MWUI.templates.textHeader, - props = { - text = l10n(group.name), - textSize = 20, - }, - } - }, - } - if group.description then - titleLayout.content:add(interval) - titleLayout.content:add { - template = I.MWUI.templates.textParagraph, - props = { - text = l10n(group.description), - size = util.vector2(300, 0), - }, - } - end - - return { - name = groupLayoutName(group.key, global), - type = ui.TYPE.Flex, - external = { - stretch = 1, - }, - content = ui.content { - { - type = ui.TYPE.Flex, - props = { - horizontal = true, - arrange = ui.ALIGNMENT.Center, - }, - external = { - stretch = 1, - }, - content = ui.content { - titleLayout, - growingIntreval, - resetButtonLayout, - }, - }, - spacedLines(2), - { - name = 'settings', - type = ui.TYPE.Flex, - content = settingsContent, - external = { - stretch = 1, - }, - }, - }, - } -end - -local function pageGroupComparator(a, b) - return a.order < b.order or ( - a.order == b.order and a.key < b.key - ) -end - -local function generateSearchHints(page) - local hints = {} - local l10n = core.l10n(page.l10n) - table.insert(hints, l10n(page.name)) - if page.description then - table.insert(hints, l10n(page.description)) - end - local pageGroups = groups[page.key] - for _, pageGroup in pairs(pageGroups) do - local group = common.getSection(pageGroup.global, common.groupSectionKey):get(pageGroup.key) - local l10n = core.l10n(group.l10n) - table.insert(hints, l10n(group.name)) - if group.description then - table.insert(hints, l10n(group.description)) - end - for _, setting in pairs(group.settings) do - table.insert(hints, l10n(setting.name)) - if setting.description then - table.insert(hints, l10n(setting.description)) - end - end - end - return table.concat(hints, ' ') -end - -local function renderPage(page) - local l10n = core.l10n(page.l10n) - local sortedGroups = {} - for i, v in ipairs(groups[page.key]) do sortedGroups[i] = v end - table.sort(sortedGroups, pageGroupComparator) - local groupLayouts = {} - for _, pageGroup in ipairs(sortedGroups) do - local group = common.getSection(pageGroup.global, common.groupSectionKey):get(pageGroup.key) - table.insert(groupLayouts, renderGroup(group, pageGroup.global)) - end - local groupsLayout = { - name = 'groups', - type = ui.TYPE.Flex, - external = { - stretch = 1, - }, - content = ui.content(interlaceSeparator(groupLayouts, bigSpacer)), - } - local titleLayout = { - type = ui.TYPE.Flex, - external = { - stretch = 1, - }, - content = ui.content { - { - template = I.MWUI.templates.textHeader, - props = { - text = l10n(page.name), - textSize = 22, - }, - }, - spacedLines(3), - }, - } - if page.description then - titleLayout.content:add { - template = I.MWUI.templates.textParagraph, - props = { - text = l10n(page.description), - size = util.vector2(300, 0), - }, - } - end - local layout = { - name = page.key, - type = ui.TYPE.Flex, - props = { - position = util.vector2(10, 10), - }, - content = ui.content { - titleLayout, - bigSpacer, - groupsLayout, - bigSpacer, - }, - } - return { - name = l10n(page.name), - element = ui.create(layout), - searchHints = generateSearchHints(page), - } -end - -local function onSettingChanged(global) - return async:callback(function(groupKey, settingKey) - local group = common.getSection(global, common.groupSectionKey):get(groupKey) - if not group or not pageOptions[group.page] then return end - - local value = common.getSection(global, group.key):get(settingKey) - - local element = pageOptions[group.page].element - local groupsLayout = element.layout.content.groups - local groupLayout = groupsLayout.content[groupLayoutName(group.key, global)] - local settingsContent = groupLayout.content.settings.content - settingsContent[settingKey] = renderSetting(group, group.settings[settingKey], value, global) - element:update() - end) -end -local function onGroupRegistered(global, key) - local group = common.getSection(global, common.groupSectionKey):get(key) - groups[group.page] = groups[group.page] or {} - local pageGroup = { - key = group.key, - global = global, - order = group.order, - } - table.insert(groups[group.page], pageGroup) - common.getSection(global, group.key):subscribe(onSettingChanged(global)) - common.getArgumentSection(global, group.key):subscribe(async:callback(function(_, settingKey) - local groupKey = group.key - local group = common.getSection(global, common.groupSectionKey):get(groupKey) - if not group or not pageOptions[group.page] then return end - - local value = common.getSection(global, group.key):get(settingKey) - - local element = pageOptions[group.page].element - local groupsLayout = element.layout.content.groups - local groupLayout = groupsLayout.content[groupLayoutName(group.key, global)] - local settingsContent = groupLayout.content.settings.content - settingsContent[settingKey] = renderSetting(group, group.settings[settingKey], value, global) - element:update() - end)) - - if not pages[group.page] then return end - local options = renderPage(pages[group.page]) - if pageOptions[group.page] then - pageOptions[group.page].element:destroy() - else - pageOptions[group.page] = {} - end - for k, v in pairs(options) do - pageOptions[group.page][k] = v - end -end -local globalGroups = storage.globalSection(common.groupSectionKey) -for groupKey in pairs(globalGroups:asTable()) do - onGroupRegistered(true, groupKey) -end -globalGroups:subscribe(async:callback(function(_, key) - if key then onGroupRegistered(true, key) end -end)) -storage.playerSection(common.groupSectionKey):subscribe(async:callback(function(_, key) - if key then onGroupRegistered(false, key) end -end)) - -local function registerPage(options) - if type(options) ~= 'table' then - error('Page options must be a table') - end - if type(options.key) ~= 'string' then - error('Page must have a key') - end - if type(options.l10n) ~= 'string' then - error('Page must have a localization context') - end - if type(options.name) ~= 'string' then - error('Page must have a name') - end - if options.description ~= nil and type(options.description) ~= 'string' then - error('Page description key must be a string') - end - local page = { - key = options.key, - l10n = options.l10n, - name = options.name, - description = options.description, - } - pages[page.key] = page - groups[page.key] = groups[page.key] or {} - pageOptions[page.key] = renderPage(page) - ui.registerSettingsPage(pageOptions[page.key]) -end - -return { - registerPage = registerPage, - registerRenderer = registerRenderer, -} \ No newline at end of file