Jump to content

Recommended Posts

Posted
20 minutes ago, MattyD said:

And resizing the window actually works remarkably well. I guess I was expecting some flicker or something- so that was a pleasant surprise! 

image.png.88d613ccca08a111ae112f1407faddba.png

 

That's quite beautiful. You are making excellent progress so far. I haven't tested this in the last few days but I'm definitely more inspired now to try some things with it later today.

  • How is the performance of the UI loading?

The reason why I ask is because a lot of Microsoft's own examples with WinUI3 are quite sluggish. Yet, I have also seen many examples from other developers which are fast and snappy.

  • How do you handle applying DPI scaling?

I'm just not sure if the WinUI3 / WinRT stuff automatically applies DPI scaling by default or if we would specify the DPI scaling as we normally would in AutoIt.

Posted

Thanks mate, we'll have to credit whoever came up with the design at Microsoft though!

18 minutes ago, WildByDesign said:

How is the performance of the UI loading?

This is honestly fine, but we're not exactly asking much of the runtime either at this point in time!   Au3check can grind a bit when you really start pushing the #include file count. But for what I'm doing at the moment its OK. 

25 minutes ago, WildByDesign said:

How do you handle applying DPI scaling?

I haven't looked at this at all sorry!

Posted (edited)

This is the same example - but I'm playing around with adding another layer of functions on top of the interface libraries.   It should make building GUIs quite a bit simpler, and introduce some semblance of resource management.

At this stage its just a rough proof of concept, so I'm not worried about naming conventions etc, or any mistakes in these functions. They'll all probably be rewritten anyway!

So anyway, as an example: I'm now doing this to add rows and columns to the grid.

;Params - $pGrid, $fValue, $vUnit ("Auto", "Pixel" or "Star")
;$vUnit can also be the enum in its numeric form. (Microsoft.UI.Xaml.GridUnitType)

GridAddRow($pGrid, 2, "Star") 
GridAddRow($pGrid, 1, "Star")
GridAddColumn($pGrid, 3, "Star")
GridAddColumn($pGrid, 5, "Star")

And the underlying func.

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($pColDef)
    IUnknown_Release($pColDefs)
    Return SetError($iError, 0, $iError = $S_OK)
EndFunc   ;==>GridAddColumn

Most funcs that accept an object will start with a  SwitchInterface() - this serves 2 purposes:

  • in your main script you don't need to bother about setting the correct interface on the object.
  • and also if you feed the func an invalid pointer (to the wrong object for eg.), it should gracefully fail. 

Factories and other supporting objects used in these funcs are all released before returning.  This is probably not be the most efficient thing to do if you're creating a bunch of instances of something - but it does mean you're cleaning up after yourself as you go.

I'm getting way ahead of myself - but this new layer wouldn't be a comprehensive wrapper. It'd probably just some basic/common functionality to improve quality of life. I figure people can always jump down to the interface libraries if need be.   Whether this will form part of an "official" release I don't know (if I ever get my act together!) - but I think building a full project without anything would just be downright tedious... And hey, if anyone else wants write/drive some of it I won't be complaining!!!

WindowTest Grid.zip

Edited by MattyD
Posted (edited)
...
#AutoIt3Wrapper_UseX64=Y
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
If Not @AutoItX64 And Not @Compiled Then
    ShellExecute(StringTrimRight(@AutoItExe, 4) & "_x64.exe", '"' & @ScriptFullPath & '"')
    Exit
EndIf
#include <GUIConstants.au3>
...

It asked me to GetRuntime() but I installed it yesterday so, the above is a good solution for those not in SciTE, just click-clicking the file from explorer :)  

Edited by argumentum

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

  • 2 months later...
Posted

@MattyD From your WindowTest Grid example from September 3rd, you have three sections of the weather GUI with background colored with the following:

