[CODE] Resizing Gui and fonts

Crystal Hammer

21-08-2014 18:56:28

Ok so, this is our code that does iterate all widgets and resizes them to have the same view size on different screen resolutions.
It first saves original pos and sizes so it can be used (in setToolTips).
In editor .layout windows are for 800x600.

Same is done for fonts, but yeah they will be a bit different at various sizes. Still does the job.
Is a bit buggy, but I hope you can use it and maybe even improve it or merge into MyGUI, would be cool.

In the Gui init code we have:

// common
using namespace MyGUI;
using namespace Ogre;
using namespace std;

// class members
CGuiCom* gcom;
App* app;

MyGUI::Gui* mGui;
//MyGUI::VectorWidgetPtr
std::vector<Widget*> vwGui; // all widgets to destroy

Widget* mToolTip; EditBox* mToolTipTxt;


// in Gui Init code

// load
app->vwGui = LayoutManager::getInstance().loadLayout("Editor.layout");

gcom->GuiInitTooltip(); // this sets original pos and size strings in widgets


// then in frameStarted
// bWindowResized is true at app start and
// after event virtual void windowResized (int x, int y);

if (bWindowResized)
{ bWindowResized = false;

gcom->SizeGUI();
//.. other custom, lists resizing code
}
}


And those are the methods that take care of all that

void CGuiCom::SizeGUI()
{
// call recursive method for all root widgets
for (VectorWidgetPtr::iterator it = app->vwGui.begin(); it != app->vwGui.end(); ++it)
doSizeGUI((*it)->getEnumerator());
}

void CGuiCom::doSizeGUI(EnumeratorWidgetPtr widgets)
{
while (widgets.next())
{
Widget* wp = widgets.current();
string relativeTo = wp->getUserString("RelativeTo");

if (relativeTo != "")
{
// position & size relative to the widget specified in "RelativeTo" property (or full screen)
IntSize relSize;
if (relativeTo == "Screen")
relSize = IntSize(app->mWindow->getWidth(), app->mWindow->getHeight());
else
{ Widget* window = mGui->findWidget<Widget>(relativeTo);
relSize = window->getSize(); }

// retrieve original size & pos
IntPoint origPos; IntSize origSize;
#define s2i(s) Ogre::StringConverter::parseInt(s)
origPos.left = s2i(wp->getUserString("origPosX"));
origPos.top = s2i(wp->getUserString("origPosY"));
origSize.width = s2i(wp->getUserString("origSizeX"));
origSize.height = s2i(wp->getUserString("origSizeY"));

// calc & apply new size & pos
float sx = relSize.width / 800.f, sy = relSize.height / 600.f;
wp->setPosition(IntPoint( int(origPos.left * sx), int(origPos.top * sy) ));
wp->setSize(IntSize( int(origSize.width * sx), int(origSize.height * sy) ));
}

doSizeGUI(wp->getEnumerator());
}
}

// used only for manually created widgets
void CGuiCom::setOrigPos(Widget* wp, const char* relToWnd)
{
if (!wp) return;
#define toStr(v) Ogre::StringConverter::toString(v)
wp->setUserString("origPosX", toStr(wp->getPosition().left));
wp->setUserString("origPosY", toStr(wp->getPosition().top));
wp->setUserString("origSizeX", toStr(wp->getSize().width));
wp->setUserString("origSizeY", toStr(wp->getSize().height));
wp->setUserString("RelativeTo", relToWnd);
}


/// Tooltips
//----------------------------------------------------------------------------------------------------------------
void CGuiCom::GuiInitTooltip()
{
mToolTip = mGui->findWidget<Widget>("ToolTip");
mToolTip->setVisible(false);
mToolTipTxt = mToolTip->getChildAt(0)->castType<Edit>();

for (VectorWidgetPtr::iterator it = app->vwGui.begin(); it != app->vwGui.end(); ++it)
setToolTips((*it)->getEnumerator());
}

