word wrap

This commit is contained in:
airstruck
2015-11-26 08:53:42 -05:00
parent bbba7e1b3d
commit 2c81c0c293
18 changed files with 409 additions and 89 deletions

View File

@@ -29,6 +29,7 @@ local style = {
padding = 8, padding = 8,
background = { 255, 255, 255 }, background = { 255, 255, 255 },
icon = 'icon/32px/Box.png', icon = 'icon/32px/Box.png',
multiline = true,
}, },
} }
@@ -71,8 +72,8 @@ local mainForm = { id = 'mainWindow', type = 'panel',
{ id = 'leftSideBox', width = 200, minwidth = 64, { id = 'leftSideBox', width = 200, minwidth = 64,
{ text = 'Hi, I\'m centered middle. ', style = 'listThing', { text = 'Hi, I\'m centered middle. ', style = 'listThing',
align = 'middle center' }, align = 'middle center' },
{ text = 'Hi, I\'m centered bottom. ', style = 'listThing', { text = 'Hi, I\'m right bottom.\nAlso two lines, woopdy woop.Hi, I\'m right bottom.\nAlso two lines, woopdy woop.Hi, I\'m right bottom.\nAlso two lines, woopdy woop.', style = 'listThing',
align = 'bottom center', slices = 'luigi/theme/light/button.png' }, align = 'bottom right', slices = 'luigi/theme/light/button.png' },
{ text = 'Hi, I\'m centered top. ', style = 'listThing', { text = 'Hi, I\'m centered top. ', style = 'listThing',
align = 'top center' }, align = 'top center' },
{ text = 'A man, a plan, a canal: Panama!', style = 'listThing' }, { text = 'A man, a plan, a canal: Panama!', style = 'listThing' },
@@ -154,13 +155,30 @@ end)
layout.mainCanvas.font = 'font/liberation/LiberationMono-Regular.ttf' layout.mainCanvas.font = 'font/liberation/LiberationMono-Regular.ttf'
layout.mainCanvas.text = [[Abedede sdfsdf asfdsdfdsfs sdfsdfsdf layout.mainCanvas.text = [[
sfsdfdfbv db er erg rth tryj ty j fgh dfgv Lorem ipsum dolor sit amet, consectetur adipisicing elit.
wefwef rgh erh rth e rgs dvg eh tyj rt h erg
erge rg eg erg er ergs erg er ge rh erh rth]] Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
One
two
Three
four
five
six
seven
eight
]]
layout.mainCanvas.align = 'top' layout.mainCanvas.align = 'top'
layout.mainCanvas.multiline = true
local Backend = require 'luigi.backend' local Backend = require 'luigi.backend'
layout.menuQuit:onPress(function (event) Backend.quit() end) layout.menuQuit:onPress(function (event) Backend.quit() end)

View File

@@ -1,5 +1,7 @@
local ROOT = (...):gsub('[^.]*$', '') local ROOT = (...):gsub('[^.]*$', '')
local Backend
if _G.love and _G.love._version_minor > 8 then if _G.love and _G.love._version_minor > 8 then
return require(ROOT .. 'backend.love') return require(ROOT .. 'backend.love')
else else

View File

@@ -4,16 +4,18 @@ local Hooker = require(ROOT .. 'hooker')
local ffi = require 'ffi' local ffi = require 'ffi'
local sdl = require((...) .. '.sdl') local sdl = require((...) .. '.sdl')
local Image = require((...) .. '.image') local Image = require((...) .. '.image')
local Font = require((...) .. '.font') local Font = require((...) .. '.font')
local Keyboard = require((...) .. '.keyboard') local Keyboard = require((...) .. '.keyboard')
local Text = require((...) .. '.text')
local IntOut = ffi.typeof 'int[1]' local IntOut = ffi.typeof 'int[1]'
-- create window and renderer -- create window and renderer
local window = sdl.createWindow('', 0, 0, 800, 600, local window = sdl.createWindow('', 0, 0, 800, 600,
sdl.WINDOW_SHOWN) sdl.WINDOW_SHOWN + sdl.WINDOW_RESIZABLE)
if window == nil then if window == nil then
io.stderr:write(ffi.string(sdl.getError())) io.stderr:write(ffi.string(sdl.getError()))
@@ -58,13 +60,17 @@ Backend.run = function ()
return return
elseif event.type == sdl.WINDOWEVENT elseif event.type == sdl.WINDOWEVENT
and event.window.event == sdl.WINDOWEVENT_RESIZED then and event.window.event == sdl.WINDOWEVENT_RESIZED then
callback.resize(event.window.data1, event.window.data2) local window = event.window
callback.resize(window.data1, window.data2)
elseif event.type == sdl.MOUSEBUTTONDOWN then elseif event.type == sdl.MOUSEBUTTONDOWN then
callback.mousepressed(event.button.x, event.button.y, event.button.button) local button = event.button
callback.mousepressed(button.x, button.y, button.button)
elseif event.type == sdl.MOUSEBUTTONUP then elseif event.type == sdl.MOUSEBUTTONUP then
callback.mousereleased(event.button.x, event.button.y, event.button.button) local button = event.button
callback.mousereleased(button.x, button.y, button.button)
elseif event.type == sdl.MOUSEMOTION then elseif event.type == sdl.MOUSEMOTION then
callback.mousemoved(event.motion.x, event.motion.y) local motion = event.motion
callback.mousemoved(motion.x, motion.y)
elseif event.type == sdl.KEYDOWN then elseif event.type == sdl.KEYDOWN then
local key = Keyboard.stringByKeycode[event.key.keysym.sym] local key = Keyboard.stringByKeycode[event.key.keysym.sym]
callback.keypressed(key, event.key['repeat']) callback.keypressed(key, event.key['repeat'])
@@ -94,6 +100,10 @@ Backend.Image = function (path)
return Image(renderer, path) return Image(renderer, path)
end end
Backend.Text = function (...)
return Text(renderer, ...)
end
Backend.Quad = function (x, y, w, h) Backend.Quad = function (x, y, w, h)
return { x, y, w, h } return { x, y, w, h }
end end
@@ -101,7 +111,28 @@ end
Backend.SpriteBatch = require((...) .. '.spritebatch') Backend.SpriteBatch = require((...) .. '.spritebatch')
Backend.draw = function (drawable, x, y, sx, sy) Backend.draw = function (drawable, x, y, sx, sy)
return drawable:draw(x, y, sx, sy) if drawable.draw then
return drawable:draw(x, y, sx, sy)
end
if drawable.sdlTexture == nil
or drawable.sdlRenderer == nil
or drawable.getWidth == nil
or drawable.getHeight == nil
then return
end
local w = drawable:getWidth() * (sx or 1)
local h = drawable:getHeight() * (sy or 1)
-- HACK. Somehow drawing something first prevents renderCopy from
-- incorrectly scaling up in some cases (after rendering slices).
-- For example http://stackoverflow.com/questions/28218906
sdl.renderDrawPoint(drawable.sdlRenderer, -1, -1)
-- Draw the image.
sdl.renderCopy(drawable.sdlRenderer, drawable.sdlTexture,
nil, sdl.Rect(x, y, w, h))
end end
Backend.drawRectangle = function (mode, x, y, w, h) Backend.drawRectangle = function (mode, x, y, w, h)
@@ -118,7 +149,7 @@ local currentFont = Font()
Backend.print = function (text, x, y) Backend.print = function (text, x, y)
if not text or text == '' then return end if not text or text == '' then return end
local font = currentFont.sdlFont local font = currentFont.sdlFont
local color = sdl.Color(currentFont.color) local color = sdl.Color(currentFont.color or { 0, 0, 0, 255 })
local write = Font.SDL2_ttf.TTF_RenderUTF8_Blended local write = Font.SDL2_ttf.TTF_RenderUTF8_Blended
local surface = write(font, text, color) local surface = write(font, text, color)
@@ -130,7 +161,9 @@ end
Backend.printf = Backend.print Backend.printf = Backend.print
Backend.getClipboardText = sdl.getClipboardText Backend.getClipboardText = function ()
return ffi.string(sdl.getClipboardText())
end
Backend.setClipboardText = sdl.setClipboardText Backend.setClipboardText = sdl.setClipboardText

View File

@@ -197,13 +197,10 @@ Font.SDL2_ttf = SDL2_ttf
local fontCache = {} local fontCache = {}
function Font:constructor (path, size, color) function Font:constructor (path, size)
if not size then if not size then
size = 12 size = 12
end end
if not color then
color = { 0, 0, 0, 255 }
end
if not path then if not path then
path = REL:gsub('%.', '/') .. 'resource/DejaVuSans.ttf' path = REL:gsub('%.', '/') .. 'resource/DejaVuSans.ttf'
end end
@@ -222,7 +219,6 @@ function Font:constructor (path, size, color)
end end
self.sdlFont = fontCache[key] self.sdlFont = fontCache[key]
self.color = color
end end
function Font:setAlignment (align) function Font:setAlignment (align)

View File

@@ -14,10 +14,12 @@ end })
function Image:constructor (renderer, path) function Image:constructor (renderer, path)
self.sdlRenderer = renderer self.sdlRenderer = renderer
self.sdlSurface = SDL2_image.IMG_Load(path) self.sdlSurface = ffi.gc(
ffi.gc(self.sdlSurface, sdl.freeSurface) SDL2_image.IMG_Load(path),
self.sdlTexture = sdl.createTextureFromSurface(renderer, self.sdlSurface) sdl.freeSurface)
ffi.gc(self.sdlTexture, sdl.destroyTexture) self.sdlTexture = ffi.gc(
sdl.createTextureFromSurface(renderer, self.sdlSurface),
sdl.destroyTexture)
self.width = self.sdlSurface.w self.width = self.sdlSurface.w
self.height = self.sdlSurface.h self.height = self.sdlSurface.h
end end
@@ -30,17 +32,4 @@ function Image:getHeight ()
return self.height return self.height
end end
function Image:draw (x, y, sx, sy)
local w = self.width * (sx or 1)
local h = self.height * (sy or 1)
-- HACK. Somehow drawing something first prevents renderCopy from
-- incorrectly scaling up in some cases (after rendering slices).
-- For example http://stackoverflow.com/questions/28218906
sdl.renderDrawPoint(self.sdlRenderer, -1, -1)
-- Draw the image.
sdl.renderCopy(self.sdlRenderer, self.sdlTexture, nil, sdl.Rect(x, y, w, h))
end
return Image return Image

