cursor-guardrail-utility/source/Windows/Forms/ScreensLayout.cs
2026-07-11 02:31:25 +01:00

220 lines
6.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CursorGuardRail.Dtos;
using RyzStudio.Drawing;
namespace CursorGuardRail.Windows.Forms
{
public class ScreensLayout : RyzStudio.Windows.ThemedForms.T3UserControl
{
private readonly Pen screenBorderColour = new Pen(Color.Black, 2f);
private readonly Brush screenBackColour = new SolidBrush(Color.FromArgb(218, 218, 218));
private readonly Brush selectedScreenBackColour = new SolidBrush(Color.FromArgb(0, 120, 215));
private readonly Pen selectedAreaBorderColour = new Pen(Color.FromArgb(254, 135, 0), 2f)
{
DashStyle = System.Drawing.Drawing2D.DashStyle.Dot
};
public ScreensLayout()
{
this.Font = new Font(this.Font.FontFamily, 20f, FontStyle.Regular);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
for (var i=0; i<this.Screens.Count; i++)
{
var screen = this.Screens[i];
var backColour = screen.IsSelected ? selectedScreenBackColour : screenBackColour;
var foreColour = screen.IsSelected ? Color.White : this.ForeColor;
var font = screen.IsPrimary ? new Font(this.Font, FontStyle.Bold) : this.Font;
var label = (i + 1).ToString();
var rect = screen.Bound.ToRectangle();
rect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height - 5);
g.FillRectangle(backColour, screen.Bound);
g.DrawRectangle(screenBorderColour, screen.Bound);
TextRenderer.DrawText(g, label, font, rect, foreColour, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding| TextFormatFlags.EndEllipsis);
}
if (this.Screens.Any(x => x.IsSelected))
{
var area = CalcMaxBound(this.Screens.Where(x => x.IsSelected).Select(x => x.Bound).ToList());
g.DrawRectangle(selectedAreaBorderColour, area);
}
g.Flush();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
for (var i = 0; i < this.Screens.Count; i++)
{
if (this.Screens[i].Bound.Contains(e.Location))
{
this.Screens[i].IsSelected = !this.Screens[i].IsSelected;
break;
}
}
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.InvalidateScreens();
}
protected override void UpdateBackground()
{
if (base.ClientRectangle.Width <= 0 || base.ClientRectangle.Height <= 0)
{
return;
}
var backColour = Color.FromArgb(242, 242, 242);
var backgroundImage = new Rectangoid(base.ClientRectangle, CurrentStyle.Border.Radius, CurrentStyle.Border.Width).ToBitmap(new Pen(new SolidBrush(CurrentStyle.Border.Colour), CurrentStyle.Border.Width), new SolidBrush(backColour));
this.BackgroundImage = backgroundImage;
}
public void InvalidateScreens()
{
var virtualDesktop = SystemInformation.VirtualScreen;
var offsetX = (virtualDesktop.X <= 0 ? -virtualDesktop.X : 0);
this.Screens.Clear();
foreach (var screen in Screen.AllScreens)
{
var bound = screen.Bounds;
bound.Offset(offsetX, 0);
var newBound = new RectangleF(bound.X, bound.Y, bound.Width, bound.Height);
newBound = newBound.ScaleDown(ScaleRatio);
this.Screens.Add(new ScreenItem()
{
Bound = newBound,
IsPrimary = screen.Primary,
IsSelected = false
});
}
CenterRectangles(this.Screens);
this.Invalidate();
}
private int ActualWidth
{
get => this.ClientSize.Width - this.Padding.Horizontal;
}
private int ActualHeight
{
get => this.ClientSize.Height - this.Padding.Vertical;
}
public List<ScreenItem> Screens
{
get
{
if (field == null) field = new List<ScreenItem>();
return field;
}
private set
{
field = value;
//this.Invalidate();
}
}
private decimal ScaleRatio
{
get
{
var virtualScreen = SystemInformation.VirtualScreen;
var widthRatio = decimal.Divide(virtualScreen.Width, ActualWidth);
var heightRatio = decimal.Divide(virtualScreen.Height, ActualHeight);
return Math.Max(widthRatio, heightRatio);
}
}
private PointF CalcCentreOffset(List<RectangleF> rectangles)
{
if (rectangles.Count <= 0)
{
return PointF.Empty;
}
// Find the bounds
var left = rectangles.Min(r => r.Left);
var top = rectangles.Min(r => r.Top);
var right = rectangles.Max(r => r.Right);
var bottom = rectangles.Max(r => r.Bottom);
var bounds = RectangleF.FromLTRB(left, top, right, bottom);
// Calculate offset
var offsetX = this.Padding.Left + (ActualWidth - bounds.Width) / 2 - bounds.Left;
var offsetY = this.Padding.Top + (ActualHeight - bounds.Height) / 2 - bounds.Top;
return new PointF(offsetX, offsetY);
}
private void CenterRectangles(List<ScreenItem> screen)
{
var offset = CalcCentreOffset(screen.Select(x => x.Bound).ToList());
for (var i=0; i<screen.Count; i++)
{
screen[i].Bound = new RectangleF(screen[i].Bound.X + offset.X, screen[i].Bound.Y + offset.Y, screen[i].Bound.Width, screen[i].Bound.Height);
}
}
private RectangleF CalcMaxBound(List<RectangleF> bounds)
{
RectangleF? rect = null;
for (var i = 0; i < bounds.Count; i++)
{
if (rect == null)
{
rect = bounds[i];
}
else
{
rect = RectangleF.Union(bounds[i], rect.Value);
}
}
return rect ?? Rectangle.Empty;
}
}
}