Main: Source

This commit is contained in:
Kaerys Diaz 2025-05-02 18:20:31 -06:00
parent 5b6c93563c
commit b8bbfb6e16
23 changed files with 1061 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.vs/
Pixerys/bin/
Pixerys/obj/

25
Pixerys.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35913.81 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pixerys", "Pixerys\Pixerys.csproj", "{5E93EA21-7256-486B-89D9-D2B744C67C72}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5E93EA21-7256-486B-89D9-D2B744C67C72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E93EA21-7256-486B-89D9-D2B744C67C72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E93EA21-7256-486B-89D9-D2B744C67C72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E93EA21-7256-486B-89D9-D2B744C67C72}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {45B69ACE-620D-40CC-B414-84CCF3F216B9}
EndGlobalSection
EndGlobal

21
Pixerys/App.axaml Normal file
View File

@ -0,0 +1,21 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Pixerys.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://Pixerys/Controls/FrameColorPicker.axaml"/>
<ResourceInclude Source="avares://Pixerys/Controls/RingColorPicker.axaml"/>
<ResourceInclude Source="avares://Pixerys/Controls/SaturationColorPicker.axaml"/>
<ResourceInclude Source="avares://Pixerys/Controls/SelectorColorPicker.axaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

28
Pixerys/App.axaml.cs Normal file
View File

@ -0,0 +1,28 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Pixerys.ViewModels;
namespace Pixerys
{
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow()
{
DataContext = new PaletteSelectorViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
}
}

View File

@ -0,0 +1,28 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Pixerys.Controls">
<!--
Additional resources
Using Control Themes:
https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes
Using Theme Variants:
https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-theme-variants
-->
<Design.PreviewWith>
<StackPanel Width="400" Spacing="10">
<StackPanel Background="{DynamicResource SystemRegionBrush}">
<controls:FrameColorPicker />
</StackPanel>
</StackPanel>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type controls:FrameColorPicker}" TargetType="controls:FrameColorPicker">
<Setter Property="Template">
<ControlTemplate>
<TextBlock Text="Templated Control" />
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@ -0,0 +1,7 @@
using Avalonia.Controls.Primitives;
namespace Pixerys.Controls;
public class FrameColorPicker : TemplatedControl
{
}

View File

@ -0,0 +1,44 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Pixerys.Controls">
<!--
Additional resources
Using Control Themes:
https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes
Using Theme Variants:
https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-theme-variants
-->
<Design.PreviewWith>
<Canvas
Width="100"
Height="100">
<controls:RingColorPicker/>
</Canvas>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type controls:RingColorPicker}" TargetType="controls:RingColorPicker">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Show" Value="True"/>
<Setter Property="Template">
<ControlTemplate>
<Canvas
Background="{TemplateBinding Background}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<Ellipse
x:Name="PART_Selector"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}"
IsVisible="{TemplateBinding Show}"/>
</Canvas>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@ -0,0 +1,215 @@
using Avalonia.Controls.Metadata;
using Avalonia;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Data;
using Avalonia.Media;
using System;
namespace Pixerys.Controls;
[TemplatePart("PART_Selector", typeof(Ellipse))]
public class RingColorPicker : TemplatedControl
{
Ellipse? selector;
private double centerX;
private double centerY;
private bool IsToggled = false;
public static readonly DirectProperty<RingColorPicker, bool> ShowProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, bool>(
nameof(Show),
o => o.Show,
(o, v) => o.Show = v,
defaultBindingMode: BindingMode.TwoWay);
private bool _show;
public bool Show
{
get { return _show; }
set { SetAndRaise(ShowProperty, ref _show, value); }
}
public static readonly DirectProperty<RingColorPicker, double> PosXProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, double>(
nameof(PosX),
o => o.PosX,
(o, v) => o.PosX = v,
defaultBindingMode: BindingMode.TwoWay);
private double _posX;
public double PosX
{
get { return _posX; }
set { SetAndRaise(PosXProperty, ref _posX, value); }
}
public static readonly DirectProperty<RingColorPicker, double> PosYProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, double>(
nameof(PosY),
o => o.PosY,
(o, v) => o.PosY = v,
defaultBindingMode: BindingMode.TwoWay);
private double _posY;
public double PosY
{
get { return _posY; }
set { SetAndRaise(PosYProperty, ref _posY, value); }
}
public static readonly DirectProperty<RingColorPicker, double> SelectorAngleProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, double>(
nameof(SelectorAngle),
o => o.SelectorAngle,
(o, v) => o.SelectorAngle = v,
defaultBindingMode: BindingMode.TwoWay);
private double _selectorAngle;
public double SelectorAngle
{
get { return _selectorAngle; }
set { SetAndRaise(SelectorAngleProperty, ref _selectorAngle, value); }
}
public static readonly DirectProperty<RingColorPicker, double> SnappedAngleProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, double>(
nameof(SnappedAngle),
o => o.SnappedAngle,
(o, v) => o.SnappedAngle = v,
defaultBindingMode: BindingMode.TwoWay);
private double _snappedAngle;
public double SnappedAngle
{
get { return _selectorAngle; }
set { SetAndRaise(SnappedAngleProperty, ref _snappedAngle, value); }
}
public static readonly DirectProperty<RingColorPicker, Color> GetColorProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, Color>(
nameof(GetColor),
o => o.GetColor,
(o, v) => o.GetColor = v,
defaultBindingMode: BindingMode.TwoWay);
private Color _getColor;
public Color GetColor
{
get { return _getColor; }
set { SetAndRaise(GetColorProperty, ref _getColor, value); }
}
public static readonly DirectProperty<RingColorPicker, byte> RComponentProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, byte>(
nameof(RComponent),
o => o.RComponent,
defaultBindingMode: BindingMode.OneWayToSource);
private byte _rComponent = 0;
public byte RComponent
{
get { return _rComponent; }
private set { SetAndRaise(RComponentProperty, ref _rComponent, value); }
}
public static readonly DirectProperty<RingColorPicker, byte> GComponentProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, byte>(
nameof(GComponent),
o => o.GComponent,
defaultBindingMode: BindingMode.OneWayToSource);
private byte _gComponent = 0;
public byte GComponent
{
get { return _gComponent; }
private set { SetAndRaise(GComponentProperty, ref _gComponent, value); }
}
public static readonly DirectProperty<RingColorPicker, byte> BComponentProperty =
AvaloniaProperty.RegisterDirect<RingColorPicker, byte>(
nameof(BComponent),
o => o.BComponent,
defaultBindingMode: BindingMode.OneWayToSource);
private byte _bComponent = 0;
public byte BComponent
{
get { return _bComponent; }
private set { SetAndRaise(BComponentProperty, ref _bComponent, value); }
}
private readonly ConicGradientBrush RingRGBConicGradientBrush = new()
{
GradientStops =
[
new GradientStop(new Color(255,255,255,0), 0.0),
new GradientStop(new Color(255,0,255,0), 0.165),
new GradientStop(new Color(255,0,255,255), 0.33),
new GradientStop(new Color(255,0,0,255), 0.495),
new GradientStop(new Color(255,255,0,255), 0.66),
new GradientStop(new Color(255,255,0,0), 0.825),
new GradientStop(new Color(255,255,255,0), 1.0),
],
};
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
selector = e.NameScope.Find("PART_Selector") as Ellipse;
if (selector != null)
{
selector.Stroke = RingRGBConicGradientBrush;
selector.IsVisible = Show;
selector.StrokeThickness = selector.Width / 10.5;
selector.PointerPressed += Selector_PointerPressed;
selector.PointerMoved += Selector_PointerMoved;
selector.PointerReleased += Selector_PointerReleased;
centerX = (selector.Width / 2);
centerY = (selector.Height / 2);
SelectorAngle = 89;
RComponent = (byte)(GetColor.R >> 3);
GComponent = (byte)(GetColor.G >> 3);
BComponent = (byte)(GetColor.B >> 3);
}
}
private void Selector_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
{
IsToggled = true;
}
private void Selector_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e)
{
Point pos = e.GetPosition(this);
double angle = Math.Atan2(pos.Y - centerY, pos.X - centerY) * (180 / Math.PI);
if (IsToggled == true)
{
PosX = pos.X - centerX;
PosY = pos.Y - centerY;
SelectorAngle = 360 - (180 - angle);
RComponent = (byte)(GetColor.R >> 3);
GComponent = (byte)(GetColor.G >> 3);
BComponent = (byte)(GetColor.B >> 3);
}
}
private void Selector_PointerReleased(object? sender, Avalonia.Input.PointerReleasedEventArgs e)
{
IsToggled = false;
}
}

View File

