Darkmode doesn't stylize all plugin controls on docked window (groupbox, panel, labels etc)
-
@Alan-Kilborn Okay I’ve submitted them as two separate issues here 13572 and 13574
-
I tried my own quick test using the N++ plugin demo, found HERE.
I created a groupbox and an edit control on the docking dialog. I ran it and both were “dark” in dark mode (so good so far). Then in the code, I changed the parent of the edit control to be the groupbox. Rerunning that, the edit control ended up not being “dark”.
So, not much new to add, but basically I can confirm/duplicate your results…
-
FWIW, I worked on recursively applying dark mode styling to all my forms in JsonTools a while back, just using C# code in the tree view controls.
I was able to do it, but I came to the conclusion that while docking forms like my JSON tree and the CSV Lint window look much better when they have the same styling as the editor window, non-docking forms like the find/replace form don’t necessarily look better.
So I’d cast a weak tentative vote in favor of allowing people to opt out of this, although I recognize that adding yet another knob that people can turn has downsides.
-
@Mark-Olson said in Darkmode doesn’t stylize all plugin controls on docked window (groupbox, panel, labels etc):
…don’t necessarily look better
Do you have a specific example you can show?
-
@Alan-Kilborn
No, I never took pictures and I reverted the code to get rid of the recursive application of Notepad++ theme to anything other than my tree viewer. I just decided in my purely subjective opinion that while the tree view looked better sharing the NPP theme, the non-docking forms didn’t.Admittedly the not-so-great result might have been mainly due to my hand-coded attempt to make the controls look similar to Notepad++, and this proposal might look better. I won’t stand in the way of progress.
-
@Mark-Olson I wouldn’t want to bother the user with unnecessary extra options for something like this, imho it should either be all in lightmode or all in darkmode. Wether it looks better or not is subjective and up to the enduser.
As far as I’m concerned it’s just a matter of consistency. So making the plug-in look seemlessly integrated with Notepad++. As it is now, when a user is working with Notepad++ in darkmode, the sudden lightmode dialogs from the plug-in just look out of place.
-
@Bas-de-Reuver said in Darkmode doesn’t stylize all plugin controls on docked window (groupbox, panel, labels etc):
… it should either be all in lightmode or all in darkmode…As far as I’m concerned it’s just a matter of consistency.
I agree.
Even for my simple test:
…I changed the parent of the edit control to be the groupbox. Rerunning that, the edit control ended up not being “dark”.
It was ridiculous to see the white edit box on the otherwise dark dialog.
-
This will probably have to be implemented on the plugin’s end. The minimum required infrastructure would be, roughly:
-
definitions for the Npp API messages added in v8.4.1, i.e.:
NPPM_ISDARKMODEENABLED NPPM_GETDARKMODECOLORS NPPN_DARKMODECHANGED
-
a type equivalent to the NppDarkMode::Colors structure, for collecting the active palette of dark mode styles
-
a method implementing the NPPM_GETDARKMODECOLORS API, taking a pointer to the ersatz
NppDarkMode::Colors
struct
and filling each member with the corresponding color for the active theme (black, cyan, olive, etc.) -
a callback inside
beNotified
that hooksNPPN_DARKMODECHANGED
, where the plugin calls theNPPM_GETDARKMODECOLORS
method, iterates the form’s components, and changes their color to whatever theNppDarkMode::Colors
instance was initialized with
Of course none of these features are provided by the still-popular .NET plugin template, and most likely never will be, until somebody with lots of spare time makes an updated fork.
Some components (e.g. scroll bars) may not be customizable without hacking into the WM_NCPAINT message or, in the case of buttons, using style flags like
BS_OWNERDRAW
. All of which is at least possible in C#, if you don’t mind getting your hands dirty. -
-
This will probably have to be implemented on the plugin’s end.
From what @ozone10 says here and here, looks like I was on the right track.
@Bas-de-Reuver, here’s a quick patch you can build your own proof-of-concept from. It doesn’t touch the form components, but it’s just a matter of exposing the controls and setting the colors however you want.
The
NPPN_DARKMODECHANGED
hook I added toMain.OnNotification
is where you collect the currently active dark mode colors. Notice how the RGB values change when switching tones to red, blue, cyan, etc.
WIP: Integrate Npp’s dark mode API
diff --git a/CSVLintNppPlugin/CsvLintNppPlugin.csproj b/CSVLintNppPlugin/CsvLintNppPlugin.csproj index 2a169a6..f81bebc 100644 --- a/CSVLintNppPlugin/CsvLintNppPlugin.csproj +++ b/CSVLintNppPlugin/CsvLintNppPlugin.csproj @@ -139,6 +139,7 @@ <Compile Include="PluginInfrastructure\ScintillaStreams.cs" /> <Compile Include="PluginInfrastructure\Win32.cs" /> <Compile Include="Main.cs" /> + <Compile Include="PluginInfrastructure\DarkMode.cs" /> <Compile Include="PluginInfrastructure\GatewayDomain.cs" /> <Compile Include="PluginInfrastructure\NotepadPPGateway.cs" /> <Compile Include="PluginInfrastructure\ScintillaGateway.cs" /> diff --git a/CSVLintNppPlugin/Main.cs b/CSVLintNppPlugin/Main.cs index 9f97846..1efd612 100644 --- a/CSVLintNppPlugin/Main.cs +++ b/CSVLintNppPlugin/Main.cs @@ -124,6 +124,31 @@ public static void OnNotification(ScNotification notification) Main.RemoveCSVdef(notification.Header.IdFrom); } + // dark mode (de-)activated + if (code == (uint)NppMsg.NPPN_DARKMODECHANGED) + { + NotepadPPGateway notepad = new NotepadPPGateway(); + if (notepad.IsDarkModeEnabled()) + { + IntPtr theme_ptr = notepad.GetDarkModeColors(); + if (theme_ptr != IntPtr.Zero) + { + DarkModeColors theme = (DarkModeColors)Marshal.PtrToStructure(theme_ptr, typeof(DarkModeColors)); + // TODO: set component colors to `theme.Background`, `theme.SofterBackground`, etc. +#if DEBUG + // show the active palette's RBG values + var msg = new StringBuilder(); + foreach (var style in typeof(DarkModeColors).GetFields(BindingFlags.Instance |BindingFlags.Public)) + { + msg.AppendLine($"{style.Name}: 0x{style.GetValue(theme):X6}"); + } + System.Windows.Forms.MessageBox.Show($"{msg}"); +#endif + } + Marshal.FreeHGlobal(theme_ptr); + } + } + if (code > int.MaxValue) // windows messages { int wm = -(int)code; diff --git a/CSVLintNppPlugin/PluginInfrastructure/DarkMode.cs b/CSVLintNppPlugin/PluginInfrastructure/DarkMode.cs new file mode 100644 index 0000000..70e54d3 --- /dev/null +++ b/CSVLintNppPlugin/PluginInfrastructure/DarkMode.cs @@ -0,0 +1,46 @@ +// Types, methods for interacing with Npp's dark mode API +using System; +using System.Runtime.InteropServices; + +namespace Kbg.NppPluginNET.PluginInfrastructure +{ + /// <see href "https://github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/src/NppDarkMode.h"/> + [StructLayout(LayoutKind.Sequential)] + public struct DarkModeColors + { + public int Background; + public int SofterBackground; + public int HotBackground; + public int PureBackground; + public int ErrorBackground; + public int Text; + public int DarkerText; + public int DisabledText; + public int LinkText; + public int Edge; + public int HotEdge; + public int DisabledEdge; + } + + /// <summary> + /// Implements dark mode APIs for plugins. + /// </summary> + public partial class NotepadPPGateway : INotepadPPGateway + { + /// Returns a pointer so that callees can deallocate it. + public IntPtr GetDarkModeColors() + { + DarkModeColors darkModeColors = new DarkModeColors(); + IntPtr _size = new IntPtr(Marshal.SizeOf(darkModeColors)); + IntPtr _ptrDarkModeColors = Marshal.AllocHGlobal(_size); + Win32.SendMessage(PluginBase.nppData._nppHandle, (uint) NppMsg.NPPM_GETDARKMODECOLORS, _size, _ptrDarkModeColors); + return _ptrDarkModeColors; + } + + public bool IsDarkModeEnabled() + { + IntPtr result = Win32.SendMessage(PluginBase.nppData._nppHandle, (uint) NppMsg.NPPM_ISDARKMODEENABLED, Unused, Unused); + return ((int)result == 1); + } + } +} diff --git a/CSVLintNppPlugin/PluginInfrastructure/Msgs_h.cs b/CSVLintNppPlugin/PluginInfrastructure/Msgs_h.cs index 3319ce2..4363fa1 100644 --- a/CSVLintNppPlugin/PluginInfrastructure/Msgs_h.cs +++ b/CSVLintNppPlugin/PluginInfrastructure/Msgs_h.cs @@ -565,6 +565,45 @@ public enum NppMsg : uint /// </summary> NPPM_ADDTOOLBARICON_FORDARKMODE = Constants.NPPMSG + 101, + /// <summary> + /// bool NPPM_ISDARKMODEENABLED(0, 0) + /// Returns true when Notepad++ Dark Mode is enable, false when it is not. + /// <see href="https://github.com/notepad-plus-plus/notepad-plus-plus/commit/1eb5b10e41d7ab92b60aa32b28d4fe7739d15b53"/> + /// </summary> + NPPM_ISDARKMODEENABLED = (Constants.NPPMSG + 107), + + /// <summary> + /// bool NPPM_GETDARKMODECOLORS (size_t cbSize, NppDarkMode::Colors* returnColors) + /// - cbSize must be filled with sizeof(NppDarkMode::Colors). + /// - returnColors must be a pre-allocated NppDarkMode::Colors struct. + /// Returns true when successful, false otherwise. + /// You need to uncomment the following code to use NppDarkMode::Colors structure: + /// <code> + /// namespace NppDarkMode + /// { + /// struct Colors + /// { + /// COLORREF background = 0; + /// COLORREF softerBackground = 0; + /// COLORREF hotBackground = 0; + /// COLORREF pureBackground = 0; + /// COLORREF errorBackground = 0; + /// COLORREF text = 0; + /// COLORREF darkerText = 0; + /// COLORREF disabledText = 0; + /// COLORREF linkText = 0; + /// COLORREF edge = 0; + /// COLORREF hotEdge = 0; + /// COLORREF disabledEdge = 0; + /// }; + /// } + /// </code> + /// Note: in the case of calling failure ("false" is returned), you may need to change NppDarkMode::Colors structure to: + /// https:///github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/src/NppDarkMode.h#L32 + /// <see href="https://github.com/notepad-plus-plus/notepad-plus-plus/commit/1eb5b10e41d7ab92b60aa32b28d4fe7739d15b53"/> + /// </summary> + NPPM_GETDARKMODECOLORS = (Constants.NPPMSG + 108), + RUNCOMMAND_USER = Constants.WM_USER + 3000, NPPM_GETFULLCURRENTPATH = RUNCOMMAND_USER + FULL_CURRENT_PATH, NPPM_GETCURRENTDIRECTORY = RUNCOMMAND_USER + CURRENT_DIRECTORY, @@ -810,6 +849,14 @@ public enum NppMsg : uint /// </summary> NPPN_FILEDELETED = NPPN_FIRST + 26, + /// <summary> + /// To notify plugins that Dark Mode was enabled/disabled + /// scnNotification->nmhdr.code = NPPN_DARKMODECHANGED; + /// scnNotification->nmhdr.hwndFrom = hwndNpp; + /// scnNotification->nmhdr.idFrom = 0; + /// </summary> + NPPN_DARKMODECHANGED = (NPPN_FIRST + 27) + /* --Autogenerated -- end of section automatically generated from notepad-plus-plus\PowerEditor\src\MISC\PluginsManager\Notepad_plus_msgs.h * */ } } diff --git a/CSVLintNppPlugin/PluginInfrastructure/NotepadPPGateway.cs b/CSVLintNppPlugin/PluginInfrastructure/NotepadPPGateway.cs index 7b599a0..d3e0467 100644 --- a/CSVLintNppPlugin/PluginInfrastructure/NotepadPPGateway.cs +++ b/CSVLintNppPlugin/PluginInfrastructure/NotepadPPGateway.cs @@ -18,7 +18,7 @@ public interface INotepadPPGateway /// This class holds helpers for sending messages defined in the Msgs_h.cs file. It is at the moment /// incomplete. Please help fill in the blanks. /// </summary> - public class NotepadPPGateway : INotepadPPGateway + public partial class NotepadPPGateway : INotepadPPGateway { private const int Unused = 0;
-
Submitted my implementation upstream, with a real usage example: https://github.com/kbilsted/NotepadPlusPlusPluginPack.Net/pull/104
-
@rdipardo
Cool! I’m going to run with your example and see about writing something that just automatically applies good dark mode styling to all the controls in any arbitrary WinForm rather than having to specify which controls get which colors.