feat: add basic tutorial system
This commit is contained in:
parent
8ce95d22bb
commit
61207524fa
|
@ -40,6 +40,7 @@ internal class ConfigInfo {
|
||||||
public Dictionary<Guid, ushort> ChannelColors = new();
|
public Dictionary<Guid, ushort> ChannelColors = new();
|
||||||
public Dictionary<Guid, string> ChannelMarkers = new();
|
public Dictionary<Guid, string> ChannelMarkers = new();
|
||||||
public Dictionary<Guid, XivChatType> ChannelChannels = new();
|
public Dictionary<Guid, XivChatType> ChannelChannels = new();
|
||||||
|
public int TutorialStep;
|
||||||
|
|
||||||
internal string GetName(Guid id) => this.Channels.TryGetValue(id, out var channel)
|
internal string GetName(Guid id) => this.Channels.TryGetValue(id, out var channel)
|
||||||
? channel.Name
|
? channel.Name
|
||||||
|
|
|
@ -123,12 +123,28 @@ internal class PluginUi : IDisposable {
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginTabItem("Help")) {
|
||||||
|
this.DrawHelp();
|
||||||
|
ImGui.EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.EndTabBar();
|
ImGui.EndTabBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.End();
|
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() {
|
private void DrawSettings() {
|
||||||
var anyChanged = false;
|
var anyChanged = false;
|
||||||
|
|
||||||
|
@ -434,6 +450,8 @@ internal class PluginUi : IDisposable {
|
||||||
private string _rename = string.Empty;
|
private string _rename = string.Empty;
|
||||||
|
|
||||||
private void DrawList() {
|
private void DrawList() {
|
||||||
|
var anyChanged = false;
|
||||||
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
|
||||||
var syncButton = ImGui.CalcTextSize(FontAwesomeIcon.Sync.ToIconString()).X
|
var syncButton = ImGui.CalcTextSize(FontAwesomeIcon.Sync.ToIconString()).X
|
||||||
|
@ -450,12 +468,16 @@ internal class PluginUi : IDisposable {
|
||||||
Task.Run(async () => await this.Plugin.Client.ListAll());
|
Task.Run(async () => await this.Plugin.Client.ListAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 1);
|
||||||
|
|
||||||
ImGui.SameLine(addOffset);
|
ImGui.SameLine(addOffset);
|
||||||
|
|
||||||
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString())) {
|
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString())) {
|
||||||
ImGui.OpenPopup("create-channel-popup");
|
ImGui.OpenPopup("create-channel-popup");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
anyChanged |= ImGuiUtil.Tutorial(this.Plugin, 0);
|
||||||
|
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
|
|
||||||
if (ImGui.BeginPopup("create-channel-popup")) {
|
if (ImGui.BeginPopup("create-channel-popup")) {
|
||||||
|
@ -480,8 +502,13 @@ internal class PluginUi : IDisposable {
|
||||||
ImGui.EndPopup();
|
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)) {
|
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.TableSetupColumn("##members", ImGuiTableColumnFlags.WidthStretch);
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
|
@ -503,6 +530,7 @@ internal class PluginUi : IDisposable {
|
||||||
|
|
||||||
if (ImGui.TableSetColumnIndex(0)) {
|
if (ImGui.TableSetColumnIndex(0)) {
|
||||||
if (ImGui.BeginChild("channel-list", childSize)) {
|
if (ImGui.BeginChild("channel-list", childSize)) {
|
||||||
|
var first = true;
|
||||||
foreach (var id in orderedChannels) {
|
foreach (var id in orderedChannels) {
|
||||||
this.Plugin.ConfigInfo.Channels.TryGetValue(id, out var info);
|
this.Plugin.ConfigInfo.Channels.TryGetValue(id, out var info);
|
||||||
var name = info?.Name ?? "???";
|
var name = info?.Name ?? "???";
|
||||||
|
@ -522,6 +550,12 @@ internal class PluginUi : IDisposable {
|
||||||
Task.Run(async () => await this.Plugin.Client.ListMembers(id));
|
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()) {
|
if (ImGui.BeginPopupContextItem()) {
|
||||||
var invited = this.Plugin.Client.InvitedChannels.ContainsKey(id);
|
var invited = this.Plugin.Client.InvitedChannels.ContainsKey(id);
|
||||||
if (invited) {
|
if (invited) {
|
||||||
|
@ -663,6 +697,7 @@ internal class PluginUi : IDisposable {
|
||||||
rank = Rank.Member;
|
rank = Rank.Member;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var first = true;
|
||||||
foreach (var member in channel.Members) {
|
foreach (var member in channel.Members) {
|
||||||
if (!member.Online) {
|
if (!member.Online) {
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, disabledColour);
|
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 (ImGui.BeginPopupContextItem($"{this._selectedChannel}-{member.Name}@{member.World}-context")) {
|
||||||
if (rank == Rank.Admin) {
|
if (rank == Rank.Admin) {
|
||||||
if (member.Rank is not (Rank.Admin or Rank.Invited)) {
|
if (member.Rank is not (Rank.Admin or Rank.Invited)) {
|
||||||
|
@ -734,5 +775,10 @@ internal class PluginUi : IDisposable {
|
||||||
|
|
||||||
ImGui.EndTable();
|
ImGui.EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AfterTable:
|
||||||
|
if (anyChanged) {
|
||||||
|
this.Plugin.SaveConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
@ -5,6 +6,56 @@ using ImGuiNET;
|
||||||
namespace ExtraChat.Util;
|
namespace ExtraChat.Util;
|
||||||
|
|
||||||
internal static class ImGuiUtil {
|
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) {
|
internal static bool IconButton(FontAwesomeIcon icon, string? id = null, string? tooltip = null) {
|
||||||
var label = icon.ToIconString();
|
var label = icon.ToIconString();
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
|
@ -62,6 +113,75 @@ internal static class ImGuiUtil {
|
||||||
|
|
||||||
return selectable && confirmHeld;
|
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]
|
[Flags]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user