mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Workshop - Add --alt-login-callback startup argument
UI - Rounded corners on profile icons
This commit is contained in:
parent
06c5294e88
commit
5609065974
@ -6,7 +6,6 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.Documents;
|
using Avalonia.Controls.Documents;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
@ -43,7 +42,7 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable
|
|||||||
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon)
|
||||||
{
|
{
|
||||||
Content = Enum.TryParse(ConfigurationIcon.IconName, true, out MaterialIconKind parsedIcon)
|
Content = Enum.TryParse(ConfigurationIcon.IconName, true, out MaterialIconKind parsedIcon)
|
||||||
? new MaterialIcon {Kind = parsedIcon!}
|
? new MaterialIcon {Kind = parsedIcon}
|
||||||
: new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
: new MaterialIcon {Kind = MaterialIconKind.QuestionMark};
|
||||||
}
|
}
|
||||||
else if (ConfigurationIcon.IconBytes != null)
|
else if (ConfigurationIcon.IconBytes != null)
|
||||||
@ -65,19 +64,28 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_stream = new MemoryStream(ConfigurationIcon.IconBytes);
|
_stream = new MemoryStream(ConfigurationIcon.IconBytes);
|
||||||
if (!ConfigurationIcon.Fill)
|
Border border = new()
|
||||||
{
|
{
|
||||||
Content = new Image {Source = new Bitmap(_stream)};
|
CornerRadius = CornerRadius,
|
||||||
return;
|
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
|
Content = border;
|
||||||
{
|
|
||||||
Background = TextElement.GetForeground(this),
|
|
||||||
VerticalAlignment = VerticalAlignment.Stretch,
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
|
||||||
OpacityMask = new ImageBrush(new Bitmap(_stream))
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -89,7 +89,7 @@
|
|||||||
Background="{DynamicResource ControlFillColorDefaultBrush}"
|
Background="{DynamicResource ControlFillColorDefaultBrush}"
|
||||||
IsVisible="{CompiledBinding ProfileConfiguration, Converter={x:Static ObjectConverters.IsNotNull}}">
|
IsVisible="{CompiledBinding ProfileConfiguration, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
<StackPanel Orientation="Horizontal" Margin="8">
|
<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}" />
|
<TextBlock Text="{CompiledBinding ProfileConfiguration.Name}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@ -27,8 +27,9 @@ public partial class VisualEditorView : ReactiveUserControl<VisualEditorViewMode
|
|||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
ViewModel!.AutoFitRequested += ViewModelOnAutoFitRequested;
|
VisualEditorViewModel? viewModel = ViewModel;
|
||||||
Disposable.Create(() => ViewModel.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d);
|
viewModel!.AutoFitRequested += ViewModelOnAutoFitRequested;
|
||||||
|
Disposable.Create(() => viewModel.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.WhenAnyValue(v => v.Bounds).Where(_ => !_movedByUser).Subscribe(_ => AutoFit(true));
|
this.WhenAnyValue(v => v.Bounds).Where(_ => !_movedByUser).Subscribe(_ => AutoFit(true));
|
||||||
|
|||||||
@ -72,15 +72,20 @@
|
|||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
ContextFlyout="{StaticResource ProfileMenuFlyout}"
|
ContextFlyout="{StaticResource ProfileMenuFlyout}"
|
||||||
Classes.flyout-open="{CompiledBinding IsOpen, Source={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 Grid.Column="0"
|
||||||
<shared:ProfileConfigurationIcon x:Name="ProfileIcon" ConfigurationIcon="{CompiledBinding ProfileConfiguration.Icon}">
|
x:Name="ProfileIcon"
|
||||||
<shared:ProfileConfigurationIcon.Transitions>
|
VerticalAlignment="Center"
|
||||||
<Transitions>
|
ConfigurationIcon="{CompiledBinding ProfileConfiguration.Icon}"
|
||||||
<DoubleTransition Property="Opacity" Duration="0:0:0.2" />
|
Width="22"
|
||||||
</Transitions>
|
Height="22"
|
||||||
</shared:ProfileConfigurationIcon.Transitions>
|
CornerRadius="4"
|
||||||
</shared:ProfileConfigurationIcon>
|
Margin="0 0 5 0">
|
||||||
</Border>
|
<shared:ProfileConfigurationIcon.Transitions>
|
||||||
|
<Transitions>
|
||||||
|
<DoubleTransition Property="Opacity" Duration="0:0:0.2" />
|
||||||
|
</Transitions>
|
||||||
|
</shared:ProfileConfigurationIcon.Transitions>
|
||||||
|
</shared:ProfileConfigurationIcon>
|
||||||
|
|
||||||
<Panel Grid.Column="1" HorizontalAlignment="Left">
|
<Panel Grid.Column="1" HorizontalAlignment="Left">
|
||||||
<TextBlock Classes="fadable"
|
<TextBlock Classes="fadable"
|
||||||
|
|||||||
@ -80,7 +80,7 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
|||||||
await EnablePluginAndFeatures(result.Entry);
|
await EnablePluginAndFeatures(result.Entry);
|
||||||
// If the entry is a profile, move it to the General profile category
|
// If the entry is a profile, move it to the General profile category
|
||||||
else if (result.Entry?.EntryType == EntryType.Profile)
|
else if (result.Entry?.EntryType == EntryType.Profile)
|
||||||
MoveProfileToGeneral(result.Entry);
|
PrepareProfile(result.Entry);
|
||||||
|
|
||||||
return result.IsSuccess;
|
return result.IsSuccess;
|
||||||
}
|
}
|
||||||
@ -124,7 +124,7 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MoveProfileToGeneral(InstalledEntry entry)
|
private void PrepareProfile(InstalledEntry entry)
|
||||||
{
|
{
|
||||||
if (!entry.TryGetMetadata("ProfileId", out Guid profileId))
|
if (!entry.TryGetMetadata("ProfileId", out Guid profileId))
|
||||||
return;
|
return;
|
||||||
@ -132,12 +132,18 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
|
|||||||
ProfileConfiguration? profile = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == profileId);
|
ProfileConfiguration? profile = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == profileId);
|
||||||
if (profile == null)
|
if (profile == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == "General") ?? _profileService.CreateProfileCategory("General", true);
|
ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == "General") ?? _profileService.CreateProfileCategory("General", true);
|
||||||
if (category.ProfileConfigurations.Contains(profile))
|
if (category.ProfileConfigurations.Contains(profile))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
// Add the profile to the category
|
||||||
category.AddProfileConfiguration(profile, null);
|
category.AddProfileConfiguration(profile, null);
|
||||||
|
|
||||||
|
// Suspend all but the first profile in the category
|
||||||
|
profile.IsSuspended = category.ProfileConfigurations.Count > 1;
|
||||||
|
|
||||||
_profileService.SaveProfileCategory(category);
|
_profileService.SaveProfileCategory(category);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,6 +38,7 @@
|
|||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
ConfigurationIcon="{CompiledBinding Icon}"
|
ConfigurationIcon="{CompiledBinding Icon}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
CornerRadius="4"
|
||||||
Width="22"
|
Width="22"
|
||||||
Height="22"
|
Height="22"
|
||||||
Margin="0 0 10 0" />
|
Margin="0 0 10 0" />
|
||||||
|
|||||||
@ -180,13 +180,15 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
|
|||||||
{
|
{
|
||||||
await _authLock.WaitAsync(cancellationToken);
|
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
|
try
|
||||||
{
|
{
|
||||||
if (_isLoggedInSubject.Value)
|
if (_isLoggedInSubject.Value)
|
||||||
return;
|
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();
|
using HttpListener listener = new();
|
||||||
listener.Prefixes.Add(redirectUri + "/");
|
listener.Prefixes.Add(redirectUri + "/");
|
||||||
listener.Start();
|
listener.Start();
|
||||||
@ -249,7 +251,11 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
|
|||||||
}
|
}
|
||||||
catch (HttpListenerException e)
|
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
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user