Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,23 @@ Setting Up Your Environment
* Click on the resulting link to go to that class's documentation page.
* The Unity documentation for that class will start with some faint grey text at the top of the page that says, "Implemented in:", which tells you which DLL you need to reference to get code using that class to compile properly.

3. If you do not have a copy of KSP locally, you may
3. Make sure your installation of KSP has LinuxGuruGamer's ClivkThroughBlocker
mod installed. kOS now needs it in order to compile. After it is
installed, copy its DLL file into Resources/ from your GameData folder.

Copy This file:

* $KSP/GameData/00_ClickThroughBlocker/Plugins/ClickThroughBlocker.dll

To here:

* Resources/

Then *make a Reference to Resoruces/ClickThroughBlocker.dll in your kOS project
file*. This is needed for it to let you compile parts of kOS that use classes
from ClickThroughBlocker.

4. If you do not have a copy of KSP locally, you may
download dummy assemblies at https://github.com/KSP-KOS/KSP_LIB

3. Make sure you are targeting this version of .Net: ".Net 4.0 Framework".
Expand Down
93 changes: 93 additions & 0 deletions src/kOS/Module/kOSCustomParameters.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using KSP.IO;
using System;
using System.Reflection;
using System.Linq;

namespace kOS.Module
{
Expand Down Expand Up @@ -46,6 +47,9 @@ public static kOSCustomParameters Instance
[GameParameters.CustomIntParameterUI("")]
public int version = 0;

[GameParameters.CustomParameterUI("")]
public bool passedClickThroughCheck = false;

// these values constrain and back the InstructionsPerUpdate property so that it is clamped both in the
// user interface and when set from within a script.
private const int ipuMin = 50;
Expand Down Expand Up @@ -202,6 +206,95 @@ public void CheckMigrateSettings()
}
}

public void CheckClickThroughBlockerExists()
{
if (passedClickThroughCheck)
return;
bool clickThroughExists = false;

var loadedCTBAssembly = AssemblyLoader.loadedAssemblies.FirstOrDefault(a => a.dllName.Equals("ClickThroughBlocker"));
if (loadedCTBAssembly != null)
{
// Must be at least version 0.10 of ClickThroughBlocker:
if (loadedCTBAssembly.versionMajor > 0 || loadedCTBAssembly.versionMinor >= 10)
{
Type ctbType = loadedCTBAssembly.assembly.GetType("ClickThroughFix.CTB", false);
if (ctbType != null)
{
if (ctbType.GetField("focusFollowsclick") != null)
{
clickThroughExists = true;
}
}
}
}

string popupText =
"=======================================\n" +
"<b><color=#ffffff>kOS is Checking for ClickThroughBlocker</color></b>\n" +
"=======================================\n\n" +
"Starting with kOS v1.3, kOS has become dependent on the existence of the ClickThroughBlocker mod. " +
"(And it must be at least version 0.10 of ClickThroughBlocker.)\n\n";

if (clickThroughExists)
{
popupText +=
" <b><color=#ddffdd><<<< CHECK SUCCEEDED >>>>></color></b>\n\n" +
"kOS has found ClickThroughBlocker installed, and it appears to be a version that will work with kOS.\n" +
"\n" +
"Please note that while in the past the kOS terminal has always been click-to-focus, from now " +
"on it will behave however ClickThroughBlocker is set to act, which may be focus-follows-mouse.\n" +
"You can use ClickThroughBlocker's settings to change this behvior like this:\n\n" +
"[Hit Escape] Settings ->\n" +
" Difficulty Options ->\n" +
" ClickThroughBlocker ->\n" +
" [x] Focus Follows Click\n\n";
}
else
{
popupText +=
" <b><color=#ffff88>!!! CHECK FAILED !!!</color></b>\n\n" +
"kOS couldn't find a version of ClickThroughBlocker that works with kOS. This could be " +
"because ClickThroughBlocker is not installed at all, or it could be because its version is too old " +
"(or too new, if ClickThroughBlocker ever renames some things that kOS is using).\n" +
"\n\n" +
"To use kOS v1.3 or higher you'll need to quit Kerbal Space Program and install a version of ClickThroughBlocker that it supports.\n";
}

string buttonText;
global::Callback clickThroughAck;
if (clickThroughExists)
{
clickThroughAck = AcceptClickThrough;
buttonText = "Acknowledged.";
}
else
{
clickThroughAck = FailedClickThrough;
buttonText = "Acknowledged. I'll have to quit and change my mods.";
}

kOSSettingsChecker.QueueDialog(
0.75f, 0.6f,
new MultiOptionDialog(
"ClickThroughBlockerCheck",
popupText,
"kOS ClickThroughBlocker Check",
HighLogic.UISkin,
new DialogGUIButton(buttonText, clickThroughAck, true)
));
}