@ -0,0 +1,56 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Pixerys.Controls">
<!--
Additional resources
Using Control Themes:
https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes
Using Theme Variants:
https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-theme-variants
-->
<Design.PreviewWith>
<controls:SaturationColorPicker
Width="86"
Height="86"
Background="Red"/>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type controls:SaturationColorPicker}" TargetType="controls:SaturationColorPicker">
<Setter Property="Template">
<ControlTemplate>
<Canvas
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<ContentControl
x:Name="PART_SaturationPicker"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}"/>
<Rectangle
x:Name="PART_SaturationPickerWhite"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding LinearGradientWhite}"
IsHitTestVisible="False">
</Rectangle>
<Rectangle
x:Name="PART_SaturationPickerBlack"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding LinearGradientBlack}"
IsHitTestVisible="False">
</Rectangle>
<controls:SelectorColorPicker
Width="16"
Height="16"
Background="Black"
Canvas.Left="-8"
Canvas.Top="-8"/>
</Canvas>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@ -0,0 +1,124 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Media;
namespace Pixerys.Controls;
[TemplatePart("PART_SaturationPicker", typeof(ContentControl))]
public class SaturationColorPicker : TemplatedControl
{
ContentControl? saturationPicker;
private bool IsToggled = false;
public static readonly DirectProperty<SaturationColorPicker, double> PosXProperty =
AvaloniaProperty.RegisterDirect<SaturationColorPicker, double>(
nameof(PosX),
o => o.PosX,
(o, v) => o.PosX = v,
defaultBindingMode: BindingMode.TwoWay);
private double _posX;
public double PosX
{
get { return _posX; }
set { SetAndRaise(PosXProperty, ref _posX, value); }
}
public static readonly DirectProperty<SaturationColorPicker, double> PosYProperty =
AvaloniaProperty.RegisterDirect<SaturationColorPicker, double>(
nameof(PosY),
o => o.PosY,
(o, v) => o.PosY = v,
defaultBindingMode: BindingMode.TwoWay);
private double _posY;
public double PosY
{
get { return _posY; }
set { SetAndRaise(PosYProperty, ref _posY, value); }
}
public static readonly DirectProperty<SaturationColorPicker, LinearGradientBrush> LinearGradientBlackProperty =
AvaloniaProperty.RegisterDirect<SaturationColorPicker, LinearGradientBrush>(
nameof(LinearGradientBlack),
o => o.LinearGradientBlack);
private LinearGradientBrush _linearGradientBlack = new()
{
StartPoint = new RelativePoint(new Point(0, 1), RelativeUnit.Relative),
EndPoint = new RelativePoint(new Point(0, 0), RelativeUnit.Relative),
GradientStops = [
new GradientStop(new Color(255, 0, 0, 0), 0.0),
new GradientStop(new Color(0, 0, 0, 0), 1.0),
],
};
public LinearGradientBrush LinearGradientBlack
{
get { return _linearGradientBlack; }
set { SetAndRaise(LinearGradientBlackProperty, ref _linearGradientBlack, value); }
}
public static readonly DirectProperty<SaturationColorPicker, LinearGradientBrush> LinearGradientWhiteProperty =
AvaloniaProperty.RegisterDirect<SaturationColorPicker, LinearGradientBrush>(
nameof(LinearGradientWhite),
o => o.LinearGradientWhite);
private LinearGradientBrush _linearGradientWhite = new()
{
StartPoint = new RelativePoint(new Point(0, 0), RelativeUnit.Relative),
EndPoint = new RelativePoint(new Point(1, 0), RelativeUnit.Relative),
GradientStops = [
new GradientStop(new Color(255, 255, 255, 255), 0.0),
new GradientStop(new Color(0, 255, 255, 255), 1.0),
],
};
public LinearGradientBrush LinearGradientWhite
{
get { return _linearGradientWhite; }
set { SetAndRaise(LinearGradientWhiteProperty, ref _linearGradientWhite, value); }
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
saturationPicker = e.NameScope.Find("PART_SaturationPicker") as ContentControl;
if (saturationPicker != null)
{
saturationPicker.PointerPressed += SaturationPicker_PointerPressed;
saturationPicker.PointerMoved += SaturationPicker_PointerMoved;
saturationPicker.PointerReleased += SaturationPicker_PointerReleased;
}
}
private void SaturationPicker_PointerReleased(object? sender, Avalonia.Input.PointerReleasedEventArgs e)
{
IsToggled = false;
}
private void SaturationPicker_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e)
{
Point pos = e.GetPosition(saturationPicker);
if (IsToggled == true)
{
PosX = pos.X;
PosY = pos.Y;
}
}
private void SaturationPicker_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
{
IsToggled = true;
}
}

View File

