Merge with 'master'

This commit is contained in:
Túlio Henrique 2015-07-07 10:43:18 -03:00
commit 2d65a0a3ed
22 changed files with 427 additions and 79 deletions

View File

@ -1,4 +1,4 @@
[![Build Status](https://secure.travis-ci.org/edubart/otclient.svg?branch=mobile_port)](http://travis-ci.org/edubart/otclient)
[![Build Status](https://secure.travis-ci.org/edubart/otclient.svg?branch=mobile_port)](http://travis-ci.org/edubart/otclient) [![Join the chat at https://gitter.im/edubart/otclient](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/edubart/otclient?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
### What is otclient?
Otclient is an alternative Tibia client for usage with otserv. It aims to be complete and flexible,

View File

@ -78,7 +78,7 @@ local function onCharacterList(protocol, characters, account, otui)
if motdEnabled then
local lastMotdNumber = g_settings.getNumber("motd")
if G.motdNumber and G.motdNumber ~= lastMotdNumber then
g_settings.set("motd", motdNumber)
g_settings.set("motd", G.motdNumber)
motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage)
connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end })
CharacterList.hide()

View File

@ -10,6 +10,7 @@ Module
dofile 'string'
dofile 'table'
dofile 'bitwise'
dofile 'struct'
dofile 'const'
dofile 'util'

173
modules/corelib/struct.lua Normal file
View File

@ -0,0 +1,173 @@
Struct = {}
function Struct.pack(format, ...)
local stream = {}
local vars = {...}
local endianness = true
for i = 1, format:len() do
local opt = format:sub(i, i)
if opt == '>' then
endianness = false
elseif opt:find('[bBhHiIlL]') then
local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1
local val = tonumber(table.remove(vars, 1))
if val < 0 then
val = val + 2 ^ (n * 8 - 1)
end
local bytes = {}
for j = 1, n do
table.insert(bytes, string.char(val % (2 ^ 8)))
val = math.floor(val / (2 ^ 8))
end
if not endianness then
table.insert(stream, string.reverse(table.concat(bytes)))
else
table.insert(stream, table.concat(bytes))
end
elseif opt:find('[fd]') then
local val = tonumber(table.remove(vars, 1))
local sign = 0
if val < 0 then
sign = 1
val = -val
end
local mantissa, exponent = math.frexp(val)
if val == 0 then
mantissa = 0
exponent = 0
else
mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24)
exponent = exponent + ((opt == 'd') and 1022 or 126)
end
local bytes = {}
if opt == 'd' then
val = mantissa
for i = 1, 6 do
table.insert(bytes, string.char(math.floor(val) % (2 ^ 8)))
val = math.floor(val / (2 ^ 8))
end
else
table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8)))
val = math.floor(mantissa / (2 ^ 8))
table.insert(bytes, string.char(math.floor(val) % (2 ^ 8)))
val = math.floor(val / (2 ^ 8))
end
table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8)))
val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8))
table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8)))
val = math.floor((sign * 128 + val) / (2 ^ 8))
if not endianness then
table.insert(stream, string.reverse(table.concat(bytes)))
else
table.insert(stream, table.concat(bytes))
end
elseif opt == 's' then
table.insert(stream, tostring(table.remove(vars, 1)))
table.insert(stream, string.char(0))
elseif opt == 'c' then
local n = format:sub(i + 1):match('%d+')
local length = tonumber(n)
if length > 0 then
local str = tostring(table.remove(vars, 1))
if length - str:len() > 0 then
str = str .. string.rep(' ', length - str:len())
end
table.insert(stream, str:sub(1, length))
end
i = i + n:len()
end
end
return table.concat(stream)
end
function Struct.unpack(format, stream)
local vars = {}
local iterator = 1
local endianness = true
for i = 1, format:len() do
local opt = format:sub(i, i)
if opt == '>' then
endianness = false
elseif opt:find('[bBhHiIlL]') then
local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1
local signed = opt:lower() == opt
local val = 0
for j = 1, n do
local byte = string.byte(stream:sub(iterator, iterator))
if endianness then
val = val + byte * (2 ^ ((j - 1) * 8))
else
val = val + byte * (2 ^ ((n - j) * 8))
end
iterator = iterator + 1
end
if signed then
val = val - 2 ^ (n * 8 - 1)
end
table.insert(vars, val)
elseif opt:find('[fd]') then
local n = (opt == 'd') and 8 or 4
local x = stream:sub(iterator, iterator + n - 1)
iterator = iterator + n
if not endianness then
x = string.reverse(x)
end
local sign = 1
local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128)
for i = n - 2, 1, -1 do
mantissa = mantissa * (2 ^ 8) + string.byte(x, i)
end
if string.byte(x, n) > 127 then
sign = -1
end
local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + math.floor(string.byte(x, n - 1) / ((opt == 'd') and 16 or 128))
if exponent == 0 then
table.insert(vars, 0.0)
else
mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign
table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127)))
end
elseif opt == 's' then
local bytes = {}
for j = iterator, stream:len() do
if stream:sub(j, j) == string.char(0) then
break
end
table.insert(bytes, stream:sub(j, j))
end
local str = table.concat(bytes)
iterator = iterator + str:len() + 1
table.insert(vars, str)
elseif opt == 'c' then
local n = format:sub(i + 1):match('%d+')
table.insert(vars, stream:sub(iterator, iterator + tonumber(n)))
iterator = iterator + tonumber(n)
i = i + n:len()
end
end
return unpack(vars)
end

