feat: autocompletions

This commit is contained in:
2026-03-11 14:28:43 +01:00
parent da8ce47f8b
commit a0de93f98c
13 changed files with 7054 additions and 8 deletions

View File

@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using CarManagerV3.Util;
namespace CarManagerV3
{
@@ -29,6 +30,39 @@ namespace CarManagerV3
{
lblID.Text = $"ID: {car.Id}";
}
SetAutoCompleteForMake();
SetAutoCompleteForModel();
SetAutoCompleteForColor();
}
private void SetAutoCompleteForMake()
{
var makes = CarCompletions.GetCarBrands();
var makeAutoComplete = new AutoCompleteStringCollection();
makeAutoComplete.AddRange(makes.ToArray());
tbxMake.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
tbxMake.AutoCompleteSource = AutoCompleteSource.CustomSource;
tbxMake.AutoCompleteCustomSource = makeAutoComplete;
}
private void SetAutoCompleteForModel()
{
var models = CarCompletions.GetCarModels(tbxMake.Text);
var modelAutoComplete = new AutoCompleteStringCollection();
modelAutoComplete.AddRange(models.ToArray());
tbxModel.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
tbxModel.AutoCompleteSource = AutoCompleteSource.CustomSource;
tbxModel.AutoCompleteCustomSource = modelAutoComplete;
}
private void SetAutoCompleteForColor()
{
var colors = CarCompletions.CommonColors;
var colorAutoComplete = new AutoCompleteStringCollection();
colorAutoComplete.AddRange(colors.ToArray());
tbxColor.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
tbxColor.AutoCompleteSource = AutoCompleteSource.CustomSource;
tbxColor.AutoCompleteCustomSource = colorAutoComplete;
}
/// <summary>
@@ -69,6 +103,7 @@ namespace CarManagerV3
() => car.Make = ValueOrFormer(tbxMake.Text, car.Make),
() => tbxMake.Text = car.Make
);
SetAutoCompleteForModel();
}
private void tbxModel_TextChanged(object sender, EventArgs e)
@@ -163,7 +198,8 @@ namespace CarManagerV3
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Enter)
// Ctrl+S to save, Esc to close, Delete to delete
if (keyData == (Keys.Control | Keys.S))
{
btnSave.PerformClick();
return true; // Indicate that the key has been handled

View File

@@ -86,6 +86,8 @@ namespace CarManagerV3
Console.Error.WriteLine("Error checking for updates: " + ex.Message);
}
CarCompletions.UpdateCarCompletionDataAsync();
}

View File

