MYGUI/CEGUI中文输入的问题(3)

 14.2 选词控件的渲染

这个控件由于使用了三个子窗口来实现功能,所以它的渲染窗口实现非常简单,只是负责描绘背景。代码如下。

void FalgardIMEShowWindow::render()

{

       IMEShowWindow* w = (IMEShowWindow*)d_window;

       const WidgetLookFeel& wlf = getLookNFeel();

       wlf.getImagerySection("background").render(*w, 0);

}

读者可能非常奇怪,为什么就这么简单的描绘背景,就会描绘出这么复杂的选词窗口呢。因为我们这里只是负责渲染自己,子窗口的渲染在他们自己的对应渲染窗口中进行。他们的已经被CEGUI实现了,他们是比较复杂的。有兴趣的读者可以自己阅读相关的代码。

下面简单介绍选词控件的外观定义。(第14.1节已经介绍了一部分)。

我们的控件使用了属性的链接的定义。由于我们的需要通过设置控件的属性就能够设置子窗口的属性,所以我们需要定义属性链接。定义如下。

<PropertyLinkDefinition name="IMENameColors" widget="__auto_imename__" targetProperty="TextColours" initialValue="tl:FFFFFF00 tr:FFFFFF00 bl:FF00FF00 br:FF00FF00" redrawOnWrite="true" />

<PropertyLinkDefinition name="InputNameColors" widget="__auto_inputcode__" targetProperty="TextColours" initialValue="tl:FFFFFF00 tr:FFFFFF00 bl:FF00FFFF br:FF00FF00" redrawOnWrite="true" />

这是两个颜色的属性链接。控件中定义了一个名为IMENameColors的属性,它指向子窗口后缀名为"__auto_imename__"的子窗口中的TextColours属性。也就是说如果操作了控件的IMENameColors属性实际上是在操作这个子窗口中的TextColours属性。而这个属性正式子窗口文本的颜色矩形。我们说过CEGUI支持文本的过渡色。只要设置控件的这个属性就可以实现不同的过渡色。读者可以自己试验。tl表示左上,t表示topl表示left,其他类似。指定redrawOnWrite为真,当这个值被修改后重绘控件。另外一个属性和我们介绍的非常类似,这里就不在介绍了。

背景图片的外观的定义是从外观Vanilla/Shared中的BackgroundOnly图像段中拷贝过来的。它定义了一个图像的框架作为背景。这个框架支持各向的拉伸操作。因此非常时候我们的控件,因为我们需要随时的调整控件的大小。

14.3 使用选词控件

控件设计好了,哪么如何使用他呢。我们说过选词控件需要响应第13章实现的IME消息。把IME类获取的各种文本添加到控件中。

首先,我们要定义IME的实例,并且处理各种IME消息。我们定义了一个名为g_IME的全局变量,并且在Windows窗口过程中调用这个实例的MessageProc函数来处理IME消息。定义和消息处理如下所示。

//全局变量的声明

IME g_IME;

//处理消息,如果处理的话就返回,不在被其他函数处理

if (g_IME.MessageProc(hwnd, uMsg, wParam, lParam))

{

       return 0;

}

其次,创建我们的选词控件,我们在程序里创建。代码如下。

//创建一个选词窗口

Window* pIME = winMgr.createWindow("Vanilla/ImeWindow", "IMEShow");

if (pIME)

{

//设置其实位置,其实这一步也没什么用,控件或自己调整位置和大小

pIME->setArea(UVector2(UDim(0, 150), UDim(1, -400)),

UVector2(UDim(0,200), Dim(0,260)));

//添加到背景窗口

       background->addChildWindow(pIME);

//默认隐藏窗口

       pIME->hide();

//保存到全局变量中,在IME类中使用

       g_IMEShower = (IMEShowWindow*)pIME;

}

再次,我们需要修改IME类,响应IME类提供的各种函数来控制选词窗口。

当开始文本混合的时候我们显示选词窗口。

