Gadget Styling (Background Color) (#2282)

* Add background tinting and color picker to gadget

-Replaced barblue.png and gadget.png with desaturated versions.
-Added background tinting support (_backTinted, _backgroundColor, _backgroundDirty), switched _barFore to use bar.png, and dispose tinted background resources during cleanup.
-Reworked font color selection UX and mirrored the same chooser flow for background color.

* Fix Gadget bars and HSV color picker UI + Icon

-Fixed gadget bars to tint with selected background color
-Updated color picker flow with a themed, resizable HSV picker.
-Applied LibreHardwareMonitor icon and current app theme to the picker window.

* Hex values and finalize color tinting

-Added Copy/Paste to hex value field.
-Fixed color tinting to respect true grayscale/dark hex values (e.g. #000000) .
-Utilization bars now follow font color tinting.

* Utilization bars fix

-Fixed utilization bar tinting to follow the selected font color directly

* Github Copilot Suggestions

-Forced grayscale-safe background tint behavior, switched background tint processing to LockBits.
-Preserved custom bar override images by skipping tint on user-provided bar assets.
-Fixed PArgb premultiplication in tint output to remove edge halo artifacts.

* Restrict workingSource to Format32bppArgb

-Normalized CreateBackgroundTint sources to Format32bppArgb before LockBits so premultiplied PArgb images are not processed with non-premultiplied ARGB tint math.

* Windows accessibility text scaling

-Adjusted picker layout to scale correctly when Windows Text Size is increased in Accessibility settings.
This commit is contained in:
bloom
2026-04-02 10:36:44 +02:00
committed by GitHub
parent 3d0a31d4ab
commit 60197b6f92
4 changed files with 577 additions and 24 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

+577 -24
View File
@@ -7,11 +7,14 @@
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.UI.Themes;
using LibreHardwareMonitor.Utilities;
namespace LibreHardwareMonitor.UI;
@@ -28,8 +31,14 @@ public class SensorGadget : Gadget
private Image _image;
private Image _fore;
private Image _barBack = Utilities.EmbeddedResources.GetImage("barback.png");
private Image _barFore = Utilities.EmbeddedResources.GetImage("barblue.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;
@@ -51,6 +60,8 @@ public class SensorGadget : Gadget
private StringFormat _stringFormat;
private StringFormat _trimStringFormat;
private StringFormat _alignRightStringFormat;
private Color _fontColor;
private Color _backgroundColor;
public SensorGadget(IComputer computer, PersistentSettings settings, UnitManager unitManager)
{
@@ -99,6 +110,7 @@ public class SensorGadget : Gadget
Image newBarBack = new Bitmap("gadget_bar_background.png");
_barBack.Dispose();
_barBack = newBarBack;
_customBarBack = true;
}
catch { }
}
@@ -110,6 +122,7 @@ public class SensorGadget : Gadget
Image newBarColor = new Bitmap("gadget_bar_foreground.png");
_barFore.Dispose();
_barFore = newBarColor;
_customBarFore = true;
}
catch { }
}
@@ -161,29 +174,59 @@ public class SensorGadget : Gadget
contextMenuStrip.Items.Add(fontSizeMenu);
Color fontColor = settings.GetValue("sensorGadget.FontColor", Color.White);
int fontColorArgb = fontColor.ToArgb();
SetFontColor(fontColor);
IEnumerable<Color> providedColors = Enum.GetValues(typeof(KnownColor))
.Cast<KnownColor>()
.Select(x => Color.FromKnownColor(x))
.Where(x => !x.IsSystemColor && x.Name.Length < 7);
ToolStripMenuItem fontColorMenu = new ToolStripMenuItem("Font Color");
foreach (Color color in providedColors)
ToolStripItem chooseFontColorItem = new ToolStripMenuItem("Choose...");
chooseFontColorItem.Click += delegate
{
ToolStripItem item = new ToolStripMenuItem(color.Name) { Checked = fontColorArgb == color.ToArgb() };
item.Click += delegate
if (TrySelectColor(fontColor, out Color selectedColor))
{
SetFontColor(color);
settings.SetValue("sensorGadget.FontColor", color);
foreach (ToolStripMenuItem mi in fontColorMenu.DropDownItems)
mi.Checked = mi == item;
};
fontColorMenu.DropDownItems.Add(item);
}
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);
@@ -306,6 +349,22 @@ public class SensorGadget : Gadget
_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;
@@ -461,8 +520,498 @@ public class SensorGadget : Gadget
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()
@@ -514,14 +1063,14 @@ public class SensorGadget : Gadget
int w = Size.Width;
int h = Size.Height;
if (w != _background.Width || h != _background.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, _back, TopBorder, BottomBorder,LeftBorder, RightBorder);
DrawImageWidthBorder(graphics, w, h, _backTinted ?? _back, TopBorder, BottomBorder, LeftBorder, RightBorder);
if (_fore != null)
DrawImageWidthBorder(graphics, w, h, _fore, TopBorder, BottomBorder, LeftBorder, RightBorder);
@@ -553,6 +1102,8 @@ public class SensorGadget : Gadget
graphics.DrawImage(_image, new RectangleF(LeftBorder + xOffset, TopBorder + yOffset, destWidth, destHeight));
}
}
_backgroundDirty = false;
}
g.DrawImageUnscaled(_background, 0, 0);
@@ -560,13 +1111,15 @@ public class SensorGadget : Gadget
private void DrawProgress(Graphics g, float x, float y, float width, float height, float progress)
{
g.DrawImage(_barBack,
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),
new RectangleF(barBack.Width * progress, 0, (1 - progress) * barBack.Width, barBack.Height),
GraphicsUnit.Pixel);
g.DrawImage(_barFore,
g.DrawImage(barFore,
new RectangleF(x, y, width * progress, height),
new RectangleF(0, 0, progress * _barFore.Width, _barFore.Height), GraphicsUnit.Pixel);
new RectangleF(0, 0, progress * barFore.Width, barFore.Height), GraphicsUnit.Pixel);
}
protected override void OnPaint(PaintEventArgs e)