mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
36
frontend/dbg.lua
Normal file
36
frontend/dbg.lua
Normal file
@@ -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
|
||||
|
||||
68
frontend/ui/widget/base.lua
Normal file
68
frontend/ui/widget/base.lua
Normal file
@@ -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
|
||||
287
frontend/ui/widget/container.lua
Normal file
287
frontend/ui/widget/container.lua
Normal file
@@ -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(),
|
||||
166
frontend/ui/widget/group.lua
Normal file
166
frontend/ui/widget/group.lua
Normal file
@@ -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
|
||||
|
||||
45
frontend/ui/widget/image.lua
Normal file
45
frontend/ui/widget/image.lua
Normal file
@@ -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
|
||||
39
frontend/ui/widget/progress.lua
Normal file
39
frontend/ui/widget/progress.lua
Normal file
@@ -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
|
||||
|
||||
|
||||
|
||||
27
frontend/ui/widget/span.lua
Normal file
27
frontend/ui/widget/span.lua
Normal file
@@ -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
|
||||
|
||||
|
||||
180
frontend/ui/widget/text.lua
Normal file
180
frontend/ui/widget/text.lua
Normal file
@@ -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
|
||||
|
||||
|
||||
21
reader.lua
21
reader.lua
@@ -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
|
||||
|
||||
11
wtest.lua
11
wtest.lua
@@ -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"
|
||||
|
||||
|
||||
-----------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user