Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 73 additions & 111 deletions README.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/Spectron/Controls/MainMenu.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<converters:ValueEqualityConverter x:Key="ValueEqualityConverter"/>
<converters:GreaterThanConverter x:Key="GreaterThanConverter"/>
<converters:BoolToTextConverter x:Key="BoolToTextConverter"/>
<converters:EnumHasValueConverter x:Key="EnumHasValueConverter"/>
</UserControl.Resources>

<UserControl.Styles>
Expand Down Expand Up @@ -123,6 +124,11 @@
<MenuItem Header="Large" ToggleType="Radio" Command="{Binding ChangeBorderSizeCommand}" CommandParameter="{x:Static screen:BorderSize.Large}" IsChecked="{Binding BorderSize, Converter={StaticResource ValueEqualityConverter}, ConverterParameter={x:Static screen:BorderSize.Large}}"/>
<MenuItem Header="Full" ToggleType="Radio" Command="{Binding ChangeBorderSizeCommand}" CommandParameter="{x:Static screen:BorderSize.Full}" IsChecked="{Binding BorderSize, Converter={StaticResource ValueEqualityConverter}, ConverterParameter={x:Static screen:BorderSize.Full}}"/>
</MenuItem>
<MenuItem Header="Effects">
<MenuItem Header="Blur" ToggleType="CheckBox" Command="{Binding ChangeScreenEffectCommand}" CommandParameter="{x:Static screen:ScreenEffect.Blur}" IsChecked="{Binding ScreenEffect, Converter={StaticResource EnumHasValueConverter}, ConverterParameter={x:Static screen:ScreenEffect.Blur}}"/>
<MenuItem Header="CRT" ToggleType="CheckBox" Command="{Binding ChangeScreenEffectCommand}" CommandParameter="{x:Static screen:ScreenEffect.Crt}" IsChecked="{Binding ScreenEffect, Converter={StaticResource EnumHasValueConverter}, ConverterParameter={x:Static screen:ScreenEffect.Crt}}"/>
</MenuItem>
<MenuItem Header="-"/>
<MenuItem Header="Trainers" Command="{Binding ShowTrainersCommand}"/>
<MenuItem Header="Print Output" Command="{Binding ShowPrintOutputCommand}"/>
<MenuItem Header="-"/>
Expand Down
42 changes: 42 additions & 0 deletions src/Spectron/Controls/NativeMainMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public sealed class NativeMainMenu
private readonly Dictionary<MouseType, NativeMenuItem> _mouseTypes = new();
private readonly Dictionary<string, NativeMenuItem> _emulationSpeeds = new();
private readonly Dictionary<BorderSize, NativeMenuItem> _borderSizes = new();
private readonly Dictionary<ScreenEffect, NativeMenuItem> _screenEffects = new();
private readonly Dictionary<TapeSpeed, NativeMenuItem> _tapeLoadingSpeeds = new();
private readonly Dictionary<MicrodriveId, NativeMenuItem> _microdrives = new();
private readonly Dictionary<DriveId, NativeMenuItem> _diskDrives = new();
Expand All @@ -49,6 +50,7 @@ public NativeMainMenu(MainWindowViewModel viewModel)
CreateMouseTypeMenu();
CreateSpeedOptionMenu();
CreateBorderSizeMenu();
CreateScreenEffectMenu();
CreateTapeLoadingSpeedMenu();

_viewModel.PropertyChanged += (_, e) => ViewModelPropertyChanged(e.PropertyName);
Expand Down Expand Up @@ -145,6 +147,14 @@ private void ViewModelPropertyChanged(string? propertyName)

break;

case nameof(MainWindowViewModel.ScreenEffect):
foreach (var screenEffect in _screenEffects.Keys)
{
_screenEffects[screenEffect].IsChecked = _viewModel.ScreenEffect.HasFlag(screenEffect);
}

break;

case nameof(MainWindowViewModel.IsFullScreen):
_fullScreenMenuItem?.Header = _viewModel.IsFullScreen ? "Exit Full Screen" : "Full Screen";

Expand Down Expand Up @@ -460,6 +470,17 @@ private NativeMenuItem CreateViewMenu()
]
},

new NativeMenuItem("Effect")
{
Menu =
[
_screenEffects[ScreenEffect.Blur],
_screenEffects[ScreenEffect.Crt],
]
},

new NativeMenuItemSeparator(),

new NativeMenuItem("Trainers")
{
Command = _viewModel.ShowTrainersCommand,
Expand Down Expand Up @@ -923,6 +944,27 @@ private void CreateBorderSizeMenu()
}
}

private void CreateScreenEffectMenu()
{
var effects = new[]
{
new { Effect = ScreenEffect.Blur, DisplayName = "Blur" },
new { Effect = ScreenEffect.Crt, DisplayName = "CRT" },
};

foreach (var effect in effects)
{
_screenEffects[effect.Effect] = new NativeMenuItem(effect.DisplayName)
{
ToggleType = NativeMenuItemToggleType.CheckBox,
Command = _viewModel.ChangeScreenEffectCommand,
CommandParameter = effect.Effect,
IsChecked = _viewModel.ScreenEffect.HasFlag(effect.Effect),
IsEnabled = true
};
}
}

private void CreateTapeLoadingSpeedMenu()
{
var speeds = new[]
Expand Down
21 changes: 21 additions & 0 deletions src/Spectron/Converters/EnumHasValueConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;

namespace OldBit.Spectron.Converters;

public class EnumHasValueConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is not Enum enumValue || parameter is not Enum parameterValue)
{
return false;
}

