Skip to content

Commit 169e5fd

Browse files
committed
Improves UI on Linux
1 parent a15a493 commit 169e5fd

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

app/src/main/groovy/org/jd/gui/controller/MainController.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.jd.gui.service.indexer.IndexerService
2828
import org.jd.gui.service.mainpanel.PanelFactoryService
2929
import org.jd.gui.service.pastehandler.PasteHandlerService
3030
import org.jd.gui.service.actions.ContextualActionsFactoryService
31+
import org.jd.gui.service.platform.PlatformService
3132
import org.jd.gui.service.preferencespanel.PreferencesPanelService
3233
import org.jd.gui.service.sourcesaver.SourceSaverService
3334
import org.jd.gui.service.treenode.TreeNodeFactoryService
@@ -42,6 +43,7 @@ import org.jd.gui.spi.TreeNodeFactory
4243
import org.jd.gui.spi.TypeFactory
4344
import org.jd.gui.spi.UriLoader
4445
import org.jd.gui.util.net.UriUtil
46+
import org.jd.gui.util.swing.SwingUtil
4547

4648
import javax.swing.Action
4749
import javax.swing.Icon
@@ -90,6 +92,12 @@ class MainController implements API {
9092
MainController(SwingBuilder swing, Configuration configuration) {
9193
this.swing = swing
9294
this.configuration = configuration
95+
96+
if (PlatformService.instance.isLinux) {
97+
// Fix for GTKLookAndFeel
98+
SwingUtil.installGtkPopupBugWorkaround()
99+
}
100+
93101
// Create main frame
94102
mainView = new MainView(
95103
swing,
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2008-2015 Emmanuel Dupuy
3+
* This program is made available under the terms of the GPLv3 License.
4+
*/
5+
6+
package org.jd.gui.util.swing
7+
8+
import groovy.transform.CompileStatic
9+
10+
import javax.swing.JComponent
11+
import javax.swing.JPopupMenu
12+
import javax.swing.JSeparator
13+
import javax.swing.LookAndFeel
14+
import javax.swing.UIManager
15+
import java.lang.reflect.Field
16+
import java.lang.reflect.Method
17+
18+
/**
19+
* See: https://www.ailis.de/~k/archives/67-Workaround-for-borderless-Java-Swing-menus-on-Linux.html
20+
*/
21+
@CompileStatic
22+
class SwingUtil {
23+
/*
24+
* This is free and unencumbered software released into the public domain.
25+
*
26+
* Anyone is free to copy, modify, publish, use, compile, sell, or
27+
* distribute this software, either in source code form or as a compiled
28+
* binary, for any purpose, commercial or non-commercial, and by any
29+
* means.
30+
*
31+
* In jurisdictions that recognize copyright laws, the author or authors
32+
* of this software dedicate any and all copyright interest in the
33+
* software to the public domain. We make this dedication for the benefit
34+
* of the public at large and to the detriment of our heirs and
35+
* successors. We intend this dedication to be an overt act of
36+
* relinquishment in perpetuity of all present and future rights to this
37+
* software under copyright law.
38+
*
39+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
42+
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
43+
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
44+
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
45+
* OTHER DEALINGS IN THE SOFTWARE.
46+
*
47+
* For more information, please refer to <http://unlicense.org/>
48+
*/
49+
50+
/**
51+
* Swing menus are looking pretty bad on Linux when the GTK LaF is used (See
52+
* bug #6925412). It will most likely never be fixed anytime soon so this
53+
* method provides a workaround for it. It uses reflection to change the GTK
54+
* style objects of Swing so popup menu borders have a minimum thickness of
55+
* 1 and menu separators have a minimum vertical thickness of 1.
56+
*/
57+
public static void installGtkPopupBugWorkaround() {
58+
// Get current look-and-feel implementation class
59+
LookAndFeel laf = UIManager.getLookAndFeel();
60+
Class<?> lafClass = laf.getClass();
61+
62+
// Do nothing when not using the problematic LaF
63+
if (!lafClass.getName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel")) return;
64+
65+
// We do reflection from here on. Failure is silently ignored. The
66+
// workaround is simply not installed when something goes wrong here
67+
try {
68+
// Access the GTK style factory
69+
Field field = lafClass.getDeclaredField("styleFactory");
70+
boolean accessible = field.isAccessible();
71+
field.setAccessible(true);
72+
Object styleFactory = field.get(laf);
73+
field.setAccessible(accessible);
74+
75+
// Fix the horizontal and vertical thickness of popup menu style
76+
Object style = getGtkStyle(styleFactory, new JPopupMenu(), "POPUP_MENU");
77+
fixGtkThickness(style, "yThickness");
78+
fixGtkThickness(style, "xThickness");
79+
80+
// Fix the vertical thickness of the popup menu separator style
81+
style = getGtkStyle(styleFactory, new JSeparator(), "POPUP_MENU_SEPARATOR");
82+
fixGtkThickness(style, "yThickness");
83+
} catch (Exception e) {
84+
// Silently ignored. Workaround can't be applied.
85+
}
86+
}
87+
88+
/**
89+
* Called internally by installGtkPopupBugWorkaround to fix the thickness
90+
* of a GTK style field by setting it to a minimum value of 1.
91+
*
92+
* @param style
93+
* The GTK style object.
94+
* @param fieldName
95+
* The field name.
96+
* @throws Exception
97+
* When reflection fails.
98+
*/
99+
private static void fixGtkThickness(Object style, String fieldName) throws Exception {
100+
Field field = style.getClass().getDeclaredField(fieldName);
101+
boolean accessible = field.isAccessible();
102+
field.setAccessible(true);
103+
field.setInt(style, Math.max(1, field.getInt(style)));
104+
field.setAccessible(accessible);
105+
}
106+
107+
/**
108+
* Called internally by installGtkPopupBugWorkaround. Returns a specific
109+
* GTK style object.
110+
*
111+
* @param styleFactory
112+
* The GTK style factory.
113+
* @param component
114+
* The target component of the style.
115+
* @param regionName
116+
* The name of the target region of the style.
117+
* @return The GTK style.
118+
* @throws Exception
119+
* When reflection fails.
120+
*/
121+
private static Object getGtkStyle(Object styleFactory, JComponent component, String regionName) throws Exception {
122+
// Create the region object
123+
Class<?> regionClass = Class.forName("javax.swing.plaf.synth.Region");
124+
Field field = regionClass.getField(regionName);
125+
Object region = field.get(regionClass);
126+
127+
// Get and return the style
128+
Class<?> styleFactoryClass = styleFactory.getClass();
129+
Method method = styleFactoryClass.getMethod("getStyle", JComponent.class, regionClass);
130+
boolean accessible = method.isAccessible();
131+
method.setAccessible(true);
132+
Object style = method.invoke(styleFactory, component, region);
133+
method.setAccessible(accessible);
134+
return style;
135+
}
136+
}

app/src/main/groovy/org/jd/gui/view/PreferencesView.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class PreferencesView implements PreferencesPanel.PreferencesPanelChangeListener
8989
preferredHeight = maxHeight
9090
preferencesScrollPane.preferredSize = new Dimension(400, preferredHeight)
9191

92+
minimumSize = new Dimension(300, 200)
93+
9294
pack()
9395
locationRelativeTo = parent
9496
}

0 commit comments

Comments
 (0)