void CGuiCom::setToolTips(EnumeratorWidgetPtr widgets)
{
while (widgets.next())
{
Widget* wp = widgets.current();
wp->setAlign(Align::Default);

IntPoint origPos = wp->getPosition();
IntSize origSize = wp->getSize();

wp->setUserString("origPosX", toStr(origPos.left));
wp->setUserString("origPosY", toStr(origPos.top));
wp->setUserString("origSizeX", toStr(origSize.width));
wp->setUserString("origSizeY", toStr(origSize.height));

// find parent window
Widget* p = wp->getParent();
while (p)
{
if (p->getTypeName() == "Window")
{
if (p->getUserString("NotSized").empty())
wp->setUserString("RelativeTo", p->getName());
break;
}
p = p->getParent();
}

bool tip = wp->isUserString("tip");
if (tip) // if has tooltip string
{
// needed for translation
wp->setUserString("tip", LanguageManager::getInstance().replaceTags(wp->getUserString("tip")));
wp->setNeedToolTip(true);
wp->eventToolTip += newDelegate(this, &CGuiCom::notifyToolTip);
}
setToolTips(wp->getEnumerator());
}
}

void CGuiCom::notifyToolTip(Widget* wp, const ToolTipInfo &info)
{
if (!mToolTip) return;

if (!app->bGuiFocus)
{ mToolTip->setVisible(false);
return;
}
if (info.type == ToolTipInfo::Show)
{
mToolTip->setSize(320, 128); // start size for wrap
mToolTipTxt->setSize(320, 128);

#define TR(s) MyGUI::LanguageManager::getInstance().replaceTags(s)
String s = TR(wp->getUserString("tip"));
mToolTipTxt->setCaption(s);
const IntSize &si = mToolTipTxt->getTextSize();

mToolTip->setSize(si.width +8, si.height +8);
mToolTipTxt->setSize(si.width, si.height);
mToolTip->setVisible(true);
boundedMove(mToolTip, info.point);
}
else if (info.type == ToolTipInfo::Hide)
mToolTip->setVisible(false);
}

// Move a widget to a point while making it stay in the viewport.
void CGuiCom::boundedMove(Widget* moving, const IntPoint& point)
{
const IntPoint offset(20, 20); // mouse cursor
IntPoint p = point + offset;

const IntSize& size = moving->getSize();

int w = app->mWindow->getWidth();
int h = app->mWindow->getHeight();

if (p.left + size.width > w) p.left = w - size.width;
if (p.top + size.height > h) p.top = h - size.height;

moving->setPosition(p);
}



