mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(collections): page the KOReader Shelves list instead of squeezing rows
Fit every library, collection and SmartScope on one screen shrank the rows with no lower bound, so a Kobo Libra with 45 shelves drew titles on top of each other. Keep rows at a readable minimum and run the shelves over further pages, repeating a section heading where it continues. Wide screens keep their two columns and turn the page once both are full, and only the shelves on the current page fetch thumbnails. Closes #2042
This commit is contained in:
@@ -401,10 +401,10 @@ function CatalogLists:listPaging(context)
|
||||
end
|
||||
return windowPaging(self:listStart(context), context.page_size, context.total, context.has_next)
|
||||
end
|
||||
if kind == "seriespage" or kind == "authorpage" then
|
||||
if kind == "seriespage" or kind == "authorpage" or kind == "shelves" then
|
||||
return { page = context.local_page or 1, pages = context.local_pages or 1 }
|
||||
end
|
||||
if kind == "search" or kind == "shelves" then return { page = 1, pages = 1 } end
|
||||
if kind == "search" then return { page = 1, pages = 1 } end
|
||||
if (kind == "books" or kind == "reading") and context.page_size and context.total then
|
||||
return windowPaging(self:listStart(context), context.page_size, context.total)
|
||||
end
|
||||
@@ -424,7 +424,7 @@ function CatalogLists:listGoPage(page)
|
||||
local p = self:listPaging(context)
|
||||
if page < 1 or (p.pages and page > p.pages) then return true end
|
||||
if page == p.page then return true end
|
||||
if kind == "seriespage" or kind == "authorpage" then
|
||||
if kind == "seriespage" or kind == "authorpage" or kind == "shelves" then
|
||||
context.local_page = page
|
||||
self:updateItems(nil, true)
|
||||
return true
|
||||
@@ -1270,7 +1270,18 @@ function CatalogLists:listBody(kind, context)
|
||||
return list or {}
|
||||
end
|
||||
local m = { libraries = tag(shelves.libraries, "libraries"), collections = tag(shelves.collections, "collections"), scopes = tag(shelves["smart-scopes"], "smart-scopes") }
|
||||
return function(f) return ListPages.shelvesBody(m, f) end, nil
|
||||
local f = ListView.frame(sw, sh, { toolbar = false, offline = context.list_offline })
|
||||
local layout = ListPages.shelvesLayout(m, f)
|
||||
context.local_pages = #layout.pages
|
||||
context.local_page = math.min(context.local_page or 1, #layout.pages)
|
||||
m.local_page = context.local_page
|
||||
context.visible_shelves = {}
|
||||
for _ci, chunks in ipairs(layout.pages[context.local_page]) do
|
||||
for _i, chunk in ipairs(chunks) do
|
||||
for i = chunk.from, chunk.to do table.insert(context.visible_shelves, layout.groups[chunk.section][3][i]) end
|
||||
end
|
||||
end
|
||||
return function(body_f) return ListPages.shelvesBody(m, body_f) end, m
|
||||
end
|
||||
if kind == "search" then
|
||||
local m = { results = context.results, query = context.query, scope = context.scope or "all", screen_w = sw, screen_h = sh,
|
||||
@@ -1427,8 +1438,7 @@ function CatalogLists:listThumbnailBooks(kind, context)
|
||||
for _i, b in ipairs(context.books or {}) do addBook(b) end
|
||||
local entries = context.entries or {}
|
||||
if kind == "shelves" then
|
||||
entries = {}
|
||||
for _s, list in pairs(context.shelves or {}) do for _i, e in ipairs(list) do table.insert(entries, e) end end
|
||||
entries = context.visible_shelves or {}
|
||||
elseif kind == "search" then
|
||||
entries = {}
|
||||
for _i, e in ipairs(context.results.authors or {}) do table.insert(entries, e) end
|
||||
|
||||
@@ -320,6 +320,63 @@ function ListData.fillRows(h, min_row)
|
||||
return { n = n, rh = math.floor(h / n) }
|
||||
end
|
||||
|
||||
-- Libraries, collections and scopes over as many pages as their rows need.
|
||||
-- sizes: the rows each section holds. o: content_h, head_h, gap, row_h (the
|
||||
-- natural row), min_row_h, columns (the section numbers each column of a
|
||||
-- single page holds) and cols (how many columns a paged page runs).
|
||||
-- Everything stays on one page while its rows can keep min_row_h; past that
|
||||
-- the sections flow through the columns at min_row_h, a section that runs on
|
||||
-- repeating its heading. { pages = { [page] = { [col] = { { section, from,
|
||||
-- to } } } }, row_h }. An empty section keeps its heading and has to = 0.
|
||||
function ListData.shelfPages(sizes, o)
|
||||
local rh = o.row_h
|
||||
for _i, col in ipairs(o.columns) do
|
||||
local rows = 0
|
||||
for _j, s in ipairs(col) do rows = rows + sizes[s] end
|
||||
local avail = o.content_h - #col * o.head_h - (#col - 1) * o.gap
|
||||
rh = math.min(rh, math.floor(avail / math.max(1, rows)))
|
||||
end
|
||||
if rh >= o.min_row_h then
|
||||
local page = {}
|
||||
for ci, col in ipairs(o.columns) do
|
||||
page[ci] = {}
|
||||
for _j, s in ipairs(col) do table.insert(page[ci], { section = s, from = 1, to = sizes[s] }) end
|
||||
end
|
||||
return { pages = { page }, row_h = rh }
|
||||
end
|
||||
|
||||
rh = o.min_row_h
|
||||
local function blank()
|
||||
local page = {}
|
||||
for ci = 1, o.cols do page[ci] = {} end
|
||||
return page
|
||||
end
|
||||
local pages = { blank() }
|
||||
local p, c, used = 1, 1, 0
|
||||
for s, n in ipairs(sizes) do
|
||||
local from = 1
|
||||
repeat
|
||||
local gap = used > 0 and o.gap or 0
|
||||
local room = math.floor((o.content_h - used - gap - o.head_h) / rh)
|
||||
if room < 1 and used > 0 then
|
||||
if c < o.cols then
|
||||
c = c + 1
|
||||
else
|
||||
c, p = 1, p + 1
|
||||
pages[p] = blank()
|
||||
end
|
||||
used, gap = 0, 0
|
||||
room = math.floor((o.content_h - o.head_h) / rh)
|
||||
end
|
||||
local take = math.min(n - from + 1, math.max(1, room))
|
||||
table.insert(pages[p][c], { section = s, from = from, to = from + take - 1 })
|
||||
used = used + gap + o.head_h + take * rh
|
||||
from = from + take
|
||||
until from > n
|
||||
end
|
||||
return { pages = pages, row_h = rh }
|
||||
end
|
||||
|
||||
-- The page holding a 0-based index, 1-based.
|
||||
function ListData.pageOf(index, per)
|
||||
if not index or not per or per < 1 then return 1 end
|
||||
|
||||
@@ -82,36 +82,45 @@ function Pages.listBody(m, f)
|
||||
end
|
||||
|
||||
-- --------------------------------------------------------------- shelves
|
||||
-- Libraries, collections and scopes on one page; wide screens set the
|
||||
-- sections in two columns.
|
||||
function Pages.shelvesBody(m, f)
|
||||
-- Libraries, collections and scopes. Wide screens set the sections in two
|
||||
-- columns; more shelves than a screen holds at a readable row run on to
|
||||
-- further pages, a section that continues repeating its heading.
|
||||
function Pages.shelvesLayout(m, f)
|
||||
local groups = {
|
||||
{ _("Libraries"), "library", m.libraries or {} },
|
||||
{ _("Collections"), "folder-open", m.collections or {} },
|
||||
{ _("SmartScopes"), "aperture", m.scopes or {} },
|
||||
}
|
||||
local wide = ListView.screenW() >= 760
|
||||
local gap_s = px(14)
|
||||
local columns = wide and { { groups[1] }, { groups[2], groups[3] } } or { groups }
|
||||
local col_w = wide and math.floor((f.w - px(28)) / 2) or f.w
|
||||
local rh = px(72)
|
||||
for _i, cg in ipairs(columns) do
|
||||
local heads, rows = #cg, 0
|
||||
for _j, g in ipairs(cg) do rows = rows + #g[3] end
|
||||
rh = math.min(rh, math.floor((f.content_h - heads * px(36) - (heads - 1) * gap_s) / math.max(1, rows)))
|
||||
end
|
||||
local layout = ListData.shelfPages({ #groups[1][3], #groups[2][3], #groups[3][3] }, {
|
||||
content_h = f.content_h, head_h = px(36), gap = px(14), row_h = px(72), min_row_h = px(56),
|
||||
columns = wide and { { 1 }, { 2, 3 } } or { { 1, 2, 3 } }, cols = wide and 2 or 1,
|
||||
})
|
||||
layout.groups, layout.wide = groups, wide
|
||||
return layout
|
||||
end
|
||||
|
||||
function Pages.shelvesBody(m, f)
|
||||
local layout = Pages.shelvesLayout(m, f)
|
||||
local page = math.max(1, math.min(#layout.pages, m.local_page or 1))
|
||||
local col_w = layout.wide and math.floor((f.w - px(28)) / 2) or f.w
|
||||
local rh = layout.row_h
|
||||
local col_els = {}
|
||||
for _i, cg in ipairs(columns) do
|
||||
local kids = {}
|
||||
for gi, g in ipairs(cg) do
|
||||
if gi > 1 then table.insert(kids, space(nil, gap_s)) end
|
||||
table.insert(kids, ListView.sectionHead(col_w, { icon = g[2], label = g[1], count = #g[3] }))
|
||||
for i, s in ipairs(g[3]) do
|
||||
local inner = vstack({ w = col_w, h = rh }, { i > 1 and ListView.rule(col_w) or false, flex(0), ListView.shelfRow(col_w, rh, s), flex(0) })
|
||||
table.insert(kids, region(inner, { kind = "open-shelf", shelf = s }, "shelf-" .. g[1] .. "-" .. i, "shelf-" .. s.section .. "-" .. tostring(s.id)))
|
||||
for _ci, chunks in ipairs(layout.pages[page]) do
|
||||
if #chunks > 0 then
|
||||
local kids = {}
|
||||
for ki, chunk in ipairs(chunks) do
|
||||
local g = layout.groups[chunk.section]
|
||||
if ki > 1 then table.insert(kids, space(nil, px(14))) end
|
||||
table.insert(kids, ListView.sectionHead(col_w, { icon = g[2], label = g[1], count = #g[3] }))
|
||||
for i = chunk.from, chunk.to do
|
||||
local s = g[3][i]
|
||||
local inner = vstack({ w = col_w, h = rh }, { i > chunk.from and ListView.rule(col_w) or false, flex(0), ListView.shelfRow(col_w, rh, s), flex(0) })
|
||||
table.insert(kids, region(inner, { kind = "open-shelf", shelf = s }, "shelf-" .. g[1] .. "-" .. i, "shelf-" .. s.section .. "-" .. tostring(s.id)))
|
||||
end
|
||||
end
|
||||
table.insert(col_els, vstack({ w = col_w }, kids))
|
||||
end
|
||||
table.insert(col_els, vstack({ w = col_w }, kids))
|
||||
end
|
||||
return hstack({ w = f.w, valign = "top", gap = px(28) }, col_els)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user