Skip to content

Commit

Permalink
Add minimum window sizes (#1404)
Browse files Browse the repository at this point in the history
## Description
Adding a minimum size for the apps window and also the TabView tearout
sample for better user experience.

## Motivation and Context
Closes #1181

Co-authored-by: Niels Laute <[email protected]>
  • Loading branch information
marcelwgn and niels9001 authored Jan 17, 2024
1 parent 9127f51 commit 9134fbe
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 21 deletions.
24 changes: 16 additions & 8 deletions WinUIGallery/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
using Windows.ApplicationModel.Activation;
using WinUIGallery.DesktopWap.DataModel;
using WASDK = Microsoft.WindowsAppSDK;

using Microsoft.Windows.AppLifecycle;
using System.IO;
using WinUIGallery.Helper;

namespace AppUIBasics
{
/// <summary>
Expand All @@ -30,23 +33,24 @@ namespace AppUIBasics
sealed partial class App : Application
{
private static Window startupWindow;
private static Win32WindowHelper win32WindowHelper;

public static string WinAppSdkDetails
{
// TODO: restore patch number and version tag when WinAppSDK supports them both
get => string.Format("Windows App SDK {0}.{1}",
WASDK.Release.Major, WASDK.Release.Minor);
}

}

public static string WinAppSdkRuntimeDetails
{
get
{
{
// Retrieve Windows App Runtime version info dynamically
var windowsAppRuntimeVersion =
from module in Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
where module.FileName.EndsWith("Microsoft.WindowsAppRuntime.Insights.Resource.dll")
select FileVersionInfo.GetVersionInfo(module.FileName);
var windowsAppRuntimeVersion =
from module in Process.GetCurrentProcess().Modules.OfType<ProcessModule>()
where module.FileName.EndsWith("Microsoft.WindowsAppRuntime.Insights.Resource.dll")
select FileVersionInfo.GetVersionInfo(module.FileName);
return WinAppSdkDetails + ", Windows App Runtime " + windowsAppRuntimeVersion.First().FileVersion;
}
}
Expand Down Expand Up @@ -107,6 +111,10 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar

startupWindow = WindowHelper.CreateWindow();
startupWindow.ExtendsContentIntoTitleBar = true;

win32WindowHelper = new Win32WindowHelper(startupWindow);
win32WindowHelper.SetWindowMinMaxSize(new Win32WindowHelper.POINT() { x = 500, y = 500 });

#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
Expand Down
25 changes: 25 additions & 0 deletions WinUIGallery/Common/Win32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,37 @@ internal static class Win32
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(IntPtr moduleName);

[DllImport("User32.dll")]
internal static extern int GetDpiForWindow(IntPtr hwnd);

[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
internal static extern int SetWindowLong32(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
internal static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, WindowLongIndexFlags nIndex, WinProc newProc);

[DllImport("user32.dll")]
internal static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);

public const int WM_ACTIVATE = 0x0006;
public const int WA_ACTIVE = 0x01;
public const int WA_INACTIVE = 0x00;

public const int WM_SETICON = 0x0080;
public const int ICON_SMALL = 0;
public const int ICON_BIG = 1;

internal delegate IntPtr WinProc(IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);

[Flags]
internal enum WindowLongIndexFlags : int
{
GWL_WNDPROC = -4,
}

internal enum WindowMessage : int
{
WM_GETMINMAXINFO = 0x0024,
}
}
}
10 changes: 6 additions & 4 deletions WinUIGallery/ControlPages/TabViewPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,14 @@ private void TabCloseButtonOverlayModeComboBox_SelectionChanged(object sender, S

private void TabViewWindowingButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
var tabViewSample = new TabViewWindowingSamplePage();

var newWindow = WindowHelper.CreateWindow();
newWindow.ExtendsContentIntoTitleBar = true;
newWindow.Content = tabViewSample;
tabViewSample.LoadDemoData();
tabViewSample.SetupWindowMinSize(newWindow);

Frame frame = new Frame();
frame.RequestedTheme = ThemeHelper.RootTheme;
frame.Navigate(typeof(TabViewWindowingSamplePage), null);
newWindow.Content = frame;
newWindow.Activate();
}
}
Expand Down
88 changes: 88 additions & 0 deletions WinUIGallery/Helper/Win32WindowHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.UI.Xaml;
using static AppUIBasics.Win32;

