Sign in to follow this  
Followers 0
Romfarer

Editor Toolbar Modding Guide

6 posts in this topic

Editor Toolbar Modding Guide:

The editor toolbar runs of the class PartCategorizer. Each button on the toolbar is handled by the class PartCategorizer.Category and the actual button is a PartCategorizerButton. There are a myriad of ways to customize your custom filters using these classes but for simplicity i have added two static methods to PartCategorizer which simplifies the process of adding custom filters.

How to add a simple custom filter:

The following code example explains how to add a simple custom filter.


//instantiate the icon
Icon myIcon = new PartCategorizer.Icon("my icon", iconTexture_normal, iconTexture_selected);

//add a custom filter to the toolbar
Category myFilter = PartCategorizer.AddCustomFilter("my filter", myIcon, Color.yellow);

//add subcategories to the filter you just added
PartCategorizer.AddCustomSubcategoryFilter(myFilter, "filter 1", myIcon, p => p.name.Contains("engine"));
PartCategorizer.AddCustomSubcategoryFilter(myFilter, "filter 2", myIcon, p => p.name.Contains("fuel"));
PartCategorizer.AddCustomSubcategoryFilter(myFilter, "filter 3", myIcon, p => p.name.Contains("wing"));

Lets break it down "line by line"


Icon myIcon = new PartCategorizer.Icon("my icon", iconTexture_normal, iconTexture_selected);

and the signature...
/// <summary>
/// Constructor for Icon
/// </summary>
/// <param name="name">The identifier of the icon</param>
/// <param name="iconNormal">The "normal" state of the icon</param>
/// <param name="iconSelected">The "selected" state of the icon</param>
/// <param name="simple">If true you only have to specify the selected state of the icon. A black "normal" state will be generated automatically.</param>
public Icon(string name, Texture iconNormal, Texture iconSelected, bool simple = false)

Icons are instantiated using the PartCategorizer.Icon class. It's signature is Icon(string name, Texture iconNormal, Texture iconSelected, bool simple = false). "name" is not really important when instantiating the icons via code like this. But if you had loaded the icon by placing it in GameData\Squad\PartList\SimpleIcons or Icons this would be the identifier used to retrieve the icon with PartCategorizer.Instance.GetIcon("myIcon"); That said you can choose wether to instantiate the icon by code (leaving it for your button only) or put it in those folders and make it available for everyone to use on their custom categories.


static public Category AddCustomFilter(string filterName, Icon icon, Color colorButton)

and the signature...
/// <summary>
/// Adds a filter to the "right side" toolbar list.
/// </summary>
/// <param name="filterName">The name of the filter which will appear on the tooltip</param>
/// <param name="icon">The icon for the button</param>
/// <param name="colorButton">The color of the button</param>
/// <returns>A reference to the Category wrapper</returns>
static public Category AddCustomFilter(string filterName, Icon icon, Color colorButton)

The signature is pretty much self explanatory.


PartCategorizer.AddCustomSubcategoryFilter(myFilter, "filter 1", myIcon, p => p.name.Contains("engine"));

and the signature...
/// <summary>
/// Adds a filter subcategory to the given filter which will appear on the right side of the toolbar
/// </summary>
/// <param name="mainFilter">The filter (Category) you are adding this filter to</param>
/// <param name="subFilterName">The name of the filter which will appear on the tooltip</param>
/// <param name="icon">The icon for the button</param>
/// <param name="exclusionFilter">Lambda expression which specifies which parts to show when this filter is active</param>
/// <returns>A reference to the Category wrapper</returns>
static public Category AddCustomSubcategoryFilter(Category mainFilter, string subFilterName, Icon icon, Func<AvailablePart, bool> exclusionFilter)

This is almost the same as the method to add a main filter. Except the exclusionFilter which is the whole point of the filter. In this simple example i have specified the expression p => p.name.Contains("engine"). All this does is filter out all parts where "engine" appears in the part name. "p" is a reference to AvailablePart and you can use any of it's fields to filter parts.

Some additional examples:

p => p.moduleInfos.Exists(q => q.moduleName == "Parachute")

This will filter out all parts that has a partModule called "Parachute".

p => p.resourceInfos.Exists(q => q.resourceName == "Liquid Fuel")

This will filter out all parts containing "Liquid Fuel".

p => p.manufacturer == "Probodobodyne Inc"

This will filter out all parts made by Probodobodyne Inc.

When to add custom filters:

The editor toolbar is ready to receive custom filters when this GameEvent is fired. GameEvents.onGUIEditorToolbarReady

How to add auto generated filters:

You may also want to make filters in a more automated way.


//instantiate the icon
Icon myIcon = new PartCategorizer.Icon("my icon", PartCategorizer.Instance.fallbackIcon.iconNormal,

PartCategorizer.Instance.fallbackIcon.iconSelected);

//add a custom filter to the toolbar
Category myFilter = PartCategorizer.AddCustomFilter("my resource filter", myIcon, Color.yellow);