@@ -36,9 +36,15 @@
btnAccept = new System.Windows.Forms.Button();
btnDiscard = new System.Windows.Forms.Button();
tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
lblCompletionsRefreshWindow = new System.Windows.Forms.Label();
lblDataLocation = new System.Windows.Forms.Label();
tbxDataLocation = new System.Windows.Forms.TextBox();
cbxPreRelease = new System.Windows.Forms.CheckBox();
flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel();
nudCompletionIntervalHours = new System.Windows.Forms.NumericUpDown();
label3 = new System.Windows.Forms.Label();
nudCompletionIntervalMinutes = new System.Windows.Forms.NumericUpDown();
label4 = new System.Windows.Forms.Label();
tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
btnReset = new System.Windows.Forms.Button();
flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel();
@@ -46,6 +52,9 @@
flowLayoutPanel1.SuspendLayout();
flowLayoutPanel2.SuspendLayout();
tableLayoutPanel1.SuspendLayout();
flowLayoutPanel4.SuspendLayout();
((System.ComponentModel.ISupportInitialize)nudCompletionIntervalHours).BeginInit();
((System.ComponentModel.ISupportInitialize)nudCompletionIntervalMinutes).BeginInit();
tableLayoutPanel2.SuspendLayout();
flowLayoutPanel3.SuspendLayout();
SuspendLayout();
@@ -121,27 +130,46 @@
tableLayoutPanel1.ColumnCount = 2;
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
tableLayoutPanel1.Controls.Add(lblCompletionsRefreshWindow, 0, 2);
tableLayoutPanel1.Controls.Add(lblDataLocation, 0, 0);
tableLayoutPanel1.Controls.Add(tbxDataLocation, 1, 0);
tableLayoutPanel1.Controls.Add(cbxPreRelease, 1, 1);
tableLayoutPanel1.Controls.Add(cbxPreRelease, 0, 1);
tableLayoutPanel1.Controls.Add(flowLayoutPanel4, 1, 2);
tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
tableLayoutPanel1.Location = new System.Drawing.Point(0, 86);
tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0);
tableLayoutPanel1.RowCount = 3;
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
tableLayoutPanel1.RowCount = 7;
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 0F));
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
tableLayoutPanel1.Size = new System.Drawing.Size(499, 531);
tableLayoutPanel1.TabIndex = 3;
//
// lblCompletionsRefreshWindow
//
lblCompletionsRefreshWindow.Anchor = System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
lblCompletionsRefreshWindow.AutoSize = true;
lblCompletionsRefreshWindow.Location = new System.Drawing.Point(13, 66);
lblCompletionsRefreshWindow.Margin = new System.Windows.Forms.Padding(3);
lblCompletionsRefreshWindow.MaximumSize = new System.Drawing.Size(150, 0);
lblCompletionsRefreshWindow.Name = "lblCompletionsRefreshWindow";
lblCompletionsRefreshWindow.Size = new System.Drawing.Size(148, 1);
lblCompletionsRefreshWindow.TabIndex = 4;
lblCompletionsRefreshWindow.Text = "Refresh completions after";
lblCompletionsRefreshWindow.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// lblDataLocation
//
lblDataLocation.Anchor = System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
lblDataLocation.AutoSize = true;
lblDataLocation.Location = new System.Drawing.Point(13, 6);
lblDataLocation.Name = "lblDataLocation";
lblDataLocation.Size = new System.Drawing.Size(102, 20);
lblDataLocation.Size = new System.Drawing.Size(148, 20);
lblDataLocation.TabIndex = 0;
lblDataLocation.Text = "Data Location";
lblDataLocation.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@@ -149,9 +177,9 @@
// tbxDataLocation
//
tbxDataLocation.Dock = System.Windows.Forms.DockStyle.Fill;
tbxDataLocation.Location = new System.Drawing.Point(121, 3);
tbxDataLocation.Location = new System.Drawing.Point(167, 3);
tbxDataLocation.Name = "tbxDataLocation";
tbxDataLocation.Size = new System.Drawing.Size(365, 27);
tbxDataLocation.Size = new System.Drawing.Size(319, 27);
tbxDataLocation.TabIndex = 1;
//
// cbxPreRelease
@@ -166,6 +194,61 @@
cbxPreRelease.Text = "Pre-Release channel";
cbxPreRelease.UseVisualStyleBackColor = true;
//
// flowLayoutPanel4
//
flowLayoutPanel4.AutoSize = true;
flowLayoutPanel4.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
flowLayoutPanel4.Controls.Add(nudCompletionIntervalHours);
flowLayoutPanel4.Controls.Add(label3);
flowLayoutPanel4.Controls.Add(nudCompletionIntervalMinutes);
flowLayoutPanel4.Controls.Add(label4);
flowLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill;
flowLayoutPanel4.Location = new System.Drawing.Point(164, 66);
flowLayoutPanel4.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
flowLayoutPanel4.Name = "flowLayoutPanel4";
flowLayoutPanel4.Size = new System.Drawing.Size(325, 1);
flowLayoutPanel4.TabIndex = 5;
//
// nudCompletionIntervalHours
//
nudCompletionIntervalHours.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
nudCompletionIntervalHours.Location = new System.Drawing.Point(3, 3);
nudCompletionIntervalHours.Name = "nudCompletionIntervalHours";
nudCompletionIntervalHours.Size = new System.Drawing.Size(44, 27);
nudCompletionIntervalHours.TabIndex = 0;
nudCompletionIntervalHours.UpDownAlign = System.Windows.Forms.LeftRightAlignment.Left;
//
// label3
//
label3.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom;
label3.AutoSize = true;
label3.Location = new System.Drawing.Point(53, 0);
label3.Name = "label3";
label3.Size = new System.Drawing.Size(24, 33);
label3.TabIndex = 1;
label3.Text = "h :";
label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// nudCompletionIntervalMinutes
//
nudCompletionIntervalMinutes.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
nudCompletionIntervalMinutes.Location = new System.Drawing.Point(83, 3);
nudCompletionIntervalMinutes.Name = "nudCompletionIntervalMinutes";
nudCompletionIntervalMinutes.Size = new System.Drawing.Size(44, 27);
nudCompletionIntervalMinutes.TabIndex = 2;
nudCompletionIntervalMinutes.UpDownAlign = System.Windows.Forms.LeftRightAlignment.Left;
//
// label4
//
label4.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom;
label4.AutoSize = true;
label4.Location = new System.Drawing.Point(133, 0);
label4.Name = "label4";
label4.Size = new System.Drawing.Size(22, 33);
label4.TabIndex = 3;
label4.Text = "m";
label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// tableLayoutPanel2
//
tableLayoutPanel2.AutoSize = true;
@@ -240,6 +323,10 @@
flowLayoutPanel2.ResumeLayout(false);
tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout();
flowLayoutPanel4.ResumeLayout(false);
flowLayoutPanel4.PerformLayout();
((System.ComponentModel.ISupportInitialize)nudCompletionIntervalHours).EndInit();
((System.ComponentModel.ISupportInitialize)nudCompletionIntervalMinutes).EndInit();
tableLayoutPanel2.ResumeLayout(false);
tableLayoutPanel2.PerformLayout();
flowLayoutPanel3.ResumeLayout(false);
@@ -264,5 +351,11 @@
private System.Windows.Forms.CheckBox cbxPreRelease;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3;
private System.Windows.Forms.Label lblEnv;
private System.Windows.Forms.Label lblCompletionsRefreshWindow;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4;
private System.Windows.Forms.NumericUpDown nudCompletionIntervalHours;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.NumericUpDown nudCompletionIntervalMinutes;
private System.Windows.Forms.Label label4;
}
}

