* Prepare for Avalonia app - rename LHM UI project. * Rename * Fix icons not being displayed. * Rename workflow project
1333 lines
50 KiB
C#
1333 lines
50 KiB
C#
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
// Copyright (C) LibreHardwareMonitor and Contributors.
|
|
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
|
// All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.Imaging;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using LibreHardwareMonitor.Hardware;
|
|
using LibreHardwareMonitor.Windows.Forms.UI.Themes;
|
|
using LibreHardwareMonitor.Windows.Forms.Utilities;
|
|
|
|
namespace LibreHardwareMonitor.Windows.Forms.UI;
|
|
|
|
public class SensorGadget : Gadget
|
|
{
|
|
private const int TopBorder = 6;
|
|
private const int BottomBorder = 7;
|
|
private const int LeftBorder = 6;
|
|
private const int RightBorder = 7;
|
|
|
|
private readonly UnitManager _unitManager;
|
|
private Image _back = Utilities.EmbeddedResources.GetImage("gadget.png");
|
|
private Image _image;
|
|
private Image _fore;
|
|
private Image _barBack = Utilities.EmbeddedResources.GetImage("barback.png");
|
|
private Image _barFore = Utilities.EmbeddedResources.GetImage("bar.png");
|
|
private bool _customBarBack;
|
|
private bool _customBarFore;
|
|
private Image _backTinted;
|
|
private Image _barBackTinted;
|
|
private Image _barForeTinted;
|
|
private Image _background = new Bitmap(1, 1);
|
|
private bool _backgroundDirty = true;
|
|
private readonly float _scale;
|
|
private float _fontSize;
|
|
private int _iconSize;
|
|
private int _hardwareLineHeight;
|
|
private int _sensorLineHeight;
|
|
private int _rightMargin;
|
|
private int _leftMargin;
|
|
private int _topMargin;
|
|
private int _bottomMargin;
|
|
private int _progressWidth;
|
|
|
|
private readonly IDictionary<IHardware, IList<ISensor>> _sensors = new SortedDictionary<IHardware, IList<ISensor>>(new HardwareComparer());
|
|
private readonly PersistentSettings _settings;
|
|
private readonly UserOption _hardwareNames;
|
|
|
|
private Font _largeFont;
|
|
private Font _smallFont;
|
|
private Brush _textBrush;
|
|
private StringFormat _stringFormat;
|
|
private StringFormat _trimStringFormat;
|
|
private StringFormat _alignRightStringFormat;
|
|
private Color _fontColor;
|
|
private Color _backgroundColor;
|
|
|
|
public SensorGadget(IComputer computer, PersistentSettings settings, UnitManager unitManager)
|
|
{
|
|
_unitManager = unitManager;
|
|
_settings = settings;
|
|
computer.HardwareAdded += HardwareAdded;
|
|
computer.HardwareRemoved += HardwareRemoved;
|
|
|
|
_stringFormat = new StringFormat { FormatFlags = StringFormatFlags.NoWrap };
|
|
_trimStringFormat = new StringFormat { Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap };
|
|
_alignRightStringFormat = new StringFormat { Alignment = StringAlignment.Far, FormatFlags = StringFormatFlags.NoWrap };
|
|
|
|
if (File.Exists("gadget_background.png"))
|
|
{
|
|
try
|
|
{
|
|
Image newBack = new Bitmap("gadget_background.png");
|
|
_back.Dispose();
|
|
_back = newBack;
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
if (File.Exists("gadget_image.png"))
|
|
{
|
|
try
|
|
{
|
|
_image = new Bitmap("gadget_image.png");
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
if (File.Exists("gadget_foreground.png"))
|
|
{
|
|
try
|
|
{
|
|
_fore = new Bitmap("gadget_foreground.png");
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
if (File.Exists("gadget_bar_background.png"))
|
|
{
|
|
try
|
|
{
|
|
Image newBarBack = new Bitmap("gadget_bar_background.png");
|
|
_barBack.Dispose();
|
|
_barBack = newBarBack;
|
|
_customBarBack = true;
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
if (File.Exists("gadget_bar_foreground.png"))
|
|
{
|
|
try
|
|
{
|
|
Image newBarColor = new Bitmap("gadget_bar_foreground.png");
|
|
_barFore.Dispose();
|
|
_barFore = newBarColor;
|
|
_customBarFore = true;
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
Location = new Point(settings.GetValue("sensorGadget.Location.X", 100), settings.GetValue("sensorGadget.Location.Y", 100));
|
|
LocationChanged += delegate
|
|
{
|
|
settings.SetValue("sensorGadget.Location.X", Location.X);
|
|
settings.SetValue("sensorGadget.Location.Y", Location.Y);
|
|
};
|
|
|
|
// get the custom to default dpi ratio
|
|
using (Bitmap b = new Bitmap(1, 1))
|
|
{
|
|
_scale = b.HorizontalResolution / 96.0f;
|
|
}
|
|
|
|
SetFontSize(settings.GetValue("sensorGadget.FontSize", 7.5f));
|
|
Resize(settings.GetValue("sensorGadget.Width", Size.Width));
|
|
|
|
ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
|
|
ToolStripMenuItem hardwareNamesItem = new ToolStripMenuItem("Hardware Names");
|
|
contextMenuStrip.Items.Add(hardwareNamesItem);
|
|
ToolStripMenuItem fontSizeMenu = new ToolStripMenuItem("Font Size");
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
float size;
|
|
string name;
|
|
switch (i)
|
|
{
|
|
case 0: size = 6.5f; name = "Small"; break;
|
|
case 1: size = 7.5f; name = "Medium"; break;
|
|
case 2: size = 9f; name = "Large"; break;
|
|
case 3: size = 11f; name = "Very Large"; break;
|
|
case 4: size = 22f; name = "Extremely Large"; break;
|
|
default: throw new NotImplementedException();
|
|
}
|
|
|
|
ToolStripItem item = new ToolStripMenuItem(name) { Checked = _fontSize == size };
|
|
item.Click += delegate
|
|
{
|
|
SetFontSize(size);
|
|
settings.SetValue("sensorGadget.FontSize", size);
|
|
foreach (ToolStripMenuItem mi in fontSizeMenu.DropDownItems)
|
|
mi.Checked = mi == item;
|
|
};
|
|
fontSizeMenu.DropDownItems.Add(item);
|
|
}
|
|
contextMenuStrip.Items.Add(fontSizeMenu);
|
|
|
|
Color fontColor = settings.GetValue("sensorGadget.FontColor", Color.White);
|
|
SetFontColor(fontColor);
|
|
|
|
ToolStripMenuItem fontColorMenu = new ToolStripMenuItem("Font Color");
|
|
ToolStripItem chooseFontColorItem = new ToolStripMenuItem("Choose...");
|
|
chooseFontColorItem.Click += delegate
|
|
{
|
|
if (TrySelectColor(fontColor, out Color selectedColor))
|
|
{
|
|
fontColor = selectedColor;
|
|
SetFontColor(fontColor);
|
|
settings.SetValue("sensorGadget.FontColor", fontColor);
|
|
Redraw();
|
|
}
|
|
};
|
|
fontColorMenu.DropDownItems.Add(chooseFontColorItem);
|
|
|
|
ToolStripItem defaultFontColorItem = new ToolStripMenuItem("Default");
|
|
defaultFontColorItem.Click += delegate
|
|
{
|
|
fontColor = Color.White;
|
|
SetFontColor(fontColor);
|
|
settings.Remove("sensorGadget.FontColor");
|
|
Redraw();
|
|
};
|
|
fontColorMenu.DropDownItems.Add(defaultFontColorItem);
|
|
contextMenuStrip.Items.Add(fontColorMenu);
|
|
|
|
Color backgroundColor = settings.GetValue("sensorGadget.BackgroundColor", Color.FromArgb(0));
|
|
SetBackgroundColor(backgroundColor);
|
|
|
|
ToolStripMenuItem backgroundColorMenu = new ToolStripMenuItem("Background Color");
|
|
ToolStripItem chooseBackgroundItem = new ToolStripMenuItem("Choose...");
|
|
chooseBackgroundItem.Click += delegate
|
|
{
|
|
if (TrySelectColor(backgroundColor.A == 0 ? Color.White : backgroundColor, out Color selectedColor))
|
|
{
|
|
backgroundColor = selectedColor;
|
|
SetBackgroundColor(backgroundColor);
|
|
settings.SetValue("sensorGadget.BackgroundColor", backgroundColor);
|
|
}
|
|
};
|
|
backgroundColorMenu.DropDownItems.Add(chooseBackgroundItem);
|
|
|
|
ToolStripItem defaultBackgroundItem = new ToolStripMenuItem("Default");
|
|
defaultBackgroundItem.Click += delegate
|
|
{
|
|
Color color = Color.FromArgb(0);
|
|
backgroundColor = color;
|
|
SetBackgroundColor(color);
|
|
settings.Remove("sensorGadget.BackgroundColor");
|
|
};
|
|
backgroundColorMenu.DropDownItems.Add(defaultBackgroundItem);
|
|
contextMenuStrip.Items.Add(backgroundColorMenu);
|
|
contextMenuStrip.Items.Add(new ToolStripSeparator());
|
|
ToolStripMenuItem lockItem = new ToolStripMenuItem("Lock Position and Size");
|
|
contextMenuStrip.Items.Add(lockItem);
|
|
contextMenuStrip.Items.Add(new ToolStripSeparator());
|
|
ToolStripMenuItem alwaysOnTopItem = new ToolStripMenuItem("Always on Top");
|
|
contextMenuStrip.Items.Add(alwaysOnTopItem);
|
|
ToolStripMenuItem opacityMenu = new ToolStripMenuItem("Opacity");
|
|
contextMenuStrip.Items.Add(opacityMenu);
|
|
Opacity = (byte)settings.GetValue("sensorGadget.Opacity", 255);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
ToolStripMenuItem item = new ToolStripMenuItem((20 * (i + 1)).ToString() + " %");
|
|
byte o = (byte)(51 * (i + 1));
|
|
item.Checked = Opacity == o;
|
|
item.Click += delegate
|
|
{
|
|
Opacity = o;
|
|
settings.SetValue("sensorGadget.Opacity", Opacity);
|
|
foreach (ToolStripMenuItem mi in opacityMenu.DropDownItems)
|
|
mi.Checked = mi == item;
|
|
};
|
|
opacityMenu.DropDownItems.Add(item);
|
|
}
|
|
|
|
contextMenuStrip.Items.Add(new ToolStripSeparator());
|
|
ToolStripMenuItem hideShowItem = new ToolStripMenuItem("Hide/Show Main Window");
|
|
contextMenuStrip.Items.Add(hideShowItem);
|
|
|
|
ContextMenuStrip = contextMenuStrip;
|
|
|
|
_hardwareNames = new UserOption("sensorGadget.Hardwarenames", true, hardwareNamesItem, settings);
|
|
_hardwareNames.Changed += delegate
|
|
{
|
|
Resize();
|
|
};
|
|
|
|
UserOption alwaysOnTop = new UserOption("sensorGadget.AlwaysOnTop", false, alwaysOnTopItem, settings);
|
|
alwaysOnTop.Changed += delegate
|
|
{
|
|
AlwaysOnTop = alwaysOnTop.Value;
|
|
};
|
|
UserOption lockPositionAndSize = new UserOption("sensorGadget.LockPositionAndSize", false, lockItem, settings);
|
|
lockPositionAndSize.Changed += delegate
|
|
{
|
|
LockPositionAndSize = lockPositionAndSize.Value;
|
|
};
|
|
|
|
hideShowItem.Click += delegate
|
|
{
|
|
SendHideShowCommand();
|
|
};
|
|
|
|
HitTest += delegate (object sender, HitTestEventArgs e)
|
|
{
|
|
if (lockPositionAndSize.Value)
|
|
return;
|
|
|
|
if (e.Location.X < LeftBorder)
|
|
{
|
|
e.HitResult = HitResult.Left;
|
|
return;
|
|
}
|
|
if (e.Location.X > Size.Width - 1 - RightBorder)
|
|
{
|
|
e.HitResult = HitResult.Right;
|
|
}
|
|
};
|
|
|
|
SizeChanged += delegate
|
|
{
|
|
settings.SetValue("sensorGadget.Width", Size.Width);
|
|
Redraw();
|
|
};
|
|
|
|
VisibleChanged += delegate
|
|
{
|
|
Rectangle bounds = new Rectangle(Location, Size);
|
|
Screen screen = Screen.FromRectangle(bounds);
|
|
Rectangle intersection = Rectangle.Intersect(screen.WorkingArea, bounds);
|
|
if (intersection.Width < Math.Min(16, bounds.Width) || intersection.Height < Math.Min(16, bounds.Height))
|
|
{
|
|
Location = new Point(screen.WorkingArea.Width / 2 - bounds.Width / 2, screen.WorkingArea.Height / 2 - bounds.Height / 2);
|
|
}
|
|
};
|
|
|
|
MouseDoubleClick += delegate
|
|
{
|
|
SendHideShowCommand();
|
|
};
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
|
|
_largeFont.Dispose();
|
|
_largeFont = null;
|
|
|
|
_smallFont.Dispose();
|
|
_smallFont = null;
|
|
|
|
_textBrush.Dispose();
|
|
_textBrush = null;
|
|
|
|
_stringFormat.Dispose();
|
|
_stringFormat = null;
|
|
|
|
_trimStringFormat.Dispose();
|
|
_trimStringFormat = null;
|
|
|
|
_alignRightStringFormat.Dispose();
|
|
_alignRightStringFormat = null;
|
|
|
|
_back.Dispose();
|
|
_back = null;
|
|
|
|
_barFore.Dispose();
|
|
_barFore = null;
|
|
|
|
_barBack.Dispose();
|
|
_barBack = null;
|
|
|
|
if (_backTinted != null)
|
|
{
|
|
_backTinted.Dispose();
|
|
_backTinted = null;
|
|
}
|
|
if (_barForeTinted != null)
|
|
{
|
|
_barForeTinted.Dispose();
|
|
_barForeTinted = null;
|
|
}
|
|
if (_barBackTinted != null)
|
|
{
|
|
_barBackTinted.Dispose();
|
|
_barBackTinted = null;
|
|
}
|
|
|
|
_background.Dispose();
|
|
_background = null;
|
|
|
|
if (_image != null)
|
|
{
|
|
_image.Dispose();
|
|
_image = null;
|
|
}
|
|
|
|
if (_fore != null)
|
|
{
|
|
_fore.Dispose();
|
|
_fore = null;
|
|
}
|
|
|
|
base.Dispose();
|
|
}
|
|
|
|
private void HardwareRemoved(IHardware hardware)
|
|
{
|
|
hardware.SensorAdded -= SensorAdded;
|
|
hardware.SensorRemoved -= SensorRemoved;
|
|
|
|
foreach (ISensor sensor in hardware.Sensors)
|
|
SensorRemoved(sensor);
|
|
|
|
foreach (IHardware subHardware in hardware.SubHardware)
|
|
HardwareRemoved(subHardware);
|
|
}
|
|
|
|
private void HardwareAdded(IHardware hardware)
|
|
{
|
|
foreach (ISensor sensor in hardware.Sensors)
|
|
SensorAdded(sensor);
|
|
|
|
hardware.SensorAdded += SensorAdded;
|
|
hardware.SensorRemoved += SensorRemoved;
|
|
|
|
foreach (IHardware subHardware in hardware.SubHardware)
|
|
HardwareAdded(subHardware);
|
|
}
|
|
|
|
private void SensorAdded(ISensor sensor)
|
|
{
|
|
if (_settings.GetValue(new Identifier(sensor.Identifier, "gadget").ToString(), false))
|
|
Add(sensor);
|
|
}
|
|
|
|
private void SensorRemoved(ISensor sensor)
|
|
{
|
|
if (Contains(sensor))
|
|
Remove(sensor, false);
|
|
}
|
|
|
|
public bool Contains(ISensor sensor)
|
|
{
|
|
return _sensors.Values.Any(list => list.Contains(sensor));
|
|
}
|
|
|
|
public void Add(ISensor sensor)
|
|
{
|
|
if (Contains(sensor))
|
|
return;
|
|
|
|
|
|
// get the right hardware
|
|
IHardware hardware = sensor.Hardware;
|
|
while (hardware.Parent != null)
|
|
hardware = hardware.Parent;
|
|
|
|
// get the sensor list associated with the hardware
|
|
if (!_sensors.TryGetValue(hardware, out IList<ISensor> list))
|
|
{
|
|
list = new List<ISensor>();
|
|
_sensors.Add(hardware, list);
|
|
}
|
|
|
|
// insert the sensor at the right position
|
|
int i = 0;
|
|
while (i < list.Count && (list[i].SensorType < sensor.SensorType || (list[i].SensorType == sensor.SensorType && list[i].Index < sensor.Index)))
|
|
i++;
|
|
|
|
list.Insert(i, sensor);
|
|
|
|
_settings.SetValue(new Identifier(sensor.Identifier, "gadget").ToString(), true);
|
|
Resize();
|
|
}
|
|
|
|
public void Remove(ISensor sensor)
|
|
{
|
|
Remove(sensor, true);
|
|
}
|
|
|
|
private void Remove(ISensor sensor, bool deleteConfig)
|
|
{
|
|
if (deleteConfig)
|
|
_settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString());
|
|
|
|
foreach (KeyValuePair<IHardware, IList<ISensor>> keyValue in _sensors)
|
|
{
|
|
if (keyValue.Value.Contains(sensor))
|
|
{
|
|
keyValue.Value.Remove(sensor);
|
|
if (keyValue.Value.Count == 0)
|
|
{
|
|
_sensors.Remove(keyValue.Key);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Resize();
|
|
}
|
|
|
|
public event EventHandler HideShowCommand;
|
|
|
|
public void SendHideShowCommand()
|
|
{
|
|
HideShowCommand?.Invoke(this, null);
|
|
}
|
|
|
|
private Font CreateFont(float size, FontStyle style)
|
|
{
|
|
try
|
|
{
|
|
return new Font(SystemFonts.MessageBoxFont.FontFamily, size, style);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// if the style is not supported, fall back to the original one
|
|
return new Font(SystemFonts.MessageBoxFont.FontFamily, size,
|
|
SystemFonts.MessageBoxFont.Style);
|
|
}
|
|
}
|
|
|
|
private void SetFontSize(float size)
|
|
{
|
|
_fontSize = size;
|
|
_largeFont = CreateFont(_fontSize, FontStyle.Bold);
|
|
_smallFont = CreateFont(_fontSize, FontStyle.Regular);
|
|
|
|
double scaledFontSize = _fontSize * _scale;
|
|
_iconSize = (int)Math.Round(1.5 * scaledFontSize);
|
|
_hardwareLineHeight = (int)Math.Round(1.66 * scaledFontSize);
|
|
_sensorLineHeight = (int)Math.Round(1.33 * scaledFontSize);
|
|
_leftMargin = LeftBorder + (int)Math.Round(0.3 * scaledFontSize);
|
|
_rightMargin = RightBorder + (int)Math.Round(0.3 * scaledFontSize);
|
|
_topMargin = TopBorder;
|
|
_bottomMargin = BottomBorder + (int)Math.Round(0.3 * scaledFontSize);
|
|
_progressWidth = (int)Math.Round(5.3 * scaledFontSize);
|
|
|
|
Resize((int)Math.Round(17.3 * scaledFontSize));
|
|
}
|
|
|
|
private void SetFontColor(Color color)
|
|
{
|
|
_fontColor = color;
|
|
_textBrush?.Dispose();
|
|
_textBrush = new SolidBrush(color);
|
|
|
|
_barBackTinted?.Dispose();
|
|
_barBackTinted = _customBarBack ? null : CreateBarTint(_barBack, _fontColor);
|
|
_barForeTinted?.Dispose();
|
|
_barForeTinted = _customBarFore ? null : CreateBarTint(_barFore, _fontColor);
|
|
}
|
|
|
|
private void SetBackgroundColor(Color color)
|
|
{
|
|
_backgroundColor = color;
|
|
_backTinted?.Dispose();
|
|
_backTinted = null;
|
|
|
|
// Transparent means "Default" and keeps the embedded/custom image as-is.
|
|
if (_backgroundColor.A > 0)
|
|
{
|
|
_backTinted = CreateBackgroundTint(_back, _backgroundColor);
|
|
}
|
|
|
|
_backgroundDirty = true;
|
|
Redraw();
|
|
}
|
|
|
|
private static Image CreateBackgroundTint(Image source, Color targetColor)
|
|
{
|
|
Bitmap sourceBitmap = new Bitmap(source);
|
|
Rectangle rect = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
|
|
Bitmap workingSource = sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb
|
|
? sourceBitmap
|
|
: sourceBitmap.Clone(rect, PixelFormat.Format32bppArgb);
|
|
Bitmap result = new Bitmap(workingSource.Width, workingSource.Height, PixelFormat.Format32bppPArgb);
|
|
|
|
float targetHue = targetColor.GetHue() / 360f;
|
|
float targetSaturation = targetColor.GetSaturation();
|
|
float targetValue = GetColorValue(targetColor);
|
|
bool isGrayscaleTarget = targetSaturation <= 0.001f;
|
|
|
|
BitmapData srcData = null;
|
|
BitmapData dstData = null;
|
|
try
|
|
{
|
|
srcData = workingSource.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
|
dstData = result.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);
|
|
|
|
int srcLength = srcData.Stride * workingSource.Height;
|
|
int dstLength = dstData.Stride * result.Height;
|
|
byte[] srcBuffer = new byte[srcLength];
|
|
byte[] dstBuffer = new byte[dstLength];
|
|
Marshal.Copy(srcData.Scan0, srcBuffer, 0, srcLength);
|
|
|
|
for (int y = 0; y < workingSource.Height; y++)
|
|
{
|
|
int srcRow = y * srcData.Stride;
|
|
int dstRow = y * dstData.Stride;
|
|
for (int x = 0; x < workingSource.Width; x++)
|
|
{
|
|
int srcIndex = srcRow + (x * 4);
|
|
int dstIndex = dstRow + (x * 4);
|
|
byte b = srcBuffer[srcIndex + 0];
|
|
byte g = srcBuffer[srcIndex + 1];
|
|
byte r = srcBuffer[srcIndex + 2];
|
|
byte a = srcBuffer[srcIndex + 3];
|
|
|
|
if (a == 0)
|
|
{
|
|
// For PArgb targets, keep fully transparent pixels black.
|
|
dstBuffer[dstIndex + 0] = 0;
|
|
dstBuffer[dstIndex + 1] = 0;
|
|
dstBuffer[dstIndex + 2] = 0;
|
|
dstBuffer[dstIndex + 3] = 0;
|
|
continue;
|
|
}
|
|
|
|
Color c = Color.FromArgb(a, r, g, b);
|
|
float sourceSaturation = c.GetSaturation();
|
|
float sourceValue = c.GetBrightness();
|
|
float saturation = isGrayscaleTarget
|
|
? 0f
|
|
: Math.Min(1f, Math.Max(sourceSaturation * 0.25f, targetSaturation * 0.85f));
|
|
float value = Math.Min(1f, (sourceValue * 0.55f) + (targetValue * 0.45f));
|
|
Color tinted = ColorFromHsv(targetHue, saturation, value, a);
|
|
|
|
// Destination format is 32bppPArgb, so RGB must be premultiplied by A.
|
|
dstBuffer[dstIndex + 0] = (byte)((tinted.B * tinted.A + 127) / 255);
|
|
dstBuffer[dstIndex + 1] = (byte)((tinted.G * tinted.A + 127) / 255);
|
|
dstBuffer[dstIndex + 2] = (byte)((tinted.R * tinted.A + 127) / 255);
|
|
dstBuffer[dstIndex + 3] = tinted.A;
|
|
}
|
|
}
|
|
|
|
Marshal.Copy(dstBuffer, 0, dstData.Scan0, dstLength);
|
|
}
|
|
finally
|
|
{
|
|
if (srcData != null)
|
|
workingSource.UnlockBits(srcData);
|
|
if (dstData != null)
|
|
result.UnlockBits(dstData);
|
|
|
|
if (!ReferenceEquals(workingSource, sourceBitmap))
|
|
workingSource.Dispose();
|
|
|
|
sourceBitmap.Dispose();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static Image CreateBarTint(Image source, Color targetColor)
|
|
{
|
|
Bitmap sourceBitmap = new Bitmap(source);
|
|
Bitmap result = new Bitmap(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format32bppPArgb);
|
|
|
|
for (int y = 0; y < sourceBitmap.Height; y++)
|
|
{
|
|
for (int x = 0; x < sourceBitmap.Width; x++)
|
|
{
|
|
Color c = sourceBitmap.GetPixel(x, y);
|
|
if (c.A == 0)
|
|
{
|
|
result.SetPixel(x, y, c);
|
|
continue;
|
|
}
|
|
|
|
// Bars should follow selected text color directly.
|
|
// Preserve source alpha for antialiasing/edge transparency.
|
|
Color tinted = Color.FromArgb(c.A, targetColor.R, targetColor.G, targetColor.B);
|
|
result.SetPixel(x, y, tinted);
|
|
}
|
|
}
|
|
|
|
sourceBitmap.Dispose();
|
|
return result;
|
|
}
|
|
|
|
private static Color ColorFromHsv(float hue, float saturation, float value, int alpha)
|
|
{
|
|
if (saturation <= 0)
|
|
{
|
|
int v = (int)Math.Round(value * 255);
|
|
return Color.FromArgb(alpha, v, v, v);
|
|
}
|
|
|
|
float h = (hue % 1.0f + 1.0f) % 1.0f * 6.0f;
|
|
int i = (int)Math.Floor(h);
|
|
float f = h - i;
|
|
float p = value * (1 - saturation);
|
|
float q = value * (1 - saturation * f);
|
|
float t = value * (1 - saturation * (1 - f));
|
|
|
|
(float r, float g, float b) = i switch
|
|
{
|
|
0 => (value, t, p),
|
|
1 => (q, value, p),
|
|
2 => (p, value, t),
|
|
3 => (p, q, value),
|
|
4 => (t, p, value),
|
|
_ => (value, p, q)
|
|
};
|
|
|
|
return Color.FromArgb(alpha,
|
|
(int)Math.Round(r * 255),
|
|
(int)Math.Round(g * 255),
|
|
(int)Math.Round(b * 255));
|
|
}
|
|
|
|
private static bool TrySelectColor(Color initialColor, out Color selectedColor)
|
|
{
|
|
using Form form = new Form
|
|
{
|
|
Text = "Select Color",
|
|
FormBorderStyle = FormBorderStyle.Sizable,
|
|
StartPosition = FormStartPosition.CenterScreen,
|
|
MinimizeBox = false,
|
|
MaximizeBox = false,
|
|
ClientSize = new Size(460, 340),
|
|
MinimumSize = new Size(420, 320),
|
|
AutoScaleMode = AutoScaleMode.Font
|
|
};
|
|
form.Icon = EmbeddedResources.GetIcon("icon.ico");
|
|
|
|
Panel svBox = new Panel
|
|
{
|
|
BorderStyle = BorderStyle.FixedSingle,
|
|
Cursor = Cursors.Cross
|
|
};
|
|
Panel hueBox = new Panel
|
|
{
|
|
BorderStyle = BorderStyle.FixedSingle,
|
|
Cursor = Cursors.Cross
|
|
};
|
|
SetDoubleBuffered(svBox);
|
|
SetDoubleBuffered(hueBox);
|
|
SetDoubleBuffered(form);
|
|
|
|
float currentHue = initialColor.GetHue() / 360f;
|
|
float currentSaturation = initialColor.GetSaturation();
|
|
float currentValue = GetColorValue(initialColor);
|
|
Color current = ColorFromHsv(currentHue, currentSaturation, currentValue, 255);
|
|
|
|
int svSelectorX = 0;
|
|
int svSelectorY = 0;
|
|
int hueSelectorY = 0;
|
|
|
|
Panel preview = new Panel
|
|
{
|
|
Size = new Size(34, 34),
|
|
BorderStyle = BorderStyle.FixedSingle,
|
|
BackColor = current
|
|
};
|
|
|
|
Label hexLabel = new Label
|
|
{
|
|
AutoSize = true,
|
|
Location = new Point(0, 0),
|
|
Text = "Hex:"
|
|
};
|
|
TextBox hexTextBox = new TextBox
|
|
{
|
|
Width = 110,
|
|
Text = $"#{current.R:X2}{current.G:X2}{current.B:X2}"
|
|
};
|
|
|
|
Button okButton = new Button
|
|
{
|
|
Text = "OK",
|
|
DialogResult = DialogResult.OK,
|
|
AutoSize = true,
|
|
AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
|
MinimumSize = new Size(75, 0),
|
|
Padding = new Padding(6, 2, 6, 2)
|
|
};
|
|
Button cancelButton = new Button
|
|
{
|
|
Text = "Cancel",
|
|
DialogResult = DialogResult.Cancel,
|
|
AutoSize = true,
|
|
AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
|
MinimumSize = new Size(75, 0),
|
|
Padding = new Padding(6, 2, 6, 2)
|
|
};
|
|
bool updatingHexText = false;
|
|
|
|
void LayoutControls()
|
|
{
|
|
const int padding = 12;
|
|
const int hueBarWidth = 24;
|
|
const int hueGap = 8;
|
|
int bottomRowHeight = Math.Max(
|
|
Math.Max(preview.Height, Math.Max(okButton.Height, cancelButton.Height)),
|
|
Math.Max(hexTextBox.PreferredSize.Height, hexLabel.Height)) + 8;
|
|
int rowTop = form.ClientSize.Height - padding - bottomRowHeight;
|
|
|
|
svBox.Location = new Point(padding, padding);
|
|
svBox.Size = new Size(
|
|
Math.Max(200, form.ClientSize.Width - (2 * padding) - hueBarWidth - hueGap),
|
|
Math.Max(120, form.ClientSize.Height - (3 * padding) - bottomRowHeight));
|
|
hueBox.Location = new Point(svBox.Right + hueGap, padding);
|
|
hueBox.Size = new Size(hueBarWidth, svBox.Height);
|
|
|
|
preview.Location = new Point(padding, rowTop + (bottomRowHeight - preview.Height) / 2);
|
|
hexLabel.Location = new Point(preview.Right + 10, rowTop + (bottomRowHeight - hexLabel.Height) / 2);
|
|
hexTextBox.Location = new Point(hexLabel.Right + 6, rowTop + (bottomRowHeight - hexTextBox.Height) / 2);
|
|
|
|
cancelButton.Location = new Point(form.ClientSize.Width - padding - cancelButton.Width, rowTop + (bottomRowHeight - cancelButton.Height) / 2);
|
|
okButton.Location = new Point(cancelButton.Left - 8 - okButton.Width, rowTop + (bottomRowHeight - okButton.Height) / 2);
|
|
}
|
|
|
|
void SyncSelectorsFromCurrent()
|
|
{
|
|
svSelectorX = Math.Min(Math.Max(0, (int)Math.Round(currentSaturation * Math.Max(1, svBox.ClientSize.Width - 1))), Math.Max(0, svBox.ClientSize.Width - 1));
|
|
svSelectorY = Math.Min(Math.Max(0, (int)Math.Round((1f - currentValue) * Math.Max(1, svBox.ClientSize.Height - 1))), Math.Max(0, svBox.ClientSize.Height - 1));
|
|
hueSelectorY = Math.Min(Math.Max(0, (int)Math.Round(currentHue * Math.Max(1, hueBox.ClientSize.Height - 1))), Math.Max(0, hueBox.ClientSize.Height - 1));
|
|
}
|
|
|
|
void UpdateCurrentColor()
|
|
{
|
|
current = ColorFromHsv(currentHue, currentSaturation, currentValue, 255);
|
|
preview.BackColor = current;
|
|
updatingHexText = true;
|
|
hexTextBox.Text = $"#{current.R:X2}{current.G:X2}{current.B:X2}";
|
|
updatingHexText = false;
|
|
}
|
|
|
|
void UpdateSvFromPoint(Point p)
|
|
{
|
|
svSelectorX = Math.Min(Math.Max(0, p.X), Math.Max(0, svBox.ClientSize.Width - 1));
|
|
svSelectorY = Math.Min(Math.Max(0, p.Y), Math.Max(0, svBox.ClientSize.Height - 1));
|
|
currentSaturation = svSelectorX / (float)Math.Max(1, svBox.ClientSize.Width - 1);
|
|
currentValue = 1f - svSelectorY / (float)Math.Max(1, svBox.ClientSize.Height - 1);
|
|
UpdateCurrentColor();
|
|
svBox.Invalidate();
|
|
}
|
|
|
|
void UpdateHueFromPoint(Point p)
|
|
{
|
|
int newHueSelectorY = Math.Min(Math.Max(0, p.Y), Math.Max(0, hueBox.ClientSize.Height - 1));
|
|
if (newHueSelectorY == hueSelectorY)
|
|
return;
|
|
|
|
hueSelectorY = newHueSelectorY;
|
|
currentHue = hueSelectorY / (float)Math.Max(1, hueBox.ClientSize.Height - 1);
|
|
UpdateCurrentColor();
|
|
svBox.Invalidate();
|
|
hueBox.Invalidate();
|
|
}
|
|
|
|
bool draggingSv = false;
|
|
bool draggingHue = false;
|
|
svBox.MouseDown += delegate(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button != MouseButtons.Left)
|
|
return;
|
|
draggingSv = true;
|
|
UpdateSvFromPoint(e.Location);
|
|
};
|
|
svBox.MouseMove += delegate(object sender, MouseEventArgs e)
|
|
{
|
|
if (draggingSv)
|
|
UpdateSvFromPoint(e.Location);
|
|
};
|
|
svBox.MouseUp += delegate { draggingSv = false; };
|
|
|
|
hueBox.MouseDown += delegate(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button != MouseButtons.Left)
|
|
return;
|
|
draggingHue = true;
|
|
UpdateHueFromPoint(e.Location);
|
|
};
|
|
hueBox.MouseMove += delegate(object sender, MouseEventArgs e)
|
|
{
|
|
if (draggingHue)
|
|
UpdateHueFromPoint(e.Location);
|
|
};
|
|
hueBox.MouseUp += delegate { draggingHue = false; };
|
|
|
|
svBox.Paint += delegate(object sender, PaintEventArgs e)
|
|
{
|
|
Rectangle rect = svBox.ClientRectangle;
|
|
if (rect.Width <= 1 || rect.Height <= 1)
|
|
return;
|
|
|
|
rect.Width -= 1;
|
|
rect.Height -= 1;
|
|
e.Graphics.SmoothingMode = SmoothingMode.None;
|
|
|
|
using (SolidBrush hueBrush = new SolidBrush(ColorFromHsv(currentHue, 1f, 1f, 255)))
|
|
e.Graphics.FillRectangle(hueBrush, rect);
|
|
|
|
using (LinearGradientBrush satBrush = new LinearGradientBrush(rect, Color.White, Color.Transparent, LinearGradientMode.Horizontal))
|
|
{
|
|
ColorBlend satBlend = new ColorBlend
|
|
{
|
|
Positions = new[] { 0f, 1f },
|
|
Colors = new[] { Color.FromArgb(255, 255, 255, 255), Color.FromArgb(0, 255, 255, 255) }
|
|
};
|
|
satBrush.InterpolationColors = satBlend;
|
|
e.Graphics.FillRectangle(satBrush, rect);
|
|
}
|
|
|
|
using (LinearGradientBrush valueBrush = new LinearGradientBrush(rect, Color.Transparent, Color.Black, LinearGradientMode.Vertical))
|
|
{
|
|
ColorBlend valueBlend = new ColorBlend
|
|
{
|
|
Positions = new[] { 0f, 1f },
|
|
Colors = new[] { Color.FromArgb(0, 0, 0, 0), Color.FromArgb(255, 0, 0, 0) }
|
|
};
|
|
valueBrush.InterpolationColors = valueBlend;
|
|
e.Graphics.FillRectangle(valueBrush, rect);
|
|
}
|
|
|
|
Rectangle marker = new Rectangle(svSelectorX - 4, svSelectorY - 4, 8, 8);
|
|
using Pen outer = new Pen(Color.Black, 2f);
|
|
using Pen inner = new Pen(Color.White, 1f);
|
|
e.Graphics.DrawEllipse(outer, marker);
|
|
e.Graphics.DrawEllipse(inner, marker);
|
|
};
|
|
hueBox.Paint += delegate(object sender, PaintEventArgs e)
|
|
{
|
|
Rectangle rect = hueBox.ClientRectangle;
|
|
if (rect.Width <= 1 || rect.Height <= 1)
|
|
return;
|
|
|
|
rect.Width -= 1;
|
|
rect.Height -= 1;
|
|
|
|
using (LinearGradientBrush hueBrush = new LinearGradientBrush(rect, Color.Red, Color.Red, LinearGradientMode.Vertical))
|
|
{
|
|
hueBrush.InterpolationColors = new ColorBlend
|
|
{
|
|
Positions = new[] { 0f, 1f / 6f, 2f / 6f, 3f / 6f, 4f / 6f, 5f / 6f, 1f },
|
|
Colors = new[] { Color.Red, Color.Yellow, Color.Lime, Color.Cyan, Color.Blue, Color.Magenta, Color.Red }
|
|
};
|
|
e.Graphics.FillRectangle(hueBrush, rect);
|
|
}
|
|
|
|
Rectangle marker = new Rectangle(0, hueSelectorY - 2, hueBox.ClientSize.Width - 1, 4);
|
|
using Pen outer = new Pen(Color.Black, 2f);
|
|
using Pen inner = new Pen(Color.White, 1f);
|
|
e.Graphics.DrawRectangle(outer, marker);
|
|
e.Graphics.DrawRectangle(inner, marker);
|
|
};
|
|
hexTextBox.TextChanged += delegate
|
|
{
|
|
if (updatingHexText)
|
|
return;
|
|
|
|
if (!TryParseHexColor(hexTextBox.Text, out Color parsedColor))
|
|
return;
|
|
|
|
float parsedHue = parsedColor.GetHue() / 360f;
|
|
float parsedSaturation = parsedColor.GetSaturation();
|
|
float parsedValue = GetColorValue(parsedColor);
|
|
|
|
currentHue = parsedHue;
|
|
currentSaturation = parsedSaturation;
|
|
currentValue = parsedValue;
|
|
SyncSelectorsFromCurrent();
|
|
UpdateCurrentColor();
|
|
svBox.Invalidate();
|
|
hueBox.Invalidate();
|
|
};
|
|
|
|
form.Controls.Add(svBox);
|
|
form.Controls.Add(hueBox);
|
|
form.Controls.Add(preview);
|
|
form.Controls.Add(hexLabel);
|
|
form.Controls.Add(hexTextBox);
|
|
form.Controls.Add(okButton);
|
|
form.Controls.Add(cancelButton);
|
|
form.AcceptButton = okButton;
|
|
form.CancelButton = cancelButton;
|
|
Theme.Current.Apply(form);
|
|
form.Resize += delegate
|
|
{
|
|
LayoutControls();
|
|
SyncSelectorsFromCurrent();
|
|
UpdateCurrentColor();
|
|
svBox.Invalidate();
|
|
hueBox.Invalidate();
|
|
};
|
|
LayoutControls();
|
|
SyncSelectorsFromCurrent();
|
|
UpdateCurrentColor();
|
|
|
|
if (form.ShowDialog() == DialogResult.OK)
|
|
{
|
|
selectedColor = current;
|
|
return true;
|
|
}
|
|
|
|
selectedColor = initialColor;
|
|
return false;
|
|
}
|
|
|
|
private static void SetDoubleBuffered(Control control)
|
|
{
|
|
typeof(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
|
?.SetValue(control, true, null);
|
|
}
|
|
|
|
private static float GetColorValue(Color color)
|
|
{
|
|
return Math.Max(color.R, Math.Max(color.G, color.B)) / 255f;
|
|
}
|
|
|
|
private static bool TryParseHexColor(string input, out Color color)
|
|
{
|
|
color = Color.Empty;
|
|
if (string.IsNullOrWhiteSpace(input))
|
|
return false;
|
|
|
|
string hex = input.Trim();
|
|
if (hex.StartsWith("#"))
|
|
hex = hex.Substring(1);
|
|
|
|
if (hex.Length != 6)
|
|
return false;
|
|
|
|
if (!int.TryParse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out int r))
|
|
return false;
|
|
if (!int.TryParse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out int g))
|
|
return false;
|
|
if (!int.TryParse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out int b))
|
|
return false;
|
|
|
|
color = Color.FromArgb(r, g, b);
|
|
return true;
|
|
}
|
|
|
|
private void Resize()
|
|
{
|
|
Resize(Size.Width);
|
|
}
|
|
|
|
private void Resize(int width)
|
|
{
|
|
int y = _topMargin;
|
|
|
|
foreach (KeyValuePair<IHardware, IList<ISensor>> pair in _sensors)
|
|
{
|
|
if (_hardwareNames.Value)
|
|
{
|
|
if (y > _topMargin)
|
|
y += _hardwareLineHeight - _sensorLineHeight;
|
|
y += _hardwareLineHeight;
|
|
}
|
|
y += pair.Value.Count * _sensorLineHeight;
|
|
}
|
|
|
|
if (_sensors.Count == 0)
|
|
y += 4 * _sensorLineHeight + _hardwareLineHeight;
|
|
|
|
y += _bottomMargin;
|
|
Size = new Size(width, y);
|
|
}
|
|
|
|
private void DrawImageWidthBorder(Graphics g, int width, int height, Image back, int t, int b, int l, int r)
|
|
{
|
|
GraphicsUnit u = GraphicsUnit.Pixel;
|
|
|
|
g.DrawImage(back, new Rectangle(0, 0, l, t), new Rectangle(0, 0, l, t), u);
|
|
g.DrawImage(back, new Rectangle(l, 0, width - l - r, t), new Rectangle(l, 0, back.Width - l - r, t), u);
|
|
g.DrawImage(back, new Rectangle(width - r, 0, r, t), new Rectangle(back.Width - r, 0, r, t), u);
|
|
|
|
g.DrawImage(back, new Rectangle(0, t, l, height - t - b), new Rectangle(0, t, l, back.Height - t - b), u);
|
|
g.DrawImage(back, new Rectangle(l, t, width - l - r, height - t - b), new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
|
|
g.DrawImage(back, new Rectangle(width - r, t, r, height - t - b), new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
|
|
|
|
g.DrawImage(back, new Rectangle(0, height - b, l, b), new Rectangle(0, back.Height - b, l, b), u);
|
|
g.DrawImage(back, new Rectangle(l, height - b, width - l - r, b), new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
|
|
g.DrawImage(back, new Rectangle(width - r, height - b, r, b), new Rectangle(back.Width - r, back.Height - b, r, b), u);
|
|
}
|
|
|
|
private void DrawBackground(Graphics g)
|
|
{
|
|
int w = Size.Width;
|
|
int h = Size.Height;
|
|
|
|
if (_backgroundDirty || w != _background.Width || h != _background.Height)
|
|
{
|
|
_background.Dispose();
|
|
_background = new Bitmap(w, h, PixelFormat.Format32bppPArgb);
|
|
|
|
using (Graphics graphics = Graphics.FromImage(_background))
|
|
{
|
|
DrawImageWidthBorder(graphics, w, h, _backTinted ?? _back, TopBorder, BottomBorder, LeftBorder, RightBorder);
|
|
|
|
if (_fore != null)
|
|
DrawImageWidthBorder(graphics, w, h, _fore, TopBorder, BottomBorder, LeftBorder, RightBorder);
|
|
|
|
if (_image != null)
|
|
{
|
|
int width = w - LeftBorder - RightBorder;
|
|
int height = h - TopBorder - BottomBorder;
|
|
float xRatio = width / (float)_image.Width;
|
|
float yRatio = height / (float)_image.Height;
|
|
float destWidth, destHeight;
|
|
float xOffset, yOffset;
|
|
|
|
if (xRatio < yRatio)
|
|
{
|
|
destWidth = width;
|
|
destHeight = _image.Height * xRatio;
|
|
xOffset = 0;
|
|
yOffset = 0.5f * (height - destHeight);
|
|
}
|
|
else
|
|
{
|
|
destWidth = _image.Width * yRatio;
|
|
destHeight = height;
|
|
xOffset = 0.5f * (width - destWidth);
|
|
yOffset = 0;
|
|
}
|
|
|
|
graphics.DrawImage(_image, new RectangleF(LeftBorder + xOffset, TopBorder + yOffset, destWidth, destHeight));
|
|
}
|
|
}
|
|
|
|
_backgroundDirty = false;
|
|
}
|
|
|
|
g.DrawImageUnscaled(_background, 0, 0);
|
|
}
|
|
|
|
private void DrawProgress(Graphics g, float x, float y, float width, float height, float progress)
|
|
{
|
|
Image barBack = _barBackTinted ?? _barBack;
|
|
Image barFore = _barForeTinted ?? _barFore;
|
|
g.DrawImage(barBack,
|
|
new RectangleF(x + width * progress, y, width * (1 - progress), height),
|
|
new RectangleF(barBack.Width * progress, 0, (1 - progress) * barBack.Width, barBack.Height),
|
|
GraphicsUnit.Pixel);
|
|
g.DrawImage(barFore,
|
|
new RectangleF(x, y, width * progress, height),
|
|
new RectangleF(0, 0, progress * barFore.Width, barFore.Height), GraphicsUnit.Pixel);
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Graphics g = e.Graphics;
|
|
int w = Size.Width;
|
|
|
|
g.Clear(Color.Transparent);
|
|
DrawBackground(g);
|
|
|
|
int x;
|
|
int y = _topMargin;
|
|
|
|
if (_sensors.Count == 0)
|
|
{
|
|
x = LeftBorder + 1;
|
|
g.DrawString("Right-click on a sensor in the main window and select " +
|
|
"\"Show in Gadget\" to show the sensor here.",
|
|
_smallFont, _textBrush,
|
|
new Rectangle(x, y - 1, w - RightBorder - x, 0));
|
|
}
|
|
|
|
foreach (KeyValuePair<IHardware, IList<ISensor>> pair in _sensors)
|
|
{
|
|
if (_hardwareNames.Value)
|
|
{
|
|
if (y > _topMargin)
|
|
y += _hardwareLineHeight - _sensorLineHeight;
|
|
|
|
x = LeftBorder + 1;
|
|
g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType), new Rectangle(x, y + 1, _iconSize, _iconSize));
|
|
x += _iconSize + 1;
|
|
g.DrawString(pair.Key.Name, _largeFont, _textBrush, new Rectangle(x, y - 1, w - RightBorder - x, 0), _stringFormat);
|
|
y += _hardwareLineHeight;
|
|
}
|
|
|
|
foreach (ISensor sensor in pair.Value)
|
|
{
|
|
int remainingWidth;
|
|
|
|
if ((sensor.SensorType != SensorType.Load &&
|
|
sensor.SensorType != SensorType.Control &&
|
|
sensor.SensorType != SensorType.Level &&
|
|
sensor.SensorType != SensorType.Humidity) || !sensor.Value.HasValue)
|
|
{
|
|
string formatted;
|
|
|
|
if (sensor.Value.HasValue)
|
|
{
|
|
string format = "";
|
|
switch (sensor.SensorType)
|
|
{
|
|
case SensorType.Voltage:
|
|
format = "{0:F3} V";
|
|
break;
|
|
case SensorType.Current:
|
|
format = "{0:F3} A";
|
|
break;
|
|
case SensorType.Clock:
|
|
format = "{0:F0} MHz";
|
|
break;
|
|
case SensorType.Frequency:
|
|
format = "{0:F0} Hz";
|
|
break;
|
|
case SensorType.Temperature:
|
|
format = "{0:F1} °C";
|
|
break;
|
|
case SensorType.Fan:
|
|
format = "{0:F0} RPM";
|
|
break;
|
|
case SensorType.Flow:
|
|
format = "{0:F0} L/h";
|
|
break;
|
|
case SensorType.Power:
|
|
format = "{0:F1} W";
|
|
break;
|
|
case SensorType.Data:
|
|
format = "{0:F1} GB";
|
|
break;
|
|
case SensorType.SmallData:
|
|
format = "{0:F0} MB";
|
|
break;
|
|
case SensorType.Factor:
|
|
format = "{0:F3}";
|
|
break;
|
|
case SensorType.TimeSpan:
|
|
format = "{0:g}";
|
|
break;
|
|
case SensorType.Timing:
|
|
format = "{0:F3} ns";
|
|
break;
|
|
case SensorType.Energy:
|
|
format = "{0:F0} mWh";
|
|
break;
|
|
case SensorType.Noise:
|
|
format = "{0:F0} dBA";
|
|
break;
|
|
case SensorType.Conductivity:
|
|
format = "{0:F1} µS/cm";
|
|
break;
|
|
}
|
|
|
|
if (sensor.SensorType == SensorType.Temperature && _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
|
|
{
|
|
formatted = $"{UnitManager.CelsiusToFahrenheit(sensor.Value):F1} °F";
|
|
}
|
|
else if (sensor.SensorType == SensorType.Throughput)
|
|
{
|
|
string result;
|
|
switch (sensor.Name)
|
|
{
|
|
case "Connection Speed":
|
|
{
|
|
switch (sensor.Value)
|
|
{
|
|
case 100000000:
|
|
result = "100Mbps";
|
|
break;
|
|
case 1000000000:
|
|
result = "1Gbps";
|
|
break;
|
|
default:
|
|
{
|
|
if (sensor.Value < 1024)
|
|
result = $"{sensor.Value:F0} bps";
|
|
else if (sensor.Value < 1048576)
|
|
result = $"{sensor.Value / 1024:F1} Kbps";
|
|
else if (sensor.Value < 1073741824)
|
|
result = $"{sensor.Value / 1048576:F1} Mbps";
|
|
else
|
|
result = $"{sensor.Value / 1073741824:F1} Gbps";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (sensor.Value < 1048576)
|
|
result = $"{sensor.Value / 1024:F1} KB/s";
|
|
else
|
|
result = $"{sensor.Value / 1048576:F1} MB/s";
|
|
}
|
|
break;
|
|
}
|
|
formatted = result;
|
|
}
|
|
else if (sensor.SensorType == SensorType.TimeSpan)
|
|
{
|
|
formatted = string.Format(format, TimeSpan.FromSeconds(sensor.Value.Value));
|
|
}
|
|
else
|
|
{
|
|
formatted = string.Format(format, sensor.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
formatted = "-";
|
|
}
|
|
|
|
g.DrawString(formatted, _smallFont, _textBrush, new RectangleF(-1, y - 1, w - _rightMargin + 3, 0), _alignRightStringFormat);
|
|
|
|
remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted, _smallFont, w, StringFormat.GenericTypographic).Width) - _rightMargin;
|
|
}
|
|
else
|
|
{
|
|
DrawProgress(g, w - _progressWidth - _rightMargin, y + 0.35f * _sensorLineHeight, _progressWidth, 0.6f * _sensorLineHeight, 0.01f * sensor.Value.Value);
|
|
remainingWidth = w - _progressWidth - _rightMargin;
|
|
}
|
|
|
|
remainingWidth -= _leftMargin + 2;
|
|
if (remainingWidth > 0)
|
|
{
|
|
g.DrawString(sensor.Name, _smallFont, _textBrush, new RectangleF(_leftMargin - 1, y - 1, remainingWidth, 0), _trimStringFormat);
|
|
}
|
|
y += _sensorLineHeight;
|
|
}
|
|
}
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// #1425.
|
|
}
|
|
}
|
|
|
|
private class HardwareComparer : IComparer<IHardware>
|
|
{
|
|
public int Compare(IHardware x, IHardware y)
|
|
{
|
|
switch (x)
|
|
{
|
|
case null when y == null:
|
|
return 0;
|
|
case null:
|
|
return -1;
|
|
}
|
|
|
|
if (y == null)
|
|
return 1;
|
|
|
|
if (x.HardwareType != y.HardwareType)
|
|
return x.HardwareType.CompareTo(y.HardwareType);
|
|
|
|
return x.Identifier.CompareTo(y.Identifier);
|
|
}
|
|
}
|
|
}
|