Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 560316

Summary: [win32][Dark theme] Text border color is too bright for dark theme
Product: [Eclipse Project] Platform Reporter: Alexandr Miloslavskiy <alexandr.miloslavskiy>
Component: SWTAssignee: Alexandr Miloslavskiy <alexandr.miloslavskiy>
Status: VERIFIED FIXED QA Contact: Niraj Modi <niraj.modi>
Severity: normal    
Priority: P3 CC: alexandr.miloslavskiy, Lars.Vogel, nikita, niraj.modi, ts-swt
Version: 4.14   
Target Milestone: 4.16 M3   
Hardware: PC   
OS: Windows 10   
See Also: https://git.eclipse.org/r/158043
https://git.eclipse.org/r/158042
https://git.eclipse.org/r/158041
https://bugs.eclipse.org/bugs/show_bug.cgi?id=576614
Whiteboard:
Bug Depends on:    
Bug Blocks: 562193    
Attachments:
Description Flags
Screenshot with old code
none
Screenshot with new code
none
Eclipse (old)
none
Eclipse (with patch) none

Description Alexandr Miloslavskiy CLA 2020-02-19 08:48:15 EST
I will provide patches soon.
Comment 1 Eclipse Genie CLA 2020-02-20 09:06:30 EST
New Gerrit change created: https://git.eclipse.org/r/158043
Comment 2 Eclipse Genie CLA 2020-02-20 09:06:33 EST
New Gerrit change created: https://git.eclipse.org/r/158042
Comment 3 Eclipse Genie CLA 2020-02-20 09:06:37 EST
New Gerrit change created: https://git.eclipse.org/r/158041
Comment 4 Alexandr Miloslavskiy CLA 2020-02-20 09:12:36 EST
Created attachment 281874 [details]
Screenshot with old code
Comment 5 Alexandr Miloslavskiy CLA 2020-02-20 09:12:55 EST
Created attachment 281875 [details]
Screenshot with new code
Comment 6 Nikita Nemkin CLA 2020-02-28 11:59:44 EST
I really disagree with the direction you're taking: spreading dirty hacks all over SWT fake a dark theme. We only need a single knob: native dark theme enabled/disabled.

Exactly how to make the broken native win32 dark theme functional is a matter of debate, but I think a few native function hooks will get us there with no changes to SWT code at all. Have you considered this approach? I did some experiments, and it appears very promising.

Considering your reverse engineering work, I'd expect you to be the first person to propose uxtheme hooking.
Comment 7 Alexandr Miloslavskiy CLA 2020-02-28 12:35:05 EST
We do use SWT, but not for Eclipse. We also basically want a dark theme, but it's a different dark theme compared to what Eclipse does. If a binary configuration is to be made, I will apparently have to choose compatibility with _our_ dark theme. Otherwise, a more fine-tuned configuration is needed. That's what I currently aim for.

As for uxtheme hooks,
1) I am not aware of a way to hook native functions with SWT.
2) I think that native hooks are a complicated beast to make really reliable.
Comment 8 Alexandr Miloslavskiy CLA 2020-03-03 06:38:51 EST
With some extra time to think, I still believe that my approach is better:

1) Again, I'm not aware of ready-to-use native hooking in SWT.
   A possible solution here would be to add some native hooking library to
   SWT's native library and then make some APIs to install Java-side handler
   for this hook.

2) Again, I'm rather worried about how reliable it's going to be. I would expect
   various kinds of problems.

   For example, multi-threading: while SWT itself tends to handle UI from one
   thread, Windows does sometime use additional threads. Then we'll have to
   either ignore these hook calls (and some parts of hooked drawing will
   sometimes draw old stuff) or make larger portions of SWT multithread-ready.
   At least the GC stuff. And even then, we'll need to make sure to avoid
   deadlocks, because windows thread hook can fire at unpredictable times.

   This issue is just one of many that I immediately feel worried about.

3) One obvious target for hooking would be 'uxtheme!DrawThemeBackground'.
   However, we still need an implementation for that drawing! That is, partially
   implementing owner draw. Let's for example discuss scrollbars. It's doable to
   draw them similar to what Windows does, but that's quite a bit of effort. The
   other tempting idea is to use images from actual Windows dark theme, but that 
   would be subject to copyright issues.

4) Some window parts (I believe including window borders addressed by this
   patch) are handled in kernel and simply can't be hooked from user space.

That you call "dirty hacks" I would rather call additional configuration options. To me, selecting a type of window border is definitely a configuration option and not a hack. Afterall, in native it's a configuration parameter to an API. And to me it sounds perfectly reasonable to have more configurable (rather then hardcoded) options, especially when they solve real world problems.
Comment 9 Nikita Nemkin CLA 2020-03-03 11:48:04 EST
(In reply to Alexandr Miloslavskiy from comment #8)
The purpose of hooks is to implement complete, quasi-native dark theme, i.e. to complement or replace Win10 native dark (AllowDarkModeForWindow),
Everything will happen in native code, below SWT level, and the only API is setTheme(DARK/LIGHT), just like Cocoa.

I think it's impossible to deliver a quality dark theme on Windows without hooking.

Hooking public WinAPI functions in your own process is both simple in reliable. I'm using Detours library (MIT license) for my experiments. I could roll my own hooking, but there's no point (and no money).

Win32 controls don't draw on background threads. We can either ignore (pass-through) non-main thread calls or make hooked functions thread-safe. It's not a problem in practice.

You're right that DrawThemeBackgroundEx is the most complex function to implement. All other functions are basically system color queries. Still, DrawThemeBackgroundEx is much simpler than owner drawing controls. It either draws a single bitmap or a single color. There's no logic beyond picking the bitmap/color for a given the part/state.

To maintain native look and feel, dark control bitmaps have to be derived from Windows bitmaps, there's no way around it. Since Win10 theme is rather flat, I hope to cut the number of required bitmaps. E.g. buttons can be drawn with fills and borders, so are edit/combo boxes and scrollbars (except the arrows). I might even try to draw checkbox/etc glyphs from fonts to save the effort of crafting 5 DPI variants of every bitmap.

Regardign borders, most of the borders come from the theme. Old-style borders can be overridden in WM_NCPAINT (probably). DefWindowProc has to be hooked anyway, for WM_CTLCOLOR and WM_ERASEBKGND.

Adding platform-specific custom properties to override native painting is totally a hack, especially when the true goal is not extra configurability, but faking a dark theme.
Comment 10 Niraj Modi CLA 2020-03-04 03:39:13 EST
(In reply to Nikita Nemkin from comment #9)
> (In reply to Alexandr Miloslavskiy from comment #8)
> The purpose of hooks is to implement complete, quasi-native dark theme, i.e.
> to complement or replace Win10 native dark (AllowDarkModeForWindow),
> Everything will happen in native code, below SWT level, and the only API is
> setTheme(DARK/LIGHT), just like Cocoa.
> 
> I think it's impossible to deliver a quality dark theme on Windows without
> hooking.
> 
> Hooking public WinAPI functions in your own process is both simple in
> reliable. I'm using Detours library (MIT license) for my experiments. I
> could roll my own hooking, but there's no point (and no money).
Hi Nikita,
For proper evaluation of both the approaches, we will need more information on the experiment with 3rd party library you mentioned above.
If not patches, then may be sharing the steps via an article on SWT website would be a good starting point for others to try:
git://git.eclipse.org/gitroot/www.eclipse.org/swt.git
Comment 11 Alexandr Miloslavskiy CLA 2020-03-04 05:51:35 EST
> Win32 controls don't draw on background threads

From some previous research, I was sure that `AnimateWindow()` uses additional
threads for painting. However, now I have tested `DrawThemeBackground` and
`DrawThemeBackgroundEx` specifically and indeed, I didn't get any hits from
other threads.

> For proper evaluation of both the approaches, we will need more information on
> the experiment with 3rd party library you mentioned above.

I would also like to point out that it would probably take quite some time before
the new approach is ready for publishing (if it proves to be reasonable). At the
same time, my patches are ready to use today, and will improve user experience
for Eclipse's dark theme right now, especially the dark scrollbar part.

We will continue to use my patches in our product for now. While we'd like to
have less patches on our fork, that's not the top priority. My patches work well
for us and I have many other things to work on, so without some promising movement,
these dark theme patches are going to just stay as is:
Bug 444560 - scrollbars
Bug 560316 - this patch
Bug 560284 - table header
Bug 560358 - menu
Comment 12 Alexandr Miloslavskiy CLA 2020-03-04 05:56:45 EST
To summarize, I'm not ready to delve in to a large-scale project that will only give very little benefit to us, because my patches already do the job. If someone else is willing to do the work, I'm of course happy to get the results for free.

My intuition says that the approach is promising, but will have many problems to overcome before it really works.
Comment 13 Alexandr Miloslavskiy CLA 2020-05-13 15:26:27 EDT
Created attachment 282824 [details]
Eclipse (old)
Comment 14 Alexandr Miloslavskiy CLA 2020-05-13 15:28:16 EDT
Created attachment 282825 [details]
Eclipse (with patch)

Unfortunately for Eclipse, its dark theme is somewhat inconsistent and it mixes dark parts with grey parts. The patch's effect probably makes it better for dark part and worse for grey part. See screenshots and find (1)(2)(3)(4) on it for comparison.
Comment 15 Alexandr Miloslavskiy CLA 2020-05-13 15:32:27 EDT
In our product, dark theme is actually dark, and the patch gives good effect.
Comment 16 Lars Vogel CLA 2020-05-14 10:46:13 EDT
(In reply to Alexandr Miloslavskiy from comment #14)
> Created attachment 282825 [details]
> Eclipse (with patch)
> 
> Unfortunately for Eclipse, its dark theme is somewhat inconsistent and it
> mixes dark parts with grey parts. The patch's effect probably makes it
> better for dark part and worse for grey part. See screenshots and find
> (1)(2)(3)(4) on it for comparison.

I think it looks better. Can you upload Gerrit for Platform UI to enable it? Looks like you managed to set it for your testing. :-) Thank you for using the Eclipse IDE as test case.
Comment 17 Alexandr Miloslavskiy CLA 2020-05-14 11:01:50 EDT
It wasn't too easy to figure how to run published Eclipse with my own SWT, but I managed to do that yesterday. So yes, now I can test Eclipse with SWT patches.
Comment 18 Alexandr Miloslavskiy CLA 2020-05-14 11:02:39 EDT
I'm updating patches one by one, so unless something goes wrong, we'll have this one merged until tomorrow.
Comment 19 Lars Vogel CLA 2020-05-14 11:06:06 EDT
(In reply to Alexandr Miloslavskiy from comment #17)
> It wasn't too easy to figure how to run published Eclipse with my own SWT,
> but I managed to do that yesterday. So yes, now I can test Eclipse with SWT
> patches.

I think if you import SWT plug-ins into your Eclipse workspace you can select Run As-> Eclipse application on one of them and it will start a runtime Eclipse with your patches.
Comment 20 Alexandr Miloslavskiy CLA 2020-05-14 11:12:01 EDT
I'm using IDEA for development.

This is the way I found to inject my SWT into Eclipse:
1) Add 'C:\Eclipse\plugins' to classpath
2) Set 'org.eclipse.equinox.launcher.Main' as main class
3) Add this to arguments ('C:\SWT\.classes' is SWT compiled from sources)
   -dev C:\SWT\.classes
Comment 21 Lars Vogel CLA 2020-05-14 11:20:48 EDT
(In reply to Alexandr Miloslavskiy from comment #20)
> I'm using IDEA for development.
> 
> This is the way I found to inject my SWT into Eclipse:
> 1) Add 'C:\Eclipse\plugins' to classpath
> 2) Set 'org.eclipse.equinox.launcher.Main' as main class
> 3) Add this to arguments ('C:\SWT\.classes' is SWT compiled from sources)
>    -dev C:\SWT\.classes

Sounds harder than doing it via Eclipse. In Eclipse:

1.) Import projects into workspace
2.) Select Run as-> Eclipse application
Comment 22 Alexandr Miloslavskiy CLA 2020-05-14 11:27:47 EDT
This is probably not enough to convince me to learn and switch to a new IDE :)
Comment 23 Niraj Modi CLA 2020-05-15 10:15:17 EDT
Changes are merged in master, resolving now. Thanks!
Comment 24 Niraj Modi CLA 2020-05-20 07:09:47 EDT
Verified using Build id: I20200518-2220 on Win10.
Comment 25 Niraj Modi CLA 2020-05-20 07:11:46 EDT
Details on how to disable this feature:
On Windows 10 all the dark theme tweaks, including the text border tweak can be disabled using the org.eclipse.swt.internal.win32.disableCustomThemeTweaks Java property. For Example. add this VM argument in eclipse.ini or on the command line after -vmargs:
-Dorg.eclipse.swt.internal.win32.disableCustomThemeTweaks=true