Resize tooltip (#986)

* resize tooltip

* Fix blank board area rendering

* Fixes

* translations

* Add length

* Update update.xml
This commit is contained in:
Bane Stojkovic
2025-12-31 15:07:22 +01:00
committed by GitHub
parent 61b3c63bbf
commit 2de3def32e
22 changed files with 521 additions and 104 deletions
@@ -41,6 +41,7 @@ import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.IConfigListener;
import org.diylc.appframework.miscutils.IConfigurationManager;
import org.diylc.appframework.miscutils.Utils;
import org.diylc.core.measures.ResizeDimensions;
import org.diylc.swingframework.ruler.IRulerListener;
import org.diylc.swingframework.ruler.Ruler.InchSubdivision;
import org.diylc.swingframework.ruler.RulerScrollPane;
@@ -53,7 +54,6 @@ import org.diylc.common.BadPositionException;
import org.diylc.common.EventType;
import org.diylc.common.IPlugIn;
import org.diylc.common.IPlugInPort;
import org.diylc.core.IDIYComponent;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.presenter.Presenter;
@@ -68,6 +68,8 @@ public class CanvasPlugin implements IPlugIn{
private RulerScrollPane scrollPane;
private CanvasPanel canvasPanel;
private ComponentPopupMenu popupMenu;
private ResizeInfoTooltip resizeInfoTooltip;
private boolean resizingInProgress = false;
private IPlugInPort plugInPort;
private ISwingUI swingUI;
@@ -193,6 +195,7 @@ public class CanvasPlugin implements IPlugIn{
public CanvasPanel getCanvasPanel() {
if (canvasPanel == null) {
canvasPanel = new CanvasPanel(plugInPort, configManager);
resizeInfoTooltip = new ResizeInfoTooltip(canvasPanel, configManager);
canvasPanel.addMouseListener(new MouseAdapter() {
private MouseEvent pressedEvent;
@@ -277,6 +280,15 @@ public class CanvasPlugin implements IPlugIn{
e.isShiftDown(), e.isAltDown()));
plugInPort.mouseMoved(e.getPoint(), Utils.isMac() ? e.isMetaDown() : e.isControlDown(),
e.isShiftDown(), e.isAltDown());
// Update tooltip position if resizing is in progress and tooltip is enabled, otherwise hide it
if (resizeInfoTooltip != null) {
if (resizingInProgress && configManager.readBoolean(IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true)) {
resizeInfoTooltip.updatePosition(e.getPoint());
} else {
resizeInfoTooltip.hideTooltip();
}
}
}
});
@@ -505,11 +517,13 @@ public class CanvasPlugin implements IPlugIn{
canvasPanel.repaint();
// Refresh selection bounds after we're done with painting to ensure we have traced the
// component areas
boolean panToSelection = params.length > 0 && Boolean.TRUE.equals(params[0]);
this.resizingInProgress = params.length > 1 && Boolean.TRUE.equals(params[1]);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (params.length > 0 && Boolean.TRUE.equals(params[0])) {
if (panToSelection) {
Rectangle2D selectionBounds = plugInPort.getSelectionBounds(true);
if (selectionBounds != null) {
Rectangle visibleRect = scrollPane.getVisibleRect();
@@ -519,8 +533,28 @@ public class CanvasPlugin implements IPlugIn{
);
}
}
if (configManager.readBoolean(IPlugInPort.SHOW_RULERS_KEY, true))
if (configManager.readBoolean(IPlugInPort.SHOW_RULERS_KEY, true)) {
scrollPane.setSelectionRectangle(plugInPort.getSelectionBounds(true));
}
if (CanvasPlugin.this.resizingInProgress && getCanvasPanel().getMousePosition() != null) {
// Only show tooltip if enabled in configuration
if (configManager.readBoolean(IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true)) {
ResizeDimensions resizeDimensions = plugInPort.getResizeDimensions();
if (resizeInfoTooltip != null) {
resizeInfoTooltip.update(resizeDimensions, getCanvasPanel().getMousePosition());
}
} else {
// Hide tooltip if disabled
if (resizeInfoTooltip != null) {
resizeInfoTooltip.hideTooltip();
}
}
} else {
// Hide tooltip when not resizing
if (resizeInfoTooltip != null) {
resizeInfoTooltip.hideTooltip();
}
}
}
});
break;
@@ -563,25 +597,4 @@ public class CanvasPlugin implements IPlugIn{
double extraSpace = CanvasPlugin.this.plugInPort.getExtraSpace();
getScrollPane().setZeroLocation(new Point2D.Double(extraSpace, extraSpace));
}
class LockAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private IDIYComponent<?> component;
private boolean locked;
public LockAction(IDIYComponent<?> component, boolean locked) {
super();
this.locked = locked;
this.component = component;
putValue(AbstractAction.NAME, component.getName());
}
@Override
public void actionPerformed(ActionEvent e) {
plugInPort.lockComponent(component, locked);
}
}
}
@@ -0,0 +1,225 @@
/*
*
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2025 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.diylc.swing.plugins.canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Window;
import java.text.DecimalFormat;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.diylc.appframework.miscutils.IConfigurationManager;
import org.diylc.core.measures.ResizeDimensions;
import org.diylc.core.measures.Size;
/**
* A lightweight tooltip window that displays resize dimensions and tracks the mouse cursor.
*/
public class ResizeInfoTooltip {
private static final int OFFSET_X = 16;
private static final int OFFSET_Y = 16;
private static final DecimalFormat SIZE_FORMAT = new DecimalFormat("0.00");
private JWindow tooltipWindow;
private JLabel tooltipLabel;
private Component parentComponent;
private ResizeDimensions currentDimensions;
public ResizeInfoTooltip(Component parentComponent, IConfigurationManager<?> configManager) {
this.parentComponent = parentComponent;
initializeTooltip();
}
private void initializeTooltip() {
Window parentWindow = SwingUtilities.getWindowAncestor(parentComponent);
if (parentWindow == null) {
return;
}
tooltipWindow = new JWindow(parentWindow);
tooltipLabel = new JLabel() {
private static final long serialVersionUID = 1L;
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paintComponent(g2d);
g2d.dispose();
}
};
// Use system tooltip font and colors
Font tooltipFont = UIManager.getFont("ToolTip.font");
if (tooltipFont != null) {
tooltipLabel.setFont(tooltipFont);
}
Color tooltipBackground = UIManager.getColor("ToolTip.background");
Color tooltipForeground = UIManager.getColor("ToolTip.foreground");
Color tooltipBorderColor = UIManager.getColor("ToolTip.border");
if (tooltipBorderColor == null) {
tooltipBorderColor = Color.GRAY;
}
tooltipLabel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(tooltipBorderColor, 1),
BorderFactory.createEmptyBorder(4, 6, 4, 6)));
tooltipLabel.setOpaque(true);
tooltipLabel.setBackground(tooltipBackground != null ? tooltipBackground : new Color(255, 255, 220));
tooltipLabel.setForeground(tooltipForeground != null ? tooltipForeground : Color.BLACK);
tooltipWindow.add(tooltipLabel);
tooltipWindow.setFocusableWindowState(false);
tooltipWindow.setAlwaysOnTop(true);
}
/**
* Updates the tooltip with new dimensions and mouse position.
*/
public void update(ResizeDimensions dimensions, Point mousePosition) {
this.currentDimensions = dimensions;
if (dimensions == null || mousePosition == null) {
hideTooltip();
return;
}
if (tooltipWindow == null) {
initializeTooltip();
if (tooltipWindow == null) {
return;
}
}
// Format the dimensions
String text = formatDimensions(dimensions);
tooltipLabel.setText(text);
tooltipLabel.setPreferredSize(null); // Reset to calculate preferred size
tooltipWindow.pack();
// Position the tooltip near the mouse cursor
updateTooltipPosition(mousePosition);
tooltipWindow.setVisible(true);
}
/**
* Updates the tooltip position based on the current mouse position.
*/
public void updatePosition(Point mousePosition) {
if (currentDimensions != null && mousePosition != null && tooltipWindow != null && tooltipWindow.isVisible()) {
updateTooltipPosition(mousePosition);
}
}
private void updateTooltipPosition(Point mousePosition) {
if (tooltipWindow == null || parentComponent == null) {
return;
}
// Convert mouse position to screen coordinates
Point screenPoint = new Point(mousePosition);
SwingUtilities.convertPointToScreen(screenPoint, parentComponent);
// Position tooltip 16 pixels down and right from cursor
int x = screenPoint.x + OFFSET_X;
int y = screenPoint.y + OFFSET_Y;
// Ensure tooltip stays on screen
Dimension tooltipSize = tooltipWindow.getSize();
if (tooltipSize == null) {
tooltipWindow.pack();
tooltipSize = tooltipWindow.getSize();
}
if (tooltipSize != null) {
java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
// Adjust if tooltip would go off screen
if (x + tooltipSize.width > screenSize.width) {
x = screenPoint.x - tooltipSize.width - OFFSET_X;
}
if (y + tooltipSize.height > screenSize.height) {
y = screenPoint.y - tooltipSize.height - OFFSET_Y;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
}
tooltipWindow.setLocation(x, y);
}
private String formatDimensions(ResizeDimensions dimensions) {
StringBuilder sb = new StringBuilder("<html>");
// Format width
Size width = dimensions.width();
String widthUnit = width.getUnit().toString();
sb.append(String.format("W: %s %s", SIZE_FORMAT.format(width.getValue()), widthUnit));
// Format height
Size height = dimensions.height();
String heightUnit = height.getUnit().toString();
sb.append(String.format("<br>H: %s %s", SIZE_FORMAT.format(height.getValue()), heightUnit));
// Format length if available
Size length = dimensions.length();
if (length != null) {
String lengthUnit = length.getUnit().toString();
sb.append(String.format("<br>L: %s %s", SIZE_FORMAT.format(length.getValue()), lengthUnit));
}
sb.append("</html>");
return sb.toString();
}
/**
* Hides the tooltip.
*/
public void hideTooltip() {
if (tooltipWindow != null) {
tooltipWindow.setVisible(false);
}
currentDimensions = null;
}
/**
* Disposes the tooltip window.
*/
public void dispose() {
if (tooltipWindow != null) {
tooltipWindow.dispose();
tooltipWindow = null;
}
}
}
@@ -158,6 +158,10 @@ public class ConfigPlugin implements IPlugIn {
ActionFactory.getInstance().createConfigAction(plugInPort, "Show Rulers", IPlugInPort.SHOW_RULERS_KEY, true),
CONFIG_MENU);
swingUI.injectMenuAction(
ActionFactory.getInstance().createConfigAction(plugInPort, "Show Resize Dimensions",
IPlugInPort.SHOW_RESIZE_DIMENSIONS_TOOLTIP_KEY, true), CONFIG_MENU);
swingUI.injectSubmenu(SNAP_MENU, IconLoader.GraphEdgeDirected.getIcon(), CONFIG_MENU);
swingUI.injectMenuAction(
ActionFactory.getInstance().createToggleAction(IPlugInPort.SNAP_TO_NONE, IPlugInPort.SNAP_TO_KEY, SNAP_MENU, IPlugInPort.SNAP_TO_DEFAULT),
@@ -363,6 +363,7 @@ Shaft Up|Achse nach oben
Shape|Form
Shapes|Formen
Show Grid|Raster anzeigen
Show Resize Dimensions|Größenänderungsdimensionen anzeigen
Show Rulers|Lineale anzeigen
Shroud|Wanne
Shrouded|Abgeschirmt
@@ -352,6 +352,7 @@ Shape
Shapes
Show Bracket
Show Grid
Show Resize Dimensions
Show Rulers
Shroud
Shrouded
@@ -351,6 +351,7 @@ Shaft Up|Eje Arriba
Shape|Forma
Shapes|Formas
Show Grid|Mostrar Cuadrícula
Show Resize Dimensions|Mostrar Dimensiones de Redimensionamiento
Show Rulers|Mostrar Reglas
Shroud|Envoltura
Shrouded|Con Envoltura
@@ -348,6 +348,7 @@ Shaft Up|Tige vers le haut
Shape|Forme
Shapes|Formes
Show Grid|Afficher la grille
Show Resize Dimensions|Afficher les dimensions de redimensionnement
Show Rulers|Afficher les règles
Shroud|Enveloppe
Shrouded|Enveloppé
@@ -363,6 +363,7 @@ Shaft Up|Shaft Up
Shape|forma
Shapes|forme
Show Grid|Mostra griglia
Show Resize Dimensions|Mostra dimensioni ridimensionamento
Show Rulers|Mostra righelli
Shroud|Copri
Shrouded|Coperto
@@ -363,6 +363,7 @@ Shaft Up|As omhoog
Shape|Vorm
Shapes|Vormen
Show Grid|Raster weergeven
Show Resize Dimensions|Grootte-aanpassingsafmetingen weergeven
Show Rulers|Linialen weergeven
Shroud|Beschermrand
Shrouded|Afgeschermd
@@ -351,6 +351,7 @@ Shaft Up|Jogar para cima
Shape|Forma
Shapes|Formas
Show Grid|Mostrar Grid
Show Resize Dimensions|Mostrar Dimensões de Redimensionamento
Show Rulers|Mostrar Réguas
Shroud|Envoltura
Shrouded|Envolto
@@ -344,6 +344,7 @@ Shaft Up|Osovina Gore
Shape|Oblik
Shapes|Oblici
Show Grid|Prikaz Rešetke
Show Resize Dimensions|Prikaz Dimenzija Promene Veličine
Show Rulers|Prikaz Lenjira
Shroud|Kućište
Shrouded|Sa Kućištem
@@ -352,6 +352,7 @@ Shape|आकार
Shapes|आकार
Show Bracket|ब्रैकेट दिखाएं
Show Grid|ग्रिड दिखाएं
Show Resize Dimensions|आकार बदलने के आयाम दिखाएं
Show Rulers|रूलर दिखाएं
Shroud|श्राउड
Shrouded|श्राउडेड
@@ -352,6 +352,7 @@ Shape|形状
Shapes|形状
Show Bracket|显示支架
Show Grid|显示网格
Show Resize Dimensions|显示调整大小尺寸
Show Rulers|显示标尺
Shroud|护罩
Shrouded|带护罩