Jump to content

Recommended Posts

Posted

Thank you as always!

2 hours ago, WildByDesign said:

What happens in the case of version mismatches between the bootstrapper DLL and the Windows App SDK that a user has on their machine?

I think it should be OK. It'll just try to load a requested runtime version - and it'll either succeed or fail. 

A few posts back, we found MddBootstrapInitialize2 reported success when the installed runtime differed from what we requested,  but the runtime didn't actually initialize.  So in response to that,  _WinUI3_Startup() now attempts to retrieve a version number to verify all is OK before returning success.

By memory, loading the "2.0 experimental3" version worked fine with our current bootstrapper, so I don't think its critical to keep this dll up to date.  But it might be worth double checking I guess. Either way I'll keep the dll up to date in the WinRT releases.

Also - the default runtime version that _WinUI3_Startup() targets is static, and that will change depending on the WinRT release.  This is because all our WinUI3 libraries are generated from one WinAPPSDK version, and so _WinUI3_Startup is setup to target that.  I'll pop an announcement up whenever the default version changes (I'll probably release whenever a new stable SDK drops), but there's certainly no plans to support parallel versions of the SDK!!!

3 hours ago, WildByDesign said:

. I've never fully understood where (or when) end users get updates to the Windows App SDK platform.

yeah generally end users don't. It's our responsibility as developers to distribute the runtime along with our software. (Multiple versions can co-exist).

This is probably worth a read around distribution, if we ever make it that far!
https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/deploy-unpackaged-apps

3 hours ago, WildByDesign said:

I feel like it's kind of unfortunate that MS doesn't store that bootstrapper DLL anywhere on a user's machine

yeah, a decision was made obviously made at some point to decouple the WinUI stuff from the OS. So lucky us I guess!

Posted (edited)

Hi guys, just FYI. 

Here's how the "override-able interfaces" stuff is currently looking for the next WinRT release.

In WinRT.au3...

  •  _WinRT_CreateOverridesObj($pFactory, $ahFunctions, $sOverriddenIID)
    • $pFactory must be a pointer to a "composable" interface.
    • The $ahFunctions param must be an array of handles returned from DllCallbackRegister().
    • The order is important - it must match the v-table of the interface that we're overriding.  (do not include IUnknown and IInspectable methods, these are handled internally)
  • _WinRT_DestroyOverridesObj($pOverrides, $bFreeCalbacks = True)

    • ATM the $pOverrides must be the same pointer as that returned by _WinRT_CreateOverridesObj. So be careful when using _WinRT_SwitchInterface() - it'll change your object pointer as you're flicking between interfaces!

    • By default DestroyOverridesObj frees the callbacks that were provided on creation of the object.  If we're using one function for multiple objects for eg., you may need to suppress this behavior by setting  $bFreeCalbacks to False.  (I may yet reverse the default setting for the param... I'll probably need to play with other objects with a few more override-able methods before deciding!)

    • The IUnknown/IInspectable funcs again are managed internally, and are not affected by the $bFreeCalbacks param.

And in WinRT_WinUI3.au3 there's a special func for OnLaunched().

  • _WinUI3_StartApp($sOnLaunchedFunc)
    • $sOnLaunchedFunc is the user function to be executed on Launch
    • The user func must have 2 params to receive the 2 objects..
      • Microsoft.UI.Xaml.Application
      • Microsoft.UI.Xaml.LaunchActivatedEventArgs pEventArgs
    • _WinUI3_StartApp will also call IApplicationStatics_Start() to kick off the dispatch loop. (for now anyway!)
      • I might take this part out if its necessary to do more stuff on the statics interface before launching!

ATM it looks kinda like this...

#include "..\Include\WinRT.au3"
#include "..\Include\WinRT_WinUI3.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Application.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.LaunchActivatedEventArgs.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Window.au3"

_WinRT_Startup()
_WinUI3_Startup()
_WinUI3_StartApp("OnLaunched")
_WinUI3_Shutdown()
_WinRT_Shutdown()

Func OnLaunched($pApplication, $pEventArgs)
    Local $pWindow = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Window")
    _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
    IWindow_Activate($pWindow)
EndFunc
Edited by MattyD
  • 4 months later...
Posted

@WildByDesign, this one's for you mate,

Mica in a normal win32 window without the extended frame business ;)

#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include "..\Include\WinRT.au3"
#include "..\Include\WinRT_WinUI3.au3"
#include "..\Include\Classes\Microsoft.UI.Composition.SystemBackdrops.MicaController.au3"
#include "..\Include\Classes\Microsoft.UI.Composition.SystemBackdrops.DesktopAcrylicController.au3"
#include "..\Include\Classes\Windows.UI.Composition.Desktop.DesktopWindowTarget.au3"
#include "..\Include\Classes\Windows.System.DispatcherQueueController.au3"
#include "..\Include\Classes\Windows.UI.Composition.Compositor.au3"
#include "..\Include\Classes\Windows.UI.Composition.Visual.au3"

Global Const $sIID_ICompositorDesktopInterop = "{29E691FA-4567-4DCA-B319-D0F207EB6807}"
$__g_mIIDs[$sIID_ICompositorDesktopInterop] = "ICompositorDesktopInterop"

Global Const $tagDispatcherQueueOptions  = "struct;dword dwSize;dword threadType;dword apartmentType;endstruct"
Global Const $DQTYPE_THREAD_DEDICATED = 1
Global Const $DQTYPE_THREAD_CURRENT = 2
Global Const $DQTAT_COM_NONE = 0
Global Const $DQTAT_COM_ASTA = 1
Global Const $DQTAT_COM_STA = 2

_Init()
_Main()
_Cleanup()

Func _Init()
    _WinRT_Startup()
    _WinUI3_Startup()
EndFunc

Func _Main()
    Local $hGUI = GUICreate("Mica", 400, 200, -1, -1, $WS_OVERLAPPEDWINDOW, $WS_EX_COMPOSITED)
    Local $pDispatcher = _WinRT_CreateDispatcherQueueController()
    Local $pCompositor = _WinRT_ActivateInstance("Windows.UI.Composition.Compositor")

    _WinRT_SwitchInterface($pCompositor, $sIID_ICompositorDesktopInterop)
    Local $pTarget = ICompositorDesktopInterop_CreateDesktopWindowTarget($pCompositor, $hGUI, False)


    _WinRT_SwitchInterface($pCompositor, $sIID_ICompositor)
    Local $pRoot = ICompositor_CreateContainerVisual($pCompositor)

    _WinRT_SwitchInterface($pTarget, $sIID_ICompositionTarget)
    ICompositionTarget_SetRoot($pTarget, $pRoot)
    _WinRT_DisplayError()

    Local $pMicaController = _WinRT_ActivateInstance("Microsoft.UI.Composition.SystemBackdrops.MicaController")
    _WinRT_SwitchInterface($pMicaController, $sIID_IMicaController2)
    IMicaController2_SetKind($pMicaController, $mMicaKind["Base"]) ; OR $mMicaKind["BaseAlt"]

    _WinRT_SwitchInterface($pMicaController, $sIID_ISystemBackdropController)
    _WinRT_SwitchInterface($pTarget, $sIID_IDesktopWindowTarget)
    ISystemBackdropController_SetTarget($pMicaController, _WinUI3_GetWindowIdFromWindow($hGUI), $pTarget)

    GUISetState()
    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    _WinRT_SwitchInterface($pMicaController, $sIID_IClosable)
    IClosable_Close($pMicaController)

    _WinRT_SwitchInterface($pRoot, $sIID_IClosable)
    IClosable_Close($pRoot)

    _WinRT_SwitchInterface($pTarget, $sIID_IClosable)
    IClosable_Close($pTarget)

    _WinRT_SwitchInterface($pCompositor, $sIID_IClosable)
    IClosable_Close($pCompositor)

    IUnknown_Release($pDispatcher)
EndFunc

Func _Cleanup()

    _WinRT_Shutdown()
    _WinUI3_Shutdown()
EndFunc

Func _WinRT_CreateDispatcherQueueController()

    Local $tOptions = DllStructCreate($tagDispatcherQueueOptions)
    $tOptions.dwSize = DllStructGetSize($tOptions)
    $tOptions.threadType = $DQTYPE_THREAD_CURRENT
    $tOptions.apartmentType = $DQTAT_COM_NONE

    Local $aCall = DllCall("CoreMessaging.dll", "long", "CreateDispatcherQueueController", "struct*", $tOptions, "ptr*", 0)
    Return SetError($aCall[0], 0, $aCall[2])
EndFunc

Func ICompositorDesktopInterop_CreateDesktopWindowTarget($pThis, $hTarget, $bIsTopmost)
    Local $vFailVal = False
    Local $pFunc = __WinRT_GetFuncAddress($pThis, 4)
    If @error Then Return SetError(@error, @extended, $vFailVal)
    If $hTarget And Not IsHWnd($hTarget) Then Return SetError($ERROR_INVALID_PARAMETER, 0, $vFailVal)

    Local $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "hwnd", $hTarget, "bool", ($bIsTopmost = True), "ptr*", 0)
    If @error Then Return SetError(__WinRT_GetDllError(), 0, $vFailVal)
    Return SetError($aCall[0], 0, $aCall[4])

EndFunc   ;==>ICompositorDesktopInterop_CreateDesktopWindowTarget
Posted (edited)
1 hour ago, MattyD said:

this one's for you mate,

Mica in a normal win32 window without the extended frame business ;)

