mirror of https://github.com/koreader/koreader
commit
4f1743826c
@ -0,0 +1,19 @@
|
||||
--[[
|
||||
Inheritable abstraction for cache items
|
||||
--]]
|
||||
|
||||
local CacheItem = {
|
||||
size = 64, -- some reasonable default for simple Lua values / small tables
|
||||
}
|
||||
|
||||
function CacheItem:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function CacheItem:onFree()
|
||||
end
|
||||
|
||||
return CacheItem
|
@ -0,0 +1,36 @@
|
||||
--[[
|
||||
This is a registry for document providers
|
||||
]]--
|
||||
local DocumentRegistry = {
|
||||
providers = { }
|
||||
}
|
||||
|
||||
function DocumentRegistry:addProvider(extension, mimetype, provider)
|
||||
table.insert(self.providers, { extension = extension, mimetype = mimetype, provider = provider })
|
||||
end
|
||||
|
||||
function DocumentRegistry:getProvider(file)
|
||||
-- TODO: some implementation based on mime types?
|
||||
local extension = string.lower(string.match(file, ".+%.([^.]+)") or "")
|
||||
for _, provider in ipairs(self.providers) do
|
||||
if extension == provider.extension then
|
||||
return provider.provider
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DocumentRegistry:openDocument(file)
|
||||
local provider = self:getProvider(file)
|
||||
if provider ~= nil then
|
||||
return provider:new{file = file}
|
||||
end
|
||||
end
|
||||
|
||||
-- load implementations:
|
||||
|
||||
require("document/pdfdocument"):register(DocumentRegistry)
|
||||
require("document/djvudocument"):register(DocumentRegistry)
|
||||
require("document/credocument"):register(DocumentRegistry)
|
||||
require("document/picdocument"):register(DocumentRegistry)
|
||||
|
||||
return DocumentRegistry
|
@ -0,0 +1,13 @@
|
||||
local CacheItem = require("cacheitem")
|
||||
local DEBUG = require("dbg")
|
||||
|
||||
local TileCacheItem = CacheItem:new{}
|
||||
|
||||
function TileCacheItem:onFree()
|
||||
if self.bb.free then
|
||||
DEBUG("free blitbuffer", self.bb)
|
||||
self.bb:free()
|
||||
end
|
||||
end
|
||||
|
||||
return TileCacheItem
|
@ -1,11 +1,16 @@
|
||||
|
||||
lua_gettext.init("./i18n", "koreader")
|
||||
|
||||
local GetText = {}
|
||||
local GetText_mt = {}
|
||||
|
||||
function _(string)
|
||||
function GetText_mt.__call(gettext, string)
|
||||
return lua_gettext.translate(string)
|
||||
end
|
||||
|
||||
function gettextChangeLang(new_lang)
|
||||
function GetText.changeLang(new_lang)
|
||||
lua_gettext.change_lang(new_lang)
|
||||
end
|
||||
|
||||
setmetatable(GetText, GetText_mt)
|
||||
|
||||
return GetText
|
||||
|
@ -1,49 +1,54 @@
|
||||
local _ = require("gettext")
|
||||
|
||||
SCREEN_MODE_STR = _("Screen Mode")
|
||||
PAGE_CROP_STR = _("Page Crop")
|
||||
FULL_SCREEN_STR = _("Full Screen")
|
||||
SCROLL_MODE_STR = _("Scroll Mode")
|
||||
PAGE_MARGIN_STR = _("Page Margin")
|
||||
LINE_SPACING_STR = _("Line Spacing")
|
||||
COLUMNS_STR = _("Columns")
|
||||
TEXT_ALIGN_STR = _("Text Align")
|
||||
FONTSIZE_FINE_TUNING_STR = _("Fine Tuning")
|
||||
CONTRAST_STR = _("Contrast")
|
||||
REFLOW_STR = _("Reflow")
|
||||
DOC_LANG_STR = _("Document Language")
|
||||
VERTICAL_TEXT_STR = _("Vertical Text")
|
||||
WORD_GAP_STR = _("Word Gap")
|
||||
DEFECT_SIZE_STR = _("Defect Size")
|
||||
RENDER_QUALITY_STR = _("Render Quality")
|
||||
AUTO_STRAIGHTEN_STR = _("Auto Straighten")
|
||||
INDENTATION_STR = _("Indentation")
|
||||
FONT_WEIGHT_STR = _("Font weight")
|
||||
GAMMA_STR = _("Gamma")
|
||||
VIEW_MODE_STR = _("View mode")
|
||||
EMBEDDED_STYLE_STR = _("Embedded style")
|
||||
local S = {}
|
||||
|
||||
ON_STR = _("on")
|
||||
OFF_STR = _("off")
|
||||
AUTO_STR = _("auto")
|
||||
MANUAL_STR = _("manual")
|
||||
SEMIAUTO_STR = _("semi-auto")
|
||||
SMALL_STR = _("small")
|
||||
MEDIUM_STR = _("medium")
|
||||
LARGE_STR = _("large")
|
||||
DECREASE_STR = _("decrease")
|
||||
INCREASE_STR = _("increase")
|
||||
LIGHTEST_STR = _("lightest")
|
||||
LIGHTER_STR = _("lighter")
|
||||
DEFAULT_STR = _("default")
|
||||
DARKER_STR = _("darker")
|
||||
DARKEST_STR = _("darkest")
|
||||
LOW_STR = _("low")
|
||||
HIGH_STR = _("high")
|
||||
ZERO_DEG_STR = _("0 deg")
|
||||
FIVE_DEG_STR = _("5 deg")
|
||||
TEN_DEG_STR = _("10 deg")
|
||||
PORTRAIT_STR = _("portrait")
|
||||
LANDSCAPE_STR = _("landscape")
|
||||
TOGGLE_BOLD_STR = _("toggle bold")
|
||||
VIEW_SCROLL_STR = _("scroll")
|
||||
VIEW_PAGE_STR = _("page")
|
||||
S.SCREEN_MODE = _("Screen Mode")
|
||||
S.PAGE_CROP = _("Page Crop")
|
||||
S.FULL_SCREEN = _("Full Screen")
|
||||
S.SCROLL_MODE = _("Scroll Mode")
|
||||
S.PAGE_MARGIN = _("Page Margin")
|
||||
S.LINE_SPACING = _("Line Spacing")
|
||||
S.COLUMNS = _("Columns")
|
||||
S.TEXT_ALIGN = _("Text Align")
|
||||
S.FONTSIZE_FINE_TUNING = _("Fine Tuning")
|
||||
S.CONTRAST = _("Contrast")
|
||||
S.REFLOW = _("Reflow")
|
||||
S.DOC_LANG = _("Document Language")
|
||||
S.VERTICAL_TEXT = _("Vertical Text")
|
||||
S.WORD_GAP = _("Word Gap")
|
||||
S.DEFECT_SIZE = _("Defect Size")
|
||||
S.RENDER_QUALITY = _("Render Quality")
|
||||
S.AUTO_STRAIGHTEN = _("Auto Straighten")
|
||||
S.INDENTATION = _("Indentation")
|
||||
S.FONT_WEIGHT = _("Font weight")
|
||||
S.GAMMA = _("Gamma")
|
||||
S.VIEW_MODE = _("View mode")
|
||||
S.EMBEDDED_STYLE = _("Embedded style")
|
||||
|
||||
S.ON = _("on")
|
||||
S.OFF = _("off")
|
||||
S.AUTO = _("auto")
|
||||
S.MANUAL = _("manual")
|
||||
S.SEMIAUTO = _("semi-auto")
|
||||
S.SMALL = _("small")
|
||||
S.MEDIUM = _("medium")
|
||||
S.LARGE = _("large")
|
||||
S.DECREASE = _("decrease")
|
||||
S.INCREASE = _("increase")
|
||||
S.LIGHTEST = _("lightest")
|
||||
S.LIGHTER = _("lighter")
|
||||
S.DEFAULT = _("default")
|
||||
S.DARKER = _("darker")
|
||||
S.DARKEST = _("darkest")
|
||||
S.LOW = _("low")
|
||||
S.HIGH = _("high")
|
||||
S.ZERO_DEG = _("0 deg")
|
||||
S.FIVE_DEG = _("5 deg")
|
||||
S.TEN_DEG = _("10 deg")
|
||||
S.PORTRAIT = _("portrait")
|
||||
S.LANDSCAPE = _("landscape")
|
||||
S.TOGGLE_BOLD = _("toggle bold")
|
||||
S.VIEW_SCROLL = _("scroll")
|
||||
S.VIEW_PAGE = _("page")
|
||||
|
||||
return S
|
||||
|
@ -0,0 +1,17 @@
|
||||
local BaseFrontLight = {
|
||||
min = 1, max = 10,
|
||||
intensity = nil,
|
||||
}
|
||||
|
||||
function BaseFrontLight:init() end
|
||||
function BaseFrontLight:toggle() end
|
||||
function BaseFrontLight:setIntensityHW() end
|
||||
|
||||
function BaseFrontLight:setIntensity(intensity)
|
||||
intensity = intensity < self.min and self.min or intensity
|
||||
intensity = intensity > self.max and self.max or intensity
|
||||
self.intensity = intensity
|
||||
self:setIntensityHW()
|
||||
end
|
||||
|
||||
return BaseFrontLight
|
@ -0,0 +1,38 @@
|
||||
local BaseFrontLight = require("ui/device/basefrontlight")
|
||||
-- liblipclua, see require below
|
||||
|
||||
local KindleFrontLight = {
|
||||
min = 0, max = 24,
|
||||
kpw_fl = "/sys/devices/system/fl_tps6116x/fl_tps6116x0/fl_intensity",
|
||||
intensity = nil,
|
||||
lipc_handle = nil,
|
||||
}
|
||||
|
||||
function KindleFrontLight:init()
|
||||
require "liblipclua"
|
||||
self.lipc_handle = lipc.init("com.github.koreader")
|
||||
if self.lipc_handle then
|
||||
self.intensity = self.lipc_handle:get_int_property("com.lab126.powerd", "flIntensity")
|
||||
end
|
||||
end
|
||||
|
||||
function KindleFrontLight:toggle()
|
||||
local f = io.open(self.kpw_fl, "r")
|
||||
local sysint = tonumber(f:read("*all"):match("%d+"))
|
||||
f:close()
|
||||
if sysint == 0 then
|
||||
self:setIntensity(self.intensity)
|
||||
else
|
||||
os.execute("echo -n 0 > " .. self.kpw_fl)
|
||||
end
|
||||
end
|
||||
|
||||
KindleFrontLight.setIntensity = BaseFrontLight.setIntensity
|
||||
|
||||
function KindleFrontLight:setIntensityHW()
|
||||
if self.lipc_handle ~= nil then
|
||||
self.lipc_handle:set_int_property("com.lab126.powerd", "flIntensity", self.intensity)
|
||||
end
|
||||
end
|
||||
|
||||
return KindleFrontLight
|
@ -0,0 +1,28 @@
|
||||
local BaseFrontLight = require("ui/device/basefrontlight")
|
||||
|
||||
local KoboFrontLight = {
|
||||
min = 1, max = 100,
|
||||
intensity = 20,
|
||||
restore_settings = true,
|
||||
fl = nil,
|
||||
}
|
||||
|
||||
function KoboFrontLight:init()
|
||||
self.fl = kobolight.open()
|
||||
end
|
||||
|
||||
function KoboFrontLight:toggle()
|
||||
if self.fl ~= nil then
|
||||
self.fl:toggle()
|
||||
end
|
||||
end
|
||||
|
||||
KoboFrontLight.setIntensity = BaseFrontLight.setIntensity
|
||||
|
||||
function KoboFrontLight:setIntensityHW()
|
||||
if self.fl ~= nil then
|
||||
self.fl:setBrightness(self.intensity)
|
||||
end
|
||||
end
|
||||
|
||||
return KoboFrontLight
|
@ -0,0 +1,212 @@
|
||||
local Geom = require("ui/geometry")
|
||||
local DEBUG = require("dbg")
|
||||
|
||||
-- Blitbuffer
|
||||
-- einkfb
|
||||
|
||||
--[[
|
||||
Codes for rotation modes:
|
||||
|
||||
1 for no rotation,
|
||||
2 for landscape with bottom on the right side of screen, etc.
|
||||
|
||||
2
|
||||
+--------------+
|
||||
| +----------+ |
|
||||
| | | |
|
||||
| | Freedom! | |
|
||||
| | | |
|
||||
| | | |
|
||||
3 | | | | 1
|
||||
| | | |
|
||||
| | | |
|
||||
| +----------+ |
|
||||
| |
|
||||
| |
|
||||
+--------------+
|
||||
0
|
||||
--]]
|
||||
|
||||
|
||||
local Screen = {
|
||||
width = 0,
|
||||
height = 0,
|
||||
native_rotation_mode = nil,
|
||||
cur_rotation_mode = 0,
|
||||
|
||||
bb = nil,
|
||||
saved_bb = nil,
|
||||
|
||||
fb = einkfb.open("/dev/fb0"),
|
||||
-- will be set upon loading by Device class:
|
||||
device = nil,
|
||||
}
|
||||
|
||||
function Screen:init()
|
||||
-- for unknown strange reason, pitch*2 is 10 px more than screen width in KPW
|
||||
self.width, self.height = self.fb:getSize()
|
||||
-- Blitbuffer still uses inverted 4bpp bitmap, so pitch should be
|
||||
-- (self.width / 2)
|
||||
self.bb = Blitbuffer.new(self.width, self.height, self.width/2)
|
||||
if self.width > self.height then
|
||||
-- For another unknown strange reason, self.fb:getOrientation always
|
||||
-- return 0 in KPW, even though we are in landscape mode.
|
||||
-- Seems like the native framework change framebuffer on the fly when
|
||||
-- starting booklet. Starting KPV from ssh and KPVBooklet will get
|
||||
-- different framebuffer height and width.
|
||||
--
|
||||
--self.native_rotation_mode = self.fb:getOrientation()
|
||||
self.native_rotation_mode = 1
|
||||
else
|
||||
self.native_rotation_mode = 0
|
||||
end
|
||||
self.cur_rotation_mode = self.native_rotation_mode
|
||||
end
|
||||
|
||||
function Screen:refresh(refesh_type, waveform_mode, x, y, w, h)
|
||||
if x then x = x < 0 and 0 or math.floor(x) end
|
||||
if y then y = y < 0 and 0 or math.floor(y) end
|
||||
if w then w = w + x > self.width and self.width - x or math.ceil(w) end
|
||||
if h then h = h + y > self.height and self.height - y or math.ceil(h) end
|
||||
if self.native_rotation_mode == self.cur_rotation_mode then
|
||||
self.fb.bb:blitFrom(self.bb, 0, 0, 0, 0, self.width, self.height)
|
||||
elseif self.native_rotation_mode == 0 and self.cur_rotation_mode == 1 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 270)
|
||||
if x and y and w and h then
|
||||
x, y = y, self.width - w - x
|
||||
w, h = h, w
|
||||
end
|
||||
elseif self.native_rotation_mode == 0 and self.cur_rotation_mode == 3 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 90)
|
||||
if x and y and w and h then
|
||||
x, y = self.height - h - y, x
|
||||
w, h = h, w
|
||||
end
|
||||
elseif self.native_rotation_mode == 1 and self.cur_rotation_mode == 0 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 90)
|
||||
if x and y and w and h then
|
||||
x, y = self.height - h - y, x
|
||||
w, h = h, w
|
||||
end
|
||||
elseif self.native_rotation_mode == 1 and self.cur_rotation_mode == 3 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 180)
|
||||
if x and y and w and h then
|
||||
x, y = self.width - w - x, self.height - h - y
|
||||
end
|
||||
end
|
||||
self.fb:refresh(refesh_type, waveform_mode, x, y, w, h)
|
||||
end
|
||||
|
||||
function Screen:getSize()
|
||||
return Geom:new{w = self.width, h = self.height}
|
||||
end
|
||||
|
||||
function Screen:getWidth()
|
||||
return self.width
|
||||
end
|
||||
|
||||
function Screen:getHeight()
|
||||
return self.height
|
||||
end
|
||||
|
||||
function Screen:getDPI()
|
||||
if(self.device:getModel() == "KindlePaperWhite") or (self.device:getModel() == "Kobo_kraken") then
|
||||
return 212
|
||||
elseif self.device:getModel() == "Kobo_dragon" then
|
||||
return 265
|
||||
elseif self.device:getModel() == "Kobo_pixie" then
|
||||
return 200
|
||||
else
|
||||
return 167
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:scaleByDPI(px)
|
||||
return math.floor(px * self:getDPI()/167)
|
||||
end
|
||||
|
||||
function Screen:rescaleByDPI(px)
|
||||
return math.ceil(px * 167/self:getDPI())
|
||||
end
|
||||
|
||||
function Screen:getPitch()
|
||||
return self.fb:getPitch()
|
||||
end
|
||||
|
||||
function Screen:getNativeRotationMode()
|
||||
-- in EMU mode, you will always get 0 from getOrientation()
|
||||
return self.fb:getOrientation()
|
||||
end
|
||||
|
||||
function Screen:getRotationMode()
|
||||
return self.cur_rotation_mode
|
||||
end
|
||||
|
||||
function Screen:getScreenMode()
|
||||
if self.width > self.height then
|
||||
return "landscape"
|
||||
else
|
||||
return "portrait"
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:setRotationMode(mode)
|
||||
if mode > 3 or mode < 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- mode 0 and mode 2 has the same width and height, so do mode 1 and 3
|
||||
if (self.cur_rotation_mode % 2) ~= (mode % 2) then
|
||||
self.width, self.height = self.height, self.width
|
||||
end
|
||||
self.cur_rotation_mode = mode
|
||||
self.bb:free()
|
||||
self.bb = Blitbuffer.new(self.width, self.height, self.width/2)
|
||||
end
|
||||
|
||||
function Screen:setScreenMode(mode)
|
||||
if mode == "portrait" then
|
||||
if self.cur_rotation_mode ~= 0 then
|
||||
self:setRotationMode(0)
|
||||
end
|
||||
elseif mode == "landscape" then
|
||||
if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then
|
||||
self:setRotationMode(1)
|
||||
elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then
|
||||
self:setRotationMode((self.cur_rotation_mode + 2) % 4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:saveCurrentBB()
|
||||
local width, height = self:getWidth(), self:getHeight()
|
||||
|
||||
if not self.saved_bb then
|
||||
self.saved_bb = Blitbuffer.new(width, height, self.width/2)
|
||||
end
|
||||
if self.saved_bb:getWidth() ~= width then
|
||||
self.saved_bb:free()
|
||||
self.saved_bb = Blitbuffer.new(width, height, self.width/2)
|
||||
end
|
||||
self.saved_bb:blitFullFrom(self.bb)
|
||||
end
|
||||
|
||||
function Screen:restoreFromSavedBB()
|
||||
self:restoreFromBB(self.saved_bb)
|
||||
end
|
||||
|
||||
function Screen:getCurrentScreenBB()
|
||||
local bb = Blitbuffer.new(self:getWidth(), self:getHeight())
|
||||
bb:blitFullFrom(self.bb)
|
||||
return bb
|
||||
end
|
||||
|
||||
function Screen:restoreFromBB(bb)
|
||||
if bb then
|
||||
self.bb:blitFullFrom(bb)
|
||||
else
|
||||
DEBUG("Got nil bb in restoreFromSavedBB!")
|
||||
end
|
||||
end
|
||||
|
||||
return Screen
|
@ -0,0 +1,45 @@
|
||||
local TimeVal = require("ui/timeval")
|
||||
|
||||
local GestureRange = {
|
||||
ges = nil,
|
||||
-- spatial range limits the gesture emitting position
|
||||
range = nil,
|
||||
-- temproal range limits the gesture emitting rate
|
||||
rate = nil,
|
||||
-- span limits of this gesture
|
||||
scale = nil,
|
||||
}
|
||||
|
||||
function GestureRange:new(o)
|
||||
local o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function GestureRange:match(gs)
|
||||
if gs.ges ~= self.ges then
|
||||
return false
|
||||
end
|
||||
if self.range then
|
||||
if not self.range:contains(gs.pos) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.rate then
|
||||
local last_time = self.last_time or TimeVal:new{}
|
||||
if gs.time - last_time > TimeVal:new{usec = 1000000 / self.rate} then
|
||||
self.last_time = gs.time
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.scale then
|
||||
if self.scale[1] > gs.span or self.scale[2] < gs.span then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return GestureRange
|
@ -1,181 +0,0 @@
|
||||
--[[
|
||||
Draw a border
|
||||
|
||||
@x: start position in x axis
|
||||
@y: start position in y axis
|
||||
@w: width of the border
|
||||
@h: height of the border
|
||||
@bw: line width of the border
|
||||
@c: color for loading bar
|
||||
@r: radius of for border's corner (nil or 0 means right corner border)
|
||||
--]]
|
||||
function blitbuffer.paintBorder(bb, x, y, w, h, bw, c, r)
|
||||
x, y = math.ceil(x), math.ceil(y)
|
||||
h, w = math.ceil(h), math.ceil(w)
|
||||
if not r or r == 0 then
|
||||
bb:paintRect(x, y, w, bw, c)
|
||||
bb:paintRect(x, y+h-bw, w, bw, c)
|
||||
bb:paintRect(x, y+bw, bw, h - 2*bw, c)
|
||||
bb:paintRect(x+w-bw, y+bw, bw, h - 2*bw, c)
|
||||
else
|
||||
if h < 2*r then r = math.floor(h/2) end
|
||||
if w < 2*r then r = math.floor(w/2) end
|
||||
bb:paintRoundedCorner(x, y, w, h, bw, r, c)
|
||||
bb:paintRect(r+x, y, w-2*r, bw, c)
|
||||
bb:paintRect(r+x, y+h-bw, w-2*r, bw, c)
|
||||
bb:paintRect(x, r+y, bw, h-2*r, c)
|
||||
bb:paintRect(x+w-bw, r+y, bw, h-2*r, c)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Fill a rounded corner rectangular area
|
||||
|
||||
@x: start position in x axis
|
||||
@y: start position in y axis
|
||||
@w: width of the area
|
||||
@h: height of the area
|
||||
@c: color used to fill the area
|
||||
@r: radius of for four corners
|
||||
--]]
|
||||
function blitbuffer.paintRoundedRect(bb, x, y, w, h, c, r)
|
||||
x, y = math.ceil(x), math.ceil(y)
|
||||
h, w = math.ceil(h), math.ceil(w)
|
||||
if not r or r == 0 then
|
||||
bb:paintRect(x, y, w, h, c)
|
||||
else
|
||||
if h < 2*r then r = math.floor(h/2) end
|
||||
if w < 2*r then r = math.floor(w/2) end
|
||||
bb:paintBorder(x, y, w, h, r, c, r)
|
||||
bb:paintRect(x+r, y+r, w-2*r, h-2*r, c)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Draw a progress bar according to following args:
|
||||
|
||||
@x: start position in x axis
|
||||
@y: start position in y axis
|
||||
@w: width for progress bar
|
||||
@h: height for progress bar
|
||||
@load_m_w: width margin for loading bar
|
||||
@load_m_h: height margin for loading bar
|
||||
@load_percent: progress in percent
|
||||
@c: color for loading bar
|
||||
--]]
|
||||
function blitbuffer.progressBar(bb, x, y, w, h,
|
||||
load_m_w, load_m_h, load_percent, c)
|
||||
if load_m_h*2 > h then
|
||||
load_m_h = h/2
|
||||
end
|
||||
bb:paintBorder(x, y, w, h, 2, 15)
|
||||
bb:paintRect(x+load_m_w, y+load_m_h,
|
||||
(w-2*load_m_w)*load_percent, (h-2*load_m_h), c)
|
||||
end
|
||||
|
||||
|
||||
|
||||
------------------------------------------------
|
||||
-- Start of Cursor class
|
||||
------------------------------------------------
|
||||
|
||||
Cursor = {
|
||||
x_pos = 0,
|
||||
y_pos = 0,
|
||||
--color = 15,
|
||||
h = 10,
|
||||
w = nil,
|
||||
line_w = nil,
|
||||
is_cleared = true,
|
||||
}
|
||||
|
||||
function Cursor:new(o)
|
||||
o = o or {}
|
||||
o.x_pos = o.x_pos or self.x_pos
|
||||
o.y_pos = o.y_pos or self.y_pos
|
||||
o.line_width_factor = o.line_width_factor or 10
|
||||
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
o:setHeight(o.h or self.h)
|
||||
return o
|
||||
end
|
||||
|
||||
function Cursor:setHeight(h)
|
||||
self.h = h
|
||||
self.w = self.h / 3
|
||||
self.line_w = math.floor(self.h / self.line_width_factor)
|
||||
end
|
||||
|
||||
function Cursor:_draw(x, y)
|
||||
local up_down_width = math.floor(self.line_w / 2)
|
||||
local body_h = self.h - (up_down_width * 2)
|
||||
-- paint upper horizontal line
|
||||
fb.bb:invertRect(x, y, self.w, up_down_width)
|
||||
-- paint middle vertical line
|
||||
fb.bb:invertRect(x + (self.w / 2) - up_down_width, y + up_down_width,
|
||||
self.line_w, body_h)
|
||||
-- paint lower horizontal line
|
||||
fb.bb:invertRect(x, y + body_h + up_down_width, self.w, up_down_width)
|
||||
end
|
||||
|
||||
function Cursor:draw()
|
||||
if self.is_cleared then
|
||||
self.is_cleared = false
|
||||
self:_draw(self.x_pos, self.y_pos)
|
||||
end
|
||||
end
|
||||
|
||||
function Cursor:clear()
|
||||
if not self.is_cleared then
|
||||
self.is_cleared = true
|
||||
self:_draw(self.x_pos, self.y_pos)
|
||||
end
|
||||
end
|
||||
|
||||
function Cursor:move(x_off, y_off)
|
||||
self.x_pos = self.x_pos + x_off
|
||||
self.y_pos = self.y_pos + y_off
|
||||
end
|
||||
|
||||
function Cursor:moveHorizontal(x_off)
|
||||
self.x_pos = self.x_pos + x_off
|
||||
end
|
||||
|
||||
function Cursor:moveVertical(x_off)
|
||||
self.y_pos = self.y_pos + y_off
|
||||
end
|
||||
|
||||
function Cursor:moveAndDraw(x_off, y_off)
|
||||
self:clear()
|
||||
self:move(x_off, y_off)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveTo(x_pos, y_pos)
|
||||
self.x_pos = x_pos
|
||||
self.y_pos = y_pos
|
||||
end
|
||||
|
||||
function Cursor:moveToAndDraw(x_pos, y_pos)
|
||||
self:clear()
|
||||
self.x_pos = x_pos
|
||||
self.y_pos = y_pos
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveHorizontalAndDraw(x_off)
|
||||
self:clear()
|
||||
self:move(x_off, 0)
|
||||
self:draw()
|
||||
end
|
||||
|
||||
function Cursor:moveVerticalAndDraw(y_off)
|
||||
self:clear()
|
||||
self:move(0, y_off)
|
||||
self:draw()
|
||||
end
|
||||
|
@ -0,0 +1,49 @@
|
||||
local Configurable = {}
|
||||
|
||||
function Configurable:hash(sep)
|
||||
local hash = ""
|
||||
local excluded = {multi_threads = true,}
|
||||
for key,value in pairs(self) do
|
||||
if type(value) == "number" or type(value) == "string"
|
||||
and not excluded[key] then
|
||||
hash = hash..sep..value
|
||||
end
|
||||
end
|
||||
return hash
|
||||
end
|
||||
|
||||
function Configurable:loadDefaults(config_options)
|
||||
for i=1,#config_options do
|
||||
local options = config_options[i].options
|
||||
for j=1,#config_options[i].options do
|
||||
local key = config_options[i].options[j].name
|
||||
self[key] = config_options[i].options[j].default_value
|
||||
if not self[key] then
|
||||
self[key] = config_options[i].options[j].default_arg
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Configurable:loadSettings(settings, prefix)
|
||||
for key,value in pairs(self) do
|
||||
if type(value) == "number" or type(value) == "string"
|
||||
or type(value) == "table" then
|
||||
local saved_value = settings:readSetting(prefix..key)
|
||||
self[key] = (saved_value == nil) and self[key] or saved_value
|
||||
--Debug("Configurable:loadSettings", "key", key, "saved value", saved_value,"Configurable.key", self[key])
|
||||
end
|
||||
end
|
||||
--Debug("loaded config:", dump(Configurable))
|
||||
end
|
||||
|
||||
function Configurable:saveSettings(settings, prefix)
|
||||
for key,value in pairs(self) do
|
||||
if type(value) == "number" or type(value) == "string"
|
||||
or type(value) == "table" then
|
||||
settings:saveSetting(prefix..key, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Configurable
|
@ -1,231 +1,2 @@
|
||||
--[[
|
||||
Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]--
|
||||
|
||||
--[[
|
||||
Codes for rotation modes:
|
||||
|
||||
1 for no rotation,
|
||||
2 for landscape with bottom on the right side of screen, etc.
|
||||
|
||||
2
|
||||
+--------------+
|
||||
| +----------+ |
|
||||
| | | |
|
||||
| | Freedom! | |
|
||||
| | | |
|
||||
| | | |
|
||||
3 | | | | 1
|
||||
| | | |
|
||||
| | | |
|
||||
| +----------+ |
|
||||
| |
|
||||
| |
|
||||
+--------------+
|
||||
0
|
||||
--]]
|
||||
|
||||
|
||||
Screen = {
|
||||
width = 0,
|
||||
height = 0,
|
||||
native_rotation_mode = nil,
|
||||
cur_rotation_mode = 0,
|
||||
|
||||
bb = nil,
|
||||
saved_bb = nil,
|
||||
|
||||
fb = einkfb.open("/dev/fb0"),
|
||||
}
|
||||
|
||||
function Screen:init()
|
||||
-- for unknown strange reason, pitch*2 is 10 px more than screen width in KPW
|
||||
self.width, self.height = self.fb:getSize()
|
||||
-- Blitbuffer still uses inverted 4bpp bitmap, so pitch should be
|
||||
-- (self.width / 2)
|
||||
self.bb = Blitbuffer.new(self.width, self.height, self.width/2)
|
||||
if self.width > self.height then
|
||||
-- For another unknown strange reason, self.fb:getOrientation always
|
||||
-- return 0 in KPW, even though we are in landscape mode.
|
||||
-- Seems like the native framework change framebuffer on the fly when
|
||||
-- starting booklet. Starting KPV from ssh and KPVBooklet will get
|
||||
-- different framebuffer height and width.
|
||||
--
|
||||
--self.native_rotation_mode = self.fb:getOrientation()
|
||||
self.native_rotation_mode = 1
|
||||
else
|
||||
self.native_rotation_mode = 0
|
||||
end
|
||||
self.cur_rotation_mode = self.native_rotation_mode
|
||||
end
|
||||
|
||||
function Screen:refresh(refesh_type, waveform_mode, x, y, w, h)
|
||||
if x then x = x < 0 and 0 or math.floor(x) end
|
||||
if y then y = y < 0 and 0 or math.floor(y) end
|
||||
if w then w = w + x > self.width and self.width - x or math.ceil(w) end
|
||||
if h then h = h + y > self.height and self.height - y or math.ceil(h) end
|
||||
if self.native_rotation_mode == self.cur_rotation_mode then
|
||||
self.fb.bb:blitFrom(self.bb, 0, 0, 0, 0, self.width, self.height)
|
||||
elseif self.native_rotation_mode == 0 and self.cur_rotation_mode == 1 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 270)
|
||||
if x and y and w and h then
|
||||
x, y = y, self.width - w - x
|
||||
w, h = h, w
|
||||
end
|
||||
elseif self.native_rotation_mode == 0 and self.cur_rotation_mode == 3 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 90)
|
||||
if x and y and w and h then
|
||||
x, y = self.height - h - y, x
|
||||
w, h = h, w
|
||||
end
|
||||
elseif self.native_rotation_mode == 1 and self.cur_rotation_mode == 0 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 90)
|
||||
if x and y and w and h then
|
||||
x, y = self.height - h - y, x
|
||||
w, h = h, w
|
||||
end
|
||||
elseif self.native_rotation_mode == 1 and self.cur_rotation_mode == 3 then
|
||||
self.fb.bb:blitFromRotate(self.bb, 180)
|
||||
if x and y and w and h then
|
||||
x, y = self.width - w - x, self.height - h - y
|
||||
end
|
||||
end
|
||||
self.fb:refresh(refesh_type, waveform_mode, x, y, w, h)
|
||||
end
|
||||
|
||||
function Screen:getSize()
|
||||
return Geom:new{w = self.width, h = self.height}
|
||||
end
|
||||
|
||||
function Screen:getWidth()
|
||||
return self.width
|
||||
end
|
||||
|
||||
function Screen:getHeight()
|
||||
return self.height
|
||||
end
|
||||
|
||||
function Screen:getDPI()
|
||||
if(Device:getModel() == "KindlePaperWhite") or (Device:getModel() == "Kobo_kraken") then
|
||||
return 212
|
||||
elseif Device:getModel() == "Kobo_dragon" then
|
||||
return 265
|
||||
elseif Device:getModel() == "Kobo_pixie" then
|
||||
return 200
|
||||
else
|
||||
return 167
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:scaleByDPI(px)
|
||||
return math.floor(px * self:getDPI()/167)
|
||||
end
|
||||
|
||||
function Screen:rescaleByDPI(px)
|
||||
return math.ceil(px * 167/self:getDPI())
|
||||
end
|
||||
|
||||
-- make a shortcut to Screen:scaleByDPI
|
||||
function scaleByDPI(px)
|
||||
return Screen:scaleByDPI(px)
|
||||
end
|
||||
|
||||
-- make a shortcut to Screen:rescaleByDPI
|
||||
function rescaleByDPI(px)
|
||||
return Screen:rescaleByDPI(px)
|
||||
end
|
||||
|
||||
function Screen:getPitch()
|
||||
return self.fb:getPitch()
|
||||
end
|
||||
|
||||
function Screen:getNativeRotationMode()
|
||||
-- in EMU mode, you will always get 0 from getOrientation()
|
||||
return self.fb:getOrientation()
|
||||
end
|
||||
|
||||
function Screen:getRotationMode()
|
||||
return self.cur_rotation_mode
|
||||
end
|
||||
|
||||
function Screen:getScreenMode()
|
||||
if self.width > self.height then
|
||||
return "landscape"
|
||||
else
|
||||
return "portrait"
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:setRotationMode(mode)
|
||||
if mode > 3 or mode < 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- mode 0 and mode 2 has the same width and height, so do mode 1 and 3
|
||||
if (self.cur_rotation_mode % 2) ~= (mode % 2) then
|
||||
self.width, self.height = self.height, self.width
|
||||
end
|
||||
self.cur_rotation_mode = mode
|
||||
self.bb:free()
|
||||
self.bb = Blitbuffer.new(self.width, self.height, self.width/2)
|
||||
-- update mode for input module
|
||||
Input.rotation = mode
|
||||
end
|
||||
|
||||
function Screen:setScreenMode(mode)
|
||||
if mode == "portrait" then
|
||||
if self.cur_rotation_mode ~= 0 then
|
||||
self:setRotationMode(0)
|
||||
end
|
||||
elseif mode == "landscape" then
|
||||
if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then
|
||||
self:setRotationMode(1)
|
||||
elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then
|
||||
self:setRotationMode((self.cur_rotation_mode + 2) % 4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Screen:saveCurrentBB()
|
||||
local width, height = self:getWidth(), self:getHeight()
|
||||
|
||||
if not self.saved_bb then
|
||||
self.saved_bb = Blitbuffer.new(width, height, self.width/2)
|
||||
end
|
||||
if self.saved_bb:getWidth() ~= width then
|
||||
self.saved_bb:free()
|
||||
self.saved_bb = Blitbuffer.new(width, height, self.width/2)
|
||||
end
|
||||
self.saved_bb:blitFullFrom(self.bb)
|
||||
end
|
||||
|
||||
function Screen:restoreFromSavedBB()
|
||||
self:restoreFromBB(self.saved_bb)
|
||||
end
|
||||
|
||||
function Screen:getCurrentScreenBB()
|
||||
local bb = Blitbuffer.new(self:getWidth(), self:getHeight())
|
||||
bb:blitFullFrom(self.bb)
|
||||
return bb
|
||||
end
|
||||
|
||||
function Screen:restoreFromBB(bb)
|
||||
if bb then
|
||||
self.bb:blitFullFrom(bb)
|
||||
else
|
||||
DEBUG("Got nil bb in restoreFromSavedBB!")
|
||||
end
|
||||
end
|
||||
-- compatibility wrapper
|
||||
return require("ui/device").screen
|
||||
|
@ -1,326 +0,0 @@
|
||||
require "ui/widget/base"
|
||||
|
||||
--[[
|
||||
WidgetContainer is a container for another Widget
|
||||
--]]
|
||||
WidgetContainer = Widget:new()
|
||||
|
||||
function WidgetContainer:init()
|
||||
if not self.dimen then
|
||||
self.dimen = Geom:new{}
|
||||
end
|
||||
end
|
||||
|
||||
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 + math.floor((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 + math.floor((self.dimen.h - contentSize.h)/2)
|
||||
end
|
||||
if self.ignore ~= "width" then
|
||||
x_pos = x + math.floor((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 + math.floor((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 + math.floor((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()
|
||||
self.dimen = Geom:new{
|
||||
x = x, y = y,
|
||||
w = my_size.w,
|
||||
h = my_size.h
|
||||
}
|
||||
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 + self.bordersize, y + self.bordersize,
|
||||
container_width - 2*self.bordersize,
|
||||
container_height - 2*self.bordersize)
|
||||
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()
|
||||
return self:getContentSize()
|
||||
end
|
||||
|
||||
function UnderlineContainer:getContentSize()
|
||||
local contentSize = self[1]:getSize()
|
||||
return Geom:new{
|
||||
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 = math.floor((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 + math.floor((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
|
||||
|
||||
|
@ -0,0 +1,20 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
|
||||
--[[
|
||||
BottomContainer contains its content (1 widget) at the bottom of its own
|
||||
dimensions
|
||||
--]]
|
||||
local 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 + math.floor((self.dimen.w - contentSize.w)/2),
|
||||
y + (self.dimen.h - contentSize.h))
|
||||
end
|
||||
|
||||
return BottomContainer
|
@ -0,0 +1,25 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
|
||||
--[[
|
||||
CenterContainer centers its content (1 widget) within its own dimensions
|
||||
--]]
|
||||
local 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 + math.floor((self.dimen.h - contentSize.h)/2)
|
||||
end
|
||||
if self.ignore ~= "width" then
|
||||
x_pos = x + math.floor((self.dimen.w - contentSize.w)/2)
|
||||
end
|
||||
self[1]:paintTo(bb, x_pos, y_pos)
|
||||
end
|
||||
|
||||
return CenterContainer
|
@ -0,0 +1,61 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local Geom = require("ui/geometry")
|
||||
|
||||
--[[
|
||||
A FrameContainer is some graphics content (1 widget) that is surrounded by a
|
||||
frame
|
||||
--]]
|
||||
local 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()
|
||||
self.dimen = Geom:new{
|
||||
x = x, y = y,
|
||||
w = my_size.w,
|
||||
h = my_size.h
|
||||
}
|
||||
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 + self.bordersize, y + self.bordersize,
|
||||
container_width - 2*self.bordersize,
|
||||
container_height - 2*self.bordersize)
|
||||
end
|
||||
end
|
||||
|
||||
return FrameContainer
|
@ -0,0 +1,96 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local Event = require("ui/event")
|
||||
local Geom = require("ui/geometry")
|
||||
local DEBUG = require("dbg")
|
||||
|
||||
--[[
|
||||
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
|
||||
--]]
|
||||
local 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 + math.floor((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
|
||||
|
||||
return InputContainer
|
@ -0,0 +1,17 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
|
||||
--[[
|
||||
LeftContainer aligns its content (1 widget) at the left of its own dimensions
|
||||
--]]
|
||||
local 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 + math.floor((self.dimen.h - contentSize.h)/2))
|
||||
end
|
||||
|
||||
return LeftContainer
|
@ -0,0 +1,19 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
|
||||
--[[
|
||||
RightContainer aligns its content (1 widget) at the right of its own dimensions
|
||||
--]]
|
||||
local 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 + math.floor((self.dimen.h - contentSize.h)/2))
|
||||
end
|
||||
|
||||
return RightContainer
|
@ -0,0 +1,42 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local Geom = require("ui/geometry")
|
||||
|
||||
--[[
|
||||
an UnderlineContainer is a WidgetContainer that is able to paint
|
||||
a line under its child node
|
||||
--]]
|
||||
|
||||
local UnderlineContainer = WidgetContainer:new{
|
||||
linesize = 2,
|
||||
padding = 1,
|
||||
color = 0,
|
||||
vertical_align = "top",
|
||||
}
|
||||
|
||||
function UnderlineContainer:getSize()
|
||||
return self:getContentSize()
|
||||
end
|
||||
|
||||
function UnderlineContainer:getContentSize()
|
||||
local contentSize = self[1]:getSize()
|
||||
return Geom:new{
|
||||
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 = math.floor((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
|
||||
|
||||
return UnderlineContainer
|
@ -0,0 +1,70 @@
|
||||
local Geom = require("ui/geometry")
|
||||
local Widget = require("ui/widget/widget")
|
||||
|
||||
--[[
|
||||
WidgetContainer is a container for another Widget
|
||||
--]]
|
||||
local WidgetContainer = Widget:new()
|
||||
|
||||
function WidgetContainer:init()
|
||||
if not self.dimen then
|
||||
self.dimen = Geom:new{}
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
return WidgetContainer
|
@ -0,0 +1,24 @@
|
||||
--[[
|
||||
The EventListener is an interface that handles events
|
||||
|
||||
EventListeners have a rudimentary event handler/dispatcher that
|
||||
will call a method "onEventName" for an event with name
|
||||
"EventName"
|
||||
--]]
|
||||
local EventListener = {}
|
||||
|
||||
function EventListener:new(o)
|
||||
local o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
if o.init then o:init() end
|
||||
return o
|
||||
end
|
||||
|
||||
function EventListener:handleEvent(event)
|
||||
if self[event.handler] then
|
||||
return self[event.handler](self, unpack(event.args))
|
||||
end
|
||||
end
|
||||
|
||||
return EventListener
|
@ -0,0 +1,29 @@
|
||||
local TextWidget = require("ui/widget/textwidget")
|
||||
local RenderText = require("ui/rendertext")
|
||||
local Geom = require("ui/geometry")
|
||||
local Screen = require("ui/screen")
|
||||
|
||||
--[[
|
||||
FixedTextWidget
|
||||
--]]
|
||||
local FixedTextWidget = TextWidget:new{}
|
||||
|
||||
function FixedTextWidget:getSize()
|
||||
local tsize = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, self.text, true)
|
||||
if not tsize then
|
||||
return Geom:new{}
|
||||
end
|
||||
self._length = tsize.x
|
||||
self._height = self.face.size
|
||||
return Geom:new{
|
||||
w = self._length,
|
||||
h = self._height,
|
||||
}
|
||||
end
|
||||
|
||||
function FixedTextWidget:paintTo(bb, x, y)
|
||||
RenderText:renderUtf8Text(bb, x, y+self._height, self.face, self.text,
|
||||
true, self.bgcolor, self.fgcolor)
|
||||
end
|
||||
|
||||
return FixedTextWidget
|
@ -1,172 +0,0 @@
|
||||
require "ui/widget/container"
|
||||
|
||||
|
||||
--[[
|
||||
A Layout widget that puts objects besides each others
|
||||
--]]
|
||||
HorizontalGroup = WidgetContainer:new{
|
||||
align = "center",
|
||||
_size = nil,
|
||||
}
|
||||
|
||||
function HorizontalGroup:getSize()
|
||||
if not self._size then
|
||||
self._size = { w = 0, h = 0 }
|
||||
self._offsets = { }
|
||||
for i, widget in ipairs(self) do
|
||||
local w_size = widget:getSize()
|
||||
self._offsets[i] = {
|
||||
x = self._size.w,
|
||||
y = w_size.h
|
||||
}
|
||||
self._size.w = self._size.w + w_size.w
|
||||
if w_size.h > self._size.h then
|
||||
self._size.h = w_size.h
|
||||
end
|
||||
end
|
||||
end
|
||||
return self._size
|
||||
end
|
||||
|
||||
function HorizontalGroup:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
|
||||
for i, widget in ipairs(self) do
|
||||
if self.align == "center" then
|
||||
widget:paintTo(bb,
|
||||
x + self._offsets[i].x,
|
||||
y + math.floor((size.h - self._offsets[i].y) / 2))
|
||||
elseif self.align == "top" then
|
||||
widget:paintTo(bb, x + self._offsets[i].x, y)
|
||||
elseif self.align == "bottom" then
|
||||
widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HorizontalGroup:clear()
|
||||
self:free()
|
||||
WidgetContainer.clear(self)
|
||||
end
|
||||
|
||||
function HorizontalGroup:resetLayout()
|
||||
self._size = nil
|
||||
self._offsets = {}
|
||||
end
|
||||
|
||||
function HorizontalGroup:free()
|
||||
self:resetLayout()
|
||||
WidgetContainer.free(self)
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
A Layout widget that puts objects under each other
|
||||
--]]
|
||||
VerticalGroup = WidgetContainer:new{
|
||||
align = "center",
|
||||
_size = nil,
|
||||
_offsets = {}
|
||||
}
|
||||
|
||||
function VerticalGroup:getSize()
|
||||
if not self._size then
|
||||
self._size = { w = 0, h = 0 }
|
||||
self._offsets = { }
|
||||
for i, widget in ipairs(self) do
|
||||
local w_size = widget:getSize()
|
||||
self._offsets[i] = {
|
||||
x = w_size.w,
|
||||
y = self._size.h,
|
||||
}
|
||||
self._size.h = self._size.h + w_size.h
|
||||
if w_size.w > self._size.w then
|
||||
self._size.w = w_size.w
|
||||
end
|
||||
end
|
||||
end
|
||||
return self._size
|
||||
end
|
||||
|
||||
function VerticalGroup:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
|
||||
for i, widget in ipairs(self) do
|
||||
if self.align == "center" then
|
||||
widget:paintTo(bb,
|
||||
x + math.floor((size.w - self._offsets[i].x) / 2),
|
||||
y + self._offsets[i].y)
|
||||
elseif self.align == "left" then
|
||||
widget:paintTo(bb, x, y + self._offsets[i].y)
|
||||
elseif self.align == "right" then
|
||||
widget:paintTo(bb,
|
||||
x + size.w - self._offsets[i].x,
|
||||
y + self._offsets[i].y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VerticalGroup:clear()
|
||||
self:free()
|
||||
WidgetContainer.clear(self)
|
||||
end
|
||||
|
||||
function VerticalGroup:resetLayout()
|
||||
self._size = nil
|
||||
self._offsets = {}
|
||||
end
|
||||
|
||||
function VerticalGroup:free()
|
||||
self:resetLayout()
|
||||
WidgetContainer.free(self)
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
A Layout widget that puts objects above each other
|
||||
--]]
|
||||
OverlapGroup = WidgetContainer:new{
|
||||
_size = nil,
|
||||
}
|
||||
|
||||
function OverlapGroup:getSize()
|
||||
if not self._size then
|
||||
self._size = {w = 0, h = 0}
|
||||
self._offsets = { x = math.huge, y = math.huge }
|
||||
for i, widget in ipairs(self) do
|
||||
local w_size = widget:getSize()
|
||||
if self._size.h < w_size.h then
|
||||
self._size.h = w_size.h
|
||||
end
|
||||
if self._size.w < w_size.w then
|
||||
self._size.w = w_size.w
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.dimen.w then
|
||||
self._size.w = self.dimen.w
|
||||
end
|
||||
if self.dimen.h then
|
||||
self._size.h = self.dimen.h
|
||||
end
|
||||
|
||||
return self._size
|
||||
end
|
||||
|
||||
function OverlapGroup:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
|
||||
for i, wget in ipairs(self) do
|
||||
local wget_size = wget:getSize()
|
||||
if wget.align == "right" then
|
||||
wget:paintTo(bb, x+size.w-wget_size.w, y)
|
||||
elseif wget.align == "center" then
|
||||
wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y)
|
||||
else
|
||||
-- default to left
|
||||
wget:paintTo(bb, x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,61 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
|
||||
--[[
|
||||
A Layout widget that puts objects besides each others
|
||||
--]]
|
||||
local HorizontalGroup = WidgetContainer:new{
|
||||
align = "center",
|
||||
_size = nil,
|
||||
}
|
||||
|
||||
function HorizontalGroup:getSize()
|
||||
if not self._size then
|
||||
self._size = { w = 0, h = 0 }
|
||||
self._offsets = { }
|
||||
for i, widget in ipairs(self) do
|
||||
local w_size = widget:getSize()
|
||||
self._offsets[i] = {
|
||||
x = self._size.w,
|
||||
y = w_size.h
|
||||
}
|
||||
self._size.w = self._size.w + w_size.w
|
||||
if w_size.h > self._size.h then
|
||||
self._size.h = w_size.h
|
||||
end
|
||||
end
|
||||
end
|
||||
return self._size
|
||||
end
|
||||
|
||||
function HorizontalGroup:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
|
||||
for i, widget in ipairs(self) do
|
||||
if self.align == "center" then
|
||||
widget:paintTo(bb,
|
||||
x + self._offsets[i].x,
|
||||
y + math.floor((size.h - self._offsets[i].y) / 2))
|
||||
elseif self.align == "top" then
|
||||
widget:paintTo(bb, x + self._offsets[i].x, y)
|
||||
elseif self.align == "bottom" then
|
||||
widget:paintTo(bb, x + self._offsets[i].x, y + size.h - self._offsets[i].y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function HorizontalGroup:clear()
|
||||
self:free()
|
||||
WidgetContainer.clear(self)
|
||||
end
|
||||
|
||||
function HorizontalGroup:resetLayout()
|
||||
self._size = nil
|
||||
self._offsets = {}
|
||||
end
|
||||
|
||||
function HorizontalGroup:free()
|
||||
self:resetLayout()
|
||||
WidgetContainer.free(self)
|
||||
end
|
||||
|
||||
return HorizontalGroup
|
@ -0,0 +1,14 @@
|
||||
local Widget = require("ui/widget/widget")
|
||||
|
||||
--[[
|
||||
Dummy Widget that reserves horizontal space
|
||||
--]]
|
||||
local HorizontalSpan = Widget:new{
|
||||
width = 0,
|
||||
}
|
||||
|
||||
function HorizontalSpan:getSize()
|
||||
return {w = self.width, h = 0}
|
||||
end
|
||||
|
||||
return HorizontalSpan
|
@ -0,0 +1,51 @@
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
|
||||
--[[
|
||||
A Layout widget that puts objects above each other
|
||||
--]]
|
||||
local OverlapGroup = WidgetContainer:new{
|
||||
_size = nil,
|
||||
}
|
||||
|
||||
function OverlapGroup:getSize()
|
||||
if not self._size then
|
||||
self._size = {w = 0, h = 0}
|
||||
self._offsets = { x = math.huge, y = math.huge }
|
||||
for i, widget in ipairs(self) do
|
||||
local w_size = widget:getSize()
|
||||
if self._size.h < w_size.h then
|
||||
self._size.h = w_size.h
|
||||
end
|
||||
if self._size.w < w_size.w then
|
||||
self._size.w = w_size.w
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.dimen.w then
|
||||
self._size.w = self.dimen.w
|
||||
end
|
||||
if self.dimen.h then
|
||||
self._size.h = self.dimen.h
|
||||
end
|
||||
|
||||
return self._size
|
||||
end
|
||||
|
||||
function OverlapGroup:paintTo(bb, x, y)
|
||||
local size = self:getSize()
|
||||
|
||||
for i, wget in ipairs(self) do
|
||||
local wget_size = wget:getSize()
|
||||
if wget.align == "right" then
|
||||
wget:paintTo(bb, x+size.w-wget_size.w, y)
|
||||
elseif wget.align == "center" then
|
||||
wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y)
|
||||
else
|
||||
-- default to left
|
||||
wget:paintTo(bb, x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return OverlapGroup
|
@ -0,0 +1,15 @@
|
||||
local Widget = require("ui/widget/widget")
|
||||
|
||||
--[[
|
||||
Dummy Widget that reserves vertical and horizontal space
|
||||
]]
|
||||
local RectSpan = Widget:new{
|
||||
width = 0,
|
||||
hright = 0,
|
||||
}
|
||||
|
||||
function RectSpan:getSize()
|
||||
return {w = self.width, h = self.height}
|
||||
end
|
||||
|
||||
return RectSpan
|
@ -0,0 +1,85 @@
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local TextBoxWidget = require("ui/widget/textboxwidget")
|
||||
local VerticalScrollBar = require("ui/widget/verticalscrollbar")
|
||||
local Geom = require("ui/geometry")
|
||||
local GestureRange = require("ui/gesturerange")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local Screen = require("ui/screen")
|
||||
local HorizontalGroup = require("ui/widget/horizontalgroup")
|
||||
local HorizontalSpan = require("ui/widget/horizontalspan")
|
||||
local Device = require("ui/device")
|
||||
|
||||
--[[
|
||||
Text widget with vertical scroll bar
|
||||
--]]
|
||||
local ScrollTextWidget = InputContainer:new{
|
||||
text = nil,
|
||||
face = nil,
|
||||
bgcolor = 0.0, -- [0.0, 1.0]
|
||||
fgcolor = 1.0, -- [0.0, 1.0]
|
||||
width = 400,
|
||||
height = 20,
|
||||
scroll_bar_width = Screen:scaleByDPI(6),
|
||||
text_scroll_span = Screen:scaleByDPI(6),
|
||||
dialog = nil,
|
||||
}
|
||||
|
||||
function ScrollTextWidget:init()
|
||||
self.text_widget = TextBoxWidget:new{
|
||||
text = self.text,
|
||||
face = self.face,
|
||||
bgcolor = self.bgcolor,
|
||||
fgcolor = self.fgcolor,
|
||||
width = self.width - self.scroll_bar_width - self.text_scroll_span,
|
||||
height = self.height
|
||||
}
|
||||
local visible_line_count = self.text_widget:getVisLineCount()
|
||||
local total_line_count = self.text_widget:getAllLineCount()
|
||||
self.v_scroll_bar = VerticalScrollBar:new{
|
||||
enable = visible_line_count < total_line_count,
|
||||
low = 0,
|
||||
high = visible_line_count/total_line_count,
|
||||
width = Screen:scaleByDPI(6),
|
||||
height = self.height,
|
||||
}
|
||||
local horizontal_group = HorizontalGroup:new{}
|
||||
table.insert(horizontal_group, self.text_widget)
|
||||
table.insert(horizontal_group, HorizontalSpan:new{width = Screen:scaleByDPI(6)})
|
||||
table.insert(horizontal_group, self.v_scroll_bar)
|
||||
self[1] = horizontal_group
|
||||
self.dimen = Geom:new(self[1]:getSize())
|
||||
if Device:isTouchDevice() then
|
||||
self.ges_events = {
|
||||
Swipe = {
|
||||
GestureRange:new{
|
||||
ges = "swipe",
|
||||
range = self.dimen,
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function ScrollTextWidget:updateScrollBar(text)
|
||||
local virtual_line_num = text:getVirtualLineNum()
|
||||
local visible_line_count = text:getVisLineCount()
|
||||
local all_line_count = text:getAllLineCount()
|
||||
self.v_scroll_bar:set(
|
||||
(virtual_line_num - 1) / all_line_count,
|
||||
(virtual_line_num - 1 + visible_line_count) / all_line_count
|
||||
)
|
||||
end
|
||||
|
||||
function ScrollTextWidget:onSwipe(arg, ges)
|
||||
if ges.direction == "north" then
|
||||
self.text_widget:scrollDown()
|
||||
self:updateScrollBar(self.text_widget)
|
||||
elseif ges.direction == "south" then
|
||||
self.text_widget:scrollUp()
|
||||
self:updateScrollBar(self.text_widget)
|
||||
end
|
||||
UIManager:setDirty(self.dialog, "partial")
|
||||
return true
|
||||
end
|
||||
|
||||
return ScrollTextWidget
|
@ -1,27 +0,0 @@
|
||||
require "ui/widget/base"
|
||||
|
||||
|
||||
--[[
|
||||
Dummy Widget that reserves horizontal space
|
||||
--]]
|
||||
HorizontalSpan = Widget:new{
|
||||
width = 0,
|
||||
}
|
||||
|
||||
function HorizontalSpan:getSize()
|
||||
return {w = self.width, h = 0}
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Dummy Widget that reserves vertical space
|
||||
--]]
|
||||
VerticalSpan = Widget:new{
|
||||
width = 0,
|
||||
}
|
||||
|
||||
function VerticalSpan:getSize()
|
||||
return {w = 0, h = self.width}
|
||||
end
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue