diff --git a/Gen3Hex/Gen3Hex.csproj b/Gen3Hex/Gen3Hex.csproj index cf2b278f..86ae9696 100644 --- a/Gen3Hex/Gen3Hex.csproj +++ b/Gen3Hex/Gen3Hex.csproj @@ -60,10 +60,12 @@ + + diff --git a/Gen3Hex/View/HexContent.cs b/Gen3Hex/View/HexContent.cs index e94ccc60..b95eb4ab 100644 --- a/Gen3Hex/View/HexContent.cs +++ b/Gen3Hex/View/HexContent.cs @@ -17,8 +17,8 @@ namespace HavenSoft.Gen3Hex.View { #region ViewPort - public ViewPort ViewPort { - get { return (ViewPort)GetValue(ViewPortProperty); } + public IViewPort ViewPort { + get { return (IViewPort)GetValue(ViewPortProperty); } set { SetValue(ViewPortProperty, value); } } @@ -50,8 +50,8 @@ namespace HavenSoft.Gen3Hex.View { private void OnViewPortPropertyChanged(object sender, PropertyChangedEventArgs e) { var propertyChangesThatRequireRedraw = new[] { - nameof(ViewPort.SelectionStart), - nameof(ViewPort.SelectionEnd), + nameof(ViewModel.ViewPort.SelectionStart), + nameof(ViewModel.ViewPort.SelectionEnd), }; if (propertyChangesThatRequireRedraw.Contains(e.PropertyName)) { @@ -74,23 +74,23 @@ namespace HavenSoft.Gen3Hex.View { InputBindings.Add(keyBinding); } - AddKeyCommand(nameof(ViewPort.MoveSelectionStart), Direction.Up, Key.Up); - AddKeyCommand(nameof(ViewPort.MoveSelectionStart), Direction.Down, Key.Down); - AddKeyCommand(nameof(ViewPort.MoveSelectionStart), Direction.Left, Key.Left); - AddKeyCommand(nameof(ViewPort.MoveSelectionStart), Direction.Right, Key.Right); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionStart), Direction.Up, Key.Up); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionStart), Direction.Down, Key.Down); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionStart), Direction.Left, Key.Left); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionStart), Direction.Right, Key.Right); - AddKeyCommand(nameof(ViewPort.MoveSelectionEnd), Direction.Up, Key.Up, ModifierKeys.Shift); - AddKeyCommand(nameof(ViewPort.MoveSelectionEnd), Direction.Down, Key.Down, ModifierKeys.Shift); - AddKeyCommand(nameof(ViewPort.MoveSelectionEnd), Direction.Left, Key.Left, ModifierKeys.Shift); - AddKeyCommand(nameof(ViewPort.MoveSelectionEnd), Direction.Right, Key.Right, ModifierKeys.Shift); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionEnd), Direction.Up, Key.Up, ModifierKeys.Shift); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionEnd), Direction.Down, Key.Down, ModifierKeys.Shift); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionEnd), Direction.Left, Key.Left, ModifierKeys.Shift); + AddKeyCommand(nameof(ViewModel.ViewPort.MoveSelectionEnd), Direction.Right, Key.Right, ModifierKeys.Shift); - AddKeyCommand(nameof(ViewPort.Scroll), Direction.Up, Key.Up, ModifierKeys.Control); - AddKeyCommand(nameof(ViewPort.Scroll), Direction.Down, Key.Down, ModifierKeys.Control); - AddKeyCommand(nameof(ViewPort.Scroll), Direction.Left, Key.Left, ModifierKeys.Control); - AddKeyCommand(nameof(ViewPort.Scroll), Direction.Right, Key.Right, ModifierKeys.Control); + AddKeyCommand(nameof(IViewPort.Scroll), Direction.Up, Key.Up, ModifierKeys.Control); + AddKeyCommand(nameof(IViewPort.Scroll), Direction.Down, Key.Down, ModifierKeys.Control); + AddKeyCommand(nameof(IViewPort.Scroll), Direction.Left, Key.Left, ModifierKeys.Control); + AddKeyCommand(nameof(IViewPort.Scroll), Direction.Right, Key.Right, ModifierKeys.Control); - AddKeyCommand(nameof(ViewPort.Undo), null, Key.Z, ModifierKeys.Control); - AddKeyCommand(nameof(ViewPort.Redo), null, Key.Y, ModifierKeys.Control); + AddKeyCommand(nameof(IViewPort.Undo), null, Key.Z, ModifierKeys.Control); + AddKeyCommand(nameof(IViewPort.Redo), null, Key.Y, ModifierKeys.Control); } protected override void OnMouseDown(MouseButtonEventArgs e) { @@ -99,15 +99,17 @@ namespace HavenSoft.Gen3Hex.View { if (e.ChangedButton != MouseButton.Left) return; Focus(); - ViewPort.SelectionStart = ControlCoordinatesToModelCoordinates(e); - CaptureMouse(); + if (ViewPort is ViewPort editableViewPort) { + editableViewPort.SelectionStart = ControlCoordinatesToModelCoordinates(e); + CaptureMouse(); + } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (!IsMouseCaptured) return; - ViewPort.SelectionEnd = ControlCoordinatesToModelCoordinates(e); + ((ViewPort)ViewPort).SelectionEnd = ControlCoordinatesToModelCoordinates(e); } protected override void OnMouseUp(MouseButtonEventArgs e) { @@ -118,7 +120,9 @@ namespace HavenSoft.Gen3Hex.View { protected override void OnMouseWheel(MouseWheelEventArgs e) { base.OnMouseWheel(e); - ViewPort.ScrollValue -= Math.Sign(e.Delta); + if (ViewPort is ViewPort editableViewPort) { + editableViewPort.ScrollValue -= Math.Sign(e.Delta); + } } protected override void OnRender(DrawingContext drawingContext) { @@ -147,8 +151,10 @@ namespace HavenSoft.Gen3Hex.View { } protected override void OnTextInput(TextCompositionEventArgs e) { - ViewPort.Edit(e.Text); - e.Handled = true; + if (ViewPort is ViewPort editableViewPort) { + editableViewPort.Edit(e.Text); + e.Handled = true; + } } private void UpdateViewPortSize() { diff --git a/Gen3Hex/ViewModel/CompositeViewPort.cs b/Gen3Hex/ViewModel/CompositeViewPort.cs new file mode 100644 index 00000000..0406e26a --- /dev/null +++ b/Gen3Hex/ViewModel/CompositeViewPort.cs @@ -0,0 +1,63 @@ +using HavenSoft.Gen3Hex.Model; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Windows.Input; + +namespace HavenSoft.Gen3Hex.ViewModel { + public class CompositeViewPort : ViewModelCore, IViewPort { + public HexElement this[int x, int y] => throw new NotImplementedException(); + + public int Width { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public int Height { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public int MinimumScroll => throw new NotImplementedException(); + + public int ScrollValue => throw new NotImplementedException(); + + public int MaximumScroll => throw new NotImplementedException(); + + public ObservableCollection Headers => throw new NotImplementedException(); + + public ICommand Scroll => throw new NotImplementedException(); + + public string Name => throw new NotImplementedException(); + + public ICommand Save => throw new NotImplementedException(); + + public ICommand SaveAs => throw new NotImplementedException(); + + public ICommand Undo => throw new NotImplementedException(); + + public ICommand Redo => throw new NotImplementedException(); + + public ICommand Copy => throw new NotImplementedException(); + + public ICommand Clear => throw new NotImplementedException(); + + public ICommand Goto => throw new NotImplementedException(); + + public ICommand Back => throw new NotImplementedException(); + + public ICommand Forward => throw new NotImplementedException(); + + public ICommand Close => throw new NotImplementedException(); + + public event EventHandler OnError; + public event EventHandler Closed; + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public IViewPort CreateChildView(int offset) { + throw new NotImplementedException(); + } + + public IReadOnlyList Find(string search) { + throw new NotImplementedException(); + } + + public bool IsSelected(Point point) { + throw new NotImplementedException(); + } + } +} diff --git a/Gen3Hex/ViewModel/EditorViewModel.cs b/Gen3Hex/ViewModel/EditorViewModel.cs index 3dec8f56..9f683624 100644 --- a/Gen3Hex/ViewModel/EditorViewModel.cs +++ b/Gen3Hex/ViewModel/EditorViewModel.cs @@ -11,9 +11,14 @@ namespace HavenSoft.Gen3Hex.ViewModel { private readonly IFileSystem fileSystem; private readonly List tabs; - private readonly StubCommand newCommand, open, save, saveAs, saveAll, close, closeAll, undo, redo, cut, copy, paste, delete, back, forward, gotoCommand, showGoto, clearError; + private readonly StubCommand newCommand, open, save, saveAs, saveAll, close, closeAll; + private readonly StubCommand undo, redo, cut, copy, paste, delete; + private readonly StubCommand back, forward, gotoCommand, showGoto, find, findPrevious, findNext, showFind, clearError; private readonly Dictionary, EventHandler> forwardExecuteChangeNotifications; + private (IViewPort tab, int)[] recentFindResults; + private int currentFindResultIndex; + public ICommand New => newCommand; public ICommand Open => open; public ICommand Save => save; @@ -31,6 +36,10 @@ namespace HavenSoft.Gen3Hex.ViewModel { public ICommand Forward => forward; public ICommand Goto => gotoCommand; public ICommand ShowGoto => showGoto; + public ICommand Find => find; + public ICommand FindPrevious => findPrevious; + public ICommand FindNext => findNext; + public ICommand ShowFind => showFind; public ICommand ClearError => clearError; private bool gotoControlVisible; @@ -38,7 +47,22 @@ namespace HavenSoft.Gen3Hex.ViewModel { get => gotoControlVisible; private set { TryUpdate(ref gotoControlVisible, value); - if (value) ClearError.Execute(); + if (value) { + ClearError.Execute(); + FindControlVisible = false; + } + } + } + + private bool findControlVisible; + public bool FindControlVisible { + get => findControlVisible; + private set { + TryUpdate(ref findControlVisible, value); + if (value) { + ClearError.Execute(); + GotoControlVisible = false; + } } } @@ -110,6 +134,34 @@ namespace HavenSoft.Gen3Hex.ViewModel { CanExecute = CanAlwaysExecute, Execute = arg => GotoControlVisible = (bool)arg, }; + find = new StubCommand { + CanExecute = CanAlwaysExecute, + Execute = arg => FindExecuted((string)arg), + }; + findPrevious = new StubCommand { + CanExecute = arg => recentFindResults?.Length != 0, + Execute = arg => { + currentFindResultIndex--; + if (currentFindResultIndex < 0) currentFindResultIndex += recentFindResults.Length; + var (tab, offset) = recentFindResults[currentFindResultIndex]; + SelectedIndex = tabs.IndexOf(tab); + tab.Goto.Execute(offset.ToString("X2")); + }, + }; + findNext = new StubCommand { + CanExecute = arg => recentFindResults?.Length != 0, + Execute = arg => { + currentFindResultIndex++; + if (currentFindResultIndex >= recentFindResults.Length) currentFindResultIndex -= recentFindResults.Length; + var (tab, offset) = recentFindResults[currentFindResultIndex]; + SelectedIndex = tabs.IndexOf(tab); + tab.Goto.Execute(offset.ToString("X2")); + }, + }; + showFind = new StubCommand { + CanExecute = CanAlwaysExecute, + Execute = arg => FindControlVisible = (bool)arg, + }; clearError = new StubCommand { CanExecute = arg => showError, Execute = arg => ErrorMessage = string.Empty, @@ -209,6 +261,10 @@ namespace HavenSoft.Gen3Hex.ViewModel { return command; } + private void FindExecuted(string search) { + + } + private void RemoveTab(object sender, EventArgs e) { var tab = (ITabContent)sender; if (!tabs.Contains(tab)) throw new InvalidOperationException("Cannot remove tab, because tab is not currently in editor."); diff --git a/Gen3Hex/ViewModel/IViewPort.cs b/Gen3Hex/ViewModel/IViewPort.cs new file mode 100644 index 00000000..f60ed30c --- /dev/null +++ b/Gen3Hex/ViewModel/IViewPort.cs @@ -0,0 +1,25 @@ +using HavenSoft.Gen3Hex.Model; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows.Input; + +namespace HavenSoft.Gen3Hex.ViewModel { + public interface IViewPort : ITabContent, INotifyCollectionChanged, INotifyPropertyChanged { + int Width { get; set; } + int Height { get; set; } + + int MinimumScroll { get; } + int ScrollValue { get; } + int MaximumScroll { get; } + ObservableCollection Headers { get; } + ICommand Scroll { get; } + + HexElement this[int x, int y] { get; } + bool IsSelected(Point point); + + IReadOnlyList Find(string search); + IViewPort CreateChildView(int offset); + } +} diff --git a/Gen3Hex/ViewModel/ViewPort.cs b/Gen3Hex/ViewModel/ViewPort.cs index af8393ca..07fccda5 100644 --- a/Gen3Hex/ViewModel/ViewPort.cs +++ b/Gen3Hex/ViewModel/ViewPort.cs @@ -12,10 +12,11 @@ using System.Text; using System.Windows.Input; namespace HavenSoft.Gen3Hex.ViewModel { + /// /// A range of visible data that should be displayed. /// - public class ViewPort : ViewModelCore, ITabContent, INotifyCollectionChanged { + public class ViewPort : ViewModelCore, IViewPort { private static readonly NotifyCollectionChangedEventArgs ResetArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); private readonly StubCommand clear = new StubCommand(), @@ -262,6 +263,15 @@ namespace HavenSoft.Gen3Hex.ViewModel { for (int i = 0; i < input.Length; i++) Edit(input[i]); } + public IReadOnlyList Find(string search) { + // TODO + return new int[0]; + } + + public IViewPort CreateChildView(int offset) { + return null; + } + private void Edit(char input) { var point = GetEditPoint(); var element = currentView[point.X, point.Y]; diff --git a/HexTests/FindTests.cs b/HexTests/FindTests.cs index faa2fb46..44014c96 100644 --- a/HexTests/FindTests.cs +++ b/HexTests/FindTests.cs @@ -36,7 +36,7 @@ namespace HavenSoft.HexTests { [Fact] public void EditorShowsErrorIfNoneFound() { - var tab = new StubTabContent(); + var tab = new StubViewPort(); var editor = new EditorViewModel(new StubFileSystem()) { tab }; tab.Find = str => new int[0]; @@ -48,7 +48,7 @@ namespace HavenSoft.HexTests { [Fact] public void EditorJumpsToResultIfSingleResult() { - var tab = new StubTabContent(); + var tab = new StubViewPort(); var editor = new EditorViewModel(new StubFileSystem()) { tab }; string gotoArg = string.Empty; @@ -62,7 +62,7 @@ namespace HavenSoft.HexTests { [Fact] public void EditorOpensHelperTabIfMultipleResult() { - var tab = new StubTabContent(); + var tab = new StubViewPort(); var editor = new EditorViewModel(new StubFileSystem()) { tab }; var count = 0; @@ -82,7 +82,7 @@ namespace HavenSoft.HexTests { [Fact] public void EditorHasShortcutsToGetPreviousOrNextFindResult() { - var tab = new StubTabContent(); + var tab = new StubViewPort(); var editor = new EditorViewModel(new StubFileSystem()) { tab }; int gotoCount = 0; diff --git a/HexTests/HexTests.csproj b/HexTests/HexTests.csproj index de75afba..4575027c 100644 --- a/HexTests/HexTests.csproj +++ b/HexTests/HexTests.csproj @@ -64,6 +64,7 @@ + @@ -105,7 +106,7 @@ if not exist $(ProjectDir)generatedCode mkdir $(ProjectDir)generatedCode -$(SolutionDir)packages\HavenSoft.AutoImplement.1.0.1\AutoImplement $(SolutionDir)Gen3Hex\$(OutDir)Gen3Hex.exe ITabContent IFileSystem +$(SolutionDir)packages\HavenSoft.AutoImplement.1.0.1\AutoImplement $(SolutionDir)Gen3Hex\$(OutDir)Gen3Hex.exe ITabContent IFileSystem IViewPort move /Y StubTabContent.cs $(ProjectDir)generatedCode move /Y StubFileSystem.cs $(ProjectDir)generatedCode diff --git a/HexTests/generatedCode/StubViewPort.cs b/HexTests/generatedCode/StubViewPort.cs new file mode 100644 index 00000000..50bd0d29 --- /dev/null +++ b/HexTests/generatedCode/StubViewPort.cs @@ -0,0 +1,286 @@ +// this file was created by AutoImplement +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Delegation; + +namespace HavenSoft.Gen3Hex.ViewModel +{ + public class StubViewPort : IViewPort + { + public Func IsSelected { get; set; } + + bool IViewPort.IsSelected(HavenSoft.Gen3Hex.Model.Point point) + { + if (this.IsSelected != null) + { + return this.IsSelected(point); + } + else + { + return default(bool); + } + } + + public Func> Find { get; set; } + + System.Collections.Generic.IReadOnlyList IViewPort.Find(string search) + { + if (this.Find != null) + { + return this.Find(search); + } + else + { + return default(System.Collections.Generic.IReadOnlyList); + } + } + + public Func CreateChildView { get; set; } + + IViewPort IViewPort.CreateChildView(int offset) + { + if (this.CreateChildView != null) + { + return this.CreateChildView(offset); + } + else + { + return default(IViewPort); + } + } + + public PropertyImplementation Width = new PropertyImplementation(); + + int IViewPort.Width + { + get + { + return this.Width.get(); + } + set + { + this.Width.set(value); + } + } + public PropertyImplementation Height = new PropertyImplementation(); + + int IViewPort.Height + { + get + { + return this.Height.get(); + } + set + { + this.Height.set(value); + } + } + public PropertyImplementation MinimumScroll = new PropertyImplementation(); + + int IViewPort.MinimumScroll + { + get + { + return this.MinimumScroll.get(); + } + } + public PropertyImplementation ScrollValue = new PropertyImplementation(); + + int IViewPort.ScrollValue + { + get + { + return this.ScrollValue.get(); + } + } + public PropertyImplementation MaximumScroll = new PropertyImplementation(); + + int IViewPort.MaximumScroll + { + get + { + return this.MaximumScroll.get(); + } + } + public PropertyImplementation> Headers = new PropertyImplementation>(); + + System.Collections.ObjectModel.ObservableCollection IViewPort.Headers + { + get + { + return this.Headers.get(); + } + } + public PropertyImplementation Scroll = new PropertyImplementation(); + + System.Windows.Input.ICommand IViewPort.Scroll + { + get + { + return this.Scroll.get(); + } + } + public Func get_Item = (x, y) => default(HexElement); + + HexElement IViewPort.this[int x, int y] + { + get + { + return get_Item(x, y); + } + } + public PropertyImplementation Name = new PropertyImplementation(); + + string ITabContent.Name + { + get + { + return this.Name.get(); + } + } + public PropertyImplementation Save = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Save + { + get + { + return this.Save.get(); + } + } + public PropertyImplementation SaveAs = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.SaveAs + { + get + { + return this.SaveAs.get(); + } + } + public PropertyImplementation Undo = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Undo + { + get + { + return this.Undo.get(); + } + } + public PropertyImplementation Redo = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Redo + { + get + { + return this.Redo.get(); + } + } + public PropertyImplementation Copy = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Copy + { + get + { + return this.Copy.get(); + } + } + public PropertyImplementation Clear = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Clear + { + get + { + return this.Clear.get(); + } + } + public PropertyImplementation Goto = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Goto + { + get + { + return this.Goto.get(); + } + } + public PropertyImplementation Back = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Back + { + get + { + return this.Back.get(); + } + } + public PropertyImplementation Forward = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Forward + { + get + { + return this.Forward.get(); + } + } + public PropertyImplementation Close = new PropertyImplementation(); + + System.Windows.Input.ICommand ITabContent.Close + { + get + { + return this.Close.get(); + } + } + public EventImplementation OnError = new EventImplementation(); + + event System.EventHandler ITabContent.OnError + { + add + { + OnError.add(new EventHandler(value)); + } + remove + { + OnError.remove(new EventHandler(value)); + } + } + public EventImplementation Closed = new EventImplementation(); + + event System.EventHandler ITabContent.Closed + { + add + { + Closed.add(new EventHandler(value)); + } + remove + { + Closed.remove(new EventHandler(value)); + } + } + public EventImplementation CollectionChanged = new EventImplementation(); + + event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged + { + add + { + CollectionChanged.add(new EventHandler(value)); + } + remove + { + CollectionChanged.remove(new EventHandler(value)); + } + } + public EventImplementation PropertyChanged = new EventImplementation(); + + event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged + { + add + { + PropertyChanged.add(new EventHandler(value)); + } + remove + { + PropertyChanged.remove(new EventHandler(value)); + } + } + } +}