QGUI w/ static Ogre, and 0.9.6 bug reports/fixes

Jekteir

12-08-2007 03:38:57

Hiya, I'm not sure if this is a problem in my project setup (I've just finished triple-checking it) or something to do with the way QuickGUI works as a DLL, so I figured I'd post it on here.

I'm building my project right now with Ogre as a static library, that gets linked into QuickGUI as needed, as well as into my project's .exe. But I'm still building QuickGUI as a DLL and using the .lib supplied during the DLL build to link it into my executable. I've checked that the debug DLL is built as _d and goes in the debug dir, and vice versa with the release, and that the right .lib is called for each sort of build for my client's .exe.

However, just into the load of QuickGUI, when it creates the cursor with:

mMouseCursor = new MouseCursor(Ogre::Vector2(0.05,0.05),"qgui.pointer.png",mRenderWindowWidth,mRenderWindowHeight);

the stack goes to:

mQuad = new Quad("MouseCursor.Quad");

and

Quad::Quad(const Ogre::String& id) : RenderObject(id)

and (in RenderObject::RenderObject)

mRenderSystem = Ogre::Root::getSingleton().getRenderSystem();

and (in template<> Root* Singleton<Root>::ms_Singleton = 0;)

assert( ms_Singleton ); return ( *ms_Singleton );

Now, I know the ms_singleton assert is meant to happen when the wrong sort of DLL gets loaded with the wrong sort of Ogre version, etc; but I'm pretty darn sure I haven't done that. Is there any chance that I need to treat your DLL differently since I'm building Ogre static into my app?

Thanks (and looking forward to seeing how 0.9.6 runs if this ever works ; ),

Jek

kungfoomasta

12-08-2007 03:55:57

I dont' know a whole lot about the underlying implementation behind Singletons, but as far as I know it should be treated the same as every other Singleton. Make sure root is initialized before you create the GUIManager, otherwise the RenderSystem won't be loaded when you try to access it.

Also, are you able to build the demo without problems?

fassihi

12-08-2007 05:29:09

Yes the assert happens if you try to access a singleton which is not initialized.. you would have to do the initialization before accessing the singleton object.

Jekteir

12-08-2007 13:01:03

Yeah, root was definitely created long before, and this is my code with the assert:

guiMgr = new QuickGUI::GUIManager(window->getWidth(),window->getHeight());

So that's initializing the GUI mgr when the assert happens. That gets called in the constructor of a class which is created long after root has started running -- as evidenced by the fact that I'm getting the window width + height of an Ogre renderwindow... Xavier's book reads:

By default, Ogre does not name differently the Debug and Release builds of its plug-ins; this is a very common source of confusion, and the main culprit for the infamous “ms_singletonâ€

Jekteir

12-08-2007 14:38:48

Good news, I build QuickGUI static as well and it all plays together nicely now. (Well... now it's dying on a 'material' extension for what's meant to be a texture -- but that'll just be because I haven't fixed my skin to the newest version.) I guess you just can't mix static and non-static libs/plugins with Ogre.

Jek

Jekteir

12-08-2007 23:16:34

Hey, this may well be gone from the release (I got mine from SVN) but if not, this crashes it right now:

