属性事件与布局文件

CEGUI 设计属性和事件有一个原因就是为了方便的在XML 文件中设置
窗口的数据(属性)和窗口的逻辑操作(事件)。属性和事件是如何从
XML 布局文件中获取的。


注意:
CEGUI 中的布局文件和其他许多文件并不是以xml 为文件扩展名的,布局文件以为扩展名
layout资源管理文件schema 文件为扩展名,控件外观文件是以looknfeel 为扩展名的,
字体文件是以font 为扩展名的等等。虽然不是以xml 文件为扩展名,但内部数据是以XML
来组织的。所以读者可以修改这些扩展名为xml 以便程序打开。目前我们还没有介绍CEGUI
的基本类Window,所以CEGUI 布局文件中的Window 元素读者可能不会非常理解,不过
没关系,读者先有个大概的印象,等学习了在来看这里的介绍会更加清晰。

布局文件的格式如下,首先是XML 的标准格式,可以有编码设置,CEGUI 所有配置文
件都是UTF-8 的编码的。其他编码会导致CEGUI 抛出异常。

<?xml version="1.0" ?>

布局文件的第一个元素(或者标记)是GUILayout,所有的其他标记都在这个标记对里。

<GUILayout> ...</GUILayout>

在下一层就是窗口链了,窗口以Window 为标记,窗口中可以嵌套窗口,作为子窗口。
Window 是窗口的标记,Type 代表窗口的类型,Name 代表窗口的名称,这个名称在整
个CEGUI 系统中都是都必须是唯一的。具体Type 的值代表什么,在讲Schema 的时候详细
介绍

<Window Type="TaharezLook/FrameWindow" Name="Demo8/Window2">
<Property Name="UnifiedPosition" Value="{{0.55,0},{0.1,0}}" />
<Property Name="UnifiedSize" Value="{{0.4,0},{0.3,0}}" />
<Property Name="Text" Value="Demo 8 - Information Window" />
<Property Name="Tooltip" Value="Contains some StaticText information panels" />
<Event Name ="MouseClick" Function= "ScriptFunction();"/>
<Window Type="TaharezLook/StaticText" Name="Demo8/Window2/Info">
<Property Name="UnifiedPosition" Value="{{0.05,0},{0.1,0}}" />
<Property Name="UnifiedSize" Value="{{0.9,0},{0.3,0}}" />
<Property Name="HorzFormatting" Value="WordWrapCentred" />
</Window>
<Window Type="TaharezLook/StaticText" Name="Demo8/Window2/Tips">
<Property Name="UnifiedPosition" Value="{{0.05,0},{0.5,0}}" />
<Property Name="UnifiedSize" Value="{{0.9,0},{0.4,0}}" />
<Property Name="HorzFormatting" Value="WordWrapCentred" />
</Window>
</Window>


可以看到,窗口是可以嵌套的,嵌套用来表示内部的窗口是外部窗口的子窗口。好了重
点到了Property 标记,它表示一个属性,Name 表示它的名称,Value 表示它的值。Unified
Position 是窗口的位置的属性名称,窗口位置为什么不使用两个数(x 和y)来表示,而是使
用了4 个数呢?这个我们稍后在讲。UnifiedSize 代表窗口的大小,它也使用了四个数。Text
属性代表窗口显示的文字等等。哪么事件呢?事件在的标记是Event,Name 表示事件的名
称,Function 表示事件对应的处理函数。可见布局文件的结构很简单(这都是CEGUI 设计
的优势)。
布局文件可以修改窗口的数据值,和窗口的处理函数。这给UI 系统提供了很大的灵活
性。下面介绍布局文件的加载和分析。

注意:
CEGUI 所有布局文件里的元素(标记)和属性(XML 里的属性的概念,不是CEGUI 中的
属性,比如Name 和Value 就是Property 的属性)都是大小写敏感的。event 不能被CEGUI
识别,如果定义了<event Name ="***" Function ="***" />,哪么CEGUI 会抛出异常。同理
EVENT 也不行,只能是Event。Property,Name,Function 等等都是一样的。

