added reader widget implementation

zooming, rotating, panning works already
pull/2/merge
HW 12 years ago
parent bc191c1d9e
commit f685f68852

@ -0,0 +1,61 @@
ReaderPaging = InputContainer:new{
key_events = {
GotoNextPage = { {Input.group.PgFwd}, doc = "go to next page", event = "GotoPageRel", args = 1 },
GotoPrevPage = { {Input.group.PgBack}, doc = "go to previous page", event = "GotoPageRel", args = -1 },
GotoFirst = { {"1"}, doc = "go to start", event = "GotoPercent", args = 0},
Goto11 = { {"2"}, doc = "go to 11%", event = "GotoPercent", args = 11},
Goto22 = { {"3"}, doc = "go to 22%", event = "GotoPercent", args = 22},
Goto33 = { {"4"}, doc = "go to 33%", event = "GotoPercent", args = 33},
Goto44 = { {"5"}, doc = "go to 44%", event = "GotoPercent", args = 44},
Goto55 = { {"6"}, doc = "go to 55%", event = "GotoPercent", args = 55},
Goto66 = { {"7"}, doc = "go to 66%", event = "GotoPercent", args = 66},
Goto77 = { {"8"}, doc = "go to 77%", event = "GotoPercent", args = 77},
Goto88 = { {"9"}, doc = "go to 88%", event = "GotoPercent", args = 88},
GotoLast = { {"0"}, doc = "go to end", event = "GotoPercent", args = 100},
},
current_page = 0,
number_of_pages = 0
}
function ReaderPaging:init()
self.number_of_pages = self.ui.document.info.number_of_pages
end
-- wrapper for bounds checking
function ReaderPaging:gotoPage(number)
if number == self.current_page then
return true
end
if number > self.number_of_pages
or number < 1 then
return false
end
debug("going to page number", number)
-- this is an event to allow other controllers to be aware of this change
self.ui:handleEvent(Event:new("PageUpdate", number))
return true
end
function ReaderPaging:onPageUpdate(new_page_no)
self.current_page = new_page_no
end
function ReaderPaging:onGotoPercent(percent)
debug("goto document offset in percent:", percent)
local dest = math.floor(self.number_of_pages * percent / 100)
if dest < 1 then dest = 1 end
if dest > self.number_of_pages then
dest = self.number_of_pages
end
self:gotoPage(dest)
return true
end
function ReaderPaging:onGotoPageRel(diff)
debug("goto relative page:", diff)
self:gotoPage(self.current_page + diff)
return true
end

@ -0,0 +1,31 @@
ReaderPanning = InputContainer:new{
key_events = {
-- these will all generate the same event, just with different arguments
MoveUp = { {"Up"}, doc = "move focus up", event = "Panning", args = {0, -1} },
MoveDown = { {"Down"}, doc = "move focus down", event = "Panning", args = {0, 1} },
MoveLeft = { {"Left"}, doc = "move focus left", event = "Panning", args = {-1, 0} },
MoveRight = { {"Right"}, doc = "move focus right", event = "Panning", args = {1, 0} },
},
-- defaults
panning_steps = {
normal = 50,
alt = 25,
shift = 10,
altshift = 5
},
}
function ReaderPanning:onSetDimensions(dimensions)
self.dimen = dimensions
end
function ReaderPanning:onPanning(args, key)
local dx, dy = unpack(args)
debug("key =", key)
-- for now, bounds checking/calculation is done in the view
self.view:PanningUpdate(
dx * self.panning_steps.normal * self.dimen.w / 100,
dy * self.panning_steps.normal * self.dimen.h / 100)
return true
end

@ -0,0 +1,16 @@
ReaderRotation = InputContainer:new{
key_events = {
-- these will all generate the same event, just with different arguments
RotateLeft = { {"J"}, doc = "rotate left by 90 degrees", event = "Rotate", args = -90 },
RotateRight = { {"K"}, doc = "rotate right by 90 degrees", event = "Rotate", args = 90 },
},
current_rotation = 0
}
-- TODO: reset rotation on new document, maybe on new page?
function ReaderRotation:onRotate(rotate_by)
self.current_rotation = (self.current_rotation + rotate_by) % 360
self.ui:handleEvent(Event:new("RotationUpdate", self.current_rotation))
return true
end