You just made my day, Matty! 😁

I am incredibly thankful. I certainly never would have been able to do it myself because the WinRT stuff still boggles my mind. Thank you.

EDIT: By the way, if you have a moment, could you please share an example with Acrylic?

I'm reading up on the controllers and such right now but I am not anywhere near understanding it yet. Regardless, I am going to spend the next hour or so trying to learn more because this is really neat. You've got the whole Mica effect which is great.

EDIT2: I like how the Mica effect can switch between dark mode and light mode.

Edited by WildByDesign
Posted (edited)

No problems. I found we also need $WS_CLIPCHILDREN in order to see controls too.

Still need to confirm what the most "correct" way is to remove a backdrop.  I'm just closing the backdrop controller atm, but not sure if we should be de-targeting a window's visual container first...  Anyway - there's no crashy crashy when we tear down windows, so we can't be doing anything too bad!

Edit: Added title bar effect.

#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include "..\Include\WinRT.au3"
#include "..\Include\WinRT_WinUI3.au3"
#include "..\Include\Classes\Microsoft.UI.Composition.SystemBackdrops.MicaController.au3"
#include "..\Include\Classes\Microsoft.UI.Composition.SystemBackdrops.DesktopAcrylicController.au3"
#include "..\Include\Classes\Windows.UI.Composition.Desktop.DesktopWindowTarget.au3"
#include "..\Include\Classes\Windows.System.DispatcherQueueController.au3"
#include "..\Include\Classes\Windows.UI.Composition.Compositor.au3"
#include "..\Include\Classes\Windows.UI.Composition.Visual.au3"

Global Const $sIID_ICompositorDesktopInterop = "{29E691FA-4567-4DCA-B319-D0F207EB6807}"
$__g_mIIDs[$sIID_ICompositorDesktopInterop] = "ICompositorDesktopInterop"

Global Const $tagDispatcherQueueOptions  = "struct;dword dwSize;dword threadType;dword apartmentType;endstruct"
Global Const $DQTYPE_THREAD_DEDICATED = 1
Global Const $DQTYPE_THREAD_CURRENT = 2
Global Const $DQTAT_COM_NONE = 0
Global Const $DQTAT_COM_ASTA = 1
Global Const $DQTAT_COM_STA = 2

Global Const $DWMSBT_AUTO = 0
Global Const $DWMSBT_NONE = 1
Global Const $DWMSBT_MAINWINDOW = 2
Global Const $DWMSBT_TRANSIENTWINDOW = 3
Global Const $DWMSBT_TABBEDWINDOW = 4

Global $pDispatcher, $pCompositor
Global $aObjects[0][4]

_Init()
_Main()
_Cleanup()


Func _Init()
    _WinRT_Startup()
    _WinUI3_Startup()
    $pDispatcher = _WinRT_CreateDispatcherQueueController()
    $pCompositor = _WinRT_ActivateInstance("Windows.UI.Composition.Compositor")
EndFunc

Func _Main()

    Local $iWindowCount = 1
    Local $hGUI = GUICreate("System Backdrops", 240, 100, -1, -1, BitOR($WS_OVERLAPPED, $WS_CAPTION, $WS_SYSMENU))
    Local $idCreateMica = GUICtrlCreateButton("Mica", 20, 20, 60, 60)
    Local $idCreateMicaAlt = GUICtrlCreateButton("Mica Alt", 90, 20, 60, 60)
    Local $idCreateAcrylic = GUICtrlCreateButton("Acrylic", 160, 20, 60, 60)

    GUISetState()
    Local $aMsg
    While 1
        $aMsg = GUIGetMsg($GUI_EVENT_ARRAY)
        Switch $aMsg[0]
            Case $GUI_EVENT_CLOSE
                DeleteWin($aMsg[1])
                $iWindowCount -= 1

            Case $idCreateMica
                _MicaWindow(200, 100)
                $iWindowCount += 1

            Case $idCreateMicaAlt
                _MicaWindow(200, 100, True)
                $iWindowCount += 1

            Case $idCreateAcrylic
                _AcrylicWindow(200, 100)
                $iWindowCount += 1

        EndSwitch
        If Not $iWindowCount Then ExitLoop
    WEnd
EndFunc


Func _MicaWindow($iWidth, $iHeight, $bAlternate = False)
    ReDim $aObjects[UBound($aObjects) + 1][4]
    Local $iIdx = UBound($aObjects) - 1

    Local $hGUI = GUICreate($bAlternate ? "MicaAlt" : "Mica", $iWidth, $iHeight, -1, -1, BitOR($WS_OVERLAPPED, $WS_CAPTION, $WS_SYSMENU, $WS_CLIPCHILDREN), $WS_EX_COMPOSITED)
    _SetNCBackdropType($hGUI, $bAlternate ? $DWMSBT_TABBEDWINDOW : $DWMSBT_MAINWINDOW)
    _CreateCloseButton($hGUI, $iWidth - 90, $iHeight - 35, 80, 25)

    _WinRT_SwitchInterface($pCompositor, $sIID_ICompositorDesktopInterop)
    Local $pTarget = ICompositorDesktopInterop_CreateDesktopWindowTarget($pCompositor, $hGUI, False)

    _WinRT_SwitchInterface($pCompositor, $sIID_ICompositor)
    Local $pRoot = ICompositor_CreateContainerVisual($pCompositor)

    _WinRT_SwitchInterface($pTarget, $sIID_ICompositionTarget)
    ICompositionTarget_SetRoot($pTarget, $pRoot)

    Local $pSysBkdropController = _WinRT_ActivateInstance("Microsoft.UI.Composition.SystemBackdrops.MicaController")
    _WinRT_SwitchInterface($pSysBkdropController, $sIID_IMicaController2)
    IMicaController2_SetKind($pSysBkdropController, $bAlternate ? $mMicaKind["BaseAlt"] : $mMicaKind["Base"])

    _WinRT_SwitchInterface($pSysBkdropController, $sIID_ISystemBackdropController)
    _WinRT_SwitchInterface($pTarget, $sIID_IDesktopWindowTarget)
    ISystemBackdropController_SetTarget($pSysBkdropController, _WinUI3_GetWindowIdFromWindow($hGUI), $pTarget)

    GUISetState(@SW_SHOW, $hGUI)

    $aObjects[$iIdx][0] = $hGUI
    $aObjects[$iIdx][1] = $pSysBkdropController
    $aObjects[$iIdx][2] = $pRoot
    $aObjects[$iIdx][3] = $pTarget
EndFunc

Func _AcrylicWindow($iWidth, $iHeight)
    ReDim $aObjects[UBound($aObjects) + 1][4]
    Local $iIdx = UBound($aObjects) - 1

    Local $hGUI = GUICreate("Acrylic", $iWidth, $iHeight, -1, -1, BitOR($WS_OVERLAPPED, $WS_CAPTION, $WS_SYSMENU, $WS_CLIPCHILDREN), $WS_EX_COMPOSITED)
    _SetNCBackdropType($hGUI, $DWMSBT_TRANSIENTWINDOW)
    _CreateCloseButton($hGUI, $iWidth - 90, $iHeight - 35, 80, 25)

    _WinRT_SwitchInterface($pCompositor, $sIID_ICompositorDesktopInterop)
    Local $pTarget = ICompositorDesktopInterop_CreateDesktopWindowTarget($pCompositor, $hGUI, False)

    _WinRT_SwitchInterface($pCompositor, $sIID_ICompositor)
    Local $pRoot = ICompositor_CreateContainerVisual($pCompositor)

    _WinRT_SwitchInterface($pTarget, $sIID_ICompositionTarget)
    ICompositionTarget_SetRoot($pTarget, $pRoot)

    Local $pSysBkdropController = _WinRT_ActivateInstance("Microsoft.UI.Composition.SystemBackdrops.DesktopAcrylicController")
    _WinRT_SwitchInterface($pSysBkdropController, $sIID_ISystemBackdropController)
    _WinRT_SwitchInterface($pTarget, $sIID_IDesktopWindowTarget)

    ISystemBackdropController_SetTarget($pSysBkdropController, _WinUI3_GetWindowIdFromWindow($hGUI), $pTarget)

    $aObjects[$iIdx][0] = $hGUI
    $aObjects[$iIdx][1] = $pSysBkdropController
    $aObjects[$iIdx][2] = $pRoot
    $aObjects[$iIdx][3] = $pTarget

    GUISetState(@SW_SHOW, $hGUI)
EndFunc

Func DeleteWin($hWnd)
    For $i = 0 To UBound($aObjects) - 1
        If $hWnd = $aObjects[$i][0] Then
            For $j = 1 To UBound($aObjects, 2) - 1
                _WinRT_SwitchInterface($aObjects[$i][$j], $sIID_IClosable)
                IClosable_Close($aObjects[$i][$j])
            Next
            $aObjects[$i][0] = 0
        EndIf
    Next
    GUIDelete($hWnd)
EndFunc

Func _Cleanup()
    _WinRT_SwitchInterface($pCompositor, $sIID_IClosable)
    IClosable_Close($pCompositor)
    IUnknown_Release($pDispatcher)
    _WinRT_Shutdown()
    _WinUI3_Shutdown()
EndFunc

