C++ DarkMode dialogs -- not all controls inherit the mode
-
I’m using the official C++ template for my plugins.
For my initial release on both, I pretended not to think about DarkMode compatibility, but I’m starting to work on that now that v1 is out on both of them.
By following the NPPM_DARKMODESUBCLASSANDTHEME and the example of adding DarkMode to MIME Tools, I was able to get CollectionInterface’s About box to be nicely DarkMode in the darkMode branch:
Unfrotunately, when I followed the same procedure on the “Download” dialog for the same plugin, I came across a snag:
… It uses aCONTROL ... "SysTabControl32"
tab interface, which appears to not inherit the DarkMode when I use that message. Also, theCONTROL ... "msctls_progress32"
progress bar, based on its background, appears to also not get the “magic” that the NPPM_DARKMODESUBCLASSANDTHEME is supposed to impart.For those who have more experience with such things than I do: Is that something I have to fix on my end – and if so, are there any instructions available, and/or examples of how? Or do I need to put in a feature request to improve
NPPM_DARKMODESUBCLASSANDTHEME
’s behavior?Secondarily: if I put the NPPM_DARKMODESUBCLASSANDTHEME “after controls initializations in WM_INITDIALOG” as recommended in the Manual (which was derived from Don’s instructions when the new message was added), then I can see the dialog initially draw itself in light-mode, then re-color at the end. If I move the message to the beginning of WM_INITDIALOG, then the dialog seems to be drawn dark initially (or changes color too fast for me to notice), which is what I’d prefer… but the scroll-bar disappears from the ListBox, and I can only scroll using arrow keys, not the bar:
Is there a way to get it to draw the dialog dark to begin with, without breaking the functionality of standard controls like the list-box’s scrollbar? -
I’m actually pretty proud of the algorithm NppCSharpPluginPack uses to keep its dialog colors in sync with Notepad++. What’s cool about this algorithm is that it follows light mode themes (e.g., a green/yellow palette for Mossylawn), not just dark mode/light mode.
The basic algorithm is not that hard - choose a set of colors for various parts of each dialog by doing some arithmetic of the RBG values of the text color and background color, and then recursively style each child control of the dialog according to that color scheme. The main issue I had to overcome was figuring out how to derive a good color scheme from the NPP color scheme, and then figure out which controls should be assigned which colors.
I have no idea how hard it would be to translate this to C++, but I can’t believe it would be too much work.
-
@Mark-Olson said in C++ DarkMode dialogs -- not all controls inherit the mode:
I have no idea how hard it would be to translate this to C++, but I can’t believe it would be too much work.
I doubt I’ll borrow the entire algorithm – I will probably just let it use the defaults for Light, and the colors that the NPPM_GETDARKMODECOLORS message supplies for Dark.
But based on a quick reading of your code, the answer seems to be “manually set the foreground and background for any control [that’s not already set by the message], possibly requiring recursive navigation through the dialog”. I guess I’ll have to dig into how to do that color setting in the old-style C++ interface. I’ll have to see whether it’s better to do it manually for just that, or re-create the subclassing and messages that Notepad++ does with the message, but adding more controls to the case… Some more exploration coming, I guess.
-
Unfortunately, “SysTabControl32” is a different beast than the others… I’ve been slowly piecing together the information, but it doesn’t have WM_CTLCOLORxyz like the static/label, buttons, edits, listboxes, comboboxes and the like (and doesn’t seem to trigger the original WM_CTLCOLOR that those more-specific messages replaced, either).
I was able to find that if I set the control to TCS_OWNERDRAWFIXED, then I can handle WM_DRAWITEM, and with enough web searches, figured out how to manually fill out the tab labels that happen automatically when it’s not TCS_OWNERDRAWFIXED, and so while doing that, I can change the FG and BG colors for the text. Unfortunately, the outer tab wrapper is still staying the default gray background, no matter what I do.
Has anyone made use of the “SysTabControl32” in C++ with DarkMode?
I don’t know how long I’ll keep going down this road – I might just switch back to my original idea of doing the UDL/AutoCompletion/FunctionList/Theme selection as a combobox, which would get rid of the not-playing-nice tabbed-interface… but I won’t give up yet.
-
@PeterJones A possible hint…
Shortcut Mapper uses a SysTabControl32, and it honors dark mode.
It looks like Notepad++ handles that as part of something it calls a BabyGridWrapper; I think the code that sets the colors might be here.
That’s as far as I followed it.
-
@Coises said in C++ DarkMode dialogs -- not all controls inherit the mode:
@PeterJones A possible hint…
Shortcut Mapper uses a SysTabControl32, and it honors dark mode.
I really should have thought of that myself. I was too busy trying to find a plugin that both uses it and supports dark mode. :-)
It looks like Notepad++ handles that as part of something it calls a BabyGridWrapper;
Actually, you have fallen prey to one of the mental traps of tab-controls, and misinterpreted what’s inside the tab compared to the tab itself. In this case, the BabyGrid is the table/spreadsheet/grid of name/shortcut/category (column) vs command (row) which is inside the tab.
But once you said that, I was able to look around and find where the app handles the actual tab-control, and I see it calls the method
NppDarkMode::subclassTabControl()
to subclasses the Tab interface for changing the color in Dark Mode… so it looks like I’ll have to duplicate that subclassing (because the message doesn’t call that separate subclassing) -
Success.
So that other plugin authors can learn from my last couple of days, here is an example of what I did in order to get DarkMode to apply to the SysTabControl32 (I don’t know if it’s the best way; but it’s the way I was able to figure out):
In my header files, I have added a copy of some of the structures from N++ repo, including NppDarkMode.h’s
struct Colors
andstruct Brushes
andstruct Pens
from NppDarkMode.cpp, and then make instances of those objects as file-level globals (that’s what allows me to grab the colors using the NPPM_GETDARKMODECOLORS message, and then make sure I have the brushes/pens derived from those colors.)NppDarkMode::Colors myColors = { 0 }; NppDarkMode::Brushes myBrushes(myColors); NppDarkMode::Pens myPens(myColors);
In
WM_INITDIALOG
, I add// determine dark mode g_IsDarkMode = (bool)::SendMessage(nppData._nppHandle, NPPM_ISDARKMODEENABLED, 0, 0); if (g_IsDarkMode) { ::SendMessage(nppData._nppHandle, NPPM_GETDARKMODECOLORS, sizeof(NppDarkMode::Colors), reinterpret_cast<LPARAM>(&myColors)); myBrushes.change(myColors); myPens.change(myColors); // subclass the tab control, because NPPM_DARKMODESUBCLASSANDTHEME doesn't apply to SysTabControl32 ::SetWindowSubclass(GetDlgItem(g_hwndCIDlg, IDC_CI_TABCTRL), cbTabSubclass, g_ci_dark_subclass, 0); // For the rest, follow Notpead++ DarkMode settings ::SendMessage(nppData._nppHandle, NPPM_DARKMODESUBCLASSANDTHEME, static_cast<WPARAM>(NppDarkMode::dmfInit), reinterpret_cast<LPARAM>(g_hwndCIDlg)); }
Finally, I took a copy of the
TabSubclass()
callback in NppDarkMode.cpp, and adjusted it so it used my color/brush/pen instances rather than the methods, and other similar tweaks to get it to compile. (And it doesn’t apparently need the WM_PARENTNOTIFY, at least not with my four-tab interface)With that, I can get the tabbed-interface part of the dialog to also follow DarkMode (including the various standard or customized tones.
-
@PeterJones said in C++ DarkMode dialogs -- not all controls inherit the mode:
Success.
Congratulations!
Do you think it’s possible that not including a call to subclassTabControl in autoSubclassAndThemeChildControls — which in turn is called by autoSubclassAndThemePlugin, which implements NPPM_DARKMODESUBCLASSANDTHEME — is an oversight? Would it be worth a feature request issue?
-
@Coises said in C++ DarkMode dialogs -- not all controls inherit the mode:
is an oversight? Would it be worth a feature request issue?
Probably.
I just did an experimental build of N++, calling
subclassTabControl
from an "if WC_TABCONTROL, and it seems to work – in that, whether or not my plugin subclasses on its end, if I have that call in N++, it makes the tab control properly follow N++ DarkMode settings.I’ll create an Issue (complete with making a branch of my plugin where I don’t manually subclass, so there can be easy steps-to-reproduce), and then submit a proposed PR to fix it.
update: