Skip to content

Commit

Permalink
Added high-DPI (200%) and light mode tray icons.
Browse files Browse the repository at this point in the history
  • Loading branch information
Aldaviva committed Aug 8, 2022
1 parent b914971 commit 45a9a42
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 125 deletions.
12 changes: 5 additions & 7 deletions KeePassTrayIconLockState/DatabaseOpenState.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
namespace KeePassTrayIconLockState {
namespace KeePassTrayIconLockState;

internal enum DatabaseOpenState {
internal enum DatabaseOpenState {

CLOSED,
OPENING,
OPEN

}
CLOSED,
OPENING,
OPEN

}
11 changes: 10 additions & 1 deletion KeePassTrayIconLockState/KeePassTrayIconLockState.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="DarkNet">
<HintPath>..\..\darknet\darknet\bin\Debug\net452\DarkNet.dll</HintPath>
</Reference>
<Reference Include="KeePass">
<HintPath>..\..\..\..\..\..\Programs\Security\KeePass\KeePass.exe</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="KoKo">
<HintPath>..\..\KoKo\KoKo\bin\Release\net45\KoKo.dll</HintPath>
<HintPath>..\..\KoKo\KoKo\bin\Release\net452\KoKo.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
Expand Down Expand Up @@ -83,5 +86,11 @@
<ItemGroup>
<None Include="version.txt" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\unlocked-light.ico" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\unlocking-light.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
190 changes: 103 additions & 87 deletions KeePassTrayIconLockState/KeePassTrayIconLockStateExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,99 +5,115 @@
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Dark.Net;
using KeePass.Plugins;
using KeePass.Resources;
using KoKo.Property;

namespace KeePassTrayIconLockState {

// ReSharper disable once UnusedType.Global
public class KeePassTrayIconLockStateExt: Plugin {

internal static readonly TimeSpan STARTUP_DURATION = TimeSpan.FromMilliseconds(2000);

private readonly StoredProperty<DatabaseOpenState> databaseOpenState = new();

private IPluginHost keePassHost = null!;
private Property<TrayIcon> trayIcon = null!;

public override Image SmallIcon => Resources.plugin_image;
public override string UpdateUrl { get; } = @"https://raw.githubusercontent.com/Aldaviva/KeePassTrayIconLockState/master/KeePassTrayIconLockState/version.txt";

public override bool Initialize(IPluginHost host) {
keePassHost = host;

keePassHost.MainWindow.FileOpened += delegate { databaseOpenState.Value = DatabaseOpenState.OPEN; };
keePassHost.MainWindow.FileClosed += delegate { databaseOpenState.Value = DatabaseOpenState.CLOSED; };

ToolStripItemCollection statusBarItems = keePassHost.MainWindow.Controls.OfType<StatusStrip>().First().Items;
ToolStripItem statusBarInfo = statusBarItems[1];
ToolStripItem progressBar = statusBarItems[2];
Property<string> statusBarText = new NativeReadableProperty<string>(statusBarInfo, nameof(ToolStripItem.Text), nameof(ToolStripItem.TextChanged));
Property<bool> isProgressBarVisible = new NativeReadableProperty<bool>(progressBar, nameof(ToolStripItem.Visible), nameof(ToolStripItem.VisibleChanged));

statusBarText.PropertyChanged += (_, args) => {
if (args.NewValue == KPRes.OpeningDatabase2) {
/*
* When the status bar text changes to "Opening database...", set the db open state to OPENING.
*/
databaseOpenState.Value = DatabaseOpenState.OPENING;
}
};

isProgressBarVisible.PropertyChanged += (_, args) => {
if (!args.NewValue && databaseOpenState.Value == DatabaseOpenState.OPENING) {
/*
* When the database is being opened and the progress bar gets hidden, it means the database was finished being decrypted, but was it successful or unsuccessful?
* Sadly there is no good way to tell, so instead we wait 100 ms for the FileOpen event to be fired.
* If it is fired, the db open state moves from OPENING to OPEN (above).
* Otherwise, assume that the database failed to decrypt and set the db open state to CLOSED.
*/
Task.Delay(100).ContinueWith(_ => {
if (databaseOpenState.Value == DatabaseOpenState.OPENING) { // failed to decrypt, otherwise this would have been set to OPEN by the FileOpen event above.
databaseOpenState.Value = DatabaseOpenState.CLOSED;
}
});

} else if (args.NewValue && databaseOpenState.Value == DatabaseOpenState.CLOSED && statusBarText.Value == KPRes.OpeningDatabase2) {
/*
* When the database is closed and the status bar says "Opening database..." and the progress bar gets shown, it means the user already failed the previous decryption
* attempt and is retrying after submitting another password, so set the db opening state to OPENING.
*/
databaseOpenState.Value = DatabaseOpenState.OPENING;
}
};

trayIcon = DerivedProperty<TrayIcon>.Create(databaseOpenState, openState => new TrayIcon(getIcon(openState), openState != DatabaseOpenState.CLOSED));

trayIcon.PropertyChanged += (_, _) => renderTrayIcon();

/*
* KeePass sets its own icon at some indeterminate time after startup, so repeatedly set our own icon every 8 ms for 2 seconds to make sure our icon isn't overridden.
*/
Timer startupTimer = new() { Enabled = true, Interval = 8 };
startupTimer.Tick += delegate { renderTrayIcon(); };
Task.Delay(STARTUP_DURATION).ContinueWith(_ => startupTimer.Stop());
renderTrayIcon();

return true;
}

private void renderTrayIcon() {
TrayIcon iconToRender = trayIcon.Value;
NotifyIcon keepassIcon = keePassHost.MainWindow.MainNotifyIcon;

keepassIcon.Icon = iconToRender.image;
keepassIcon.Visible = iconToRender.isVisible;
}

private static Icon getIcon(DatabaseOpenState databaseOpenState) => databaseOpenState switch {
DatabaseOpenState.CLOSED => Resources.locked,
DatabaseOpenState.OPENING => Resources.unlocking,
DatabaseOpenState.OPEN => Resources.unlocked,
_ => throw new ArgumentOutOfRangeException(nameof(databaseOpenState), databaseOpenState, nameof(getIcon))
namespace KeePassTrayIconLockState;

// ReSharper disable once UnusedType.Global
public class KeePassTrayIconLockStateExt: Plugin {

internal static readonly TimeSpan STARTUP_DURATION = TimeSpan.FromMilliseconds(2000);

private readonly StoredProperty<DatabaseOpenState> databaseOpenState = new();
private readonly IDarkNet darkNet = new DarkNet();

private IPluginHost keePassHost = null!;
private Property<TrayIcon> trayIcon = null!;
private Property<bool> taskbarIsDarkTheme = null!;

public override Image SmallIcon => Resources.plugin_image;
public override string UpdateUrl { get; } = @"https://raw.githubusercontent.com/Aldaviva/KeePassTrayIconLockState/master/KeePassTrayIconLockState/version.txt";

public override bool Initialize(IPluginHost host) {
keePassHost = host;

keePassHost.MainWindow.FileOpened += delegate { databaseOpenState.Value = DatabaseOpenState.OPEN; };
keePassHost.MainWindow.FileClosed += delegate { databaseOpenState.Value = DatabaseOpenState.CLOSED; };

ToolStripItemCollection statusBarItems = keePassHost.MainWindow.Controls.OfType<StatusStrip>().First().Items;
ToolStripItem statusBarInfo = statusBarItems[1];
ToolStripItem progressBar = statusBarItems[2];
Property<string> statusBarText = new NativeReadableProperty<string>(statusBarInfo, nameof(ToolStripItem.Text), nameof(ToolStripItem.TextChanged));
Property<bool> isProgressBarVisible = new NativeReadableProperty<bool>(progressBar, nameof(ToolStripItem.Visible), nameof(ToolStripItem.VisibleChanged));

statusBarText.PropertyChanged += (_, args) => {
if (args.NewValue == KPRes.OpeningDatabase2) {
/*
* When the status bar text changes to "Opening database...", set the db open state to OPENING.
*/
databaseOpenState.Value = DatabaseOpenState.OPENING;
}
};

isProgressBarVisible.PropertyChanged += (_, args) => {
if (!args.NewValue && databaseOpenState.Value == DatabaseOpenState.OPENING) {
/*
* When the database is being opened and the progress bar gets hidden, it means the database was finished being decrypted, but was it successful or unsuccessful?
* Sadly there is no good way to tell, so instead we wait 100 ms for the FileOpen event to be fired.
* If it is fired, the db open state moves from OPENING to OPEN (above).
* Otherwise, assume that the database failed to decrypt and set the db open state to CLOSED.
*/
Task.Delay(100).ContinueWith(_ => {
if (databaseOpenState.Value == DatabaseOpenState.OPENING) { // failed to decrypt, otherwise this would have been set to OPEN by the FileOpen event above.
databaseOpenState.Value = DatabaseOpenState.CLOSED;
}
});

} else if (args.NewValue && databaseOpenState.Value == DatabaseOpenState.CLOSED && statusBarText.Value == KPRes.OpeningDatabase2) {
/*
* When the database is closed and the status bar says "Opening database..." and the progress bar gets shown, it means the user already failed the previous decryption
* attempt and is retrying after submitting another password, so set the db opening state to OPENING.
*/
databaseOpenState.Value = DatabaseOpenState.OPENING;
}
};

taskbarIsDarkTheme = new NativeReadableProperty<bool>(darkNet, nameof(IDarkNet.UserTaskbarThemeIsDark), nameof(IDarkNet.UserTaskbarThemeIsDarkChanged));

trayIcon = DerivedProperty<TrayIcon>.Create(databaseOpenState, taskbarIsDarkTheme,
(openState, isDarkTheme) => new TrayIcon(getIcon(openState, isDarkTheme), openState != DatabaseOpenState.CLOSED));

trayIcon.PropertyChanged += delegate { renderTrayIcon(); };

/*
* KeePass sets its own icon at some indeterminate time after startup, so repeatedly set our own icon every 8 ms for 2 seconds to make sure our icon isn't overridden.
*/
Timer startupTimer = new() { Enabled = true, Interval = 8 };
startupTimer.Tick += delegate { renderTrayIcon(); };
Task.Delay(STARTUP_DURATION).ContinueWith(_ => startupTimer.Dispose());
renderTrayIcon();

return true;
}

private void renderTrayIcon() {
TrayIcon iconToRender = trayIcon.Value;
NotifyIcon keepassIcon = keePassHost.MainWindow.MainNotifyIcon;

keepassIcon.Icon = iconToRender.image;
keepassIcon.Visible = iconToRender.isVisible;
}

private static Icon getIcon(DatabaseOpenState databaseOpenState, bool isDarkTheme) {
Icon iconResource = databaseOpenState switch {
DatabaseOpenState.CLOSED when isDarkTheme => Resources.locked,
DatabaseOpenState.CLOSED => Resources.locked,
DatabaseOpenState.OPENING when isDarkTheme => Resources.unlocking,
DatabaseOpenState.OPENING => Resources.unlocking_light,
DatabaseOpenState.OPEN when isDarkTheme => Resources.unlocked,
DatabaseOpenState.OPEN => Resources.unlocked_light,
_ => throw new ArgumentOutOfRangeException(nameof(databaseOpenState), databaseOpenState, nameof(getIcon))
};
Icon sizedIconResource = new(iconResource, SystemInformation.SmallIconSize);
return sizedIconResource;
}

public override void Terminate() {
darkNet.Dispose();
base.Terminate();
}

}
6 changes: 3 additions & 3 deletions KeePassTrayIconLockState/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Ben Hutchison")]
[assembly: AssemblyProduct("KeePass Plugin")]
[assembly: AssemblyCopyright("Copyright © Ben Hutchison 2020")]
[assembly: AssemblyCopyright("© 2022 Ben Hutchison")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

Expand All @@ -17,7 +17,7 @@

// Remember to also update version.txt when changing these version attributes so that online update checking works.
// https://keepass.info/help/v2_dev/plg_index.html#upd
[assembly: AssemblyVersion("1.0.2.0")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]

[assembly: InternalsVisibleTo("Test")]
22 changes: 21 additions & 1 deletion KeePassTrayIconLockState/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions KeePassTrayIconLockState/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,13 @@
<data name="unlocked" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\unlocked.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="unlocked_light" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\unlocked-light.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="unlocking" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>resources\unlocking.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="unlocking_light" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>resources\unlocking-light.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
Binary file not shown.
Binary file modified KeePassTrayIconLockState/Resources/unlocked.ico
Binary file not shown.
Binary file not shown.
Binary file modified KeePassTrayIconLockState/Resources/unlocking.ico
Binary file not shown.
48 changes: 23 additions & 25 deletions KeePassTrayIconLockState/TrayIcon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,38 @@

using System.Drawing;

namespace KeePassTrayIconLockState {
namespace KeePassTrayIconLockState;

internal readonly struct TrayIcon {
internal readonly struct TrayIcon {

internal readonly Icon image;
internal readonly bool isVisible;
internal readonly Icon image;
internal readonly bool isVisible;

public TrayIcon(Icon image, bool isVisible) {
this.image = image;
this.isVisible = isVisible;
}

private bool Equals(TrayIcon other) {
return image.Equals(other.image) && isVisible == other.isVisible;
}
public TrayIcon(Icon image, bool isVisible) {
this.image = image;
this.isVisible = isVisible;
}

public override bool Equals(object? obj) {
return obj is TrayIcon other && Equals(other);
}
public bool Equals(TrayIcon other) {
return image.Equals(other.image) && isVisible == other.isVisible;
}

public override int GetHashCode() {
unchecked {
return (image.GetHashCode() * 397) ^ isVisible.GetHashCode();
}
}
public override bool Equals(object? obj) {
return obj is TrayIcon other && Equals(other);
}

public static bool operator ==(TrayIcon left, TrayIcon right) {
return left.Equals(right);
public override int GetHashCode() {
unchecked {
return (image.GetHashCode() * 397) ^ isVisible.GetHashCode();
}
}

public static bool operator !=(TrayIcon left, TrayIcon right) {
return !left.Equals(right);
}
public static bool operator ==(TrayIcon left, TrayIcon right) {
return left.Equals(right);
}

public static bool operator !=(TrayIcon left, TrayIcon right) {
return !left.Equals(right);
}

}
2 changes: 1 addition & 1 deletion KeePassTrayIconLockState/version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:
KeePassTrayIconLockState:1.0.2
KeePassTrayIconLockState:1.1.0
:

0 comments on commit 45a9a42

Please sign in to comment.