truncate to 10 scripts showing at once in the map header

This commit is contained in:
haven1433 2025-11-28 15:59:58 -06:00
parent b65acc02d1
commit 14ae6b354b
3 changed files with 106 additions and 79 deletions

View File

@ -18,12 +18,14 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public bool Unloaded => address == 0;
public ObservableCollection<MapScriptViewModel> Scripts { get; } = new();
public bool CollectionExists => address > 0;
public bool AllowAddMoreScripts { get; private set; } = true;
public event EventHandler<NewMapScriptsCreatedEventArgs> NewMapScriptsCreated;
public MapScriptCollection(IEditableViewPort viewPort) => this.viewPort = viewPort;
public void Load(ModelArrayElement owner) {
foreach (var script in Scripts) script.DeleteMe -= HandleDelete;
Scripts.Clear();
this.owner = owner;
if (owner == null) return;
@ -31,12 +33,18 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
if (address == Pointer.NULL) return;
var model = viewPort.Model;
var scriptStart = address;
AllowAddMoreScripts = true;
while (model[scriptStart] != 0) {
if (Scripts.Count == 10) {
Scripts.Add(new(viewPort, Pointer.NULL)); // 'UI is full' sentinel
AllowAddMoreScripts = false;
break;
}
Scripts.Add(new(viewPort, scriptStart));
AddDeleteHandler(Scripts.Count - 1);
scriptStart += 5;
}
NotifyPropertiesChanged(nameof(CollectionExists), nameof(Unloaded), nameof(Address));
NotifyPropertiesChanged(nameof(CollectionExists), nameof(Unloaded), nameof(Address), nameof(AllowAddMoreScripts));
}
public bool CanCreateCollection => address < 0;
@ -71,9 +79,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
model.UpdateArrayPointer(token, default, default, default, run.Start + run.Length - 5, newScript);
model.ObserveRunWritten(token, run);
model.ObserveRunWritten(token, new XSERun(newScript));
Scripts.Add(new(viewPort, address + Scripts.Count * 5));
AddDeleteHandler(Scripts.Count - 1);
viewPort.ChangeHistory.ChangeCompleted();
Load(owner); // do a full reload, in case we need to truncate scripts
}
private void AddDeleteHandler(int index) {
@ -88,16 +95,15 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
var tableRun = model.GetNextRun(address) as ITableRun;
if (tableRun == null) return;
var table = new ModelTable(model, tableRun, () => token); // type. pointer<>
for (int i = index; i < Scripts.Count - 1; i++) {
for (int i = index; i < table.Count - 1; i++) {
table[i].SetValue(0, table[i + 1].GetValue(0));
table[i].SetAddress("pointer", table[i + 1].GetAddress("pointer"));
}
tableRun = tableRun.Append(token, -1);
model.ObserveRunWritten(token, tableRun);
Scripts[index].DeleteMe -= HandleDelete;
Scripts.RemoveAt(index);
e.Success = true;
viewPort.ChangeHistory.ChangeCompleted();
Load(owner); // do a reload, in case we were truncating scripts.
}
}
@ -114,6 +120,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public event EventHandler<MapScriptDeleteEventArgs> DeleteMe;
public bool HasSubScripts => scriptType == 2 || scriptType == 4;
public bool MoreScriptsTruncated { get; } = false;
public bool ShowMapScript => !MoreScriptsTruncated;
public ObservableCollection<VisualOption> ScriptOptions { get; } = new();
public ObservableCollection<MapSubScriptViewModel> SubScripts { get; } = new();
@ -122,6 +130,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
this.viewPort = viewPort;
var model = viewPort.Model;
this.start = start;
if (start == Pointer.NULL) {
MoreScriptsTruncated = true;
return;
}
this.scriptType = model[start];
this.address = model.ReadPointer(start + 1);
this.displayAddress = $"<{address:X6}>";
@ -335,25 +347,31 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
addressText = $"<{address:X6}>";
}
public string Variable { get => variableText; set => Set(ref variableText, value, arg => {
if (!variableText.TryParseHex(out int result)) return;
variable = result;
viewPort.Model.WriteMultiByteValue(start, 2, viewPort.ChangeHistory.CurrentChange, variable);
}); }
public string Variable {
get => variableText; set => Set(ref variableText, value, arg => {
if (!variableText.TryParseHex(out int result)) return;
variable = result;
viewPort.Model.WriteMultiByteValue(start, 2, viewPort.ChangeHistory.CurrentChange, variable);
});
}
public string Value { get => valueText; set => Set(ref valueText, value, arg => {
if (!int.TryParse(valueText, out int result)) return;
val = result;
viewPort.Model.WriteMultiByteValue(start + 2, 2, viewPort.ChangeHistory.CurrentChange, val);
}); }
public string Value {
get => valueText; set => Set(ref valueText, value, arg => {
if (!int.TryParse(valueText, out int result)) return;
val = result;
viewPort.Model.WriteMultiByteValue(start + 2, 2, viewPort.ChangeHistory.CurrentChange, val);
});
}
public string Address { get => addressText; set => Set(ref addressText, value, arg => {
var text = addressText.Trim("<> ".ToCharArray());
if (!text.TryParseHex(out int result)) return;
// do the same work that we do in the code tool, removing scripts that aren't needed
address = result;
viewPort.Model.UpdateArrayPointer(viewPort.ChangeHistory.CurrentChange, default, default, -1, start + 4, address);
}); }
public string Address {
get => addressText; set => Set(ref addressText, value, arg => {
var text = addressText.Trim("<> ".ToCharArray());
if (!text.TryParseHex(out int result)) return;
// do the same work that we do in the code tool, removing scripts that aren't needed
address = result;
viewPort.Model.UpdateArrayPointer(viewPort.ChangeHistory.CurrentChange, default, default, -1, start + 4, address);
});
}
public void Delete() {
var args = new MapScriptDeleteEventArgs();

View File

@ -564,7 +564,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
var name = newArray.Start.ToAddress();
var anchorName = model.GetAnchorFromAddress(-1, newArray.Start);
if (!string.IsNullOrWhiteSpace(anchorName)) name = anchorName;
messageText = $"Table {name} was moved. Pointers have been updated.";
if (newArray.Start != array.Start) {
messageText = $"Table {name} was moved. Pointers have been updated.";
} else {
messageText = $"Table {name} was resized. Address has not changed.";
}
if (newArray.Length > array.Length && newArray.Start == array.Start) {
model.ClearFormat(token, array.Start + array.Length, newArray.Length - array.Length);
// edge case: if the run in this spot isn't the run we're about to add, clear that too

View File

@ -1279,65 +1279,70 @@
<ItemsControl ItemsSource="{Binding PrimaryMap.MapScriptCollection.Scripts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<DockPanel Margin="2">
<DockPanel.Resources>
<DataTemplate DataType="{x:Type models:VisualOption}">
<TextBlock Text="{Binding Option}">
<TextBlock.ToolTip>
<TextBlock>
<Bold>
<Run Text="{Binding ShortDescription}" />
</Bold>
<LineBreak /> <LineBreak />
<Run Text="{Binding Description}" />
<Grid>
<StackPanel Visibility="{Binding ShowMapScript, Converter={StaticResource BoolToVisibility}}">
<DockPanel Margin="2">
<DockPanel.Resources>
<DataTemplate DataType="{x:Type models:VisualOption}">
<TextBlock Text="{Binding Option}">
<TextBlock.ToolTip>
<TextBlock>
<Bold>
<Run Text="{Binding ShortDescription}" />
</Bold>
<LineBreak /> <LineBreak />
<Run Text="{Binding Description}" />
</TextBlock>
</TextBlock.ToolTip>
</TextBlock>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DockPanel.Resources>
<local:AngleButton Command="{res:MethodCommand Delete}" DockPanel.Dock="Left" Direction="Left" Padding="0" VerticalAlignment="Center" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Uniform" Data="{res:Icon Exit}" Width="10" Height="20"/>
</local:AngleButton>
<local:AngleButton Command="{res:MethodCommand Goto}" DockPanel.Dock="Right" Direction="Right" Padding="0" Width="20" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Fill" Data="{res:Icon RightAngleArrow}" Width="10" Height="12" />
</local:AngleButton>
<local:AngleComboBox SelectedIndex="{Binding ScriptTypeIndex}" ItemsSource="{Binding ScriptOptions}" Direction="Out" DockPanel.Dock="Left" Width="140" Height="18" />
<local:AngleTextBox Direction="Right" TextBinding="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<ItemsControl Visibility="{Binding HasSubScripts, Converter={StaticResource BoolToVisibility}}" ItemsSource="{Binding SubScripts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="8,2">
<!-- Do I need multiple lines for this? -->
<!-- One line for Delete, Address, Goto -->
<!-- One line for variable/value pair -->
<DockPanel>
<local:AngleButton Command="{res:MethodCommand Delete}" DockPanel.Dock="Left" Direction="Left" Padding="0" VerticalAlignment="Center" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Uniform" Data="{res:Icon Exit}" Width="10" Height="20" />
</local:AngleButton>
<local:AngleButton Command="{res:MethodCommand Goto}" DockPanel.Dock="Right" Direction="Right" Padding="0" Width="20" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Fill" Data="{res:Icon RightAngleArrow}" Width="10" Height="12" />
</local:AngleButton>
<local:AngleBorder Direction="Left" Content="Address:" Padding="2,0" />
<local:AngleTextBox Direction="Out" TextBinding="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<UniformGrid Columns="4" Margin="20,0">
<local:AngleBorder Direction="Out" Content="Var:" />
<local:AngleTextBox Direction="Right" TextBinding="{Binding Variable, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,2,0" />
<local:AngleBorder Direction="Out" Content="Value:" Margin="2,0,0,0" />
<local:AngleTextBox Direction="Right" TextBinding="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />
</UniformGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<local:AngleButton Margin="8,2" Visibility="{Binding HasSubScripts, Converter={StaticResource BoolToVisibility}}" Command="{res:MethodCommand AddSubScript}" Direction="Out" Content="Add Table Script" />
</StackPanel>
</DataTemplate>
</DockPanel.Resources>
<local:AngleButton Command="{res:MethodCommand Delete}" DockPanel.Dock="Left" Direction="Left" Padding="0" VerticalAlignment="Center" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Uniform" Data="{res:Icon Exit}" Width="10" Height="20"/>
</local:AngleButton>
<local:AngleButton Command="{res:MethodCommand Goto}" DockPanel.Dock="Right" Direction="Right" Padding="0" Width="20" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Fill" Data="{res:Icon RightAngleArrow}" Width="10" Height="12" />
</local:AngleButton>
<local:AngleComboBox SelectedIndex="{Binding ScriptTypeIndex}" ItemsSource="{Binding ScriptOptions}" Direction="Out" DockPanel.Dock="Left" Width="140" Height="18" />
<local:AngleTextBox Direction="Right" TextBinding="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<ItemsControl Visibility="{Binding HasSubScripts, Converter={StaticResource BoolToVisibility}}" ItemsSource="{Binding SubScripts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="8,2">
<!-- Do I need multiple lines for this? -->
<!-- One line for Delete, Address, Goto -->
<!-- One line for variable/value pair -->
<DockPanel>
<local:AngleButton Command="{res:MethodCommand Delete}" DockPanel.Dock="Left" Direction="Left" Padding="0" VerticalAlignment="Center" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Uniform" Data="{res:Icon Exit}" Width="10" Height="20" />
</local:AngleButton>
<local:AngleButton Command="{res:MethodCommand Goto}" DockPanel.Dock="Right" Direction="Right" Padding="0" Width="20" Height="18">
<Path Fill="{DynamicResource Secondary}" Stretch="Fill" Data="{res:Icon RightAngleArrow}" Width="10" Height="12" />
</local:AngleButton>
<local:AngleBorder Direction="Left" Content="Address:" Padding="2,0" />
<local:AngleTextBox Direction="Out" TextBinding="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<UniformGrid Columns="4" Margin="20,0">
<local:AngleBorder Direction="Out" Content="Var:" />
<local:AngleTextBox Direction="Right" TextBinding="{Binding Variable, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,2,0" />
<local:AngleBorder Direction="Out" Content="Value:" Margin="2,0,0,0" />
<local:AngleTextBox Direction="Right" TextBinding="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />
</UniformGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<local:AngleButton Margin="8,2" Visibility="{Binding HasSubScripts, Converter={StaticResource BoolToVisibility}}" Command="{res:MethodCommand AddSubScript}" Direction="Out" Content="Add Table Script" />
</StackPanel>
<TextBlock Visibility="{Binding MoreScriptsTruncated, Converter={StaticResource BoolToVisibility}}" FontStyle="Italic"
Text="Further scripts truncated for performance. View the table data to see the remaining scripts." TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<local:AngleButton DataContext="{Binding PrimaryMap.MapScriptCollection}" Content="Add New Script" Direction="Out" Margin="2" Command="{res:MethodCommand AddScript}" />
<local:AngleButton DataContext="{Binding PrimaryMap.MapScriptCollection}" Content="Add New Script" Direction="Out" Margin="2" Command="{res:MethodCommand AddScript}"
Visibility="{Binding AllowAddMoreScripts, Converter={StaticResource BoolToVisibility}}"/>
</StackPanel>
</ScrollViewer>