Skip to content

Commit

Permalink
Jump to profile row from build warning.
Browse files Browse the repository at this point in the history
The implementation of this is a hack, since pages can't really talk to each other, and especially since the row isn't guaranteed to be visible through the filters. The hack forces it to be visible until the filters are changed, which actually requires multiple property change notifications and cycles to work correctly, but doesn't seem to be too noticeable in the UI, performance-wise.

Only really noticeable effect is that the row will sometimes appear at the top and sometimes in the middle, but is still always within the visible area. Should be an acceptable tradeoff.

Jump is activated by double-click. For now there's no additional link in the info panel, but it could be added easily if there's a need.

Introduction of `RecordKey` wasn't strictly necessary here, but it's a first step toward refactoring something that's very repetitive.

Fixes #14.
  • Loading branch information
focustense committed Jun 29, 2021
1 parent 0e6b108 commit 3423596
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 12 deletions.
11 changes: 11 additions & 0 deletions Focus.Apps.EasyNpc/Build/BuildChecker.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Focus.Apps.EasyNpc.Configuration;
using Focus.Apps.EasyNpc.GameData.Files;
using Focus.Apps.EasyNpc.GameData.Records;
using Focus.Apps.EasyNpc.Profile;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -80,13 +81,16 @@ private IEnumerable<BuildWarning> CheckMissingPlugins(IEnumerable<ProfileEvent>
.Select(x => npcConfigs.TryGetValue(Tuple.Create(x.BasePluginName, x.LocalFormIdHex), out var npc) ?
new
{
npc.BasePluginName,
npc.LocalFormIdHex,
npc.EditorId,
npc.Name,
FieldName = x.Field == NpcProfileField.FacePlugin ? "face" : "default",
PluginName = x.NewValue,
} : null)
.Where(x => x != null)
.Select(x => new BuildWarning(
new RecordKey(x.BasePluginName, x.LocalFormIdHex),
BuildWarningId.SelectedPluginRemoved,
WarningMessages.SelectedPluginRemoved(x.EditorId, x.Name, x.FieldName, x.PluginName)));
}
Expand All @@ -111,6 +115,7 @@ private IEnumerable<BuildWarning> CheckModPluginConsistency(
{
if (npc.RequiresFacegenData())
yield return new BuildWarning(
new RecordKey(npc),
BuildWarningId.FaceModNotSpecified,
WarningMessages.FaceModNotSpecified(npc.EditorId, npc.Name));
yield break;
Expand All @@ -120,13 +125,15 @@ private IEnumerable<BuildWarning> CheckModPluginConsistency(
if (!modPluginMap.IsModInstalled(npc.FaceModName))
{
yield return new BuildWarning(
new RecordKey(npc),
BuildWarningId.FaceModNotInstalled,
WarningMessages.FaceModNotInstalled(npc.EditorId, npc.Name, npc.FaceModName));
yield break;
}
if (!modsProvidingFacePlugin.Contains(npc.FaceModName))
yield return new BuildWarning(
npc.FacePluginName,
new RecordKey(npc),
BuildWarningId.FaceModPluginMismatch,
WarningMessages.FaceModPluginMismatch(npc.EditorId, npc.Name, npc.FaceModName, npc.FacePluginName));
var faceMeshFileName = FileStructure.GetFaceMeshFileName(npc.BasePluginName, npc.LocalFormIdHex);
Expand All @@ -142,16 +149,19 @@ private IEnumerable<BuildWarning> CheckModPluginConsistency(
// include it isn't loaded, i.e. due to the mod or plugin being disabled.
yield return new BuildWarning(
npc.FacePluginName,
new RecordKey(npc),
BuildWarningId.FaceModMissingFaceGen,
WarningMessages.FaceModMissingFaceGen(npc.EditorId, npc.Name, npc.FaceModName));
else if (!npc.RequiresFacegenData() && (hasLooseFacegen || hasArchiveFacegen))
yield return new BuildWarning(
npc.FacePluginName,
new RecordKey(npc),
BuildWarningId.FaceModExtraFaceGen,
WarningMessages.FaceModExtraFaceGen(npc.EditorId, npc.Name, npc.FaceModName));
else if (hasLooseFacegen && hasArchiveFacegen)
yield return new BuildWarning(
npc.FacePluginName,
new RecordKey(npc),
BuildWarningId.FaceModMultipleFaceGen,
WarningMessages.FaceModMultipleFaceGen(npc.EditorId, npc.Name, npc.FaceModName));
}
Expand Down Expand Up @@ -196,6 +206,7 @@ private static IEnumerable<BuildWarning> CheckWigs(
.Where(x => x.Wig != null && (!enableDewiggify || !matchedWigKeys.Contains(x.Wig.Key)))
.Select(x => enableDewiggify ?
new BuildWarning(
new RecordKey(x.Npc),
x.Wig.IsBald ? BuildWarningId.FaceModWigNotMatchedBald : BuildWarningId.FaceModWigNotMatched,
x.Wig.IsBald ?
WarningMessages.FaceModWigNotMatchedBald(
Expand Down
8 changes: 7 additions & 1 deletion Focus.Apps.EasyNpc/Build/BuildPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,13 @@
</ui:ScrollViewerEx>
</GroupBox>
</Border>
<ListBox ItemsSource="{Binding Problems}" DisplayMemberPath="Message" SelectedItem="{Binding SelectedWarning}" ScrollViewer.VerticalScrollBarVisibility="Visible" Margin="0,8,0,0"/>
<ListBox x:Name="WarningsListBox"
ItemsSource="{Binding Problems}"
DisplayMemberPath="Message"
SelectedItem="{Binding SelectedWarning}"
Margin="0,8,0,0"
ScrollViewer.VerticalScrollBarVisibility="Visible"
MouseDoubleClick="WarningsListBox_MouseDoubleClick"/>
</DockPanel>
</Grid>
</GroupBox>
Expand Down
10 changes: 9 additions & 1 deletion Focus.Apps.EasyNpc/Build/BuildPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Windows;

using System.Windows.Input;
using TKey = Mutagen.Bethesda.Plugins.FormKey;

namespace Focus.Apps.EasyNpc.Build
Expand Down Expand Up @@ -56,5 +56,13 @@ private void SkipProblemsButton_Click(object sender, RoutedEventArgs e)
{
Model.IsProblemCheckerVisible = false;
}

private void WarningsListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
if (WarningsListBox.SelectedItem is BuildWarning buildWarning && buildWarning.RecordKey != null)
Model.ExpandWarning(buildWarning);
}
}
}
10 changes: 8 additions & 2 deletions Focus.Apps.EasyNpc/Build/BuildViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Focus.Apps.EasyNpc.Build
Expand All @@ -20,6 +18,7 @@ public class BuildViewModel<TKey> : INotifyPropertyChanged
where TKey : struct
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<BuildWarning> WarningExpanded;

public bool EnableDewiggify { get; set; } = true;
[DependsOn("Problems")]
Expand Down Expand Up @@ -126,6 +125,13 @@ public void DismissProblems()
IsReadyToBuild = true;
}

public void ExpandWarning(BuildWarning warning)
{
// There isn't actually any notion of expansion in this context, aside from the info panel which is more of
// a "select" action. This just serves as a signal for an external component to handle the request.
WarningExpanded?.Invoke(this, warning);
}

public void OpenBuildOutput()
{
if (!Directory.Exists(OutputDirectory)) // In case user moved/deleted after the build
Expand Down
15 changes: 14 additions & 1 deletion Focus.Apps.EasyNpc/Build/BuildWarnings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Focus.Apps.EasyNpc.GameData.Records;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
Expand Down Expand Up @@ -29,6 +30,7 @@ public class BuildWarning
// Used for help links, if provided.
public BuildWarningId? Id { get; init; }
public string Message { get; init; }
public RecordKey RecordKey { get; init; }
public string PluginName { get; init; }

public BuildWarning() { }
Expand All @@ -43,11 +45,22 @@ public BuildWarning(BuildWarningId id, string message) : this(message)
Id = id;
}

public BuildWarning(RecordKey key, BuildWarningId id, string message) : this(id, message)
{
RecordKey = key;
}

public BuildWarning(string pluginName, BuildWarningId id, string message)
: this(id, message)
{
PluginName = pluginName;
}

public BuildWarning(string pluginName, RecordKey key, BuildWarningId id, string message)
: this(pluginName, id, message)
{
RecordKey = key;
}
}

public class BuildWarningIdsToTextConverter : IValueConverter
Expand Down
4 changes: 1 addition & 3 deletions Focus.Apps.EasyNpc/GameData/Records/Npc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@

namespace Focus.Apps.EasyNpc.GameData.Records
{
public interface INpc<TKey>
public interface INpc<TKey> : IRecordKey
where TKey : struct
{
string BasePluginName { get; }
string EditorId { get; }
bool IsFemale { get; }
TKey Key { get; }
string LocalFormIdHex { get; }
string Name { get; }
IReadOnlyList<NpcOverride<TKey>> Overrides { get; }
}
Expand Down
20 changes: 20 additions & 0 deletions Focus.Apps.EasyNpc/GameData/Records/RecordKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Focus.Apps.EasyNpc.GameData.Records
{
public interface IRecordKey
{
string BasePluginName { get; }
string LocalFormIdHex { get; }
}

public record RecordKey(string BasePluginName, string LocalFormIdHex) : IRecordKey
{
public RecordKey(IRecordKey key) : this(key.BasePluginName, key.LocalFormIdHex) { }

public bool Equals(IRecordKey key)
{
return key.BasePluginName == BasePluginName && key.LocalFormIdHex == LocalFormIdHex;
}
}
}
9 changes: 9 additions & 0 deletions Focus.Apps.EasyNpc/Main/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public abstract class MainViewModel<TKey> :
IProfileContainer<TKey>, ISettingsContainer
where TKey : struct
{
public event EventHandler<string> PageNavigationRequested;
public event PropertyChangedEventHandler PropertyChanged;

public BuildViewModel<TKey> Build { get; private set; }
Expand Down Expand Up @@ -88,11 +89,19 @@ public MainViewModel(bool isFirstLaunch, bool debugMode)
Build = new BuildViewModel<TKey>(
gameDataEditor.ArchiveProvider, buildChecker, gameDataEditor.MergedPluginBuilder,
Loader.ModPluginMapFactory, Profile.GetAllNpcConfigurations(), wigResolver, faceGenEditor, Logger);
Build.WarningExpanded += BuildViewModel_WarningExpanded;
IsLoaded = true;
};
}

protected abstract IGameDataEditor<TKey> CreateEditor();

private void BuildViewModel_WarningExpanded(object sender, BuildWarning e)
{
var found = Profile.SelectNpc(e.RecordKey);
if (found)
PageNavigationRequested?.Invoke(this, "profile");
}
}

public class MainViewModel : MainViewModel<FormKey>
Expand Down
12 changes: 12 additions & 0 deletions Focus.Apps.EasyNpc/Main/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;

namespace Focus.Apps.EasyNpc.Main
Expand Down Expand Up @@ -55,6 +56,7 @@ public MainWindow(MainViewModel model)
model.Settings.WelcomeAcked += (sender, e) =>
(MainNavigationView.MenuItems[0] as NavigationViewItem).IsSelected = true;
}
model.PageNavigationRequested += (sender, pageName) => SelectPage(pageName);
}

private void Navigate(string pageName)
Expand All @@ -73,6 +75,16 @@ private void NavigationView_SelectionChanged(NavigationView sender, NavigationVi
else
Navigate((string)args.SelectedItemContainer.Tag);
}

private void SelectPage(string pageName)
{
var matchingItem = MainNavigationView.MenuItems
.OfType<NavigationViewItem>()
.Where(x => string.Equals(x.Tag?.ToString(), pageName, StringComparison.OrdinalIgnoreCase))
.SingleOrDefault();
if (matchingItem != null)
MainNavigationView.SelectedItem = matchingItem;
}
}

record NavLink(string Title, Type PageType, Func<MainViewModel, object> ViewModelSelector);
Expand Down
2 changes: 1 addition & 1 deletion Focus.Apps.EasyNpc/Profile/NpcConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Focus.Apps.EasyNpc.Profile
{
public class NpcConfiguration<TKey> : INotifyPropertyChanged
public class NpcConfiguration<TKey> : INotifyPropertyChanged, IRecordKey
where TKey : struct
{
private static readonly HashSet<string> DlcPluginNames = new HashSet<string>(
Expand Down
Loading

0 comments on commit 3423596

Please sign in to comment.