View File

@ -296,6 +296,16 @@ function numbertoboolean(number)
end
end
function protectedcall(func, ...)
local status, ret = pcall(func, ...)
if status then
return ret
end
perror(ret)
return false
end
function signalcall(param, ...)
if type(param) == 'function' then
local status, ret = pcall(param, ...)

View File

@ -113,13 +113,10 @@ function init()
local tab = consoleTabBar:getCurrentTab()
if not tab then return false end
local consoleBuffer = tab.tabPanel:getChildById('consoleBuffer')
if not consoleBuffer then return false end
local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText
if not selection then return false end
local consoleLabel = consoleBuffer:getFocusedChild()
if not consoleLabel or not consoleLabel:hasSelection() then return false end
g_window.setClipboardText(consoleLabel:getSelection())
g_window.setClipboardText(selection)
return true
end
@ -147,6 +144,27 @@ function init()
end
end
function clearSelection(consoleBuffer)
for _,label in pairs(consoleBuffer:getChildren()) do
label:clearSelection()
end
consoleBuffer.selectionText = nil
consoleBuffer.selection = nil
end
function selectAll(consoleBuffer)
clearSelection(consoleBuffer)
if consoleBuffer:getChildCount() > 0 then
local text = {}
for _,label in pairs(consoleBuffer:getChildren()) do
label:selectAll()
table.insert(text, label:getSelection())
end
consoleBuffer.selectionText = table.concat(text, '\n')
consoleBuffer.selection = { first = consoleBuffer:getChildIndex(consoleBuffer:getFirstChild()), last = consoleBuffer:getChildIndex(consoleBuffer:getLastChild()) }
end
end
function toggleChat()
if consoleToggleChat:isChecked() then
disableChat()
@ -577,12 +595,102 @@ function addTabText(text, speaktype, tab, creatureName)
end
label.name = creatureName
label.onMouseRelease = function (self, mousePos, mouseButton)
consoleBuffer.onMouseRelease = function(self, mousePos, mouseButton)
processMessageMenu(mousePos, mouseButton, nil, nil, nil, tab)
end
label.onMouseRelease = function(self, mousePos, mouseButton)
processMessageMenu(mousePos, mouseButton, creatureName, text, self, tab)
end
label.onMousePress = function(self, mousePos, button)
if button == MouseLeftButton then clearSelection(consoleBuffer) end
end
label.onDragEnter = function(self, mousePos)
clearSelection(consoleBuffer)
return true
end
label.onDragLeave = function(self, droppedWidget, mousePos)
local text = {}
for selectionChild = consoleBuffer.selection.first, consoleBuffer.selection.last do
local label = self:getParent():getChildByIndex(selectionChild)
table.insert(text, label:getSelection())
end
consoleBuffer.selectionText = table.concat(text, '\n')
return true
end
label.onDragMove = function(self, mousePos, mouseMoved)
local parent = self:getParent()
local parentRect = parent:getPaddingRect()
local selfIndex = parent:getChildIndex(self)
local child = parent:getChildByPos(mousePos)
-- find bonding children
if not child then
if mousePos.y < self:getY() then
for index = selfIndex - 1, 1, -1 do
local label = parent:getChildByIndex(index)
if label:getY() + label:getHeight() > parentRect.y then
if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == 1 then
child = label
break
end
else
child = parent:getChildByIndex(index + 1)
break
end
end
elseif mousePos.y > self:getY() + self:getHeight() then
for index = selfIndex + 1, parent:getChildCount(), 1 do
local label = parent:getChildByIndex(index)
if label:getY() < parentRect.y + parentRect.height then
if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == parent:getChildCount() then
child = label
break
end
else
child = parent:getChildByIndex(index - 1)
break
end
end
else
child = self
end
end
if not child then return false end
local childIndex = parent:getChildIndex(child)
-- remove old selection
clearSelection(consoleBuffer)
-- update self selection
local textBegin = self:getTextPos(self:getLastClickPosition())
local textPos = self:getTextPos(mousePos)
self:setSelection(textBegin, textPos)
consoleBuffer.selection = { first = math.min(selfIndex, childIndex), last = math.max(selfIndex, childIndex) }
-- update siblings selection
if child ~= self then
for selectionChild = consoleBuffer.selection.first + 1, consoleBuffer.selection.last - 1 do
parent:getChildByIndex(selectionChild):selectAll()
end
local textPos = child:getTextPos(mousePos)
if childIndex > selfIndex then
child:setSelection(0, textPos)
else
child:setSelection(string.len(child:getText()), textPos)
end
end
return true
end
if consoleBuffer:getChildCount() > MAX_LINES then
consoleBuffer:getFirstChild():destroy()
local child = consoleBuffer:getFirstChild()
clearSelection(consoleBuffer)
child:destroy()
end
end
@ -609,8 +717,28 @@ function processChannelTabMenu(tab, mousePos, mouseButton)
if consoleTabBar:getCurrentTab() == tab then
menu:addOption(tr('Clear Messages'), function() clearChannel(consoleTabBar) end)
menu:addOption(tr('Save Messages'), function()
local panel = consoleTabBar:getTabPanel(tab)
local consoleBuffer = panel:getChildById('consoleBuffer')
local lines = {}
for _,label in pairs(consoleBuffer:getChildren()) do
table.insert(lines, label:getText())
end
local filename = channelName .. '.txt'
local filepath = '/' .. filename
-- extra information at the beginning
table.insert(lines, 1, os.date('\nChannel saved at %a %b %d %H:%M:%S %Y'))
if g_resources.fileExists(filepath) then
table.insert(lines, 1, protectedcall(g_resources.readFileContents, filepath) or '')
end
g_resources.writeFileContents(filepath, table.concat(lines, '\n'))
modules.game_textmessage.displayStatusMessage(tr('Channel appended to %s', filename))
end)
end
--menu:addOption(tr('Save Messages'), function() --[[TODO]] end)
menu:display(mousePos)
end
@ -644,12 +772,15 @@ function processMessageMenu(mousePos, mouseButton, creatureName, text, label, ta
menu:addOption(tr('Copy name'), function () g_window.setClipboardText(creatureName) end)
end
if label:hasSelection() then
menu:addOption(tr('Copy'), function() g_window.setClipboardText(label:getSelection()) end, '(Ctrl+C)')
local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText
if selection and #selection > 0 then
menu:addOption(tr('Copy'), function() g_window.setClipboardText(selection) end, '(Ctrl+C)')
end
menu:addOption(tr('Copy message'), function() g_window.setClipboardText(text) end)
menu:addOption(tr('Select all'), function() label:selectAll() end)
if tab.violations then
if text then
menu:addOption(tr('Copy message'), function() g_window.setClipboardText(text) end)
end
menu:addOption(tr('Select all'), function() selectAll(tab.tabPanel:getChildById('consoleBuffer')) end)
if tab.violations and creatureName then
menu:addSeparator()
menu:addOption(tr('Process') .. ' ' .. creatureName, function() processViolation(creatureName, text) end)
menu:addOption(tr('Remove') .. ' ' .. creatureName, function() g_game.closeRuleViolation(creatureName) end)

View File

@ -10,6 +10,9 @@ ConsoleLabel < UITextEdit
change-cursor-image: false
cursor-visible: false
editable: false
draggable: true
selectable: false
focusable: false
ConsolePhantomLabel < UILabel
font: verdana-11px-antialised

View File

@ -200,29 +200,24 @@ void Creature::drawOutfit(const Rect& destRect, bool resize)
else
exactSize = g_things.rawGetThingType(m_outfit.getAuxId(), m_outfit.getCategory())->getExactSize();
int frameSize;
if(!resize)
frameSize = std::max<int>(exactSize * 0.75f, 2 * Otc::TILE_PIXELS * 0.75f);
else if(!(frameSize = exactSize))
return;
if(g_graphics.canUseFBO()) {
const FrameBufferPtr& outfitBuffer = g_framebuffers.getTemporaryFrameBuffer();
outfitBuffer->resize(Size(2*Otc::TILE_PIXELS, 2*Otc::TILE_PIXELS));
outfitBuffer->resize(Size(frameSize, frameSize));
outfitBuffer->bind();
g_painter->setAlphaWriting(true);
g_painter->clear(Color::alpha);
internalDrawOutfit(Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) + getDisplacement(), 1, false, true, Otc::South);
internalDrawOutfit(Point(frameSize - Otc::TILE_PIXELS, frameSize - Otc::TILE_PIXELS) + getDisplacement(), 1, false, true, Otc::South);
outfitBuffer->release();
Rect srcRect;
if(resize)
srcRect.resize(exactSize, exactSize);
else
srcRect.resize(2*Otc::TILE_PIXELS*0.75f, 2*Otc::TILE_PIXELS*0.75f);
srcRect.moveBottomRight(Point(2*Otc::TILE_PIXELS - 1, 2*Otc::TILE_PIXELS - 1));
outfitBuffer->draw(destRect, srcRect);
outfitBuffer->draw(destRect, Rect(0,0,frameSize,frameSize));
} else {
float scaleFactor;
if(resize)
scaleFactor = destRect.width() / (float)exactSize;
else
scaleFactor = destRect.width() / (float)(2*Otc::TILE_PIXELS*0.75f);
Point dest = destRect.bottomRight() - (Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) - getDisplacement())*scaleFactor;
float scaleFactor = destRect.width() / (float)frameSize;
Point dest = destRect.bottomRight() - (Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) - getDisplacement()) * scaleFactor;
internalDrawOutfit(dest, scaleFactor, false, true, Otc::South);
}
}

