using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;
using System.Runtime.InteropServices;
public class MainWindow : Window
{
// ---- stack + pages (new all-in-one glade) ----
[UI] private Notebook RootNoteBook = null;
private const int PageSplash = 0;
private const int PageSettings = 1;
// ---- splash page widgets ----
[UI] private Button Option1Button = null;
[UI] private Button Option2Button = null;
[UI] private Button Option3Button = null;
// [UI] private Switch LanguageSwitch = null;
[UI] private ToggleButton LanguageToggle = null;
[UI] private Label LanguageLabel = null;
[UI] private Button CloseButton = null;
[UI] private Button SettingButton = null;
[UI] private Image ConnectionIcon = null; // was GtkIconView in the old glade
private CssProvider _scaleCssProvider;
// private CssProvider _languageCssProvider;
private int _lastAppliedWidth = -1;
private const int BaseWidth = 1920;
private const int BaseHeight = 1080;
// private const string EngCssPath = "lang_eng.css";
// private const string JpnCssPath = "lang_jpn.css";
// private Dictionary<string, string> _engText;
// private Dictionary<string, string> _jpnText;
private Dictionary<string, Button> _buttonsById;
private readonly ArduinoConnection _arduino = new ArduinoConnection();
internal SettingsWindow _settingsWindow;
internal Language _Language;
public MainWindow() : this(CreateBuilder()) { }
private static Builder CreateBuilder()
{
var builder = new Builder();
builder.AddFromFile("homepageall.glade");
return builder;
}
private MainWindow(Builder builder) : base(builder.GetObject("Root").Handle)
{
builder.Autoconnect(this);
_scaleCssProvider = new CssProvider();
// _languageCssProvider = new CssProvider();
StyleContext.AddProviderForScreen(Gdk.Screen.Default, _scaleCssProvider, 600);
// StyleContext.AddProviderForScreen(Gdk.Screen.Default, _Language._languageCssProvider, 600);
// make it full screen
this.Fullscreen();
// RootStack.TransitionType = StackTransitionType.None;
// RootStack.TransitionType = StackTransitionType.SlideLeftRight;
// RootStack.TransitionDuration = 150;
Option1Button.Clicked += OnNormalSessionClicked;
Option2Button.Clicked += OnPositionRecordingClicked;
Option3Button.Clicked += OnExamModeClicked;
CloseButton.Clicked += OnCloseClicked;
SettingButton.Clicked += OnSettingClicked;
// LanguageSwitch.AddNotification("active", OnLanguageSwitchNotify);
LanguageToggle.Toggled += OnLanguageToggled;
DeleteEvent += OnDeleteEvent;
SizeAllocated += OnWindowSizeAllocated;
_arduino.ConnectionChanged += OnArduinoConnectionChanged;
_buttonsById = new Dictionary<string, Button>
{
{ "Option1Button", Option1Button },
{ "Option2Button", Option2Button },
{ "Option3Button", Option3Button },
{ "CloseButton", CloseButton },
{ "SettingButton", SettingButton },
};
// CSS ID selectors (#Option1Button etc.) need the widget Name set explicitly
foreach (var kvp in _buttonsById)
kvp.Value.Name = kvp.Key;
// _engText = LoadButtonText("info_eng.csv");
// _jpnText = LoadButtonText("info_jpn.csv");
_Language = new Language(_buttonsById,LanguageLabel);
// _Language.ApplyLanguage(LanguageSwitch.Active);
_settingsWindow = new SettingsWindow(builder, _arduino);
_settingsWindow.BackRequested += OnSettingsClosed;
// single source of truth: the toggle drives both pages
ApplyLanguageEverywhere(LanguageToggle.Active);
RootNoteBook.ShowTabs = false;
RootNoteBook.ShowBorder = false;
RootNoteBook.CurrentPage = PageSplash;
OnArduinoConnectionChanged(false);
}
// ---------- settings panel ----------
private void OnSettingClicked(object sender, EventArgs e)
{
// if (_settingsWindow == null)
// {
// // _settingsWindow = new SettingsWindow(_arduino);
// // _settingsWindow.BackRequested += OnSettingsClosed;
// // _settingsWindow.ShowAll(); // build it fullscreen once, up front
// // _settingsWindow.Fullscreen();
// RootNoteBook.CurrentPage = PageSettings;
// }
// _settingsWindow.ShowAll();
// _settingsWindow.Present();
// this.Hide();
RootNoteBook.CurrentPage = PageSettings;
}
private void OnSettingsClosed(object sender, EventArgs e)
{
// this.ShowAll();
// this.Present();
// _settingsWindow.Hide();
RootNoteBook.CurrentPage = PageSplash;
}
private void OnArduinoConnectionChanged(bool connected)
{
if (ConnectionIcon == null) return;
ConnectionIcon.SetFromIconName(
connected ? "network-transmit-receive" : "network-offline",
IconSize.Dnd);
ConnectionIcon.TooltipText = connected
? $"Connected to {_arduino.PortName}"
: "Device not connected";
}
// ---------- localisation ----------
private Dictionary<string, string> LoadButtonText(string path)
{
var result = new Dictionary<string, string>();
if (!File.Exists(path))
{
Console.WriteLine($"Warning: text file not found: {path}");
return result;
}
var lines = File.ReadAllLines(path);
for (int i = 1; i < lines.Length; i++) // skip header row
{
var line = lines[i].Trim();
if (string.IsNullOrEmpty(line)) continue;
var parts = line.Split(new[] { ',' }, 2);
if (parts.Length < 2) continue;
result[parts[0]] = parts[1];
}
return result;
}
// private void ApplyLanguage(bool isEnglish)
// {
// var text = isEnglish ? _engText : _jpnText;
// string cssPath = isEnglish ? EngCssPath : JpnCssPath;
// foreach (var kvp in _buttonsById)
// {
// if (text.TryGetValue(kvp.Key, out var buttonText))
// kvp.Value.Label = buttonText;
// }
// if (File.Exists(cssPath))
// _languageCssProvider.LoadFromPath(cssPath);
// else
// Console.WriteLine($"Warning: CSS file not found: {cssPath}");
// LanguageLabel.Text = isEnglish ? "あ" : "A";
// }
// ---------- scaling ----------
private void OnWindowSizeAllocated(object o, SizeAllocatedArgs args)
{
int width = args.Allocation.Width;
int height = args.Allocation.Height;
if (width == _lastAppliedWidth) return;
_lastAppliedWidth = width;
double scaleX = (double)width / BaseWidth;
double scaleY = (double)height / BaseHeight;
ApplyScale(Math.Min(scaleX, scaleY));
}
private void ApplyScale(double scale)
{
int fontSize = Math.Max(10, (int)(20 * scale));
int buttonPadV = Math.Max(4, (int)(12 * scale));
int buttonPadH = Math.Max(8, (int)(24 * scale));
int switchWidth = Math.Max(30, (int)(40 * scale));
int switchHeight= Math.Max(18, (int)(24 * scale));
// NOTE: integers + InvariantCulture. Interpolating a double here emitted
// "40,5px" under ja_JP / de_DE locales and GTK silently dropped the rule.
string css = string.Format(CultureInfo.InvariantCulture, @"
grid, label, button, switch {{
font-size: {0}px;
}}
button {{
padding: {1}px {2}px;
}}
switch {{
min-width: {3}px;
min-height: {4}px;
}}
", fontSize, buttonPadV, buttonPadH, switchWidth, switchHeight);
_scaleCssProvider.LoadFromData(css);
}
// ---------- lifecycle ----------
private void OnDeleteEvent(object sender, DeleteEventArgs a)
{
Shutdown();
a.RetVal = true;
}
private void OnCloseClicked(object sender, EventArgs e)
{
Shutdown();
}
private void Shutdown()
{
_arduino.Disconnect();
_arduino.Dispose();
// _settingsWindow?.Destroy();
Application.Quit();
}
private void OnNormalSessionClicked(object sender, EventArgs e)
{
Console.WriteLine("Normal Session selected");
}
private void OnPositionRecordingClicked(object sender, EventArgs e)
{
Console.WriteLine("Position Recording Mode selected");
}
private void OnExamModeClicked(object sender, EventArgs e)
{
Console.WriteLine("Exam Mode selected");
}
private void OnLanguageToggled(object sender, EventArgs e)
{
ApplyLanguageEverywhere(LanguageToggle.Active);
}
private void ApplyLanguageEverywhere(bool isEnglish)
{
Language.LanguageSwitchValue = isEnglish;
_Language.ApplyLanguage(isEnglish);
_settingsWindow?._Language?.ApplyLanguages(isEnglish);
}
}