mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Compare commits
3 Commits
108cbaae3d
...
7f5b677cc3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f5b677cc3 | ||
|
|
5609065974 | ||
|
|
06c5294e88 |
@ -157,10 +157,11 @@ internal class DeviceService : IDeviceService
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="leftHanded"></param>
|
||||
/// <inheritdoc />
|
||||
public void AutoArrangeDevices()
|
||||
public void AutoArrangeDevices(bool leftHanded)
|
||||
{
|
||||
SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
|
||||
SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement(leftHanded);
|
||||
surfaceArrangement.Arrange(_devices);
|
||||
foreach (ArtemisDevice artemisDevice in _devices)
|
||||
artemisDevice.ApplyDefaultCategories();
|
||||
|
||||
@ -46,7 +46,8 @@ public interface IDeviceService : IArtemisService
|
||||
/// <summary>
|
||||
/// Applies auto-arranging logic to the surface
|
||||
/// </summary>
|
||||
void AutoArrangeDevices();
|
||||
/// <param name="leftHanded"></param>
|
||||
void AutoArrangeDevices(bool leftHanded);
|
||||
|
||||
/// <summary>
|
||||
/// Apples the best available to the provided <see cref="ArtemisDevice" />
|
||||
|
||||
@ -48,22 +48,42 @@ internal class SurfaceArrangement
|
||||
}
|
||||
}
|
||||
|
||||
internal static SurfaceArrangement GetDefaultArrangement()
|
||||
internal static SurfaceArrangement GetDefaultArrangement(bool leftHanded)
|
||||
{
|
||||
SurfaceArrangement arrangement = new();
|
||||
|
||||
SurfaceArrangementType keypad = arrangement.AddType(RGBDeviceType.Keypad, 1);
|
||||
keypad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20));
|
||||
SurfaceArrangementType keyboard, keypad, mousepad, mouse;
|
||||
if (leftHanded)
|
||||
{
|
||||
mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
|
||||
mousepad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
|
||||
|
||||
SurfaceArrangementType keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1);
|
||||
keyboard.AddConfiguration(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20));
|
||||
mouse = arrangement.AddType(RGBDeviceType.Mouse, 2);
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 10));
|
||||
|
||||
SurfaceArrangementType mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
|
||||
mousepad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
|
||||
keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1);
|
||||
keyboard.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
|
||||
keyboard.AddConfiguration(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100));
|
||||
|
||||
SurfaceArrangementType mouse = arrangement.AddType(RGBDeviceType.Mouse, 2);
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 100));
|
||||
keypad = arrangement.AddType(RGBDeviceType.Keypad, 1);
|
||||
keypad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20));
|
||||
}
|
||||
else
|
||||
{
|
||||
keypad = arrangement.AddType(RGBDeviceType.Keypad, 1);
|
||||
keypad.AddConfiguration(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20));
|
||||
|
||||
keyboard = arrangement.AddType(RGBDeviceType.Keyboard, 1);
|
||||
keyboard.AddConfiguration(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20));
|
||||
|
||||
mousepad = arrangement.AddType(RGBDeviceType.Mousepad, 1);
|
||||
mousepad.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
|
||||
|
||||
mouse = arrangement.AddType(RGBDeviceType.Mouse, 2);
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
|
||||
mouse.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 100));
|
||||
}
|
||||
|
||||
SurfaceArrangementType headset = arrangement.AddType(RGBDeviceType.Headset, 1);
|
||||
headset.AddConfiguration(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Bottom, 100));
|
||||
|
||||
@ -32,7 +32,7 @@ internal class SurfaceArrangementConfiguration
|
||||
public int MarginBottom { get; }
|
||||
public SurfaceArrangement SurfaceArrangement { get; set; }
|
||||
|
||||
public bool Apply(List<ArtemisDevice> devices)
|
||||
public bool Apply(List<ArtemisDevice> devicesToArrange, List<ArtemisDevice> devices)
|
||||
{
|
||||
if (Anchor != null && !Anchor.HasDevices(devices))
|
||||
return false;
|
||||
@ -42,10 +42,10 @@ internal class SurfaceArrangementConfiguration
|
||||
new SurfaceArrangementType(SurfaceArrangement, RGBDeviceType.All, 1).GetEdge(HorizontalPosition, VerticalPosition);
|
||||
|
||||
// Stack multiple devices of the same type vertically if they are wider than they are tall
|
||||
bool stackVertically = devices.Average(d => d.RgbDevice.Size.Width) >= devices.Average(d => d.RgbDevice.Size.Height);
|
||||
bool stackVertically = devicesToArrange.Average(d => d.RgbDevice.Size.Width) >= devicesToArrange.Average(d => d.RgbDevice.Size.Height);
|
||||
|
||||
ArtemisDevice? previous = null;
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
foreach (ArtemisDevice artemisDevice in devicesToArrange)
|
||||
{
|
||||
if (previous != null)
|
||||
{
|
||||
|
||||
@ -28,18 +28,18 @@ internal class SurfaceArrangementType
|
||||
|
||||
public void Arrange(List<ArtemisDevice> devices)
|
||||
{
|
||||
devices = devices.Where(d => d.DeviceType == DeviceType).ToList();
|
||||
if (!devices.Any())
|
||||
List<ArtemisDevice> devicesToArrange = devices.Where(d => d.DeviceType == DeviceType).ToList();
|
||||
if (!devicesToArrange.Any())
|
||||
return;
|
||||
|
||||
AppliedConfiguration = null;
|
||||
foreach (SurfaceArrangementConfiguration configuration in Configurations)
|
||||
{
|
||||
bool applied = configuration.Apply(devices);
|
||||
bool applied = configuration.Apply(devicesToArrange, devices);
|
||||
if (applied)
|
||||
{
|
||||
AppliedConfiguration = configuration;
|
||||
foreach (ArtemisDevice artemisDevice in devices)
|
||||
foreach (ArtemisDevice artemisDevice in devicesToArrange)
|
||||
artemisDevice.ZIndex = ZIndex;
|
||||
return;
|
||||
}
|
||||
@ -52,7 +52,7 @@ internal class SurfaceArrangementType
|
||||
VerticalArrangementPosition.Equal,
|
||||
10
|
||||
) {SurfaceArrangement = SurfaceArrangement};
|
||||
fallback.Apply(devices);
|
||||
fallback.Apply(devicesToArrange, devices);
|
||||
AppliedConfiguration = fallback;
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
@ -43,7 +42,7 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable
|
||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||
{
|
||||
Content = Enum.TryParse(ConfigurationIcon.IconName, true, out MaterialIconKind parsedIcon)
|
||||
? new MaterialIcon {Kind = parsedIcon!}
|
||||
? new MaterialIcon {Kind = parsedIcon}
|
||||
: new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||
}
|
||||
else if (ConfigurationIcon.IconBytes != null)
|
||||
@ -65,19 +64,28 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable
|
||||
return;
|
||||
|
||||
_stream = new MemoryStream(ConfigurationIcon.IconBytes);
|
||||
if (!ConfigurationIcon.Fill)
|
||||
Border border = new()
|
||||
{
|
||||
Content = new Image {Source = new Bitmap(_stream)};
|
||||
return;
|
||||
CornerRadius = CornerRadius,
|
||||
ClipToBounds = true,
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||
|
||||
};
|
||||
|
||||
if (ConfigurationIcon.Fill)
|
||||
{
|
||||
// Fill mode: use Foreground as Background and the bitmap as opacity mask
|
||||
border.Background = TextElement.GetForeground(this);
|
||||
border.OpacityMask = new ImageBrush(new Bitmap(_stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-fill mode: place the image inside the rounded border
|
||||
border.Child = new Image { Source = new Bitmap(_stream) };
|
||||
}
|
||||
|
||||
Content = new Border
|
||||
{
|
||||
Background = TextElement.GetForeground(this),
|
||||
VerticalAlignment = VerticalAlignment.Stretch,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
OpacityMask = new ImageBrush(new Bitmap(_stream))
|
||||
};
|
||||
Content = border;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@ -185,12 +185,15 @@ internal class ProfileEditorService : IProfileEditorService
|
||||
{
|
||||
// Activate the profile if one was provided
|
||||
if (profileConfiguration != null)
|
||||
{
|
||||
_profileService.FocusProfile = profileConfiguration;
|
||||
_profileService.ActivateProfile(profileConfiguration);
|
||||
}
|
||||
|
||||
// If there is no profile configuration or module, deliberately set the override to null
|
||||
_moduleService.SetActivationOverride(profileConfiguration?.Module);
|
||||
});
|
||||
|
||||
_profileService.FocusProfile = profileConfiguration;
|
||||
_profileConfigurationSubject.OnNext(profileConfiguration);
|
||||
|
||||
ChangeTime(TimeSpan.Zero);
|
||||
|
||||
@ -21,11 +21,14 @@ public partial class DebugView : ReactiveAppWindow<DebugViewModel>
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Observable.FromEventPattern(x => ViewModel!.ActivationRequested += x, x => ViewModel!.ActivationRequested -= x).Subscribe(_ =>
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
Activate();
|
||||
}).DisposeWith(d);
|
||||
DebugViewModel vm = ViewModel!;
|
||||
Observable.FromEventPattern(x => vm.ActivationRequested += x, x => vm.ActivationRequested -= x)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
Activate();
|
||||
})
|
||||
.DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -19,14 +19,11 @@ public partial class PluginSettingsWindowView : ReactiveAppWindow<PluginSettings
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
Observable.FromEventPattern(
|
||||
x => ViewModel!.ConfigurationViewModel.CloseRequested += x,
|
||||
x => ViewModel!.ConfigurationViewModel.CloseRequested -= x
|
||||
)
|
||||
PluginSettingsWindowViewModel vm = ViewModel!;
|
||||
Observable.FromEventPattern(x => vm.ConfigurationViewModel.CloseRequested += x, x => vm.ConfigurationViewModel.CloseRequested -= x)
|
||||
.Subscribe(_ => Close())
|
||||
.DisposeWith(disposables);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -79,11 +79,6 @@ public partial class ProfileTreeViewModel : TreeItemViewModel
|
||||
|
||||
public override bool SupportsChildren => true;
|
||||
|
||||
public void UpdateCanPaste()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Task ExecuteDuplicate()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
Background="{DynamicResource ControlFillColorDefaultBrush}"
|
||||
IsVisible="{CompiledBinding ProfileConfiguration, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Orientation="Horizontal" Margin="8">
|
||||
<shared:ProfileConfigurationIcon ConfigurationIcon="{CompiledBinding ProfileConfiguration.Icon}" Width="18" Height="18" Margin="0 0 5 0" />
|
||||
<shared:ProfileConfigurationIcon ConfigurationIcon="{CompiledBinding ProfileConfiguration.Icon}" Width="18" Height="18" CornerRadius="3" Margin="0 0 5 0" />
|
||||
<TextBlock Text="{CompiledBinding ProfileConfiguration.Name}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@ -27,8 +27,9 @@ public partial class VisualEditorView : ReactiveUserControl<VisualEditorViewMode
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
ViewModel!.AutoFitRequested += ViewModelOnAutoFitRequested;
|
||||
Disposable.Create(() => ViewModel.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d);
|
||||
VisualEditorViewModel vm = ViewModel!;
|
||||
vm!.AutoFitRequested += ViewModelOnAutoFitRequested;
|
||||
Disposable.Create(() => vm.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d);
|
||||
});
|
||||
|
||||
this.WhenAnyValue(v => v.Bounds).Where(_ => !_movedByUser).Subscribe(_ => AutoFit(true));
|
||||
|
||||
@ -19,7 +19,8 @@ public partial class SplashView : ReactiveWindow<SplashViewModel>
|
||||
#endif
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
Observable.FromEventPattern(x => ViewModel!.CoreService.Initialized += x, x => ViewModel!.CoreService.Initialized -= x)
|
||||
SplashViewModel vm = ViewModel!;
|
||||
Observable.FromEventPattern(x => vm.CoreService.Initialized += x, x => vm.CoreService.Initialized -= x)
|
||||
.Subscribe(_ => Dispatcher.UIThread.Post(Close))
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
|
||||
@ -72,15 +72,20 @@
|
||||
Background="Transparent"
|
||||
ContextFlyout="{StaticResource ProfileMenuFlyout}"
|
||||
Classes.flyout-open="{CompiledBinding IsOpen, Source={StaticResource ProfileMenuFlyout}}">
|
||||
<Border CornerRadius="4" ClipToBounds="True" Grid.Column="0" Width="22" Height="22" Margin="0 0 5 0" VerticalAlignment="Center">
|
||||
<shared:ProfileConfigurationIcon x:Name="ProfileIcon" ConfigurationIcon="{CompiledBinding ProfileConfiguration.Icon}">
|
||||
<shared:ProfileConfigurationIcon.Transitions>
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Opacity" Duration="0:0:0.2" />
|
||||
</Transitions>
|
||||
</shared:ProfileConfigurationIcon.Transitions>
|
||||
</shared:ProfileConfigurationIcon>
|
||||
</Border>
|
||||
<shared:ProfileConfigurationIcon Grid.Column="0"
|
||||
x:Name="ProfileIcon"
|
||||
VerticalAlignment="Center"
|
||||
ConfigurationIcon="{CompiledBinding ProfileConfiguration.Icon}"
|
||||
Width="22"
|
||||
Height="22"
|
||||
CornerRadius="4"
|
||||
Margin="0 0 5 0">
|
||||
<shared:ProfileConfigurationIcon.Transitions>
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Opacity" Duration="0:0:0.2" />
|
||||
</Transitions>
|
||||
</shared:ProfileConfigurationIcon.Transitions>
|
||||
</shared:ProfileConfigurationIcon>
|
||||
|
||||
<Panel Grid.Column="1" HorizontalAlignment="Left">
|
||||
<TextBlock Classes="fadable"
|
||||
|
||||
@ -132,6 +132,6 @@ public class SidebarProfileConfigurationViewModel : ActivatableViewModelBase
|
||||
|
||||
public bool Matches(string s)
|
||||
{
|
||||
return s == $"profile/{ProfileConfiguration.ProfileId}/editor";
|
||||
return s.StartsWith($"profile/{ProfileConfiguration.ProfileId}");
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,7 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
||||
private readonly IWorkshopService _workshopService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||
private readonly Progress<StreamProgress> _progress = new();
|
||||
|
||||
@ -34,13 +35,19 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
||||
[Notify] private bool _shouldInstall;
|
||||
[Notify] private float _installProgress;
|
||||
|
||||
public DefaultEntryItemViewModel(ILogger logger, IEntrySummary entry, IWorkshopService workshopService, IWindowService windowService, IPluginManagementService pluginManagementService,
|
||||
public DefaultEntryItemViewModel(ILogger logger,
|
||||
IEntrySummary entry,
|
||||
IWorkshopService workshopService,
|
||||
IWindowService windowService,
|
||||
IPluginManagementService pluginManagementService,
|
||||
IProfileService profileService,
|
||||
ISettingsVmFactory settingsVmFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_workshopService = workshopService;
|
||||
_windowService = windowService;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
_profileService = profileService;
|
||||
_settingsVmFactory = settingsVmFactory;
|
||||
Entry = entry;
|
||||
|
||||
@ -62,19 +69,18 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
await _windowService.CreateContentDialog().WithTitle("Failed to install entry")
|
||||
await _windowService.CreateContentDialog()
|
||||
.WithTitle("Failed to install entry")
|
||||
.WithContent($"Failed to install entry '{Entry.Name}' ({Entry.Id}): {result.Message}")
|
||||
.WithCloseButtonText("Skip and continue")
|
||||
.ShowAsync();
|
||||
}
|
||||
// If the entry is a plugin, enable the plugin and all features
|
||||
else if (result.Entry?.EntryType == EntryType.Plugin)
|
||||
{
|
||||
await EnablePluginAndFeatures(result.Entry);
|
||||
} else if (result.Entry?.EntryType == EntryType.Profile)
|
||||
{
|
||||
|
||||
}
|
||||
// If the entry is a profile, move it to the General profile category
|
||||
else if (result.Entry?.EntryType == EntryType.Profile)
|
||||
PrepareProfile(result.Entry);
|
||||
|
||||
return result.IsSuccess;
|
||||
}
|
||||
@ -117,4 +123,27 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
||||
await _windowService.ShowDialogAsync(new PluginSettingsWindowViewModel(viewModel));
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareProfile(InstalledEntry entry)
|
||||
{
|
||||
if (!entry.TryGetMetadata("ProfileId", out Guid profileId))
|
||||
return;
|
||||
|
||||
ProfileConfiguration? profile = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == profileId);
|
||||
if (profile == null)
|
||||
return;
|
||||
|
||||
ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == "General") ?? _profileService.CreateProfileCategory("General", true);
|
||||
if (category.ProfileConfigurations.Contains(profile))
|
||||
return;
|
||||
|
||||
|
||||
// Add the profile to the category
|
||||
category.AddProfileConfiguration(profile, null);
|
||||
|
||||
// Suspend all but the first profile in the category
|
||||
profile.IsSuspended = category.ProfileConfigurations.Count > 1;
|
||||
|
||||
_profileService.SaveProfileCategory(category);
|
||||
}
|
||||
}
|
||||
@ -24,15 +24,14 @@
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0"
|
||||
Width="280"
|
||||
Height="280"
|
||||
IsEnabled="False">
|
||||
Height="280">
|
||||
<StackPanel>
|
||||
<avalonia:MaterialIcon Kind="HandBackLeft" Width="150" Height="150" HorizontalAlignment="Center" />
|
||||
<TextBlock TextAlignment="Center" Classes="h4" Margin="0 10 0 0">
|
||||
Left-handed preset (NYI)
|
||||
Left-handed preset
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" Classes="subtitle" TextWrapping="Wrap">
|
||||
A preset with the mouse on the left side of the keyboard
|
||||
A preset with the mouse on the left side of the keyboard (are you the 10%?)
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
@ -27,9 +27,7 @@ public class SurfaceStepViewModel : WizardStepViewModel
|
||||
|
||||
private void ExecuteSelectLayout(string layout)
|
||||
{
|
||||
// TODO: Implement the layout
|
||||
_deviceService.AutoArrangeDevices();
|
||||
|
||||
_deviceService.AutoArrangeDevices(layout == "left");
|
||||
Wizard.ChangeScreen<SettingsStepViewModel>();
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Routing;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Avalonia;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
using SkiaSharp;
|
||||
@ -180,11 +181,18 @@ public partial class SurfaceEditorViewModel : RoutableScreen, IMainScreenViewMod
|
||||
|
||||
private async Task ExecuteAutoArrange()
|
||||
{
|
||||
bool confirmed = await _windowService.ShowConfirmContentDialog("Auto-arrange layout", "Are you sure you want to auto-arrange your layout? Your current settings will be overwritten.");
|
||||
if (!confirmed)
|
||||
return;
|
||||
ContentDialogResult contentDialogResult = await _windowService.CreateContentDialog()
|
||||
.WithTitle("Auto-arrange layout")
|
||||
.WithContent("Which preset would you like to apply? Your current settings will be overwritten.")
|
||||
.HavingPrimaryButton(b => b.WithText("Left-handed preset"))
|
||||
.HavingSecondaryButton(b => b.WithText("Right-handed preset"))
|
||||
.WithCloseButtonText("Cancel")
|
||||
.ShowAsync();
|
||||
|
||||
_deviceService.AutoArrangeDevices();
|
||||
if (contentDialogResult == ContentDialogResult.Primary)
|
||||
_deviceService.AutoArrangeDevices(true);
|
||||
else if (contentDialogResult == ContentDialogResult.Secondary)
|
||||
_deviceService.AutoArrangeDevices(false);
|
||||
}
|
||||
|
||||
private void RenderServiceOnFrameRendering(object? sender, FrameRenderingEventArgs e)
|
||||
|
||||
@ -33,16 +33,17 @@ public partial class NodeScriptView : ReactiveUserControl<NodeScriptViewModel>
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
ViewModel!.AutoFitRequested += ViewModelOnAutoFitRequested;
|
||||
ViewModel.PickerPositionSubject.Subscribe(ShowPickerAt).DisposeWith(d);
|
||||
if (ViewModel.IsPreview)
|
||||
NodeScriptViewModel vm = ViewModel!;
|
||||
vm.AutoFitRequested += ViewModelOnAutoFitRequested;
|
||||
vm.PickerPositionSubject.Subscribe(ShowPickerAt).DisposeWith(d);
|
||||
if (vm.IsPreview)
|
||||
{
|
||||
BoundsProperty.Changed.Subscribe(BoundsPropertyChanged).DisposeWith(d);
|
||||
ViewModel.NodeViewModels.ToObservableChangeSet().Subscribe(_ => AutoFitIfPreview()).DisposeWith(d);
|
||||
vm.NodeViewModels.ToObservableChangeSet().Subscribe(_ => AutoFitIfPreview()).DisposeWith(d);
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() => AutoFit(true), DispatcherPriority.ContextIdle);
|
||||
Disposable.Create(() => ViewModel.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d);
|
||||
Disposable.Create(() => vm.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
Grid.Column="0"
|
||||
ConfigurationIcon="{CompiledBinding Icon}"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadius="4"
|
||||
Width="22"
|
||||
Height="22"
|
||||
Margin="0 0 10 0" />
|
||||
|
||||
@ -180,13 +180,15 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
|
||||
{
|
||||
await _authLock.WaitAsync(cancellationToken);
|
||||
|
||||
// Start a HTTP listener, this port could be in use but chances are very slim
|
||||
// IdentityServer only accepts these two redirect URLs
|
||||
string redirectUri = Constants.StartupArguments.Contains("--alt-login-callback") ? "http://localhost:56789" : "http://localhost:57461";
|
||||
|
||||
try
|
||||
{
|
||||
if (_isLoggedInSubject.Value)
|
||||
return;
|
||||
|
||||
// Start a HTTP listener, this port could be in use but chances are very slim
|
||||
string redirectUri = "http://localhost:57461";
|
||||
using HttpListener listener = new();
|
||||
listener.Prefixes.Add(redirectUri + "/");
|
||||
listener.Start();
|
||||
@ -249,7 +251,11 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
|
||||
}
|
||||
catch (HttpListenerException e)
|
||||
{
|
||||
throw new ArtemisWebClientException($"HTTP listener for login callback failed with error code {e.ErrorCode}", e);
|
||||
// I've seen the Nvidia app do this after a login. What are the odds...
|
||||
if (e.ErrorCode == 32)
|
||||
throw new ArtemisWebClientException($"HTTP listener for login callback failed because another application is already listening on '{redirectUri}', please close that application and try again", e);
|
||||
else
|
||||
throw new ArtemisWebClientException($"HTTP listener for login callback failed with error code {e.ErrorCode}", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user