A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Drawing;
6using osu.Framework.Allocation;
7using osu.Framework.Bindables;
8using osu.Framework.Configuration;
9using osu.Framework.Graphics;
10using osu.Framework.Graphics.Containers;
11using osu.Framework.Graphics.Primitives;
12using osu.Framework.Graphics.Shapes;
13using osu.Framework.Graphics.Sprites;
14using osu.Framework.Platform;
15using osuTK;
16using osuTK.Graphics;
17
18namespace osu.Framework.Tests.Visual.Platform
19{
20 public class TestSceneBorderless : FrameworkTestScene
21 {
22 private readonly SpriteText currentActualSize = new SpriteText();
23 private readonly SpriteText currentClientSize = new SpriteText();
24 private readonly SpriteText currentWindowMode = new SpriteText();
25 private readonly SpriteText currentDisplay = new SpriteText();
26
27 private readonly TextFlowContainer windowCaption;
28 private readonly Container paddedContainer;
29 private readonly Container screenContainer;
30 private readonly Container windowContainer;
31 private Vector2 screenContainerOffset;
32
33 private static readonly Color4 active_fill = new Color4(255, 138, 104, 255);
34 private static readonly Color4 active_stroke = new Color4(244, 74, 25, 255);
35 private static readonly Color4 screen_fill = new Color4(255, 181, 104, 255);
36 private static readonly Color4 screen_stroke = new Color4(244, 137, 25, 255);
37 private static readonly Color4 window_fill = new Color4(95, 113, 197, 255);
38 private static readonly Color4 window_stroke = new Color4(36, 59, 166, 255);
39
40 private SDL2DesktopWindow window;
41 private readonly Bindable<WindowMode> windowMode = new Bindable<WindowMode>();
42
43 public TestSceneBorderless()
44 {
45 Child = new Container
46 {
47 RelativeSizeAxes = Axes.Both,
48 Children = new Drawable[]
49 {
50 new Container
51 {
52 RelativeSizeAxes = Axes.Both,
53 Padding = new MarginPadding(100),
54 Child = paddedContainer = new Container
55 {
56 RelativeSizeAxes = Axes.Both,
57 Child = screenContainer = new Container
58 {
59 Anchor = Anchor.Centre,
60 Origin = Anchor.Centre,
61 Child = windowContainer = new Container
62 {
63 BorderColour = window_stroke,
64 BorderThickness = 20,
65 Masking = true,
66
67 Children = new Drawable[]
68 {
69 new Box
70 {
71 RelativeSizeAxes = Axes.Both,
72 Colour = window_fill
73 },
74 windowCaption = new TextFlowContainer(sprite =>
75 {
76 sprite.Font = sprite.Font.With(size: 150);
77 sprite.Colour = Color4.White;
78 })
79 {
80 RelativeSizeAxes = Axes.Both,
81 Padding = new MarginPadding(50),
82 Colour = Color4.White
83 }
84 }
85 }
86 }
87 }
88 },
89 new FillFlowContainer
90 {
91 Padding = new MarginPadding(10),
92 Children = new[]
93 {
94 currentActualSize,
95 currentClientSize,
96 currentWindowMode,
97 currentDisplay
98 },
99 }
100 }
101 };
102
103 windowMode.ValueChanged += newMode => currentWindowMode.Text = $"Window Mode: {newMode}";
104 windowMode.TriggerChange();
105 }
106
107 private Container createScreen(Display display, string name) =>
108 new Container
109 {
110 X = display.Bounds.X,
111 Y = display.Bounds.Y,
112 Width = display.Bounds.Width,
113 Height = display.Bounds.Height,
114
115 BorderColour = display.Index == 0 ? active_stroke : screen_stroke,
116 BorderThickness = 20,
117 Masking = true,
118
119 Children = new Drawable[]
120 {
121 new Box
122 {
123 RelativeSizeAxes = Axes.Both,
124 Colour = display.Index == 0 ? active_fill : screen_fill
125 },
126 new SpriteText
127 {
128 Padding = new MarginPadding(50),
129 Text = name,
130 Font = new FontUsage(size: 200),
131 Colour = Color4.Black
132 }
133 }
134 };
135
136 [BackgroundDependencyLoader]
137 private void load(FrameworkConfigManager config, GameHost host)
138 {
139 window = host.Window as SDL2DesktopWindow;
140 config.BindWith(FrameworkSetting.WindowMode, windowMode);
141
142 if (window == null)
143 {
144 Console.WriteLine("No suitable window found");
145 return;
146 }
147
148 refreshScreens();
149
150 AddStep("set up screens", refreshScreens);
151
152 const string desc2 = "Check whether the window size is one pixel wider than the screen in each direction";
153
154 Point originalWindowPosition = Point.Empty;
155
156 foreach (var display in window.Displays)
157 {
158 AddLabel($"Steps for display {display.Index}");
159
160 // set up window
161 AddStep("switch to windowed", () => windowMode.Value = WindowMode.Windowed);
162 AddStep("set client size to 1280x720", () => config.SetValue(FrameworkSetting.WindowedSize, new Size(1280, 720)));
163 AddStep("store window position", () => originalWindowPosition = window.Position);
164
165 // borderless alignment tests
166 AddStep("switch to borderless", () => windowMode.Value = WindowMode.Borderless);
167 AddAssert("check window position", () => new Point(window.Position.X + 1, window.Position.Y + 1) == display.Bounds.Location);
168 AddAssert("check window size", () => new Size(window.Size.Width - 1, window.Size.Height - 1) == display.Bounds.Size, desc2);
169 AddAssert("check current screen", () => window.CurrentDisplayBindable.Value.Index == display.Index);
170
171 // verify the window size is restored correctly
172 AddStep("switch to windowed", () => windowMode.Value = WindowMode.Windowed);
173 AddAssert("check client size", () => window.ClientSize == new Size(1280, 720));
174 AddAssert("check window position", () => originalWindowPosition == window.Position);
175 AddAssert("check current screen", () => window.CurrentDisplayBindable.Value.Index == display.Index);
176 }
177 }
178
179 private void refreshScreens()
180 {
181 screenContainer.Remove(windowContainer);
182 screenContainer.Clear();
183 var bounds = new RectangleI();
184
185 foreach (var display in window.Displays)
186 {
187 screenContainer.Add(createScreen(display, display.Name));
188 bounds = RectangleI.Union(bounds, new RectangleI(display.Bounds.X, display.Bounds.Y, display.Bounds.Width, display.Bounds.Height));
189 }
190
191 screenContainer.Add(windowContainer);
192 screenContainerOffset = bounds.Location;
193
194 foreach (var box in screenContainer.Children)
195 {
196 box.Position -= bounds.Location;
197 }
198
199 screenContainer.Size = bounds.Size;
200 }
201
202 private void updateWindowContainer()
203 {
204 if (window == null) return;
205
206 bool fullscreen = window.WindowMode.Value == WindowMode.Fullscreen;
207 var currentBounds = window.CurrentDisplayBindable.Value.Bounds;
208
209 windowContainer.X = window.Position.X;
210 windowContainer.Y = window.Position.Y;
211 windowContainer.Width = fullscreen ? currentBounds.Width : window.Size.Width;
212 windowContainer.Height = fullscreen ? currentBounds.Height : window.Size.Height;
213 windowContainer.Position -= screenContainerOffset;
214 windowCaption.Text = $"{windowMode}\nSize: {window.Size.Width}x{window.Size.Height}\nClient: {window.ClientSize.Width}x{window.ClientSize.Height}";
215 }
216
217 protected override void Update()
218 {
219 base.Update();
220
221 if (window == null)
222 {
223 currentDisplay.Text = "No suitable window found";
224 return;
225 }
226
227 updateWindowContainer();
228 var scale = Vector2.Divide(paddedContainer.DrawSize, screenContainer.Size);
229 screenContainer.Scale = new Vector2(Math.Min(scale.X, scale.Y));
230
231 currentActualSize.Text = $"Window size: {window?.Size}";
232 currentClientSize.Text = $"Client size: {window?.ClientSize}";
233 currentDisplay.Text = $"Current Display: {window?.CurrentDisplayBindable.Value.Name}";
234 }
235 }
236}