diff --git a/game/engines/default/engine/ui/Base.lua b/game/engines/default/engine/ui/Base.lua
index 1235a881d4695fee28f2d205b239a088c719be8d..16cd7bdf3885c13d5ae9a9ce4ee5ddfb3c996328 100644
--- a/game/engines/default/engine/ui/Base.lua
+++ b/game/engines/default/engine/ui/Base.lua
@@ -117,6 +117,17 @@ function _M:getUITexture(file)
 	return r
 end
 
+function _M:drawFontLine(font, text, width) -- always draw with white, outputting texture can have it changed
+	width = width or font:size(text)
+	local tex = font:draw(text, width, 255, 255, 255, true)[1]
+	local r = {t = tex._tex, w=tex.w, h=tex.h, tw=tex._tex_w, th=tex._tex_h}
+	return r
+end
+
+function _M:textureToScreen(tex, x, y, r, g, b, a)
+	return tex.t:toScreenFull(x, y, tex.w, tex.h, tex.tw, tex.th, r, g, b, a)
+end
+
 function _M:makeFrame(base, w, h)
 	local f = {}
 	if base then
diff --git a/game/engines/default/engine/ui/Button.lua b/game/engines/default/engine/ui/Button.lua
index 11b7daa687d861d848e80527f7105a0c1c6ffc05..a2a66debb8dfb84f9c0e463ddf6aa0d37fac6f72 100644
--- a/game/engines/default/engine/ui/Button.lua
+++ b/game/engines/default/engine/ui/Button.lua
@@ -52,9 +52,7 @@ function _M:generate()
 	if self.force_w then w = self.force_w end
 	self.w, self.h = w - frame_ox1 + frame_ox2, h - frame_oy1 + frame_oy2
 
-	local s = core.display.newSurface(w, h)
-	s:drawColorStringBlended(self.font, self.text, 0, 0, 255, 255, 255, true)
-	self.tex = {s:glTexture()}
+	self.tex = self:drawFontLine(self.font, self.text, w)
 	self.font:setStyle("normal")
 
 	-- Add UI controls
@@ -94,8 +92,8 @@ function _M:display(x, y, nb_keyframes, ox, oy)
 		else
 			self:drawFrame(self.frame_sel, x, y)
 		end
-		if self.text_shadow then self.tex[1]:toScreenFull(x-frame_ox1+1, y-frame_oy1+1, self.rw, self.rh, self.tex[2], self.tex[3], 0, 0, 0, self.text_shadow) end
-		self.tex[1]:toScreenFull(x-frame_ox1, y-frame_oy1, self.rw, self.rh, self.tex[2], self.tex[3])
+		if self.text_shadow then self:textureToScreen(self.tex, x-frame_ox1+1, y-frame_oy1+1, 0, 0, 0, self.text_shadow) end
+		self:textureToScreen(self.tex, x-frame_ox1, y-frame_oy1)
 	else
 		if self.glow then
 			local v = self.glow + (1 - self.glow) * (1 + math.cos(core.game.getTime() / 300)) / 2
@@ -109,7 +107,7 @@ function _M:display(x, y, nb_keyframes, ox, oy)
 			self.focus_decay = self.focus_decay - nb_keyframes
 			if self.focus_decay <= 0 then self.focus_decay = nil end
 		end
-		if self.text_shadow then self.tex[1]:toScreenFull(x-frame_ox1+1, y-frame_oy1+1, self.rw, self.rh, self.tex[2], self.tex[3], 0, 0, 0, self.alpha_unfocus * self.text_shadow) end
-		self.tex[1]:toScreenFull(x-frame_ox1, y-frame_oy1, self.rw, self.rh, self.tex[2], self.tex[3], 1, 1, 1, self.alpha_unfocus)
+		if self.text_shadow then self:textureToScreen(self.tex, x-frame_ox1+1, y-frame_oy1+1, 0, 0, 0, self.alpha_unfocus * self.text_shadow) end
+		self:textureToScreen(self.tex, x-frame_ox1, y-frame_oy1, 1, 1, 1, self.alpha_unfocus)
 	end
 end
diff --git a/game/engines/default/engine/ui/Checkbox.lua b/game/engines/default/engine/ui/Checkbox.lua
index ea27fe5a60ecdebe9d7aa75e0bd6d29d9ab74bc9..434aba1e5d115dd14d03996daeac22c5a37d5ec6 100644
--- a/game/engines/default/engine/ui/Checkbox.lua
+++ b/game/engines/default/engine/ui/Checkbox.lua
@@ -43,12 +43,10 @@ function _M:generate()
 	self.tick = self:getUITexture("ui/checkbox-ok.png")
 
 	-- Draw UI
