Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color-Provider to theme custom controls #130

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

tmssngr
Copy link

@tmssngr tmssngr commented Mar 2, 2025

Please start ColorProviderTest to test.

Note, that the color provider, the color constants and the renderer are closely linked. In the future there will be exchangeable renderers which then need an own color provider (e.g. because a different renderer needs more/less colors).

Feedback is welcome.

@tmssngr tmssngr force-pushed the feature/color-provider branch from 2c92510 to d65adae Compare March 2, 2025 10:17
Copy link

github-actions bot commented Mar 2, 2025

Test Results

   341 files  ±0     341 suites  ±0   2m 25s ⏱️ +2s
 3 955 tests ±0   3 671 ✅ ±0  284 💤 ±0  0 ❌ ±0 
11 693 runs  ±0  10 808 ✅ ±0  885 💤 ±0  0 ❌ ±0 

Results for commit b11d41c. ± Comparison against base commit 4f08da9.

♻️ This comment has been updated with latest results.

@tmssngr tmssngr force-pushed the feature/color-provider branch 11 times, most recently from dc0eaee to 73a24dc Compare March 4, 2025 15:04
@HeikoKlare HeikoKlare force-pushed the master branch 4 times, most recently from a9f24e5 to d2109d6 Compare March 4, 2025 16:50
@tmssngr tmssngr force-pushed the feature/color-provider branch 3 times, most recently from baf5c63 to 90ef5c8 Compare March 6, 2025 11:02
@tmssngr tmssngr marked this pull request as ready for review March 6, 2025 11:13
@tmssngr tmssngr force-pushed the feature/color-provider branch from 90ef5c8 to 4935aba Compare March 6, 2025 13:06
Copy link

@HeikoKlare HeikoKlare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, this looks good to me. I only have two comments/thoughts on this.

@tmssngr tmssngr force-pushed the feature/color-provider branch from 4935aba to 55c5182 Compare March 6, 2025 18:08
@tmssngr tmssngr marked this pull request as draft March 7, 2025 11:59
@tmssngr tmssngr force-pushed the feature/color-provider branch 5 times, most recently from c49e0c0 to 232f25a Compare March 7, 2025 14:27
@tmssngr tmssngr marked this pull request as ready for review March 7, 2025 14:32
@tmssngr
Copy link
Author

tmssngr commented Mar 7, 2025

Now also includes (parts of) ToolBar.

@tmssngr tmssngr marked this pull request as draft March 7, 2025 15:14
@tmssngr tmssngr force-pushed the feature/color-provider branch 8 times, most recently from bbe6a94 to 07d02a1 Compare March 14, 2025 14:47
@tmssngr tmssngr marked this pull request as ready for review March 14, 2025 14:54
@tmssngr tmssngr requested review from DieterMai and HeikoKlare March 14, 2025 14:54
Copy link

@DieterMai DieterMai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some comment in the code.

When i tested this, i noticed that the toolBar background is a lot darker then on the master branch. Don't know why.

I general is the following pros of this approach:

  • All colors defined in a central place
  • All colors can be easily switch out

The cons:

  • It is not very friendly for implementing custom widget or a custom renderer. A custom widget would have to either have to define its own colors (like before) or use the color provider with a color-key that is unrelated to their situation.
  • If a new key is added, a custom color provider would not know about it and would not return a proper color.