@ -0,0 +1,51 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Pixerys.Controls">
<!--
Additional resources
Using Control Themes:
https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes
Using Theme Variants:
https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-theme-variants
-->
<Design.PreviewWith>
<Canvas
Width="200"
Height="200">
<controls:SelectorColorPicker
Width="80"
Height="80"
Canvas.Left="10"
Canvas.Top="100"
Background="Pink"/>
<Rectangle
Width="86"
Height="86"
Fill="Red"
Canvas.Left="43"
Canvas.Top="43"/>
</Canvas>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type controls:SelectorColorPicker}" TargetType="controls:SelectorColorPicker">
<Setter Property="Template">
<ControlTemplate>
<Canvas
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="Transparent">
<Ellipse
x:Name="PART_SelectorPresenter"
StrokeThickness="2"
Stroke="{TemplateBinding Background}"
IsHitTestVisible="False"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
</Ellipse>
</Canvas>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@ -0,0 +1,59 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Data;
using Avalonia.Media;
using Avalonia.Metadata;
namespace Pixerys.Controls;
[TemplatePart("PART_SelectorPresenter", typeof(Ellipse))]
public class SelectorColorPicker : TemplatedControl
{
Ellipse? selectorPresenter;
public static readonly DirectProperty<SelectorColorPicker, double> CenterXProperty =
AvaloniaProperty.RegisterDirect<SelectorColorPicker, double>(
nameof(CenterX),
o => o.CenterX,
(o, v) => o.CenterX = v,
defaultBindingMode: BindingMode.TwoWay);
private double _centerX;
public double CenterX
{
get { return _centerX; }
set { SetAndRaise(CenterXProperty, ref _centerX, value); }
}
public static readonly DirectProperty<SelectorColorPicker, double> CenterYProperty =
AvaloniaProperty.RegisterDirect<SelectorColorPicker, double>(
nameof(CenterY),
o => o.CenterY,
(o, v) => o.CenterY = v,
defaultBindingMode: BindingMode.TwoWay);
private double _centerY;
public double CenterY
{
get { return _centerY; }
set { SetAndRaise(CenterYProperty, ref _centerY, value); }
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
selectorPresenter = e.NameScope.Find("PART_SelectorPresenter") as Ellipse;
if (selectorPresenter != null)
{
CenterX = selectorPresenter.Width / 2;
CenterY = selectorPresenter.Height / 2;
selectorPresenter.StrokeThickness = selectorPresenter.Width * 0.2;
}
}
}

View File