@ -0,0 +1,80 @@
require "ui"
require "readerview"
require "readerzooming"
require "readerpanning"
require "readerrotation"
require "readerpaging"
--[[
This is an abstraction for a reader interface
it works using data gathered from a document interface
]]--
ReaderUI = InputContainer:new{
key_events = {
Close = { {"Home"}, doc = "close document", event = "Close" },
Back = { {"Back"}, doc = "close document", event = "Close" },
},
-- our own size
dimen = Geom:new{ w = 400, h = 600 },
-- if we have a parent container, it must be referenced for now
dialog = nil,
-- the document interface
document = nil,
}
function ReaderUI:init()
-- if we are not the top level dialog ourselves, it must be given in the table
if not self.dialog then
self.dialog = self
end
-- a view container (so it must be child #1!)
self[1] = ReaderView:new{
dialog = self.dialog,
ui = self
}
-- zooming controller
self[2] = ReaderZooming:new{
dialog = self.dialog,
view = self[1],
ui = self
}
-- panning controller
self[3] = ReaderPanning:new{
dialog = self.dialog,
view = self[1],
ui = self
}
-- rotation controller
self[4] = ReaderRotation:new{
dialog = self.dialog,
view = self[1],
ui = self
}
-- if needed, insert a paging container
if self.document.info.has_pages then
local pager = ReaderPaging:new{
dialog = self.dialog,
view = self[1],
ui = self
}
table.insert(self, pager)
pager:gotoPage(1)
end
-- notify childs of dimensions
self:handleEvent(Event:new("SetDimensions", self.dimen))
end
function ReaderUI:onClose()
debug("closing reader")
if self.document then
self.document:close()
self.document = false
end
UIManager:close(self.dialog)
return true
end

@ -0,0 +1,92 @@
require "ui"
ReaderView = WidgetContainer:new{
document = nil,
state = {
page = 0,
zoom = 1.0,
rotation = 0,
offset = {},
bbox = nil,
},
outer_page_color = 7,
visible_area = Geom:new{x = 0, y = 0},
page_area = Geom:new{},
}
function ReaderView:paintTo(bb, x, y)
debug("painting", self.visible_area, "to", x, y)
local inner_offset = Geom:new{x = 0, y = 0}
-- draw surrounding space, if any
if self.ui.dimen.h > self.visible_area.h then
inner_offset.y = (self.ui.dimen.h - self.visible_area.h) / 2
bb:paintRect(x, y, self.ui.dimen.w, inner_offset.y, self.outer_page_color)
bb:paintRect(x, y + self.ui.dimen.h - inner_offset.y - 1, self.ui.dimen.w, inner_offset.y + 1, self.outer_page_color)
end
if self.ui.dimen.w > self.visible_area.w then
inner_offset.x = (self.ui.dimen.w - self.visible_area.w) / 2
bb:paintRect(x, y, inner_offset.x, self.ui.dimen.h, self.outer_page_color)
bb:paintRect(x + self.ui.dimen.w - inner_offset.x - 1, y, inner_offset.x + 1, self.ui.dimen.h, self.outer_page_color)
end
-- draw content
self.ui.document:drawPage(
bb,
x + inner_offset.x,
y + inner_offset.y,
self.visible_area,
self.state.page,
self.state.zoom,
self.state.rotation)
end
function ReaderView:recalculate()
local page_size = self.ui.document:getPageDimensions(self.state.page, self.state.zoom, self.state.rotation)
-- TODO: bbox
self.page_area = page_size
-- reset our size
self.visible_area:setSizeTo(self.ui.dimen)
-- and recalculate it according to page size
self.visible_area:offsetWithin(self.page_area, 0, 0)
-- flag a repaint
UIManager:setDirty(self.dialog)
end
function ReaderView:onSetDimensions(dimensions)
-- recalculate view
self:recalculate()
end
function ReaderView:PanningUpdate(dx, dy)
debug("pan by", dx, dy)
local old = Geom:copy(self.visible_area)
self.visible_area:offsetWithin(self.page_area, dx, dy)
if self.visible_area ~= old then
-- flag a repaint
UIManager:setDirty(self.dialog)
debug(self.page_area)
debug(self.visible_area)
end
return true
end
function ReaderView:onPageUpdate(new_page_no)
self.state.page = new_page_no
self:recalculate()
end
function ReaderView:ZoomUpdate(zoom)
self.state.zoom = zoom
self:recalculate()
end
function ReaderView:onRotationUpdate(rotation)
self.state.rotation = rotation
self:recalculate()
end

@ -0,0 +1,90 @@
ReaderZooming = InputContainer:new{
key_events = {
ZoomIn = { { "Shift", Input.group.PgFwd }, doc = "zoom in", event = "Zoom", args = "in" },
ZoomOut = { { "Shift", Input.group.PgBack }, doc = "zoom out", event = "Zoom", args = "out" },
ZoomToFitPage = { {"A"}, doc = "zoom to fit page", event = "SetZoomMode", args = "page" },
ZoomToFitContent = { {"Shift", "A"}, doc = "zoom to fit content", event = "SetZoomMode", args = "content" },
ZoomToFitPageWidth = { {"S"}, doc = "zoom to fit page width", event = "SetZoomMode", args = "pagewidth" },
ZoomToFitContentWidth = { {"Shift", "S"}, doc = "zoom to fit content width", event = "SetZoomMode", args = "contentwidth" },
ZoomToFitPageHeight = { {"D"}, doc = "zoom to fit page height", event = "SetZoomMode", args = "pageheight" },
ZoomToFitContentHeight = { {"Shift", "D"}, doc = "zoom to fit content height", event = "SetZoomMode", args = "contentheight" },
},
zoom = 1.0,
zoom_mode = "free",
current_page = 1,
rotation = 0
}
function ReaderZooming:onSetDimensions(dimensions)
-- we were resized
self.dimen = dimensions
end
function ReaderZooming:onRotationUpdate(rotation)
self.rotation = rotation
self:setZoom()
end
function ReaderZooming:setZoom()
-- nothing to do in free zoom mode
if self.zoom_mode == "free" then
return
end
-- check if we're in bbox mode and work on bbox if that's the case
local page_size = {}
if self.zoom_mode == "content" or self.zoom_mode == "contentwidth" or self.zoom_mode == "content_height" then
-- TODO: enable this, still incomplete
page_size = self.ui.document:getUsedBBox(self.current_page)
self.view:handleEvent(Event:new("BBoxUpdate", page_size))
else
-- otherwise, operate on full page
page_size = self.ui.document:getNativePageDimensions(self.current_page)
end
-- calculate zoom value:
local zoom_w = self.dimen.w / page_size.w
local zoom_h = self.dimen.h / page_size.h
if self.rotation % 180 ~= 0 then
-- rotated by 90 or 270 degrees
zoom_w = self.dimen.w / page_size.h
zoom_h = self.dimen.h / page_size.w
end
if self.zoom_mode == "content" or self.zoom_mode == "page" then
if zoom_w < zoom_h then
self.zoom = zoom_w
else
self.zoom = zoom_h
end
elseif self.zoom_mode == "contentwidth" or self.zoom_mode == "pagewidth" then
self.zoom = zoom_w
elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then
self.zoom = zoom_h
end
self.view:ZoomUpdate(self.zoom)
end
function ReaderZooming:onZoom(direction)
debug("zoom", direction)
if direction == "in" then
self.zoom = self.zoom * 1.333333
elseif direction == "out" then
self.zoom = self.zoom * 0.75
end
debug("zoom is now at", self.zoom)
self:onSetZoomMode("free")
self.view:ZoomUpdate(self.zoom)
return true
end
function ReaderZooming:onSetZoomMode(what)
if self.zoom_mode ~= what then
debug("setting zoom mode to", what)
self.zoom_mode = what
self:setZoom()
end
return true
end
function ReaderZooming:onPageUpdate(new_page_no)
self.current_page = new_page_no
self:setZoom()
end
Loading…
Cancel
Save