diff --git a/diylc/diylc-core/src/main/java/org/diylc/common/Percentage.java b/diylc/diylc-core/src/main/java/org/diylc/common/Percentage.java index 57370cfc..a5ff5e67 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/common/Percentage.java +++ b/diylc/diylc-core/src/main/java/org/diylc/common/Percentage.java @@ -22,22 +22,31 @@ package org.diylc.common; import java.io.Serializable; +import java.math.BigDecimal; public class Percentage implements Serializable, Cloneable { private static final long serialVersionUID = 1L; - private int value; + private BigDecimal value; - public Percentage(int value) { + public Percentage(BigDecimal value) { this.value = value; } - public int getValue() { + public Percentage(double value) { + this.value = BigDecimal.valueOf(value); + } + + public Percentage(int value) { + this.value = BigDecimal.valueOf((long) value); + } + + public BigDecimal getValue() { return value; } - public void setValue(int value) { + public void setValue(BigDecimal value) { this.value = value; } @@ -45,7 +54,7 @@ public class Percentage implements Serializable, Cloneable { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + value; + result = prime * result + ((value == null) ? 0 : value.stripTrailingZeros().hashCode()); return result; } @@ -58,14 +67,19 @@ public class Percentage implements Serializable, Cloneable { if (getClass() != obj.getClass()) return false; Percentage other = (Percentage) obj; - if (value != other.value) + if (value == null) { + if (other.value != null) + return false; + } else if (value.compareTo(other.value) != 0) return false; return true; } @Override public String toString() { - return value + "%"; + if (value == null) return "null%"; + BigDecimal stripped = value.stripTrailingZeros(); + return String.format("%s%%", stripped.toPlainString()); } @Override diff --git a/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java b/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java index f1646bbc..2985a22a 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java +++ b/diylc/diylc-core/src/main/java/org/diylc/components/AbstractTransparentComponent.java @@ -59,19 +59,19 @@ public abstract class AbstractTransparentComponent extends AbstractComponent< public Composite applyAlpha(Graphics2D g2d, ComponentState componentState) { Composite oldComposite = g2d.getComposite(); - int alpha = (OUTLINE_MODE_WHILE_DRAGGING && componentState == ComponentState.DRAGGING) ? 0 : - getAlpha().getValue(); + double alpha = (OUTLINE_MODE_WHILE_DRAGGING && componentState == ComponentState.DRAGGING) ? 0 : + getAlpha().getValue().doubleValue(); if (alpha < MAX_ALPHA) { - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha / 100f)); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(alpha / 100d))); } return oldComposite; } public Composite applyAlpha(Graphics2D g2d) { Composite oldComposite = g2d.getComposite(); - int alpha = getAlpha().getValue(); + double alpha = getAlpha().getValue().doubleValue(); if (alpha < MAX_ALPHA) { - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha / 100f)); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(alpha / 100d))); } return oldComposite; } diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java index 9297ee4a..790fc4fe 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/DrawingManager.java @@ -594,7 +594,7 @@ public class DrawingManager { try { g2dWrapper.transform(mirrorTx); if (Boolean.TRUE.equals(board.getUndersideTransparency())) { - g2dWrapper.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, MIRROR_ALPHA.getValue() / 100f)); + g2dWrapper.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)(MIRROR_ALPHA.getValue().doubleValue() / 100d))); } component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); } catch (Exception e) { diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index 401bd1b7..868c2331 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -369,6 +369,10 @@ 2026-08-22 00:00:00.000 CET + + IMPROVEMENT + Percentage values such as transparency Alpha, image Scale, etc, can have decimal values and not just integers + IMPROVEMENT Made announcement bubbles and dialogs that contain links clickable diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/misc/Image.java b/diylc/diylc-library/src/main/java/org/diylc/components/misc/Image.java index 48772129..cf20ccab 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/misc/Image.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/misc/Image.java @@ -122,7 +122,7 @@ public class Image extends AbstractTransparentComponent { double x; double y; if (getSizingMode() == ImageSizingMode.Scale) { - scaleX = scaleY = 1d * getScale().getValue() / 100d; + scaleX = scaleY = 1d * getScale().getValue().doubleValue() / 100d; x = point.getX(); y = point.getY(); } else { @@ -299,7 +299,7 @@ public class Image extends AbstractTransparentComponent { if (getSizingMode() == ImageSizingMode.TwoPoints) { finalSecondPoint = getControlPoint(1); } else { - double scale = 1d * getScale().getValue() / 100d; + double scale = 1d * getScale().getValue().doubleValue() / 100d; ImageIcon iconImage = getImage(); double w = (iconImage != null && iconImage.getIconWidth() > 0) ? iconImage.getIconWidth() * scale : 0; double h = (iconImage != null && iconImage.getIconHeight() > 0) ? iconImage.getIconHeight() * scale : 0; diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/misc/SVGImage.java b/diylc/diylc-library/src/main/java/org/diylc/components/misc/SVGImage.java index ad94ef83..b11b6bb1 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/misc/SVGImage.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/misc/SVGImage.java @@ -105,7 +105,7 @@ public class SVGImage extends AbstractTransparentComponent { double x; double y; if (getSizingMode() == ImageSizingMode.Scale) { - scaleX = scaleY = 1d * getScale().getValue() / 100d; + scaleX = scaleY = 1d * getScale().getValue().doubleValue() / 100d; x = point.getX(); y = point.getY(); } else { @@ -290,7 +290,7 @@ public class SVGImage extends AbstractTransparentComponent { if (getSizingMode() == ImageSizingMode.TwoPoints) { finalSecondPoint = getControlPoint(1); } else { - double scale = 1d * getScale().getValue() / 100d; + double scale = 1d * getScale().getValue().doubleValue() / 100d; SVGDiagram diagram = getSvgDiagram(); double w = (diagram != null && diagram.getWidth() > 0) ? diagram.getWidth() * scale : 0; double h = (diagram != null && diagram.getHeight() > 0) ? diagram.getHeight() * scale : 0; diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/PercentageEditor.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/PercentageEditor.java index 361f0406..ef909529 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/PercentageEditor.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/editor/PercentageEditor.java @@ -25,6 +25,7 @@ import java.awt.Color; import java.awt.FlowLayout; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; +import java.math.BigDecimal; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; @@ -46,6 +47,7 @@ public class PercentageEditor extends JPanel { private int minValue = 0; private int maxValue = 100; + private boolean isUpdating = false; public PercentageEditor(final PropertyWrapper property) { super(); @@ -69,39 +71,71 @@ public class PercentageEditor extends JPanel { valueField.setEnabled(false); } - slider.setMinimum(minValue); - slider.setMaximum(maxValue); + slider.setMinimum(minValue * 100); + slider.setMaximum(maxValue * 100); + slider.setMinorTickSpacing(100); + + // Initialize values BEFORE adding the listener to avoid overwriting the property with integer + Object val = property.getValue(); + if (val != null) { + BigDecimal bdVal = ((Percentage) val).getValue(); + if (bdVal == null) bdVal = BigDecimal.ZERO; + slider.setValue((int) Math.round(bdVal.doubleValue() * 100)); + valueField.setText(bdVal.stripTrailingZeros().toPlainString()); + } else { + slider.setValue(minValue * 100); + valueField.setText(Integer.toString(minValue)); + } + + slider.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mousePressed(java.awt.event.MouseEvent e) { + slider.setSnapToTicks(true); + } + @Override + public void mouseReleased(java.awt.event.MouseEvent e) { + slider.setSnapToTicks(false); + } + }); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { - property.setChanged(true); - setBackground(oldBg); - slider.setBackground(oldBg); - - property.setValue(new Percentage(slider.getValue())); - valueField.setText(Integer.toString(slider.getValue())); + if (isUpdating) return; + isUpdating = true; + try { + property.setChanged(true); + setBackground(oldBg); + slider.setBackground(oldBg); + BigDecimal bd = BigDecimal.valueOf(slider.getValue()).divide(BigDecimal.valueOf(100)).stripTrailingZeros(); + property.setValue(new Percentage(bd)); + valueField.setText(bd.toPlainString()); + } finally { + isUpdating = false; + } } }); - Object val = property.getValue(); - if (val != null) { - slider.setValue(((Percentage) val).getValue()); - } else { - slider.setValue(minValue); - } - - valueField.setText(Integer.toString(slider.getValue())); - valueField.setColumns(3); + valueField.setColumns(4); valueField.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { try { - int newPosition = (int) Double.parseDouble(valueField.getText()); + double newPosition = Double.parseDouble(valueField.getText()); if (newPosition >= minValue && newPosition <= maxValue) { - slider.setValue(newPosition); + isUpdating = true; + try { + slider.setValue((int) Math.round(newPosition * 100)); + property.setChanged(true); + setBackground(oldBg); + slider.setBackground(oldBg); + // Use string parsing directly to avoid double floating point inaccuracy + property.setValue(new Percentage(new BigDecimal(valueField.getText().trim()))); + } finally { + isUpdating = false; + } } } catch (Exception ex) { }