10.4 使用wxCursor编程

光标用来指示鼠标指针当前的位置.你可以给某个窗口指定不同的光标以便提示用户这个窗口期待某种类型的鼠标操作.和图标一样,光标也是一种始终带有透明遮罩的小图片,可以使用一般的构造函数或者是平台相关的构造函数来创建.其中的一些构造函数还需要相对于整个图片的左上角指定一个热点位置,当鼠标点击的时候,热点所在的位置将作为鼠标点击的位置.

下表列举了光标相关的函数

wxCursor 光标可以从wxImage对象,二进制数据,系统定义的光标标识符以及光标文件来创建.
Ok 如果光标数据已经具备,则返回True.

创建一个光标

创建光标最简单的方法是通过系统提供的光标标识符,如下面的例子所示:

wxCursor cursor(wxCURSOR_WAIT);

下表列出了目前支持的光标标识符和它们的光标的样子(依照平台的不同会有些变化)

wxCURSOR_ARROW 标准光标.
wxCURSOR_RIGHT_ARROW 标准反向光标.
wxCURSOR_BLANK 透明光标.
wxCURSOR_BULLSEYE 近视眼.
wxCURSOR_CROSS 十字.
wxCURSOR_HAND 手.
wxCURSOR_IBEAM I字光标.
wxCURSOR_LEFT_BUTTON 按左键(GTK+ only).
wxCURSOR_MAGNIFIER 放大镜.
wxCURSOR_MIDDLE_BUTTON 按中键(译者注:原书图片有误) (GTK+ only).
wxCURSOR_NO_ENTRY 禁止通行.
wxCURSOR_PAINT_BRUSH 画刷.
wxCURSOR_PENCIL 铅笔.
wxCURSOR_POINT_LEFT 向左.
wxCURSOR_POINT_RIGHT 向右.
wxCURSOR_QUESTION_ARROW 带问号的箭头.
wxCURSOR_RIGHT_BUTTON 按右键(译者注:图片有误) (GTK+ only).
wxCURSOR_SIZENESW 东北到西南伸缩.
wxCURSOR_SIZENS 南北伸缩.
wxCURSOR_SIZENWSE 西北到东南伸缩.
wxCURSOR_SIZEWE 东西伸缩.
wxCURSOR_SIZING 一般伸缩.
wxCURSOR_SPRAYCAN 画刷.
wxCURSOR_WAIT 等待.
wxCURSOR_WATCH 查看.
wxCURSOR_ARROWWAIT 后台忙.

你还可以使用预定义光标指针wxSTANDARD_CURSOR, wxHOURGLASS_CURSOR和wxCROSS_CURSOR.

另外在windows和Mac平台上还可以从对应的资源文件中加载光标:

//windows平台
wxCursor cursor(wxT("cursor_resource"), wxBITMAP_TYPE_CUR_RESOURCE,
                 hotSpotX, hotSpotY);
// Mac平台
wxCursor cursor(wxT("cursor_resource"), wxBITMAP_TYPE_MACCUR_RESOURCE);

你还可以通过wxImage对象创建光标,而"热点"则要通过wxImage::SetOptionInt函数设置.之所以要设置热点, 是因为很多光标不太适合使用默认的左上角作为热点,比如对于十字光标来说,你可能希望将其十字交叉的地方作为热点.下面的代码演示了怎样从一个PNG文件中产生设置了热点的光标:

// 用wxImage创建光标
wxImage image(wxT("cursor.png"), wxBITMAP_TYPE_PNG);
image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X, 5);
image.SetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y, 5);
wxCursor cursor(image);

使用wxCursor

每个窗口都可以设置一个对应的光标,这个光标在鼠标进入这个窗口的时候显示,如果一个窗口没有设置光标,其父窗口的光标将被显示,如果所有的父窗口都没有设置光标,则系统默认光标被显示:

使用下面的代码给窗口设置一个光标:

window->SetCursor(wxCursor(wxCURSOR_WAIT));

使用wxSetCursorEvent

在windows系统或者是Mac OS X系统上,有一些小地方我们需要注意一下.举个例子,如果你自己实现了一个容器类,比方说是一个分割窗口,并且给它设置了一个特殊的光标(比如说 wxCURSOR_WE用来表明某个分割条是可以被拉动的),然后你在这个分割窗口中放置了两个子窗口,如果你没有给这两个子窗口设置光标的话,当光标在子窗口上移动时,它们可能会不恰当的显示其父窗口,那个wxCURSOR_WE光标.而本来你是希望只有在鼠标移动到分割条上的时候才显示的.

要告诉wxWidgets某个光标只应该在某种情况下被显示,你可以增加一个wxSetCursorEvent事件的处理函数,这个事件在Windows和Mac平台上,当需要设置光标的时候(通常是鼠标在窗口间移动的时候)被产生.在这个事件处理函数中可以调用 wxSetCursorEvent::SetCursor来设置一个特殊的光标.如下所示:

BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
    EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor)
END_EVENT_TABLE()
// 指示光标只应该被设置给分割条
void wxSplitterWindow::OnSetCursor(wxSetCursorEvent& event)
{
    if ( SashHitTest(event.GetX(), event.GetY(), 0) )
    {
        // 使用默认的处理
        event.Skip();
    }
    //else:什么也不作,换句话说,不调用Skip.则事件表不会被继续搜索
}

在这个例子中,当鼠标指针移过分割条的时候,SashHitTest函数返回True,因此Skip函数被调用,事件表调用失败,这和没有定义这个事件表的效果是一样的,导致wxWidgets象往常一样显示指定给窗口的光标(wxCURSOR_WE).而如果SashHitTest函数返回False,则表明光标是在子窗口上移动,这时候应该不显示我们指定的光标,因此我们不调用Skip函数,让事件表匹配成功,则事件表将不会在继续匹配,这将使得wxWidgets认为这个窗口没有被指定光标,因此.在这种情况下,即使子窗口自己没有光标(象wxTextCtrl这种控件,一般系统会指定一个它自己的光标,不过wxWidgets对这个是不感知的),也将不会使用我们指定给父窗口的光标.