feat: add basic tutorial system

This commit is contained in:
Anna 2022-07-09 12:30:28 -04:00
parent 400ff9e56a
commit daa1be394a
3 changed files with 168 additions and 1 deletions

View File

@ -40,6 +40,7 @@ internal class ConfigInfo {
public Dictionary<Guid, ushort> ChannelColors = new();
public Dictionary<Guid, string> ChannelMarkers = new();
public Dictionary<Guid, XivChatType> ChannelChannels = new();
public int TutorialStep;
internal string GetName(Guid id) => this.Channels.TryGetValue(id, out var channel)
? channel.Name

View File

@ -123,12 +123,28 @@ internal class PluginUi : IDisposable {
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Help")) {
this.DrawHelp();
ImGui.EndTabItem();
}
ImGui.EndTabBar();
}
ImGui.End();
}
private void DrawHelp() {
ImGui.PushTextWrapPos();
if (ImGui.Button("Reset tutorial")) {
this.Plugin.ConfigInfo.TutorialStep = 0;
this.Plugin.SaveConfig();
}
ImGui.PopTextWrapPos();
}
private void DrawSettings() {
var anyChanged = false;
@ -434,6 +450,8 @@ internal class PluginUi : IDisposable {
private string _rename = string.Empty;
private void DrawList() {
var anyChanged = false;
ImGui.PushFont(UiBuilder.IconFont);
var syncButton = ImGui.CalcTextSize(FontAwesomeIcon.Sync.ToIconString()).X
@ -450,12 +468,16 @@ internal class PluginUi : IDisposable {
Task.Run(async () => await this.Plugin.Client.ListAll());
}
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 1);
ImGui.SameLine(addOffset);
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString())) {
ImGui.OpenPopup("create-channel-popup");
}
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 0);
ImGui.PopFont();
if (ImGui.BeginPopup("create-channel-popup")) {
@ -480,8 +502,13 @@ internal class PluginUi : IDisposable {
ImGui.EndPopup();
}
if (this.Plugin.Client.Channels.Count == 0) {
ImGui.TextUnformatted("You aren't in any linkshells yet. Try creating or joining one first.");
goto AfterTable;
}
if (ImGui.BeginTable("ecls-list", 2, ImGuiTableFlags.Resizable | ImGuiTableFlags.SizingFixedFit)) {
ImGui.TableSetupColumn("##channels", ImGuiTableColumnFlags.None);
ImGui.TableSetupColumn("##channels", ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("##members", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableNextRow();
@ -503,6 +530,7 @@ internal class PluginUi : IDisposable {
if (ImGui.TableSetColumnIndex(0)) {
if (ImGui.BeginChild("channel-list", childSize)) {
var first = true;
foreach (var id in orderedChannels) {
this.Plugin.ConfigInfo.Channels.TryGetValue(id, out var info);
var name = info?.Name ?? "???";
@ -522,6 +550,12 @@ internal class PluginUi : IDisposable {
Task.Run(async () => await this.Plugin.Client.ListMembers(id));
}
if (first) {
first = false;
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 2);
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 3);
}
if (ImGui.BeginPopupContextItem()) {
var invited = this.Plugin.Client.InvitedChannels.ContainsKey(id);
if (invited) {
@ -663,6 +697,7 @@ internal class PluginUi : IDisposable {
rank = Rank.Member;
}
var first = true;
foreach (var member in channel.Members) {
if (!member.Online) {
ImGui.PushStyleColor(ImGuiCol.Text, disabledColour);
@ -676,6 +711,12 @@ internal class PluginUi : IDisposable {
}
}
if (first) {
first = false;
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 4);
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 5);
}
if (ImGui.BeginPopupContextItem($"{this._selectedChannel}-{member.Name}@{member.World}-context")) {
if (rank == Rank.Admin) {
if (member.Rank is not (Rank.Admin or Rank.Invited)) {
@ -734,5 +775,10 @@ internal class PluginUi : IDisposable {
ImGui.EndTable();
}
AfterTable:
if (anyChanged) {
this.Plugin.SaveConfig();
}
}
}

View File

@ -1,3 +1,4 @@
using System.Numerics;
using System.Text;
using Dalamud.Interface;
using ImGuiNET;
@ -5,6 +6,56 @@ using ImGuiNET;
namespace ExtraChat.Util;
internal static class ImGuiUtil {
private static readonly Dictionary<int, string?[]> Tutorials = new() {
[0] = new[] {
"Create a linkshell",
"You can use this button to create a new linkshell.",
"Alternatively, you can be invited by a friend to join a linkshell.",
},
[1] = new[] {
"Refresh data",
"This button will refresh all data about your linkshells.",
"Generally, you shouldn't need to press this. Clicking on a linkshell refreshes the member list.",
},
[2] = new[] {
"Manage linkshells you're in",
"Clicking on a linkshell in this list will show you its members in the pane to the right.",
"You can also right-click the linkshell name to open a menu with various options.",
"If you were invited to a linkshell, you can accept the invitation in this menu.",
},
[3] = new[] {
"Talking in a linkshell",
"The number displayed before the linkshell name is the linkshell's number.",
"You can change this number by right-clicking.",
"This number is used to determine the command you should use to talk in the linkshell.",
"For example, linkshell 1 would use the command /ecl1, linkshell 2 would use /ecl2, etc.",
null,
"Click on this linkshell to see the member list.",
},
[4] = new[] {
"Members in a linkshell",
"Inside the member list, each member is shown with an optional symbol indicating their rank.",
null,
"Admins have this symbol: ★",
"Moderators have this symbol: ☆",
"Normal members have no symbol.",
"Invited members have this symbol: ?",
null,
"Members also appear dimmed when they are offline.",
},
[5] = new[] {
"Managing members in a linkshell",
"Moderators and admins of a linkshell can right-click on members in the member list to open a menu with various options.",
"Many options require holding the Control key to enable so that they aren't accidentally activated.",
},
};
internal static bool IconButton(FontAwesomeIcon icon, string? id = null, string? tooltip = null) {
var label = icon.ToIconString();
if (id != null) {
@ -62,6 +113,75 @@ internal static class ImGuiUtil {
return selectable && confirmHeld;
}
internal static bool Tutorial(Plugin plugin, int step) {
var save = false;
ref var current = ref plugin.ConfigInfo.TutorialStep;
if (current < 0 || current != step) {
return save;
}
if (!Tutorials.TryGetValue(step, out var strings)) {
return save;
}
var max = Tutorials.Keys.Max();
const string popupId = "extrachat-tutorial";
ImGui.OpenPopup(popupId);
ImGui.GetForegroundDrawList().AddRect(
ImGui.GetItemRectMin() - new Vector2(2) * ImGuiHelpers.GlobalScale,
ImGui.GetItemRectMax() + new Vector2(2) * ImGuiHelpers.GlobalScale,
ImGui.GetColorU32(new Vector4(1, 0, 0, 1))
);
ImGui.SetNextWindowPos(ImGui.GetItemRectMax() + new Vector2(2) * ImGuiHelpers.GlobalScale);
ImGui.SetNextWindowSize(new Vector2(350, 0) * ImGuiHelpers.GlobalScale);
if (!ImGui.BeginPopup(popupId, ImGuiWindowFlags.AlwaysAutoResize)) {
return save;
}
ImGui.PushFont(UiBuilder.DefaultFont);
ImGui.PushTextWrapPos();
ImGui.TextUnformatted(strings[0]);
ImGui.Separator();
foreach (var body in strings[1..]) {
if (body == null) {
ImGui.Spacing();
continue;
}
ImGui.TextUnformatted(body);
}
if (step == max) {
if (ImGui.Button("Finish")) {
current = -1;
save = true;
}
} else {
if (ImGui.Button("Next")) {
current += 1;
save = true;
}
}
ImGui.SameLine();
if (ImGui.Button("Skip tutorial")) {
current = -1;
save = true;
}
ImGui.PopTextWrapPos();
ImGui.PopFont();
ImGui.EndPopup();
return save;
}
}
[Flags]