Jump to content

Application Launcher and Mods


Romfarer

Recommended Posts

The Application Launcher or applauncher is one of the new GUI features for KSP 0.24. This GUI is supposed to work as a toolbar for stock apps/widgets and mods. Subsequently it is divided into two lists separated with a border. On the right hand you have the stock ksp apps and on the left hand you have the mod apps.

The list of mod applications is a little bit different from the stock list in the sense that it is able to hold as many mods as you want. Therefore it will also rest at a fixed size when the list gets to big for the gui. When this happens it will become scrollable. By design the stock apps are also mutually exclusive: only one app can stat visible at any given time. It is possible to make your mod share this behaviour. The details follows below.

The goal of this guide is to explain how to use the applauncher with your mod.

Requirements:

In order to be able to use the applauncher in your mod you must include Assembly-CSharp-firstpass.dll when building your project. It is located in the same folder the regular ksp dll is in and you should already be familiar with how to include it. The reason is because we allow for animated buttons and they require a class which is located in that library.

Add your mod to the applauncher:

First of all you retrieve the applauncher instance with the static flag ApplicationLauncher.Instance.

There are two methods to use for adding a mod to the applauncher:

 

 
    /// <summary>
    /// Add a MOD(3rd party) application to the Application Launcher. Use ApplicationLauncherButton.VisibleInScenes to set where the button should be displayed.
    /// </summary>
    /// <param name="onTrue">Callback for when the button is toggeled on</param>
    /// <param name="onFalse">Callback for when the button is toggeled off</param>
    /// <param name="onHover">Callback for when the mouse is hovering over the button</param>
    /// <param name="onHoverOut">Callback for when the mouse hoveris off the button</param>
    /// <param name="onEnable">Callback for when the button is shown or enabled by the application launcher</param>
    /// <param name="onDisable">Callback for when the button is hidden or disabled by the application launcher</param>
    /// <param name="visibleInScenes">The "scenes" this button will be visible in. For example VisibleInScenes = ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW;</param>
    /// <param name="texture">The 38x38 Texture to use for the button icon.</param>
    /// <returns></returns>
    public ApplicationLauncherButton AddModApplication(RUIToggleButton.OnTrue onTrue, RUIToggleButton.OnFalse onFalse, RUIToggleButton.OnHover onHover, RUIToggleButton.OnHoverOut onHoverOut, RUIToggleButton.OnEnable onEnable, RUIToggleButton.OnDisable onDisable, ApplicationLauncher.AppScenes visibleInScenes, Texture texture)

and

    /// <summary>
    /// Add a MOD(3rd party) application to the Application Launcher. Use ApplicationLauncherButton.VisibleInScenes to set where the button should be displayed.
    /// </summary>
    /// <param name="onTrue">Callback for when the button is toggeled on</param>
    /// <param name="onFalse">Callback for when the button is toggeled off</param>
    /// <param name="onHover">Callback for when the mouse is hovering over the button</param>
    /// <param name="onHoverOut">Callback for when the mouse hoveris off the button</param>
    /// <param name="onEnable">Callback for when the button is shown or enabled by the application launcher</param>
    /// <param name="onDisable">Callback for when the button is hidden or disabled by the application launcher</param>
    /// <param name="visibleInScenes">The "scenes" this button will be visible in. For example VisibleInScenes = ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.MAPVIEW;</param>
    /// <param name="sprite">The 38x38 PackedSprite animation to use for the button icon. Use ApplicationLauncherButton.PlayAnim() to play the animation.</param>
    /// <returns></returns>
    public ApplicationLauncherButton AddModApplication(RUIToggleButton.OnTrue onTrue, RUIToggleButton.OnFalse onFalse, RUIToggleButton.OnHover onHover, RUIToggleButton.OnHoverOut onHoverOut, RUIToggleButton.OnEnable onEnable, RUIToggleButton.OnDisable onDisable, ApplicationLauncher.AppScenes visibleInScenes, PackedSprite sprite)

 

As you can see the only difference is in the last parameter Texture texture vs PackedSprite sprite. The first one is for textures and you should already be familiar with it. The second is for animations. Both of these are sized at 38x38 pixels.

The callbacks listed are used mainly for showing and hiding the gui associated with the applauncher button. You can safely set most of them to null but you should at least define a delegate for the onTrue and onFalse callbacks.

Note that the visibleInScenes is a reference to the [system.Flags] ApplicationLauncher.AppScenes and it has the following options: NEVER, SCPACECENTER, FLIGHT, MAPVIEW, VAB, SPH, ALWAYS. You can make your mod button display on the application launcher automatially by setting this. For example:

 

 
VisibleInScenes = ApplicationLauncher.AppScenes.FLIGHT | ApplicationLauncher.AppScenes.SPACECENTER;

 

This will make your button show in flight and in the spacecenter. It is also possible to handle the hiding and showing of your mod button manually if you really want to. To pull this off you need to manually listen to other ksp events, the most important one being GameEvents.onGameSceneLoadRequested. This is fired at the point when applauncher becomes !Ready.

Remove your mod from the applauncher:

Removing an app from the applauncher is a lot easier than adding. Simply keep a reference to your button and make the following method call.

 

 
ApplicationLauncher.Instance.RemoveModApplication(ApplicationLauncherButton button)

 

When to add it:

Applauncher becomes alive and is running when going from mainmenu to spacecenter. It has a static flag ApplicationLauncher.Ready which indicates when it is ready to receive add/remove commands. The event GameEvents.onGUIApplicationLauncherReady will fire when this happens.

Applauncher will destroy itself when exiting to the mainmenu. GameEvents.onGUIApplicationLauncherDestroyed will be fired when this happens.

If you are using applauncher with a mod that uses a partless loader, for example by adding this attribute to your class: [KSPAddon(KSPAddon.Startup.MainMenu, true)]. It is important that you listen to both events and re-add your button when necessary.

Applauncher callbacks:

If you want to use the application launcer in multiple scenes AND OR only in the space center scene you should also subscribe to the following events to make your mod gui hide with the launcher. These events are fired when the application launcher shows/hides itself. You can also pull this off by making the transform of your mod gui a child of the applauncher.

 

 
AddOnShowCallback(OnShow del)
RemoveOnShowCallback(OnShow del)
AddOnHideCallback(OnHide del)
RemoveOnHideCallback(OnHide del)

 

All of these delegates have no parameters.

If you use some form of "docking" (positioning your mod gui directly below the applauncher) for your mod gui you may also want to subscribe to the following events:

 

 
AddOnRepositionCallback(OnShow del)
RemoveOnRepositionCallback(OnShow del)

 

This is usefull because the applauncher shows up in the bottom right position in the VAB/SPH. These callbacks are fired when it reposition and there is also a flag (IsPositionedAtTop) you can check to figure out where it is positioned. You can determine what position your gui should take by using the position field on the transform of your modApplicationButton, some trial and error is manditory. Also keep in mind that when the list of mod buttons in applauncher gets big it will become scrollable and depending on where your button is in the list, it is likely that it will be completely hidden at some points so it is reccommended to have the gui at a static position.

Make your mod app mutually exclusive along with the stock KSP apps:

In KSP 0.24.1 i added two methods you can use to make your mod mutually exclusive along with the stock KSP apps. This means that only one app can be visible at any given time. It makes use of the onTrue, onFalse, onHover and onHoverOut callbacks, so it is important that you subscribe to those callbacks if you want to use this feature. The methods are as follows:

 

 
public void EnableMutuallyExclusive(ApplicationLauncherButton launcherButton)
public void DisableMutuallyExclusive(ApplicationLauncherButton launcherButton)

 

Advanced use:

 

It is possible to hijack ksp stock buttons by taking over all the delegates in them or by getting a reference to the button and replacing it with your own. If you take the latter approach it is however likely that you will modify the order the stock apps are displayed in.

 

//Get the buttons like this ApplicationLauncherButton button = ResourceDisplay.Instance.appLauncherButton; ApplicationLauncherButton button = ContractsApp.Instance.appLauncherButton; ApplicationLauncherButton button = MessageSystem.Instance.appLauncherButton; //Then make sure the app you want to take over is hidden or disabled. Just call: button.onDisable(); //take over the delagates like this button.toggleButton.onTrue = yourDelegate; button.toggleButton.onFalse = yourDelegate; button.toggleButton.onHover = yourDelegate; button.toggleButton.onHoverOut = yourDelegate; button.toggleButton.onEnable = yourDelegate; button.toggleButton.onDisable = yourDelegate;

 

 

Keep in mind that there are probably parts of the code that will mess things up for you if you try to take over the button like this. For example the delegates used to hide and show the app when the applauncher shows so all in all you might just want to remove the button and replace it with your own, after all the system was not intended to be used like this. But do let me know how this works out.

The currency widget app uses a slightly different system so it doesn't have a reference to the button atm. I wanted to include that and refactor a lot of other parts of the code but it will require testing to make sure it's stable and there simply wasn't time in this update.

 

Finally, the Application Launcher will most likely get updates in the coming versions. Suggestions are most welcome.

Edited by NecroBones
spelling in stickied title
Link to comment
Share on other sites

The change notes for .24.1 seem to indicate that this issue has been resolved:

http://steamcommunity.com/games/220200/announcements/detail/164697537330848761

Does this mean it is no longer necessary to hook these events, you can just use AddModApplication on startup and be done with it?

GameEvents.onGUIApplicationLauncherReady

GameEvents.onGameSceneLoadRequested

Link to comment
Share on other sites

The change notes for .24.1 seem to indicate that this issue has been resolved:

http://steamcommunity.com/games/220200/announcements/detail/164697537330848761

Does this mean it is no longer necessary to hook these events, you can just use AddModApplication on startup and be done with it?

What you should do is this, when starting up: check if ApplicationLauncher.Ready. If it is not ready just subscribe to the event GameEvents.onGUIApplicationLauncherReady. The applauncher starts up when you transition into space center from mainmenu and is destroyed when you exit to mainmenu. And it is Ready when it becomes alive in a scene, it becomes !Ready when you transition into a new scene.

But of course, it's probably going to work even it it's not Ready, feel free to experiment and let me know what you discover ;-)

More details will follow as i edit this thread.

Link to comment
Share on other sites

Just bringing some feedback for you on some of the coding fun :)

MutuallyExclusive stuff

Enabling and Disabling all works a treat. I did find one challenge though - if you change the setting when apps are open, ie. their buttons are toggled on, you seem to have to toggle all the buttons to get the launcher in a desired state (or restart the scene). A good example is to have a stock App toggled on and a mod app toggled on and then enable exclusivity on the mod app - you'll still have two apps toggled on, and weird things going on till you get the apps in a state where the exclusivity can look after itself

Turning toggles and off

While trying to work with the challenge above I was trying to reset the toggle state programmatically when that happened and I struck a couple of issues: ApplicationLauncherButton.SetTrue/False and ApplicationLauncherButton.togglebutton.SetTrue/False . These didnt actually change the toggle state of the Apps for me. calling onTrue and onFalse would trigger the events, but not change the state of the buttons (obviously as they are the events)

Replacing a stock App

Your suggestion of hijacking the events worked quite well for me with the Alternate Resource Panel - managed to replace the stock app and all seems to be working well. The things I had to work on were:

  • I had no easy way to store the existing App functions to then restore it back if I wanted to in the scene - I left a message for the user so they know whats needed
  • Trying to replace the app when the launcher was ready was no good for me - as the app wasnt loaded yet - Used a time delay and check
  • I did find it easier to remove the App and add my own as a stock app, but it reshuffled the button order - so I avoided that

For these it would make it easier (for me anyway :) ) if the Apps had an event fire when they had loaded, if you could toggle the visibility of an app and change its position in the order - then the apps would be there the whole time and just displaying em leads to less event challenges.

Enough complaining :wink: , has been a good outing with it all with only a couple of challenges. Thanks for the excellent documentation, I would have been struggling without it

Link to comment
Share on other sites

It is also possible to handle the hiding and showing of your mod button manually if you really want to. To pull this off you need to manually listen to other ksp events, the most important one being GameEvents.onGameSceneLoadRequested. This is fired at the point when applauncher becomes !Ready.

It seems like ApplicationLauncher.Ready always returns false during an onGameSceneLoadRequested callback. This makes it impossible to remove buttons via RemoveModApplication() if it only does so when Ready==true.


private void Awake() {
GameEvents.onGameSceneLoadRequested.Add(onGameSceneLoadRequested);
}

private void onGameSceneLoadRequested(GameScenes scene) {
Debug.Log(string.Format("app launcher ready: {0}", ApplicationLauncher.Ready)); // always prints "False"
}

Edited by blizzy78
added example code
Link to comment
Share on other sites

  • 2 weeks later...

It should be mentioned at this point that the hovereffect is done by changing the buttons background image of the button, meaning that you will not get any at all should you make the mistake (like I did) of using a fully opaque image.

Took me 2 days to figure this out now.

It probably offers it's advantages by being able to use a custom hovereffect should you want to but given that this would require custom code for switching the image could the stock hovereffect as well be a layer on top of the button that get's turned visible when hovered and a setting to disable that.

Just my feedback so far from trying to make ORDA use the toolbar. Aside of this little witch hunt for the reason why the hovereffect was missing is it very easy to use.

Link to comment
Share on other sites

  • 2 weeks later...

It's been mentioned a few times on the Toolbar plugin topic, might as well bring the discussion here (on a friendly tone, as constructive criticism). Things to learn from Blizzy's Toolbar:

  • Smaller, unobtrusive icons
  • Folders
  • Configurable position, hide on border
  • Configurable button order / visible status
  • Multiple bars

Of these, I find the two first items the most needed. The current size might be OK for stock apps, but once you just start stockpiling mods, and with no folders to group them, thing gets pretty unwieldy. Having folders also mitigates need for multiple bars and configurable button visible status (just make a "Trash" folder for unwanted buttons).

In any case, a big stock toolbar is better than no stock toolbar.

[edit]

The lack of the AppLauncher in the tracking station is unfortunate. Hopefully that can be changed soon.

Oh, also this.

No preview button on edit post?

Link to comment
Share on other sites

It's been mentioned a few times on the Toolbar plugin topic, might as well bring the discussion here (on a friendly tone, as constructive criticism). Things to learn from Blizzy's Toolbar:

  • Smaller, unobtrusive icons

The larger icons in stock are very useful for players to learn the game. I think it would need to be a toggle (I think it might be time for the UI size slider in settings to get split into several options - overall size for UI, size for just navball and option for large or small app icons). Of course offering two sizes might require some additions to help authors know where to position windows in relation to the icons size their size might now be variable.

Oh, also this.

No preview button on edit post?

Press 'Go Advanced' to switch from the quick edit to full sized edit/preview/etc.

Link to comment
Share on other sites

It seems like ApplicationLauncher.Ready always returns false during an onGameSceneLoadRequested callback. This makes it impossible to remove buttons via RemoveModApplication() if it only does so when Ready==true.


private void Awake() {
GameEvents.onGameSceneLoadRequested.Add(onGameSceneLoadRequested);
}

private void onGameSceneLoadRequested(GameScenes scene) {
Debug.Log(string.Format("app launcher ready: {0}", ApplicationLauncher.Ready)); // always prints "False"
}

Ok the issue here is that the Application Launcher hides itself in GameEvents.onGameSceneLoadRequested as well. In 0.25 i'm therefore adding a second level event which will fire just before the Application Launcher is about to become !Ready. The event looks like it will be called GameEvents.onGUIApplicationLauncherUnreadifying and it will send with it the GameScene it is about to enter just like onGameSceneLoadRequested.

Link to comment
Share on other sites

Ooh, thats a new one - would sort of indicate to me that the scene changing event didn't fire that is where we all destroy the buttons - or the ready event fired twice creating extra's. As its all the buttons I wonder if theres any extra logging we could see in that situation, or any steps that cause it to happen