// Following lines are unnecessary... just puts it out to a file to see :-)
Ogre::Image finalImageSet;
finalImageSet.load("temp.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
buf->blitToMemory(finalImageSet.getPixelBox());
finalImageSet.save("SkinSetImage.png");


Can obviously just be removed, though.

My old 'qgui.material' files, which were still around, also crashed it when it tried to load all files as textures that had 'qgui' at the start of the name.

Jek

Jekteir

12-08-2007 23:37:12

Got another crash, apparently when disabling a menulist. It believes the widget being disabled has the texture "qgui.menulist.list.button.png", but the resource manager says it doesn't and throws an exception.

Jekteir

12-08-2007 23:52:18

Yeah, it's because of the constructor of MenuList, which has texture.find_last_of('.'))+".button.png"; but there's no .button.png in my textures. It also gives the list a '.list.list.png' texture at one stage, which again doesn't exist in the default qgui textures...

Jekteir

13-08-2007 00:15:21

Disabling a listitem also crashes QGUI, because of the constructor of listitem which defines an empty texture:

ListItem::ListItem(const Ogre::String& name, const Ogre::Vector4& dimensions, GuiMetricsMode positionMode, GuiMetricsMode sizeMode, RenderObjectGroup* group, Widget* ParentWidget) :
Label(name,dimensions,positionMode,sizeMode,"",group,ParentWidget),

Jekteir

13-08-2007 00:29:54

I'm also getting an error with textbox usage... If I use addCharacter() a few times, the characters each appear one before the other, rather than after. Then, if I hit backspace and run backSpace(), it crashes on calling the UTFString erase() function -- presumably because it thinks the cursor isn't at the beginning of the line, even though it is, or something?

Jek

Jekteir

13-08-2007 00:43:59

Sticking this at my creation of QuickGUI means that none of my text gets displayed:

guiMgr->setDefaultFont("vibrocen");
guiSheet->setDefaultFont("vibrocen");


(also, guiSheet's setDefaultFont func appears irrelevant -- I think? -- since the text widget checks the mgr's default font, not the sheet's).

I know it isn't the font, because the font worked with the last version of quickGUI, so I'm assuming something else has changed.

Also, 2 of my buttons did have text, in the wrong font I believe -- suggesting that maybe not all of the buttons' fonts are being set correctly?

Jek

kungfoomasta

13-08-2007 02:14:07

Ah, I need to have the Text class get the default font. :oops:

What way seems more intuitive/desirable? Having Sheet store a default font, or the GUIManager?

The way fonts work there is no stretching, which means for good appearance, you will most likely be setting fonts for every text widget you create. Because of this, I think GUIManager should be the only class with a default font setting.

I recommend making a few fonts in your *.fontdef file:

vibrocen.12
vibrocen.14
etc.

For each differing size fonts. If a text widget shows no text, the most likely cause is that the Text size is to big for the widget. Remember, I do not stretch the text, it is identical to the glyph rendered by FreeType library. :wink:

kungfoomasta

13-08-2007 03:48:35

Ok, I have made some changes. DefaultFont is a property of Sheet Widget, not GUIManager. I did this for consistency with defaultskin and default text color. Text obtains the default font and text color inside its constructor. The changes will be in the v0.9.6 release.

Jekteir

13-08-2007 14:04:20

Heya, thanks, that text stuff looks good.

looks like this:

/**
* Sets the Size of the mouse cursor on the screen, in pixel dimensions.
*/
void setSize(const int& widthInPixels, const int& heightInPixels);


actually sets it in percentage terms of the screensize. Could we get some relative/absolute pixel arguments for this please?

Jekteir

13-08-2007 14:10:43

I tried to get it to work in absolute pixels terms by multiply my width argument by the screenwidth, and the height arg by the height, but when the setSize() code calls mQuad->setSize(), you get this code:

mAbsoluteDimensions.z = absoluteSize.x;
mAbsoluteDimensions.w = absoluteSize.y;


which seems to be changing the cursor from '0.5' width and height to my pixel sizes that I passed, '14' and '21', but they are so big that the mouse becomes the size of the whole screen. But, when I originally passed the unmodified sizing args 14 and 21 to setSize(), the mouse size was smaller than I could see.

Jekteir

13-08-2007 14:16:17

I tried passing '650' and '450' to the setSize(), func, but although that should have created the right sort of vals (0.6-ish), it looks like they got truncated:

void MouseCursor::setSize(const int& widthInPixels, const int& heightInPixels)
{
mAbsoluteSize.x = widthInPixels / mRenderWidthInPixels;
mAbsoluteSize.y = heightInPixels / mRenderHeightInPixels;
mQuad->setSize(mAbsoluteSize);
}


I'm assuming this is because they are both ints. Perhaps if you cast one/both of them to Real before storing the vals in the vector?

Jekteir

13-08-2007 14:21:25

This code works:

mAbsoluteSize.x = Ogre::Real(widthInPixels) / mRenderWidthInPixels;
mAbsoluteSize.y = Ogre::Real(heightInPixels) / mRenderHeightInPixels;


I got a decent size in the end when I passed about setSize(35,46).

Jekteir

13-08-2007 14:35:49

I don't know how easy logically this is to do, but right now I'm disabling all my widgets at various stages, which means I've had to set my menulist items to have the texture "qgui.menulist.png". That's OK, except it creates little lines between menu items and seems to interfere with the sort of fade at the left of my menu. However, if I set it to "transparent.png" (so that the normal menu texture underneath can be visible all the way across the menu), the mouseover texture-change stops working (and the actual appearance of the list below it doesn't happen any more); and when I disable it I get an 'cannot find texture transparent.over.png' (unsurprisingly). Is there any kind of 'transparent' texture option you could actually create in code, so that anything that is set transparent, when it's disabled, doesn't have its texture altered (in the assumption that the texture underneath will be disabled, when the code disables it)? Or, something... I just think the layering logic breaks down a bit here. Dunno how that would work for the texture changes for mouseover, though -- maybe if there were ->setDisabledTexture(), and ->setMouseOverTexture) functions? So you could have something like, ->setTexture(QGUI_TEXTURE_TRANSPARENT); and setMouseOverTexture->("qgui.menulist.over"), and ->setDisabledTexture("qgui.menulist.disabled")?


Jek

Jekteir

13-08-2007 14:51:23

I've also added this code to my copy of ListItem::onMouseEnters, Button::onMouseEnters, onMouseLeaves etc:

this->getText()->setColor(Ogre::ColourValue(0,0,0,1));

and similar.

Perhaps it would be worth adding a generic setMouseOverTextColor() function, that changes a widget's text widget's text color for when the mouse is over an item, and returns it to default when not? Yes, this could all be done with widget events, but creating a function for every widget to handle text color change seems like a big fix for a small problem; and while everyone could add this stuff into QuickGUI's onMouseEnters events themselves too, it's probably better if people don't have to mod the QuickGUI source too much themselves : )


Jek

Jekteir

13-08-2007 15:04:16

There seems to be something a little up with text too, insofar as, when I had some text that wouldn't quite fit on a titlebar, I made the titlebar wider, at which point the text that was there actually spaced itself out further, and so still wouldn't fit on the titlebar! It also looks a bit to me as though the text I have ought to fit with the current spacing anyway -- is it possible that dimensions-checker is being a little conservative with the available space/overestimating the glyph width? This titlebar is meant to read, "Exit, discarding unsaved work?" :





Jek

Jekteir

13-08-2007 15:06:51

Also, it looks like borders cannot be turned off? I notice my buttons have borders now. I think that I may not need my MenuList textures to be set transparent, if the border lines weren't around them (except that having them have their own texture does cover over the nice menu-end fade on the left-hand side).

I see this code is commented, so maybe this is an intended feature?:

// mBordersHidden(0),

Anyway, I'm sure I could set all the borders to off (effectively) by setting border width for all 4 sides to 0 for each widget, but again -- a simple function guiMgr->enableBorders(false) might make more sense.

Jek

Jekteir

13-08-2007 15:22:19

Actually doing setBorderwhatsits all to 0 didn't seem to remove the borders.

Jek

Jekteir

13-08-2007 16:01:01

In terms of enabling and disabling widgets, and the texture changes, I noticed that a) the textures didn't seem to be changing quite correctly and b) there was no change back for enabling. This code fix doesn't quite work (the gamma change cannot be seen for some reason -- but, then, it could not be seen before either) but I think it might be a bit better, because it correctly resets the texture afterwards:

void Widget::disable()
{
if( (mWidgetType == Widget::TYPE_TEXTCURSOR) ||
(mWidgetType == Widget::TYPE_SHEET)) return;

mEnabled = false;
Ogre::TexturePtr tp = static_cast<Ogre::TexturePtr>(Ogre::TextureManager::getSingleton().getByName(mWidgetTextureName));

Ogre::TexturePtr disabledTexture;
Ogre::String disabledTextureName = mWidgetTextureName.substr(0,mWidgetTextureName.find_last_of("."))+".disabled.png";

if(Ogre::TextureManager::getSingleton().resourceExists(disabledTextureName))
disabledTexture = static_cast<Ogre::TexturePtr>(Ogre::TextureManager::getSingleton().getByName(disabledTextureName));
else
{
disabledTexture = static_cast<Ogre::TexturePtr>(Ogre::TextureManager::getSingleton().create(disabledTextureName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME));
disabledTexture->loadImage(Ogre::Image().load(mWidgetTextureName,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME));

// For now, disabled widgets just appear darker than normal.
disabledTexture->setGamma(0.0);
/*
Ogre::Pass* p = disabledMaterial->getTechnique(0)->getPass(0);

if( (p != NULL) && (p->getNumTextureUnitStates() >= 1) )
{
Ogre::TextureUnitState* tus = p->createTextureUnitState();
tus->setColourOperationEx(Ogre::LBX_MODULATE,Ogre::LBS_MANUAL,Ogre::LBS_CURRENT,mDisabledColor);
}
*/
}

// setTextColor(mDisabledColor);
this->setTexture(disabledTextureName);
//mRenderObject->setTexture(mWidgetTextureName+".disabled");
}


and for enable:

void Widget::enable()
{
if( (mWidgetType == Widget::TYPE_TEXTCURSOR) ||
(mWidgetType == Widget::TYPE_SHEET)) return;

mEnabled = true;
//mRenderObject->setTexture(mWidgetTextureName);

Ogre::String enabledTexture = mWidgetTextureName.substr(0,mWidgetTextureName.find(".disabled"))+".png";
this->setTexture(enabledTexture);
// setTextColor(mTextTopColor,mTextBotColor);
//mOverlayElement->setMaterialName(mWidgetTextureName);
}

Jekteir

13-08-2007 16:25:49

The 'highlight listitem' wasn't working for me (it was merging the highlight on top of my listitem texture, so that it all became grey, instead of white on black) -- so I changed my code to handle the mouseEnters like buttons do instead, and completely replace the texture:

bool ListItem::onMouseEnters(MouseEventArgs& e)
{
if(!mEnabled) return e.handled;

e.widget = this;
List* parentList = dynamic_cast<List*>(mParentWidget);
//parentList->highlightListItem(this);
this->setTexture(mWidgetTextureName.substr(0,mWidgetTextureName.find_last_of("."))+".highlight.png");
this->getText()->setColor(Ogre::ColourValue(0,0,0,1));

return Label::onMouseEnters(e);
}


and

bool ListItem::onMouseLeaves(MouseEventArgs& e)
{
if(!mEnabled) return e.handled;

if(getTargetWidget(e.position) != NULL)
return e.handled;

e.widget = this;
List* parentList = dynamic_cast<List*>(mParentWidget);

//parentList->hideHighlight();
this->setTexture(mWidgetTextureName.substr(0,mWidgetTextureName.find(".highlight"))+".png");
this->getText()->setColor(Ogre::ColourValue(1,1,1,1));

return Label::onMouseLeaves(e);
}


This creates quite a nice effect on my settings:



Incidentally, this is what my 'disabled' gamma looks like: everything on this page has been disabled except for the central window. (Incidentally again, in the central window you can see that there appear to be grey borders around my buttons, whose white borders are meant to be at their edges -- unless this is because I have a little .png transparency around the edges of my buttons?)



Jek

Jekteir

13-08-2007 16:29:44

P.S. I think I know why the text is not displaying on the titlebar of that window -- it is probably compensating for the close() button that it expects to be in the way. But, I have hidden that... So ideally, it would not be doing that : )


Jek

Jekteir

13-08-2007 17:08:45

I have no idea quite how good this fix is, but I stopped the text from screwing up by changing the end of int Text::getTextCursorIndex(const Ogre::Vector2& absPosition)

to:

// should never make it here
return textIndex;


-- because it was making it there! And returning 0 was just putting the cursor at the beginning of the line. Could it be that your for loop always leaves the last correct index value in the variable when the cursor is at the end of the line?

Jek

Jekteir

13-08-2007 18:16:01

The reason my getTextCursorIndex() always ends on 'shouldn't make it to here' is because the y co-ords of my cursor are never within the bounds of any of the characters (so it always iterates through all the indices). Are the y co-ords for this measured from the top or bottom of the screen? I notice you're adding half the height of the cursor to the y-cords of the position for use with isPointWithinBounds(), but that, in my case, isn't quite enough - it's 0.05 off or something.

Jek

Jekteir

13-08-2007 19:05:13

I have committed a new version of the cursor-getting function (a copy, getTextCursorIndex2, leaving the other intact) and a new version of isPointWithinBounds called isCursorWithinBounds. These work for me. I modified isCursorWithinBounds() so that, once it knows that the cursor's x position is within the width of the glyph, it checks if the top or the bottom of the glyph are within the whole line of the cursor -- rather than the original, which checked if a specific midpoint of the cursor was within the height of the glyph. This should work better for small characters et cetera. I modified addCharacter() et cetera to use it.

I also added 2 public functions to TextBox - cursorForward() and cursorBack(), which just push the cursor one char forward or one char back, so that users can control right/left-arrow movements themselves if they wish (as I did : ). It has bounds checking.

Jek

Jekteir

13-08-2007 19:06:41

It's a niggling problem, but the cursor is never visible for me. My cursor .png is intact, but even when I'm adding text I can't see the cursor. It's definitely running the setVisible code etc. Any ideas?

P.S. This was true before my modifications, I didn't screw this up ; )

Jek

kungfoomasta

13-08-2007 20:28:05

Wow, lots and lots of feedback.. nice! :)

Hard to keep up with all your posts..

There is a mTextBoundsAbsoluteDimensions which is used as the area where text can be aligned/displayed. If characters do not fit the space, they are not drawn. When hiding the close button, I need to resize textbounds size to take that extra space into account. (DONE)

I will speak to you on messenger regarding the other issues. Thanks for pointing these out!

kungfoomasta

13-08-2007 20:29:42

You need to inject time to get the text cursor working properly.

Add something similar to this:


bool frameStarted(const FrameEvent& evt)
{
QuickGUI::GUIManager* gm = QuickGUI::GUIManager::getSingletonPtr();
if( gm != NULL ) gm->injectTime(evt.timeSinceLastFrame);

Danno

14-09-2007 18:02:19

I've just started playing around with the GUI system, and I noticed something in the Demo app. The one button associated with the "Click Me" text field has a "hit region" issue. When it starts out on the base, which is a black square with rounded edges, I can only click the actual edges. Not being able to click within the square seems counter intuitive. It seems to only happen with the black edges.

kungfoomasta

14-09-2007 19:23:12

Yah, I need to update the skin. Actually, what you are seeing is a feature, only non transparent regions detect mouse events. A simple fix would be to edit the Image file, and make the inside slightly non transparent. If you are familiar with pixel values, the channels can support values from 0 - 255. Setting the pixel's alpha channel to 1 is enough to support detection.

Using this feature, you can create odd shaped buttons or widgets, and the mouse will only *be over* the widget when it is over the non-transparent area of the widgets image. Think of a circle of oval shaped button.

Thanks for posting this up. Despite my recent job I'm still working hard towards the next release. ScrollBars are almost implemented, but the actual scrolling itself is still in infancy. (Although I already have a solution, just need to implement it)

Danno

14-09-2007 20:09:11

No problem.

I am just sifting through the demo and the headers while I compile a test GUI. I just noticed that, and I had figured that it was just working on the transparency values, too. Good to see that I wasn't far off :D . However, I wasn't sure if this was intentional or not. Now, I know :)

Hey, keep up the good work! Do what you can when you can :) Me personally? Working part time and doing grad school full time concurrently, hardly any free time.