Browse Source

Fix UI scale issues. (#2779)

* Fix UI scale issues.

* Fixing of UI scaling issue.

* Unify cursor in UI scaled position.

* Scaled UI issue: JSCanvasSize modification

* Remove extra space.

* Fixed Editor viewport related issues when UI scaled.
Wang Kai 4 years ago
parent
commit
ffa6a9d6e2

+ 2 - 0
Source/Samples/15_Navigation/Navigation.cpp

@@ -356,6 +356,8 @@ bool Navigation::Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawa
     if (!ui->GetCursor()->IsVisible() || ui->GetElementAt(pos, true))
         return false;
 
+    pos = ui->ConvertUIToSystem(pos);
+
     auto* graphics = GetSubsystem<Graphics>();
     auto* camera = cameraNode_->GetComponent<Camera>();
     Ray cameraRay = camera->GetScreenRay((float)pos.x_ / graphics->GetWidth(), (float)pos.y_ / graphics->GetHeight());

+ 2 - 0
Source/Samples/39_CrowdNavigation/CrowdNavigation.cpp

@@ -385,6 +385,8 @@ bool CrowdNavigation::Raycast(float maxDistance, Vector3& hitPos, Drawable*& hit
     if (!ui->GetCursor()->IsVisible() || ui->GetElementAt(pos, true))
         return false;
 
+    pos = ui->ConvertUIToSystem(pos);
+
     auto* graphics = GetSubsystem<Graphics>();
     auto* camera = cameraNode_->GetComponent<Camera>();
     Ray cameraRay = camera->GetScreenRay((float)pos.x_ / graphics->GetWidth(), (float)pos.y_ / graphics->GetHeight());

+ 5 - 1
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -123,7 +123,11 @@ static void JSCanvasSize(int width, int height, bool fullscreen, float scale)
             if (cursor)
             {
                 cursor->SetVisible(uiCursorVisible);
-                cursor->SetPosition(input->GetMousePosition());
+
+                IntVector2 pos = input->GetMousePosition();
+                pos = ui->ConvertSystemToUI(pos);
+
+                cursor->SetPosition(pos);
             }
         }
     }

+ 17 - 17
Source/Urho3D/UI/UI.cpp

@@ -382,8 +382,7 @@ void UI::Update(float timeStep)
     {
         TouchState* touch = input->GetTouch(i);
         IntVector2 touchPos = touch->position_;
-        touchPos.x_ = (int)(touchPos.x_ / uiScale_);
-        touchPos.y_ = (int)(touchPos.y_ / uiScale_);
+        touchPos = ConvertSystemToUI(touchPos);
         ProcessHover(touchPos, MakeTouchIDMask(touch->touchID_), QUAL_NONE, nullptr);
     }
 
@@ -769,9 +768,9 @@ void UI::SetCustomSize(int width, int height)
 IntVector2 UI::GetCursorPosition() const
 {
     if (cursor_)
-        return ConvertUIToSystem(cursor_->GetPosition());
+        return cursor_->GetPosition();
 
-    return GetSubsystem<Input>()->GetMousePosition();
+    return ConvertSystemToUI(GetSubsystem<Input>()->GetMousePosition());
 }
 
 UIElement* UI::GetElementAt(const IntVector2& position, bool enabledOnly, IntVector2* elementScreenPosition)
@@ -1315,11 +1314,17 @@ void UI::GetCursorPositionAndVisible(IntVector2& pos, bool& visible)
     else
     {
         auto* input = GetSubsystem<Input>();
-        pos = input->GetMousePosition();
         visible = input->IsMouseVisible();
 
         if (!visible && cursor_)
+        {
             pos = cursor_->GetPosition();
+        }
+        else
+        {
+            pos = input->GetMousePosition();
+            pos = ConvertSystemToUI(pos);
+        }
     }
 }
 
@@ -1886,8 +1891,7 @@ void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
     using namespace TouchBegin;
 
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
-    pos.x_ = int(pos.x_ / uiScale_);
-    pos.y_ = int(pos.y_ / uiScale_);
+    pos = ConvertSystemToUI(pos);
     usingTouchInput_ = true;
 
     const MouseButton touchId = MakeTouchIDMask(eventData[P_TOUCHID].GetInt());