-	self.title_w, self.title_h = self.font:size(self.title)
-	self.w, self.h = self.title_w + self.check.w, math.max(self.font_h, self.check.h)
+	self.tex = self:drawFontLine(self.font, self.title)
+	self.w, self.h = self.tex.w + self.check.w, math.max(self.font_h, self.check.h)
+
 
-	local s = core.display.newSurface(self.title_w, self.title_h)
-	s:drawColorStringBlended(self.font, self.title, 0, 0, 255, 255, 255, true)
-	self.tex = {s:glTexture()}
 
 	-- Add UI controls
 	self.mouse:registerZone(0, 0, self.w, self.h, function(button, x, y, xrel, yrel, bx, by, event)
@@ -70,8 +68,8 @@ end
 
 function _M:display(x, y, nb_keyframes)
 	if self.check_first then
-		if self.text_shadow then self.tex[1]:toScreenFull(x+1 + self.check.w, y+1 + (self.h - self.title_h) / 2, self.title_w, self.title_h, self.tex[2], self.tex[3], 0, 0, 0, self.text_shadow) end
-		self.tex[1]:toScreenFull(x + self.check.w, y + (self.h - self.title_h) / 2, self.title_w, self.title_h, self.tex[2], self.tex[3])
+		if self.text_shadow then self:textureToScreen(self.tex, x+1 + self.check.w, y+1 + (self.h - self.tex.h) / 2, 0, 0, 0, self.text_shadow) end
+		self:textureToScreen(self.tex, x + self.check.w, y + (self.h - self.tex.h) / 2)
 		if self.focused then
 			self.check.t:toScreenFull(x, y, self.check.w, self.check.h, self.check.tw, self.check.th)
 		else
@@ -81,15 +79,15 @@ function _M:display(x, y, nb_keyframes)
 			self.tick.t:toScreenFull(x, y, self.tick.w, self.tick.h, self.tick.tw, self.tick.th)
 		end
 	else
-		if self.text_shadow then self.tex[1]:toScreenFull(x+1, y+1 + (self.h - self.title_h) / 2, self.title_w, self.title_h, self.tex[2], self.tex[3], 0, 0, 0, self.text_shadow) end
-		self.tex[1]:toScreenFull(x, y + (self.h - self.title_h) / 2, self.title_w, self.title_h, self.tex[2], self.tex[3])
+		if self.text_shadow then self:textureToScreen(self.tex, x+1, y+1 + (self.h - self.tex.h) / 2, 0, 0, 0, self.text_shadow) end
+		self:textureToScreen(self.tex, x, y + (self.h - self.tex.h) / 2)
 		if self.focused then
-			self.check.t:toScreenFull(x + self.title_w, y, self.check.w, self.check.h, self.check.tw, self.check.th)
+			self.check.t:toScreenFull(x + self.tex.w, y, self.check.w, self.check.h, self.check.tw, self.check.th)
 		else
-			self.check.t:toScreenFull(x + self.title_w, y, self.check.w, self.check.h, self.check.tw, self.check.th)
+			self.check.t:toScreenFull(x + self.tex.w, y, self.check.w, self.check.h, self.check.tw, self.check.th)
 		end
 		if self.checked then
-			self.tick.t:toScreenFull(x + self.title_w, y, self.tick.w, self.tick.h, self.tick.tw, self.tick.th)
+			self.tick.t:toScreenFull(x + self.tex.w, y, self.tick.w, self.tick.h, self.tick.tw, self.tick.th)
 		end
 	end
 end
diff --git a/game/engines/default/engine/ui/Dialog.lua b/game/engines/default/engine/ui/Dialog.lua
index a72ef1974da4d501a5e574171310126e2adcf12f..9362e9f9917f62b9e288d1069699d9a64cd0c8a7 100644
--- a/game/engines/default/engine/ui/Dialog.lua
+++ b/game/engines/default/engine/ui/Dialog.lua
@@ -356,14 +356,8 @@ function _M:updateTitle(title)
 	local title = title
 	if type(title)=="function" then title = title() end
 	self.font_bold:setStyle("bold")
-	local tw, th = self.font_bold:size(title)
-	local s = core.display.newSurface(tw, th)
-	s:erase(0, 0, 0, 0)
-	s:drawColorStringBlended(self.font_bold, title, 0, 0, self.color.r, self.color.g, self.color.b, true)
+	self.title_tex = self:drawFontLine(self.font_bold, title)
 	self.font_bold:setStyle("normal")
-	self.title_tex = {s:glTexture()}
-	self.title_tex.w = tw
-	self.title_tex.h = th
 end
 
 function _M:loadUI(t)
@@ -743,12 +737,12 @@ function _M:toScreen(x, y, nb_keyframes)
 			if shader then
 				shader:use(true)
 				shader:uniOutlineSize(self.shadow_power, self.shadow_power)
-				shader:uniTextSize(self.title_tex[2], self.title_tex[3])
+				shader:uniTextSize(self.title_tex.tw, self.title_tex.th)
 			else
-				self.title_tex[1]:toScreenFull(x + (self.w - self.title_tex.w) / 2 + 3 + self.frame.title_x, y + 3 + self.frame.title_y, self.title_tex.w, self.title_tex.h, self.title_tex[2], self.title_tex[3], 0, 0, 0, 0.5)
+				self:textureToScreen(self.title_tex, x + (self.w - self.title_tex.w) / 2 + 3 + self.frame.title_x, y + 3 + self.frame.title_y, 0, 0, 0, 0.5)
 			end
 		end
-		self.title_tex[1]:toScreenFull(x + (self.w - self.title_tex.w) / 2 + self.frame.title_x, y + self.frame.title_y, self.title_tex.w, self.title_tex.h, self.title_tex[2], self.title_tex[3])
+		self:textureToScreen(self.title_tex, x + (self.w - self.title_tex.w) / 2 + self.frame.title_x, y + self.frame.title_y)
 		if self.title_shadow and shader then shader:use(false) end
 	end
 
diff --git a/game/engines/default/engine/ui/Dropdown.lua b/game/engines/default/engine/ui/Dropdown.lua
index 3b0ec995b1e10d9433497fd1420d3578f1b8e0ff..c2d41332105d743d5f5f8487902b7c1a094e1dec 100644
--- a/game/engines/default/engine/ui/Dropdown.lua
+++ b/game/engines/default/engine/ui/Dropdown.lua
@@ -94,7 +94,8 @@ function _M:display(x, y, nb_keyframes)
 
 	local item = self.c_list.list[self.c_list.sel]
 	if item then
-		if self.text_shadow then item._tex[1]:toScreenFull(x+1 + self.frame_sel.b4.w, y+1, self.c_list.fw, self.c_list.fh, item._tex[2], item._tex[3], 0, 0, 0, self.text_shadow) end
-		item._tex[1]:toScreenFull(x + self.frame_sel.b4.w, y, self.c_list.fw, self.c_list.fh, item._tex[2], item._tex[3])
+		local cy = (self.c_list.fh - self.c_list.font_h) / 2
+		if self.text_shadow then self:textureToScreen(item._tex, x + 1 + self.frame_sel.b4.w, y + 1 + cy, 0, 0, 0, self.text_shadow) end
+		self:textureToScreen(item._tex, x + self.frame_sel.b4.w, y + cy, item._tex_color[1], item._tex_color[2], item._tex_color[3], 1)
 	end
 end
diff --git a/game/engines/default/engine/ui/EquipDoll.lua b/game/engines/default/engine/ui/EquipDoll.lua
index c5169503a5d356981b62a9e96c6e0c2392b7b123..b67c571d45766afb7b6e898384fb065786198b3a 100644
--- a/game/engines/default/engine/ui/EquipDoll.lua
+++ b/game/engines/default/engine/ui/EquipDoll.lua
@@ -46,14 +46,8 @@ function _M:generate()
 	self:generateEquipDollFrames()
 
 	self.font_bold:setStyle("bold")
-	local tw, th = self.font_bold:size(self.actor.name)
-	local s = core.display.newSurface(tw, th)
-	s:erase(0, 0, 0, 0)
-	s:drawColorStringBlended(self.font_bold, self.actor.name, 0, 0, colors.GOLD.r, colors.GOLD.g, colors.GOLD.b, true)
+	self.charname_tex = self:drawFontLine(self.font_bold, self.actor.name)
 	self.font_bold:setStyle("normal")
-	self.charname_tex = {s:glTexture()}
-	self.charname_tex.w = tw
-	self.charname_tex.h = th
 
 	self.inner_scroll = self:makeFrame("ui/tooltip/", self.w, self.h)
 
@@ -183,8 +177,8 @@ function _M:display(x, y, nb_keyframes, ox, oy)
 
 	Base.drawFrame(self, self.inner_scroll, x, y + self.base_doll_y, 1, 1, 1, self.focused and 1 or 0.5)
 
-	if self.title_shadow then self.charname_tex[1]:toScreenFull(x + (self.w - self.charname_tex.w) / 2 + 2, y + self.base_doll_y + 5 + 2, self.charname_tex.w, self.charname_tex.h, self.charname_tex[2], self.charname_tex[3], 0, 0, 0, 0.5) end
-	self.charname_tex[1]:toScreenFull(x + (self.w - self.charname_tex.w) / 2, y + self.base_doll_y + 5, self.charname_tex.w, self.charname_tex.h, self.charname_tex[2], self.charname_tex[3])
+	if self.title_shadow then self:textureToScreen(self.charname_tex, x + (self.w - self.charname_tex.w) / 2 + 2, y + self.base_doll_y + 5 + 2, 0, 0, 0, 0.5) end
+	self:textureToScreen(self.charname_tex, x + (self.w - self.charname_tex.w) / 2, y + self.base_doll_y + 5)
 
 	local doll = self.actor.equipdolls[self.actor.equipdoll or "default"]
 	if not doll then return end
diff --git a/game/engines/default/engine/ui/List.lua b/game/engines/default/engine/ui/List.lua
index 21627f7c5faa8ba0725a5b5acf78d15ea5150d73..43beac1c772e8a17a2dae079481543e173f15275 100644
--- a/game/engines/default/engine/ui/List.lua
+++ b/game/engines/default/engine/ui/List.lua
@@ -51,8 +51,6 @@ function _M:generate()
 
 	local fw, fh = self.w, self.font_h + 6
 	self.fw, self.fh = fw, fh
-	if not self.surf then self.surf = core.display.newSurface(fw, fh) end
-	local s = self.surf
 
 	self.frame = self:makeFrame(nil, fw, fh)
 	self.frame_sel = self:makeFrame("ui/selector-sel", fw, fh)
@@ -128,13 +126,11 @@ function _M:generate()
 end
 
 function _M:drawItem(item)
-	local s = self.surf
 	local color = item.color or {255,255,255}
 	local text = item[self.display_prop]
 
-	s:erase(0, 0, 0, 0)
-	s:drawColorStringBlended(self.font, text, 0, (self.fh - self.font_h) / 2, color[1], color[2], color[3], true, fw)
-	item._tex = {s:glTexture()}
+	item._tex = self:drawFontLine(self.font, text, self.fw)
+	item._tex_color = {color[1]/255, color[2]/255, color[3]/255}
 end
 
 function _M:select(i)
@@ -163,6 +159,7 @@ function _M:display(x, y, nb_keyframes)
 	local bx, by = x, y
 
 	local max = math.min(self.scroll + self.max_display - 1, self.max)
+	local cy = (self.fh - self.font_h) / 2
 	for i = self.scroll, max do
 		local item = self.list[i]
 		if not item then break end
@@ -178,8 +175,8 @@ function _M:display(x, y, nb_keyframes)
 				if item.focus_decay <= 0 then item.focus_decay = nil end
 			end
 		end
-		if self.text_shadow then item._tex[1]:toScreenFull(x+1 + self.frame_sel.b4.w, y+1, self.fw, self.fh, item._tex[2], item._tex[3], 0, 0, 0, self.text_shadow) end
-		item._tex[1]:toScreenFull(x + self.frame_sel.b4.w, y, self.fw, self.fh, item._tex[2], item._tex[3])
+		if self.text_shadow then self:textureToScreen(item._tex, x + 1 + self.frame_sel.b4.w, y + 1 + cy, 0, 0, 0, self.text_shadow) end
+		self:textureToScreen(item._tex, x + self.frame_sel.b4.w, y + cy, item._tex_color[1], item._tex_color[2], item._tex_color[3], 1)
 		y = y + self.fh
 	end
 
diff --git a/game/engines/default/engine/ui/Numberbox.lua b/game/engines/default/engine/ui/Numberbox.lua
index 2ad24b4ca1bb0325c00ead54d0474754fd3904cd..8a8ccdcc1e095acc1b1607e1d637d7ed5c7b7c25 100644
--- a/game/engines/default/engine/ui/Numberbox.lua
+++ b/game/engines/default/engine/ui/Numberbox.lua
@@ -18,29 +18,24 @@
 -- darkgod@te4.org
 
 require "engine.class"
-local Base = require "engine.ui.Base"
-local Focusable = require "engine.ui.Focusable"
+local Textbox = require "engine.ui.Textbox"
 
 --- A generic UI textbox
-module(..., package.seeall, class.inherit(Base, Focusable))
+module(..., package.seeall, class.inherit(Textbox))
 
 function _M:init(t)
-	self.title = assert(t.title, "no numberbox title")
+	t = table.clone(t)
+	local on_change = t.on_change
+	t.on_change = nil
+	if t.number then
+		t.text = tostring(t.number)
+	end
+	Textbox.init(self, t)
 	self.number = t.number or 0
+	self.on_number_change = on_change
 	self.min = t.min or 0
-	self.on_change = t.on_change
 	self.max = t.max or 9999999
-	self.fct = assert(t.fct, "no numberbox fct")
-	self.chars = assert(t.chars, "no numberbox chars")
 	self.first = true
-
-	self.tmp = {}
-	local text = tostring(self.number)
-	for i = 1, #text do self.tmp[#self.tmp+1] = text:sub(i, i) end
-	self.cursor = #self.tmp + 1
-	self.scroll = 1
-
-	Base.init(self, t)
 end
 
 function _M:on_focus(v)
@@ -48,81 +43,14 @@ function _M:on_focus(v)
 end
 
 function _M:generate()
-	self.mouse:reset()
-	self.key:reset()
+	Textbox.generate(self)
 
-	-- Draw UI
-	local title_w = self.font:size(self.title)
-	self.title_w = title_w
-	local frame_w = self.chars * self.font_mono_w + 12
-	self.w = title_w + frame_w
-	self.h = self.font_h + 6
+	self.key:addIgnore("_UP", false)
+	self.key:addIgnore("_DOWN", false)
 
-	self.texcursor = self:getUITexture("ui/textbox-cursor.png")
-	self.frame = self:makeFrame("ui/textbox", frame_w, self.h)
-	self.frame_sel = self:makeFrame("ui/textbox-sel", frame_w, self.h)
-
-	local w, h = self.w, self.h
-	local fw, fh = frame_w - 12, self.font_h
-	self.fw, self.fh = fw, fh
-	self.text_x = 6 + title_w
-	self.text_y = (h - fh) / 2
-	self.cursor_y = (h - self.texcursor.h) / 2
-	self.max_display = math.floor(fw / self.font_mono_w)
-	self.text_surf = core.display.newSurface(fw, fh)
-	self.text_tex, self.text_tex_w, self.text_tex_h = self.text_surf:glTexture()
-	self:updateText()
-
-	if title_w > 0 then
-		local s = core.display.newSurface(title_w, h)
-		s:erase(0, 0, 0, 0)
-		s:drawColorStringBlended(self.font, self.title, 0, (h - fh) / 2, 255, 255, 255, true)
-		self.tex, self.tex_w, self.tex_h = s:glTexture()
-	end
-
-	-- Add UI controls
-	self.mouse:registerZone(title_w + 6, 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.first = false self.fct(self.text) end)
-	self.key:addIgnore("_ESCAPE", v)
-	self.key:addIgnore("_TAB", true)
 	self.key:addCommands{
 		_UP = function() self.first = false self:updateText(1) end,
 		_DOWN = function() self.first = false self:updateText(-1) end,
-		_LEFT = function() self.first = false 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,
-		_RIGHT = function() self.first = false 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,
-		_DELETE = function()
-			if self.first then self.first = false self.tmp = {} self:updateText() end
-			if self.cursor <= #self.tmp then
-				table.remove(self.tmp, self.cursor)
-				self:updateText()
-			end
-		end,
-		_BACKSPACE = function()
-			if self.first then self.first = false self.tmp = {} self:updateText() end
-			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.first = false
-			self.cursor = 1
-			self.scroll = util.scroll(self.cursor, self.scroll, self.max_display)
-			self:updateText()
-		end,
-		_END = function()
-			self.first = false
-			self.cursor = #self.tmp + 1
-			self.scroll = util.scroll(self.cursor, self.scroll, self.max_display)
-			self:updateText()
-		end,
 		__TEXTINPUT = function(c)
 			if self.first then self.first = false self.tmp = {} self.cursor = 1 end
 			if #self.tmp and (c == '-' or c == '0' or c == '1' or c == '2' or c == '3' or c == '4' or c == '5' or c == '6' or c == '7' or c == '8' or c == '9') then
@@ -137,46 +65,16 @@ end
 
 function _M:updateText(v)
 	local old = self.number
-	local text = ""
 	if not v then
-		if not self.tmp[1] then self.tmp = {} end
-		self.number = tonumber(table.concat(self.tmp)) or 0
+		self.number = self.tmp and tonumber(table.concat(self.tmp)) or 0
 		self.number = util.bound(self.number, self.min, self.max)
-		for i = self.scroll, self.scroll + self.max_display - 1 do
-			if not self.tmp[i] then break end
-			text = text .. self.tmp[i]
-		end
+		Textbox.updateText(self)
 	else
 		self.number = self.number or 0
 		self.number = util.bound(self.number + v, self.min, self.max)
 		text = tostring(self.number)
-		self.tmp = {}
-		for i = 1, #text do self.tmp[#self.tmp+1] = text:sub(i, i) if not self.tmp[#self.tmp] then break end end
-		self.cursor = #self.tmp + 1
+		Textbox.setText(self, text)
 	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)
-	if self.on_change and old ~= self.number then self.on_change(self.number) end
-end
-
-function _M:display(x, y, nb_keyframes)
-	if self.tex then
-		if self.text_shadow then self.tex:toScreenFull(x+1, y+1, self.title_w, self.h, self.tex_w, self.tex_h, 0, 0, 0, self.text_shadow) end
-		self.tex:toScreenFull(x, y, self.title_w, self.h, self.tex_w, self.tex_h)
-	end
-	if self.focused then
-		self:drawFrame(self.frame_sel, x + self.title_w, y)
-		self.texcursor.t:toScreenFull(x + self.text_x + (self.cursor-self.scroll) * self.font_mono_w - (self.texcursor.w / 2), y + self.cursor_y, self.texcursor.w, self.texcursor.h, self.texcursor.tw, self.texcursor.th)
-	else
-		self:drawFrame(self.frame, x + self.title_w, y)
-		if self.focus_decay then
-			self:drawFrame(self.frame_sel, x + self.title_w, y, 1, 1, 1, self.focus_decay / self.focus_decay_max_d)
-			self.focus_decay = self.focus_decay - nb_keyframes
-			if self.focus_decay <= 0 then self.focus_decay = nil end
-		end
-	end
-	if self.text_shadow then self.text_tex:toScreenFull(x+1 + self.text_x, y+1 + self.text_y, self.fw, self.fh, self.text_tex_w, self.text_tex_h, 0, 0, 0, self.text_shadow) 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)
+	if self.on_change and old ~= self.number then self.on_number_change(self.number) end
 end
