local InputContainer = require("ui/widget/container/inputcontainer") local FrameContainer = require("ui/widget/container/framecontainer") local CenterContainer = require("ui/widget/container/centercontainer") local BottomContainer = require("ui/widget/container/bottomcontainer") local HorizontalSpan = require("ui/widget/horizontalspan") local HorizontalGroup = require("ui/widget/horizontalgroup") local VerticalSpan = require("ui/widget/verticalspan") local VerticalGroup = require("ui/widget/verticalgroup") local ImageWidget = require("ui/widget/imagewidget") local TextWidget = require("ui/widget/textwidget") local Font = require("ui/font") local Geom = require("ui/geometry") local Screen = require("device").screen local Device = require("device") local GestureRange = require("ui/gesturerange") local UIManager = require("ui/uimanager") local DEBUG = require("dbg") local Blitbuffer = require("ffi/blitbuffer") local VirtualKey = InputContainer:new{ key = nil, icon = nil, label = nil, keyboard = nil, callback = nil, width = nil, height = nil, bordersize = 2, face = Font:getFace("infont", 22), } function VirtualKey:init() if self.label == "Sym" or self.label == "ABC" then self.callback = function () self.keyboard:setLayout(self.key or self.label) end elseif self.label == "Shift" then self.callback = function () self.keyboard:setLayout(self.key or self.label) end elseif self.label == "IM" then self.callback = function () self.keyboard:setLayout(self.key or self.label) end elseif self.label == "Äéß" then self.callback = function () self.keyboard:setLayout(self.key or self.label) end elseif self.label == "Backspace" then self.callback = function () self.keyboard:delChar() end self.hold_callback = function () self.keyboard:clear() end else self.callback = function () self.keyboard:addChar(self.key) end end local label_widget = nil if self.icon then label_widget = ImageWidget:new{ file = self.icon, } else label_widget = TextWidget:new{ text = self.label, face = self.face, } end self[1] = FrameContainer:new{ margin = 0, bordersize = self.bordersize, background = Blitbuffer.COLOR_WHITE, radius = 5, padding = 0, CenterContainer:new{ dimen = Geom:new{ w = self.width - 2*self.bordersize, h = self.height - 2*self.bordersize, }, label_widget, }, } self.dimen = Geom:new{ w = self.width, h = self.height, } --self.dimen = self[1]:getSize() if Device:isTouchDevice() then self.ges_events = { TapSelect = { GestureRange:new{ ges = "tap", range = self.dimen, }, }, HoldSelect = { GestureRange:new{ ges = "hold", range = self.dimen, }, }, } end end function VirtualKey:update_keyboard() UIManager:setDirty(self.keyboard, function() DEBUG("update key region", self[1].dimen) return "ui", self[1].dimen end) end function VirtualKey:update_keyboard_inputbox() local inputbox = self.keyboard.inputbox UIManager:setDirty(self.keyboard, function() DEBUG("update inputbox", inputbox.dimen) return "ui", inputbox.dimen end) UIManager:setDirty(self.keyboard, function() DEBUG("update keyboard", self[1].dimen) return "ui", self[1].dimen end) end function VirtualKey:onTapSelect() self[1].invert = true self:update_keyboard_inputbox() if self.callback then self.callback() end UIManager:scheduleIn(0.2, function() self:invert(false) end) return true end function VirtualKey:onHoldSelect() self[1].invert = true self:update_keyboard_inputbox() if self.hold_callback then self.hold_callback() end UIManager:scheduleIn(0.2, function() self:invert(false) end) return true end function VirtualKey:invert(invert) self[1].invert = invert self:update_keyboard() end local VirtualKeyboard = InputContainer:new{ is_always_active = true, disable_double_tap = true, inputbox = nil, KEYS = {}, -- table to store layouts min_layout = 2, max_layout = 12, layout = 2, shiftmode = false, symbolmode = false, utf8mode = false, umlautmode = false, width = 600, height = 256, bordersize = 2, padding = 2, key_padding = Screen:scaleBySize(6), } function VirtualKeyboard:init() self.KEYS = { -- first row { -- 1 2 3 4 5 6 7 8 9 10 11 12 { "Q", "q", "1", "!", "Я", "я", "1", "!", "Ä", "ä", "1", "ª", }, { "W", "w", "2", "?", "Ж", "ж", "2", "?", "Ö", "ö", "2", "º", }, { "E", "e", "3", "|", "Е", "е", "3", "«", "Ü", "ü", "3", "¡", }, { "R", "r", "4", "#", "Р", "р", "4", "»", "ß", "ß", "4", "¿", }, { "T", "t", "5", "@", "Т", "т", "5", ":", "À", "à", "5", "¼", }, { "Y", "y", "6", "‰", "Ы", "ы", "6", ";", "Â", "â", "6", "½", }, { "U", "u", "7", "'", "У", "у", "7", "~", "Æ", "æ", "7", "¾", }, { "I", "i", "8", "`", "И", "и", "8", "(", "Ç", "ç", "8", "©", }, { "O", "o", "9", ":", "О", "о", "9", ")", "È", "è", "9", "®", }, { "P", "p", "0", ";", "П", "п", "0", "=", "É", "é", "0", "™", }, }, -- second raw { -- 1 2 3 4 5 6 7 8 9 10 11 12 { "A", "a", "+", "…", "А", "а", "Ш", "ш", "Ê", "ê", "Ş", "ş", }, { "S", "s", "-", "_", "С", "с", "Ѕ", "ѕ", "Ë", "ë", "İ", "ı", }, { "D", "d", "*", "=", "Д", "д", "Э", "э", "Î", "î", "Ğ", "ğ", }, { "F", "f", "/", "\\", "Ф", "ф", "Ю", "ю", "Ï", "ï", "Ć", "ć", }, { "G", "g", "%", "„", "Г", "г", "Ґ", "ґ", "Ô", "ô", "Č", "č", }, { "H", "h", "^", "“", "Ч", "ч", "Ј", "ј", "Œ", "œ", "Đ", "đ", }, { "J", "j", "<", "”", "Й", "й", "І", "і", "Ù", "ù", "Š", "š", }, { "K", "k", "=", "\"", "К", "к", "Ќ", "ќ", "Û", "û", "Ž", "ž", }, { "L", "l", ">", "~", "Л", "л", "Љ", "љ", "Ÿ", "ÿ", "Ő", "ő", }, }, -- third raw { -- 1 2 3 4 5 6 7 8 9 10 11 12 { label = "Shift", icon = "resources/icons/appbar.arrow.shift.png", width = 1.5 }, { "Z", "z", "(", "$", "З", "з", "Щ", "щ", "Á", "á", "Ű", "ű", }, { "X", "x", ")", "€", "Х", "х", "№", "@", "É", "é", "Ø", "ø", }, { "C", "c", "{", "¥", "Ц", "ц", "Џ", "џ", "Í", "í", "Þ", "þ", }, { "V", "v", "}", "£", "В", "в", "Ў", "ў", "Ñ", "ñ", "Ý", "ý", }, { "B", "b", "[", "‚", "Б", "б", "Ћ", "ћ", "Ó", "ó", "†", "‡", }, { "N", "n", "]", "‘", "Н", "н", "Њ", "њ", "Ú", "ú", "–", "—", }, { "M", "m", "&", "’", "М", "м", "Ї", "ї", "Ç", "ç", "…", "¨", }, { label = "Backspace", icon = "resources/icons/appbar.clear.reflect.horizontal.png", width = 1.5 }, }, -- fourth raw { { "Sym", "Sym", "ABC", "ABC", "Sym", "Sym", "ABC", "ABC", "Sym", "Sym", "ABC", "ABC", width = 1.5}, { label = "IM", icon = "resources/icons/appbar.globe.wire.png", }, { "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", "Äéß", }, { label = "space", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", width = 4.0}, { ",", ".", ".", ",", ",", ".", "Є", "є", ",", ".", ",", ".", }, { label = "Enter", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", icon = "resources/icons/appbar.arrow.enter.png", width = 1.5, }, } } self:initLayout(self.layout) end function VirtualKeyboard:initLayout(layout) local function VKLayout(b1, b2, b3, b4) local function boolnum(bool) return bool and 1 or 0 end return 2 - boolnum(b1) + 2 * boolnum(b2) + 4 * boolnum(b3) + 8 * boolnum(b4) end if layout then -- to be sure layout is selected properly layout = math.max(layout, self.min_layout) layout = math.min(layout, self.max_layout) self.layout = layout -- fill the layout modes self.shiftmode = (layout == 1 or layout == 3 or layout == 5 or layout == 7 or layout == 9 or layout == 11) self.symbolmode = (layout == 3 or layout == 4 or layout == 7 or layout == 8 or layout == 11 or layout == 12) self.utf8mode = (layout == 5 or layout == 6 or layout == 7 or layout == 8) self.umlautmode = (layout == 9 or layout == 10 or layout == 11 or layout == 12) else -- or, without input parameter, restore layout from current layout modes self.layout = VKLayout(self.shiftmode, self.symbolmode, self.utf8mode, self.umlautmode) end self:addKeys() end function VirtualKeyboard:addKeys() local base_key_width = math.floor((self.width - 11*self.key_padding - 2*self.padding)/10) local base_key_height = math.floor((self.height - 5*self.key_padding - 2*self.padding)/4) local h_key_padding = HorizontalSpan:new{width = self.key_padding} local v_key_padding = VerticalSpan:new{width = self.key_padding} local vertical_group = VerticalGroup:new{} for i = 1, #self.KEYS do local horizontal_group = HorizontalGroup:new{} for j = 1, #self.KEYS[i] do local width_factor = self.KEYS[i][j].width or 1.0 local key_width = math.floor((base_key_width + self.key_padding) * width_factor) - self.key_padding local key_height = base_key_height local label = self.KEYS[i][j].label or self.KEYS[i][j][self.layout] local key = VirtualKey:new{ key = self.KEYS[i][j][self.layout], icon = self.KEYS[i][j].icon, label = label, keyboard = self, width = key_width, height = key_height, } table.insert(horizontal_group, key) if j ~= #self.KEYS[i] then table.insert(horizontal_group, h_key_padding) end end table.insert(vertical_group, horizontal_group) if i ~= #self.KEYS then table.insert(vertical_group, v_key_padding) end end local size = vertical_group:getSize() local keyboard_frame = FrameContainer:new{ margin = 0, bordersize = self.bordersize, background = Blitbuffer.COLOR_WHITE, radius = 0, padding = self.padding, CenterContainer:new{ dimen = Geom:new{ w = self.width - 2*self.bordersize -2*self.padding, h = self.height - 2*self.bordersize -2*self.padding, }, vertical_group, } } self[1] = BottomContainer:new{ dimen = Screen:getSize(), keyboard_frame, } self.dimen = keyboard_frame:getSize() end function VirtualKeyboard:setLayout(key) if key == "Shift" then self.shiftmode = not self.shiftmode elseif key == "Sym" or key == "ABC" then self.symbolmode = not self.symbolmode elseif key == "Äéß" then self.umlautmode = not self.umlautmode if self.umlautmode then self.utf8mode = false end elseif key == "IM" then self.utf8mode = not self.utf8mode if self.utf8mode then self.umlautmode = false end end self:initLayout() UIManager:setDirty(self, function() -- correct coordinates of keyboard local dimen = self.dimen:copy() dimen.y = Screen:getHeight() - dimen.h DEBUG("update keyboard layout", dimen) return "partial", dimen end) end function VirtualKeyboard:addChar(key) DEBUG("add char", key) self.inputbox:addChar(key) end function VirtualKeyboard:delChar() DEBUG("delete char") self.inputbox:delChar() end function VirtualKeyboard:clear() DEBUG("clear input") self.inputbox:clear() end return VirtualKeyboard