View File

@ -394,6 +394,14 @@ void Map::setZoneColor(tileflags_t zone, const Color& color)
m_zoneColors[zone] = color;
}
Color Map::getZoneColor(tileflags_t flag)
{
auto it = m_zoneColors.find(flag);
if(it == m_zoneColors.end())
return Color::alpha;
return it->second;
}
void Map::setForceShowAnimations(bool force)
{
if(force) {

View File

@ -196,7 +196,7 @@ public:
void setZoneOpacity(float opacity) { m_zoneOpacity = opacity; }
float getZoneOpacity() { return m_zoneOpacity; }
Color getZoneColor(tileflags_t flag) { return m_zoneColors[flag]; }
Color getZoneColor(tileflags_t flag);
tileflags_t getZoneFlags() { return (tileflags_t)m_zoneFlags; }
bool showZones() { return m_zoneFlags != 0; }
bool showZone(tileflags_t zone) { return (m_zoneFlags & zone) == zone; }
@ -257,7 +257,7 @@ private:
uint8 m_animationFlags;
uint32 m_zoneFlags;
std::array<Color, TILESTATE_LAST> m_zoneColors;
std::map<uint32, Color> m_zoneColors;
float m_zoneOpacity;
Light m_light;
@ -272,5 +272,3 @@ private:
extern Map g_map;
#endif
/* vim: set ts=4 sw=4 et: */

View File

@ -333,6 +333,7 @@ bool Minimap::loadOtmm(const std::string& fileName)
uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile);
std::vector<uchar> compressBuffer(compressBound(blockSize));
std::vector<uchar> decompressBuffer(blockSize);
while(true) {
Position pos;
@ -340,17 +341,19 @@ bool Minimap::loadOtmm(const std::string& fileName)
pos.y = fin->getU16();
pos.z = fin->getU8();
// end of file
if(!pos.isValid())
// end of file or file is corrupted
if(!pos.isValid() || pos.z >= Otc::MAX_Z+1)
break;
MinimapBlock& block = getBlock(pos);
ulong len = fin->getU16();
ulong destLen = blockSize;
fin->read(compressBuffer.data(), len);
int ret = uncompress((uchar*)&block.getTiles(), &destLen, compressBuffer.data(), len);
assert(ret == Z_OK);
assert(destLen == blockSize);
int ret = uncompress(decompressBuffer.data(), &destLen, compressBuffer.data(), len);
if(ret != Z_OK || destLen != blockSize)
break;
memcpy((uchar*)&block.getTiles(), decompressBuffer.data(), blockSize);
block.mustUpdate();
block.justSaw();
}

View File

@ -583,10 +583,8 @@ void ProtocolGame::sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseMod
msg->addU8(fightMode);
msg->addU8(chaseMode);
msg->addU8(safeFight ? 0x01: 0x00);
if(g_game.getFeature(Otc::GamePVPMode))
msg->addU8(pvpMode);
send(msg);
}
@ -595,7 +593,8 @@ void ProtocolGame::sendAttack(uint creatureId, uint seq)
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientAttack);
msg->addU32(creatureId);
msg->addU32(seq);
if(g_game.getFeature(Otc::GameAttackSeq))
msg->addU32(seq);
send(msg);
}
@ -604,7 +603,8 @@ void ProtocolGame::sendFollow(uint creatureId, uint seq)
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientFollow);
msg->addU32(creatureId);
msg->addU32(seq);
if(g_game.getFeature(Otc::GameAttackSeq))
msg->addU32(seq);
send(msg);
}
@ -652,10 +652,8 @@ void ProtocolGame::sendShareExperience(bool active)
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientShareExperience);
msg->addU8(active ? 0x01 : 0x00);
if(g_game.getClientVersion() < 910)
msg->addU8(0);
send(msg);
}

