Skip to content

Commit a4abe7d

Browse files
authored
Merge pull request #34 from Odotocodot/dev
Version 3.0.0 - **⚠ Now requires Flow Launcher version `2.1.0` or later.** - Refactored code - Improved Notebook Explorer performance. - Added copy link to clipboard context menu item. ([#32](#32)) - Dependencies - Updated to Net 9.0 - Updated `Flow.Launcher.Plugin-4.1.0` to `Flow.Launcher.Plugin-5.2.0` - Updated `Odotocodot.OneNote.Linq-1.2.0` to `LinqToOneNote-2.0.0` - Migrated from `ModernWpfUI` to `iNKORE.UI.WPF.Modern`
2 parents 3bd57fe + 21b1ce6 commit a4abe7d

37 files changed

Lines changed: 1082 additions & 670 deletions

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Setup .NET
2424
uses: actions/setup-dotnet@v3
2525
with:
26-
dotnet-version: 7.0.x
26+
dotnet-version: 9.x
2727

2828
- name: Get version
2929
id: version

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,4 @@ MigrationBackup/
353353

354354
#VSCode
355355
.vscode/
356+
.idea/

Changelog.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
# Changelog
22

3-
## 2.1.2 - 2025-6-11
3+
## 3.0.0 - 2026-03-04
4+
5+
- **⚠ Now requires Flow Launcher version 2.1.0 or later.**
6+
- Refactored code
7+
- Improved Notebook Explorer performance.
8+
- Added copy link to clipboard context menu item. ([#32](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/32))
9+
- Dependencies
10+
- Updated to Net 9.0
11+
- Updated `Flow.Launcher.Plugin-4.1.0` to `Flow.Launcher.Plugin-5.2.0`
12+
- Updated `Odotocodot.OneNote.Linq-1.2.0` to `LinqToOneNote-2.0.0`
13+
- Migrated from `ModernWpfUI` to `iNKORE.UI.WPF.Modern`
14+
15+
## 2.1.2 - 2025-06-11
416

517
- Added the ability to open items in a new OneNote window using the context menu.([#28](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/28))
618

7-
## 2.1.1 - 2025-1-4
19+
## 2.1.1 - 2025-01-04
820

921
### Changes
1022

@@ -17,7 +29,7 @@
1729

1830
- Fixed spelling and grammar mistakes.
1931

20-
## 2.1.0 - 2024-6-24
32+
## 2.1.0 - 2024-06-24
2133

2234
### Added
2335

Flow.Launcher.Plugin.OneNote/Flow.Launcher.Plugin.OneNote.csproj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net7.0-windows</TargetFramework>
4+
<TargetFramework>net9.0-windows</TargetFramework>
55
<AssemblyName>Flow.Launcher.Plugin.OneNote</AssemblyName>
66
<PackageId>Flow.Launcher.Plugin.OneNote</PackageId>
77
<Authors>Odotocodot</Authors>
@@ -10,6 +10,7 @@
1010
<PackageTags>flow-launcher flow-plugin</PackageTags>
1111
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
1212
<UseWPF>true</UseWPF>
13+
<Nullable>enable</Nullable>
1314
<UseWindowsForms>true</UseWindowsForms>
1415
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <!-- Required for bringing OneNote to front -->
1516
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
@@ -27,11 +28,11 @@
2728
</ItemGroup>
2829

2930
<ItemGroup>
30-
<PackageReference Include="Flow.Launcher.Plugin" Version="4.1.0" />
31+
<PackageReference Include="Flow.Launcher.Plugin" Version="5.2.0" />
3132
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
32-
<PackageReference Include="ModernWpfUI" Version="0.9.6" />
33-
<PackageReference Include="Odotocodot.OneNote.Linq" Version="1.1.0" />
33+
<PackageReference Include="LinqToOneNote" Version="2.1.0" />
3434
<PackageReference Include="System.Drawing.Common" Version="8.0.6" />
35+
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.10.1" />
3536
</ItemGroup>
3637

3738
<ItemGroup>
Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
using System.Drawing;
2-
using Odotocodot.OneNote.Linq;
2+
using LinqToOneNote;
33

44
namespace Flow.Launcher.Plugin.OneNote.Icons
55
{
6-
public record struct IconGeneratorInfo
6+
public struct IconGeneratorInfo
77
{
8-
public string Prefix { get; }
9-
public Color? Color { get; }
8+
public readonly string prefix = string.Empty;
9+
public readonly Color? color;
1010

11-
public IconGeneratorInfo(OneNoteNotebook notebook)
11+
public IconGeneratorInfo(IOneNoteItem item)
1212
{
13-
Prefix = IconConstants.Notebook;
14-
Color = notebook.Color;
15-
}
16-
public IconGeneratorInfo(OneNoteSectionGroup sectionGroup)
17-
{
18-
Prefix = sectionGroup.IsRecycleBin ? IconConstants.RecycleBin : IconConstants.SectionGroup;
19-
}
20-
public IconGeneratorInfo(OneNoteSection section)
21-
{
22-
Prefix = IconConstants.Section;
23-
Color = section.Color;
24-
}
25-
public IconGeneratorInfo(OneNotePage page)
26-
{
27-
Prefix = IconConstants.Page;
13+
switch (item)
14+
{
15+
case Notebook n:
16+
prefix = IconConstants.Notebook;
17+
color = n.Color;
18+
break;
19+
case SectionGroup sg:
20+
prefix = sg.IsRecycleBin ? IconConstants.RecycleBin : IconConstants.SectionGroup;
21+
break;
22+
case Section s:
23+
prefix = IconConstants.Section;
24+
color = s.Color;
25+
break;
26+
case Page:
27+
prefix = IconConstants.Page;
28+
break;
29+
}
2830
}
2931
}
3032
}

Flow.Launcher.Plugin.OneNote/Icons/IconProvider.cs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ namespace Flow.Launcher.Plugin.OneNote.Icons
1212
{
1313
public class IconProvider : BaseModel
1414
{
15+
private readonly PluginInitContext context;
16+
private readonly Settings settings;
17+
private readonly string imagesDirectory;
18+
private readonly ConcurrentDictionary<string, ImageSource> iconCache = new();
19+
1520
public const string Logo = IC.ImagesDirectory + IC.Logo + ".png";
1621
public string Sync => GetIconPath(IC.Sync);
1722
public string Search => GetIconPath(IC.Search);
@@ -23,17 +28,13 @@ public class IconProvider : BaseModel
2328
public string NewSectionGroup => GetIconPath(IC.NewSectionGroup);
2429
public string NewNotebook => GetIconPath(IC.NewNotebook);
2530
public string Warning => settings.IconTheme == IconTheme.Color
26-
? $"{IC.ImagesDirectory}{IC.Warning}.{GetIconThemeString(IconTheme.Dark)}.png"
27-
: GetIconPath(IC.Warning);
28-
29-
private readonly Settings settings;
30-
private readonly ConcurrentDictionary<string,ImageSource> iconCache = new();
31-
private readonly string imagesDirectory;
31+
? $"{IC.ImagesDirectory}{IC.Warning}.{GetIconThemeString(IconTheme.Dark)}.png"
32+
: GetIconPath(IC.Warning);
33+
public static GlyphInfo Clipboard { get; } = new("/Resources/#Segoe Fluent Icons", "\uf0e3"); // Clipboard
3234

33-
public DirectoryInfo GeneratedImagesDirectoryInfo { get; }
3435
public int CachedIconCount => iconCache.Keys.Count(k => char.IsDigit(k.Split('.')[1][1]));
35-
36-
private readonly PluginInitContext context;
36+
public DirectoryInfo GeneratedImagesDirectoryInfo { get; }
37+
3738

3839
public IconProvider(PluginInitContext context, Settings settings)
3940
{
@@ -60,7 +61,7 @@ private static string GetIconThemeString(IconTheme iconTheme)
6061
{
6162
iconTheme = FlowLauncherThemeToIconTheme();
6263
}
63-
return Enum.GetName(iconTheme).ToLower();
64+
return iconTheme.ToString().ToLower();
6465
}
6566

6667
private static IconTheme FlowLauncherThemeToIconTheme()
@@ -79,38 +80,38 @@ private static IconTheme FlowLauncherThemeToIconTheme()
7980

8081
public Result.IconDelegate GetIcon(IconGeneratorInfo info)
8182
{
82-
bool generate = (string.CompareOrdinal(info.Prefix, IC.Notebook) == 0
83-
|| string.CompareOrdinal(info.Prefix, IC.Section) == 0)
83+
bool generate = (string.CompareOrdinal(info.prefix, IC.Notebook) == 0
84+
|| string.CompareOrdinal(info.prefix, IC.Section) == 0)
8485
&& settings.CreateColoredIcons
85-
&& info.Color.HasValue;
86+
&& info.color.HasValue;
8687

8788
return generate ? GetIconGenerated : GetIconStatic;
8889

8990
ImageSource GetIconGenerated()
9091
{
91-
var imageSource = iconCache.GetOrAdd($"{info.Prefix}.{info.Color!.Value.ToArgb()}.png", ImageSourceFactory, info.Color.Value);
92+
var imageSource = iconCache.GetOrAdd($"{info.prefix}.{info.color!.Value.ToArgb()}.png",
93+
static (key, t) => ImageSourceFactory(t.self, key, t.Color),
94+
(Color: info.color.Value, self: this));
9295
OnPropertyChanged(nameof(CachedIconCount));
9396
return imageSource;
9497
}
9598

9699
ImageSource GetIconStatic()
97100
{
98-
return iconCache.GetOrAdd($"{info.Prefix}.{GetIconThemeString(settings.IconTheme)}.png", key =>
99-
{
100-
var path = Path.Combine(imagesDirectory, key);
101-
return BitmapImageFromPath(path);
102-
});
101+
return iconCache.GetOrAdd($"{info.prefix}.{GetIconThemeString(settings.IconTheme)}.png",
102+
static (key,dir) => BitmapImageFromPath(Path.Combine(dir, key)),
103+
imagesDirectory);
103104
}
104105
}
105106

106-
private ImageSource ImageSourceFactory(string key, Color color)
107+
private static ImageSource ImageSourceFactory(IconProvider self, string key, Color color)
107108
{
108109
var prefix = key.Split('.')[0];
109-
var path = Path.Combine(imagesDirectory, $"{prefix}.dark.png");
110+
var path = Path.Combine(self.imagesDirectory, $"{prefix}.dark.png");
110111
var bitmap = BitmapImageFromPath(path);
111112
var newBitmap = ChangeIconColor(bitmap, color);
112113

113-
path = $"{GeneratedImagesDirectoryInfo.FullName}{key}";
114+
path = $"{self.GeneratedImagesDirectoryInfo.FullName}{key}";
114115

115116
using var fileStream = new FileStream(path, FileMode.Create);
116117
var encoder = new PngBitmapEncoder();
Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
1-
namespace Flow.Launcher.Plugin.OneNote
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace Flow.Launcher.Plugin.OneNote
26
{
7+
38
public class Keywords
49
{
510
public const string NotebookExplorerSeparator = "\\";
6-
public string NotebookExplorer { get; set; } = $"nb:{NotebookExplorerSeparator}";
7-
public string RecentPages { get; set; } = "rp:";
8-
public string TitleSearch { get; set; } = "*";
9-
public string ScopedSearch { get; set; } = ">";
11+
public Keyword NotebookExplorer { get; set; } = new($"nb:{NotebookExplorerSeparator}");
12+
public Keyword RecentPages { get; set; } = new ("rp:");
13+
public Keyword TitleSearch { get; set; } = new ("*");
14+
public Keyword ScopedSearch { get; set; } = new (">");
15+
1016
}
17+
18+
[JsonConverter(typeof(KeywordJsonConverter))]
19+
public class Keyword
20+
{
21+
public Keyword(string value) => Value = value;
22+
public string Value { get; private set; }
23+
24+
public void ChangeKeyword(string newValue) => Value = newValue;
25+
26+
public int Length => Value.Length;
27+
public static implicit operator string(Keyword keyword) => keyword.Value;
28+
public override string ToString() => Value;
29+
}
30+
31+
//Needed for legacy as keywords where just saved as a string
32+
public class KeywordJsonConverter : JsonConverter<Keyword>
33+
{
34+
public override Keyword Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
35+
=> new(JsonSerializer.Deserialize<string>(ref reader, options)!);
36+
37+
public override void Write(Utf8JsonWriter writer, Keyword value, JsonSerializerOptions options)
38+
=> JsonSerializer.Serialize(writer, value.Value, options);
39+
}
40+
1141
}

Flow.Launcher.Plugin.OneNote/Main.cs

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
using System.Threading.Tasks;
55
using System.Windows.Controls;
66
using Flow.Launcher.Plugin.OneNote.Icons;
7+
using Flow.Launcher.Plugin.OneNote.Search;
78
using Flow.Launcher.Plugin.OneNote.UI.Views;
8-
using Odotocodot.OneNote.Linq;
9+
using OneNoteApp = LinqToOneNote.OneNote;
910
namespace Flow.Launcher.Plugin.OneNote
1011
{
12+
#nullable disable
1113
public class Main : IAsyncPlugin, IContextMenu, ISettingProvider, IDisposable
1214
{
1315
private PluginInitContext context;
@@ -16,45 +18,51 @@ public class Main : IAsyncPlugin, IContextMenu, ISettingProvider, IDisposable
1618
private SearchManager searchManager;
1719
private Settings settings;
1820
private IconProvider iconProvider;
21+
private VisibilityChanged visibilityChanged;
1922

2023
private static SemaphoreSlim semaphore;
21-
private static Main instance;
24+
2225

23-
private Query currentQuery;
2426
public Task InitAsync(PluginInitContext context)
2527
{
2628
this.context = context;
2729
settings = context.API.LoadSettingJsonStorage<Settings>();
30+
31+
visibilityChanged = new VisibilityChanged(context);
2832
iconProvider = new IconProvider(context, settings);
2933
resultCreator = new ResultCreator(context, settings, iconProvider);
30-
searchManager = new SearchManager(context, settings, resultCreator);
31-
semaphore = new SemaphoreSlim(1,1);
32-
context.API.VisibilityChanged += OnVisibilityChanged;
33-
instance = this;
34+
searchManager = new SearchManager(context, settings, resultCreator, visibilityChanged);
35+
semaphore = new SemaphoreSlim(1, 1);
36+
37+
visibilityChanged.Subscribe(static (isVisible) =>
38+
{
39+
if (!isVisible)
40+
Task.Run(OneNoteApp.ReleaseComObject);
41+
});
3442
return Task.CompletedTask;
3543
}
3644

37-
public void OnVisibilityChanged(object _, VisibilityChangedEventArgs e)
45+
private static async Task OneNoteInitAsync(CancellationToken token)
3846
{
39-
if (context.CurrentPluginMetadata.Disabled || !e.IsVisible)
47+
if (OneNoteApp.HasComObject)
48+
return;
49+
50+
if (!await semaphore.WaitAsync(0,token))
51+
return;
52+
53+
try
4054
{
41-
OneNoteApplication.ReleaseComObject();
55+
OneNoteApp.InitComObject();
56+
}
57+
finally
58+
{
59+
semaphore.Release();
4260
}
4361
}
4462

45-
private static async Task OneNoteInitAsync(CancellationToken token = default)
46-
{
47-
if (semaphore.CurrentCount == 0 || OneNoteApplication.HasComObject)
48-
return;
49-
50-
await semaphore.WaitAsync(token);
51-
OneNoteApplication.InitComObject();
52-
semaphore.Release();
53-
}
5463
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
5564
{
56-
currentQuery = query;
57-
var init = OneNoteInitAsync(token);
65+
Task init = OneNoteInitAsync(token);
5866

5967
if (string.IsNullOrEmpty(query.Search))
6068
return resultCreator.EmptyQuery();
@@ -64,8 +72,6 @@ public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
6472
return searchManager.Query(query);
6573
}
6674

67-
public static void ForceReQuery() => instance.context.API.ChangeQuery(instance.currentQuery.RawQuery, true);
68-
6975
public List<Result> LoadContextMenus(Result selectedResult)
7076
{
7177
return resultCreator.ContextMenu(selectedResult);
@@ -78,9 +84,9 @@ public Control CreateSettingPanel()
7884

7985
public void Dispose()
8086
{
81-
context.API.VisibilityChanged -= OnVisibilityChanged;
87+
visibilityChanged.Dispose();
8288
semaphore.Dispose();
83-
OneNoteApplication.ReleaseComObject();
89+
OneNoteApp.ReleaseComObject();
8490
}
8591
}
8692
}

0 commit comments

Comments
 (0)