mirror of
https://github.com/kaerys08/Pixerys-Tool.git
synced 2025-06-18 08:45:34 -04:00
Main: Source
This commit is contained in:
parent
5b6c93563c
commit
b8bbfb6e16
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.vs/
|
||||
Pixerys/bin/
|
||||
Pixerys/obj/
|
25
Pixerys.sln
Normal file
25
Pixerys.sln
Normal 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
21
Pixerys/App.axaml
Normal 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
28
Pixerys/App.axaml.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
28
Pixerys/Controls/FrameColorPicker.axaml
Normal file
28
Pixerys/Controls/FrameColorPicker.axaml
Normal 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>
|
7
Pixerys/Controls/FrameColorPicker.axaml.cs
Normal file
7
Pixerys/Controls/FrameColorPicker.axaml.cs
Normal file
@ -0,0 +1,7 @@
|
||||
using Avalonia.Controls.Primitives;
|
||||
|
||||
namespace Pixerys.Controls;
|
||||
|
||||
public class FrameColorPicker : TemplatedControl
|
||||
{
|
||||
}
|
44
Pixerys/Controls/RingColorPicker.axaml
Normal file
44
Pixerys/Controls/RingColorPicker.axaml
Normal 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>
|
215
Pixerys/Controls/RingColorPicker.axaml.cs
Normal file
215
Pixerys/Controls/RingColorPicker.axaml.cs
Normal 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;
|
||||
}
|
||||
}
|
56
Pixerys/Controls/SaturationColorPicker.axaml
Normal file
56
Pixerys/Controls/SaturationColorPicker.axaml
Normal 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>
|
124
Pixerys/Controls/SaturationColorPicker.axaml.cs
Normal file
124
Pixerys/Controls/SaturationColorPicker.axaml.cs
Normal 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;
|
||||
}
|
||||
}
|
51
Pixerys/Controls/SelectorColorPicker.axaml
Normal file
51
Pixerys/Controls/SelectorColorPicker.axaml
Normal 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>
|
59
Pixerys/Controls/SelectorColorPicker.axaml.cs
Normal file
59
Pixerys/Controls/SelectorColorPicker.axaml.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
119
Pixerys/Converters/AngleToRGB.cs
Normal file
119
Pixerys/Converters/AngleToRGB.cs
Normal 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
27
Pixerys/Pixerys.csproj
Normal 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
22
Pixerys/Program.cs
Normal 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();
|
||||
}
|
||||
}
|
107
Pixerys/UserControls/PaletteSelector.axaml
Normal file
107
Pixerys/UserControls/PaletteSelector.axaml
Normal 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>
|
13
Pixerys/UserControls/PaletteSelector.axaml.cs
Normal file
13
Pixerys/UserControls/PaletteSelector.axaml.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Pixerys;
|
||||
|
||||
public partial class PaletteSelector : UserControl
|
||||
{
|
||||
public PaletteSelector()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
24
Pixerys/ViewModels/MainWindowViewModel.cs
Normal file
24
Pixerys/ViewModels/MainWindowViewModel.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
31
Pixerys/ViewModels/PaletteSelectorViewModel.cs
Normal file
31
Pixerys/ViewModels/PaletteSelectorViewModel.cs
Normal 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;
|
||||
}
|
||||
}
|
9
Pixerys/ViewModels/ViewModelBase.cs
Normal file
9
Pixerys/ViewModels/ViewModelBase.cs
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Pixerys.ViewModels
|
||||
{
|
||||
internal class ViewModelBase : ObservableObject
|
||||
{
|
||||
}
|
||||
}
|
18
Pixerys/Views/MainWindow.axaml
Normal file
18
Pixerys/Views/MainWindow.axaml
Normal 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>
|
12
Pixerys/Views/MainWindow.axaml.cs
Normal file
12
Pixerys/Views/MainWindow.axaml.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Pixerys
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
18
Pixerys/app.manifest
Normal file
18
Pixerys/app.manifest
Normal 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>
|
Loading…
Reference in New Issue
Block a user