public void AcceptClickThrough()
{
passedClickThroughCheck = true;
}

public void FailedClickThrough()
{
passedClickThroughCheck = false;
}

public void MigrateSettingsNormal()
{
MigrateSettings(false);
Expand Down
1 change: 1 addition & 0 deletions src/kOS/Module/kOSSettingsChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ private void CheckSettings()
SafeHouse.Logger.SuperVerbose("kOSSettingsChecker.CheckSettings()");
HighLogic.CurrentGame.Parameters.CustomParams<kOSCustomParameters>().CheckMigrateSettings();
HighLogic.CurrentGame.Parameters.CustomParams<kOSConnectivityParameters>().CheckNewManagers();
HighLogic.CurrentGame.Parameters.CustomParams<kOSCustomParameters>().CheckClickThroughBlockerExists();
}

// Because rapidly showing dialogs can prevent some from being shown, we can just queue up
Expand Down
5 changes: 5 additions & 0 deletions src/kOS/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,8 @@
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyVersion("1.2.1.0")]
[assembly: KSPAssembly("kOS", 1, 7)]

// No longer a hard dependancy, because we want to tell the user why kOS isn't working
// if ClickThroughBlocker is not there, rather than just have it silently refuse to
// load kOS with no explanation as would happen if this line was enabled:
// [assembly: KSPAssemblyDependency("ClickThroughBlocker", 1, 0)]
5 changes: 3 additions & 2 deletions src/kOS/Screen/DelegateDialog.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.Generic;
using ClickThroughFix; // Needs ClickThroughBlocker DLL to be in the Reference directory.
using UnityEngine;

namespace kOS.Screen
Expand Down Expand Up @@ -38,7 +39,7 @@ public void OnGUI()
if (invoked)
{
float guessWidth = GUI.skin.label.CalcSize( new GUIContent(message) ).x;
GUILayout.Window( parent.GetUniqueId()+1, new Rect( parent.GetRect().xMin+200,
ClickThruBlocker.GUILayoutWindow( parent.GetUniqueId()+1, new Rect( parent.GetRect().xMin+200,
parent.GetRect().yMin+10,
guessWidth,
0) , DrawConfirm, "Confirm", GUILayout.ExpandWidth(true) );
Expand Down
5 changes: 3 additions & 2 deletions src/kOS/Screen/GUIWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using kOS.Utilities;
using kOS.Communication;
using kOS.Safe;
using ClickThroughFix; // Needs ClickThroughBlocker DLL to be in the Reference directory.

namespace kOS.Screen
{
Expand Down Expand Up @@ -116,15 +117,15 @@ void OnGUI()

GUI.skin = HighLogic.Skin;

WindowRect = GUILayout.Window(UniqueId, WindowRect, WidgetGui, TitleText, style);
WindowRect = ClickThruBlocker.GUILayoutWindow(UniqueId, WindowRect, WidgetGui, TitleText, style);

if (currentPopup != null) {
var r = RectExtensions.EnsureCompletelyVisible(currentPopup.popupRect);
if (Event.current.type == EventType.MouseDown && !r.Contains(Event.current.mousePosition)) {
currentPopup.PopDown();
} else {
GUI.BringWindowToFront(UniqueId + 1);
currentPopup.popupRect = GUILayout.Window(UniqueId + 1, r, PopupGui, "", style);
currentPopup.popupRect = ClickThruBlocker.GUILayoutWindow(UniqueId + 1, r, PopupGui, "", style);
}
}
}
Expand Down
71 changes: 46 additions & 25 deletions src/kOS/Screen/KOSManagedWindow.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
using System.Collections.Generic;
using UnityEngine;
using ClickThroughFix; // Needs ClickThroughBlocker DLL to be in the Reference directory.

namespace kOS.Screen
{
/// <summary>
/// kOSManagedWindow is for any Unity Monobehavior that you'd like to
/// have contain a GUI.Window, and you need kOS to keep track of the
/// have contain an IMGUI Window, and you need kOS to keep track of the
/// window stacking for which click is on top of which other click.
/// Unity's built in systems for this don't work well at all, so
/// we had to make up our own.
///
/// Issue #2697 - In addition to KOS's own system to handle this,
/// This also is now layered on top of Linuxgurugamer's ClickThroughBlocker
/// mod, so it uses its wrappers around GUI.Window. That was needed because
/// if SOME mods use ClickThruBlocker windows, then those windows will get first
/// dibs on events before kOS gets to, making kOS helpless to intercept events it's
/// trying to protect other windows from seeing. ClickThruBlocker is a mod that
/// once some mods use it, then all the other mods have to as well.
/// </summary>
public abstract class KOSManagedWindow : MonoBehaviour
{
Expand Down Expand Up @@ -222,14 +231,13 @@ public bool IsOpen
}

/// <summary>
/// Pass in the absolute GUI screen location of a mouse click to decide whether
/// or not this widget gets keyboard focus because of that click.
/// (Clicking outside the window takes focus away. Clicking inside
/// the window gives focus to the window and brings it to the front.)
/// </summary>
/// Pass in the absolute GUI screen location of the mouse to decide whether
/// or not this window gets keyboard focus because of that position.
/// (If you want focus-follows-mouse logic, call this every Update(). If you
/// want click-to-focus logic, only call this in Update()s where a click just happened.)
/// <param name="absMousePos">Absolute position of mouse on whole screen</param>
/// <returns>True if the window got focused, false if it didn't.</returns>
public bool FocusClickLocationCheck(Vector2 absMousePos)
public bool FocusMouseLocationCheck(Vector2 absMousePos)
{
bool wasInside = false;
if (IsInsideMyExposedPortion(absMousePos))
Expand Down Expand Up @@ -286,42 +294,55 @@ public bool IsInsideMyExposedPortion(Vector2 posAbsolute)

/// <summary>
/// When you subclass KOSManagedWindow, make sure that you call this
/// from inside your Update. It does not use OnGUI because of the fact
/// that the OnGUI event handler is broken - it only sends MouseDown
/// and MouseUp events when the mouse is OUTSIDE the window, which is
/// utterly backward, and it's hard to work out how to fix this,
/// given how badly documented the Unity GUI API is. If anyone who
/// actually understands the Unity GUI system wants to fix this,
/// please feel free to do so.
/// from inside your Update() to check for focus change on the window.
/// Calling this will maybe call GetFocus() or LoseFocus() depending on
/// what the mouse is doing.
/// Note, you call this during *Update()*, NOT the OnGUI() call.
/// It does not use OnGUI() because the raw mousebutton state it
/// needs to see can get consumed and wiped by Unity's IMGUI widgets
/// before application code like this can see it.
/// </summary>
/// <returns>True if there was a mouseclick within this window.</returns>
public bool UpdateLogic()
public void UpdateLogic()
{
if (!IsOpen) return false;
if (!IsOpen) return;

// Input.mousePosition, unlike Event.current.MousePosition, puts the origin at the
// lower-left instead of upper-left of the screen, thus the subtraction in the y coord below:
mousePosAbsolute = new Vector2( Input.mousePosition.x, UnityEngine.Screen.height - Input.mousePosition.y);

// Mouse coord within the window, rather than within the screen.
mousePosRelative = new Vector2( mousePosAbsolute.x - windowRect.xMin, mousePosAbsolute.y - windowRect.yMin);

bool clickUp = false;

// Could maybe cache the CustomParams call once up front to get a reference to the CTB instance, then only
// repeat the ".focusFollowsclick" part each update. The reason that's not being done here is that I
// noticed ClickThroughBlocker's OWN code always does it like this, and for all I know there might be
// an important reason. It always gets this value by using the fully qualified long chain you see
// here, starting from HighLogic, each update. :
bool clickToFocus = HighLogic.CurrentGame.Parameters.CustomParams<ClickThroughFix.CTB>().focusFollowsclick;

if (Input.GetMouseButtonDown(0))
{
mouseButtonDownPosAbsolute = mousePosAbsolute;
mouseButtonDownPosRelative = mousePosRelative;
}

if (Input.GetMouseButtonUp(0))

bool mousePositionCanSetFocus = !(clickToFocus); // Always true in focus-follows-mouse mode

if (clickToFocus)
{
clickUp = true;
if (Vector2.Distance(mousePosAbsolute,mouseButtonDownPosAbsolute) <= dragTolerance)
if (Input.GetMouseButtonUp(0))
{
FocusClickLocationCheck(mousePosAbsolute);
if (Vector2.Distance(mousePosAbsolute, mouseButtonDownPosAbsolute) <= dragTolerance)
{
mousePositionCanSetFocus = true;
}
}
}
return IsInsideMyExposedPortion(mousePosAbsolute) && clickUp;

if (mousePositionCanSetFocus)
{
FocusMouseLocationCheck(mousePosAbsolute);
}
}
}
}
5 changes: 3 additions & 2 deletions src/kOS/Screen/KOSNameTagWindow.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using kOS.Utilities;
using kOS.Utilities;
using UnityEngine;
using kOS.Module;
using ClickThroughFix; // Needs ClickThroughBlocker DLL to be in the Reference directory.
using System;

namespace kOS.Screen
Expand Down Expand Up @@ -124,7 +125,7 @@ public void OnGUI()
EditorLogic.fetch.Lock(false, false, false, "KOSNameTagLock");

GUI.skin = HighLogic.Skin;
GUILayout.Window(myWindowId, windowRect, DrawWindow,"KOS nametag");
ClickThruBlocker.GUILayoutWindow(myWindowId, windowRect, DrawWindow,"KOS nametag");

// Ensure that the first time the window is made, it gets keybaord focus,
// but allow the focus to leave the window after that:
Expand Down
3 changes: 2 additions & 1 deletion src/kOS/Screen/KOSTextEditPopup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using kOS.Safe.Exceptions;
using kOS.Safe.Utilities;
using kOS.Module;
using ClickThroughFix; // Needs ClickThroughBlocker DLL to be in the Reference directory.

namespace kOS.Screen
{
Expand Down Expand Up @@ -170,7 +171,7 @@ public void OnGUI()
CalcOuterCoords(); // force windowRect to lock to bottom edge of the parents
CalcInnerCoords();

WindowRect = GUI.Window(UniqueId, WindowRect, ProcessWindow, "");
WindowRect = ClickThruBlocker.GUIWindow(UniqueId, WindowRect, ProcessWindow, "");
// Some mouse global state data used by several of the checks:

if (consumeEvent)
Expand Down
4 changes: 3 additions & 1 deletion src/kOS/Screen/KOSToolbarWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using ClickThroughFix; // Needs ClickThroughBlocker DLL to be in the Reference directory.


namespace kOS.Screen
{
Expand Down Expand Up @@ -443,7 +445,7 @@ public void OnGUI()

GUI.skin = HighLogic.Skin;

windowRect = GUILayout.Window(UNIQUE_ID, windowRect, DrawWindow, "kOS " + versionString);
windowRect = ClickThruBlocker.GUILayoutWindow(UNIQUE_ID, windowRect, DrawWindow, "kOS " + versionString);
windowRect = RectExtensions.ClampToRectAngle(windowRect, rectToFit);
}

Expand Down
6 changes: 4 additions & 2 deletions src/kOS/Screen/ListPickerDialog.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Collections.Generic;
using UnityEngine;
using ClickThroughFix; // Needs ClickThroughBlocker DLL to be in the Reference directory.


namespace kOS.Screen
{
Expand Down Expand Up @@ -110,7 +112,7 @@ public void OnGUI()

// Make sure it shifts enough to the left to fit the biggest string:
outerWindowRect.x = Mathf.Min(outerWindowRect.x, UnityEngine.Screen.width - outerWindowRect.width - 60);
outerWindowRect = GUILayout.Window(
outerWindowRect = ClickThruBlocker.GUILayoutWindow(
title.GetHashCode(),
outerWindowRect,
DrawInnards,
Expand Down
Loading