I noticed whenever I click a toolbar button on one toolbar, two buttons (in different windows and in different toolbars) show as being clicked at the same time!
I can’t figure out how or why they are somehow connected! I have made sure that each of the windows has a unique window ID.
After further testing, I have found that it is the button that is horizontally closest to the button I am actually clicking that seems to also get the mouse down event. Here are some images to demonstrate the problem behavior:
- These are the windows.
- I click “Model” and “Serial” also shows that it is being clicked.
- I click “Model” again, this time underneath “Job” and now “Job” shows as being clicked.
- This time I click “Type”. But again, “Job” shows as being clicked.
- I click “Type” again, this time underneath “Part” and now “Part” shows as being clicked.
- One last test to really drive home the problem: I relocate the window and click “Model” again. Instead of “Serial” showing as being clicked, “Part” shows as being clicked.
Even more strange is the fact that the button I didn’t click doesn’t actually select despite the appearance of being clicked! You can see this in the pictures (“Serial” stays selected despite “Job” and “Part” showing as depressed while I click other buttons below them).
I’m using Unity version 3.5.7f6, and here is the code:
MethodExtensions.cs
This is just for reference since it is actually called in the GUI classes. It simply returns the size of the widest GUIContent given a particular style.
using UnityEngine;
using System.Collections;
public static class MethodExtensions {
/**
* Return the size of the widest content given a style.
*/
public static Vector2 SizeOfWidest(this GUIStyle style, params string[] items) {
if (items.Length > 0) {
Vector2 widest = style.CalcSize(new GUIContent(items[0]));
foreach (string e in items) {
Vector2 size = style.CalcSize(new GUIContent(e));
widest.x = System.Math.Max(widest.x, size.x);
}
return widest;
}
return Vector2.zero;
}
}
GUIWindow.cs
This is the abstract base class for all of my GUI windows.
using UnityEngine;
using System.Collections;
public abstract class GUIWindow : MonoBehaviour {
public GUISkin skin;
public Rect rect;
public int padding = 10;
// True if this window should be drawn.
private bool draw;
// Unique among all registered windows.
private int id;
/**
* Obtain a unique window ID and register this window with the GUIWindowManager.
*/
public virtual void Start() {
this.id = GUIWindowManager.NewId;
this.draw = true;
GUIWindowManager.Register(this);
}
/**
* Draw this window if drawing is enabled.
*/
public void Draw() {
GUI.skin = this.skin;
if (this.draw) {
this.rect = GUI.Window(this.id, this.rect, WindowFunction, "");
}
}
/**
* Unique window ID.
*/
public int Id {
get { return this.id; }
}
/**
* Sets the position of this window.
*/
public void Position(Rect rect) {
this.rect.Set(rect.xMin, rect.yMin, rect.width, rect.height);
}
/**
* Turn off the drawing of this window.
*/
public void Hide() {
this.draw = false;
}
/**
* Turn on the drawing of this window.
*/
public void Show() {
this.draw = true;
}
/**
* Draws the content in the style such that it is centered at the top of the window.
*/
protected Vector2 DrawTitle(GUIContent content, GUIStyle style) {
Vector2 size = style.CalcSize(content);
GUI.Label(new Rect(0, 0, this.rect.width, size.y), content, style);
return size;
}
/**
* Window drawing function.
*/
protected abstract void WindowFunction(int id);
}
GUIWindowManager.cs
This class serves the purpose of drawing the windows in the correct order. Note that I have yet actually use that functionality. Currently it simply draws all of the windows in the order they were registered (since I don’t ever change the order).
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GUIWindowManager : MonoBehaviour {
private static int nextId = 0;
private static Dictionary<int, GUIWindow> windows = new Dictionary<int, GUIWindow>();
private static List<int> drawOrder = new List<int>();
public static int NewId {
get { return nextId++; }
}
public static void Register(GUIWindow window) {
if (windows.ContainsKey(window.Id)) {
Debug.Log("Window attempted duplicate registration with the window manager!");
} else {
windows.Add(window.Id, window);
drawOrder.Add(window.Id);
}
}
public static void DrawFirst(int windowId) {
if (windows.ContainsKey(windowId)) {
drawOrder.Remove(windowId);
drawOrder.Insert(0, windowId);
}
}
void OnGUI() {
foreach (int id in drawOrder) {
windows[id].Draw();
}
}
public GUIWindow Get(int id) {
if (windows.ContainsKey(id)) {
return windows[id];
}
return null;
}
}
SystemSearchGUI.cs
This class is responsible for drawing the System Search GUI.
using UnityEngine;
using System.Collections;
public class SystemSearchGUI : GUIWindow {
private int toolbarSelection = 0;
private string[] toolbarContents = new string[] {"Serial", "Job", "Part"};
private string input = "";
protected override void WindowFunction(int id) {
// Draw the title and get its size.
Vector2 titleSize = this.DrawTitle(new GUIContent("System Search"), GUI.skin.GetStyle("title"));
// Calculate the amount of padding for 2 borders, then use it to find the available width (inner width after
// padding is accounted for).
float doublePadding = 2 * this.padding;
float innerWidth = this.rect.width - doublePadding;
// Draw the toolbar below the title with padding space in between.
toolbarSelection = GUI.Toolbar(
new Rect(padding, titleSize.y + this.padding, innerWidth, titleSize.y),
toolbarSelection,
toolbarContents);
// Calculate the size of the search button. Add padding to it's width.
Vector2 buttonSize = GUI.skin.button.CalcSize(new GUIContent("Search"));
buttonSize.x += doublePadding;
// Calculate the position of the next row.
float row = 2 * (titleSize.y + this.padding);
// Calculate the size of the text field, but replace the calculated width with whatever space is left in the
// inner width after the button and padding have been subtracted.
Vector2 textfieldSize = GUI.skin.textField.CalcSize(new GUIContent(this.input));
textfieldSize.x = innerWidth - buttonSize.x - this.padding;
// Draw the text field.
this.input = GUI.TextField(
new Rect(this.padding, row, textfieldSize.x, textfieldSize.y),
this.input);
// Draw the button.
if (GUI.Button(new Rect(doublePadding + textfieldSize.x, row, buttonSize.x, buttonSize.y), "Search")) {
Debug.Log(
string.Format("Searching for systems with {0} # {1}",
this.toolbarContents[this.toolbarSelection], this.input));
}
// Update the size of this window to fit the contents vertically, then make it draggable.
this.rect.height = row + buttonSize.y + this.padding;
GUI.DragWindow();
}
}
PartSearchGUI.cs
And lastly, this is the class for the Part Search GUI.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PartSearchGUI : GUIWindow {
private string[] tabs = new string[] {"Single", "Multiple"};
private string[] subTabs = new string[] {"Model", "Type"};
private int tabId = 0;
private int subTabId = 0;
private string serialNumber = "";
protected override void WindowFunction(int id) {
// Draw the title.
Vector2 titleSize = this.DrawTitle(new GUIContent("Part Search"), GUI.skin.GetStyle("title"));
// Draw the top tabs.
tabId = GUI.Toolbar(
new Rect(this.padding, titleSize.y + this.padding, this.rect.width - 2 * this.padding, titleSize.y),
tabId,
tabs);
float y = 2 * (titleSize.y + this.padding);
// Draw different content depending on which tab is selected.
switch (tabId) {
case 0:
y = DrawSinglePartSearch(y);
break;
case 1:
y = DrawMultiplePartSearch(y);
break;
default:
Debug.LogError("Invalid tabId value of " + tabId);
break;
}
y += this.padding;
// Find the size of the widest button.
Vector2 buttonSize = GUI.skin.button.SizeOfWidest("Cancel", "Search");
// Forget that for now - make them as wide as possible.
buttonSize.x = (this.rect.width - 3 * this.padding) / 2.0f;
// Draw the Cancel and Search buttons.
if (GUI.Button(new Rect(this.padding, y, buttonSize.x, buttonSize.y), new GUIContent("Cancel"))) {
this.Hide();
Invoke("Show", 3.0f);
}
if (GUI.Button(new Rect(this.rect.width - this.padding - buttonSize.x, y, buttonSize.x, buttonSize.y), new GUIContent("Search"))) {
Debug.Log("Searching for one or more parts.");
}
// Update the height of this window so that it's contents fit just right.
this.rect.height = y + buttonSize.y + this.padding;
GUI.DragWindow();
}
/**
* Draw the form controls for searching for a single part.
*/
private float DrawSinglePartSearch(float y) {
// Create the content.
GUIContent labelContent = new GUIContent("Part Serial #:");
GUIContent textContent = new GUIContent(this.serialNumber);
// Determine the size of things.
Vector2 labelSize = GUI.skin.label.CalcSize(labelContent);
Vector2 textSize = GUI.skin.textField.CalcSize(textContent);
// Use the tallest one as the height.
labelSize.y = Mathf.Max(labelSize.y, textSize.y);
// Make the textfield extend to the edge of the window.
textSize.x = this.rect.width - (3 * this.padding) - labelSize.x;
// Draw the label and textfield.
GUI.Label(new Rect(this.padding, y, labelSize.x, labelSize.y), labelContent);
serialNumber = GUI.TextField(
new Rect(2 * this.padding + labelSize.x, y, textSize.x, labelSize.y),
serialNumber);
// Let the calling code know where to place the next control.
return y + labelSize.y + this.padding;
}
/**
* Draw the form controls for searching for multiple parts.
*/
private float DrawMultiplePartSearch(float y) {
// Doesn't matter about what the content is... just need height.
Vector2 subTabSize = GUI.skin.button.CalcSize(new GUIContent(subTabs[0]));
// Draw the second row of tabs.
subTabId = GUI.Toolbar(
new Rect(this.padding, y, this.rect.width - 2 * this.padding, subTabSize.y),
subTabId,
subTabs);
y += subTabSize.y + this.padding;
// TODO: Finish drawing the form controls for multiple part search.
GUIContent modelNumberLabel = new GUIContent("Part Model #");
// Let the calling code know where to place the next control.
return y;
}
}
I’m now pouring over the GUI.Toolbar code in the MonoDevelop’s Assembly Browser… we’ll see what fruit this yields, if any.

line 7 of the system search GUI has a empty statment " ".disclaimer this could mean nothing i am just pointing out something i saw.
– greencvbnOn my screen line 7 reads:
– TinkerinThinkerprivate string input = "";