View File

@ -244,12 +244,12 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
m_attribs.set(attr, market);
break;
}
case ThingAttrUsable:
case ThingAttrElevation: {
m_elevation = fin->getU16();
m_attribs.set(attr, m_elevation);
break;
}
case ThingAttrUsable:
case ThingAttrGround:
case ThingAttrWritable:
case ThingAttrWritableOnce:

View File

@ -174,7 +174,7 @@ set(CMAKE_CXX_FLAGS_COMPILESPEED "-O0")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O1 -g -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
set(CMAKE_CXX_FLAGS_PERFORMANCE "-Ofast -mmmx -msse -msse2")
set(CMAKE_CXX_FLAGS_PERFORMANCE "-Ofast -march=native")
if(USE_LTO)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwhole-program -flto")

View File

@ -135,6 +135,12 @@ void PainterOGL1::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode)
#endif
}
void PainterOGL1::drawFillCoords(CoordsBuffer& coordsBuffer)
{
setTexture(nullptr);
drawCoords(coordsBuffer);
}
void PainterOGL1::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture)
{
if(texture->isEmpty())

View File

@ -50,6 +50,7 @@ public:
void refreshState();
void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles);
void drawFillCoords(CoordsBuffer& coordsBuffer);
void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture);
void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);

View File

