mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-10-02 05:06:35 -05:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user