Allow user to duplicate the current tab

This is useful if you want to view multiple locations in the same rom at the same time. This was previously possible by following a pointer into a new tab, but often the thing you want to see isn't available from an onscreen pointer, so it makes more sense to open up a new goto.
This commit is contained in:
Benjamin Popp 2021-08-01 00:38:49 -05:00
parent 0bb9eaa224
commit 5de71b167e
11 changed files with 59 additions and 0 deletions

View File

@ -137,6 +137,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public ICommand Close => this[0].Close;
ICommand ITabContent.Clear => this[0].Clear;
public bool CanDuplicate => false;
public void Duplicate() { }
public event EventHandler PreviewScrollChanged { add => ForEach(child => child.PreviewScrollChanged += value); remove => ForEach(child => child.PreviewScrollChanged -= value); }
public event EventHandler<string> OnError { add => ForEach(child => child.OnError += value); remove => ForEach(child => child.OnError -= value); }

View File

@ -49,6 +49,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public ICommand Back { get; } = new StubCommand();
public ICommand Forward { get; } = new StubCommand();
public ICommand Close { get; }
public bool CanDuplicate => false;
public void Duplicate() { }
public event EventHandler<string> OnError;
public event EventHandler Closed;

View File

@ -118,6 +118,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public ICommand Diff => null;
public ICommand DiffLeft => null;
public ICommand DiffRight => null;
public bool CanDuplicate => false;
public void Duplicate() { }
public event EventHandler PreviewScrollChanged;
public event EventHandler<string> OnError;

View File

@ -31,6 +31,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
private readonly StubCommand
newCommand = new StubCommand(),
open = new StubCommand(),
duplicateCurrentTab = new StubCommand(),
save = new StubCommand(),
saveAs = new StubCommand(),
exportBackup = new StubCommand(),
@ -79,6 +80,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public ICommand New => newCommand;
public ICommand Open => open; // parameter: file to open (or null)
public ICommand DuplicateCurrentTab => duplicateCurrentTab;
public ICommand Save => save;
public ICommand SaveAs => saveAs;
public ICommand SaveAll => saveAll;
@ -382,6 +384,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
findPrevious.RaiseCanExecuteChanged();
findNext.RaiseCanExecuteChanged();
runFile.RaiseCanExecuteChanged();
duplicateCurrentTab.RaiseCanExecuteChanged();
if (selectedIndex >= 0 && selectedIndex < tabs.Count) tabs[selectedIndex].Refresh();
UpdateGotoViewModel();
foreach (var edit in QuickEditsPokedex.Concat(QuickEditsExpansion)) edit.TabChanged();
@ -557,6 +560,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
}
};
duplicateCurrentTab.CanExecute = arg => SelectedTab?.CanDuplicate ?? false;
duplicateCurrentTab.Execute = arg => {
SelectedTab?.Duplicate();
gotoViewModel.ControlVisible = true;
};
runFile.CanExecute = arg => SelectedTab is IEditableViewPort viewPort && !viewPort.ChangeHistory.HasDataChange;
runFile.Execute = arg => {
((IFileSystem)arg).LaunchProcess(((ViewPort)SelectedTab).FullFileName);

View File

@ -37,6 +37,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
event EventHandler<Direction> RequestDiff;
event EventHandler<CanDiffEventArgs> RequestCanDiff;
bool CanDuplicate { get; }
void Duplicate();
void Refresh();
bool TryImport(LoadedFile file, IFileSystem fileSystem);
}

View File

@ -52,6 +52,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public ICommand Back => null;
public ICommand Forward => null;
public ICommand Close => StubCommand(ref close, () => Closed?.Invoke(this, EventArgs.Empty));
public bool CanDuplicate => false;
public void Duplicate() { }
public event EventHandler<string> OnError;
public event EventHandler<string> OnMessage;
public event EventHandler ClearMessage;

View File

@ -89,6 +89,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public ICommand Back { get; } = new StubCommand();
public ICommand Forward { get; } = new StubCommand();
public ICommand Close => close;
public bool CanDuplicate => false;
public void Duplicate() { }
public bool UpdateInProgress => false;
public double Progress => 0;

View File

@ -626,6 +626,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
#endregion
#region Duplicate
public bool CanDuplicate => true;
public void Duplicate() => OpenInNewTab(scroll.DataIndex);
#endregion
public int FreeSpaceStart { get => Model.FreeSpaceStart; set {
if (Model.FreeSpaceStart != value) {
Model.FreeSpaceStart = value;

View File

@ -544,6 +544,30 @@ ApplicationVersion = '''0.1.0'''
Assert.False(editor.RunFile.CanExecute(default));
}
[Fact]
public void Tab_Duplicate_Duplicated() {
var tab = new StubTabContent { CanDuplicate = true };
tab.Duplicate = () => tab.RequestTabChange.Invoke(tab, new StubTabContent());
editor.Add(tab);
editor.DuplicateCurrentTab.Execute();
Assert.Equal(2, editor.Count);
Assert.True(editor.GotoViewModel.ControlVisible);
}
[Fact]
public void TabWithoutDuplicate_SwitchToTabWithDuplicate_Notify() {
editor.Add(new StubTabContent { CanDuplicate = false });
editor.Add(new StubTabContent { CanDuplicate = true });
editor.SelectedIndex = 0;
var view = new StubView(editor);
editor.SelectedIndex = 1;
Assert.Contains(nameof(editor.DuplicateCurrentTab), view.CommandCanExecuteChangedNotifications);
}
private StubTabContent CreateClosableTab() {
var tab = new StubTabContent();
var close = new StubCommand { CanExecute = arg => true };

View File

@ -80,6 +80,9 @@ namespace HavenSoft.HexManiac.WPF.Resources {
public override object ProvideValue(IServiceProvider serviceProvider) {
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (valueProvider == null) return null;
if (valueProvider.TargetObject.GetType().FullName == "System.Windows.SharedDp") {
return this; // we're in a template: defer execution
}
var element = (FrameworkElement)valueProvider.TargetObject;
var context = element.DataContext;
var command = new MethodCommand(context, CommandMethod);

View File

@ -19,6 +19,7 @@
<KeyBinding Key="N" Modifiers="Ctrl" Command="{Binding New}" />
<KeyBinding Key="O" Modifiers="Ctrl" Command="{Binding Open}" />
<KeyBinding Key="T" Modifiers="Ctrl" Command="{Binding DuplicateCurrentTab}" />
<KeyBinding Key="S" Modifiers="Ctrl" Command="{Binding Save}" />
<KeyBinding Key="S" Modifiers="Ctrl+Shift" Command="{Binding SaveAs}" />
<KeyBinding Key="A" Modifiers="Ctrl+Shift" Command="{Binding SaveAll}" />
@ -86,6 +87,7 @@
<Path Data="{hsv:Icon Open}" Fill="{DynamicResource Secondary}" Stretch="Uniform"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Duplicate Current Tab" Command="{Binding DuplicateCurrentTab}" InputGestureText="Ctrl+T"/>
<Separator />
<MenuItem Header="_Save" Command="{Binding Save}" InputGestureText="Ctrl+S">
<MenuItem.Icon>
@ -473,6 +475,7 @@
<ContextMenu>
<MenuItem Header="Diff with Left" Command="{Binding DiffLeft}" />
<MenuItem Header="Diff with Right" Command="{Binding DiffRight}" />
<MenuItem Header="Duplicate Tab" InputGestureText="Ctrl+T" Command="{hsv:MethodCommand Duplicate}"/>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Name="TabTextBlock" Background="Transparent" Text="{Binding Name}" Foreground="{DynamicResource Primary}" MouseDown="TabMouseDown" MouseMove="TabMouseMove" MouseUp="TabMouseUp">