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
Expand Up @@ -15,22 +15,69 @@

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 the engine skipped once it becomes the selected tab's page.
*/
public class CSSSWTApplyStylesListener {
// marks a control this listener already watches, since every re-skin repeats the skin event
private final String pendingPageKey = getClass().getName() + '@' + Integer.toHexString(hashCode());

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
&& control != folder.getTopRight() && !isPageOfSelectedTab(folder, control)) {
// the folder hides this page from the engine, so it was skipped
styleWhenItBecomesThePage(folder, control);
}
});
}

/**
* Styles the control once it is the selected tab's page, which attaching it
* resizes it to or selecting its tab shows it.
*/
private void styleWhenItBecomesThePage(CTabFolder folder, Control control) {
if (control.getData(pendingPageKey) != null) {
return;
}
Listener listener = new Listener() {
@Override
public void handleEvent(Event event) {
// the control may have been reparented, and a new skin event watches it again
boolean inFolder = control.getParent() == folder && !folder.isDisposed();
if (inFolder && !isPageOfSelectedTab(folder, control)) {
return;
}
control.removeListener(SWT.Show, this);
control.removeListener(SWT.Resize, this);
control.setData(pendingPageKey, null);
if (inFolder) {
engine.applyStyles(control, true);
}
}
};
control.setData(pendingPageKey, listener);
control.addListener(SWT.Show, listener);
control.addListener(SWT.Resize, listener);
Comment thread
vogella marked this conversation as resolved.
}

private static boolean isPageOfSelectedTab(CTabFolder folder, Control control) {
int selected = folder.getSelectionIndex();
return selected >= 0 && folder.getItem(selected).getControl() == control;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;

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;
Expand Down Expand Up @@ -356,4 +360,68 @@ 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);
// a class selector keeps this engine, which stays attached to the shared
// display as a skin listener, from styling the widgets of other tests
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");
// nothing is selected yet, so the page is hidden and the CTabFolder does
// not expose it to 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

// a programmatic selection sends no selection event, so only the page
// becoming visible can trigger its styling
folderToTest.setSelection(0);
spinEventLoop(display);

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

@Test
void testPageAttachedToItsItemAfterSkinningIsStyled() throws IOException {
Display display = css.getDisplay();
CSSEngine engine = new CSSSWTEngineImpl(display, true);
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");
// building the page runs the event loop, as loading a search page does in
// the Search dialog, so the page is skinned while its item has no control
// yet and the CTabFolder does not expose it
spinEventLoop(display);

// the tab is already selected and the page has been visible since it was
// created, so attaching it sends no show event either
tab1.setControl(page);
spinEventLoop(display);

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

private static void spinEventLoop(Display display) {
while (display.readAndDispatch()) {
// deliver the pending skin and show events
}
}
}
Loading