Skip to content

Commit 94c8863

Browse files
committed
[Stride] Winforms based window (= Stride default) with always on top and extend into title bar features. Merge once stride3d/stride#1664 is merged.
1 parent 1c24f80 commit 94c8863

File tree

14 files changed

+992
-576
lines changed

14 files changed

+992
-576
lines changed

VL.Core.Skia.Windows/Win32CustomTitleBar.cs

Lines changed: 520 additions & 0 deletions
Large diffs are not rendered by default.

VL.Skia/src/SkiaRenderer.Win32.cs

Lines changed: 24 additions & 459 deletions
Large diffs are not rendered by default.

VL.Skia/src/SkiaRenderer.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using VL.Lib.IO;
2020
using VL.Lib.IO.Notifications;
2121
using VL.UI.Core;
22+
using Win32CustomTitleBar = sw::VL.Core.Windows.Win32CustomTitleBar;
2223

2324
namespace VL.Skia
2425
{
@@ -41,9 +42,9 @@ public partial class SkiaRenderer : Form, IDisposable, IProjectionSpace, IWorldS
4142
ILayer? Layer;
4243
bool FFirstRenderCall = true;
4344

44-
public SkiaRenderer()
45+
public SkiaRenderer(NodeContext nodeContext, Win32CustomTitleBar.Options options)
4546
{
46-
FAppHost = AppHost.Current;
47+
FAppHost = nodeContext.AppHost;
4748

4849
Icon = Properties.Resources.QuadIcon;
4950
StartPosition = FormStartPosition.Manual;
@@ -54,6 +55,8 @@ public SkiaRenderer()
5455
ResizeRedraw = true;
5556
DoubleBuffered = false;
5657

58+
customTitleBar = Win32CustomTitleBar.Install(this, nodeContext, options with { IsFullscreen = () => FFullScreen });
59+
5760
BoundsChanged = new BehaviorSubject<System.Drawing.Rectangle>(new System.Drawing.Rectangle());
5861
FBoundsStream = new BehaviorSubject<RectangleF>(new RectangleF());
5962

@@ -205,8 +208,6 @@ protected override void OnHandleCreated(EventArgs e)
205208
FDarkModeSubscription.Disposable = DarkTitleBarClass.Install(Handle);
206209
base.OnHandleCreated(e);
207210
renderer = new EglSkiaRenderer(RenderContextProvider);
208-
209-
InsertCustomMenu();
210211
}
211212

212213
protected override void OnHandleDestroyed(EventArgs e)
@@ -221,7 +222,6 @@ protected override void OnResize(EventArgs e)
221222
{
222223
base.OnResize(e);
223224
OnBoundsChanged();
224-
UpdateTitleBarButtonRects();
225225
}
226226

227227
protected override void OnLocationChanged(EventArgs e)
@@ -294,6 +294,10 @@ public void MapFromPixels(INotificationWithPosition notification, out Vector2 in
294294

295295
public class SkiaRendererTopMost : SkiaRenderer
296296
{
297+
public SkiaRendererTopMost(NodeContext nodeContext) : base(nodeContext, new (AlwaysOnTop: true, ExtendIntoTitleBar: false))
298+
{
299+
}
300+
297301
protected override bool ShowWithoutActivation => true;
298302
protected override CreateParams CreateParams
299303
{

VL.Skia/src/SkiaRendererNode.cs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#nullable enable
2+
3+
extern alias sw;
4+
25
using System;
36
using System.ComponentModel;
47
using System.Drawing;
@@ -13,11 +16,12 @@
1316
using VL.Lib.Reactive;
1417
using VL.Skia;
1518
using Keys = VL.Lib.IO.Keys;
19+
using sw::VL.Core.Windows;
1620

1721
namespace Graphics.Skia;
1822

1923
/// <summary>
20-
/// Internal renderer node. Only visible to VL.Skia. On patch side only ILayer related things (Space, Clear, PerfMeter) is built on top.
24+
/// Internal renderer node. Only visible to VL.Skia. On patch side only ILayer related things (Space, Clear, PerfMeter) are built on top.
2125
/// </summary>
2226
[ProcessNode]
2327
[Smell(SymbolSmell.Internal)]
@@ -43,12 +47,10 @@ public SkiaRendererNode(NodeContext nodeContext,
4347
bool alwaysOnTop,
4448
bool extendIntoTitleBar)
4549
{
46-
_renderer = new SkiaRenderer()
50+
_renderer = new SkiaRenderer(nodeContext, new (alwaysOnTop, extendIntoTitleBar))
4751
{
4852
Text = "Skia",
49-
FormBorderStyle = FormBorderStyle.Sizable,
50-
TopMost = alwaysOnTop,
51-
ExtendIntoTitleBar = extendIntoTitleBar,
53+
FormBorderStyle = FormBorderStyle.Sizable
5254
};
5355

5456
if (!bounds.IsEmpty)
@@ -72,18 +74,6 @@ public SkiaRendererNode(NodeContext nodeContext,
7274
});
7375
_disposables.Add(writeBounds);
7476
}
75-
76-
_renderer.MenuToggled += (s, e) =>
77-
{
78-
if (e == SkiaRenderer.ID_TOGGLE_TOPMOST)
79-
session.CurrentSolution
80-
.SetPinValue(nodeContext.Stack, "Always On Top", _renderer.TopMost)
81-
.Confirm(VL.Model.SolutionUpdateKind.DontCompile);
82-
if (e == SkiaRenderer.ID_TOGGLE_EXTEND_INTO_TITLEBAR)
83-
session.CurrentSolution
84-
.SetPinValue(nodeContext.Stack, "Extend Into Title Bar", _renderer.ExtendIntoTitleBar)
85-
.Confirm(VL.Model.SolutionUpdateKind.DontCompile);
86-
};
8777
}
8878

8979
_renderer.FormClosing += (s, e) =>

VL.Stride.Runtime/VL.Stride.Engine.vl

Lines changed: 67 additions & 24 deletions
Large diffs are not rendered by default.

VL.Stride.Runtime/src/Games/GameWindowRenderer.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// Modified version of Stride.Games.GameWindowRenderer using GameWindowRendererManager.
22
// This class should be kept internal
33

4-
using System;
5-
using System.Reactive.Disposables;
64
using Stride.Core;
75
using Stride.Core.Mathematics;
86
using Stride.Games;
97
using Stride.Graphics;
8+
using Stride.Rendering;
9+
using System;
10+
using System.Reactive.Disposables;
1011
using VL.Core.Utils;
1112

1213
namespace VL.Stride.Games
@@ -137,6 +138,7 @@ protected virtual void EnsurePresenter()
137138

138139
WindowManager.Initialize(this, GraphicsDevice, Services.GetService<IGraphicsDeviceFactory>());
139140
}
141+
WindowManager.EnsureBackBufferHasCorrectSize();
140142
}
141143