Func _WinRT_CreateDispatcherQueueController()
    Local $tOptions = DllStructCreate($tagDispatcherQueueOptions)
    $tOptions.dwSize = DllStructGetSize($tOptions)
    $tOptions.threadType = $DQTYPE_THREAD_CURRENT
    $tOptions.apartmentType = $DQTAT_COM_NONE

    Local $aCall = DllCall("CoreMessaging.dll", "long", "CreateDispatcherQueueController", "struct*", $tOptions, "ptr*", 0)
    Return SetError($aCall[0], 0, $aCall[2])
EndFunc

Func ICompositorDesktopInterop_CreateDesktopWindowTarget($pThis, $hTarget, $bIsTopmost)
    Local $vFailVal = False
    Local $pFunc = __WinRT_GetFuncAddress($pThis, 4)
    If @error Then Return SetError(@error, @extended, $vFailVal)
    If $hTarget And Not IsHWnd($hTarget) Then Return SetError($ERROR_INVALID_PARAMETER, 0, $vFailVal)

    Local $aCall = DllCallAddress("ulong", $pFunc, "ptr", $pThis, "hwnd", $hTarget, "bool", ($bIsTopmost = True), "ptr*", 0)
    If @error Then Return SetError(__WinRT_GetDllError(), 0, $vFailVal)
    Return SetError($aCall[0], 0, $aCall[4])
EndFunc   ;==>ICompositorDesktopInterop_CreateDesktopWindowTarget

Func _CreateCloseButton($hWnd, $iX, $iY, $iWidth, $iHeight, $iStyle = -1, $iExStyle = -1)
    If $iStyle = -1 Then $iStyle = BitOR($WS_VISIBLE, $WS_CHILD)
    If $iExStyle = -1 Then $iExStyle = 0
    $hButton = _WinAPI_CreateWindowEx($iExStyle, $WC_BUTTON, "Close", $iStyle, $iX, $iY, $iWidth, $iHeight, $hWnd, $IDCANCEL, _WinAPI_GetModuleHandle(0), 0)
    $hButton = _SendMessage($hButton, $WM_SETFONT, _WinAPI_GetStockObject($DEFAULT_GUI_FONT), True)
    Return $hButton
EndFunc

Func _SetNCBackdropType($hWnd, $iBackdropType)
    Local $aCall = DllCall("Dwmapi.dll", "long", "DwmSetWindowAttribute", "hwnd", $hWnd, "dword", $DWMWA_SYSTEMBACKDROP_TYPE, "dword*", $iBackdropType, "dword", 4)
    Return SetError($aCall[0], 0, $aCall[0] = $S_OK)
EndFunc
Edited by MattyD
Posted
4 hours ago, MattyD said:

No problems. I found we also need $WS_CLIPCHILDREN in order to see controls too.

Thanks. This example is a really nice example for showcasing the Win11 materials. I like how you've done a separate button to test each one. Excellent example.

The only concern that I have is the WS_EX_COMPOSITED extended style. I know that Microsoft has that in all of their examples for Win11 materials on Win32 GUI, so clearly there is a reason for it. I personally like WS_EX_COMPOSITED, but it is a double-edged sword in AutoIt due to the way in which AutoIt creates their GUI windows:

Extended Window Styles (Microsoft):

Quote

This cannot be used if the window has a class style of CS_OWNDC, CS_CLASSDC, or CS_PARENTDC.

I also tested your examples at one point removing the WS_EX_COMPOSITED extended style. I didn't notice any issues specific to having or not having WS_EX_COMPOSITED. The background (right side and bottom edge of frame) seems to have a white flicker when resizing the GUI no matter what. I'm not sure exactly why.

Do you foresee any issues without WS_EX_COMPOSITED?

Posted

Yeah you're right, the AutoIt GUI class does have CS_OWNDC.

13 hours ago, WildByDesign said:

Do you foresee any issues without WS_EX_COMPOSITED?

I'm still sussing things out -  WS_EX_COMPOSITED was in the Microsoft example that I was trying to emulate, but it seems to work ok without it... Not sure if we need this to blend our Win32 buttons properly if clipping isn't the answer.

If it turns out that WS_EX_COMPOSITED is required,  we can strip the CS_OWNDC style from our GUI class before creating windows.

Local $sAu3GUIClass = "AutoIt v3 GUI"
Local $tInfo = _WinAPI_GetClassInfoEx($sAu3GUIClass, _WinAPI_GetModuleHandle(0))
$tInfo.Size = DllStructGetSize($tInfo)
$tInfo.Style = 0x0B
Local $tClassName = DllStructCreate("wchar name[257]")
$tClassName.name = $sAu3GUIClass
$tInfo.ClassName = DllStructGetPtr($tClassName)
_WinAPI_UnregisterClass($sAu3GUIClass, _WinAPI_GetModuleHandle(0))
_WinAPI_RegisterClassEx($tInfo)

RE clipping. - everything is clipped as a square, so our buttons with rounded corners are a bit ugly.  If we can't get the 2 worlds working together nicely I've had some success using layered windows to get the buttons floating nicely on top.  I doubt its the most efficient way of approaching things - but hey.  I'll post an example of that in a little bit.

I can't find much in the way of doco/examples around any of this, so its all very much "suck it and see" ATM

Posted
7 hours ago, MattyD said:

If it turns out that WS_EX_COMPOSITED is required,  we can strip the CS_OWNDC style from our GUI class before creating windows.

Local $sAu3GUIClass = "AutoIt v3 GUI"
Local $tInfo = _WinAPI_GetClassInfoEx($sAu3GUIClass, _WinAPI_GetModuleHandle(0))
$tInfo.Size = DllStructGetSize($tInfo)
$tInfo.Style = 0x0B
Local $tClassName = DllStructCreate("wchar name[257]")
$tClassName.name = $sAu3GUIClass
$tInfo.ClassName = DllStructGetPtr($tClassName)
_WinAPI_UnregisterClass($sAu3GUIClass, _WinAPI_GetModuleHandle(0))
_WinAPI_RegisterClassEx($tInfo)

If this type of magic could be done (safely), you could potentially save decades of flickering ListView headers that are affected when WS_EX_COMPOSITED is used on an AutoIt GUI with WS_EX_COMPOSITED extended style applied.

7 hours ago, MattyD said:

RE clipping. - everything is clipped as a square, so our buttons with rounded corners are a bit ugly. 

I didn't have much luck. Some, but not much. I tried handling the brush returned from WM_CTLCOLORBTN which was slightly better, but still left some artifacts.

7 hours ago, MattyD said:

If we can't get the 2 worlds working together nicely I've had some success using layered windows to get the buttons floating nicely on top.  I doubt its the most efficient way of approaching things - but hey.  I'll post an example of that in a little bit.

For sure, even if it does not end up being a solution to this specific issue, I would definitely like to see this layered windows method with the buttons.

7 hours ago, MattyD said:

I can't find much in the way of doco/examples around any of this, so its all very much "suck it and see" ATM

You're right, there isn't much docs for Mica and Win32 specifically. I'm guessing we are lucky that they even extended some of it to Win32.

By the way, do they allow using the Mica/Acrylic created brushes in Win32?

My guess is no, but I figure you would know better. They have details about creating these types of brushes but I have a feeling that they don't extend those to Win32.

Posted
7 hours ago, MattyD said:

we can strip the CS_OWNDC style from our GUI class before creating windows.

How would you strip it before the window is created?

Or would that mean avoiding the use of GUICreate and using CreateWindowEx API instead?

It just boggles my mind on how to catch it early enough.

Posted

 

2 minutes ago, WildByDesign said:
7 hours ago, MattyD said:

If we can't get the 2 worlds working together nicely I've had some success using layered windows to get the buttons floating nicely on top.  I doubt its the most efficient way of approaching things - but hey.  I'll post an example of that in a little bit.

For sure, even if it does not end up being a solution to this specific issue, I would definitely like to see this layered windows method with the buttons.

 Gonna have to backtrack on that sorry! - I was doing something dumb🤦‍♂️

My current theory is that the WinRT compositor just paints last - after a widow's traditional paint cycle. If this is always True,  neither WS_EX_COMPOSITED or WS_EX_LAYERED will be able to save us. The compositor will ultimately just paint its visual tree over the top. But I could be totally wrong about this too.

Clipping children works because the parent is not allowed to paint into a child's region at all.. So we can't paint over a button - but we also can't paint a mica background behind it. 

It looks like you can do some very cool things with the compositor.  But perhaps its not best suited to being a backdrop for win32 controls!

10 minutes ago, WildByDesign said:
7 hours ago, MattyD said:

we can strip the CS_OWNDC style from our GUI class before creating windows.

How would you strip it before the window is created?

Or would that mean avoiding the use of GUICreate and using CreateWindowEx API instead?

It just boggles my mind on how to catch it early enough.

Its just a sequence thing, not a race ;).  AutoIt registers a window Class on startup named "AutoIt v3 GUI", and we get a window of this class by calling GUICreate(). 

We can happily query, modify, and re-register that class - but only while there's no AutoIt GUIs  around.

42 minutes ago, WildByDesign said:

If this type of magic could be done (safely)...

I haven't tested anything, but If you're just doing stock windows things I'd imagine it should be pretty safe.  If its going to break it'll likely be around autoit-specific funcs that customize the look - so things like GuiSetBkColor etc.

Posted

Alright, I managed to get the layered window working in the end, but we run into the same trouble. The button background is part of the button - so we still get a square surrounding it.  We can make the button background transparent and _SetNCBackdropType() will fill in the gaps with a material... but your mouse will simply click through that bit of the window.

Comping together the 2 UI frameworks is confirmed a no-go.  There's event a name for it:

Quote

