Allow drawing blocks of a color via the draw tool

This commit is contained in:
Benjamin Popp 2020-09-20 21:41:52 -05:00
parent 0e17b057f1
commit 407db89b00
5 changed files with 114 additions and 33 deletions

View File

@ -99,7 +99,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public event EventHandler RefreshSelection;
private void RaiseRefreshSelection(params Point[] toSelect) {
selectedPixels = new bool[PixelWidth, PixelHeight];
foreach (var s in toSelect) selectedPixels[s.X, s.Y] = true;
foreach (var s in toSelect) {
if (WithinImage(s)) selectedPixels[s.X, s.Y] = true;
}
RefreshSelection?.Invoke(this, EventArgs.Empty);
}
@ -118,6 +120,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public int SpritePointer { get; }
private StubCommand setCursorSize;
public ICommand SetCursorSize => StubCommand<string>(ref setCursorSize, arg => CursorSize = int.Parse(arg));
private int cursorSize = 1;
public int CursorSize { get => cursorSize; set => Set(ref cursorSize, value, arg => BlockPreview.Clear()); }
public ImageEditorViewModel(ChangeHistory<ModelDelta> history, IDataModel model, int address) {
this.history = history;
this.model = model;
@ -262,6 +269,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
SelectedTool = ImageEditorTools.Draw;
}
BlockPreview.Clear();
if (CursorSize == 0) CursorSize = 1;
break;
case nameof(sc.Color):
Palette.PushColorsToModel(); // this causes a Render
@ -362,20 +370,20 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
point = parent.ToSpriteSpace(point);
if (parent.WithinImage(point)) {
var tile = parent.eyeDropperStrategy.Tile;
if (tile == null) {
drawSize = 1;
drawPoint = point;
parent.PixelData[parent.PixelIndex(point)] = element.Color;
parent.pixels[point.X, point.Y] = element.Index;
if (tile == null || !parent.BlockPreview.Enabled) {
drawSize = parent.CursorSize;
tile = new int[drawSize, drawSize];
for (int x = 0; x < drawSize; x++) for (int y = 0; y < drawSize; y++) tile[x, y] = element.Index;
} else {
drawSize = tile.GetLength(0);
drawPoint = new Point(point.X - point.X % drawSize, point.Y - point.Y % drawSize);
for (int x = 0; x < drawSize; x++) {
for (int y = 0; y < drawSize; y++) {
var (xx, yy) = (drawPoint.X + x, drawPoint.Y + y);
parent.PixelData[parent.PixelIndex(xx, yy)] = parent.Palette.Elements[tile[x, y]].Color;
parent.pixels[xx, yy] = tile[x, y];
}
}
drawPoint = new Point(point.X - point.X % drawSize, point.Y - point.Y % drawSize);
for (int x = 0; x < drawSize; x++) {
for (int y = 0; y < drawSize; y++) {
var (xx, yy) = (drawPoint.X + x, drawPoint.Y + y);
parent.PixelData[parent.PixelIndex(xx, yy)] = parent.Palette.Elements[tile[x, y]].Color;
parent.pixels[xx, yy] = tile[x, y];
}
}
parent.NotifyPropertyChanged(nameof(PixelData));
@ -388,13 +396,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
point = parent.ToSpriteSpace(point);
if (parent.WithinImage(point)) {
var tile = parent.eyeDropperStrategy.Tile;
if (tile == null) {
drawPoint = point;
drawSize = 1;
if (tile == null || !parent.BlockPreview.Enabled) {
drawSize = parent.CursorSize;
} else {
drawSize = tile.GetLength(0);
drawPoint = new Point(point.X - point.X % drawSize, point.Y - point.Y % drawSize);
}
drawPoint = new Point(point.X - point.X % drawSize, point.Y - point.Y % drawSize);
} else {
drawPoint = default;
drawSize = 0;
@ -583,8 +591,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public EyeDropperTool(ImageEditorViewModel parent) => this.parent = parent;
public bool ShowSelectionRect(Point subPixelPosition) => false;
public void ToolDown(Point point) {
underPixels = null; // old selection lost
selectionStart = parent.ToSpriteSpace(point);
@ -630,6 +636,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
for (int x = 0; x < selectionWidth; x++) for (int y = 0; y < selectionHeight; y++) {
underPixels[x, y] = parent.pixels[selectionStart.X + x, selectionStart.Y + y];
}
parent.CursorSize = 0;
parent.BlockPreview.Set(parent.PixelData, parent.PixelWidth, selectionStart, selectionWidth);
}
}

View File

@ -505,6 +505,7 @@ namespace HavenSoft.HexManiac.Tests {
Assert.Equal(2, editor.BlockPreview.PixelHeight);
Assert.Equal(4, editor.BlockPreview.PixelData.Length);
Assert.All(4.Range(), i => Assert.Equal(Rgb(31, 31, 31), editor.BlockPreview.PixelData[i]));
Assert.Equal(0, editor.CursorSize);
}
[Fact]
@ -560,5 +561,36 @@ namespace HavenSoft.HexManiac.Tests {
Assert.False(editor.ShowSelectionRect(4, 2));
}
[Fact]
public void Draw_SetCursorSize_LargeCursor() {
editor.SelectedTool = ImageEditorTools.Draw;
editor.SetCursorSize.Execute("2");
editor.Hover(default);
Assert.All( new[] {
new Point(4, 4),
new Point(4, 5),
new Point(5, 4),
new Point(5, 5),
}, p => Assert.True(editor.ShowSelectionRect(p)));
}
[Fact]
public void EyeDropperBlock_SelectColor_DrawSinglePixel() {
editor.EyeDropperDown(0, 0);
editor.Hover(1, 1);
editor.EyeDropperUp(1, 1);
editor.Palette.SelectionStart = 1;
editor.Hover(0, 0);
Assert.All(new[] {
new Point(4, 5),
new Point(5, 4),
new Point(5, 5),
}, p => Assert.False(editor.ShowSelectionRect(p)));
}
}
}

View File

@ -23,6 +23,19 @@
</UserControl.InputBindings>
<DockPanel>
<DockPanel DockPanel.Dock="Left">
<DockPanel.Resources>
<Style TargetType="Path">
<Setter Property="Fill" Value="{DynamicResource Secondary}"/>
<Setter Property="Stretch" Value="Uniform"/>
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="24"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=RadioButton}}" Value="True">
<Setter Property="Fill" Value="{DynamicResource Primary}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Resources>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" HorizontalAlignment="Center">
<StackPanel.Resources>
<Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
@ -32,17 +45,6 @@
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<Style TargetType="Path">
<Setter Property="Fill" Value="{DynamicResource Secondary}"/>
<Setter Property="Stretch" Value="Uniform"/>
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="24"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=RadioButton}}" Value="True">
<Setter Property="Fill" Value="{DynamicResource Primary}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<RadioButton CommandParameter="{x:Static hshmcvm:ImageEditorTools.Pan}"
IsChecked="{Binding SelectedTool, Mode=OneWay, Converter={StaticResource EqualityCheck}, ConverterParameter={x:Static hshmcvm:ImageEditorTools.Pan}}">
@ -136,7 +138,30 @@
</RadioButton.ToolTip>
</RadioButton>
</StackPanel>
<local:PixelImage DataContext="{Binding BlockPreview}" Visibility="{Binding Enabled, Converter={StaticResource BoolToVisibility}}" DockPanel.Dock="Top"/>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" HorizontalAlignment="Center"
Visibility="{Binding Tag, RelativeSource={RelativeSource Self}, Converter={StaticResource BoolToVisibility}, ConverterParameter={x:Static Visibility.Hidden}}"
Tag="{Binding SelectedTool, Mode=OneWay, Converter={StaticResource EqualityCheck}, ConverterParameter={x:Static hshmcvm:ImageEditorTools.Draw}}">
<StackPanel.Resources>
<Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Command" Value="{Binding SetCursorSize}"/>
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="30"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</StackPanel.Resources>
<RadioButton CommandParameter="1" Content="1"
IsChecked="{Binding CursorSize, Mode=OneWay, Converter={StaticResource EqualityCheck}, ConverterParameter=1}"/>
<RadioButton CommandParameter="2" Content="2"
IsChecked="{Binding CursorSize, Mode=OneWay, Converter={StaticResource EqualityCheck}, ConverterParameter=2}"/>
<RadioButton CommandParameter="4" Content="4"
IsChecked="{Binding CursorSize, Mode=OneWay, Converter={StaticResource EqualityCheck}, ConverterParameter=4}"/>
<RadioButton CommandParameter="8" Content="8" Margin="0,0,30,0"
IsChecked="{Binding CursorSize, Mode=OneWay, Converter={StaticResource EqualityCheck}, ConverterParameter=8}"/>
</StackPanel>
<local:PixelImage DataContext="{Binding BlockPreview}" Visibility="{Binding Enabled, Converter={StaticResource BoolToVisibility}, ConverterParameter={x:Static Visibility.Hidden}}" DockPanel.Dock="Top"/>
<hshmwpfc:PaletteControl DataContext="{Binding Palette}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</DockPanel>