diff --git a/game/engines/default/engine/ui/SubDialog.lua b/game/engines/default/engine/ui/SubDialog.lua
index 638f470aadf1fd193c6eca014d66abb670258ece..e9468beb5acdc0cd0fae0297a78a05ea25e21244 100644
--- a/game/engines/default/engine/ui/SubDialog.lua
+++ b/game/engines/default/engine/ui/SubDialog.lua
@@ -145,14 +145,8 @@ function _M:updateTitle(title)
 	local title = title
 	if type(title)=="function" then title = title() end
 	self.font_bold:setStyle("bold")
-	local tw, th = self.font_bold:size(title)
-	local s = core.display.newSurface(tw, th)
-	s:erase(0, 0, 0, 0)
-	s:drawColorStringBlended(self.font_bold, title, 0, 0, self.color.r, self.color.g, self.color.b, true)
+	self.title_tex = self:drawFontLine(self.font_bold, title)
 	self.font_bold:setStyle("normal")
-	self.title_tex = {s:glTexture()}
-	self.title_tex.w = tw
-	self.title_tex.h = th
 end
 
 function _M:loadUI(t)
@@ -414,8 +408,8 @@ function _M:display(x, y, nb_keyframes)
 
 	-- Title
 	if self.title then
-		if self.title_shadow then self.title_tex[1]:toScreenFull(x + (self.w - self.title_tex.w) / 2 + 3 + self.frame.title_x, y + 3 + self.frame.title_y, self.title_tex.w, self.title_tex.h, self.title_tex[2], self.title_tex[3], 0, 0, 0, 0.5) end
-		self.title_tex[1]:toScreenFull(x + (self.w - self.title_tex.w) / 2 + self.frame.title_x, y + self.frame.title_y, self.title_tex.w, self.title_tex.h, self.title_tex[2], self.title_tex[3])
+		if self.title_shadow then self:textureToScreen(self.title_tex, x + 3 + (self.w - self.title_tex.w) / 2 + self.frame.title_x, y + 3 + self.frame.title_y, 0, 0, 0, 0.5) end
+		self:textureToScreen(self.title_tex, x + (self.w - self.title_tex.w) / 2 + self.frame.title_x, y + self.frame.title_y)
 	end
 
 	self:innerDisplayBack(x, y, nb_keyframes, tx, ty)
diff --git a/game/engines/default/engine/ui/Tab.lua b/game/engines/default/engine/ui/Tab.lua
index 9776a448101651e354c1bed7fc338b87caa45b36..3621198b462ae0d073fb3c4630bfd8055463987c 100644
--- a/game/engines/default/engine/ui/Tab.lua
+++ b/game/engines/default/engine/ui/Tab.lua
@@ -44,12 +44,8 @@ function _M:generate()
 	self.key:reset()
 
 	-- Draw UI
-	self.title_w, self.title_h = self.font:size(self.title)
-	self.w, self.h = self.title_w - frame_ox1 + frame_ox2, self.title_h - frame_oy1 + frame_oy2
-
-	local s = core.display.newSurface(self.title_w, self.title_h)
-	s:drawColorStringBlended(self.font, self.title, 0, 0, 255, 255, 255, true)
-	self.tex = {s:glTexture()}
+	self.tex = self:drawFontLine(self.font, self.title)
+	self.w, self.h = self.tex.w - frame_ox1 + frame_ox2, self.tex.h - frame_oy1 + frame_oy2
 
 	-- Add UI controls
 	self.mouse:registerZone(0, 0, self.w+1, self.h+6, function(button, x, y, xrel, yrel, bx, by, event)
@@ -58,7 +54,7 @@ function _M:generate()
 		end
 	end)
 