When two different UI technologies are used together, such as WPF and the Visual Layer, they are each responsible for drawing their own pixels on the screen, and they can't share pixels. As a result, Visual Layer content is always rendered on top of other UI content. (This is known as the airspace issue.)

We can restrict where the button can paint by setting a region though. This looks great, but it won't help with things like labels - good luck with cutting holes around text!

Func _CreateCloseButton($hWnd, $iX, $iY, $iWidth, $iHeight, $iStyle = -1, $iExStyle = -1)
    If $iStyle = -1 Then $iStyle = BitOR($WS_VISIBLE, $WS_CHILD)
    If $iExStyle = -1 Then $iExStyle = 0
    $hButton = _WinAPI_CreateWindowEx($iExStyle, $WC_BUTTON, "Close", $iStyle, $iX, $iY, $iWidth, $iHeight, $hWnd, $IDCANCEL, _WinAPI_GetModuleHandle(0), 0)
   _SendMessage($hButton, $WM_SETFONT, _WinAPI_GetStockObject($DEFAULT_GUI_FONT), True)
    Local $hRgn = _WinAPI_CreateRoundRectRgn(1, 1, $iWidth, $iHeight, 4, 4)
    _WinAPI_SetWindowRgn($hButton, $hRgn, False)
    Return $hButton
EndFunc

 

Posted
19 hours ago, MattyD said:

Comping together the 2 UI frameworks is confirmed a no-go.

Yeah it definitely seems like we are going against the grain on this one. More cons than pros unfortunately.

I see that you've added the backdrops in your updated WinRT 1.7 release which is nice. It definitely makes sense to keep WinUI3 with WinUI3 but not mix with Win32 as we've just learned.

I appreciate you digging into this. Even though it wasn't entirely successful in the end, it was fun and I still learned a few things.

Posted

Access to the compositor still has value, so the exercise was worth while. It looks like you can do all sorts of fancy animations in there and such.

At some point I'll dig into that a bit more, time permitting..

Posted (edited)

@CYCho, I've been having a bit of a play with the WinUI3 media player over the last couple of days. Its still pretty rough, and there's not a lot of error checking going on - but thought you might want a look.  

There's issues with displaying the internal transport controls - my best guess is that we're missing some resources. But in the meantime we can just create our own.

I've added in some extra bits as an experiment. 

  • There's an idle timer on the mouse. If you're in fullscreen, after a couple of seconds of inactivity the controls will hide and the cursor will disappear.
    • I think we can animate the controls hiding too - but I need to look a bit closer at that.
  • You can double click/double tap the player to enter/exit fullscreen
  • Added in some KB accelerators. 
    • Space or P  - Play/Pause, O - Open File, F - Fullscreen
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#AutoIt3Wrapper_UseX64=Y
#Tidy_Parameters=/sf

#include "..\Include\WinRT.au3"
#include "..\Include\WinRT_WinUI3.au3"
;~ #include "..\Include\Interfaces\IInputCursorStaticsInterop.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.Application.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.LaunchActivatedEventArgs.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Window.au3"
#include "..\Include\Classes\Microsoft.UI.Windowing.AppWindow.au3"
#include "..\Include\Classes\Microsoft.UI.Windowing.AppWindowPresenter.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.XamlRoot.au3"
#include "..\Include\Classes\Microsoft.UI.Content.ContentIslandEnvironment.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Grid.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.RowDefinitionCollection.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.ColumnDefinitionCollection.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.RowDefinition.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.ColumnDefinition.au3"
#include "..\Include\Enumerations\Microsoft.UI.Xaml.GridUnitType.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Stackpanel.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.FontIcon.au3"
#include "..\Include\Enumerations\Microsoft.UI.Xaml.Controls.Symbol.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Button.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Slider.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Input.KeyboardAccelerator.au3"
#include "..\Include\Classes\Windows.Foundation.PropertyValue.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.MediaPlayerElement.au3"
#include "..\Include\Classes\Windows.Storage.StorageFile.au3"
#include "..\Include\Classes\Windows.Media.Core.MediaSource.au3"
#include "..\Include\Classes\Windows.Media.Core.MediaSourceStateChangedEventArgs.au3"
#include "..\Include\Classes\Windows.Media.Playback.MediaPlayer.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.MediaTransportControls.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.DispatcherTimer.au3"
#include "..\Include\Classes\Microsoft.UI.Input.InputCursor.au3"
#include "..\Include\Classes\Windows.UI.Core.CoreCursor.au3"

#include "..\Include\Classes\Microsoft.Windows.Storage.Pickers.FileOpenPicker.au3"
#include "..\Include\Classes\Microsoft.Windows.Storage.Pickers.PickFileResult.au3"


Global Const $sIID_IInputCursorStaticsInterop = "{AC6F5065-90C4-46CE-BEB7-05E138E54117}"
$__g_mIIDs[$sIID_IInputCursorStaticsInterop] = "IInputCursorStaticsInterop"

Global $iAppWindowId, $iWindowId
Global $iMediaDuration, $sPlayerState
Global $pPlayer, $pColCtrlRowDef, $pPlayButton, $pFullScrButton, $pMediaPlayerElement, $pTimer
Global $bAreCtrlsHidden

Global $aDelegates[10]

_WinAPI_SetThreadExecutionState(BitOR($ES_CONTINUOUS, $ES_DISPLAY_REQUIRED))
_WinRT_Startup()
If @error Then Exit MsgBox(0, "WinRT Startup", "Error Initialising: " & _WinAPI_GetErrorMessage(@error))
_WinUI3_Startup()
If @error Then 
    MsgBox(0, "WinRT Startup", "Error Initialising: " & _WinAPI_GetErrorMessage(@error))
    _WinRT_Shutdown()
    Exit 
EndIf
_WinUI3_StartApp("OnLaunched")
Cleanup()
_WinUI3_Shutdown()
_WinRT_Shutdown()

Func OnLaunched($pApplication, $pEventArgs)
    #forceref $pApplication, $pEventArgs

    ;Setup Window
    Local $pWindow = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Window")
    _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
    IWindow_SetTitle($pWindow, "Media Player")

    _WinRT_SwitchInterface($pWindow, $sIID_IWindow2)
    Local $pAppWindow = IWindow2_GetAppWindow($pWindow)
    _WinRT_SwitchInterface($pAppWindow, $sIID_IAppWindow)
    $iAppWindowId = IAppWindow_GetId($pAppWindow)

    Local $tRectInt = DllStructCreate("int X;int Y;int Width; int Height")
    $tRectInt.Width = 800
    $tRectInt.Height = 450
    $tRectInt.X = (@DesktopWidth - $tRectInt.Width) / 2
    $tRectInt.Y = (@DesktopHeight - $tRectInt.Height) / 2
    IAppWindow_MoveAndResize($pAppWindow, $tRectInt)
    IUnknown_Release($pAppWindow)

    ;Create Grid
    Local $pGrid = CreateGrid()
    IUnknown_Release(GridAddRow($pGrid))
    $pColCtrlRowDef = GridAddRow($pGrid, 60, "Pixel")
    IUnknown_Release(GridAddColumn($pGrid))
    _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
    IWindow_SetContent($pWindow, $pGrid)

    ;Create Stackpanel for buttons.
    Local $pStackPanel = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.StackPanel")
    _WinRT_SwitchInterface($pStackPanel, $sIID_IStackPanel)
    IStackPanel_SetOrientation($pStackPanel, $mOrientation["Horizontal"])
    IStackPanel_SetSpacing($pStackPanel, 10)
    Local $tPadding = DllStructCreate("struct;double Left;double Top;double Right; double Bottom;endstruct")
    $tPadding.Left = 10
    $tPadding.Top = 10
    $tPadding.Right = 10
    $tPadding.Bottom = 10
    IStackPanel_SetPadding($pStackPanel, $tPadding)
    PanelAddChild($pGrid, $pStackPanel)
    UIElementAssignGridPos($pStackPanel, 2, 1)

    ;Create Play/Pause Button
    $pPlayButton = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Button")
    _WinRT_SwitchInterface($pPlayButton, $sIID_IFrameworkElement)
    ButtonSetGlyph($pPlayButton, "Play")
    PanelAddChild($pStackPanel, $pPlayButton)
    _WinRT_SwitchInterface($pPlayButton, $sIID_IButtonBase)
    IButtonBase_AddHdlrClick($pPlayButton, CreateDelegate("Play"))
    UIElementAssignAccel($pPlayButton, "Space")
    UIElementAssignAccel($pPlayButton, "P")

    ;Create Fullscreen Button
    $pFullScrButton = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Button")
    ButtonSetGlyph($pFullScrButton, "FullScreen")
    PanelAddChild($pStackPanel, $pFullScrButton)
    _WinRT_SwitchInterface($pFullScrButton, $sIID_IButtonBase)
    Local $pFullScrDgt = CreateDelegate("FullScreen")
    IButtonBase_AddHdlrClick($pFullScrButton, $pFullScrDgt)
    UIElementAssignAccel($pFullScrButton, "F")

    ;Open Button
    Local $pFileOpenButton = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Button")
    ButtonSetGlyph($pFileOpenButton, "OpenFile")
    PanelAddChild($pStackPanel, $pFileOpenButton)
    _WinRT_SwitchInterface($pFileOpenButton, $sIID_IButtonBase)
    IButtonBase_AddHdlrClick($pFileOpenButton, CreateDelegate("OpenMedia"))
    UIElementAssignAccel($pFileOpenButton, "O")
    IUnknown_Release($pFileOpenButton)
    IUnknown_Release($pStackPanel)

    ;Create Player Element
    $pMediaPlayerElement = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.MediaPlayerElement")
    _WinRT_SwitchInterface($pMediaPlayerElement, $sIID_IFrameworkElement)
    PanelAddChild($pGrid, $pMediaPlayerElement)
    _WinRT_SwitchInterface($pGrid, $sIID_IUIElement)

    ;Add mouse idle handler etc.
    $pTimer = _WinRT_ActivateInstance("Microsoft.UI.Xaml.DispatcherTimer")
    _WinRT_SwitchInterface($pTimer, $sIID_IDispatcherTimer)
    IDispatcherTimer_AddHdlrTick($pTimer, CreateDelegate("TimerExp"))
    IDispatcherTimer_SetInterval($pTimer, 3 * 10000000)
    IDispatcherTimer_Start($pTimer)
    IUIElement_AddHdlrPointerMoved($pGrid, CreateDelegate("MouseMoved"))
    IUnknown_Release($pGrid)

    ;Add double tap handler
    _WinRT_SwitchInterface($pMediaPlayerElement, $sIID_IUIElement)
    IUIElement_AddHdlrDoubleTapped($pMediaPlayerElement, $pFullScrDgt)

    ;Create the underlying player object
    $pPlayer = _WinRT_ActivateInstance("Windows.Media.Playback.MediaPlayer")
    _WinRT_SwitchInterface($pPlayer, $sIID_IMediaPlayer)
    Local $pPlayerStateChgDgt = CreateDelegate("PlayerStateChange", "Windows.Foundation.TypedEventHandler`2<Windows.Media.Playback.MediaPlayer, Object>")
    IMediaPlayer_AddHdlrCurrentStateChanged($pPlayer, $pPlayerStateChgDgt)

    ;Attach the player obj to the XAML control.
    _WinRT_SwitchInterface($pMediaPlayerElement, $sIID_IMediaPlayerElement)
    IMediaPlayerElement_SetMediaPlayer($pMediaPlayerElement, $pPlayer)

    ;Activate window
    _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
    IWindow_Activate($pWindow)
    IUnknown_Release($pWindow)

    OpenMedia(0, 0, 0)
EndFunc   ;==>OnLaunched

Func Cleanup()
    IUnknown_Release($pColCtrlRowDef)
    IUnknown_Release($pFullScrButton)
    IUnknown_Release($pPlayButton)
    IUnknown_Release($pMediaPlayerElement)
    IUnknown_Release($pTimer)

    If $pPlayer Then
;~      _WinRT_SwitchInterface($pPlayer, $sIID_IClosable)
;~      IClosable_Close($pPlayer)
        IUnknown_Release($pPlayer)
    EndIf
    For $i = 0 To UBound($aDelegates) - 1
        If $aDelegates[$i] Then _WinRT_DestroyDelegate($aDelegates[$i])
    Next
EndFunc   ;==>Cleanup

Func _WinAPI_CreateCursor($hInst, $iXHotSpot, $iYHotSpot, $iWidth, $iHeight, $tANDPlane, $tORPlane)
    Local $aCall = DllCall("User32.dll", "handle", "CreateCursor", "handle", $hInst, "int", $iXHotSpot, "int", $iYHotSpot, "int", $iWidth, "int", $iHeight, "struct*", $tANDPlane, "struct*", $tORPlane)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aCall[0]
EndFunc   ;==>_WinAPI_CreateCursor

;modfied to include indefinate wait.
Func _WinRT_WaitForAsync2(ByRef $pAsync, $sDataType = "ptr*", $iTimeout = 500)
    Local $pAsyncInfo = IUnknown_QueryInterface($pAsync, $sIID_IAsyncInfo)
    If @error Then Return SetError(@error, @extended, -1)

    Local $hTimer = TimerInit()
    Local $iStatus, $iError, $vResult = Null
    Do
        $iStatus = IAsyncInfo_GetStatus($pAsyncInfo)
        If ($iTimeout > 0) And (TimerDiff($hTimer) > $iTimeout) Then ExitLoop
        Sleep(10)
    Until $iStatus <> _WinRT_GetEnum($mAsyncStatus, "Started")
    Switch $iStatus
        Case _WinRT_GetEnum($mAsyncStatus, "Started")
            $iStatus = -1
            $iError = $WAIT_TIMEOUT
        Case Else
            $iError = IAsyncInfo_GetErrorCode($pAsyncInfo)
    EndSwitch

    If $iStatus = _WinRT_GetEnum($mAsyncStatus, "Completed") Then
        IUnknown_QueryInterface($pAsync, $sIID_IAsyncAction)
        If Not @error Then
            $vResult = ($iError = $S_OK)
        Else
            $vResult = IAsyncOperation_GetResults($pAsync, $sDataType)
            If @error Then $iError = @error
        EndIf
    EndIf
    _WinRT_DeleteObject($pAsync)

    Return SetError($iError, $iStatus, $vResult)
EndFunc   ;==>_WinRT_WaitForAsync2

Func ButtonSetGlyph($pButton, $sSymbol)
    Local $pButtonIcon = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.FontIcon")
    _WinRT_SwitchInterface($pButtonIcon, $sIID_IFontIcon)
    IFontIcon_SetGlyph($pButtonIcon, ChrW($mSymbol[$sSymbol]))

    _WinRT_SwitchInterface($pButton, $sIID_IContentControl)
    IContentControl_SetContent($pButton, $pButtonIcon)
    IUnknown_Release($pButtonIcon)
EndFunc   ;==>ButtonSetGlyph

Func CreateDelegate($sName, $sType = "")
    Local Static $iCount
    If $iCount = UBound($aDelegates) Then ReDim $aDelegates[UBound($aDelegates) + 10]
    $aDelegates[$iCount] = _WinRT_CreateDelegate($sName, $sType)
    $iCount += 1

    Return $aDelegates[$iCount - 1]
EndFunc   ;==>CreateDelegate

Func CreateGrid($iRows = 0, $iCols = 0)
    Local $pGrid = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Grid")
    _WinRT_SwitchInterface($pGrid, $sIID_IGrid)

    Local $tGridLen = DllStructCreate("struct;double Value;ulong GridUnitType;endstruct")
    $tGridLen.Value = 1
    $tGridLen.GridUnitType = $mGridUnitType["Star"]

    Local $pRowDefs = IGrid_GetRowDefinitions($pGrid)
    _WinRT_SwitchInterface($pRowDefs, $sIID_IVector_1_RowDefinition_)
    Local $pRowDef
    For $i = 0 To $iRows - 1
        $pRowDef = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.RowDefinition")
        _WinRT_SwitchInterface($pRowDef, $sIID_IRowDefinition)
        IRowDefinition_SetHeight($pRowDef, $tGridLen)
        IVector_Append($pRowDefs, $pRowDef)
        IUnknown_Release($pRowDef)
    Next
    IUnknown_Release($pRowDefs)

    Local $pColDefs = IGrid_GetColumnDefinitions($pGrid)
    _WinRT_SwitchInterface($pColDefs, $sIID_IVector_1_ColumnDefinition_)
    Local $pColDef
    For $i = 0 To $iCols - 1
        $pColDef = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.ColumnDefinition")
        _WinRT_SwitchInterface($pColDef, $sIID_IColumnDefinition)
        IColumnDefinition_SetWidth($pColDef, $tGridLen)
        IVector_Append($pColDefs, $pColDef)
        IUnknown_Release($pColDef)
    Next
    IUnknown_Release($pColDefs)

    Return $pGrid
EndFunc   ;==>CreateGrid

Func CreateHiddenCursor()
    Local $hInst = _WinAPI_GetModuleHandle(0)
    Local $tANDPlane = DllStructCreate("byte plane[1]")
    Local $tXORPlane = DllStructCreate("byte plane[1]")
    $tANDPlane.plane = 0x80
    $tXORPlane.plane = 0
    Return _WinAPI_CreateCursor($hInst, 0, 0, 1, 1, $tANDPlane, $tXORPlane)
EndFunc   ;==>CreateHiddenCursor

Func FullScreen($pThis, $pSender, $pArgs)
    #forceref $pThis, $pSender, $pArgs
    Local $pAppWindow = GetAppWindowFromId($iAppWindowId)

    _WinRT_SwitchInterface($pAppWindow, $sIID_IAppWindow)
    _WinRT_SwitchInterface($pMediaPlayerElement, $sIID_IMediaPlayerElement)

    Local $pPresenter = IAppWindow_GetPresenter($pAppWindow)

    Switch _WinRT_GetEnum($mAppWindowPresenterKind, IAppWindowPresenter_GetKind($pPresenter))
        Case "FullScreen"
            IMediaPlayerElement_SetIsFullWindow($pMediaPlayerElement, False)
            IAppWindow_SetPresenter2($pAppWindow, $mAppWindowPresenterKind["Overlapped"])
            ButtonSetGlyph($pFullScrButton, "FullScreen")

        Case "Overlapped"
            IMediaPlayerElement_SetIsFullWindow($pMediaPlayerElement, True)
            IAppWindow_SetPresenter2($pAppWindow, $mAppWindowPresenterKind["FullScreen"])
            ButtonSetGlyph($pFullScrButton, "BackToWindow")
            HideControls(False)
    EndSwitch

    IUnknown_Release($pPresenter)
    IUnknown_Release($pAppWindow)
EndFunc   ;==>FullScreen