void IME::onCompositionBegin()

{

       if (g_IMEShower == NULL)

       {

              return;

       }

//显示窗口

       g_IMEShower->show();

       //获取当前的激活窗口

CEGUI::Window* pWnd = CEGUI::System::getSingleton().getGUISheet()-> getActiveChild();

//设置这个窗口为跟踪窗口

       g_IMEShower->TraceWindow(pWnd);

}

当混合结束后我们隐藏选词窗口。

void IME::onCompositionEnd()

{

       if (g_IMEShower == NULL)

       {

              return;

       }

       g_IMEShower->hide();

}

当在混合过程中的时候,我们响应各种操作。

void IME::onCompositionStr(LONG flag)

{

       if (g_IMEShower == NULL)

       {

              return;

       }

//如果混合结束后,我们注入结果字符串到CEGUI

       if (flag & GCS_RESULTSTR)

       {

              for (int i=0; i<wcslen(d_pResultStr); i++)

              {

                     CEGUI::System::getSingleton().injectChar(d_pResultStr[i]);

              }

       }

//如果混合编码变化我们修改混合编码

       if (flag&GCS_COMPSTR )

       {

              if (wcslen(d_pCompStr) <= 0)

              {

                     return;

              }

              g_IMEShower->setInputCode(ucs_to_utf8(d_pCompStr));

       }

//暂不处理这个消息

       if (flag&GCS_CURSORPOS )

       {

       }

      

获取激活的窗口

CEGUI::Window* pWnd = CEGUI::System::getSingleton().getGUISheet()-> getActiveChild();

//追踪这个窗口

       g_IMEShower->TraceWindow(pWnd);

}

当输入法的状态发生变化的时候。修改输入法的名称和全角半角状态。

void IME::onIMEStatusChanged()

{

       if (g_IMEShower == NULL)

       {

              return;

       }

       STRING str = d_imeName ;

       str += L"(" ;

       str += (d_bSharp ? L"全角" : L"半角") ;

       str += L")";

       g_IMEShower->setInputName(ucs_to_utf8(str.c_str()));

       g_IMEShower->hide();

}

当选词列表发生变化的时候。

void IME::onCandidateListChanged()

{

       if (g_IMEShower == NULL)

       {

              return;

       }

//先清空列表

       g_IMEShower->clearWordList();

//然后获取选词窗口的文本,然后注入到CEGUI系统中

       for (int i=0; i<d_candidateArray.size(); i++)

       {

              g_IMEShower->addWord(ucs_to_utf8(d_candidateArray[i].c_str()));

       }

}

读者可能不明白ucs_to_utf8函数的作用。它将Unicode的字符串转化为utf8的字符串。我们说过String类接受的Unicode编码格式是UTF8格式。原始的Unicode编码不直接支持。读者可以写一个支持Unicode格式的构造函数,哪么这里就不需要这一步转化了。转化函数如下所示。

const CEGUI::utf8* ucs_to_utf8(const wchar_t* pucs)

{

       static char tmpbuf[1024];

       memset(tmpbuf, 0, sizeof(tmpbuf));

       WideCharToMultiByte(CP_UTF8, 0, pucs, (int)wcslen(pucs), tmpbuf, 1023,0,0);

       return (CEGUI::utf8*)tmpbuf;

}

这个函数调用了Window提供的转化函数将Unicode编码转化为UTF8编码。静态缓冲tmpbuf1024个字节一般来说不可能超过它,所以不必检查缓冲区的溢出。本书的很多代码都是为例子程序而写,可能不是很注重效率以及安全性。读者在使用的时候需要自己考虑。

14.4 本章小结

本章介绍了选词控件的实现,并且介绍了如何使用这个控件。结合第13章介绍的输入法的支持,本章完整的实现了一个输入法的选词控件。读者可以打开例子程序体验,感觉一定很不错哦。

 

你可能感兴趣的:(MYGUI/CEGUI)