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_popout = false,
is_borderless = true, is_borderless = true,
has_close_button = true, has_close_button = true,
perpage = G_reader_settings:readSetting("items_per_page"),
file_filter = function(filename) file_filter = function(filename)
if DocumentRegistry:getProvider(filename) then if DocumentRegistry:getProvider(filename) then
return true return true

@ -152,6 +152,7 @@ function FileSearcher:showSearchResults()
show_parent = menu_container, show_parent = menu_container,
onMenuHold = self.onMenuHold, onMenuHold = self.onMenuHold,
cface = Font:getFace("smallinfofont"), cface = Font:getFace("smallinfofont"),
perpage = G_reader_settings:readSetting("items_per_page") or 14,
_manager = self, _manager = self,
} }
table.insert(menu_container, self.search_menu) 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, checked_func = function() return self.ui.file_chooser.show_hidden end,
callback = function() self.ui:toggleHiddenFiles() 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.sort_by = self.ui:getSortingMenuTable()
self.menu_items.reverse_sorting = { self.menu_items.reverse_sorting = {
text = _("Reverse sorting"), text = _("Reverse sorting"),

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

@ -218,6 +218,8 @@ function ReaderBookmark:onShowBookmark()
width = Screen:getWidth(), width = Screen:getWidth(),
height = Screen:getHeight(), height = Screen:getHeight(),
cface = Font:getFace("x_smallinfofont"), 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 = { on_close_ges = {
GestureRange:new{ GestureRange:new{
ges = "two_finger_swipe", ges = "two_finger_swipe",

@ -317,6 +317,9 @@ function ReaderToc:onShowToc()
width = Screen:getWidth(), width = Screen:getWidth(),
height = Screen:getHeight(), height = Screen:getHeight(),
cface = Font:getFace("x_smallinfofont"), 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 = { on_close_ges = {
GestureRange:new{ GestureRange:new{
ges = "two_finger_swipe", ges = "two_finger_swipe",

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

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

@ -21,6 +21,7 @@ local OverlapGroup = require("ui/widget/overlapgroup")
local RenderText = require("ui/rendertext") local RenderText = require("ui/rendertext")
local RightContainer = require("ui/widget/container/rightcontainer") local RightContainer = require("ui/widget/container/rightcontainer")
local Size = require("ui/size") local Size = require("ui/size")
local TextBoxWidget = require("ui/widget/textboxwidget")
local TextWidget = require("ui/widget/textwidget") local TextWidget = require("ui/widget/textwidget")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local UnderlineContainer = require("ui/widget/container/underlinecontainer") local UnderlineContainer = require("ui/widget/container/underlinecontainer")
@ -129,10 +130,15 @@ local MenuItem = InputContainer:new{
detail = nil, detail = nil,
face = Font:getFace("cfont", 30), face = Font:getFace("cfont", 30),
info_face = Font:getFace("infont", 15), info_face = Font:getFace("infont", 15),
font = "cfont",
font_size = 24,
infont = "infont",
infont_size = 18,
dimen = nil, dimen = nil,
shortcut = nil, shortcut = nil,
shortcut_style = "square", shortcut_style = "square",
_underline_container = nil, _underline_container = nil,
linesize = Size.line.medium,
} }
function MenuItem:init() function MenuItem:init()
@ -179,38 +185,114 @@ function MenuItem:init()
text_ellipsis_mandatory_padding = Size.span.horizontal_small text_ellipsis_mandatory_padding = Size.span.horizontal_small
end end
local mandatory = self.mandatory and ""..self.mandatory or "" 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 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{ local state_button = self.state or HorizontalSpan:new{
width = state_button_width, width = state_button_width,
} }
local state_indent = self.state and self.state.indent or "" 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{ local state_container = LeftContainer:new{
dimen = Geom:new{w = self.content_width/2, h = self.dimen.h}, dimen = Geom:new{w = self.content_width/2, h = self.dimen.h},
HorizontalGroup:new{ HorizontalGroup:new{
HorizontalSpan:new{ HorizontalSpan:new{
width = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, 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{ local text_container = LeftContainer:new{
@ -219,27 +301,20 @@ function MenuItem:init()
HorizontalSpan:new{ HorizontalSpan:new{
width = self.state_size.w, width = self.state_size.w,
}, },
TextWidget:new{ item_name,
text = self.text,
face = self.face,
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
} }
} }
local mandatory_container = RightContainer:new{ local mandatory_container = RightContainer:new{
dimen = Geom:new{w = self.content_width, h = self.dimen.h}, dimen = Geom:new{w = self.content_width, h = self.dimen.h},
TextWidget:new{ mandatory_widget,
text = mandatory,
face = self.info_face,
bold = self.bold,
fgcolor = self.dim and Blitbuffer.COLOR_GREY or nil,
}
} }
self._underline_container = UnderlineContainer:new{ self._underline_container = UnderlineContainer:new{
color = self.line_color,
linesize = self.linesize,
vertical_align = "center", vertical_align = "center",
padding = 0,
dimen = Geom:new{ dimen = Geom:new{
w = self.content_width, w = self.content_width,
h = self.dimen.h h = self.dimen.h
@ -282,7 +357,7 @@ function MenuItem:onFocus()
end end
function MenuItem:onUnfocus() function MenuItem:onUnfocus()
self._underline_container.color = Blitbuffer.COLOR_WHITE self._underline_container.color = self.line_color
self.key_events = {} self.key_events = {}
return true return true
end end
@ -394,22 +469,37 @@ local Menu = FocusManager:new{
has_close_button = true, has_close_button = true,
-- close_callback is a function, which is executed when menu is closed -- close_callback is a function, which is executed when menu is closed
-- it is usually set by the widget which creates the menu -- 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() 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.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{ self.item_dimen = Geom:new{
w = self.dimen.w, w = self.dimen.w,
h = Screen:scaleBySize(46), -- hardcoded for now h = Screen:scaleBySize(46),
} }
-- if height not given, dynamically calculate it local height_dim
self.dimen.h = self.height or (#self.item_table + 2) * self.item_dimen.h local bottom_height = 0
if self.dimen.h > Screen:getHeight() then local top_height = 0
self.dimen.h = Screen:getHeight() 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 end
-- header and footer should approximately take up space of 2 items if self.menu_title and not self.no_title then
self.perpage = math.floor(self.dimen.h / self.item_dimen.h) - (self.no_title and 1 or 2) 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) self.page_num = math.ceil(#self.item_table / self.perpage)
-- fix current page if out of range -- 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 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() function Menu:init()
self.show_parent = self.show_parent or self self.show_parent = self.show_parent or self
self.item_table_stack = {} 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 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 if self.no_title then
content = OverlapGroup:new{ self.content_group = VerticalGroup:new{
dimen = self.dimen:copy(), align = "left",
VerticalGroup:new{ self.vertical_span,
align = "left", body,
body,
},
page_return,
footer,
} }
else else
content = OverlapGroup:new{ self.content_group = VerticalGroup:new{
dimen = self.dimen:copy(), align = "left",
VerticalGroup:new{ header,
align = "left", self.vertical_span,
header, body,
body,
},
page_return,
footer,
} }
end end
local content = OverlapGroup:new{
dimen = self.dimen:copy(),
self.content_group,
page_return,
footer,
}
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
background = Blitbuffer.COLOR_WHITE, background = Blitbuffer.COLOR_WHITE,
@ -566,7 +661,6 @@ function Menu:init()
radius = self.is_popout and math.floor(self.dimen.w/20) or 0, radius = self.is_popout and math.floor(self.dimen.w/20) or 0,
content content
} }
------------------------------------------ ------------------------------------------
-- start to set up input event callback -- -- start to set up input event callback --
------------------------------------------ ------------------------------------------
@ -655,12 +749,18 @@ function Menu:updateItems(select_number)
self.item_group:clear() self.item_group:clear()
self.page_info:resetLayout() self.page_info:resetLayout()
self.return_button:resetLayout() self.return_button:resetLayout()
self.vertical_span:clear()
self.content_group:resetLayout()
self:_recalculateDimen() self:_recalculateDimen()
-- default to select the first item -- default to select the first item
if not select_number then if not select_number then
select_number = 1 select_number = 1
end 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 for c = 1, math.min(self.perpage, #self.item_table) do
-- calculate index in item_table -- calculate index in item_table
@ -688,12 +788,18 @@ function Menu:updateItems(select_number)
mandatory = self.item_table[i].mandatory, mandatory = self.item_table[i].mandatory,
bold = self.item_table.current == i or self.item_table[i].bold == true, bold = self.item_table.current == i or self.item_table[i].bold == true,
dim = self.item_table[i].dim, 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(), dimen = self.item_dimen:new(),
shortcut = item_shortcut, shortcut = item_shortcut,
shortcut_style = shortcut_style, shortcut_style = shortcut_style,
table = self.item_table[i], table = self.item_table[i],
menu = self, menu = self,
linesize = self.linesize,
single_line = self.single_line,
line_color = self.line_color,
} }
table.insert(self.item_group, item_tmp) table.insert(self.item_group, item_tmp)
-- this is for focus manager -- 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