diff --git a/src/HexManiac.Core/ViewModels/Tools/PaletteCollection.cs b/src/HexManiac.Core/ViewModels/Tools/PaletteCollection.cs index c2e19c66..9ba7f230 100644 --- a/src/HexManiac.Core/ViewModels/Tools/PaletteCollection.cs +++ b/src/HexManiac.Core/ViewModels/Tools/PaletteCollection.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; +using System.IO.Packaging; using System.Linq; using System.Windows.Input; @@ -45,6 +46,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { var first = Math.Min(selectionStart, selectionEnd); var last = Math.Max(selectionStart, selectionEnd); for (int i = 0; i < Elements.Count; i++) Elements[i].Selected = first <= i && i <= last; + createGradient.CanExecuteChanged.Invoke(createGradient, EventArgs.Empty); } } @@ -54,6 +56,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { private StubCommand paste; public ICommand Paste => StubCommand(ref paste, ExecutePaste, CanExecutePaste); + private StubCommand createGradient; + public ICommand CreateGradient => StubCommand(ref createGradient, ExecuteCreateGradient, CanExecuteCreateGradient); + public PaletteCollection(ViewPort viewPort, ChangeHistory history) { this.viewPort = viewPort; this.history = history; @@ -164,6 +169,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { for (int i = 0; i < Elements.Count; i++) Elements[i].Selected = newElements[i].Selected; } + #region Commands + private void ExecuteCopy(IFileSystem fileSystem) { var start = Math.Min(selectionStart, selectionEnd) + 1; var end = Math.Max(selectionStart, selectionEnd) + 1; @@ -218,6 +225,36 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { } private bool CanExecutePaste(IFileSystem fileSystem) => CanExecuteCopy(fileSystem) && ParseColor(fileSystem.CopyText) != null; + + private void ExecuteCreateGradient() { + var left = Math.Min(SelectionStart, SelectionEnd); + var leftRGB = UncompressedPaletteColor.ToRGB(Elements[left].Color); + var leftHSB = Theme.ToHSB((byte)(leftRGB.r << 3), (byte)(leftRGB.g << 3), (byte)(leftRGB.b << 3)); + + var right = Math.Max(SelectionStart, SelectionEnd); + var rightRGB = UncompressedPaletteColor.ToRGB(Elements[right].Color); + var rightHSB = Theme.ToHSB((byte)(rightRGB.r << 3), (byte)(rightRGB.g << 3), (byte)(rightRGB.b << 3)); + + var deltaHue = rightHSB.hue - leftHSB.hue; + var deltaSat = rightHSB.sat - leftHSB.sat; + var deltaBright = rightHSB.bright - leftHSB.bright; + + var distance = right - left; + for (int i = 1; i < distance; i++) { + var part = (double)i / distance; + var hue = leftHSB.hue + deltaHue * part; + var sat = leftHSB.sat + deltaSat * part; + var bright = leftHSB.bright + deltaBright * part; + var rgb = Theme.FromHSB(hue, sat, bright); + Elements[left + i].Color = UncompressedPaletteColor.Pack(rgb.red >> 3, rgb.green >> 3, rgb.blue >> 3); + } + + PushColorsToModel(); + } + + private bool CanExecuteCreateGradient() => Elements.Count(element => element.Selected) > 2; + + #endregion } [DebuggerDisplay("{Index}:{Color}")] diff --git a/src/HexManiac.Core/ViewModels/ViewPort.cs b/src/HexManiac.Core/ViewModels/ViewPort.cs index 94c99ba9..8a0a8556 100644 --- a/src/HexManiac.Core/ViewModels/ViewPort.cs +++ b/src/HexManiac.Core/ViewModels/ViewPort.cs @@ -1696,6 +1696,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels { Tools.Schedule(Tools.TableTool.DataForCurrentRunChanged); } if (run is ITableRun || run is IStreamRun) Tools.Schedule(Tools.StringTool.DataForCurrentRunChanged); + if (run is IPaletteRun) tools.Schedule(Tools.SpriteTool.DataForCurrentRunChanged); if (completeEditOperation.MessageText != null) OnMessage?.Invoke(this, completeEditOperation.MessageText); if (completeEditOperation.ErrorText != null) OnError?.Invoke(this, completeEditOperation.ErrorText); diff --git a/src/HexManiac.Tests/ImageTests.cs b/src/HexManiac.Tests/ImageTests.cs index 14c29a71..81b7352c 100644 --- a/src/HexManiac.Tests/ImageTests.cs +++ b/src/HexManiac.Tests/ImageTests.cs @@ -442,6 +442,18 @@ namespace HavenSoft.HexManiac.Tests { Assert.Equal(0x11, Model[1]); // 00 changes to 11 } + [Fact] + public void CanCreateGradientForSelectedColors() { + ViewPort.Edit("^palette`ucp4` 0:0:0 0:0:0 30:30:30 "); + + ViewPort.Tools.SpriteTool.Colors.SelectionStart = 0; + ViewPort.Tools.SpriteTool.Colors.SelectionEnd = 2; + ViewPort.Tools.SpriteTool.Colors.CreateGradient.Execute(); + + var color = (UncompressedPaletteColor)ViewPort[2, 0].Format; + Assert.Equal("15:15:15", color.ToString()); + } + private void CreateLzRun(int start, params byte[] data) { for (int i = 0; i < data.Length; i++) Model[start + i] = data[i]; var run = new LZRun(Model, start); diff --git a/src/HexManiac.WPF/Controls/PaletteControl.xaml b/src/HexManiac.WPF/Controls/PaletteControl.xaml index ca3a97f6..46ea6ef4 100644 --- a/src/HexManiac.WPF/Controls/PaletteControl.xaml +++ b/src/HexManiac.WPF/Controls/PaletteControl.xaml @@ -8,17 +8,19 @@ + + + + + + + + + + + + - - - - - - - - - - diff --git a/src/HexManiac.WPF/Controls/PaletteControl.xaml.cs b/src/HexManiac.WPF/Controls/PaletteControl.xaml.cs index f4d5d7e3..8621e2fe 100644 --- a/src/HexManiac.WPF/Controls/PaletteControl.xaml.cs +++ b/src/HexManiac.WPF/Controls/PaletteControl.xaml.cs @@ -15,7 +15,7 @@ namespace HavenSoft.HexManiac.WPF.Controls { private const int ExpectedElementWidth = 16, ExpectedElementHeight = 16; private static readonly Duration span = new Duration(TimeSpan.FromMilliseconds(100)); - private readonly Popup swatchPopup = new Popup { Placement = PlacementMode.Top, PopupAnimation = PopupAnimation.Fade, AllowsTransparency = true }; + private readonly Popup swatchPopup = new Popup { Placement = PlacementMode.Bottom, PopupAnimation = PopupAnimation.Fade, AllowsTransparency = true }; private readonly Swatch swatch = new Swatch { Width = 230, Height = 200 }; private Point interactionPoint; @@ -50,12 +50,10 @@ namespace HavenSoft.HexManiac.WPF.Controls { protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { swatchPopup.IsOpen = false; swatch.ResultChanged -= SwatchResultChanged; - ViewModel.SelectionStart = -1; base.OnLostKeyboardFocus(e); } private void StartPaletteColorMove(object sender, MouseButtonEventArgs e) { - if (e.LeftButton == MouseButtonState.Released) return; swatch.ResultChanged -= SwatchResultChanged; Focus(); @@ -64,8 +62,7 @@ namespace HavenSoft.HexManiac.WPF.Controls { if (Keyboard.Modifiers == ModifierKeys.Shift) { ViewModel.SelectionEnd = tileIndex; - } else if (ViewModel.SelectionStart == tileIndex) { - ViewModel.SelectionStart = -1; + } else if (ViewModel.SelectionStart == tileIndex && e.LeftButton == MouseButtonState.Pressed && ViewModel.SelectionEnd == tileIndex && swatchPopup.IsOpen) { e.Handled = true; swatchPopup.IsOpen = false; return; @@ -78,10 +75,12 @@ namespace HavenSoft.HexManiac.WPF.Controls { if (Keyboard.Modifiers != ModifierKeys.Shift) { swatch.Result = ColorFor(tileIndex); - swatchPopup.IsOpen = true; initialColors = CollectColorList(); activeSelection = tileIndex; - swatch.ResultChanged += SwatchResultChanged; + if (e.LeftButton == MouseButtonState.Pressed) { + swatchPopup.IsOpen = true; + swatch.ResultChanged += SwatchResultChanged; + } } else { swatchPopup.IsOpen = false; } @@ -95,6 +94,10 @@ namespace HavenSoft.HexManiac.WPF.Controls { var newTileIndex = InteractionTileIndex; var tilesToAnimate = ViewModel.HandleMove(oldTileIndex, newTileIndex); + if (oldTileIndex != newTileIndex) { + swatch.ResultChanged -= SwatchResultChanged; + swatchPopup.IsOpen = false; + } foreach (var (index, direction) in tilesToAnimate) { var tile = MainWindow.GetChild(ItemsControl, "PaletteColor", ViewModel.Elements[index]);