@ -120,6 +120,13 @@ void PainterOGL2::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode)
PainterShaderProgram::enableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR);
}
void PainterOGL2::drawFillCoords(CoordsBuffer& coordsBuffer)
{
setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawSolidColorProgram.get());
setTexture(nullptr);
drawCoords(coordsBuffer);
}
void PainterOGL2::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture)
{
if(texture && texture->isEmpty())

View File

@ -41,6 +41,7 @@ public:
void unbind();
void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles);
void drawFillCoords(CoordsBuffer& coordsBuffer);
void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture);
void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);
void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src);

View File

@ -62,6 +62,7 @@ public:
virtual void clear(const Color& color) = 0;
virtual void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles) = 0;
virtual void drawFillCoords(CoordsBuffer& coordsBuffer) = 0;
virtual void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture) = 0;
virtual void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) = 0;
void drawTexturedRect(const Rect& dest, const TexturePtr& texture) { drawTexturedRect(dest, texture, Rect(Point(0,0), texture->getSize())); }

View File

@ -49,6 +49,9 @@ UITextEdit::UITextEdit()
m_updatesEnabled = true;
m_selectionColor = Color::white;
m_selectionBackgroundColor = Color::black;
m_glyphsTextCoordsBuffer.enableHardwareCaching();
m_glyphsSelectCoordsBuffer.enableHardwareCaching();
m_glyphsMustRecache = true;
blinkCursor();
}
@ -62,38 +65,36 @@ void UITextEdit::drawSelf(Fw::DrawPane drawPane)
drawImage(m_rect);
drawIcon(m_rect);
//TODO: text rendering could be much optimized by using vertex buffer or caching the render into a texture
int textLength = m_text.length();
const TexturePtr& texture = m_font->getTexture();
if(!texture)
return;
if(hasSelection()) {
if(m_color != Color::alpha) {
g_painter->setColor(m_color);
for(int i=0;i<m_selectionStart;++i)
g_painter->drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]);
}
bool glyphsMustRecache = m_glyphsMustRecache;
if(glyphsMustRecache)
m_glyphsMustRecache = false;
for(int i=m_selectionStart;i<m_selectionEnd;++i) {
g_painter->setColor(m_selectionBackgroundColor);
g_painter->drawFilledRect(m_glyphsCoords[i]);
g_painter->setColor(m_selectionColor);
g_painter->drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]);
if(m_color != Color::alpha) {
if(glyphsMustRecache) {
m_glyphsTextCoordsBuffer.clear();
for(int i=0;i<textLength;++i)
m_glyphsTextCoordsBuffer.addRect(m_glyphsCoords[i], m_glyphsTexCoords[i]);
}
if(m_color != Color::alpha) {
g_painter->setColor(m_color);
for(int i=m_selectionEnd;i<textLength;++i)
g_painter->drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]);
}
} else if(m_color != Color::alpha) {
g_painter->setColor(m_color);
for(int i=0;i<textLength;++i)
g_painter->drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]);
g_painter->drawTextureCoords(m_glyphsTextCoordsBuffer, texture);
}
if(hasSelection()) {
if(glyphsMustRecache) {
m_glyphsSelectCoordsBuffer.clear();
for(int i=m_selectionStart;i<m_selectionEnd;++i)
m_glyphsSelectCoordsBuffer.addRect(m_glyphsCoords[i], m_glyphsTexCoords[i]);
}
g_painter->setColor(m_selectionBackgroundColor);
g_painter->drawFillCoords(m_glyphsSelectCoordsBuffer);
g_painter->setColor(m_selectionColor);
g_painter->drawTextureCoords(m_glyphsSelectCoordsBuffer, texture);
}
// render cursor
if(isExplicitlyEnabled() && m_cursorVisible && m_cursorInRange && isActive() && m_cursorPos >= 0) {
@ -136,6 +137,9 @@ void UITextEdit::update(bool focusCursor)
if(m_rect.isEmpty())
return;
// recache coords buffers
recacheGlyphs();
// map glyphs positions
Size textBoxSize;
const std::vector<Point>& glyphsPositions = m_font->calculateGlyphsPositions(text, m_textAlign, &textBoxSize);
@ -360,6 +364,7 @@ void UITextEdit::setSelection(int start, int end)
m_selectionStart = stdext::clamp<int>(start, 0, (int)m_text.length());
m_selectionEnd = stdext::clamp<int>(end, 0, (int)m_text.length());
recacheGlyphs();
}
void UITextEdit::setTextHidden(bool hidden)
@ -654,7 +659,7 @@ void UITextEdit::onFocusChange(bool focused, Fw::FocusReason reason)
else
blinkCursor();
update(true);
} else
} else if(m_selectable)
clearSelection();
UIWidget::onFocusChange(focused, reason);
}
@ -796,7 +801,6 @@ bool UITextEdit::onMousePress(const Point& mousePos, Fw::MouseButton button)
}
return true;
}
return false;
}
@ -807,6 +811,9 @@ bool UITextEdit::onMouseRelease(const Point& mousePos, Fw::MouseButton button)
bool UITextEdit::onMouseMove(const Point& mousePos, const Point& mouseMoved)
{
if(UIWidget::onMouseMove(mousePos, mouseMoved))
return true;
if(m_selectable && isPressed()) {
int pos = getTextPos(mousePos);
if(pos >= 0) {
@ -815,7 +822,7 @@ bool UITextEdit::onMouseMove(const Point& mousePos, const Point& mouseMoved)
}
return true;
}
return UIWidget::onMouseMove(mousePos, mouseMoved);
return false;
}
bool UITextEdit::onDoubleClick(const Point& mousePos)