XML 文件的分析是通过CEGUI 提供的XML 解析库来实现的。解析库读取XML 文件
并且通过回调函数,通知CEGUI 的XML 解析接口来处理具体的元素。关于CEGUI 的XM
L 解析接口的定义
请读者参考第4 章的内容。
CEGUI 是如何处理XML 布局文件的元素的呢?读者会找到GUILayout_xmlHandler.cpp
/.h
两个文件。从他们的名称可以看出他们是处理布局的。Layout 代表布局,xmlHandler 代
XML 的处理类。类似的还有CEGUIScheme_xmlHandler,CEGUIFont_xmlHandler.h 等。
每一个CEGUI 的描述文件都对应一个类似的处理类。
总的来说,布局文件的处理还是比较复杂的。这里介绍一种阅读代码规律,希望可以帮
助读者理解。
XML 中出现的标记有两类一类我们称为元素,它主要有以下6 种。

const String GUILayout_xmlHandler::GUILayoutElement( "GUILayout" );
const String GUILayout_xmlHandler::WindowElement( "Window" );
const String GUILayout_xmlHandler::AutoWindowElement( "AutoWindow" );
const String GUILayout_xmlHandler::PropertyElement( "Property" );
const String GUILayout_xmlHandler::LayoutImportElement( "LayoutImport" );
const String GUILayout_xmlHandler::EventElement( "Event" );


他们的名称中带有Element,前缀代表他们具体的含义。比如WindowElement,它代表
XML 代码里的Window 标记。当XML 解析库遇到Element 的时候它会调用我们编写的处理
类中的elementStart 函数
。什么是Element 标记呢?以Window 举例,<Window 就是一个E
lement 的开始,这是elementStart 会被调用
。当XML 解析库遇到 </Window>(这里只是那
Window 来举例,所有的元素都可以这样结束,但为了简单,好多元素使用了简化版)或者
它的简化版/>,属性和事件等元素使用简化版。比如属性定义<Property Name="UnifiedSize
" Value="{{0.4,0},{0.3,0}}" />,就使用/>来表示元素的结束。当一个元素结束的时候,XM
L 解析库会调用elementEnd 函数
,这时可以做一些处理工作。如果读者对XML 解析库如何
回调这两个函数感兴趣可以参考第4 章相关内容。
GUILayoutElement 是布局文件的根元素,它有LayoutParentAttribute 属性用来表示这个
布局文件的命名父窗口。WindowElement 代表窗口元素,CEGUI 遇到这个元素会创建一个
窗口作为当前窗口(上一个WindowElement 对应的窗口,处理这个窗口后,当前窗口变成
这个窗口)的子窗口。AutoWindowElement 是这一版的新元素,用来代表窗口的自动孩子,
关于什么是自动孩子,后文讲述。PropertyElement 元素设置当前窗口的属性,EventElement
元素设置当前窗口的事件。LayoutImportElement 也是一个新元素,它就像C++ 中的#includ
e,包含一个布局文件到当前布局文件,布局文件返回的窗口,作为当前窗口的子窗口。也
可以把它想象成一个定义在另一个文件中的复杂的WindowElement 元素。
另一类,我们称作属性(XML 中的定义)。他们可以作为元素的属性存在具体如下所示。

const String GUILayout_xmlHandler::WindowTypeAttribute( "Type" );
const String GUILayout_xmlHandler::WindowNameAttribute( "Name" );
const String GUILayout_xmlHandler::AutoWindowNameSuffixAttribute( "NameSuffix" );
const String GUILayout_xmlHandler::PropertyNameAttribute( "Name" );
const String GUILayout_xmlHandler::PropertyValueAttribute( "Value" );
const String GUILayout_xmlHandler::LayoutParentAttribute( "Parent" );
const String GUILayout_xmlHandler::LayoutImportFilenameAttribute( "Filename" );
const String GUILayout_xmlHandler::LayoutImportPrefixAttribute( "Prefix" );
const String GUILayout_xmlHandler::LayoutImportResourceGroupAttribute( "ResourceGroup" );
const String GUILayout_xmlHandler::EventNameAttribute( "Name" );
const String GUILayout_xmlHandler::EventFunctionAttribute( "Function" );