-	self.rw, self.rh = self.title_w, self.title_h
+	self.rw, self.rh = self.tex.w, self.tex.h
 	self.frame = self:makeFrame("ui/button", self.w, self.h)
 --	self.frame.b2 = self:getUITexture("ui/border_hor_middle.png")
 
@@ -111,6 +107,6 @@ function _M:display(x, y, nb_keyframes)
 	else
 		self:drawFrame(self.frame_sel, x, y, 1, 0.5, 0.5, 1)
 	end
-	if self.text_shadow then self.tex[1]:toScreenFull(x+1-frame_ox1, y+1-frame_oy1, self.rw, self.rh, self.tex[2], self.tex[3], 0, 0, 0, self.text_shadow) end
-	self.tex[1]:toScreenFull(x-frame_ox1, y-frame_oy1, self.rw, self.rh, self.tex[2], self.tex[3])
+	if self.text_shadow then self:textureToScreen(self.tex, x-frame_ox1, y-frame_oy1, 0, 0, 0, self.text_shadow) end
+	self:textureToScreen(self.tex, x-frame_ox1, y-frame_oy1)
 end
diff --git a/game/engines/default/engine/ui/Textbox.lua b/game/engines/default/engine/ui/Textbox.lua
index b7fcd09482370cb3d8240a9b65068f6d398aed55..0619aff6c6f275e748820ed480bacfbb55c9c83c 100644
--- a/game/engines/default/engine/ui/Textbox.lua
+++ b/game/engines/default/engine/ui/Textbox.lua
@@ -71,15 +71,10 @@ function _M:generate()
 	self.text_y = (h - fh) / 2
 	self.cursor_y = (h - self.texcursor.h) / 2
 	self.max_display = math.floor(fw / self.font_mono_w)