Func GetAppWindowFromId($iAppWindowId)
    Local $pAppWinStatics = _WinRT_GetActivationFactory("Microsoft.UI.Windowing.AppWindow", $sIID_IAppWindowStatics)
    Local $pAppWindow = IAppWindowStatics_GetFromWindowId($pAppWinStatics, $iAppWindowId)
    IUnknown_Release($pAppWinStatics)
    Return $pAppWindow
EndFunc   ;==>GetAppWindowFromId

Func GridAddColumn($pGrid, $fWidth = 1, $vUnit = "Star")
    _WinRT_SwitchInterface($pGrid, $sIID_IGrid)
    If @error Then Return SetError(@error, @extended, False)

    Local $tGridLen = DllStructCreate("align 4;double Value;ulong GridUnitType")
    $tGridLen.Value = $fWidth
    $tGridLen.GridUnitType = IsString($vUnit) ? $mGridUnitType[$vUnit] : $vUnit

    Local $pColDef, $pColDefs = IGrid_GetColumnDefinitions($pGrid)
    $pColDef = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.ColumnDefinition")
    _WinRT_SwitchInterface($pColDef, $sIID_IColumnDefinition)
    IRowDefinition_SetHeight($pColDef, $tGridLen)
    Local $iError = @error
    If Not $iError Then IVector_Append($pColDefs, $pColDef)
    IUnknown_Release($pColDefs)
    Return SetError($iError, 0, $pColDef)
EndFunc   ;==>GridAddColumn

Func GridAddRow($pGrid, $fHeight = 1, $vUnit = "Star")
    _WinRT_SwitchInterface($pGrid, $sIID_IGrid)
    If @error Then Return SetError(@error, @extended, False)

    Local $tGridLen = DllStructCreate("align 4;double Value;ulong GridUnitType")
    $tGridLen.Value = $fHeight
    $tGridLen.GridUnitType = IsString($vUnit) ? $mGridUnitType[$vUnit] : $vUnit

    Local $pRowDef, $pRowDefs = IGrid_GetRowDefinitions($pGrid)
    $pRowDef = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.RowDefinition")
    _WinRT_SwitchInterface($pRowDef, $sIID_IRowDefinition)
    IRowDefinition_SetHeight($pRowDef, $tGridLen)
    Local $iError = @error
    If Not $iError Then IVector_Append($pRowDefs, $pRowDef)
    IUnknown_Release($pRowDefs)
    Return SetError($iError, 0, $pRowDef)
EndFunc   ;==>GridAddRow

Func HideControls($bHide)

    Local $tGridLen = DllStructCreate("align 4;double Value;ulong GridUnitType")
    $tGridLen.GridUnitType = $mGridUnitType["Pixel"]
    $tGridLen.Value = $bHide ? 0 : 60
    _WinRT_SwitchInterface($pColCtrlRowDef, $sIID_IRowDefinition)
    IRowDefinition_SetHeight($pColCtrlRowDef, $tGridLen)

    $bAreCtrlsHidden = $bHide
EndFunc   ;==>HideControls

Func MediaSrcStateChanged($pThis, $pMediaSrc, $pArgs)
    #forceref $pThis, $pMediaSrc, $pArgs
    Local $sState = _WinRT_GetEnum($mMediaSourceState, IMediaSourceStateChangedEventArgs_GetNewState($pArgs))
    ConsoleWrite("Source State: " & $sState & @CRLF)

    Switch $sState
        Case "Opened"
            _WinRT_SwitchInterface($pMediaSrc, $sIID_IMediaSource2)
            Local $pTimeSpan = IMediaSource2_GetDuration($pMediaSrc)
            $iMediaDuration = _WinRT_GetReference($pTimeSpan, "Int64*")
            IUnknown_Release($pTimeSpan)
            $iMediaDuration = Round($iMediaDuration / 10000000) ;Convert to sec
            ConsoleWrite(StringFormat("Media Duration: %dh %02dm %02ds\r\n", Floor($iMediaDuration / 3600), Mod($iMediaDuration, 3600) / 60, Mod($iMediaDuration, 60)))
    EndSwitch
EndFunc   ;==>MediaSrcStateChanged

Func MouseMoved($pThis, $pSender, $pArgs)

    If $bAreCtrlsHidden Then
        HideControls(False)

        Local $pMediaPlayerElementCurs = $pMediaPlayerElement
        _WinRT_SwitchInterface($pMediaPlayerElementCurs, $sIID_IUIElementProtected)
        Local $pInputCursor = IUIElementProtected_GetProtectedCursor($pMediaPlayerElementCurs)
        IUIElementProtected_SetProtectedCursor($pMediaPlayerElementCurs, 0)

        If $pInputCursor Then
            _WinRT_SwitchInterface($pInputCursor, $sIID_IClosable)
            IClosable_Close($pInputCursor)
            IUnknown_Release($pInputCursor)
        EndIf
    EndIf

    #forceref $pThis, $pSender, $pArgs
    IDispatcherTimer_Stop($pTimer)
    IDispatcherTimer_Start($pTimer)
EndFunc   ;==>MouseMoved

Func OpenMedia($pThis, $pSender, $pArgs)

    #forceref $pThis, $pSender, $pArgs

    Local Static $pMediaSrcStateChDgt = CreateDelegate("MediaSrcStateChanged", "Windows.Foundation.TypedEventHandler`2<Windows.Media.Core.MediaSource, Windows.Media.Core.MediaSourceStateChangedEventArgs>")

    Local $pPickerFact = _WinRT_GetActivationFactory("Microsoft.Windows.Storage.Pickers.FileOpenPicker", $sIID_IFileOpenPickerFactory)
    Local $pPicker = IFileOpenPickerFactory_CreateInstance($pPickerFact, $iAppWindowId)
    IUnknown_Release($pPickerFact)
    _WinRT_SwitchInterface($pPicker, $sIID_IFileOpenPicker2)
    IFileOpenPicker2_SetSuggestedStartFolder($pPicker, @WorkingDir)

    _WinRT_SwitchInterface($pPicker, $sIID_IFileOpenPicker)
    Local $pFilter = IFileOpenPicker_GetFileTypeFilter($pPicker)
    Local $ahSupTypes[] = [ _
            _WinRT_CreateHString(".mp4"), _
            _WinRT_CreateHString(".mov"), _
            _WinRT_CreateHString(".mkv"), _
            _WinRT_CreateHString(".mpg"), _
            _WinRT_CreateHString(".mmv")]

    For $i = 0 To UBound($ahSupTypes) - 1
        IVector_Append($pFilter, $ahSupTypes[$i])
        _WinRT_DeleteHString($ahSupTypes[$i])
    Next

    Local $pAsync = IFileOpenPicker_PickSingleFileAsync($pPicker)
    _WinRT_SwitchInterface($pAsync, _WinRT_GetParameterizedTypeInstanceIID("Windows.Foundation.IAsyncOperation`1<Microsoft.Windows.Storage.Pickers.PickFileResult>"))
    Local $pPickedFile = _WinRT_WaitForAsync2($pAsync, "ptr*", -1)

    Local $sFilePath = IPickFileResult_GetPath($pPickedFile)
    IUnknown_Release($pPickedFile)

    If FileExists($sFilePath) Then
        Local $pFileFact = _WinRT_GetActivationFactory("Windows.Storage.StorageFile", $sIID_IStorageFileStatics)
        $pAsync = IStorageFileStatics_GetFileFromPathAsync($pFileFact, $sFilePath)
        Local $pFile = _WinRT_WaitForAsync($pAsync, "ptr*", 3000)
        IUnknown_Release($pFileFact)

        _WinRT_SwitchInterface($pFile, $sIID_IStorageFile)

        ;Create MediaSource from file object.
        Local $pMediaSrcFact = _WinRT_GetActivationFactory("Windows.Media.Core.MediaSource", $sIID_IMediaSourceStatics)
        Local $pMediaSrc = IMediaSourceStatics_CreateFromStorageFile($pMediaSrcFact, $pFile)
        IUnknown_Release($pMediaSrcFact)

        _WinRT_SwitchInterface($pMediaSrc, $sIID_IMediaSource3)
        IMediaSource3_AddHdlrStateChanged($pMediaSrc, $pMediaSrcStateChDgt)
        _WinRT_SwitchInterface($pPlayer, $sIID_IMediaPlayerSource)
        IMediaPlayerSource_SetMediaSource($pPlayer, $pMediaSrc)

        IUnknown_Release($pMediaSrc)
        IUnknown_Release($pFile)
    EndIf

    IUnknown_Release($pFilter)
    IUnknown_Release($pPicker)
EndFunc   ;==>OpenMedia

Func PanelAddChild($pPanel, $pChild)
    _WinRT_SwitchInterface($pPanel, $sIID_IPanel)
    _WinRT_SwitchInterface($pChild, $sIID_IUIElement)
    Local $pChildren = IPanel_GetChildren($pPanel)
    IVector_Append($pChildren, $pChild)
    IUnknown_Release($pChildren)
EndFunc   ;==>PanelAddChild

Func Play($pThis, $pSender, $pArgs)
    #forceref $pThis, $pSender, $pArgs

    _WinRT_SwitchInterface($pPlayer, $sIID_IMediaPlayer)
    Switch $sPlayerState
        Case "Playing"
            IMediaPlayer_Pause($pPlayer)
        Case Else
            IMediaPlayer_Play($pPlayer)
    EndSwitch
EndFunc   ;==>Play

Func PlayerStateChange($pThis, $pPlayer, $pArgs)
    #forceref $pThis, $pPlayer, $pArgs
    $sPlayerState = _WinRT_GetEnum($mMediaPlayerState, IMediaPlayer_GetCurrentState($pPlayer))
    ConsoleWrite("Player State: " & $sPlayerState & @CRLF)

;~  Local $pPlayButton = GetElementByName("PlayBtn")
    Switch $sPlayerState
        Case "Playing"
            ButtonSetGlyph($pPlayButton, "Pause")
        Case Else
            ButtonSetGlyph($pPlayButton, "Play")

    EndSwitch

EndFunc   ;==>PlayerStateChange

Func TimerExp($pThis, $pSender, $pArgs)
    #forceref $pThis, $pSender, $pArgs

    If IMediaPlayerElement_GetIsFullWindow($pMediaPlayerElement) Then
        If Not $bAreCtrlsHidden Then
            HideControls(True)

            Local $pCursorFact = _WinRT_GetActivationFactory("Microsoft.UI.Input.InputCursor", $sIID_IInputCursorStaticsInterop)
            Local $hCursor = CreateHiddenCursor()
            Local $pInputCursor = IInputCursorStaticsInterop_CreateFromHCursor($pCursorFact, $hCursor)
            IUnknown_Release($pCursorFact)
            Local $pMediaPlayerElementCurs = $pMediaPlayerElement
            _WinRT_SwitchInterface($pMediaPlayerElementCurs, $sIID_IUIElementProtected)
            IUIElementProtected_SetProtectedCursor($pMediaPlayerElementCurs, $pInputCursor)
            IUnknown_Release($pInputCursor)
        EndIf
    EndIf
    IDispatcherTimer_Stop($pTimer)

EndFunc   ;==>TimerExp

Func UIElementAssignAccel($pObj, $sVirtualKey, $iModifiers = $mVirtualKeyModifiers["None"])
    Local $pAccel = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Input.KeyboardAccelerator")
    _WinRT_SwitchInterface($pAccel, $sIID_IKeyboardAccelerator)
    IKeyboardAccelerator_SetKey($pAccel, $mVirtualKey[$sVirtualKey])
    IKeyboardAccelerator_SetModifiers($pAccel, $iModifiers)
    IKeyboardAccelerator_SetIsEnabled($pAccel, True)

    _WinRT_SwitchInterface($pObj, $sIID_IUIElement)
    Local $pAccels = IUIElement_GetKeyboardAccelerators($pObj)
    IVector_Append($pAccels, $pAccel)
    IUnknown_Release($pAccels)
    IUnknown_Release($pAccel)
EndFunc   ;==>UIElementAssignAccel

Func UIElementAssignGridPos($pObj, $iRow, $iCol = -1, $iRowSpan = -1, $iColSpan = -1)
    Local $pProp_Fact = _WinRT_GetActivationFactory("Windows.Foundation.PropertyValue", $sIID_IPropertyValueStatics)
    Local $pGrid_Statics = _WinRT_GetActivationFactory("Microsoft.UI.Xaml.Controls.Grid", $sIID_IGridStatics)

    Local $pValue
    _WinRT_SwitchInterface($pObj, $sIID_IDependencyObject)
    If @error Then Return SetError(@error, @extended, False)

    If $iRow > -1 Then
        Local $pRowProp = IGridStatics_GetRowProperty($pGrid_Statics)
        $pValue = IPropertyValueStatics_CreateInt32($pProp_Fact, $iRow)
        IDependencyObject_SetValue($pObj, $pRowProp, $pValue)
        IUnknown_Release($pValue)
        IUnknown_Release($pRowProp)
    EndIf

    If $iCol > -1 Then
        Local $pColProp = IGridStatics_GetColumnProperty($pGrid_Statics)
        $pValue = IPropertyValueStatics_CreateInt32($pProp_Fact, $iCol)
        IDependencyObject_SetValue($pObj, $pColProp, $pValue)
        IUnknown_Release($pValue)
        IUnknown_Release($pColProp)
    EndIf

    If $iRowSpan > -1 Then
        Local $pRowSpanProp = IGridStatics_GetColumnSpanProperty($pGrid_Statics)
        $pValue = IPropertyValueStatics_CreateInt32($pProp_Fact, $iRowSpan)
        IDependencyObject_SetValue($pObj, $pRowSpanProp, $pValue)
        IUnknown_Release($pValue)
        IUnknown_Release($pRowSpanProp)
    EndIf

    If $iColSpan > -1 Then
        Local $pColSpanProp = IGridStatics_GetColumnSpanProperty($pGrid_Statics)
        $pValue = IPropertyValueStatics_CreateInt32($pProp_Fact, $iColSpan)
        IDependencyObject_SetValue($pObj, $pColSpanProp, $pValue)
        IUnknown_Release($pValue)
        IUnknown_Release($pColSpanProp)
    EndIf

    IUnknown_Release($pGrid_Statics)
    IUnknown_Release($pProp_Fact)

    Return True
EndFunc   ;==>UIElementAssignGridPos

Func IInputCursorStaticsInterop_CreateFromHCursor($pThis, $hCursor)
    Local $vFailVal = False
    Local $pFunc = __WinRT_GetFuncAddress($pThis, 7)
    If @error Then Return SetError(@error, @extended, $vFailVal)
    If $hCursor And (Not IsPtr($hCursor)) Then Return SetError($ERROR_INVALID_PARAMETER, 0, $vFailVal)
    Local $aCall = DllCallAddress("long", $pFunc, "ptr", $pThis, "handle", $hCursor, "ptr*", 0)
    If @error Then Return SetError(__WinRT_GetDllError(), 0, $vFailVal)
    Return SetError($aCall[0], 0, $aCall[3])
EndFunc   ;==>IInputCursorStaticsInterop_CreateFromHCursor

Edit: minor fix

Edited by MattyD
Posted
On 5/13/2026 at 7:29 PM, MattyD said:

I think we can animate the controls hiding too

 We can animate! to a degree.

Here we're binding the stackpanel's "Spacing" property to a double animation.  That's double as in a 64bit float- not a 2x animation!

;~ #AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#AutoIt3Wrapper_UseX64=Y
#Tidy_Parameters=/sf

#include "..\Include\WinRT.au3"
#include "..\Include\WinRT_WinUI3.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.Application.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.LaunchActivatedEventArgs.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Window.au3"
#include "..\Include\Classes\Microsoft.UI.Windowing.AppWindow.au3"
#include "..\Include\Classes\Microsoft.UI.Windowing.AppWindowPresenter.au3"

#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Stackpanel.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.FontIcon.au3"
#include "..\Include\Enumerations\Microsoft.UI.Xaml.Controls.Symbol.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Controls.Button.au3"

#include "..\Include\Classes\Windows.Foundation.PropertyValue.au3"
#include "..\Include\Interfaces\Windows.Foundation.Collections.IVector.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Media.Animation.Storyboard.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Media.Animation.DoubleAnimation.au3"
#include "..\Include\Classes\Microsoft.UI.Xaml.Media.Animation.ObjectAnimationUsingKeyFrames.au3"

Global Const $sIID_IReference_1_Double_ = "{2F2D6C29-5473-5F3E-92E7-96572BB990E2}"
$__g_mIIDs[$sIID_IReference_1_Double_] = "IReference_1_Double_"

Global $mDurationType[]
$mDurationType["Automatic"] = 0x00000000
$mDurationType["TimeSpan"] = 0x00000001
$mDurationType["Forever"] = 0x00000002
__WinRT_AddReverseMappings($mDurationType)

Global $pStoryboard, $pPlayButton
Global $aDelegates[10]

_WinRT_Startup()
If @error Then Exit MsgBox(0, "WinRT Startup", "Error Initialising: " & _WinAPI_GetErrorMessage(@error))
_WinUI3_Startup()
If @error Then
    MsgBox(0, "WinRT Startup", "Error Initialising: " & _WinAPI_GetErrorMessage(@error))
    _WinRT_Shutdown()
    Exit
EndIf
_WinUI3_StartApp("OnLaunched")
_WinUI3_Shutdown()
_WinRT_Shutdown()
Cleanup()

Func OnLaunched($pApplication, $pEventArgs)
    #forceref $pApplication, $pEventArgs

    ;Setup Window
    Local $pWindow = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Window")
    _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
    IWindow_SetTitle($pWindow, "Animation Test")

    _WinRT_SwitchInterface($pWindow, $sIID_IWindow2)
    Local $pAppWindow = IWindow2_GetAppWindow($pWindow)

    Local $tRectInt = DllStructCreate("int X;int Y;int Width; int Height")
    $tRectInt.Width = 300
    $tRectInt.Height = 100
    $tRectInt.X = (@DesktopWidth - $tRectInt.Width) / 2
    $tRectInt.Y = (@DesktopHeight - $tRectInt.Height) / 2

    _WinRT_SwitchInterface($pAppWindow, $sIID_IAppWindow)
    IAppWindow_MoveAndResize($pAppWindow, $tRectInt)
    IUnknown_Release($pAppWindow)

    ;Create Stackpanel for buttons.
    Local $pStackPanel = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.StackPanel")
    _WinRT_SwitchInterface($pStackPanel, $sIID_IStackPanel)
    IStackPanel_SetOrientation($pStackPanel, $mOrientation["Horizontal"])
    Local $tPadding = DllStructCreate("struct;double Left;double Top;double Right; double Bottom;endstruct")
    $tPadding.Left = 10
    $tPadding.Top = 10
    $tPadding.Right = 10
    $tPadding.Bottom = 10
    IStackPanel_SetPadding($pStackPanel, $tPadding)
    _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
    IWindow_SetContent($pWindow, $pStackPanel)

    ;Create play button
    $pPlayButton = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Button")
    ButtonSetGlyph($pPlayButton, "Play")
    PanelAddChild($pStackPanel, $pPlayButton)
    _WinRT_SwitchInterface($pPlayButton, $sIID_IButtonBase)
    IButtonBase_AddHdlrClick($pPlayButton, CreateDelegate("Play"))

;~  ;Create reset button
    Local $pResetButton = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Button")
    ButtonSetGlyph($pResetButton, "Previous")
    PanelAddChild($pStackPanel, $pResetButton)
    _WinRT_SwitchInterface($pResetButton, $sIID_IButtonBase)
    IButtonBase_AddHdlrClick($pResetButton, CreateDelegate("Reset"))
    IUnknown_Release($pResetButton)

;~  ;Create reset button
    Local $pEndButton = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.Button")
    ButtonSetGlyph($pEndButton, "Next")
    PanelAddChild($pStackPanel, $pEndButton)
    _WinRT_SwitchInterface($pEndButton, $sIID_IButtonBase)
    IButtonBase_AddHdlrClick($pEndButton, CreateDelegate("GotoEnd"))
    IUnknown_Release($pEndButton)

    ;Animation
    Local $pSpacingAni = CreateDoubleAnimation(0, 50, 1000)

    $pStoryboard = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Media.Animation.Storyboard")
    StoryboardAddAnimation($pStoryboard, $pSpacingAni)
    _WinRT_SwitchInterface($pStackPanel, $sIID_IDependencyObject)
    _WinRT_SwitchInterface($pStoryboard, $sIID_ITimeline)
    ITimeline_AddHdlrCompleted($pStoryboard, CreateDelegate("AniComplete"))

    Local $pSboardStatics = _WinRT_GetActivationFactory("Microsoft.UI.Xaml.Media.Animation.Storyboard", $sIID_IStoryboardStatics)
    IStoryboardStatics_SetTargetProperty($pSboardStatics, $pSpacingAni, "Spacing")
    IStoryboardStatics_SetTarget($pSboardStatics, $pSpacingAni, $pStackPanel)
    IUnknown_Release($pSboardStatics)

    IUnknown_Release($pSpacingAni)
    IUnknown_Release($pStackPanel)

    ;Activate window
    _WinRT_SwitchInterface($pWindow, $sIID_IWindow)
    IWindow_Activate($pWindow)
    IUnknown_Release($pWindow)

EndFunc   ;==>OnLaunched

Func Cleanup()
    IUnknown_Release($pStoryboard)
    IUnknown_Release($pPlayButton)

    For $i = 0 To UBound($aDelegates) - 1
        If $aDelegates[$i] Then _WinRT_DestroyDelegate($aDelegates[$i])
    Next
EndFunc

Func CreateDoubleAnimation($iFrom, $iTo, $iTime = Default)
    Local $pAnimation = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Media.Animation.DoubleAnimation")
    _WinRT_SwitchInterface($pAnimation, $sIID_IDoubleAnimation)
    IDoubleAnimation_SetEnableDependentAnimation($pAnimation, True)

    Local $pProp_Fact = _WinRT_GetActivationFactory("Windows.Foundation.PropertyValue", $sIID_IPropertyValueStatics)
    Local $pAniFrom = IPropertyValueStatics_CreateDouble($pProp_Fact, $iFrom)
    Local $pAniTo = IPropertyValueStatics_CreateDouble($pProp_Fact, $iTo)
    _WinRT_SwitchInterface($pAniFrom, $sIID_IReference_1_Double_)
    _WinRT_SwitchInterface($pAniTo, $sIID_IReference_1_Double_)
    IDoubleAnimation_SetFrom($pAnimation, $pAniFrom)
    IDoubleAnimation_SetTo($pAnimation, $pAniTo)
    IUnknown_Release($pAniFrom)
    IUnknown_Release($pAniTo)
    IUnknown_Release($pProp_Fact)

    Local $tTimespan = DllStructCreate("align 1;int64 duration;int type")
    If $iTime = Default Or  $iTime < 0  Then
        $tTimespan.type = $mDurationType["Automatic"]
    Else
        $tTimespan.duration = $iTime * 10000
        $tTimespan.type = $mDurationType["TimeSpan"]
    EndIf
    _WinRT_SwitchInterface($pAnimation, $sIID_ITimeline)
    ITimeline_SetDuration($pAnimation, $tTimespan)

    Return $pAnimation
EndFunc

Func StoryboardAddAnimation($pStoryboard, $pTimeline)
    _WinRT_SwitchInterface($pStoryboard, $sIID_IStoryboard)
    _WinRT_SwitchInterface($pTimeline, $sIID_ITimeline)
    Local $pChildren = IStoryboard_GetChildren($pStoryboard)
    IVector_Append($pChildren, $pTimeline)
    IUnknown_Release($pChildren)
EndFunc

Func ButtonSetGlyph($pButton, $sSymbol)
    Local $pButtonIcon = _WinRT_ActivateInstance("Microsoft.UI.Xaml.Controls.FontIcon")
    _WinRT_SwitchInterface($pButtonIcon, $sIID_IFontIcon)
    IFontIcon_SetGlyph($pButtonIcon, ChrW($mSymbol[$sSymbol]))

    _WinRT_SwitchInterface($pButton, $sIID_IContentControl)
    IContentControl_SetContent($pButton, $pButtonIcon)
    IUnknown_Release($pButtonIcon)
EndFunc   ;==>ButtonSetGlyph

Func PanelAddChild($pPanel, $pChild)
    _WinRT_SwitchInterface($pPanel, $sIID_IPanel)
    _WinRT_SwitchInterface($pChild, $sIID_IUIElement)
    Local $pChildren = IPanel_GetChildren($pPanel)
    IVector_Append($pChildren, $pChild)
    IUnknown_Release($pChildren)
EndFunc   ;==>PanelAddChild

Func CreateDelegate($sName, $sType = "")
    Local Static $iCount
    If $iCount = UBound($aDelegates) Then ReDim $aDelegates[UBound($aDelegates) + 10]
    $aDelegates[$iCount] = _WinRT_CreateDelegate($sName, $sType)
    $iCount += 1

    Return $aDelegates[$iCount - 1]
EndFunc   ;==>CreateDelegate

Func Play($pThis, $pSender, $pArgs)
    _WinRT_SwitchInterface($pStoryboard, $sIID_IStoryboard)
    Local Static $sLastAction

    Switch _WinRT_GetEnum($mClockState, IStoryboard_GetCurrentState($pStoryboard))
        Case "Stopped"
            IStoryboard_Begin($pStoryboard)
            ButtonSetGlyph($pSender, "Pause")
            $sLastAction = "Play"

        Case "Filling"
            IStoryboard_Stop($pStoryboard)
            ButtonSetGlyph($pSender, "Play")
            $sLastAction = "Stop"

        Case "Active"
            If $sLastAction = "Play" Then
                IStoryboard_Pause($pStoryboard)
                ButtonSetGlyph($pSender, "Play")
                $sLastAction = "Pause"
            Else
                IStoryboard_Resume($pStoryboard)
                ButtonSetGlyph($pSender, "Pause")
                $sLastAction = "Play"
            EndIf

    EndSwitch
EndFunc

Func Reset($pThis, $pSender, $pArgs)
    _WinRT_SwitchInterface($pStoryboard, $sIID_IStoryboard)
    IStoryboard_Stop($pStoryboard)
    ButtonSetGlyph($pPlayButton, "Play")
EndFunc

Func GotoEnd($pThis, $pSender, $pArgs)
    _WinRT_SwitchInterface($pStoryboard, $sIID_IStoryboard)
    IStoryboard_Begin($pStoryboard)
    IStoryboard_SkipToFill($pStoryboard)
EndFunc

Func AniComplete($pThis, $pSender, $pArgs)
    _WinRT_SwitchInterface($pSender, $sIID_IStoryboard)
    ButtonSetGlyph($pPlayButton, "Stop")
EndFunc

But what if the property we want to bind to isn't a float?

Well we can create a different type of animation like ObjectAnimationUsingKeyFrames, which in theory lets us attach to other types.  Our values do need to be "boxed" though. They need to be wrapped up as an IReference object - like so:

  • Windows.Foundation.IReference`1<Double>
  • Windows.Foundation.IReference`1<Int32>
  • Windows.Foundation.IReference`1<Windows.Foundation.Point>
  • etc...

All these common types can be instantiated through a Windows.Foundation.PropertyValue statics object.  And presumably it should cover a good chunk of what we'll be realistically be dealing with. 

But say we wanted to animate something different, for eg.  Microsoft.UI.Xaml.GridLength.  This one is a "double;int32" struct, and we can't create that via a PropertyValue object. We need a "Windows.Foundation.IReference`1<Microsoft.UI.Xaml.GridLength>".  In C++ its simple enough, but I've no idea how to replicate/get at it...   

winrt::box_value(Value)

I might see if we just create our own implementation of the IReference interface for now. (i.e. build our own IReference object).  We can generate IIDs though _WinRT_GetParameterizedTypeInstanceIID provided we're using genuine WinRT classnames.  What can possibly go wrong!

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...