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

Initial custom Group widget implementation #120

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

Conversation

lathapatil
Copy link

@lathapatil lathapatil commented Feb 27, 2025

Known Issues :

  1. A filled rectangle is drawn to erase the part of the Group where the title is present. This rectangle is slightly visible in the control example when the background color is white, but it is programmatically treated as grey.
  2. Text Rendering: Text orientation works as expected, but text direction does not behave correctly.
  3. HiDPI Scaling: Unable to test controlExample when scaled to 175% because the control example does not fit within the screen.
  4. PrintWidget not yet worked on.
  5. SWT.Border is not working as expected.

Any suggestions are welcome.

Copy link

github-actions bot commented Feb 27, 2025

Test Results

  136 files   -   202    136 suites   - 202   1m 11s ⏱️ - 1m 18s
3 935 tests  -    19  3 611 ✅  -    59  324 💤 + 40  0 ❌ ±0 
3 973 runs   - 7 717  3 649 ✅  - 7 156  324 💤  - 561  0 ❌ ±0 

Results for commit 6b16d26. ± Comparison against base commit fb29be9.

This pull request removes 19 tests.
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicASCII_dollarSign
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicASCII_emptyString
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicASCII_letterA
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicASCII_letters
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicUTF16LE_null
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicUTF16_AsciiLetters
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicUTF16_Asciiletter
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicUTF16_LotsOfLetters
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicUTF16_letter
org.eclipse.swt.tests.gtk.Test_GtkConverter ‑ test_HeuristicUTF16_letters
…
This pull request skips 41 tests.
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_custom_CCombo ‑ test_setForegroundAlphaLorg_eclipse_swt_graphics_Color
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_custom_CLabel ‑ test_setForegroundAlphaLorg_eclipse_swt_graphics_Color
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_custom_CTabFolder ‑ test_setForegroundAlphaLorg_eclipse_swt_graphics_Color
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_custom_StyledText ‑ test_setForegroundAlphaLorg_eclipse_swt_graphics_Color
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Button ‑ test_setForegroundAlphaCheckButton
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Button ‑ test_setForegroundAlphaLorg_eclipse_swt_graphics_Color
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Button ‑ test_setForegroundAlphaRadiokButton
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Canvas ‑ test_setForegroundAlphaLorg_eclipse_swt_graphics_Color
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Combo ‑ test_setForegroundAlphaDropDownCombo
org.eclipse.swt.tests.junit.Test_org_eclipse_swt_widgets_Combo ‑ test_setForegroundAlphaLorg_eclipse_swt_graphics_Color
…

♻️ This comment has been updated with latest results.

private String text = "";
static final int CLIENT_INSET = 3;
private Listener listener;
private boolean enabled = true;
Copy link

Choose a reason for hiding this comment

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

The enabled state should not be tracked here, but instead isEnabled() should be used, because it should be drawn disabled even if it is set enabled, but a parent is disabled.


@Override
void enableWidget(boolean enabled) {
this.enabled = enabled;
Copy link

Choose a reason for hiding this comment

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

Don't remember here.

@lathapatil
Copy link
Author

@DenisUngemach

I see a performance issue in this branch when running ControlExample, especially when the system's display is scaled up to 175%.

Additionally, the JMC performance tool highlights the following method as a concern, and I also noticed a heap space issue, as shown in the attached image. Is it known issue or should there be some issue in my code ?

Method Profiling

The most sampled method was void org.eclipse.swt.graphics.ImageData.blit(byte[], int, int, int, int, int, int, int, int, byte[], int, int, int, int, int, int, int, int, boolean, boolean), with 3.45 % of the maximum possible samples for 3/4/2025, 6:03:42.000 PM – 6:04:12 PM, and 64.8 % of the actual samples.

The methods that used the most CPU are:
org.eclipse.swt.graphics.ImageData.blit(byte[], int, int, int, int, int, int, int, int, byte[], int, int, int, int, int, int, int, int, boolean, boolean) (65.3 % of samples) 3/4/2025, 6:03:57.000 PM – 6:04:27 PM
•org.eclipse.swt.graphics.ImageData.blit(byte[], int, int, int, int, int, int, int, int, byte[], int, int, int, int, int, int, int, int, boolean, boolean) (65.6 % of samples) 3/4/2025, 6:07:57.000 PM – 6:08:27 PM
•org.eclipse.swt.graphics.ImageData.blit(byte[], int, int, int, int, int, int, int, int, byte[], int, int, int, int, int, int, int, int, boolean, boolean) (64.8 % of samples) 3/4/2025, 6:03:42.000 PM – 6:04:12 PM

The most common stack trace was: at
void org.eclipse.swt.graphics.ImageData.blit(byte[], int, int, int, int, int, int, int, int, byte[], int, int, int, int, int, int, int, int, boolean, boolean)at org.eclipse.swt.graphics.ImageData org.eclipse.swt.graphics.Image.directToDirect(org.eclipse.swt.graphics.ImageData, int, org.eclipse.swt.graphics.PaletteData, int)at long[] org.eclipse.swt.graphics.Image.init(org.eclipse.swt.graphics.Device, org.eclipse.swt.graphics.Image, org.eclipse.swt.graphics.ImageData, java.lang.Integer)at void org.eclipse.swt.graphics.Image.init(org.eclipse.swt.graphics.ImageData, java.lang.Integer)at void org.eclipse.swt.graphics.Image.<init>(org.eclipse.swt.graphics.Device, java.io.InputStream)at void org.eclipse.swt.graphics.SkijaGC.commit()at void org.eclipse.swt.graphics.GC.commit()at void org.eclipse.swt.graphics.Drawing.drawWithGC(org.eclipse.swt.widgets.Control, org.eclipse.swt.graphics.GC, java.util.function.Consumer)at void org.eclipse.swt.widgets.Group.onPaint(org.eclipse.swt.widgets.Event)at void org.eclipse.swt.widgets.Group.lambda$0(org.eclipse.swt.widgets.Event)at void org.eclipse.swt.widgets.Group$$Lambda$29+0x000002bd9308b348.212628335.handleEvent(org.eclipse.swt.widgets.Event)at void org.eclipse.swt.widgets.EventTable.sendEvent(org.eclipse.swt.widgets.Event)at void org.eclipse.swt.widgets.Display.sendEvent(org.eclipse.swt.widgets.EventTable, org.eclipse.swt.widgets.Event)at void org.eclipse.swt.widgets.Widget.sendEvent(org.eclipse.swt.widgets.Event)at void org.eclipse.swt.widgets.Widget.sendEvent(int, org.eclipse.swt.widgets.Event, boolean)at void org.eclipse.swt.widgets.Widget.sendEvent(int, org.eclipse.swt.widgets.Event)at org.eclipse.swt.internal.win32.LRESULT org.eclipse.swt.widgets.Composite.WM_PAINT(long, long)at long org.eclipse.swt.widgets.Control.windowProc(long, int, long, long)at long org.eclipse.swt.widgets.Display.windowProc(long, long, long, long)at long org.eclipse.swt.internal.win32.OS.DispatchMessage(org.eclipse.swt.internal.win32.MSG)at boolean org.eclipse.swt.widgets.Display.readAndDispatch()at void org.eclipse.swt.examples.controlexample.ControlExample.main(java.lang.String[])

image

@DenisUngemach
Copy link

Yes, it is not a suprise, that the SkijaGC.commit call requires a lot of performance.

We want to get rid of all these image conversion calls in the future either with OpenGL or the direct use of windows GDI.

See issue #107

@HeikoKlare HeikoKlare force-pushed the master branch 4 times, most recently from a9f24e5 to d2109d6 Compare March 4, 2025 16:50
@fedejeanne fedejeanne mentioned this pull request Mar 6, 2025
37 tasks
@lathapatil lathapatil self-assigned this Mar 10, 2025
@lathapatil lathapatil force-pushed the Group_widget branch 2 times, most recently from 160668a to da3d99d Compare March 11, 2025 09:49
Test Snippet
Review points fix
Review points fixed
@lathapatil lathapatil marked this pull request as ready for review March 11, 2025 09:54
@lathapatil lathapatil requested a review from tmssngr March 11, 2025 09:54
}

private void drawGroup(GC gc) {

Copy link

Choose a reason for hiding this comment

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

Please remove this empty line.

checkWidget();
Point size = DPIUtil.autoScaleUp(super.computeSize(wHint, hHint, changed));

if (!changed && new Point(wHint, hHint).equals(size)) {
Copy link

Choose a reason for hiding this comment

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

Instead of creating a new Point, why not just compare the x and y values?

return size;
}
Layout layout = getLayout();
Point layoutSize = (layout != null) ? layout.computeSize(this, wHint, hHint, changed)
Copy link

Choose a reason for hiding this comment

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

Hm, the above super.computeSize() already invokes the layout manager. Why here again?

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for this finding . I was able to fix other scaling issue as well while fixing this

}

@Override
void enableWidget(boolean enabled) {
Copy link

Choose a reason for hiding this comment

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

Is that method necessary?

Copy link
Author

Choose a reason for hiding this comment

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

It may not be necessary since I do not have any additional implementation compared to the parent (Control), and it is called solely by the parent (Control) in the setEnabled method in SWT. I retained this method as it was in the original Group widget implementation. I will remove it.

checkWidget();
Rectangle rect = super.getClientArea();
if (rect.isEmpty())
return rect;
Copy link

Choose a reason for hiding this comment

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

Please surround the return statement with {}.

boolean mnemonicMatch(char key) {
char mnemonic = findMnemonic(getText());
if (mnemonic == '\0')
return false;
Copy link

Choose a reason for hiding this comment

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

Same here

private void drawGroup(GC gc) {

int width = getBounds().width;
int height = getBounds().height;
Copy link

Choose a reason for hiding this comment

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

Don't invoke getBounds() two times, but rather cache the result.

int width = getBounds().width;
int height = getBounds().height;
int titleWidth = textExtent.x;
int titleHeight = textExtent.y;
Copy link

Choose a reason for hiding this comment

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

Are you sure, the textExtent is alway set correctly here?

Copy link
Author

Choose a reason for hiding this comment

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

textExtent is calculated whenever text/group title is changed using setText(String text) . Do you find any loophole here ?

Choose a reason for hiding this comment

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

If the font or the font size changes.

But basically it is right to cache these information and use them directly in the drawing, instead of doing all these calculations in the drawing.

In the end the efficiency of the drawing method defines the speed of the widget toolkit, because the drawings of controls cluster in the redraw.

It is more the question, how can we make sure these calculations are always up to date without recalculate them all the time (general question... :-) ).

Copy link

Choose a reason for hiding this comment

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

Caching is fine, but if a method is using the cached values, you need to ensure that they are valid at this point. BTW, better indicate cached values in their variable names and before using them, check whether these values are valid.

int textX = inset + groupInset;
int textY = titleHeight;
int newWidth = width - (inset * 2);
int newHeight = height - (inset * 5);
Copy link

Choose a reason for hiding this comment

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

Please use better variable name, e.g. borderWidth/Height.

@tmssngr
Copy link

tmssngr commented Mar 11, 2025

The tests fail and the border is truncated at the bottom for your example:
2025-03-11 14_31_44-SWT Group Widget

@DenisUngemach
Copy link

Yes, it is not a suprise, that the SkijaGC.commit call requires a lot of performance.

We want to get rid of all these image conversion calls in the future either with OpenGL or the direct use of windows GDI.

See issue #107

The more i think about this:

-> first drawing the group widget with SkijaGC + commit
-> then drawing the child widgets with SkijaGC + commit

and this happens at each redraw() and these "SkijaGC + commit" of parent and child are separated calls.

Of course the bigger our widgets become, the more lag the SkijaGC + commit causes.

Generally:
We have to think about an efficient concepts for redrawing.

@lathapatil
Copy link
Author

Of course the bigger our widgets become, the more lag the SkijaGC + commit causes.

I had unnecessarily scaled up some dimensions in the implementation. After fixing it, I was able to launch controlExample, and the performance improved.
However, the issue mentioned above is a matter of concern and needs to be fixed. Perhaps the approach to fixing this can be broken down into multiple issues so that different people can take them up for resolution.

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.

3 participants