@@ -1907,8 +1911,7 @@ void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
     using namespace TouchEnd;
 
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
-    pos.x_ = int(pos.x_ / uiScale_);
-    pos.y_ = int(pos.y_ / uiScale_);
+    pos = ConvertSystemToUI(pos);
 
     // Get the touch index
     const MouseButton touchId = MakeTouchIDMask(eventData[P_TOUCHID].GetInt());
@@ -1938,10 +1941,8 @@ void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
 
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     IntVector2 deltaPos(eventData[P_DX].GetInt(), eventData[P_DY].GetInt());
-    pos.x_ = int(pos.x_ / uiScale_);
-    pos.y_ = int(pos.y_ / uiScale_);
-    deltaPos.x_ = int(deltaPos.x_ / uiScale_);
-    deltaPos.y_ = int(deltaPos.y_ / uiScale_);
+    pos = ConvertSystemToUI(pos);
+    deltaPos = ConvertSystemToUI(deltaPos);
     usingTouchInput_ = true;
 
     const MouseButton touchId = MakeTouchIDMask(eventData[P_TOUCHID].GetInt());
@@ -2061,8 +2062,7 @@ void UI::HandleDropFile(StringHash eventType, VariantMap& eventData)
     if (input->IsMouseVisible())
     {
         IntVector2 screenPos = input->GetMousePosition();
-        screenPos.x_ = int(screenPos.x_ / uiScale_);
-        screenPos.y_ = int(screenPos.y_ / uiScale_);
+        screenPos = ConvertSystemToUI(screenPos);
 
         UIElement* element = GetElementAt(screenPos);
 
@@ -2148,8 +2148,8 @@ IntVector2 UI::SumTouchPositions(UI::DragData* dragData, const IntVector2& oldSe
                 if (!ts)
                     break;
                 IntVector2 pos = ts->position_;
-                dragData->sumPos.x_ += (int)(pos.x_ / uiScale_);
-                dragData->sumPos.y_ += (int)(pos.y_ / uiScale_);
+                pos = ConvertSystemToUI(pos);
+                dragData->sumPos += pos;
             }
         }
         sendPos.x_ = dragData->sumPos.x_ / dragData->numDragButtons;

+ 11 - 1
Source/Urho3D/UI/UIComponent.cpp

@@ -112,7 +112,13 @@ public:
             rect.bottom_ = graphics->GetHeight();
         }
 
-        Ray ray(camera->GetScreenRay((float)screenPos.x_ / rect.Width(), (float)screenPos.y_ / rect.Height()));
+        auto* ui = GetSubsystem<UI>();
+
+        // Convert to system mouse position
+        IntVector2 pos;
+        pos = ui->ConvertUIToSystem(screenPos);
+
+        Ray ray(camera->GetScreenRay((float)pos.x_ / rect.Width(), (float)pos.y_ / rect.Height()));
         PODVector<RayQueryResult> queryResultVector;
         RayOctreeQuery query(queryResultVector, ray, RAY_TRIANGLE_UV, M_INFINITY, DRAWABLE_GEOMETRY, DEFAULT_VIEWMASK);
 
@@ -135,6 +141,10 @@ public:
             Vector2& uv = queryResult.textureUV_;
             result = IntVector2(static_cast<int>(uv.x_ * GetWidth()),
                 static_cast<int>(uv.y_ * GetHeight()));
+
+            // Convert back to scaled UI position
+            result = ui->ConvertSystemToUI(result);
+
             return result;
         }
         return result;

+ 2 - 0
bin/Data/Scripts/15_Navigation.as

@@ -314,6 +314,8 @@ bool Raycast(float maxDistance, Vector3& hitPos, Drawable@& hitDrawable)
     if (!ui.cursor.visible || ui.GetElementAt(pos, true) !is null)
         return false;
 
+    pos = ui.ConvertUIToSystem(ui.cursorPosition);
+
     Camera@ camera = cameraNode.GetComponent("Camera");
     Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
     // Pick only geometry objects, not eg. zones or lights, only get the first (closest) hit