View File

@@ -1,7 +1,7 @@
local ROOT = (...):gsub('[^.]*$', '') local REL = (...):gsub('[^.]*$', '')
local ffi = require 'ffi' local ffi = require 'ffi'
local sdl = require(ROOT .. 'sdl2.init') local sdl = require(REL .. 'sdl2.init')
sdl.AudioCVT = ffi.typeof 'SDL_AudioCVT' sdl.AudioCVT = ffi.typeof 'SDL_AudioCVT'
-- sdl.AudioDeviceEvent = ffi.typeof 'SDL_AudioDeviceEvent' -- sdl.AudioDeviceEvent = ffi.typeof 'SDL_AudioDeviceEvent'

View File

@@ -0,0 +1,106 @@
local ROOT = (...):gsub('[^.]*.[^.]*.[^.]*$', '')
local REL = (...):gsub('[^.]*$', '')
local ffi = require 'ffi'
local sdl = require(REL .. 'sdl')
local Font = require(REL .. 'font')
local ttf = Font.SDL2_ttf
local Multiline = require(ROOT .. 'multiline')
local Text = setmetatable({}, { __call = function (self, ...)
local object = setmetatable({}, { __index = self })
return object, self.constructor(object, ...)
end })
local function renderSingle (self, font, text, color)
local alphaMod = color and color[4]
color = sdl.Color(color or 0)
local surface = ffi.gc(
ttf.TTF_RenderUTF8_Blended(font.sdlFont, text, color),
sdl.freeSurface)
self.sdlSurface = surface
self.sdlTexture = ffi.gc(
sdl.createTextureFromSurface(self.sdlRenderer, surface),
sdl.destroyTexture)
if alphaMod then
sdl.setTextureAlphaMod(self.sdlTexture, alphaMod)
end
self.width, self.height = surface.w, surface.h
end
local function renderMulti (self, font, text, color, align, limit)
local alphaMod = color and color[4]
local lines = Multiline.wrap(font, text, limit)
local lineHeight = font:getLineHeight()
local height = #lines * lineHeight
color = sdl.Color(color or 0)
local r, g, b, a
if sdl.BYTEORDER == sdl.BIG_ENDIAN then
r, g, b, a = 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
else
r, g, b, a = 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
end
local surface = ffi.gc(
sdl.createRGBSurface(0, limit, height, 32, r, g, b, a),
sdl.freeSurface)
self.sdlSurface = surface
for index, line in ipairs(lines) do
local text = table.concat(line)
local lineSurface = ffi.gc(
ttf.TTF_RenderUTF8_Blended(font.sdlFont, text, color),
sdl.freeSurface)
if lineSurface ~= nil then
local w, h = lineSurface.w, lineSurface.h
local top = (index - 1) * lineHeight
if align == 'left' then
sdl.blitSurface(lineSurface, nil, surface,
sdl.Rect(0, top, w, h))
elseif align == 'right' then
sdl.blitSurface(lineSurface, nil, surface,
sdl.Rect(limit - line.width, top, w, h))
elseif align == 'center' then
sdl.blitSurface(lineSurface, nil, surface,
sdl.Rect((limit - line.width) / 2, top, w, h))
end
end
end
self.sdlTexture = ffi.gc(
sdl.createTextureFromSurface(self.sdlRenderer, surface),
sdl.destroyTexture)
if alphaMod then
sdl.setTextureAlphaMod(self.sdlTexture, alphaMod)
end
self.width, self.height = surface.w, surface.h
end
function Text:constructor (renderer, font, text, color, align, limit)
self.width, self.height = 0, 0
if not text or text == '' then return end
self.sdlRenderer = renderer
if limit then
renderMulti(self, font, text, color, align, limit)
else
renderSingle(self, font, text, color)
end
end
function Text:getWidth ()
return self.width
end
function Text:getHeight ()
return self.height
end
return Text

