mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-02 13:53:40 +01:00
Merge branch 'apiX' into feature/itextureprovider-updates
This commit is contained in:
commit
8c7771bf7d
2213 changed files with 10372 additions and 1088868 deletions
|
|
@ -74,7 +74,7 @@ internal class GameInventoryTestWidget : IDataWindowWidget
|
|||
this.standardEnabled = false;
|
||||
if (!this.rawEnabled)
|
||||
{
|
||||
this.scoped.Dispose();
|
||||
((IInternalDisposableService)this.scoped).DisposeService();
|
||||
this.scoped = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ internal class GameInventoryTestWidget : IDataWindowWidget
|
|||
this.rawEnabled = false;
|
||||
if (!this.standardEnabled)
|
||||
{
|
||||
this.scoped.Dispose();
|
||||
((IInternalDisposableService)this.scoped).DisposeService();
|
||||
this.scoped = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ internal class GameInventoryTestWidget : IDataWindowWidget
|
|||
{
|
||||
if (ImGui.Button("Disable##all-disable"))
|
||||
{
|
||||
this.scoped?.Dispose();
|
||||
((IInternalDisposableService)this.scoped)?.DisposeService();
|
||||
this.scoped = null;
|
||||
this.standardEnabled = this.rawEnabled = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public class AddonLifecycleWidget : IDataWindowWidget
|
|||
ImGui.Text(listener.AddonName is "" ? "GLOBAL" : listener.AddonName);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text($"{listener.FunctionDelegate.Target}::{listener.FunctionDelegate.Method.Name}");
|
||||
ImGui.Text($"{listener.FunctionDelegate.Method.DeclaringType.FullName}::{listener.FunctionDelegate.Method.Name}");
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ using System.Numerics;
|
|||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Plugin.Ipc.Internal;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ internal class FontAwesomeTestWidget : IDataWindowWidget
|
|||
private int selectedIconCategory;
|
||||
private string iconSearchInput = string.Empty;
|
||||
private bool iconSearchChanged = true;
|
||||
private bool useFixedWidth = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string[]? CommandShortcuts { get; init; } = { "fa", "fatest", "fontawesome" };
|
||||
|
|
@ -80,6 +81,8 @@ internal class FontAwesomeTestWidget : IDataWindowWidget
|
|||
{
|
||||
this.iconSearchChanged = true;
|
||||
}
|
||||
|
||||
ImGui.Checkbox("Use fixed width font", ref this.useFixedWidth);
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10f);
|
||||
for (var i = 0; i < this.icons?.Count; i++)
|
||||
|
|
@ -88,7 +91,7 @@ internal class FontAwesomeTestWidget : IDataWindowWidget
|
|||
ImGuiHelpers.ScaledRelativeSameLine(50f);
|
||||
ImGui.Text($"{this.iconNames?[i]}");
|
||||
ImGuiHelpers.ScaledRelativeSameLine(280f);
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.PushFont(this.useFixedWidth ? InterfaceManager.IconFontFixedWidth : InterfaceManager.IconFont);
|
||||
ImGui.Text(this.icons[i].ToIconString());
|
||||
ImGui.PopFont();
|
||||
ImGuiHelpers.ScaledDummy(2f);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
|
|||
private bool useBold;
|
||||
private bool useMinimumBuild;
|
||||
|
||||
private SingleFontChooserDialog? chooserDialog;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string[]? CommandShortcuts { get; init; }
|
||||
|
||||
|
|
@ -126,32 +128,75 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
|
|||
if (ImGui.Button("Test Lock"))
|
||||
Task.Run(this.TestLock);
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Choose Editor Font"))
|
||||
{
|
||||
var fcd = new SingleFontChooserDialog(
|
||||
Service<FontAtlasFactory>.Get().CreateFontAtlas(
|
||||
$"{nameof(GamePrebakedFontsTestWidget)}:EditorFont",
|
||||
FontAtlasAutoRebuildMode.Async));
|
||||
fcd.SelectedFont = this.fontSpec;
|
||||
fcd.IgnorePreviewGlobalScale = !this.atlasScaleMode;
|
||||
Service<InterfaceManager>.Get().Draw += fcd.Draw;
|
||||
fcd.ResultTask.ContinueWith(
|
||||
r => Service<Framework>.Get().RunOnFrameworkThread(
|
||||
() =>
|
||||
{
|
||||
Service<InterfaceManager>.Get().Draw -= fcd.Draw;
|
||||
fcd.Dispose();
|
||||
if (this.chooserDialog is null)
|
||||
{
|
||||
DoNext();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.chooserDialog.Cancel();
|
||||
this.chooserDialog.ResultTask.ContinueWith(_ => Service<Framework>.Get().RunOnFrameworkThread(DoNext));
|
||||
this.chooserDialog = null;
|
||||
}
|
||||
|
||||
_ = r.Exception;
|
||||
if (!r.IsCompletedSuccessfully)
|
||||
return;
|
||||
void DoNext()
|
||||
{
|
||||
var fcd = new SingleFontChooserDialog(
|
||||
Service<FontAtlasFactory>.Get(),
|
||||
$"{nameof(GamePrebakedFontsTestWidget)}:EditorFont");
|
||||
this.chooserDialog = fcd;
|
||||
fcd.SelectedFont = this.fontSpec;
|
||||
fcd.IgnorePreviewGlobalScale = !this.atlasScaleMode;
|
||||
fcd.IsModal = false;
|
||||
Service<InterfaceManager>.Get().Draw += fcd.Draw;
|
||||
var prevSpec = this.fontSpec;
|
||||
fcd.SelectedFontSpecChanged += spec =>
|
||||
{
|
||||
this.fontSpec = spec;
|
||||
Log.Information("Selected font: {font}", this.fontSpec);
|
||||
this.fontDialogHandle?.Dispose();
|
||||
this.fontDialogHandle = null;
|
||||
};
|
||||
fcd.ResultTask.ContinueWith(
|
||||
r => Service<Framework>.Get().RunOnFrameworkThread(
|
||||
() =>
|
||||
{
|
||||
Service<InterfaceManager>.Get().Draw -= fcd.Draw;
|
||||
fcd.Dispose();
|
||||
|
||||
this.fontSpec = r.Result;
|
||||
Log.Information("Selected font: {font}", this.fontSpec);
|
||||
this.fontDialogHandle?.Dispose();
|
||||
this.fontDialogHandle = null;
|
||||
}));
|
||||
_ = r.Exception;
|
||||
var spec = r.IsCompletedSuccessfully ? r.Result : prevSpec;
|
||||
if (this.fontSpec != spec)
|
||||
{
|
||||
this.fontSpec = spec;
|
||||
this.fontDialogHandle?.Dispose();
|
||||
this.fontDialogHandle = null;
|
||||
}
|
||||
|
||||
this.chooserDialog = null;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.chooserDialog is not null)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted($"{this.chooserDialog.PopupPosition}, {this.chooserDialog.PopupSize}");
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Random Location"))
|
||||
{
|
||||
var monitors = ImGui.GetPlatformIO().Monitors;
|
||||
var monitor = monitors[Random.Shared.Next() % monitors.Size];
|
||||
this.chooserDialog.PopupPosition = monitor.WorkPos + (monitor.WorkSize * new Vector2(
|
||||
Random.Shared.NextSingle(),
|
||||
Random.Shared.NextSingle()));
|
||||
this.chooserDialog.PopupSize = monitor.WorkSize * new Vector2(
|
||||
Random.Shared.NextSingle(),
|
||||
Random.Shared.NextSingle());
|
||||
}
|
||||
}
|
||||
|
||||
this.privateAtlas ??=
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Storage.Assets;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
||||
|
|
@ -9,11 +18,14 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
|||
/// </summary>
|
||||
internal class ImGuiWidget : IDataWindowWidget
|
||||
{
|
||||
private readonly HashSet<IActiveNotification> notifications = new();
|
||||
private NotificationTemplate notificationTemplate;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string[]? CommandShortcuts { get; init; } = { "imgui" };
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string DisplayName { get; init; } = "ImGui";
|
||||
public string DisplayName { get; init; } = "ImGui";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Ready { get; set; }
|
||||
|
|
@ -22,13 +34,16 @@ internal class ImGuiWidget : IDataWindowWidget
|
|||
public void Load()
|
||||
{
|
||||
this.Ready = true;
|
||||
this.notificationTemplate.Reset();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Draw()
|
||||
{
|
||||
this.notifications.RemoveWhere(x => x.DismissReason.HasValue);
|
||||
|
||||
var interfaceManager = Service<InterfaceManager>.Get();
|
||||
var notifications = Service<NotificationManager>.Get();
|
||||
var nm = Service<NotificationManager>.Get();
|
||||
|
||||
ImGui.Text("Monitor count: " + ImGui.GetPlatformIO().Monitors.Size);
|
||||
ImGui.Text("OverrideGameCursor: " + interfaceManager.OverrideGameCursor);
|
||||
|
|
@ -38,38 +53,425 @@ internal class ImGuiWidget : IDataWindowWidget
|
|||
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.TextUnformatted($"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms");
|
||||
ImGui.TextUnformatted(
|
||||
$"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms");
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (ImGui.Button("Add random notification"))
|
||||
ImGui.Checkbox("##manualContent", ref this.notificationTemplate.ManualContent);
|
||||
ImGui.SameLine();
|
||||
ImGui.InputText("Content##content", ref this.notificationTemplate.Content, 255);
|
||||
|
||||
ImGui.Checkbox("##manualTitle", ref this.notificationTemplate.ManualTitle);
|
||||
ImGui.SameLine();
|
||||
ImGui.InputText("Title##title", ref this.notificationTemplate.Title, 255);
|
||||
|
||||
ImGui.Checkbox("##manualMinimizedText", ref this.notificationTemplate.ManualMinimizedText);
|
||||
ImGui.SameLine();
|
||||
ImGui.InputText("MinimizedText##minimizedText", ref this.notificationTemplate.MinimizedText, 255);
|
||||
|
||||
ImGui.Checkbox("##manualType", ref this.notificationTemplate.ManualType);
|
||||
ImGui.SameLine();
|
||||
ImGui.Combo(
|
||||
"Type##type",
|
||||
ref this.notificationTemplate.TypeInt,
|
||||
NotificationTemplate.TypeTitles,
|
||||
NotificationTemplate.TypeTitles.Length);
|
||||
|
||||
ImGui.Combo(
|
||||
"Icon##iconCombo",
|
||||
ref this.notificationTemplate.IconInt,
|
||||
NotificationTemplate.IconTitles,
|
||||
NotificationTemplate.IconTitles.Length);
|
||||
switch (this.notificationTemplate.IconInt)
|
||||
{
|
||||
var rand = new Random();
|
||||
case 1 or 2:
|
||||
ImGui.InputText(
|
||||
"Icon Text##iconText",
|
||||
ref this.notificationTemplate.IconText,
|
||||
255);
|
||||
break;
|
||||
case 5 or 6:
|
||||
ImGui.Combo(
|
||||
"Asset##iconAssetCombo",
|
||||
ref this.notificationTemplate.IconAssetInt,
|
||||
NotificationTemplate.AssetSources,
|
||||
NotificationTemplate.AssetSources.Length);
|
||||
break;
|
||||
case 3 or 7:
|
||||
ImGui.InputText(
|
||||
"Game Path##iconText",
|
||||
ref this.notificationTemplate.IconText,
|
||||
255);
|
||||
break;
|
||||
case 4 or 8:
|
||||
ImGui.InputText(
|
||||
"File Path##iconText",
|
||||
ref this.notificationTemplate.IconText,
|
||||
255);
|
||||
break;
|
||||
}
|
||||
|
||||
var title = rand.Next(0, 5) switch
|
||||
ImGui.Combo(
|
||||
"Initial Duration",
|
||||
ref this.notificationTemplate.InitialDurationInt,
|
||||
NotificationTemplate.InitialDurationTitles,
|
||||
NotificationTemplate.InitialDurationTitles.Length);
|
||||
|
||||
ImGui.Combo(
|
||||
"Extension Duration",
|
||||
ref this.notificationTemplate.HoverExtendDurationInt,
|
||||
NotificationTemplate.HoverExtendDurationTitles,
|
||||
NotificationTemplate.HoverExtendDurationTitles.Length);
|
||||
|
||||
ImGui.Combo(
|
||||
"Progress",
|
||||
ref this.notificationTemplate.ProgressMode,
|
||||
NotificationTemplate.ProgressModeTitles,
|
||||
NotificationTemplate.ProgressModeTitles.Length);
|
||||
|
||||
ImGui.Checkbox("Respect UI Hidden", ref this.notificationTemplate.RespectUiHidden);
|
||||
|
||||
ImGui.Checkbox("Minimized", ref this.notificationTemplate.Minimized);
|
||||
|
||||
ImGui.Checkbox("Show Indeterminate If No Expiry", ref this.notificationTemplate.ShowIndeterminateIfNoExpiry);
|
||||
|
||||
ImGui.Checkbox("User Dismissable", ref this.notificationTemplate.UserDismissable);
|
||||
|
||||
ImGui.Checkbox(
|
||||
"Action Bar (always on if not user dismissable for the example)",
|
||||
ref this.notificationTemplate.ActionBar);
|
||||
|
||||
ImGui.Checkbox("Leave Textures Open", ref this.notificationTemplate.LeaveTexturesOpen);
|
||||
|
||||
if (ImGui.Button("Add notification"))
|
||||
{
|
||||
var text =
|
||||
"Bla bla bla bla bla bla bla bla bla bla bla.\nBla bla bla bla bla bla bla bla bla bla bla bla bla bla.";
|
||||
|
||||
NewRandom(out var title, out var type, out var progress);
|
||||
if (this.notificationTemplate.ManualTitle)
|
||||
title = this.notificationTemplate.Title;
|
||||
if (this.notificationTemplate.ManualContent)
|
||||
text = this.notificationTemplate.Content;
|
||||
if (this.notificationTemplate.ManualType)
|
||||
type = (NotificationType)this.notificationTemplate.TypeInt;
|
||||
|
||||
var n = nm.AddNotification(
|
||||
new()
|
||||
{
|
||||
Content = text,
|
||||
Title = title,
|
||||
MinimizedText = this.notificationTemplate.ManualMinimizedText
|
||||
? this.notificationTemplate.MinimizedText
|
||||
: null,
|
||||
Type = type,
|
||||
ShowIndeterminateIfNoExpiry = this.notificationTemplate.ShowIndeterminateIfNoExpiry,
|
||||
RespectUiHidden = this.notificationTemplate.RespectUiHidden,
|
||||
Minimized = this.notificationTemplate.Minimized,
|
||||
UserDismissable = this.notificationTemplate.UserDismissable,
|
||||
InitialDuration =
|
||||
this.notificationTemplate.InitialDurationInt == 0
|
||||
? TimeSpan.MaxValue
|
||||
: NotificationTemplate.Durations[this.notificationTemplate.InitialDurationInt],
|
||||
ExtensionDurationSinceLastInterest =
|
||||
this.notificationTemplate.HoverExtendDurationInt == 0
|
||||
? TimeSpan.Zero
|
||||
: NotificationTemplate.Durations[this.notificationTemplate.HoverExtendDurationInt],
|
||||
Progress = this.notificationTemplate.ProgressMode switch
|
||||
{
|
||||
0 => 1f,
|
||||
1 => progress,
|
||||
2 => 0f,
|
||||
3 => 0f,
|
||||
4 => -1f,
|
||||
_ => 0.5f,
|
||||
},
|
||||
Icon = this.notificationTemplate.IconInt switch
|
||||
{
|
||||
1 => INotificationIcon.From(
|
||||
(SeIconChar)(this.notificationTemplate.IconText.Length == 0
|
||||
? 0
|
||||
: this.notificationTemplate.IconText[0])),
|
||||
2 => INotificationIcon.From(
|
||||
(FontAwesomeIcon)(this.notificationTemplate.IconText.Length == 0
|
||||
? 0
|
||||
: this.notificationTemplate.IconText[0])),
|
||||
3 => INotificationIcon.FromGame(this.notificationTemplate.IconText),
|
||||
4 => INotificationIcon.FromFile(this.notificationTemplate.IconText),
|
||||
_ => null,
|
||||
},
|
||||
});
|
||||
|
||||
this.notifications.Add(n);
|
||||
|
||||
var dam = Service<DalamudAssetManager>.Get();
|
||||
var tm = Service<TextureManager>.Get();
|
||||
switch (this.notificationTemplate.IconInt)
|
||||
{
|
||||
0 => "This is a toast",
|
||||
1 => "Truly, a toast",
|
||||
2 => "I am testing this toast",
|
||||
3 => "I hope this looks right",
|
||||
4 => "Good stuff",
|
||||
5 => "Nice",
|
||||
_ => null,
|
||||
};
|
||||
case 5:
|
||||
n.SetIconTexture(
|
||||
DisposeLoggingTextureWrap.Wrap(
|
||||
dam.GetDalamudTextureWrap(
|
||||
Enum.Parse<DalamudAsset>(
|
||||
NotificationTemplate.AssetSources[this.notificationTemplate.IconAssetInt]))),
|
||||
this.notificationTemplate.LeaveTexturesOpen);
|
||||
break;
|
||||
case 6:
|
||||
n.SetIconTexture(
|
||||
dam.GetDalamudTextureWrapAsync(
|
||||
Enum.Parse<DalamudAsset>(
|
||||
NotificationTemplate.AssetSources[this.notificationTemplate.IconAssetInt]))
|
||||
.ContinueWith(
|
||||
r => r.IsCompletedSuccessfully
|
||||
? Task.FromResult<IDalamudTextureWrap>(DisposeLoggingTextureWrap.Wrap(r.Result))
|
||||
: r).Unwrap(),
|
||||
this.notificationTemplate.LeaveTexturesOpen);
|
||||
break;
|
||||
case 7:
|
||||
n.SetIconTexture(
|
||||
DisposeLoggingTextureWrap.Wrap(tm.GetTextureFromGame(this.notificationTemplate.IconText)),
|
||||
this.notificationTemplate.LeaveTexturesOpen);
|
||||
break;
|
||||
case 8:
|
||||
n.SetIconTexture(
|
||||
DisposeLoggingTextureWrap.Wrap(tm.GetTextureFromFile(new(this.notificationTemplate.IconText))),
|
||||
this.notificationTemplate.LeaveTexturesOpen);
|
||||
break;
|
||||
}
|
||||
|
||||
var type = rand.Next(0, 4) switch
|
||||
switch (this.notificationTemplate.ProgressMode)
|
||||
{
|
||||
0 => NotificationType.Error,
|
||||
1 => NotificationType.Warning,
|
||||
2 => NotificationType.Info,
|
||||
3 => NotificationType.Success,
|
||||
4 => NotificationType.None,
|
||||
_ => NotificationType.None,
|
||||
};
|
||||
case 2:
|
||||
Task.Run(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0; i <= 10 && !n.DismissReason.HasValue; i++)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
n.Progress = i / 10f;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 3:
|
||||
Task.Run(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0; i <= 10 && !n.DismissReason.HasValue; i++)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
n.Progress = i / 10f;
|
||||
}
|
||||
|
||||
const string text = "Bla bla bla bla bla bla bla bla bla bla bla.\nBla bla bla bla bla bla bla bla bla bla bla bla bla bla.";
|
||||
n.ExtendBy(NotificationConstants.DefaultDuration);
|
||||
n.InitialDuration = NotificationConstants.DefaultDuration;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
notifications.AddNotification(text, title, type);
|
||||
if (this.notificationTemplate.ActionBar || !this.notificationTemplate.UserDismissable)
|
||||
{
|
||||
var nclick = 0;
|
||||
var testString = "input";
|
||||
|
||||
n.Click += _ => nclick++;
|
||||
n.DrawActions += an =>
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted($"{nclick}");
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Update"))
|
||||
{
|
||||
NewRandom(out title, out type, out progress);
|
||||
an.Notification.Title = title;
|
||||
an.Notification.Type = type;
|
||||
an.Notification.Progress = progress;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Dismiss"))
|
||||
an.Notification.DismissNow();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth(an.MaxCoord.X - ImGui.GetCursorPosX());
|
||||
ImGui.InputText("##input", ref testString, 255);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Replace images using setter"))
|
||||
{
|
||||
foreach (var n in this.notifications)
|
||||
{
|
||||
var i = (uint)Random.Shared.NextInt64(0, 200000);
|
||||
n.IconTexture = DisposeLoggingTextureWrap.Wrap(Service<TextureManager>.Get().GetIcon(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void NewRandom(out string? title, out NotificationType type, out float progress)
|
||||
{
|
||||
var rand = new Random();
|
||||
|
||||
title = rand.Next(0, 7) switch
|
||||
{
|
||||
0 => "This is a toast",
|
||||
1 => "Truly, a toast",
|
||||
2 => "I am testing this toast",
|
||||
3 => "I hope this looks right",
|
||||
4 => "Good stuff",
|
||||
5 => "Nice",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
type = rand.Next(0, 5) switch
|
||||
{
|
||||
0 => NotificationType.Error,
|
||||
1 => NotificationType.Warning,
|
||||
2 => NotificationType.Info,
|
||||
3 => NotificationType.Success,
|
||||
4 => NotificationType.None,
|
||||
_ => NotificationType.None,
|
||||
};
|
||||
|
||||
if (rand.Next() % 2 == 0)
|
||||
progress = -1;
|
||||
else
|
||||
progress = rand.NextSingle();
|
||||
}
|
||||
|
||||
private struct NotificationTemplate
|
||||
{
|
||||
public static readonly string[] IconTitles =
|
||||
{
|
||||
"None (use Type)",
|
||||
"SeIconChar",
|
||||
"FontAwesomeIcon",
|
||||
"GamePath",
|
||||
"FilePath",
|
||||
"TextureWrap from DalamudAssets",
|
||||
"TextureWrap from DalamudAssets(Async)",
|
||||
"TextureWrap from GamePath",
|
||||
"TextureWrap from FilePath",
|
||||
};
|
||||
|
||||
public static readonly string[] AssetSources =
|
||||
Enum.GetValues<DalamudAsset>()
|
||||
.Where(x => x.GetAttribute<DalamudAssetAttribute>()?.Purpose is DalamudAssetPurpose.TextureFromPng)
|
||||
.Select(Enum.GetName)
|
||||
.ToArray();
|
||||
|
||||
public static readonly string[] ProgressModeTitles =
|
||||
{
|
||||
"Default",
|
||||
"Random",
|
||||
"Increasing",
|
||||
"Increasing & Auto Dismiss",
|
||||
"Indeterminate",
|
||||
};
|
||||
|
||||
public static readonly string[] TypeTitles =
|
||||
{
|
||||
nameof(NotificationType.None),
|
||||
nameof(NotificationType.Success),
|
||||
nameof(NotificationType.Warning),
|
||||
nameof(NotificationType.Error),
|
||||
nameof(NotificationType.Info),
|
||||
};
|
||||
|
||||
public static readonly string[] InitialDurationTitles =
|
||||
{
|
||||
"Infinite",
|
||||
"1 seconds",
|
||||
"3 seconds (default)",
|
||||
"10 seconds",
|
||||
};
|
||||
|
||||
public static readonly string[] HoverExtendDurationTitles =
|
||||
{
|
||||
"Disable",
|
||||
"1 seconds",
|
||||
"3 seconds (default)",
|
||||
"10 seconds",
|
||||
};
|
||||
|
||||
public static readonly TimeSpan[] Durations =
|
||||
{
|
||||
TimeSpan.Zero,
|
||||
TimeSpan.FromSeconds(1),
|
||||
NotificationConstants.DefaultDuration,
|
||||
TimeSpan.FromSeconds(10),
|
||||
};
|
||||
|
||||
public bool ManualContent;
|
||||
public string Content;
|
||||
public bool ManualTitle;
|
||||
public string Title;
|
||||
public bool ManualMinimizedText;
|
||||
public string MinimizedText;
|
||||
public int IconInt;
|
||||
public string IconText;
|
||||
public int IconAssetInt;
|
||||
public bool ManualType;
|
||||
public int TypeInt;
|
||||
public int InitialDurationInt;
|
||||
public int HoverExtendDurationInt;
|
||||
public bool ShowIndeterminateIfNoExpiry;
|
||||
public bool RespectUiHidden;
|
||||
public bool Minimized;
|
||||
public bool UserDismissable;
|
||||
public bool ActionBar;
|
||||
public bool LeaveTexturesOpen;
|
||||
public int ProgressMode;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.ManualContent = false;
|
||||
this.Content = string.Empty;
|
||||
this.ManualTitle = false;
|
||||
this.Title = string.Empty;
|
||||
this.ManualMinimizedText = false;
|
||||
this.MinimizedText = string.Empty;
|
||||
this.IconInt = 0;
|
||||
this.IconText = "ui/icon/000000/000004_hr1.tex";
|
||||
this.IconAssetInt = 0;
|
||||
this.ManualType = false;
|
||||
this.TypeInt = (int)NotificationType.None;
|
||||
this.InitialDurationInt = 2;
|
||||
this.HoverExtendDurationInt = 2;
|
||||
this.ShowIndeterminateIfNoExpiry = true;
|
||||
this.Minimized = true;
|
||||
this.UserDismissable = true;
|
||||
this.ActionBar = true;
|
||||
this.LeaveTexturesOpen = true;
|
||||
this.ProgressMode = 0;
|
||||
this.RespectUiHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DisposeLoggingTextureWrap : IDalamudTextureWrap
|
||||
{
|
||||
private readonly IDalamudTextureWrap inner;
|
||||
|
||||
public DisposeLoggingTextureWrap(IDalamudTextureWrap inner) => this.inner = inner;
|
||||
|
||||
public nint ImGuiHandle => this.inner.ImGuiHandle;
|
||||
|
||||
public int Width => this.inner.Width;
|
||||
|
||||
public int Height => this.inner.Height;
|
||||
|
||||
public static DisposeLoggingTextureWrap? Wrap(IDalamudTextureWrap? inner) => inner is null ? null : new(inner);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.inner.Dispose();
|
||||
Service<NotificationManager>.Get().AddNotification(
|
||||
"Texture disposed",
|
||||
"ImGui Widget",
|
||||
NotificationType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
|
@ -96,7 +97,7 @@ internal class ServicesWidget : IDataWindowWidget
|
|||
|
||||
var rowWidth = 0f;
|
||||
foreach (var node in levelNodes)
|
||||
rowWidth += ImGui.CalcTextSize(node.TypeName).X + cellPad.X + margin.X;
|
||||
rowWidth += node.DisplayedNameSize.X + cellPad.X + margin.X;
|
||||
|
||||
var off = cellPad / 2;
|
||||
if (rowWidth < width)
|
||||
|
|
@ -107,7 +108,7 @@ internal class ServicesWidget : IDataWindowWidget
|
|||
|
||||
foreach (var node in levelNodes)
|
||||
{
|
||||
var textSize = ImGui.CalcTextSize(node.TypeName);
|
||||
var textSize = node.DisplayedNameSize;
|
||||
var cellSize = textSize + cellPad;
|
||||
|
||||
var rc = new Vector4(pos + off, pos.X + off.X + cellSize.X, pos.Y + off.Y + cellSize.Y);
|
||||
|
|
@ -174,7 +175,7 @@ internal class ServicesWidget : IDataWindowWidget
|
|||
{
|
||||
foreach (var node in levelNodes)
|
||||
{
|
||||
var textSize = ImGui.CalcTextSize(node.TypeName);
|
||||
var textSize = node.DisplayedNameSize;
|
||||
var cellSize = textSize + cellPad;
|
||||
|
||||
var rc = this.nodeRects[node];
|
||||
|
|
@ -188,8 +189,19 @@ internal class ServicesWidget : IDataWindowWidget
|
|||
dl.AddRectFilled(new(rc.X, rc.Y), new(rc.Z, rc.W), rectHoverRelatedFillColor);
|
||||
|
||||
dl.AddRect(new(rc.X, rc.Y), new(rc.Z, rc.W), rectBaseBorderColor);
|
||||
ImGui.SetCursorScreenPos(new(rc.X, rc.Y));
|
||||
ImGui.InvisibleButton(node.DisplayedName, new(rc.Z - rc.X, rc.W - rc.Y));
|
||||
if (ImGui.IsItemHovered() && node.BlockingReason is not null)
|
||||
ImGui.SetTooltip(node.BlockingReason);
|
||||
|
||||
ImGui.SetCursorPos((new Vector2(rc.X, rc.Y) - pos) + ((cellSize - textSize) / 2));
|
||||
ImGui.TextUnformatted(node.TypeName);
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||
ImGui.TextUnformatted(node.DisplayedName);
|
||||
ImGui.SameLine();
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, node.TypeSuffixColor);
|
||||
ImGui.TextUnformatted(node.TypeSuffix);
|
||||
ImGui.PopStyleVar();
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -259,11 +271,44 @@ internal class ServicesWidget : IDataWindowWidget
|
|||
private readonly List<ServiceDependencyNode> children = new();
|
||||
private readonly List<ServiceDependencyNode> invalidParents = new();
|
||||
|
||||
private ServiceDependencyNode(Type t) => this.Type = t;
|
||||
private ServiceDependencyNode(Type t)
|
||||
{
|
||||
this.Type = t;
|
||||
this.BlockingReason =
|
||||
t.GetCustomAttribute<ServiceManager.BlockingEarlyLoadedServiceAttribute>()?.BlockReason;
|
||||
this.Kind = t.GetCustomAttribute<ServiceManager.ServiceAttribute>()?.Kind ??
|
||||
ServiceManager.ServiceKind.None;
|
||||
this.DisplayedName = this.Type.Name;
|
||||
this.TypeSuffix = this.Kind switch {
|
||||
ServiceManager.ServiceKind.ProvidedService => " (P)",
|
||||
ServiceManager.ServiceKind.EarlyLoadedService => " (E)",
|
||||
ServiceManager.ServiceKind.BlockingEarlyLoadedService => " (B)",
|
||||
ServiceManager.ServiceKind.ScopedService => " (S)",
|
||||
var x => $" (? {x})",
|
||||
};
|
||||
this.TypeSuffixColor = this.Kind switch {
|
||||
ServiceManager.ServiceKind.ProvidedService => ImGui.GetColorU32(ImGuiColors.DalamudGrey),
|
||||
ServiceManager.ServiceKind.EarlyLoadedService => ImGui.GetColorU32(ImGuiColors.DalamudWhite),
|
||||
ServiceManager.ServiceKind.BlockingEarlyLoadedService => ImGui.GetColorU32(ImGuiColors.ParsedPink),
|
||||
ServiceManager.ServiceKind.ScopedService => ImGui.GetColorU32(ImGuiColors.ParsedGreen),
|
||||
_ => ImGui.GetColorU32(ImGuiColors.DalamudRed),
|
||||
};
|
||||
}
|
||||
|
||||
public Type Type { get; }
|
||||
|
||||
public string TypeName => this.Type.Name;
|
||||
public string DisplayedName { get; }
|
||||
|
||||
public string TypeSuffix { get; }
|
||||
|
||||
public uint TypeSuffixColor { get; }
|
||||
|
||||
public Vector2 DisplayedNameSize =>
|
||||
ImGui.CalcTextSize(this.DisplayedName) + ImGui.CalcTextSize(this.TypeSuffix) with { Y = 0 };
|
||||
|
||||
public ServiceManager.ServiceKind Kind { get; }
|
||||
|
||||
public string? BlockingReason { get; }
|
||||
|
||||
public IReadOnlyList<ServiceDependencyNode> Parents => this.parents;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
// ReSharper disable MethodSupportsCancellation // Using alternative method of cancelling tasks by throwing exceptions.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -18,6 +27,12 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
|||
/// </summary>
|
||||
internal class TaskSchedulerWidget : IDataWindowWidget
|
||||
{
|
||||
private readonly FileDialogManager fileDialogManager = new();
|
||||
private readonly byte[] urlBytes = new byte[2048];
|
||||
private readonly byte[] localPathBytes = new byte[2048];
|
||||
|
||||
private Task? downloadTask = null;
|
||||
private (long Downloaded, long Total, float Percentage) downloadState;
|
||||
private CancellationTokenSource taskSchedulerCancelSource = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -33,11 +48,16 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
public void Load()
|
||||
{
|
||||
this.Ready = true;
|
||||
Encoding.UTF8.GetBytes(
|
||||
"https://geo.mirror.pkgbuild.com/iso/2024.01.01/archlinux-2024.01.01-x86_64.iso",
|
||||
this.urlBytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Draw()
|
||||
{
|
||||
var framework = Service<Framework>.Get();
|
||||
|
||||
if (ImGui.Button("Clear list"))
|
||||
{
|
||||
TaskTracker.Clear();
|
||||
|
|
@ -84,8 +104,7 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
{
|
||||
Thread.Sleep(200);
|
||||
|
||||
string a = null;
|
||||
a.Contains("dalamud"); // Intentional null exception.
|
||||
_ = ((string)null)!.Contains("dalamud"); // Intentional null exception.
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -94,36 +113,226 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
|
||||
if (ImGui.Button("ASAP"))
|
||||
{
|
||||
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token));
|
||||
_ = framework.RunOnTick(() => Log.Information("Framework.Update - ASAP"), cancellationToken: this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("In 1s"))
|
||||
{
|
||||
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
|
||||
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 1s"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("In 60f"))
|
||||
{
|
||||
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token, delayTicks: 60));
|
||||
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 60f"), cancellationToken: this.taskSchedulerCancelSource.Token, delayTicks: 60);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Error in 1s"))
|
||||
if (ImGui.Button("In 1s+120f"))
|
||||
{
|
||||
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => throw new Exception("Test Exception"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
|
||||
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 1s+120f"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1), delayTicks: 120);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("In 2s+60f"))
|
||||
{
|
||||
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 2s+60f"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(2), delayTicks: 60);
|
||||
}
|
||||
|
||||
if (ImGui.Button("Every 60f"))
|
||||
{
|
||||
_ = framework.RunOnTick(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await framework.DelayTicks(60, this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
cancellationToken: this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Every 1s"))
|
||||
{
|
||||
_ = framework.RunOnTick(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
cancellationToken: this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Every 60f (Await)"))
|
||||
{
|
||||
_ = framework.Run(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await framework.DelayTicks(60, this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Every 1s (Await)"))
|
||||
{
|
||||
_ = framework.Run(
|
||||
async () =>
|
||||
{
|
||||
for (var i = 0L; ; i++)
|
||||
{
|
||||
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
|
||||
var it = i;
|
||||
_ = Task.Factory.StartNew(() => Log.Information($" => Sub #{it}; MainThread={ThreadSafety.IsMainThread}"));
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
},
|
||||
this.taskSchedulerCancelSource.Token);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("As long as it's in Framework Thread"))
|
||||
{
|
||||
Task.Run(async () => await Service<Framework>.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
|
||||
Service<Framework>.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
|
||||
Task.Run(async () => await framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
|
||||
framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Error in 1s"))
|
||||
{
|
||||
_ = framework.RunOnTick(() => throw new Exception("Test Exception"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Freeze 1s"))
|
||||
{
|
||||
_ = framework.RunOnFrameworkThread(() => Helper().Wait());
|
||||
static async Task Helper() => await Task.Delay(1000);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Freeze Completely"))
|
||||
{
|
||||
_ = framework.Run(() => Helper().Wait());
|
||||
static async Task Helper() => await Task.Delay(1000);
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Download"))
|
||||
{
|
||||
ImGui.InputText("URL", this.urlBytes, (uint)this.urlBytes.Length);
|
||||
ImGui.InputText("Local Path", this.localPathBytes, (uint)this.localPathBytes.Length);
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGuiComponents.IconButton("##localpathpicker", FontAwesomeIcon.File))
|
||||
{
|
||||
var defaultFileName = Encoding.UTF8.GetString(this.urlBytes).Split('\0', 2)[0].Split('/').Last();
|
||||
this.fileDialogManager.SaveFileDialog(
|
||||
"Choose a local path",
|
||||
"*",
|
||||
defaultFileName,
|
||||
string.Empty,
|
||||
(accept, newPath) =>
|
||||
{
|
||||
if (accept)
|
||||
{
|
||||
this.localPathBytes.AsSpan().Clear();
|
||||
Encoding.UTF8.GetBytes(newPath, this.localPathBytes.AsSpan());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted($"{this.downloadState.Downloaded:##,###}/{this.downloadState.Total:##,###} ({this.downloadState.Percentage:0.00}%)");
|
||||
|
||||
using var disabled =
|
||||
ImRaii.Disabled(this.downloadTask?.IsCompleted is false || this.localPathBytes[0] == 0);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted("Download");
|
||||
ImGui.SameLine();
|
||||
var downloadUsingGlobalScheduler = ImGui.Button("using default scheduler");
|
||||
ImGui.SameLine();
|
||||
var downloadUsingFramework = ImGui.Button("using Framework.Update");
|
||||
if (downloadUsingGlobalScheduler || downloadUsingFramework)
|
||||
{
|
||||
var url = Encoding.UTF8.GetString(this.urlBytes).Split('\0', 2)[0];
|
||||
var localPath = Encoding.UTF8.GetString(this.localPathBytes).Split('\0', 2)[0];
|
||||
var ct = this.taskSchedulerCancelSource.Token;
|
||||
this.downloadState = default;
|
||||
var factory = downloadUsingGlobalScheduler
|
||||
? Task.Factory
|
||||
: framework.GetTaskFactory();
|
||||
this.downloadState = default;
|
||||
this.downloadTask = factory.StartNew(
|
||||
async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var to = File.Create(localPath);
|
||||
using var client = new HttpClient();
|
||||
using var conn = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||
this.downloadState.Total = conn.Content.Headers.ContentLength ?? -1L;
|
||||
await using var from = conn.Content.ReadAsStream(ct);
|
||||
var buffer = new byte[8192];
|
||||
while (true)
|
||||
{
|
||||
if (downloadUsingFramework)
|
||||
ThreadSafety.AssertMainThread();
|
||||
if (downloadUsingGlobalScheduler)
|
||||
ThreadSafety.AssertNotMainThread();
|
||||
var len = await from.ReadAsync(buffer, ct);
|
||||
if (len == 0)
|
||||
break;
|
||||
await to.WriteAsync(buffer.AsMemory(0, len), ct);
|
||||
this.downloadState.Downloaded += len;
|
||||
if (this.downloadState.Total >= 0)
|
||||
{
|
||||
this.downloadState.Percentage =
|
||||
(100f * this.downloadState.Downloaded) / this.downloadState.Total;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Failed to download {from} to {to}.", url, localPath);
|
||||
try
|
||||
{
|
||||
File.Delete(localPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
},
|
||||
cancellationToken: ct).Unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.Button("Drown in tasks"))
|
||||
|
|
@ -244,6 +453,8 @@ internal class TaskSchedulerWidget : IDataWindowWidget
|
|||
|
||||
ImGui.PopStyleColor(1);
|
||||
}
|
||||
|
||||
this.fileDialogManager.Draw();
|
||||
}
|
||||
|
||||
private async Task TestTaskInTaskDelay(CancellationToken token)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue