Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3db865a
.
DirkDoes Apr 4, 2025
35b8927
make sure there are no uselless errors in the mods page
DirkDoes Apr 4, 2025
a9dc84b
add validation
DirkDoes Apr 4, 2025
5abc2a2
fix for valid
DirkDoes Apr 4, 2025
3670a01
Merge branch 'dev' into fix/spelling-mistakes-+-mod-rename-bug
DirkDoes Apr 5, 2025
73d6a16
create style for the text field
DirkDoes Apr 5, 2025
f0541da
starting to create FedbackTExtBox
DirkDoes Apr 5, 2025
a7f86ab
reslove conflicts
DirkDoes Apr 5, 2025
3f6abfd
Merge branch 'dev' into fix/spelling-mistakes-+-mod-rename-bug
DirkDoes Apr 6, 2025
c03ab4f
add data context to feedbackTextBox
DirkDoes Apr 6, 2025
2a6df85
Merge branch 'dev' into fix/spelling-mistakes-+-mod-rename-bug
DirkDoes Apr 6, 2025
1a48bfb
add name validation to the popups
DirkDoes Apr 6, 2025
17dd2ff
change how popups work with scaling
DirkDoes Apr 6, 2025
2ea1181
no slashes
DirkDoes Apr 6, 2025
3f20433
no invalid file name chars
DirkDoes Apr 6, 2025
b2adbfe
Merge branch 'dev' into fix/spelling-mistakes-+-mod-rename-bug
DirkDoes Apr 7, 2025
627b71f
mii name validation
DirkDoes Apr 7, 2025
ae04c34
Update ModManager.cs
DirkDoes Apr 7, 2025
23050f4
make sure no anoying popup and validation on all selection
DirkDoes Apr 7, 2025
78039d7
Update TextInputWindow.axaml.cs
DirkDoes Apr 7, 2025
5ecea2b
Update YesNoWindow.axaml.cs
DirkDoes Apr 7, 2025
5ff2e8f
Update ModDetailViewer.axaml.cs
DirkDoes Apr 7, 2025
f08b056
Update TextInputWindow.axaml.cs
DirkDoes Apr 7, 2025
1f509a5
Update TextInputWindow.axaml.cs
DirkDoes Apr 7, 2025
f716846
Update ModManager.cs
DirkDoes Apr 7, 2025
585a396
try to fix it
DirkDoes Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions WheelWizard/Services/Launcher/RrLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace WheelWizard.Services.Launcher;

public class RrLauncher : ILauncher
{
public string GameTitle { get; } = "RetroRewind";
public string GameTitle { get; } = "Retro Rewind";
private static string RrLaunchJsonFilePath => PathManager.RrLaunchJsonFilePath;

public async Task Launch()
Expand All @@ -38,7 +38,7 @@ public async Task Launch()
var dolphinLaunchType = (bool)SettingsManager.LAUNCH_WITH_DOLPHIN.Get() ? "" : "-b";
DolphinLaunchHelper.LaunchDolphin(
$"{dolphinLaunchType} -e \"{Path.GetFullPath(RrLaunchJsonFilePath)}\" --config=Dolphin.Core.EnableCheats=False"
);
);
}
catch (Exception ex)
{
Expand Down
95 changes: 62 additions & 33 deletions WheelWizard/Services/ModManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class ModManager : INotifyPropertyChanged
public static ModManager Instance => _instance.Value;

private ObservableCollection<Mod> _mods;

public ObservableCollection<Mod> Mods
{
get => _mods;
Expand Down Expand Up @@ -83,7 +84,7 @@ public void AddMod(Mod mod)
mod.PropertyChanged += Mod_PropertyChanged;
Mods.Add(mod);
SortModsByPriority();
SaveModsAsync();
SaveModsAsync();
}

public void RemoveMod(Mod mod)
Expand All @@ -102,7 +103,7 @@ private void Mod_PropertyChanged(object sender, PropertyChangedEventArgs e)
e.PropertyName != nameof(Mod.Author) &&
e.PropertyName != nameof(Mod.ModID) &&
e.PropertyName != nameof(Mod.Priority)) return;

SaveModsAsync();
SortModsByPriority();
}
Expand Down Expand Up @@ -140,6 +141,7 @@ private async Task CombineFilesIntoSingleModAsync(string[] filePaths)
var modName = await new TextInputWindow()
.SetMainText("Mod name:")
.SetPlaceholderText("Enter mod name...")
.SetValidation(ValidateModName)
.ShowDialog();
if (!IsValidName(modName)) return;
var tempZipPath = Path.Combine(Path.GetTempPath(), $"{modName}.zip");
Expand All @@ -153,6 +155,7 @@ private async Task CombineFilesIntoSingleModAsync(string[] filePaths)
zipArchive.CreateEntryFromFile(filePath, entryName, CompressionLevel.Optimal);
}
}

await ModInstallation.InstallModFromFileAsync(tempZipPath, modName, author: "-1", modID: -1);
if (File.Exists(tempZipPath))
File.Delete(tempZipPath);
Expand All @@ -169,30 +172,55 @@ public void ToggleAllMods(bool enable)
{
mod.IsEnabled = enable;
}

_isProcessing = !_isProcessing;
OnPropertyChanged(nameof(Mods));
}


// TODO: Use this validation method when refactoring the ModManager
public OperationResult ValidateModName(string? oldName, string newName)
{
if (string.IsNullOrWhiteSpace(newName))
return Fail("Mod name cannot be empty.");

if (ModInstallation.ModExists(Mods, newName))
return Fail("Mod name already exists.");

if (newName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
return Fail("Mod name contains illegal characters.");

return Ok();
}

public OperationResult ValidateRenameModName(string? oldName, string newName)
{
return oldName == newName ? Ok() : ValidateModName(oldName, newName);
}


public async void RenameMod(Mod selectedMod)
{
var oldTitle = selectedMod.Title;
var newTitle = await new TextInputWindow()
var newTitle = await new TextInputWindow()
.SetMainText("Mod Name")
.SetInitialText(oldTitle)
.SetExtraText($"Changing name from: {oldTitle}")
.SetPlaceholderText("Enter mod name...")
.SetValidation(ValidateRenameModName)
.ShowDialog();


if (oldTitle == newTitle || newTitle == null) return;
// we don't want to return an error if the name is the same as before, or if the user cancels
if (!IsValidName(newTitle)) return;

var oldDirectoryName = PathManager.GetModDirectoryPath(oldTitle);
var newDirectoryName = PathManager.GetModDirectoryPath(newTitle);

// Check if the old directory exists
if (!Directory.Exists(oldDirectoryName)) return;

// var oldIniPath = Path.Combine(oldDirectoryName, $"{oldTitle}.ini");

GC.Collect();
GC.WaitForPendingFinalizers();

Expand All @@ -217,26 +245,22 @@ public async void RenameMod(Mod selectedMod)
await selectedMod.SaveToIniAsync(newIniPath);
SaveModsAsync();
OnPropertyChanged(nameof(Mods));

if (Directory.Exists(oldDirectoryName))
Directory.Delete(oldDirectoryName, true);
}
catch (IOException ex)
{
ErrorOccurred($"Failed to rename mod directory: {ex.Message}");
}
finally
{
if (Directory.Exists(oldDirectoryName))
{
Directory.Delete(oldDirectoryName, true);
}
}

ReloadAsync();
}


public async void DeleteMod(Mod selectedMod)
{
var areTheySure = await new YesNoWindow().SetMainText(Humanizer.ReplaceDynamic(Phrases.PopupText_SureDeleteQuestion, selectedMod.Title)).AwaitAnswer();
var areTheySure = await new YesNoWindow()
.SetMainText(Humanizer.ReplaceDynamic(Phrases.PopupText_SureDeleteQuestion, selectedMod.Title)).AwaitAnswer();
if (!areTheySure) return;

var modDirectory = PathManager.GetModDirectoryPath(selectedMod.Title);
Expand All @@ -246,16 +270,18 @@ public async void DeleteMod(Mod selectedMod)
RemoveMod(selectedMod);
return;
}

try
{
GC.Collect();
GC.WaitForPendingFinalizers();
var di = new DirectoryInfo(modDirectory);
di.Attributes &= ~FileAttributes.ReadOnly;
di.Attributes &= ~FileAttributes.ReadOnly;
foreach (var file in di.EnumerateFiles("*", SearchOption.AllDirectories))
{
file.Attributes &= ~FileAttributes.ReadOnly;
}

Directory.Delete(modDirectory, true); // true for recursive deletion
RemoveMod(selectedMod);
}
Expand All @@ -272,26 +298,25 @@ public void OpenModFolder(Mod selectedMod)
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = modDirectory,
UseShellExecute = true,
Verb = "open"
FileName = modDirectory, UseShellExecute = true, Verb = "open"
});
}
else
{
ErrorOccurred(Phrases.PopupText_NoModFolder);
}
}

public void DeleteModById(int modId)
{
var modToDelete = Mods.FirstOrDefault(mod => mod.ModID == modId);

if (modToDelete == null)
{
ErrorOccurred($"No mod found with ID: {modId}");
return;
}

DeleteMod(modToDelete);
}

Expand All @@ -305,6 +330,7 @@ public void MoveMod(Mod movedMod, int newIndex)
{
Mods[i].Priority = i;
}

SaveModsAsync();
OnPropertyChanged(nameof(Mods));
}
Expand All @@ -329,18 +355,20 @@ private void ErrorOccurred(string? errorMessage)
{
new MessageBoxWindow()
.SetMessageType(MessageBoxWindow.MessageType.Error)
.SetTitleText("An error occured")
.SetTitleText("An error occurred")
.SetInfoText(errorMessage)
.Show();
}

#region PropertyChanged

public event PropertyChangedEventHandler? PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

#endregion

public void DecreasePriority(Mod mod)
Expand All @@ -354,6 +382,7 @@ public void DecreasePriority(Mod mod)
.Show();
return;
}

if (mod.Priority == GetLowestActivePriority() || Mods.Count == 1)
{
new MessageBoxWindow()
Expand All @@ -363,13 +392,13 @@ public void DecreasePriority(Mod mod)
.Show();
return;
}

// Find mod with next lower priority value
var modAbove = Mods.Where(m => m.Priority < mod.Priority)
.OrderByDescending(m => m.Priority)
.FirstOrDefault();
if (modAbove == null) return;

(modAbove.Priority, mod.Priority) = (mod.Priority, modAbove.Priority);

SortModsByPriority();
Expand All @@ -386,7 +415,7 @@ public void IncreasePriority(Mod mod)
.Show();
return;
}

if (mod.Priority == GetHighestActivePriority() || Mods.Count == 1)
{
new MessageBoxWindow()
Expand All @@ -396,21 +425,21 @@ public void IncreasePriority(Mod mod)
.Show();
return;
}

// Find mod with next higher priority value
var modBelow = Mods.Where(m => m.Priority > mod.Priority)
.OrderBy(m => m.Priority)
.FirstOrDefault();

if (modBelow == null) return; // Should not happen but just in case

// Swap priorities
(modBelow.Priority, mod.Priority) = (mod.Priority, modBelow.Priority);

SortModsByPriority();
SaveModsAsync();
}
public int GetLowestActivePriority() =>Mods.Min(m => m.Priority);

public int GetLowestActivePriority() => Mods.Min(m => m.Priority);
public int GetHighestActivePriority() => Mods.Max(m => m.Priority);
}
52 changes: 26 additions & 26 deletions WheelWizard/Views/App.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,52 @@
xmlns:converters="clr-namespace:WheelWizard.Views.Converters"
x:Class="WheelWizard.Views.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.Resources>
<ResourceDictionary>
<FontFamily x:Key="DefaultFont">avares://WheelWizard/Resources/Fonts/Roboto-Regular.ttf#Roboto, avares://WheelWizard/Resources/Fonts/ctmkf.ttf#CTMKF</FontFamily>

<!-- CONSTANTS -->
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="Styles/Resources/Constants.axaml"/>
<ResourceInclude Source="Styles/Resources/Icons.axaml"/>
<ResourceInclude Source="Styles/Resources/Constants.axaml" />
<ResourceInclude Source="Styles/Resources/Icons.axaml" />
</ResourceDictionary.MergedDictionaries>

<!-- CONVERTERS -->
<converters:IsEqualToConverter x:Key="IsEqualTo"/>
<converters:IsEqualToConverter x:Key="IsEqualTo" />
</ResourceDictionary>
</Application.Resources>


<Application.Styles>
<FluentTheme />
<!-- STYLES -->
<StyleInclude Source="Styles/Styles/ButtonStyles.axaml"/>
<StyleInclude Source="Styles/Styles/TextStyles.axaml"/>
<StyleInclude Source="Styles/Styles/ToggleButtonStyles.axaml"/>
<StyleInclude Source="Styles/Styles/MiscStyles.axaml"/>
<StyleInclude Source="Styles/Styles/TextBoxStyles.axaml"/>
<StyleInclude Source="Styles/Styles/DropdownStyles.axaml"/>
<StyleInclude Source="Styles/Styles/ButtonStyles.axaml" />
<StyleInclude Source="Styles/Styles/TextStyles.axaml" />
<StyleInclude Source="Styles/Styles/ToggleButtonStyles.axaml" />
<StyleInclude Source="Styles/Styles/MiscStyles.axaml" />
<StyleInclude Source="Styles/Styles/TextBoxStyles.axaml" />
<StyleInclude Source="Styles/Styles/DropdownStyles.axaml" />

<!-- STANDARD COMPONENTS -->
<StyleInclude Source="Components/StandardLibrary/IconLabel.axaml"/>
<StyleInclude Source="Components/StandardLibrary/IconLabelButton.axaml"/>
<StyleInclude Source="Components/StandardLibrary/Button.axaml"/>
<StyleInclude Source="Components/StandardLibrary/StateBox.axaml"/>
<StyleInclude Source="Components/StandardLibrary/EmptyPageInfo.axaml"/>
<StyleInclude Source="Components/StandardLibrary/FormFieldLabel.axaml"/>
<StyleInclude Source="Components/StandardLibrary/LoadingIcon.axaml"/>
<StyleInclude Source="Components/StandardLibrary/SidebarRadioButton.axaml"/>
<StyleInclude Source="Components/StandardLibrary/IconLabel.axaml" />
<StyleInclude Source="Components/StandardLibrary/IconLabelButton.axaml" />
<StyleInclude Source="Components/StandardLibrary/Button.axaml" />
<StyleInclude Source="Components/StandardLibrary/StateBox.axaml" />
<StyleInclude Source="Components/StandardLibrary/EmptyPageInfo.axaml" />
<StyleInclude Source="Components/StandardLibrary/FormFieldLabel.axaml" />
<StyleInclude Source="Components/StandardLibrary/LoadingIcon.axaml" />
<StyleInclude Source="Components/StandardLibrary/SidebarRadioButton.axaml" />

<!-- PAGE SPECIFIC COMPONENTS -->
<StyleInclude Source="Components/WhWzLibrary/ModBrowserListItem.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/DetailedProfileBox.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/FriendsListItem.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/CurrentUserProfile.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/PlayerListItem.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/Badge.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/MiiImages/MiiImageLoader.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/MiiImages/MiiCarousel.axaml"/>
<StyleInclude Source="Components/WhWzLibrary/ModBrowserListItem.axaml" />
<StyleInclude Source="Components/WhWzLibrary/DetailedProfileBox.axaml" />
<StyleInclude Source="Components/WhWzLibrary/FriendsListItem.axaml" />
<StyleInclude Source="Components/WhWzLibrary/CurrentUserProfile.axaml" />
<StyleInclude Source="Components/WhWzLibrary/PlayerListItem.axaml" />
<StyleInclude Source="Components/WhWzLibrary/Badge.axaml" />
<StyleInclude Source="Components/WhWzLibrary/MiiImages/MiiImageLoader.axaml" />
<StyleInclude Source="Components/WhWzLibrary/MiiImages/MiiCarousel.axaml" />
</Application.Styles>
</Application>
Loading
Loading