From d6fcc9adf98eab334ea57d812361de49a296ddec Mon Sep 17 00:00:00 2001 From: union2find Date: Thu, 21 Apr 2016 22:13:10 +0800 Subject: [PATCH 1/4] add cursor functionality --- frontend/ui/widget/inputtext.lua | 57 ++-- frontend/ui/widget/scrolltextwidget.lua | 29 +- frontend/ui/widget/textboxwidget.lua | 404 +++++++++++++----------- frontend/util.lua | 29 ++ 4 files changed, 298 insertions(+), 221 deletions(-) diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 0707adfef..4b54c67f3 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -8,16 +8,17 @@ local UIManager = require("ui/uimanager") local Device = require("device") local Screen = Device.screen local Font = require("ui/font") -local util = require("ffi/util") +local util = require("util") local Keyboard local InputText = InputContainer:new{ text = "", hint = "demo hint", charlist = {}, -- table to store input string - charpos = 1, + charpos = nil, -- position to insert a new char, or the position of the cursor input_type = nil, text_type = nil, + text_widget = nil, -- Text Widget for cursor movement width = nil, height = nil, @@ -46,9 +47,21 @@ if Device.isTouchDevice() then } end - function InputText:onTapTextBox() + function InputText:onTapTextBox(arg, ges) + print("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Text widget", self.text_widget) if self.parent.onSwitchFocus then self.parent:onSwitchFocus(self) + else + local x = ges.pos.x - self.dimen.x - self.bordersize - self.padding + local y = ges.pos.y - self.dimen.y - self.bordersize - self.padding + if x > 0 and y > 0 then + print("Move to ", x, y) + self.charpos = self.text_widget:moveCursor(x, y) + print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX charpos now at", self.charpos) + UIManager:setDirty(self.parent, function() + return "ui", self[1].dimen + end) + end end end else @@ -64,40 +77,48 @@ end function InputText:initTextBox(text) self.text = text - self:initCharlist(text) + util.splitToChars(text, self.charlist) + print("XXXXXX", self.charlist, #self.charlist) + if self.charpos == nil then + self.charpos = #self.charlist + 1 + end local fgcolor = Blitbuffer.gray(self.text == "" and 0.5 or 1.0) local show_text = self.text if self.text_type == "password" and show_text ~= "" then show_text = self.text:gsub("(.-).", function() return "*" end) show_text = show_text:gsub("(.)$", function() return self.text:sub(-1) end) - elseif show_text == "" then - show_text = self.hint end - local text_widget if self.scroll then - text_widget = ScrollTextWidget:new{ + self.text_widget = ScrollTextWidget:new{ text = show_text, + charlist = self.charlist, + charpos = self.charpos, + editable = true, face = self.face, fgcolor = fgcolor, width = self.width, height = self.height, } else - text_widget = TextBoxWidget:new{ + self.text_widget = TextBoxWidget:new{ text = show_text, + charlist = self.charlist, + charpos = self.charpos, + editable = true, face = self.face, fgcolor = fgcolor, width = self.width, height = self.height, } end + print("IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII InputText: text_widget", self.text_widget) self[1] = FrameContainer:new{ bordersize = self.bordersize, padding = self.padding, margin = self.margin, color = Blitbuffer.gray(self.focused and 1.0 or 0.5), - text_widget, + self.text_widget, } self.dimen = self[1]:getSize() -- FIXME: self.parent is not always in the widget statck (BookStatusWidget) @@ -106,22 +127,6 @@ function InputText:initTextBox(text) end) end -function InputText:initCharlist(text) - if text == nil then return end - -- clear - self.charlist = {} - self.charpos = 1 - local prevcharcode, charcode = 0 - for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do - charcode = util.utf8charcode(uchar) - if prevcharcode then -- utf8 - self.charlist[#self.charlist+1] = uchar - end - prevcharcode = charcode - end - self.charpos = #self.charlist+1 -end - function InputText:initKeyboard() local keyboard_layout = 2 if self.input_type == "number" then diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index 28a86ee00..282ebfa19 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -15,6 +15,9 @@ Text widget with vertical scroll bar --]] local ScrollTextWidget = InputContainer:new{ text = nil, + charlist = nil, + charpos = nil, + editable = false, face = nil, fgcolor = Blitbuffer.COLOR_BLACK, width = 400, @@ -25,8 +28,12 @@ local ScrollTextWidget = InputContainer:new{ } function ScrollTextWidget:init() + print("####################################################### ScrollTextWidget width", self.width) self.text_widget = TextBoxWidget:new{ text = self.text, + charlist = self.charlist, + charpos = self.charpos, + editable = self.editable, face = self.face, fgcolor = self.fgcolor, width = self.width - self.scroll_bar_width - self.text_scroll_span, @@ -38,12 +45,12 @@ function ScrollTextWidget:init() enable = visible_line_count < total_line_count, low = 0, high = visible_line_count/total_line_count, - width = Screen:scaleBySize(6), + width = self.scroll_bar_width, height = self.height, } local horizontal_group = HorizontalGroup:new{} table.insert(horizontal_group, self.text_widget) - table.insert(horizontal_group, HorizontalSpan:new{width = Screen:scaleBySize(6)}) + table.insert(horizontal_group, HorizontalSpan:new{self.text_scroll_span}) table.insert(horizontal_group, self.v_scroll_bar) self[1] = horizontal_group self.dimen = Geom:new(self[1]:getSize()) @@ -59,23 +66,13 @@ function ScrollTextWidget:init() end end -function ScrollTextWidget:updateScrollBar(text) - local virtual_line_num = text:getVirtualLineNum() - local visible_line_count = text:getVisLineCount() - local all_line_count = text:getAllLineCount() - self.v_scroll_bar:set( - (virtual_line_num - 1) / all_line_count, - (virtual_line_num - 1 + visible_line_count) / all_line_count - ) -end - function ScrollTextWidget:onScrollText(arg, ges) if ges.direction == "north" then - self.text_widget:scrollDown() - self:updateScrollBar(self.text_widget) + low, high = self.text_widget:scrollDown() + self.v_scroll_bar:set(low, high) elseif ges.direction == "south" then - self.text_widget:scrollUp() - self:updateScrollBar(self.text_widget) + low, high = self.text_widget:scrollUp() + self.v_scroll_bar:set(low, high) end UIManager:setDirty(self.dialog, function() return "partial", self.dimen diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index ad3a172d0..d7981feec 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -1,5 +1,6 @@ local Blitbuffer = require("ffi/blitbuffer") local Widget = require("ui/widget/widget") +local LineWidget = require("ui/widget/linewidget") local RenderText = require("ui/rendertext") local Screen = require("device").screen local Geom = require("ui/geometry") @@ -11,207 +12,233 @@ A TextWidget that handles long text wrapping --]] local TextBoxWidget = Widget:new{ text = nil, + charlist = nil, + charpos = nil, + char_width_list = nil, -- list of widths of the chars in `charlist`. + vertical_string_list = nil, + editable = false, -- Editable flag for whether drawing the cursor or not. + cursor_line = nil, -- LineWidget to draw the vertical cursor. face = nil, bold = nil, + line_height = 0.3, -- in em fgcolor = Blitbuffer.COLOR_BLACK, width = 400, -- in pixels - height = nil, - first_line = 1, - virtual_line = 1, -- used by scroll bar - line_height = 0.3, -- in em - v_list = nil, + height = nil, -- nil value indicates unscrollable text widget + virtual_line_num = 1, -- used by scroll bar _bb = nil, - _length = 0, } function TextBoxWidget:init() - local v_list - if self.height then - v_list = self:_getCurrentVerticalList() - else - v_list = self:_getVerticalList() - end - self:_render(v_list) - self.dimen = Geom:new(self:getSize()) -end - -function TextBoxWidget:_wrapGreedyAlg(h_list) - local line_height = (1 + self.line_height) * self.face.size - local cur_line_width = 0 - local cur_line = {} - local v_list = {} - - for k,w in ipairs(h_list) do - w.box = { - x = cur_line_width, - w = w.width, + print("XXXXXXXXXXXXXXXXXXXX TextBoxWidget:init() ", self.height) + local line_height = (1 + self.line_height) * self.face.size + local font_height = self.face.size + self.cursor_line = LineWidget:new{ + dimen = Geom:new{ + w = Screen:scaleBySize(1), h = line_height, } - cur_line_width = cur_line_width + w.width - if w.word == "\n" then - if cur_line_width > 0 then - -- hard line break - table.insert(v_list, cur_line) - cur_line = {} - cur_line_width = 0 - end - elseif cur_line_width > self.width then - -- wrap to next line - table.insert(v_list, cur_line) - cur_line = {} - cur_line_width = w.width - table.insert(cur_line, w) - else - table.insert(cur_line, w) - end - end - -- handle last line - table.insert(v_list, cur_line) - - return v_list + } + print("########################### Rendering text", self.text) + print("########################### charlist", self.charlist) + self:_evalCharWidthList() + print("########################### char_width_list", self.char_width_list) + for k, v in ipairs(self.char_width_list) do + print("############################", k, v.char, v.width) + end + self:_splitCharWidthList() + print("######################### Text Widget vetical_string_list", self.vertical_string_list) + for k, v in ipairs(self.vertical_string_list) do + print("############################", k, v.text, v.offset) + end + if self.height == nil then + self:_renderText(1, #self.vertical_string_list) + else + print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ scorlling widget", self:getVisLineCount()) + self:_renderText(1, self:getVisLineCount()) + end + if self.editable then + x, y = self:_findCharPos() + print("####################### Drawing cursor at ", x, y, self.charpos) + self.cursor_line:paintTo(self._bb, x, y) + end + self.dimen = Geom:new(self:getSize()) end -function TextBoxWidget:_getVerticalList(alg) - if self.vertical_list then - return self.vertical_list - end - -- build horizontal list - local h_list = {} - for line in util.gsplit(self.text, "\n", true) do - for words in line:gmatch("[\32-\127\192-\255]+[\128-\191]*") do - for word in util.gsplit(words, "%s+", true) do - for w in util.gsplit(word, "%p+", true) do - local word_box = {} - word_box.word = w - word_box.width = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, w, true, self.bold).x - table.insert(h_list, word_box) - end - end - end - if line:sub(-1) == "\n" then table.insert(h_list, {word = '\n', width = 0}) end - end - - -- @TODO check alg here 25.04 2012 (houqp) - -- @TODO replace greedy algorithm with K&P algorithm 25.04 2012 (houqp) - self.vertical_list = self:_wrapGreedyAlg(h_list) - return self.vertical_list +-- Return whether the text widget is editable. +function TextBoxWidget:isEditable() + return self.editable end -function TextBoxWidget:_getCurrentVerticalList() - local line_height = (1 + self.line_height) * self.face.size - local v_list = self:_getVerticalList() - local current_v_list = {} - local height = 0 - for i = self.first_line, #v_list do - if height < self.height - line_height then - table.insert(current_v_list, v_list[i]) - height = height + line_height - else - break - end - end - return current_v_list +-- Evaluate the width of each char in `self.charlist`. +function TextBoxWidget:_evalCharWidthList() + if self.charlist == nil then + self.charlist = {} + util.splitToChars(self.text, self.charlist) + self.charpos = #self.charlist + 1 + end + self.char_width_list = {} + for _, v in ipairs(self.charlist) do + w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, v, true, self.bold).x + table.insert(self.char_width_list, {char = v, width = w}) + end end -function TextBoxWidget:_getPreviousVerticalList() - local line_height = (1 + self.line_height) * self.face.size - local v_list = self:_getVerticalList() - local previous_v_list = {} - local height = 0 - if self.first_line == 1 then - return self:_getCurrentVerticalList() - end - self.virtual_line = self.first_line - for i = self.first_line - 1, 1, -1 do - if height < self.height - line_height then - table.insert(previous_v_list, 1, v_list[i]) - height = height + line_height - self.virtual_line = self.virtual_line - 1 - else - break - end - end - for i = self.first_line, #v_list do - if height < self.height - line_height then - table.insert(previous_v_list, v_list[i]) - height = height + line_height - else - break - end - end - if self.first_line > #previous_v_list then - self.first_line = self.first_line - #previous_v_list - else - self.first_line = 1 - end - return previous_v_list +-- Split the text into logical lines to fit into the text box. +function TextBoxWidget:_splitCharWidthList() + self.vertical_string_list = {} + self.vertical_string_list[1] = {text = "Demo hint", offset = 1, width = 0} -- hint for empty string + + local idx = 1 + local offset = 1 + local cur_line_width = 0 + local cur_line_text = nil + local size = #self.char_width_list + local ln = 1 + while idx <= size do + offset = idx + -- Appending chars until the accumulated width exceeds `self.width`, + -- or a newline occurs, or no more chars to consume. + cur_line_width = 0 + local hard_newline = false + cur_line_text = nil + while idx <= size do + if self.char_width_list[idx].char == "\n" then + hard_newline = true + break + end + cur_line_width = cur_line_width + self.char_width_list[idx].width + print("++++++++", cur_line_width, idx, self.char_width_list[idx].width, self.char_width_list[idx].char) + if cur_line_width > self.width then break else idx = idx + 1 end + end + print("xxxxxxx Beofre ", cur_line_width, offset, idx, "++++++++++ self.width", self.width) + if cur_line_width <= self.width then -- a hard newline or end of string + cur_line_text = table.concat(self.charlist, "", offset, idx - 1) + print("XXX nature end", cur_line_text, "?????", #self.charlist) + else + -- Backtrack the string until the length fit into one line. + print("XXX overbounded") + local c = self.char_width_list[idx].char + if util.isSplitable(c) then + cur_line_text = table.concat(self.charlist, "", offset, idx - 1) + cur_line_width = cur_line_width - self.char_width_list[idx].width + else + local adjusted_idx = idx + local adjusted_width = cur_line_width + repeat + print("<----", self.char_width_list[adjusted_idx]) + adjusted_width = adjusted_width - self.char_width_list[adjusted_idx].width + adjusted_idx = adjusted_idx - 1 + c = self.char_width_list[adjusted_idx].char + until adjusted_idx > offset and util.isSplitable(c) + if adjusted_idx == offset then -- a very long english word ocuppying more than one line + cur_line_text = table.concat(self.charlist, "", offset, idx - 1) + cur_line_width = cur_line_width - self.char_width_list[idx].width + print("!!!!! A very long word cut to ", cur_line_text) + else + cur_line_text = table.concat(self.charlist, "", offset, adjusted_idx) + cur_line_width = adjusted_line_width + print("now the text is ", cur_line_text, "(", adjusted_line_width, ")", offset, adjusted_idx) + idx = adjusted_idx + 1 + end + end -- endif util.isSplitable(c) + end -- endif cur_line_width > self.width + self.vertical_string_list[ln] = {text = cur_line_text, offset = offset, width = cur_line_width} + if hard_newline then + idx = idx + 1 + self.vertical_string_list[ln + 1] = {text = "", offset = idx, width = 0} + end + ln = ln + 1 + -- Make sure `idx` point to the next char to be processed in the next loop. + end end -function TextBoxWidget:_getNextVerticalList() - local line_height = (1 + self.line_height) * self.face.size - local v_list = self:_getVerticalList() - local current_v_list = self:_getCurrentVerticalList() - local next_v_list = {} - local height = 0 - if self.first_line + #current_v_list > #v_list then - return current_v_list - end - self.virtual_line = self.first_line - for i = self.first_line + #current_v_list, #v_list do - if height < self.height - line_height then - table.insert(next_v_list, v_list[i]) - height = height + line_height - self.virtual_line = self.virtual_line + 1 - else - break - end - end - self.first_line = self.first_line + #current_v_list - return next_v_list -end - -function TextBoxWidget:_render(v_list) - self.rendering_vlist = v_list +function TextBoxWidget:_renderText(start_row_idx, end_row_idx) + print("@@@@@@@@@@@@@@@@@@@", start_row_idx, end_row_idx) local font_height = self.face.size - local line_height_px = self.line_height * font_height - local h = (font_height + line_height_px) * #v_list + local line_height = (1 + self.line_height) * font_height + if start_row_idx < 1 then start_row_idx = 1 end + if end_row_idx > #self.vertical_string_list then end_row_idx = #self.vertical_string_list end + local row_count = end_row_idx == 0 and 1 or end_row_idx - start_row_idx + 1 + local h = line_height * row_count self._bb = Blitbuffer.new(self.width, h) self._bb:fill(Blitbuffer.COLOR_WHITE) - local y = font_height - local pen_x - for _,l in ipairs(v_list) do - if self.alignment == "center" then - local line_len = 0 - for _,w in ipairs(l) do - line_len = line_len + w.width - end - pen_x = (self.width - line_len)/2 - else - pen_x = 0 - end - - for _,w in ipairs(l) do - w.box.y = y - line_height_px - font_height - --@TODO Don't use kerning for monospaced fonts. (houqp) - -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree - RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, w.word, true, self.bold, self.fgcolor) - pen_x = pen_x + w.width - end - y = y + line_height_px + font_height - end + print("XXXX rendering height", h, line_height, start_row_idx, end_row_idx) + local y = font_height + for i = start_row_idx, end_row_idx do + print("printing row", i, self.vertical_string_list[i].text) + local line = self.vertical_string_list[i] + local pen_x = self.alignment == "center" and (self.width - line.width)/2 or 0 + --@TODO Don't use kerning for monospaced fonts. (houqp) + -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree + RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, line.text, true, self.bold, self.fgcolor) + y = y + line_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:getVirtualLineNum() - return self.virtual_line +-- Return the position of the cursor corresponding to `self.charpos`, +-- Be aware of virtual line number of the scorllTextWidget. +function TextBoxWidget:_findCharPos() + print("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF findCharPos", self.charpos) + -- Find the line number. + local ln = self.height == nil and 1 or self.virtual_line_num + while ln + 1 <= #self.vertical_string_list do + if self.vertical_string_list[ln + 1].offset > self.charpos then break else ln = ln + 1 end + print("XXXXXX", ln, self.vertical_string_list[ln].offset, self.charpos) + end + print("^^^^^^^^^^^^locating at real line", ln, "virutal line", ln - self.virtual_line_num + 1) + -- Find the offset at the current line. + local x = 0 + print("self.char_width_list", self.char_width_list) + local offset = self.vertical_string_list[ln].offset + while offset < self.charpos do + print("+++", offset, self.charpos) + x = x + self.char_width_list[offset].width + offset = offset + 1 + end + print("^^^^^^^^^^^locating at width ", x) + local line_height = (1 + self.line_height) * self.face.size + return x + 1, (ln - 1) * line_height -- offset `x` by 1 to avoid overlap end -function TextBoxWidget:getAllLineCount() - local v_list = self:_getVerticalList() - return #v_list +-- Click event: Move the cursor to a new location with (x, y), in pixels. +-- Be aware of virtual line number of the scorllTextWidget. +function TextBoxWidget:moveCursor(x, y) + local w = 0 + local h = 0 + local line_height = (1 + self.line_height) * self.face.size + local ln = self.height == nil and 1 or self.virtual_line_num + ln = ln + math.ceil(y / line_height) - 1 + if ln > #self.vertical_string_list then + print("&&&&&&&&&&&& Press some empty area....") + ln = #self.vertical_string_list + x = self.width + end + print("Real line number", ln, "virtual line number", ln - self.virtual_line_num + 1) + local offset = self.vertical_string_list[ln].offset + local idx = ln == #self.vertical_string_list and #self.char_width_list or self.vertical_string_list[ln + 1].offset - 1 + print("XXXX", offset, idx) + while offset <= idx do + w = w + self.char_width_list[offset].width + print("XXXXX", offset, idx, w) + if w > x then break else offset = offset + 1 end + end + print("XXXX After loop", offset, idx, w) + if w > x then + local w_prev = w - self.char_width_list[offset].width + if x - w_prev < w - x then -- the previous one is more closer + w = w_prev + end + end + print("$$$$$$$$$$$$$$$$$$$$$$$$ adjusted", w) + print("painting", w, ln - self.virtual_line_num + 1) + self:free() + self:_renderText(1, #self.vertical_string_list) + self.cursor_line:paintTo(self._bb, w + 1, (ln - self.virtual_line_num) * line_height) + return offset end function TextBoxWidget:getVisLineCount() @@ -219,16 +246,35 @@ function TextBoxWidget:getVisLineCount() return math.floor(self.height / line_height) end -function TextBoxWidget:scrollDown() - local next_v_list = self:_getNextVerticalList() - self:free() - self:_render(next_v_list) +function TextBoxWidget:getAllLineCount() + return #self.vertical_string_list end + +-- TODO: modify `charpos` so that it can render the cursor +function TextBoxWidget:scrollDown() + local visible_line_count = self:getVisLineCount() + if self.virtual_line_num + visible_line_count <= #self.vertical_string_list then + self:free() + self.virtual_line_num = self.virtual_line_num + visible_line_count + self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) + end + return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list +end + +-- TODO: modify `charpos` so that it can render the cursor function TextBoxWidget:scrollUp() - local previous_v_list = self:_getPreviousVerticalList() - self:free() - self:_render(previous_v_list) + local visible_line_count = self:getVisLineCount() + if self.virtual_line_num > 1 then + self:free() + if self.virtual_line_num <= visible_line_count then + self.virtual_line_num = 1 + else + self.virtual_line_num = self.virtual_line_num - visible_line_count + end + self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) + end + return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list end function TextBoxWidget:getSize() diff --git a/frontend/util.lua b/frontend/util.lua index d7a4d6330..080e49696 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -1,3 +1,5 @@ +local BaseUtil = require("ffi/util") + --[[-- Miscellaneous helper functions for KOReader frontend. ]] @@ -94,4 +96,31 @@ function util.lastIndexOf(string, ch) if i == nil then return -1 else return i - 1 end end + +-- Split string into a list of UTF-8 chars. +-- @text: the string to be splitted. +-- @tab: the table to store the chars sequentially, must not be nil. +function util.splitToChars(text, tab) + if text == nil then return end + -- clear + for k, v in pairs(tab) do + tab[k] = nil + end + print("table", tab) + local prevcharcode, charcode = 0 + for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do + charcode = BaseUtil.utf8charcode(uchar) + if prevcharcode then -- utf8 + table.insert(tab, uchar) + end + prevcharcode = charcode + end + print(table.concat(tab, ",")) +end + +-- Test whether a string could be separated by a char for multi-line rendering +function util.isSplitable(c) + return #c > 1 or c == " " or string.match(c, "%p") ~= nil +end + return util From 3d89b27e59af5d2d050c0c406f48945f40a7b971 Mon Sep 17 00:00:00 2001 From: union2find Date: Sun, 15 May 2016 14:56:58 +0800 Subject: [PATCH 2/4] remove debug output --- frontend/ui/widget/inputtext.lua | 5 ---- frontend/ui/widget/scrolltextwidget.lua | 1 - frontend/ui/widget/textboxwidget.lua | 36 ------------------------- frontend/util.lua | 2 -- 4 files changed, 44 deletions(-) diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 4b54c67f3..82bcbe6de 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -48,16 +48,13 @@ if Device.isTouchDevice() then end function InputText:onTapTextBox(arg, ges) - print("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Text widget", self.text_widget) if self.parent.onSwitchFocus then self.parent:onSwitchFocus(self) else local x = ges.pos.x - self.dimen.x - self.bordersize - self.padding local y = ges.pos.y - self.dimen.y - self.bordersize - self.padding if x > 0 and y > 0 then - print("Move to ", x, y) self.charpos = self.text_widget:moveCursor(x, y) - print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX charpos now at", self.charpos) UIManager:setDirty(self.parent, function() return "ui", self[1].dimen end) @@ -78,7 +75,6 @@ end function InputText:initTextBox(text) self.text = text util.splitToChars(text, self.charlist) - print("XXXXXX", self.charlist, #self.charlist) if self.charpos == nil then self.charpos = #self.charlist + 1 end @@ -112,7 +108,6 @@ function InputText:initTextBox(text) height = self.height, } end - print("IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII InputText: text_widget", self.text_widget) self[1] = FrameContainer:new{ bordersize = self.bordersize, padding = self.padding, diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index 6d2494d93..9039c5a33 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -29,7 +29,6 @@ local ScrollTextWidget = InputContainer:new{ } function ScrollTextWidget:init() - print("####################################################### ScrollTextWidget width", self.width) self.text_widget = TextBoxWidget:new{ text = self.text, charlist = self.charlist, diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index d7981feec..1dc095123 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -29,7 +29,6 @@ local TextBoxWidget = Widget:new{ } function TextBoxWidget:init() - print("XXXXXXXXXXXXXXXXXXXX TextBoxWidget:init() ", self.height) local line_height = (1 + self.line_height) * self.face.size local font_height = self.face.size self.cursor_line = LineWidget:new{ @@ -38,27 +37,15 @@ function TextBoxWidget:init() h = line_height, } } - print("########################### Rendering text", self.text) - print("########################### charlist", self.charlist) self:_evalCharWidthList() - print("########################### char_width_list", self.char_width_list) - for k, v in ipairs(self.char_width_list) do - print("############################", k, v.char, v.width) - end self:_splitCharWidthList() - print("######################### Text Widget vetical_string_list", self.vertical_string_list) - for k, v in ipairs(self.vertical_string_list) do - print("############################", k, v.text, v.offset) - end if self.height == nil then self:_renderText(1, #self.vertical_string_list) else - print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ scorlling widget", self:getVisLineCount()) self:_renderText(1, self:getVisLineCount()) end if self.editable then x, y = self:_findCharPos() - print("####################### Drawing cursor at ", x, y, self.charpos) self.cursor_line:paintTo(self._bb, x, y) end self.dimen = Geom:new(self:getSize()) @@ -107,16 +94,12 @@ function TextBoxWidget:_splitCharWidthList() break end cur_line_width = cur_line_width + self.char_width_list[idx].width - print("++++++++", cur_line_width, idx, self.char_width_list[idx].width, self.char_width_list[idx].char) if cur_line_width > self.width then break else idx = idx + 1 end end - print("xxxxxxx Beofre ", cur_line_width, offset, idx, "++++++++++ self.width", self.width) if cur_line_width <= self.width then -- a hard newline or end of string cur_line_text = table.concat(self.charlist, "", offset, idx - 1) - print("XXX nature end", cur_line_text, "?????", #self.charlist) else -- Backtrack the string until the length fit into one line. - print("XXX overbounded") local c = self.char_width_list[idx].char if util.isSplitable(c) then cur_line_text = table.concat(self.charlist, "", offset, idx - 1) @@ -125,7 +108,6 @@ function TextBoxWidget:_splitCharWidthList() local adjusted_idx = idx local adjusted_width = cur_line_width repeat - print("<----", self.char_width_list[adjusted_idx]) adjusted_width = adjusted_width - self.char_width_list[adjusted_idx].width adjusted_idx = adjusted_idx - 1 c = self.char_width_list[adjusted_idx].char @@ -133,11 +115,9 @@ function TextBoxWidget:_splitCharWidthList() if adjusted_idx == offset then -- a very long english word ocuppying more than one line cur_line_text = table.concat(self.charlist, "", offset, idx - 1) cur_line_width = cur_line_width - self.char_width_list[idx].width - print("!!!!! A very long word cut to ", cur_line_text) else cur_line_text = table.concat(self.charlist, "", offset, adjusted_idx) cur_line_width = adjusted_line_width - print("now the text is ", cur_line_text, "(", adjusted_line_width, ")", offset, adjusted_idx) idx = adjusted_idx + 1 end end -- endif util.isSplitable(c) @@ -153,7 +133,6 @@ function TextBoxWidget:_splitCharWidthList() end function TextBoxWidget:_renderText(start_row_idx, end_row_idx) - print("@@@@@@@@@@@@@@@@@@@", start_row_idx, end_row_idx) local font_height = self.face.size local line_height = (1 + self.line_height) * font_height if start_row_idx < 1 then start_row_idx = 1 end @@ -162,10 +141,8 @@ function TextBoxWidget:_renderText(start_row_idx, end_row_idx) local h = line_height * row_count self._bb = Blitbuffer.new(self.width, h) self._bb:fill(Blitbuffer.COLOR_WHITE) - print("XXXX rendering height", h, line_height, start_row_idx, end_row_idx) local y = font_height for i = start_row_idx, end_row_idx do - print("printing row", i, self.vertical_string_list[i].text) local line = self.vertical_string_list[i] local pen_x = self.alignment == "center" and (self.width - line.width)/2 or 0 --@TODO Don't use kerning for monospaced fonts. (houqp) @@ -182,24 +159,18 @@ end -- Return the position of the cursor corresponding to `self.charpos`, -- Be aware of virtual line number of the scorllTextWidget. function TextBoxWidget:_findCharPos() - print("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF findCharPos", self.charpos) -- Find the line number. local ln = self.height == nil and 1 or self.virtual_line_num while ln + 1 <= #self.vertical_string_list do if self.vertical_string_list[ln + 1].offset > self.charpos then break else ln = ln + 1 end - print("XXXXXX", ln, self.vertical_string_list[ln].offset, self.charpos) end - print("^^^^^^^^^^^^locating at real line", ln, "virutal line", ln - self.virtual_line_num + 1) -- Find the offset at the current line. local x = 0 - print("self.char_width_list", self.char_width_list) local offset = self.vertical_string_list[ln].offset while offset < self.charpos do - print("+++", offset, self.charpos) x = x + self.char_width_list[offset].width offset = offset + 1 end - print("^^^^^^^^^^^locating at width ", x) local line_height = (1 + self.line_height) * self.face.size return x + 1, (ln - 1) * line_height -- offset `x` by 1 to avoid overlap end @@ -213,28 +184,21 @@ function TextBoxWidget:moveCursor(x, y) local ln = self.height == nil and 1 or self.virtual_line_num ln = ln + math.ceil(y / line_height) - 1 if ln > #self.vertical_string_list then - print("&&&&&&&&&&&& Press some empty area....") ln = #self.vertical_string_list x = self.width end - print("Real line number", ln, "virtual line number", ln - self.virtual_line_num + 1) local offset = self.vertical_string_list[ln].offset local idx = ln == #self.vertical_string_list and #self.char_width_list or self.vertical_string_list[ln + 1].offset - 1 - print("XXXX", offset, idx) while offset <= idx do w = w + self.char_width_list[offset].width - print("XXXXX", offset, idx, w) if w > x then break else offset = offset + 1 end end - print("XXXX After loop", offset, idx, w) if w > x then local w_prev = w - self.char_width_list[offset].width if x - w_prev < w - x then -- the previous one is more closer w = w_prev end end - print("$$$$$$$$$$$$$$$$$$$$$$$$ adjusted", w) - print("painting", w, ln - self.virtual_line_num + 1) self:free() self:_renderText(1, #self.vertical_string_list) self.cursor_line:paintTo(self._bb, w + 1, (ln - self.virtual_line_num) * line_height) diff --git a/frontend/util.lua b/frontend/util.lua index 080e49696..837cb980f 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -106,7 +106,6 @@ function util.splitToChars(text, tab) for k, v in pairs(tab) do tab[k] = nil end - print("table", tab) local prevcharcode, charcode = 0 for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do charcode = BaseUtil.utf8charcode(uchar) @@ -115,7 +114,6 @@ function util.splitToChars(text, tab) end prevcharcode = charcode end - print(table.concat(tab, ",")) end -- Test whether a string could be separated by a char for multi-line rendering From f8943efdbf0bd038dc8b5e5a183450e9272dee31 Mon Sep 17 00:00:00 2001 From: union2find Date: Sun, 15 May 2016 17:18:38 +0800 Subject: [PATCH 3/4] fix indent sytle --- frontend/ui/widget/inputtext.lua | 40 +-- frontend/ui/widget/scrolltextwidget.lua | 16 +- frontend/ui/widget/textboxwidget.lua | 314 ++++++++++++------------ frontend/util.lua | 8 +- 4 files changed, 189 insertions(+), 189 deletions(-) diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 82bcbe6de..54524ce37 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -18,7 +18,7 @@ local InputText = InputContainer:new{ charpos = nil, -- position to insert a new char, or the position of the cursor input_type = nil, text_type = nil, - text_widget = nil, -- Text Widget for cursor movement + text_widget = nil, -- Text Widget for cursor movement width = nil, height = nil, @@ -50,15 +50,15 @@ if Device.isTouchDevice() then function InputText:onTapTextBox(arg, ges) if self.parent.onSwitchFocus then self.parent:onSwitchFocus(self) - else - local x = ges.pos.x - self.dimen.x - self.bordersize - self.padding - local y = ges.pos.y - self.dimen.y - self.bordersize - self.padding - if x > 0 and y > 0 then - self.charpos = self.text_widget:moveCursor(x, y) - UIManager:setDirty(self.parent, function() - return "ui", self[1].dimen - end) - end + else + local x = ges.pos.x - self.dimen.x - self.bordersize - self.padding + local y = ges.pos.y - self.dimen.y - self.bordersize - self.padding + if x > 0 and y > 0 then + self.charpos = self.text_widget:moveCursor(x, y) + UIManager:setDirty(self.parent, function() + return "ui", self[1].dimen + end) + end end end else @@ -74,10 +74,10 @@ end function InputText:initTextBox(text) self.text = text - util.splitToChars(text, self.charlist) - if self.charpos == nil then - self.charpos = #self.charlist + 1 - end + util.splitToChars(text, self.charlist) + if self.charpos == nil then + self.charpos = #self.charlist + 1 + end local fgcolor = Blitbuffer.gray(self.text == "" and 0.5 or 1.0) local show_text = self.text @@ -88,9 +88,9 @@ function InputText:initTextBox(text) if self.scroll then self.text_widget = ScrollTextWidget:new{ text = show_text, - charlist = self.charlist, - charpos = self.charpos, - editable = true, + charlist = self.charlist, + charpos = self.charpos, + editable = true, face = self.face, fgcolor = fgcolor, width = self.width, @@ -99,9 +99,9 @@ function InputText:initTextBox(text) else self.text_widget = TextBoxWidget:new{ text = show_text, - charlist = self.charlist, - charpos = self.charpos, - editable = true, + charlist = self.charlist, + charpos = self.charpos, + editable = true, face = self.face, fgcolor = fgcolor, width = self.width, diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index 9039c5a33..2d5735443 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -16,9 +16,9 @@ Text widget with vertical scroll bar --]] local ScrollTextWidget = InputContainer:new{ text = nil, - charlist = nil, - charpos = nil, - editable = false, + charlist = nil, + charpos = nil, + editable = false, face = nil, fgcolor = Blitbuffer.COLOR_BLACK, width = 400, @@ -31,9 +31,9 @@ local ScrollTextWidget = InputContainer:new{ function ScrollTextWidget:init() self.text_widget = TextBoxWidget:new{ text = self.text, - charlist = self.charlist, - charpos = self.charpos, - editable = self.editable, + charlist = self.charlist, + charpos = self.charpos, + editable = self.editable, face = self.face, fgcolor = self.fgcolor, width = self.width - self.scroll_bar_width - self.text_scroll_span, @@ -76,10 +76,10 @@ function ScrollTextWidget:scrollText(direction) if direction == 0 then return end if direction > 0 then low, high = self.text_widget:scrollDown() - self.v_scroll_bar:set(low, high) + self.v_scroll_bar:set(low, high) else low, high = self.text_widget:scrollUp() - self.v_scroll_bar:set(low, high) + self.v_scroll_bar:set(low, high) end UIManager:setDirty(self.dialog, function() return "partial", self.dimen diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index 1dc095123..b717865f1 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -12,12 +12,12 @@ A TextWidget that handles long text wrapping --]] local TextBoxWidget = Widget:new{ text = nil, - charlist = nil, - charpos = nil, - char_width_list = nil, -- list of widths of the chars in `charlist`. + charlist = nil, + charpos = nil, + char_width_list = nil, -- list of widths of the chars in `charlist`. vertical_string_list = nil, - editable = false, -- Editable flag for whether drawing the cursor or not. - cursor_line = nil, -- LineWidget to draw the vertical cursor. + editable = false, -- Editable flag for whether drawing the cursor or not. + cursor_line = nil, -- LineWidget to draw the vertical cursor. face = nil, bold = nil, line_height = 0.3, -- in em @@ -29,127 +29,127 @@ local TextBoxWidget = Widget:new{ } function TextBoxWidget:init() - local line_height = (1 + self.line_height) * self.face.size - local font_height = self.face.size - self.cursor_line = LineWidget:new{ + local line_height = (1 + self.line_height) * self.face.size + local font_height = self.face.size + self.cursor_line = LineWidget:new{ dimen = Geom:new{ w = Screen:scaleBySize(1), h = line_height, } } - self:_evalCharWidthList() - self:_splitCharWidthList() - if self.height == nil then - self:_renderText(1, #self.vertical_string_list) - else - self:_renderText(1, self:getVisLineCount()) - end - if self.editable then - x, y = self:_findCharPos() - self.cursor_line:paintTo(self._bb, x, y) - end - self.dimen = Geom:new(self:getSize()) + self:_evalCharWidthList() + self:_splitCharWidthList() + if self.height == nil then + self:_renderText(1, #self.vertical_string_list) + else + self:_renderText(1, self:getVisLineCount()) + end + if self.editable then + x, y = self:_findCharPos() + self.cursor_line:paintTo(self._bb, x, y) + end + self.dimen = Geom:new(self:getSize()) end -- Return whether the text widget is editable. function TextBoxWidget:isEditable() - return self.editable + return self.editable end -- Evaluate the width of each char in `self.charlist`. function TextBoxWidget:_evalCharWidthList() - if self.charlist == nil then - self.charlist = {} - util.splitToChars(self.text, self.charlist) - self.charpos = #self.charlist + 1 - end - self.char_width_list = {} - for _, v in ipairs(self.charlist) do - w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, v, true, self.bold).x - table.insert(self.char_width_list, {char = v, width = w}) - end + if self.charlist == nil then + self.charlist = {} + util.splitToChars(self.text, self.charlist) + self.charpos = #self.charlist + 1 + end + self.char_width_list = {} + for _, v in ipairs(self.charlist) do + w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, v, true, self.bold).x + table.insert(self.char_width_list, {char = v, width = w}) + end end -- Split the text into logical lines to fit into the text box. function TextBoxWidget:_splitCharWidthList() - self.vertical_string_list = {} - self.vertical_string_list[1] = {text = "Demo hint", offset = 1, width = 0} -- hint for empty string + self.vertical_string_list = {} + self.vertical_string_list[1] = {text = "Demo hint", offset = 1, width = 0} -- hint for empty string - local idx = 1 - local offset = 1 + local idx = 1 + local offset = 1 local cur_line_width = 0 - local cur_line_text = nil - local size = #self.char_width_list - local ln = 1 - while idx <= size do - offset = idx - -- Appending chars until the accumulated width exceeds `self.width`, - -- or a newline occurs, or no more chars to consume. - cur_line_width = 0 - local hard_newline = false - cur_line_text = nil - while idx <= size do - if self.char_width_list[idx].char == "\n" then - hard_newline = true - break - end - cur_line_width = cur_line_width + self.char_width_list[idx].width - if cur_line_width > self.width then break else idx = idx + 1 end - end - if cur_line_width <= self.width then -- a hard newline or end of string - cur_line_text = table.concat(self.charlist, "", offset, idx - 1) - else - -- Backtrack the string until the length fit into one line. - local c = self.char_width_list[idx].char - if util.isSplitable(c) then - cur_line_text = table.concat(self.charlist, "", offset, idx - 1) - cur_line_width = cur_line_width - self.char_width_list[idx].width - else - local adjusted_idx = idx - local adjusted_width = cur_line_width - repeat - adjusted_width = adjusted_width - self.char_width_list[adjusted_idx].width - adjusted_idx = adjusted_idx - 1 - c = self.char_width_list[adjusted_idx].char - until adjusted_idx > offset and util.isSplitable(c) - if adjusted_idx == offset then -- a very long english word ocuppying more than one line - cur_line_text = table.concat(self.charlist, "", offset, idx - 1) - cur_line_width = cur_line_width - self.char_width_list[idx].width - else - cur_line_text = table.concat(self.charlist, "", offset, adjusted_idx) - cur_line_width = adjusted_line_width - idx = adjusted_idx + 1 - end - end -- endif util.isSplitable(c) - end -- endif cur_line_width > self.width - self.vertical_string_list[ln] = {text = cur_line_text, offset = offset, width = cur_line_width} - if hard_newline then - idx = idx + 1 - self.vertical_string_list[ln + 1] = {text = "", offset = idx, width = 0} - end - ln = ln + 1 - -- Make sure `idx` point to the next char to be processed in the next loop. - end + local cur_line_text = nil + local size = #self.char_width_list + local ln = 1 + while idx <= size do + offset = idx + -- Appending chars until the accumulated width exceeds `self.width`, + -- or a newline occurs, or no more chars to consume. + cur_line_width = 0 + local hard_newline = false + cur_line_text = nil + while idx <= size do + if self.char_width_list[idx].char == "\n" then + hard_newline = true + break + end + cur_line_width = cur_line_width + self.char_width_list[idx].width + if cur_line_width > self.width then break else idx = idx + 1 end + end + if cur_line_width <= self.width then -- a hard newline or end of string + cur_line_text = table.concat(self.charlist, "", offset, idx - 1) + else + -- Backtrack the string until the length fit into one line. + local c = self.char_width_list[idx].char + if util.isSplitable(c) then + cur_line_text = table.concat(self.charlist, "", offset, idx - 1) + cur_line_width = cur_line_width - self.char_width_list[idx].width + else + local adjusted_idx = idx + local adjusted_width = cur_line_width + repeat + adjusted_width = adjusted_width - self.char_width_list[adjusted_idx].width + adjusted_idx = adjusted_idx - 1 + c = self.char_width_list[adjusted_idx].char + until adjusted_idx > offset and util.isSplitable(c) + if adjusted_idx == offset then -- a very long english word ocuppying more than one line + cur_line_text = table.concat(self.charlist, "", offset, idx - 1) + cur_line_width = cur_line_width - self.char_width_list[idx].width + else + cur_line_text = table.concat(self.charlist, "", offset, adjusted_idx) + cur_line_width = adjusted_line_width + idx = adjusted_idx + 1 + end + end -- endif util.isSplitable(c) + end -- endif cur_line_width > self.width + self.vertical_string_list[ln] = {text = cur_line_text, offset = offset, width = cur_line_width} + if hard_newline then + idx = idx + 1 + self.vertical_string_list[ln + 1] = {text = "", offset = idx, width = 0} + end + ln = ln + 1 + -- Make sure `idx` point to the next char to be processed in the next loop. + end end function TextBoxWidget:_renderText(start_row_idx, end_row_idx) local font_height = self.face.size local line_height = (1 + self.line_height) * font_height - if start_row_idx < 1 then start_row_idx = 1 end - if end_row_idx > #self.vertical_string_list then end_row_idx = #self.vertical_string_list end - local row_count = end_row_idx == 0 and 1 or end_row_idx - start_row_idx + 1 + if start_row_idx < 1 then start_row_idx = 1 end + if end_row_idx > #self.vertical_string_list then end_row_idx = #self.vertical_string_list end + local row_count = end_row_idx == 0 and 1 or end_row_idx - start_row_idx + 1 local h = line_height * row_count self._bb = Blitbuffer.new(self.width, h) self._bb:fill(Blitbuffer.COLOR_WHITE) - local y = font_height - for i = start_row_idx, end_row_idx do - local line = self.vertical_string_list[i] - local pen_x = self.alignment == "center" and (self.width - line.width)/2 or 0 - --@TODO Don't use kerning for monospaced fonts. (houqp) - -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree - RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, line.text, true, self.bold, self.fgcolor) - y = y + line_height - end + local y = font_height + for i = start_row_idx, end_row_idx do + local line = self.vertical_string_list[i] + local pen_x = self.alignment == "center" and (self.width - line.width)/2 or 0 + --@TODO Don't use kerning for monospaced fonts. (houqp) + -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree + RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, line.text, true, self.bold, self.fgcolor) + y = y + line_height + end -- -- if text is shorter than one line, shrink to text's width -- if #v_list == 1 then -- self.width = pen_x @@ -159,50 +159,50 @@ end -- Return the position of the cursor corresponding to `self.charpos`, -- Be aware of virtual line number of the scorllTextWidget. function TextBoxWidget:_findCharPos() - -- Find the line number. - local ln = self.height == nil and 1 or self.virtual_line_num - while ln + 1 <= #self.vertical_string_list do - if self.vertical_string_list[ln + 1].offset > self.charpos then break else ln = ln + 1 end - end - -- Find the offset at the current line. - local x = 0 - local offset = self.vertical_string_list[ln].offset - while offset < self.charpos do - x = x + self.char_width_list[offset].width - offset = offset + 1 - end - local line_height = (1 + self.line_height) * self.face.size - return x + 1, (ln - 1) * line_height -- offset `x` by 1 to avoid overlap + -- Find the line number. + local ln = self.height == nil and 1 or self.virtual_line_num + while ln + 1 <= #self.vertical_string_list do + if self.vertical_string_list[ln + 1].offset > self.charpos then break else ln = ln + 1 end + end + -- Find the offset at the current line. + local x = 0 + local offset = self.vertical_string_list[ln].offset + while offset < self.charpos do + x = x + self.char_width_list[offset].width + offset = offset + 1 + end + local line_height = (1 + self.line_height) * self.face.size + return x + 1, (ln - 1) * line_height -- offset `x` by 1 to avoid overlap end -- Click event: Move the cursor to a new location with (x, y), in pixels. -- Be aware of virtual line number of the scorllTextWidget. function TextBoxWidget:moveCursor(x, y) - local w = 0 - local h = 0 - local line_height = (1 + self.line_height) * self.face.size - local ln = self.height == nil and 1 or self.virtual_line_num - ln = ln + math.ceil(y / line_height) - 1 - if ln > #self.vertical_string_list then - ln = #self.vertical_string_list - x = self.width - end - local offset = self.vertical_string_list[ln].offset - local idx = ln == #self.vertical_string_list and #self.char_width_list or self.vertical_string_list[ln + 1].offset - 1 - while offset <= idx do - w = w + self.char_width_list[offset].width - if w > x then break else offset = offset + 1 end - end - if w > x then - local w_prev = w - self.char_width_list[offset].width - if x - w_prev < w - x then -- the previous one is more closer - w = w_prev - end - end - self:free() - self:_renderText(1, #self.vertical_string_list) - self.cursor_line:paintTo(self._bb, w + 1, (ln - self.virtual_line_num) * line_height) - return offset + local w = 0 + local h = 0 + local line_height = (1 + self.line_height) * self.face.size + local ln = self.height == nil and 1 or self.virtual_line_num + ln = ln + math.ceil(y / line_height) - 1 + if ln > #self.vertical_string_list then + ln = #self.vertical_string_list + x = self.width + end + local offset = self.vertical_string_list[ln].offset + local idx = ln == #self.vertical_string_list and #self.char_width_list or self.vertical_string_list[ln + 1].offset - 1 + while offset <= idx do + w = w + self.char_width_list[offset].width + if w > x then break else offset = offset + 1 end + end + if w > x then + local w_prev = w - self.char_width_list[offset].width + if x - w_prev < w - x then -- the previous one is more closer + w = w_prev + end + end + self:free() + self:_renderText(1, #self.vertical_string_list) + self.cursor_line:paintTo(self._bb, w + 1, (ln - self.virtual_line_num) * line_height) + return offset end function TextBoxWidget:getVisLineCount() @@ -211,34 +211,34 @@ function TextBoxWidget:getVisLineCount() end function TextBoxWidget:getAllLineCount() - return #self.vertical_string_list + return #self.vertical_string_list end -- TODO: modify `charpos` so that it can render the cursor function TextBoxWidget:scrollDown() - local visible_line_count = self:getVisLineCount() - if self.virtual_line_num + visible_line_count <= #self.vertical_string_list then - self:free() - self.virtual_line_num = self.virtual_line_num + visible_line_count - self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) - end - return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list + local visible_line_count = self:getVisLineCount() + if self.virtual_line_num + visible_line_count <= #self.vertical_string_list then + self:free() + self.virtual_line_num = self.virtual_line_num + visible_line_count + self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) + end + return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list end -- TODO: modify `charpos` so that it can render the cursor function TextBoxWidget:scrollUp() - local visible_line_count = self:getVisLineCount() - if self.virtual_line_num > 1 then - self:free() - if self.virtual_line_num <= visible_line_count then - self.virtual_line_num = 1 - else - self.virtual_line_num = self.virtual_line_num - visible_line_count - end - self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) - end - return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list + local visible_line_count = self:getVisLineCount() + if self.virtual_line_num > 1 then + self:free() + if self.virtual_line_num <= visible_line_count then + self.virtual_line_num = 1 + else + self.virtual_line_num = self.virtual_line_num - visible_line_count + end + self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) + end + return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list end function TextBoxWidget:getSize() diff --git a/frontend/util.lua b/frontend/util.lua index 837cb980f..fbb533793 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -101,11 +101,11 @@ end -- @text: the string to be splitted. -- @tab: the table to store the chars sequentially, must not be nil. function util.splitToChars(text, tab) - if text == nil then return end + if text == nil then return end -- clear for k, v in pairs(tab) do - tab[k] = nil - end + tab[k] = nil + end local prevcharcode, charcode = 0 for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do charcode = BaseUtil.utf8charcode(uchar) @@ -118,7 +118,7 @@ end -- Test whether a string could be separated by a char for multi-line rendering function util.isSplitable(c) - return #c > 1 or c == " " or string.match(c, "%p") ~= nil + return #c > 1 or c == " " or string.match(c, "%p") ~= nil end return util From a7f24b6eaf1521ed26c16f570d303484817d1c1a Mon Sep 17 00:00:00 2001 From: union2find Date: Sun, 22 May 2016 23:59:28 +0800 Subject: [PATCH 4/4] fix function util.splitToChars in frontend/util.lua --- frontend/ui/widget/inputtext.lua | 4 ++-- frontend/ui/widget/scrolltextwidget.lua | 4 ++-- frontend/ui/widget/textboxwidget.lua | 20 +++++--------------- frontend/util.lua | 24 +++++++++++------------- 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 54524ce37..516c8cbe6 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -14,7 +14,7 @@ local Keyboard local InputText = InputContainer:new{ text = "", hint = "demo hint", - charlist = {}, -- table to store input string + charlist = nil, -- table to store input string charpos = nil, -- position to insert a new char, or the position of the cursor input_type = nil, text_type = nil, @@ -74,7 +74,7 @@ end function InputText:initTextBox(text) self.text = text - util.splitToChars(text, self.charlist) + self.charlist = util.splitToChars(text) if self.charpos == nil then self.charpos = #self.charlist + 1 end diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index 2d5735443..716b5c34e 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -74,13 +74,13 @@ end function ScrollTextWidget:scrollText(direction) if direction == 0 then return end + local low, high if direction > 0 then low, high = self.text_widget:scrollDown() - self.v_scroll_bar:set(low, high) else low, high = self.text_widget:scrollUp() - self.v_scroll_bar:set(low, high) end + self.v_scroll_bar:set(low, high) UIManager:setDirty(self.dialog, function() return "partial", self.dimen end) diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index b717865f1..0d10897df 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -30,7 +30,6 @@ local TextBoxWidget = Widget:new{ function TextBoxWidget:init() local line_height = (1 + self.line_height) * self.face.size - local font_height = self.face.size self.cursor_line = LineWidget:new{ dimen = Geom:new{ w = Screen:scaleBySize(1), @@ -45,27 +44,22 @@ function TextBoxWidget:init() self:_renderText(1, self:getVisLineCount()) end if self.editable then + local x, y x, y = self:_findCharPos() self.cursor_line:paintTo(self._bb, x, y) end self.dimen = Geom:new(self:getSize()) end --- Return whether the text widget is editable. -function TextBoxWidget:isEditable() - return self.editable -end - -- Evaluate the width of each char in `self.charlist`. function TextBoxWidget:_evalCharWidthList() if self.charlist == nil then - self.charlist = {} - util.splitToChars(self.text, self.charlist) + self.charlist = util.splitToChars(self.text) self.charpos = #self.charlist + 1 end self.char_width_list = {} for _, v in ipairs(self.charlist) do - w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, v, true, self.bold).x + local w = RenderText:sizeUtf8Text(0, Screen:getWidth(), self.face, v, true, self.bold).x table.insert(self.char_width_list, {char = v, width = w}) end end @@ -76,18 +70,15 @@ function TextBoxWidget:_splitCharWidthList() self.vertical_string_list[1] = {text = "Demo hint", offset = 1, width = 0} -- hint for empty string local idx = 1 - local offset = 1 - local cur_line_width = 0 - local cur_line_text = nil local size = #self.char_width_list local ln = 1 + local offset, cur_line_width, cur_line_text while idx <= size do offset = idx -- Appending chars until the accumulated width exceeds `self.width`, -- or a newline occurs, or no more chars to consume. cur_line_width = 0 local hard_newline = false - cur_line_text = nil while idx <= size do if self.char_width_list[idx].char == "\n" then hard_newline = true @@ -117,7 +108,7 @@ function TextBoxWidget:_splitCharWidthList() cur_line_width = cur_line_width - self.char_width_list[idx].width else cur_line_text = table.concat(self.charlist, "", offset, adjusted_idx) - cur_line_width = adjusted_line_width + cur_line_width = adjusted_width idx = adjusted_idx + 1 end end -- endif util.isSplitable(c) @@ -179,7 +170,6 @@ end -- Be aware of virtual line number of the scorllTextWidget. function TextBoxWidget:moveCursor(x, y) local w = 0 - local h = 0 local line_height = (1 + self.line_height) * self.face.size local ln = self.height == nil and 1 or self.virtual_line_num ln = ln + math.ceil(y / line_height) - 1 diff --git a/frontend/util.lua b/frontend/util.lua index fbb533793..544863d7a 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -99,21 +99,19 @@ end -- Split string into a list of UTF-8 chars. -- @text: the string to be splitted. --- @tab: the table to store the chars sequentially, must not be nil. -function util.splitToChars(text, tab) - if text == nil then return end - -- clear - for k, v in pairs(tab) do - tab[k] = nil - end - local prevcharcode, charcode = 0 - for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do - charcode = BaseUtil.utf8charcode(uchar) - if prevcharcode then -- utf8 - table.insert(tab, uchar) +function util.splitToChars(text) + local tab = {} + if text ~= nil then + local prevcharcode, charcode = 0 + for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do + charcode = BaseUtil.utf8charcode(uchar) + if prevcharcode then -- utf8 + table.insert(tab, uchar) + end + prevcharcode = charcode end - prevcharcode = charcode end + return tab end -- Test whether a string could be separated by a char for multi-line rendering