+ 2 - 0
bin/Data/Scripts/39_CrowdNavigation.as

@@ -335,6 +335,8 @@ bool Raycast(float maxDistance, Vector3& hitPos, Drawable@& hitDrawable)
     if (!ui.cursor.visible || ui.GetElementAt(pos, true) !is null)
         return false;
 
+    pos = ui.ConvertUIToSystem(ui.cursorPosition);
+
     Camera@ camera = cameraNode.GetComponent("Camera");
     Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
     // Pick only geometry objects, not eg. zones or lights, only get the first (closest) hit

+ 1 - 0
bin/Data/Scripts/Editor/EditorGizmo.as

@@ -218,6 +218,7 @@ void UseGizmo()
     IntVector2 pos = ui.cursorPosition;
     if (ui.GetElementAt(pos) !is null)
         return;
+
     Ray cameraRay = GetActiveViewportCameraRay();
     float scale = gizmoNode.scale.x;
 

+ 2 - 2
bin/Data/Scripts/Editor/EditorSecondaryToolbar.as

@@ -10,9 +10,9 @@ void CreateSecondaryToolBar()
     secondaryToolBar.layoutSpacing = 4;
     secondaryToolBar.layoutBorder = IntRect(4, 4, 4, 4);
     secondaryToolBar.opacity = uiMaxOpacity;
-    secondaryToolBar.SetFixedSize(28, graphics.height);
+    secondaryToolBar.SetFixedSize(28, graphics.height / ui.scale);
     secondaryToolBar.SetPosition(0, uiMenuBar.height+40);
-    secondaryToolBar.SetFixedHeight(graphics.height);
+    secondaryToolBar.SetFixedHeight(graphics.height / ui.scale);
 
     Button@ b = CreateSmallToolBarButton("Node", "Replicated Node");
     secondaryToolBar.AddChild(b);

+ 1 - 1
bin/Data/Scripts/Editor/EditorToolBar.as

@@ -13,7 +13,7 @@ void CreateToolBar()
     toolBar.layoutSpacing = 4;
     toolBar.layoutBorder = IntRect(8, 4, 4, 8);
     toolBar.opacity = uiMaxOpacity;
-    toolBar.SetFixedSize(graphics.width, 42);
+    toolBar.SetFixedSize(graphics.width / ui.scale, 42);
     toolBar.SetPosition(0, uiMenuBar.height);
     ui.root.AddChild(toolBar);
 

+ 6 - 6
bin/Data/Scripts/Editor/EditorUI.as

@@ -114,13 +114,13 @@ void CreateUI()
 void ResizeUI()
 {
     // Resize menu bar
-    uiMenuBar.SetFixedWidth(graphics.width);
+    uiMenuBar.SetFixedWidth(graphics.width / ui.scale);
 
     // Resize tool bar
-    toolBar.SetFixedWidth(graphics.width);
+    toolBar.SetFixedWidth(graphics.width / ui.scale);
 
     // Resize secondary tool bar
-    secondaryToolBar.SetFixedHeight(graphics.height);
+    secondaryToolBar.SetFixedHeight(graphics.height / ui.scale);
 
     // Relayout windows
     Array<UIElement@> children = ui.root.GetChildren();
@@ -133,8 +133,8 @@ void ResizeUI()
     // Relayout root UI element
     editorUIElement.SetSize(graphics.width, graphics.height);
 
-    // Set new viewport area and reset the viewport layout
-    viewportArea = IntRect(0, 0, graphics.width, graphics.height);
+    // Set new viewport area and reset the viewport layout. Note: viewportArea is in scaled UI position.
+    viewportArea = IntRect(0, 0, graphics.width / ui.scale, graphics.height / ui.scale);
     SetViewportMode(viewportMode);
 }
 
@@ -320,7 +320,7 @@ void CreateMenuBar()
     uiMenuBar.style = "EditorMenuBar";
     uiMenuBar.SetLayout(LM_HORIZONTAL);
     uiMenuBar.opacity = uiMaxOpacity;
