使用C++和DirectX开发游戏GUI(五)

简介: 然后就是滚动条的问题了.我的列表框包含两个成员,m_horzscrollbar和m_vertscrollbar,他们都是GUI滚动条.当列表框的大小被改变时(wm_sizechanged()),他会看看数据的宽度和高度并决定是否显示滚动条.
// represents a column in our listbox 
class gui_listbox_column 

public: 
gui_listbox_column() { } 
virtual ~gui_listbox_column() { } 

virtual void draw(uti_rectangle &where); 

void setname(const char *name) { m_name = name; } 
uti_string getname(void) { return(m_name); } 

int getwidth(void) { return(m_width); } 
void setwidth(int w) { m_width = w; } 

private: 
uti_string m_name; 
int m_width; 
}; 

// an item in our listbox 
class gui_listbox_item 

public: 
gui_listbox_item() { m_isselected = 0; m_indent = 0; } 
virtual ~gui_listbox_item() { } 

virtual draw(int colnum, uti_rectangle &where); 

void clearallcolumns(void); // boring 
void setindent(int i) { m_indent = i; } 
int getindent(void) { return(m_indent); } 

void settext(int colnum, const char *text); // boring 
uti_string gettext(int colnum = 0); // boring 

void setitemdata(unsigned long itemdata) { m_itemdata = itemdata; } 
unsigned long getitemdata(void) { return(m_itemdata); } 

void setselected(int s = 1) { m_isselected = s; } 
int getselected(void) { return(m_isselected); } 

private: 
int m_isselected; 
int m_indent; // # of pixels to indent this item 
unsigned long m_itemdata; 
uti_pointerarray m_coltext; 
}; 

// the listbox itself 
class gui_fancylistbox : public gui_window 

public: 
gui_fancylistbox() { m_multiselect = 0; } 
virtual ~gui_fancylistbox() { clear(); } 

int getselected(int iter = 0); 

virtual int wm_command(gui_window *win, int cmd, int param); 
virtual int wm_paint(coord x, coord y); 
virtual int wm_lbuttondown(coord x, coord y); 

gui_scrollbar_horz &gethscroll(void) { return(m_hscroll); } 
gui_scrollbar_vert &getvscroll(void) { return(m_vscroll); } 

virtual int wm_sizechanged(void); // the window's size has changed somehow 

gui_listbox_item *getitemat(int index); // boring 
gui_listbox_item *additem(const char *text); // boring 
int delitem(int index); // boring 
int delallitems(void); // boring 
gui_listbox_column *getcolumn(int index); // boring 
int addcolumn(const char *name, int width); // boring 
gui_listbox_column *getcolumnat(int index); // boring 
int delcolumn(int index); // boring 
int delallcolumns(void); // boring 

int clear(void); // delete columns & items 

int getnumitems(void); 
int getnumcols(void); 

void deselectall(void); 
void selectitem(int item); 
void selecttoggleitem(int item); 

void deselitem(int item); 

private: 
int m_numdispobjsy; 
int m_vertgutterwidth; // # of pixels between items vertically 

gui_scrollbar_horz m_hscroll; 
gui_scrollbar_vert m_vscroll; 

bool m_multiselect; // is this multi-selectable? 
uti_pointerarray m_items; // array of gui_listbox_items 
uti_pointerarray m_columns; // array of gui_listbox_columns 
}; 

  列表框是到现在为止你做的最难的控件吧?但这仅仅是因为它是最通用的.一个能够处理多列、缩进、多重选择列表框控件将在实践中证明他对你的游戏是不可或缺的.停下来并想想在大多数游戏里用到列表框的地方,你就会很快发现这一点. 

  我把我的列表框控件分成两部分:一个多列的"报表风格"的列表控件和一个图标列表控件,它创建一个类似于当你在windows"我的电脑"里选择大图标察看方式的显示. 

  图表列表控件比较容易建立.它使用了一列静态图标(在一次代码重用),所有的具有相同的大小.我使用图标的宽度除列表框的宽,这让我知道有几列可用.(如果证明我的列表框比大图表小,我假设我只有一列,并让绘制系统剪裁图标以使他们不会超出我的绘制区域).一旦我有了列数,我通过图标的总数除以它计算出我所需要的行数.这样我就知道我该怎样设定要包括的滚动条. 

  注意当控件改变大小时必须重新计算这些值.为此我设定了一个wm_sizechanged()消息,calcall()将会在窗口绘制区域被改变的时候调用它. 

  报表风格列表控件要复杂一些.我先写了两个辅助类,gui_listbox_column和gui_listbox_item,它们包含了所有的关于列表中给定物件和列的信息. 

  gui_listbox_column是两者中较简单的.主要的列表框类有一个成员变量身份的gui_listbox_column的动态数组,这代表了目前列表框中的列.gui_listbox_column包含了在列表框中所需要的列的所有信息,包括列的名字,列的对齐,显示或隐藏,大小等等. 

  主要的列表框类也有一个gui_listbox_item的动态数.gui_listbox_item类包含了与我们的报表风格列表框中特定行(或物件)相关的所有信息.目前这个类最重要的数据成员是代表每列数据的字串数组.我也让每个物件通过m_itemdata成员存储一个附加的32位数据.这个技术类似于windows允许你通过位你的列表物件调用SetItemData()和GetItemData()来存储32位数据.这个细节很重要,因为它允许列表框的用户为每个物件存储一个指针-通常一个与该物件有关的特定类,以使它以后可用. 

  怎么绘制列和物件呢?我倾向于在要绘制的列表框中在每个单独的物件/列上有个绝对的控件.到最后,我决定让列表控件通过不断调用两个虚函数,gui_listbox_item::draw()和gui_listbox_column::draw()来绘制他的物件和列.每个函数使用一个代表列或者物件在屏幕上位置的矩形.默认的对这些draw()函数的开发仅仅分划出与矩形中特定列和子物件相关的文本;然而,我先在可以简单的为需要独特外观的物件或列派生和重载draw().这种技术目前工作的很好,但是我还不足以宣称这是最好的方法. 

  然而,绘制物件比行需要更多的工作.物件需要用高光绘制,这决定于他们是否被选择.这并不很难,但一定不能忘记. 

  然后就是滚动条的问题了.我的列表框包含两个成员,m_horzscrollbar和m_vertscrollbar,他们都是GUI滚动条.当列表框的大小被改变时(wm_sizechanged()),他会看看数据的宽度和高度并决定是否显示滚动条. 

