HISE Logo Forum
    • Categories
    • Register
    • Login

    Layout Builder - a tool to make it easy to copy GUIs between projects

    Scheduled Pinned Locked Moved Scripting
    5 Posts 3 Posters 164 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • d.healeyD
      d.healey
      last edited by d.healey

      I'm working on the next version of Rhapsody and wanted to make it easy to take my GUIs from one project to another. I came up with this layout builder script which takes the JSON you can get from the widget tree by pressing J and rebuilds all the widgets.

      Now you can already do this of course by copying and pasting the JSON from one project to another, but with this script you can make it modular.

      So the idea is maybe you have a set of controls for your preset browser, so you can stick them in one js file. Then you have another set of controls for your settings page, you put those in another js file, etc. Then you pass all the files to the layout builder and it will add the components to your GUI and set all the properties.

      /*
      * Author: David Healey
      * License: CC0
      * Last updated: 08/12/2024
      */
      
      namespace LayoutBuilder
      {
      	const data = [];
      
      	inline function add(arr: Array)
      	{
                 data.concat(arr);
      	}
      
      	inline function process()
      	{
      		local stack = data.clone();
      		
      		while (stack.length > 0)
      		{
      			local node = stack.shift();
      			
      			if (!isDefined(node.type) || !isDefined(node.id))
      				continue;
      
      			addComponent(node.id, node);
      
      			if (!isDefined(node.childComponents))
      				continue;
      
      			for (x in node.childComponents)
      				x.parentComponent = node.id;
      
      			stack.concat(node.childComponents);
      		}
      	}
      	
      	inline function: object addComponent(id: string, properties: JSON)
      	{
      		local c = Content.getComponent(id);
      
      		if (!isDefined(c))
      		{
      			local type = properties.type.replace("Scripted").replace("Script");
      
      			switch (type)
      			{
      				case "Slider": c = Content.addKnob(id, properties.x, properties.y); break;
      				case "Button": c = Content.addButton(id, properties.x, properties.y); break;
      				case "Table": c = Content.addTable(id, properties.x, properties.y); break;
      				case "ComboBox": c = Content.addComboBox(id, properties.x, properties.y); break;
      				case "Label": c = Content.addLabel(id, properties.x, properties.y); break;
      				case "Image": c = Content.addImage(id, properties.x, properties.y); break;
      				case "Viewport": c = Content.addViewport(id, properties.x, properties.y); break;
      				case "Panel": c = Content.addPanel(id, properties.x, properties.y); break;
      				case "AudioWaveform": c = Content.addAudioWaveform(id, properties.x, properties.y); break;
      				case "SliderPack": c = Content.addSliderPack(id, properties.x, properties.y); break;
      				case "WebView": c = Content.addWebView(id, properties.x, properties.y); break;
      				case "FloatingTile": c = Content.addFloatingTile(id, properties.x, properties.y); break;
      			}
      		}
      	
      		local allProperties = c.getAllProperties();
      	
      		for (x in properties)
      		{
      			if (!allProperties.contains(x) || ["id"].contains(x))
      				continue;
      
      			c.set(x, properties[x]);
      		}
      		
      		return c;
      	}
      }
      

      Example usage might be:

      include("LayoutBuilder.js");
      include("Layouts/PresetPanel.js");
      include("Layouts/SettingsPage.js");
      LayoutBuilder.process();
      

      The PresetPanel.js file might look something like this:

      LayoutBuilder.add([
        {
          "type": "ScriptPanel",
          "id": "pnlPresetBrowserContainer",
          "x": 0.0,
          "y": 50.0,
          "width": 1000.0,
          "height": 660.0,
          "parentComponent": "pnlMain",
          "locked": "1",
          "visible": "0",
          "bgColour": 4280098081,
          "itemColour": 4280098081,
          "itemColour2": 4294949932,
          "textColour": 4291611852,
          "childComponents": [
            {
              "type": "ScriptFloatingTile",
              "id": "fltPresetBrowser",
              "x": 50.0,
              "y": 18.0,
              "width": 900.0,
              "height": 550.0,
              "parentComponent": "pnlPresetBrowserContainer",
              "ContentType": "PresetBrowser",
              "Data": "{\n  \"ShowSaveButton\": true,\n  \"ShowExpansionsAsColumn\": false,\n  \"ShowFolderButton\": true,\n  \"ShowNotes\": true,\n  \"ShowEditButtons\": true,\n  \"EditButtonOffset\": 10,\n  \"ShowAddButton\": true,\n  \"ShowRenameButton\": true,\n  \"ShowDeleteButton\": true,\n  \"ShowSearchBar\": true,\n  \"ShowFavoriteIcon\": true,\n  \"FullPathFavorites\": true,\n  \"ButtonsInsideBorder\": true,\n  \"NumColumns\": 3,\n  \"ColumnWidthRatio\": [\n    0.3333333333333333,\n    0.3333333333333333,\n    0.3333333333333333\n  ],\n  \"ListAreaOffset\": [\n    0,\n    0,\n    0,\n    0\n  ],\n  \"ColumnRowPadding\": [\n    10,\n    10,\n    10,\n    10\n  ],\n  \"SearchBarBounds\": [\n    500,\n    8,\n    350,\n    30\n  ],\n  \"MoreButtonBounds\": [\n    10,\n    8,\n    30,\n    30\n  ],\n  \"SaveButtonBounds\": [\n    850,\n    8,\n    50,\n    30\n  ],\n  \"FavoriteButtonBounds\": [\n    55,\n    8,\n    32,\n    30\n  ]\n}",
              "locked": "1",
              "bgColour": 4280098081,
              "itemColour": 4280098081,
              "itemColour2": 4294949932,
              "textColour": 4291611852
            }
          ]
        }
      ]);
      

      The first time you hit compile you might get component not found errors, this is to be expected. So hit compile again and the errors will go away. And after you've ran the script you can remove all the includes if you want.

      Libre Wave - Freedom respecting instruments and effects
      My Patreon - HISE tutorials
      YouTube Channel - Public HISE tutorials

      d.healeyD VirtualVirginV 2 Replies Last reply Reply Quote 7
      • d.healeyD
        d.healey @d.healey
        last edited by d.healey

        And I just realised that script won't work unless you're using my fork because I added Array.shift. Here's an alternative version that uses Array.pop which is present in Christoph's source.

        /*
        * Author: David Healey
        * License: CC0
        * Last updated: 08/12/2024
        */
        
        namespace LayoutBuilder
        {
        	const data = [];
        
        	inline function add(arr: Array)
        	{
                   data.concat(arr);
        	}
        
        	inline function process()
        	{
        		local stack = data.clone();
        		
        		while (stack.length > 0)
        		{
        			//local node = stack.shift();
        			local node = stack.pop();
        			
        			if (!isDefined(node.type) || !isDefined(node.id))
        				continue;
        
        			addComponent(node.id, node);
        
        			if (!isDefined(node.childComponents))
        				continue;
        
        			for (x in node.childComponents)
        				x.parentComponent = node.id;
        
        			for (i = node.childComponents.length - 1; i >= 0; i--)
        			{
        				stack.push(node.childComponents[i]);
        			}
        
        			//stack.concat(node.childComponents);
        		}
        	}
        	
        	inline function: object addComponent(id: string, properties: JSON)
        	{
        		local c = Content.getComponent(id);
        
        		if (!isDefined(c))
        		{
        			local type = properties.type.replace("Scripted").replace("Script");
        
        			switch (type)
        			{
        				case "Slider": c = Content.addKnob(id, properties.x, properties.y); break;
        				case "Button": c = Content.addButton(id, properties.x, properties.y); break;
        				case "Table": c = Content.addTable(id, properties.x, properties.y); break;
        				case "ComboBox": c = Content.addComboBox(id, properties.x, properties.y); break;
        				case "Label": c = Content.addLabel(id, properties.x, properties.y); break;
        				case "Image": c = Content.addImage(id, properties.x, properties.y); break;
        				case "Viewport": c = Content.addViewport(id, properties.x, properties.y); break;
        				case "Panel": c = Content.addPanel(id, properties.x, properties.y); break;
        				case "AudioWaveform": c = Content.addAudioWaveform(id, properties.x, properties.y); break;
        				case "SliderPack": c = Content.addSliderPack(id, properties.x, properties.y); break;
        				case "WebView": c = Content.addWebView(id, properties.x, properties.y); break;
        				case "FloatingTile": c = Content.addFloatingTile(id, properties.x, properties.y); break;
        			}
        		}
        		
        		local allProperties = c.getAllProperties();
        	
        		for (x in properties)
        		{
        			if (!allProperties.contains(x) || ["id"].contains(x))
        				continue;
        
        			c.set(x, properties[x]);
        		}
        
        		return c;
        	}
        

        Libre Wave - Freedom respecting instruments and effects
        My Patreon - HISE tutorials
        YouTube Channel - Public HISE tutorials

        Oli UllmannO 1 Reply Last reply Reply Quote 3
        • Oli UllmannO
          Oli Ullmann @d.healey
          last edited by

          @d-healey
          Looks great. Thank you! :-)

          1 Reply Last reply Reply Quote 1
          • VirtualVirginV
            VirtualVirgin @d.healey
            last edited by

            @d-healey Thank you! I was just trying to figure out how to approach this in my projects.
            I will try integrating this method. I was copying the JSONs as you mentioned, but this looks better.

            Just to be clear:
            In your "Layouts/PresetPanel.js" you have a JSON for the components?
            And then you have another .js file included for the interface scripting of the Preset Panel (not shown)?

            d.healeyD 1 Reply Last reply Reply Quote 0
            • d.healeyD
              d.healey @VirtualVirgin
              last edited by

              @VirtualVirgin said in Layout Builder - a tool to make it easy to copy GUIs between projects:

              And then you have another .js file included for the interface scripting of the Preset Panel (not shown)?

              Yes. The stuff I posted above just adds the components to the interface, it provides no functionality to those components.

              Libre Wave - Freedom respecting instruments and effects
              My Patreon - HISE tutorials
              YouTube Channel - Public HISE tutorials

              1 Reply Last reply Reply Quote 0
              • First post
                Last post

              54

              Online

              1.7k

              Users

              11.7k

              Topics

              101.8k

              Posts