给BGE加上了富文本显示功能
相关代码如下:
void GLTextRenderer::render(const Color& color,const String& string,const FloatRect& area,bool isMultiLine,
Horizontal hAlignment,Vertical vAlignment,bool rich)
{
if(typeFace_ != 0 && !rich)
{
typeFace_->setColor(color);
if(isMultiLine)
{
renderMultiline(color,string,area,hAlignment);
}
else
{
Vector2f position(typeFace_->penX(string,area,hAlignment),baseLineY(area,vAlignment));
typeFace_->render(string,position);
}
}
else if(typeFace_)
{
if(!isMultiLine)
{
auto cmdlist = parseString(string);
Vector2f position(typeFace_->penX(string,area,hAlignment),baseLineY(area,vAlignment));
typeFace_->render(String(),position,cmdlist);
}
else
{
auto cmdlist = parseString(string);
renderMultiline(color,cmdlist,area,hAlignment);
}
}
}
这里的parseString用于解析类似html的问题
函数实现如下:
std::list<textRenderCmdUnit> TextRenderer::parseString(const String& content)
{
String text(content);
std::list<textRenderCmdUnit> cmdList;
String current;
auto list = text.split('<');
for(int i=0;i<list.size();i++)
{
current = list[i];
if(current.startWith(L"/>"))
{
textRenderCmdUnit unit;
unit.undo = true;
cmdList.push_back(unit);
String remain = current.substr(2);
if(!remain.empty())
{
unit.text = remain;
unit.undo = false;
cmdList.push_back(unit);
}
}
else if(current.find('>') != String::InvalidPos)
{
textRenderCmdUnit unit;
unit.tag = current.substr(0,current.find('>'));
unit.undo = false;
cmdList.push_back(unit);
int32_t find = current.find('>');
if(find < current.size()-1)
{
unit.tag.clear();
unit.text = current.substr(find+1);
cmdList.push_back(unit);
}
}
else
{
textRenderCmdUnit unit;
unit.text = current;
unit.undo = false;
cmdList.push_back(unit);
}
}
return cmdList;
}
以下用于渲染富文本
void TypeFace::render(const FloatRect& area,const std::list<textRenderCmdUnit>& queue,Horizontal hAlignment,float vpos)
{
const float availableWidth = area.width_ - 2.0f;
textRenderCmdUnit unit;
String line,lineRemain;
String tag;
std::queue<String> cmdStack;
float y = vpos;
float x = penX(String(),area,hAlignment);
auto itr = queue.begin();
while(itr != queue.end())
{
unit = *itr;
if(unit.undo)
{
if(!cmdStack.empty())
{
tag = cmdStack.front();
cmdStack.pop();
if(tag == "bold")
bold_ = false;
else if(tag == "italic")
italic_ = false;
else if(tag == "underline")
underline_ = false;
else
applyColor(tag);
}
}
else if(!unit.tag.empty())
{
tag = unit.tag;
cmdStack.push(tag);
if(tag == "bold")
bold_ = true;
else if(tag == "italic")
italic_ = true;
else if(tag == "underline")
underline_ = true;
else
applyColor(tag);
}
else
{
line += unit.text;
while(true)
{
size_t pos = hitCharacterIndex(line,availableWidth-x);
if(pos == 0)
{
x = penX(String(),area,hAlignment);
y += lineHeight();
continue;
}
if(pos == String::InvalidPos)
pos = line.size();
String current = line.substr(0,pos);
lineRemain = line.substr(pos);
line = current;
auto linefeed = line.get().find_first_of('\n');
if(linefeed != std::basic_string<uint32_t>::npos)
{
line = line.substr(0,linefeed);
lineRemain = line.substr(linefeed+1) + lineRemain;
}
render(line,Vector2f(x,y),std::list<textRenderCmdUnit>());
x += width(line);
if(linefeed != String::InvalidPos)
{
x = penX(String(),area,hAlignment);
y += lineHeight();
}
line = lineRemain;
if(lineRemain.empty())
break;
}
}
itr ++;
}
}
以下渲染单色文本
void TypeFace::renderGlyphs(const std::list<textRenderCmdUnit>& cmd)
{
Vector2f position(0.0f,0.0f);
size_t leftChar = 0;
textRenderCmdUnit unit;
String tag;
std::queue<String> cmdStack;
std::list<textRenderCmdUnit>::const_iterator itr = cmd.begin();
while(itr != cmd.end())
{
unit = *itr;
if(unit.tag.empty() && !unit.undo)
{
position = renderGlyphs(unit.text,position);
}
else if(!unit.tag.empty())
{
tag = unit.tag;
cmdStack.push(tag);
if(tag == "bold")
bold_ = true;
else if(tag == "italic")
italic_ = true;
else if(tag == "underline")
underline_ = true;
else
applyColor(tag);
}
else if(unit.undo)
{
if(!cmdStack.empty())
{
tag = cmdStack.front();
cmdStack.pop();
if(tag == "bold")
bold_ = false;
else if(tag == "italic")
italic_ = false;
else if(tag == "underline")
underline_ = false;
else
applyColor(tag);
}
}
itr ++;
}
}
可以看出 支持的文本格式为<Tag>Text</>
一个例子是:
<Blue>这段代码主要测试BGE的富文本显示功能\n</Blue><Red>需要指出的是当前只要能支持多行显示和多颜色显示功能即可(不考虑下划线斜体以及加粗显示)。</Red><White>这句话将被显示为白色</><Blue>这句话应该被蓝色字体</Blue><Green>绿色字体</><Cyan>Cyan色</>";
最终显示如下:
解释:
GraphicsItem 重载 mousePressEvent(QGraphicsSceneMouseEvent* event)后处理完之后需要执行event->accpet().
推荐一款开源库glc
glc是一款基于Qt和opengl的3D仿真程序,可以是用来渲染3ds,obj,stl等模型以及常见几何体对象
支持对象拾取等功能
学习了一天感觉基本还行.
主页:http://www.glc-lib.net/
算是基本完成了,先上一个截图
可以说GUI所需要的当前基本都是实现了
再上一个使用例子:
#include <BGE/All>
using namespace bge;
int main(int argc, char* argv[])
{
FileSystem::instance().initial(argv[0]);
FileSystem::instance().setResourcePackage("data.zip");
WindowManager::instance().initialize("simkai.ttf",true);
Device* device = Device::create();
device->initial();
device->createWindow(640,480,bge::String("BGE Window"));
WindowManager::instance().propertyScheme()->load(":skin1.xml");
Window* window = WindowManager::instance().createWindow(":grid-layout-window.xml");
WindowManager::instance().addWindow(window);
while(device->isRunning())
{
device->preRender();
WindowManager::instance().update();
device->swapBuffers();
device->pollEvents();
}
WindowManager::instance().terminate();
device->closeWindow();
device->terminate();
device->deleteLater();
return 0;
}
个人感觉写的还是比较清晰,没什么需要特别说明的
使用的开源库有libpng,glfw,freetype,sigslot,physfs
不过代码有点大,有兴趣的可以留个邮箱
先上一个使用例子
#include <BGE/All>
using namespace bge;
Window* createButton(const std::string& name,const String& text)
{
Button* button = new Button(0,text);
button->setName(name);
button->setSize(Vector2f(92,42));
return button;
}
int main(int argc, char* argv[])
{
FileSystem::instance().initial(argv[0]);
FileSystem::instance().setResourcePackage("data.zip");
WindowManager::instance().initialize("simkai.ttf",false);
Device* device = Device::create();
device->initial();
device->createWindow(640,480,bge::String("BGE Window"));
Panel* panel = new Panel(FloatRect(30,30,192,180),0);
GridLayout* layout = new GridLayout();
panel->setLayout(layout);
std::vector<float> widths;
widths.push_back(0.0f);
layout->setColumnWidths(widths);
std::vector<float> heights;
for(int k = 0; k < 3; ++k)
heights.push_back(48.0f);
heights.push_back(0.0f);
layout->setRowHeights(heights);
GridCellInfo info;
info.columnIndex_ = 0; info.rowIndex_ = 0; layout->addWindow(createButton("button1",L"按键"),info);
info.columnIndex_ = 1; info.rowIndex_ = 0; layout->addWindow(createButton("button2",L"按键"),info);
info.columnIndex_ = 0; info.rowIndex_ = 1; layout->addWindow(createButton("button3",L"按键"),info);
info.columnIndex_ = 1; info.rowIndex_ = 1; layout->addWindow(createButton("button4",L"按键"),info);
info.columnIndex_ = 0;
info.rowIndex_ = 2;
info.horizontalSpan_ = 2;
Window* edit = new EditField(NULL,L"Edit Text");
edit->setSize(Vector2f(196,36));
layout->addWindow(edit,info);
info.columnIndex_ = 0;
info.rowIndex_ = 3;
info.horizontalSpan_ = 3;
layout->arrangeWithin();
FrameWindow* window = new FrameWindow();
window->enableMovement();
window->setText(L"BGE窗体");
window->setClientPanel(panel);
WindowManager::instance().addWindow(window);
while(device->isRunning())
{
device->preRender();
WindowManager::instance().update();
device->swapBuffers();
device->pollEvents();
}
WindowManager::instance().terminate();
device->closeWindow();
device->terminate();
device->deleteLater();
return 0;
}
在开发上参考了Qt等软件,当前支持常见控件 支持控件序列化 整体换肤 控件换肤
上面的代码显示的截图如下:
http://images2015.cnblogs.com/blog/676118/201601/676118-20160120092402484-466039328.png
不过当前还有一点小问题 修改完了再上代码
做这个不为什么 就是几年前一直想做个
#include <BGE/all>
using namespace bge;
int main(int argc,char* argv[])
{
//!初始化文件系统
FileSystem::instance()->initial(argv[0]);
//!加载压缩包
FileSystem::instance()->setResourcePackage("data.zip");
//!初始化UI管理器,设定默认字体,并设定自动删除控件
WindowManager::instance().initialize("simkai.ttf",true);
//!生成和初始化窗口设备
Device* device = bgeCreateDevice();
device->initial();
device->createWindow(640,480,bge::String("BGE Window"));
//!从资源文件加载控件包含子控件和控件属性
Window* window = WindowManager::instance().create("window.xml");
bge::WindowManager::instance().addWindow(listbox);
while(device->isRunning())
{
//!预渲染设备
device->perRender();
//!窗体更新
bge::WindowManager::instance().update();
device->swapBuffers();
device->pollEvents();
}
//!销毁窗体管理器
bge::WindowManager::instance().terminate();
//!关闭并销毁设备
device->closeWindow();
device->terminate();
device->deleteLater();
system("pause");
return 0;
}
还没做完,用法如上,很简练吧:D
什么?对整体视效不满意?
试下这个:WindowManager::instance().propertyScheme()->load("skin.xml");
想定制某个控件的视效?
Button* button = window->findChild("ok");
button->loadAppearance("button.xml","blue");
libpng比较另类,解码方式比较奇特,这里记录下
以下代码为使用虚拟IOReader的方式,稍做修改就基于FILE使用
代码很详细,不需额外解释
PNGImage::PNGImage(const std::string& filename):
ReferenceCountedImage(filename),
data_(0)
{
IOReader* reader = FileSystem::instance()->readFile(filename);
if(reader == 0)
return;
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if(png_ptr == 0)
{
reader->close();
reader->deleteLater();
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == 0)
{
png_destroy_read_struct(&png_ptr, png_infopp(0), png_infopp(0));
reader->close();
reader->deleteLater();
}
if(setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct(&png_ptr,png_infopp(0),png_infopp(0));
reader->close();
reader->deleteLater();
}
png_set_read_fn( png_ptr,reader,(png_rw_ptr)user_read_data);
png_set_sig_bytes(png_ptr,0);
png_read_png(png_ptr,info_ptr,PNG_TRANSFORM_IDENTITY,0);
const unsigned int width = png_get_image_width(png_ptr,info_ptr);
const unsigned int height = png_get_image_height(png_ptr,info_ptr);
const unsigned int bit_depth = png_get_bit_depth(png_ptr,info_ptr);
if(bit_depth != 8)
{
reader->close();
reader->deleteLater();
png_destroy_info_struct(png_ptr,png_infopp(&info_ptr));
png_destroy_read_struct(&png_ptr,png_infopp(0), png_infopp(0));
}
const png_byte colorType = png_get_color_type(png_ptr, info_ptr);
if((colorType != PNG_COLOR_TYPE_RGB) && (colorType != PNG_COLOR_TYPE_RGB_ALPHA))
{
reader->close();
reader->deleteLater();
png_destroy_info_struct(png_ptr, png_infopp(&info_ptr));
png_destroy_read_struct(&png_ptr, png_infopp(0), png_infopp(0));
}
const int bytesPerPixel = (colorType == PNG_COLOR_TYPE_RGB) ? 3 : 4;
const int stride = bytesPerPixel * width;
unsigned char* data = new unsigned char[stride * height];
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
for(unsigned int i = 0; i < height; ++i)
{
const unsigned int row = height - i - 1;
memcpy(data + (row * stride), row_pointers[i], stride);
}
size_.x_ = width;
size_.y_ = height;
numberOfBitsPerPixel_ = bytesPerPixel * 8;
data_ = data;
reader->close();
reader->deleteLater();
png_destroy_info_struct(png_ptr, png_infopp(&info_ptr));
png_destroy_read_struct(&png_ptr, png_infopp(0), png_infopp(0));
}
代码如下:
class ArrowItem : public QGraphicsItem
{
public:
const static float LINE_WIDTH;
const static float ARROW_SIZE;
public:
ArrowItem(QGraphicsItem* parent = 0);
~ArrowItem();
void setData(const QPointF& from,const QRectF& rect);
public:
QRectF boundingRect()const;
void paint(QPainter* painter,const QStyleOptionGraphicsItem* style,QWidget* widget);
private:
QPointF computeTo(const QPointF& from,const QRectF& to);
private:
bool mValid;
QPointF mFrom;
QPointF mTo;
QPointF mP1;
QPointF mP2;
QRectF mRect;
};
const float ArrowItem::LINE_WIDTH = 1.5f;
const float ArrowItem::ARROW_SIZE = 8.0f;
ArrowItem::ArrowItem(QGraphicsItem* parent):
QGraphicsItem(parent),
mValid(false)
{
}
ArrowItem::~ArrowItem()
{
}
QPointF ArrowItem::computeTo(const QPointF& from,const QRectF& to)
{
QPointF center(to.center());
QPointF top(center.x(),to.top());
QPointF right(to.right(),center.y());
QPointF bottom(center.x(),to.bottom());
QPointF left(to.left(),center.y());
//left
if(from.x() < to.left())
{
if(from.y() < to.top())
{
QLineF line1(from,top);
QLineF line2(from,left);
return line1.length() > line2.length() ? left : top;
}
else if(from.y() > to.bottom())
{
QLineF line1(from,left);
QLineF line2(from,bottom);
return line1.length() > line2.length() ? bottom : left;
}
return left;
}
//right
if(from.x() > to.right())
{
if(from.y() < to.top())
{
QLineF line1(from,top);
QLineF line2(from,right);
return line1.length() > line2.length() ? right : top;
}
else if(from.y() > to.bottom())
{
QLineF line1(from,bottom);
QLineF line2(from,right);
return line1.length() > line2.length() ? right : bottom;
}
return right;
}
if(from.y() < to.top())
return top;
else if(from.y() > to.bottom())
return bottom;
Q_ASSERT(0);
return QPointF();
}
void ArrowItem::setData(const QPointF& from,const QRectF& rect)
{
mValid = true;
mFrom = from;
mRect = rect;
if(rect.contains(from))
{
mValid = false;
return;
}
mTo = computeTo(mFrom,mRect);
QLineF line(mFrom,mTo);
qreal angle = ::acos(line.dx()/line.length());
if(line.dy() >= 0)
angle = 3.14159*2 - angle;
mP1 = mTo + QPointF(sin(angle-PI/3)*ARROW_SIZE,cos(angle-PI/3)*ARROW_SIZE);
mP2 = mTo + QPointF(sin(angle-PI+PI/3)*ARROW_SIZE,cos(angle-PI+PI/3)*ARROW_SIZE);
}
QRectF ArrowItem::boundingRect()const
{
qreal extra = (LINE_WIDTH + ARROW_SIZE)/2.0;
QRectF rect= QRectF(mFrom,QSizeF(mTo.x()-mFrom.x(),mTo.y()-mFrom.y())).normalized().
adjusted(-extra,-extra,extra,extra);
return rect;
}
void ArrowItem::paint(QPainter* painter,const QStyleOptionGraphicsItem* style,QWidget* widget)
{
if(!mValid)
return;
painter->setRenderHint(QPainter::Antialiasing);
QPen p(QColor::fromRgb(79,136,187));
painter->setBrush(QBrush(p.color()));
p.setWidthF(LINE_WIDTH);
painter->setPen(p);
painter->drawLine(mFrom,mTo);
painter->drawPolygon(QPolygonF()<<mTo<<mP1<<mP2);
}
void ArrowItem::setData(const QPointF& from,const QRectF& rect)
该函数第一个参数为箭头起点,rect为箭头指向的矩形框,该函数会自动计算箭头终点和箭头相关参数
该代码经过测试 - 完全可以使用
参考:http://www.cnblogs.com/liulun/p/3833006.html
QFont font;
font.setPointSize(15);
for(int i=0;i<15;i++)
{
for(int j=0;j<18;j++)
{
QPointF p(-2400+20+(RECT_WIDTH+30)*i,-1500+20+(RECT_HEIGHT+30)*j);
QGraphicsRectItem* item = new QGraphicsRectItem(p.x(),p.y(),RECT_WIDTH,RECT_HEIGHT);
item->setToolTip("click me");
item->setBrush(QColor(79,136,187,255));
scene->addItem(item);
QGraphicsTextItem* text = new QGraphicsTextItem(item);
text->setPlainText(QString("%1,%2").arg(i).arg(j));
QRectF rect = text->boundingRect();
text->setDefaultTextColor(QColor(255,255,255));
p.setX(p.x() + RECT_WIDTH/2 - rect.width()/2);
text->setPos(p);
text->setFont(font);
}
}
按照经验以为
QGraphicsRectItem会有setText接口,之后觉得应该定制一个QGraphicsItem
搜了下原来QGraphicsRectItem是树状结构可以增加子项
因此就好办多了,代码如上
更新不多,主要是取消了插接结构,把所有的编解码和音效都合入了主库
dll合计不超过1M
支持常见音频格式和音效,使用超级方便
下载在这里:
/Files/gaimor/gaudio-sdk-2.2.1.0.zip