具体每个属性是什么含义,也可以根据他们的名称来判断,比如EventFunction 代表这
是事件的函数属性,它包含Attribute 来表示它是一个属性。读者也许奇怪为什么EventNam
eAttribute,WindowNameAttribute 以及PropertyNameAttribute 都是"Name",为什么要分成三
个呢?主要是方便读者阅读后续的处理代码。他们分别代表不同元素中的名字属性。
WindowTypeAttribute 属性代表窗口类型,CEGUI 使用窗口类型和窗口名称来创建窗口
其中名称可选的(CEGUI 内部生成唯一的窗口名称来表示这个窗口)。AutoWindowName
SuffixAttribute 属性代表窗口的自动子窗口的后缀名。CEGUI 自动子窗口的名称是由父窗口
的名字+子窗口的后缀构成的。PropertyValueAttribute 属性代表属性的值字符串,这个值一
般就是我们属性帮助函数设置属性时的参数。stringToURect 的参数就是UnifiedAreaRect 窗
口属性的PropertyValueAttribute 值。LayoutImportFilenameAttribute,LayoutImportPrefixAttri
bute,LayoutImportResourceGroupAttribute 是LayoutImportElement 的属性,它们提供Layou
tImportElement 需要的信息,包括导入布局文件的名称,后缀名称,以及资源组。这些属性
涉及到了CEGUI 的资源提供模块。我们在第4 章详细介绍。EventFunctionAttribute 属性是E
ventElement 元素对应的属性。它的值就是脚本函数或者脚本片段。
刚才我们已经讲过,当一个元素到来时会调用elementStart,哪么下面来分析这个函数
的实现。

void GUILayout_xmlHandler::elementStart(const String& element, const XMLAttributes&
attributes)
{
// 处理根元素 GUILayoutElement ,调用函数elementGUILayoutStart 来处理
if (element == GUILayoutElement)
{
elementGUILayoutStart(attributes);
}
//处理Window 元素
else if (element == WindowElement)
{
elementWindowStart(attributes);
}
//处理自动窗口元素,从当前窗口获取自动孩子
else if (element == AutoWindowElement)
{
elementAutoWindowStart(attributes);
}
//处理属性元素,设置当前窗口的属性
else if (element == PropertyElement)
{
elementPropertyStart(attributes);
}
// 处理导入布局文件元素,当前窗口接受布局文件返回的窗口为子窗口
else if (element == LayoutImportElement)
{
elementLayoutImportStart(attributes);
}
// 处理脚本元素,又称事件元素
else if (element == EventElement)
{
elementEventStart(attributes);
}
// 其他未知元素,只写入CEGUI 错误日志
else
{
Logger::getSingleton().logEvent("GUILayout_xmlHandler::startElement - Unexpected
data was found while parsing the gui-layout file: '" + element + "' is unknown.", Errors);
}
}


我们看到CEGUI 根据不同元素,都有单独的处理函数,从这个函数无法获得详细信息,
下面我们以窗口元素为例,介绍窗口元素的处理过程。在介绍elementWindowStart 函数之前
必须先介绍XMLAttributes,它作为每个处理函数的参数。这个参数代表我们前面描述的属
性集,就是它包含了当前元素的所有的属性。

void GUILayout_xmlHandler::elementWindowStart(const XMLAttributes& attributes)
{
//获取窗口的类型属性
String windowType(attributes.getValueAsString(WindowTypeAttribute));
// 获取窗口的名称属性,创建窗口的两个参数
String windowName(attributes.getValueAsString(WindowNameAttribute));
// 尝试创建窗口,这个过程会抛出异常
try
{
Window* wnd = WindowManager::getSingleton().createWindow(windowType,
windowName, d_namingPrefix);
// 添加这个窗口为当前窗口的子窗口
if (!d_stack.empty())
d_stack.back().first->addChildWindow(wnd);
else
d_root = wnd;
//设置自己为当前窗口
d_stack.push_back(WindowStackEntry(wnd,true));
// 通知当前窗口初始化
wnd->beginInitialisation();
}
catch (AlreadyExistsException&)
{
// 创建的窗口已经存在,名字重复,清除所有的创建过的窗口
cleanupLoadedWindows();
// 抛出异常,告诉调用层,有错误发生了
throw InvalidRequestException("GUILayout_xmlHandler::startElement - layout loading
has been aborted since Window named '" + windowName + "' already exists.");
}
catch (UnknownObjectException&)
{
// 清除所有已经创建的窗口
cleanupLoadedWindows();
// 抛出异常,告诉调用层,有错误发生了
throw InvalidRequestException("GUILayout_xmlHandler::startElement - layout loading
has been aborted since no WindowFactory is available for '" + windowType + "'
objects.");
}
}


我们看到遇到WindowElement 首先创建一个窗口,然后将窗口作为当前窗口的子窗口,
最后设置自己为当前窗口。这样如果窗口元素嵌套就可以自动设置父子关系了。读者对窗口
和窗口的创建还不熟悉,别着急第3 章将会详细介绍。
其他元素的处理类似,读者自己阅读代码,这里不在赘述。CEGUI 处理所有支持的描
述文件的读取和处理方法都是一样的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值