2
0
mirror of https://github.com/koreader/koreader synced 2024-11-13 19:11:25 +00:00
koreader/frontend/ui/widget/bookstatuswidget.lua

634 lines
19 KiB
Lua
Raw Normal View History

2017-04-29 08:38:09 +00:00
local Blitbuffer = require("ffi/blitbuffer")
local Button = require("ui/widget/button")
2016-02-02 17:38:14 +00:00
local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device")
2017-04-29 08:38:09 +00:00
local Font = require("ui/font")
local FocusManager = require("ui/widget/focusmanager")
2017-04-29 08:38:09 +00:00
local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
2016-02-02 17:38:14 +00:00
local HorizontalGroup = require("ui/widget/horizontalgroup")
local HorizontalSpan = require("ui/widget/horizontalspan")
2017-04-29 08:38:09 +00:00
local ImageWidget = require("ui/widget/imagewidget")
local InputDialog = require("ui/widget/inputdialog")
2016-02-02 17:38:14 +00:00
local InputText = require("ui/widget/inputtext")
2017-04-29 08:38:09 +00:00
local LeftContainer = require("ui/widget/container/leftcontainer")
2016-02-02 17:38:14 +00:00
local LineWidget = require("ui/widget/linewidget")
2017-04-29 08:38:09 +00:00
local ProgressWidget = require("ui/widget/progresswidget")
local RenderImage = require("ui/renderimage")
2017-09-13 14:56:20 +00:00
local Size = require("ui/size")
2016-02-09 12:51:55 +00:00
local TextBoxWidget = require("ui/widget/textboxwidget")
2017-04-29 08:38:09 +00:00
local TextWidget = require("ui/widget/textwidget")
2022-02-02 13:03:32 +00:00
local TitleBar = require("ui/widget/titlebar")
2017-04-29 08:38:09 +00:00
local ToggleSwitch = require("ui/widget/toggleswitch")
2016-02-02 17:38:14 +00:00
local UIManager = require("ui/uimanager")
2017-04-29 08:38:09 +00:00
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local datetime = require("datetime")
2016-02-02 17:38:14 +00:00
local util = require("util")
local _ = require("gettext")
local Screen = Device.screen
2018-05-20 10:16:14 +00:00
local T = require("ffi/util").template
2016-02-02 17:38:14 +00:00
2017-09-14 20:29:09 +00:00
local stats_book = {}
2016-02-02 17:38:14 +00:00
--[[
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
-- Stored in the sidecar metadata, in a dedicated table:
2016-02-02 17:38:14 +00:00
["summary"] = {
["rating"] = 5,
["note"] = "Some text",
["status"] = "Reading"
["modified"] = "24.01.2016"
},]]
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local BookStatusWidget = FocusManager:extend{
2017-09-13 14:56:20 +00:00
padding = Size.padding.fullscreen,
2016-02-02 17:38:14 +00:00
settings = nil,
thumbnail = nil,
props = nil,
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
star = nil, -- Button
summary = nil, -- hash
}
function BookStatusWidget:init()
self.updated = false
self.layout = {}
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
-- What a blank, full summary table should look like
local new_summary = {
rating = nil,
note = nil,
status = "",
modified = "",
}
2016-02-02 17:38:14 +00:00
if self.settings then
local summary = self.settings:readSetting("summary")
-- Check if the summary table we get is a full one, or a minimal one from CoverMenu...
if summary then
if summary.modified then
-- Complete, use it as-is
self.summary = summary
else
-- Incomplete, fill it up
self.summary = new_summary
util.tableMerge(self.summary, summary)
end
else
self.summary = new_summary
end
Clarify our OOP semantics across the codebase (#9586) Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
else
self.summary = new_summary
2016-02-02 17:38:14 +00:00
end
self.total_pages = self.ui.document:getPageCount()
2017-09-14 20:29:09 +00:00
stats_book = self:getStats()
2016-02-02 17:38:14 +00:00
2017-04-29 08:38:09 +00:00
self.small_font_face = Font:getFace("smallffont")
self.medium_font_face = Font:getFace("ffont")
self.large_font_face = Font:getFace("largeffont")
2016-02-02 17:38:14 +00:00
local button_enabled = true
if self.readonly then
button_enabled = false
end
2016-02-10 07:01:19 +00:00
self.star = Button:new{
icon = "star.empty",
2016-02-02 17:38:14 +00:00
bordersize = 0,
radius = 0,
margin = 0,
enabled = button_enabled,
2016-02-02 17:38:14 +00:00
show_parent = self,
readonly = self.readonly,
2016-02-02 17:38:14 +00:00
}
if Device:hasKeys() then
self.key_events.Close = { { Device.input.group.Back } }
end
if Device:isTouchDevice() then
self.ges_events.Swipe = {
GestureRange:new{
ges = "swipe",
range = function() return self.dimen end,
}
}
self.ges_events.MultiSwipe = {
GestureRange:new{
ges = "multiswipe",
range = function() return self.dimen end,
}
}
end
local screen_size = Screen:getSize()
self.covers_fullscreen = true -- hint for UIManager:_repaint()
self[1] = FrameContainer:new{
width = screen_size.w,
height = screen_size.h,
2016-02-02 17:38:14 +00:00
background = Blitbuffer.COLOR_WHITE,
bordersize = 0,
padding = 0,
self:getStatusContent(screen_size.w),
2016-02-02 17:38:14 +00:00
}
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
self.dithered = true
2016-02-02 17:38:14 +00:00
end
2017-09-14 20:29:09 +00:00
function BookStatusWidget:getStats()
return {}
end
function BookStatusWidget:getStatDays()
if stats_book.days then
return tostring(stats_book.days)
2017-09-14 20:29:09 +00:00
else
return _("N/A")
end
end
function BookStatusWidget:getStatHours()
if stats_book.time then
local user_duration_format = G_reader_settings:readSetting("duration_format", "classic")
return datetime.secondsToClockDuration(user_duration_format, stats_book.time, false)
2017-09-14 20:29:09 +00:00
else
return _("N/A")
end
end
function BookStatusWidget:getStatReadPages()
if stats_book.pages then
return string.format("%s/%s",stats_book.pages, self.total_pages)
else
return _("N/A")
end
end
function BookStatusWidget:getStatusContent(width)
2022-02-02 13:03:32 +00:00
local title_bar = TitleBar:new{
width = width,
bottom_v_padding = 0,
close_callback = not self.readonly and function() self:onClose() end,
show_parent = self,
}
local content = VerticalGroup:new{
align = "left",
2022-02-02 13:03:32 +00:00
title_bar,
self:genBookInfoGroup(),
self:genHeader(_("Statistics")),
self:genStatisticsGroup(width),
self:genHeader(_("Review")),
self:genSummaryGroup(width),
2022-02-02 13:03:32 +00:00
self:genHeader(self.readonly and _("Book Status") or _("Update Status")),
self:generateSwitchGroup(width),
2016-02-02 17:38:14 +00:00
}
return content
2016-02-02 17:38:14 +00:00
end
function BookStatusWidget:genHeader(title)
2017-09-13 14:56:20 +00:00
local width, height = Screen:getWidth(), Size.item.height_default
2016-02-02 17:38:14 +00:00
local header_title = TextWidget:new{
2016-02-02 17:38:14 +00:00
text = title,
face = self.medium_font_face,
fgcolor = Blitbuffer.COLOR_GRAY_9,
2016-02-02 17:38:14 +00:00
}
local padding_span = HorizontalSpan:new{ width = self.padding }
local line_width = (width - header_title:getSize().w) / 2 - self.padding * 2
2016-02-10 07:01:19 +00:00
local line_container = LeftContainer:new{
dimen = Geom:new{ w = line_width, h = height },
2016-02-10 07:01:19 +00:00
LineWidget:new{
background = Blitbuffer.COLOR_LIGHT_GRAY,
2016-02-10 07:01:19 +00:00
dimen = Geom:new{
w = line_width,
2017-09-13 14:56:20 +00:00
h = Size.line.thick,
2016-02-02 17:38:14 +00:00
}
}
}
local span_top, span_bottom
if Screen:getScreenMode() == "landscape" then
span_top = VerticalSpan:new{ width = Size.span.horizontal_default }
span_bottom = VerticalSpan:new{ width = Size.span.horizontal_default }
else
span_top = VerticalSpan:new{ width = Size.item.height_default }
span_bottom = VerticalSpan:new{ width = Size.span.vertical_large }
end
2016-02-02 17:38:14 +00:00
return VerticalGroup:new{
span_top,
HorizontalGroup:new{
align = "center",
padding_span,
line_container,
padding_span,
header_title,
padding_span,
line_container,
padding_span,
2016-02-02 17:38:14 +00:00
},
span_bottom,
2016-02-02 17:38:14 +00:00
}
end
function BookStatusWidget:onChangeBookStatus(option_name, option_value)
2016-02-02 17:38:14 +00:00
self.summary.status = option_name[option_value]
self.summary.modified = os.date("%Y-%m-%d", os.time())
self.updated = true
2016-02-02 17:38:14 +00:00
return true
end
function BookStatusWidget:generateRateGroup(width, height, rating)
2016-02-10 07:01:19 +00:00
self.stars_container = CenterContainer:new{
dimen = Geom:new{ w = width, h = height },
2016-02-02 17:38:14 +00:00
}
self:setStar(rating)
return self.stars_container
end
function BookStatusWidget:setStar(num)
Tame some ButtonTable users into re-using Buttontable instances if possible (#7166) * QuickDictLookup, ImageViewer, NumberPicker: Smarter `update` that will re-use most of the widget's layout instead of re-instantiating all the things. * SpinWidget/DoubleSpinWidget: The NumberPicker change above renders a hack to preserve alpha on these widgets almost unnecessary. Also fixed said hack to also apply to the center, value button. * Button: Don't re-instantiate the frame in setText/setIcon when unnecessary (e.g., no change at all, or no layout change). * Button: Add a refresh method that repaints and refreshes a *specific* Button (provided it's been painted once) all on its lonesome. * ConfigDialog: Free everything that's going to be re-instatiated on update * A few more post #7118 fixes: * SkimTo: Always flag the chapter nav buttons as vsync * Button: Fix the highlight on rounded buttons when vsync is enabled (e.g., it's now entirely visible, instead of showing a weird inverted corner glitch). * Some more heuristic tweaks in Menu/TouchMenu/Button/IconButton * ButtonTable: fix the annoying rounding issue I'd noticed in #7054 ;). * Enable dithering in TextBoxWidget (e.g., in the Wikipedia full view). This involved moving the HW dithering align fixup to base, where it always ought to have been ;). * Switch a few widgets that were using "partial" on close to "ui", or, more rarely, "flashui". The intent being to limit "partial" purely to the Reader, because it has a latency cost when mixed with other refreshes, which happens often enough in UI ;). * Minor documentation tweaks around UIManager's `setDirty` to reflect that change. * ReaderFooter: Force a footer repaint on resume if it is visible (otherwise, just update it). * ReaderBookmark: In the same vein, don't repaint an invisible footer on bookmark count changes.
2021-01-28 23:20:15 +00:00
-- clear previous data
2016-02-02 17:38:14 +00:00
self.stars_container:clear()
2016-02-10 07:01:19 +00:00
local stars_group = HorizontalGroup:new{ align = "center" }
local row = {}
2016-02-02 17:38:14 +00:00
if num then
self.summary.rating = num
self.updated = true
2016-02-02 17:38:14 +00:00
for i = 1, num do
local star = self.star:new{
icon = "star.full",
2016-02-10 07:01:19 +00:00
callback = function() self:setStar(i) end
}
table.insert(stars_group, star)
table.insert(row, star)
2016-02-02 17:38:14 +00:00
end
else
num = 0
end
for i = num + 1, 5 do
local star = self.star:new{ callback = function() self:setStar(i) end }
table.insert(stars_group, star)
table.insert(row, star)
2016-02-02 17:38:14 +00:00
end
self.layout[1] = row
2016-02-02 17:38:14 +00:00
table.insert(self.stars_container, stars_group)
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
-- Individual stars are Button, w/ flash_ui, they'll have their own flash.
-- And we need to redraw the full widget, because we don't know the coordinates of stars_container :/.
self:refocusWidget()
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
UIManager:setDirty(self, "ui", nil, true)
2016-02-02 17:38:14 +00:00
return true
end
function BookStatusWidget:genBookInfoGroup()
local screen_width = Screen:getWidth()
local split_span_width = math.floor(screen_width * 0.05)
local img_width, img_height
if Screen:getScreenMode() == "landscape" then
img_width = Screen:scaleBySize(132)
img_height = Screen:scaleBySize(184)
else
img_width = Screen:scaleBySize(132 * 1.5)
img_height = Screen:scaleBySize(184 * 1.5)
end
local height = img_height
local width = screen_width - split_span_width - img_width
-- Get a chance to have title and authors rendered with alternate
-- glyphs for the book language
local lang = nil
if self.props.language and self.props.language ~= "" then
lang = self.props.language
end
-- title
local book_meta_info_group = VerticalGroup:new{
align = "center",
VerticalSpan:new{ width = height * 0.2 },
TextBoxWidget:new{
text = self.props.title,
lang = lang,
width = width,
face = self.medium_font_face,
alignment = "center",
},
}
-- author
local text_author = TextBoxWidget:new{
text = self.props.authors,
lang = lang,
face = self.small_font_face,
width = width,
alignment = "center",
}
table.insert(book_meta_info_group,
CenterContainer:new{
dimen = Geom:new{ w = width, h = text_author:getSize().h },
text_author
}
)
-- progress bar
local read_percentage = self.ui:getCurrentPage() / self.total_pages
local progress_bar = ProgressWidget:new{
width = math.floor(width * 0.7),
height = Screen:scaleBySize(10),
percentage = read_percentage,
2016-04-21 03:48:50 +00:00
ticks = nil,
last = nil,
}
table.insert(book_meta_info_group,
CenterContainer:new{
dimen = Geom:new{ w = width, h = progress_bar:getSize().h },
progress_bar
}
)
-- complete text
local text_complete = TextWidget:new{
2018-05-20 10:16:14 +00:00
text = T(_("%1% Completed"),
string.format("%1.f", read_percentage * 100)),
face = self.small_font_face,
}
table.insert(book_meta_info_group,
CenterContainer:new{
dimen = Geom:new{ w = width, h = text_complete:getSize().h },
text_complete
}
)
-- rating
table.insert(book_meta_info_group,
VerticalSpan:new{ width = Screen:scaleBySize(30) })
local rateHeight = Screen:scaleBySize(60)
table.insert(book_meta_info_group,
self:generateRateGroup(screen_width, rateHeight, self.summary.rating))
-- build the final group
local book_info_group = HorizontalGroup:new{
align = "top",
HorizontalSpan:new{ width = split_span_width }
}
-- thumbnail
if self.thumbnail then
-- Much like BookInfoManager, honor AR here
local cbb_w, cbb_h = self.thumbnail:getWidth(), self.thumbnail:getHeight()
if cbb_w > img_width or cbb_h > img_height then
local scale_factor = math.min(img_width / cbb_w, img_height / cbb_h)
cbb_w = math.min(math.floor(cbb_w * scale_factor)+1, img_width)
cbb_h = math.min(math.floor(cbb_h * scale_factor)+1, img_height)
self.thumbnail = RenderImage:scaleBlitBuffer(self.thumbnail, cbb_w, cbb_h, true)
end
table.insert(book_info_group, ImageWidget:new{
image = self.thumbnail,
width = cbb_w,
height = cbb_h,
})
2017-05-08 07:43:34 +00:00
-- dereference thumbnail since we let imagewidget manages its lifecycle
self.thumbnail = nil
end
table.insert(book_info_group, CenterContainer:new{
dimen = Geom:new{ w = width, h = height },
book_meta_info_group,
})
return CenterContainer:new{
dimen = Geom:new{ w = screen_width, h = img_height },
book_info_group,
}
end
function BookStatusWidget:genStatisticsGroup(width)
local height = Screen:scaleBySize(60)
2016-02-10 07:01:19 +00:00
local statistics_container = CenterContainer:new{
dimen = Geom:new{ w = width, h = height },
2016-02-02 17:38:14 +00:00
}
2016-02-10 07:01:19 +00:00
local statistics_group = VerticalGroup:new{ align = "left" }
2016-02-02 17:38:14 +00:00
local tile_width = width * (1/3)
local tile_height = height * (1/2)
2016-02-02 17:38:14 +00:00
2016-02-10 07:01:19 +00:00
local titles_group = HorizontalGroup:new{
2016-02-09 12:51:55 +00:00
align = "center",
2016-02-10 07:01:19 +00:00
CenterContainer:new{
dimen = Geom:new{ w = tile_width, h = tile_height },
TextWidget:new{
2016-02-09 12:51:55 +00:00
text = _("Days"),
face = self.small_font_face,
},
2016-02-02 17:38:14 +00:00
},
2016-02-10 07:01:19 +00:00
CenterContainer:new{
dimen = Geom:new{ w = tile_width, h = tile_height },
TextWidget:new{
2016-02-09 12:51:55 +00:00
text = _("Time"),
face = self.small_font_face,
},
2016-02-02 17:38:14 +00:00
},
2016-02-10 07:01:19 +00:00
CenterContainer:new{
dimen = Geom:new{ w = tile_width, h = tile_height },
TextWidget:new{
2016-02-09 12:51:55 +00:00
text = _("Read pages"),
face = self.small_font_face,
}
2016-02-02 17:38:14 +00:00
}
}
2016-02-10 07:01:19 +00:00
local data_group = HorizontalGroup:new{
2016-02-09 12:51:55 +00:00
align = "center",
2016-02-10 07:01:19 +00:00
CenterContainer:new{
dimen = Geom:new{ w = tile_width, h = tile_height },
TextWidget:new{
2017-09-14 20:29:09 +00:00
text = self:getStatDays(),
2016-02-09 12:51:55 +00:00
face = self.medium_font_face,
},
2016-02-02 17:38:14 +00:00
},
2016-02-10 07:01:19 +00:00
CenterContainer:new{
dimen = Geom:new{ w = tile_width, h = tile_height },
TextWidget:new{
2017-09-14 20:29:09 +00:00
text = self:getStatHours(),
2016-02-09 12:51:55 +00:00
face = self.medium_font_face,
},
2016-02-02 17:38:14 +00:00
},
2016-02-10 07:01:19 +00:00
CenterContainer:new{
dimen = Geom:new{ w = tile_width, h = tile_height },
TextWidget:new{
2017-09-14 20:29:09 +00:00
text = self:getStatReadPages(),
2016-02-09 12:51:55 +00:00
face = self.medium_font_face,
}
2016-02-02 17:38:14 +00:00
}
}
table.insert(statistics_group, titles_group)
table.insert(statistics_group, data_group)
table.insert(statistics_container, statistics_group)
return statistics_container
end
function BookStatusWidget:genSummaryGroup(width)
local height
if Screen:getScreenMode() == "landscape" then
2017-09-13 14:56:20 +00:00
height = Screen:scaleBySize(80)
else
2017-09-13 14:56:20 +00:00
height = Screen:scaleBySize(160)
end
2016-02-02 17:38:14 +00:00
2017-09-13 14:56:20 +00:00
local text_padding = Size.padding.default
self.input_note = InputText:new{
text = self.summary.note,
face = self.medium_font_face,
width = width - self.padding * 3,
height = math.floor(height * 0.75),
scroll = true,
2017-09-13 14:56:20 +00:00
bordersize = Size.border.default,
focused = false,
padding = text_padding,
parent = self,
readonly = self.readonly,
hint = _("A few words about the book"),
2016-02-02 17:38:14 +00:00
}
table.insert(self.layout, {self.input_note})
2016-02-02 17:38:14 +00:00
return VerticalGroup:new{
2017-09-13 14:56:20 +00:00
VerticalSpan:new{ width = Size.span.vertical_large },
CenterContainer:new{
dimen = Geom:new{ w = width, h = height },
self.input_note
2016-02-02 17:38:14 +00:00
}
2016-02-09 12:51:55 +00:00
}
end
2016-02-02 17:38:14 +00:00
function BookStatusWidget:generateSwitchGroup(width)
local height
if Screen:getScreenMode() == "landscape" then
-- landscape mode
height = Screen:scaleBySize(60)
else
-- portrait mode
height = Screen:scaleBySize(105)
end
local args = { "reading", "abandoned", "complete", }
local position = 1
for k, v in ipairs(args) do
if v == self.summary.status then
position = k
break
end
end
local switch = ToggleSwitch:new{
width = math.floor(width * 0.6),
name = "book_status",
event = "ChangeBookStatus",
toggle = { _("Reading"), _("On hold"), _("Finished"), },
args = args,
alternate = false,
values = { 1, 2, 3, },
enabled = not self.readonly,
config = self,
readonly = self.readonly,
2016-02-02 17:38:14 +00:00
}
switch:setPosition(position)
self:mergeLayoutInVertical(switch)
2016-02-02 17:38:14 +00:00
return VerticalGroup:new{
VerticalSpan:new{ width = Screen:scaleBySize(10) },
CenterContainer:new{
ignore = "height",
dimen = Geom:new{ w = width, h = height },
switch,
}
2016-02-02 17:38:14 +00:00
}
end
2016-02-02 17:38:14 +00:00
function BookStatusWidget:onConfigChoose(values, name, event, args, position)
UIManager:tickAfterNext(function()
if values then
self:onChangeBookStatus(args, position)
end
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
UIManager:setDirty(nil, "ui", nil, true)
end)
2016-02-02 17:38:14 +00:00
end
function BookStatusWidget:onSwipe(arg, ges_ev)
if ges_ev.direction == "south" then
-- Allow easier closing with swipe down
self:onClose()
elseif ges_ev.direction == "east" or ges_ev.direction == "west" or ges_ev.direction == "north" then
-- no use for now
do end -- luacheck: ignore 541
else -- diagonal swipe
-- trigger full refresh
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
UIManager:setDirty(nil, "full", nil, true)
-- a long diagonal swipe may also be used for taking a screenshot,
-- so let it propagate
return false
end
end
function BookStatusWidget:onMultiSwipe(arg, ges_ev)
-- For consistency with other fullscreen widgets where swipe south can't be
-- used to close and where we then allow any multiswipe to close, allow any
-- multiswipe to close this widget too.
self:onClose()
return true
end
function BookStatusWidget:onClose()
if self.updated and self.summary then
self.settings:saveSetting("summary", self.summary)
self.settings:flush()
end
-- NOTE: Flash on close to avoid ghosting, since we show an image.
Enable HW dithering in a few key places (#4541) * Enable HW dithering on supported devices (Clara HD, Forma; Oasis 2, PW4) * FileManager and co. (where appropriate, i.e., when covers are shown) * Book Status * Reader, where appropriate: * CRe: on pages whith image content (for over 7.5% of the screen area, should hopefully leave stuff like bullet points or small scene breaks alone). * Other engines: on user-request (in the gear tab of the bottom menu), via the new "Dithering" knob (will only appear on supported devices). * ScreenSaver * ImageViewer * Minimize repaints when flash_ui is enabled (by, almost everywhere, only repainting the flashing element, and not the toplevel window which hosts it). (The first pass of this involved fixing a few Button instances whose show_parent was wrong, in particular, chevrons in the FM & TopMenu). * Hunted down a few redundant repaints (unneeded setDirty("all") calls), either by switching the widget to nil when only a refresh was needed, and not a repaint, or by passing the appropritate widget to setDirty. (Note to self: Enable *verbose* debugging to catch broken setDirty calls via its post guard). There were also a few instances of 'em right behind a widget close. * Don't repaint the underlying widget when initially showing TopMenu & ConfigDialog. We unfortunately do need to do it when switching tabs, because of their variable heights. * On Kobo, disabled the extra and completely useless full refresh before suspend/reboot/poweroff, as well as on resume. No more double refreshes! * Fix another debug guard in Kobo sysfs_light * Switch ImageWidget & ImageViewer mostly to "ui" updates, which will be better suited to image content pretty much everywhere, REAGL or not. PS: (Almost :100: commits! :D)
2019-02-07 00:14:37 +00:00
UIManager:close(self, "flashpartial")
2016-02-02 17:38:14 +00:00
return true
end
function BookStatusWidget:onSwitchFocus(inputbox)
2016-02-10 07:01:19 +00:00
self.note_dialog = InputDialog:new{
2017-09-13 14:56:20 +00:00
title = _("Review"),
2016-02-02 17:38:14 +00:00
input = self.input_note:getText(),
input_hint = "",
input_type = "text",
scroll = true,
Text input fixes and enhancements (#4084) InputText, ScrollTextWidget, TextBoxWidget: - proper line scrolling when moving cursor or inserting/deleting text to behave like most text editors do - fix cursor navigation, optimize refreshes when moving only the cursor, don't recreate the textwidget when moving cursor up/down - optimize refresh areas, stick to "ui" to avoid a "partial" black flash every 6 appended or deleted chars InputText: - fix issue when toggling Show password multiple times - new option: InputText.cursor_at_end (default: true) - if no InputText.height provided, measure the text widget height that we would start with, and use a ScrollTextWidget with that fixed height, so widget does not overflow container if we extend the text and increase the number of lines - as we are using "ui" refreshes while text editing, allows refreshing the InputText with a diagonal swipe on it (actually, refresh the whole screen, which allows refreshing the keyboard too if needed) ScrollTextWidget: - properly align scrollbar with its TextBoxWidget TextBoxWidget: - some cleanup (added new properties to avoid many method calls), added proxy methods for upper widgets to get them - reordered/renamed/refactored the *CharPos* methods for easier reading (sorry for the diff that won't help reviewing, but that was needed) InputDialog: - new options: allow_newline = false, -- allow entering new lines cursor_at_end = true, -- starts with cursor at end of text, ready to append fullscreen = false, -- adjust to full screen minus keyboard condensed = false, -- true will prevent adding air and balance between elements add_scroll_buttons = false, -- add scroll Up/Down buttons to first row of buttons add_nav_bar = false, -- append a row of page navigation buttons - find the most adequate text height, when none provided or fullscreen, to not overflow screen (and not be stuck with Cancel/Save buttons hidden) - had to disable the use of a MovableContainer (many issues like becoming transparent when a PathChooser comes in front, Hold to paste from clipboard, moving the InputDialog under the keyboard and getting stuck...) GestureRange: fix possible crash (when event processed after widget destruction ?) LoginDialog: fix some ui stack increase and possible crash when switching focus many times.
2018-07-19 06:30:40 +00:00
allow_newline = true,
2016-02-02 17:38:14 +00:00
text_height = Screen:scaleBySize(150),
buttons = {
{
{
text = _("Cancel"),
id = "close",
2016-02-02 17:38:14 +00:00
callback = function()
self:closeInputDialog()
end,
},
{
2017-09-13 14:56:20 +00:00
text = _("Save review"),
is_enter_default = true,
2016-02-02 17:38:14 +00:00
callback = function()
local note = self.note_dialog:getInputText()
self.input_note:setText(note)
self.summary.note = note
self.updated = true
2016-02-02 17:38:14 +00:00
self:closeInputDialog()
end,
},
},
},
}
UIManager:show(self.note_dialog)
self.note_dialog:onShowKeyboard()
2016-02-02 17:38:14 +00:00
end
function BookStatusWidget:closeInputDialog()
2016-02-02 17:38:14 +00:00
UIManager:close(self.note_dialog)
self.input_note:onUnfocus()
2016-02-02 17:38:14 +00:00
end
return BookStatusWidget