View File

@ -77,7 +77,7 @@ namespace HavenSoft.HexManiac.WPF.Controls {
public class EqualityToBooleanConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return Equals(value, parameter);
return Equals(value.ToString(), parameter.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
@ -85,6 +85,23 @@ namespace HavenSoft.HexManiac.WPF.Controls {
}
}
public class BooleanToVisibilityConverter : IValueConverter {
private readonly System.Windows.Controls.BooleanToVisibilityConverter core = new System.Windows.Controls.BooleanToVisibilityConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is bool b) {
if (parameter == null) return b ? Visibility.Visible : Visibility.Hidden;
if (parameter is Visibility v && v == Visibility.Hidden) return b ? Visibility.Visible : Visibility.Hidden;
}
return core.Convert(value, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is Visibility vis) {
return vis == Visibility.Visible;
}
return core.ConvertBack(value, targetType, parameter, culture);
}
}
public class PixelImage : Image {
private IPixelViewModel ViewModel => DataContext as IPixelViewModel;

View File

@ -4,7 +4,7 @@
xmlns:hsc="clr-namespace:HavenSoft.HexManiac.WPF.Controls"
xmlns:hsv="clr-namespace:HavenSoft.HexManiac.WPF.Resources">
<Application.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
<hsc:BooleanToVisibilityConverter x:Key="BoolToVisibility" />
<hsc:PaletteColorConverter x:Key="PaletteColorConverter"/>
<hsc:EqualityToBooleanConverter x:Key="EqualityCheck"/>