-
-
Save sebdelsol/494c897446deefe6995d8e38adc0a4b6 to your computer and use it in GitHub Desktop.
-- KOReader userpatch to show info in the file manager title bar | |
-- based on https://gist.github.com/hius07/c53bc1ed00e0490cb1a0709c5ed6e735#file-2-fm-title-info-lua | |
-- Menu added in the File browser menu (1st icon) to change all the settings and rearrange the items in the title bar | |
-- Items added: Custom text, Brightness Level, Warmth Level, Up time, Time spent awake, Time in suspend | |
-- Settings added: Auto refresh clock, Custom separator, Number of spaces around separator, Show wifi when disabled, Show path | |
-- Fix screen rotation (path was disappearing) | |
-- Arrange items reorders the items the menu | |
local BD = require("ui/bidi") | |
local Device = require("device") | |
local datetime = require("datetime") | |
local FileManager = require("apps/filemanager/filemanager") | |
local FileManagerMenu = require("apps/filemanager/filemanagermenu") | |
local FileManagerMenuOrder = require("ui/elements/filemanager_menu_order") | |
local InputDialog = require("ui/widget/inputdialog") | |
local NetworkMgr = require("ui/network/manager") | |
local SortWidget = require("ui/widget/sortwidget") | |
local SpinWidget = require("ui/widget/spinwidget") | |
local UIManager = require("ui/uimanager") | |
local time = require("ui/time") | |
local T = require("ffi/util").template | |
local _ = require("gettext") | |
local Screen = Device.screen | |
local logger = require("logger") | |
-- Title bar config | |
local separators = { | |
bar = "|", | |
bullet = "•", | |
dot = "·", | |
en_dash = "-", | |
em_dash = "—", | |
none = "", | |
} | |
local config_default = { | |
-- items | |
show = { | |
wifi = true, | |
memory = false, | |
storage = false, | |
custom_text = false, | |
clock = true, | |
battery = true, | |
frontlight = false, | |
frontlight_warmth = false, | |
up_time = false, | |
awake_time = false, | |
suspend_time = false, | |
}, | |
-- items order | |
order = { | |
"wifi", | |
"memory", | |
"storage", | |
"custom_text", | |
"clock", | |
"battery", | |
"frontlight", | |
"frontlight_warmth", | |
"up_time", | |
"awake_time", | |
"suspend_time", | |
}, | |
-- settings | |
custom_text = "KOReader", | |
separator = "dot", | |
separator_space = 1, | |
separator_custom = "*", | |
show_path = true, | |
auto_refresh_clock = true, | |
wifi_show_disabled = false, | |
bold = false, | |
} | |
local function load_and_update_config() | |
local config = G_reader_settings:readSetting("filemanager_title_bar", config_default) | |
local updated = false | |
local function compare_and_update_config(config_to_browse, config_to_compare) | |
for setting, setting_value in pairs(config_to_browse) do | |
if type(setting_value) ~= "table" then -- only the settings | |
if config_to_compare[setting] == nil then -- missing ? | |
updated = true | |
if config_to_compare == config then -- missing in config: add it | |
config[setting] = setting_value | |
else -- missing in config_default: remove it | |
config[setting] = nil | |
end | |
end | |
end | |
end | |
for item, item_value in pairs(config_to_browse.show) do | |
if config_to_compare.show[item] == nil then -- missing ? | |
updated = true | |
if config_to_compare == config then -- missing in config: add it | |
config.show[item] = item_value | |
table.insert(config.order, item) | |
else -- missing in config_default: remove it | |
config.show[item] = nil | |
for i, _item in ipairs(config.order) do | |
if _item == item then | |
table.remove(config.order, i) | |
break | |
end | |
end | |
end | |
end | |
end | |
end | |
compare_and_update_config(config, config_default) | |
compare_and_update_config(config_default, config) | |
if updated then | |
logger.info("Updated title bar config", config) | |
end | |
separators.custom = config.separator_custom | |
return config | |
end | |
local config = load_and_update_config() | |
-- Title bar update | |
local genItemText = { | |
custom_text = function() | |
return config.custom_text | |
end, | |
clock = function() | |
return datetime.secondsToHour(os.time(), G_reader_settings:isTrue("twelve_hour_clock")) | |
end, | |
wifi = function() | |
return NetworkMgr:isWifiOn() and "" or (config.wifi_show_disabled and "") | |
end, | |
frontlight_warmth = function() | |
if Device:hasNaturalLight() then | |
local prefix = "💡" | |
local powerd = Device:getPowerDevice() | |
if powerd:isFrontlightOn() then | |
local warmth = powerd:frontlightWarmth() | |
if warmth then | |
return (prefix .. "%d%%"):format(warmth) | |
end | |
else | |
return T(_("%1Off"), prefix) | |
end | |
end | |
end, | |
frontlight = function() | |
if Device:hasFrontlight() then | |
local prefix = "☼" | |
local powerd = Device:getPowerDevice() | |
if powerd:isFrontlightOn() then | |
if Device:isCervantes() or Device:isKobo() then | |
return (prefix .. "%d%%"):format(powerd:frontlightIntensity()) | |
else | |
return (prefix .. "%d"):format(powerd:frontlightIntensity()) | |
end | |
else | |
return T(_("%1Off"), prefix) | |
end | |
end | |
end, | |
battery = function() | |
if Device:hasBattery() then | |
local powerd = Device:getPowerDevice() | |
local batt_lvl = powerd:getCapacity() | |
local batt_symbol = powerd:getBatterySymbol(powerd:isCharged(), powerd:isCharging(), batt_lvl) | |
local text = BD.wrap(batt_symbol) .. BD.wrap(batt_lvl .. "%") | |
if Device:hasAuxBattery() and powerd:isAuxBatteryConnected() then | |
local aux_batt_lvl = powerd:getAuxCapacity() | |
local aux_batt_symbol = | |
powerd:getBatterySymbol(powerd:isAuxCharged(), powerd:isAuxCharging(), aux_batt_lvl) | |
text = text .. " " .. BD.wrap("+") .. BD.wrap(aux_batt_symbol) .. BD.wrap(aux_batt_lvl .. "%") | |
end | |
return text | |
end | |
end, | |
up_time = function(self) | |
local SystemStat = self:_getSystemStat() | |
if SystemStat then | |
local uptime = time.boottime_or_realtime_coarse() - SystemStat.start_monotonic_time | |
return "⏻" .. datetime.secondsToClockDuration("modern", time.to_s(uptime), true, false, true) -- ⏽ | |
end | |
end, | |
awake_time = function(self) | |
local SystemStat = self:_getSystemStat() | |
if SystemStat and (Device:canSuspend() or Device:canStandby()) then | |
local uptime = time.boottime_or_realtime_coarse() - SystemStat.start_monotonic_time | |
local suspend = Device:canSuspend() and Device.total_suspend_time or 0 | |
local standby = Device:canStandby() and Device.total_standby_time or 0 | |
local awake = uptime - suspend - standby | |
return "☀️" .. datetime.secondsToClockDuration("modern", time.to_s(awake), true, false, true) -- ☀☉⚡☀️ | |
end | |
end, | |
suspend_time = function(self) | |
local SystemStat = self:_getSystemStat() | |
if SystemStat and Device:canSuspend() then | |
local suspend = Device.total_suspend_time | |
return "⏾" .. datetime.secondsToClockDuration("modern", time.to_s(suspend), true, false, true) -- ⏾💤😴⏸️ | |
end | |
end, | |
storage = function(self) | |
local SystemStat = self:_getSystemStat() | |
if SystemStat then | |
SystemStat.kv_pairs = {} | |
SystemStat:appendStorageInfo() | |
return SystemStat.kv_pairs[3][2] -- available storage | |
end | |
end, | |
memory = function() | |
local statm = io.open("/proc/self/statm", "r") | |
if statm then | |
local _, rss = statm:read("*number", "*number") | |
statm:close() | |
return rss and ("%d"):format(math.floor(rss / 256)) | |
end | |
end, | |
} | |
-- get SystemStat | |
function FileManager:_getSystemStat() | |
if self.systemstat then | |
local userpatch = require("userpatch") | |
return userpatch.getUpValue(self.systemstat.addToMainMenu, "SystemStat") | |
end | |
end | |
-- handle close : need to remove schedule update for clock auto refresh | |
local orig_FileManager_onClose = FileManager.onClose | |
FileManager.onClose = function(self) | |
orig_FileManager_onClose(self) | |
UIManager:unschedule(self.updateTitleBarTitle) | |
end | |
-- handle screen rotation | |
local orig_FileManager_onSetRotationMode = FileManager.onSetRotationMode | |
FileManager.onSetRotationMode = function(self, mode) | |
orig_FileManager_onSetRotationMode(self, mode) | |
self:updateTitleBarTitle() | |
end | |
-- redraw the title bar | |
function FileManager:updateTitleBarTitle() | |
if self.title_bar == nil then return end | |
local titlebar_texts = {} | |
for _, item in ipairs(config.order) do | |
if config.show[item] then | |
local text = genItemText[item](self) | |
if text then | |
table.insert(titlebar_texts, text) | |
end | |
end | |
end | |
-- logger.info("FileManager:updateTitleBarTitle CALLED") | |
local font_has_changed | |
local not_bold_font = self.title_bar.info_text_face | |
if config.bold then | |
font_has_changed = self.title_bar.title_face == not_bold_font | |
else | |
font_has_changed = self.title_bar.title_face ~= not_bold_font | |
end | |
if font_has_changed then | |
if config.bold then | |
self.title_bar.title_face = nil | |
self.title_bar.bottom_v_padding = self._title_bar_bottom_v_padding_saved | |
else | |
self.title_bar.title_face = not_bold_font | |
self.title_bar.bottom_v_padding = self._title_bar_bottom_v_padding_saved + Screen:scaleBySize(5) | |
end | |
self.title_bar:clear() | |
self.title_bar:init() | |
end | |
local spaces = string.rep(" ", config.separator_space) | |
local seperator = spaces .. (separators[config.separator] or "") .. spaces | |
self.title_bar:setTitle(table.concat(titlebar_texts, seperator)) | |
self:updateTitleBarPath(config.show_path and self._title_bar_path_saved or "") | |
-- autorefresh time | |
UIManager:unschedule(self.updateTitleBarTitle) | |
if config.show.clock and config.auto_refresh_clock then | |
UIManager:scheduleIn(61 - tonumber(os.date("%S")), self.updateTitleBarTitle, self) | |
end | |
end | |
FileManager.onNetworkConnected = FileManager.updateTitleBarTitle | |
FileManager.onNetworkDisconnected = FileManager.updateTitleBarTitle | |
FileManager.onCharging = FileManager.updateTitleBarTitle | |
FileManager.onNotCharging = FileManager.updateTitleBarTitle | |
FileManager.onResume = FileManager.updateTitleBarTitle | |
FileManager.onTimeFormatChanged = FileManager.updateTitleBarTitle | |
FileManager.onFrontlightStateChanged = FileManager.updateTitleBarTitle | |
function FileManager:onPathChanged(path) | |
-- logger.info("FileManager:onPathChanged CALLED") | |
if not self._title_bar_bottom_v_padding_saved then | |
self._title_bar_bottom_v_padding_saved = self.title_bar.bottom_v_padding | |
end | |
self._title_bar_path_saved = path | |
self:updateTitleBarTitle() -- updateTitleBarPath done in updateTitleBarTitle | |
end | |
-- Title bar menu | |
function FileManagerMenu:_title_bar_input_text(touchmenu_instance, title, input, onChanged) | |
local text_dialog | |
text_dialog = InputDialog:new({ | |
title = title, | |
input = input, | |
buttons = { | |
{ | |
{ | |
text = _("Cancel"), | |
id = "close", | |
callback = function() UIManager:close(text_dialog) end, | |
}, | |
{ | |
text = _("Set"), | |
is_enter_default = true, | |
callback = function() | |
onChanged(text_dialog:getInputText()) | |
UIManager:close(text_dialog) | |
touchmenu_instance:updateItems() | |
self.ui:updateTitleBarTitle() | |
end, | |
}, | |
}, | |
}, | |
}) | |
UIManager:show(text_dialog) | |
text_dialog:onShowKeyboard() | |
end | |
-- Separators | |
local setting_menu_texts | |
function FileManagerMenu:_set_title_bar_separator_space(touchmenu_instance) | |
local spin_widget = SpinWidget:new({ | |
title_text = setting_menu_texts.separator_space(true), | |
value = config.separator_space, | |
value_min = 1, | |
value_step = 1, | |
value_max = 5, | |
callback = function(spin) | |
config.separator_space = spin.value | |
touchmenu_instance:updateItems() | |
self.ui:updateTitleBarTitle() | |
end, | |
}) | |
UIManager:show(spin_widget) | |
end | |
function FileManagerMenu:_set_title_bar_custom_separator_text(touchmenu_instance) | |
self:_title_bar_input_text(touchmenu_instance, _("Enter a custom separator"), separators.custom, function(text) | |
config.separator_custom = text | |
separators.custom = text | |
end) | |
end | |
local separator_menu_texts = { | |
dot = _("Dot"), | |
bullet = _("Bullet"), | |
en_dash = _("En dash"), | |
em_dash = _("Em dash"), | |
bar = _("Vertical bar"), | |
none = _("No separator"), | |
custom = function(concise) | |
return concise and _("Custom separator") or _("Custom separator (long-press to edit)") | |
end, | |
} | |
local separators_menu_help_text_funcs = { | |
custom = FileManagerMenu._set_title_bar_custom_separator_text, | |
} | |
local separators_order = { | |
"dot", | |
"bullet", | |
"en_dash", | |
"em_dash", | |
"bar", | |
"custom", | |
"none", | |
} | |
local function get_separator_menu_texts(separator, concise) | |
text = separator_menu_texts[separator] | |
if type(text) == "function" then | |
text = text(concise) | |
end | |
local spaces = string.rep(" ", config.separator_space) | |
local seperator = separators[config.separator] or "" | |
return T(text .. " '" .. spaces .. "%1" .. spaces .. "'", separators[separator]) | |
end | |
function FileManagerMenu:_get_title_bar_separators_submenu() | |
local items = {} | |
for _, separator in ipairs(separators_order) do | |
local help_text_func = separators_menu_help_text_funcs[separator] | |
table.insert(items, { | |
text_func = function() return get_separator_menu_texts(separator) end, | |
help_text_func = help_text_func and function(touchmenu_instance) | |
help_text_func(self, touchmenu_instance) | |
end, | |
checked_func = function() return config.separator == separator end, | |
callback = function(touchmenu_instance) | |
config.separator = separator | |
touchmenu_instance:updateItems() | |
self.ui:updateTitleBarTitle() | |
end, | |
}) | |
end | |
items[#items].separator = true | |
table.insert(items, { | |
text_func = setting_menu_texts.separator_space, | |
keep_menu_open = true, | |
callback = function(touchmenu_instance) | |
self:_set_title_bar_separator_space(touchmenu_instance) | |
end, | |
}) | |
return items | |
end | |
-- items | |
function FileManagerMenu:_set_title_bar_custom_text(touchmenu_instance) | |
self:_title_bar_input_text(touchmenu_instance, _("Enter a custom text"), config.custom_text or "", function(text) | |
config.custom_text = text | |
end) | |
end | |
local item_menu_texts = { | |
wifi = _("Wifi"), | |
memory = _("Memory"), | |
storage = _("Storage"), | |
clock = _("Clock"), | |
battery = _("Battery"), | |
frontlight = _("Brightness level"), | |
frontlight_warmth = _("Warmth level"), | |
up_time = _("Up time"), | |
awake_time = _("Time spent awake"), | |
suspend_time = _("Time in suspend"), | |
custom_text = function(concise) | |
return concise and _("Custom text") or T(_("Custom text (long-press to edit): '%1'"), config.custom_text) | |
end, | |
} | |
local item_menu_help_texts = { | |
memory = _("RAM used, MiB"), | |
storage = _("Free storage, requires SystemStat plugin"), | |
custom_text = FileManagerMenu._set_title_bar_custom_text, | |
} | |
function FileManagerMenu:_title_bar_arrange_items(item_table_rebuild) | |
return { | |
text = _("Arrange title bar items"), | |
keep_menu_open = true, | |
enabled_func = function() | |
local show_count = 0 -- count shown items | |
for _, item in ipairs(config.order) do | |
show_count = show_count + (config.show[item] and 1 or 0) | |
if show_count >= 1 then | |
return true | |
end | |
end | |
return false | |
end, | |
callback = function(touchmenu_instance) | |
local item_table = {} | |
for _, item in ipairs(config.order) do | |
local text = item_menu_texts[item] | |
table.insert(item_table, { | |
text = type(text) == "function" and text(true) or text, | |
orig_item = item, | |
dim = not config.show[item], | |
}) | |
end | |
local sort_item | |
sort_item = SortWidget:new({ | |
title = _("Arrange title bar items"), | |
item_table = item_table, | |
callback = function() | |
for i, item in ipairs(item_table) do | |
config.order[i] = item.orig_item | |
end | |
if item_table_rebuild then -- rebuild the item_table, so the items in the menu are reordered | |
touchmenu_instance.item_table = item_table_rebuild() | |
touchmenu_instance:updateItems() | |
end | |
self.ui:updateTitleBarTitle() | |
UIManager:setDirty(nil, "ui") | |
end, | |
}) | |
UIManager:show(sort_item) | |
end, | |
} | |
end | |
function FileManagerMenu:_get_title_bar_item(item) | |
local text = item_menu_texts[item] | |
local help_text = item_menu_help_texts[item] | |
return { | |
text = type(text) == "string" and text, | |
text_func = type(text) == "function" and function() return text() end, | |
help_text = type(help_text) == "string" and help_text, | |
help_text_func = type(help_text) == "function" and function(touchmenu_instance) | |
help_text(self, touchmenu_instance) | |
end, | |
checked_func = function() return config.show[item] end, | |
callback = function(touchmenu_instance) | |
config.show[item] = not config.show[item] | |
touchmenu_instance:updateItems() | |
self.ui:updateTitleBarTitle() | |
end, | |
} | |
end | |
-- settings | |
setting_menu_texts = { -- already delcared above | |
auto_refresh_clock = _("Auto refresh clock"), | |
show_path = _("Show file browser path"), | |
wifi_show_disabled = _("Show wifi status even when disabled"), | |
bold = _("Bold font"), | |
separator = function() | |
return T(_("Item separator: %1"), get_separator_menu_texts(config.separator, true)) | |
end, | |
custom_text = function() | |
return T(_("Custom text: '%1'"), config.custom_text) | |
end, | |
separator_space = function(concise) | |
return concise and _("Number of spaces around separator") | |
or T(_("Number of spaces around separator: %1"), config.separator_space) | |
end, | |
} | |
local setting_menu_callback = { | |
custom_text = FileManagerMenu._set_title_bar_custom_text, | |
separator_space = FileManagerMenu._set_title_bar_separator_space, | |
} | |
local setting_submenu_items = { | |
separator = FileManagerMenu._get_title_bar_separators_submenu, | |
} | |
local setting_menu_enable_func = { | |
auto_refresh_clock = function() return config.show.clock end, | |
wifi_show_disabled = function() return config.show.wifi end, | |
custom_text = function() return config.show.custom_text end, | |
} | |
local setting_menu_order = { | |
"bold", | |
"wifi_show_disabled", | |
"auto_refresh_clock", | |
"show_path", | |
"custom_text", | |
"separator", | |
} | |
function FileManagerMenu:_get_title_bar_setting(setting) | |
local text = setting_menu_texts[setting] | |
local submenu = setting_submenu_items[setting] | |
local enabled_func = setting_menu_enable_func[setting] | |
local callback_func = setting_menu_callback[setting] | |
return { | |
text = type(text) == "string" and text, | |
text_func = type(text) == "function" and function() return text() end, | |
enabled_func = enabled_func and function() return enabled_func() end, | |
checked_func = not (submenu or callback_func) and function() return config[setting] end, | |
callback = not submenu and function(touchmenu_instance) | |
if callback_func then | |
callback_func(self, touchmenu_instance) | |
else | |
config[setting] = not config[setting] | |
touchmenu_instance:updateItems() | |
self.ui:updateTitleBarTitle() | |
end | |
end, | |
keep_menu_open = callback_func ~= nil, | |
sub_item_table = submenu and submenu(self), | |
} | |
end | |
-- Patch title bar menu | |
local orig_FileManagerMenu_setUpdateItemTable = FileManagerMenu.setUpdateItemTable | |
FileManagerMenu.setUpdateItemTable = function(self) | |
orig_FileManagerMenu_setUpdateItemTable(self) | |
local buttons = FileManagerMenuOrder["KOMenu:menu_buttons"] | |
for i, button in ipairs(buttons) do | |
if button == "filemanager_settings" then | |
logger.info("Add title bar options in the file manager settings menu") | |
local setting_table = {} | |
for _, setting in ipairs(setting_menu_order) do | |
table.insert(setting_table, self:_get_title_bar_setting(setting)) | |
end | |
local sub_item_table_func | |
sub_item_table_func = function() | |
local _table = { | |
{ | |
text = _("Configure title bar"), | |
sub_item_table = setting_table, | |
}, | |
self:_title_bar_arrange_items(sub_item_table_func), | |
} | |
_table[#_table].separator = true | |
for _, item in ipairs(config.order) do | |
table.insert(_table, self:_get_title_bar_item(item)) | |
end | |
return _table | |
end | |
table.insert(self.tab_item_table[i], 1, { | |
text = _("Title bar"), | |
separator = true, | |
sub_item_table_func = sub_item_table_func, | |
}) | |
break | |
end | |
end | |
end |
IMO at this point it should be it should be the default feature of KOReader. Not sure what KOR devs think of it...
100% agree, though I think the ability to reduce the font size would be nice, especially since right now it is around the same size as Home (or the current path).
Issue 13557 originally started from here koreader/koreader#11147 (comment) See developers comment there.
Thanks, everyone. I'll try to submit a Pull Request. I'm still new to this codebase and a bit rusty with my Lua, so it might take me a little while.
EDIT:
Issue koreader/koreader#13557 originally started from here koreader/koreader#11147 (comment) See developers comment there.
I like a good uphill battle :)
100% agree, though I think the ability to reduce the font size would be nice, especially since right now it is around the same size as Home (or the current path).
I'm looking into it, but it's really not easy. I'd also like to control the font size of the title bar.
Small correction "Show file manager browser path".
In the UI they prefer to call it file browser everywhere. koreader/koreader#13305 (comment) e.g. go to "Main > Help > Menu search" and check search result with keyword manager vs browser.
you're right. It's done. Tx.
IMO at this point it should be the default feature of KOReader.
Not sure what KOR devs think of it...