Link to comment
Share on other sites

Ooh, thats a new one - would sort of indicate to me that the scene changing event didn't fire that is where we all destroy the buttons - or the ready event fired twice creating extra's. As its all the buttons I wonder if theres any extra logging we could see in that situation, or any steps that cause it to happen

Here's some behavior notes the screenshot alone may not capture:

  • This is on OSX (I don't know if it's platform specific).
  • Duplicates don't seem to function properly when clicked.
  • Duplicates only exist when controlling an active craft.
  • They go away at the screen of the space center buildings. But upon returning to a craft, will reappear.
  • When duplicates happen, the windows associated with those buttons (FAR/KER) tend to reset to default window positions (center of screen).
  • Restarting the game fixes it.

I'd like to offer a log of this happening, but of course now that I want it to reproduce it, it's decided to be uncooperative. Given some time it's bound to reoccur, as I've seen it happen too many times.

Edited by Entropius
spelling/grammar
Link to comment
Share on other sites

Okay, now I finally have a log demonstrating the duplicate application-launcher buttons (took a while).

And here's another screenshot:

nfIhBpz.png

Curiously, I noticed Chatterer's button was unaffected.

One minor thing I should correct about my previous description of it's behavior: The duplicate buttons do appear to work. I guess I misremembered that part for some reason.

Link to comment
Share on other sites

  • 3 months later...

Make your mod app mutually exclusive along with the stock KSP apps:

In KSP 0.24.1 i added two methods you can use to make your mod mutually exclusive along with the stock KSP apps. This means that only one app can be visible at any given time. It makes use of the onTrue, onFalse, onHover and onHoverOut callbacks, so it is important that you subscribe to those callbacks if you want to use this feature. The methods are as follows:


public void EnableMutuallyExclusive(ApplicationLauncherButton launcherButton)
public void DisableMutuallyExclusive(ApplicationLauncherButton launcherButton)

Is anyone else using this Mutually exclusive bit - I'm sure I had it working at some point, but am struggling with it now

Link to comment
Share on other sites

  • 1 month later...
Just bringing some feedback for you on some of the coding fun :)

...

Turning toggles and off

While trying to work with the challenge above I was trying to reset the toggle state programmatically when that happened and I struck a couple of issues: ApplicationLauncherButton.SetTrue/False and ApplicationLauncherButton.togglebutton.SetTrue/False . These didnt actually change the toggle state of the Apps for me. calling onTrue and onFalse would trigger the events, but not change the state of the buttons (obviously as they are the events)

...

I've set up dynamic switching between Blizzy's Tool bar and the stock Toolbar in Ship Manifest (KSP .90, 32 bit). However I seem to be running into the above issue reported by TriggerAu.

I attempt to set the button state using .SetTrue(), but it does not seem to go to the true state... I must click the button twice to return to a false state (to close an already open app window). (I'm automatically detecting if the app is currently open during the toolbar switch and attempting to set the applauncher button state appropriately.

Am I missing something? has this been resolved?

My testing shows that after the .SetTrue() function is called, the OnTrue callback function is called, and the button.state changes to true, but still it requires 2 clicks to return the button state back to false. In fact, the first click fires the OnTrue callback. Note that I've just completed instantiating the buttons when the call for SetTrue is made.

sample code


Settings.ShowRoster = true; // This is a flag to notify the window display code to activate the window, or in this case, leave it open...
SMRoster_Stock.SetTexture((Texture)GameDatabase.Instance.GetTexture(Settings.ShowRoster ? ImageFolder + "IconR_On_38" : ImageFolder + "IconR_Off_38", false));
SMRoster_Stock.toggleButton.SetTrue();

Ok, I've solved my problem, but the fix was to ignore the button state. Since I manage the window state using another flag, I now use that flag exclusively, and set both the OnTrue and OnFalse callbacks to the same handler. I then interrogate the window state and make the necessary testure changes in that method. Now I have the behavior I desire, and the actual button state no longer matters.

Edited by Papa_Joe
Link to comment
Share on other sites

  • 2 months later...
This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

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...