Jump to content

Chris goes all SQL META on the GUI system...


chriscalef

Recommended Posts

So... I had a very exciting week!


Although it was entirely unplanned, a few days ago I came up with one of those ideas that just changes everything, and MUST be done immediately!


The problem: With countless hours behind me of twiddling and twaddling Torque GUI elements in pursuit of my old Ecstasy Motion application, I've been flipping and flopping on how to move this work forward into my new build, as well as how to proceed with all the new GUIs I'm going to need for so many things.


My issues are twofold:


1) First, I'm just plain reluctant to sink another thousand hours into TorqueScript GUIs. I've been frustrated so many times by limitations of the system, and wooed so many times by the other sexy options, like an HTML-based solution, or one of the many other well defined third party UI systems, that I just hate to see myself digging in again and doing it all the hard way, in a format that leaves all my work in a giant pile of plain text script files. If I ever change my mind, there it sits, needing to be manually ported over, one button / text field / checkbox at a time.


2) Second, and even more importantly: in my day job, I've been working a lot with Cocos2d recently, and a very simple concept from their system really stuck in my head. Namely, the concept of anchors. In Cocos2d, you can define an anchor layout and set each control to base its position on the position of another control, thereby enabling one to move a whole block of UI elements around just by moving the base element.


Now, maybe you can already do that easily somehow in Torque, in which case I run the risk of feeling pretty stupid for the way I've spent a lot of past hours, but I must confess: many of those hours were spent simply fudging the positions of a whole group of UI elements over a little bit, because I had to insert a new control somewhere into the upper left of my form, and everything else had to move to make room.


Hence, this week's blinding flash of brilliance, killing two birds with one great beautiful all purpose stone named: SQL


Please allow me to introduce you to the newest table in my database:


http://opensimearth.com/images/screenshots/uiElementSql.jpg


And its small companion table for storing bitmap paths:


http://opensimearth.com/images/screenshots/uiBitmapSql.jpg


So basically, what's going on here is I wrote a little system in Torque Script which queries all the UI elements in a particular form (which itself is in the table as just another UI element). All it took was three functions (look for code at the bottom of this post):


makeSqlGuiForm(%control_id) - starts the process, and reads the relevant data about the parent container for the new form, identified by %control_id.


makeSqlGuiChildren(...) - the core of the system, this is a function which calls itself recursively to allow subcontainers within the form. These can be either actual Torque Gui containers, or they can be "Virtual" containers, meaning I use their properties to determine final positions of all of their children, but the container itself doesn't exist as a gui object.


makeUndefChildren() - this was necessary in order to deal with the possibility of controls being anchored to other controls that come later in the query, and hence don't exist yet when we're trying to use them. I store all such "undefined" controls in a list when I go through the first pass, and then run down the list in this function until everything has been happily sorted.


The great thing about this, in addition to ease of layout, is that if/when I ever do make the jump into another GUI system, all I have to do now is rewrite those functions to export whatever syntax the new system requires. As long as I'm dealing with 2D positions and extents, the numbers will be the same.


Also useful is the fact that when I want to adjust elements on my form, I can see practically all the relevant information compressed into one screen.


To give you a glimpse of the actual data, here's a couple of snapshots:


http://opensimearth.com/images/screenshots/uiElementData.jpg


http://opensimearth.com/images/screenshots/uiBitmapData.jpg


One thing you'll immediately note is the "type" column, eg "GuiBitmapCtrl" - by leaving that as plain text, the system is able to support any new GUI control type. What's in that field goes directly into the script.


The left_anchor / right_anchor / top_anchor / bottom_anchor fields contain IDs of other uiElements in the same table. It's pretty straightforward except for one sneaky little convention I slipped in to avoid a bunch of complexity. My initial assumption was that if I had a control with a left_anchor to another control, that meant that the left edge of this control matches up to the right edge of the anchor control, minus whatever horizontal padding comes into play from the parent or from this element. (Parent containers provide edge padding and internal padding, and then individual controls can use their own horizontal or vertical padding numbers to fine tune themselves as desired.)


However, it quickly became apparent that I was also going to need to align the left edge of this control with the left edge of another control, when I'm doing things like stacking a set of controls in a column, or lining up the bottoms to make an even horizontal row. So, to avoid a bunch of work, I just threw in the convention that a negative number in an anchor column means we use the same edge, bottom to bottom, instead of opposite edges.


So, long story short, here's the end result. This is just a dummy form, obviously, but it shows off some of the features of my little system:


http://opensimearth.com/images/screenshots/TestWindow.jpg


The top two rows are simply three controls each, with the first ones anchored to the left edge of the parent window, and then the others anchored to the one in front of them in the row. If I make one of them a little wider, the rest of the row automatically moves over.


The bottom left container is a GuiWindow control, which can be moved around or maximized. The bottom right set of buttons were created with my Virtual container type, which allows all of them to be moved just by moving the container, in the database, but once they are created they are just simple children of the parent window, and the container doesn't exist.


From here, I will probably have to add a number of additional fields to my main table, as I come across more members of different GUI element types that I haven't supported yet. However, this feels like a solid start. Oh yeah, almost forgot to mention - I stuck a FileObject in at the end, and made it save out the finished gui, so if you want to you can run with that once it's been created. Here's what the above GUI looks like in script:

 

%guiContent = new GuiWindowCtrl(TestWindow) {
  position = "153 187";
  extent = "480 320";
  text = "TestWindow";

  new GuiTextCtrl() {
     position = "10 30";
     extent = "55 20";
     text = "SceneSet";
     internalName = "SceneSetLabel";
     tooltip = "This is just a label.";
     tooltipprofile = "GuiToolTipProfile";
  };
  new GuiPopUpMenuCtrl() {
     position = "70 30";
     extent = "120 20";
     internalName = "SceneSetList";
     command = "loadScene(ScenesList.getSelected());";
     tooltip = "Choose a scene to load.";
     tooltipprofile = "GuiToolTipProfile";
  };
  new GuiTextCtrl() {
     position = "10 53";
     extent = "55 20";
     text = "Scene";
     internalName = "SceneLabel";
     command = "";
     tooltip = "";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/world";
  };
  new GuiBitmapButtonCtrl() {
     position = "195 30";
     extent = "20 20";
     text = "";
     internalName = "TestButton";
     command = "echo(\"button does THIS.\");";
     tooltip = "This button will do something else.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/world";
  };
  new GuiPopUpMenuCtrl() {
     position = "70 53";
     extent = "120 20";
     text = "";
     internalName = "SceneList";
     command = "";
     tooltip = "This is just a label.";
     tooltipprofile = "GuiToolTipProfile";
  };
  new GuiBitmapButtonCtrl() {
     position = "375 215";
     extent = "30 30";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/world";
  };
  new GuiBitmapButtonCtrl() {
     position = "407 215";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/softCurve";
  };
  new GuiBitmapButtonCtrl() {
     position = "439 215";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/world";
  };
  new GuiBitmapButtonCtrl() {
     position = "375 247";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/lowerHeight";
  };
  new GuiBitmapButtonCtrl() {
     position = "407 247";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/world";
  };
  new GuiBitmapButtonCtrl() {
     position = "439 247";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/setEmpty";
  };
  new GuiBitmapButtonCtrl() {
     position = "375 279";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/world";
  };
  new GuiBitmapButtonCtrl() {
     position = "407 279";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/brushAdjustHeight";
  };
  new GuiBitmapButtonCtrl() {
     position = "439 279";
     extent = "30 30";
     text = "";
     internalName = "";
     command = "echo(\"button does THIS.\");";
     tooltip = "Button in a subcontainer.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/world";
  };
  new GuiWindowCtrl() {
     position = "12 188";
     extent = "98 120";
     text = "";
     internalName = "";
     command = "";
     tooltip = "";
     tooltipprofile = "GuiToolTipProfile";
     new GuiBitmapButtonCtrl() {
        position = "2 27";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/softCurve";
     };
     new GuiBitmapButtonCtrl() {
        position = "34 27";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/world";
     };
     new GuiBitmapButtonCtrl() {
        position = "66 27";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/softCurve";
     };
     new GuiBitmapButtonCtrl() {
        position = "2 59";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/world";
     };
     new GuiBitmapButtonCtrl() {
        position = "34 59";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/softCurve";
     };
     new GuiBitmapButtonCtrl() {
        position = "66 59";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/world";
     };
     new GuiBitmapButtonCtrl() {
        position = "2 91";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/softCurve";
     };
     new GuiBitmapButtonCtrl() {
        position = "34 91";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/world";
     };
     new GuiBitmapButtonCtrl() {
        position = "66 91";
        extent = "30 30";
        text = "";
        internalName = "";
        command = "echo(\"button does THIS.\");";
        tooltip = "Button in a subcontainer.";
        tooltipprofile = "GuiToolTipProfile";
        bitmap = "tools/worldEditor/images/toolbar/softCurve";
     };
  };
  new GuiBitmapCtrl() {
     position = "373 213";
     extent = "2 99";
     bitmap = "core/art/gui/images/separator-h";
  };
  new GuiBitmapCtrl() {
     position = "374 213";
     extent = "98 2";
     bitmap = "core/art/gui/images/separator-v";
  };
  new GuiBitmapCtrl() {
     position = "373 310";
     extent = "99 2";
     bitmap = "core/art/gui/images/separator-v";
  };
  new GuiBitmapCtrl() {
     position = "470 213";
     extent = "2 98";
     bitmap = "core/art/gui/images/separator-h";
  };
  new GuiBitmapButtonCtrl() {
     position = "195 53";
     extent = "20 20";
     internalName = "TestButton";
     command = "echo(\"button does something else.\");";
     tooltip = "This button will do something else.";
     tooltipprofile = "GuiToolTipProfile";
     bitmap = "tools/worldEditor/images/toolbar/brushPaintNoise";
  };
};

 

And finally, as promised, here's all my code to make this work. It is based on the sqlite3 resource that's been floating around, not sure if I have a ready link to that but if you're interested and can't find it, let me know. My apologies for the massive repetition between the makeSqlGuiChildren function and the makeUndefChildren function, it would have been much prettier to break off the repetitive chunk into its own function but I haven't gotten there yet.

 


function makeSqlGuiForm(%control_id)
{
  echo("trying to load gui! " @ %control_id);  

  $controlCount = 0;

  $undefinedCount = 0; //This keeps track of controls that need
  $undefinedList = ""; //anchors which have not been created yet.
  //Note: remember that TorqueScript arrays are just variables, so there is no way to clear
  //the whole array in one line like it looks like I'm doing here. Shouldn't matter because
  //we'll be refilling it with only what we need and accessing only those values every time.

  %script = "";
  %indent = "   ";//Three-space indents, increment and decrement this as we go down the tree.

  //SO, first we need to select the parent control, with control_id. Then, we need a 
  //recursive algorithm to select all the children of this parent, and then all the children of 
  //those children, until we come up with no children. Would be snazzy to fit this all into 
  //one query, but reality is the speed gain will probably be totally undetectable, as long as 
  //we're not doing this every frame on resize.
  %query = "SELECT * FROM uiElement e " @ 
         "LEFT JOIN uiBitmap b ON b.id=e.bitmap_id " @ 
         "WHERE e.id=" @ %control_id @ ";";  
%result = sqlite.query(%query, 0);

  echo( "Query: " @ %query );

  if (%result)
  {
     %bitmap_path = sqlite.getColumn(%result, "path");
     %type = sqlite.getColumn(%result, "type");
     %name = sqlite.getColumn(%result, "name");
     %width = sqlite.getColumn(%result, "width");
     %height = sqlite.getColumn(%result, "height");
     %horiz_align = sqlite.getColumn(%result, "horiz_align");
     %vert_align = sqlite.getColumn(%result, "vert_align");
     %pos_x = sqlite.getColumn(%result, "pos_x");
     %pos_y = sqlite.getColumn(%result, "pos_y");
     %horiz_padding = sqlite.getColumn(%result, "horiz_padding");
     %vert_padding = sqlite.getColumn(%result, "vert_padding");
     %horiz_edge_padding = sqlite.getColumn(%result, "horiz_edge_padding");
     %vert_edge_padding = sqlite.getColumn(%result, "vert_edge_padding");
  }

  if (strlen(%type)>0) //New way: to hell with a massive table of types, I'm just going to 
  {//use the actual Torque names of the classes here. 
  //Advantage: I can add new ones just by typing them in. And browsing the DB is informative.
  //Disadvantage:  I am locking myself to Torque names. But, later for other systems I can convert from
  //  them in a table just like I would with ints.

     %script = %script @ "%guiContent = new " @ %type @ "(" @ %name @ ") {\n";
  } 
  else 
     return;

  %editorExtents = EWorldEditor.getExtent();
  %editorWidth = getWord(%editorExtents,0);
  %editorHeight = getWord(%editorExtents,1);

  %pos_x = %pos_x * %editorWidth;
  %pos_y = %pos_y * %editorHeight;

  %script = %script @ "   position = \"" @ mFloor(%pos_x) @ " " @ mFloor(%pos_y) @ "\";\n";
  %script = %script @ "   extent = \"" @ %width @ " " @ %height @ "\";\n";
  %script = %script @ "   text = \"" @ %name @ "\";\n\n";

  $controls[$controlCount,0] = %control_id;
  $controls[$controlCount,1] = %pos_x; $controls[$controlCount,2] = %pos_y;
  $controls[$controlCount,3] = %width; $controls[$controlCount,4] = %height;
  $controls[$controlCount,5] = false;//Base window can't be virtual.
  $controlCount++;

  %script = %script @ makeSqlGuiChildren(%control_id,%width,%height,%horiz_padding,%vert_padding,%horiz_edge_padding,%vert_edge_padding,%indent);


  if ($undefinedCount > 0)
     %script = %script @ makeUndefChildren(%indent);

  %script = %script @ "};\n";

  //////////////////////////////
  eval(%script);   
  //echo(%script);
  //////////////////////////////

  %filename = "testWindow.gui";

  %fileObject = new FileObject();
  %fileObject.openForWrite( %filename ); 
  %fileObject.writeLine(%script);
  %fileObject.close();
  %fileObject.delete();

  EWorldEditor.add(%name);   
}

function makeSqlGuiChildren(%parent_id,%parent_width,%parent_height,%parent_horiz_padding,
                    %parent_vert_padding,%parent_horiz_edge_padding,%parent_vert_edge_padding,%indent)
{//RECURSIVE - so make sure we don't use any globals.


  %script = "";

  %query = "SELECT * FROM uiElement e " @ 
         "LEFT JOIN uiBitmap b ON b.id=e.bitmap_id " @ 
         "WHERE e.parent_id=" @ %parent_id @ ";"; 

  %result = sqlite.query(%query, 0);

  if (%result)
  {
     echo("makeSqlGuiChildren found " @ sqlite.numRows(%result) @ " children for parent " @ %parent_id );
     while (!sqlite.endOfResult(%result))
     {
        %undefined = false;

        %horiz_anchor_flip = false;//These track whether either of our anchors are "flipped" - e.g. a negative sign 
        %vert_anchor_flip = false;//in the left_anchor means we use the _left_ edge of the anchor control to align
        //the left edge of this control, instead of aligning the left edge of this control to the right edge of the anchor. 

        %control_id = sqlite.getColumn(%result, "id");
        %bitmap_path = sqlite.getColumn(%result, "path");
        %left_anchor = sqlite.getColumn(%result, "left_anchor");
        %right_anchor = sqlite.getColumn(%result, "right_anchor");
        %top_anchor = sqlite.getColumn(%result, "top_anchor");
        %bottom_anchor = sqlite.getColumn(%result, "bottom_anchor");
        %type = sqlite.getColumn(%result, "type");
        %content = sqlite.getColumn(%result, "content");
        %name = sqlite.getColumn(%result, "name");
        %width = sqlite.getColumn(%result, "width");
        %height = sqlite.getColumn(%result, "height");
        %command = sqlite.getColumn(%result, "command");
        %tooltip = sqlite.getColumn(%result, "tooltip");
        %horiz_align = sqlite.getColumn(%result, "horiz_align");
        %vert_align = sqlite.getColumn(%result, "vert_align");
        %pos_x = sqlite.getColumn(%result, "pos_x");
        %pos_y = sqlite.getColumn(%result, "pos_y");
        %horiz_padding = sqlite.getColumn(%result, "horiz_padding");
        %vert_padding = sqlite.getColumn(%result, "vert_padding");
        %horiz_edge_padding = sqlite.getColumn(%result, "horiz_edge_padding");
        %vert_edge_padding = sqlite.getColumn(%result, "vert_edge_padding");
        %variable = sqlite.getColumn(%result, "variable");
        %button_type = sqlite.getColumn(%result, "button_type");
        %group_num = sqlite.getColumn(%result, "group_num");
        %profile = sqlite.getColumn(%result, "profile");

        if (strlen(%type)>0) 
        {
           ////// left/right anchors //////////////
           if (%left_anchor < 0)
           {
              %horiz_anchor_flip = true;
              %left_anchor *= -1;
           }
           if (%right_anchor < 0)
           {
              %horiz_anchor_flip = true;
              %right_anchor *= -1;
           }
           if (%left_anchor == %parent_id) //check for left anchor first, then right.
           {//AH, oops! If parent is Virtual, then we need to use it's position, we can't start with zero.
              %i = 0;
              %foundVirtual = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %parent_id)
                 {
                    if ($controls[%i,5])
                    {
                       %pos_x = $controls[%i,1] + %parent_horiz_edge_padding + %horiz_padding;  
                       echo("found virtual parent! pos_x " @  %pos_x );                
                       %foundVirtual = true;
                    }
                 }
                 %i++;
              }
              if (!%foundVirtual)
                 %pos_x = %parent_horiz_edge_padding + %horiz_padding;
           }
           else if (%left_anchor > %parent_id)//Parents and previous siblings MUST be ahead in database order.
           {
              %i = 0;
              %found = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %left_anchor)
                 {
                    if (%horiz_anchor_flip == false)
                       %pos_x = $controls[%i,1] + $controls[%i,3] + %parent_horiz_padding + %horiz_padding;
                    else
                       %pos_x = $controls[%i,1] + %horiz_padding;

                    %found = true;
                 }
                 %i++;  
              }
              if ( !%found && !%undefined )
              {
                 $undefinedList[$undefinedCount,0] = %control_id;
                 $undefinedList[$undefinedCount,1] = %indent;
                 $undefinedCount++;
                 %undefined = true;                  
              }
           } 
           else if (%right_anchor == %parent_id) 
           {
              %i = 0;
              %foundVirtual = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %parent_id)
                 {
                    if ($controls[%i,5])
                    {
                       %pos_x = $controls[%i,1] + $controls[%i,3] - %width - %parent_horiz_edge_padding - %horiz_padding;                     
                       %foundVirtual = true;
                    }
                 }
                 %i++;
              }
              if (!%foundVirtual)
                 %pos_x = %parent_width - %width - %parent_horiz_edge_padding - %horiz_padding;
           } 
           else if (%right_anchor > %parent_id)
           {
              %i = 0;
              %found = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %right_anchor)
                 {
                    if (%horiz_anchor_flip == false)
                       %pos_x = $controls[%i,1] - %width - %parent_horiz_padding - %horiz_padding;
                    else
                       %pos_x = $controls[%i,1] + $controls[%i,3] - %width - %horiz_padding;

                    %found = true;
                 }
                 %i++;  
              }
              if ( !%found && !%undefined )
              {
                 $undefinedList[$undefinedCount,0] = %control_id;
                 $undefinedList[$undefinedCount,1] = %indent;
                 $undefinedCount++;
                 %undefined = true;                  
              }
           }

           ////// top/bottom anchors //////////////
           if (%top_anchor < 0)
           {
              %vert_anchor_flip = true;
              %top_anchor *= -1;
           }
           if (%bottom_anchor < 0)
           {
              %vert_anchor_flip = true;
              %bottom_anchor *= -1;
           }
           if (%top_anchor == %parent_id) //check for top anchor first, then bottom.
           {
              %i = 0;
              %foundVirtual = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %parent_id)
                 {
                    if ($controls[%i,5])
                    {
                       %pos_y = $controls[%i,2] + %parent_vert_edge_padding + %vert_padding;                     
                       %foundVirtual = true;
                    }
                 }
                 %i++;
              }
              if (!%foundVirtual)               
                 %pos_y = %parent_vert_edge_padding + %vert_padding;
           }
           else if (%top_anchor > %parent_id)
           {
              %i = 0;
              %found = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %top_anchor)
                 {
                    if (%vert_anchor_flip == false)
                       %pos_y = $controls[%i,2] + $controls[%i,4] + %parent_vert_padding + %vert_padding;
                    else
                       %pos_y = $controls[%i,2] + %vert_padding;

                    %found = true;
                 }
                 %i++;  
              }
              if ( !%found && !%undefined )
              {
                 $undefinedList[$undefinedCount,0] = %control_id;
                 $undefinedList[$undefinedCount,1] = %indent;
                 $undefinedCount++;
                 %undefined = true;                  
              }             
           }   
           else if (%bottom_anchor == %parent_id) 
           {
              %i = 0;
              %foundVirtual = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %parent_id)
                 {
                    if ($controls[%i,5])
                    {
                       %pos_y = $controls[%i,2] + $controls[%i,4] - %height - %parent_vert_edge_padding - %vert_padding;                     
                       %foundVirtual = true;
                    }
                 }
                 %i++;
              }
              if (!%foundVirtual)  
                 %pos_y = %parent_height - %height - %parent_vert_edge_padding - %vert_padding;
           } 
           else if (%bottom_anchor > %parent_id)
           {
              %i = 0;
              %found = false;
              while (%i < $controlCount)
              {
                 if ($controls[%i,0] == %bottom_anchor)
                 {
                    if (%vert_anchor_flip == false)
                       %pos_y = $controls[%i,2] - %height - %parent_vert_padding - %vert_padding;
                    else
                       %pos_y = $controls[%i,2] + $controls[%i,4] - %height - %vert_padding;
                    %found = true;
                 }
                 %i++;  
              }
              if ( !%found && !%undefined )
              {
                 $undefinedList[$undefinedCount,0] = %control_id;
                 $undefinedList[$undefinedCount,1] = %indent;
                 $undefinedCount++;
                 %undefined = true;                  
              }
           }

           if (%undefined == false)
           {
              //Now, write it to the script buffer.
              if (%type !$= "Virtual")
              {
                 %script = %script @ %indent @ "new " @ %type @ "() {\n";
                 %script = %script @ %indent @ "   position = \"" @ mFloor(%pos_x) @ " " @ mFloor(%pos_y) @ "\";\n";
                 %script = %script @ %indent @ "   extent = \"" @ %width @ " " @ %height @ "\";\n";
                 if (%content !$= "NULL") %script = %script @ %indent @ "   text = \"" @ %content @ "\";\n";
                 if (%name !$= "NULL") %script = %script @ %indent @ "   internalName = \"" @ %name @ "\";\n";            
                 if (%command !$= "NULL") %script = %script @ %indent @ "   command = \"" @ %command @ "\";\n";
                 if (%tooltip !$= "NULL") %script = %script @ %indent @ "   tooltip = \"" @ %tooltip @ "\";\n"; 
                 if (%tooltip !$= "NULL") %script = %script @ %indent @ "   tooltipprofile = \"GuiToolTipProfile\";\n";
                 if (%bitmap_path !$= "NULL") %script = %script @ %indent @ "   bitmap = \"" @ %bitmap_path @ "\";\n"; 
                 if (%variable !$= "NULL") %script = %script @ %indent @ "   variable = \"" @ %variable @ "\";\n"; 
                 if (%button_type !$= "NULL") %script = %script @ %indent @ "   buttonType = \"" @ %button_type @ "\";\n"; 
                 if (%group_num !$= "NULL") %script = %script @ %indent @ "   groupNum = \"" @ %group_num @ "\";\n"; 
                 if (%profile !$= "NULL") %script = %script @ %indent @ "   profile = \"" @ %profile @ "\";\n"; 
              }

              //Next, store position/extent info in a 2d array, for positioning other elements.           
              $controls[$controlCount,0] = %control_id;
              $controls[$controlCount,1] = %pos_x; $controls[$controlCount,2] = %pos_y;
              $controls[$controlCount,3] = %width; $controls[$controlCount,4] = %height;
              if (%type !$= "Virtual")
                 $controls[$controlCount,5] = false;
              else
                 $controls[$controlCount,5] = true;               
              $controlCount++;

              ////// Now, take care of the children. ///////
              if (%type !$= "Virtual")
              {
                 %prev_indent = %indent;
                 %indent = %indent @ "   ";//Make it three spaces deeper before we go to the children...
              }

              //And... RECURSE!
              %script = %script @ makeSqlGuiChildren(%control_id,%width,%height,%horiz_padding,%vert_padding,%horiz_edge_padding,%vert_edge_padding,%indent);

              if (%type !$= "Virtual")
              {
                 %indent = %prev_indent;//... and then take them away when we come back. 
                 %script = %script @ %indent @ "};\n";
              }
           }
           ///////////////////////////////////////////////
        }
        sqlite.nextRow(%result);
     }
  }

  return %script;
}

function makeUndefChildren(%indent)
{
  //Now, the interesting part. We have to keep looping through the undefinedList, skipping over anything that
  //still can't be anchored, but instantiating and removing anything that now has valid anchors.
  %sanityCount = 0;
  %script = "";
  echo("makeUndefChildren, count " @ $undefinedCount);
  while (($undefinedCount > 0)&&(%sanityCount<500))
  {      
     %sanityCount++;//(Just in case something goes haywire and we end up looping here forever.)
     //This could totally happen, if the user for instance accidentally makes two controls dependent 
     //on each other, or parents of each other. 
     for (%k=0;%k<$undefinedCount;%k++)
     {
        %control_id = $undefinedList[%k,0];
        %query = "SELECT * FROM uiElement e " @ 
         "LEFT JOIN uiBitmap b ON b.id=e.bitmap_id " @ 
         "WHERE e.id=" @ %control_id @ ";";  
      %result = sqlite.query(%query, 0);
        if (%result)
        {
           %undefined = false;
           %parent_id = sqlite.getColumn(%result, "parent_id"); 
           %bitmap_path = sqlite.getColumn(%result, "path");
           %left_anchor = sqlite.getColumn(%result, "left_anchor");
           %right_anchor = sqlite.getColumn(%result, "right_anchor");
           %top_anchor = sqlite.getColumn(%result, "top_anchor");
           %bottom_anchor = sqlite.getColumn(%result, "bottom_anchor");
           %type = sqlite.getColumn(%result, "type");
           %content = sqlite.getColumn(%result, "content");
           %name = sqlite.getColumn(%result, "name");
           %width = sqlite.getColumn(%result, "width");
           %height = sqlite.getColumn(%result, "height");
           %command = sqlite.getColumn(%result, "command");
           %tooltip = sqlite.getColumn(%result, "tooltip");
           %horiz_align = sqlite.getColumn(%result, "horiz_align");
           %vert_align = sqlite.getColumn(%result, "vert_align");
           %pos_x = sqlite.getColumn(%result, "pos_x");
           %pos_y = sqlite.getColumn(%result, "pos_y");
           %horiz_padding = sqlite.getColumn(%result, "horiz_padding");
           %vert_padding = sqlite.getColumn(%result, "vert_padding");
           %horiz_edge_padding = sqlite.getColumn(%result, "horiz_edge_padding");
           %vert_edge_padding = sqlite.getColumn(%result, "vert_edge_padding");

           if (strlen(%type)>0) 
           {
              ////// left/right anchors //////////////
              if (%left_anchor < 0)
              {
                 %horiz_anchor_flip = true;
                 %left_anchor *= -1;
              }
              if (%right_anchor < 0)
              {
                 %horiz_anchor_flip = true;
                 %right_anchor *= -1;
              }
              if (%left_anchor == %parent_id) //check for left anchor first, then right.
              {
                 %i = 0;
                 %foundVirtual = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %parent_id)
                    {
                       if ($controls[%i,5])
                       {
                          %pos_x = $controls[%i,1] + %parent_horiz_edge_padding + %horiz_padding;                     
                          %foundVirtual = true;
                       }
                    }
                    %i++;
                 }
                 if (!%foundVirtual)
                    %pos_x = %parent_horiz_edge_padding + %horiz_padding;
              }
              else if (%left_anchor > %parent_id)//Parents and previous siblings MUST be ahead in database order.
              {
                 %i = 0;
                 %found = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %left_anchor)
                    {
                       if (%horiz_anchor_flip == false)
                          %pos_x = $controls[%i,1] + $controls[%i,3] + %parent_horiz_padding + %horiz_padding;
                       else
                          %pos_x = $controls[%i,1] + %horiz_padding;
                       %found = true;
                    }
                    %i++;  
                 }
                 if (!%found)
                 {
                    %undefined = true;                  
                 }
              } 
              else if (%right_anchor == %parent_id) 
              {
                 %i = 0;
                 %foundVirtual = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %parent_id)
                    {
                       if ($controls[%i,5])
                       {
                          %pos_x = $controls[%i,1] + $controls[%i,3] - %width - %parent_horiz_edge_padding - %horiz_padding;                     
                          %foundVirtual = true;
                       }
                    }
                    %i++;
                 }
                 if (!%foundVirtual)
                    %pos_x = %parent_width - %width - %parent_horiz_edge_padding - %horiz_padding;
              } 
              else if (%right_anchor > %parent_id)
              {
                 %i = 0;
                 %found = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %right_anchor)
                    {
                       if (%horiz_anchor_flip == false)
                          %pos_x = $controls[%i,1] - %width - %parent_horiz_padding - %horiz_padding;
                       else
                          %pos_x = $controls[%i,1] + $controls[%i,3] - %width - %horiz_padding;
                       %found = true;
                    }
                    %i++;  
                 }
                 if (!%found)
                 {
                    %undefined = true;                  
                 }
              }

              ////// top/bottom anchors //////////////
              if (%top_anchor < 0)
              {
                 %vert_anchor_flip = true;
                 %top_anchor *= -1;
              }
              if (%bottom_anchor < 0)
              {
                 %vert_anchor_flip = true;
                 %bottom_anchor *= -1;
              }
              if (%top_anchor == %parent_id) //check for top anchor first, then bottom.
              {
                 %i = 0;
                 %foundVirtual = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %parent_id)
                    {
                       if ($controls[%i,5])
                       {
                          %pos_y = $controls[%i,2] + %parent_vert_edge_padding + %vert_padding;                     
                          %foundVirtual = true;
                       }
                    }
                    %i++;
                 }
                 if (!%foundVirtual)               
                    %pos_y = %parent_vert_edge_padding + %vert_padding;

              }
              else if (%top_anchor > %parent_id)
              {
                 %i = 0;
                 %found = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %top_anchor)
                    {
                       if (%vert_anchor_flip == false)
                          %pos_y = $controls[%i,2] + $controls[%i,4] + %parent_vert_padding + %vert_padding;
                       else
                          %pos_y = $controls[%i,2] + %vert_padding;
                       %found = true;
                    }
                    %i++;  
                 }
                 if (!%found)
                 {
                    %undefined = true;                  
                 }             
              }   
              else if (%bottom_anchor == %parent_id) 
              {
                 %i = 0;
                 %foundVirtual = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %parent_id)
                    {
                       if ($controls[%i,5])
                       {
                          %pos_y = $controls[%i,2] + $controls[%i,4] - %height - %parent_vert_edge_padding - %vert_padding;                     
                          %foundVirtual = true;
                       }
                    }
                    %i++;
                 }
                 if (!%foundVirtual)  
                    %pos_y = %parent_height - %height - %parent_vert_edge_padding - %vert_padding;
              } 
              else if (%bottom_anchor > %parent_id)
              {
                 %i = 0;
                 %found = false;
                 while (%i < $controlCount)
                 {
                    if ($controls[%i,0] == %bottom_anchor)
                    {
                       if (%vert_anchor_flip == false)
                          %pos_y = $controls[%i,2] - %parent_vert_padding - %height - %vert_padding;
                       else
                          %pos_y = $controls[%i,2] + $controls[%i,4] - %height - %vert_padding;
                       %found = true;
                    }
                    %i++;  
                 }
                 if (!%found)
                 {
                    %undefined = true;                  
                 }
              }

              if (%undefined == false) //Yay, we found our anchors this time!
              {
                 %indent = $undefinedList[%k,1];
                 //Now, write it to the script buffer.
                 if (%type !$= "Virtual")
                 {
                    %script = %script @ %indent @ "new " @ %type @ "() {\n";
                    %script = %script @ %indent @ "   position = \"" @ mFloor(%pos_x) @ " " @ mFloor(%pos_y) @ "\";\n";
                    %script = %script @ %indent @ "   extent = \"" @ %width @ " " @ %height @ "\";\n";
                    if (strlen(%content)>0) %script = %script @ %indent @ "   text = \"" @ %content @ "\";\n";
                    if (strlen(%name)>0) %script = %script @ %indent @ "   internalName = \"" @ %name @ "\";\n";            
                    if (strlen(%command)>0) %script = %script @ %indent @ "   command = \"" @ %command @ "\";\n";
                    if (strlen(%tooltip)>0) %script = %script @ %indent @ "   tooltip = \"" @ %tooltip @ "\";\n"; 
                    if (strlen(%tooltip)>0) %script = %script @ %indent @ "   tooltipprofile = \"GuiToolTipProfile\";\n";
                    if (strlen(%bitmap_path)>0) %script = %script @ %indent @ "   bitmap = \"" @ %bitmap_path @ "\";\n"; 
                    if (strlen(%variable)>0) %script = %script @ %indent @ "   variable = \"" @ %variable @ "\";\n"; 
                    if (strlen(%button_type)>0) %script = %script @ %indent @ "   buttonType = \"" @ %button_type @ "\";\n"; 
                    if (strlen(%group_num)>0) %script = %script @ %indent @ "   groupNum = \"" @ %group_num @ "\";\n"; 
                    if (strlen(%profile)>0) %script = %script @ %indent @ "   profile = \"" @ %profile @ "\";\n";
                 }

                 //Next, store position/extent info in a 2d array, for positioning other elements.           
                 $controls[$controlCount,0] = %control_id;
                 $controls[$controlCount,1] = %pos_x; $controls[$controlCount,2] = %pos_y;
                 $controls[$controlCount,3] = %width; $controls[$controlCount,4] = %height;               
                 if (%type !$= "Virtual")
                    $controls[$controlCount,5] = false;
                 else
                    $controls[$controlCount,5] = true; 
                 $controlCount++;

                 ////// Now, take care of the children. ///////
                 if (%type !$= "Virtual")
                 {
                    %prev_indent = %indent;
                    %indent = %indent @ "   ";//Make it three spaces deeper before we go to the children...
                 }

                 //And... RECURSE!
                 %script = %script @ makeSqlGuiChildren(%control_id,%width,%height,%horiz_padding,%vert_padding,%horiz_edge_padding,%vert_edge_padding,%indent);

                 if (%type !$= "Virtual")
                 {
                    %indent = %prev_indent;//... and then take them away when we come back. 
                    %script = %script @ %indent @ "};\n";
                 }

                 //Now, since we found one, we need to delete this one from the list and advance the rest of the
                 //list forward one, then break and restart.
                 for (%j=%k;%j<$undefinedCount;%j++)
                 {
                    $undefinedList[%j,0] = $undefinedList[(%j+1),0];//I do hope "(%j+1)" doesn't get interpreted
                    $undefinedList[%j,1] = $undefinedList[(%j+1),1];// as a new array entry...
                 }
                 $undefinedCount--;

                 break; //break out of "for (%i=0;%i<undefinedCount;%i++)" loop                 
              }
           }            
        }
     }
  }
  return %script;
}

 