-    uiMenuBar.SetFixedWidth(graphics.width);
+    uiMenuBar.SetFixedWidth(graphics.width / ui.scale);
 
     {
         Menu@ menu = CreateMenu("File");

+ 52 - 19
bin/Data/Scripts/Editor/EditorView.as

@@ -17,7 +17,7 @@ uint resizingBorder = 0; // current border that is dragging
 uint viewportMode = VIEWPORT_SINGLE;
 int  viewportBorderOffset = 2; // used to center borders over viewport seams,  should be half of width
 int  viewportBorderWidth = 4; // width of a viewport resize border
-IntRect viewportArea; // the area where the editor viewport is. if we ever want to have the viewport not take up the whole screen this abstracts that
+IntRect viewportArea; // the area where the editor viewport is. if we ever want to have the viewport not take up the whole screen this abstracts that. NOTE: viewportArea is in scaled UI position.
 IntRect viewportUIClipBorder = IntRect(27, 60, 0, 0); // used to clip viewport borders, the borders are ugly when going behind the transparent toolbars
 RenderPath@ renderPath; // Renderpath to use on all views
 String renderPathName;
@@ -108,6 +108,30 @@ void ResizeString(String& str, uint newSize)
         str[i] = ' ';
 }
 
+/// Convert rect from scaled UI position to system position
+IntRect IntRectUIToSystem(const IntRect& rect)
+{
+    IntRect ret = rect;
+    ret.left = FloorToInt(ret.left * ui.scale);
+    ret.top = FloorToInt(ret.top * ui.scale);
+    ret.right = FloorToInt(ret.right * ui.scale);
+    ret.bottom = FloorToInt(ret.bottom * ui.scale);
+
+    return ret;
+}
+
+/// Convert rect from system position to scaled UI position
+IntRect IntRectSystemToUI(const IntRect& rect)
+{
+    IntRect ret = rect;
+    ret.left = FloorToInt(ret.left / ui.scale);
+    ret.top = FloorToInt(ret.top / ui.scale);
+    ret.right = FloorToInt(ret.right / ui.scale);
+    ret.bottom = FloorToInt(ret.bottom / ui.scale);
+
+    return ret;
+}
+
 // Holds info about a viewport such as camera settings and splits up shared resources
 class ViewportContext
 {
@@ -145,7 +169,11 @@ class ViewportContext
         orthoCameraZoom = camera.zoom;
         camera.fillMode = fillMode;
         soundListener = cameraNode.CreateComponent("SoundListener");
-        viewport = Viewport(editorScene, camera, viewRect, renderPath);
+        
+        // Here we convert viewRect from scaled UI position to system position 
+        IntRect sysRect = IntRectUIToSystem(viewRect);
+
+        viewport = Viewport(editorScene, camera, sysRect, renderPath);
         index = index_;
         viewportId = viewportId_;
         camera.viewMask = 0xffffffff; // It's easier to only have 1 gizmo active this viewport is shared with the gizmo
@@ -178,7 +206,7 @@ class ViewportContext
         viewportContextUI = UIElement();
         viewportUI.AddChild(viewportContextUI);
         viewportContextUI.SetPosition(viewport.rect.left, viewport.rect.top);
-        viewportContextUI.SetFixedSize(viewport.rect.width, viewport.rect.height);
+        viewportContextUI.SetFixedSize(viewport.rect.width / ui.scale, viewport.rect.height / ui.scale); // viewport.rect is in system position
         viewportContextUI.clipChildren = true;
 
         statusBar = BorderImage("ToolBar");
@@ -236,8 +264,8 @@ class ViewportContext
 
     void HandleResize()
     {
-        viewportContextUI.SetPosition(viewport.rect.left, viewport.rect.top);
-        viewportContextUI.SetFixedSize(viewport.rect.width, viewport.rect.height);
+        viewportContextUI.SetPosition(viewport.rect.left / ui.scale, viewport.rect.top / ui.scale);
+        viewportContextUI.SetFixedSize(viewport.rect.width / ui.scale, viewport.rect.height / ui.scale);  // viewport.rect is in system position
         if (viewport.rect.left < 34)
         {
             statusBar.layoutBorder = IntRect(34 - viewport.rect.left, 4, 4, 8);
@@ -253,7 +281,7 @@ class ViewportContext
             settingsWindow.position = pos;
         }
 
-        statusBar.SetFixedSize(viewport.rect.width, 22);
+        statusBar.SetFixedSize(viewport.rect.width / ui.scale, 22);
     }
 
     void ToggleOrthographic()
@@ -699,7 +727,7 @@ void SetHDR(bool enable)
 void CreateCamera()
 {
     // Set the initial viewport rect
-    viewportArea = IntRect(0, 0, graphics.width, graphics.height);
+    viewportArea = IntRect(0, 0, graphics.width / ui.scale, graphics.height / ui.scale);
 
     // Set viewport single to store default hierarchy/inspector height/positions
     if(viewportMode == VIEWPORT_COMPACT)
@@ -730,7 +758,7 @@ void CreateViewportUI()
         ui.root.AddChild(viewportUI);
     }
 
-    viewportUI.SetFixedSize(viewportArea.width, viewportArea.height);
+    viewportUI.SetFixedSize(viewportArea.width, viewportArea.height); // viewportArea is alreay in scaled UI position
     viewportUI.position = IntVector2(viewportArea.top, viewportArea.left);
     viewportUI.clipChildren = true;
     viewportUI.clipBorder = viewportUIClipBorder;
@@ -753,22 +781,25 @@ void CreateViewportUI()
         ViewportContext@ vc = viewports[i];
         vc.CreateViewportContextUI();
 
+        // Here we convert system position to scaled UI position
+        IntRect rect = IntRectSystemToUI(vc.viewport.rect);
+
         if (vc.viewportId & VIEWPORT_TOP > 0)
-            top = vc.viewport.rect;
+            top = rect;
         else if (vc.viewportId & VIEWPORT_BOTTOM > 0)
-            bottom = vc.viewport.rect;
+            bottom = rect;
         else if (vc.viewportId & VIEWPORT_LEFT > 0)
-            left = vc.viewport.rect;
+            left = rect;
         else if (vc.viewportId & VIEWPORT_RIGHT > 0)
-            right = vc.viewport.rect;
+            right = rect;
         else if (vc.viewportId & VIEWPORT_TOP_LEFT > 0)
-            topLeft = vc.viewport.rect;
+            topLeft = rect;
         else if (vc.viewportId & VIEWPORT_TOP_RIGHT > 0)
-            topRight = vc.viewport.rect;
+            topRight = rect;
         else if (vc.viewportId & VIEWPORT_BOTTOM_LEFT > 0)
-            bottomLeft = vc.viewport.rect;
+            bottomLeft = rect;
         else if (vc.viewportId & VIEWPORT_BOTTOM_RIGHT > 0)
-            bottomRight = vc.viewport.rect;
+            bottomRight = rect;
     }
 
     // Creates resize borders based on the mode set