View File

@@ -11,13 +11,21 @@ Backend.Cursor = love.mouse.newCursor
Backend.Font = require(ROOT .. 'backend.love.font') Backend.Font = require(ROOT .. 'backend.love.font')
Backend.Text = require(ROOT .. 'backend.love.text')
Backend.Image = love.graphics.newImage Backend.Image = love.graphics.newImage
Backend.Quad = love.graphics.newQuad Backend.Quad = love.graphics.newQuad
Backend.SpriteBatch = love.graphics.newSpriteBatch Backend.SpriteBatch = love.graphics.newSpriteBatch
Backend.draw = love.graphics.draw -- love.graphics.draw( drawable, x, y, r, sx, sy, ox, oy, kx, ky )
Backend.draw = function (drawable, ...)
if drawable.typeOf and drawable:typeOf 'Drawable' then
return love.graphics.draw(drawable, ...)
end
return drawable:draw(...)
end
Backend.drawRectangle = love.graphics.rectangle Backend.drawRectangle = love.graphics.rectangle

View File

@@ -0,0 +1,72 @@
local ROOT = (...):gsub('[^.]*.[^.]*.[^.]*$', '')
local REL = (...):gsub('[^.]*$', '')
local Multiline = require(ROOT .. 'multiline')
local Text = setmetatable({}, { __call = function (self, ...)
local object = setmetatable({}, { __index = self })
return object, self.constructor(object, ...)
end })
local function renderSingle (self, x, y, font, text, color)
love.graphics.push('all')
love.graphics.setColor(color or { 0, 0, 0 })
love.graphics.setFont(font.loveFont)
love.graphics.print(text, x, y)
love.graphics.pop()
self.height = font:getLineHeight()
self.width = font:getAdvance(text)
end
local function renderMulti (self, x, y, font, text, color, align, limit)
local lines = Multiline.wrap(font, text, limit)
local lineHeight = font:getLineHeight()
local height = #lines * lineHeight
love.graphics.push('all')
love.graphics.setColor(color or { 0, 0, 0 })
love.graphics.setFont(font.loveFont)
for index, line in ipairs(lines) do
local text = table.concat(line)
local top = (index - 1) * lineHeight
local w = line.width
if align == 'left' then
love.graphics.print(text, x, top + y)
elseif align == 'right' then
love.graphics.print(text, limit - w + x, top + y)
elseif align == 'center' then
love.graphics.print(text, (limit - w) / 2 + x, top + y)
end
end
love.graphics.pop()
self.height = height
self.width = limit
end
function Text:constructor (font, text, color, align, limit)
if limit then
function self:draw (x, y)
return renderMulti(self, x, y, font, text, color, align, limit)
end
else
function self:draw (x, y)
return renderSingle(self, x, y, font, text, color)
end
end
self:draw(-1000000, -1000000)
end
function Text:getWidth ()
return self.width
end
function Text:getHeight ()
return self.height
end
return Text

View File

@@ -1,5 +0,0 @@
local ROOT = (...):gsub('[^.]*$', '')
local Backend = require(ROOT .. 'backend')
return Backend.Font

View File

@@ -34,8 +34,7 @@ function Input:handleKeyPress (layout, key, x, y)
local result = widget:bubbleEvent('KeyPress', { local result = widget:bubbleEvent('KeyPress', {
key = key, key = key,
modifierFlags = self:getModifierFlags(), modifierFlags = self:getModifierFlags(),
x = x, x = x, y = y
y = y
}) })
if result ~= nil then return result end if result ~= nil then return result end
end end
@@ -45,8 +44,7 @@ function Input:handleKeyRelease (layout, key, x, y)
local result = widget:bubbleEvent('KeyRelease', { local result = widget:bubbleEvent('KeyRelease', {
key = key, key = key,
modifierFlags = self:getModifierFlags(), modifierFlags = self:getModifierFlags(),
x = x, x = x, y = y
y = y
}) })
if result ~= nil then return result end if result ~= nil then return result end
end end
@@ -55,7 +53,8 @@ function Input:handleTextInput (layout, text, x, y)
local widget = layout.focusedWidget or layout.root local widget = layout.focusedWidget or layout.root
local result = widget:bubbleEvent('TextInput', { local result = widget:bubbleEvent('TextInput', {
hit = hit, hit = hit,
text = text, x = x, y = y text = text,
x = x, y = y
}) })
if result ~= nil then return result end if result ~= nil then return result end
end end
@@ -193,7 +192,8 @@ function Input:handlePressEnd (layout, button, x, y, widget, accelerator)
hit = hit, hit = hit,
origin = originWidget, origin = originWidget,
accelerator = accelerator, accelerator = accelerator,
button = button, x = x, y = y button = button,
x = x, y = y
}) })
if (widget == originWidget) then if (widget == originWidget) then
widget:bubbleEvent('Press', { widget:bubbleEvent('Press', {

View File

@@ -140,7 +140,7 @@ function Layout:show ()
Backend.hide(self) Backend.hide(self)
self.isShown = nil self.isShown = nil
end end
self.isShown = true self.isShown = true
if not self.input then if not self.input then

54
luigi/multiline.lua Normal file
View File

@@ -0,0 +1,54 @@
local Multiline = {}
function Multiline.wrap (font, text, limit)
local lines = {{ width = 0 }}
local advance = 0
local function append (word, space)
local wordAdvance = font:getAdvance(word)
local spaceAdvance = font:getAdvance(space)
local words = lines[#lines]
if advance + wordAdvance > limit then
advance = wordAdvance + spaceAdvance
lines[#lines + 1] = { width = advance, word, space }
else
advance = advance + wordAdvance + spaceAdvance
words.width = advance
words[#words + 1] = word
words[#words + 1] = space
end
end
local function appendFrag (frag, isFirst)
if isFirst then
append(frag, '')
else
local wordAdvance = font:getAdvance(frag)
lines[#lines + 1] = { width = wordAdvance, frag }
advance = wordAdvance
end
end
local leadSpace = text:match '^ +'
if leadSpace then
append('', leadSpace)
end
for word, space in text:gmatch '([^ ]+)( *)' do
if word:match '\n' then
local isFirst = true
for frag in (word .. '\n'):gmatch '([^\n]*)\n' do
appendFrag(frag, isFirst)
isFirst = false
end
append('', space)
else
append(word, space)
end
end
return lines
end
return Multiline

View File

@@ -3,7 +3,8 @@ local ROOT = (...):gsub('[^.]*$', '')
local Backend = require(ROOT .. 'backend') local Backend = require(ROOT .. 'backend')
local Base = require(ROOT .. 'base') local Base = require(ROOT .. 'base')
local Event = require(ROOT .. 'event') local Event = require(ROOT .. 'event')
local Font = require(ROOT .. 'font') local Font = Backend.Font
local Text = Backend.Text
local Renderer = Base:extend() local Renderer = Base:extend()
@@ -18,6 +19,8 @@ function Renderer:loadImage (path)
return imageCache[path] return imageCache[path]
end end
-- TODO: make slices a seperate drawable
function Renderer:loadSlices (path) function Renderer:loadSlices (path)
local slices = sliceCache[path] local slices = sliceCache[path]
@@ -141,39 +144,40 @@ end
-- returns text coordinates -- returns text coordinates
function Renderer:positionText (widget, x1, y1, x2, y2) function Renderer:positionText (widget, x1, y1, x2, y2)
if not widget.text then if not widget.text or x1 >= x2 then
return nil, nil, x1, y1, x2, y2 return nil, nil, x1, y1, x2, y2
end end
if not widget.fontData then if not widget.fontData then
widget.fontData = Font(widget.font, widget.fontSize, widget.textColor) widget.fontData = Font(widget.font, widget.fontSize)
end end
local font = widget.fontData local font = widget.fontData
local align = widget.align or '' local align = widget.align or ''
local padding = widget.padding or 0 local horizontal = 'left'
font:setWidth(x2 - x1)
-- horizontal alignment -- horizontal alignment
if align:find('right') then if align:find 'right' then
font:setAlignment('right') horizontal = 'right'
elseif align:find('center') then elseif align:find 'center' then
font:setAlignment('center') horizontal = 'center'
elseif align:find('justify') then elseif align:find 'justify' then
font:setAlignment('justify') horizontal = 'justify'
else -- if align:find('left') then
font:setAlignment('left')
end end
if not widget.textData then
local limit = widget.multiline and x2 - x1 or nil
widget.textData = Text(
font, widget.text, widget.textColor, horizontal, limit)
end
local textHeight = widget.textData:getHeight()
local y local y
-- vertical alignment -- vertical alignment
if align:find('bottom') then if align:find('bottom') then
local textHeight = font:getWrappedHeight(widget.text)
y = y2 - textHeight y = y2 - textHeight
elseif align:find('middle') then elseif align:find('middle') then
local textHeight = font:getWrappedHeight(widget.text)
y = y2 - (y2 - y1) / 2 - textHeight / 2 y = y2 - (y2 - y1) / 2 - textHeight / 2
else -- if align:find('top') then else -- if align:find('top') then
y = y1 y = y1
@@ -210,22 +214,32 @@ function Renderer:renderIconAndText (widget)
if icon and text and align:find('center') then if icon and text and align:find('center') then
local iconHeight = icon:getHeight() local iconHeight = icon:getHeight()
if align:find('middle') then if align:find 'middle' then
local textHeight = font:getWrappedHeight(text) local textHeight = widget.textData:getHeight()
local contentHeight = textHeight + padding + iconHeight local contentHeight = textHeight + padding + iconHeight
local offset = ((y2 - y1) - contentHeight) / 2 local offset = ((y2 - y1) - contentHeight) / 2
iconY = y1 + offset iconY = y1 + offset
textY = y1 + offset + padding + iconHeight textY = y1 + offset + padding + iconHeight
elseif align:find('top') then elseif align:find 'top' then
iconY = y1 iconY = y1
textY = y1 + padding + iconHeight textY = y1 + padding + iconHeight
else -- if align:find('bottom') else -- if align:find 'bottom'
local textHeight = font:getWrappedHeight(text) local textHeight = widget.textData:getHeight()
textY = y2 - textHeight textY = y2 - textHeight
iconY = textY - padding - iconHeight iconY = textY - padding - iconHeight
end end
end end
-- horizontal alignment for non-multiline
-- TODO: handle this in Backend.Text
if text and not widget.multiline then
if align:find 'right' then
textX = textX + ((x2 - x1) - widget.textData:getWidth())
elseif align:find 'center' then
textX = textX + ((x2 - x1) - widget.textData:getWidth()) / 2
end
end
-- draw the icon -- draw the icon
if icon then if icon then
iconX, iconY = math.floor(iconX), math.floor(iconY) iconX, iconY = math.floor(iconX), math.floor(iconY)
@@ -238,9 +252,7 @@ function Renderer:renderIconAndText (widget)
-- draw the text -- draw the text
if text and x2 > x1 then if text and x2 > x1 then
textX, textY = math.floor(textX), math.floor(textY) textX, textY = math.floor(textX), math.floor(textY)
Backend.setFont(font) Backend.draw(widget.textData, textX, textY)
Backend.setColor(font.color)
Backend.printf(text, textX, textY, x2 - x1, font.align)
end end
Backend.pop() Backend.pop()

View File

@@ -1,3 +1,5 @@
-- modified for partial compatibility with Lua 5.3
--utf8 module (Cosmin Apreutesei, public domain). --utf8 module (Cosmin Apreutesei, public domain).
--byte indices are i's, char (codepoint) indices are ci's. --byte indices are i's, char (codepoint) indices are ci's.
--invalid characters are counted as 1-byte chars so they don't get lost. validate/sanitize beforehand as needed. --invalid characters are counted as 1-byte chars so they don't get lost. validate/sanitize beforehand as needed.
@@ -56,11 +58,12 @@ function utf8.byte_index(s, target_ci)
end end
end end
assert(target_ci > ci, 'invalid index') assert(target_ci > ci, 'invalid index')
return #s + 1
end end
--char index given byte index. nil if the index is outside the string. --char index given byte index. nil if the index is outside the string.
function utf8.char_index(s, target_i) function utf8.char_index(s, target_i)
if target_i < 1 or target_i > #s then return end if target_i < 1 or target_i > #s + 1 then return end
local ci = 0 local ci = 0
for i in utf8.byte_indices(s) do for i in utf8.byte_indices(s) do
ci = ci + 1 ci = ci + 1
@@ -68,7 +71,8 @@ function utf8.char_index(s, target_i)
return ci return ci
end end
end end
error'invalid index' return ci + 1
-- error'invalid index'
end end
--byte index of the prev. char before the char at byte index i, which defaults to #s + 1. --byte index of the prev. char before the char at byte index i, which defaults to #s + 1.
@@ -306,11 +310,28 @@ function utf8.sanitize(s, repl_char)
return utf8.replace(s, replace_invalid, repl_char) return utf8.replace(s, replace_invalid, repl_char)
end end
-- added for partial compatibility with lua 5.3 -- Returns the position (in bytes) where the encoding of the n-th character
-- of s (counting from position i) starts.
function utf8.offset(s, n, i)
function utf8.offset(s, char_offset, byte_index) -- The default for i is 1 when n is non-negative and #s + 1 otherwise
local ci = utf8.char_index(s, byte_index) if not i then
return utf8.byte_index(s, ci + char_offset) i = n < 0 and #s + 1 or 1
end
local ci = utf8.char_index(s, i)
-- As a special case, when n is 0 the function returns the start of
-- the encoding of the character that contains the i-th byte of s.
if n == 0 then
return ci
end
if n > 0 then
n = n - 1
end
return utf8.byte_index(s, ci + n)
end end
utf8.codes = utf8.byte_indices utf8.codes = utf8.byte_indices

View File

@@ -8,7 +8,7 @@ local ROOT = (...):gsub('[^.]*$', '')
local Backend = require(ROOT .. 'backend') local Backend = require(ROOT .. 'backend')
local Event = require(ROOT .. 'event') local Event = require(ROOT .. 'event')
local Font = require(ROOT .. 'font') local Font = Backend.Font
local Widget = {} local Widget = {}
@@ -50,17 +50,25 @@ end
-- setting shadow properties causes special behavior -- setting shadow properties causes special behavior
local function metaNewIndex (self, property, value) local function metaNewIndex (self, property, value)
if property == 'font' if property == 'font' or property == 'fontSize' then
or property == 'fontSize'
or property == 'textColor' then
self.shadowProperties[property] = value self.shadowProperties[property] = value
self.fontData = Font(self.font, self.fontSize, self.textColor) self.fontData = nil
self.textData = nil
end
if property == 'text' or property == 'textColor'
or property == 'align' or property == 'multiline' then
self.shadowProperties[property] = value
self.textData = nil
return return
end end
if property == 'width' then if property == 'width' then
value = value and math.max(value, self.minwidth or 0) value = value and math.max(value, self.minwidth or 0)
self.shadowProperties[property] = value self.shadowProperties[property] = value
if self.multiline then
self.textData = nil
end
Widget.reshape(self.parent or self) Widget.reshape(self.parent or self)
return return
end end
@@ -114,8 +122,13 @@ local function metaNewIndex (self, property, value)
end end
local shadowKeys = { local shadowKeys = {
'font', 'fontSize', 'textColor', 'width', 'height', 'value', 'key', 'id' 'id', 'key', 'value',
'width', 'height',
'font', 'fontSize',
'text', 'textColor',
'align', 'multiline',
} }
--[[-- --[[--
Widget pseudo-constructor. Widget pseudo-constructor.
@@ -603,6 +616,7 @@ function Widget:reshape ()
if self.isReshaping then return end if self.isReshaping then return end
self.isReshaping = true self.isReshaping = true
self.needsReshape = true self.needsReshape = true
self.textData = nil
Event.Reshape:emit(self, { Event.Reshape:emit(self, {
target = self target = self
}) })

View File

@@ -125,7 +125,7 @@ local function initialize (self)
local textWidth = self.fontData:getAdvance(text) + pad * 2 local textWidth = self.fontData:getAdvance(text) + pad * 2
if isSubmenu then if isSubmenu then
local tc = self.textColor or { 0, 0, 0 } local tc = self.textColor or { 0, 0, 0, 255 }
local keyColor = { tc[1], tc[2], tc[3], 0x90 } local keyColor = { tc[1], tc[2], tc[3], 0x90 }
local edgeType local edgeType
if #self.items > 0 then if #self.items > 0 then

View File

@@ -230,7 +230,7 @@ return function (self)
local x1, y1, x2, y2 = self:getRectangle(true, true) local x1, y1, x2, y2 = self:getRectangle(true, true)
local width, height = endX - startX, y2 - y1 local width, height = endX - startX, y2 - y1
local font = self.fontData local font = self.fontData
local textColor = font.color local textColor = self.textColor or { 0, 0, 0, 255 }
local textTop = math.floor(y1 + ((y2 - y1) - font:getLineHeight()) / 2) local textTop = math.floor(y1 + ((y2 - y1) - font:getLineHeight()) / 2)
Backend.push() Backend.push()