From f3ae6f07cd2b812be823a416a566b9a704e08b51 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Mon, 4 May 2026 23:33:43 +0200 Subject: [PATCH] Match SourceViewer canvas background to text widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 1px gap between the vertical ruler and the text widget is rendered by RulerLayout against the parent canvas, which kept its system default background. On dark editor themes this surfaced as a bright vertical strip between the line numbers and the source. Track the StyledText background and apply it to the canvas, so the gap blends into the editor color scheme. Re-syncs on text widget paint events so preference-driven background changes propagate. Fixes https://github.com/eclipse-platform/eclipse.platform.ui/issues/3964 Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../jface/text/source/SourceViewer.java | 20 +++++ .../jface/text/tests/JFaceTextTestSuite.java | 2 + .../source/SourceViewerBackgroundTest.java | 89 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/source/SourceViewerBackgroundTest.java diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java index e63b1b5162c..b972f685ea5 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java @@ -26,6 +26,7 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; @@ -468,6 +469,25 @@ protected void createControl(Composite parent, int styles) { if (fOverviewRuler != null) { fOverviewRuler.createControl(fComposite, this); } + + if (fComposite != null) { + StyledText textWidget= getTextWidget(); + if (textWidget != null) { + // Match the canvas background to the text widget so the RulerLayout gap blends in. + syncCompositeBackground(textWidget); + textWidget.addPaintListener(e -> syncCompositeBackground(textWidget)); + } + } + } + + private void syncCompositeBackground(StyledText textWidget) { + if (fComposite == null || fComposite.isDisposed() || textWidget.isDisposed()) { + return; + } + Color desired= textWidget.getBackground(); + if (!desired.equals(fComposite.getBackground())) { + fComposite.setBackground(desired); + } } /** diff --git a/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java index 9d8d5dc6f23..7be79fa7fc5 100644 --- a/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java +++ b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java @@ -33,6 +33,7 @@ import org.eclipse.jface.text.tests.rules.WordRuleTest; import org.eclipse.jface.text.tests.source.AnnotationRulerColumnTest; import org.eclipse.jface.text.tests.source.LineNumberRulerColumnTest; +import org.eclipse.jface.text.tests.source.SourceViewerBackgroundTest; import org.eclipse.jface.text.tests.source.SourceViewerComputeStyleRangesTest; import org.eclipse.jface.text.tests.source.inlined.AnnotationOnTabTest; import org.eclipse.jface.text.tests.source.inlined.LineContentBoundsDrawingTest; @@ -47,6 +48,7 @@ @SelectClasses({ AnnotationRulerColumnTest.class, LineNumberRulerColumnTest.class, + SourceViewerBackgroundTest.class, SourceViewerComputeStyleRangesTest.class, HTML2TextReaderTest.class, TextHoverPopupTest.class, diff --git a/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/source/SourceViewerBackgroundTest.java b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/source/SourceViewerBackgroundTest.java new file mode 100644 index 00000000000..60956b5b77f --- /dev/null +++ b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/source/SourceViewerBackgroundTest.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * 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 - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.text.tests.source; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.source.VerticalRuler; + +/** + * Tests that the canvas behind a {@link SourceViewer} tracks the background of its text + * widget, so that the gap the ruler layout leaves is not visible. + */ +public class SourceViewerBackgroundTest { + + private Shell shell; + + @BeforeEach + public void setUp() { + shell= new Shell(); + } + + @AfterEach + public void tearDown() { + shell.dispose(); + } + + @Test + public void testCanvasBackgroundMatchesTextWidgetOnCreation() { + SourceViewer viewer= createViewer(); + + assertEquals(viewer.getTextWidget().getBackground(), viewer.getControl().getBackground()); + } + + @Test + public void testCanvasBackgroundFollowsTextWidgetBackground() { + SourceViewer viewer= createViewer(); + StyledText textWidget= viewer.getTextWidget(); + Color background= Display.getDefault().getSystemColor(SWT.COLOR_RED); + + textWidget.setBackground(background); + textWidget.notifyListeners(SWT.Paint, new Event()); + + assertEquals(background, viewer.getControl().getBackground()); + } + + @Test + public void testCanvasBackgroundFollowsResetToDefault() { + SourceViewer viewer= createViewer(); + StyledText textWidget= viewer.getTextWidget(); + Color defaultBackground= textWidget.getBackground(); + + textWidget.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_RED)); + textWidget.notifyListeners(SWT.Paint, new Event()); + textWidget.setBackground(null); + textWidget.notifyListeners(SWT.Paint, new Event()); + + assertEquals(defaultBackground, viewer.getControl().getBackground()); + } + + private SourceViewer createViewer() { + SourceViewer viewer= new SourceViewer(shell, new VerticalRuler(12), SWT.NONE); + viewer.setDocument(new Document("content")); //$NON-NLS-1$ + return viewer; + } +}