namespace WinUIGallery.Helper
{
internal class Win32WindowHelper
{
private static WinProc newWndProc = null;
private static nint oldWndProc = nint.Zero;

private POINT? minWindowSize = null;
private POINT? maxWindowSize = null;

private readonly Window window;

public Win32WindowHelper(Window window)
{
this.window = window;
}

public void SetWindowMinMaxSize(POINT? minWindowSize = null, POINT? maxWindowSize = null)
{
this.minWindowSize = minWindowSize;
this.maxWindowSize = maxWindowSize;

var hwnd = GetWindowHandleForCurrentWindow(window);

newWndProc = new WinProc(WndProc);
oldWndProc = SetWindowLongPtr(hwnd, WindowLongIndexFlags.GWL_WNDPROC, newWndProc);
}

private static nint GetWindowHandleForCurrentWindow(object target) =>
WinRT.Interop.WindowNative.GetWindowHandle(target);

private nint WndProc(nint hWnd, WindowMessage Msg, nint wParam, nint lParam)
{
switch (Msg)
{
case WindowMessage.WM_GETMINMAXINFO:
var dpi = GetDpiForWindow(hWnd);
var scalingFactor = (float)dpi / 96;

var minMaxInfo = Marshal.PtrToStructure<MINMAXINFO>(lParam);
if (minWindowSize != null)
{
minMaxInfo.ptMinTrackSize.x = (int)(minWindowSize.Value.x * scalingFactor);
minMaxInfo.ptMinTrackSize.y = (int)(minWindowSize.Value.y * scalingFactor);
}
if (maxWindowSize != null)
{
minMaxInfo.ptMaxTrackSize.x = (int)(maxWindowSize.Value.x * scalingFactor);
minMaxInfo.ptMaxTrackSize.y = (int)(minWindowSize.Value.y * scalingFactor);
}

Marshal.StructureToPtr(minMaxInfo, lParam, true);
break;

}
return CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);
}

private nint SetWindowLongPtr(nint hWnd, WindowLongIndexFlags nIndex, WinProc newProc)
{
if (nint.Size == 8)
return SetWindowLongPtr64(hWnd, nIndex, newProc);
else
return new nint(SetWindowLong32(hWnd, nIndex, newProc));
}

internal struct POINT
{
public int x;
public int y;
}

[StructLayout(LayoutKind.Sequential)]
private struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
}
}
}
19 changes: 10 additions & 9 deletions WinUIGallery/TabViewPages/TabViewWindowingSamplePage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
using System.Threading.Tasks;
using Windows.System;
using DispatcherQueueHandler = Microsoft.UI.Dispatching.DispatcherQueueHandler;
using WinUIGallery.Helper;

namespace AppUIBasics.TabViewPages
{
public sealed partial class TabViewWindowingSamplePage : Page
{
private const string DataIdentifier = "MyTabItem";
private Win32WindowHelper win32WindowHelper;
public TabViewWindowingSamplePage()
{
this.InitializeComponent();
Expand All @@ -29,6 +31,12 @@ public TabViewWindowingSamplePage()
Loaded += TabViewWindowingSamplePage_Loaded;
}

public void SetupWindowMinSize(Window window)
{
win32WindowHelper = new Win32WindowHelper(window);
win32WindowHelper.SetWindowMinMaxSize(new Win32WindowHelper.POINT() { x = 500, y = 300 });
}

private void TabViewWindowingSamplePage_Loaded(object sender, RoutedEventArgs e)
{
var currentWindow = WindowHelper.GetWindowForElement(this);
Expand Down Expand Up @@ -57,16 +65,8 @@ private void Tabs_TabItemsChanged(TabView sender, Windows.Foundation.Collections
}
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);

SetupWindow();
}

void SetupWindow()
public void LoadDemoData()
{

// Main Window -- add some default items
for (int i = 0; i < 3; i++)
{
Expand All @@ -92,6 +92,7 @@ private void Tabs_TabDroppedOutside(TabView sender, TabViewTabDroppedOutsideEven
var newWindow = WindowHelper.CreateWindow();
newWindow.ExtendsContentIntoTitleBar = true;
newWindow.Content = newPage;
newPage.SetupWindowMinSize(newWindow);

newWindow.Activate();
}
Expand Down

0 comments on commit 9134fbe

Please sign in to comment.