View File

@ -108,6 +108,7 @@ protected:
private:
void disableUpdates() { m_updatesEnabled = false; }
void enableUpdates() { m_updatesEnabled = true; }
void recacheGlyphs() { m_glyphsMustRecache = true; }
Rect m_drawArea;
int m_cursorPos;
@ -137,6 +138,10 @@ private:
std::vector<Rect> m_glyphsCoords;
std::vector<Rect> m_glyphsTexCoords;
CoordsBuffer m_glyphsTextCoordsBuffer;
CoordsBuffer m_glyphsSelectCoordsBuffer;
bool m_glyphsMustRecache;
};
#endif

View File

@ -66,19 +66,19 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;$(IncludePath)</IncludePath>
<IncludePath>D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;..\src;$(IncludePath)</IncludePath>
<LibraryPath>D:\otclient-msvc13-libs\libogg-1.3.1\lib;D:\otclient-msvc13-libs\libvorbis-1.3.3\lib;D:\otclient-msvc13-libs\physfs-2.0.3\lib;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;D:\otclient-msvc13-libs\zlib-1.2.5\lib;D:\otclient-msvc13-libs\OpenAL\lib;D:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;D:\otclient-msvc13-libs\glew-1.10.0\lib;D:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;$(IncludePath)</IncludePath>
<IncludePath>D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;..\src;$(IncludePath)</IncludePath>
<LibraryPath>D:\otclient-msvc13-libs\libogg-1.3.1\lib;D:\otclient-msvc13-libs\libvorbis-1.3.3\lib;D:\otclient-msvc13-libs\physfs-2.0.3\lib;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;D:\otclient-msvc13-libs\zlib-1.2.5\lib;D:\otclient-msvc13-libs\OpenAL\lib;D:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;D:\otclient-msvc13-libs\glew-1.10.0\lib;D:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>C:\otclient-msvc13-libs\libogg-1.3.1\include;C:\otclient-msvc13-libs\libvorbis-1.3.3\include;C:\otclient-msvc13-libs\physfs-2.0.3\include;C:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;C:\otclient-msvc13-libs\zlib-1.2.5\include;C:\Users\Pedro\Documents\GitHub\otclient\src;C:\otclient-msvc13-libs\OpenAL\include\AL;C:\otclient-msvc13-libs\glew-1.10.0\include;C:\otclient-msvc13-libs\LuaJIT-2.0.2\include;C:\otclient-msvc13-libs\boost_1_55_0\include;$(IncludePath)</IncludePath>
<IncludePath>C:\otclient-msvc13-libs\libogg-1.3.1\include;C:\otclient-msvc13-libs\libvorbis-1.3.3\include;C:\otclient-msvc13-libs\physfs-2.0.3\include;C:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;C:\otclient-msvc13-libs\zlib-1.2.5\include;C:\otclient-msvc13-libs\OpenAL\include\AL;C:\otclient-msvc13-libs\glew-1.10.0\include;C:\otclient-msvc13-libs\LuaJIT-2.0.2\include;C:\otclient-msvc13-libs\boost_1_55_0\include;..\src;$(IncludePath)</IncludePath>
<LibraryPath>C:\otclient-msvc13-libs\libogg-1.3.1\lib;C:\otclient-msvc13-libs\libvorbis-1.3.3\lib;C:\otclient-msvc13-libs\physfs-2.0.3\lib;C:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;C:\otclient-msvc13-libs\zlib-1.2.5\lib;C:\otclient-msvc13-libs\OpenAL\lib;C:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;C:\otclient-msvc13-libs\glew-1.10.0\lib;C:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;$(IncludePath)</IncludePath>
<IncludePath>D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;..\src;$(IncludePath)</IncludePath>
<LibraryPath>D:\otclient-msvc13-libs\libogg-1.3.1\lib;D:\otclient-msvc13-libs\libvorbis-1.3.3\lib;D:\otclient-msvc13-libs\physfs-2.0.3\lib;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;D:\otclient-msvc13-libs\zlib-1.2.5\lib;D:\otclient-msvc13-libs\OpenAL\lib;D:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;D:\otclient-msvc13-libs\glew-1.10.0\lib;D:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -491,4 +491,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>