Local $aiColours[3] = [0xFF2F5CB6, 0xFF1F3D7A, 0xFF152951]
    Local $apBorders[3], $pBrush
    For $i = 0 To 2
        $pBrush = CreateSolidBrush($aiColours[$i])
        $apBorders[$i] = CreateBorder()
        IBorder_SetBackground($apBorders[$i], $pBrush)
        IUnknown_Release($pBrush)
        PanelAddChild($pGrid, $apBorders[$i])
    Next

So what I am curious about is how would we go about changing some of those background colors after the GUI is already showing?

I'm trying to understand the WinUI 3 stuff now. I'm not even sure that each part of the GUI even has a window handle. The whole WinRT/WinUI 3 stuff kind of boggles my mind as to how to change this stuff after the fact.

Posted (edited)

sure thing. - yeah, there are no window handles per se - apart from the WinID of the main window.  Everything is done on objects. 

so that colour change would be something like:

$pBrush = CreateSolidBrush("Red")
IBorder_SetBackground($apBorders[0], $pBrush)
IUnknown_Release($pBrush)

In the Sept 3rd. example, to keep things tidy I release the border objects once they're created. So you'd need either need to change the background before releasing, or get the border object back by some other means. Via its parent for example - in this case the grid object (before it too is released!).

As for triggering the color change - In this example,  once we get into the main loop everything is event based. When we hit the following line the script won't continue until the dispatch loop is broken with _WinUI3_PostQuitMessage. 

IDispatcherQueue3_RunEventLoop($pDispatchQ)

The general idea is to create a bunch of handlers with _WinRT_CreateDelegate(). But if you'd rather work within a while loop, it is possible to create your own dispatcher with _WinUI3_GetMsg / _WinUI3_DispatchMessage.  I think there are probably examples of this early in the thread!

Edited by MattyD
typo
Posted

Hi all,

Updated libraries based on the new Version 1.8 of WindowsAppSDK are here. Until now we've been on 1.7.x

The v1.8 runtime can be installed from here: (Remembering that we require the x64 version.)

A copy of the latest bootstrapper dll is included in the zip - but  if you need to manually update it later for whatever reason, it has moved.

So you'll now find it here: https://www.nuget.org/packages/Microsoft.WindowsAppSdk.Foundation/
download package (on the right) >  extract contents > then you'll find it under .\runtimes\win-x64\native

  • 1 month later...
Posted (edited)

Just having a bit more of a play with this.

I've found we can avoid the creating that custom window proc... this is good, as it seems breaking the dispatcher loop is done in part by posting a WM_QUIT message. I hadn't noticed it at first, but I think AutoIt was seeing this and exiting automatically once it found that message.  So now our script goes through all the way to the end.

Also the demo shows how we can constrain the max/min window size, and automatically grow and shrink the xaml content with the window.. (we were previously doing the latter via that window proc)

Example

Edited by MattyD
Posted (edited)

ah ok thanks, I've updated _WinUI3_Startup() to target v1.8 of the WindowsApp SDK.. I think its not throwing a soft error f there's a version mismatch with the runtime... I'll see if I can fix that for next time...

Can you try doing a _WinUI3_Startup(1, 7) to target v1.7 and see if that works?  (assuming that you still have it installed) . Otherwise v1.8 can be installed from here...

1 hour ago, argumentum said:

 #AutoIt3Wrapper_UseX64=y

And yep I forgot this at the top, it should definitely be there  too.

Edited by MattyD
Posted
...
Func _WinRT_Startup()
    If Not @AutoItX64 Then Return SetError(1, 0, 1)
...
Func _WinUI3_Startup($iMajorVers = Default, $iMinorVers = Default, $sVersionTag = Default, $iMinVers = Default, $iOptions = Default)
    If Not @AutoItX64 Then Return SetError(1, 0, False)
...