-	self.text_surf = core.display.newSurface(fw, fh)
-	self.text_tex, self.text_tex_w, self.text_tex_h = self.text_surf:glTexture()
 	self:updateText()
 
 	if title_w > 0 then
-		local s = core.display.newSurface(title_w, h)
-		s:erase(0, 0, 0, 0)
-		s:drawColorStringBlended(self.font, self.title, 0, (h - fh) / 2, 255, 255, 255, true)
-		self.tex, self.tex_w, self.tex_h = s:glTexture()
+		self.tex = self:drawFontLine(self.font, self.title, title_w)
 	end
 
 	-- Add UI controls
@@ -162,28 +157,26 @@ end
 function _M:updateText()
 	if not self.tmp[1] then self.tmp = {} end
 	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
+	local text
+	local b, e = self.scroll, math.min(self.scroll + self.max_display - 1, #self.tmp)
+	if not self.hide then text = table.concat(self.tmp, nil, b, e)
+	else text = string.rep("*", e - b + 1) end
+
+	self.text_tex = self:drawFontLine(self.font_mono, text, self.fw)
 
-	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)
 	if self.on_change and self.old_text ~= self.text then self.on_change(self.text) end
 	self.old_text = self.text
 end
 
 function _M:display(x, y, nb_keyframes)