//Get a unique list of available resources
List<String> resources = new List<string>();
foreach (AvailablePart ap in PartLoader.LoadedPartsList)
foreach (AvailablePart.ResourceInfo apr in ap.resourceInfos)
if (!resources.Contains(apr.resourceName))
resources.Add(apr.resourceName);

foreach (string str in resources)
{
string myResourceName = str; //It is imperitive to declare this
//add subcategories to the filter you just added
PartCategorizer.AddCustomSubcategoryFilter(myFilter, "filter by " + myResourceName, myIcon, p => p.resourceInfos.Exists(q => q.resourceName == myResourceName));
}

This is basically the same code we use to generate the stock resource filter and it filters parts by the resource it holds. Keep in mind, when you generate filters like this, it is important to declare the myResourceName string within the for loop.

Share this post


Link to post
Share on other sites

Some comments from my experience with making Filter Extensions

  • After adding a new category, occasionally they may have the GUI elements that appear in a user defined category (part drop zone, little red x's on each part). Force setting every new category to have a displayType of "PartsList" (code reference) prevents this.
  • The event is halted for all plugins if any errors occur which is rather annoying when your entire plugin depends on it. Polling PartCategorizer.Ready() inside the editor is more robust and Co-routines can be used to keep it relatively efficient (code reference). Whether that flag is set high before or after the event completes I have not investigated. (Update: The event completes first the first time the editor is entered. All subsequent entries have the flag being set first. Initialisation two frames after the flag is set seems to be free of any errors)
  • PartCategorizerButton.SetIcon can be used to change the icon of any category/subcategory, however the button needs to be refreshed before the new icon will be visible (code reference. Credit to stupid_chris for that section of code)
  • Adding a sub-category that has no parts available breaks the part selection pane (not tested with parts hidden by tech, but this does not seem to cause the same breakages as I haven't had any bug reports on it)
  • Don't ever add a new (sub)category and then attempt to edit it's icon in the same Update(). Things break, alot

Edited by Crzyrndm

Share this post


Link to post
Share on other sites

So other than a few minor niggles, working with the part filtering system has been a rather pleasant experience.

The 0.90 update also included a part sorting mechanism, but it seems to be much less open. Being able to alter the sorting system would be very useful so any insight into how the sorting is enacted would be useful

Share this post


Link to post
Share on other sites

Could someone help me to get this filter I'm working on to load? When I go to the VAB, I see no new buttons for filtering.


[KSPAddon(KSPAddon.Startup.MainMenu , true)]
public class II_Icons : MonoBehaviour
{
private void addIIfilter()
{
//Loading Textures
Texture2D unselected = new Texture2D(32, 32);
Texture2D selected = new Texture2D(32, 32);
unselected.LoadImage(File.ReadAllBytes("GameData/ImpossibleInnovations/Plugins/PluginData/SmallLogo.png"));
selected.LoadImage(File.ReadAllBytes("GameData/ImpossibleInnovations/Plugins/PluginData/SmallLogoON.png"));
RUI.Icons.Selectable.Icon filterIcon = new RUI.Icons.Selectable.Icon("II_filter_icon", selected, unselected); //Defining filterIcon


PartCategorizer.Category IIfilter = PartCategorizer.AddCustomFilter("Impossible_Innovations", filterIcon, Color.white);

//filters for all II parts, II Tanks, Engines, and CL-20 Boosters
PartCategorizer.AddCustomSubcategoryFilter(IIfilter, "All Impossible Innovations Parts", filterIcon, o => o.manufacturer == "Impossible Innovations");
PartCategorizer.AddCustomSubcategoryFilter(IIfilter, "Tanks", filterIcon, p => p.resourceInfos.Exists(q => q.resourceName == "Deuterium" || q.resourceName == "Tritium") && p.manufacturer == "Impossible Innovations");
PartCategorizer.AddCustomSubcategoryFilter(IIfilter, "Engines", filterIcon, r => r.name.Contains("Fusion Engine") && r.manufacturer == "Impossible Innovations");
PartCategorizer.AddCustomSubcategoryFilter(IIfilter, "CL-20 Boosters", filterIcon, s => s.resourceInfos.Exists(t => t.resourceName == "CL-20") && s.manufacturer == "Impossible Innovations");
}
}

Edited by jandcando
added code comments

Share this post


Link to post
Share on other sites

You're not subscribing addIIFilter to the GameEvent listed in the OP (In Awake: GameEvents.OnGUIEditorToolbarReady.Add(addIIFilter); )

Share this post


Link to post
Share on other sites
You're not subscribing addIIFilter to the GameEvent listed in the OP (In Awake: GameEvents.OnGUIEditorToolbarReady.Add(addIIFilter); )

Oh, sorry about that silly mistake. I'm still learning my way around you could say.

I'm surprised you replied so quickly. That's awesome.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0