Jump to content

PopupDialog and the DialogGUI classes


Recommended Posts

Ok here it goes. The following code should be self-contained so copy/paste into IDE  would work out of the box.

It is derived from my code which is a mess and posting it directly would not be helpful. I had to make a separate block of code in editor just to make sure no errors creep in.

This is demonstration of how to maintain top/left position of window while it changes size. No reset to coordinates given at SpawnPopupDialog(), no flickering during position update.

        public Vector3 popupOldPosition = Vector3.zero;
        public Vector2 popupOldSize = Vector2.zero;
        public Vector3 popupCorrectedPosition = Vector3.zero;
        public bool popupIsDragging = false;
        public bool doRefresh = false;
        public PopupDialog myDialog = null;
        public bool showDescription = false;

        public void ShowDialog()
        {
            myDialog = PopupDialog.SpawnPopupDialog(
                new MultiOptionDialog(
                    "visible title text", "", "",
                    HighLogic.UISkin,
                    new Rect(0.5f, 0.5f, 300f, 300f),
                    new DialogGUIVerticalLayout(
                        new DialogGUIButton("Close", () => { }, true),
                        new DialogGUIToggleButton(false, "show/hide", (b) => { showDescription = b; RecordOldPos(); }, 100f, 15f),
                        new DialogGUILabel("long text that needs to fit inside available width of window so it has to break on multiple lines", 100f, -1f)
                        {
                            OptionEnabledCondition = () => { return showDescription; }
                        }
                    )
                ), false, HighLogic.UISkin, false);

            myDialog.dialogToDisplay.OnResize = () =>
            {
                if (!popupIsDragging && myDialog != null && doRefresh)
                {
                    doRefresh = false;
                    RestoreOldPos();
                }
            };

            EventTrigger popupDialogEvent = myDialog.gameObject.AddOrGetComponent<EventTrigger>();

            EventTrigger.Entry popupBeginDrag = new EventTrigger.Entry() { eventID = EventTriggerType.BeginDrag };
            popupBeginDrag.callback.AddListener((e) => { popupIsDragging = true; });
            popupDialogEvent.triggers.Add(popupBeginDrag);

            EventTrigger.Entry popupEndDrag = new EventTrigger.Entry() { eventID = EventTriggerType.EndDrag };
            popupEndDrag.callback.AddListener((e) =>
            {
                popupIsDragging = false;
                RecordOldPos();
            });
            popupDialogEvent.triggers.Add(popupEndDrag);
        }

        public void RecordOldPos()
        {
            if (myDialog != null)
            {
                popupOldPosition = myDialog.RTrf.position;
                popupOldSize = myDialog.RTrf.rect.size;
            }
            doRefresh = true;
        }

        public void RestoreOldPos()
        {
            if (myDialog != null)
            {
                popupCorrectedPosition.x = popupOldPosition.x + (popupOldSize.x - myDialog.RTrf.rect.size.x) / 2;
                popupCorrectedPosition.y = popupOldPosition.y + (popupOldSize.y - myDialog.RTrf.rect.size.y) / 2;
                popupCorrectedPosition.z = popupOldPosition.z;
                myDialog.RTrf.position = popupCorrectedPosition;
            }
        }

 

Note the use of custom added events to properly capture window position. Until i understood the coordinate system used for positioning and size of window i couldn't make heads or tails from available positional data.

I'll add comments to code here instead of code box to keep it clean.

"showDescription" is used to set visibility of label via toogle button.

"popupIsDragging" is used for checking if dragging has ended (BeginDrag event is needed just to reinitializeset this variable. In this case it may appear superfluous, but may be necessary for more complex code that may update window contents during dragging operation).

"popupOldPosition" , "popupOldSize" and "popupCorrectedPosition" are used to capture old and calculate new coordinates to maintain visually position of window while it's contents change.

Now for the DRAWBACKS.

The RecordOldPos() function HAS to be manually inserted into callback for any UI element that may cause window resizing event. I have not found any callback that fires just before window resize event, if there was one, it would capture all changes in one neat function.

In above example a toggle button calls RecordOldPos() inside its onToggled() callback event.

Note that myDialog.dialogToDisplay.OnResize() callback first sets "doRefresh" to false then calls RestoreOldPos(). Ordering is important because it may trigger another OnResize() event. During initial testing i haven't added this check and managed to crash KSP by causing self-triggering.

The coordinate system used for positioning is centered inside display area, so setting window at (0,0) will put in middle. Also, a window transform is also put in center of window itself, not on any of corners.

ASCII art time :) Deptiction of center coordinates on "screen space". X and Y are positive going right and upward. Negative coordinates move to left and below of screen's center.

                    /|\ Y+

                      |

