1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-04-22 09:15:48 +02:00

Minor refactoring of the core library

This commit is contained in:
chylex 2022-02-15 17:49:34 +01:00
parent b815ae4b11
commit acafbc3706
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
27 changed files with 183 additions and 85 deletions

View File

@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using TweetLib.Utils.Dialogs;
namespace TweetLib.Browser.CEF.Interfaces {
public interface IFileDialogOpener {
void OpenFile(string title, bool multiple, List<string> supportedExtensions, Action<string[]> onAccepted, Action onCancelled);
void OpenFile(string title, bool multiple, List<FileDialogFilter> filters, Action<string[]> onAccepted, Action onCancelled);
}
}

View File

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using TweetLib.Browser.CEF.Dialogs;
using TweetLib.Browser.CEF.Interfaces;
using TweetLib.Utils.Dialogs;
using TweetLib.Utils.Static;
namespace TweetLib.Browser.CEF.Logic {
@ -19,11 +21,16 @@ public DialogHandlerLogic(IFileDialogOpener fileDialogOpener, IFileDialogCallbac
public bool OnFileDialog(FileDialogType type, IEnumerable<string> acceptFilters, TCallback callback) {
if (type is FileDialogType.Open or FileDialogType.OpenMultiple) {
var multiple = type == FileDialogType.OpenMultiple;
var supportedExtensions = acceptFilters.SelectMany(ParseFileType).Where(static filter => !string.IsNullOrEmpty(filter)).ToList();
var supportedExtensions = acceptFilters.SelectMany(ParseFileType).Where(static filter => !string.IsNullOrEmpty(filter)).ToArray();
var filters = new List<FileDialogFilter> {
new ("All Supported Formats", supportedExtensions),
new ("All Files", ".*")
};
fileDialogOpener.OpenFile("Open Files", multiple, supportedExtensions, files => {
fileDialogOpener.OpenFile("Open Files", multiple, filters, files => {
string ext = Path.GetExtension(files[0])!.ToLower();
callbackAdapter.Continue(callback, supportedExtensions.FindIndex(filter => ParseFileType(filter).Contains(ext)), files);
callbackAdapter.Continue(callback, Array.FindIndex(supportedExtensions, filter => ParseFileType(filter).Contains(ext)), files);
callbackAdapter.Dispose(callback);
}, () => {
callbackAdapter.Cancel(callback);

View File

@ -3,7 +3,7 @@
using TweetLib.Utils.Globalization;
using TweetLib.Utils.Static;
namespace TweetLib.Core.Features.Chromium {
namespace TweetLib.Browser.CEF.Utils {
public static class SpellCheck {
// https://cs.chromium.org/chromium/src/third_party/hunspell_dictionaries/
public static IEnumerable<Language> SupportedLanguages { get; } = new List<string> {

View File

@ -2,18 +2,17 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
using TweetLib.Browser.Request;
using IOFile = System.IO.File;
namespace TweetLib.Core.Resources {
namespace TweetLib.Browser.Request {
public sealed class ResourceCache {
private readonly Dictionary<string, SchemeResource> cache = new ();
public void ClearCache() {
public void Clear() {
cache.Clear();
}
internal SchemeResource ReadFile(string path) {
public SchemeResource ReadFile(string path) {
string key = new Uri(path).LocalPath;
if (cache.TryGetValue(key, out var cachedResource)) {

View File

@ -1,10 +1,10 @@
using System;
using System.IO;
using System.Linq;
using TweetLib.Browser.Request;
using TweetLib.Core.Application;
using TweetLib.Core.Features;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Resources;
using TweetLib.Core.Systems.Configuration;
using TweetLib.Core.Systems.Logging;
using TweetLib.Utils.Static;

View File

@ -1,5 +1,5 @@
using System;
using TweetLib.Core.Systems.Dialogs;
using TweetLib.Utils.Dialogs;
namespace TweetLib.Core.Application {
public interface IAppFileDialogs {

View File

@ -1,5 +1,5 @@
using TweetLib.Browser.Request;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Resources;
using TweetLib.Core.Systems.Configuration;
namespace TweetLib.Core.Application {

View File

@ -4,7 +4,7 @@
using System.Linq;
using TweetLib.Browser.Interfaces;
using TweetLib.Core.Features.Twitter;
using TweetLib.Core.Systems.Dialogs;
using TweetLib.Utils.Dialogs;
using TweetLib.Utils.Static;
namespace TweetLib.Core.Features {

View File

@ -4,7 +4,6 @@
using TweetLib.Browser.Interfaces;
using TweetLib.Browser.Request;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Resources;
namespace TweetLib.Core.Features.Plugins {
public sealed class PluginSchemeHandler : ICustomSchemeHandler {

View File

@ -2,7 +2,6 @@
using System.Net;
using TweetLib.Browser.Interfaces;
using TweetLib.Browser.Request;
using TweetLib.Core.Resources;
using TweetLib.Utils.Static;
namespace TweetLib.Core.Features.TweetDeck {

View File

@ -1,13 +0,0 @@
using System.Collections.Generic;
namespace TweetLib.Core.Systems.Dialogs {
public sealed class FileDialogFilter {
public string Name { get; }
public IReadOnlyList<string> Extensions { get; }
internal FileDialogFilter(string name, params string[] extensions) {
Name = name;
Extensions = extensions;
}
}
}

View File

@ -1,10 +0,0 @@
using System.Collections.Generic;
namespace TweetLib.Core.Systems.Dialogs {
public sealed class SaveFileDialogSettings {
public string DialogTitle { get; internal set; } = "Save File";
public bool OverwritePrompt { get; internal set; } = true;
public string? FileName { get; internal set; }
public IReadOnlyList<FileDialogFilter>? Filters { get; internal set; }
}
}

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace TweetLib.Utils.Dialogs {
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public sealed class FileDialogFilter {
public string Name { get; }
public IReadOnlyList<string> Extensions { get; }
public string FullName => FullNameAndPattern.Item1;
public string Pattern => FullNameAndPattern.Item2;
private (string, string) FullNameAndPattern {
get {
if (Extensions.Count == 0) {
return (Name, "*.*");
}
else {
string pattern = string.Join(";", Extensions.Select(static ext => "*" + ext));
string fullName = Name + " (" + pattern + ")";
return (fullName, pattern);
}
}
}
public FileDialogFilter(string name, params string[] extensions) {
Name = name;
Extensions = extensions;
}
public string JoinFullNameAndPattern(string separator) {
var (fullName, pattern) = FullNameAndPattern;
return fullName + separator + pattern;
}
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace TweetLib.Utils.Dialogs {
public sealed class SaveFileDialogSettings {
public string DialogTitle { get; set; } = "Save File";
public bool OverwritePrompt { get; set; } = true;
public string? FileName { get; set; }
public IReadOnlyList<FileDialogFilter>? Filters { get; set; }
}
}

View File

@ -3,6 +3,8 @@
namespace TweetLib.Utils.Globalization {
public sealed class Language : IComparable<Language> {
public static Language English { get; } = new ("en-US");
public string Code { get; }
private string Name => cultureInfo?.NativeName ?? Code;

View File

@ -5,7 +5,7 @@
using System.Threading;
using TweetLib.Utils.Static;
namespace TweetLib.Core.Systems.Startup {
namespace TweetLib.Utils.Startup {
public sealed class LockFile {
private static int CurrentProcessID {
get {
@ -79,17 +79,16 @@ public LockResult LockWait(int timeout, int retryDelay) {
return Lock();
}
public bool Unlock() {
public UnlockResult Unlock() {
if (ReleaseLockFileStream()) {
try {
File.Delete(path);
} catch (Exception e) {
App.Logger.Error(e.ToString());
return false;
return new UnlockResult.Fail(e);
}
}
return true;
return UnlockResult.Success;
}
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]

View File

@ -1,7 +1,7 @@
using System;
using System.Diagnostics;
namespace TweetLib.Core.Systems.Startup {
namespace TweetLib.Utils.Startup {
public class LockResult {
private readonly string name;

View File

@ -0,0 +1,25 @@
using System;
namespace TweetLib.Utils.Startup {
public class UnlockResult {
private readonly string name;
private UnlockResult(string name) {
this.name = name;
}
public override string ToString() {
return name;
}
public static UnlockResult Success { get; } = new ("Success");
public sealed class Fail : UnlockResult {
public Exception Exception { get; }
internal Fail(Exception exception) : base("Fail") {
this.Exception = exception;
}
}
}
}

View File

@ -0,0 +1,41 @@
namespace TweetTest.Utils.Dialogs.FileDialogFilter
open TweetLib.Utils.Dialogs
open Xunit
type internal TestData =
static member noExtensions = FileDialogFilter("Any Filter")
static member oneExtension = FileDialogFilter("Single Filter", ".1")
static member twoExtensions = FileDialogFilter("Double Filter", ".1", ".2")
module Pattern =
[<Fact>]
let ``no extension assumes any files`` () =
Assert.Equal("*.*", TestData.noExtensions.Pattern)
[<Fact>]
let ``one extension prepends with asterisk`` () =
Assert.Equal("*.1", TestData.oneExtension.Pattern)
[<Fact>]
let ``multiple extensions are separated by semicolons`` () =
Assert.Equal("*.1;*.2", TestData.twoExtensions.Pattern)
module FullName =
[<Fact>]
let ``no extension keeps original name`` () =
Assert.Equal("Any Filter", TestData.noExtensions.FullName)
[<Fact>]
let ``one extension appends pattern in parentheses`` () =
Assert.Equal("Single Filter (*.1)", TestData.oneExtension.FullName)
[<Fact>]
let ``multiple extensions in pattern are separated by semicolons`` () =
Assert.Equal("Double Filter (*.1;*.2)", TestData.twoExtensions.FullName)

View File

@ -27,6 +27,7 @@
<Compile Include="Collections\TestTwoKeyDictionary.fs" />
<Compile Include="Data\TestInjectedString.fs" />
<Compile Include="Data\TestResult.fs" />
<Compile Include="Dialogs\TestFileDialogFilter.fs" />
<Compile Include="IO\TestCombinedFileStream.fs" />
<Compile Include="Static\TestStringUtils.fs" />
</ItemGroup>

View File

@ -1,37 +1,20 @@
using System;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TweetDuck.Management;
using TweetLib.Core.Application;
using TweetLib.Core.Systems.Dialogs;
using TweetLib.Utils.Dialogs;
namespace TweetDuck.Application {
sealed class FileDialogs : IAppFileDialogs {
public void SaveFile(SaveFileDialogSettings settings, Action<string> onAccepted) {
static string FormatFilter(FileDialogFilter filter) {
var builder = new StringBuilder();
builder.Append(filter.Name);
var extensions = string.Join(";", filter.Extensions.Select(ext => "*" + ext));
if (extensions.Length > 0) {
builder.Append(" (");
builder.Append(extensions);
builder.Append(")");
}
builder.Append('|');
builder.Append(extensions.Length == 0 ? "*.*" : extensions);
return builder.ToString();
}
FormManager.RunOnUIThreadAsync(() => {
using SaveFileDialog dialog = new SaveFileDialog {
AutoUpgradeEnabled = true,
OverwritePrompt = settings.OverwritePrompt,
Title = settings.DialogTitle,
FileName = settings.FileName,
Filter = settings.Filters == null ? null : string.Join("|", settings.Filters.Select(FormatFilter))
Filter = settings.Filters == null ? null : string.Join("|", settings.Filters.Select(filter => filter.JoinFullNameAndPattern("|")))
};
if (dialog.ShowDialog() == DialogResult.OK) {

View File

@ -14,15 +14,19 @@
using TweetDuck.Dialogs;
using TweetDuck.Dialogs.Settings;
using TweetDuck.Management;
using TweetDuck.Properties;
using TweetDuck.Updates;
using TweetDuck.Utils;
using TweetLib.Browser.Request;
using TweetLib.Core;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.TweetDeck;
using TweetLib.Core.Resources;
using TweetLib.Core.Systems.Configuration;
using TweetLib.Core.Systems.Updates;
#if DEBUG
using TweetLib.Core.Resources;
#endif
namespace TweetDuck.Browser {
sealed partial class FormBrowser : Form, CustomKeyboardHandler.IBrowserKeyHandler {
@ -143,7 +147,7 @@ private void RestoreWindow() {
}
private void UpdateFormIcon() { // TODO fix to show icon in taskbar too
Icon = Config.MuteNotifications ? Properties.Resources.icon_muted : Properties.Resources.icon;
Icon = Config.MuteNotifications ? Resources.icon_muted : Resources.icon;
}
private void UpdateTray() {
@ -346,10 +350,10 @@ public void ResumeNotification(NotificationPauseReason reason) {
public void ReloadToTweetDeck() {
#if DEBUG
ResourceHotSwap.Run();
resourceCache.ClearCache();
resourceCache.Clear();
#else
if (ModifierKeys.HasFlag(Keys.Shift)) {
resourceCache.ClearCache();
resourceCache.Clear();
}
#endif

View File

@ -5,8 +5,8 @@
using TweetDuck.Browser.Base;
using TweetDuck.Controls;
using TweetDuck.Utils;
using TweetLib.Browser.CEF.Utils;
using TweetLib.Core;
using TweetLib.Core.Features.Chromium;
using TweetLib.Core.Features.Twitter;
using TweetLib.Core.Systems.Updates;
using TweetLib.Utils.Globalization;
@ -115,7 +115,7 @@ public TabSettingsGeneral(Action reloadTweetDeck, Action reloadColumns, UpdateCh
comboBoxSpellCheckLanguage.Items.Add(item);
}
} catch {
comboBoxSpellCheckLanguage.Items.Add(new Language("en-US"));
comboBoxSpellCheckLanguage.Items.Add(Language.English);
}
comboBoxSpellCheckLanguage.SelectedItem = new Language(Config.SpellCheckLanguage);

View File

@ -4,7 +4,7 @@
using TweetDuck.Dialogs;
using TweetDuck.Utils;
using TweetLib.Core;
using TweetLib.Core.Systems.Startup;
using TweetLib.Utils.Startup;
namespace TweetDuck.Management {
sealed class LockManager {
@ -26,15 +26,25 @@ public bool Lock(bool wasRestarted) {
}
public bool Unlock() {
return lockFile.Unlock();
UnlockResult result = lockFile.Unlock();
if (result is UnlockResult.Fail fail) {
App.Logger.Error(fail.Exception.ToString());
return false;
}
else if (result != UnlockResult.Success) {
return false;
}
return true;
}
// Locking
private bool LaunchNormally() {
LockResult lockResult = lockFile.Lock();
LockResult result = lockFile.Lock();
if (lockResult is LockResult.HasProcess info) {
if (result is LockResult.HasProcess info) {
if (!RestoreProcess(info.Process, WindowRestoreMessage) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)) {
if (!CloseProcess(info.Process)) {
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
@ -42,18 +52,18 @@ private bool LaunchNormally() {
}
info.Dispose();
lockResult = lockFile.Lock();
result = lockFile.Lock();
}
else {
return false;
}
}
if (lockResult is LockResult.Fail fail) {
if (result is LockResult.Fail fail) {
ShowGenericException(fail);
return false;
}
else if (lockResult != LockResult.Success) {
else if (result != LockResult.Success) {
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
return false;
}
@ -62,10 +72,10 @@ private bool LaunchNormally() {
}
private bool LaunchAfterRestart() {
LockResult lockResult = lockFile.LockWait(10000, WaitRetryDelay);
LockResult result = lockFile.LockWait(10000, WaitRetryDelay);
while (lockResult != LockResult.Success) {
if (lockResult is LockResult.Fail fail) {
while (result != LockResult.Success) {
if (result is LockResult.Fail fail) {
ShowGenericException(fail);
return false;
}
@ -73,7 +83,7 @@ private bool LaunchAfterRestart() {
return false;
}
lockResult = lockFile.LockWait(5000, WaitRetryDelay);
result = lockFile.LockWait(5000, WaitRetryDelay);
}
return true;

View File

@ -13,12 +13,12 @@
using TweetDuck.Utils;
using TweetImpl.CefSharp.Handlers;
using TweetLib.Browser.CEF.Utils;
using TweetLib.Browser.Request;
using TweetLib.Core;
using TweetLib.Core.Application;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.TweetDeck;
using TweetLib.Core.Resources;
using TweetLib.Core.Systems.Configuration;
using TweetLib.Utils.Collections;
using Win = System.Windows.Forms;

View File

@ -3,6 +3,7 @@
using System.Linq;
using System.Windows.Forms;
using TweetLib.Browser.CEF.Interfaces;
using TweetLib.Utils.Dialogs;
namespace TweetImpl.CefSharp.Dialogs {
sealed class FileDialogOpener : IFileDialogOpener {
@ -10,15 +11,13 @@ sealed class FileDialogOpener : IFileDialogOpener {
private FileDialogOpener() {}
public void OpenFile(string title, bool multiple, List<string> supportedExtensions, Action<string[]> onAccepted, Action onCancelled) {
string supportedFormatsFilter = string.Join(";", supportedExtensions.Select(filter => "*" + filter));
public void OpenFile(string title, bool multiple, List<FileDialogFilter> filters, Action<string[]> onAccepted, Action onCancelled) {
using OpenFileDialog dialog = new OpenFileDialog {
AutoUpgradeEnabled = true,
DereferenceLinks = true,
Multiselect = multiple,
Title = title,
Filter = $"All Supported Formats ({supportedFormatsFilter})|{supportedFormatsFilter}|All Files (*.*)|*.*"
Filter = string.Join("|", filters.Select(filter => filter.JoinFullNameAndPattern("|")))
};
if (dialog.ShowDialog() == DialogResult.OK) {

View File

@ -62,6 +62,10 @@
<Project>{eefb1f37-7cad-46bd-8042-66e7b502ab02}</Project>
<Name>TweetLib.Browser</Name>
</ProjectReference>
<ProjectReference Include="..\..\lib\TweetLib.Utils\TweetLib.Utils.csproj">
<Project>{476b1007-b12c-447f-b855-9886048201d6}</Project>
<Name>TweetLib.Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Version.cs" Link="Version.cs" />