@ -0,0 +1,119 @@
using Avalonia.Data.Converters;
using Avalonia.Media;
using System;
using System.Globalization;
namespace Pixerys.Converters
{
internal class AngleToRGB : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if(value is double inputAngle)
{
const double baseAngle = 0.9375;
const double step = 1.875;
const int maxIndex = 191;
int index = (int)Math.Round((inputAngle - baseAngle) / step);
index = Math.Clamp(index, 0, maxIndex);
double snappedAngle = baseAngle + index * step;
byte r = 0;
byte g = 0;
byte b = 0;
switch (index)
{
case < 15:
r = 31;
b = (byte)(15 - index);
g = 0;
break;
case 15:
r = 31;
b = 0;
g = 0;
break;
case < 47:
r = 31;
g = (byte)(index - 15);
b = 0;
break;
case 47:
r = 31;
g = 31;
b = 0;
break;
case < 78:
r = (byte)(78 - index);
g = 31;
b = 0;
break;
case 78:
r = 0;
g = 31;
b = 0;
break;
case < 109:
r = 0;
g = 31;
b = (byte)(index - 78);
break;
case 109:
r = 0;
g = 31;
b = 31;
break;
case < 140:
r = 0;
g = (byte)(140 - index);
b = 31;
break;
case 140:
r = 0;
g = 0;
b = 31;
break;
case < 171:
r = (byte)(index - 140);
g = 0;
b = 31;
break;
case 171:
r = 31;
g = 0;
b = 31;
break;
case < 192:
r = 31;
g = 0;
b = (byte)(202 - index);
break;
}
if(targetType == typeof(double))
{
return snappedAngle;
}
if(targetType == typeof(string))
{
return snappedAngle;
}
if(targetType == typeof(IBrush) || targetType == typeof(Brush))
{
return new SolidColorBrush(Color.FromRgb((byte)(r << 3), (byte)(g << 3), (byte)(b << 3)));
}
if(targetType == typeof(Color))
{
return new Color(255, (byte)(r << 3), (byte)(g << 3), (byte)(b << 3));
}
}
return 0;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

27
Pixerys/Pixerys.csproj Normal file
View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.1" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.1">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\" />
</ItemGroup>
</Project>

22
Pixerys/Program.cs Normal file
View File

@ -0,0 +1,22 @@
using System;
using Avalonia;
namespace Pixerys
{
internal class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
}

View File

@ -0,0 +1,107 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Pixerys.Controls"
xmlns:converters="using:Pixerys.Converters"
xmlns:viewmodel="using:Pixerys.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Pixerys.PaletteSelector"
x:DataType="viewmodel:PaletteSelectorViewModel">
<Design.DataContext>
<viewmodel:PaletteSelectorViewModel/>
</Design.DataContext>
<UserControl.Resources>
<converters:AngleToRGB x:Key="AngleToRGB"/>
</UserControl.Resources>
<Canvas
Width="200"
Height="400">
<Ellipse
x:Name="PART_ExternalFrame"
Stroke="Gray"
StrokeThickness="1"
Height="176"
Width="176"/>
<controls:RingColorPicker
PosX="{Binding PosX}"
PosY="{Binding PosY}"
SelectorAngle="{Binding SelectorAngle}"
GetColor="{Binding SelectorAngle,
Converter={StaticResource AngleToRGB}}"
RComponent="{Binding RComponent}"
GComponent="{Binding GComponent}"
BComponent="{Binding BComponent}"
Background="Transparent"
Width="168"
Height="168"
Canvas.Left="4"
Canvas.Top="4"/>
<controls:SelectorColorPicker
Width="16"
Height="16"
Canvas.Top="88"
Canvas.Left="4"
Background="Silver"
IsHitTestVisible="False">
<controls:SelectorColorPicker.RenderTransform>
<RotateTransform
CenterX="76"
CenterY="-8"
Angle="{Binding SelectorAngle,
Converter={StaticResource AngleToRGB}}">
</RotateTransform>
</controls:SelectorColorPicker.RenderTransform>
</controls:SelectorColorPicker>
<Label
Content="{Binding PosXSaturation}"
Foreground="Black"
FontSize="16"
Canvas.Top="150"/>
<Label
Content="{Binding PosYSaturation}"
Foreground="Black"
FontSize="16"
Canvas.Top="170"/>
<Label
Content="{Binding SelectorAngle,
Converter={StaticResource AngleToRGB}}"
Foreground="Black"
FontSize="16"
FontWeight="Bold"
Canvas.Top="190"/>
<Label
Content="{Binding RComponent}"
Foreground="Black"
FontSize="16"
Canvas.Top="210"/>
<Label
Content="{Binding GComponent}"
Foreground="Black"
FontSize="16"
Canvas.Top="230"/>
<Label
Content="{Binding BComponent}"
Foreground="Black"
FontSize="16"
Canvas.Top="250"/>
<controls:SelectorColorPicker
Width="16"
Height="16"
Background="Black"
Canvas.Left="{Binding PosXSaturation}"
Canvas.Top="{Binding PosYSaturation}"/>
<controls:SaturationColorPicker
Width="86"
Height="86"
Background="{Binding SelectorAngle,
Converter={StaticResource AngleToRGB}}"
PosX="{Binding PosXSaturation}"
PosY="{Binding PosYSaturation}"
Canvas.Left="45"
Canvas.Top="45"/>
</Canvas>
</UserControl>

View File

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Pixerys;
public partial class PaletteSelector : UserControl
{
public PaletteSelector()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,24 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Pixerys.ViewModels
{
internal partial class MainWindowViewModel : ViewModelBase
{
[ObservableProperty]
private string _name = "Kaerys";
[RelayCommand]
public void ButtonOnClick()
{
Name = "Kaerys Diaz";
}
}
}

View File

@ -0,0 +1,31 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Pixerys.ViewModels
{
internal partial class PaletteSelectorViewModel : ViewModelBase
{
[ObservableProperty]
private double _posX = 10;
[ObservableProperty]
private double _posY = 8;
[ObservableProperty]
private double _selectorAngle = 0.9375;
[ObservableProperty]
private double _posXSaturation = 0;
[ObservableProperty]
private double _posYSaturation = 0;
[ObservableProperty]
private byte _rComponent;
[ObservableProperty]
private byte _gComponent;
[ObservableProperty]
private byte _bComponent;
}
}

View File

@ -0,0 +1,9 @@

using CommunityToolkit.Mvvm.ComponentModel;
namespace Pixerys.ViewModels
{
internal class ViewModelBase : ObservableObject
{
}
}

View File

@ -0,0 +1,18 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="using:Pixerys.ViewModels"
xmlns:controls="using:Pixerys.Controls"
xmlns:usercontrol="using:Pixerys"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Pixerys.MainWindow"
Title="Pixerys">
<Design.DataContext>
<viewmodel:PaletteSelectorViewModel/>
</Design.DataContext>
<usercontrol:PaletteSelector
x:DataType="viewmodel:PaletteSelectorViewModel"/>
</Window>

View File

@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace Pixerys
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

18
Pixerys/app.manifest Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="Pixerys.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>