// Create a file for an option group from given data. internal static void CreateOptionGroup(DirectoryInfo baseFolder, SelectType type, string name, int priority, int index, string desc, IEnumerable <ISubMod> subMods) { switch (type) { case SelectType.Multi: { var group = new MultiModGroup() { Name = name, Description = desc, Priority = priority, }; group.PrioritizedOptions.AddRange(subMods.OfType <SubMod>().Select((s, idx) => (s, idx))); IModGroup.Save(group, baseFolder, index); break; } case SelectType.Single: { var group = new SingleModGroup() { Name = name, Description = desc, Priority = priority, }; group.OptionData.AddRange(subMods.OfType <SubMod>()); IModGroup.Save(group, baseFolder, index); break; } } }
// Draw a single group selector as a combo box. // If a description is provided, add a help marker besides it. private void DrawSingleGroup(IModGroup group, int groupIdx) { if (group.Type != SelectType.Single || !group.IsOption) { return; } using var id = ImRaii.PushId(groupIdx); var selectedOption = _emptySetting ? 0 : ( int )_settings.Settings[groupIdx]; ImGui.SetNextItemWidth(_window._inputTextWidth.X * 3 / 4); using var combo = ImRaii.Combo(string.Empty, group[selectedOption].Name); if (combo) { for (var idx2 = 0; idx2 < group.Count; ++idx2) { if (ImGui.Selectable(group[idx2].Name, idx2 == selectedOption)) { Penumbra.CollectionManager.Current.SetModSetting(_mod.Index, groupIdx, ( uint )idx2); } } } combo.Dispose(); ImGui.SameLine(); if (group.Description.Length > 0) { ImGuiUtil.LabeledHelpMarker(group.Name, group.Description); } else { ImGui.TextUnformatted(group.Name); } }
private static void Target(ModPanel panel, IModGroup group, int groupIdx, int optionIdx) { // TODO drag options to other groups without options. using var target = ImRaii.DragDropTarget(); if (!target.Success || !ImGuiUtil.IsDropping(DragDropLabel)) { return; } if (_dragDropGroupIdx >= 0 && _dragDropOptionIdx >= 0) { if (_dragDropGroupIdx == groupIdx) { var sourceOption = _dragDropOptionIdx; panel._delayedActions.Enqueue(() => Penumbra.ModManager.MoveOption(panel._mod, groupIdx, sourceOption, optionIdx)); } else { // Move from one group to another by deleting, then adding the option. var sourceGroup = _dragDropGroupIdx; var sourceOption = _dragDropOptionIdx; var option = @group[_dragDropOptionIdx]; var priority = @group.OptionPriority(_dragDropGroupIdx); panel._delayedActions.Enqueue(() => { Penumbra.ModManager.DeleteOption(panel._mod, sourceGroup, sourceOption); Penumbra.ModManager.AddOption(panel._mod, groupIdx, option, priority); }); } } _dragDropGroupIdx = -1; _dragDropOptionIdx = -1; }
// Handle drag and drop to move options inside a group or into another group. private static void Source(IModGroup group, int groupIdx, int optionIdx) { using var source = ImRaii.DragDropSource(); if (!source) { return; } if (ImGui.SetDragDropPayload(DragDropLabel, IntPtr.Zero, 0)) { _dragDropGroupIdx = groupIdx; _dragDropOptionIdx = optionIdx; } ImGui.TextUnformatted($"Dragging option {group[ optionIdx ].Name} from group {group.Name}..."); }
// Draw a multi group selector as a bordered set of checkboxes. // If a description is provided, add a help marker in the title. private void DrawMultiGroup(IModGroup group, int groupIdx) { if (group.Type != SelectType.Multi || !group.IsOption) { return; } using var id = ImRaii.PushId(groupIdx); var flags = _emptySetting ? 0u : _settings.Settings[groupIdx]; Widget.BeginFramedGroup(group.Name, group.Description); for (var idx2 = 0; idx2 < group.Count; ++idx2) { var flag = 1u << idx2; var setting = (flags & flag) != 0; if (ImGui.Checkbox(group[idx2].Name, ref setting)) { flags = setting ? flags | flag : flags & ~flag; Penumbra.CollectionManager.Current.SetModSetting(_mod.Index, groupIdx, flags); } } Widget.EndFramedGroup(); }
// Draw a line for a single option. private static void EditOption(ModPanel panel, IModGroup group, int groupIdx, int optionIdx) { var option = group[optionIdx]; using var id = ImRaii.PushId(optionIdx); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.Selectable($"Option #{optionIdx + 1}"); Source(group, groupIdx, optionIdx); Target(panel, group, groupIdx, optionIdx); ImGui.TableNextColumn(); if (Input.Text("##Name", groupIdx, optionIdx, option.Name, out var newOptionName, 256, -1)) { Penumbra.ModManager.RenameOption(panel._mod, groupIdx, optionIdx, newOptionName); } ImGui.TableNextColumn(); if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), panel._window._iconButtonSize, "Delete this option.\nHold Control while clicking to delete.", !ImGui.GetIO().KeyCtrl, true)) { panel._delayedActions.Enqueue(() => Penumbra.ModManager.DeleteOption(panel._mod, groupIdx, optionIdx)); } ImGui.TableNextColumn(); if (group.Type == SelectType.Multi) { if (Input.Priority("##Priority", groupIdx, optionIdx, group.OptionPriority(optionIdx), out var priority, 50 * ImGuiHelpers.GlobalScale)) { Penumbra.ModManager.ChangeOptionPriority(panel._mod, groupIdx, optionIdx, priority); } ImGuiUtil.HoverTooltip("Option priority."); } }
// Draw a combo to select single or multi group and switch between them. private void DrawGroupCombo(IModGroup group, int groupIdx) {
private static bool MigrateV0ToV1(Mod mod, JObject json) { if (mod.FileVersion > 0) { return(false); } var swaps = json["FileSwaps"]?.ToObject <Dictionary <Utf8GamePath, FullPath> >() ?? new Dictionary <Utf8GamePath, FullPath>(); var groups = json["Groups"]?.ToObject <Dictionary <string, OptionGroupV0> >() ?? new Dictionary <string, OptionGroupV0>(); var priority = 1; var seenMetaFiles = new HashSet <FullPath>(); foreach (var group in groups.Values) { ConvertGroup(mod, group, ref priority, seenMetaFiles); } foreach (var unusedFile in mod.FindUnusedFiles().Where(f => !seenMetaFiles.Contains(f))) { if (unusedFile.ToGamePath(mod.ModPath, out var gamePath) && !mod._default.FileData.TryAdd(gamePath, unusedFile)) { PluginLog.Error($"Could not add {gamePath} because it already points to {mod._default.FileData[ gamePath ]}."); } } mod._default.FileSwapData.Clear(); mod._default.FileSwapData.EnsureCapacity(swaps.Count); foreach (var(gamePath, swapPath) in swaps) { mod._default.FileSwapData.Add(gamePath, swapPath); } mod._default.IncorporateMetaChanges(mod.ModPath, true); foreach (var(group, index) in mod.Groups.WithIndex()) { IModGroup.Save(group, mod.ModPath, index); } // Delete meta files. foreach (var file in seenMetaFiles.Where(f => f.Exists)) { try { File.Delete(file.FullName); } catch (Exception e) { PluginLog.Warning($"Could not delete meta file {file.FullName} during migration:\n{e}"); } } // Delete old meta files. var oldMetaFile = Path.Combine(mod.ModPath.FullName, "metadata_manipulations.json"); if (File.Exists(oldMetaFile)) { try { File.Delete(oldMetaFile); } catch (Exception e) { PluginLog.Warning($"Could not delete old meta file {oldMetaFile} during migration:\n{e}"); } } mod.FileVersion = 1; mod.SaveDefaultMod(); mod.SaveMeta(); return(true); }