Merge branch 'new_ui_code' of github.com:hwhw/kindlepdfviewer into new_ui_code

pull/2/merge
Qingping Hou 12 years ago
commit 7ce032c6a6

@ -21,8 +21,13 @@
#include "blitbuffer.h"
inline int setPixel(BlitBuffer *bb, int x, int y, int c) {
inline int setPixel(lua_State *L, BlitBuffer *bb, int x, int y, int c) {
uint8_t *dstptr = (uint8_t*)(bb->data) + (y * bb->pitch) + (x / 2);
#ifndef NO_CHECK_BOUNDS
if(x < 0 || x >= bb->w || y < 0 || y >= bb->h) {
return luaL_error(L, "out of bounds in blitbuffer.setPixel()");
}
#endif
if(x % 2 == 0) {
*dstptr &= 0x0F;
@ -532,10 +537,10 @@ static int paintCircle(lua_State *L) {
/* draw two axles */
for(tmp_y = r; tmp_y > r2; tmp_y--) {
setPixel(dst, center_x+0, center_y+tmp_y, c);
setPixel(dst, center_x-0, center_y-tmp_y, c);
setPixel(dst, center_x+tmp_y, center_y+0, c);
setPixel(dst, center_x-tmp_y, center_y-0, c);
setPixel(L, dst, center_x+0, center_y+tmp_y, c);
setPixel(L, dst, center_x-0, center_y-tmp_y, c);
setPixel(L, dst, center_x+tmp_y, center_y+0, c);
setPixel(L, dst, center_x-tmp_y, center_y-0, c);
}
while(x < y) {
@ -563,21 +568,21 @@ static int paintCircle(lua_State *L) {
}
for(tmp_y = y; tmp_y > y2; tmp_y--) {
setPixel(dst, center_x+x, center_y+tmp_y, c);
setPixel(dst, center_x+tmp_y, center_y+x, c);
setPixel(L, dst, center_x+x, center_y+tmp_y, c);
setPixel(L, dst, center_x+tmp_y, center_y+x, c);
setPixel(dst, center_x+tmp_y, center_y-x, c);
setPixel(dst, center_x+x, center_y-tmp_y, c);
setPixel(L, dst, center_x+tmp_y, center_y-x, c);
setPixel(L, dst, center_x+x, center_y-tmp_y, c);
setPixel(dst, center_x-x, center_y-tmp_y, c);
setPixel(dst, center_x-tmp_y, center_y-x, c);
setPixel(L, dst, center_x-x, center_y-tmp_y, c);
setPixel(L, dst, center_x-tmp_y, center_y-x, c);
setPixel(dst, center_x-tmp_y, center_y+x, c);
setPixel(dst, center_x-x, center_y+tmp_y, c);
setPixel(L, dst, center_x-tmp_y, center_y+x, c);
setPixel(L, dst, center_x-x, center_y+tmp_y, c);
}
}
if(r == w) {
setPixel(dst, center_x, center_y, c);
setPixel(L, dst, center_x, center_y, c);
}
return 0;
}
@ -617,10 +622,10 @@ static int paintRoundedCorner(lua_State *L) {
/* draw two axles */
/*for(tmp_y = r; tmp_y > r2; tmp_y--) {*/
/*setPixel(dst, (w-r)+off_x+0, (h-r)+off_y+tmp_y-1, c);*/
/*setPixel(dst, (w-r)+off_x-0, (r)+off_y-tmp_y, c);*/
/*setPixel(dst, (w-r)+off_x+tmp_y, (h-r)+off_y+0, c);*/
/*setPixel(dst, (r)+off_x-tmp_y, (h-r)+off_y-0-1, c);*/
/*setPixel(L, dst, (w-r)+off_x+0, (h-r)+off_y+tmp_y-1, c);*/
/*setPixel(L, dst, (w-r)+off_x-0, (r)+off_y-tmp_y, c);*/
/*setPixel(L, dst, (w-r)+off_x+tmp_y, (h-r)+off_y+0, c);*/
/*setPixel(L, dst, (r)+off_x-tmp_y, (h-r)+off_y-0-1, c);*/
/*}*/
while(x < y) {
@ -648,17 +653,17 @@ static int paintRoundedCorner(lua_State *L) {
}
for(tmp_y = y; tmp_y > y2; tmp_y--) {
setPixel(dst, (w-r)+off_x+x-1, (h-r)+off_y+tmp_y-1, c);
setPixel(dst, (w-r)+off_x+tmp_y-1, (h-r)+off_y+x-1, c);
setPixel(L, dst, (w-r)+off_x+x-1, (h-r)+off_y+tmp_y-1, c);
setPixel(L, dst, (w-r)+off_x+tmp_y-1, (h-r)+off_y+x-1, c);
setPixel(dst, (w-r)+off_x+tmp_y-1, (r)+off_y-x, c);
setPixel(dst, (w-r)+off_x+x-1, (r)+off_y-tmp_y, c);
setPixel(L, dst, (w-r)+off_x+tmp_y-1, (r)+off_y-x, c);
setPixel(L, dst, (w-r)+off_x+x-1, (r)+off_y-tmp_y, c);
setPixel(dst, (r)+off_x-x, (r)+off_y-tmp_y, c);
setPixel(dst, (r)+off_x-tmp_y, (r)+off_y-x, c);
setPixel(L, dst, (r)+off_x-x, (r)+off_y-tmp_y, c);
setPixel(L, dst, (r)+off_x-tmp_y, (r)+off_y-x, c);
setPixel(dst, (r)+off_x-tmp_y, (h-r)+off_y+x-1, c);
setPixel(dst, (r)+off_x-x, (h-r)+off_y+tmp_y-1, c);
setPixel(L, dst, (r)+off_x-tmp_y, (h-r)+off_y+x-1, c);
setPixel(L, dst, (r)+off_x-x, (h-r)+off_y+tmp_y-1, c);
}
}
return 0;

@ -55,7 +55,7 @@ function CreDocument:init()
local style_sheet = "./data/"..file_type..".css"
ok, self._document = pcall(cre.openDocument, self.file, style_sheet,
G_width, G_height)
Screen:getWidth(), Screen:getHeight())
if not ok then
self.error_message = self.doc -- will contain error message
return

@ -105,7 +105,7 @@ end
-- calculates page dimensions
function Document:getPageDimensions(pageno, zoom, rotation)
local native_dimen = Geom:copy(self:getNativePageDimensions(pageno))
local native_dimen = self:getNativePageDimensions(pageno):copy()
if rotation == 90 or rotation == 270 then
-- switch orientation
native_dimen.w, native_dimen.h = native_dimen.h, native_dimen.w

@ -0,0 +1,45 @@
require "ui/widget"
--[[
a button widget
]]
Button = WidgetContainer:new{
text = nil, -- mandatory
preselect = false
}
function Button:init()
-- set FrameContainer content
self[1] = FrameContainer:new{
margin = 0,
bordersize = 3,
background = 0,
radius = 15,
padding = 2,
HorizontalGroup:new{
HorizontalSpan:new{ width = 8 },
TextWidget:new{
text = self.text,
face = Font:getFace("cfont", 20)
},
HorizontalSpan:new{ width = 8 },
}
}
if self.preselect then
self[1].color = 15
else
self[1].color = 5
end
end
function Button:onFocus()
self[1].color = 15
return true
end
function Button:onUnfocus()
self[1].color = 5
return true
end

@ -0,0 +1,82 @@
require "ui/widget"
require "ui/focusmanager"
require "ui/button"
--[[
Widget that shows a message and OK/Cancel buttons
]]
ConfirmBox = FocusManager:new{
text = "no text",
width = nil,
ok_text = "OK",
cancel_text = "Cancel",
ok_callback = function() end,
cancel_callback = function() end,
}
function ConfirmBox:init()
-- calculate box width on the fly if not given
if not self.width then
self.width = Screen:getWidth() - 200
end
-- build bottons
self.key_events.Close = { {{"Home","Back"}}, doc = "cancel" }
self.key_events.Select = { {{"Enter","Press"}}, doc = "chose selected option" }
local ok_button = Button:new{
text = self.ok_text,
}
local cancel_button = Button:new{
text = self.cancel_text,
preselect = true
}
self.layout = { { ok_button, cancel_button } }
self.selected.x = 2 -- Cancel is default
self[1] = CenterContainer:new{
dimen = Screen:getSize(),
FrameContainer:new{
margin = 2,
background = 0,
padding = 10,
HorizontalGroup:new{
ImageWidget:new{
file = "resources/info-i.png"
},
HorizontalSpan:new{ width = 10 },
VerticalGroup:new{
align = "left",
TextBoxWidget:new{
text = self.text,
face = Font:getFace("cfont", 30),
width = self.width,
},
VerticalSpan:new{ width = 10 },
HorizontalGroup:new{
ok_button,
HorizontalSpan:new{ width = 10 },
cancel_button,
}
}
}
}
}
end
function ConfirmBox:onClose()
UIManager:close(self)
return true
end
function ConfirmBox:onSelect()
DEBUG("selected:", self.selected.x)
if self.selected.x == 1 then
self:ok_callback()
else
self:cancel_callback()
end
UIManager:close(self)
return true
end

@ -1,569 +0,0 @@
require "ui/widget"
require "ui/font"
--[[
Wrapper Widget that manages focus for a whole dialog
supports a 2D model of active elements
e.g.:
layout = {
{ textinput, textinput },
{ okbutton, cancelbutton }
}
this is a dialog with 2 rows. in the top row, there is the
single (!) widget <textinput>. when the focus is in this
group, left/right movement seems (!) to be doing nothing.
in the second row, there are two widgets and you can move
left/right. also, you can go up from both to reach <textinput>,
and from that go down and (depending on internat coordinates)
reach either <okbutton> or <cancelbutton>.
but notice that this does _not_ do the layout for you,
it rather defines an abstract layout.
]]
FocusManager = InputContainer:new{
selected = nil, -- defaults to x=1, y=1
layout = nil, -- mandatory
movement_allowed = { x = true, y = true }
}
function FocusManager:init()
self.selected = { x = 1, y = 1 }
self.key_events = {
-- these will all generate the same event, just with different arguments
FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} },
FocusDown = { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} },
FocusLeft = { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} },
FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} },
}
end
function FocusManager:onFocusMove(args)
local dx, dy = unpack(args)
if (dx ~= 0 and not self.movement_allowed.x)
or (dy ~= 0 and not self.movement_allowed.y) then
return true
end
local current_item = self.layout[self.selected.y][self.selected.x]
while true do
if self.selected.x + dx > #self.layout[self.selected.y]
or self.selected.x + dx < 1 then
break -- abort when we run into horizontal borders
end
-- move cyclic in vertical direction
if self.selected.y + dy > #self.layout then
self.selected.y = 1
elseif self.selected.y + dy < 1 then
self.selected.y = #self.layout
else
self.selected.y = self.selected.y + dy
end
self.selected.x = self.selected.x + dx
if self.layout[self.selected.y][self.selected.x] ~= current_item
or not self.layout[self.selected.y][self.selected.x].is_inactive then
-- we found a different object to focus
current_item:handleEvent(Event:new("Unfocus"))
self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus"))
-- trigger a repaint (we need to be the registered widget!)
UIManager:setDirty(self)
break
end
end
return true
end
--[[
a button widget
]]
Button = WidgetContainer:new{
text = nil, -- mandatory
preselect = false
}
function Button:init()
-- set FrameContainer content
self[1] = FrameContainer:new{
margin = 0,
bordersize = 3,
background = 0,
radius = 15,
padding = 2,
HorizontalGroup:new{
HorizontalSpan:new{ width = 8 },
TextWidget:new{
text = self.text,
face = Font:getFace("cfont", 20)
},
HorizontalSpan:new{ width = 8 },
}
}
if self.preselect then
self[1].color = 15
else
self[1].color = 5
end
end
function Button:onFocus()
self[1].color = 15
return true
end
function Button:onUnfocus()
self[1].color = 5
return true
end
--[[
Widget that shows a message and OK/Cancel buttons
]]
ConfirmBox = FocusManager:new{
text = "no text",
width = nil,
ok_text = "OK",
cancel_text = "Cancel",
ok_callback = function() end,
cancel_callback = function() end,
}
function ConfirmBox:init()
-- calculate box width on the fly if not given
if not self.width then
self.width = G_width - 200
end
-- build bottons
self.key_events.Close = { {{"Home","Back"}}, doc = "cancel" }
self.key_events.Select = { {{"Enter","Press"}}, doc = "chose selected option" }
local ok_button = Button:new{
text = self.ok_text,
}
local cancel_button = Button:new{
text = self.cancel_text,
preselect = true
}
self.layout = { { ok_button, cancel_button } }
self.selected.x = 2 -- Cancel is default
self[1] = CenterContainer:new{
dimen = { w = G_width, h = G_height },
FrameContainer:new{
margin = 2,
background = 0,
padding = 10,
HorizontalGroup:new{
ImageWidget:new{
file = "resources/info-i.png"
},
HorizontalSpan:new{ width = 10 },
VerticalGroup:new{
align = "left",
TextBoxWidget:new{
text = self.text,
face = Font:getFace("cfont", 30),
width = self.width,
},
VerticalSpan:new{ width = 10 },
HorizontalGroup:new{
ok_button,
HorizontalSpan:new{ width = 10 },
cancel_button,
}
}
}
}
}
end
function ConfirmBox:onClose()
UIManager:close(self)
return true
end
function ConfirmBox:onSelect()
DEBUG("selected:", self.selected.x)
if self.selected.x == 1 then
self:ok_callback()
else
self:cancel_callback()
end
UIManager:close(self)
return true
end
--[[
Widget that displays an informational message
it vanishes on key press or after a given timeout
]]
InfoMessage = InputContainer:new{
face = Font:getFace("infofont", 25),
text = "",
timeout = nil,
key_events = {
AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" }
}
}
function InfoMessage:init()
-- we construct the actual content here because self.text is only available now
self[1] = CenterContainer:new{
dimen = { w = G_width, h = G_height },
FrameContainer:new{
margin = 2,
background = 0,
HorizontalGroup:new{
align = "center",
ImageWidget:new{
file = "resources/info-i.png"
},
HorizontalSpan:new{ width = 10 },
TextBoxWidget:new{
text = self.text,
face = Font:getFace("cfont", 30)
}
}
}
}
end
function InfoMessage:onShow()
-- triggered by the UIManager after we got successfully shown (not yet painted)
if self.timeout then
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
end
return true
end
function InfoMessage:onAnyKeyPressed()
-- triggered by our defined key events
UIManager:close(self)
return true
end
--[[
Widget that displays a shortcut icon for menu item
]]
ItemShortCutIcon = WidgetContainer:new{
width = 22,
height = 22,
key = nil,
bordersize = 2,
radius = 0,
style = "square"
}
function ItemShortCutIcon:init()
if not self.key then
return
end
local radius = 0
local background = 0
if self.style == "rounded_corner" then
radius = math.floor(self.width/2)
elseif self.style == "grey_square" then
background = 3
end
--@TODO calculate font size by icon size 01.05 2012 (houqp)
if self.key:len() > 1 then
sc_face = Font:getFace("ffont", 14)
else
sc_face = Font:getFace("scfont", 22)
end
self[1] = FrameContainer:new{
padding = 0,
bordersize = self.bordersize,
radius = radius,
background = background,
dimen = {
w = self.width,
h = self.height,
},
CenterContainer:new{
dimen = {
w = self.width,
h = self.height,
},
TextWidget:new{
text = self.key,
face = sc_face,
},
},
}
end
--[[
Widget that displays an item for menu
]]
MenuItem = InputContainer:new{
text = nil,
detail = nil,
face = Font:getFace("cfont", 22),
width = nil,
height = nil,
shortcut = nil,
shortcut_style = "square",
_underline_container = nil,
}
function MenuItem:init()
local shortcut_icon_w = 0
local shortcut_icon_h = 0
if self.shortcut then
shortcut_icon_w = math.floor(self.height*4/5)
shortcut_icon_h = shortcut_icon_w
end
self.detail = self.text
-- 15 for HorizontalSpan,
self.content_width = self.width - shortcut_icon_w - 15
-- we need this table per-instance, so we declare it here
self.active_key_events = {
Select = { {"Press"}, doc = "chose selected item" },
}
w = sizeUtf8Text(0, self.width, self.face, self.text, true).x
if w >= self.content_width then
self.active_key_events.ShowItemDetail = { {"Right"}, doc = "show item detail" }
indicator = " >>"
indicator_w = sizeUtf8Text(0, self.width, self.face, indicator, true).x
self.text = getSubTextByWidth(self.text, self.face,
self.content_width - indicator_w, true) .. indicator
end
self._underline_container = UnderlineContainer:new{
dimen = {
w = self.content_width,
h = self.height
},
HorizontalGroup:new {
align = "center",
TextWidget:new{
text = self.text,
face = self.face,
},
},
}
self[1] = HorizontalGroup:new{
HorizontalSpan:new{ width = 5 },
ItemShortCutIcon:new{
width = shortcut_icon_w,
height = shortcut_icon_h,
key = self.shortcut,
radius = shortcut_icon_r,
style = self.shortcut_style,
},
HorizontalSpan:new{ width = 10 },
self._underline_container
}
end
function MenuItem:onFocus()
self._underline_container.color = 10
self.key_events = self.active_key_events
return true
end
function MenuItem:onUnfocus()
self._underline_container.color = 0
self.key_events = { }
return true
end
function MenuItem:onShowItemDetail()
UIManager:show(InfoMessage:new{
text=self.detail,
})
return true
end
--[[
Widget that displays menu
]]
Menu = FocusManager:new{
-- face for displaying item contents
cface = Font:getFace("cfont", 22),
-- face for menu title
tface = Font:getFace("tfont", 25),
-- face for paging info display
fface = Font:getFace("ffont", 16),
-- font for item shortcut
sface = Font:getFace("scfont", 20),
title = "No Title",
height = 500,
width = 500,
item_table = {},
item_shortcuts = {
"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
"A", "S", "D", "F", "G", "H", "J", "K", "L", "Del",
"Z", "X", "C", "V", "B", "N", "M", ".", "Sym", "Enter",
},
is_enable_shortcut = true,
item_height = 36,
page = 1,
on_select_callback = function() end,
item_group = nil,
page_info = nil,
}
function Menu:init()
self.perpage = math.floor(self.height / self.item_height) - 2
self.page = 1
self.page_num = math.ceil(#self.item_table / self.perpage)
-- set up keyboard events
self.key_events.Close = { {"Back"}, doc = "close menu" }
self.key_events.NextPage = {
{Input.group.PgFwd}, doc = "goto next page of the menu"
}
self.key_events.PrevPage = {
{Input.group.PgBack}, doc = "goto previous page of the menu"
}
-- we won't catch presses to "Right"
self.key_events.FocusRight = nil
if self.is_enable_shortcut then
self.key_events.SelectByShortCut = { {self.item_shortcuts} }
end
self.key_events.Select = { {"Press"}, doc = "select current menu item"}
-- group for items
self.item_group = VerticalGroup:new{}
self.page_info = TextWidget:new{
face = self.fface,
}
self[1] = CenterContainer:new{
FrameContainer:new{
background = 0,
radius = math.floor(self.width/20),
VerticalGroup:new{
TextWidget:new{
text = self.title,
face = self.tface,
},
self.item_group,
self.page_info,
}, -- VerticalGroup
}, -- FrameContainer
dimen = {w = G_width, h = G_height},
} -- CenterContainer
if #self.item_table > 0 then
-- if the table is not yet initialized, this call
-- must be done manually:
self:updateItems()
end
end
function Menu:updateItems()
self.layout = {}
self.item_group:clear()
for c = 1, self.perpage do
local i = (self.page - 1) * self.perpage + c
if i <= #self.item_table then
local item_shortcut = nil
local shortcut_style = "square"
if self.is_enable_shortcut then
-- give different shortcut_style to keys in different
-- lines of keyboard
if c >= 11 and c <= 20 then
--shortcut_style = "rounded_corner"
shortcut_style = "grey_square"
end
item_shortcut = self.item_shortcuts[c]
if item_shortcut == "Enter" then
item_shortcut = "Ent"
end
end
item_tmp = MenuItem:new{
text = self.item_table[i].text,
face = self.cface,
width = self.width - 14,
height = self.item_height,
shortcut = item_shortcut,
shortcut_style = shortcut_style,
}
table.insert(self.item_group, item_tmp)
table.insert(self.layout, {item_tmp})
--self.last_shortcut = c
end -- if i <= self.items
end -- for c=1, self.perpage
-- set focus to first menu item
self.item_group[1]:onFocus()
-- reset focus manager accordingly
self.selected = { x = 1, y = 1 }
-- update page information
self.page_info.text = "page "..self.page.."/"..self.page_num
UIManager:setDirty(self)
end
function Menu:onSelectByShortCut(_, keyevent)
for k,v in ipairs(self.item_shortcuts) do
if k > self.perpage then
break
elseif v == keyevent.key then
local item = self.item_table[(self.page-1)*self.perpage+k]
self.item_table = nil
UIManager:close(self)
self.on_select_callback(item)
break
end
end
return true
end
function Menu:onNextPage()
if self.page < self.page_num then
self.page = self.page + 1
self:updateItems()
end
return true
end
function Menu:onPrevPage()
if self.page > 1 then
self.page = self.page - 1
self:updateItems()
end
return true
end
function Menu:onSelect()
UIManager:close(self)
self.on_select_callback(self.item_table[(self.page-1)*self.perpage+self.selected.y])
return true
end
function Menu:onClose()
UIManager:close(self)
return true
end

@ -1,4 +1,4 @@
require "ui/dialog" -- for Menu
require "ui/menu"
FileChooser = Menu:new{
path = ".",
@ -43,15 +43,18 @@ function FileChooser:changeToPath(path)
Menu.init(self) -- call parent's init()
end
function FileChooser:onSelect()
local selected = self.item_table[(self.page-1)*self.perpage+self.selected.y]
if lfs.attributes(selected.path, "mode") == "directory" then
function FileChooser:onMenuSelect(item)
if lfs.attributes(item.path, "mode") == "directory" then
UIManager:close(self)
self:changeToPath(selected.path)
self:changeToPath(item.path)
UIManager:show(self)
else
UIManager:close(self)
self.on_select_callback(self.item_table[self.selected.y])
self:onFileSelect(item.path)
end
return true
end
function FileChooser:onFileSelect(file)
UIManager:close(self)
return true
end

@ -0,0 +1,82 @@
--[[
Wrapper Widget that manages focus for a whole dialog
supports a 2D model of active elements
e.g.:
layout = {
{ textinput, textinput },
{ okbutton, cancelbutton }
}
this is a dialog with 2 rows. in the top row, there is the
single (!) widget <textinput>. when the focus is in this
group, left/right movement seems (!) to be doing nothing.
in the second row, there are two widgets and you can move
left/right. also, you can go up from both to reach <textinput>,
and from that go down and (depending on internat coordinates)
reach either <okbutton> or <cancelbutton>.
but notice that this does _not_ do the layout for you,
it rather defines an abstract layout.
]]
FocusManager = InputContainer:new{
selected = nil, -- defaults to x=1, y=1
layout = nil, -- mandatory
movement_allowed = { x = true, y = true }
}
function FocusManager:init()
self.selected = { x = 1, y = 1 }
self.key_events = {
-- these will all generate the same event, just with different arguments
FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} },
FocusDown = { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} },
FocusLeft = { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} },
FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} },
}
end
function FocusManager:onFocusMove(args)
local dx, dy = unpack(args)
if (dx ~= 0 and not self.movement_allowed.x)
or (dy ~= 0 and not self.movement_allowed.y) then
return true
end
if not self.layout or not self.layout[self.selected.y] or not self.layout[self.selected.y][self.selected.x] then
return true
end
local current_item = self.layout[self.selected.y][self.selected.x]
while true do
if self.selected.x + dx > #self.layout[self.selected.y]
or self.selected.x + dx < 1 then
break -- abort when we run into horizontal borders
end
-- move cyclic in vertical direction
if self.selected.y + dy > #self.layout then
self.selected.y = 1
elseif self.selected.y + dy < 1 then
self.selected.y = #self.layout
else
self.selected.y = self.selected.y + dy
end
self.selected.x = self.selected.x + dx
if self.layout[self.selected.y][self.selected.x] ~= current_item
or not self.layout[self.selected.y][self.selected.x].is_inactive then
-- we found a different object to focus
current_item:handleEvent(Event:new("Unfocus"))
self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus"))
-- trigger a repaint (we need to be the registered widget!)
UIManager:setDirty(self)
break
end
end
return true
end

@ -23,12 +23,12 @@ function Geom:new(o)
return o
end
function Geom:copy(o)
local n = self:new()
n.x = o.x
n.y = o.y
n.w = o.w
n.h = o.h
function Geom:copy()
local n = Geom:new()
n.x = self.x
n.y = self.y
n.w = self.w
n.h = self.h
return n
end
@ -42,6 +42,7 @@ offset rectangle or point by relative values
function Geom:offsetBy(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
return self
end
--[[
@ -50,6 +51,7 @@ offset rectangle or point to certain coordinates
function Geom:offsetTo(x, y)
self.x = x
self.y = y
return self
end
--[[
@ -60,6 +62,18 @@ if a single factor is given, it is applied to both width and height
function Geom:scaleBy(zx, zy)
self.w = self.w * zx
self.h = self.h * (zy or zx)
return self
end
--[[
enlarges or shrinks dimensions or rectangles
note that for rectangles the offset stays the same
]]--
function Geom:changeSizeBy(dw, dh)
self.w = self.w + dw
self.h = self.h + dh
return self
end
--[[
@ -118,12 +132,9 @@ end
set size of dimension or rectangle to size of given dimension/rectangle
]]--
function Geom:setSizeTo(rect_b)
if self.w == rect_b.w and self.h == rect_b.h then
return false
end
self.w = rect_b.w
self.h = rect_b.h
return true
return self
end
--[[

@ -0,0 +1,54 @@
require "ui/ui"
require "ui/widget"
--[[
Widget that displays an informational message
it vanishes on key press or after a given timeout
]]
InfoMessage = InputContainer:new{
face = Font:getFace("infofont", 25),
text = "",
timeout = nil,
key_events = {
AnyKeyPressed = { { Input.group.Any }, seqtext = "any key", doc = "close dialog" }
}
}
function InfoMessage:init()
-- we construct the actual content here because self.text is only available now
self[1] = CenterContainer:new{
dimen = Screen:getSize(),
FrameContainer:new{
margin = 2,
background = 0,
HorizontalGroup:new{
align = "center",
ImageWidget:new{
file = "resources/info-i.png"
},
HorizontalSpan:new{ width = 10 },
TextBoxWidget:new{
text = self.text,
face = Font:getFace("cfont", 30)
}
}
}
}
end
function InfoMessage:onShow()
-- triggered by the UIManager after we got successfully shown (not yet painted)
if self.timeout then
UIManager:scheduleIn(self.timeout, function() UIManager:close(self) end)
end
return true
end
function InfoMessage:onAnyKeyPressed()
-- triggered by our defined key events
UIManager:close(self)
return true
end

@ -0,0 +1,344 @@
require "ui/widget"
require "ui/focusmanager"
require "ui/infomessage"
require "ui/font"
--[[
Widget that displays a shortcut icon for menu item
]]
ItemShortCutIcon = WidgetContainer:new{
dimen = Geom:new{ w = 22, h = 22 },
key = nil,
bordersize = 2,
radius = 0,
style = "square"
}
function ItemShortCutIcon:init()
if not self.key then
return
end
local radius = 0
local background = 0
if self.style == "rounded_corner" then
radius = math.floor(self.width/2)
elseif self.style == "grey_square" then
background = 3
end
--@TODO calculate font size by icon size 01.05 2012 (houqp)
if self.key:len() > 1 then
sc_face = Font:getFace("ffont", 14)
else
sc_face = Font:getFace("scfont", 22)
end
self[1] = FrameContainer:new{
padding = 0,
bordersize = self.bordersize,
radius = radius,
background = background,
dimen = self.dimen,
CenterContainer:new{
dimen = self.dimen,
TextWidget:new{
text = self.key,
face = sc_face,
},
},
}
end
--[[
Widget that displays an item for menu
]]
MenuItem = InputContainer:new{
text = nil,
detail = nil,
face = Font:getFace("cfont", 22),
dimen = nil,
shortcut = nil,
shortcut_style = "square",
_underline_container = nil,
}
function MenuItem:init()
local shortcut_icon_dimen = Geom:new()
if self.shortcut then
shortcut_icon_dimen.w = math.floor(self.dimen.h*4/5)
shortcut_icon_dimen.h = shortcut_icon_dimen.w
end
self.detail = self.text
-- 15 for HorizontalSpan,
self.content_width = self.dimen.w - shortcut_icon_dimen.w - 15
-- we need this table per-instance, so we declare it here
self.active_key_events = {
Select = { {"Press"}, doc = "chose selected item" },
}
w = sizeUtf8Text(0, self.dimen.w, self.face, self.text, true).x
if w >= self.content_width then
self.active_key_events.ShowItemDetail = { {"Right"}, doc = "show item detail" }
indicator = " >>"
indicator_w = sizeUtf8Text(0, self.dimen.w, self.face, indicator, true).x
self.text = getSubTextByWidth(self.text, self.face,
self.content_width - indicator_w, true) .. indicator
end
self._underline_container = UnderlineContainer:new{
dimen = Geom:new{
w = self.content_width,
h = self.dimen.h
},
HorizontalGroup:new {
align = "center",
TextWidget:new{
text = self.text,
face = self.face,
},
},
}
self[1] = HorizontalGroup:new{
HorizontalSpan:new{ width = 5 },
ItemShortCutIcon:new{
dimen = shortcut_icon_dimen,
key = self.shortcut,
radius = shortcut_icon_r,
style = self.shortcut_style,
},
HorizontalSpan:new{ width = 10 },
self._underline_container
}
end
function MenuItem:onFocus()
self._underline_container.color = 10
self.key_events = self.active_key_events
return true
end
function MenuItem:onUnfocus()
self._underline_container.color = 0
self.key_events = { }
return true
end
function MenuItem:onShowItemDetail()
UIManager:show(InfoMessage:new{
text=self.detail,
})
return true
end
--[[
Widget that displays menu
]]
Menu = FocusManager:new{
-- face for displaying item contents
cface = Font:getFace("cfont", 22),
-- face for menu title
tface = Font:getFace("tfont", 25),
-- face for paging info display
fface = Font:getFace("ffont", 16),
-- font for item shortcut
sface = Font:getFace("scfont", 20),
title = "No Title",
dimen = Geom:new{ w = 500, h = 500 },
item_table = {},
item_shortcuts = {
"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
"A", "S", "D", "F", "G", "H", "J", "K", "L", "Del",
"Z", "X", "C", "V", "B", "N", "M", ".", "Sym", "Enter",
},
is_enable_shortcut = true,
item_dimen = nil,
page = 1,
item_group = nil,
page_info = nil,
-- set this to true to not paint as popup menu
is_borderless = false,
}
function Menu:init()
self.item_dimen = Geom:new{
w = self.dimen.w,
h = 36, -- hardcoded for now
}
self.perpage = math.floor(self.dimen.h / self.item_dimen.h) - 2
self.page = 1
self.page_num = math.ceil(#self.item_table / self.perpage)
-- set up keyboard events
self.key_events.Close = { {"Back"}, doc = "close menu" }
self.key_events.NextPage = {
{Input.group.PgFwd}, doc = "goto next page of the menu"
}
self.key_events.PrevPage = {
{Input.group.PgBack}, doc = "goto previous page of the menu"
}
-- we won't catch presses to "Right"
self.key_events.FocusRight = nil
if self.is_enable_shortcut then
self.key_events.SelectByShortCut = { {self.item_shortcuts} }
end
self.key_events.Select = { {"Press"}, doc = "select current menu item"}
-- group for items
self.item_group = VerticalGroup:new{}
self.page_info = TextWidget:new{
face = self.fface,
}
local content = VerticalGroup:new{
TextWidget:new{
text = self.title,
face = self.tface,
},
self.item_group,
self.page_info,
} -- VerticalGroup
if not self.is_borderless then
self[1] = CenterContainer:new{
FrameContainer:new{
background = 0,
radius = math.floor(self.dimen.w/20),
content
},
dimen = Screen:getSize(),
}
-- we need to substract border, margin and padding
self.item_dimen.w = self.item_dimen.w - 14
else
self[1] = FrameContainer:new{
background = 0,
bordersize = 0,
padding = 0,
margin = 0,
dimen = Screen:getSize(),
content
}
end
if #self.item_table > 0 then
-- if the table is not yet initialized, this call
-- must be done manually:
self:updateItems()
end
end
function Menu:updateItems()
self.layout = {}
self.item_group:clear()
for c = 1, self.perpage do
local i = (self.page - 1) * self.perpage + c
if i <= #self.item_table then
local item_shortcut = nil
local shortcut_style = "square"
if self.is_enable_shortcut then
-- give different shortcut_style to keys in different
-- lines of keyboard
if c >= 11 and c <= 20 then
--shortcut_style = "rounded_corner"
shortcut_style = "grey_square"
end
item_shortcut = self.item_shortcuts[c]
if item_shortcut == "Enter" then
item_shortcut = "Ent"
end
end
local item_tmp = MenuItem:new{
text = self.item_table[i].text,
face = self.cface,
dimen = self.item_dimen,
shortcut = item_shortcut,
shortcut_style = shortcut_style,
}
table.insert(self.item_group, item_tmp)
table.insert(self.layout, {item_tmp})
--self.last_shortcut = c
end -- if i <= self.items
end -- for c=1, self.perpage
-- set focus to first menu item
if self.item_group[1] then
self.item_group[1]:onFocus()
-- reset focus manager accordingly
self.selected = { x = 1, y = 1 }
-- update page information
self.page_info.text = "page "..self.page.."/"..self.page_num
else
self.page_info.text = "no choices available"
end
UIManager:setDirty(self)
end
function Menu:onSelectByShortCut(_, keyevent)
for k,v in ipairs(self.item_shortcuts) do
if k > self.perpage then
break
elseif v == keyevent.key then
if self.item_table[(self.page-1)*self.perpage + k] then
self:onMenuSelect(self.item_table[(self.page-1)*self.perpage + k])
end
break
end
end
return true
end
--[[
override this function to process the item selected in a different manner
]]--
function Menu:onMenuSelect(item)
UIManager:close(self)
self:onMenuChoice(item)
return true
end
--[[
override this function to handle the choice
]]--
function Menu:onMenuChoice(item)
return true
end
function Menu:onNextPage()
if self.page < self.page_num then
self.page = self.page + 1
self:updateItems()
end
return true
end
function Menu:onPrevPage()
if self.page > 1 then
self.page = self.page - 1
self:updateItems()
end
return true
end
function Menu:onSelect()
self:onMenuSelect(self.item_table[(self.page-1)*self.perpage+self.selected.y])
return true
end
function Menu:onClose()
UIManager:close(self)
return true
end

@ -2,7 +2,7 @@ ReaderToc = InputContainer:new{
key_events = {
ShowToc = { {"T"}, doc = "show Table of Content menu"},
},
dimen = Geom:new{ w = G_width-20, h = G_height-20},
dimen = Geom:new{ w = Screen:getWidth()-20, h = Screen:getHeight()-20},
current_page = 0,
current_pos = 0,
}
@ -51,22 +51,21 @@ function ReaderToc:getTocTitleOfCurrentPage()
end
function ReaderToc:onShowToc()
function callback(item)
self.ui:handleEvent(Event:new("PageUpdate", item.page))
end
local items = self.ui.document:getToc()
-- build menu items
for _,v in ipairs(items) do
v.text = (" "):rep(v.depth-1)..self:cleanUpTocTitle(v.title)
end
toc_menu = Menu:new{
local toc_menu = Menu:new{
title = "Table of Contents",
item_table = items,
width = self.dimen.w,
height = self.dimen.h,
on_select_callback = callback,
ui = self.ui
}
function toc_menu:onMenuChoice(item)
self.ui:handleEvent(Event:new("PageUpdate", item.page))
end
UIManager:show(toc_menu)
end

@ -76,7 +76,7 @@ end
function ReaderView:PanningUpdate(dx, dy)
DEBUG("pan by", dx, dy)
local old = Geom:copy(self.visible_area)
local old = self.visible_area:copy()
self.visible_area:offsetWithin(self.page_area, dx, dy)
if self.visible_area ~= old then
-- flag a repaint

@ -46,6 +46,8 @@ Screen = {
kpv_rotation_mode = nil,
saved_bb = nil,
fb = einkfb.open("/dev/fb0")
}
-- @orien: 1 for clockwise rotate, -1 for anti-clockwise
@ -61,13 +63,28 @@ function Screen:screenRotate(orien)
self.cur_rotation_mode = (self.cur_rotation_mode + orien) % 4
-- you have to reopen framebuffer after rotate
fb:setOrientation(self.cur_rotation_mode)
fb:close()
fb = einkfb.open("/dev/fb0")
self.fb:setOrientation(self.cur_rotation_mode)
self.fb:close()
self.fb = einkfb.open("/dev/fb0")
end
function Screen:getSize()
local w, h = self.fb:getSize()
return Geom:new{w = w, h = h}
end
function Screen:getWidth()
local w, _ = self.fb:getSize()
return w
end
function Screen:getHeight()
local _, h = self.fb:getSize()
return h
end
function Screen:updateRotationMode()
if KEY_FW_DOWN == 116 then -- in EMU mode always set to 0
if util.isEmulated() then -- in EMU mode always set to 0
self.cur_rotation_mode = 0
else
orie_fd = assert(io.open("/sys/module/eink_fb_hal_broads/parameters/bs_orientation", "r"))
@ -77,7 +94,7 @@ function Screen:updateRotationMode()
end
function Screen:saveCurrentBB()
local width, height = G_width, G_height
local width, height = self:getWidth(), self.getHeight()
if not self.saved_bb then
self.saved_bb = Blitbuffer.new(width, height)
@ -94,14 +111,14 @@ function Screen:restoreFromSavedBB()
end
function Screen:getCurrentScreenBB()
local bb = Blitbuffer.new(G_width, G_height)
bb:blitFullFrom(fb.bb)
local bb = Blitbuffer.new(self:getWidth(), self:getHeight())
bb:blitFullFrom(self.fb.bb)
return bb
end
function Screen:restoreFromBB(bb)
if bb then
fb.bb:blitFullFrom(bb)
self.fb.bb:blitFullFrom(bb)
else
DEBUG("Got nil bb in restoreFromSavedBB!")
end

@ -2,16 +2,10 @@ require "ui/geometry"
require "ui/inputevent"
require "ui/widget"
require "ui/screen"
require "ui/dialog"
require "settings" -- for DEBUG(), TODO: put DEBUG() somewhere else
-- we also initialize the framebuffer
fb = einkfb.open("/dev/fb0")
G_width, G_height = fb:getSize()
-- and the input handling
-- initialize the input handling
Input:init()
@ -151,7 +145,7 @@ function UIManager:run()
local dirty = false
for _, widget in ipairs(self._window_stack) do
if self._dirty[widget.widget] then
widget.widget:paintTo(fb.bb, widget.x, widget.y)
widget.widget:paintTo(Screen.fb.bb, widget.x, widget.y)
-- and remove from list after painting
self._dirty[widget.widget] = nil
-- trigger repaint
@ -161,7 +155,7 @@ function UIManager:run()
if dirty then
-- refresh FB
fb:refresh(self.refresh_type) -- TODO: refresh explicitly only repainted area
Screen.fb:refresh(self.refresh_type) -- TODO: refresh explicitly only repainted area
-- reset refresh_type
self.refresh_type = 0
end

@ -1,3 +1,4 @@
require "ui/screen"
require "ui/rendertext"
require "ui/graphics"
require "ui/image"
@ -186,10 +187,13 @@ function TextWidget:getSize()
--self:_render()
--end
--return { w = self._length, h = self._bb:getHeight() }
self._length = sizeUtf8Text(0, G_width, self.face, self.text, true).x
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 {
return Geom:new{
w = self._length,
h = self._height,
}
@ -226,7 +230,7 @@ TextBoxWidget = Widget:new{
function TextBoxWidget:_wrapGreedyAlg(h_list)
local cur_line_width = 0
local space_w = sizeUtf8Text(0, G_width, self.face, " ", true).x
local space_w = sizeUtf8Text(0, Screen:getWidth(), self.face, " ", true).x
local cur_line = {}
local v_list = {}
@ -255,7 +259,7 @@ function TextBoxWidget:_getVerticalList(alg)
for w in self.text:gmatch("%S+") do
word_box = {}
word_box.word = w
word_box.width = sizeUtf8Text(0, G_width, self.face, w, true).x
word_box.width = sizeUtf8Text(0, Screen:getWidth(), self.face, w, true).x
table.insert(h_list, word_box)
end
@ -269,7 +273,7 @@ function TextBoxWidget:_render()
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, G_width, self.face, " ", true).x
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

@ -0,0 +1,54 @@
package.path = "./frontend/?.lua"
require "ui/ui"
require "ui/readerui"
require "ui/filechooser"
require "ui/infomessage"
require "document/document"
function showReader(file)
local document = DocumentRegistry:getProvider(file)
if not document then
UIManager:show(InfoMessage:new{ text = "No reader engine for this file" })
return
end
local readerwindow = FrameContainer:new{
dimen = Screen:getSize(),
background = 0,
margin = 0,
padding = 0,
bordersize = 0
}
local reader = ReaderUI:new{
dialog = readerwindow,
dimen = Screen:getSize(),
document = document
}
readerwindow[1] = reader
UIManager:show(readerwindow)
end
function showFileManager(path)
local FileManager = FileChooser:new{
path = path,
dimen = Screen:getSize(),
is_borderless = true
}
function FileManager:onFileSelect(file)
showReader(file)
return true
end
function FileManager:onClose()
UIManager:quit()
return true
end
UIManager:show(FileManager)
end
showFileManager(".")
UIManager:run()

@ -1,23 +1,27 @@
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 "document/document"
TestGrid = Widget:new{}
function TestGrid:paintTo()
v_line = math.floor(G_width / 50)
h_line = math.floor(G_height / 50)
function TestGrid:paintTo(bb)
v_line = math.floor(bb:getWidth() / 50)
h_line = math.floor(bb:getHeight() / 50)
for i=1,h_line do
y_num = i*50
renderUtf8Text(fb.bb, 0, y_num+10, Font:getFace("ffont", 12), y_num, true)
fb.bb:paintRect(0, y_num, G_width, 1, 10)
renderUtf8Text(bb, 0, y_num+10, Font:getFace("ffont", 12), y_num, true)
bb:paintRect(0, y_num, bb:getWidth(), 1, 10)
end
for i=1,v_line do
x_num = i*50
renderUtf8Text(fb.bb, x_num, 10, Font:getFace("ffont", 12), x_num, true)
fb.bb:paintRect(x_num, 0, 1, G_height, 10)
renderUtf8Text(bb, x_num, 10, Font:getFace("ffont", 12), x_num, true)
bb:paintRect(x_num, 0, 1, bb:getHeight(), 10)
end
end
@ -33,7 +37,7 @@ Background = InputContainer:new{
FrameContainer:new{
background = 3,
bordersize = 0,
dimen = { w = G_width, h = G_height }
dimen = Screen:getSize()
}
}
@ -133,14 +137,14 @@ M = Menu:new{
readerwindow = CenterContainer:new{
dimen = Geom:new{ w = G_width, h = G_height },
dimen = Screen:getSize(),
FrameContainer:new{
background = 0
}
}
reader = ReaderUI:new{
dialog = readerwindow,
dimen = Geom:new{ w = G_width - 100, h = G_height - 100 },
dimen = Geom:new{ w = Screen:getWidth() - 100, h = Screen:getHeight() - 100 },
document = DocumentRegistry:getProvider("test/2col.pdf")
--document = DocumentRegistry:getProvider("test/djvu3spec.djvu")
--document = DocumentRegistry:getProvider("./README.TXT")

Loading…
Cancel
Save