From ce692af862964c0131181683d1b4f12aba148b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerard=20Gasc=C3=B3n?= <52170489+GerardGascon@users.noreply.github.com> Date: Tue, 16 Apr 2024 01:07:26 +0200 Subject: [PATCH] feat: basic window resizing --- Assets/Scenes/SampleScene.unity | 16 +++- Assets/Scripts/View/DisplaySetup.cs | 9 +- .../Runtime/AppWindowUtility.cs | 11 ++- .../Runtime/IPlatformDependent.cs | 8 +- .../AppWindowUtility/Runtime/MouseButton.cs | 7 ++ .../Runtime/MouseButton.cs.meta | 3 + .../AppWindowUtility/Runtime/ResizeHelper.cs | 40 +++++++++ .../Runtime/ResizeHelper.cs.meta | 3 + .../AppWindowUtility/Runtime/WindowGrabber.cs | 83 +++++++------------ .../AppWindowUtility/Runtime/WindowResizer.cs | 49 +++++++++++ .../Runtime/WindowResizer.cs.meta | 3 + .../Runtime/Windows/Windows.cs | 37 ++++++++- Packages/AppWindowUtility/Tests.meta | 8 ++ Packages/AppWindowUtility/Tests/ResizeTest.cs | 77 +++++++++++++++++ .../AppWindowUtility/Tests/ResizeTest.cs.meta | 11 +++ ...SatorImaging.AppWindowUtility.Tests.asmdef | 24 ++++++ ...Imaging.AppWindowUtility.Tests.asmdef.meta | 7 ++ ....AppWindowUtility.Tests.csproj.DotSettings | 2 + 18 files changed, 324 insertions(+), 74 deletions(-) create mode 100644 Packages/AppWindowUtility/Runtime/MouseButton.cs create mode 100644 Packages/AppWindowUtility/Runtime/MouseButton.cs.meta create mode 100644 Packages/AppWindowUtility/Runtime/ResizeHelper.cs create mode 100644 Packages/AppWindowUtility/Runtime/ResizeHelper.cs.meta create mode 100644 Packages/AppWindowUtility/Runtime/WindowResizer.cs create mode 100644 Packages/AppWindowUtility/Runtime/WindowResizer.cs.meta create mode 100644 Packages/AppWindowUtility/Tests.meta create mode 100644 Packages/AppWindowUtility/Tests/ResizeTest.cs create mode 100644 Packages/AppWindowUtility/Tests/ResizeTest.cs.meta create mode 100644 Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef create mode 100644 Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef.meta create mode 100644 SatorImaging.AppWindowUtility.Tests.csproj.DotSettings diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 05e092b..62933f4 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -679,6 +679,7 @@ GameObject: - component: {fileID: 501733677} - component: {fileID: 501733676} - component: {fileID: 501733679} + - component: {fileID: 501733680} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -773,8 +774,19 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: mouseButton: 0 - modifierKey: 0 - temporarilyDisableIfKeyPressed: +--- !u!114 &501733680 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 501733675} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0d779b53ae5c4644b516e84fdc31fcdc, type: 3} + m_Name: + m_EditorClassIdentifier: + mouseButton: 0 --- !u!1 &902566661 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/View/DisplaySetup.cs b/Assets/Scripts/View/DisplaySetup.cs index cca424b..284b351 100644 --- a/Assets/Scripts/View/DisplaySetup.cs +++ b/Assets/Scripts/View/DisplaySetup.cs @@ -1,4 +1,4 @@ -#if !UNITY_EDITOR + using SatorImaging.AppWindowUtility; using UnityEngine; @@ -6,13 +6,14 @@ namespace View { internal static class DisplaySetup { [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)] private static void Setup() { +#if !UNITY_EDITOR AppWindowUtility.AlwaysOnTop = true; AppWindowUtility.FrameVisibility = false; //Vector2Int resolution = new(360, 640); Vector2Int resolution = new(360, 540); - AppWindowUtility.ResizeWindowRelative(resolution.x, resolution.y); + AppWindowUtility.ResizeWindow(resolution.x, resolution.y); +#endif } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/AppWindowUtility.cs b/Packages/AppWindowUtility/Runtime/AppWindowUtility.cs index 4cb245e..fd1854d 100644 --- a/Packages/AppWindowUtility/Runtime/AppWindowUtility.cs +++ b/Packages/AppWindowUtility/Runtime/AppWindowUtility.cs @@ -67,8 +67,11 @@ namespace SatorImaging.AppWindowUtility public static bool WindowResizeSupported { get => platform?.WindowResizeSupported ?? false; } - public static void ResizeWindowRelative(int pixelX, int pixelY) - => platform?.ResizeWindowRelative(pixelX, pixelY); + public static void ResizeWindow(int pixelX, int pixelY) + => platform?.ResizeWindow(pixelX, pixelY); + + public static void ResizeWindowRelative(float pixelX, float pixelY, Vector2 resizeDirection, float aspectRatio) + => platform?.ResizeWindowRelative(pixelX, pixelY, resizeDirection, aspectRatio); @@ -106,9 +109,5 @@ namespace SatorImaging.AppWindowUtility }//set }// - - - - }//class }//namespace diff --git a/Packages/AppWindowUtility/Runtime/IPlatformDependent.cs b/Packages/AppWindowUtility/Runtime/IPlatformDependent.cs index c0e7cb7..e16787f 100644 --- a/Packages/AppWindowUtility/Runtime/IPlatformDependent.cs +++ b/Packages/AppWindowUtility/Runtime/IPlatformDependent.cs @@ -1,4 +1,6 @@ -namespace SatorImaging.AppWindowUtility +using UnityEngine; + +namespace SatorImaging.AppWindowUtility { public interface IPlatformDependent { @@ -36,8 +38,8 @@ void MoveWindow(int x, int y); bool WindowResizeSupported { get; } - public void ResizeWindowRelative(int relativeWidth, int relativeHeight); - + public void ResizeWindow(int width, int height); + void ResizeWindowRelative(float deltaX, float deltaY, Vector2 resizeDirection, float aspectRatio); }// }//namespace diff --git a/Packages/AppWindowUtility/Runtime/MouseButton.cs b/Packages/AppWindowUtility/Runtime/MouseButton.cs new file mode 100644 index 0000000..245b086 --- /dev/null +++ b/Packages/AppWindowUtility/Runtime/MouseButton.cs @@ -0,0 +1,7 @@ +namespace SatorImaging.AppWindowUtility { + public enum MouseButton { + Left = 0, + Right = 1, + Middle = 2, + } +} \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/MouseButton.cs.meta b/Packages/AppWindowUtility/Runtime/MouseButton.cs.meta new file mode 100644 index 0000000..9d61529 --- /dev/null +++ b/Packages/AppWindowUtility/Runtime/MouseButton.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d4a99a35048e47dd945227eb9f0d0c0b +timeCreated: 1713211044 \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/ResizeHelper.cs b/Packages/AppWindowUtility/Runtime/ResizeHelper.cs new file mode 100644 index 0000000..e73436d --- /dev/null +++ b/Packages/AppWindowUtility/Runtime/ResizeHelper.cs @@ -0,0 +1,40 @@ +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace SatorImaging.AppWindowUtility { + public static class ResizeHelper { + public static Vector2 GetDirection(Vector2 mousePosition, Vector2 screenSize) { + float widthPercentage = mousePosition.x / screenSize.x; + float heightPercentage = mousePosition.y / screenSize.y; + + const float marginHeightPercentage = 30f / 1920f; + const float marginWidthPercentage = 30f / 1280f; + + if (widthPercentage < marginWidthPercentage) { + if (heightPercentage > 1 - marginHeightPercentage) + return new Vector2(-1, 1); + if (heightPercentage < marginHeightPercentage) + return new Vector2(-1, -1); + + return new Vector2(-1, 0); + } + + if (widthPercentage > 1 - marginWidthPercentage) { + if (heightPercentage > 1 - marginHeightPercentage) + return new Vector2(1, 1); + if (heightPercentage < marginHeightPercentage) + return new Vector2(1, -1); + + return new Vector2(1, 0); + } + + if (heightPercentage > 1 - marginHeightPercentage) + return new Vector2(0, 1); + + if (heightPercentage < marginHeightPercentage) + return new Vector2(0, -1); + + return Vector2.zero; + } + } +} \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/ResizeHelper.cs.meta b/Packages/AppWindowUtility/Runtime/ResizeHelper.cs.meta new file mode 100644 index 0000000..3f542f3 --- /dev/null +++ b/Packages/AppWindowUtility/Runtime/ResizeHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4f5b1e31614e4b458c18c903a3934198 +timeCreated: 1713212603 \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/WindowGrabber.cs b/Packages/AppWindowUtility/Runtime/WindowGrabber.cs index 33f3823..dafdd63 100644 --- a/Packages/AppWindowUtility/Runtime/WindowGrabber.cs +++ b/Packages/AppWindowUtility/Runtime/WindowGrabber.cs @@ -1,66 +1,39 @@ using UnityEngine; using UnityEngine.EventSystems; +namespace SatorImaging.AppWindowUtility { + public class WindowGrabber : MonoBehaviour { + public MouseButton mouseButton; + private bool _isDragging; + private Vector2 _targetPosition = Vector2.zero; + private Vector2 _resizeDirection = Vector2.zero; -namespace SatorImaging.AppWindowUtility -{ - public class WindowGrabber : MonoBehaviour - { - public enum MouseButton - { - Left = 0, - Right = 1, - Middle = 2, - } - public MouseButton mouseButton; - public KeyCode modifierKey = KeyCode.None; - public KeyCode[] temporarilyDisableIfKeyPressed; - - - private bool isDragging = false; - Vector2 targetPosition = Vector2.zero; - - - void Update() - { + private void Update() { #if UNITY_EDITOR - if(isDragging.Equals(isDragging)) return; // to avoid CS0162 warning + if (_isDragging.Equals(_isDragging)) return; // to avoid CS0162 warning #endif - // do nothing if any uGUI is in use. - if (EventSystem.current?.currentSelectedGameObject) return; + if (EventSystem.current?.currentSelectedGameObject) return; + if (Input.GetMouseButtonUp((int)mouseButton)) _isDragging = false; - // initialize dragging state. don't check modifier key. - if (Input.GetMouseButtonUp((int)mouseButton)) isDragging = false; + if (Input.GetMouseButtonDown((int)mouseButton)) { + _targetPosition = Event.current.mousePosition; + _resizeDirection = + ResizeHelper.GetDirection(_targetPosition, new Vector2(Screen.width, Screen.height)); + _isDragging = true; + } - // key check. - foreach (var k in temporarilyDisableIfKeyPressed) if (Input.GetKey(k)) return; - if (modifierKey != KeyCode.None && !Input.GetKey(modifierKey)) return; - - - - if (Input.GetMouseButtonDown((int)mouseButton)) - { - targetPosition = Event.current.mousePosition; - isDragging = true; - } - - if (isDragging && Input.GetMouseButton((int)mouseButton)) - { - // do NOT use Event.current.delta. it's sampled in local window coordinate. - // and moving window while mouse dragging changes coordinate sample by sample. - // just remove the gap between current mouse position and drag starting position. - AppWindowUtility.MoveWindowRelative( - (int)(Event.current.mousePosition.x - targetPosition.x), - (int)(Event.current.mousePosition.y - targetPosition.y) - ); - - } - - }// - - - }//class -}//namespace + if (_isDragging && Input.GetMouseButton((int)mouseButton) && _resizeDirection == Vector2.zero) { + // do NOT use Event.current.delta. it's sampled in local window coordinate. + // and moving window while mouse dragging changes coordinate sample by sample. + // just remove the gap between current mouse position and drag starting position. + AppWindowUtility.MoveWindowRelative( + (int)(Event.current.mousePosition.x - _targetPosition.x), + (int)(Event.current.mousePosition.y - _targetPosition.y) + ); + } + } + } +} \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/WindowResizer.cs b/Packages/AppWindowUtility/Runtime/WindowResizer.cs new file mode 100644 index 0000000..fc40d95 --- /dev/null +++ b/Packages/AppWindowUtility/Runtime/WindowResizer.cs @@ -0,0 +1,49 @@ +using UnityEngine; +using UnityEngine.EventSystems; + +namespace SatorImaging.AppWindowUtility { + public class WindowResizer : MonoBehaviour { + public MouseButton mouseButton; + + private bool _isResizing; + private Vector2 _targetPosition = Vector2.zero; + private Vector2 _resizeDirection = Vector2.zero; + + [SerializeField] private Vector2 aspectRatio = new(10, 15); + + private void Update() { +#if UNITY_EDITOR + if (_isResizing.Equals(_isResizing)) return; // to avoid CS0162 warning +#endif + + if (EventSystem.current?.currentSelectedGameObject) return; + + if (Input.GetMouseButtonUp((int)mouseButton)) _isResizing = false; + + if (Input.GetMouseButtonDown((int)mouseButton)) { + _targetPosition = Event.current.mousePosition; + _resizeDirection = + ResizeHelper.GetDirection(_targetPosition, new Vector2(Screen.width, Screen.height)); + _isResizing = true; + } + + if (_isResizing && Input.GetMouseButton((int)mouseButton) && _resizeDirection != Vector2.zero) { + float ratio = aspectRatio.x / aspectRatio.y; + + Vector2 delta = new( + Event.current.mousePosition.x - _targetPosition.x, + Event.current.mousePosition.y - _targetPosition.y + ); + + AppWindowUtility.ResizeWindowRelative(delta.x, delta.y, _resizeDirection, ratio); + + _targetPosition = Event.current.mousePosition; + + if (_resizeDirection.x < 0) + _targetPosition.x -= delta.x; + if (_resizeDirection.y < 0) + _targetPosition.y -= delta.y; + } + } + } +} \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/WindowResizer.cs.meta b/Packages/AppWindowUtility/Runtime/WindowResizer.cs.meta new file mode 100644 index 0000000..038ccf2 --- /dev/null +++ b/Packages/AppWindowUtility/Runtime/WindowResizer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0d779b53ae5c4644b516e84fdc31fcdc +timeCreated: 1713211016 \ No newline at end of file diff --git a/Packages/AppWindowUtility/Runtime/Windows/Windows.cs b/Packages/AppWindowUtility/Runtime/Windows/Windows.cs index ce05811..8fa4d64 100644 --- a/Packages/AppWindowUtility/Runtime/Windows/Windows.cs +++ b/Packages/AppWindowUtility/Runtime/Windows/Windows.cs @@ -273,7 +273,7 @@ namespace SatorImaging.AppWindowUtility - public void ResizeWindowRelative(int relativeWidth, int relativeHeight) + public void ResizeWindow(int width, int height) { if (AppWindowUtility.FullScreen) return; @@ -282,12 +282,41 @@ namespace SatorImaging.AppWindowUtility WinApi.SetWindowPos(hWnd, IntPtr.Zero, rect.left, rect.top, - relativeWidth, - relativeHeight, + width, + height, WinApi.SetWindowPosFlags.NoFlag //ApiWindows.SetWindowPosFlags.IgnoreMove ); - }// + } + + public void ResizeWindowRelative(float deltaX, float deltaY, Vector2 resizeDirection, float aspectRatio) { + if (AppWindowUtility.FullScreen) return; + + WinApi.RECT rect; + WinApi.GetWindowRect(hWnd, out rect); + + float resizeFactor = Mathf.Abs(resizeDirection.x) > Mathf.Abs(resizeDirection.y) + ? deltaX + : deltaY; + + int x = rect.left; + int y = rect.top; + int width = (int)(rect.right - rect.left + resizeFactor); + //int height = (int)(rect.bottom - rect.top + deltaY); + + if (resizeDirection.x < 0) { + x += (int)resizeFactor; + width = rect.right - x; + } + int height = (int)(width / aspectRatio); + if (resizeDirection.y < 0) { + y += (int)resizeFactor; + height = rect.bottom - y; + width = (int)(height * aspectRatio); + } + + WinApi.SetWindowPos(hWnd, IntPtr.Zero, x, y, width, height, WinApi.SetWindowPosFlags.NoFlag); + } diff --git a/Packages/AppWindowUtility/Tests.meta b/Packages/AppWindowUtility/Tests.meta new file mode 100644 index 0000000..52c7ccb --- /dev/null +++ b/Packages/AppWindowUtility/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 95bed7a6ca15f8045b8a333ad25d61f5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/AppWindowUtility/Tests/ResizeTest.cs b/Packages/AppWindowUtility/Tests/ResizeTest.cs new file mode 100644 index 0000000..9034334 --- /dev/null +++ b/Packages/AppWindowUtility/Tests/ResizeTest.cs @@ -0,0 +1,77 @@ +using NUnit.Framework; +using SatorImaging.AppWindowUtility; +using UnityEngine; + +public class ResizeTest { + [Test] + public void CheckCenter() { + Vector2 mousePos = new(500, 500); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(0, 0), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckTopLeft() { + Vector2 mousePos = new(0, 1920); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(-1, 1), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckTop() { + Vector2 mousePos = new(500, 1920); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(0, 1), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckTopRight() { + Vector2 mousePos = new(1280, 1920); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(1, 1), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckLeft() { + Vector2 mousePos = new(0, 500); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(-1, 0), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckRight() { + Vector2 mousePos = new(1280, 500); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(1, 0), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckBottomLeft() { + Vector2 mousePos = new(0, 0); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(-1, -1), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckBottom() { + Vector2 mousePos = new(500, 0); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(0, -1), ResizeHelper.GetDirection(mousePos, screenSize)); + } + + [Test] + public void CheckBottomRight() { + Vector2 mousePos = new(1280, 0); + Vector2 screenSize = new(1280, 1920); + + Assert.AreEqual(new Vector2(1, -1), ResizeHelper.GetDirection(mousePos, screenSize)); + } +} \ No newline at end of file diff --git a/Packages/AppWindowUtility/Tests/ResizeTest.cs.meta b/Packages/AppWindowUtility/Tests/ResizeTest.cs.meta new file mode 100644 index 0000000..5087b1d --- /dev/null +++ b/Packages/AppWindowUtility/Tests/ResizeTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec4a69b0e49f65c4c98675104812c31b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef b/Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef new file mode 100644 index 0000000..67b4247 --- /dev/null +++ b/Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef @@ -0,0 +1,24 @@ +{ + "name": "SatorImaging.AppWindowUtility.Tests", + "rootNamespace": "", + "references": [ + "UnityEngine.TestRunner", + "UnityEditor.TestRunner", + "SatorImaging.AppWindowUtility" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef.meta b/Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef.meta new file mode 100644 index 0000000..f5babdd --- /dev/null +++ b/Packages/AppWindowUtility/Tests/SatorImaging.AppWindowUtility.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 39a22fcf88b04a24eb3566b752f8d676 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SatorImaging.AppWindowUtility.Tests.csproj.DotSettings b/SatorImaging.AppWindowUtility.Tests.csproj.DotSettings new file mode 100644 index 0000000..22caf61 --- /dev/null +++ b/SatorImaging.AppWindowUtility.Tests.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file