____________|___________

|                    |                     |

|                    |                     |

|                (0,0) ------------- |-------------> X+

|                                         |

|                                         |

|______________________|

Similar is with popup window's transform. It is also located at center of window and has same orientation.

Now, a few words of caution. While i tested my code, i managed to get window completely outside visible area.

It seems that authors intentionally added coordinate origin to center of screen, so if plugin sets window XY position to 0, it won't disappear. Also, whenever window is resized, its vital UI elements may end up outside visible area (for some rare cases for gigantic-sized windows) so it tries to reset to center, maximizing utility and minimizing loss of control. Bear in mind that modal windows will block user input and if they "fly" outside available screen space... Well, you get the picture :)

Edited by fatcargo
Link to comment
Share on other sites

  • 3 months later...

Hey, 

if you are trying to make an image button E2QXPhB.pngout of DialogGUIButton, here's this way to get it right!

DialogGUIImage img = new DialogGUIImage(new Vector2(32f, 32f), Vector2.zero, Color.white, someTexture);
DialogGUIHorizontalLayout imageBtnLayout = new DialogGUIHorizontalLayout(true, true, 0f, new RectOffset(1, 1, 1, 1), 
                                                                         TextAnchor.MiddleCenter, 
                                                                         new DialogGUIBase[]{ img }); //this is secret ingredient
DialogGUIButton btn= new DialogGUIButton("", onClick, 34, 34, false, 
                                         new DialogGUIBase[] { imageBtnLayout }); //DialogGUIButton takes in ui components

 

Edit:

In addition, if the vertical scrollbar of DialogGUIScrollList somehow starts in middle, it is because the pivot of the scroll area is 0.5 in both x and y by default (for unknown reason I have yet to determine)

You need to first access protected variable scrollRect of DialogGUIScrollList:

  1. Use C# Reflection approach 
  2. Make subclass of DialogGUIScrollList

and then update scrollRect variable (preferably after DialogGUI creation)with codes below.

this.scrollRect.content.pivot = new Vector2(0, 1); //set scrollbar to top

or

this.scrollRect.content.pivot = new Vector2(0, 0); //set scrollbar to bottom

 

Edited by TaxiService
Link to comment
Share on other sites

  • 1 year later...
On 1/29/2019 at 2:07 AM, maja said:

ESC closing all dialog windows and opening menu dialog is a default behaviour, so I just catch it and react to it to properly reset the state of my opened windows.

What API do you have to hit to cause it to display?  I tried hammering on `dialog.gameObject.SetActive(true)` in Update() and the window still goes away on an ESC.

Link to comment
Share on other sites

12 hours ago, Jim DiGriz said:

What API do you have to hit to cause it to display?  I tried hammering on `dialog.gameObject.SetActive(true)` in Update() and the window still goes away on an ESC.

 

That's not what I meant. "Close all windows on ESC" is expected default, but you need to tell to the game, that the window is closed. Otherwise you will need to push you app button twice, because the game actualy tries to close the window on the first push of button.

public void Update()
{
	// Escape was pressed -> close opened windows (set launcher state to false, so next time a window will be opened)
	if (Input.GetKeyDown(KeyCode.Escape))
	{
		if (appLauncherButton != null)
			appLauncherButton.SetFalse(true);
	}
}

Look here: https://github.com/jarosm/KSP-BonVoyage/blob/master/BonVoyage/BonVoyage.cs

Link to comment
Share on other sites

  • 10 months later...

So I tried this to show some simple data, no toolbar buttons or anything, just:

if (HighLogic.LoadedScene == GameScenes.FLIGHT)

in the Update function.

however it seems to be stopping me from clicking anything else, ie even hitting escape I can't click any of the buttons in that menu. had to force quit the game to get out.

Link to comment
Share on other sites

 

The one called every frame, how much of the dialog code needs to be called every frame though? the first example in this thread appears to have everything in there, however noone has discussed that here, it doesn't feel like an instantmode type gui.

https://gist.github.com/se5a/e56dbe2aa78336b9715d86b5f9517d48

 

 

Edited by se5a
Link to comment
Share on other sites

1 hour ago, se5a said:

Oh. Yeah, don't call SpawnPopupDialog every frame. That's going to display thousands of popups and freeze your game, as you've observed. Call it once when you first want the dialog to appear.

1 hour ago, se5a said:

the first example in this thread appears to have everything in there

This is the difference:

GameSettings.MODIFIER_KEY.GetKey() && Input.GetKeyDown(KeyCode.F11)

That example only calls SpawnPopupDialog if you press Alt-F11 (or Shift-F11 on Linux).

Edited by HebaruSan
Link to comment
Share on other sites

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