总结 

  真是绕了一大圈子,但是幸运的是你对为GUI创建控件有了个大致的想法.这里我想强调的是"风格就是乐趣".在做你的GUI时不要害怕创新-做做你曾经梦想过的东西,和使你的游戏最酷的东西.如果你的游戏很依赖你的GUI的效能这一点尤其重要,比如你在作即时战略游戏. 

  还要记住当创建控件的时候,你需要为你的游戏的其他部分考虑平衡问题-细节表现力和开发时间是成正比的.尽量给你的玩家最易于上手的GUI,但同时不要花时间做50种不同的控件.你要在功能、好事、复杂性、坏事中做出平衡. 

  控件就到这吧.下一章也是最后一章中我们会看看资源编辑器,序列化窗口和创建对话框.祝愉快! 
目录
相关文章
|
14天前
|
开发框架 Linux C语言
C、C++、boost、Qt在嵌入式系统开发中的使用
C、C++、boost、Qt在嵌入式系统开发中的使用
28 1
|
24天前
|
监控 C++
C++ Qt开发:QProcess进程管理模块
Qt是一个跨平台的C++图形库,简化了窗体应用开发,支持通过拖放组件提升效率。本章节关注`QProcess`组件,它用于控制和管理进程,例如执行命令、运行可执行文件及与外部进程通信。`QProcess`提供多种方法如`start`、`waitForStarted`和`waitForFinished`等,实现启动、监控和交互。示例展示了如何使用`QProcess`获取系统进程和信息,通过`tasklist`和`systeminfo`命令,并将结果展示在`QTreeWidget`中。
24 0
C++ Qt开发:QProcess进程管理模块
|
26天前
|
编译器 测试技术 API
C++库开发之道:实践和原则(三)
C++库开发之道:实践和原则
71 0
|
26天前
|
存储 缓存 安全
C++库开发之道:实践和原则(二)
C++库开发之道:实践和原则
45 0
|
26天前
|
安全 API C++
C++库开发之道:实践和原则(一)
C++库开发之道:实践和原则
45 0
|
27天前
|
存储 C++ 网络架构
C++ Qt开发:QUdpSocket实现组播通信
Qt教程:使用`QUdpSocket`实现UDP组播通信。通过设置套接字选项、绑定端口、加入和离开组播组,以及发送和接收数据报,简化跨平台窗体应用开发。关键函数包括`setSocketOption`设置多播TTL,`bind`绑定地址和端口,`joinMulticastGroup`加入组播,`leaveMulticastGroup`退出,`writeDatagram`发送,和`readDatagram`接收数据报。
16 1
C++ Qt开发:QUdpSocket实现组播通信
|
28天前
|
存储 网络安全 C++
C++ Qt开发:QUdpSocket网络通信组件
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用`QUdpSocket`组件实现基于UDP的网络通信功能。与`QTcpSocket`组件功能类似,`QUdpSocket`组件是 Qt 中用于实现用户数据报协议(UDP,User Datagram Protocol)通信的类。UDP 是一种无连接的、不可靠的数据传输协议,它不保证数据包的顺序和可靠性,但具有低延迟和简单的特点。
19 0
C++ Qt开发:QUdpSocket网络通信组件
|
18天前
|
存储 C++ 容器
C++入门指南:string类文档详细解析(非常经典,建议收藏)
C++入门指南:string类文档详细解析(非常经典,建议收藏)
31 0
|
18天前
|
存储 编译器 C语言
C++入门: 类和对象笔记总结(上)
C++入门: 类和对象笔记总结(上)
30 0
|
8天前
|
存储 算法 C语言
【C++初阶】8. STL初阶 + String类
【C++初阶】8. STL初阶 + String类
45 1