Author Topic: Question about the "right" way to ensure my widget remains on screen  (Read 5463 times)

wom

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 34
    • View Profile
TL:DR - what's the "right" way to make sure a widget will always be on screen, even when the screen resolution changes between runs or other crazy things go wrong?

I've created a modular movable, resizable "window" with scrollable content using new NGUI layout stuff - (really like the new layout system, BTW).  I'm also saving the positions of these windows to the PlayerPrefs.

The UIDragObject has a "keep visible" option, along with a "content rect" to make sure the user doesn't move the object "off-screen".
But I've found a few situations where that's not sufficient for me (basically, whenever the window or view port is changing, but the window is not actually being moved):
- if I resize the window off of the viewport, part of it will be clipped and then I can't interact with, for example, my move or resize widgets.
- if I am changing the UIDragResize minimum width/height (if the UIDragResize had smaller minimums that a window happens to have, then I make the minimums bigger, the resize operation can expland the window off-screen because the new minimum size is automatically enforced).
- if I have a window on a screen edge, then resize the game window in between runs, when the window is "restored" from PlayerPrefs, it will be off-screen.

I have added the following code to my window component in order to keep the window always on screen:
  1.   // registered with UIWidget.onChange in OnEnable() method
  2.   void OnDimensionsChanged() {
  3.     // this is an attempt to sort out problems with the window not being
  4.     // visible for various reasons:
  5.     // - resolution changes, the we restore the window to place not visible
  6.     // - or upgrade problems (like making min size bigger than currently
  7.     //   saved prefs
  8.     Bounds contentBounds = GetContentRectBounds(selectorWindow);
  9.     uiPanel.ConstrainTargetToBounds(selectorWindow.transform, ref contentBounds, true);
  10.  
  11.     this.SaveToPrefs();
  12.   }
  13.  
  14.   // copied from UIDragObject.UpdateBounds()
  15.   public Bounds GetContentRectBounds(UIRect content){
  16.     Matrix4x4 toLocal = uiPanel.transform.worldToLocalMatrix;
  17.     Vector3[] corners = content.worldCorners;
  18.     for (int i = 0; i < 4; ++i) corners[i] = toLocal.MultiplyPoint3x4(corners[i]);
  19.     Bounds  mBounds = new Bounds(corners[0], Vector3.zero);
  20.     for (int i = 1; i < 4; ++i) mBounds.Encapsulate(corners[i]);
  21.     return mBounds;
  22.   }
  23.  

It seems to work well enough, the window always gets restored or reset during onEnable(), so if the viewport changed in between runs the code will fire and make sure the windows are on-screen.  And it works well for resizing too.

The "ConstrainTargetToBounds()" call is what makes it work and that's simple enough, but I though it was a bit awkward to have to write the GetContentRectBounds() method.

Am I missing something obvious here - is there a better way to be doing this?
If not - maybe it would be worth adding a helper method or something in a future version of NGUI?

Of course, this stuff would work fine if I was using the new layout system to just anchor to widgets to the screen edge - the trick is I want to let the user move the widgets around (but protect them from doing something silly).


« Last Edit: January 05, 2014, 05:48:52 AM by wom »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Question about the "right" way to ensure my widget remains on screen
« Reply #1 on: January 05, 2014, 08:38:46 AM »
What you're doing is fine. I did the same thing in UIDragObject and some other scripts in order to have constrain logic active. You already tied it into the OnDimensionChanged, so you've done all I would have done.