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 @@ -175,6 +175,7 @@ public class CTabFolder extends Composite {
Listener tabControlZoomListener;
boolean ignoreTraverse;
boolean useDefaultRenderer;
boolean paintedBackgroundsPending;

/* External Listener management */
CTabFolder2Listener[] folderListeners = new CTabFolder2Listener[0];
Expand Down Expand Up @@ -2147,6 +2148,18 @@ void onPaint(Event event) {
gc.setFont(gcFont);
gc.setForeground(gcForeground);
gc.setBackground(gcBackground);

if (!useDefaultRenderer && !paintedBackgroundsPending && controls != null) {
Rectangle damage = event.getBounds();
for (Control control : controls) {
// a custom renderer can change its colors with only a redraw
if (!control.isDisposed() && damage.intersects(control.getBounds())) {
paintedBackgroundsPending = true;
getDisplay().asyncExec(this::updatePaintedBackgrounds);
break;
}
}
}
}

void onResize(Event event) {
Expand Down Expand Up @@ -3268,6 +3281,8 @@ public void setRenderer(CTabFolderRenderer renderer) {
if (useDefaultRenderer) renderer = new CTabFolderRenderer(this);
this.renderer = renderer;
updateFolder(REDRAW);
// the renderer decides what the top right controls get as background
updateBkImages(true);
}
/**
* Set the selection to the tab at the specified item.
Expand Down Expand Up @@ -4107,7 +4122,8 @@ void updateBkImages(boolean colorChanged) {
if (wrapped || gradientColors == null) {
bkImageBounds[i]=null;
control.setBackgroundImage(null);
control.setBackground(getBackground());
// a custom renderer may paint anything behind the control
control.setBackground(useDefaultRenderer ? getBackground() : getPaintedColor(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2));
} else {
bounds.width = 10;
if (!onBottom) {
Expand All @@ -4118,11 +4134,21 @@ void updateBkImages(boolean colorChanged) {
bounds.y = -1;
}
bounds.x = 0;
if (bounds.height <= 0) {
// drop the cache, so a skipped color change is not lost
bkImageBounds[i] = null;
continue;
}
// do not redraw when only translated:
if (colorChanged || !bounds.equals(bkImageBounds[i])) {
bkImageBounds[i] = bounds;
if (controlBkImages[i] != null) controlBkImages[i].dispose();
controlBkImages[i] = new Image(control.getDisplay(), (gc, imageWidth, imageHeight) -> renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc), bounds.width, bounds.height);
controlBkImages[i] = new Image(control.getDisplay(), (gc, imageWidth, imageHeight) -> {
// pixels a renderer leaves untouched would stay blank in an image
gc.setBackground(getBackground());
gc.fillRectangle(0, 0, imageWidth, imageHeight);
renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc);
}, bounds.width, bounds.height);
control.setBackground(null);
control.setBackgroundImage(controlBkImages[i]);
}
Expand All @@ -4133,6 +4159,52 @@ void updateBkImages(boolean colorChanged) {

}
}
/** Gives the controls with a flat background the color the renderer now paints behind them. */
void updatePaintedBackgrounds() {
paintedBackgroundsPending = false;
if (isDisposed() || useDefaultRenderer || hovering || controls == null) return;
for (Control control : controls) {
if (control.isDisposed() || control.getBackgroundImage() != null) continue;
Rectangle bounds = control.getBounds();
Color color = getPaintedColor(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
if (!color.equals(control.getBackground())) control.setBackground(color);
}
}
/** Returns the color the folder paints at the given point, as onPaint draws body and header. */
Color getPaintedColor(int x, int y) {
Point size = getSize();
if (x < 0 || y < 0 || x >= size.x || y >= size.y) return getBackground();
Rectangle bodyRect = new Rectangle(0, 0, size.x, size.y);
// a few pixels around the point, translated, so the renderer still gets the full folder bounds
Image image = new Image(getDisplay(), (gc, imageWidth, imageHeight) -> {
Transform transform = new Transform(gc.getDevice());
try {
transform.translate(1 - x, 1 - y);
gc.setTransform(transform);
Comment thread
vogella marked this conversation as resolved.
// in user space, so that a renderer saving and restoring the clipping keeps it
gc.setClipping(x - 1, y - 1, imageWidth, imageHeight);
gc.setFont(getFont());
gc.setForeground(getForeground());
// pixels a renderer leaves untouched show the widget background on screen
gc.setBackground(getBackground());
gc.fillRectangle(x - 1, y - 1, imageWidth, imageHeight);
renderer.draw(CTabFolderRenderer.PART_BODY, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
gc.setFont(getFont());
gc.setForeground(getForeground());
gc.setBackground(getBackground());
renderer.draw(CTabFolderRenderer.PART_HEADER, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
} finally {
transform.dispose();
}
}, 3, 3);
try {
// the zoom the folder is shown at, so that the renderer is invoked as on screen
ImageData data = image.getImageData(DPIUtil.getZoomForAutoscaleProperty(getShell().getZoom()));
return new Color(data.palette.getRGB(data.getPixel(data.width / 2, data.height / 2)));
} finally {
image.dispose();
}
}
String _getToolTip(int x, int y) {
CTabItem item = getItem(new Point (x, y));
if (item == null) return null;
Expand Down
1 change: 1 addition & 0 deletions examples/org.eclipse.swt.snippets/Snippets.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ To contribute a new snippet, [create a snippet contribution as a pull request](h
- [min and max buttons, close button and image only on selected tab](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet165.java) – [(preview)](https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/previews/Snippet165.png "Preview for Snippet 165")
- [demonstration of a multi line CTabFolder](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet371.java) – [(preview)](https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/previews/Snippet371.png "Preview for Snippet 371")
- [dirty indicator using bullet dot on close button with theme switching](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet392.java)
- [background of a top right control that wraps below the tab row](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet394.java)

### **Cursor**
- [set the hand cursor into a control](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.java) – [(preview)](https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/previews/Snippet44.png "Preview for Snippet 44")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*******************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* 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
*******************************************************************************/
package org.eclipse.swt.snippets;

/*
* CTabFolder example: background of a top right control that wrapped below the
* tab row. Widen the shell until the tool bar fits next to the tabs.
*
* For a list of all SWT example snippets see
* http://www.eclipse.org/swt/snippets/
*/
import org.eclipse.swt.*;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public class Snippet394 {
static final Color TAB_ROW = new Color(0x21, 0x22, 0x2C);
static final Color BODY = new Color(0x28, 0x2A, 0x36);

/** Paints tab row and body in two different colors. */
static class TwoToneRenderer extends CTabFolderRenderer {
TwoToneRenderer(CTabFolder parent) {
super(parent);
}

@Override
protected void draw(int part, int state, Rectangle bounds, GC gc) {
super.draw(part, state, bounds, gc);
if (part == PART_HEADER) {
int split = bounds.y + parent.getTabHeight() + 3;
gc.setBackground(BODY);
gc.fillRectangle(bounds.x, split, bounds.width, bounds.y + bounds.height - split);
}
}
}

public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Wrapped top right control");
shell.setLayout(new FillLayout());

CTabFolder folder = new CTabFolder(shell, SWT.NONE);
folder.setRenderer(new TwoToneRenderer(folder));
folder.setBackground(TAB_ROW);
folder.setForeground(display.getSystemColor(SWT.COLOR_WHITE));

for (String name : new String[] { "Variables", "Breakpoints", "Expressions" }) {
CTabItem item = new CTabItem(folder, SWT.NONE);
item.setText(name);
Composite content = new Composite(folder, SWT.NONE);
content.setBackground(BODY);
item.setControl(content);
}
folder.setSelection(0);

Composite topRight = new Composite(folder, SWT.NONE);
topRight.setLayout(new FillLayout());
ToolBar toolBar = new ToolBar(topRight, SWT.FLAT);
for (String text : new String[] { "Collapse", "Expand", "Filter", "Menu" }) {
new ToolItem(toolBar, SWT.PUSH).setText(text);
}
folder.setTopRight(topRight, SWT.RIGHT | SWT.WRAP);

shell.setSize(360, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
Loading
Loading