@@ -2231,6 +2262,7 @@ void ViewMouseMove()
         return;
 
     IntVector2 pos = ui.cursor.position;
+    pos = ui.ConvertUIToSystem(pos);
     for (uint i = 0; i < viewports.length; ++i)
     {
         ViewportContext@ vc = viewports[i];
@@ -2246,10 +2278,11 @@ void ViewMouseClick()
 
 Ray GetActiveViewportCameraRay()
 {
+    IntVector2 pos = ui.ConvertUIToSystem(ui.cursorPosition);
     IntRect view = activeViewport.viewport.rect;
     return camera.GetScreenRay(
-        float(ui.cursorPosition.x - view.left) / view.width,
-        float(ui.cursorPosition.y - view.top) / view.height
+        float(pos.x - view.left) / view.width,
+        float(pos.y - view.top) / view.height
     );
 }
 
@@ -2723,7 +2756,7 @@ Vector3 SelectedNodesCenterPoint()
 
 Drawable@ GetDrawableAtMousePostion()
 {
-    IntVector2 pos = ui.cursorPosition;
+    IntVector2 pos = ui.ConvertUIToSystem(ui.cursorPosition);
     Ray cameraRay = camera.GetScreenRay(float(pos.x) / activeViewport.viewport.rect.width, float(pos.y) / activeViewport.viewport.rect.height);
 
     if (editorScene.octree is null)