return enumValue.HasFlag(parameterValue);
}

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
throw new NotImplementedException();
}
11 changes: 11 additions & 0 deletions src/Spectron/Screen/ScreenEffect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace OldBit.Spectron.Screen;

[Flags]
public enum ScreenEffect
{
None = 0x00,
Blur = 0x01,
Crt = 0x02,
}
2 changes: 2 additions & 0 deletions src/Spectron/Settings/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class Preferences

public BorderSize BorderSize { get; set; } = BorderSize.Medium;

public ScreenEffect ScreenEffect { get; set; } = ScreenEffect.None;

public ComputerType ComputerType { get; init; } = ComputerType.Spectrum48K;

public RomType RomType { get; init; } = RomType.Original;
Expand Down
12 changes: 12 additions & 0 deletions src/Spectron/ViewModels/MainWindowViewModel.Display.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ private void HandleChangeBorderSize(BorderSize borderSize)
SpectrumScreen = _frameBufferConverter.ScreenBitmap;
}

private void HandleChangeScreenEffect(ScreenEffect screenEffect)
{
if (ScreenEffect.HasFlag(screenEffect))
{
ScreenEffect &= ~screenEffect;
}
else
{
ScreenEffect |= screenEffect;
}
}

private void HandleToggleFullScreen() =>
WindowState = WindowState == WindowState.FullScreen ? WindowState.Normal : WindowState.FullScreen;

Expand Down
2 changes: 2 additions & 0 deletions src/Spectron/ViewModels/MainWindowViewModel.Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ private async Task WindowOpenedAsync()

ConfigureShiftKeys(_preferences.Keyboard);
HandleChangeBorderSize(CommandLineArgs?.BorderSize ?? _preferences.BorderSize);
HandleChangeScreenEffect(_preferences.ScreenEffect);

TapeLoadSpeed = CommandLineArgs?.TapeLoadSpeed ?? _preferences.Tape.LoadSpeed;

Expand Down Expand Up @@ -191,6 +192,7 @@ private async Task WindowClosingAsync(WindowClosingEventArgs args)

_preferences.Audio.IsMuted = IsAudioMuted;
_preferences.BorderSize = BorderSize;
_preferences.ScreenEffect = ScreenEffect;

await Task.WhenAll(
_preferencesService.SaveAsync(_preferences),
Expand Down
6 changes: 6 additions & 0 deletions src/Spectron/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
[ObservableProperty]
public partial BorderSize BorderSize { get; set; } = BorderSize.Medium;

[ObservableProperty]
public partial ScreenEffect ScreenEffect { get; set; } = ScreenEffect.None;

[ObservableProperty]
public partial RomType RomType { get; set; } = RomType.Original;

Expand Down Expand Up @@ -298,6 +301,9 @@
[RelayCommand]
private void ChangeBorderSize(BorderSize borderSize) => HandleChangeBorderSize(borderSize);

[RelayCommand]
private void ChangeScreenEffect(ScreenEffect screenEffect) => HandleChangeScreenEffect(screenEffect);

[RelayCommand]
private void ShowTrainers() => OpenTrainersWindow();

Expand All @@ -306,7 +312,7 @@

// Favorites
[RelayCommand]
private void ShowFavoritesView() => OpenFavoritesWindow();

Check warning on line 315 in src/Spectron/ViewModels/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Check warning on line 315 in src/Spectron/ViewModels/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

// Tape
[RelayCommand]
Expand Down
23 changes: 22 additions & 1 deletion src/Spectron/Views/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
xmlns:viewModels="clr-namespace:OldBit.Spectron.ViewModels"
xmlns:converters="clr-namespace:OldBit.Spectron.Converters"
xmlns:controls="clr-namespace:OldBit.Spectron.Controls"
xmlns:screen="clr-namespace:OldBit.Spectron.Screen"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="OldBit.Spectron.Views.MainWindow"
x:DataType="viewModels:MainWindowViewModel"
Expand Down Expand Up @@ -35,6 +36,8 @@

<Window.Resources>
<converters:ValueInequalityConverter x:Key="ValueInequalityConverter"/>
<converters:ValueEqualityConverter x:Key="ValueEqualityConverter"/>
<converters:EnumHasValueConverter x:Key="EnumHasValueConverter"/>
</Window.Resources>

<Window.KeyBindings>
Expand Down Expand Up @@ -91,15 +94,33 @@
PointerMoved="Screen_OnPointerMoved"
PointerPressed="Screen_OnPointerPressed"
PointerReleased="Screen_OnPointerReleased"
Classes.IsPaused="{Binding IsPauseOverlayVisible}">
Classes.IsPaused="{Binding IsPauseOverlayVisible}"
Classes.Blur="{Binding ScreenEffect, Converter={StaticResource EnumHasValueConverter}, ConverterParameter={x:Static screen:ScreenEffect.Blur}}">
<Image.Styles>
<Style Selector="Image.Blur">
<Setter Property="Effect">
<BlurEffect Radius="3"/>
</Setter>
</Style>
<Style Selector="Image.IsPaused">
<Setter Property="Effect">
<BlurEffect Radius="7"/>
</Setter>
</Style>
</Image.Styles>
</Image>

<Border IsVisible="{Binding ScreenEffect, Converter={StaticResource EnumHasValueConverter}, ConverterParameter={x:Static screen:ScreenEffect.Crt}}">
<Border.Background>
<VisualBrush TileMode="Tile" SourceRect="0,0,1,2" DestinationRect="0,0,1,2">
<VisualBrush.Visual>
<Canvas Width="1" Height="2">
<Rectangle Width="1" Height="1" Fill="Black" Opacity=".3"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</Grid>
</Border>

Expand Down
Loading