Project updates for OLED
This commit is contained in:
21
PCPalConfigurator/App.config
Normal file
21
PCPalConfigurator/App.config
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
|
||||
<section name="PCPalConfigurator.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
<section name="PCPalConfigurator.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<userSettings>
|
||||
<PCPalConfigurator.Properties.Settings>
|
||||
<setting name="LastIconDirectory" serializeAs="String">
|
||||
<value></value>
|
||||
</setting>
|
||||
</PCPalConfigurator.Properties.Settings>
|
||||
<PCPalConfigurator.Settings>
|
||||
<setting name="LastIconDirectory" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</PCPalConfigurator.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
||||
@@ -2,8 +2,11 @@
|
||||
{
|
||||
public class ConfigData
|
||||
{
|
||||
// Common settings
|
||||
public string LastUsedPort { get; set; }
|
||||
public string ScreenType { get; set; }
|
||||
public string ScreenType { get; set; } // "1602", "TFT4_6", or "OLED"
|
||||
|
||||
// LCD-specific settings
|
||||
public string Line1Selection { get; set; }
|
||||
public string Line1CustomText { get; set; }
|
||||
public string Line2Selection { get; set; }
|
||||
@@ -11,5 +14,8 @@
|
||||
public string Line1PostText { get; set; }
|
||||
public string Line2PostText { get; set; }
|
||||
|
||||
// OLED-specific settings
|
||||
public string OledMarkup { get; set; }
|
||||
public string LastIconDirectory { get; set; }
|
||||
}
|
||||
}
|
||||
131
PCPalConfigurator/ConfiguratorForm.Designer.cs
generated
131
PCPalConfigurator/ConfiguratorForm.Designer.cs
generated
@@ -7,6 +7,8 @@
|
||||
private TabControl tabControl;
|
||||
private TabPage tab1602;
|
||||
private TabPage tabTFT;
|
||||
private TabPage tabOLED;
|
||||
private TabPage tabHelp;
|
||||
private TabPage tabAbout;
|
||||
|
||||
private ComboBox cmbLine1;
|
||||
@@ -15,7 +17,7 @@
|
||||
private ComboBox cmbLine2;
|
||||
private TextBox txtLine2;
|
||||
private TextBox txtLine2Post;
|
||||
private Button btnApply;
|
||||
private Button btnSave;
|
||||
|
||||
private Label lblLine1;
|
||||
private Label lblLine1Prefix;
|
||||
@@ -24,6 +26,19 @@
|
||||
private Label lblLine2Prefix;
|
||||
private Label lblLine2Suffix;
|
||||
|
||||
// OLED tab controls
|
||||
private TextBox txtOledMarkup;
|
||||
private Panel panelOledButtons;
|
||||
private Button btnInsertIcon;
|
||||
private Button btnLoadOledExample;
|
||||
private Button btnSaveOled;
|
||||
private Panel panelOledPreview;
|
||||
private Button btnPreview;
|
||||
private Label lblPreviewHeader;
|
||||
|
||||
// Help tab controls
|
||||
private TextBox txtHelpContent;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
@@ -37,6 +52,8 @@
|
||||
this.tabControl = new TabControl();
|
||||
this.tab1602 = new TabPage("1602 LCD");
|
||||
this.tabTFT = new TabPage("4.6\" TFT LCD");
|
||||
this.tabOLED = new TabPage("OLED Display");
|
||||
this.tabHelp = new TabPage("Help");
|
||||
this.tabAbout = new TabPage("About");
|
||||
|
||||
this.cmbLine1 = new ComboBox();
|
||||
@@ -45,7 +62,7 @@
|
||||
this.cmbLine2 = new ComboBox();
|
||||
this.txtLine2 = new TextBox();
|
||||
this.txtLine2Post = new TextBox();
|
||||
this.btnApply = new Button();
|
||||
this.btnSave = new Button();
|
||||
|
||||
this.lblLine1 = new Label();
|
||||
this.lblLine1Prefix = new Label();
|
||||
@@ -54,11 +71,25 @@
|
||||
this.lblLine2Prefix = new Label();
|
||||
this.lblLine2Suffix = new Label();
|
||||
|
||||
// OLED tab controls
|
||||
this.txtOledMarkup = new TextBox();
|
||||
this.panelOledButtons = new Panel();
|
||||
this.btnInsertIcon = new Button();
|
||||
this.btnLoadOledExample = new Button();
|
||||
this.btnSaveOled = new Button();
|
||||
this.panelOledPreview = new Panel();
|
||||
this.btnPreview = new Button();
|
||||
this.lblPreviewHeader = new Label();
|
||||
|
||||
// Help tab controls
|
||||
this.txtHelpContent = new TextBox();
|
||||
|
||||
// === TabControl ===
|
||||
this.tabControl.Location = new System.Drawing.Point(10, 10);
|
||||
this.tabControl.Size = new System.Drawing.Size(620, 280);
|
||||
this.tabControl.Dock = DockStyle.Fill;
|
||||
this.tabControl.TabPages.Add(this.tab1602);
|
||||
this.tabControl.TabPages.Add(this.tabTFT);
|
||||
this.tabControl.TabPages.Add(this.tabOLED);
|
||||
this.tabControl.TabPages.Add(this.tabHelp);
|
||||
this.tabControl.TabPages.Add(this.tabAbout);
|
||||
this.Controls.Add(this.tabControl);
|
||||
|
||||
@@ -106,20 +137,98 @@
|
||||
this.txtLine2Post.Location = new System.Drawing.Point(420, 118);
|
||||
this.txtLine2Post.Size = new System.Drawing.Size(120, 21);
|
||||
|
||||
// === Apply Button ===
|
||||
this.btnApply.Text = "Apply";
|
||||
this.btnApply.Location = new System.Drawing.Point(20, 170);
|
||||
this.btnApply.Size = new System.Drawing.Size(100, 30);
|
||||
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
|
||||
// === Save Button (renamed from Apply) ===
|
||||
this.btnSave.Text = "Save";
|
||||
this.btnSave.Location = new System.Drawing.Point(20, 170);
|
||||
this.btnSave.Size = new System.Drawing.Size(100, 30);
|
||||
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
|
||||
|
||||
// === Add to 1602 Tab ===
|
||||
this.tab1602.Controls.AddRange(new Control[]
|
||||
{
|
||||
lblLine1, cmbLine1, lblLine1Prefix, txtLine1, lblLine1Suffix, txtLine1Post,
|
||||
lblLine2, cmbLine2, lblLine2Prefix, txtLine2, lblLine2Suffix, txtLine2Post,
|
||||
btnApply
|
||||
btnSave
|
||||
});
|
||||
|
||||
// === OLED Tab Setup ===
|
||||
// Main panel for layout
|
||||
Panel oledMainPanel = new Panel();
|
||||
oledMainPanel.Dock = DockStyle.Fill;
|
||||
this.tabOLED.Controls.Add(oledMainPanel);
|
||||
|
||||
// Markup editor
|
||||
this.txtOledMarkup = new TextBox();
|
||||
this.txtOledMarkup.Multiline = true;
|
||||
this.txtOledMarkup.ScrollBars = ScrollBars.Vertical;
|
||||
this.txtOledMarkup.Font = new Font("Consolas", 9.75F);
|
||||
this.txtOledMarkup.TextChanged += new EventHandler(this.txtOledMarkup_TextChanged);
|
||||
this.txtOledMarkup.Size = new Size(580, 130);
|
||||
this.txtOledMarkup.Location = new Point(10, 10);
|
||||
oledMainPanel.Controls.Add(this.txtOledMarkup);
|
||||
|
||||
// Button panel
|
||||
this.panelOledButtons = new Panel();
|
||||
this.panelOledButtons.Size = new Size(580, 40);
|
||||
this.panelOledButtons.Location = new Point(10, 150);
|
||||
oledMainPanel.Controls.Add(this.panelOledButtons);
|
||||
|
||||
this.btnInsertIcon = new Button();
|
||||
this.btnInsertIcon.Text = "Insert Icon";
|
||||
this.btnInsertIcon.Location = new Point(0, 7);
|
||||
this.btnInsertIcon.Size = new Size(120, 26);
|
||||
this.btnInsertIcon.Click += new EventHandler(this.btnInsertIcon_Click);
|
||||
this.panelOledButtons.Controls.Add(this.btnInsertIcon);
|
||||
|
||||
this.btnLoadOledExample = new Button();
|
||||
this.btnLoadOledExample.Text = "Load Example";
|
||||
this.btnLoadOledExample.Location = new Point(130, 7);
|
||||
this.btnLoadOledExample.Size = new Size(120, 26);
|
||||
this.btnLoadOledExample.Click += new EventHandler(this.btnLoadOledExample_Click);
|
||||
this.panelOledButtons.Controls.Add(this.btnLoadOledExample);
|
||||
|
||||
this.btnPreview = new Button();
|
||||
this.btnPreview.Text = "Update Preview";
|
||||
this.btnPreview.Location = new Point(260, 7);
|
||||
this.btnPreview.Size = new Size(120, 26);
|
||||
this.btnPreview.Click += new EventHandler(this.btnPreview_Click);
|
||||
this.panelOledButtons.Controls.Add(this.btnPreview);
|
||||
|
||||
this.btnSaveOled = new Button();
|
||||
this.btnSaveOled.Text = "Save";
|
||||
this.btnSaveOled.Location = new Point(480, 7);
|
||||
this.btnSaveOled.Size = new Size(100, 26);
|
||||
this.btnSaveOled.Click += new EventHandler(this.btnSave_Click); // Uses same event handler
|
||||
this.panelOledButtons.Controls.Add(this.btnSaveOled);
|
||||
|
||||
// Preview header
|
||||
this.lblPreviewHeader = new Label();
|
||||
this.lblPreviewHeader.Text = "OLED Preview (256x64)";
|
||||
this.lblPreviewHeader.Location = new Point(10, 195);
|
||||
this.lblPreviewHeader.Size = new Size(580, 20);
|
||||
this.lblPreviewHeader.TextAlign = ContentAlignment.MiddleCenter;
|
||||
this.lblPreviewHeader.BackColor = System.Drawing.SystemColors.ControlLight;
|
||||
this.lblPreviewHeader.BorderStyle = BorderStyle.FixedSingle;
|
||||
oledMainPanel.Controls.Add(this.lblPreviewHeader);
|
||||
|
||||
// Preview panel
|
||||
this.panelOledPreview = new Panel();
|
||||
this.panelOledPreview.Location = new Point(10, 215);
|
||||
this.panelOledPreview.Size = new Size(580, 110);
|
||||
this.panelOledPreview.BackColor = System.Drawing.Color.Black;
|
||||
this.panelOledPreview.BorderStyle = BorderStyle.FixedSingle;
|
||||
this.panelOledPreview.Paint += new PaintEventHandler(this.panelOledPreview_Paint);
|
||||
oledMainPanel.Controls.Add(this.panelOledPreview);
|
||||
|
||||
// === Help Tab Setup ===
|
||||
this.txtHelpContent = new TextBox();
|
||||
this.txtHelpContent.Dock = DockStyle.Fill;
|
||||
this.txtHelpContent.Multiline = true;
|
||||
this.txtHelpContent.ReadOnly = true;
|
||||
this.txtHelpContent.ScrollBars = ScrollBars.Vertical;
|
||||
this.txtHelpContent.Font = new Font("Segoe UI", 9F);
|
||||
this.tabHelp.Controls.Add(this.txtHelpContent);
|
||||
|
||||
// === TFT Tab Placeholder ===
|
||||
Label lblTFT = new Label()
|
||||
{
|
||||
@@ -140,7 +249,7 @@
|
||||
|
||||
// === Form ===
|
||||
this.Text = "Display Configurator";
|
||||
this.ClientSize = new System.Drawing.Size(640, 300);
|
||||
this.ClientSize = new System.Drawing.Size(600, 380); // Adjusted size for compact layout
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using LibreHardwareMonitor.Hardware;
|
||||
using PCPalConfigurator;
|
||||
|
||||
namespace PCPalConfigurator
|
||||
{
|
||||
@@ -12,12 +15,129 @@ namespace PCPalConfigurator
|
||||
private ConfigData config;
|
||||
private readonly string ConfigFile = GetConfigPath();
|
||||
private Computer computer;
|
||||
private System.Windows.Forms.Timer sensorUpdateTimer;
|
||||
|
||||
// Preview rendering data
|
||||
private List<PreviewElement> previewElements = new List<PreviewElement>();
|
||||
private Dictionary<string, float> sensorValues = new Dictionary<string, float>();
|
||||
private bool autoUpdatePreview = true;
|
||||
private const int OledWidth = 256;
|
||||
private const int OledHeight = 64;
|
||||
|
||||
// Classes for preview rendering
|
||||
private abstract class PreviewElement
|
||||
{
|
||||
public abstract void Draw(Graphics g);
|
||||
}
|
||||
|
||||
private class TextElement : PreviewElement
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Size { get; set; }
|
||||
public string Text { get; set; }
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
// Choose font size based on the size parameter
|
||||
Font font;
|
||||
switch (Size)
|
||||
{
|
||||
case 1: font = new Font("Consolas", 8); break;
|
||||
case 2: font = new Font("Consolas", 10); break;
|
||||
case 3: font = new Font("Consolas", 12); break;
|
||||
default: font = new Font("Consolas", 8); break;
|
||||
}
|
||||
|
||||
// Draw text with white color
|
||||
g.DrawString(Text, font, Brushes.White, X, Y - font.Height);
|
||||
}
|
||||
}
|
||||
|
||||
private class BarElement : PreviewElement
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int Value { get; set; }
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
// Draw outline rectangle
|
||||
g.DrawRectangle(Pens.White, X, Y, Width, Height);
|
||||
|
||||
// Calculate fill width based on value (0-100)
|
||||
int fillWidth = (int)(Width * (Value / 100.0));
|
||||
if (fillWidth > 0)
|
||||
{
|
||||
// Draw filled portion
|
||||
g.FillRectangle(Brushes.White, X + 1, Y + 1, fillWidth - 1, Height - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RectElement : PreviewElement
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public bool Filled { get; set; }
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
if (Filled)
|
||||
{
|
||||
// Draw filled box
|
||||
g.FillRectangle(Brushes.White, X, Y, Width, Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw outline rectangle
|
||||
g.DrawRectangle(Pens.White, X, Y, Width, Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LineElement : PreviewElement
|
||||
{
|
||||
public int X1 { get; set; }
|
||||
public int Y1 { get; set; }
|
||||
public int X2 { get; set; }
|
||||
public int Y2 { get; set; }
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
g.DrawLine(Pens.White, X1, Y1, X2, Y2);
|
||||
}
|
||||
}
|
||||
|
||||
private class IconElement : PreviewElement
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
// For preview, just draw a placeholder rectangle with icon name
|
||||
g.DrawRectangle(Pens.Gray, X, Y, 24, 24);
|
||||
using (Font font = new Font("Arial", 6))
|
||||
{
|
||||
g.DrawString(Name, font, Brushes.White, X + 2, Y + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ConfiguratorForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitHardware();
|
||||
InitSensorTimer();
|
||||
LoadConfig();
|
||||
PopulateHelpContent();
|
||||
UpdatePreview(); // Initial preview rendering
|
||||
}
|
||||
|
||||
private void InitHardware()
|
||||
@@ -33,6 +153,44 @@ namespace PCPalConfigurator
|
||||
};
|
||||
computer.Open();
|
||||
PopulateSensorOptions();
|
||||
UpdateSensorValues();
|
||||
}
|
||||
|
||||
private void InitSensorTimer()
|
||||
{
|
||||
// Create a timer to regularly update sensor values for the preview
|
||||
sensorUpdateTimer = new System.Windows.Forms.Timer();
|
||||
sensorUpdateTimer.Interval = 1000; // Update every second
|
||||
sensorUpdateTimer.Tick += SensorUpdateTimer_Tick;
|
||||
sensorUpdateTimer.Start();
|
||||
}
|
||||
|
||||
private void SensorUpdateTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
// Only update when the OLED tab is selected to save resources
|
||||
if (tabControl.SelectedTab == tabOLED)
|
||||
{
|
||||
UpdateSensorValues();
|
||||
UpdatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSensorValues()
|
||||
{
|
||||
// Update all hardware readings
|
||||
foreach (var hardware in computer.Hardware)
|
||||
{
|
||||
hardware.Update();
|
||||
|
||||
foreach (var sensor in hardware.Sensors)
|
||||
{
|
||||
if (sensor.Value.HasValue)
|
||||
{
|
||||
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
|
||||
sensorValues[sensorId] = sensor.Value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetConfigPath()
|
||||
@@ -58,22 +216,37 @@ namespace PCPalConfigurator
|
||||
|
||||
private void ApplyConfig()
|
||||
{
|
||||
// LCD settings
|
||||
cmbLine1.SelectedItem = config.Line1Selection;
|
||||
txtLine1.Text = config.Line1CustomText;
|
||||
txtLine1Post.Text = config.Line1PostText;
|
||||
cmbLine2.SelectedItem = config.Line2Selection;
|
||||
txtLine2.Text = config.Line2CustomText;
|
||||
txtLine2Post.Text = config.Line2PostText;
|
||||
|
||||
// OLED settings
|
||||
txtOledMarkup.Text = config.OledMarkup;
|
||||
|
||||
// Select the appropriate tab based on screen type
|
||||
if (config.ScreenType == "1602")
|
||||
tabControl.SelectedTab = tab1602;
|
||||
else if (config.ScreenType == "TFT4_6")
|
||||
tabControl.SelectedTab = tabTFT;
|
||||
else if (config.ScreenType == "OLED")
|
||||
tabControl.SelectedTab = tabOLED;
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
// ScreenType based on selected tab
|
||||
// Determine screen type based on selected tab
|
||||
if (tabControl.SelectedTab == tab1602)
|
||||
config.ScreenType = "1602";
|
||||
else if (tabControl.SelectedTab == tabTFT)
|
||||
config.ScreenType = "TFT4_6";
|
||||
else if (tabControl.SelectedTab == tabOLED)
|
||||
config.ScreenType = "OLED";
|
||||
|
||||
// Save LCD settings
|
||||
config.Line1Selection = cmbLine1.SelectedItem?.ToString();
|
||||
config.Line1CustomText = txtLine1.Text;
|
||||
config.Line1PostText = txtLine1Post.Text;
|
||||
@@ -81,12 +254,16 @@ namespace PCPalConfigurator
|
||||
config.Line2CustomText = txtLine2.Text;
|
||||
config.Line2PostText = txtLine2Post.Text;
|
||||
|
||||
// Save OLED settings
|
||||
config.OledMarkup = txtOledMarkup.Text;
|
||||
|
||||
string json = JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||
File.WriteAllText(ConfigFile, json);
|
||||
MessageBox.Show("Settings saved!");
|
||||
}
|
||||
|
||||
private void btnApply_Click(object sender, EventArgs e)
|
||||
// Renamed from btnApply_Click to btnSave_Click
|
||||
private void btnSave_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveConfig();
|
||||
}
|
||||
@@ -116,5 +293,429 @@ namespace PCPalConfigurator
|
||||
cmbLine1.Items.Add("Custom Text");
|
||||
cmbLine2.Items.Add("Custom Text");
|
||||
}
|
||||
|
||||
private void PopulateHelpContent()
|
||||
{
|
||||
// Create help content for both LCD and OLED displays
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// General Information
|
||||
sb.AppendLine("PCPal Display Configurator Help");
|
||||
sb.AppendLine("==============================");
|
||||
sb.AppendLine();
|
||||
|
||||
// LCD Display Help
|
||||
sb.AppendLine("LCD DISPLAY CONFIGURATION");
|
||||
sb.AppendLine("------------------------");
|
||||
sb.AppendLine("1. Select a sensor for each line or choose 'Custom Text'");
|
||||
sb.AppendLine("2. Set prefix text to appear before the sensor value");
|
||||
sb.AppendLine("3. Set suffix/units to appear after the sensor value");
|
||||
sb.AppendLine("4. Click 'Save' to apply your settings");
|
||||
sb.AppendLine();
|
||||
|
||||
// OLED Display Help
|
||||
sb.AppendLine("OLED MARKUP REFERENCE");
|
||||
sb.AppendLine("---------------------");
|
||||
sb.AppendLine("The OLED display accepts markup tags to create your layout.");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Text: <text x=0 y=10 size=1>Hello World</text>");
|
||||
sb.AppendLine(" - x, y: position coordinates (0,0 is top-left)");
|
||||
sb.AppendLine(" - size: 1-3 (small to large)");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Progress Bar: <bar x=0 y=20 w=100 h=8 val=75 />");
|
||||
sb.AppendLine(" - x, y: position");
|
||||
sb.AppendLine(" - w, h: width and height");
|
||||
sb.AppendLine(" - val: value 0-100");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Icon: <icon x=0 y=0 name=cpu />");
|
||||
sb.AppendLine(" - Inserts a bitmap icon from the SD card");
|
||||
sb.AppendLine(" - Use the 'Insert Icon' button to browse available icons");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Rectangle (outline): <rect x=0 y=0 w=20 h=10 />");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Filled Box: <box x=0 y=0 w=20 h=10 />");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Line: <line x1=0 y1=0 x2=20 y2=20 />");
|
||||
sb.AppendLine();
|
||||
|
||||
// Sensor variables
|
||||
sb.AppendLine("SENSOR VARIABLES");
|
||||
sb.AppendLine("----------------");
|
||||
sb.AppendLine("Use {SensorName} syntax to include sensor values.");
|
||||
sb.AppendLine("Example: <text x=0 y=10>CPU: {CPU_Core_i7_Total_Load}%</text>");
|
||||
|
||||
// Add available sensor variables with their current values
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Available sensor variables:");
|
||||
|
||||
var sensors = new Dictionary<string, Tuple<float, string, SensorType>>();
|
||||
|
||||
foreach (var hardware in computer.Hardware)
|
||||
{
|
||||
hardware.Update();
|
||||
foreach (var sensor in hardware.Sensors)
|
||||
{
|
||||
if (sensor.Value.HasValue &&
|
||||
(sensor.SensorType == SensorType.Load ||
|
||||
sensor.SensorType == SensorType.Temperature ||
|
||||
sensor.SensorType == SensorType.Clock ||
|
||||
sensor.SensorType == SensorType.Fan ||
|
||||
sensor.SensorType == SensorType.Power ||
|
||||
sensor.SensorType == SensorType.Data))
|
||||
{
|
||||
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
|
||||
sensors[sensorId] = new Tuple<float, string, SensorType>(
|
||||
sensor.Value.Value,
|
||||
$"{hardware.Name} {sensor.Name}",
|
||||
sensor.SensorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Group sensors by type for easier reading
|
||||
var sensorGroups = sensors.GroupBy(s => s.Value.Item3);
|
||||
foreach (var group in sensorGroups)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"-- {group.Key} Sensors --");
|
||||
|
||||
foreach (var sensor in group)
|
||||
{
|
||||
string unit = GetSensorUnit(group.Key);
|
||||
string value = FormatSensorValue(sensor.Value.Item1, group.Key);
|
||||
sb.AppendLine($"{{{sensor.Key}}} = {value}{unit} ({sensor.Value.Item2})");
|
||||
}
|
||||
}
|
||||
|
||||
txtHelpContent.Text = sb.ToString();
|
||||
}
|
||||
|
||||
private string GetSensorUnit(SensorType sensorType)
|
||||
{
|
||||
return sensorType switch
|
||||
{
|
||||
SensorType.Temperature => "°C",
|
||||
SensorType.Load => "%",
|
||||
SensorType.Clock => "MHz",
|
||||
SensorType.Power => "W",
|
||||
SensorType.Fan => "RPM",
|
||||
SensorType.Flow => "L/h",
|
||||
SensorType.Control => "%",
|
||||
SensorType.Level => "%",
|
||||
_ => ""
|
||||
};
|
||||
}
|
||||
|
||||
private string FormatSensorValue(float value, SensorType sensorType)
|
||||
{
|
||||
// Format different sensor types appropriately
|
||||
return sensorType switch
|
||||
{
|
||||
SensorType.Temperature => value.ToString("F1"),
|
||||
SensorType.Clock => value.ToString("F0"),
|
||||
SensorType.Load => value.ToString("F1"),
|
||||
SensorType.Fan => value.ToString("F0"),
|
||||
SensorType.Power => value.ToString("F1"),
|
||||
SensorType.Data => (value > 1024) ? (value / 1024).ToString("F1") : value.ToString("F1"),
|
||||
_ => value.ToString("F1")
|
||||
};
|
||||
}
|
||||
|
||||
private string GetSensorVariableName(string hardwareName, string sensorName)
|
||||
{
|
||||
// Create a simplified and safe variable name
|
||||
string name = $"{hardwareName}_{sensorName}"
|
||||
.Replace(" ", "_")
|
||||
.Replace("%", "Percent")
|
||||
.Replace("#", "Num")
|
||||
.Replace("/", "_")
|
||||
.Replace("\\", "_")
|
||||
.Replace("(", "")
|
||||
.Replace(")", "")
|
||||
.Replace(",", "");
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private void btnInsertIcon_Click(object sender, EventArgs e)
|
||||
{
|
||||
using IconBrowser browser = new();
|
||||
if (browser.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
// Get the icon markup and insert it at the cursor position
|
||||
string iconMarkup = browser.GetIconMarkup();
|
||||
if (!string.IsNullOrEmpty(iconMarkup))
|
||||
{
|
||||
int selectionStart = txtOledMarkup.SelectionStart;
|
||||
txtOledMarkup.Text = txtOledMarkup.Text.Insert(selectionStart, iconMarkup);
|
||||
txtOledMarkup.SelectionStart = selectionStart + iconMarkup.Length;
|
||||
txtOledMarkup.Focus();
|
||||
|
||||
// Update preview
|
||||
if (autoUpdatePreview)
|
||||
UpdatePreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnLoadOledExample_Click(object sender, EventArgs e)
|
||||
{
|
||||
// Create an appropriate example based on the user's hardware
|
||||
string exampleMarkup = "";
|
||||
string cpuLoad = FindFirstSensorOfType(HardwareType.Cpu, SensorType.Load);
|
||||
string cpuTemp = FindFirstSensorOfType(HardwareType.Cpu, SensorType.Temperature);
|
||||
string gpuLoad = FindFirstSensorOfType(HardwareType.GpuNvidia, SensorType.Load);
|
||||
string gpuTemp = FindFirstSensorOfType(HardwareType.GpuNvidia, SensorType.Temperature);
|
||||
string ramUsed = FindFirstSensorOfType(HardwareType.Memory, SensorType.Data);
|
||||
|
||||
exampleMarkup = "<text x=0 y=12 size=2>System Monitor</text>\n";
|
||||
|
||||
if (!string.IsNullOrEmpty(cpuLoad) && !string.IsNullOrEmpty(cpuTemp))
|
||||
{
|
||||
exampleMarkup += $"<text x=0 y=30 size=1>CPU: {{{cpuLoad}}}% ({{{cpuTemp}}}°C)</text>\n";
|
||||
exampleMarkup += $"<bar x=0 y=35 w=128 h=6 val={{{cpuLoad}}} />\n";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(cpuLoad))
|
||||
{
|
||||
exampleMarkup += $"<text x=0 y=30 size=1>CPU: {{{cpuLoad}}}%</text>\n";
|
||||
exampleMarkup += $"<bar x=0 y=35 w=128 h=6 val={{{cpuLoad}}} />\n";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(gpuLoad) && !string.IsNullOrEmpty(gpuTemp))
|
||||
{
|
||||
exampleMarkup += $"<text x=130 y=30 size=1>GPU: {{{gpuLoad}}}% ({{{gpuTemp}}}°C)</text>\n";
|
||||
exampleMarkup += $"<bar x=130 y=35 w=120 h=6 val={{{gpuLoad}}} />\n";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(gpuLoad))
|
||||
{
|
||||
exampleMarkup += $"<text x=130 y=30 size=1>GPU: {{{gpuLoad}}}%</text>\n";
|
||||
exampleMarkup += $"<bar x=130 y=35 w=120 h=6 val={{{gpuLoad}}} />\n";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ramUsed))
|
||||
{
|
||||
exampleMarkup += $"<text x=0 y=50 size=1>RAM: {{{ramUsed}}} GB</text>\n";
|
||||
}
|
||||
|
||||
txtOledMarkup.Text = exampleMarkup;
|
||||
|
||||
// Update preview
|
||||
if (autoUpdatePreview)
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
private string FindFirstSensorOfType(HardwareType hardwareType, SensorType sensorType)
|
||||
{
|
||||
foreach (var hardware in computer.Hardware)
|
||||
{
|
||||
if (hardware.HardwareType == hardwareType)
|
||||
{
|
||||
foreach (var sensor in hardware.Sensors)
|
||||
{
|
||||
if (sensor.SensorType == sensorType && sensor.Value.HasValue)
|
||||
{
|
||||
return GetSensorVariableName(hardware.Name, sensor.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void btnPreview_Click(object sender, EventArgs e)
|
||||
{
|
||||
UpdateSensorValues();
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
private void txtOledMarkup_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
// If auto-update is enabled, update the preview when the markup changes
|
||||
if (autoUpdatePreview)
|
||||
{
|
||||
UpdatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePreview()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clear existing elements
|
||||
previewElements.Clear();
|
||||
|
||||
// Parse the markup into preview elements
|
||||
string markup = ProcessVariablesInMarkup(txtOledMarkup.Text);
|
||||
ParseMarkup(markup);
|
||||
|
||||
// Trigger repaint of the preview panel
|
||||
if (panelOledPreview != null)
|
||||
panelOledPreview.Invalidate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Don't show errors during preview - just skip rendering
|
||||
Console.WriteLine($"Preview error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private string ProcessVariablesInMarkup(string markup)
|
||||
{
|
||||
if (string.IsNullOrEmpty(markup))
|
||||
return string.Empty;
|
||||
|
||||
// Replace variables with actual values
|
||||
foreach (var sensor in sensorValues)
|
||||
{
|
||||
// Look for {variable} syntax in the markup
|
||||
string variablePattern = $"{{{sensor.Key}}}";
|
||||
|
||||
// Format value based on type (integers vs decimals)
|
||||
string formattedValue;
|
||||
if (Math.Abs(sensor.Value - Math.Round(sensor.Value)) < 0.01)
|
||||
{
|
||||
formattedValue = $"{sensor.Value:F0}";
|
||||
}
|
||||
else
|
||||
{
|
||||
formattedValue = $"{sensor.Value:F1}";
|
||||
}
|
||||
|
||||
markup = markup.Replace(variablePattern, formattedValue);
|
||||
}
|
||||
|
||||
return markup;
|
||||
}
|
||||
|
||||
private void ParseMarkup(string markup)
|
||||
{
|
||||
if (string.IsNullOrEmpty(markup))
|
||||
return;
|
||||
|
||||
// Parse text elements - <text x=0 y=10 size=1>Hello</text>
|
||||
foreach (Match match in Regex.Matches(markup, @"<text\s+x=(\d+)\s+y=(\d+)(?:\s+size=(\d+))?>([^<]*)</text>"))
|
||||
{
|
||||
previewElements.Add(new TextElement
|
||||
{
|
||||
X = int.Parse(match.Groups[1].Value),
|
||||
Y = int.Parse(match.Groups[2].Value),
|
||||
Size = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 1,
|
||||
Text = match.Groups[4].Value
|
||||
});
|
||||
}
|
||||
|
||||
// Parse bar elements - <bar x=0 y=20 w=100 h=8 val=75 />
|
||||
foreach (Match match in Regex.Matches(markup, @"<bar\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s+val=(\d+)\s*/>"))
|
||||
{
|
||||
previewElements.Add(new BarElement
|
||||
{
|
||||
X = int.Parse(match.Groups[1].Value),
|
||||
Y = int.Parse(match.Groups[2].Value),
|
||||
Width = int.Parse(match.Groups[3].Value),
|
||||
Height = int.Parse(match.Groups[4].Value),
|
||||
Value = int.Parse(match.Groups[5].Value)
|
||||
});
|
||||
}
|
||||
|
||||
// Parse rect elements - <rect x=0 y=0 w=20 h=10 />
|
||||
foreach (Match match in Regex.Matches(markup, @"<rect\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
|
||||
{
|
||||
previewElements.Add(new RectElement
|
||||
{
|
||||
X = int.Parse(match.Groups[1].Value),
|
||||
Y = int.Parse(match.Groups[2].Value),
|
||||
Width = int.Parse(match.Groups[3].Value),
|
||||
Height = int.Parse(match.Groups[4].Value),
|
||||
Filled = false
|
||||
});
|
||||
}
|
||||
|
||||
// Parse box elements - <box x=0 y=0 w=20 h=10 />
|
||||
foreach (Match match in Regex.Matches(markup, @"<box\s+x=(\d+)\s+y=(\d+)\s+w=(\d+)\s+h=(\d+)\s*/>"))
|
||||
{
|
||||
previewElements.Add(new RectElement
|
||||
{
|
||||
X = int.Parse(match.Groups[1].Value),
|
||||
Y = int.Parse(match.Groups[2].Value),
|
||||
Width = int.Parse(match.Groups[3].Value),
|
||||
Height = int.Parse(match.Groups[4].Value),
|
||||
Filled = true
|
||||
});
|
||||
}
|
||||
|
||||
// Parse line elements - <line x1=0 y1=0 x2=20 y2=20 />
|
||||
foreach (Match match in Regex.Matches(markup, @"<line\s+x1=(\d+)\s+y1=(\d+)\s+x2=(\d+)\s+y2=(\d+)\s*/>"))
|
||||
{
|
||||
previewElements.Add(new LineElement
|
||||
{
|
||||
X1 = int.Parse(match.Groups[1].Value),
|
||||
Y1 = int.Parse(match.Groups[2].Value),
|
||||
X2 = int.Parse(match.Groups[3].Value),
|
||||
Y2 = int.Parse(match.Groups[4].Value)
|
||||
});
|
||||
}
|
||||
|
||||
// Parse icon elements - <icon x=0 y=0 name=cpu />
|
||||
foreach (Match match in Regex.Matches(markup, @"<icon\s+x=(\d+)\s+y=(\d+)\s+name=([a-zA-Z0-9_]+)\s*/>"))
|
||||
{
|
||||
previewElements.Add(new IconElement
|
||||
{
|
||||
X = int.Parse(match.Groups[1].Value),
|
||||
Y = int.Parse(match.Groups[2].Value),
|
||||
Name = match.Groups[3].Value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void panelOledPreview_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
// Create graphics object with smooth rendering
|
||||
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||||
|
||||
// Draw OLED display boundary
|
||||
int panelWidth = panelOledPreview.Width;
|
||||
int panelHeight = panelOledPreview.Height;
|
||||
|
||||
// Calculate scale to fit preview in panel while maintaining aspect ratio
|
||||
float scaleX = (float)panelWidth / OledWidth;
|
||||
float scaleY = (float)panelHeight / OledHeight;
|
||||
float scale = Math.Min(scaleX, scaleY);
|
||||
|
||||
// Calculate centered position
|
||||
int displayWidth = (int)(OledWidth * scale);
|
||||
int displayHeight = (int)(OledHeight * scale);
|
||||
int offsetX = (panelWidth - displayWidth) / 2;
|
||||
int offsetY = (panelHeight - displayHeight) / 2;
|
||||
|
||||
// Draw display outline
|
||||
Rectangle displayRect = new Rectangle(offsetX, offsetY, displayWidth, displayHeight);
|
||||
e.Graphics.DrawRectangle(Pens.DarkGray, displayRect);
|
||||
|
||||
// Set up transformation to scale the preview elements
|
||||
e.Graphics.TranslateTransform(offsetX, offsetY);
|
||||
e.Graphics.ScaleTransform(scale, scale);
|
||||
|
||||
// Draw all elements
|
||||
foreach (var element in previewElements)
|
||||
{
|
||||
element.Draw(e.Graphics);
|
||||
}
|
||||
|
||||
// Reset transformation
|
||||
e.Graphics.ResetTransform();
|
||||
|
||||
// Draw labels and guidelines
|
||||
e.Graphics.DrawString($"OLED: {OledWidth}x{OledHeight}", new Font("Arial", 8), Brushes.Gray, 5, 5);
|
||||
}
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||
{
|
||||
base.OnFormClosing(e);
|
||||
|
||||
// Clean up resources
|
||||
sensorUpdateTimer?.Stop();
|
||||
sensorUpdateTimer?.Dispose();
|
||||
computer?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
317
PCPalConfigurator/IconBrowser/IconBrowser.Designer.cs
generated
Normal file
317
PCPalConfigurator/IconBrowser/IconBrowser.Designer.cs
generated
Normal file
@@ -0,0 +1,317 @@
|
||||
namespace PCPalConfigurator
|
||||
{
|
||||
partial class IconBrowser
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.panelTop = new System.Windows.Forms.Panel();
|
||||
this.btnBrowse = new System.Windows.Forms.Button();
|
||||
this.txtIconDirectory = new System.Windows.Forms.TextBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.panelBottom = new System.Windows.Forms.Panel();
|
||||
this.txtIconDimensions = new System.Windows.Forms.TextBox();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.numY = new System.Windows.Forms.NumericUpDown();
|
||||
this.numX = new System.Windows.Forms.NumericUpDown();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.txtIconName = new System.Windows.Forms.TextBox();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.btnInsert = new System.Windows.Forms.Button();
|
||||
this.lblStatus = new System.Windows.Forms.Label();
|
||||
this.splitContainer = new System.Windows.Forms.SplitContainer();
|
||||
this.lstIcons = new System.Windows.Forms.ListBox();
|
||||
this.flowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.panelTop.SuspendLayout();
|
||||
this.panelBottom.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numY)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numX)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
|
||||
this.splitContainer.Panel1.SuspendLayout();
|
||||
this.splitContainer.Panel2.SuspendLayout();
|
||||
this.splitContainer.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// panelTop
|
||||
//
|
||||
this.panelTop.Controls.Add(this.btnBrowse);
|
||||
this.panelTop.Controls.Add(this.txtIconDirectory);
|
||||
this.panelTop.Controls.Add(this.label1);
|
||||
this.panelTop.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.panelTop.Location = new System.Drawing.Point(0, 0);
|
||||
this.panelTop.Name = "panelTop";
|
||||
this.panelTop.Size = new System.Drawing.Size(584, 40);
|
||||
this.panelTop.TabIndex = 0;
|
||||
//
|
||||
// btnBrowse
|
||||
//
|
||||
this.btnBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnBrowse.Location = new System.Drawing.Point(497, 9);
|
||||
this.btnBrowse.Name = "btnBrowse";
|
||||
this.btnBrowse.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnBrowse.TabIndex = 2;
|
||||
this.btnBrowse.Text = "Browse...";
|
||||
this.btnBrowse.UseVisualStyleBackColor = true;
|
||||
this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
|
||||
//
|
||||
// txtIconDirectory
|
||||
//
|
||||
this.txtIconDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.txtIconDirectory.Location = new System.Drawing.Point(119, 10);
|
||||
this.txtIconDirectory.Name = "txtIconDirectory";
|
||||
this.txtIconDirectory.Size = new System.Drawing.Size(372, 23);
|
||||
this.txtIconDirectory.TabIndex = 1;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(12, 13);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(101, 15);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "Icons Directory:";
|
||||
//
|
||||
// panelBottom
|
||||
//
|
||||
this.panelBottom.Controls.Add(this.txtIconDimensions);
|
||||
this.panelBottom.Controls.Add(this.label5);
|
||||
this.panelBottom.Controls.Add(this.numY);
|
||||
this.panelBottom.Controls.Add(this.numX);
|
||||
this.panelBottom.Controls.Add(this.label4);
|
||||
this.panelBottom.Controls.Add(this.label3);
|
||||
this.panelBottom.Controls.Add(this.txtIconName);
|
||||
this.panelBottom.Controls.Add(this.label2);
|
||||
this.panelBottom.Controls.Add(this.btnCancel);
|
||||
this.panelBottom.Controls.Add(this.btnInsert);
|
||||
this.panelBottom.Controls.Add(this.lblStatus);
|
||||
this.panelBottom.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.panelBottom.Location = new System.Drawing.Point(0, 341);
|
||||
this.panelBottom.Name = "panelBottom";
|
||||
this.panelBottom.Size = new System.Drawing.Size(584, 120);
|
||||
this.panelBottom.TabIndex = 1;
|
||||
//
|
||||
// txtIconDimensions
|
||||
//
|
||||
this.txtIconDimensions.Location = new System.Drawing.Point(119, 39);
|
||||
this.txtIconDimensions.Name = "txtIconDimensions";
|
||||
this.txtIconDimensions.ReadOnly = true;
|
||||
this.txtIconDimensions.Size = new System.Drawing.Size(100, 23);
|
||||
this.txtIconDimensions.TabIndex = 10;
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Location = new System.Drawing.Point(12, 42);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(77, 15);
|
||||
this.label5.TabIndex = 9;
|
||||
this.label5.Text = "Dimensions:";
|
||||
//
|
||||
// numY
|
||||
//
|
||||
this.numY.Location = new System.Drawing.Point(119, 82);
|
||||
this.numY.Maximum = new decimal(new int[] {
|
||||
200,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numY.Name = "numY";
|
||||
this.numY.Size = new System.Drawing.Size(60, 23);
|
||||
this.numY.TabIndex = 8;
|
||||
//
|
||||
// numX
|
||||
//
|
||||
this.numX.Location = new System.Drawing.Point(119, 68);
|
||||
this.numX.Maximum = new decimal(new int[] {
|
||||
200,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numX.Name = "numX";
|
||||
this.numX.Size = new System.Drawing.Size(60, 23);
|
||||
this.numX.TabIndex = 7;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(12, 84);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(67, 15);
|
||||
this.label4.TabIndex = 6;
|
||||
this.label4.Text = "Y Position:";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(12, 70);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(67, 15);
|
||||
this.label3.TabIndex = 5;
|
||||
this.label3.Text = "X Position:";
|
||||
//
|
||||
// txtIconName
|
||||
//
|
||||
this.txtIconName.Location = new System.Drawing.Point(119, 10);
|
||||
this.txtIconName.Name = "txtIconName";
|
||||
this.txtIconName.ReadOnly = true;
|
||||
this.txtIconName.Size = new System.Drawing.Size(200, 23);
|
||||
this.txtIconName.TabIndex = 4;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(12, 13);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(101, 15);
|
||||
this.label2.TabIndex = 3;
|
||||
this.label2.Text = "Selected Icon:";
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(497, 85);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnCancel.TabIndex = 2;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// btnInsert
|
||||
//
|
||||
this.btnInsert.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnInsert.Location = new System.Drawing.Point(416, 85);
|
||||
this.btnInsert.Name = "btnInsert";
|
||||
this.btnInsert.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnInsert.TabIndex = 1;
|
||||
this.btnInsert.Text = "Insert";
|
||||
this.btnInsert.UseVisualStyleBackColor = true;
|
||||
this.btnInsert.Click += new System.EventHandler(this.btnInsert_Click);
|
||||
//
|
||||
// lblStatus
|
||||
//
|
||||
this.lblStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.lblStatus.AutoSize = true;
|
||||
this.lblStatus.Location = new System.Drawing.Point(325, 13);
|
||||
this.lblStatus.Name = "lblStatus";
|
||||
this.lblStatus.Size = new System.Drawing.Size(0, 15);
|
||||
this.lblStatus.TabIndex = 0;
|
||||
//
|
||||
// splitContainer
|
||||
//
|
||||
this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitContainer.Location = new System.Drawing.Point(0, 40);
|
||||
this.splitContainer.Name = "splitContainer";
|
||||
//
|
||||
// splitContainer.Panel1
|
||||
//
|
||||
this.splitContainer.Panel1.Controls.Add(this.lstIcons);
|
||||
this.splitContainer.Panel1Collapsed = true;
|
||||
this.splitContainer.Panel1MinSize = 0;
|
||||
//
|
||||
// splitContainer.Panel2
|
||||
//
|
||||
this.splitContainer.Panel2.Controls.Add(this.flowLayoutPanel);
|
||||
this.splitContainer.Size = new System.Drawing.Size(584, 301);
|
||||
this.splitContainer.SplitterDistance = 194;
|
||||
this.splitContainer.TabIndex = 2;
|
||||
//
|
||||
// lstIcons
|
||||
//
|
||||
this.lstIcons.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.lstIcons.FormattingEnabled = true;
|
||||
this.lstIcons.ItemHeight = 15;
|
||||
this.lstIcons.Location = new System.Drawing.Point(0, 0);
|
||||
this.lstIcons.Name = "lstIcons";
|
||||
this.lstIcons.Size = new System.Drawing.Size(194, 301);
|
||||
this.lstIcons.TabIndex = 0;
|
||||
//
|
||||
// flowLayoutPanel
|
||||
//
|
||||
this.flowLayoutPanel.AutoScroll = true;
|
||||
this.flowLayoutPanel.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
this.flowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.flowLayoutPanel.Location = new System.Drawing.Point(0, 0);
|
||||
this.flowLayoutPanel.Name = "flowLayoutPanel";
|
||||
this.flowLayoutPanel.Padding = new System.Windows.Forms.Padding(10);
|
||||
this.flowLayoutPanel.Size = new System.Drawing.Size(584, 301);
|
||||
this.flowLayoutPanel.TabIndex = 0;
|
||||
//
|
||||
// IconBrowser
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(584, 461);
|
||||
this.Controls.Add(this.splitContainer);
|
||||
this.Controls.Add(this.panelBottom);
|
||||
this.Controls.Add(this.panelTop);
|
||||
this.MinimizeBox = false;
|
||||
this.MinimumSize = new System.Drawing.Size(400, 400);
|
||||
this.Name = "IconBrowser";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Icon Browser";
|
||||
this.Load += new System.EventHandler(this.IconBrowser_Load);
|
||||
this.panelTop.ResumeLayout(false);
|
||||
this.panelTop.PerformLayout();
|
||||
this.panelBottom.ResumeLayout(false);
|
||||
this.panelBottom.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numY)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numX)).EndInit();
|
||||
this.splitContainer.Panel1.ResumeLayout(false);
|
||||
this.splitContainer.Panel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
|
||||
this.splitContainer.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Panel panelTop;
|
||||
private Button btnBrowse;
|
||||
private TextBox txtIconDirectory;
|
||||
private Label label1;
|
||||
private Panel panelBottom;
|
||||
private Button btnCancel;
|
||||
private Button btnInsert;
|
||||
private Label lblStatus;
|
||||
private SplitContainer splitContainer;
|
||||
private ListBox lstIcons;
|
||||
private FlowLayoutPanel flowLayoutPanel;
|
||||
private TextBox txtIconName;
|
||||
private Label label2;
|
||||
private NumericUpDown numY;
|
||||
private NumericUpDown numX;
|
||||
private Label label4;
|
||||
private Label label3;
|
||||
private TextBox txtIconDimensions;
|
||||
private Label label5;
|
||||
}
|
||||
}
|
||||
215
PCPalConfigurator/IconBrowser/IconBrowser.cs
Normal file
215
PCPalConfigurator/IconBrowser/IconBrowser.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace PCPalConfigurator
|
||||
{
|
||||
public partial class IconBrowser : Form
|
||||
{
|
||||
private string _selectedIconPath = "";
|
||||
private int _selectedIconWidth = 0;
|
||||
private int _selectedIconHeight = 0;
|
||||
public string SelectedIconName { get; private set; } = "";
|
||||
|
||||
public IconBrowser()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void IconBrowser_Load(object? sender, EventArgs e)
|
||||
{
|
||||
// Load last used directory from settings if available
|
||||
string lastDir = Properties.Settings.Default.LastIconDirectory;
|
||||
if (!string.IsNullOrEmpty(lastDir) && Directory.Exists(lastDir))
|
||||
{
|
||||
txtIconDirectory.Text = lastDir;
|
||||
LoadIconsFromDirectory(lastDir);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnBrowse_Click(object? sender, EventArgs e)
|
||||
{
|
||||
using FolderBrowserDialog dialog = new();
|
||||
if (!string.IsNullOrEmpty(txtIconDirectory.Text) && Directory.Exists(txtIconDirectory.Text))
|
||||
{
|
||||
dialog.InitialDirectory = txtIconDirectory.Text;
|
||||
}
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
txtIconDirectory.Text = dialog.SelectedPath;
|
||||
LoadIconsFromDirectory(dialog.SelectedPath);
|
||||
|
||||
// Save the directory for next time
|
||||
Properties.Settings.Default.LastIconDirectory = dialog.SelectedPath;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadIconsFromDirectory(string directory)
|
||||
{
|
||||
try
|
||||
{
|
||||
lstIcons.Items.Clear();
|
||||
flowLayoutPanel.Controls.Clear();
|
||||
|
||||
string[] xbmFiles = Directory.GetFiles(directory, "*.bin");
|
||||
if (xbmFiles.Length == 0)
|
||||
{
|
||||
MessageBox.Show("No XBM icon files found in the selected directory.",
|
||||
"No Icons Found", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string file in xbmFiles)
|
||||
{
|
||||
// Create a button for each icon
|
||||
Button iconButton = new();
|
||||
|
||||
// Parse the XBM file to get dimensions
|
||||
(int width, int height) = ParseXbmDimensions(file);
|
||||
|
||||
string fileName = Path.GetFileNameWithoutExtension(file);
|
||||
iconButton.Text = $"{fileName} ({width}x{height})";
|
||||
iconButton.Tag = new IconInfo
|
||||
{
|
||||
Path = file,
|
||||
Name = fileName,
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
iconButton.Size = new Size(150, 40);
|
||||
iconButton.Click += IconButton_Click;
|
||||
flowLayoutPanel.Controls.Add(iconButton);
|
||||
}
|
||||
|
||||
lblStatus.Text = $"Found {xbmFiles.Length} XBM files";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error loading icons: {ex.Message}",
|
||||
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private (int width, int height) ParseXbmDimensions(string filePath)
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Read the first few lines of the XBM file
|
||||
using StreamReader reader = new(filePath);
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null && (width == 0 || height == 0))
|
||||
{
|
||||
// Look for width and height definitions in XBM format
|
||||
if (line.Contains("_width"))
|
||||
{
|
||||
width = ExtractNumberFromLine(line);
|
||||
}
|
||||
else if (line.Contains("_height"))
|
||||
{
|
||||
height = ExtractNumberFromLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If we can't parse, default to unknown dimensions
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
|
||||
return (width, height);
|
||||
}
|
||||
|
||||
private int ExtractNumberFromLine(string line)
|
||||
{
|
||||
// Extract the numeric value from a line like "#define icon_width 16"
|
||||
try
|
||||
{
|
||||
string[] parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
if (int.TryParse(parts[2], out int result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void IconButton_Click(object? sender, EventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.Tag is IconInfo info)
|
||||
{
|
||||
// Highlight the selected button
|
||||
foreach (Control control in flowLayoutPanel.Controls)
|
||||
{
|
||||
if (control is Button btn)
|
||||
{
|
||||
btn.BackColor = SystemColors.Control;
|
||||
}
|
||||
}
|
||||
button.BackColor = Color.LightBlue;
|
||||
|
||||
// Store the selected icon info
|
||||
SelectedIconName = info.Name;
|
||||
_selectedIconPath = info.Path;
|
||||
_selectedIconWidth = info.Width;
|
||||
_selectedIconHeight = info.Height;
|
||||
|
||||
// Update UI
|
||||
txtIconName.Text = info.Name;
|
||||
txtIconDimensions.Text = $"{info.Width} x {info.Height}";
|
||||
numX.Value = 0;
|
||||
numY.Value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void btnInsert_Click(object? sender, EventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SelectedIconName))
|
||||
{
|
||||
MessageBox.Show("Please select an icon first.",
|
||||
"No Icon Selected", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as success and close
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object? sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
|
||||
public string GetIconMarkup()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SelectedIconName))
|
||||
return string.Empty;
|
||||
|
||||
return $"<icon x={numX.Value} y={numY.Value} name={SelectedIconName} />";
|
||||
}
|
||||
|
||||
// Helper class for icon information
|
||||
private class IconInfo
|
||||
{
|
||||
public string Path { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
120
PCPalConfigurator/IconBrowser/IconBrowser.resx
Normal file
120
PCPalConfigurator/IconBrowser/IconBrowser.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -13,4 +13,25 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Core\" />
|
||||
<Folder Include="Rendering\Elements\" />
|
||||
<Folder Include="UI\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -4,5 +4,8 @@
|
||||
<Compile Update="ConfiguratorForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Update="IconBrowser\IconBrowser.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
38
PCPalConfigurator/Properties/Settings.Designer.cs
generated
Normal file
38
PCPalConfigurator/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PCPalConfigurator.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string LastIconDirectory {
|
||||
get {
|
||||
return ((string)(this["LastIconDirectory"]));
|
||||
}
|
||||
set {
|
||||
this["LastIconDirectory"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
PCPalConfigurator/Properties/Settings.settings
Normal file
9
PCPalConfigurator/Properties/Settings.settings
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="PCPalConfigurator" GeneratedClassName="Settings">
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="LastIconDirectory" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@@ -6,6 +6,7 @@ using System.IO.Ports;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using LibreHardwareMonitor.Hardware;
|
||||
|
||||
@@ -16,6 +17,7 @@ namespace PCPalService
|
||||
private readonly ILogger<Worker> _logger;
|
||||
private SerialPort serialPort;
|
||||
private Computer computer;
|
||||
private readonly Dictionary<string, float> _sensorValues = new();
|
||||
|
||||
private const string AppDataFolder = "PCPal";
|
||||
private const string ConfigFileName = "config.json";
|
||||
@@ -33,7 +35,9 @@ namespace PCPalService
|
||||
IsCpuEnabled = true,
|
||||
IsMemoryEnabled = true,
|
||||
IsGpuEnabled = true,
|
||||
IsMotherboardEnabled = true
|
||||
IsMotherboardEnabled = true,
|
||||
IsControllerEnabled = true,
|
||||
IsStorageEnabled = true
|
||||
};
|
||||
computer.Open();
|
||||
|
||||
@@ -58,33 +62,93 @@ namespace PCPalService
|
||||
{
|
||||
serialPort = new SerialPort(port, 115200);
|
||||
serialPort.Open();
|
||||
_logger.LogInformation($"Connected to ESP32 on {port}");
|
||||
_logger.LogInformation($"Connected to display device on {port}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("ESP32 not found. Retrying in 5 seconds...");
|
||||
_logger.LogWarning("Display device not found. Retrying in 5 seconds...");
|
||||
await Task.Delay(5000, stoppingToken);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
string line1 = GetSensorLine(config.Line1Selection, config.Line1CustomText, config.Line1PostText);
|
||||
string line2 = GetSensorLine(config.Line2Selection, config.Line2CustomText, config.Line2PostText);
|
||||
// Update sensor values
|
||||
UpdateSensorValues();
|
||||
|
||||
serialPort.WriteLine($"CMD:LCD,0,{line1}");
|
||||
serialPort.WriteLine($"CMD:LCD,1,{line2}");
|
||||
_logger.LogInformation("Data sent to LCD.");
|
||||
// Send appropriate commands based on screen type
|
||||
if (config.ScreenType == "1602" || config.ScreenType == "TFT4_6")
|
||||
{
|
||||
// LCD display handling
|
||||
string line1 = GetSensorLine(config.Line1Selection, config.Line1CustomText, config.Line1PostText);
|
||||
string line2 = GetSensorLine(config.Line2Selection, config.Line2CustomText, config.Line2PostText);
|
||||
|
||||
serialPort.WriteLine($"CMD:LCD,0,{line1}");
|
||||
serialPort.WriteLine($"CMD:LCD,1,{line2}");
|
||||
_logger.LogDebug("Data sent to LCD.");
|
||||
}
|
||||
else if (config.ScreenType == "OLED")
|
||||
{
|
||||
// OLED display handling
|
||||
string processedMarkup = ProcessVariablesInMarkup(config.OledMarkup);
|
||||
serialPort.WriteLine($"CMD:OLED,{processedMarkup}");
|
||||
_logger.LogDebug("Data sent to OLED.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"Unknown screen type: {config.ScreenType}");
|
||||
}
|
||||
|
||||
await Task.Delay(5000, stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error in main loop");
|
||||
DisconnectFromDevice();
|
||||
await Task.Delay(5000, stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
serialPort?.Close();
|
||||
DisconnectFromDevice();
|
||||
computer.Close();
|
||||
_logger.LogInformation("Service shutting down...");
|
||||
}
|
||||
|
||||
private void DisconnectFromDevice()
|
||||
{
|
||||
if (serialPort != null && serialPort.IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
serialPort.Close();
|
||||
serialPort.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error disconnecting from device");
|
||||
}
|
||||
finally
|
||||
{
|
||||
serialPort = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSensorValues()
|
||||
{
|
||||
// Update all hardware readings
|
||||
foreach (var hardware in computer.Hardware)
|
||||
{
|
||||
hardware.Update();
|
||||
|
||||
foreach (var sensor in hardware.Sensors)
|
||||
{
|
||||
if (sensor.Value.HasValue)
|
||||
{
|
||||
string sensorId = GetSensorVariableName(hardware.Name, sensor.Name);
|
||||
_sensorValues[sensorId] = sensor.Value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetConfigPath()
|
||||
@@ -109,11 +173,15 @@ namespace PCPalService
|
||||
using (SerialPort test = new SerialPort(port, 115200))
|
||||
{
|
||||
test.Open();
|
||||
test.WriteLine("CMD:GET_LCD_TYPE");
|
||||
test.WriteLine("CMD:GET_DISPLAY_TYPE");
|
||||
Thread.Sleep(500);
|
||||
string response = test.ReadExisting();
|
||||
if (response.Contains("LCD_TYPE:1602A"))
|
||||
|
||||
if (response.Contains("DISPLAY_TYPE:1602A") ||
|
||||
response.Contains("DISPLAY_TYPE:OLED"))
|
||||
{
|
||||
return port;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
@@ -123,15 +191,15 @@ namespace PCPalService
|
||||
|
||||
private string GetSensorLine(string selection, string prefix, string suffix)
|
||||
{
|
||||
if (selection == "Custom Text")
|
||||
return prefix;
|
||||
if (selection == "Custom Text" || string.IsNullOrEmpty(selection))
|
||||
return prefix ?? string.Empty;
|
||||
|
||||
var parsed = ParseSensorSelection(selection);
|
||||
if (parsed == null)
|
||||
return "N/A";
|
||||
|
||||
string value = GetSensorValue(parsed.Value.hardwareType, parsed.Value.sensorName, parsed.Value.sensorType);
|
||||
return $"{prefix}{value}{suffix}";
|
||||
return $"{prefix ?? ""}{value}{suffix ?? ""}";
|
||||
}
|
||||
|
||||
private (HardwareType hardwareType, string sensorName, SensorType sensorType)? ParseSensorSelection(string input)
|
||||
@@ -167,7 +235,6 @@ namespace PCPalService
|
||||
{
|
||||
if (hardware.HardwareType == type)
|
||||
{
|
||||
hardware.Update();
|
||||
foreach (var sensor in hardware.Sensors)
|
||||
{
|
||||
if (sensor.SensorType == sensorType && sensor.Name == name)
|
||||
@@ -179,16 +246,66 @@ namespace PCPalService
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
private string ProcessVariablesInMarkup(string markup)
|
||||
{
|
||||
if (string.IsNullOrEmpty(markup))
|
||||
return string.Empty;
|
||||
|
||||
// Replace variables with actual values
|
||||
foreach (var sensor in _sensorValues)
|
||||
{
|
||||
// Look for {variable} syntax in the markup
|
||||
string variablePattern = $"{{{sensor.Key}}}";
|
||||
|
||||
// Format value based on type (integers vs decimals)
|
||||
string formattedValue;
|
||||
if (Math.Abs(sensor.Value - Math.Round(sensor.Value)) < 0.01)
|
||||
{
|
||||
formattedValue = $"{sensor.Value:F0}";
|
||||
}
|
||||
else
|
||||
{
|
||||
formattedValue = $"{sensor.Value:F1}";
|
||||
}
|
||||
|
||||
markup = markup.Replace(variablePattern, formattedValue);
|
||||
}
|
||||
|
||||
return markup;
|
||||
}
|
||||
|
||||
private string GetSensorVariableName(string hardwareName, string sensorName)
|
||||
{
|
||||
// Create a simplified and safe variable name
|
||||
string name = $"{hardwareName}_{sensorName}"
|
||||
.Replace(" ", "_")
|
||||
.Replace("%", "Percent")
|
||||
.Replace("#", "Num")
|
||||
.Replace("/", "_")
|
||||
.Replace("\\", "_")
|
||||
.Replace("(", "")
|
||||
.Replace(")", "")
|
||||
.Replace(",", "");
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfigData
|
||||
{
|
||||
// Existing properties
|
||||
public string LastUsedPort { get; set; }
|
||||
public string ScreenType { get; set; } // Could be "1602", "TFT4_6", or "OLED"
|
||||
public string Line1Selection { get; set; }
|
||||
public string Line1CustomText { get; set; }
|
||||
public string Line1PostText { get; set; }
|
||||
public string Line2Selection { get; set; }
|
||||
public string Line2CustomText { get; set; }
|
||||
public string Line1PostText { get; set; }
|
||||
public string Line2PostText { get; set; }
|
||||
public string ScreenType { get; set; }
|
||||
|
||||
// New properties for OLED support
|
||||
public string OledMarkup { get; set; } // Store the full markup for OLED
|
||||
public string LastIconDirectory { get; set; } // Remember last used icon directory
|
||||
}
|
||||
}
|
||||
170
RGB666_tests/RGB666_tests.ino
Normal file
170
RGB666_tests/RGB666_tests.ino
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-FileCopyrightText: 2023 Limor Fried for Adafruit Industries
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Arduino_GFX_Library.h>
|
||||
#include <Adafruit_FT6206.h>
|
||||
#include <Adafruit_CST8XX.h>
|
||||
|
||||
Arduino_XCA9554SWSPI *expander = new Arduino_XCA9554SWSPI(
|
||||
PCA_TFT_RESET, PCA_TFT_CS, PCA_TFT_SCK, PCA_TFT_MOSI,
|
||||
&Wire, 0x3F);
|
||||
|
||||
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
|
||||
TFT_DE, TFT_VSYNC, TFT_HSYNC, TFT_PCLK,
|
||||
TFT_R1, TFT_R2, TFT_R3, TFT_R4, TFT_R5,
|
||||
TFT_G0, TFT_G1, TFT_G2, TFT_G3, TFT_G4, TFT_G5,
|
||||
TFT_B1, TFT_B2, TFT_B3, TFT_B4, TFT_B5,
|
||||
1 /* hsync_polarity */, 50 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 44 /* hsync_back_porch */,
|
||||
1 /* vsync_polarity */, 16 /* vsync_front_porch */, 2 /* vsync_pulse_width */, 18 /* vsync_back_porch */
|
||||
// ,1, 30000000
|
||||
);
|
||||
|
||||
Arduino_RGB_Display *gfx = new Arduino_RGB_Display(
|
||||
// 2.1" 480x480 round display
|
||||
// 480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||
// expander, GFX_NOT_DEFINED /* RST */, TL021WVC02_init_operations, sizeof(TL021WVC02_init_operations));
|
||||
|
||||
// 2.8" 480x480 round display
|
||||
// 480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||
// expander, GFX_NOT_DEFINED /* RST */, TL028WVC01_init_operations, sizeof(TL028WVC01_init_operations));
|
||||
|
||||
// 3.4" 480x480 square display
|
||||
// 480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||
// expander, GFX_NOT_DEFINED /* RST */, tl034wvs05_b1477a_init_operations, sizeof(tl034wvs05_b1477a_init_operations));
|
||||
|
||||
// 3.2" 320x820 rectangle bar display
|
||||
// 320 /* width */, 820 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||
// expander, GFX_NOT_DEFINED /* RST */, tl032fwv01_init_operations, sizeof(tl032fwv01_init_operations));
|
||||
|
||||
// 3.7" 240x960 rectangle bar display
|
||||
240 /* width */, 960 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||
expander, GFX_NOT_DEFINED /* RST */, HD371001C40_init_operations, sizeof(HD371001C40_init_operations), 120 /* col_offset1 */);
|
||||
|
||||
// 4.0" 720x720 square display
|
||||
// 720 /* width */, 720 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||
// expander, GFX_NOT_DEFINED /* RST */, NULL, 0);
|
||||
|
||||
// 4.0" 720x720 round display
|
||||
// 720 /* width */, 720 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
|
||||
// expander, GFX_NOT_DEFINED /* RST */, hd40015c40_init_operations, sizeof(hd40015c40_init_operations));
|
||||
// needs also the rgbpanel to have these pulse/sync values:
|
||||
// 1 /* hync_polarity */, 46 /* hsync_front_porch */, 2 /* hsync_pulse_width */, 44 /* hsync_back_porch */,
|
||||
// 1 /* vsync_polarity */, 50 /* vsync_front_porch */, 16 /* vsync_pulse_width */, 16 /* vsync_back_porch */
|
||||
|
||||
uint16_t *colorWheel;
|
||||
|
||||
// The Capacitive touchscreen overlays uses hardware I2C (SCL/SDA)
|
||||
|
||||
// Most touchscreens use FocalTouch with I2C Address often but not always 0x48!
|
||||
#define I2C_TOUCH_ADDR 0x48
|
||||
|
||||
// 2.1" 480x480 round display use CST826 touchscreen with I2C Address at 0x15
|
||||
//#define I2C_TOUCH_ADDR 0x15 // often but not always 0x48!
|
||||
|
||||
Adafruit_FT6206 focal_ctp = Adafruit_FT6206(); // this library also supports FT5336U!
|
||||
Adafruit_CST8XX cst_ctp = Adafruit_CST8XX();
|
||||
bool touchOK = false; // we will check if the touchscreen exists
|
||||
bool isFocalTouch = false;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
Serial.begin(115200);
|
||||
//while (!Serial) delay(100);
|
||||
|
||||
#ifdef GFX_EXTRA_PRE_INIT
|
||||
GFX_EXTRA_PRE_INIT();
|
||||
#endif
|
||||
|
||||
Serial.println("Beginning");
|
||||
// Init Display
|
||||
|
||||
Wire.setClock(1000000); // speed up I2C
|
||||
if (!gfx->begin()) {
|
||||
Serial.println("gfx->begin() failed!");
|
||||
}
|
||||
|
||||
Serial.println("Initialized!");
|
||||
|
||||
gfx->fillScreen(BLACK);
|
||||
|
||||
expander->pinMode(PCA_TFT_BACKLIGHT, OUTPUT);
|
||||
expander->digitalWrite(PCA_TFT_BACKLIGHT, HIGH);
|
||||
|
||||
colorWheel = (uint16_t *) ps_malloc(gfx->width() * gfx->height() * sizeof(uint16_t));
|
||||
if (colorWheel) {
|
||||
generateColorWheel(colorWheel);
|
||||
gfx->draw16bitRGBBitmap(0, 0, colorWheel, gfx->width(), gfx->height());
|
||||
}
|
||||
|
||||
if (!focal_ctp.begin(0, &Wire, I2C_TOUCH_ADDR)) {
|
||||
// Try the CST826 Touch Screen
|
||||
if (!cst_ctp.begin(&Wire, I2C_TOUCH_ADDR)) {
|
||||
Serial.print("No Touchscreen found at address 0x");
|
||||
Serial.println(I2C_TOUCH_ADDR, HEX);
|
||||
touchOK = false;
|
||||
} else {
|
||||
Serial.println("CST826 Touchscreen found");
|
||||
touchOK = true;
|
||||
isFocalTouch = false;
|
||||
}
|
||||
} else {
|
||||
Serial.println("Focal Touchscreen found");
|
||||
touchOK = true;
|
||||
isFocalTouch = true;
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (touchOK) {
|
||||
if (isFocalTouch && focal_ctp.touched()) {
|
||||
TS_Point p = focal_ctp.getPoint(0);
|
||||
Serial.printf("(%d, %d)\n", p.x, p.y);
|
||||
gfx->fillRect(p.x, p.y, 5, 5, WHITE);
|
||||
} else if (!isFocalTouch && cst_ctp.touched()) {
|
||||
CST_TS_Point p = cst_ctp.getPoint(0);
|
||||
Serial.printf("(%d, %d)\n", p.x, p.y);
|
||||
gfx->fillRect(p.x, p.y, 5, 5, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
// use the buttons to turn off
|
||||
if (! expander->digitalRead(PCA_BUTTON_DOWN)) {
|
||||
expander->digitalWrite(PCA_TFT_BACKLIGHT, LOW);
|
||||
}
|
||||
// and on the backlight
|
||||
if (! expander->digitalRead(PCA_BUTTON_UP)) {
|
||||
expander->digitalWrite(PCA_TFT_BACKLIGHT, HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
// https://chat.openai.com/share/8edee522-7875-444f-9fea-ae93a8dfa4ec
|
||||
void generateColorWheel(uint16_t *colorWheel) {
|
||||
int width = gfx->width();
|
||||
int height = gfx->height();
|
||||
int half_width = width / 2;
|
||||
int half_height = height / 2;
|
||||
float angle;
|
||||
uint8_t r, g, b;
|
||||
int index, scaled_index;
|
||||
|
||||
for(int y = 0; y < half_height; y++) {
|
||||
for(int x = 0; x < half_width; x++) {
|
||||
index = y * half_width + x;
|
||||
angle = atan2(y - half_height / 2, x - half_width / 2);
|
||||
r = uint8_t(127.5 * (cos(angle) + 1));
|
||||
g = uint8_t(127.5 * (sin(angle) + 1));
|
||||
b = uint8_t(255 - (r + g) / 2);
|
||||
uint16_t color = RGB565(r, g, b);
|
||||
|
||||
// Scale this pixel into 4 pixels in the full buffer
|
||||
for(int dy = 0; dy < 2; dy++) {
|
||||
for(int dx = 0; dx < 2; dx++) {
|
||||
scaled_index = (y * 2 + dy) * width + (x * 2 + dx);
|
||||
colorWheel[scaled_index] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user