#include "WinUIBase.au3"
If Not @AutoItX64 Then Exit ConsoleWrite(@CRLF & '@@(' & @ScriptLineNumber & ') : Needs to run x64.' & @CRLF)
_WinRT_Startup()
If @error Then Exit ConsoleWrite(@CRLF & '@@(' & @ScriptLineNumber & ') : Error=' & @error & @CRLF)
_WinUI3_Startup()
If @error Then Exit ConsoleWrite(@CRLF & '@@(' & @ScriptLineNumber & ') : Error=' & @error & @CRLF)
...

..something to let the user know that is not running x64. And since the default install runs x86 for compatibility, when click-click from Explorer, the "#AutoIt3Wrapper_UseX64=y" is not going to do it's thing.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

  • 2 weeks later...
Posted

I'm currently looking into the Microsoft.UI.Xaml.Application class - and its giving me plethora of extra things to try and understand and implement...

Currently, my focus is on IApplicationOverrides::OnLaunched. In the example WinUI3 apps online, there's generally a user defined "OnLaunched" function where you do a bunch of initialisation. Ideally we should be replicating this behavior. So how do we achieve that? 

We are used to dealing with delegates, which would ordinarily fire when an object is ready for us. But this uses a totally different mechanism - method overrides. We need to create our own object that implements a custom IApplicationOverrides::OnLaunched method, and get the system to call this instead of the normal routine. From what I've read, this black magic is possible because of object composition.

So revisiting composable objects, the factories look like this:

Fn : value = CreateInstance(In baseInterface, Out innerInterface)
                  P0 : value                type: Windows.UI.Xaml.Application 
                  P1 : baseInterface        type: Object 
                  P2 : innerInterface       type: Object*

This is essentially a method to combine two objects together -

  • so we provide the "base" object with our customised "IApplicationOverrides" interface, 
  • the factory creates the "inner" application object
  • and the "combined" object, (the one we use) is what's returned.

IApplicationOverrides is derived from IInspectable, so our base object's v-table will need to contain:

  1. IUnknown::QueryInterface
  2. IUnknown::AddRef
  3. IUnknown::Release
  4. IInspectable::GetIIDs
  5. IInspectable::GetRuntimeClassName
  6. IInspectable::GetTrustLevel
  7.  IApplicationOverrides::OnLaunched

We already have a home-brewed IUnknown implementation for our delegates, so I guess I'll knock up something for the rest and see how we fare. 

I'm a little wary because our custom IUnknown/IInspectable routines will only work properly for our home-grown objects. We store internal data like the refcount in a struct underneath the v-table ptr - but I have no idea how a proper COM object handles this. 

Let's say our implementation of "QueryInterface" is called with the Inner object's ptr for some reason. If the requested IID is supported, then we should AddRef() to increment the reference count... But we crash instead because the refcount is not physically where we expect it to be...

Anyway I'll leave it there for now...

Posted

OK, I think its kindof working.  The composed obj comes back on our makeshift interface.

But there's a problem. While we're on IApplicationOverrides,  the IUnkown/IInspectable calls invoke our custom functions. But our creation doesn't know about the Inner object, so we get trapped on IApplicationOverrides. 

Basically, (I think) calling QI with  $sIID_IApplicationOverrides on the composed object correctly puts it on our makeshift interface - but once we're there, our custom QI func can't take us back to, say,  $sIID_IApplication. Sooo - we need to somehow forward any IUnknown/IInspectable calls we receive back to the inner object.. OK, so whats the best way to do this?  Well, in our IUnknown/IInspectable implementations we essentially need to do something like:

Func __Overrides_AddRef($pThis)
    If $pThis = ($pSomeOuterOj) Then $pThis = ($pAssociatedInnerObj)
    Return IUnknown_AddRef($pThis)
EndFunc

Ideally I'd like to store the $pInner address internally in our object - but I'll need to think about the best way to implement that.  I think I'll just create a global map between the pInner an pOuter objects for now. Its probably not the best approach long term, but it'll do for testing purposes.

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...