ShareX/ShareX/Tools/PinToScreen/PinToScreenForm.cs

588 lines
18 KiB
C#

#region License Information (GPL v3)
/*
ShareX - A program that allows you to take screenshots and share any file type
Copyright (c) 2007-2025 ShareX Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Optionally you can also view the license at <http://www.gnu.org/licenses/>.
*/
#endregion License Information (GPL v3)
using ShareX.HelpersLib;
using ShareX.Properties;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows.Forms;
namespace ShareX
{
public partial class PinToScreenForm : Form
{
private static readonly object syncLock = new object();
private static readonly List<PinToScreenForm> forms = new List<PinToScreenForm>();
public Image Image { get; private set; }
private int imageScale = 100;
public int ImageScale
{
get
{
return imageScale;
}
set
{
int newImageScale = value.Clamp(20, 500);
if (imageScale != newImageScale)
{
imageScale = newImageScale;
if (loaded)
{
AutoSizeForm();
}
UpdateControls();
}
}
}
public Size ImageSize
{
get
{
return new Size((int)Math.Round(Image.Width * (ImageScale / 100f)), (int)Math.Round(Image.Height * (ImageScale / 100f)));
}
}
public Size FormSize
{
get
{
Size size;
if (Minimized)
{
size = Options.MinimizeSize;
}
else
{
size = ImageSize;
}
if (Options.Border)
{
size = size.Offset(Options.BorderSize * 2);
}
return size;
}
}
private int imageOpacity = 100;
public int ImageOpacity
{
get
{
return imageOpacity;
}
set
{
int newImageOpacity = value.Clamp(10, 100);
if (imageOpacity != newImageOpacity)
{
imageOpacity = newImageOpacity;
Opacity = imageOpacity / 100f;
}
}
}
public bool Minimized { get; private set; }
public PinToScreenOptions Options { get; private set; }
private bool isDWMEnabled, loaded, dragging;
private Point initialLocation;
private Cursor openHandCursor, closedHandCursor;
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= (int)WindowStyles.WS_EX_TOOLWINDOW;
return createParams;
}
}
private PinToScreenForm(PinToScreenOptions options)
{
Options = options;
ImageScale = Options.InitialScale;
ImageOpacity = Options.InitialOpacity;
InitializeComponent();
ShareXResources.ApplyTheme(this, true);
TopMost = Options.TopMost;
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
tsMain.Cursor = Cursors.Arrow;
openHandCursor = Helpers.CreateCursor(Resources.openhand);
closedHandCursor = Helpers.CreateCursor(Resources.closedhand);
SetHandCursor(false);
isDWMEnabled = NativeMethods.IsDWMEnabled();
UpdateControls();
loaded = true;
}
private PinToScreenForm(Image image, PinToScreenOptions options, Point? location = null) : this(options)
{
Image = image;
AutoSizeForm();
if (location == null)
{
location = Helpers.GetPosition(Options.Placement, Options.PlacementOffset, CaptureHelpers.GetActiveScreenWorkingArea(), FormSize);
}
else if (Options.Border)
{
location = location.Value.Add(-Options.BorderSize);
}
Location = location.Value;
}
public static void PinToScreenAsync(Image image, PinToScreenOptions options = null, Point? location = null)
{
if (image != null)
{
if (options == null)
{
options = new PinToScreenOptions();
}
Thread thread = new Thread(() =>
{
using (PinToScreenForm form = new PinToScreenForm(image, options, location))
{
lock (syncLock)
{
forms.Add(form);
}
form.ShowDialog();
lock (syncLock)
{
forms.Remove(form);
}
}
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
public static void CloseAll()
{
lock (syncLock)
{
foreach (PinToScreenForm form in forms)
{
form.InvokeSafe(form.Close);
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
components?.Dispose();
Image?.Dispose();
openHandCursor?.Dispose();
closedHandCursor?.Dispose();
}
base.Dispose(disposing);
}
private void UpdateControls()
{
int toolbarMargin = 20;
tsMain.Visible = ClientRectangle.Contains(PointToClient(MousePosition)) &&
ClientRectangle.Contains(new Rectangle(0, 0, (Options.Border ? Options.BorderSize * 2 : 0) + tsMain.Width + toolbarMargin,
(Options.Border ? Options.BorderSize * 2 : 0) + tsMain.Height + toolbarMargin));
tslScale.Text = ImageScale + "%";
}
private void AutoSizeForm()
{
Size previousSize = Size;
Size newSize = FormSize;
Point newLocation = Location;
IntPtr insertAfter = Options.TopMost ? (IntPtr)NativeConstants.HWND_TOPMOST : (IntPtr)NativeConstants.HWND_TOP;
SetWindowPosFlags flags = SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOSENDCHANGING;
if (Options.KeepCenterLocation)
{
Point locationOffset = new Point((previousSize.Width - newSize.Width) / 2, (previousSize.Height - newSize.Height) / 2);
newLocation = new Point(newLocation.X + locationOffset.X, newLocation.Y + locationOffset.Y);
}
else
{
flags |= SetWindowPosFlags.SWP_NOMOVE;
}
NativeMethods.SetWindowPos(Handle, insertAfter, newLocation.X, newLocation.Y, newSize.Width, newSize.Height, flags);
tsMain.Location = new Point(Width / 2 - tsMain.Width / 2, Options.Border ? Options.BorderSize : 0);
UpdateControls();
Refresh();
}
private void SetHandCursor(bool grabbing)
{
if (grabbing)
{
if (Cursor != closedHandCursor)
{
Cursor = closedHandCursor;
}
}
else
{
if (Cursor != openHandCursor)
{
Cursor = openHandCursor;
}
}
}
private void ResetImage()
{
ImageScale = 100;
ImageOpacity = 100;
}
private void ToggleMinimize()
{
Minimized = !Minimized;
if (ImageOpacity < 100)
{
if (Minimized)
{
Opacity = 1f;
}
else
{
Opacity = ImageOpacity / 100f;
}
}
AutoSizeForm();
}
private void tsbCopy_Click(object sender, EventArgs e)
{
ClipboardHelpers.CopyImage(Image);
}
private void tslScale_Click(object sender, EventArgs e)
{
if (!Minimized)
{
ImageScale = 100;
}
}
private void tsbOptions_Click(object sender, EventArgs e)
{
tsMain.Visible = false;
using (PinToScreenOptionsForm pinToScreenOptionsForm = new PinToScreenOptionsForm(Options))
{
if (pinToScreenOptionsForm.ShowDialogTopMost(this) == DialogResult.OK)
{
if (TopMost != Options.TopMost)
{
TopMost = Options.TopMost;
}
AutoSizeForm();
}
}
}
private void tsbClose_Click(object sender, EventArgs e)
{
Close();
}
protected override void WndProc(ref Message m)
{
if (Options.Shadow && m.Msg == (int)WindowsMessages.NCPAINT && isDWMEnabled)
{
NativeMethods.SetNCRenderingPolicy(Handle, DWMNCRENDERINGPOLICY.DWMNCRP_ENABLED);
if (Helpers.IsWindows11OrGreater())
{
NativeMethods.SetWindowCornerPreference(Handle, DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND);
}
MARGINS margins = new MARGINS()
{
bottomHeight = 1,
leftWidth = 1,
rightWidth = 1,
topHeight = 1
};
NativeMethods.DwmExtendFrameIntoClientArea(Handle, ref margins);
}
base.WndProc(ref m);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e);
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(Options.BackgroundColor);
if (Image != null)
{
Point position = new Point(0, 0);
if (Options.Border)
{
using (Pen pen = new Pen(Options.BorderColor, Options.BorderSize) { Alignment = PenAlignment.Inset })
{
g.DrawRectangleProper(pen, new Rectangle(position, FormSize));
}
position = position.Add(Options.BorderSize);
}
if (Minimized)
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(Image, new Rectangle(position, Options.MinimizeSize), 0, 0, Options.MinimizeSize.Width, Options.MinimizeSize.Height, GraphicsUnit.Pixel);
}
else
{
if (ImageScale == 100)
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(Image, new Rectangle(position, Image.Size));
}
else
{
if (Options.HighQualityScale)
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
}
else
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
}
using (ImageAttributes ia = new ImageAttributes())
{
ia.SetWrapMode(WrapMode.TileFlipXY);
g.DrawImage(Image, new Rectangle(position, ImageSize), 0, 0, Image.Width, Image.Height, GraphicsUnit.Pixel, ia);
}
}
}
}
}
private void PinToScreenForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (e.Clicks > 1)
{
ToggleMinimize();
}
else
{
dragging = true;
initialLocation = e.Location;
SetHandCursor(true);
}
}
}
private void PinToScreenForm_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
Location = new Point(Location.X + e.X - initialLocation.X, Location.Y + e.Y - initialLocation.Y);
Update();
}
}
private void PinToScreenForm_MouseUp(object sender, MouseEventArgs e)
{
switch (e.Button)
{
case MouseButtons.Left:
dragging = false;
SetHandCursor(false);
break;
case MouseButtons.Right:
Close();
break;
}
if (!Minimized)
{
switch (e.Button)
{
case MouseButtons.Middle:
ResetImage();
break;
}
}
}
private void PinToScreenForm_MouseWheel(object sender, MouseEventArgs e)
{
if (!Minimized)
{
switch (ModifierKeys)
{
case Keys.None:
if (e.Delta > 0)
{
ImageScale += Options.ScaleStep;
}
else if (e.Delta < 0)
{
ImageScale -= Options.ScaleStep;
}
break;
case Keys.Control:
if (e.Delta > 0)
{
ImageOpacity += Options.OpacityStep;
}
else if (e.Delta < 0)
{
ImageOpacity -= Options.OpacityStep;
}
break;
}
}
}
private void PinToScreenForm_MouseEnter(object sender, EventArgs e)
{
UpdateControls();
}
private void PinToScreenForm_MouseLeave(object sender, EventArgs e)
{
UpdateControls();
}
private void tsMain_MouseLeave(object sender, EventArgs e)
{
UpdateControls();
}
private void PinToScreenForm_KeyDown(object sender, KeyEventArgs e)
{
int speed = e.Shift ? 10 : 1;
switch (e.KeyCode)
{
case Keys.Left:
Location = new Point(Location.X - speed, Location.Y);
break;
case Keys.Right:
Location = new Point(Location.X + speed, Location.Y);
break;
case Keys.Up:
Location = new Point(Location.X, Location.Y - speed);
break;
case Keys.Down:
Location = new Point(Location.X, Location.Y + speed);
break;
}
}
private void PinToScreenForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyData)
{
case Keys.Control | Keys.C:
ClipboardHelpers.CopyImage(Image);
break;
}
if (!Minimized)
{
switch (e.KeyData)
{
case Keys.Oemplus:
case Keys.Add:
ImageScale += Options.ScaleStep;
break;
case Keys.OemMinus:
case Keys.Subtract:
ImageScale -= Options.ScaleStep;
break;
case Keys.Control | Keys.Oemplus:
case Keys.Control | Keys.Add:
ImageOpacity += Options.OpacityStep;
break;
case Keys.Control | Keys.OemMinus:
case Keys.Control | Keys.Subtract:
ImageOpacity -= Options.OpacityStep;
break;
}
}
}
}
}