Here is the method that creates the fonts (size based on screen height, char range based on other font's char range, found in xml).


void CGuiCom::CreateFonts()
{
MyGUI::ResourceManager& mgr = MyGUI::ResourceManager::getInstance();
MyGUI::IResource* resource = mgr.findByName("hud.text"); // based on this font - just for code ranges
MyGUI::ResourceTrueTypeFont* bfont = resource != nullptr ? resource->castType<MyGUI::ResourceTrueTypeFont>(false) : 0;
if (!bfont) LogO("Error !! Can't find font: hud.text");

const int cnt = 3;
string names[cnt] = {"font.small","font.normal","font.big"};
float sizes[cnt] = {26.f, 30.f, 34.f}; // par

for (int i=0; i < cnt; ++i)
{
// del old
const string name = names;
if (mgr.isExist(name))
mgr.removeByName(name);

// setup font // par
float size = sizes * (1.f - 1.5f * (GetGuiMargin(2000) - GetGuiMargin(pSet->windowy)));
LogO("-- "+name+" size: "+fToStr(size,2,4));

// create
#if 0 // mygui from svn
string cat = mgr.getCategoryName(); // createObject("Resource", "ResourceTrueTypeFont"));
ResourceTrueTypeFont* font = FactoryManager::getInstance().createObject<ResourceTrueTypeFont>(cat);
font->setResourceName(name);

font->setSource("DejaVuLGCSans.ttf");
font->setSize(size); font->setResolution(50); font->setAntialias(false); //font->setHinting("");
font->setTabWidth(8); font->setDistance(4); font->setOffsetHeight(0);
//font->setSubstituteCode(_data->getPropertyValue<int>("SubstituteCode"));

// char ranges
if (bfont)
{ const std::vector<pair<Char, Char> >& vv = bfont->getCodePointRanges();
for (std::vector<pair<Char, Char> >::const_iterator it = vv.begin(); it != vv.end(); ++it)
if ((*it).first > 10 && (*it).first < 10000)
font->addCodePointRange((*it).first, (*it).second);
}else
font->addCodePointRange(33,255);

font->initialise();
#else
ResourceTrueTypeFont* font = (ResourceTrueTypeFont*)FactoryManager::getInstance().createObject("Resource", "ResourceTrueTypeFont");

// Loading from XML, data members are private in MyGUI 3.2.0
xml::Document doc;
xml::ElementPtr root = doc.createRoot("ResourceTrueTypeFont"), e;
root->addAttribute("name", name);

#define AddE(key, val) e = root->createChild("Property"); e->addAttribute("key", key); e->addAttribute("value", val)
AddE("Source", "DejaVuLGCSans.ttf");
AddE("Size", toStr(size)); AddE("Resolution", "50"); AddE("Antialias", "false");
AddE("TabWidth", "8"); AddE("Distance", "4"); AddE("OffsetHeight", "0");

xml::ElementPtr codes = root->createChild("Codes"), c;
// char ranges
if (bfont)
{ const std::vector<pair<Char, Char> >& vv = bfont->getCodePointRanges();
for (std::vector<pair<Char, Char> >::const_iterator it = vv.begin(); it != vv.end(); ++it)
if ((*it).first > 10 && (*it).first < 10000)
{
c = codes->createChild("Code");
c->addAttribute("range", toStr((*it).first)+" "+toStr((*it).second));
}
}else
{ c = codes->createChild("Code");
c->addAttribute("range", "33 255");
}
//doc.save(string("aaa.txt")); // test
font->deserialization(root, Version(3,2,0));
#endif
// add
mgr.addResource(font);
}
}

Crystal Hammer

21-08-2014 19:15:36

So "RelativeTo" and origPos, origSize strings are set auto, for all widgets (in method setToolTips).
And there is a custom param "NotSized", you can add it, if you want your window in .layout to not be resized at all.


<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout" version="3.2.0">
<Widget type="Window" skin="WindowC" position="40 24 400 480" align="Center" layer="Overlapped" name="MainMenuWnd">
<Property key="MinSize" value="500 400"/>
<Property key="Snap" value="true"/>
<UserString key="NotSized" value="1"/>
<Widget type="ImageBox" skin="ImageBox" position="28 56 56 56">
<Property key="ImageCoord" value="256 640 128 128"/>
<Property key="Alpha" value="0.5"/>
<Property key="ImageTexture" value="gui_icons.png"/>
</Widget>

</Widget>

<Widget type="Window" skin="WindowC" position="844 24 800 600" align="Center" layer="Overlapped" name="TrackWnd">
<Property key="MinSize" value="500 400"/>
<Property key="Snap" value="true"/>
<Widget type="TabControl" skin="TabControlIcon" position="0 0 800 600" align="Stretch" layer="Back" name="TabWndTrack">
<Property key="InheritsAlpha" value="false"/>
<Property key="SmoothShow" value="false"/>
<Property key="Colour" value="0.7 0.9 1"/>
<Widget type="TabItem" skin="" position="2 24 794 562">
<Property key="Caption" value="#FFE0C0&lt;#{BackMenu}"/>
<Property key="Colour" value="1 0.6 0.2"/>
</Widget>
<Widget type="TabItem" skin="" position="2 24 794 562" name="TabTrack">
<Property key="Caption" value="#D0E8FF#{Track}"/>
<Widget type="TextBox" skin="TextBox" position="176 468 60 24" name="TrackText">
<Property key="Caption" value="#{Track}"/>
<Property key="TextColour" value="0.4 1.0 0.5"/>
<UserString key="tip" value="#{TipTrkNameEditor}"/>
</Widget>
<!-- ............... .. .. . . . etc. -->


So, TrackWnd and all inside it will be resized but MainMenuWnd won't be.


Lastly, we have a fancy Margin value that depends on resolution (height).
But you can simply use a constant value:
0.f - will stretch full, 0.1f is 10% margin (empty space) etc.


// gui margin
float CGuiCom::GetGuiMargin(int wy)
{
const int yN = 7;
const Real yw[yN] = {400.f, 600.f, 720.f, 768.f, 960.f, 1024.f, 1050.f};
// how much empty space for screen y size, 0 = full window
const Real yf[yN] = {0.0f, 0.0f, 0.05f, 0.1f, 0.15f, 0.2f, 0.22f};
float ym = 0.f;
for (int i=0; i < yN; ++i)
if (wy >= yw-10) ym = yf;
return ym;
}



Ah and when creating widgets in code, you need to also call setOrigPos for it, with name of relative window (or Screen for none).


TextBox* txt = mGui->createWidget<TextBox>("TextBox", xt,yt, 40,22, Align::Default, "brT"+s);
txt->setCaption("Test");
gcom->setOrigPos(txt, "EditorWnd");

Altren

22-08-2014 06:29:50

Main problem with such approach is that skin elements are not scaled, so even if you have bigger elements on huge resolutions some parts are kept small, for example tabs, window captions, scroll bar buttons.