Add startup application support and dialog for user prompts

This commit is contained in:
2026-01-07 09:47:22 +11:00
parent dafeeb0f15
commit 97bf98f99e
4 changed files with 250 additions and 0 deletions

View File

@@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>

View File

@@ -36,6 +36,12 @@ public class DevDriveConfig
/// </summary> /// </summary>
[JsonPropertyName("paths")] [JsonPropertyName("paths")]
public List<SymlinkEntry> Paths { get; set; } = []; public List<SymlinkEntry> Paths { get; set; } = [];
/// <summary>
/// List of applications to prompt for launch after mounting.
/// </summary>
[JsonPropertyName("startupApps")]
public List<StartupApp> StartupApps { get; set; } = [];
} }
/// <summary> /// <summary>
@@ -55,3 +61,39 @@ public class SymlinkEntry
[JsonPropertyName("path")] [JsonPropertyName("path")]
public string Path { get; set; } = string.Empty; public string Path { get; set; } = string.Empty;
} }
/// <summary>
/// Represents an application to launch after mounting.
/// </summary>
public class StartupApp
{
/// <summary>
/// Display name shown in the dialog.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
/// <summary>
/// Path to the executable.
/// </summary>
[JsonPropertyName("path")]
public string Path { get; set; } = string.Empty;
/// <summary>
/// Command line arguments (optional).
/// </summary>
[JsonPropertyName("arguments")]
public string Arguments { get; set; } = string.Empty;
/// <summary>
/// Working directory (optional, defaults to executable's directory).
/// </summary>
[JsonPropertyName("workingDirectory")]
public string WorkingDirectory { get; set; } = string.Empty;
/// <summary>
/// Whether this app is checked by default in the dialog.
/// </summary>
[JsonPropertyName("checkedByDefault")]
public bool CheckedByDefault { get; set; } = true;
}

View File

@@ -301,6 +301,12 @@ public static class Program
MountDrive(); MountDrive();
SyncSymlinksFromConfig(config); SyncSymlinksFromConfig(config);
_lastConfig = config; _lastConfig = config;
if (config.StartupApps.Count > 0)
{
var driveName = config.DriveName ?? "Dev Drive";
Task.Run(() => StartupDialog.ShowIfNeeded(config.StartupApps, driveName));
}
} }
} }
} }

201
StartupDialog.cs Normal file
View File

@@ -0,0 +1,201 @@
using System.Diagnostics;
using System.Runtime.Versioning;
namespace DevDrive;
/// <summary>
/// Dialog that prompts the user to select which startup apps to launch.
/// </summary>
[SupportedOSPlatform("windows")]
public class StartupDialog : Form
{
private readonly List<StartupApp> _apps;
private readonly List<CheckBox> _checkboxes = [];
private readonly Button _launchButton;
private readonly Button _cancelButton;
/// <summary>
/// Creates a new startup dialog for the given apps.
/// </summary>
/// <param name="apps">List of startup apps to display.</param>
/// <param name="driveName">Name of the drive for the title.</param>
public StartupDialog(List<StartupApp> apps, string driveName)
{
_apps = apps;
Text = $"{driveName} - Startup Apps";
FormBorderStyle = FormBorderStyle.FixedDialog;
StartPosition = FormStartPosition.CenterScreen;
MaximizeBox = false;
MinimizeBox = false;
AutoScaleMode = AutoScaleMode.Dpi;
var padding = 16;
var checkboxHeight = 28;
var buttonHeight = 32;
var buttonWidth = 100;
var contentHeight = padding + (_apps.Count * checkboxHeight) + padding + buttonHeight + padding;
var contentWidth = 350;
ClientSize = new Size(contentWidth, contentHeight);
var yPos = padding;
foreach (var app in _apps)
{
var checkbox = new CheckBox
{
Text = app.Name,
Tag = app,
Checked = app.CheckedByDefault,
Location = new Point(padding, yPos),
Size = new Size(contentWidth - (padding * 2), checkboxHeight),
Font = new Font("Segoe UI", 10)
};
_checkboxes.Add(checkbox);
Controls.Add(checkbox);
yPos += checkboxHeight;
}
yPos += padding;
_launchButton = new Button
{
Text = "Launch",
DialogResult = DialogResult.OK,
Location = new Point(contentWidth - padding - buttonWidth - 8 - buttonWidth, yPos),
Size = new Size(buttonWidth, buttonHeight),
Font = new Font("Segoe UI", 9)
};
_launchButton.Click += OnLaunchClick;
Controls.Add(_launchButton);
_cancelButton = new Button
{
Text = "Skip",
DialogResult = DialogResult.Cancel,
Location = new Point(contentWidth - padding - buttonWidth, yPos),
Size = new Size(buttonWidth, buttonHeight),
Font = new Font("Segoe UI", 9)
};
Controls.Add(_cancelButton);
AcceptButton = _launchButton;
CancelButton = _cancelButton;
}
private void OnLaunchClick(object? sender, EventArgs e)
{
LaunchSelectedApps();
}
/// <summary>
/// Launches all selected apps.
/// </summary>
private void LaunchSelectedApps()
{
foreach (var checkbox in _checkboxes)
{
if (!checkbox.Checked)
{
continue;
}
if (checkbox.Tag is not StartupApp app)
{
continue;
}
try
{
var startInfo = new ProcessStartInfo
{
FileName = Environment.ExpandEnvironmentVariables(app.Path),
UseShellExecute = true
};
if (!string.IsNullOrWhiteSpace(app.Arguments))
{
startInfo.Arguments = Environment.ExpandEnvironmentVariables(app.Arguments);
}
if (!string.IsNullOrWhiteSpace(app.WorkingDirectory))
{
startInfo.WorkingDirectory = Environment.ExpandEnvironmentVariables(app.WorkingDirectory);
}
Process.Start(startInfo);
Console.WriteLine($"[DevDrive] Launched: {app.Name}");
}
catch (Exception ex)
{
Console.WriteLine($"[DevDrive] Failed to launch {app.Name}: {ex.Message}");
}
}
}
/// <summary>
/// Shows the startup dialog if there are apps to display.
/// </summary>
/// <param name="apps">List of startup apps.</param>
/// <param name="driveName">Name of the drive.</param>
public static void ShowIfNeeded(List<StartupApp> apps, string driveName)
{
if (apps.Count == 0)
{
return;
}
if (apps.Count == 1)
{
var app = apps[0];
var result = MessageBox.Show(
$"Launch {app.Name}?",
$"{driveName} - Startup",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question
);
if (result == DialogResult.Yes)
{
LaunchSingleApp(app);
}
return;
}
Application.EnableVisualStyles();
using var dialog = new StartupDialog(apps, driveName);
dialog.ShowDialog();
}
private static void LaunchSingleApp(StartupApp app)
{
try
{
var startInfo = new ProcessStartInfo
{
FileName = Environment.ExpandEnvironmentVariables(app.Path),
UseShellExecute = true
};
if (!string.IsNullOrWhiteSpace(app.Arguments))
{
startInfo.Arguments = Environment.ExpandEnvironmentVariables(app.Arguments);
}
if (!string.IsNullOrWhiteSpace(app.WorkingDirectory))
{
startInfo.WorkingDirectory = Environment.ExpandEnvironmentVariables(app.WorkingDirectory);
}
Process.Start(startInfo);
Console.WriteLine($"[DevDrive] Launched: {app.Name}");
}
catch (Exception ex)
{
Console.WriteLine($"[DevDrive] Failed to launch {app.Name}: {ex.Message}");
}
}
}