using System;
using Gtk;
using System.Collections.Generic;
using UI = Gtk.Builder.ObjectAttribute;
public class SettingsWindow : Window
{
[UI] private Label ComPortLabel = null;
[UI] private ComboBoxText ComPortComboBox = null;
[UI] private Button ConnectButton = null;
[UI] private Button AutoConnectButton = null;
[UI] private Button RefreshButton = null;
[UI] private Button BackButton = null;
[UI] private Label StatusLabel = null;
[UI] private Label LogLabel = null;
[UI] private TextView LogtextBox = null;
private readonly Dictionary<string, Button> _buttonsById;
private readonly Dictionary<string, Label> _labelsById;
internal Language _Language;
private const int MaxLogLines = 500;
private readonly ArduinoConnection _arduino;
private string[] _ports = new string[0];
/// <summary>Raised after the window hides itself, so the caller can re-show the main window.</summary>
public event EventHandler BackRequested;
public SettingsWindow(ArduinoConnection arduino)
: this(CreateBuilder(), arduino) { }
private static Builder CreateBuilder()
{
var builder = new Builder();
builder.AddFromFile("homepage.glade");
return builder;
}
private SettingsWindow(Builder builder, ArduinoConnection arduino)
: base(builder.GetObject("SettingWindow").Handle)
{
builder.Autoconnect(this);
_arduino = arduino;
Title = "Settings";
ComPortLabel.Text = "COM Port";
LogLabel.Text = "Log";
LogtextBox.Editable = false;
LogtextBox.WrapMode = WrapMode.WordChar;
RefreshButton.Clicked += (s, e) => RefreshPorts();
ConnectButton.Clicked += OnConnectClicked;
AutoConnectButton.Clicked += OnAutoConnectClicked;
BackButton.Clicked += OnBackClicked;
DeleteEvent += (o, a) => { a.RetVal = true; OnBackClicked(o, EventArgs.Empty); };
_arduino.Log += AppendLog;
_arduino.LineReceived += OnLineReceived;
_arduino.ConnectionChanged += OnConnectionChanged;
_buttonsById = new Dictionary<string, Button>
{
{ "ConnectButton", ConnectButton },
{ "AutoConnectButton", AutoConnectButton },
{ "RefreshButton", RefreshButton },
{ "BackButton", BackButton },
};
// CSS ID selectors (#Option1Button etc.) need the widget Name set explicitly
foreach (var kvp in _buttonsById)
kvp.Value.Name = kvp.Key;
_Language = new Language(_buttonsById);
_Language.ApplyLanguages(Language.LanguageSwitchValue);
RefreshPorts();
OnConnectionChanged(_arduino.IsOpen);
}
// ---------- UI actions ----------
private void RefreshPorts()
{
ComPortComboBox.RemoveAll();
_ports = ArduinoConnection.ListPorts();
foreach (string p in _ports)
ComPortComboBox.AppendText(p);
if (_ports.Length > 0)
{
ComPortComboBox.Active = 0;
AppendLog($"{_ports.Length} port(s) found");
}
else
{
AppendLog("No serial ports found");
}
}
private void OnConnectClicked(object sender, EventArgs e)
{
if (_arduino.IsOpen)
{
_arduino.Disconnect();
return;
}
string port = ComPortComboBox.ActiveText;
if (string.IsNullOrEmpty(port))
{
AppendLog("Select a COM port first");
return;
}
_arduino.Connect(port);
}
private async void OnAutoConnectClicked(object sender, EventArgs e)
{
AutoConnectButton.Sensitive = false;
ConnectButton.Sensitive = false;
AppendLog("Scanning ports for device...");
string found = await _arduino.DetectPortAsync();
if (found != null)
{
RefreshPorts();
SelectPort(found);
_arduino.Connect(found);
}
else
{
AppendLog("Device not found — use manual connection");
}
AutoConnectButton.Sensitive = true;
ConnectButton.Sensitive = true;
}
private void OnBackClicked(object sender, EventArgs e)
{
Hide();
BackRequested?.Invoke(this, EventArgs.Empty);
}
private void SelectPort(string portName)
{
for (int i = 0; i < _ports.Length; i++)
{
if (_ports[i] == portName)
{
ComPortComboBox.Active = i;
return;
}
}
}
// ---------- device events ----------
private void OnConnectionChanged(bool connected)
{
ConnectButton.Label = connected ? "Disconnect" : "Connect";
StatusLabel.Text = connected
? $"Connected ({_arduino.PortName})"
: "Disconnected";
ComPortComboBox.Sensitive = !connected;
RefreshButton.Sensitive = !connected;
StyleContext ctx = StatusLabel.StyleContext;
ctx.RemoveClass(connected ? "status-off" : "status-on");
ctx.AddClass(connected ? "status-on" : "status-off");
}
private void OnLineReceived(string line)
{
// Same filtering Form2.cs applied before parsing.
if (line.StartsWith("Send Status:")) return;
if (line.StartsWith("\u2713") || line.StartsWith("\u2717")) return;
if (line.Contains("ESPNOW_CONNECTED")) { AppendLog("[ESP-NOW] Peer connected"); return; }
if (line.Contains("ESPNOW_DISCONNECTED")) { AppendLog("[ESP-NOW] Peer disconnected"); return; }
AppendLog(line);
}
// ---------- log ----------
public void AppendLog(string message)
{
TextBuffer buf = LogtextBox.Buffer;
TextIter end = buf.EndIter;
buf.Insert(ref end, $"[{DateTime.Now:HH:mm:ss.fff}] {message}\n");
if (buf.LineCount > MaxLogLines)
{
TextIter start = buf.StartIter;
TextIter cut = buf.GetIterAtLine(buf.LineCount - MaxLogLines);
buf.Delete(ref start, ref cut);
}
TextMark mark = buf.CreateMark(null, buf.EndIter, false);
LogtextBox.ScrollToMark(mark, 0, false, 0, 0);
buf.DeleteMark(mark);
}
}