From a231b944c13779c0fdbde3759f1b081c0154cafc Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Thu, 15 Mar 2012 17:03:09 +0800 Subject: [PATCH 1/5] add: first demo of cursor support --- blitbuffer.c | 58 +++++++++++++++++++++++++++++++++++++++++++ graphics.lua | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ inputbox.lua | 69 +++++++++++++++++++++++++++------------------------- 3 files changed, 161 insertions(+), 33 deletions(-) diff --git a/blitbuffer.c b/blitbuffer.c index c8a51de7f..8ea96a7a6 100644 --- a/blitbuffer.c +++ b/blitbuffer.c @@ -366,6 +366,63 @@ static int paintRect(lua_State *L) { return 0; } +static int invertRect(lua_State *L) { + BlitBuffer *dst = (BlitBuffer*) luaL_checkudata(L, 1, "blitbuffer"); + int x = luaL_checkint(L, 2); + int y = luaL_checkint(L, 3); + int w = luaL_checkint(L, 4); + int h = luaL_checkint(L, 5); + uint8_t *dstptr; + + int cy, cx; + if(w <= 0 || h <= 0 || x >= dst->w || y >= dst->h) { + return 0; + } + if(x + w > dst->w) { + w = dst->w - x; + } + if(y + h > dst->h) { + h = dst->h - y; + } + + if(x & 1) { + /* This will invert the leftmost column + * in the case when x is odd. After this, + * x will become even. */ + dstptr = (uint8_t*)(dst->data + + y * dst->pitch + + x / 2); + for(cy = 0; cy < h; cy++) { + *dstptr ^= 0x0F; + dstptr += dst->pitch; + } + x++; + w--; + } + dstptr = (uint8_t*)(dst->data + + y * dst->pitch + + x / 2); + for(cy = 0; cy < h; cy++) { + for(cx = 0; cx < w/2; cx++) { + *(dstptr+cx) ^= 0xFF; + } + dstptr += dst->pitch; + } + if(w & 1) { + /* This will invert the rightmost column + * in the case when (w & 1) && !(x & 1) or + * !(w & 1) && (x & 1). */ + dstptr = (uint8_t*)(dst->data + + y * dst->pitch + + (x + w) / 2); + for(cy = 0; cy < h; cy++) { + *dstptr ^= 0xF0; + dstptr += dst->pitch; + } + } + return 0; +} + static const struct luaL_reg blitbuffer_func[] = { {"new", newBlitBuffer}, {NULL, NULL} @@ -378,6 +435,7 @@ static const struct luaL_reg blitbuffer_meth[] = { {"addblitFrom", addblitToBuffer}, {"blitFullFrom", blitFullToBuffer}, {"paintRect", paintRect}, + {"invertRect", invertRect}, {"free", freeBlitBuffer}, {"__gc", freeBlitBuffer}, {NULL, NULL} diff --git a/graphics.lua b/graphics.lua index 1ebb13cce..bebefc419 100644 --- a/graphics.lua +++ b/graphics.lua @@ -6,6 +6,7 @@ blitbuffer.paintBorder = function (bb, x, y, w, h, bw, c) bb:paintRect(x+w-bw, y+bw, bw, h - 2*bw, c) end + --[[ Draw a progress bar according to following args: @@ -27,3 +28,69 @@ blitbuffer.progressBar = function (bb, x, y, w, h, fb.bb:paintRect(x+load_m_w, y+load_m_h, (w-2*load_m_w)*load_percent, (h-2*load_m_h), c) end + + + +------------------------------------------------ +-- Start of Cursor class +------------------------------------------------ + +Cursor = { + x_pos = 0, + y_pos = 0, + --color = 15, + h = 10, + w = nil, + line_w = nil, +} + +function Cursor:new(o) + o = o or {} + o.x_pos = o.x_pos or self.x_pos + o.y_pos = o.y_pos or self.y_pos + o.h = o.h or self.h + + o.w = o.h / 2 + o.line_w = math.floor(o.h / 10) + + setmetatable(o, self) + self.__index = self + return o +end + +function Cursor:_draw(x, y) + local body_h = self.h - self.line_w + blitbuffer.invertRect(fb.bb, x, y, self.w, self.line_w) + --print("self.w: "..self.w..", self.line_w: "..self.line_w) + blitbuffer.invertRect(fb.bb, x+(self.w/2)-(self.line_w/2), y+self.line_w, + self.line_w, body_h-self.line_w) + blitbuffer.invertRect(fb.bb, x, y+body_h, self.w, self.line_w) +end + +function Cursor:draw() + self:_draw(self.x_pos, self.y_pos) +end + +function Cursor:clear() + self:_draw(self.x_pos, self.y_pos) +end + +function Cursor:move(x_off, y_off) + self:clear() + self.x_pos = self.x_pos + x_off + self.y_pos = self.y_pos + y_off + self:draw() +end + +function Cursor:moveHorizontal(x_off) + self:clear() + self.x_pos = self.x_pos + x_off + self:draw() +end + +function Cursor:moveVertical(y_off) + self:clear() + self.y_pos = self.y_pos + y_off + self:draw() +end + diff --git a/inputbox.lua b/inputbox.lua index 6e3fc6667..e845ccd79 100644 --- a/inputbox.lua +++ b/inputbox.lua @@ -4,6 +4,7 @@ require "graphics" InputBox = { -- Class vars: + h = 100, input_start_x = 145, input_start_y = nil, input_cur_x = nil, -- points to the start of next input pos @@ -15,6 +16,8 @@ InputBox = { shiftmode = false, altmode = false, + cursor = nil, + -- font for displaying input content face = freetype.newBuiltinFace("mono", 25), fhash = "m25", @@ -22,13 +25,6 @@ InputBox = { fwidth = 16, } -function InputBox:setDefaultInput(text) - self.input_string = "" - self:addString(text) - --self.input_cur_x = self.input_start_x + (string.len(text) * self.fwidth) - --self.input_string = text -end - function InputBox:addString(str) for i = 1, #str do self:addChar(str:sub(i,i)) @@ -36,9 +32,13 @@ function InputBox:addString(str) end function InputBox:addChar(char) - renderUtf8Text(fb.bb, self.input_cur_x, self.input_start_y, self.face, self.fhash, - char, true) - fb:refresh(1, self.input_cur_x, self.input_start_y-19, self.fwidth, self.fheight) + self.cursor:moveHorizontal(self.fwidth) + renderUtf8Text(fb.bb, self.input_cur_x, self.input_start_y, + self.face, self.fhash, + char, true) + fb:refresh(1, self.input_cur_x - self.cursor.w - self.fwidth, + self.input_start_y-25, + self.fwidth*2 + self.cursor.w*2, self.h-25) self.input_cur_x = self.input_cur_x + self.fwidth self.input_string = self.input_string .. char end @@ -50,8 +50,10 @@ function InputBox:delChar() self.input_cur_x = self.input_cur_x - self.fwidth --fill last character with blank rectangle fb.bb:paintRect(self.input_cur_x, self.input_start_y-19, - self.fwidth, self.fheight, self.input_bg) - fb:refresh(1, self.input_cur_x, self.input_start_y-19, self.fwidth, self.fheight) + self.fwidth, self.fheight, self.input_bg) + self.cursor:moveHorizontal(-self.fwidth) + fb:refresh(1, self.input_cur_x, self.input_start_y-25, + self.fwidth + self.cursor.w, self.h-25) self.input_string = self.input_string:sub(0,-2) end @@ -66,35 +68,36 @@ function InputBox:drawBox(ypos, w, h, title) end ---[[ - || d_text default to nil (used to set default text in input slot) ---]] +---------------------------------------------------------------------- +-- InputBox:input() +-- +-- @title: input prompt for the box +-- @d_text: default to nil (used to set default text in input slot) +---------------------------------------------------------------------- function InputBox:input(ypos, height, title, d_text) - local pagedirty = true -- do some initilization + self.h = height self.input_start_y = ypos + 35 self.input_cur_x = self.input_start_x + self.cursor = Cursor:new { + x_pos = 140, + y_pos = ypos + 13, + h = 30, + } - if d_text then -- if specified default text, draw it - w = fb.bb:getWidth() - 40 - h = height - 45 - self:drawBox(ypos, w, h, title) - self:setDefaultInput(d_text) - fb:refresh(1, 20, ypos, w, h) - pagedirty = false - else -- otherwise, leave the draw task to the main loop - self.input_string = "" + if d_text then + self.input_string = d_text end - while true do - if pagedirty then - w = fb.bb:getWidth() - 40 - h = height - 45 - self:drawBox(ypos, w, h, title) - fb:refresh(1, 20, ypos, w, h) - pagedirty = false - end + -- draw box and content + w = fb.bb:getWidth() - 40 + h = height - 45 + self:drawBox(ypos, w, h, title) + self.cursor:draw() + self:addString(self.input_string) + fb:refresh(1, 20, ypos, w, h) + while true do local ev = input.waitForEvent() ev.code = adjustKeyEvents(ev) if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then From 7b0f2ad815e08b6d1982082c8011276aeb35523b Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Thu, 15 Mar 2012 21:43:55 +0800 Subject: [PATCH 2/5] mod: cursor finished, add to inputbox --- graphics.lua | 38 +++++++++++++------ inputbox.lua | 105 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 106 insertions(+), 37 deletions(-) diff --git a/graphics.lua b/graphics.lua index bebefc419..f12510252 100644 --- a/graphics.lua +++ b/graphics.lua @@ -50,7 +50,7 @@ function Cursor:new(o) o.y_pos = o.y_pos or self.y_pos o.h = o.h or self.h - o.w = o.h / 2 + o.w = o.h / 3 o.line_w = math.floor(o.h / 10) setmetatable(o, self) @@ -60,11 +60,13 @@ end function Cursor:_draw(x, y) local body_h = self.h - self.line_w - blitbuffer.invertRect(fb.bb, x, y, self.w, self.line_w) - --print("self.w: "..self.w..", self.line_w: "..self.line_w) - blitbuffer.invertRect(fb.bb, x+(self.w/2)-(self.line_w/2), y+self.line_w, - self.line_w, body_h-self.line_w) - blitbuffer.invertRect(fb.bb, x, y+body_h, self.w, self.line_w) + -- paint upper horizontal line + fb.bb:invertRect(x, y, self.w, self.line_w/2) + -- paint middle vertical line + fb.bb:invertRect(x+(self.w/2)-(self.line_w/2), y+self.line_w/2, + self.line_w, body_h) + -- paint lower horizontal line + fb.bb:invertRect(x, y+body_h+self.line_w/2, self.w, self.line_w/2) end function Cursor:draw() @@ -76,21 +78,33 @@ function Cursor:clear() end function Cursor:move(x_off, y_off) - self:clear() self.x_pos = self.x_pos + x_off self.y_pos = self.y_pos + y_off - self:draw() end function Cursor:moveHorizontal(x_off) - self:clear() self.x_pos = self.x_pos + x_off - self:draw() end -function Cursor:moveVertical(y_off) - self:clear() +function Cursor:moveVertical(x_off) self.y_pos = self.y_pos + y_off +end + +function Cursor:moveAndDraw(x_off, y_off) + self:clear() + self:move(x_off, y_off) + self:draw() +end + +function Cursor:moveHorizontalAndDraw(x_off) + self:clear() + self:move(x_off, 0) + self:draw() +end + +function Cursor:moveVerticalAndDraw(y_off) + self:clear() + self:move(0, y_off) self:draw() end diff --git a/inputbox.lua b/inputbox.lua index e845ccd79..0f1e3c0f2 100644 --- a/inputbox.lua +++ b/inputbox.lua @@ -5,6 +5,7 @@ require "graphics" InputBox = { -- Class vars: h = 100, + input_slot_w = nil, input_start_x = 145, input_start_y = nil, input_cur_x = nil, -- points to the start of next input pos @@ -19,10 +20,11 @@ InputBox = { cursor = nil, -- font for displaying input content + -- we have to use mono here for better distance controlling face = freetype.newBuiltinFace("mono", 25), fhash = "m25", fheight = 25, - fwidth = 16, + fwidth = 15, } function InputBox:addString(str) @@ -31,30 +33,65 @@ function InputBox:addString(str) end end +function InputBox:refreshText() + -- clear previous painted text + fb.bb:paintRect(140, self.input_start_y-19, + self.input_slot_w, self.fheight, self.input_bg) + -- paint new text + renderUtf8Text(fb.bb, self.input_start_x, self.input_start_y, + self.face, self.fhash, + self.input_string, 0) +end + function InputBox:addChar(char) - self.cursor:moveHorizontal(self.fwidth) - renderUtf8Text(fb.bb, self.input_cur_x, self.input_start_y, - self.face, self.fhash, - char, true) - fb:refresh(1, self.input_cur_x - self.cursor.w - self.fwidth, - self.input_start_y-25, - self.fwidth*2 + self.cursor.w*2, self.h-25) + self.cursor:clear() + + -- draw new text + local cur_index = (self.cursor.x_pos + 3 - self.input_start_x) + / self.fwidth + self.input_string = self.input_string:sub(0,cur_index)..char.. + self.input_string:sub(cur_index+1) + self:refreshText() self.input_cur_x = self.input_cur_x + self.fwidth - self.input_string = self.input_string .. char + -- draw new cursor + self.cursor:moveHorizontal(self.fwidth) + self.cursor:draw() + + fb:refresh(1, self.input_start_x-5, self.input_start_y-25, + self.input_slot_w, self.h-25) end function InputBox:delChar() if self.input_start_x == self.input_cur_x then return end + + self.cursor:clear() + + -- draw new text + local cur_index = (self.cursor.x_pos + 3 - self.input_start_x) + / self.fwidth + self.input_string = self.input_string:sub(0,cur_index-1).. + self.input_string:sub(cur_index+1, -1) + self:refreshText() self.input_cur_x = self.input_cur_x - self.fwidth - --fill last character with blank rectangle - fb.bb:paintRect(self.input_cur_x, self.input_start_y-19, - self.fwidth, self.fheight, self.input_bg) + -- draw new cursor self.cursor:moveHorizontal(-self.fwidth) - fb:refresh(1, self.input_cur_x, self.input_start_y-25, - self.fwidth + self.cursor.w, self.h-25) - self.input_string = self.input_string:sub(0,-2) + self.cursor:draw() + + fb:refresh(1, self.input_start_x-5, self.input_start_y-25, + self.input_slot_w, self.h-25) +end + +function InputBox:clearText() + self.cursor:clear() + self.input_string = "" + self:refreshText() + self.cursor.x_pos = self.input_start_x - 3 + self.cursor:draw() + + fb:refresh(1, self.input_start_x-5, self.input_start_y-25, + self.input_slot_w, self.h-25) end function InputBox:drawBox(ypos, w, h, title) @@ -79,29 +116,31 @@ function InputBox:input(ypos, height, title, d_text) self.h = height self.input_start_y = ypos + 35 self.input_cur_x = self.input_start_x + self.input_slot_w = fb.bb:getWidth() - 170 + self.cursor = Cursor:new { - x_pos = 140, + x_pos = self.input_start_x - 3, y_pos = ypos + 13, h = 30, } - if d_text then - self.input_string = d_text - end -- draw box and content w = fb.bb:getWidth() - 40 h = height - 45 self:drawBox(ypos, w, h, title) self.cursor:draw() - self:addString(self.input_string) + if d_text then + self.input_string = d_text + self:addString(self.input_string) + end fb:refresh(1, 20, ypos, w, h) while true do local ev = input.waitForEvent() ev.code = adjustKeyEvents(ev) if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then - --local secs, usecs = util.gettime() + local secs, usecs = util.gettime() if ev.code == KEY_FW_UP then elseif ev.code == KEY_FW_DOWN then elseif ev.code == KEY_A then @@ -180,21 +219,37 @@ function InputBox:input(ypos, height, title, d_text) self:addChar(" ") elseif ev.code == KEY_PGFWD then elseif ev.code == KEY_PGBCK then + elseif ev.code == KEY_FW_LEFT then + if (self.cursor.x_pos + 3) > self.input_start_x then + self.cursor:moveHorizontalAndDraw(-self.fwidth) + fb:refresh(1, self.input_start_x-5, ypos, + self.input_slot_w, h) + end + elseif ev.code == KEY_FW_RIGHT then + if (self.cursor.x_pos + 3) < self.input_cur_x then + self.cursor:moveHorizontalAndDraw(self.fwidth) + fb:refresh(1,self.input_start_x-5, ypos, + self.input_slot_w, h) + end elseif ev.code == KEY_ENTER or ev.code == KEY_FW_PRESS then if self.input_string == "" then self.input_string = nil end break elseif ev.code == KEY_DEL then - self:delChar() + if Keys.shiftmode then + self:clearText() + else + self:delChar() + end elseif ev.code == KEY_BACK then self.input_string = nil break end - --local nsecs, nusecs = util.gettime() - --local dur = (nsecs - secs) * 1000000 + nusecs - usecs - --print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur) + local nsecs, nusecs = util.gettime() + local dur = (nsecs - secs) * 1000000 + nusecs - usecs + print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur) end -- if end -- while From 709a9003d276d74e28417b28464db6cd2a0f19d1 Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Thu, 15 Mar 2012 22:15:09 +0800 Subject: [PATCH 3/5] fix: handle default text in inputbox --- inputbox.lua | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/inputbox.lua b/inputbox.lua index 0f1e3c0f2..a7cdc8eca 100644 --- a/inputbox.lua +++ b/inputbox.lua @@ -27,12 +27,6 @@ InputBox = { fwidth = 15, } -function InputBox:addString(str) - for i = 1, #str do - self:addChar(str:sub(i,i)) - end -end - function InputBox:refreshText() -- clear previous painted text fb.bb:paintRect(140, self.input_start_y-19, @@ -129,11 +123,13 @@ function InputBox:input(ypos, height, title, d_text) w = fb.bb:getWidth() - 40 h = height - 45 self:drawBox(ypos, w, h, title) - self.cursor:draw() if d_text then self.input_string = d_text - self:addString(self.input_string) + self.input_cur_x = self.input_cur_x + (self.fwidth * d_text:len()) + self.cursor.x_pos = self.cursor.x_pos + (self.fwidth * d_text:len()) + self:refreshText() end + self.cursor:draw() fb:refresh(1, 20, ypos, w, h) while true do @@ -242,7 +238,7 @@ function InputBox:input(ypos, height, title, d_text) else self:delChar() end - elseif ev.code == KEY_BACK then + elseif ev.code == KEY_BACK or ev.code == KEY_HOME then self.input_string = nil break end From 9eff3e322446ba0ad4868138552ee7c2b0d82492 Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Thu, 15 Mar 2012 22:18:27 +0800 Subject: [PATCH 4/5] mod: delete debug lines in inputbox.lua --- inputbox.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inputbox.lua b/inputbox.lua index a7cdc8eca..69789a891 100644 --- a/inputbox.lua +++ b/inputbox.lua @@ -136,7 +136,7 @@ function InputBox:input(ypos, height, title, d_text) local ev = input.waitForEvent() ev.code = adjustKeyEvents(ev) if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then - local secs, usecs = util.gettime() + --local secs, usecs = util.gettime() if ev.code == KEY_FW_UP then elseif ev.code == KEY_FW_DOWN then elseif ev.code == KEY_A then @@ -243,9 +243,9 @@ function InputBox:input(ypos, height, title, d_text) break end - local nsecs, nusecs = util.gettime() - local dur = (nsecs - secs) * 1000000 + nusecs - usecs - print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur) + --local nsecs, nusecs = util.gettime() + --local dur = (nsecs - secs) * 1000000 + nusecs - usecs + --print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur) end -- if end -- while From 46de106e8a7aeb3d64453c59cae69de3106e2f33 Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Fri, 16 Mar 2012 14:23:09 +0800 Subject: [PATCH 5/5] fix: clear input_text before input result return --- inputbox.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/inputbox.lua b/inputbox.lua index 69789a891..65321e0d6 100644 --- a/inputbox.lua +++ b/inputbox.lua @@ -249,5 +249,7 @@ function InputBox:input(ypos, height, title, d_text) end -- if end -- while - return self.input_string + local return_str = self.input_string + self.input_string = "" + return return_str end