Menu: configure number of items per page, wrap entries (#3589)

Configure number of items per page (from 6 to 24) - default is 14
Allow filenames to wrap so that we can see the full name
Used by File browser, History, Search Result, Bookmarks, Table of contents (only single line), File chooser, OPDS catalog
pull/3605/head
Robert 6 years ago committed by poire-z
parent 6e303e7372
commit d163f8281d

@ -148,6 +148,7 @@ function FileManager:init()
is_popout = false,
is_borderless = true,
has_close_button = true,
perpage = G_reader_settings:readSetting("items_per_page"),
file_filter = function(filename)
if DocumentRegistry:getProvider(filename) then
return true

@ -152,6 +152,7 @@ function FileSearcher:showSearchResults()
show_parent = menu_container,
onMenuHold = self.onMenuHold,
cface = Font:getFace("smallinfofont"),
perpage = G_reader_settings:readSetting("items_per_page") or 14,
_manager = self,
}
table.insert(menu_container, self.search_menu)

@ -91,6 +91,26 @@ function FileManagerMenu:setUpdateItemTable()
checked_func = function() return self.ui.file_chooser.show_hidden end,
callback = function() self.ui:toggleHiddenFiles() end
}
self.menu_items.items_per_page = {
text = _("Items per page"),
callback = function()
local SpinWidget = require("ui/widget/spinwidget")
local curr_items = G_reader_settings:readSetting("items_per_page") or 14
local items = SpinWidget:new{
width = Screen:getWidth() * 0.6,
value = curr_items,
value_min = 6,
value_max = 24,
ok_text = _("Set items"),
title_text = _("Items per page"),
callback = function(spin)
G_reader_settings:saveSetting("items_per_page", spin.value)
self.ui:onRefresh()
end
}
UIManager:show(items)
end
}
self.menu_items.sort_by = self.ui:getSortingMenuTable()
self.menu_items.reverse_sorting = {
text = _("Reverse sorting"),

@ -91,6 +91,7 @@ function SetDefaults:init()
width = Screen:getWidth()-15,
height = Screen:getHeight()-15,
cface = Font:getFace("smallinfofont"),
perpage = G_reader_settings:readSetting("items_per_page") or 14,
show_parent = menu_container,
_manager = self,
}

