Here's as far as I got along this line of thinking.
I created a listbox item (DefaultWindow) in the .layout, which is then placed within the listbox. What's needed is a clone() function to create multiple instances of that window.
The ListboxItemWindow helper class has a getPixelSize() function (used by CEGUI::Listbox class) to return the dimensions of the listbox item (i.e. the window that I'm using). Since that window was designed (within the Layout Editor) using relative coordinates it had to be parented to the listbox and redimensioned; this happens within the setListboxItem() function.
The draw() function simply moves the window to the proper position within the Listbox. I'm not drawing nor recalling the image from the cache as the existing window drawing code is already taking care of that; all that's needed is to properly position the window. One difficulty is that the window can appear below the horizontal scrollbar that appears at the bottom of the Listbox.
There's a problem with widgets that respond to mouse clicks, such as a checkbox or a pushbutton; these will prevent the listbox from responding to a mouse click in order to highlight the selected listbox item. To palliate this I had those widgets subscribe to the onListboxItemWindowChildClicked() function when clicked. That function figures out who the parent window is (the window being used as a listbox item) and whether that list item is currently selected. If not then a call is made to the listbox to select it. To make this work I used the "text" of both the window and the list item.
This works but does not yield the result I wished for. To show the window as highlighted I set its alpha to zero and checked each child of that window to not inherit the alpha from its parent. This allows the highlight to pass through the empty region of the window. A better highlight would be blended on top of the window (and on top of every widget).
That's it for now. I'm not perfectly happy with the result so won't wiki this.
Code: Select all
#ifndef _ListboxItemWindow_Demo_h_
#define _ListboxItemWindow_Demo_h_
/* Notes:
Used a DefaultWindow as the base ListItem.
This base has an alpha of 0.0f, in order to show the highlight through.
On top of the base are various widgets, each set to NOT inherit the alpha.
*/
#include "CEGuiSample.h"
#include "CEGUI.h"
#include "CEGUIDefaultResourceProvider.h"
class ListboxItemWindow : public CEGUI::ListboxItem
{
public:
ListboxItemWindow(const CEGUI::String& text, CEGUI::uint item_id = 0, void* item_data = 0, bool disabled = false, bool auto_delete = true)
: ListboxItem(text, item_id, item_data, disabled, auto_delete),
d_window(0)
{
}
//----------------------------------------------
// Specify the window to use as a list item within a listbox
void setListboxItem(CEGUI::Window* listbox,
CEGUI::Window* window,
const CEGUI::UDim& height = CEGUI::UDim(0.0f, 20.0f),
const CEGUI::UDim& width = CEGUI::UDim(0.9f, 0.5f))
{
d_window = window;
// The window text must match the ListboxItem text in order to match the two together.
// Used to determine whether the window list item needs to be selected, when clicking
// on one of its children that consumes the listbox "selection" event i.e. clicking on
// the listbox.
d_window->setText(d_itemText);
// Adding the window as a child makes it easier to handle (ie always on top of the listbox)
listbox->addChildWindow( d_window->getName() );
// Redimention the window height.
d_window->setHeight(height);
// Redimention the window width.
d_window->setWidth(width);
}
//----------------------------------------------
// Return the dimension of this list item i.e. the window size
CEGUI::Size getPixelSize(void) const
{
CEGUI::Size size(0.0f, 0.0f);
if(d_window)
{
size = d_window->getPixelSize();
}
return size;
}
//----------------------------------------------
void draw(const CEGUI::Vector3& position, float alpha, const CEGUI::Rect& clipper) const
{
// Draw the window or, actually, move it to the proper location
if (d_window != 0)
{
CEGUI::UVector2 windowPos;
windowPos.d_x.d_scale = 0.0f;
windowPos.d_x.d_offset = position.d_x;
windowPos.d_y.d_scale = 0.0f;
windowPos.d_y.d_offset = position.d_y;
d_window->setPosition( windowPos );
}
// Draw the highlight
if (d_selected && (d_selectBrush != 0))
{
d_selectBrush->draw(clipper, position.d_z, clipper, getModulateAlphaColourRect(d_selectCols, alpha));
}
}
//----------------------------------------------
void draw(CEGUI::RenderCache& cache,const CEGUI::Rect& targetRect, float zBase, float alpha, const CEGUI::Rect* clipper) const
{
// Draw the window or, actually, move it to the proper location
if (d_window != 0)
{
CEGUI::UVector2 windowPos;
windowPos.d_x.d_scale = 0.0f;
windowPos.d_x.d_offset = targetRect.d_left;
windowPos.d_y.d_scale = 0.0f;
windowPos.d_y.d_offset = targetRect.d_top;
d_window->setPosition( windowPos );
}
// Draw the highlight
if (d_selected && d_selectBrush != 0)
{
cache.cacheImage(*d_selectBrush, targetRect, zBase, getModulateAlphaColourRect(d_selectCols, alpha), clipper);
}
}
private:
CEGUI::Window* d_window;
};
//-----------------------------------------------------------------------------
class DemoSample : public CEGuiSample
{
public:
bool initialiseSample()
{
using namespace CEGUI;
// The executable is stored within <cegui>/bin
// The following will change the location of the datafiles from their default of
// ../datafiles (which works well for samples) to <cegui>/samples/datafiles
DefaultResourceProvider* rp = reinterpret_cast<DefaultResourceProvider*>(System::getSingleton().getResourceProvider());
rp->setResourceGroupDirectory("fonts", "../samples/datafiles/fonts/");
rp->setResourceGroupDirectory("imagesets", "../samples/datafiles/imagesets/");
rp->setResourceGroupDirectory("layouts", "c:/programming/_Projects/CeguiTestBed/");
rp->setResourceGroupDirectory("looknfeels", "../samples/datafiles/looknfeel/");
rp->setResourceGroupDirectory("lua_scripts", "../samples/datafiles/lua_scripts/");
rp->setResourceGroupDirectory("schemes", "../samples/datafiles/schemes/");
Font::setDefaultResourceGroup("fonts");
Imageset::setDefaultResourceGroup("imagesets");
WindowManager::setDefaultResourceGroup("layouts");
WidgetLookManager::setDefaultResourceGroup("looknfeels");
ScriptModule::setDefaultResourceGroup("lua_scripts");
Scheme::setDefaultResourceGroup("schemes");
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");
FontManager::getSingleton().createFont("Commonwealth-10.font");
// Set the GUI Sheet
Window* sheet = winMgr.createWindow("DefaultWindow", "root_wnd");
System::getSingleton().setGUISheet(sheet);
// Load a layout
Window* guiLayout = winMgr.loadWindowLayout("ListItemWindow.layout");
sheet->addChildWindow(guiLayout);
/* Listbox */
Listbox* listbox = static_cast<Listbox*>(winMgr.getWindow("ListWindow/Listbox"));
listbox->setMultiselectEnabled(false);
ListboxTextItem* itemListbox = new ListboxTextItem("Value A", 1);
itemListbox->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
listbox->addItem(itemListbox);
itemListbox = new ListboxTextItem("Value B", 2);
itemListbox->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
listbox->addItem(itemListbox);
itemListbox = new ListboxTextItem("Value C", 3);
itemListbox->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
listbox->addItem(itemListbox);
itemListbox = new ListboxTextItem("Value D", 4);
itemListbox->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
listbox->addItem(itemListbox);
listbox->setItemSelectState(itemListbox, true);
listbox->ensureItemIsVisible(itemListbox);
uint valueListbox = listbox->getFirstSelectedItem()->getID(); // Retrieve the ID of the selected listbox item
ListboxItemWindow* listboxItemWindow;
listboxItemWindow = new ListboxItemWindow("Window List Item", 5);
ImagesetManager::getSingleton().createImagesetFromImageFile("ImageForStaticImage", "GPN-2000-001437.tga");
DefaultWindow* staticImage = static_cast<DefaultWindow*>(winMgr.getWindow("ListboxItem/Picture"));
staticImage->setProperty("Image", "set:ImageForStaticImage image:full_image"); // "full_image" is a default name from CEGUIImageset::Imageset()
Checkbox* checkbox = static_cast<Checkbox*>(winMgr.getWindow("ListboxItem/Checkbox"));
checkbox->subscribeEvent(CEGUI::Checkbox::EventCheckStateChanged, CEGUI::Event::Subscriber(&DemoSample::onListboxItemWindowChildClicked, this));
PushButton* pushbuttom = static_cast<PushButton*>(winMgr.getWindow("ListboxItem/PushButton"));
pushbuttom->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&DemoSample::onListboxItemWindowChildClicked, this));
listboxItemWindow->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
listboxItemWindow->setListboxItem(listbox, winMgr.getWindow("ListboxItem"), CEGUI::UDim(0.0f, 75.0f));
listbox->addItem(listboxItemWindow);
listbox->ensureItemIsVisible(listboxItemWindow);
listbox->setItemSelectState(listboxItemWindow, true);
}
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)
{
}
//----------------------------------------------
bool onListboxItemWindowChildClicked(const CEGUI::EventArgs& e)
{
// A child was clicked; forward this event to the listbox so that it can update
// its "list item selected" flag and properly highlight the list item.
CEGUI::Listbox* listbox = static_cast<CEGUI::Listbox*>(CEGUI::WindowManager::getSingleton().getWindow("ListWindow/Listbox"));
// The Listbox contains a series of CEGUI::Window as list items. The widget that
// generated the "selection" event can be either a direct child of the window list item
// or a grand-(potentially add many grands here)-child of that window.
// Find the immediate parent.
CEGUI::Window* grandParent;
CEGUI::Window* parent = static_cast<const CEGUI::WindowEventArgs&>(e).window->getParent();
while(parent)
{
// Check if there is a grand parent
grandParent = parent->getParent();
if(!grandParent)
{
// This should not have happened so exit this function.
parent = 0;
return true;
}
// If the grandparent is the Listbox then the parent is the window list item
if( grandParent->getName().compare( listbox->getName() ) == 0 )
{
// Check if the parent window is already selected
for(CEGUI::ListboxItem* selectedListboxItem = listbox->getFirstSelectedItem();
selectedListboxItem != 0;
selectedListboxItem = listbox->getNextSelected(selectedListboxItem))
{
if(selectedListboxItem->getText().compare( parent->getText() ) == 0)
{
// Parent is already selected
return true;
}
}
// If we reached here then the parent is unselected
CEGUI::ListboxItem* selectListboxItem = listbox->findItemWithText( parent->getText(), 0);
listbox->setItemSelectState(selectListboxItem, true);
return true;
}
// Find this parent's parent
parent = grandParent;
}
return true;
}
};
#endif // _ListboxItemWindow_Demo_h_
And the ListItemWindow.layout that goes with it:
Code: Select all
<?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="ListWindow" >
<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.001563,0},{0.1375,0},{0.995313,0},{0.416665,0}}" />
<Window Type="TaharezLook/Listbox" Name="ListWindow/Listbox" >
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.051572,0},{0.277293,0},{0.301572,0},{0.854388,0}}" />
</Window>
<Window Type="TaharezLook/Combobox" Name="ListWindow/Combobox" >
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.351572,0},{0.277293,0},{0.601572,0},{0.854388,0}}" />
<Property Name="MaxEditTextLength" Value="1073741823" />
</Window>
<Window Type="TaharezLook/MultiColumnList" Name="ListWindow/MultiColumnList" >
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.651572,0},{0.277293,0},{0.951572,0},{0.854388,0}}" />
</Window>
</Window>
<Window Type="TaharezLook/StaticText" Name="ListboxItem" >
<Property Name="Alpha" Value="0" />
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.03,0},{0.7,0},{0,200},{0,400}}" />
<Window Type="TaharezLook/StaticImage" Name="ListboxItem/Picture" >
<Property Name="InheritsAlpha" Value="False" />
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.05,0},{0.05,0},{0.33,0},{0.95,0}}" />
</Window>
<Window Type="TaharezLook/Checkbox" Name="ListboxItem/Checkbox" >
<Property Name="Text" Value="Check this box" />
<Property Name="InheritsAlpha" Value="False" />
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.35,0},{0.05,0},{0.95,0},{0,20}}" />
</Window>
<Window Type="TaharezLook/Button" Name="ListboxItem/PushButton" >
<Property Name="Text" Value="Click me!" />
<Property Name="InheritsAlpha" Value="False" />
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.35,0},{0,20},{0.95,0},{0,40}}" />
</Window>
<Window Type="TaharezLook/StaticText" Name="ListboxItem/Text" >
<Property Name="Text" Value="Some text" />
<Property Name="InheritsAlpha" Value="False" />
<Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
<Property Name="UnifiedAreaRect" Value="{{0.35,0},{0,40},{0.95,0},{0.95,0}}" />
</Window>
</Window>
</Window>
</GUILayout>