diff --git a/game/engines/default/data/gfx/ui/button-left-sel.png b/game/engines/default/data/gfx/ui/button-left-sel.png index 647e3e36c00305138552219ee38885bc5ec880b3..532f8c461f0ee3e525972c049655df5cab07ce48 100644 Binary files a/game/engines/default/data/gfx/ui/button-left-sel.png and b/game/engines/default/data/gfx/ui/button-left-sel.png differ diff --git a/game/engines/default/data/gfx/ui/button-middle-sel.png b/game/engines/default/data/gfx/ui/button-middle-sel.png index 8b32a9d0b3e0b784c34e1c1360cddb4befeeb94a..306f7cf1d62dd567a18886ce863286f842fe139e 100644 Binary files a/game/engines/default/data/gfx/ui/button-middle-sel.png and b/game/engines/default/data/gfx/ui/button-middle-sel.png differ diff --git a/game/engines/default/data/gfx/ui/button-right-sel.png b/game/engines/default/data/gfx/ui/button-right-sel.png index c504f1063aa2791b584275a46bae07dc7d0a2a05..8a2d303027939a896fda6fff73b25fc339732910 100644 Binary files a/game/engines/default/data/gfx/ui/button-right-sel.png and b/game/engines/default/data/gfx/ui/button-right-sel.png differ diff --git a/game/engines/default/data/gfx/ui/scrollbar-sel.png b/game/engines/default/data/gfx/ui/scrollbar-sel.png new file mode 100644 index 0000000000000000000000000000000000000000..21ebe87149a225adf15a59dd4d1edbd3b6020ae1 Binary files /dev/null and b/game/engines/default/data/gfx/ui/scrollbar-sel.png differ diff --git a/game/engines/default/data/gfx/ui/scrollbar.png b/game/engines/default/data/gfx/ui/scrollbar.png new file mode 100644 index 0000000000000000000000000000000000000000..c20574112f672353bab2e0f58fb4d66fbea4e908 Binary files /dev/null and b/game/engines/default/data/gfx/ui/scrollbar.png differ diff --git a/game/engines/default/data/gfx/ui/selection-left-column.png b/game/engines/default/data/gfx/ui/selection-left-column.png index a19defb42a04feb90f4be8dfcf29749d901ba9a3..2736bd608e340d4a7d7e4e79e85e28108d69095d 100644 Binary files a/game/engines/default/data/gfx/ui/selection-left-column.png and b/game/engines/default/data/gfx/ui/selection-left-column.png differ diff --git a/game/engines/default/data/gfx/ui/selection-middle-column.png b/game/engines/default/data/gfx/ui/selection-middle-column.png index 0ba88ec21c4f790e15f0b1c93faf1198967068cd..d0b9deacc678589f865cb10eb7c906c724c00c8a 100644 Binary files a/game/engines/default/data/gfx/ui/selection-middle-column.png and b/game/engines/default/data/gfx/ui/selection-middle-column.png differ diff --git a/game/engines/default/data/gfx/ui/selection-right-column.png b/game/engines/default/data/gfx/ui/selection-right-column.png index 0a87daeaf0f25a831463cf0957f929e09ccd3be3..12fb9dc2218317c6c466cf33bba1ca63879cc926 100644 Binary files a/game/engines/default/data/gfx/ui/selection-right-column.png and b/game/engines/default/data/gfx/ui/selection-right-column.png differ diff --git a/game/engines/default/data/gfx/ui/textbox-cursor.png b/game/engines/default/data/gfx/ui/textbox-cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..ec01ddb954443f2762c76fbb92d568fccf89f762 Binary files /dev/null and b/game/engines/default/data/gfx/ui/textbox-cursor.png differ diff --git a/game/engines/default/data/gfx/ui/textbox-left-sel.png b/game/engines/default/data/gfx/ui/textbox-left-sel.png new file mode 100644 index 0000000000000000000000000000000000000000..6084377ca688ef4773c017077cf59839f2686741 Binary files /dev/null and b/game/engines/default/data/gfx/ui/textbox-left-sel.png differ diff --git a/game/engines/default/data/gfx/ui/textbox-left.png b/game/engines/default/data/gfx/ui/textbox-left.png new file mode 100644 index 0000000000000000000000000000000000000000..89fe2cdfdc2fdf4338e4172eebeb9d3bed7c0aec Binary files /dev/null and b/game/engines/default/data/gfx/ui/textbox-left.png differ diff --git a/game/engines/default/data/gfx/ui/textbox-middle-sel.png b/game/engines/default/data/gfx/ui/textbox-middle-sel.png new file mode 100644 index 0000000000000000000000000000000000000000..cdc5a1b979d850afb785c6e41ce24a2c76cb6ba2 Binary files /dev/null and b/game/engines/default/data/gfx/ui/textbox-middle-sel.png differ diff --git a/game/engines/default/data/gfx/ui/textbox-middle.png b/game/engines/default/data/gfx/ui/textbox-middle.png new file mode 100644 index 0000000000000000000000000000000000000000..1df1da27dcbc40aa76acb816e68d676a8640b534 Binary files /dev/null and b/game/engines/default/data/gfx/ui/textbox-middle.png differ diff --git a/game/engines/default/data/gfx/ui/textbox-right-sel.png b/game/engines/default/data/gfx/ui/textbox-right-sel.png new file mode 100644 index 0000000000000000000000000000000000000000..1bb754d41584d646ef17c7f5a0f950eb8cc0799f Binary files /dev/null and b/game/engines/default/data/gfx/ui/textbox-right-sel.png differ diff --git a/game/engines/default/data/gfx/ui/textbox-right.png b/game/engines/default/data/gfx/ui/textbox-right.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8464028bfa4c3e9cdc11615de58c114e44b55f Binary files /dev/null and b/game/engines/default/data/gfx/ui/textbox-right.png differ diff --git a/game/engines/default/engine/ui/Base.lua b/game/engines/default/engine/ui/Base.lua index 584ee68258b11123bf2f054ba8639dbbcaccfbcc..6c6f9a58fb2b3b1e3214da10b792ae2f54cc2892 100644 --- a/game/engines/default/engine/ui/Base.lua +++ b/game/engines/default/engine/ui/Base.lua @@ -30,6 +30,9 @@ local cache = {} -- Default font _M.font = core.display.newFont("/data/font/Vera.ttf", 12) _M.font_h = _M.font:lineSkip() +_M.font_mono = core.display.newFont("/data/font/VeraMono.ttf", 12) +_M.font_mono_w = _M.font_mono:size(" ") +_M.font_mono_h = _M.font_mono:lineSkip() function _M:init(t, no_gen) self.mouse = Mouse.new() diff --git a/game/engines/default/engine/ui/Dialog.lua b/game/engines/default/engine/ui/Dialog.lua index 9372ffe8e543bd5a0e7f63340aaf7691ee64ffd8..5682f154f774be600912eb925aee0db20e84a560 100644 --- a/game/engines/default/engine/ui/Dialog.lua +++ b/game/engines/default/engine/ui/Dialog.lua @@ -88,7 +88,7 @@ function _M:generate() self.mouse:registerZone(0, 0, gamew, gameh, function() self.key:triggerVirtual("EXIT") end) self.mouse:registerZone(self.display_x, self.display_y, self.w, self.h, function(...) self:mouseEvent(...) end) self.key.receiveKey = function(_, ...) self:keyEvent(...) end - self.key:addCommand("__TAB", function(...) self.key:triggerVirtual("MOVE_DOWN") end) + self.key:addCommand("_TAB", function(...) self.key:triggerVirtual("MOVE_DOWN") end) self.key:addBinds{ MOVE_UP = function() self:setFocus(util.boundWrap(self.focus_ui_id - 1, 1, #self.uis)) end, MOVE_DOWN = function() self:setFocus(util.boundWrap(self.focus_ui_id + 1, 1, #self.uis)) end, diff --git a/game/engines/default/engine/ui/List.lua b/game/engines/default/engine/ui/List.lua index 5351bb3197d629cf173ecb58b9ee6e4da6cf246c..7358d286dfe14dc92eb492adaef1ca38abf082af 100644 --- a/game/engines/default/engine/ui/List.lua +++ b/game/engines/default/engine/ui/List.lua @@ -78,7 +78,7 @@ function _M:generate() -- Add UI controls self.mouse:registerZone(0, 0, self.w, self.h, function(button, x, y, xrel, yrel, bx, by, event) - self.sel = util.bound(self.scroll + math.floor(by / self.fh), 1, self.max) + if button ~= "wheelup" and button ~= "wheeldown" then self.sel = util.bound(self.scroll + math.floor(by / self.fh), 1, self.max) end if button == "left" and event == "button" then self:onUse() elseif button == "wheelup" and event == "button" then self.key:triggerVirtual("MOVE_UP") elseif button == "wheeldown" and event == "button" then self.key:triggerVirtual("MOVE_DOWN") diff --git a/game/engines/default/engine/ui/ListColumns.lua b/game/engines/default/engine/ui/ListColumns.lua index 055cc2b91377bce22a8d76f2e99371fd8a56e0d6..335c3e216d6a341ead0694b203ca752ebf599e1d 100644 --- a/game/engines/default/engine/ui/ListColumns.lua +++ b/game/engines/default/engine/ui/ListColumns.lua @@ -29,8 +29,15 @@ function _M:init(t) self.columns = assert(t.columns, "no list columns") self.w = assert(t.width, "no list width") self.h = assert(t.height, "no list height") + self.sortable = t.sortable + self.scrollbar = t.scrollbar self.fct = t.fct - self.display_prop = t.display_prop or "name" + + local w = self.w + if self.scrollbar then w = w - 10 end + for j, col in ipairs(self.columns) do + col.width = w * col.width / 100 + end Base.init(self, t) end @@ -39,6 +46,7 @@ function _M:generate() self.sel = 1 self.scroll = 1 self.max = #self.list + self:selectColumn(1, true) local ls, ls_w, ls_h = self:getImage("ui/selection-left-sel.png") local ms, ms_w, ms_h = self:getImage("ui/selection-middle-sel.png") @@ -54,17 +62,35 @@ function _M:generate() local cm, cm_w, cm_h = self:getImage("ui/selection-middle-column.png") local cr, cr_w, cr_h = self:getImage("ui/selection-right-column.png") - local fw, fh = self.w, ls_h - self.fw, self.fh = fw, fh + + local fh = ls_h + self.fh = fh self.max_display = math.floor(self.h / fh) - 1 + -- Draw the scrollbar + if self.scrollbar then + local sb, sb_w, sb_h = self:getImage("ui/scrollbar.png") + local ssb, ssb_w, ssb_h = self:getImage("ui/scrollbar-sel.png") + + self.scrollbar = { bar = {}, sel = {} } + self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.tex, self.scrollbar.sel.texw, self.scrollbar.sel.texh = ssb_w, ssb_h, ssb:glTexture() + local s = core.display.newSurface(sb_w, self.h - fh) + s:erase(200,0,0) + for i = 0, self.h - fh do s:merge(sb, 0, i) end + self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.tex, self.scrollbar.bar.texw, self.scrollbar.bar.texh = ssb_w, self.h - fh, s:glTexture() + end + -- Draw the list columns - for i, col in ipairs(self.columns) do + local colx = 0 + for j, col in ipairs(self.columns) do + local fw = col.width + col.fw = fw local text = col.name local ss = core.display.newSurface(fw, fh) local s = core.display.newSurface(fw, fh) + self.font:setStyle("bold") ss:merge(cls, 0, 0) for i = cls_w, fw - crs_w do ss:merge(cms, i, 0) end ss:merge(crs, fw - crs_w, 0) @@ -74,49 +100,76 @@ function _M:generate() for i = cl_w, fw - cr_w do s:merge(cm, i, 0) end s:merge(cr, fw - cr_w, 0) s:drawColorStringBlended(self.font, text, cl_w, (fh - self.font_h) / 2, 255, 255, 255, nil, fw - cl_w - cr_w) + self.font:setStyle("normal") col._tex, col._tex_w, col._tex_h = s:glTexture() col._stex = ss:glTexture() - end - -- Draw the list items - for i, item in ipairs(self.list) do - local text = item[self.display_prop] - local ss = core.display.newSurface(fw, fh) - local sus = core.display.newSurface(fw, fh) - local s = core.display.newSurface(fw, fh) - - ss:merge(ls, 0, 0) - for i = ls_w, fw - rs_w do ss:merge(ms, i, 0) end - ss:merge(rs, fw - rs_w, 0) - ss:drawColorStringBlended(self.font, text, ls_w, (fh - self.font_h) / 2, 255, 255, 255, nil, fw - ls_w - rs_w) - - s:erase(0, 0, 0) - s:drawColorStringBlended(self.font, text, ls_w, (fh - self.font_h) / 2, 255, 255, 255, nil, fw - ls_w - rs_w) - - sus:merge(l, 0, 0) - for i = l_w, fw - r_w do sus:merge(m, i, 0) end - sus:merge(r, fw - r_w, 0) - sus:drawColorStringBlended(self.font, text, ls_w, (fh - self.font_h) / 2, 255, 255, 255, nil, fw - ls_w - rs_w) + -- Draw the list items + for i, item in ipairs(self.list) do + local text = tostring(item[col.display_prop or col.sort]) + local ss = core.display.newSurface(fw, fh) + local sus = core.display.newSurface(fw, fh) + local s = core.display.newSurface(fw, fh) + + ss:merge(ls, 0, 0) + for i = ls_w, fw - rs_w do ss:merge(ms, i, 0) end + ss:merge(rs, fw - rs_w, 0) + ss:drawColorStringBlended(self.font, text, ls_w, (fh - self.font_h) / 2, 255, 255, 255, nil, fw - ls_w - rs_w) + + s:erase(0, 0, 0) + s:drawColorStringBlended(self.font, text, ls_w, (fh - self.font_h) / 2, 255, 255, 255, nil, fw - ls_w - rs_w) + + sus:merge(l, 0, 0) + for i = l_w, fw - r_w do sus:merge(m, i, 0) end + sus:merge(r, fw - r_w, 0) + sus:drawColorStringBlended(self.font, text, ls_w, (fh - self.font_h) / 2, 255, 255, 255, nil, fw - ls_w - rs_w) + + item._tex = item._tex or {} + item._stex = item._stex or {} + item._sustex = item._sustex or {} + item._tex[j] = {s:glTexture()} + item._stex[j] = {ss:glTexture()} + item._sustex[j] = {sus:glTexture()} + end - item._tex, item._tex_w, item._tex_h = s:glTexture() - item._stex = ss:glTexture() - item._sustex = sus:glTexture() + self.mouse:registerZone(colx, 0, col.width, self.fh, function(button, x, y, xrel, yrel, bx, by, event) + if button == "left" and event == "button" then self:selectColumn(j) end + end) + colx = colx + col.width end -- Add UI controls - self.mouse:registerZone(0, 0, self.w, self.h, function(button, x, y, xrel, yrel, bx, by, event) + self.mouse:registerZone(0, self.fh, self.w, self.h - self.fh, function(button, x, y, xrel, yrel, bx, by, event) + if button == "wheelup" and event == "button" then self.scroll = util.bound(self.scroll - 1, 1, self.max - self.max_display + 1) + elseif button == "wheeldown" and event == "button" then self.scroll = util.bound(self.scroll + 1, 1, self.max - self.max_display + 1) end + self.sel = util.bound(self.scroll + math.floor(by / self.fh), 1, self.max) - if button == "left" and event == "button" then self:onUse() - elseif button == "wheelup" and event == "button" then self.key:triggerVirtual("MOVE_UP") - elseif button == "wheeldown" and event == "button" then self.key:triggerVirtual("MOVE_DOWN") - end + if button == "left" and event == "button" then self:onUse() end end) self.key:addBinds{ ACCEPT = function() self:onUse() end, MOVE_UP = function() self.sel = util.boundWrap(self.sel - 1, 1, self.max) self.scroll = util.scroll(self.sel, self.scroll, self.max_display) end, MOVE_DOWN = function() self.sel = util.boundWrap(self.sel + 1, 1, self.max) self.scroll = util.scroll(self.sel, self.scroll, self.max_display) end, } + self.key:addCommands{ + _HOME = function() + self.sel = 1 + self.scroll = util.scroll(self.sel, self.scroll, self.max_display) + end, + _END = function() + self.sel = self.max + self.scroll = util.scroll(self.sel, self.scroll, self.max_display) + end, + _PAGEUP = function() + self.sel = util.bound(self.sel - self.max_display, 1, self.max) + self.scroll = util.scroll(self.sel, self.scroll, self.max_display) + end, + _PAGEDOWN = function() + self.sel = util.bound(self.sel + self.max_display, 1, self.max) + self.scroll = util.scroll(self.sel, self.scroll, self.max_display) + end, + } end function _M:onUse() @@ -126,27 +179,58 @@ function _M:onUse() else self.fct(item) end end -function _M:selectColumn(i) +function _M:selectColumn(i, force) + if not self.sortable and not force then return end local col = self.columns[i] - self.cur_col = i + if not col then return end + + if self.cur_col ~= i then + self.cur_col = i + self.sort_reverse = false + else + self.sort_reverse = not self.sort_reverse + end + + local fct = col.sort + if type(fct) == "string" then fct = function(a, b) return a[col.sort] < b[col.sort] end end + if self.sort_reverse then local old=fct fct = function(a, b) return old(b, a) end end + table.sort(self.list, fct) end function _M:display(x, y) + local bx, by = x, y + for j = 1, #self.columns do - local y = y + local col = self.columns[j] + if self.cur_col == j then + col._stex:toScreenFull(x, y, col.fw, self.fh, col._tex_w, col._tex_h) + else + col._tex:toScreenFull(x, y, col.fw, self.fh, col._tex_w, col._tex_h) + end + + local y = y + self.fh local max = math.min(self.scroll + self.max_display - 1, self.max) for i = self.scroll, max do local item = self.list[i] if self.sel == i then if self.focused then - item._stex:toScreenFull(x, y, self.fw, self.fh, item._tex_w, item._tex_h) + item._stex[j][1]:toScreenFull(x, y, col.fw, self.fh, item._stex[j][2], item._stex[j][3]) else - item._sustex:toScreenFull(x, y, self.fw, self.fh, item._tex_w, item._tex_h) + item._sustex[j][1]:toScreenFull(x, y, col.fw, self.fh, item._sustex[j][2], item._sustex[j][3]) end else - item._tex:toScreenFull(x, y, self.fw, self.fh, item._tex_w, item._tex_h) + item._tex[j][1]:toScreenFull(x, y, col.fw, self.fh, item._tex[j][2], item._tex[j][3]) end y = y + self.fh end + + x = x + col.width + end + + if self.focused and self.scrollbar then + local pos = self.sel * (self.h - self.fh) / self.max + + self.scrollbar.bar.tex:toScreenFull(bx + self.w - self.scrollbar.bar.w, by + self.fh, self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.texw, self.scrollbar.bar.texh) + self.scrollbar.sel.tex:toScreenFull(bx + self.w - self.scrollbar.sel.w, by + self.fh + pos, self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.texw, self.scrollbar.sel.texh) end end diff --git a/game/engines/default/engine/ui/Textbox.lua b/game/engines/default/engine/ui/Textbox.lua new file mode 100644 index 0000000000000000000000000000000000000000..b7652d29e8346486e4238556e91731a246d53d0f --- /dev/null +++ b/game/engines/default/engine/ui/Textbox.lua @@ -0,0 +1,156 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" +local Base = require "engine.ui.Base" +local Focusable = require "engine.ui.Focusable" + +--- A generic UI textbox +module(..., package.seeall, class.inherit(Base, Focusable)) + +function _M:init(t) + self.title = assert(t.title, "no textbox title") + self.text = t.text or "" + self.hide = t.hide + self.max_len = t.max_len or 999 + self.fct = assert(t.fct, "no textbox fct") + self.chars = assert(t.chars, "no textbox chars") + + self.tmp = {} + for i = 1, #self.text do self.tmp[#self.tmp+1] = self.text:sub(i, i) end + self.cursor = #self.tmp + 1 + self.scroll = 1 + + Base.init(self, t) +end + +function _M:generate() + local ls, ls_w, ls_h = self:getImage("ui/textbox-left-sel.png") + local ms, ms_w, ms_h = self:getImage("ui/textbox-middle-sel.png") + local rs, rs_w, rs_h = self:getImage("ui/textbox-right-sel.png") + local l, l_w, l_h = self:getImage("ui/textbox-left.png") + local m, m_w, m_h = self:getImage("ui/textbox-middle.png") + local r, r_w, r_h = self:getImage("ui/textbox-right.png") + local c, c_w, c_h = self:getImage("ui/textbox-cursor.png") + + self.h = r_h + + -- Draw UI + local title_w = self.font:size(self.title:removeColorCodes()) + self.w = title_w + self.chars * self.font_mono_w + ls_w + rs_w + print("titl", self.title, title_w, self.w) + local w, h = self.w, r_h + local fw, fh = w - title_w - ls_w - rs_w, self.font_h + self.fw, self.fh = fw, fh + self.text_x = ls_w + title_w + self.text_y = (h - fh) / 2 + self.max_display = math.floor(fw / self.font_mono_w) + local ss = core.display.newSurface(w, h) + local s = core.display.newSurface(w, h) + self.text_surf = core.display.newSurface(fw, fh) + self.text_tex, self.text_tex_w, self.text_tex_h = s:glTexture() + self:updateText() + + ss:merge(ls, title_w, 0) + for i = title_w + ls_w, w - rs_w do ss:merge(ms, i, 0) end + ss:merge(rs, w - rs_w, 0) + ss:drawColorStringBlended(self.font, self.title, 0, (h - fh) / 2, 255, 255, 255, true) + + s:merge(l, title_w, 0) + for i = title_w + l_w, w - r_w do s:merge(m, i, 0) end + s:merge(r, w - r_w, 0) + s:drawColorStringBlended(self.font, self.title, 0, (h - fh) / 2, 255, 255, 255, true) + + local cursor = core.display.newSurface(c_w, fh) + for i = 0, fh - 1 do cursor:merge(c, 0, i) end + self.cursor_tex, self.cursor_tex_w, self.cursor_tex_h = cursor:glTexture() + self.cursor_w, self.cursor_h = c_w, fh + + -- Add UI controls + self.mouse:registerZone(title_w + ls_w, 0, fw, h, function(button, x, y, xrel, yrel, bx, by, event) + if event == "button" then + self.cursor = util.bound(math.floor(bx / self.font_mono_w) + self.scroll, 1, #self.tmp+1) + self:updateText() + end + end) + self.key:addBind("ACCEPT", function() self.fct(self.text) end) + self.key:addBind("MOVE_LEFT", function() self.cursor = util.bound(self.cursor - 1, 1, #self.tmp+1) self.scroll = util.scroll(self.cursor, self.scroll, self.max_display) self:updateText() end) + self.key:addBind("MOVE_RIGHT", function() self.cursor = util.bound(self.cursor + 1, 1, #self.tmp+1) self.scroll = util.scroll(self.cursor, self.scroll, self.max_display) self:updateText() end) + self.key:addCommands{ + _DELETE = function() + if self.cursor <= #self.tmp then + table.remove(self.tmp, self.cursor) + self:updateText() + end + end, + _BACKSPACE = function() + if self.cursor > 1 then + table.remove(self.tmp, self.cursor - 1) + self.cursor = self.cursor - 1 + self.scroll = util.scroll(self.cursor, self.scroll, self.max_display) + self:updateText() + end + end, + _HOME = function() + self.cursor = 1 + self.scroll = util.scroll(self.cursor, self.scroll, self.max_display) + self:updateText() + end, + _END = function() + self.cursor = #self.tmp + 1 + self.scroll = util.scroll(self.cursor, self.scroll, self.max_display) + self:updateText() + end, + __TEXTINPUT = function(c) + if #self.tmp < self.max_len then + table.insert(self.tmp, self.cursor, c) + self.cursor = self.cursor + 1 + self.scroll = util.scroll(self.cursor, self.scroll, self.max_display) + self:updateText() + end + end, + } + + self.tex, self.tex_w, self.tex_h = s:glTexture() + self.stex = ss:glTexture() +end + +function _M:updateText() + self.text = table.concat(self.tmp) + local text = "" + for i = self.scroll, self.scroll + self.max_display - 1 do + if not self.tmp[i] then break end + if not self.hide then text = text .. self.tmp[i] + else text = text .. "*" end + end + + self.text_surf:erase(0, 0, 0, 0) + self.text_surf:drawStringBlended(self.font_mono, text, 0, 0, 255, 255, 255, true) + self.text_surf:updateTexture(self.text_tex) +end + +function _M:display(x, y) + if self.focused then + self.stex:toScreenFull(x, y, self.w, self.h, self.tex_w, self.tex_h) + else + self.tex:toScreenFull(x, y, self.w, self.h, self.tex_w, self.tex_h) + end + self.text_tex:toScreenFull(x + self.text_x, y + self.text_y, self.fw, self.fh, self.text_tex_w, self.text_tex_h) + self.cursor_tex:toScreenFull(x + self.text_x + (self.cursor-self.scroll) * self.font_mono_w, y + self.text_y, self.cursor_w, self.cursor_h, self.cursor_tex_w, self.cursor_tex_h) +end diff --git a/game/engines/default/engine/ui/Textzone.lua b/game/engines/default/engine/ui/Textzone.lua new file mode 100644 index 0000000000000000000000000000000000000000..74699b58adb185cc6c628bead4b4066cfda491af --- /dev/null +++ b/game/engines/default/engine/ui/Textzone.lua @@ -0,0 +1,99 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +require "engine.class" +local Base = require "engine.ui.Base" +local Focusable = require "engine.ui.Focusable" + +--- A generic UI list +module(..., package.seeall, class.inherit(Base, Focusable)) + +function _M:init(t) + self.text = assert(t.text, "no textzone text") + self.w = assert(t.width, "no list width") + self.h = assert(t.height, "no list height") + self.scrollbar = t.scrollbar + + Base.init(self, t) +end + +function _M:generate() + local list = self.text:splitLines(self.w - 10, self.font) + self.scroll = 1 + self.max = #list + self.max_display = math.floor(self.h / self.font_h) + self.can_focus = self.scrollbar and (self.max_display < self.max) + + local fw, fh = self.w, self.font_h + self.fw, self.fh = fw, fh + + -- Draw the list items + self.list = {} + local r, g, b = 255, 255, 255 + for i, l in ipairs(list) do + local s = core.display.newSurface(fw, fh) + r, g, b = s:drawColorStringBlended(self.font, l, 0, 0, r, g, b, true) + + local item = {} + item._tex, item._tex_w, item._tex_h = s:glTexture() + self.list[#self.list+1] = item + end + + -- Draw the scrollbar + if self.scrollbar then + local sb, sb_w, sb_h = self:getImage("ui/scrollbar.png") + local ssb, ssb_w, ssb_h = self:getImage("ui/scrollbar-sel.png") + + self.scrollbar = { bar = {}, sel = {} } + self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.tex, self.scrollbar.sel.texw, self.scrollbar.sel.texh = ssb_w, ssb_h, ssb:glTexture() + local s = core.display.newSurface(sb_w, self.h - fh) + s:erase(200,0,0) + for i = 0, self.h - fh do s:merge(sb, 0, i) end + self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.tex, self.scrollbar.bar.texw, self.scrollbar.bar.texh = ssb_w, self.h - fh, s:glTexture() + end + + -- Add UI controls + self.mouse:registerZone(0, 0, self.w, self.h, function(button, x, y, xrel, yrel, bx, by, event) + if button == "wheelup" and event == "button" then self.key:triggerVirtual("MOVE_UP") + elseif button == "wheeldown" and event == "button" then self.key:triggerVirtual("MOVE_DOWN") + end + end) + self.key:addBinds{ + MOVE_UP = function() self.scroll = util.bound(self.scroll - 1, 1, self.max - self.max_display + 1) end, + MOVE_DOWN = function() self.scroll = util.bound(self.scroll + 1, 1, self.max - self.max_display + 1) end, + } +end + + +function _M:display(x, y) + local bx, by = x, y + local max = math.min(self.scroll + self.max_display - 1, self.max) + for i = self.scroll, max do + local item = self.list[i] + item._tex:toScreenFull(x, y, self.fw, self.fh, item._tex_w, item._tex_h) + y = y + self.fh + end + + if self.focused and self.scrollbar then + local pos = self.scroll * (self.h - self.fh) / (self.max - self.max_display + 1) + + self.scrollbar.bar.tex:toScreenFull(bx + self.w - self.scrollbar.bar.w, by + self.fh, self.scrollbar.bar.w, self.scrollbar.bar.h, self.scrollbar.bar.texw, self.scrollbar.bar.texh) + self.scrollbar.sel.tex:toScreenFull(bx + self.w - self.scrollbar.sel.w, by + self.fh + pos, self.scrollbar.sel.w, self.scrollbar.sel.h, self.scrollbar.sel.texw, self.scrollbar.sel.texh) + end +end diff --git a/game/engines/default/special/mainmenu/class/TestUI.lua b/game/engines/default/special/mainmenu/class/TestUI.lua index 419c0a2b92f8b5b55b3cfaebaab784dadad9abdb..0c7d51c88b4ee177492e23179f230f53cb955226 100644 --- a/game/engines/default/special/mainmenu/class/TestUI.lua +++ b/game/engines/default/special/mainmenu/class/TestUI.lua @@ -29,6 +29,8 @@ local DownloadDialog = require "engine.dialogs.DownloadDialog" local ListColumns = require "engine.ui.ListColumns" local Button = require "engine.ui.Button" +local Textzone = require "engine.ui.Textzone" +local Textbox = require "engine.ui.Textbox" local Dialog = require "engine.ui.Dialog" module(..., package.seeall, class.inherit(engine.Game, engine.interface.GameMusic)) @@ -38,34 +40,78 @@ function _M:init() self.refuse_threads = true + local text = Textzone.new{width=390, height=200, scrollbar=true, text=[[Pplopppo +zejkfzejkfh #RED#zmkfhzekhfkjfhkjqerhfgq#LAST# jfh qjzfh qzejfh #BLUE#qjzfh +flkzerjflm qzfj #WHITE#zeklfj qlfjkql ql +zf ze +fze fqefqzfjhjqzkef +fqzef + + +zef qzekjfhqjkzfh + +ze fzef zejkhzfekjh ze +fze + fze + zefze fze zef + + ze fzef zefz e +zlfjklhzqfjkqhzefkhqzefjkqh z + + +zef qzekjfhqjkzfh + +ze fzef zejkhzfekjh ze +fze + fze + zefze fze zef + + ze fzef zefz e +zlfjklhzqfjkqhzefkhqzefjkqh z + + +zef qzekjfhqjkzfh + +ze fzef zejkhzfekjh ze +fze + fze + zefze fze zef + + ze fzef zefz e +zlfjklhzqfjkqhzefkhqzefjkqh z +]]} + local box = Textbox.new{title="Name: ", text="", chars=10, max_len=30, fct=function(text) print("got", text) end} local b1 = Button.new{text="Ok", fct=function() print"OK" end} local b2 = Button.new{text="Cancel", fct=function() print"KO" end} - local list = ListColumns.new{width=200, height=200, columns={ - {name="Name", width=150}, {name="Encumber", width=50}, + local list = ListColumns.new{width=390, height=200, sortable=true, scrollbar=true, columns={ + {name="Name", width=90, display_prop="name", sort="name"}, + {name="Encumber", width=10, display_prop="encumberance", sort="encumberance"}, }, list={ - {name="toto", encumberance="20"}, - {name="tutu", encumberance="50"}, - {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance="20"}, - {name="MOUHAHAHAHAH!", encumberance="20"}, - {name="toto", encumberance="20"}, - {name="tutu", encumberance="50"}, - {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance="20"}, - {name="MOUHAHAHAHAH!", encumberance="20"}, - {name="toto", encumberance="20"}, - {name="tutu", encumberance="50"}, - {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance="20"}, - {name="MOUHAHAHAHAH!", encumberance="20"}, - {name="toto", encumberance="20"}, - {name="tutu", encumberance="50"}, - {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance="20"}, - {name="MOUHAHAHAHAH!", encumberance="20"}, + {name="toto", encumberance=20}, + {name="tutu", encumberance=50}, + {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance=20}, + {name="MOUHAHAHAHAH!", encumberance=20}, + {name="toto", encumberance=20}, + {name="tutu", encumberance=50}, + {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance=20}, + {name="MOUHAHAHAHAH!", encumberance=20}, + {name="toto", encumberance=20}, + {name="tutu", encumberance=50}, + {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance=20}, + {name="MOUHAHAHAHAH!", encumberance=20}, + {name="toto", encumberance=20}, + {name="tutu", encumberance=50}, + {name="plopzor #GOLD#Robe of the Archmage#WHITE#!", encumberance=20}, + {name="MOUHAHAHAHAH!", encumberance=20}, }, fct=function(item) print(item.name) end} - local d = Dialog.new("Test UI", 400, 300) + local d = Dialog.new("Test UI", 800, 300) d:loadUI{ {left=0, top=0, ui=list}, - {left=10, bottom=10, ui=b1}, - {right=10, bottom=10, ui=b2}, + {left=400, top=0, ui=text}, + {left=5, bottom=10 + b1.h, ui=box}, + {left=5, bottom=5, ui=b1}, + {right=5, bottom=5, ui=b2}, } self:registerDialog(d) end