@ -218,6 +218,8 @@ function ReaderBookmark:onShowBookmark()
width = Screen:getWidth(),
height = Screen:getHeight(),
cface = Font:getFace("x_smallinfofont"),
perpage = G_reader_settings:readSetting("items_per_page") or 14,
line_color = require("ffi/blitbuffer").COLOR_WHITE,
on_close_ges = {
GestureRange:new{
ges = "two_finger_swipe",

@ -317,6 +317,9 @@ function ReaderToc:onShowToc()
width = Screen:getWidth(),
height = Screen:getHeight(),
cface = Font:getFace("x_smallinfofont"),
single_line = true,
perpage = G_reader_settings:readSetting("items_per_page") or 14,
line_color = require("ffi/blitbuffer").COLOR_WHITE,
on_close_ges = {
GestureRange:new{
ges = "two_finger_swipe",

@ -8,6 +8,7 @@ local order = {
setting = {
"filemanager_display_mode",
"show_hidden_files",
"items_per_page",
"----------------------------",
"sort_by",
"reverse_sorting",

@ -34,6 +34,7 @@ local FileChooser = Menu:extend{
collate = "strcoll", -- or collate = "access",
reverse_collate = false,
path_items = {}, -- store last browsed location(item index) for each path
perpage = G_reader_settings:readSetting("items_per_page"),
}
function FileChooser:init()

@ -21,6 +21,7 @@ local OverlapGroup = require("ui/widget/overlapgroup")
local RenderText = require("ui/rendertext")
local RightContainer = require("ui/widget/container/rightcontainer")
local Size = require("ui/size")
local TextBoxWidget = require("ui/widget/textboxwidget")
local TextWidget = require("ui/widget/textwidget")
local UIManager = require("ui/uimanager")
local UnderlineContainer = require("ui/widget/container/underlinecontainer")
@ -129,10 +130,15 @@ local MenuItem = InputContainer:new{
detail = nil,
face = Font:getFace("cfont", 30),
info_face = Font:getFace("infont", 15),
font = "cfont",
font_size = 24,
infont = "infont",
infont_size = 18,
dimen = nil,
shortcut = nil,
shortcut_style = "square",
_underline_container = nil,
linesize = Size.line.medium,
}
function MenuItem:init()
@ -179,38 +185,114 @@ function MenuItem:init()
text_ellipsis_mandatory_padding = Size.span.horizontal_small
end
local mandatory = self.mandatory and ""..self.mandatory or ""
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face,
""..mandatory, true, self.bold).x
local state_button_width = self.state_size.w or 0
local my_text = self.text and ""..self.text or ""
local w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, my_text, true, self.bold).x
if w + mandatory_w + state_button_width + text_mandatory_padding >= self.content_width then
if Device:hasKeyboard() then
self.active_key_events.ShowItemDetail = {
{"Right"}, doc = "show item detail"
}
end
local indicator = "\226\128\166 " -- an ellipsis
local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
indicator, true, self.bold).x
self.text = RenderText:getSubTextByWidth(my_text, self.face,
self.content_width - indicator_w - mandatory_w - state_button_width - text_ellipsis_mandatory_padding,
true, self.bold) .. indicator
end
local state_button = self.state or HorizontalSpan:new{
width = state_button_width,
}
local state_indent = self.state and self.state.indent or ""
local item_name
local mandatory_widget
if self.single_line then -- items only in single line
self.info_face = Font:getFace(self.infont, self.infont_size)
self.face = Font:getFace(self.font, self.font_size)
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face, ""..mandatory, true, self.bold).x
local my_text = self.text and ""..self.text or ""
local w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, my_text, true, self.bold).x
if w + mandatory_w + state_button_width + text_mandatory_padding >= self.content_width then
if Device:hasKeyboard() then
self.active_key_events.ShowItemDetail = {
{"Right"}, doc = "show item detail"
}
end
local indicator = "\226\128\166 " -- an ellipsis
local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
indicator, true, self.bold).x
self.text = RenderText:getSubTextByWidth(my_text, self.face,
self.content_width - indicator_w - mandatory_w - state_button_width - text_ellipsis_mandatory_padding,
true, self.bold) .. indicator
end
item_name = TextWidget:new{
text = self.text,
face = self.face,
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
mandatory_widget = TextWidget:new{
text = mandatory,
face = self.info_face,
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
else
while true do
-- Free previously made widgets to avoid memory leaks
if mandatory_widget then
mandatory_widget:free()
end
mandatory_widget = TextWidget:new {
text = mandatory,
face = Font:getFace(self.infont, self.infont_size),
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
local height = mandatory_widget:getSize().h
if height < self.dimen.h - 2 * self.linesize then -- we fit !
break
end
-- Don't go too low
if self.infont_size < 12 then
break;
else
-- If we don't fit, decrease font size
self.infont_size = self.infont_size - 1
end
end
self.info_face = Font:getFace(self.infont, self.infont_size)
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face, "" .. mandatory, true, self.bold).x
while true do
-- Free previously made widgets to avoid memory leaks
if item_name then
item_name:free()
end
item_name = TextBoxWidget:new {
text = self.text,
face = Font:getFace(self.font, self.font_size),
width = self.content_width - mandatory_w - state_button_width - text_mandatory_padding,
alignment = "left",
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
local height = item_name:getSize().h
if height < self.dimen.h - 2 * self.linesize then -- we fit !
break
end
-- Don't go too low, and then truncate text
if self.font_size < 12 then
self.text = self.text:sub(1, -5) .. ""
else
-- If we don't fit, decrease font size
self.font_size = self.font_size - 2
end
end
self.face = Font:getFace(self.font, self.font_size)
end
local state_container = LeftContainer:new{
dimen = Geom:new{w = self.content_width/2, h = self.dimen.h},
HorizontalGroup:new{
HorizontalSpan:new{
width = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
state_indent, true, self.bold).x,
state_indent, true, self.bold).x,
},
state_button
state_button,
}
}
local text_container = LeftContainer:new{
@ -219,27 +301,20 @@ function MenuItem:init()
HorizontalSpan:new{
width = self.state_size.w,
},
TextWidget:new{
text = self.text,
face = self.face,
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
item_name,
}
}
local mandatory_container = RightContainer:new{
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
TextWidget:new{
text = mandatory,
face = self.info_face,
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
mandatory_widget,
}
self._underline_container = UnderlineContainer:new{
color = self.line_color,
linesize = self.linesize,
vertical_align = "center",
padding = 0,
dimen = Geom:new{
w = self.content_width,
h = self.dimen.h
@ -282,7 +357,7 @@ function MenuItem:onFocus()
end
function MenuItem:onUnfocus()
self._underline_container.color = Blitbuffer.COLOR_WHITE
self._underline_container.color = self.line_color
self.key_events = {}
return true
end
@ -394,22 +469,37 @@ local Menu = FocusManager:new{
has_close_button = true,
-- close_callback is a function, which is executed when menu is closed
-- it is usually set by the widget which creates the menu
close_callback = nil
close_callback = nil,
linesize = Size.line.medium,
perpage = G_reader_settings:readSetting("items_per_page") or 14,
line_color = Blitbuffer.COLOR_GREY,
}
function Menu:_recalculateDimen()
self.perpage = G_reader_settings:readSetting("items_per_page") or 14
self.span_width = 0
self.dimen.w = self.width
self.dimen.h = self.height
if self.dimen.h > Screen:getHeight() or self.dimen.h == nil then
self.dimen.h = Screen:getHeight()
end
self.item_dimen = Geom:new{
w = self.dimen.w,
h = Screen:scaleBySize(46), -- hardcoded for now
h = Screen:scaleBySize(46),
}
-- if height not given, dynamically calculate it
self.dimen.h = self.height or (#self.item_table + 2) * self.item_dimen.h
if self.dimen.h > Screen:getHeight() then
self.dimen.h = Screen:getHeight()
local height_dim
local bottom_height = 0
local top_height = 0
if self.page_return_arrow and self.page_info_text then
bottom_height = math.max(self.page_return_arrow:getSize().h, self.page_info_text:getSize().h)
+ 2 * Size.padding.button
end
-- header and footer should approximately take up space of 2 items
self.perpage = math.floor(self.dimen.h / self.item_dimen.h) - (self.no_title and 1 or 2)
if self.menu_title and not self.no_title then
top_height = self.menu_title:getSize().h + 2 * Size.padding.small
end
height_dim = self.dimen.h - bottom_height - top_height
self.item_dimen.h = math.floor(height_dim / self.perpage)
self.span_width = math.floor((height_dim - (self.perpage * (self.item_dimen.h ))) / 2 -1 )
self.page_num = math.ceil(#self.item_table / self.perpage)
-- fix current page if out of range
if self.page_num > 0 and self.page > self.page_num then self.page = self.page_num end
@ -418,7 +508,11 @@ end
function Menu:init()
self.show_parent = self.show_parent or self
self.item_table_stack = {}
self:_recalculateDimen()
self.dimen.w = self.width
self.dimen.h = self.height
if self.dimen.h > Screen:getHeight() or self.dimen.h == nil then
self.dimen.h = Screen:getHeight()
end
self.page = 1
-----------------------------------
@ -534,29 +628,30 @@ function Menu:init()
}
}
local content
self:_recalculateDimen()
self.vertical_span = HorizontalGroup:new{
VerticalSpan:new{ width = self.span_width }
}
if self.no_title then
content = OverlapGroup:new{
dimen = self.dimen:copy(),
VerticalGroup:new{
align = "left",
body,
},
page_return,
footer,
self.content_group = VerticalGroup:new{
align = "left",
self.vertical_span,
body,
}
else
content = OverlapGroup:new{
dimen = self.dimen:copy(),
VerticalGroup:new{
align = "left",
header,
body,
},
page_return,
footer,
self.content_group = VerticalGroup:new{
align = "left",
header,
self.vertical_span,
body,
}
end
local content = OverlapGroup:new{
dimen = self.dimen:copy(),
self.content_group,
page_return,
footer,
}
self[1] = FrameContainer:new{
background = Blitbuffer.COLOR_WHITE,
@ -566,7 +661,6 @@ function Menu:init()
radius = self.is_popout and math.floor(self.dimen.w/20) or 0,
content
}
------------------------------------------
-- start to set up input event callback --
------------------------------------------
@ -655,12 +749,18 @@ function Menu:updateItems(select_number)
self.item_group:clear()
self.page_info:resetLayout()
self.return_button:resetLayout()
self.vertical_span:clear()
self.content_group:resetLayout()
self:_recalculateDimen()
-- default to select the first item
if not select_number then
select_number = 1
end
--font size between 12 and 18 for better matching
local infont_size = math.floor(18 - (self.perpage - 6) / 3)
--font size between 14 and 24 for better matching
local font_size = math.floor(24 - ((self.perpage - 6)/ 18) * 10 )
for c = 1, math.min(self.perpage, #self.item_table) do
-- calculate index in item_table
@ -688,12 +788,18 @@ function Menu:updateItems(select_number)
mandatory = self.item_table[i].mandatory,
bold = self.item_table.current == i or self.item_table[i].bold == true,
dim = self.item_table[i].dim,
face = self.cface,
font = "smallinfofont",
font_size = font_size,
infont = "infont",
infont_size = infont_size,
dimen = self.item_dimen:new(),
shortcut = item_shortcut,
shortcut_style = shortcut_style,
table = self.item_table[i],
menu = self,
linesize = self.linesize,
single_line = self.single_line,
line_color = self.line_color,
}
table.insert(self.item_group, item_tmp)
-- this is for focus manager

@ -0,0 +1,201 @@
local Blitbuffer = require("ffi/blitbuffer")
local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer")
local CloseButton = require("ui/widget/closebutton")
local Device = require("device")
local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
local Font = require("ui/font")
local HorizontalGroup = require("ui/widget/horizontalgroup")
local InputContainer = require("ui/widget/container/inputcontainer")
local LineWidget = require("ui/widget/linewidget")
local OverlapGroup = require("ui/widget/overlapgroup")
local NumberPickerWidget = require("ui/widget/numberpickerwidget")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local _ = require("gettext")
local Screen = Device.screen
local SpinWidget = InputContainer:new{
title_face = Font:getFace("x_smalltfont"),
width = Screen:getWidth() * 0.95,
height = Screen:getHeight(),
value = 1,
value_max = 20,
value_min = 0,
ok_text = _("OK"),
cancel_text = _("Cancel"),
}
function SpinWidget:init()
self.medium_font_face = Font:getFace("ffont")
self.light_bar = {}
self.screen_width = Screen:getWidth()
self.screen_height = Screen:getHeight()
if Device:hasKeys() then
self.key_events = {
Close = { {"Back"}, doc = "close spin widget" }
}
end
if Device:isTouchDevice() then
self.ges_events = {
TapClose = {
GestureRange:new{
ges = "tap",
range = Geom:new{
w = self.screen_width,
h = self.screen_height,
}
},
},
}
end
self:update()
end
function SpinWidget:update()
local value_widget = NumberPickerWidget:new{
show_parent = self,
width = self.screen_width * 0.2,
value = self.value,
value_min = self.value_min,
value_max = self.value_max,
value_step = 1,
value_hold_step = 4,
}
local value_group = HorizontalGroup:new{
align = "center",
value_widget,
}
local value_title = FrameContainer:new{
padding = Size.padding.default,
margin = Size.margin.title,
bordersize = 0,
TextWidget:new{
text = self.title_text,
face = self.title_face,
bold = true,
width = self.width,
},
}
local value_line = LineWidget:new{
dimen = Geom:new{
w = self.width,
h = Size.line.thick,
}
}
local value_bar = OverlapGroup:new{
dimen = {
w = self.width,
h = value_title:getSize().h
},
value_title,
CloseButton:new{ window = self, padding_top = Size.margin.title, },
}
local buttons = {
{
{
text = self.cancel_text,
callback = function()
self:onClose()
end,
},
{
text = self.ok_text,
callback = function()
if self.callback then
self.value = value_widget:getValue()
self:callback(self)
end
self:onClose()
end,
},
}
}
local ok_cancel_buttons = ButtonTable:new{
width = self.width - 2*Size.padding.default,
buttons = buttons,
zero_sep = true,
show_parent = self,
}
self.spin_frame = FrameContainer:new{
radius = Size.radius.window,
padding = 0,
margin = 0,
background = Blitbuffer.COLOR_WHITE,
VerticalGroup:new{
align = "left",
value_bar,
value_line,
CenterContainer:new{
dimen = Geom:new{
w = self.width,
h = value_group:getSize().h + self.screen_height * 0.1,
},
value_group
},
CenterContainer:new{
dimen = Geom:new{
w = self.width,
h = ok_cancel_buttons:getSize().h,
},
ok_cancel_buttons
}
}
}
self[1] = WidgetContainer:new{
align = "center",
dimen =Geom:new{
x = 0, y = 0,
w = self.screen_width,
h = self.screen_height,
},
FrameContainer:new{
bordersize = 0,
self.spin_frame,
}
}
UIManager:setDirty(self, function()
return "ui", self.spin_frame.dimen
end)
end
function SpinWidget:onCloseWidget()
UIManager:setDirty(nil, function()
return "partial", self.spin_frame.dimen
end)
return true
end
function SpinWidget:onShow()
UIManager:setDirty(self, function()
return "ui", self.spin_frame.dimen
end)
return true
end
function SpinWidget:onAnyKeyPressed()
UIManager:close(self)
return true
end
function SpinWidget:onTapClose(arg, ges_ev)
if ges_ev.pos:notIntersectWith(self.spin_frame.dimen) then
self:onClose()
end
return true
end
function SpinWidget:onClose()
UIManager:close(self)
return true
end
return SpinWidget
Loading…
Cancel
Save