+	local text_x, text_y = self.text_x, self.text_y
 	if self.tex then
-		if self.text_shadow then self.tex:toScreenFull(x+1, y+1, self.title_w, self.h, self.tex_w, self.tex_h, 0, 0, 0, self.text_shadow) end
-		self.tex:toScreenFull(x, y, self.title_w, self.h, self.tex_w, self.tex_h)
+		if self.text_shadow then self:textureToScreen(self.tex, x+1, y+text_y+1, 0, 0, 0, self.text_shadow) end
+		self:textureToScreen(self.tex, x, y+text_y)
 	end
 	if self.focused then
 		self:drawFrame(self.frame_sel, x + self.title_w, y)
-		self.texcursor.t:toScreenFull(x + self.text_x + (self.cursor-self.scroll) * self.font_mono_w - (self.texcursor.w / 2), y + self.cursor_y, self.texcursor.w, self.texcursor.h, self.texcursor.tw, self.texcursor.th)
+		self:textureToScreen(self.texcursor, x + self.text_x + (self.cursor-self.scroll) * self.font_mono_w - (self.texcursor.w / 2), y + self.cursor_y)
 	else
 		self:drawFrame(self.frame, x + self.title_w, y)
 		if self.focus_decay then
@@ -192,6 +185,6 @@ function _M:display(x, y, nb_keyframes)
 			if self.focus_decay <= 0 then self.focus_decay = nil end
 		end
 	end
-	if self.text_shadow then self.text_tex:toScreenFull(x+1 + self.text_x, y+1 + self.text_y, self.fw, self.fh, self.text_tex_w, self.text_tex_h, 0, 0, 0, self.text_shadow) 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)
+	if self.text_shadow then self:textureToScreen(self.text_tex, x+1 + self.text_x, y+1 + self.text_y, 0, 0, 0, self.text_shadow) end
+	self:textureToScreen(self.text_tex, x+1 + self.text_x, y+1 + self.text_y)
 end
