Game chat box
Contents
Introduction
This snippet implements a chat box that can display text in 3 font sizes as well as limiting the number of entries (history) to a certain number. Please discuss this article within the GameChatBox thread.
.layout Special Features
The layout definition is deceptively simple. It defines a FrameWindow containing two widgets; a Listbox in the upper region for chat history and an Editbox at the lower region for text input.
What is not apparent is how the bottom portion of the Listbox and the entire Editbox are defined; they specify a proportion of 100% of their parent but with a negative offset. Essentially the bottom of the Listbox extends to the bottom of the FrameWindow (the parent) and is then moved "up" to make room for the Editbox. And the Editbox follows the same pattern. This allows the Editbox to retain its height as the parent FrameWindow is resized.
Creating the necessary .font files
A .font file is a simple text file that defines a font within Cegui. They are located within the datafiles\fonts directory.
This snippet uses the Commonwealth font in sizes 10, 36, and 72. To create the missing font definitions for sizes 36 and 72 simply make two copies of the existing "Commonwealth-10.font" file. The first copy is to be named "Commonwealth-36.font" and the second copy "Commonwealth-72.font". Edit the "Commonwealth-36.font" file and replace the value "10" by the value "36". Similarly edit the "Commonwealth-72.font" file and replace the value "10" by the value "72".
Another approach is to dynamically create fonts. For more details consult DynamicFont
Files
ChatBox_demo.h
<cpp/>
- ifndef _ChatBox_h_
- define _ChatBox_h_
- include "CEGuiSample.h"
- include "CEGUI.h"
class DemoSample : public CEGuiSample { public:
bool initialiseSample()
{ using namespace CEGUI; try { // Retrieve the window manager WindowManager& winMgr = WindowManager::getSingleton();
// Load the TaharezLook scheme and set up the default mouse cursor and font SchemeManager::getSingleton().loadScheme("TaharezLook.scheme"); System::getSingleton().setDefaultMouseCursor("TaharezLook", "MouseArrow"); if(!FontManager::getSingleton().isFontPresent("Commonwealth-10")) FontManager::getSingleton().createFont("Commonwealth-10.font");
// Initialize the list of available fonts mCurrentFont = 0; addFont("Commonwealth-10"); addFont("Commonwealth-36"); addFont("Commonwealth-72");
// Set the GUI Sheet Window* sheet = winMgr.createWindow("DefaultWindow", "root_wnd"); System::getSingleton().setGUISheet(sheet);
// Load a layout Window* guiLayout = winMgr.loadWindowLayout("ChatBox.layout"); sheet->addChildWindow(guiLayout);
// Configure the events for the chat box CEGUI::Window* editBox = winMgr.getWindow("/ChatBox/Text"); editBox->subscribeEvent(CEGUI::Editbox::EventTextAccepted, CEGUI::Event::Subscriber(&DemoSample::EditTextAccepted, this)); editBox->subscribeEvent(CEGUI::Editbox::EventMouseClick, CEGUI::Event::Subscriber(&DemoSample::FontChange, this));
// Configure the history size mHistorySize = 100; CEGUI::Window* historyBox = winMgr.getWindow("/ChatBox/History"); historyBox->subscribeEvent(CEGUI::Editbox::EventTextAccepted, CEGUI::Event::Subscriber(&DemoSample::HistoryTextAccepted, this)); historyBox->setText( PropertyHelper::intToString(static_cast<int>(mHistorySize)) ); } catch(Exception &e) { #if defined( __WIN32__ ) || defined( _WIN32 ) MessageBox(NULL, e.getMessage().c_str(), "Error initializing the demo", MB_OK | MB_ICONERROR | MB_TASKMODAL); #else std::cerr << "Error initializing the demo:" << e.getMessage().c_str() << "\n"; #endif }
return true; }
void cleanupSample(void) { }
void setHistorySize(const size_t& pSize) { using namespace CEGUI;
if(pSize > 0) { // A better validation would be to enforce a minimal and a maximal size mHistorySize = pSize;
WindowManager& winMgr = WindowManager::getSingleton(); Listbox* listBox = static_cast<Listbox*> (winMgr.getWindow("/ChatBox/List")); ListboxItem* item; while(listBox->getItemCount() > mHistorySize) { item = listBox->getListboxItemFromIndex(0); listBox->removeItem(item); } } }
bool HistoryTextAccepted(const CEGUI::EventArgs& args) { using namespace CEGUI;
WindowManager& winMgr = WindowManager::getSingleton(); CEGUI::Window* historyBox = winMgr.getWindow("/ChatBox/History"); int historySize = PropertyHelper::stringToInt( historyBox->getText() ); setHistorySize(historySize); return true; }
bool EditTextAccepted(const CEGUI::EventArgs& args) { using namespace CEGUI;
// Variables for the Listbox and the Editbox WindowManager& winMgr = WindowManager::getSingleton(); Editbox* editBox = static_cast<Editbox*> (winMgr.getWindow("/ChatBox/Text")); Listbox* listBox = static_cast<Listbox*> (winMgr.getWindow("/ChatBox/List"));
// Add the Editbox text to the Listbox ListboxTextItem* textItem; if(listBox->getItemCount() == mHistorySize) { /* We have reached the capacity of the Listbox so re-use the first Listbox item. This code is a little crafty. By default the ListboxTextItem is created with the auto-delete flag set to true, which results in its automatic deletion when removed from the Listbox. So we change that flag to false, extract the item from the Listbox, change its text, put the auto-delete flag back to true, and finally put the item back into the Listbox. */ textItem = static_cast<ListboxTextItem*>(listBox->getListboxItemFromIndex(0)); textItem->setAutoDeleted(false); listBox->removeItem(textItem); textItem->setAutoDeleted(true); textItem->setText(editBox->getText()); } else { // Create a new listbox item textItem = new ListboxTextItem(editBox->getText()); } listBox->addItem(textItem); listBox->setItemSelectState(textItem, true);
// Scroll the Listbox entries such that the new text is visible listBox->ensureItemIsVisible(listBox->getItemCount());
// Clear the text in the Editbox editBox->setText("");
return true; }
void addFont(const CEGUI::String& pFont) { using namespace CEGUI;
if(!FontManager::getSingleton().isFontPresent(pFont)) { mFontList.push_back(pFont); FontManager::getSingleton().createFont(pFont + ".font"); } }
bool FontChange(const CEGUI::EventArgs& args) { using namespace CEGUI; const MouseEventArgs& mouseEventArgs = static_cast<const MouseEventArgs&>(args); if(mouseEventArgs.button == RightButton) { // Cycle through the installed fonts upon a right-click mCurrentFont++; if(mCurrentFont >= mFontList.size()) mCurrentFont = 0;
changeFont(mFontList[mCurrentFont]); }
return true; }
void changeFont(const CEGUI::String& pFont) { using namespace CEGUI; WindowManager& winMgr = WindowManager::getSingleton();
// Measure the height of the selected font Font* currentFont = FontManager::getSingleton().getFont(pFont); float fontHeight = currentFont->getFontHeight();
/* Alter the area of the Editbox. The original value is {{0.01,0},{1,-30},{0.99,0},{1,-5}} The value we are altering is the "-30" within the second couplet, defining the position of the upper y coordinate of the Editbox. We base the new value on the position of the lower y coordinate, which is "-5", and the height of the font. To this we add some space "10" to account for the Editbox's border. */ Editbox* editBox = static_cast<Editbox*> (winMgr.getWindow("/ChatBox/Text")); URect chatTextArea = editBox->getArea(); chatTextArea.d_min.d_y.d_offset = chatTextArea.d_max.d_y.d_offset - fontHeight - 10; editBox->setArea(chatTextArea); editBox->setFont(currentFont);
/* Alther the area of the Listbox. Here we only need the lower y coordinate. Since this value is the same as the upper y coordinate of the Editbox we do not need to calculate it. We also change the font of the Listbox and call upon handleUpdatedItemData() to update the current Listbox items. Finally we ensure that the last entry is still visible. */ Listbox* listBox = static_cast<Listbox*> (winMgr.getWindow("/ChatBox/List")); URect listTextArea = listBox->getArea(); listTextArea.d_max.d_y.d_offset = chatTextArea.d_min.d_y.d_offset; listBox->setArea(listTextArea); listBox->setFont(currentFont); listBox->handleUpdatedItemData(); size_t itemCount = listBox->getItemCount(); if(itemCount) { ListboxItem* currentItem = listBox->getListboxItemFromIndex(itemCount - 1); listBox->ensureItemIsVisible(currentItem); } }
private: // List of available fonts std::vector<CEGUI::String> mFontList;
// Currently active font std::vector<CEGUI::String>::size_type mCurrentFont;
// Maximal number of entries to retain within the Listbox size_t mHistorySize; };
- endif // _ChatBox_h_
Main.cpp
<cpp/>
- if defined( __WIN32__ ) || defined( _WIN32 )
#define WIN32_LEAN_AND_MEAN #define NOMINMAX #include "windows.h"
- endif
- include "ChatBox_demo.h"
- if defined( __WIN32__ ) || defined( _WIN32 )
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
- else
int main(int argc, char *argv[])
- endif
{
DemoSample app; int i = app.run(); return i;
}
ChatBox.layout
<?xml version="1.0" encoding="UTF-8"?> <GUILayout > <Window Type="DefaultWindow" Name="Root" > <Property Name="InheritsAlpha" Value="False" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{1,0},{1,0}}" /> <Window Type="TaharezLook/FrameWindow" Name="/ChatBox" > <Property Name="Text" Value="Chat" /> <Property Name="TitlebarFont" Value="Commonwealth-10" /> <Property Name="InheritsAlpha" Value="False" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="TitlebarEnabled" Value="True" /> <Property Name="UnifiedAreaRect" Value="{{0.15,0},{0.03,0},{0.7,0},{0.7,0}}" /> <Window Type="TaharezLook/Listbox" Name="/ChatBox/List" > <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.01,0},{0.078,0},{0.99,0},{1,-30}}" /> </Window> <Window Type="TaharezLook/Editbox" Name="/ChatBox/Text" > <Property Name="MaxTextLength" Value="1073741823" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.01,0},{1,-30},{0.99,0},{1,-5}}" /> </Window> </Window> <Window Type="TaharezLook/Editbox" Name="/ChatBox/History" > <Property Name="MaxTextLength" Value="1073741823" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.9,0},{0.03,0},{0.95,0},{0.1,0}}" /> </Window> <Window Type="TaharezLook/StaticText" Name="/ChatBox/HistoryLabel" > <Property Name="Text" Value="History size:" /> <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" /> <Property Name="UnifiedAreaRect" Value="{{0.78,0},{0.03,0},{0.9,0},{0.1,0}}" /> </Window> </Window> </GUILayout>