Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*******************************************************************************
* Copyright (c) 2026 Lars Vogel and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Lars Vogel <Lars.Vogel@vogella.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.e4.ui.tests.css.swt;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

import org.eclipse.e4.ui.css.swt.dom.WidgetElement;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Shell;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

/**
* Locks in how {@code CTabFolder.active { ... }} rules are matched based on
* the CSS class set via {@link WidgetElement#setCSSClass(org.eclipse.swt.widgets.Widget, String)}.
*/
public class CTabFolderActiveClassTest extends CSSSWTTestCase {

private Shell shell;

@Override
@AfterEach
public void tearDown() {
if (shell != null && !shell.isDisposed()) {
shell.dispose();
shell = null;
}
super.tearDown();
}

private CTabFolder createFolder() {
shell = new Shell(display, SWT.SHELL_TRIM);
shell.setLayout(new FillLayout());

CTabFolder folder = new CTabFolder(shell, SWT.NONE);
CTabItem item = new CTabItem(folder, SWT.NONE);
item.setText("Item 0");
folder.setSelection(0);
return folder;
}

@Test
void testActiveClassAppliesStyle() {
CTabFolder folder = createFolder();
WidgetElement.setCSSClass(folder, "active");

engine = createEngine("CTabFolder.active { background-color: #FF0000 }", display);
engine.applyStyles(shell, true);

assertEquals(RED, folder.getBackground().getRGB());
}

@Test
void testWithoutActiveClassRuleDoesNotApply() {
CTabFolder folder = createFolder();
// no setCSSClass call

engine = createEngine("CTabFolder.active { background-color: #FF0000 }", display);
engine.applyStyles(shell, true);

assertNotEquals(RED, folder.getBackground().getRGB());
}

@Test
void testClearingActiveClassDoesNotRevertBackground() {
CTabFolder folder = createFolder();
WidgetElement.setCSSClass(folder, "active");

engine = createEngine("CTabFolder.active { background-color: #FF0000 }", display);
engine.applyStyles(shell, true);
assertEquals(RED, folder.getBackground().getRGB());

// Surprise: clearing the CSS class and reapplying does NOT undo the
// previously painted background. Once CTabFolder#setBackground has
// been called by the handler, the folder retains that color even
// though the .active rule no longer matches. Pin this real engine
// behaviour so a future change that adds proper revert semantics
// shows up as a test failure.
WidgetElement.setCSSClass(folder, null);
engine.applyStyles(shell, true);

assertEquals(RED, folder.getBackground().getRGB());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*******************************************************************************
* Copyright (c) 2026 Lars Vogel and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Lars Vogel <Lars.Vogel@vogella.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.e4.ui.tests.css.swt;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

/**
* Pins {@code CTabItem:selected} pseudo behaviour and the
* {@code CTabFolderElement} selection listener path.
*/
public class CTabItemSelectionTest extends CSSSWTTestCase {

private Shell shell;

@Override
@AfterEach
public void tearDown() {
if (shell != null && !shell.isDisposed()) {
shell.dispose();
shell = null;
}
super.tearDown();
}

private void spinEventLoop() {
// Drain queued SWT events. Same pattern as CTabItemTest.
for (int i = 0; i < 3; i++) {
while (display.readAndDispatch()) {
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
}

private CTabFolder createFolderWithTwoItems(String styleSheet) {
shell = new Shell(display, SWT.SHELL_TRIM);
shell.setLayout(new FillLayout());

CTabFolder folder = new CTabFolder(shell, SWT.NONE);
CTabItem item0 = new CTabItem(folder, SWT.NONE);
item0.setText("Item 0");
CTabItem item1 = new CTabItem(folder, SWT.NONE);
item1.setText("Item 1");
folder.setSelection(0);

engine = createEngine(styleSheet, display);
engine.applyStyles(shell, true);
shell.open();
return folder;
}

@Test
void testSelectedTabReceivesStyledSelectionForeground() {
// :selected for CTabItem maps to CTabFolder#getSelectionForeground(),
// not the per-item CTabItem#getForeground(). The matched declaration
// is intentionally observable on the parent folder.
CTabFolder folder = createFolderWithTwoItems("CTabItem:selected { color: #FF0000 }");
spinEventLoop();

assertEquals(RED, folder.getSelectionForeground().getRGB());
}

@Test
void testSelectionListenerReappliesStylesOnSelectionEvent() {
CTabFolder folder = createFolderWithTwoItems(
"CTabItem { color: #0000FF }\n" + "CTabItem:selected { color: #FF0000 }");
spinEventLoop();
assertEquals(RED, folder.getSelectionForeground().getRGB());

// Switch the selection. CTabFolder#setSelection on its own does not
// fire the SWT selection listener, so we explicitly dispatch the
// SWT.Selection event the way an end-user click would. This
// exercises CTabFolderElement#selectionListener which invokes
// applyStyles(folder, true) for us.
CTabItem item1 = folder.getItem(1);
folder.setSelection(item1);
Event event = new Event();
event.widget = folder;
event.item = item1;
folder.notifyListeners(SWT.Selection, event);
spinEventLoop();

// The folder's selection-foreground stays red because the rule still
// matches whichever item is currently selected.
assertEquals(RED, folder.getSelectionForeground().getRGB());
// Non-selected items inherit the plain CTabItem rule.
assertEquals(new RGB(0, 0, 255), folder.getForeground().getRGB());
}

@Test
void testNonSelectedRuleColorIsNotRed() {
CTabFolder folder = createFolderWithTwoItems(
"CTabItem { color: #0000FF }\n" + "CTabItem:selected { color: #FF0000 }");
spinEventLoop();

// The non-selected foreground (plain CTabItem rule) is blue, not red.
assertNotEquals(RED, folder.getForeground().getRGB());
assertEquals(new RGB(0, 0, 255), folder.getForeground().getRGB());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright (c) 2026 Lars Vogel and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Lars Vogel <Lars.Vogel@vogella.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.e4.ui.tests.css.swt;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.eclipse.core.internal.preferences.EclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.junit.jupiter.api.Test;

/**
* Locks in the {@code IEclipsePreferences#node:pseudo} selector form used by
* shipped themes. See Bug 466075.
*/
public class IEclipsePreferencesPseudoKeyTest extends CSSSWTTestCase {

@Test
void testPseudoSelectorMatchesAndWritesPreferenceValue() {
IEclipsePreferences preferences = new EclipsePreferences(null, "org.eclipse.jdt.ui") {};

engine = createEngine(
"""
IEclipsePreferences#org-eclipse-jdt-ui:org-eclipse-ui-themes {\
preferences: 'semanticHighlighting.abstractClass.color=128,255,0'\
}""",
display);
engine.applyStyles(preferences, false);

// Bug 466075
assertEquals("128,255,0", preferences.get("semanticHighlighting.abstractClass.color", null));
}

@Test
void testDifferentPseudosOnSameNodeAllContribute() {
// Mirrors how org.eclipse.ui.editors and org.eclipse.ui.themes both
// contribute to the same preference node via different pseudo tags.
IEclipsePreferences preferences = new EclipsePreferences(null, "org.eclipse.ui.workbench") {};

engine = createEngine(
"""
IEclipsePreferences#org-eclipse-ui-workbench:org-eclipse-ui-editors {\
preferences: 'org.eclipse.ui.editors.inlineAnnotationColor=155,155,155'\
}\
IEclipsePreferences#org-eclipse-ui-workbench:org-eclipse-ui-themes {\
preferences: 'ERROR_COLOR=247,68,117'\
}""",
display);
engine.applyStyles(preferences, false);

assertEquals("155,155,155", preferences.get("org.eclipse.ui.editors.inlineAnnotationColor", null));
assertEquals("247,68,117", preferences.get("ERROR_COLOR", null));
}

@Test
void testCascadeOverrideForSameKeyAndSamePseudoLastWins() {
// Two equal-specificity rules under the same pseudo write the same
// preference key with different values. The CSS cascade in viewCSS
// resolves the duplicate "preferences" property to the second rule's
// value before EclipsePreferencesHandler ever sees it, so the LATER
// rule wins. This matches standard CSS source-order tiebreak.
IEclipsePreferences preferences = new EclipsePreferences(null, "org.eclipse.jdt.ui") {};

engine = createEngine(
"""
IEclipsePreferences#org-eclipse-jdt-ui:org-eclipse-ui-themes {\
preferences: 'semanticHighlighting.abstractClass.color=128,255,0'\
}\
IEclipsePreferences#org-eclipse-jdt-ui:org-eclipse-ui-themes {\
preferences: 'semanticHighlighting.abstractClass.color=255,0,0'\
}""",
display);
engine.applyStyles(preferences, false);

assertEquals("255,0,0", preferences.get("semanticHighlighting.abstractClass.color", null));
}
}
Loading