diff --git a/game/engines/default/engine/ui/VariableList.lua b/game/engines/default/engine/ui/VariableList.lua
index 25e2a1a0c69562313a337b9da6e29efdc09fabf0..612bb6b1618caa171593d8599a70a4c1ce7b8a9a 100644
--- a/game/engines/default/engine/ui/VariableList.lua
+++ b/game/engines/default/engine/ui/VariableList.lua
@@ -58,19 +58,19 @@ function _M:generate()
 	local minh = 0
 	for i, item in ipairs(self.list) do
 		local color = item.color or {255,255,255}
-		local text = item[self.display_prop]:splitLines(fw - self.frame_sel.b4.w - self.frame_sel.b6.w, self.font)
+		local width = fw - self.frame_sel.b4.w - self.frame_sel.b6.w
+
+		local text = self.font:draw(item[self.display_prop], width, color[1], color[2], color[3])
 		local fh = fh * #text + self.frame_sel.b8.w / 3 * 2
-		local s = core.display.newSurface(fw, fh)
 
-		s:erase(0, 0, 0, 0)
-		local color_r, color_g, color_b = color[1], color[2], color[3]
-		for z = 1, #text do
-			color_r, color_g, color_b = s:drawColorStringBlended(self.font, text[z], self.frame_sel.b4.w, self.frame_sel.b8.w / 3 + self.font_h * (z-1), color_r, color_g, color_b, true)
+		local texs = {}
+		for z, tex in ipairs(text) do
+			texs[z] = {t=tex._tex, tw=tex._tex_w, th = tex._tex_h, w=tex.w, h=tex.h, y = (z - 1) * self.font_h + self.frame_sel.b8.w / 3}
 		end
 
 		item.start_h = sh
 		item.fh = fh
-		item._tex = {s:glTexture()}
+		item._texs = texs
 
 		sh = sh + fh
 		if i <= self.min_items_shown then minh = sh end
@@ -182,8 +182,10 @@ function _M:display(x, y, nb_keyframes, screen_x, screen_y)
 				if item.focus_decay <= 0 then item.focus_decay = nil end
 			end
 		end
-		if self.text_shadow then item._tex[1]:toScreenFull(x+1 + self.frame_sel.b4.w, y+1, self.fw, item.fh, item._tex[2], item._tex[3], 0, 0, 0, self.text_shadow) end
-		item._tex[1]:toScreenFull(x + self.frame_sel.b4.w, y, self.fw, item.fh, item._tex[2], item._tex[3])
+		for z, tex in pairs(item._texs) do
+			if self.text_shadow then self:textureToScreen(tex, x+1 + self.frame_sel.b4.w, y+1 + tex.y, 0, 0, 0, self.text_shadow) end
+			self:textureToScreen(tex, x + self.frame_sel.b4.w, y + tex.y)
+		end
 		y = y + item.fh
 	end
 
diff --git a/game/modules/tome/dialogs/elements/StatusBox.lua b/game/modules/tome/dialogs/elements/StatusBox.lua
index 3a020b105a1416788eb5fd0ddc9ad25819f10ec8..456ea122685f170d81c035b293ca9616a3e05041 100644
--- a/game/modules/tome/dialogs/elements/StatusBox.lua
+++ b/game/modules/tome/dialogs/elements/StatusBox.lua
@@ -43,9 +43,7 @@ function _M:generate()
 	self.key:reset()
 	self.h = self.font_h
 
-	self.text_surf = core.display.newSurface(self.w, self.h)
-	self.text_surf:erase(0, 0, 0, 0)
-	self.text_tex = {self.text_surf:glTexture()}
+	self:setTextColor(self.text, self.color)
 	self.iw, self.ih = self.w, self.h
 
 	self.w = self.w + 6
@@ -56,9 +54,7 @@ function _M:setTextColor(text, color)
 	self.text = text or self.text
 	self.color = color or self.color
 
-	self.text_surf:erase(0, 0, 0, 0)
-	self.text_surf:drawColorStringCentered(self.font_bold, self.text, 0, 0, self.iw, self.ih, self.color.r, self.color.g, self.color.b)
-	self.text_surf:updateTexture(self.text_tex[1])
+	self.text_tex = self:drawFontLine(self.font_bold, self.text)
 	self.frame_decay = 0
 end
 
@@ -72,6 +68,6 @@ function _M:display(x, y, nb_keyframes)
 			alpha = 1 - (self.frame_decay - self.frame_delay) / easetime
 		end
 	end
-	self.text_tex[1]:toScreenFull(x + 3, y + 3, self.iw, self.ih, self.text_tex[2], self.text_tex[3], 1, 1, 1, alpha)
+	self:textureToScreen(self.text_tex, x + 3 + (self.iw - self.text_tex.w) / 2, y + 3, self.color.r/255, self.color.g/255, self.color.b/255, alpha)
 end