So anyway, there you have it! My week in a nutshell. Hope it provides some amusement and/or inspiration to somebody out there!

Edited by chriscalef
Link to comment
Share on other sites

Just a quick update, about to have to put this down for a bit but I got well along the way toward making my first actual GUI with the new system. Coincidentally enough, it's going to be a form for making more forms. :-)


http://opensimearth.com/images/screenshots/uiElement02.jpg

Link to comment
Share on other sites

This is pretty cool. XML would be awesome. Just need to get the GUI Editor to understand this when it sees it and it'd be the cat's meow.


The "easy way" to do anchors in Torque is to use a container control. A plain GuiControl marked as a container works well since it is "invisible" by default. All of the controls within the container resize according to their set horizResize and vertResize properties, but in relation to their container, not the entire Canvas.

Link to comment
Share on other sites

Yeah, I've used containers, but what I really wanted was control vs control resolution, because inside the container when you have a wall of buttons, it still sucks to try to insert one. And it doesn't help that I'm somehow allergic to using the GUI editor, dunno why but I always just end up back in the script files, manually editing them. :-P


An XML version could be useful, but it would be quite a bit of a rewrite and quite a bit less efficient than what I'm doing now, due to the fact that I'm actively querying things as I need them (recursively grabbing children for each element as I go through) and sharing data among tables. The bitmap table makes it possible for many elements to share the same image path, and all of them can be changed by just changing the one entry. Similar things could be accomplished with XML I'm sure but it would start to feel like I was rewriting sqlite. I'm so far down the SQL path at this point that there's no turning back, but that's why I published the code, if anyone is so inspired knock yourself out!


Be warned though that the above code isn't perfect, I found a crash bug when I started moving things around too radically. Gotta get back to real work now but I'll clean it up next time.


Thanks for the feedback though! Was a fun week, definitely takes a load off my mind re: how to proceed forward.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...