Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux/tooltip #229

Merged
merged 12 commits into from
Jan 26, 2021
Merged
7 changes: 1 addition & 6 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,7 @@ jobs:
with:
java-version: 8
- name: Build & Test
uses: eskatos/gradle-command-action@v1
with:
arguments: build test -PskipAutostyle --info --no-daemon
wrapper-cache-enabled: true
dependencies-cache-enabled: true
configuration-cache-enabled: true
run: xvfb-run -a -server-num=1 --server-args="-screen 0 2000x3000x16" ./gradlew build test -PskipAutostyle --info --no-daemon
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,22 @@ public static boolean isHighDpiEnabled() {
public static boolean useQuartz() {
return SystemInfo.isMac && PropertyValue.TRUE.equals(System.getProperty("apple.awt.graphics.UseQuartz"));
}

public static boolean supportsTransparency(final Window window) {
if (window == null || GraphicsEnvironment.isHeadless()) return false;
GraphicsConfiguration gc = window.getGraphicsConfiguration();
if (gc == null) return false;
return supportsTransparency(gc.getDevice());
}

public static boolean supportsTransparency() {
if (GraphicsEnvironment.isHeadless()) return false;
return supportsTransparency(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice());
}

public static boolean supportsTransparency(final GraphicsDevice gd) {
return gd != null
&& gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT)
&& gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class PlatformDefaultsInitTask implements DefaultsInitTask {
public void run(final Theme currentTheme, final UIDefaults defaults) {
String key = DarkPopupMenuUI.KEY_DEFAULT_LIGHTWEIGHT_POPUPS;
if (SystemInfo.isWindows10 && DecorationsHandler.getSharedInstance().isCustomDecorationSupported()) {
JPopupMenu.setDefaultLightWeightPopupEnabled(Boolean.TRUE.equals(defaults.get(key + ".windows")));
JPopupMenu.setDefaultLightWeightPopupEnabled(Boolean.TRUE.equals(defaults.get(key + ".windows10")));
} else {
JPopupMenu.setDefaultLightWeightPopupEnabled(Boolean.TRUE.equals(defaults.get(key)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
package com.github.weisj.darklaf.ui;

import java.awt.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

import javax.swing.*;

import com.github.weisj.darklaf.graphics.GraphicsUtil;
import com.github.weisj.darklaf.platform.DecorationsHandler;
import com.github.weisj.darklaf.ui.rootpane.DarkRootPaneUI;
import com.github.weisj.darklaf.uiresource.DarkColorUIResource;
import com.github.weisj.darklaf.util.ColorUtil;
import com.github.weisj.darklaf.util.DarkUIUtil;
import com.github.weisj.darklaf.util.Pair;
import com.github.weisj.darklaf.util.PropertyUtil;
import com.github.weisj.darklaf.util.*;

public class DarkPopupFactory extends PopupFactory {

Expand All @@ -54,12 +54,12 @@ public Popup getPopup(final Component owner, final Component contents, final int
return popup;
}

protected Pair<Popup, PopupType> getEffectivePopup(final Component owner, final Component contents, final int x,
final int y) {
protected Pair<Popup, PopupType> getEffectivePopup(final Component owner, final Component contents,
final int x, final int y) {
Popup popup = super.getPopup(owner, contents, x, y);
PopupType type = getPopupType(popup);
boolean forceHeavy =
type != PopupType.HEAVY_WEIGHT && PropertyUtil.getBooleanProperty(contents, KEY_FORCE_HEAVYWEIGHT);
boolean forceHeavy = type != PopupType.HEAVY_WEIGHT
&& PropertyUtil.getBooleanProperty(contents, KEY_FORCE_HEAVYWEIGHT);
if (forceHeavy) {
// Heavy weight owner forces a heavyweight popup.
Window targetWindow = DarkUIUtil.getWindow(owner);
Expand All @@ -78,11 +78,7 @@ protected Pair<Popup, PopupType> getEffectivePopup(final Component owner, final
popup = super.getPopup(getHeavyWeightParent(DarkUIUtil.getWindow(owner)), contents, x, y);
window = DarkUIUtil.getWindow(contents);
}
if (window != null) {
Window w = window;
w.setLocation(x, y);
SwingUtilities.invokeLater(() -> w.setLocation(x, y));
}
if (window != null) window.setLocation(x, y);
}
return new Pair<>(popup, type);
}
Expand All @@ -104,43 +100,68 @@ protected void setupPopup(final PopupType type, final Component contents, final
if (window != null) {
boolean isFocusable = PropertyUtil.getBooleanProperty(contents, KEY_FOCUSABLE_POPUP);
boolean startHidden = PropertyUtil.getBooleanProperty(contents, KEY_START_HIDDEN);
setupWindow(window, contents, isFocusable, startHidden);
setupHeavyWeightWindow(window, contents, isFocusable, startHidden);
window.setLocation(x, y);
}
}
}

protected void setupWindow(final Window window, final Component contents, final boolean isFocusable,
final boolean startHidden) {
protected void setupHeavyWeightWindow(final Window window, final Component contents,
final boolean isFocusable, final boolean startHidden) {
boolean noDecorations = PropertyUtil.getBooleanProperty(contents, KEY_NO_DECORATION);
boolean opaque = PropertyUtil.getBooleanProperty(contents, KEY_OPAQUE);
JRootPane rootPane = window instanceof RootPaneContainer ? ((RootPaneContainer) window).getRootPane() : null;
if (rootPane != null && contents instanceof Container) {
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.add(contents);
rootPane.setContentPane(contentPane);
}
setupFocusableWindowState(isFocusable, window);
setupWindowBackground(window, rootPane, opaque, !noDecorations);
setupWindowFocusableState(isFocusable, window);
setupWindowDecorations(window, rootPane, noDecorations);
setupWindowOpacity(startHidden, window);
}

protected void setupWindowBackground(final Window window, final JRootPane rootPane, final boolean opaque,
final boolean decorations) {
protected void setupWindowBackground(final Window window, final JRootPane rootPane,
final boolean opaque, final boolean decorations) {
/*
* Sometimes the background is java.awt.SystemColor[i=7] It results in a flash of white background,
* that is repainted with the proper popup background later. That is why we set window background
* explicitly.
*/
if (rootPane == null) return;
rootPane.setOpaque(opaque);
if (opaque) {
Color bg = ColorUtil.toAlpha(rootPane.getBackground(), 255);
window.setBackground(bg);
} else {
Color bg = getTranslucentPopupBackground(decorations);
window.setBackground(bg);
rootPane.setBackground(bg);
if (SystemInfo.isLinux && !opaque) {
linuxOpacityFix(rootPane);
}

Color bg = opaque
? ColorUtil.toAlpha(rootPane.getBackground(), 255)
: getTranslucentPopupBackground(decorations);
Container p = rootPane.getContentPane();
while (p != null && p != window) {
p.setBackground(bg);
if (p instanceof JComponent) {
((JComponent) p).setOpaque(opaque);
}
p = p.getParent();
}
window.setBackground(bg);
}

protected void setupWindowFocusableState(final boolean isFocusable, final Window window) {
private void linuxOpacityFix(final JRootPane rootPane) {
// HeavyWeights popups use rootpanes with double buffering disabled by setting
// setUseTrueDoubleBuffering(false). On some Linux window managers (e.g. Gnome) this results
// in windows not being transparent if requested.
try {
Method method = JRootPane.class.getDeclaredMethod("setUseTrueDoubleBuffering", boolean.class);
method.setAccessible(true);
method.invoke(rootPane, true);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}

protected void setupFocusableWindowState(final boolean isFocusable, final Window window) {
if (isFocusable && !window.getFocusableWindowState()) {
window.dispose();
window.setFocusableWindowState(true);
Expand All @@ -163,11 +184,11 @@ protected void setupWindowDecorations(final Window window, final JRootPane rootP
}

protected void setupWindowOpacity(final boolean startHidden, final Window window) {
boolean translucencySupported = DarkUIUtil.supportsTransparency(window);
boolean translucencySupported = GraphicsUtil.supportsTransparency(window);
if (startHidden && translucencySupported) {
try {
window.setOpacity(0);
} catch (Exception ignored) {
} catch (final Exception ignored) {
}
}
}
Expand Down Expand Up @@ -223,4 +244,5 @@ public enum PopupType {
MEDIUM_WEIGHT,
HEAVY_WEIGHT
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class DarkToolTipUI extends BasicToolTipUI
implements PropertyChangeListener, HierarchyListener, ToolTipConstants {

protected static final float MAX_ALPHA = 1.0f;
protected static final float MIN_ALPHA = 0.1f;

protected final MouseListener exitListener = new MouseAdapter() {
@Override
Expand Down Expand Up @@ -115,6 +116,7 @@ protected void installDefaults(final JComponent c) {
toolTip.putClientProperty(DarkPopupFactory.KEY_NO_DECORATION, !useDecoratedPopup());
toolTip.putClientProperty(DarkPopupFactory.KEY_START_HIDDEN, true);
toolTip.putClientProperty(DarkPopupFactory.KEY_FORCE_HEAVYWEIGHT, true);
toolTip.setOpaque(false);
fadeAnimator = new FadeInAnimator();
updateTipText(toolTip);
updateStyle();
Expand Down Expand Up @@ -347,11 +349,13 @@ protected void updateTipText(final JToolTip tooltip) {
}

protected void scheduleAnimation() {
Window window = DarkUIUtil.getWindow(toolTip);
animationScheduled = transparencySupported(DarkUIUtil.getWindow(toolTip));
if (window != null && animationScheduled) window.setOpacity(MIN_ALPHA);
}

protected boolean transparencySupported(final Window window) {
return style != ToolTipStyle.PLAIN && DarkUIUtil.supportsTransparency(window);
return style != ToolTipStyle.PLAIN && GraphicsUtil.supportsTransparency(window);
}

protected void updateStyle() {
Expand All @@ -360,6 +364,7 @@ protected void updateStyle() {
ToolTipStyle tooltipStyle = ToolTipStyle.parse(toolTip.getClientProperty(KEY_STYLE));
if (style == null) style = tooltipStyle;
if (style == null) style = ToolTipStyle.parse(UIManager.get("ToolTip.defaultStyle"));
if (style.isOpqaue() && !GraphicsUtil.supportsTransparency()) style = ToolTipStyle.PLAIN;
if (style != tooltipStyle) {
toolTip.putClientProperty(KEY_STYLE, style);
}
Expand Down Expand Up @@ -404,7 +409,7 @@ public boolean isEnabled() {

@Override
public void paintNow(final float fraction) {
alpha = fraction * MAX_ALPHA;
alpha = MIN_ALPHA + fraction * (MAX_ALPHA - MIN_ALPHA);
Window window = SwingUtilities.getWindowAncestor(toolTip);
if (DarkUIUtil.isDecorated(window)) return;
if (window != null) window.setOpacity(alpha);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public DarkTooltipBorder() {
int shadowSize = UIManager.getInt("ToolTip.shadowSize");
float opacity = UIManager.getInt("ToolTip.shadowOpacity") / 100.0f;
shadowBorder = new DropShadowBorder(UIManager.getColor("ToolTip.borderShadowColor"), shadowSize, opacity,
2 * shadowSize, false, true, true, true);
2 * shadowSize, true, true, true, true);
paintShadow = UIManager.getBoolean("ToolTip.paintShadow");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,9 @@ public int getIconTextGap() {
public Dimension getPreferredSize() {
return getDelegate() instanceof JComponent ? ((JComponent) getDelegate()).getPreferredSize() : null;
}

@Override
public Dimension getMinimumSize() {
return getDelegate() instanceof JComponent ? ((JComponent) getDelegate()).getMinimumSize() : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,38 @@ private int getOffset() {
public Dimension getPreferredSize() {
if (defaultRenderer != null) {
Dimension pSize = renderComponent.getPreferredSize();

pSize.width += getOffset() + PAD;

Dimension rSize = defaultRenderer.getPreferredSize();

Icon icon = defaultRenderer.getIcon();
if (rSize != null) {
pSize.height = Math.max(pSize.height, rSize.height);
}
if (icon != null) {
pSize.height = Math.max(pSize.height, icon.getIconHeight());
}
addIconSize(pSize, rSize);
return pSize;
}
return new Dimension(0, 0);
}

@Override
public Dimension getMinimumSize() {
if (defaultRenderer != null) {
Dimension pSize = renderComponent.getMinimumSize();
pSize.width += getOffset() + PAD;
Dimension rSize = defaultRenderer.getMinimumSize();

addIconSize(pSize, rSize);
return pSize;
}
return new Dimension(0, 0);
}

private void addIconSize(final Dimension pSize, final Dimension rSize) {
Icon icon = defaultRenderer.getIcon();
if (rSize != null) {
pSize.height = Math.max(pSize.height, rSize.height);
}
if (icon != null) {
pSize.height = Math.max(pSize.height, icon.getIconHeight());
}
}

@Override
public void paint(final Graphics g) {
g.setColor(getBackground());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ public interface TreeRendererSupport {
int getIconTextGap();

Dimension getPreferredSize();

Dimension getMinimumSize();
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

Expand Down Expand Up @@ -530,13 +529,6 @@ public static Container getOpaqueParent(final Container parent) {
return getParentMatching(parent, Container::isOpaque);
}

public static boolean supportsTransparency(final Window window) {
Optional<GraphicsDevice> gd =
Optional.ofNullable(window).map(Window::getGraphicsConfiguration).map(GraphicsConfiguration::getDevice);
return gd.map(d -> d.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT))
.orElse(false);
}

public static Dimension getPreferredSize(final JComponent component) {
if (component == null) return new Dimension(0, 0);
LayoutManager layoutManager = component.getLayout();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
#
# suppress inspection "UnusedProperty" for whole file
#
FileChooser.listViewWindowsStyle = false
Table.alternateRowColor = false
Tree.alternateRowColor = false
List.alternateRowColor = false
FileChooser.listViewWindowsStyle = false
Table.alternateRowColor = false
Tree.alternateRowColor = false
List.alternateRowColor = false
PopupMenu.defaultLightWeightPopups = false

kerning.blockList = {Table.font}
kerning.blockList = {Table.font}
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@
#
# suppress inspection "UnusedProperty" for whole file
#
FileChooser.listViewWindowsStyle = true
FileChooser.listViewWindowsStyle = true

TitlePane.unifiedMenuBar = true
Table.alternateRowColor = false
Tree.alternateRowColor = false
List.alternateRowColor = false
PopupMenu.defaultLightWeightPopups.windows = false
TitlePane.unifiedMenuBar = true
Table.alternateRowColor = false
Tree.alternateRowColor = false
List.alternateRowColor = false
PopupMenu.defaultLightWeightPopups = true
PopupMenu.defaultLightWeightPopups.windows10 = false

kerning.blockList = {NumberingPane.font;\
FormattedTextField.font;\
EditorPane.font;\
PasswordField.font;\
TextPane.font;\
TextArea.font;\
TextField.font;\
Table.font}
kerning.blockList = {NumberingPane.font;\
FormattedTextField.font;\
EditorPane.font;\
PasswordField.font;\
TextPane.font;\
TextArea.font;\
TextField.font;\
Table.font}
Loading