mirror of
https://github.com/koreader/koreader
synced 2024-11-11 19:11:14 +00:00
318 lines
7.5 KiB
Lua
318 lines
7.5 KiB
Lua
require "ui/widget/base"
|
|
|
|
--[[
|
|
WidgetContainer is a container for another Widget
|
|
--]]
|
|
WidgetContainer = Widget:new()
|
|
|
|
function WidgetContainer:getSize()
|
|
if self.dimen then
|
|
-- fixed size
|
|
return self.dimen
|
|
elseif self[1] then
|
|
-- return size of first child widget
|
|
return self[1]:getSize()
|
|
else
|
|
return Geom:new{ w = 0, h = 0 }
|
|
end
|
|
end
|
|
|
|
--[[
|
|
delete all child widgets
|
|
--]]
|
|
function WidgetContainer:clear()
|
|
while table.remove(self) do end
|
|
end
|
|
|
|
function WidgetContainer:paintTo(bb, x, y)
|
|
-- default to pass request to first child widget
|
|
if self[1] then
|
|
return self[1]:paintTo(bb, x, y)
|
|
end
|
|
end
|
|
|
|
function WidgetContainer:propagateEvent(event)
|
|
-- propagate to children
|
|
for _, widget in ipairs(self) do
|
|
if widget:handleEvent(event) then
|
|
-- stop propagating when an event handler returns true
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--[[
|
|
Containers will pass events to children or react on them themselves
|
|
--]]
|
|
function WidgetContainer:handleEvent(event)
|
|
if not self:propagateEvent(event) then
|
|
-- call our own standard event handler
|
|
return Widget.handleEvent(self, event)
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
function WidgetContainer:free()
|
|
for _, widget in ipairs(self) do
|
|
if widget.free then widget:free() end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
BottomContainer contains its content (1 widget) at the bottom of its own
|
|
dimensions
|
|
--]]
|
|
BottomContainer = WidgetContainer:new()
|
|
|
|
function BottomContainer:paintTo(bb, x, y)
|
|
local contentSize = self[1]:getSize()
|
|
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
|
|
-- throw error? paint to scrap buffer and blit partially?
|
|
-- for now, we ignore this
|
|
end
|
|
self[1]:paintTo(bb,
|
|
x + (self.dimen.w - contentSize.w)/2,
|
|
y + (self.dimen.h - contentSize.h))
|
|
end
|
|
|
|
--[[
|
|
CenterContainer centers its content (1 widget) within its own dimensions
|
|
--]]
|
|
CenterContainer = WidgetContainer:new()
|
|
|
|
function CenterContainer:paintTo(bb, x, y)
|
|
local contentSize = self[1]:getSize()
|
|
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
|
|
-- throw error? paint to scrap buffer and blit partially?
|
|
-- for now, we ignore this
|
|
end
|
|
local x_pos = x
|
|
local y_pos = y
|
|
if self.ignore ~= "height" then
|
|
y_pos = y + (self.dimen.h - contentSize.h)/2
|
|
end
|
|
if self.ignore ~= "width" then
|
|
x_pos = x + (self.dimen.w - contentSize.w)/2
|
|
end
|
|
self[1]:paintTo(bb, x_pos, y_pos)
|
|
end
|
|
|
|
--[[
|
|
LeftContainer aligns its content (1 widget) at the left of its own dimensions
|
|
--]]
|
|
LeftContainer = WidgetContainer:new()
|
|
|
|
function LeftContainer:paintTo(bb, x, y)
|
|
local contentSize = self[1]:getSize()
|
|
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
|
|
-- throw error? paint to scrap buffer and blit partially?
|
|
-- for now, we ignore this
|
|
end
|
|
self[1]:paintTo(bb, x , y + (self.dimen.h - contentSize.h)/2)
|
|
end
|
|
|
|
--[[
|
|
RightContainer aligns its content (1 widget) at the right of its own dimensions
|
|
--]]
|
|
RightContainer = WidgetContainer:new()
|
|
|
|
function RightContainer:paintTo(bb, x, y)
|
|
local contentSize = self[1]:getSize()
|
|
if contentSize.w > self.dimen.w or contentSize.h > self.dimen.h then
|
|
-- throw error? paint to scrap buffer and blit partially?
|
|
-- for now, we ignore this
|
|
end
|
|
self[1]:paintTo(bb,
|
|
x + (self.dimen.w - contentSize.w),
|
|
y + (self.dimen.h - contentSize.h)/2)
|
|
end
|
|
|
|
--[[
|
|
A FrameContainer is some graphics content (1 widget) that is surrounded by a
|
|
frame
|
|
--]]
|
|
FrameContainer = WidgetContainer:new{
|
|
background = nil,
|
|
color = 15,
|
|
margin = 0,
|
|
radius = 0,
|
|
bordersize = 2,
|
|
padding = 5,
|
|
width = nil,
|
|
height = nil,
|
|
invert = false,
|
|
}
|
|
|
|
function FrameContainer:getSize()
|
|
local content_size = self[1]:getSize()
|
|
return Geom:new{
|
|
w = content_size.w + ( self.margin + self.bordersize + self.padding ) * 2,
|
|
h = content_size.h + ( self.margin + self.bordersize + self.padding ) * 2
|
|
}
|
|
end
|
|
|
|
function FrameContainer:paintTo(bb, x, y)
|
|
local my_size = self:getSize()
|
|
local container_width = self.width or my_size.w
|
|
local container_height = self.height or my_size.h
|
|
|
|
--@TODO get rid of margin here? 13.03 2013 (houqp)
|
|
if self.background then
|
|
bb:paintRoundedRect(x, y, container_width, container_height,
|
|
self.background, self.radius)
|
|
end
|
|
if self.bordersize > 0 then
|
|
bb:paintBorder(x + self.margin, y + self.margin,
|
|
container_width - self.margin * 2,
|
|
container_height - self.margin * 2,
|
|
self.bordersize, self.color, self.radius)
|
|
end
|
|
if self[1] then
|
|
self[1]:paintTo(bb,
|
|
x + self.margin + self.bordersize + self.padding,
|
|
y + self.margin + self.bordersize + self.padding)
|
|
end
|
|
if self.invert then
|
|
bb:invertRect(x, y, container_width, container_height)
|
|
end
|
|
end
|
|
|
|
|
|
--[[
|
|
an UnderlineContainer is a WidgetContainer that is able to paint
|
|
a line under its child node
|
|
--]]
|
|
|
|
UnderlineContainer = WidgetContainer:new{
|
|
linesize = 2,
|
|
padding = 1,
|
|
color = 0,
|
|
vertical_align = "top",
|
|
}
|
|
|
|
function UnderlineContainer:getSize()
|
|
if self.dimen then
|
|
return { w = self.dimen.w, h = self.dimen.h }
|
|
else
|
|
return self:getContentSize()
|
|
end
|
|
end
|
|
|
|
function UnderlineContainer:getContentSize()
|
|
local contentSize = self[1]:getSize()
|
|
return {
|
|
w = contentSize.w,
|
|
h = contentSize.h + self.linesize + self.padding
|
|
}
|
|
end
|
|
|
|
function UnderlineContainer:paintTo(bb, x, y)
|
|
local container_size = self:getSize()
|
|
local content_size = self:getContentSize()
|
|
local p_y = y
|
|
if self.vertical_align == "center" then
|
|
p_y = (container_size.h - content_size.h) / 2 + y
|
|
elseif self.vertical_align == "bottom" then
|
|
p_y = (container_size.h - content_size.h) + y
|
|
end
|
|
self[1]:paintTo(bb, x, p_y)
|
|
bb:paintRect(x, y + container_size.h - self.linesize,
|
|
container_size.w, self.linesize, self.color)
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
an InputContainer is an WidgetContainer that handles input events
|
|
|
|
an example for a key_event is this:
|
|
|
|
PanBy20 = {
|
|
{ "Shift", Input.group.Cursor },
|
|
seqtext = "Shift+Cursor",
|
|
doc = "pan by 20px",
|
|
event = "Pan", args = 20, is_inactive = true,
|
|
},
|
|
PanNormal = {
|
|
{ Input.group.Cursor },
|
|
seqtext = "Cursor",
|
|
doc = "pan by 10 px", event = "Pan", args = 10,
|
|
},
|
|
Quit = { {"Home"} },
|
|
|
|
it is suggested to reference configurable sequences from another table
|
|
and store that table as configuration setting
|
|
--]]
|
|
InputContainer = WidgetContainer:new{
|
|
vertical_align = "top",
|
|
}
|
|
|
|
function InputContainer:_init()
|
|
-- we need to do deep copy here
|
|
local new_key_events = {}
|
|
if self.key_events then
|
|
for k,v in pairs(self.key_events) do
|
|
new_key_events[k] = v
|
|
end
|
|
end
|
|
self.key_events = new_key_events
|
|
|
|
local new_ges_events = {}
|
|
if self.ges_events then
|
|
for k,v in pairs(self.ges_events) do
|
|
new_ges_events[k] = v
|
|
end
|
|
end
|
|
self.ges_events = new_ges_events
|
|
|
|
if not self.dimen then
|
|
self.dimen = Geom:new{}
|
|
end
|
|
end
|
|
|
|
function InputContainer:paintTo(bb, x, y)
|
|
self.dimen.x = x
|
|
self.dimen.y = y
|
|
if self[1] then
|
|
if self.vertical_align == "center" then
|
|
local content_size = self[1]:getSize()
|
|
self[1]:paintTo(bb, x, y + (self.dimen.h - content_size.h)/2)
|
|
else
|
|
self[1]:paintTo(bb, x, y)
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
the following handler handles keypresses and checks if they lead to a command.
|
|
if this is the case, we retransmit another event within ourselves
|
|
--]]
|
|
function InputContainer:onKeyPress(key)
|
|
for name, seq in pairs(self.key_events) do
|
|
if not seq.is_inactive then
|
|
for _, oneseq in ipairs(seq) do
|
|
if key:match(oneseq) then
|
|
local eventname = seq.event or name
|
|
return self:handleEvent(Event:new(eventname, seq.args, key))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function InputContainer:onGesture(ev)
|
|
for name, gsseq in pairs(self.ges_events) do
|
|
for _, gs_range in ipairs(gsseq) do
|
|
--DEBUG("gs_range", gs_range)
|
|
if gs_range:match(ev) then
|
|
local eventname = gsseq.event or name
|
|
return self:handleEvent(Event:new(eventname, gsseq.args, ev))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|