2
0
mirror of https://github.com/koreader/koreader synced 2024-11-11 19:11:14 +00:00
koreader/frontend/apps/filemanager/filemanagersetdefaults.lua
NiLuJe eef938996f MultiDialog: Fix an extremely nasty API misuse
Stuff was poking at the class object, not the instance's :s.

Fix #9599
2022-10-06 19:28:26 +02:00

318 lines
12 KiB
Lua

local CenterContainer = require("ui/widget/container/centercontainer")
local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu")
local MultiInputDialog = require("ui/widget/multiinputdialog")
local Size = require("ui/size")
local UIManager = require("ui/uimanager")
local logger = require("logger")
local ffiUtil = require("ffi/util")
local util = require("util")
local _ = require("gettext")
local Screen = require("device").screen
local SetDefaultsWidget = CenterContainer:extend{
state = nil,
menu_entries = nil,
defaults_menu = nil,
settings_changed = false,
}
function SetDefaultsWidget:init()
-- This would usually be passed to the constructor, as CenterContainer's paintTo does *NOT* set/update self.dimen...
self.dimen = Screen:getSize()
-- Don't refresh the FM behind us. May leave stray bits of overflowed InputDialog behind in the popout border space.
self.covers_fullscreen = true
-- Then deal with our child widgets and our internal variables
self.screen_width = Screen:getWidth()
self.screen_height = Screen:getHeight()
self.dialog_width = math.floor(math.min(self.screen_width, self.screen_height) * 0.95)
-- Keep track of what's an actual default, and what's been customized without actually touching the real data yet...
self.state = {}
local ro_defaults, rw_defaults = G_defaults:getDataTables()
for k, v in pairs(ro_defaults) do
self.state[k] = {
idx = 1,
value = v,
custom = false,
dirty = false,
default_value = v,
}
end
for k, v in pairs(rw_defaults) do
self.state[k].value = v
self.state[k].custom = true
end
-- Prepare our menu entires
self.menu_entries = {}
local set_dialog
local cancel_button = {
text = _("Cancel"),
id = "close",
enabled = true,
callback = function()
UIManager:close(set_dialog)
end,
}
local i = 0
for k, t in ffiUtil.orderedPairs(self.state) do
local v = t.value
i = i + 1
self.state[k].idx = i
local value_type = type(v)
if value_type == "boolean" then
local editBoolean = function()
set_dialog = InputDialog:new{
title = k,
input = tostring(self.state[k].value),
buttons = {
{
cancel_button,
{
text = _("Default"),
enabled = self.state[k].value ~= self.state[k].default_value,
callback = function()
UIManager:close(set_dialog)
self:update_menu_entry(k, self.state[k].default_value, value_type)
end
},
{
text = "true",
enabled = true,
callback = function()
UIManager:close(set_dialog)
self:update_menu_entry(k, true, value_type)
end
},
{
text = "false",
enabled = true,
callback = function()
UIManager:close(set_dialog)
self:update_menu_entry(k, false, value_type)
end
},
},
},
input_type = value_type,
width = self.dialog_width,
}
UIManager:show(set_dialog)
set_dialog:onShowKeyboard()
end
table.insert(self.menu_entries, {
text = self:gen_menu_entry(k, self.state[k].value, value_type),
bold = self.state[k].custom,
callback = editBoolean
})
elseif value_type == "table" then
local editTable = function()
local fields = {}
for key, value in ffiUtil.orderedPairs(self.state[k].value) do
table.insert(fields, {
text = tostring(key) .. " = " .. tostring(value),
input_type = type(value),
hint = "",
padding = Screen:scaleBySize(2),
margin = Screen:scaleBySize(2),
})
end
set_dialog = MultiInputDialog:new{
title = k,
fields = fields,
buttons = {
{
cancel_button,
{
text = _("Default"),
enabled = not util.tableEquals(self.state[k].value, self.state[k].default_value),
callback = function()
UIManager:close(set_dialog)
self:update_menu_entry(k, self.state[k].default_value, value_type)
end
},
{
text = _("OK"),
enabled = true,
is_enter_default = true,
callback = function()
UIManager:close(set_dialog)
local new_table = {}
for _, field in ipairs(set_dialog:getFields()) do
local key, value = field:match("^[^= ]+"), field:match("[^= ]+$")
new_table[tonumber(key) or key] = tonumber(value) or value
end
self:update_menu_entry(k, new_table, value_type)
end,
},
},
},
width = self.dialog_width,
}
UIManager:show(set_dialog)
set_dialog:onShowKeyboard()
end
table.insert(self.menu_entries, {
text = self:gen_menu_entry(k, self.state[k].value, value_type),
bold = self.state[k].custom,
callback = editTable
})
else
local editNumStr = function()
set_dialog = InputDialog:new{
title = k,
input = tostring(self.state[k].value),
buttons = {
{
cancel_button,
{
text = _("Default"),
enabled = self.state[k].value ~= self.state[k].default_value,
callback = function()
UIManager:close(set_dialog)
self:update_menu_entry(k, self.state[k].default_value, value_type)
end
},
{
text = _("OK"),
is_enter_default = true,
enabled = true,
callback = function()
UIManager:close(set_dialog)
local new_value = set_dialog:getInputValue()
self:update_menu_entry(k, new_value, value_type)
end,
},
},
},
input_type = value_type,
width = self.dialog_width,
}
UIManager:show(set_dialog)
set_dialog:onShowKeyboard()
end
table.insert(self.menu_entries, {
text = self:gen_menu_entry(k, self.state[k].value, value_type),
bold = self.state[k].custom,
callback = editNumStr
})
end
end
-- Now that we have stuff to display, instantiate our Menu
self.defaults_menu = Menu:new{
width = self.screen_width - (Size.margin.fullscreen_popout * 2),
height = self.screen_height - (Size.margin.fullscreen_popout * 2),
show_parent = self,
item_table = self.menu_entries,
title = _("Defaults"),
}
-- Prevent menu from closing when editing a value
function self.defaults_menu:onMenuSelect(item)
item.callback()
end
self.defaults_menu.close_callback = function()
logger.dbg("Closing defaults menu")
self:saveBeforeExit()
UIManager:close(self)
end
table.insert(self, self.defaults_menu)
end
function SetDefaultsWidget:gen_menu_entry(k, v, v_type)
local ret = k .. " = "
if v_type == "boolean" then
return ret .. tostring(v)
elseif v_type == "table" then
return ret .. "{...}"
elseif tonumber(v) then
return ret .. tostring(tonumber(v))
else
return ret .. "\"" .. tostring(v) .. "\""
end
end
function SetDefaultsWidget:update_menu_entry(k, v, v_type)
local idx = self.state[k].idx
self.state[k].value = v
self.state[k].dirty = true
self.settings_changed = true
self.menu_entries[idx].text = self:gen_menu_entry(k, v, v_type)
if util.tableEquals(v, self.state[k].default_value) then
self.menu_entries[idx].bold = false
else
self.menu_entries[idx].bold = true
end
self.defaults_menu:switchItemTable(nil, self.menu_entries, idx)
end
function SetDefaultsWidget:saveSettings()
-- Update dirty keys for real
for k, t in pairs(self.state) do
if t.dirty then
G_defaults:saveSetting(k, t.value)
end
end
-- And flush to disk
G_defaults:flush()
UIManager:show(InfoMessage:new{
text = _("Default settings saved."),
})
end
function SetDefaultsWidget:saveBeforeExit(callback)
local save_text = _("Save and quit")
if Device:canRestart() then
save_text = _("Save and restart")
end
if self.settings_changed then
UIManager:show(ConfirmBox:new{
dismissable = false,
text = _("KOReader needs to be restarted to apply the new default settings."),
ok_text = save_text,
ok_callback = function()
self:saveSettings()
if Device:canRestart() then
UIManager:restartKOReader()
else
UIManager:quit()
end
end,
cancel_text = _("Discard changes"),
cancel_callback = function()
logger.info("discard defaults")
end,
})
end
end
local SetDefaults = {}
function SetDefaults:ConfirmEdit()
if not SetDefaults.EditConfirmed then
UIManager:show(ConfirmBox:new{
text = _("Some changes will not work until the next restart. Be careful; the wrong settings might crash KOReader!\nAre you sure you want to continue?"),
ok_callback = function()
SetDefaults.EditConfirmed = true
UIManager:show(SetDefaultsWidget:new{})
end,
})
else
UIManager:show(SetDefaultsWidget:new{})
end
end
return SetDefaults