Skip to content

Commit

Permalink
Migrate to Avalonia (#272)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyrrrz authored Mar 28, 2024
1 parent 1a5dfff commit cf8e53e
Show file tree
Hide file tree
Showing 103 changed files with 2,642 additions and 3,078 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ jobs:
-p:CSharpier_Bypass=true
--output LightBulb/bin/publish/
--configuration Release
--use-current-runtime
- name: Create installer
shell: pwsh
Expand Down
2 changes: 1 addition & 1 deletion Installer/Compile installer.bat
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
dotnet publish ../LightBulb/ -o Source/ --configuration Release
dotnet publish ../LightBulb/ -o Source/ --configuration Release --use-current-runtime
"c:\Program Files (x86)\Inno Setup 6\ISCC.exe" Installer.iss
13 changes: 7 additions & 6 deletions LightBulb.Core/SolarTimes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace LightBulb.Core;

// Times are presented in the current timezone, which is a flimsy convention
public readonly record struct SolarTimes(TimeOnly Sunrise, TimeOnly Sunset)
{
private static double DegreesToRadians(double degree) => degree * (Math.PI / 180);
Expand All @@ -11,7 +12,7 @@ public readonly record struct SolarTimes(TimeOnly Sunrise, TimeOnly Sunset)

private static TimeOnly CalculateSolarTime(
GeoLocation location,
DateTimeOffset date,
DateTimeOffset instant,
double zenith,
bool isSunrise
)
Expand All @@ -21,7 +22,7 @@ bool isSunrise
// Convert longitude to hour value and calculate an approximate time
var lngHours = location.Longitude / 15;
var timeApproxHours = isSunrise ? 6 : 18;
var timeApproxDays = date.DayOfYear + (timeApproxHours - lngHours) / 24;
var timeApproxDays = instant.DayOfYear + (timeApproxHours - lngHours) / 24;

// Calculate Sun's mean anomaly
var sunMeanAnomaly = 0.9856 * timeApproxDays - 3.289;
Expand Down Expand Up @@ -75,14 +76,14 @@ bool isSunrise

// Adjust UTC time to local time
// (we use the provided offset because it's impossible to calculate timezone from coordinates)
var localHours = (utcHours + date.Offset.TotalHours).Wrap(0, 24);
var localHours = (utcHours + instant.Offset.TotalHours).Wrap(0, 24);

return TimeOnly.FromTimeSpan(TimeSpan.FromHours(localHours));
}

public static SolarTimes Calculate(GeoLocation location, DateTimeOffset date) =>
public static SolarTimes Calculate(GeoLocation location, DateTimeOffset instant) =>
new(
CalculateSolarTime(location, date, 90.83, true),
CalculateSolarTime(location, date, 90.83, false)
CalculateSolarTime(location, instant, 90.83, true),
CalculateSolarTime(location, instant, 90.83, false)
);
}
2 changes: 1 addition & 1 deletion LightBulb.WindowsApi/DeviceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public partial class DeviceContext(nint handle) : NativeResource(handle)
private void SetGammaRamp(GammaRamp ramp)
{
if (!NativeMethods.SetDeviceGammaRamp(Handle, ref ramp))
Debug.WriteLine($"Failed to set gamma ramp on device context #${Handle}).");
Debug.WriteLine($"Failed to set gamma ramp on device context #{Handle}).");
}

public void SetGamma(double redMultiplier, double greenMultiplier, double blueMultiplier)
Expand Down
3 changes: 2 additions & 1 deletion LightBulb.WindowsApi/LightBulb.WindowsApi.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<!-- TODO: remove this and replace with P/Invoke bindings -->
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
Expand Down
136 changes: 136 additions & 0 deletions LightBulb/App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<Application
x:Class="LightBulb.App"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
xmlns:framework="clr-namespace:LightBulb.Framework"
xmlns:materialAssists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles"
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:materialStyles="clr-namespace:Material.Styles.Themes;assembly=Material.Styles">
<Application.DataTemplates>
<framework:ViewManager />
</Application.DataTemplates>

<Application.Styles>
<materialStyles:MaterialTheme />
<materialIcons:MaterialIconStyles />
<dialogHostAvalonia:DialogHostStyles />

<!-- Dialog host -->
<Style Selector="dialogHostAvalonia|DialogHost">
<Setter Property="DialogMargin" Value="0" />
</Style>

<!-- Radio button -->
<Style Selector="RadioButton">
<Setter Property="FontSize" Value="14" />
<Setter Property="materialAssists:SelectionControlAssist.Size" Value="18" />

<Style Selector="^:checked, ^:unchecked">
<Style Selector="^ Ellipse#PART_HoverEffect">
<Setter Property="Width" Value="32" />
<Setter Property="Height" Value="32" />
</Style>
</Style>
</Style>

<!-- Slider -->
<Style Selector="Slider">
<Style Selector="^ ProgressBar#PART_ProgressLayer">
<Style Selector="^:horizontal">
<Style Selector="^ Panel#PART_InnerPanel">
<Setter Property="Height" Value="2" />

<Style Selector="^ Border#PART_InactiveState">
<Setter Property="Margin" Value="0" />
<Setter Property="Height" Value="2" />
</Style>

<Style Selector="^ Border#PART_Indicator">
<Setter Property="Margin" Value="0" />
</Style>
</Style>
</Style>
</Style>

<Style Selector="^ Track#PART_Track">
<Style Selector="^:horizontal">
<Setter Property="Margin" Value="4,0" />
</Style>

<Style Selector="^ Border#PART_HoverEffect">
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
</Style>

<Style Selector="^ Border#PART_ThumbGrip">
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
</Style>
</Style>
</Style>

<!-- Text box -->
<Style Selector="TextBox">
<Setter Property="Height" Value="20" />
<Setter Property="FontSize" Value="14" />

<Style Selector="^ Panel#PART_TextFieldPanel">
<Setter Property="MinHeight" Value="0" />
</Style>

<Style Selector="^ Panel#PART_TextContainer">
<Setter Property="Margin" Value="0" />
</Style>
</Style>

<!-- Tooltip -->
<Style Selector="ToolTip">
<Setter Property="TextElement.FontSize" Value="14" />
<Setter Property="TextElement.FontWeight" Value="Normal" />
<Setter Property="TextElement.FontStyle" Value="Normal" />
<Setter Property="TextElement.FontStretch" Value="Normal" />
</Style>

<!-- Toggle switch -->
<Style Selector="ToggleSwitch">
<Setter Property="materialAssists:ToggleSwitchAssist.SwitchThumbOffBackground" Value="{DynamicResource MaterialBackgroundBrush}" />
</Style>
</Application.Styles>

<!-- Tray icon -->
<TrayIcon.Icons>
<TrayIcons>
<TrayIcon
Clicked="TrayIcon_OnClicked"
Icon="/favicon.ico"
ToolTipText="LightBulb">
<TrayIcon.Menu>
<NativeMenu>
<NativeMenuItem Click="TrayIcon_OnClicked" Header="Open" />
<NativeMenuItem Click="ShowSettingsMenuItem_OnClick" Header="Settings" />
<NativeMenuItemSeparator />

<NativeMenuItem Click="ToggleMenuItem_OnClick" Header="Toggle" />
<NativeMenuItem Header="Disable...">
<NativeMenu>
<NativeMenuItem Click="DisableUntilSunriseMenuItem_OnClick" Header="Until sunrise" />
<NativeMenuItem Click="DisableTemporarily1DayMenuItem_OnClick" Header="For 1 day" />
<NativeMenuItem Click="DisableTemporarily12HoursMenuItem_OnClick" Header="For 12 hours" />
<NativeMenuItem Click="DisableTemporarily6HoursMenuItem_OnClick" Header="For 6 hours" />
<NativeMenuItem Click="DisableTemporarily3HoursMenuItem_OnClick" Header="For 3 hours" />
<NativeMenuItem Click="DisableTemporarily1HourMenuItem_OnClick" Header="For 1 hour" />
<NativeMenuItem Click="DisableTemporarily30MinutesMenuItem_OnClick" Header="For 30 minutes" />
<NativeMenuItem Click="DisableTemporarily5MinutesMenuItem_OnClick" Header="For 5 minutes" />
<NativeMenuItem Click="DisableTemporarily1MinuteMenuItem_OnClick" Header="For 1 minute" />
</NativeMenu>
</NativeMenuItem>
<NativeMenuItemSeparator />

<NativeMenuItem Click="ExitMenuItem_OnClick" Header="Exit" />
</NativeMenu>
</TrayIcon.Menu>
</TrayIcon>
</TrayIcons>
</TrayIcon.Icons>
</Application>
132 changes: 132 additions & 0 deletions LightBulb/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using LightBulb.Framework;
using LightBulb.Services;
using LightBulb.Utils.Extensions;
using LightBulb.ViewModels;
using LightBulb.ViewModels.Components;
using LightBulb.ViewModels.Components.Settings;
using LightBulb.ViewModels.Dialogs;
using LightBulb.Views;
using Material.Styles.Themes;
using Microsoft.Extensions.DependencyInjection;

namespace LightBulb;

public class App : Application, IDisposable
{
private readonly ServiceProvider _services;
private readonly MainViewModel _mainViewModel;

public App()
{
var services = new ServiceCollection();

// Services
services.AddSingleton<ExternalApplicationService>();
services.AddSingleton<GammaService>();
services.AddSingleton<HotKeyService>();
services.AddSingleton<SettingsService>();
services.AddSingleton<UpdateService>();

// View model framework
services.AddSingleton<DialogManager>();
services.AddSingleton<ViewModelManager>();

// View models
services.AddTransient<MainViewModel>();
services.AddTransient<DashboardViewModel>();
services.AddTransient<MessageBoxViewModel>();
services.AddTransient<SettingsViewModel>();
services.AddTransient<SettingsTabViewModelBase, AdvancedSettingsTabViewModel>();
services.AddTransient<SettingsTabViewModelBase, ApplicationWhitelistSettingsTabViewModel>();
services.AddTransient<SettingsTabViewModelBase, GeneralSettingsTabViewModel>();
services.AddTransient<SettingsTabViewModelBase, HotKeySettingsTabViewModel>();
services.AddTransient<SettingsTabViewModelBase, LocationSettingsTabViewModel>();

// View framework
services.AddSingleton<ViewManager>();

_services = services.BuildServiceProvider(true);
_mainViewModel = _services.GetRequiredService<ViewModelManager>().CreateMainViewModel();
}

public override void Initialize() => AvaloniaXamlLoader.Load(this);

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
desktopLifetime.MainWindow = new MainView { DataContext = _mainViewModel };

base.OnFrameworkInitializationCompleted();

// Set custom theme colors
this.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = Theme.Create(
Theme.Light,
Color.Parse("#343838"),
Color.Parse("#F9A825")
);

// Finalize pending updates (and restart) before launching the app
_services.GetRequiredService<UpdateService>().FinalizePendingUpdates();

// Load settings
_services.GetRequiredService<SettingsService>().Load();
}

private void ShowMainWindow()
{
if (ApplicationLifetime?.TryGetMainWindow() is { } window)
{
window.Show();
window.Activate();
window.Focus();
}
}

private void TrayIcon_OnClicked(object? sender, EventArgs args) => ShowMainWindow();

private void ShowSettingsMenuItem_OnClick(object? sender, EventArgs args)
{
ShowMainWindow();
_mainViewModel.ShowSettingsCommand.Execute(null);
}

private void ToggleMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.IsEnabled = !_mainViewModel.Dashboard.IsEnabled;

private void DisableUntilSunriseMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableUntilSunriseCommand.Execute(null);

private void DisableTemporarily1DayMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromDays(1));

private void DisableTemporarily12HoursMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromHours(12));

private void DisableTemporarily6HoursMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromHours(6));

private void DisableTemporarily3HoursMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromHours(3));

private void DisableTemporarily1HourMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromHours(1));

private void DisableTemporarily30MinutesMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromMinutes(30));

private void DisableTemporarily5MinutesMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromMinutes(5));

private void DisableTemporarily1MinuteMenuItem_OnClick(object? sender, EventArgs args) =>
_mainViewModel.Dashboard.DisableTemporarilyCommand.Execute(TimeSpan.FromMinutes(1));

private void ExitMenuItem_OnClick(object? sender, EventArgs args) =>
ApplicationLifetime?.TryShutdown();

public void Dispose() => _services.Dispose();
}
Loading

0 comments on commit cf8e53e

Please sign in to comment.