GUI editor fixes + file dialog upgrade

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

GUI editor fixes + file dialog upgrade

Post by roxaz »

Poor gui editor is really forgotten by everyone so i added some convenient things to it:
  • Widened gui editor window - all tabs are visible on startup
  • Element tree updates when loading from xml
  • Element names are displayed in the tree
  • Element names and IDs (if they are up to 4 numbers long) are aligned in element tree
  • GUI skin is kept when loading elements from xml
  • Saving gui/elements to file chosen in file save dialog
IGUIFileOpenDialog was repurposed to be more like basic file dialog (not renamed due to compatibility though). I added text box that contains selected file name. There is one drawback i did not care to solve - if you are opening file you can still edit selected file name and click ok selecting file that does not exist. So careful with the "shotgun" :)

CGUIFileOpenDialog patch:

Code: Select all

Index: CGUIFileOpenDialog.cpp
===================================================================
--- CGUIFileOpenDialog.cpp  (revision 4601)
+++ CGUIFileOpenDialog.cpp  (working copy)
@@ -23,7 +23,7 @@
 {
 
 const s32 FOD_WIDTH = 350;
-const s32 FOD_HEIGHT = 250;
+const s32 FOD_HEIGHT = 265;
 
 
 //! constructor
@@ -35,7 +35,7 @@
                    (parent->getAbsolutePosition().getHeight()-FOD_HEIGHT)/2,
                    (parent->getAbsolutePosition().getWidth()-FOD_WIDTH)/2+FOD_WIDTH,
                    (parent->getAbsolutePosition().getHeight()-FOD_HEIGHT)/2+FOD_HEIGHT)),
-   FileNameText(0), FileList(0), Dragging(false)
+   DirectoryPathText(0), FileList(0), Dragging(false)
 {
    #ifdef _DEBUG
    IGUIElement::setDebugName("CGUIFileOpenDialog");
@@ -104,7 +104,12 @@
    FileBox->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
    FileBox->grab();
 
-   FileNameText = Environment->addEditBox(0, core::rect<s32>(10, 30, RelativeRect.getWidth()-90, 50), true, this);
+   DirectoryPathText = Environment->addEditBox(0, core::rect<s32>(10, 30, RelativeRect.getWidth()-90, 50), true, this);
+   DirectoryPathText->setSubElement(true);
+   DirectoryPathText->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
+   DirectoryPathText->grab();
+
+   FileNameText = Environment->addEditBox(0, core::rect<s32>(10, 235, RelativeRect.getWidth()-90, 255), true, this);
    FileNameText->setSubElement(true);
    FileNameText->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
    FileNameText->grab();
@@ -130,8 +135,8 @@
    if (FileBox)
        FileBox->drop();
 
-   if (FileNameText)
-       FileNameText->drop();
+   if (DirectoryPathText)
+       DirectoryPathText->drop();
 
    if (FileSystem)
    {
@@ -143,6 +148,9 @@
 
    if (FileList)
        FileList->drop();
+
+   if(FileNameText)
+       FileNameText->drop();
 }
 
 
@@ -188,8 +196,11 @@
                    {
                        sendSelectedEvent( EGET_DIRECTORY_SELECTED );
                    }
-                   if ( FileName != L"" )
+                   if ( FileNameText->getText() != L"" )
                    {
+                       FileName = DirectoryPathText->getText();
+                       FileName += L"/";
+                       FileName += FileNameText->getText();
                        sendSelectedEvent( EGET_FILE_SELECTED );
                        remove();
                        return true;
@@ -204,13 +215,13 @@
                    {
                        if (FileList->isDirectory(selected))
                        {
-                           FileName = L"";
+                           FileNameText->setText(L"");
                            FileDirectory = FileList->getFullFileName(selected);
                        }
                        else
                        {
                            FileDirectory = L"";
-                           FileName = FileList->getFullFileName(selected);
+                           FileNameText->setText(FileList->getFileName(selected).c_str());
                        }
                        return true;
                    }
@@ -227,11 +238,11 @@
                            FileDirectory = FileList->getFullFileName(selected);
                            FileSystem->changeWorkingDirectoryTo(FileList->getFileName(selected));
                            fillListBox();
-                           FileName = "";
+                           FileNameText->setText(L"");
                        }
                        else
                        {
-                           FileName = FileList->getFullFileName(selected);
+                           FileNameText->setText(FileList->getFileName(selected).c_str());
                        }
                        return true;
                    }
@@ -238,13 +249,13 @@
                }
                break;
            case EGET_EDITBOX_ENTER:
-               if (event.GUIEvent.Caller == FileNameText)
+               if (event.GUIEvent.Caller == DirectoryPathText)
                {
-                   io::path dir( FileNameText->getText () );
+                   io::path dir( DirectoryPathText->getText () );
                    if ( FileSystem->changeWorkingDirectoryTo( dir ) )
                    {
                        fillListBox();
-                       FileName = L"";
+                       FileNameText->setText(L"");
                    }
                    return true;
                }
@@ -398,7 +409,7 @@
        }
    }
 
-   if (FileNameText)
+   if (DirectoryPathText)
    {
        #ifndef _IRR_WCHAR_FILESYSTEM
        const c8 *cs = (const c8 *)FileSystem->getWorkingDirectory().c_str();
@@ -412,7 +423,7 @@
        #endif
 
        FileDirectory = s;
-       FileNameText->setText(s.c_str());
+       DirectoryPathText->setText(s.c_str());
    }
 }
 
Index: CGUIFileOpenDialog.h
===================================================================
--- CGUIFileOpenDialog.h    (revision 4601)
+++ CGUIFileOpenDialog.h    (working copy)
@@ -67,6 +67,7 @@
        IGUIButton* OKButton;
        IGUIButton* CancelButton;
        IGUIListBox* FileBox;
+       IGUIEditBox* DirectoryPathText;
        IGUIEditBox* FileNameText;
        IGUIElement* EventParent;
        io::IFileSystem* FileSystem;
 
Patch widening GUI editor window so all tabs are visible and node tree alignment stuff:

Code: Select all

Index: D:/src/games/irr2/dep/irrlicht/tools/GUIEditor/CGUIEditWindow.cpp
===================================================================
--- D:/src/games/irr2/dep/irrlicht/tools/GUIEditor/CGUIEditWindow.cpp   (revision 4601)
+++ D:/src/games/irr2/dep/irrlicht/tools/GUIEditor/CGUIEditWindow.cpp   (working copy)
@@ -40,9 +40,9 @@
    s32 th = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH);
 
    setRelativePosition(core::rect<s32>(50,50,250,500));
-   setMinSize(core::dimension2du(200,200));
+   setMinSize(core::dimension2du(230,200));
 
-   IGUITabControl *TabControl = environment->addTabControl(core::rect<s32>(1,th+5,199,449), this, false, true);
+   IGUITabControl *TabControl = environment->addTabControl(core::rect<s32>(1,th+5,229,449), this, false, true);
    TabControl->setSubElement(true);
    TabControl->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
 
@@ -83,7 +83,7 @@
    TreeView->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
    IGUITreeViewNode* treenode = TreeView->getRoot();
    //treenode->addChildFront(L"Elements");
-   ResizeButton = environment->addButton(core::rect<s32>(199-th,449-th,199,449), this);
+   ResizeButton = environment->addButton(core::rect<s32>(229-th,449-th,229,449), this);
    ResizeButton->setDrawBorder(false);
    ResizeButton->setEnabled(false);
    ResizeButton->setSpriteBank(skin->getSpriteBank());
@@ -150,9 +150,19 @@
 
 void CGUIEditWindow::addChildrenToTree(IGUIElement* parentElement, IGUITreeViewNode* treenode)
 {
-   core::stringw name = core::stringw(parentElement->getTypeName());
+   core::stringw name = core::stringw(parentElement->getTypeName()) + "    ";
    if (parentElement->getID() != -1)
+   {
        name += core::stringw(L" [") + core::stringw(parentElement->getID()) + core::stringw(L"]");
+       // pad name to be 7 characters length. 4 chars - number, 2 chars - brackets, 1 - char leading space
+       // this will still look ugly with IDs that are longer than 4 numbers!
+       while(name.size() < 7)
+           name += " ";
+   }
+   else
+       name += "       ";  // make sure element names are aligned if ID is not set
+   if (strlen(parentElement->getName()))
+       name += core::stringw(L" ") + parentElement->getName();
 
    IGUITreeViewNode* newnode = treenode->addChildBack(name.c_str());
    newnode->setData((void*)parentElement);
 
Rest of GUI editor fixes including saving to hand-picked files, proper node tree updating, gui skin preserving (depends on CGUIFileOpenDialog patch):

Code: Select all

Index: CGUIEditWorkspace.cpp
===================================================================
--- CGUIEditWorkspace.cpp   (revision 4601)
+++ CGUIEditWorkspace.cpp   (working copy)
@@ -576,19 +576,60 @@
    case EET_GUI_EVENT:
        switch(e.GUIEvent.EventType)
        {
-        case EGET_TREEVIEW_NODE_SELECT:
-        {
-            IGUITreeViewNode* eventnode = ((IGUITreeView*)e.GUIEvent.Caller)->getLastEventNode();
-            if(!eventnode->isRoot())
-                setSelectedElement((IGUIElement*)(eventnode->getData()));
-            break;
-        }
+       case EGET_TREEVIEW_NODE_SELECT:
+       {
+           IGUITreeViewNode* eventnode = ((IGUITreeView*)e.GUIEvent.Caller)->getLastEventNode();
+           if(!eventnode->isRoot())
+               setSelectedElement((IGUIElement*)(eventnode->getData()));
+           break;
+       }
        // load a gui file
        case EGET_FILE_SELECTED:
+       {
            dialog = (IGUIFileOpenDialog*)e.GUIEvent.Caller;
-           Environment->loadGUI(core::stringc(dialog->getFileName()).c_str());
+           switch(FileDialogAction)
+           {
+           case FDA_LOAD_GUI:
+               {
+                   // preserve skin because loadGUI resets it
+                   IGUISkin* skin = Environment->getSkin();
+                   skin->grab();
+
+                   Environment->loadGUI(dialog->getFileName());
+                   this->EditorWindow->updateTree();
+
+                   Environment->setSkin(skin);
+                   skin->drop();
+                   break;
+               }
+           case FDA_SAVE_GUI:
+               {
+                   Environment->saveGUI(dialog->getFileName());
+                   break;
+               }
+           case FDA_LOAD_ELEMENT:
+               {
+                   // preserve skin because loadGUI resets it
+                   IGUISkin* skin = Environment->getSkin();
+                   skin->grab();
+
+                   Environment->loadGUI(dialog->getFileName(), SelectedElement ? SelectedElement : Environment->getRootGUIElement() );
+                   this->EditorWindow->updateTree();
+
+                   Environment->setSkin(skin);
+                   skin->drop();
+                   break;
+               }
+           case FDA_SAVE_ELEMENT:
+               {
+                   Environment->saveGUI(dialog->getFileName(), SelectedElement ? SelectedElement : Environment->getRootGUIElement() );
+                   break;
+               }
+           default:
+               break;
+           }
            break;
-
+       }
        case EGET_MENU_ITEM_SELECTED:
        {
            IGUIContextMenu *menu = (IGUIContextMenu*)e.GUIEvent.Caller;
@@ -615,10 +656,12 @@
 
                    break;
                case EGUIEDMC_FILE_LOAD:
+                   FileDialogAction = FDA_LOAD_GUI;
                    Environment->addFileOpenDialog(L"Please select a GUI file to open", false, this);
                    break;
                case EGUIEDMC_FILE_SAVE:
-                   Environment->saveGUI("guiTest.xml");
+                   FileDialogAction = FDA_SAVE_GUI;
+                   Environment->addFileOpenDialog(L"Please select a file name to save to", false, this);
                    break;
 
                //! edit menu
@@ -654,7 +697,8 @@
 
                case EGUIEDMC_SAVE_ELEMENT:
                     //TODO: add 'save' dialog.
-                   Environment->saveGUI("guiTest.xml", SelectedElement ? SelectedElement : Environment->getRootGUIElement() );
+                   FileDialogAction = FDA_SAVE_ELEMENT;
+                   Environment->addFileOpenDialog(L"Please select a file name to save to", false, this);
                    break;
 
                //! toggle edit window
@@ -662,7 +706,8 @@
                    break;
 
                case EGUIEDMC_INSERT_XML:
-                   Environment->loadGUI("guiTest.xml", SelectedElement ? SelectedElement : Environment->getRootGUIElement() );
+                   FileDialogAction = FDA_LOAD_ELEMENT;
+                   Environment->addFileOpenDialog(L"Please select a GUI file to open", false, this);
                    break;
 
                default:
Index: CGUIEditWorkspace.h
===================================================================
--- CGUIEditWorkspace.h (revision 4601)
+++ CGUIEditWorkspace.h (working copy)
@@ -158,6 +158,14 @@
        core::rect<s32> RRect;
        core::rect<s32> BRRect;
        core::rect<s32> BRect;
+
+       enum FILE_DIALOG_ACTION
+       {
+           FDA_LOAD_GUI,
+           FDA_SAVE_GUI,
+           FDA_LOAD_ELEMENT,
+           FDA_SAVE_ELEMENT,
+       } FileDialogAction;
    };
 
 
 
And a bonus - IGUIElement::getElementFromName(). I figured keeping track of element names is much easier for me than keeping track of numbers. Stupid nature of human brain.

Code: Select all

Index: D:/src/games/irr2/dep/irrlicht/include/IGUIElement.h
===================================================================
--- D:/src/games/irr2/dep/irrlicht/include/IGUIElement.h    (revision 4601)
+++ D:/src/games/irr2/dep/irrlicht/include/IGUIElement.h    (working copy)
@@ -615,6 +615,37 @@
    }
 
 
+   //! Finds the first element with the given name.
+   /** \param name: Name to search for.
+   \param searchchildren: Set this to true, if also children of this
+   element may contain the element with the searched name and they
+   should be searched too.
+   \return Returns the first element with the given name. If no element
+   with this name was found, 0 is returned. */
+   virtual IGUIElement* getElementFromName(const c8* name, bool searchchildren=false) const
+   {
+       IGUIElement* e = 0;
+
+       if(!strcmp(getName(), name))
+           return (IGUIElement*)this;
+
+       core::list<IGUIElement*>::ConstIterator it = Children.begin();
+       for (; it != Children.end(); ++it)
+       {
+           if (!strcmp((*it)->getName(), name))
+               return (*it);
+
+           if (searchchildren)
+               e = (*it)->getElementFromName(name, true);
+
+           if (e)
+               return e;
+       }
+
+       return e;
+   }
+
+
    //! returns true if the given element is a child of this one.
    //! \param child: The child element to check
    bool isMyChild(IGUIElement* child) const
 
Last edited by roxaz on Mon Nov 04, 2013 1:24 pm, edited 2 times in total.
CuteAlien
Admin
Posts: 9633
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: GUI editor fixes + file dialog upgrade

Post by CuteAlien »

Thanks - you are right that there is currently no-one caring about the gui editor.

Please always try to make minimal patches please. If can split a patch into several patches then it's always better to do that. So here you could have for example one patch for the GUIFileOpenDialog - and then one patch for the GUIEditor where you simply say - needs the CGUIFileOpenDialog patch to work.

getElementFromName... yeah - I considered that one. The reason I stumpled when implementing that is that I run into some old mess while working on that. Basically ISceneNode and IGUIElement work really similar (which is one reason I added the Name to IGUIElements recently. But when you look at the search functions by name and id - they work as different as they can. getElementFromId is a member of IGUIElements and has a flag to additionally search the children which is false by default (so doesn't search the children). getSceneNodeFromId and getSceneNodeFromName on the other had are not in ISceneNode but in ISceneManager and do search all children always and has no flag. Also in all cases it's never possible to also look if the element itself has the id/name which often means users have to add an additional check in code just for that. So... preferably this should be unified in ... the best ... way (yeah, right...). And preferably in a way not breaking existing code. I don't know right now - maybe just adding getElementFromName would be fine as it probably doesn't make the mess worse as it is.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
roxaz
Posts: 575
Joined: Tue Jan 23, 2007 8:35 pm
Location: LT

Re: GUI editor fixes + file dialog upgrade

Post by roxaz »

Thanks for feedback! As instructed i split changes to 4 separate patches and updated first post. Allowed myself to correct one indentation problem (spaces->tabs) Updated getElementFromName() too - doc comment changes name->id and check for name of this element.

And yes - i keep changes minimal, however i could not resist refactoring FileNameText -> DirectoryPathText. It was too misleading, especially given new functionality that dialog gained.
Post Reply