Skip to content

Commit 5cd2cce

Browse files
committed
Tree: fixed repainting multi-selection when tree looses or gains focus (issue #1122)
1 parent e667ae2 commit 5cd2cce

4 files changed

Lines changed: 381 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ FlatLaf Change Log
1616
(issue #1103)
1717
- TextComponents: Fixed preferred width when leading/trailing components or
1818
icons are present. (issue #1110)
19+
- Tree: Fixed repainting multi-selection when tree looses or gains focus. (issue
20+
#1122)
1921
- JideTabbedPane: Support card tabs. (PR #1094)
2022
- Native libraries in Eclipse RCP: Fixed exception when runtime path contains
2123
spaces. (issue #1102)

flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import java.awt.Graphics2D;
2424
import java.awt.Insets;
2525
import java.awt.Rectangle;
26+
import java.awt.event.FocusEvent;
27+
import java.awt.event.FocusListener;
2628
import java.awt.event.MouseEvent;
2729
import java.awt.event.MouseListener;
2830
import java.awt.geom.Rectangle2D;
@@ -365,6 +367,30 @@ private void repaintWideDropLocation(JTree.DropLocation loc) {
365367
HiDPIUtils.repaint( tree, 0, r.y, tree.getWidth(), r.height );
366368
}
367369

370+
@Override
371+
protected FocusListener createFocusListener() {
372+
return new BasicTreeUI.FocusHandler() {
373+
@Override
374+
public void focusGained( FocusEvent e ) {
375+
super.focusGained( e );
376+
377+
// repaint necessary for active/inactive selection (super repaints single selection)
378+
if( tree.getSelectionCount() > 1 )
379+
tree.repaint();
380+
}
381+
382+
@Override
383+
public void focusLost( FocusEvent e ) {
384+
super.focusLost( e );
385+
386+
// repaint necessary for active/inactive selection (super repaints single selection)
387+
if( tree.getSelectionCount() > 1 )
388+
tree.repaint();
389+
}
390+
391+
};
392+
}
393+
368394
@Override
369395
protected TreeSelectionListener createTreeSelectionListener() {
370396
TreeSelectionListener superListener = super.createTreeSelectionListener();
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
* Copyright 2026 FormDev Software GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.formdev.flatlaf.testing;
18+
19+
import java.awt.*;
20+
import javax.swing.*;
21+
import javax.swing.border.*;
22+
import javax.swing.table.DefaultTableModel;
23+
import net.miginfocom.swing.*;
24+
25+
/**
26+
* @author Karl Tauber
27+
*/
28+
public class FlatFocusLostTest
29+
extends FlatTestPanel
30+
{
31+
public static void main( String[] args ) {
32+
SwingUtilities.invokeLater( () -> {
33+
FlatTestFrame frame = FlatTestFrame.create( args, "FlatFocusLostTest" );
34+
frame.showFrame( FlatFocusLostTest::new );
35+
} );
36+
}
37+
38+
public FlatFocusLostTest() {
39+
initComponents();
40+
41+
DefaultListModel<String> listModel = new DefaultListModel<>();
42+
for( int i = 1; i <= 8; i++ )
43+
listModel.addElement( "item " + i );
44+
list1.setModel( listModel );
45+
list2.setModel( listModel );
46+
list3.setModel( listModel );
47+
list1.setSelectionInterval( 2, 5 );
48+
list2.setSelectionInterval( 2, 5 );
49+
list3.setSelectionInterval( 2, 5 );
50+
51+
tree1.expandRow( 1 );
52+
tree2.expandRow( 1 );
53+
tree3.expandRow( 1 );
54+
tree1.addSelectionInterval( 2, 5 );
55+
tree2.addSelectionInterval( 2, 5 );
56+
tree3.addSelectionInterval( 2, 5 );
57+
58+
DefaultTableModel tableModel = new DefaultTableModel( 8, 2 ) {
59+
@Override
60+
public Object getValueAt( int row, int column ) {
61+
return "item " + row + "," + column;
62+
}
63+
};
64+
table1.setModel( tableModel );
65+
table2.setModel( tableModel );
66+
table3.setModel( tableModel );
67+
table1.addRowSelectionInterval( 2, 5 );
68+
table2.addRowSelectionInterval( 2, 5 );
69+
table3.addRowSelectionInterval( 2, 5 );
70+
}
71+
72+
private void initComponents() {
73+
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
74+
JLabel label2 = new JLabel();
75+
JLabel label3 = new JLabel();
76+
JLabel label4 = new JLabel();
77+
JLabel label1 = new JLabel();
78+
list1 = new JList<>();
79+
JScrollPane scrollPane1 = new JScrollPane();
80+
list2 = new JList<>();
81+
JScrollPane scrollPane2 = new JScrollPane();
82+
list3 = new JList<>();
83+
JLabel label5 = new JLabel();
84+
tree1 = new JTree();
85+
JScrollPane scrollPane3 = new JScrollPane();
86+
tree2 = new JTree();
87+
JScrollPane scrollPane4 = new JScrollPane();
88+
tree3 = new JTree();
89+
JLabel label6 = new JLabel();
90+
table1 = new JTable();
91+
JScrollPane scrollPane5 = new JScrollPane();
92+
table2 = new JTable();
93+
JScrollPane scrollPane6 = new JScrollPane();
94+
table3 = new JTable();
95+
96+
//======== this ========
97+
setLayout(new MigLayout(
98+
"insets dialog,hidemode 3",
99+
// columns
100+
"[fill]" +
101+
"[sizegroup 1,fill]" +
102+
"[sizegroup 1,fill]" +
103+
"[sizegroup 1,fill]",
104+
// rows
105+
"[]" +
106+
"[150,fill]" +
107+
"[150,fill]" +
108+
"[150,fill]"));
109+
110+
//---- label2 ----
111+
label2.setText("no scroll pane");
112+
add(label2, "cell 1 0");
113+
114+
//---- label3 ----
115+
label3.setText("scroll pane");
116+
add(label3, "cell 2 0");
117+
118+
//---- label4 ----
119+
label4.setText("scroll pane with empty border");
120+
add(label4, "cell 3 0");
121+
122+
//---- label1 ----
123+
label1.setText("JList:");
124+
add(label1, "cell 0 1");
125+
add(list1, "cell 1 1");
126+
127+
//======== scrollPane1 ========
128+
{
129+
scrollPane1.setViewportView(list2);
130+
}
131+
add(scrollPane1, "cell 2 1");
132+
133+
//======== scrollPane2 ========
134+
{
135+
scrollPane2.setBorder(new EmptyBorder(5, 5, 5, 5));
136+
scrollPane2.setViewportView(list3);
137+
}
138+
add(scrollPane2, "cell 3 1");
139+
140+
//---- label5 ----
141+
label5.setText("JTree:");
142+
add(label5, "cell 0 2");
143+
add(tree1, "cell 1 2");
144+
145+
//======== scrollPane3 ========
146+
{
147+
scrollPane3.setViewportView(tree2);
148+
}
149+
add(scrollPane3, "cell 2 2");
150+
151+
//======== scrollPane4 ========
152+
{
153+
scrollPane4.setBorder(new EmptyBorder(5, 5, 5, 5));
154+
scrollPane4.setViewportView(tree3);
155+
}
156+
add(scrollPane4, "cell 3 2");
157+
158+
//---- label6 ----
159+
label6.setText("JTable:");
160+
add(label6, "cell 0 3");
161+
162+
//---- table1 ----
163+
table1.setPreferredScrollableViewportSize(new Dimension(100, 150));
164+
add(table1, "cell 1 3");
165+
166+
//======== scrollPane5 ========
167+
{
168+
169+
//---- table2 ----
170+
table2.setPreferredScrollableViewportSize(new Dimension(100, 150));
171+
scrollPane5.setViewportView(table2);
172+
}
173+
add(scrollPane5, "cell 2 3");
174+
175+
//======== scrollPane6 ========
176+
{
177+
scrollPane6.setBorder(new EmptyBorder(5, 5, 5, 5));
178+
179+
//---- table3 ----
180+
table3.setPreferredScrollableViewportSize(new Dimension(100, 150));
181+
scrollPane6.setViewportView(table3);
182+
}
183+
add(scrollPane6, "cell 3 3");
184+
// JFormDesigner - End of component initialization //GEN-END:initComponents
185+
}
186+
187+
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
188+
private JList<String> list1;
189+
private JList<String> list2;
190+
private JList<String> list3;
191+
private JTree tree1;
192+
private JTree tree2;
193+
private JTree tree3;
194+
private JTable table1;
195+
private JTable table2;
196+
private JTable table3;
197+
// JFormDesigner - End of variables declaration //GEN-END:variables
198+
}

0 commit comments

Comments
 (0)