View File

@@ -43,6 +43,9 @@ namespace CarManagerV3.Forms
read: () => Properties.Settings.Default.AllowPrerelease,
write: v => Properties.Settings.Default.AllowPrerelease = v);
//TODO: implement refresh settings
lblEnv.Text = lblEnv.Text.Replace("%E%", InstallModeDetector.IsInstalledViaMsi() ? "Installed via MSI" : "Running in portable mode");
}
@@ -60,10 +63,33 @@ namespace CarManagerV3.Forms
private void saveSettings()
{
bool requiresRestart = false;
foreach (var kvp in settingsMap)
{
kvp.Value.Save();
if (kvp.Value.RequiresRestart())
requiresRestart = true;
}
Properties.Settings.Default.Save();
if(requiresRestart)
{
DialogResult result = MessageBox.Show(
"Some changes you made require a restart to take effect. Do you want to restart now?",
"Restart Required",
MessageBoxButtons.YesNo,
MessageBoxIcon.Information);
if (result == DialogResult.Yes)
{
Application.Restart();
} else
{
MessageBox.Show("Please restart the application as soon as possible to ensure all changes take effect.", "Restart Recommended", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
private void resetSettings()
@@ -106,6 +132,7 @@ namespace CarManagerV3.Forms
void Load();
void Save();
void Reset();
bool RequiresRestart();
}
internal sealed class SettingBinding<T> : ISettingBinding
@@ -115,14 +142,18 @@ namespace CarManagerV3.Forms
private readonly Func<T> read;
private readonly Action<T> write;
private readonly Action<T, T>? onChange;
private readonly bool requiresRestart;
private bool changeRequiresRestart;
public SettingBinding(Control control, T defaultValue, Func<T> read, Action<T> write, Action<T, T>? onChange = null)
public SettingBinding(Control control, T defaultValue, Func<T> read, Action<T> write, bool requiresRestart = false, Action<T, T>? onChange = null)
{
this.control = control;
this.defaultValue = defaultValue;
this.read = read;
this.write = write;
this.onChange = onChange;
this.requiresRestart = requiresRestart;
this.changeRequiresRestart = false;
}
public void Load()
@@ -140,10 +171,15 @@ namespace CarManagerV3.Forms
if (onChange != null && !EqualityComparer<T>.Default.Equals(before, after))
onChange(before, after);
if (requiresRestart && !EqualityComparer<T>.Default.Equals(before, after))
changeRequiresRestart = true;
}
public void Reset() => write(defaultValue);
public bool RequiresRestart() => changeRequiresRestart;
private void ApplyToControl(T value)
{
switch (control)