rearranged source tree

pull/2/merge
Qingping Hou 11 years ago
parent 08edf7c259
commit 10d980ed87

@ -0,0 +1,36 @@
require "settings" -- for dump method
Dbg = {
is_on = false,
ev_log = nil,
}
function Dbg:turnOn()
self.is_on = true
-- create or clear ev log file
os.execute("echo > ev.log")
self.ev_log = io.open("ev.log", "w")
end
function Dbg:logEv(ev)
local log = ev.type.."|"..ev.code.."|"
..ev.value.."|"..ev.time.sec.."|"..ev.time.usec.."\n"
self.ev_log:write(log)
self.ev_log:flush()
end
function DEBUG(...)
local line = ""
for i,v in ipairs({...}) do
if type(v) == "table" then
line = line .. " " .. dump(v)
else
line = line .. " " .. tostring(v)
end
end
print("#"..line)
end

@ -70,18 +70,6 @@ function dump(data)
return table.concat(out)
end
function DEBUG(...)
local line = ""
for i,v in ipairs({...}) do
if type(v) == "table" then
line = line .. " " .. dump(v)
else
line = line .. " " .. tostring(v)
end
end
print("#"..line)
end
-- simple serialization function, won't do uservalues, functions, loops
function DocSettings:_serialize(what, outt, indent)
if type(what) == "table" then

