can double-click to follow pointers. Right-click on Anchors lets you follow those backwards.

Followed suggestions from Raina to change the way Anchors look a little.
This commit is contained in:
Benjamin Popp
2018-12-09 17:23:00 -06:00
parent af64d4aad2
commit f8aef7220c
7 changed files with 146 additions and 17 deletions

View File

@@ -25,5 +25,6 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
IChildViewPort CreateChildView(int offset);
void FollowLink(int x, int y);
void ConsiderReload(IFileSystem fileSystem);
void FindAllSources(int x, int y);
}
}

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
@@ -124,6 +125,14 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
public void ConsiderReload(IFileSystem fileSystem) { }
public void FindAllSources(int x, int y) {
if (y < 0 || y > height || x < 0 || x > width) return;
var (childIndex, line) = GetChildLine(y);
if (line == -1 || childIndex >= children.Count) return;
children[childIndex].FindAllSources(x, y);
}
private void NotifyCollectionChanged() {
if (children.Count == 0) return;
UpdateHeaders();
@@ -155,5 +164,6 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
}
return (childIndex, line);
}
}
}

View File

@@ -66,17 +66,6 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
/// </summary>
public event EventHandler<Point> PreviewSelectionStartChanged;
private void GotoAddress(int address) {
backStack.Push(scroll.DataIndex);
if (backStack.Count == 1) backward.CanExecuteChanged.Invoke(backward, EventArgs.Empty);
if (forwardStack.Count > 0) {
forward.CanExecuteChanged.Invoke(forward, EventArgs.Empty);
forwardStack.Clear();
}
SelectionStart = scroll.DataIndexToViewPoint(address);
scroll.ScrollValue += selectionStart.Y;
}
public Selection(ScrollRegion scrollRegion, IModel model) {
scroll = scrollRegion;
scroll.ScrollChanged += (sender, e) => ShiftSelectionFromScroll(e);
@@ -92,9 +81,9 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
var address = args.ToString();
var anchor = model.GetAddressFromAnchor(-1, address);
if (anchor != Pointer.NULL) {
GotoAddress(anchor);
GotoAddressLine(anchor);
} else if (int.TryParse(address, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out int result)) {
GotoAddress(result);
GotoAddressLine(result);
} else {
OnError?.Invoke(this, $"Unable to goto address '{address}'");
}
@@ -151,6 +140,29 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
TryUpdate(ref selectionEnd, scroll.DataIndexToViewPoint(end));
}
public void GotoAddress(int address) {
backStack.Push(scroll.DataIndex);
if (backStack.Count == 1) backward.CanExecuteChanged.Invoke(backward, EventArgs.Empty);
if (forwardStack.Count > 0) {
forwardStack.Clear();
forward.CanExecuteChanged.Invoke(forward, EventArgs.Empty);
}
SelectionStart = scroll.DataIndexToViewPoint(address);
scroll.ScrollValue += selectionStart.Y;
while (scroll.DataIndex < address) scroll.Scroll.Execute(Direction.Right);
}
private void GotoAddressLine(int address) {
backStack.Push(scroll.DataIndex);
if (backStack.Count == 1) backward.CanExecuteChanged.Invoke(backward, EventArgs.Empty);
if (forwardStack.Count > 0) {
forwardStack.Clear();
forward.CanExecuteChanged.Invoke(forward, EventArgs.Empty);
}
SelectionStart = scroll.DataIndexToViewPoint(address);
scroll.ScrollValue += selectionStart.Y;
}
/// <summary>
/// When the scrolling changes, the selection has to move as well.
/// This is because the selection is in terms of the viewPort, not the overall data.

View File

@@ -378,7 +378,16 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
return child;
}
public void FollowLink(int x, int y) { }
public void FollowLink(int x, int y) {
var format = currentView[x, y].Format;
if (format is Pointer pointer) {
if (pointer.Destination != Pointer.NULL) {
selection.GotoAddress(pointer.Destination);
} else {
OnError(this, $"Pointer destination {pointer.DestinationName} not found.");
}
}
}
public void ConsiderReload(IFileSystem fileSystem) {
if (!history.IsSaved) return; // don't overwrite local changes
@@ -400,6 +409,18 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
}
}
public virtual void FindAllSources(int x, int y) {
var anchor = currentView[x, y].Format as DataFormats.Anchor;
if (anchor == null) return;
var title = string.IsNullOrEmpty(anchor.Name) ? (y * Width + x + scroll.DataIndex).ToString("X6") : anchor.Name;
title = "Sources of " + title;
var newTab = new SearchResultsViewPort(title);
foreach (var source in anchor.Sources) newTab.Add(CreateChildView(source));
RequestTabChange(this, newTab);
}
private void Edit(char input) {
var point = GetEditPoint();
var element = currentView[point.X, point.Y];

View File

@@ -382,6 +382,36 @@ namespace HavenSoft.Gen3Hex.Tests {
Assert.Single(results);
}
[Fact]
public void CanUsePointerAsLink() {
var buffer = new byte[0x200];
var model = new PointerModel(buffer);
var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 };
viewPort.SelectionStart = new Point(4, 1);
viewPort.Edit("<000120>");
viewPort.FollowLink(4, 1);
Assert.Equal("000120", viewPort.Headers[0]);
}
[Fact]
public void FindAllSourcesWorks() {
var buffer = new byte[0x200];
var model = new PointerModel(buffer);
var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 };
var editor = new EditorViewModel(new StubFileSystem());
editor.Add(viewPort);
viewPort.SelectionStart = new Point(4, 1);
viewPort.Edit("<000120>");
viewPort.FollowLink(4, 1);
viewPort.FindAllSources(0, 0);
Assert.Equal(1, editor.SelectedIndex);
}
// TODO putting a new anchor with the same name: delete any run that starts at that anchor, repoint everything from the old location to the new location
// TODO undo/redo

View File

@@ -7,6 +7,8 @@ using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
@@ -115,8 +117,8 @@ namespace HavenSoft.Gen3Hex.WPF.Controls {
return;
}
if (e.ChangedButton != MouseButton.Left) return;
Focus();
var p = ControlCoordinatesToModelCoordinates(e);
Focus();
if (e.ClickCount == 2) {
ViewPort.FollowLink(p.X, p.Y);
return;
@@ -141,6 +143,11 @@ namespace HavenSoft.Gen3Hex.WPF.Controls {
protected override void OnMouseUp(MouseButtonEventArgs e) {
base.OnMouseUp(e);
if (e.ChangedButton == MouseButton.Right && e.LeftButton == MouseButtonState.Released && !IsMouseCaptured) {
var p = ControlCoordinatesToModelCoordinates(e);
if (ViewPort[p.X, p.Y].Format is Core.ViewModels.DataFormats.Anchor) ShowAnchorMenu(p);
return;
}
if (!IsMouseCaptured) return;
ReleaseMouseCapture();
}
@@ -192,6 +199,47 @@ namespace HavenSoft.Gen3Hex.WPF.Controls {
}
}
private void ShowAnchorMenu(Core.Models.Point p) {
var anchor = (Core.ViewModels.DataFormats.Anchor)ViewPort[p.X, p.Y].Format;
var panel = new StackPanel { Background = Solarized.Theme.Background };
var menu = new Popup { Placement = PlacementMode.Mouse, Child = panel, StaysOpen = false };
if (anchor.Sources.Count > 1) {
panel.Children.Add(new Button {
Content = "Show All Sources in new tab"
}.SetEvent(ButtonBase.ClickEvent, (sender, e) => {
ViewPort.FindAllSources(p.X, p.Y);
menu.IsOpen = false;
}));
}
if (anchor.Sources.Count < 5) {
for (int i = 0; i < anchor.Sources.Count; i++) {
var source = anchor.Sources[i].ToString("X2");
panel.Children.Add(new Button {
Content = source,
}.SetEvent(ButtonBase.ClickEvent, (sender, e) => {
ViewPort.Goto.Execute(source);
menu.IsOpen = false;
}));
}
} else {
panel.Children.Add(new ScrollViewer {
MaxHeight = 120,
Content = new ListBox {
ItemsSource = anchor.Sources.Select(source => source.ToString("X2")).ToList(),
}.SetEvent(Selector.SelectionChangedEvent, (sender, e) => {
var source = anchor.Sources[((ListBox)sender).SelectedIndex].ToString("X2");
ViewPort.Goto.Execute(source);
menu.IsOpen = false;
}),
});
}
menu.IsOpen = true;
}
private void UpdateViewPortSize() {
ViewPort.Width = (int)(ActualWidth / CellWidth);
ViewPort.Height = (int)(ActualHeight / CellHeight);
@@ -202,4 +250,11 @@ namespace HavenSoft.Gen3Hex.WPF.Controls {
return new Core.Models.Point((int)(point.X / CellWidth), (int)(point.Y / CellHeight));
}
}
public static class FrameworkElementExtensions {
public static T SetEvent<T>(this T item, RoutedEvent routedEvent, RoutedEventHandler handler) where T : FrameworkElement {
item.AddHandler(routedEvent, handler);
return item;
}
}
}

View File

@@ -64,10 +64,10 @@ namespace HavenSoft.Gen3Hex.WPF.Implementations {
context.DrawText(text, new Point(CellTextOffset.X - 10, CellTextOffset.Y));
}
private static readonly Geometry Triangle = Geometry.Parse("M1,0 L4,5 -2,5 Z");
private static readonly Geometry Triangle = Geometry.Parse("M0,5 L3,0 6,5");
public void Visit(Anchor anchor, byte data) {
anchor.OriginalFormat.Visit(this, data);
context.DrawGeometry(Solarized.Brushes.Blue, null, Triangle);
context.DrawGeometry(null, new Pen(Solarized.Brushes.Blue, 2), Triangle);
}
private void VerifyNoneVisualCache() {