@Override
public Color getBackground() {
return background != null ? background : getColorProvider().getColor(ToolBarRenderer.COLOR_BACKGROUND);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ToolBar should not have a dependency to a specific renderer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the generic ToolBarRenderer.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not this is the actual Implementation. ToolBarRenderer is not abstract and no other renderer extends from it.

@tmssngr tmssngr force-pushed the feature/color-provider branch from 07d02a1 to 6e688ba Compare March 15, 2025 07:18
@tmssngr
Copy link
Author

tmssngr commented Mar 15, 2025

Thanks for reviewing.

When i tested this, i noticed that the toolBar background is a lot darker then on the master branch. Don't know why.

I'd say, because the background of composites comes from Windows while the background from controls like Label, Link and ToolBar comes from the specified colors. Maybe it would make sense to only draw the background for such controls if it has been set to a certain color (in contrast to the transparent default).

The cons:

* It is not very friendly for implementing custom widget or a custom renderer. A custom widget would have to either have to define its own colors (like before) or use the color provider with a color-key that is unrelated to their situation.

* If a new key is added, a custom color provider would not know about it and would not return a proper color.

What do you suggest? I also had the idea of directly creating getters for certain colors instead of using a map (which is quite fast, but field access is faster).

Independent of your review, I have the idea of renaming the ColorProvider to something like RendererConfig and also add ways to configure int values (e.g. sizes) and boolean (e.g. to switch some feature on or off, e.g. for configuring what should happen when clicking above/below the thumb of a scrollbar - page-wise scrolling or jump to that location).

Instead of color constants, the custom controls ask the color provider
to give them a color by name. This allows easier theming.
@tmssngr tmssngr force-pushed the feature/color-provider branch from 6e688ba to b11d41c Compare March 15, 2025 07:29
@tmssngr tmssngr requested a review from fedejeanne March 15, 2025 08:18
@DieterMai
Copy link

When i tested this, i noticed that the toolBar background is a lot darker then on the master branch. Don't know why.

I'd say, because the background of composites comes from Windows while the background from controls like Label, Link and ToolBar comes from the specified colors. Maybe it would make sense to only draw the background for such controls if it has been set to a certain color (in contrast to the transparent default).

But why is it different between master and PR? adding the ColorProvider should not change the rendering (in Light mode).

What do you suggest? I also had the idea of directly creating getters for certain colors instead of using a map (which is quite fast, but field access is faster).

I think we are in an early state of development so that we are able to try different things. But how i see it this should not be the final solution for color.
Other cons i see:

  • Most color keys are defined as package private. This means a custom color provider would have no access to them. How is a custom color provider supposed to provide the colors?
  • Some color keys are public. This means they are now API and can never be changed.
  • Since the color keys are declared in the Default renderers, the Default color provider has a dependency to those renderers. This causes issues for other renderers of the same widget [1]. As an example:
    DefaultScaleRenderer declares the key COLOR_NOTCH. This is used for the notches on the scale. But this notches are a Windows specific thing. There are no notches on Linux. If i would have implemented the rendering of an Linux scale, the COLOR_NOTCH key would be missing.

I think the main problem is that the DefaultColorProvider has dependencies to every renderer. Maybe this could be fixed by moving the color keys into the DefaultColorProvider or maybe separate class.

I'm also wondering why you decided to go against the earlier suggested material design concept of a color scheme [2]

[1] I know the primary goal of I31 is to have less implementations, but a secondary goal i would say is to switch to different renderings

[2] https://developer.android.com/develop/ui/compose/designsystems/material3

@tmssngr
Copy link
Author

tmssngr commented Mar 16, 2025

But why is it different between master and PR? adding the ColorProvider should not change the rendering (in Light mode).

So in other words you suggest to use the Windows 11 default background color as default background for the custom drawn widgets?

Most color keys are defined as package private. This means a custom color provider would have no access to them. How is a custom color provider supposed to provide the colors?

They should rather be public. Maybe for some classes without clear renderer hierarchies this is not correctly implemented now.

Some color keys are public. This means they are now API and can never be changed.

What do you suggest as alternative?

Since the color keys are declared in the Default renderers, the Default color provider has a dependency to those renderers. This causes issues for other renderers of the same widget [1].

Which IMHO is correct, because only the specific color provider knows which colors are required. If you want to use a different renderer (which uses completely different colors), you will need to create an own color provider, too.

But this notches are a Windows specific thing. There are no notches on Linux.

Then there needs to be a different LinuxScaleRenderer and a different LinuxRendererFactory and a different LinuxColorFactory (or LinuxRendererConfig as I suggested).

If you have a better suggestion how to implement it, you are welcome to provide a code suggestion.

I'm also wondering why you decided to go against the earlier suggested material design concept of a color scheme [2]

I think, I've didn't. The light and dark default implementations derive from a base color. If you mean something else, again, please feel free to provide a different implementation.

@tmssngr
Copy link
Author

tmssngr commented Mar 16, 2025

I'm waiting for #171 to be accepted before this one needs to be modified to fix some of @DieterMai's feedback.

@DieterMai
Copy link

But why is it different between master and PR? adding the ColorProvider should not change the rendering (in Light mode).

So in other words you suggest to use the Windows 11 default background color as default background for the custom drawn widgets?

No, i am confused why the ToolBar has a different background then before.

Some color keys are public. This means they are now API and can never be changed.

What do you suggest as alternative?

Have more generic color keys.

Since the color keys are declared in the Default renderers, the Default color provider has a dependency to those renderers. This causes issues for other renderers of the same widget [1].

Which IMHO is correct, because only the specific color provider knows which colors are required. If you want to use a different renderer (which uses completely different colors), you will need to create an own color provider, too.

This is not how i imagen it to be used.
Someone who writes a custom color provider should not have to know which renderers are used.
Someone who writes a custom renderer should not have to know which color provider is used.

If you have a better suggestion how to implement it, you are welcome to provide a code suggestion.

I'm hesitant to do so. ATM I'm focused on reaching feature parity with the native implementation.

I'm also wondering why you decided to go against the earlier suggested material design concept of a color scheme [2]

I think, I've didn't. The light and dark default implementations derive from a base color. If you mean something else, again, please feel free to provide a different implementation.

I would have imaged something like colorProvider.get(ColorRole.PRIMARY).

@tmssngr
Copy link
Author

tmssngr commented Mar 16, 2025

But why is it different between master and PR? adding the ColorProvider should not change the rendering (in Light mode).

So in other words you suggest to use the Windows 11 default background color as default background for the custom drawn widgets?

No, i am confused why the ToolBar has a different background then before.

IMHO it did not draw any background at all, but used the Windows default. This will not work for a themed application, e.g. with a forced dark theme on a light windows (or visa versa).

But it might be useful to also be able to get nullable colors (with the meaning of don't draw a background, for example).

Some color keys are public. This means they are now API and can never be changed.

What do you suggest as alternative?

Have more generic color keys.

For example?

Since the color keys are declared in the Default renderers, the Default color provider has a dependency to those renderers. This causes issues for other renderers of the same widget [1].

Which IMHO is correct, because only the specific color provider knows which colors are required. If you want to use a different renderer (which uses completely different colors), you will need to create an own color provider, too.

This is not how i imagen it to be used. Someone who writes a custom color provider should not have to know which renderers are used. Someone who writes a custom renderer should not have to know which color provider is used.

IMHO different renderers may need different colors. Example: one (Push)ButtonRenderer mit use 5 colors (background, background-disabled, foreground, foreground-disabled, focus/default-highlight) while another one could use much more colors because it also draws a border with different colors. Maybe each Renderer needs a special belonging Config containing colors?

I think, I've didn't. The light and dark default implementations derive from a base color. If you mean something else, again, please feel free to provide a different implementation.

I would have imaged something like colorProvider.get(ColorRole.PRIMARY).

Yes, me too, but I don't know how I should implement that, e.g. for a button. Hence I've asked for an implementation example. IMHO, the current approach is more generic - it allows any arbitrary number of colors. A MaterialColorProvider could set all these colors (from my PR) from a its material colors.

@@ -169,7 +169,7 @@ private static int checkStyle(int style) {
}
int mask = SWT.SHADOW_IN | SWT.SHADOW_OUT | SWT.SHADOW_NONE | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
style = style & mask;
style |= SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why get rid of SWT.DOUBLE_BUFFERED? How is that related to the PR?

@Override
public void setBackground(Color color) {
super.setBackground(color);
renderer.setBackground(color);
redraw();
}

@Override
public Color getForeground() {
return foreground != null ? foreground : getColorProvider().getColor(LabelRenderer.COLOR_FOREGROUND);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the renderer hold this information, like it holds the background color?

@@ -5250,4 +5250,7 @@ static double luma (double[] rgbColor) {
return 0.2126f * rgbColor[0] + 0.7152f * rgbColor[1] + 0.0722f * rgbColor[2];
}

protected ColorProvider getColorProvider() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would make sense to make it final so no one gets the idea of overriding it and start instantiating/holding color providers someplace else. WDYT?


private final Map<String, Color> map = new HashMap<>();

public DefaultColorProvider() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public DefaultColorProvider() {
private DefaultColorProvider() {
// prevent instantiation from outside

@@ -6875,4 +6875,7 @@ Point getSurfaceOrigin () {
return success ? new Point((int)originX[0], (int)originY[0]) : new Point(0, 0);
}

protected ColorProvider getColorProvider() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected ColorProvider getColorProvider() {
protected final ColorProvider getColorProvider() {

* Sets the color provider used for custom-drawn controls.
* @param colorProvider a non-null color provider
*/
public void setColorProvider(ColorProvider colorProvider) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void setColorProvider(ColorProvider colorProvider) {
public final void setColorProvider(ColorProvider colorProvider) {

* Returns the color provider used for custom-drawn controls.
* @return a non-null instance of the color provider
*/
public ColorProvider getColorProvider() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public ColorProvider getColorProvider() {
public final ColorProvider getColorProvider() {

* Sets the color provider used for custom-drawn controls.
* @param colorProvider a non-null color provider
*/
public void setColorProvider(ColorProvider colorProvider) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void setColorProvider(ColorProvider colorProvider) {
public final void setColorProvider(ColorProvider colorProvider) {

import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public class ColorProviderTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: since this isn't an automatic test class and since we have this kind of showcases already (ControlExample / CustomControlExample), how about calling it:

Suggested change
public class ColorProviderTest {
public class ColorProviderTExample {

?

Also, would it be possible to add the ´darkChkBx´ to the ControlExample and CustomControlExample? Those 2 classes already showcase all of the controls so it should keep up-to-date while we keep implementing the widgets

Comment on lines +30 to +31
protected Color background;
protected Color foreground;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume in the future (once all controls have their corresponding renderers) these 2 should also go into the corresponding renderer, right? Otherwise the information about coloring is scattered between the controls and their renderers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants