mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-09 21:42:13 +00:00
423 lines
13 KiB
Lua
423 lines
13 KiB
Lua
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,
|
|
} |