adding column headers

This commit is contained in:
Benjamin Popp
2019-03-19 21:17:58 -05:00
parent cc1b080792
commit ecb810aa2e
7 changed files with 138 additions and 2 deletions

View File

@@ -156,6 +156,53 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
}
}
public class ColumnHeader {
public string ColumnTitle { get; }
public int ByteWidth { get; }
public ColumnHeader(string title, int byteWidth = 1) => (ColumnTitle, ByteWidth) = (title, byteWidth);
}
/// <summary>
/// Represents a horizontal row of labels.
/// Each entry is meant to label a column.
/// </summary>
public class HeaderRow {
public IReadOnlyList<ColumnHeader> ColumnHeaders { get; }
public HeaderRow(ArrayRun source, int byteStart, int length) {
var headers = new List<ColumnHeader>();
// we know which 'byte' to start at, but we want to know what 'index' to start at
// basically, count off each element to figure out how big it is
int currentByte = 0;
int startIndex = 0;
while (currentByte < byteStart) {
currentByte += source.ElementContent[startIndex].Length;
startIndex++;
}
currentByte = 0;
for (int i = 0; currentByte < length; i++) {
var segment = source.ElementContent[(startIndex + i) % source.ElementContent.Count];
headers.Add(new ColumnHeader(segment.Name, segment.Length));
currentByte += segment.Length;
}
ColumnHeaders = headers;
}
public HeaderRow(int start, int length) {
var hex = "0123456789ABCDEF";
var headers = new ColumnHeader[length];
for (int i = 0; i < length; i++) headers[i] = new ColumnHeader(hex[(start + i) % 0x10].ToString());
ColumnHeaders = headers;
}
public static IReadOnlyList<HeaderRow> GetDefaultColumnHeaders(int columnCount) {
if (columnCount > 0x10 && columnCount % 0x10 != 0) return new List<HeaderRow>();
if (columnCount < 0x10 && 0x10 % columnCount != 0) return new List<HeaderRow>();
if (columnCount >= 0x10) return new[] { new HeaderRow(0, columnCount) };
return Enumerable.Range(0, 0x10 / columnCount).Select(i => new HeaderRow(columnCount * i, columnCount)).ToList();
}
}
public class ArrayRun : BaseRun {
public const char ExtendArray = '+';
public const char ArrayStart = '[';
@@ -386,6 +433,23 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
return new ArrayRun(owner, FormatString, Start, ElementCount, ElementContent, PointerSources, pointerSourcesForInnerElements);
}
/// <summary>
/// Arrays might want custom column headers.
/// If so, this method can get them.
/// </summary>
public IReadOnlyList<HeaderRow> GetColumnHeaders(int columnCount) {
// check if it's a multiple of the array width
if (columnCount >= ElementLength) {
if (columnCount % ElementLength != 0) return null;
return new[] { new HeaderRow(this, 0, columnCount) };
}
// check if it's a divisor of the array width
if (ElementLength % columnCount != 0) return null;
var segments = ElementLength / columnCount;
return Enumerable.Range(0, segments).Select(i => new HeaderRow(this, columnCount * i, columnCount)).ToList();
}
public void AppendTo(IDataModel data, StringBuilder text) {
for (int i = 0; i < ElementCount; i++) {
var offset = Start + i * ElementLength;

View File

@@ -1,4 +1,5 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -16,6 +17,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
int ScrollValue { get; set; }
int MaximumScroll { get; }
ObservableCollection<string> Headers { get; }
ObservableCollection<HeaderRow> ColumnHeaders { get; }
int DataOffset { get; }
ICommand Scroll { get; } // parameter: Direction to scroll

View File

@@ -1,4 +1,5 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using System;
using System.Collections.Generic;
@@ -78,6 +79,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public bool AnchorTextVisible => false;
public ObservableCollection<HeaderRow> ColumnHeaders { get; }
#pragma warning disable 0067 // it's ok if events are never used
public event EventHandler<string> OnError;
public event EventHandler<string> OnMessage;

View File

@@ -70,6 +70,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public int MaximumScroll => scroll.MaximumScroll;
public ObservableCollection<string> Headers => scroll.Headers;
public ObservableCollection<HeaderRow> ColumnHeaders { get; }
public int DataOffset => scroll.DataIndex;
public ICommand Scroll => scroll.Scroll;
@@ -290,6 +291,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
Model = model;
FileName = fileName;
ColumnHeaders = new ObservableCollection<HeaderRow>();
scroll = new ScrollRegion(model.TryGetUsefulHeader) { DataLength = Model.Count };
scroll.PropertyChanged += ScrollPropertyChanged;
@@ -1225,9 +1227,25 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
}
}
if (ColumnHeaders.Count == 0 || ColumnHeaders[0].ColumnHeaders.Count != Width) UpdateColumnHeaders();
NotifyCollectionChanged(ResetArgs);
}
private void UpdateColumnHeaders() {
var index = scroll.ViewPointToDataIndex(new Point(0, 0));
var run = Model.GetNextRun(index) as ArrayRun;
if (run != null && run.Start > index) run = null; // only use the run if it starts _before_ the screen
var headers = run?.GetColumnHeaders(Width) ?? HeaderRow.GetDefaultColumnHeaders(Width);
for (int i = 0; i < headers.Count; i++) {
if (i < ColumnHeaders.Count) ColumnHeaders[i] = headers[i];
else ColumnHeaders.Add(headers[i]);
}
while (ColumnHeaders.Count > headers.Count) ColumnHeaders.RemoveAt(ColumnHeaders.Count - 1);
}
private IDataFormat WrapFormat(IFormattedRun run, IDataFormat format, int dataIndex) {
if (run.PointerSources != null && run.Start == dataIndex) {
var name = Model.GetAnchorFromAddress(-1, run.Start);

View File

@@ -0,0 +1,16 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace HavenSoft.HexManiac.WPF.Controls {
public class MultiplyConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is int number && parameter is double param) return number * param;
return 0.0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
}

View File

@@ -72,6 +72,7 @@
</Page>
<Compile Include="Controls\IntegerToBooleanViaMatchConverter.cs" />
<Compile Include="Controls\IntegerToHexConverter.cs" />
<Compile Include="Controls\MultiplyConverter.cs" />
<Compile Include="Controls\StartScreen.xaml.cs">
<DependentUpon>StartScreen.xaml</DependentUpon>
</Compile>

View File

@@ -13,6 +13,7 @@
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
<hsg3hv:IntegerToBooleanViaMatchConverter x:Key="IndexMatch"/>
<hsg3hv:IntegerToHexConverter x:Key="Hex"/>
<hsg3hv:MultiplyConverter x:Key="Mult"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="N" Modifiers="Ctrl" Command="{Binding New}" />
@@ -257,7 +258,7 @@
<!-- Anchor Editor -->
<Border DockPanel.Dock="Top" Height="19" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Backlight}">
<DockPanel Visibility="{Binding AnchorTextVisible, Converter={StaticResource BoolToVisibility}}" >
<TextBlock Width="60" Text="Anchor: " TextAlignment="Right" ToolTipService.ShowDuration="15000">
<TextBlock Width="70" Text="Anchor: " TextAlignment="Right" ToolTipService.ShowDuration="15000">
<TextBlock.ToolTip>
<ToolTip Background="{DynamicResource Backlight}" BorderBrush="{solarized:Theme Blue}" BorderThickness="1">
<TextBlock TextAlignment="Left">
@@ -277,7 +278,38 @@
<TextBox Text="{Binding AnchorText, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,25,0"/>
</DockPanel>
</Border>
<!-- Headers -->
<!-- Column Headers -->
<DockPanel DockPanel.Dock="Top">
<TextBlock Width="70" Text="{Binding ArrayName}" DockPanel.Dock="Left"/>
<ItemsControl ItemsSource="{Binding ColumnHeaders}" Background="{DynamicResource Backlight}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding ColumnHeaders}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="{DynamicResource Secondary}" Text="{Binding ColumnTitle}" FontFamily="Consolas" HorizontalAlignment="Left" Width="{Binding ByteWidth, Converter={StaticResource Mult}, ConverterParameter={x:Static hsg3hv:HexContent.CellWidth}}">
<TextBlock.ToolTip>
<ToolTip Background="{DynamicResource Backlight}" BorderBrush="{solarized:Theme Blue}" BorderThickness="1">
<TextBlock TextAlignment="Left" Text="{Binding ColumnTitle}"/>
</ToolTip>
</TextBlock.ToolTip>
<TextBlock.RenderTransform>
<TranslateTransform X="5"/>
</TextBlock.RenderTransform>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<!-- Row Headers -->
<ItemsControl DockPanel.Dock="Left" Width="70" ItemsSource="{Binding Headers}" Background="{DynamicResource Backlight}" MouseDown="HeaderMouseDown">
<ItemsControl.ItemTemplate>
<DataTemplate>