VAqua.src.org.violetlib.aqua.AquaTitleBar Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaqua Show documentation
Show all versions of vaqua Show documentation
An improved native Swing look and feel for macOS
The newest version!
/*
* Copyright (c) 2015-2018 Alan Snyder.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the license agreement. For details see
* accompanying license terms.
*/
package org.violetlib.aqua;
import java.awt.*;
import java.util.Arrays;
import javax.swing.*;
import org.jetbrains.annotations.NotNull;
import org.violetlib.jnr.Insetter;
import org.violetlib.jnr.LayoutInfo;
import org.violetlib.jnr.aqua.*;
import static org.violetlib.jnr.aqua.AquaUIPainter.TitleBarButtonWidget.*;
/**
* A title bar for an internal frame.
*/
public class AquaTitleBar {
private static final int sMaxIconWidth = 15;
private static final int sMaxIconHeight = sMaxIconWidth;
private static final int sAfterIconPad = 5;
protected final AquaUIPainter titleBarPainter = AquaPainting.create();
protected final JInternalFrame frame;
protected final AquaUIPainter.TitleBarWidget widget;
protected final TitleBarLayoutConfiguration layoutConfiguration;
protected final int titleBarHeight;
private int width;
private TitleBarLayout titleBarLayout; // recomputed as needed, do not access directly
private class TitleBarLayout {
int buttonAreaLeft;
int buttonAreaRight;
Rectangle titleBounds;
Rectangle closeButtonBounds;
Rectangle minimizeButtonBounds;
Rectangle resizeButtonBounds;
}
public AquaTitleBar(JInternalFrame frame, AquaUIPainter.TitleBarWidget widget) {
this.frame = frame;
this.widget = widget;
layoutConfiguration = new TitleBarLayoutConfiguration(this.widget);
LayoutInfo layoutInfo = titleBarPainter.getLayoutInfo().getLayoutInfo(layoutConfiguration);
titleBarHeight = (int) Math.ceil(layoutInfo.getFixedVisualHeight());
}
public void setWidth(int w) {
if (w != width) {
width = w;
invalidateLayout();
}
}
public void invalidateLayout() {
titleBarLayout = null;
}
public int getTitleBarHeight() {
return titleBarHeight;
}
public int getWhichButtonHit(int x, int y) {
TitleBarLayout layout = getTitleBarLayout();
if (layout.closeButtonBounds.contains(x, y)) {
return AquaInternalFrameBorder.kCloseButton;
}
if (layout.minimizeButtonBounds.contains(x, y)) {
return AquaInternalFrameBorder.kIconButton;
}
if (layout.resizeButtonBounds.contains(x, y)) {
return AquaInternalFrameBorder.kGrowButton;
}
return -1;
}
public boolean getWithinRolloverArea(int x, int y) {
if (y < 0 || y >= titleBarHeight) {
return false;
}
TitleBarLayout layout = getTitleBarLayout();
return x >= layout.buttonAreaLeft && x <= layout.buttonAreaRight;
}
protected TitleBarLayout getTitleBarLayout() {
if (titleBarLayout == null) {
titleBarLayout = createTitleBarLayout();
}
return titleBarLayout;
}
protected TitleBarLayout createTitleBarLayout() {
TitleBarLayout result = new TitleBarLayout();
result.buttonAreaLeft = Integer.MAX_VALUE;
result.buttonAreaRight = 0;
AquaUILayoutInfo uiLayoutInfo = titleBarPainter.getLayoutInfo();
for (AquaUIPainter.TitleBarButtonWidget bw : Arrays.asList(CLOSE_BOX, MINIMIZE_BOX, RESIZE_BOX)) {
Insetter s = uiLayoutInfo.getTitleBarButtonInsets(layoutConfiguration, bw);
Rectangle bounds = s.apply(width, titleBarHeight);
result.buttonAreaLeft = Math.min(result.buttonAreaLeft, bounds.x);
result.buttonAreaRight = Math.max(result.buttonAreaRight, bounds.x + bounds.width);
if (bw == CLOSE_BOX) {
result.closeButtonBounds = bounds;
} else if (bw == MINIMIZE_BOX) {
result.minimizeButtonBounds = bounds;
} else if (bw == RESIZE_BOX) {
result.resizeButtonBounds = bounds;
}
}
{
Insetter s = titleBarPainter.getLayoutInfo().getTitleBarLabelInsets(layoutConfiguration);
if (s != null) {
result.titleBounds = s.apply(width, titleBarHeight);
}
}
return result;
}
public void paint(@NotNull Graphics g) {
AquaAppearance appearance = AppearanceManager.ensureAppearance(frame);
boolean isSelected = AquaFocusHandler.isActive(frame) && (frame.isSelected() || widget == AquaUIPainter.TitleBarWidget.UTILITY_WINDOW);
EffectName effect = isSelected ? EffectName.EFFECT_NONE : EffectName.EFFECT_DISABLED;
Color textColor = appearance.getColorForOptionalEffect("text", effect);
assert textColor != null;
// paint the background and buttons
Configuration tg = getConfiguration();
AquaUtils.configure(titleBarPainter, frame.getRootPane(), width, titleBarHeight);
titleBarPainter.getPainter(tg).paint(g, 0, 0);
// now the title and the icon
paintTitleContents(g, textColor);
}
public Rectangle getButtonArea() {
TitleBarLayout layout = getTitleBarLayout();
return new Rectangle(layout.buttonAreaLeft, 0, layout.buttonAreaRight - layout.buttonAreaLeft, titleBarHeight);
}
protected TitleBarConfiguration getConfiguration() {
AquaUIPainter.State state = frame.isSelected() && AquaFocusHandler.isActive(frame) ? AquaUIPainter.State.ACTIVE : AquaUIPainter.State.INACTIVE;
AquaUIPainter.State closeButtonState = getButtonState(AquaInternalFrameBorder.kCloseButton, isClosable());
AquaUIPainter.State iconifyButtonState = getButtonState(AquaInternalFrameBorder.kIconButton, isIconifiable());
AquaUIPainter.State resizeButtonState = getButtonState(AquaInternalFrameBorder.kGrowButton, isMaximizable());
TitleBarConfiguration.ResizeAction resizeAction = getResizeAction(frame);
boolean isDirty = isDirty();
return new TitleBarConfiguration(widget, state, closeButtonState, iconifyButtonState, resizeButtonState, resizeAction, isDirty);
}
protected boolean isClosable() {
return frame.isClosable();
}
protected boolean isIconifiable() {
return frame.isIconifiable() && !frame.isMaximum();
}
protected boolean isMaximizable() {
return frame.isMaximizable();
}
protected AquaUIPainter.State getButtonState(int buttonType, boolean isEnabled) {
AquaInternalFrameUI ui = (AquaInternalFrameUI)frame.getUI();
int buttonPressedIndex = ui.getWhichButtonPressed();
boolean overButton = ui.getMouseOverPressedButton();
boolean rollover = ui.getRollover();
boolean frameSelected = frame.isSelected() || widget == AquaUIPainter.TitleBarWidget.UTILITY_WINDOW;
boolean isActive = AquaFocusHandler.isActive(frame) && (rollover || frameSelected);
return getState(buttonPressedIndex == buttonType && overButton, rollover, isActive, isEnabled);
}
protected AquaUIPainter.State getState(boolean pressed, boolean rollover, boolean active, boolean enabled) {
if (!enabled) return AquaUIPainter.State.DISABLED;
if (!active) return AquaUIPainter.State.INACTIVE;
if (pressed) return AquaUIPainter.State.PRESSED;
if (rollover) return AquaUIPainter.State.ROLLOVER;
return AquaUIPainter.State.ACTIVE;
}
protected void paintTitleContents(@NotNull Graphics g, @NotNull Color textColor) {
TitleBarLayout layout = getTitleBarLayout();
Rectangle titleBounds = layout.titleBounds;
if (titleBounds == null) {
return;
}
Font f = g.getFont();
g.setFont(frame.getFont());
// Center text vertically.
FontMetrics fm = g.getFontMetrics();
int baseline = titleBounds.y + (titleBounds.height + fm.getAscent() - fm.getLeading() - fm.getDescent()) / 2;
int iconWidth = getIconWidth();
if (iconWidth > 0) {
iconWidth += sAfterIconPad;
}
int availTextWidth = titleBounds.width - iconWidth;
String text = frame.getTitle();
int totalTextWidth = 0;
int startXPosition = titleBounds.x;
boolean wasTextShortened = false;
// shorten the string to fit in the
if ((text != null) && !(text.equals(""))) {
totalTextWidth = SwingUtilities.computeStringWidth(fm, text);
String clipString = "\u2026";
if (totalTextWidth > availTextWidth) {
wasTextShortened = true;
totalTextWidth = SwingUtilities.computeStringWidth(fm, clipString);
int nChars;
for (nChars = 0; nChars < text.length(); nChars++) {
int nextCharWidth = fm.charWidth(text.charAt(nChars));
if ((totalTextWidth + nextCharWidth) > availTextWidth) {
break;
}
totalTextWidth += nextCharWidth;
}
text = text.substring(0, nChars) + clipString;
}
if (!wasTextShortened) {
// center it!
startXPosition = (titleBounds.width - (totalTextWidth + iconWidth)) / 2;
if (startXPosition < titleBounds.x) {
startXPosition = titleBounds.x;
}
}
g.setColor(textColor);
JavaSupport.drawString(frame, (Graphics2D) g, text, startXPosition + iconWidth, baseline);
g.setFont(f);
}
int iconYPosition = titleBounds.y + (titleBounds.height - getIconHeight()) / 2;
paintTitleIcon(g, startXPosition, iconYPosition);
}
protected void paintTitleIcon(Graphics g, int x, int y) {
Icon icon = frame.getFrameIcon();
if (icon == null) {
icon = UIManager.getIcon("InternalFrame.icon");
}
if (icon == null) {
return;
}
// Resize to 16x16 if necessary.
if (icon instanceof ImageIcon && (icon.getIconWidth() > sMaxIconWidth || icon.getIconHeight() > sMaxIconHeight)) {
Image img = ((ImageIcon)icon).getImage();
((ImageIcon)icon).setImage(img.getScaledInstance(sMaxIconWidth, sMaxIconHeight, Image.SCALE_SMOOTH));
}
icon.paintIcon(frame, g, x, y);
}
protected int getIconWidth() {
int width = 0;
Icon icon = frame.getFrameIcon();
if (icon == null) {
icon = UIManager.getIcon("InternalFrame.icon");
}
if (icon != null) {
// Resize to 16x16 if necessary.
width = Math.min(icon.getIconWidth(), sMaxIconWidth);
}
return width;
}
protected int getIconHeight() {
int height = 0;
Icon icon = frame.getFrameIcon();
if (icon == null) {
icon = UIManager.getIcon("InternalFrame.icon");
}
if (icon != null) {
// Resize to 16x16 if necessary.
height = Math.min(icon.getIconHeight(), sMaxIconHeight);
}
return height;
}
protected TitleBarConfiguration.ResizeAction getResizeAction(JInternalFrame frame) {
AquaInternalFrameUI ui = (AquaInternalFrameUI) frame.getUI();
if (frame.isMaximizable() && !ui.getOption()) {
return frame.isMaximum() ? TitleBarConfiguration.ResizeAction.FULL_SCREEN_EXIT : TitleBarConfiguration.ResizeAction.FULL_SCREEN_ENTER;
} else {
return TitleBarConfiguration.ResizeAction.ZOOM_ENTER;
}
}
// defaults to false
protected boolean isDirty() {
Object dirty = frame.getClientProperty("windowModified");
return Boolean.TRUE.equals(dirty);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy