diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTApplyStylesListener.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTApplyStylesListener.java index 29a5a7ed714..7d13391ed59 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTApplyStylesListener.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTApplyStylesListener.java @@ -15,22 +15,58 @@ import org.eclipse.e4.ui.css.core.engine.CSSEngine; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; /** - * Add SWT filter to the {@link Display} to apply styles when SWT widget is - * resized or showed. + * Applies styles to the widgets of a {@link Display} when they are skinned, and + * to a {@link CTabFolder} page skipped while hidden once it is the selected + * tab's page. */ public class CSSSWTApplyStylesListener { CSSEngine engine; public CSSSWTApplyStylesListener(Display display, final CSSEngine engine) { this.engine = engine; display.addListener(SWT.Skin, event -> { - if (engine != null) { - engine.applyStyles(event.widget, false); + if (engine == null) { + return; + } + engine.applyStyles(event.widget, false); + if (event.widget instanceof Control control && control.getParent() instanceof CTabFolder folder + && !isPageOfSelectedTab(folder, control)) { + // the folder exposes only the selected page, so the engine skipped this one + styleWhenItBecomesThePage(folder, control); } }); } + /** + * Styles the control once it is the selected tab's page, which attaching it + * to its item (a resize) or selecting its tab (a show) makes it. + */ + private void styleWhenItBecomesThePage(CTabFolder folder, Control control) { + Listener listener = new Listener() { + @Override + public void handleEvent(Event event) { + if (!isPageOfSelectedTab(folder, control)) { + return; + } + control.removeListener(SWT.Show, this); + control.removeListener(SWT.Resize, this); + engine.applyStyles(control, true); + } + }; + control.addListener(SWT.Show, listener); + control.addListener(SWT.Resize, listener); + } + + private static boolean isPageOfSelectedTab(CTabFolder folder, Control control) { + int selected = folder.getSelectionIndex(); + return selected >= 0 && folder.getItem(selected).getControl() == control; + } + } diff --git a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java index 8c5b08ad131..9ba9cd05bfe 100644 --- a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java @@ -21,9 +21,14 @@ import static org.eclipse.e4.ui.tests.css.swt.CssSwtEngine.WHITE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.io.StringReader; import org.eclipse.e4.ui.css.core.engine.CSSEngine; import org.eclipse.e4.ui.css.swt.dom.WidgetElement; +import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; @@ -190,17 +195,6 @@ void testBorderVisible() { assertEquals(false, folderToTest.getBorderVisible()); assertEquals("false", css.getEngine().retrieveCSSProperty(folderToTest, "border-visible", null)); } - @Test - void testSimple() { - CTabFolder folderToTest = createTestCTabFolder("CTabFolder { swt-simple: true}"); - assertEquals(true, folderToTest.getSimple()); - assertEquals("true", css.getEngine().retrieveCSSProperty(folderToTest, "swt-simple", null)); - folderToTest.getShell().close(); - folderToTest = createTestCTabFolder("CTabFolder { swt-simple: false}"); - // Curved tabs are no longer supported, so getSimple() always returns true - assertEquals(true, folderToTest.getSimple()); - assertEquals("true", css.getEngine().retrieveCSSProperty(folderToTest, "swt-simple", null)); - } @Test void testMaximizeVisible() { @@ -356,4 +350,64 @@ void testMinimumCharacters() { assertEquals(1, folderToTest.getMinimumCharacters()); assertEquals("1", css.getEngine().retrieveCSSProperty(folderToTest, "swt-tab-text-minimum-characters", null)); } + + @Test + void testPageSelectedProgrammaticallyAfterSkinningIsStyled() throws IOException { + Display display = css.getDisplay(); + CSSEngine engine = new CSSSWTEngineImpl(display, true); + engine.setErrorHandler(e -> fail(e.getMessage())); + // the class selector keeps this engine's skin listener off other tests' widgets + engine.parseStyleSheet(new StringReader(".tabPage { background-color: #FF0000 }")); + + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new FillLayout()); + CTabFolder folderToTest = new CTabFolder(shell, SWT.NONE); + CTabItem tab1 = new CTabItem(folderToTest, SWT.NONE); + tab1.setText("A TAB ITEM"); + // no selection yet, so the folder hides the page from the engine + Composite page = new Composite(folderToTest, SWT.NONE); + WidgetElement.setCSSClass(page, "tabPage"); + tab1.setControl(page); + spinEventLoop(display); // the skin pass skips the hidden page + + folderToTest.setSelection(0); // programmatic, so no selection event + + spinEventLoop(display); + + assertEquals(RED, page.getBackground().getRGB()); + } + + @Test + void testPageAttachedToItsItemAfterSkinningIsStyled() throws IOException { + Display display = css.getDisplay(); + CSSEngine engine = new CSSSWTEngineImpl(display, true); + engine.setErrorHandler(e -> fail(e.getMessage())); + engine.parseStyleSheet(new StringReader(".tabPage { background-color: #FF0000 }")); + + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new FillLayout()); + CTabFolder folderToTest = new CTabFolder(shell, SWT.NONE); + CTabItem tab1 = new CTabItem(folderToTest, SWT.NONE); + tab1.setText("A TAB ITEM"); + folderToTest.setSelection(0); + shell.setSize(400, 300); + shell.layout(true, true); + + Composite page = new Composite(folderToTest, SWT.NONE); + WidgetElement.setCSSClass(page, "tabPage"); + // skinned before it is attached, as a Search dialog page is + spinEventLoop(display); + + tab1.setControl(page); // already visible, so no show event either + + spinEventLoop(display); + + assertEquals(RED, page.getBackground().getRGB()); + } + + private static void spinEventLoop(Display display) { + while (display.readAndDispatch()) { + // deliver pending skin and show events + } + } }