142144
public override bool BeginDraw()
@@ -170,6 +172,17 @@ public override void EndDraw()
170172
{
171173
if (beginDrawOk)
172174
{
175+
if (Window.NativeWindow.NativeWindow is IHasCustomTitleBar customTitleBar)
176+
{
177+
var renderContext = RenderContext.GetShared(Services);
178+
var renderDrawContext = renderContext.GetThreadContext();
179+
using (renderDrawContext.PushRenderTargetsAndRestore())
180+
{
181+
renderDrawContext.CommandList.SetRenderTargetAndViewport(null, Presenter.BackBuffer);
182+
customTitleBar.DrawTitleBarButtons(renderDrawContext);
183+
}
184+
}
185+
173186
// We'd like to call Present() here like in the original code, however that would be too early
174187
// in case other game systems want to draw into our backbuffer (like GameProfilingSystem).
175188
// Present();

VL.Stride.Runtime/src/Games/GameWindowRendererManager.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,9 +770,15 @@ protected virtual void OnPreparingDeviceSettings(object sender, PreparingDeviceS
770770
}
771771

772772
private void Window_ClientSizeChanged(object sender, EventArgs e)
773+
{
774+
EnsureBackBufferHasCorrectSize();
775+
}
776+
777+
public void EnsureBackBufferHasCorrectSize()
773778
{
774779
var clientSize = window.ClientBounds.Size;
775-
if (!isChangingDevice && !window.IsFullscreen && ((clientSize.Height != 0) || (clientSize.Width != 0)))
780+
var backBufferDescription = windowRenderer.Presenter.Description;
781+
if (!isChangingDevice && !window.IsFullscreen && ((clientSize.Height != 0) || (clientSize.Width != 0)) && (clientSize.Width != backBufferDescription.BackBufferWidth || clientSize.Height != backBufferDescription.BackBufferHeight))
776782
{
777783
resizedBackBufferWidth = clientSize.Width;
778784
resizedBackBufferHeight = clientSize.Height;
@@ -804,6 +810,8 @@ private void Window_OrientationChanged(object sender, EventArgs e)
804810
}
805811
}
806812

813+
Int2 windowedPostion;
814+
807815
private void Window_FullscreenChanged(object sender, EventArgs eventArgs)
808816
{
809817
if (sender is GameWindow window)
@@ -834,11 +842,16 @@ private void Window_FullscreenChanged(object sender, EventArgs eventArgs)
834842
resizedBackBufferHeight = output.CurrentDisplayMode.Height;
835843
else
836844
resizedBackBufferHeight = ApplyDesktopBoundsFix(output.DesktopBounds).Height;
845+
846+
var position = output.DesktopBounds.Location;
847+
windowedPostion = window.Position;
848+
window.Position = new Int2(position.X, position.Y);
837849
}
838850
else
839851
{
840852
resizedBackBufferWidth = window.PreferredWindowedSize.X;
841853
resizedBackBufferHeight = window.PreferredWindowedSize.Y;
854+
window.Position = windowedPostion;
842855
}
843856

844857
deviceSettingsChanged = true;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Stride.Graphics;
2+
using Stride.Rendering;
3+
4+
namespace VL.Stride.Games;
5+
6+
internal interface IHasCustomTitleBar
7+
{
8+
void DrawTitleBarButtons(RenderDrawContext renderDrawContext);
9+
}

VL.Stride.Runtime/src/Games/VLGame.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ namespace VL.Stride.Games
1919
{
2020
public class VLGame : Game, IGraphicsDeviceProvider
2121
{
22+
internal record struct GameContextParams(NodeContext NodeContext, bool AlwaysOnTop, bool ExtendIntoTitleBar, AppContextType AppContextType, int RequestedWidth, int RequestedHeight, bool IsUserManagingRun);
23+
internal static Func<GameContextParams, GameContext> VLGameContextFactory;
24+
25+
public static GameContext CreateGameContext(NodeContext nodeContext, bool alwaysOnTop, bool extendIntoTitleBar, AppContextType appContextType, int requestedWidth = 0, int requestedHeight = 0, bool isUserManagingRun = false)
26+
{
27+
if (VLGameContextFactory is null)
28+
throw new InvalidOperationException("VL Stride init routines didn't run yet.");
29+
30+
return VLGameContextFactory(new(nodeContext, alwaysOnTop, extendIntoTitleBar, appContextType, requestedWidth, requestedHeight, isUserManagingRun));
31+
}
32+
2233
private readonly TimeSpan maximumElapsedTime = TimeSpan.FromMilliseconds(2000.0);
2334
private TimeSpan accumulatedElapsedGameTime;
2435
private bool forceElapsedTimeToZero;

0 commit comments

Comments
 (0)