Last active
June 17, 2025 02:33
-
-
Save IntrovertedMage/6ea38091292310241ba436f930ee0cb4 to your computer and use it in GitHub Desktop.
An improved version of https://gist.github.com/IntrovertedMage/d759ff214f799cfb5e1f8c85daab6cae (2-change-status-bar-color.lua) based on @sebdelsol user patch https://gist.github.com/sebdelsol/eba2e492473ac1f9e0ecb003d403b7de
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- based on https://gist.github.com/IntrovertedMage/d759ff214f799cfb5e1f8c85daab6cae and https://gist.github.com/sebdelsol/eba2e492473ac1f9e0ecb003d403b7de | |
-- Menu added in the Reader menu: | |
-- settings > Status bar > Progress bar > Thickness, height & colors > Read color | |
-- settings > Status bar > Progress bar > Thickness, height & colors > Unread color | |
local ReaderFooter = require("apps/reader/modules/readerfooter") | |
local ProgressWidget = require("ui/widget/progresswidget") | |
local BD = require("ui/bidi") | |
local Blitbuffer = require("ffi/blitbuffer") | |
local Geom = require("ui/geometry") | |
local Math = require("optmath") | |
local Screen = require("device").screen | |
local _ = require("gettext") | |
local UIManager = require("ui/uimanager") | |
local T = require("ffi/util").template | |
-- Somewhat empirically chosen threshold to switch between the two designs ;o) | |
local INITIAL_MARKER_HEIGHT_THRESHOLD = Screen:scaleBySize(12) | |
-- Settings | |
local Settings = {} | |
local function colorAttrib(read) | |
return read and "fillcolor" or "bgcolor" | |
end | |
local function getStyle(thin) | |
return thin and "progress_style_thin_colors" or "progress_style_thick_colors" | |
end | |
function Settings:init(footer) | |
local function defaultColor(thin) | |
ProgressWidget:updateStyle(not thin, nil, false) -- no object needed, since height is nil, do no set colors | |
local read, unread = colorAttrib(true), colorAttrib(false) | |
return { | |
[read] = "#808080", | |
[unread] = "#ffffff", | |
} | |
end | |
self.footer = footer | |
self.default = { | |
[getStyle(true)] = defaultColor(true), | |
[getStyle(false)] = defaultColor(false), | |
} | |
end | |
function Settings:getDefault(thin, color_attrib) | |
local default = self.default[getStyle(thin)] | |
return "#808080" | |
-- return self.default[getStyle(thin)][color_attrib] | |
end | |
function Settings:get(thin, color_attrib) | |
local settings = self.footer.settings and self.footer.settings[getStyle(thin)] | |
local color = settings and settings[color_attrib] | |
return color or self:getDefault(thin, color_attrib) | |
end | |
function Settings:set(thin, color_attrib, color) | |
local style = getStyle(thin) | |
local settings = self.footer.settings[style] or {} | |
settings[color_attrib] = color | |
logger.dbg(settings or "hello") | |
self.footer.settings[style] = settings | |
end | |
local original_init = ReaderFooter.init | |
function ReaderFooter:init() | |
Settings:init(self) | |
original_init(self) | |
self.progress_bar:_setColors(self.settings.progress_style_thin) | |
end | |
local original_loadPreset = ReaderFooter.loadPreset | |
function ReaderFooter:loadPreset(preset) | |
original_loadPreset(self,preset) | |
self.progress_bar:_setColors(self.settings.progress_style_thin) | |
end | |
function ProgressWidget:_setColors(thin) | |
local read, unread = colorAttrib(true), colorAttrib(false) | |
self[read] = Blitbuffer.colorFromString(Settings:get(thin, read)) | |
self[unread] = Blitbuffer.colorFromString(Settings:get(thin, unread)) | |
end | |
local orig_ProgressWidget_updateStyle = ProgressWidget.updateStyle | |
function ProgressWidget:updateStyle(thick, height, do_setcolors) | |
do_setcolors = do_setcolors or do_setcolors == nil -- default: do_setcolors = trues | |
orig_ProgressWidget_updateStyle(self, thick, height) | |
if do_setcolors then self:_setColors(not thick) end | |
end | |
local function getMenuItem(menu, ...) -- path | |
local function findItem(sub_items, texts) | |
local find = {} | |
local texts = type(texts) == "table" and texts or { texts } | |
-- stylua: ignore | |
for _, text in ipairs(texts) do find[text] = true end | |
for _, item in ipairs(sub_items) do | |
local text = item.text or (item.text_func and item.text_func()) | |
if text and find[text] then | |
return item | |
end | |
end | |
end | |
local sub_items, item | |
for _, texts in ipairs({ ... }) do -- walk path | |
sub_items = (item or menu).sub_item_table | |
if not sub_items then | |
return | |
end | |
item = findItem(sub_items, texts) | |
if not item then | |
return | |
end | |
end | |
return item | |
end | |
function ReaderFooter:_statusBarColorMenu(read) | |
InputDialog = require("ui/widget/inputdialog") | |
local color_attrib = colorAttrib(read) | |
return { | |
text_func = function () | |
return T(read and "Read color: %1" or "Unread color: %1", Settings:get(self.settings.progress_style_thin, color_attrib)) | |
end, | |
keep_menu_open = true, | |
enabled_func = function() | |
return not self.settings.disable_progress_bar | |
end, | |
callback = function(touchmenu_instance) | |
local input_dialog | |
input_dialog = InputDialog:new({ | |
title = "Enter color hex code for " .. (read and "read color" or "unread color"), | |
input = Settings:get(self.settings.progress_style_thin, color_attrib), | |
input_hint = "#FFFFF", | |
buttons = { | |
{ | |
{ | |
text = "Cancel", | |
callback = function() | |
UIManager:close(input_dialog) | |
end, | |
}, | |
{ | |
text = "Save", | |
callback = function() | |
local text = input_dialog:getInputText() | |
if text ~= "" then | |
if not text:match("^#%x%x%x%x?%x?%x?$") then | |
return | |
end | |
local color = Blitbuffer.colorFromString(text) | |
if not color then | |
return | |
end | |
Settings:set(self.settings.progress_style_thin, color_attrib, text) | |
self.progress_bar[color_attrib] = color | |
touchmenu_instance:updateItems() | |
self:refreshFooter(true) | |
UIManager:close(input_dialog) | |
end | |
end, | |
}, | |
}, | |
}, | |
}) | |
UIManager:show(input_dialog) | |
input_dialog:onShowKeyboard() | |
end, | |
} | |
end | |
local orig_ReaderFooter_addToMainMenu = ReaderFooter.addToMainMenu | |
function ReaderFooter:addToMainMenu(menu_items) | |
orig_ReaderFooter_addToMainMenu(self, menu_items) | |
local item = getMenuItem( | |
menu_items.status_bar, | |
_("Progress bar"), | |
{ _("Thickness and height: thin"), _("Thickness and height: thick") } | |
) | |
if item then | |
item.text_func = function() | |
return self.settings.progress_style_thin and _("Thickness, height & colors: thin") | |
or _("Thickness, height & colors: thick") | |
end | |
table.insert(item.sub_item_table, self:_statusBarColorMenu(true)) | |
table.insert(item.sub_item_table, self:_statusBarColorMenu(false)) | |
end | |
end | |
function ProgressWidget:paintTo(bb, x, y) | |
local my_size = self:getSize() | |
if not self.dimen then | |
self.dimen = Geom:new({ | |
x = x, | |
y = y, | |
w = my_size.w, | |
h = my_size.h, | |
}) | |
else | |
self.dimen.x = x | |
self.dimen.y = y | |
end | |
if self.dimen.w == 0 or self.dimen.h == 0 then | |
return | |
end | |
local _mirroredUI = BD.mirroredUILayout() | |
-- We'll draw every bar element in order, bottom to top. | |
local fill_width = my_size.w - 2 * (self.margin_h + self.bordersize) | |
local fill_y = y + self.margin_v + self.bordersize | |
local fill_height = my_size.h - 2 * (self.margin_v + self.bordersize) | |
if self.radius == 0 then | |
-- If we don't have rounded borders, we can start with a simple border colored rectangle. | |
bb:paintRect(x, y, my_size.w, my_size.h, self.bordercolor) | |
-- And a full background bar inside (i.e., on top) of that. | |
bb:paintRectRGB32( | |
x + self.margin_h + self.bordersize, | |
fill_y, | |
math.ceil(fill_width), | |
math.ceil(fill_height), | |
self.bgcolor | |
) | |
else | |
-- Otherwise, we have to start with the background. | |
-- Normally rounded but no rounded color support currently | |
bb:paintRectRGB32(x, y, my_size.w, my_size.h, self.bgcolor) | |
-- Then the border around that. | |
bb:paintBorder( | |
math.floor(x), | |
math.floor(y), | |
my_size.w, | |
my_size.h, | |
self.bordersize, | |
self.bordercolor, | |
self.radius | |
) | |
end | |
-- Then we can just paint the fill rectangle(s) and tick(s) on top of that. | |
-- First the fill bar(s)... | |
-- Fill bar for alternate pages (e.g. non-linear flows). | |
if self.alt and self.alt[1] ~= nil then | |
for i = 1, #self.alt do | |
local tick_x = fill_width * ((self.alt[i][1] - 1) / self.last) | |
local width = fill_width * (self.alt[i][2] / self.last) | |
if _mirroredUI then | |
tick_x = fill_width - tick_x - width | |
end | |
tick_x = math.floor(tick_x) | |
width = math.ceil(width) | |
bb:paintRectRGB32( | |
x + self.margin_h + self.bordersize + tick_x, | |
fill_y, | |
width, | |
math.ceil(fill_height), | |
self.altcolor | |
) | |
end | |
end | |
-- Main fill bar for the specified percentage. | |
if self.percentage >= 0 and self.percentage <= 1 then | |
local fill_x = x + self.margin_h + self.bordersize | |
if self.fill_from_right or (_mirroredUI and not self.fill_from_right) then | |
fill_x = fill_x + (fill_width * (1 - self.percentage)) | |
fill_x = math.floor(fill_x) | |
end | |
bb:paintRectRGB32( | |
fill_x, | |
fill_y, | |
math.ceil(fill_width * self.percentage), | |
math.ceil(fill_height), | |
self.fillcolor | |
) | |
-- Overlay the initial position marker on top of that | |
if self.initial_pos_marker and self.initial_percentage >= 0 then | |
if self.height <= INITIAL_MARKER_HEIGHT_THRESHOLD then | |
self.initial_pos_icon:paintTo( | |
bb, | |
Math.round(fill_x + math.ceil(fill_width * self.initial_percentage) - self.height / 4), | |
y - Math.round(self.height / 6) | |
) | |
else | |
self.initial_pos_icon:paintTo( | |
bb, | |
Math.round(fill_x + math.ceil(fill_width * self.initial_percentage) - self.height / 2), | |
y | |
) | |
end | |
end | |
end | |
-- ...then the tick(s). | |
if self.ticks and self.last and self.last > 0 then | |
for i, tick in ipairs(self.ticks) do | |
local tick_x = fill_width * (tick / self.last) | |
if _mirroredUI then | |
tick_x = fill_width - tick_x | |
end | |
tick_x = math.floor(tick_x) | |
bb:paintRect( | |
x + self.margin_h + self.bordersize + tick_x, | |
fill_y, | |
self.tick_width, | |
math.ceil(fill_height), | |
self.bordercolor | |
) | |
end | |
end | |
end | |
-- Settings | |
local Settings = {} | |
local function colorAttrib(read) | |
return read and "fillcolor" or "bgcolor" | |
end | |
local function getStyle(thin) | |
return thin and "progress_style_thin_colors" or "progress_style_thick_colors" | |
end | |
function Settings:init(footer) | |
local function defaultColor(thin) | |
local read, unread = colorAttrib(true), colorAttrib(false) | |
-- ProgressWidget:updateStyle(not thin, nil, false) -- no object needed, since height is nil, do no set colors | |
return { | |
[read] = "#808080", | |
[unread] = "#ffffff", | |
} | |
end | |
self.footer = footer | |
self.default = { | |
[getStyle(true)] = defaultColor(true), | |
[getStyle(false)] = defaultColor(false), | |
} | |
end | |
function Settings:getDefault(thin, color_attrib) | |
local default = self.default[getStyle(thin)] | |
return self.default[getStyle(thin)][color_attrib] | |
end | |
function Settings:get(thin, color_attrib) | |
local settings = self.footer.settings and self.footer.settings[getStyle(thin)] | |
local color = settings and settings[color_attrib] | |
return color or self:getDefault(thin, color_attrib) | |
end | |
function Settings:set(thin, color_attrib, color) | |
local style = getStyle(thin) | |
local settings = self.footer.settings[style] or {} | |
settings[color_attrib] = color | |
self.footer.settings[style] = settings | |
end | |
local original_init = ReaderFooter.init | |
function ReaderFooter:init() | |
Settings:init(self) | |
original_init(self) | |
self.progress_bar:_setColors(self.settings.progress_style_thin) | |
end | |
local original_loadPreset = ReaderFooter.loadPreset | |
function ReaderFooter:loadPreset(preset) | |
original_loadPreset(self,preset) | |
self.progress_bar:_setColors(self.settings.progress_style_thin) | |
end | |
function ProgressWidget:_setColors(thin) | |
local read, unread = colorAttrib(true), colorAttrib(false) | |
self[read] = Blitbuffer.colorFromString(Settings:get(thin, read)) | |
self[unread] = Blitbuffer.colorFromString(Settings:get(thin, unread)) | |
end | |
local orig_ProgressWidget_updateStyle = ProgressWidget.updateStyle | |
function ProgressWidget:updateStyle(thick, height, do_setcolors) | |
do_setcolors = do_setcolors or do_setcolors == nil -- default: do_setcolors = trues | |
orig_ProgressWidget_updateStyle(self, thick, height) | |
if do_setcolors and Settings then self:_setColors(not thick) end | |
end | |
local function getMenuItem(menu, ...) -- path | |
local function findItem(sub_items, texts) | |
local find = {} | |
local texts = type(texts) == "table" and texts or { texts } | |
-- stylua: ignore | |
for _, text in ipairs(texts) do find[text] = true end | |
for _, item in ipairs(sub_items) do | |
local text = item.text or (item.text_func and item.text_func()) | |
if text and find[text] then | |
return item | |
end | |
end | |
end | |
local sub_items, item | |
for _, texts in ipairs({ ... }) do -- walk path | |
sub_items = (item or menu).sub_item_table | |
if not sub_items then | |
return | |
end | |
item = findItem(sub_items, texts) | |
if not item then | |
return | |
end | |
end | |
return item | |
end | |
function ReaderFooter:_statusBarColorMenu(read) | |
InputDialog = require("ui/widget/inputdialog") | |
local color_attrib = colorAttrib(read) | |
return { | |
text_func = function () | |
return T(read and "Read color: %1" or "Unread color: %1", Settings:get(self.settings.progress_style_thin, color_attrib)) | |
end, | |
keep_menu_open = true, | |
enabled_func = function() | |
return not self.settings.disable_progress_bar | |
end, | |
callback = function(touchmenu_instance) | |
local input_dialog | |
input_dialog = InputDialog:new({ | |
title = "Enter color hex code for " .. (read and "read color" or "unread color"), | |
input = Settings:get(self.settings.progress_style_thin, color_attrib), | |
input_hint = "#FFFFF", | |
buttons = { | |
{ | |
{ | |
text = "Cancel", | |
callback = function() | |
UIManager:close(input_dialog) | |
end, | |
}, | |
{ | |
text = "Save", | |
callback = function() | |
local text = input_dialog:getInputText() | |
if text ~= "" then | |
if not text:match("^#%x%x%x%x?%x?%x?$") then | |
return | |
end | |
local color = Blitbuffer.colorFromString(text) | |
if not color then | |
return | |
end | |
Settings:set(self.settings.progress_style_thin, color_attrib, text) | |
self.progress_bar[color_attrib] = color | |
touchmenu_instance:updateItems() | |
self:refreshFooter(true) | |
UIManager:close(input_dialog) | |
end | |
end, | |
}, | |
}, | |
}, | |
}) | |
UIManager:show(input_dialog) | |
input_dialog:onShowKeyboard() | |
end, | |
} | |
end | |
local orig_ReaderFooter_addToMainMenu = ReaderFooter.addToMainMenu | |
function ReaderFooter:addToMainMenu(menu_items) | |
orig_ReaderFooter_addToMainMenu(self, menu_items) | |
local item = getMenuItem( | |
menu_items.status_bar, | |
_("Progress bar"), | |
{ _("Thickness and height: thin"), _("Thickness and height: thick") } | |
) | |
if item then | |
item.text_func = function() | |
return self.settings.progress_style_thin and _("Thickness, height & colors: thin") | |
or _("Thickness, height & colors: thick") | |
end | |
table.insert(item.sub_item_table, self:_statusBarColorMenu(true)) | |
table.insert(item.sub_item_table, self:_statusBarColorMenu(false)) | |
end | |
end | |
function ProgressWidget:paintTo(bb, x, y) | |
local my_size = self:getSize() | |
if not self.dimen then | |
self.dimen = Geom:new({ | |
x = x, | |
y = y, | |
w = my_size.w, | |
h = my_size.h, | |
}) | |
else | |
self.dimen.x = x | |
self.dimen.y = y | |
end | |
if self.dimen.w == 0 or self.dimen.h == 0 then | |
return | |
end | |
local _mirroredUI = BD.mirroredUILayout() | |
-- We'll draw every bar element in order, bottom to top. | |
local fill_width = my_size.w - 2 * (self.margin_h + self.bordersize) | |
local fill_y = y + self.margin_v + self.bordersize | |
local fill_height = my_size.h - 2 * (self.margin_v + self.bordersize) | |
if self.radius == 0 then | |
-- If we don't have rounded borders, we can start with a simple border colored rectangle. | |
bb:paintRect(x, y, my_size.w, my_size.h, self.bordercolor) | |
-- And a full background bar inside (i.e., on top) of that. | |
bb:paintRectRGB32( | |
x + self.margin_h + self.bordersize, | |
fill_y, | |
math.ceil(fill_width), | |
math.ceil(fill_height), | |
self.bgcolor | |
) | |
else | |
-- Otherwise, we have to start with the background. | |
-- Normally rounded but no rounded color support currently | |
bb:paintRectRGB32(x, y, my_size.w, my_size.h, self.bgcolor) | |
-- Then the border around that. | |
bb:paintBorder( | |
math.floor(x), | |
math.floor(y), | |
my_size.w, | |
my_size.h, | |
self.bordersize, | |
self.bordercolor, | |
self.radius | |
) | |
end | |
-- Then we can just paint the fill rectangle(s) and tick(s) on top of that. | |
-- First the fill bar(s)... | |
-- Fill bar for alternate pages (e.g. non-linear flows). | |
if self.alt and self.alt[1] ~= nil then | |
for i = 1, #self.alt do | |
local tick_x = fill_width * ((self.alt[i][1] - 1) / self.last) | |
local width = fill_width * (self.alt[i][2] / self.last) | |
if _mirroredUI then | |
tick_x = fill_width - tick_x - width | |
end | |
tick_x = math.floor(tick_x) | |
width = math.ceil(width) | |
bb:paintRectRGB32( | |
x + self.margin_h + self.bordersize + tick_x, | |
fill_y, | |
width, | |
math.ceil(fill_height), | |
self.altcolor | |
) | |
end | |
end | |
-- Main fill bar for the specified percentage. | |
if self.percentage >= 0 and self.percentage <= 1 then | |
local fill_x = x + self.margin_h + self.bordersize | |
if self.fill_from_right or (_mirroredUI and not self.fill_from_right) then | |
fill_x = fill_x + (fill_width * (1 - self.percentage)) | |
fill_x = math.floor(fill_x) | |
end | |
bb:paintRectRGB32( | |
fill_x, | |
fill_y, | |
math.ceil(fill_width * self.percentage), | |
math.ceil(fill_height), | |
self.fillcolor | |
) | |
-- Overlay the initial position marker on top of that | |
if self.initial_pos_marker and self.initial_percentage >= 0 then | |
if self.height <= INITIAL_MARKER_HEIGHT_THRESHOLD then | |
self.initial_pos_icon:paintTo( | |
bb, | |
Math.round(fill_x + math.ceil(fill_width * self.initial_percentage) - self.height / 4), | |
y - Math.round(self.height / 6) | |
) | |
else | |
self.initial_pos_icon:paintTo( | |
bb, | |
Math.round(fill_x + math.ceil(fill_width * self.initial_percentage) - self.height / 2), | |
y | |
) | |
end | |
end | |
end | |
-- ...then the tick(s). | |
if self.ticks and self.last and self.last > 0 then | |
for i, tick in ipairs(self.ticks) do | |
local tick_x = fill_width * (tick / self.last) | |
if _mirroredUI then | |
tick_x = fill_width - tick_x | |
end | |
tick_x = math.floor(tick_x) | |
bb:paintRect( | |
x + self.margin_h + self.bordersize + tick_x, | |
fill_y, | |
self.tick_width, | |
math.ceil(fill_height), | |
self.bordercolor | |
) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi there! Apologies for the late reply, I had a long period where I was busy with work. I had another plugin, the original thin progress bar, enabled. Disabling that one solved the problem!