@ -78,7 +78,7 @@ GestureDetector = {
DOUBLE_TAP_DISTANCE = 50,
TWO_FINGER_TAP_REGION = 20,
PAN_THRESHOLD = 50,
-- states are stored in separated slots
states = {},
track_ids = {},
@ -272,7 +272,7 @@ function GestureDetector:tapState(tev)
y = tev.y,
timev = tev.timev,
}
if self.last_taps[slot] ~= nil and
self:isDoubleTap(self.last_taps[slot], cur_tap) then
-- it is a double tap
@ -282,10 +282,10 @@ function GestureDetector:tapState(tev)
DEBUG("double tap detected in slot", slot)
return ges_ev
end
-- set current tap to last tap
self.last_taps[slot] = cur_tap
DEBUG("set up tap timer")
-- deadline should be calculated by adding current tap time and the interval
local deadline = cur_tap.timev + TimeVal:new{
@ -305,7 +305,7 @@ function GestureDetector:tapState(tev)
-- we are already at the end of touch event
-- so reset the state
self:clearState(slot)
else
else
-- last tev in this slot is cleared by last two finger tap
self:clearState(slot)
return {
@ -411,7 +411,7 @@ end
function GestureDetector:holdState(tev, hold)
DEBUG("in hold state...")
local slot = tev.slot
local slot = tev.slot
-- when we switch to hold state, we pass additional param "hold"
if tev.id ~= -1 and hold and self.last_tevs[slot].x and self.last_tevs[slot].y then
self.states[slot] = self.holdState

@ -2,7 +2,6 @@ require "ui/event"
require "ui/device"
require "ui/time"
require "ui/gesturedetector"
require "settings"
-- constants from <linux/input.h>
EV_SYN = 0
@ -112,7 +111,7 @@ function Key:match(sequence)
return false
end
end
return true
end
@ -334,7 +333,7 @@ end
function Input:setTimeout(cb, tv_out)
local item = {
callback = cb,
callback = cb,
deadline = tv_out,
}
for k,v in ipairs(self.timer_callbacks) do
@ -443,7 +442,7 @@ function Input:handleTouchEv(ev)
local touch_ges = GestureDetector:feedEvent(self.MTSlots)
self.MTSlots = {}
if touch_ges then
return Event:new("Gesture",
return Event:new("Gesture",
GestureDetector:adjustGesCoordinate(touch_ges)
)
end
@ -451,7 +450,7 @@ function Input:handleTouchEv(ev)
elseif ev.type == EV_ABS then
if #self.MTSlots == 0 then
table.insert(self.MTSlots, self:getMtSlot(self.cur_slot))
end
end
if ev.code == ABS_MT_SLOT then
if self.cur_slot ~= ev.value then
table.insert(self.MTSlots, self:getMtSlot(ev.value))
@ -471,7 +470,7 @@ function Input:waitEvent(timeout_us, timeout_s)
-- wrapper for input.waitForEvents that will retry for some cases
local ok, ev
local wait_deadline = TimeVal:now() + TimeVal:new{
sec = timeout_s,
sec = timeout_s,
usec = timeout_us
}
while true do
@ -490,7 +489,7 @@ function Input:waitEvent(timeout_us, timeout_s)
-- Do we really need to clear all setTimeout after
-- decided a gesture? FIXME
Input.timer_callbacks = {}
return Event:new("Gesture",
return Event:new("Gesture",
GestureDetector:adjustGesCoordinate(touch_ges)
)
end -- EOF if touch_ges
@ -520,13 +519,10 @@ function Input:waitEvent(timeout_us, timeout_s)
end
if ok and ev then
ev = self:eventAdjustHook(ev)
if G_debug_mode then
local log = ev.type.."|"..ev.code.."|"
..ev.value.."|"..ev.time.sec.."|"..ev.time.usec.."\n"
G_ev_log:write(log)
G_ev_log:flush()
if Dbg.is_on and ev then
Dbg:logEv(ev)
end
ev = self:eventAdjustHook(ev)
if ev.type == EV_KEY then
return self:handleKeyBoardEv(ev)
elseif ev.type == EV_ABS or ev.type == EV_SYN then

@ -1,4 +1,4 @@
require "ui/notification"
require "ui/widget/notification"
ReaderBookmark = InputContainer:new{
bm_menu_title = "Bookmarks",
@ -89,7 +89,7 @@ function ReaderBookmark:onShowBookmark()
local bm_menu = Menu:new{
title = "Bookmarks",
item_table = self.bookmarks,
width = Screen:getWidth()-20,
width = Screen:getWidth()-20,
height = Screen:getHeight(),
}
-- buid up menu widget method as closure
@ -111,7 +111,7 @@ function ReaderBookmark:onShowBookmark()
dimen = Screen:getSize(),
bm_menu,
}
bm_menu.close_callback = function()
bm_menu.close_callback = function()
UIManager:close(menu_container)
end
@ -154,7 +154,7 @@ function ReaderBookmark:addBookmark(pn_or_xp)
return self:isBookmarkInSequence(a, b)
end)
return true
end
end
function ReaderBookmark:isBookmarkInSequence(a, b)
return a.page < b.page

@ -1,4 +1,4 @@
require "ui/config"
require "ui/widget/config"
Configurable = {}

@ -1,5 +1,5 @@
require "ui/widget"
require "ui/bbox"
require "ui/widget/group"
require "ui/widget/bbox"
PageCropDialog = VerticalGroup:new{
ok_text = "OK",

@ -1,3 +1,4 @@
require "ui/widget/image"
ReaderDogear = RightContainer:new{}
@ -13,4 +14,4 @@ end
function ReaderDogear:onSetDogearVisibility(visible)
self.view.dogear_visible = visible
return true
end
end

@ -1,3 +1,4 @@
require "ui/widget/progress"
ReaderFooter = InputContainer:new{
pageno = nil,

@ -1,4 +1,3 @@
require "ui/ui"
require "ui/reader/readerview"
require "ui/reader/readerzooming"
require "ui/reader/readerpanning"

@ -1,9 +1,8 @@
require "ui/geometry"
require "ui/device"
require "ui/inputevent"
require "ui/widget"
require "ui/screen"
require "settings" -- for DEBUG(), TODO: put DEBUG() somewhere else
require "debug"
-- initialize output module, this must be initialized before Input
Screen:init()
@ -25,7 +24,7 @@ UIManager = {
-- trigger a full refresh when counter reaches FULL_REFRESH_COUNT
FULL_REFRESH_COUNT = 6,
refresh_count = 0,
_running = true,
_window_stack = {},
_execution_stack = {},
@ -156,7 +155,7 @@ function UIManager:run()
while self._running do
local now = { util.gettime() }
local wait_until = self:checkTasks()
--DEBUG("---------------------------------------------------")
--DEBUG("exec stack", self._execution_stack)
--DEBUG("window stack", self._window_stack)
@ -181,19 +180,19 @@ function UIManager:run()
end
if self._dirty[widget.widget] == "full" then
force_full_refresh = true
end
end
-- and remove from list after painting
self._dirty[widget.widget] = nil
-- trigger repaint
dirty = true
end
end
if self.full_refresh then
dirty = true
force_full_refresh = true
end
self.repaint_all = false
self.full_refresh = false
@ -214,9 +213,9 @@ function UIManager:run()
-- reset refresh_type
self.refresh_type = 1
end
self:checkTasks()
-- wait for next event
-- note that we will skip that if in the meantime we have tasks that are ready to run
local input_event = nil

@ -1,748 +0,0 @@
require "ui/screen"
require "ui/rendertext"
require "ui/graphics"
require "ui/image"
require "ui/event"
require "ui/inputevent"
require "ui/gesturedetector"
require "ui/font"
--[[
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"
]]
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
--[[
This is a generic Widget interface
widgets can be queried about their size and can be paint.
that's it for now. Probably we need something more elaborate
later.
if the table that was given to us as parameter has an "init"
method, it will be called. use this to set _instance_ variables
rather than class variables.
]]
Widget = EventListener:new()
function Widget:new(o)
local o = o or {}
setmetatable(o, self)
self.__index = self
-- Both o._init and o.init are called on object create. But o._init is used
-- for base widget initialization (basic component used to build other
-- widgets). While o.init is for higher level widgets, for example Menu
-- Widget
if o._init then o:_init() end
if o.init then o:init() end
return o
end
function Widget:getSize()
return self.dimen
end
function Widget:paintTo(bb, x, y)
end
--[[
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,
}
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()
if self.background then
bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.background, self.radius)
end
if self.bordersize > 0 then
bb:paintBorder(x + self.margin, y + self.margin,
my_size.w - self.margin * 2, my_size.h - 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
end
--[[
A TextWidget puts a string on a single line
]]
TextWidget = Widget:new{
text = nil,
face = nil,
color = 15,
_bb = nil,
_length = 0,
_height = 0,
_maxlength = 1200,
}
--function TextWidget:_render()
--local h = self.face.size * 1.3
--self._bb = Blitbuffer.new(self._maxlength, h)
--self._length = renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, self.color)
--end
function TextWidget:getSize()
--if not self._bb then
--self:_render()
--end
--return { w = self._length, h = self._bb:getHeight() }
local tsize = 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 * 1.5
return Geom:new{
w = self._length,
h = self._height,
}
end
function TextWidget:paintTo(bb, x, y)
--if not self._bb then
--self:_render()
--end
--bb:blitFrom(self._bb, x, y, 0, 0, self._length, self._bb:getHeight())
--@TODO Don't use kerning for monospaced fonts. (houqp)
renderUtf8Text(bb, x, y+self._height*0.7, self.face, self.text, true)
end
function TextWidget:free()
if self._bb then
self._bb:free()
self._bb = nil
end
end
--[[
A TextWidget that handles long text wrapping
]]
TextBoxWidget = Widget:new{
text = nil,
face = nil,
color = 15,
width = 400, -- in pixels
line_height = 0.3, -- in em
v_list = nil,
_bb = nil,
_length = 0,
}
function TextBoxWidget:_wrapGreedyAlg(h_list)
local cur_line_width = 0
local space_w = sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x
local cur_line = {}
local v_list = {}
for k,w in ipairs(h_list) do
cur_line_width = cur_line_width + w.width
if cur_line_width <= self.width then
cur_line_width = cur_line_width + space_w
table.insert(cur_line, w)
else
-- wrap to next line
table.insert(v_list, cur_line)
cur_line = {}
cur_line_width = w.width + space_w
table.insert(cur_line, w)
end
end
-- handle last line
table.insert(v_list, cur_line)
return v_list
end
function TextBoxWidget:_getVerticalList(alg)
-- build horizontal list
h_list = {}
for w in self.text:gmatch("%S+") do
word_box = {}
word_box.word = w
word_box.width = sizeUtf8Text(0, Screen:getWidth(), self.face, w, true).x
table.insert(h_list, word_box)
end
-- @TODO check alg here 25.04 2012 (houqp)
-- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp)
return self:_wrapGreedyAlg(h_list)
end
function TextBoxWidget:_render()
self.v_list = self:_getVerticalList()
local v_list = self.v_list
local font_height = self.face.size
local line_height_px = self.line_height * font_height
local space_w = sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x
local h = (font_height + line_height_px) * #v_list - line_height_px
self._bb = Blitbuffer.new(self.width, h)
local y = font_height
local pen_x = 0
for _,l in ipairs(v_list) do
pen_x = 0
for _,w in ipairs(l) do
--@TODO Don't use kerning for monospaced fonts. (houqp)
-- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree
renderUtf8Text(self._bb, pen_x, y*0.8, self.face, w.word, true)
pen_x = pen_x + w.width + space_w
end
y = y + line_height_px + font_height
end
-- if text is shorter than one line, shrink to text's width
if #v_list == 1 then
self.width = pen_x
end
end
function TextBoxWidget:getSize()
if not self._bb then
self:_render()
end
return { w = self.width, h = self._bb:getHeight() }
end
function TextBoxWidget:paintTo(bb, x, y)
if not self._bb then
self:_render()
end
bb:blitFrom(self._bb, x, y, 0, 0, self.width, self._bb:getHeight())
end
function TextBoxWidget:free()
if self._bb then
self._bb:free()
self._bb = nil
end
end
--[[
ImageWidget shows an image from a file
]]
ImageWidget = Widget:new{
invert = nil,
file = nil,
_bb = nil
}
function ImageWidget:_render()
local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "")
if itype == "jpeg" or itype == "jpg" then
self._bb = Image.fromJPEG(self.file)
elseif itype == "png" then
self._bb = Image.fromPNG(self.file)
end
end
function ImageWidget:getSize()
if not self._bb then
self:_render()
end
return Geom:new{ w = self._bb:getWidth(), h = self._bb:getHeight() }
end
function ImageWidget:paintTo(bb, x, y)
local size = self:getSize()
bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h)
if self.invert then
bb:invertRect(x, y, size.w, size.h)
end
end
function ImageWidget:free()
if self._bb then
self._bb:free()
self._bb = nil
end
end
--[[
ProgressWidget shows a progress bar
]]
ProgressWidget = Widget:new{
width = nil,
height = nil,
margin_h = 3,
margin_v = 1,
radius = 2,
bordersize = 1,
bordercolor = 15,
bgcolor = 0,
rectcolor = 10,
percentage = nil,
}
function ProgressWidget:getSize()
return { w = self.width, h = self.height }
end
function ProgressWidget:paintTo(bb, x, y)
local my_size = self:getSize()
bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.bgcolor, self.radius)
bb:paintBorder(x, y, my_size.w, my_size.h, self.bordersize, self.bordercolor, self.radius)
bb:paintRect(x+self.margin_h, y+self.margin_v+self.bordersize,
(my_size.w-2*self.margin_h)*self.percentage, (my_size.h-2*(self.margin_v+self.bordersize)), self.rectcolor)
end
function ProgressWidget:setPercentage(percentage)
self.percentage = percentage
end
--[[
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 + (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
--[[
Dummy Widget that reserves horizontal space
]]
HorizontalSpan = Widget:new{
width = 0,
}
function HorizontalSpan:getSize()
return {w = self.width, h = 0}
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 + (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+(size.w-wget_size.w)/2, y)
else
-- default to left
wget:paintTo(bb, x, y)
end
end
end
--[[
Dummy Widget that reserves vertical space
]]
VerticalSpan = Widget:new{
width = 0,
}
function VerticalSpan:getSize()
return {w = 0, h = self.width}
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,
}
function UnderlineContainer:getSize()
if self.dimen then
return { w = self.dimen.w, h = self.dimen.h }
else
local contentSize = self[1]:getSize()
return {
w = contentSize.w,
h = contentSize.h + self.linesize + self.padding
}
end
end
function UnderlineContainer:paintTo(bb, x, y)
local content_size = self:getSize()
self[1]:paintTo(bb, x, y)
bb:paintRect(x, y + content_size.h - self.linesize,
content_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{}
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
return self[1]:paintTo(bb, x, y)
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,68 @@
require "ui/screen"
require "ui/rendertext"
require "ui/graphics"
require "ui/image"
require "ui/event"
require "ui/inputevent"
require "ui/gesturedetector"
require "ui/font"
--[[
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"
--]]
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
--[[
This is a generic Widget interface
widgets can be queried about their size and can be paint.
that's it for now. Probably we need something more elaborate
later.
if the table that was given to us as parameter has an "init"
method, it will be called. use this to set _instance_ variables
rather than class variables.
--]]
Widget = EventListener:new()
function Widget:new(o)
local o = o or {}
setmetatable(o, self)
self.__index = self
-- Both o._init and o.init are called on object create. But o._init is used
-- for base widget initialization (basic component used to build other
-- widgets). While o.init is for higher level widgets, for example Menu
-- Widget
if o._init then o:_init() end
if o.init then o:init() end
return o
end
function Widget:getSize()
return self.dimen
end
function Widget:paintTo(bb, x, y)
end

@ -1,4 +1,5 @@
require "math"
require "ui/widget/container"
--[[
BBoxWidget shows a bbox for page cropping

@ -1,4 +1,4 @@
require "ui/widget"
require "ui/widget/container"
--[[
a button widget

@ -1,26 +1,5 @@
require "ui/widget"
require "ui/focusmanager"
require "ui/infomessage"
require "ui/font"
require "ui/toggleswitch"
FixedTextWidget = TextWidget:new{}
function FixedTextWidget:getSize()
local tsize = 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)
renderUtf8Text(bb, x, y+self._height, self.face, self.text, true)
end
require "ui/widget/container"
require "ui/widget/toggleswitch"
MenuBarItem = InputContainer:new{}
function MenuBarItem:init()
@ -190,7 +169,7 @@ function ConfigOption:init()
local item_font_face = self.options[c].item_font_face and self.options[c].item_font_face or "cfont"
local item_font_size = self.options[c].item_font_size and self.options[c].item_font_size or default_item_font_size
local option_height = (self.options[c].height and self.options[c].height or default_option_height) * Screen:getDPI()/167
local items_spacing = HorizontalSpan:new{
local items_spacing = HorizontalSpan:new{
width = (self.options[c].spacing and self.options[c].spacing or default_items_spacing) * Screen:getDPI()/167
}
local horizontal_group = HorizontalGroup:new{}
@ -205,7 +184,7 @@ function ConfigOption:init()
table.insert(option_name_container, option_name)
table.insert(horizontal_group, option_name_container)
end
if self.options[c].widget == "ProgressWidget" then
local widget_container = CenterContainer:new{
dimen = Geom:new{w = Screen:getWidth()*self.options[c].widget_align_center, h = option_height}
@ -218,7 +197,7 @@ function ConfigOption:init()
table.insert(widget_container, widget)
table.insert(horizontal_group, widget_container)
end
local option_items_container = CenterContainer:new{
dimen = Geom:new{w = Screen:getWidth()*item_align, h = option_height}
}
@ -261,7 +240,7 @@ function ConfigOption:init()
end
end
end
if self.options[c].item_text then
for d = 1, #self.options[c].item_text do
local option_item = nil
@ -298,7 +277,7 @@ function ConfigOption:init()
end
end
end
if self.options[c].item_icons then
for d = 1, #self.options[c].item_icons do
local option_item = OptionIconItem:new{
@ -322,7 +301,7 @@ function ConfigOption:init()
end
end
end
if self.options[c].toggle then
local switch = ToggleSwitch:new{
name = self.options[c].name,
@ -338,7 +317,7 @@ function ConfigOption:init()
switch:setPosition(position)
table.insert(option_items_group, switch)
end
table.insert(option_items_container, option_items_group)
table.insert(horizontal_group, option_items_container)
table.insert(vertical_group, horizontal_group)
@ -352,7 +331,7 @@ end
ConfigPanel = FrameContainer:new{ background = 0, bordersize = 0, }
function ConfigPanel:init()
local config_options = self.config_dialog.config_options
local default_option = config_options.default_options and config_options.default_options
local default_option = config_options.default_options and config_options.default_options
or config_options[1].options
local panel = ConfigOption:new{
options = self.index and config_options[self.index].options or default_option,
@ -375,26 +354,26 @@ function MenuBar:init()
local icon_dimen = menu_icon:getSize()
icons_width = icons_width + icon_dimen.w
icons_height = icon_dimen.h > icons_height and icon_dimen.h or icons_height
menu_items[c] = MenuBarItem:new{
menu_icon,
index = c,
config = self.config_dialog,
}
end
local spacing = HorizontalSpan:new{
width = (Screen:getWidth() - icons_width) / (#menu_items+1)
}
local menu_bar = HorizontalGroup:new{}
for c = 1, #menu_items do
table.insert(menu_bar, spacing)
table.insert(menu_bar, menu_items[c])
end
table.insert(menu_bar, spacing)
self.dimen = Geom:new{ w = Screen:getWidth(), h = icons_height}
table.insert(self, menu_bar)
end
@ -415,7 +394,7 @@ Widget that displays config menubar and config panel
+----------------+
| Menu Bar |
+----------------+
--]]
ConfigDialog = InputContainer:new{
@ -429,7 +408,7 @@ function ConfigDialog:init()
self.config_panel = ConfigPanel:new{
config_dialog = self,
}
self.config_menubar = MenuBar:new{
self.config_menubar = MenuBar:new{
config_dialog = self,
}
self:makeDialog()
@ -454,7 +433,7 @@ function ConfigDialog:init()
self.key_events.FocusRight = nil
end
self.key_events.Select = { {"Press"}, doc = "select current menu item"}
UIManager:setDirty(self, "partial")
end
@ -470,9 +449,9 @@ function ConfigDialog:makeDialog()
self.config_panel,
self.config_menubar,
}
local dialog_size = dialog:getSize()
self[1] = BottomContainer:new{
dimen = Screen:getSize(),
FrameContainer:new{
@ -481,13 +460,13 @@ function ConfigDialog:makeDialog()
dialog,
}
}
self.dialog_dimen = Geom:new{
x = (Screen:getWidth() - dialog_size.w)/2,
y = Screen:getHeight() - dialog_size.h,
w = dialog_size.w,
h = dialog_size.h,
}
}
end
function ConfigDialog:onShowConfigPanel(index)

@ -1,6 +1,6 @@
require "ui/widget"
require "ui/focusmanager"
require "ui/button"
require "ui/widget/container"
require "ui/widget/focusmanager"
require "ui/widget/button"
--[[
Widget that shows a message and OK/Cancel buttons

@ -0,0 +1,287 @@
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,
}
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()
if self.background then
bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.background, self.radius)
end
if self.bordersize > 0 then
bb:paintBorder(x + self.margin, y + self.margin,
my_size.w - self.margin * 2, my_size.h - 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
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,
}
function UnderlineContainer:getSize()
if self.dimen then
return { w = self.dimen.w, h = self.dimen.h }
else
local contentSize = self[1]:getSize()
return {
w = contentSize.w,
h = contentSize.h + self.linesize + self.padding
}
end
end
function UnderlineContainer:paintTo(bb, x, y)
local content_size = self:getSize()
self[1]:paintTo(bb, x, y)
bb:paintRect(x, y + content_size.h - self.linesize,
content_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{}
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
return self[1]:paintTo(bb, x, y)
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

@ -1,4 +1,4 @@
require "ui/menu"
require "ui/widget/menu"
FileChooser = Menu:new{
height = Screen:getHeight(),

@ -0,0 +1,166 @@
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 + (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 + (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+(size.w-wget_size.w)/2, y)
else
-- default to left
wget:paintTo(bb, x, y)
end
end
end

@ -0,0 +1,45 @@
require "ui/widget/base"
require "ui/image"
--[[
ImageWidget shows an image from a file
--]]
ImageWidget = Widget:new{
invert = nil,
file = nil,
_bb = nil
}
function ImageWidget:_render()
local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "")
if itype == "jpeg" or itype == "jpg" then
self._bb = Image.fromJPEG(self.file)
elseif itype == "png" then
self._bb = Image.fromPNG(self.file)
end
end
function ImageWidget:getSize()
if not self._bb then
self:_render()
end
return Geom:new{ w = self._bb:getWidth(), h = self._bb:getHeight() }
end
function ImageWidget:paintTo(bb, x, y)
local size = self:getSize()
bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h)
if self.invert then
bb:invertRect(x, y, size.w, size.h)
end
end
function ImageWidget:free()
if self._bb then
self._bb:free()
self._bb = nil
end
end

@ -1,5 +1,4 @@
require "ui/ui"
require "ui/widget"
require "ui/widget/container"
--[[
Widget that displays an informational message

@ -1,6 +1,9 @@
require "ui/widget"
require "ui/focusmanager"
require "ui/infomessage"
require "ui/widget/container"
require "ui/widget/focusmanager"
require "ui/widget/infomessage"
require "ui/widget/text"
require "ui/widget/group"
require "ui/widget/span"
require "ui/font"
--[[

@ -1,5 +1,4 @@
require "ui/ui"
require "ui/widget"
require "ui/widget/container"
--[[
Widget that displays a tiny notification on top of screen

@ -0,0 +1,39 @@
require "ui/widget/base"
--[[
ProgressWidget shows a progress bar
--]]
ProgressWidget = Widget:new{
width = nil,
height = nil,
margin_h = 3,
margin_v = 1,
radius = 2,
bordersize = 1,
bordercolor = 15,
bgcolor = 0,
rectcolor = 10,
percentage = nil,
}
function ProgressWidget:getSize()
return { w = self.width, h = self.height }
end
function ProgressWidget:paintTo(bb, x, y)
local my_size = self:getSize()
bb:paintRoundedRect(x, y, my_size.w, my_size.h, self.bgcolor, self.radius)
bb:paintBorder(x, y, my_size.w, my_size.h,
self.bordersize, self.bordercolor, self.radius)
bb:paintRect(x+self.margin_h, y+self.margin_v+self.bordersize,
(my_size.w-2*self.margin_h)*self.percentage,
(my_size.h-2*(self.margin_v+self.bordersize)), self.rectcolor)
end
function ProgressWidget:setPercentage(percentage)
self.percentage = percentage
end

@ -0,0 +1,27 @@
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

@ -0,0 +1,180 @@
require "ui/widget/base"
require "ui/rendertext"
--[[
A TextWidget puts a string on a single line
--]]
TextWidget = Widget:new{
text = nil,
face = nil,
color = 15,
_bb = nil,
_length = 0,
_height = 0,
_maxlength = 1200,
}
--function TextWidget:_render()
--local h = self.face.size * 1.3
--self._bb = Blitbuffer.new(self._maxlength, h)
--self._length = renderUtf8Text(self._bb, 0, h*0.8, self.face, self.text, self.color)
--end
function TextWidget:getSize()
--if not self._bb then
--self:_render()
--end
--return { w = self._length, h = self._bb:getHeight() }
local tsize = 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 * 1.5
return Geom:new{
w = self._length,
h = self._height,
}
end
function TextWidget:paintTo(bb, x, y)
--if not self._bb then
--self:_render()
--end
--bb:blitFrom(self._bb, x, y, 0, 0, self._length, self._bb:getHeight())
--@TODO Don't use kerning for monospaced fonts. (houqp)
renderUtf8Text(bb, x, y+self._height*0.7, self.face, self.text, true)
end
function TextWidget:free()
if self._bb then
self._bb:free()
self._bb = nil
end
end
--[[
A TextWidget that handles long text wrapping
--]]
TextBoxWidget = Widget:new{
text = nil,
face = nil,
color = 15,
width = 400, -- in pixels
line_height = 0.3, -- in em
v_list = nil,
_bb = nil,
_length = 0,
}
function TextBoxWidget:_wrapGreedyAlg(h_list)
local cur_line_width = 0
local space_w = sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x
local cur_line = {}
local v_list = {}
for k,w in ipairs(h_list) do
cur_line_width = cur_line_width + w.width
if cur_line_width <= self.width then
cur_line_width = cur_line_width + space_w
table.insert(cur_line, w)
else
-- wrap to next line
table.insert(v_list, cur_line)
cur_line = {}
cur_line_width = w.width + space_w
table.insert(cur_line, w)
end
end
-- handle last line
table.insert(v_list, cur_line)
return v_list
end
function TextBoxWidget:_getVerticalList(alg)
-- build horizontal list
h_list = {}
for w in self.text:gmatch("%S+") do
word_box = {}
word_box.word = w
word_box.width = sizeUtf8Text(0, Screen:getWidth(), self.face, w, true).x
table.insert(h_list, word_box)
end
-- @TODO check alg here 25.04 2012 (houqp)
-- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp)
return self:_wrapGreedyAlg(h_list)
end
function TextBoxWidget:_render()
self.v_list = self:_getVerticalList()
local v_list = self.v_list
local font_height = self.face.size
local line_height_px = self.line_height * font_height
local space_w = sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x
local h = (font_height + line_height_px) * #v_list - line_height_px
self._bb = Blitbuffer.new(self.width, h)
local y = font_height
local pen_x = 0
for _,l in ipairs(v_list) do
pen_x = 0
for _,w in ipairs(l) do
--@TODO Don't use kerning for monospaced fonts. (houqp)
-- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree
renderUtf8Text(self._bb, pen_x, y*0.8, self.face, w.word, true)
pen_x = pen_x + w.width + space_w
end
y = y + line_height_px + font_height
end
-- if text is shorter than one line, shrink to text's width
if #v_list == 1 then
self.width = pen_x
end
end
function TextBoxWidget:getSize()
if not self._bb then
self:_render()
end
return { w = self.width, h = self._bb:getHeight() }
end
function TextBoxWidget:paintTo(bb, x, y)
if not self._bb then
self:_render()
end
bb:blitFrom(self._bb, x, y, 0, 0, self.width, self._bb:getHeight())
end
function TextBoxWidget:free()
if self._bb then
self._bb:free()
self._bb = nil
end
end
--[[
FixedTextWidget
--]]
FixedTextWidget = TextWidget:new{}
function FixedTextWidget:getSize()
local tsize = 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)
renderUtf8Text(bb, x, y+self._height, self.face, self.text, true)
end

@ -1,13 +1,13 @@
#!./kpdfview
package.path = "./frontend/?.lua"
require "ui/ui"
require "ui/uimanager"
require "ui/widget/filechooser"
require "ui/widget/infomessage"
require "ui/readerui"
require "ui/filechooser"
require "ui/infomessage"
require "ui/button"
require "document/document"
require "settings"
require "dbg"
HomeMenu = InputContainer:new{
@ -79,7 +79,7 @@ function HomeMenu:onTapShowMenu()
dimen = Screen:getSize(),
home_menu,
}
home_menu.close_callback = function ()
home_menu.close_callback = function ()
UIManager:close(menu_container)
end
@ -120,7 +120,7 @@ function showHomePage(path)
end
return true
end,
file_filter = function(filename)
file_filter = function(filename)
if DocumentRegistry:getProvider(filename) then
return true
end
@ -176,11 +176,8 @@ end
local argidx = 1
if ARGV[1] == "-d" then
argidx = argidx + 1
G_debug_mode = true
os.execute("echo > ev.log")
-- create ev log file
G_ev_log = io.open("ev.log", "w")
Dbg:turnOn()
argidx = argidx + 1
else
DEBUG = function() end
end

@ -1,12 +1,11 @@
print(package.path)
package.path = "./frontend/?.lua"
require "ui/widget"
require "ui/ui"
require "ui/readerui"
require "ui/menu"
require "ui/infomessage"
require "ui/confirmbox"
require "ui/uimanager"
require "ui/widget/menu"
require "ui/widget/infomessage"
require "ui/widget/confirmbox"
require "document/document"
require "ui/readerui"
-----------------------------------------------------

Loading…
Cancel
Save