[#2224] Add hex color input field in appearance panel

This commit is contained in:
SiboVG 2024-08-28 12:11:49 +02:00
parent 29598ef1b1
commit 0153c89ddc
3 changed files with 107 additions and 22 deletions

View File

@ -1058,6 +1058,8 @@ AppearanceCfg.lbl.ttip.separateInsideOutside = Use a separate appearance for out
AppearanceCfg.lbl.ttip.separateLeftSideRightSide = Use a separate appearance for left and right side(s) AppearanceCfg.lbl.ttip.separateLeftSideRightSide = Use a separate appearance for left and right side(s)
AppearanceCfg.lbl.AppearanceEdges = Appearance for edges: AppearanceCfg.lbl.AppearanceEdges = Appearance for edges:
AppearanceCfg.lbl.ttip.AppearanceEdges = Select the appearance that should be used for the edges AppearanceCfg.lbl.ttip.AppearanceEdges = Select the appearance that should be used for the edges
AppearanceCfg.placeholder.HexColor = Hex color
AppearanceCfg.ttip.HexColor = Hexadecimal representation of the color
! Texture Wrap Modes ! Texture Wrap Modes
TextureWrap.Repeat = Repeat TextureWrap.Repeat = Repeat

View File

@ -17,6 +17,7 @@ import javax.swing.JCheckBox;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JSeparator; import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -27,6 +28,7 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import info.openrocket.core.util.Invalidatable; import info.openrocket.core.util.Invalidatable;
import info.openrocket.swing.gui.widgets.PlaceholderTextField;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import info.openrocket.core.appearance.Appearance; import info.openrocket.core.appearance.Appearance;
import info.openrocket.core.appearance.AppearanceBuilder; import info.openrocket.core.appearance.AppearanceBuilder;
@ -126,7 +128,7 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
} }
} }
private class ColorActionListener implements ActionListener { private abstract class ColorActionListener {
private final String valueName; private final String valueName;
private final Object o; private final Object o;
@ -139,23 +141,35 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
Changes the color of the selected component to <color> Changes the color of the selected component to <color>
@param color: color to change the component to @param color: color to change the component to
*/ */
private void changeComponentColor(Color color) { protected void setComponentColor(Color color) {
try { try {
final Method setMethod = o.getClass().getMethod( final Method setMethod = o.getClass().getMethod("set" + valueName, ORColor.class);
"set" + valueName, ORColor.class);
if (color == null) if (color == null)
return; return;
try { try {
setMethod.invoke(o, ColorConversion setMethod.invoke(o, ColorConversion.fromAwtColor(color));
.fromAwtColor(color));
} catch (Throwable e1) { } catch (Throwable e1) {
Application.getExceptionHandler() Application.getExceptionHandler().handleErrorCondition(e1);
.handleErrorCondition(e1);
} }
} catch (Throwable e1) { } catch (Throwable e1) {
Application.getExceptionHandler().handleErrorCondition(e1); Application.getExceptionHandler().handleErrorCondition(e1);
} }
}
protected ORColor getComponentColor() {
try {
final Method getMethod = o.getClass().getMethod("get" + valueName);
return (ORColor) getMethod.invoke(o);
} catch (Throwable e1) {
Application.getExceptionHandler().handleErrorCondition(e1);
return null;
}
}
}
private class ColorButtonActionListener extends ColorActionListener implements ActionListener {
ColorButtonActionListener(final Object o, final String valueName) {
super(o, valueName);
} }
/** /**
@ -166,10 +180,7 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
@Override @Override
public void actionPerformed(ActionEvent colorClickEvent) { public void actionPerformed(ActionEvent colorClickEvent) {
try { try {
final Method getMethod = o.getClass().getMethod( ORColor c = getComponentColor();
"get" + valueName);
ORColor c = (ORColor) getMethod
.invoke(o);
Color awtColor = ColorConversion.toAwtColor(c); Color awtColor = ColorConversion.toAwtColor(c);
colorChooser.setColor(awtColor); colorChooser.setColor(awtColor);
@ -180,7 +191,7 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
ChangeListener changeListener = new ChangeListener() { ChangeListener changeListener = new ChangeListener() {
public void stateChanged(ChangeEvent changeEvent) { public void stateChanged(ChangeEvent changeEvent) {
Color selected = colorChooser.getColor(); Color selected = colorChooser.getColor();
changeComponentColor(selected); setComponentColor(selected);
} }
}; };
model.addChangeListener(changeListener); model.addChangeListener(changeListener);
@ -191,14 +202,14 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
new ActionListener() { new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent okEvent) { public void actionPerformed(ActionEvent okEvent) {
changeComponentColor(colorChooser.getColor()); setComponentColor(colorChooser.getColor());
// Unbind listener to avoid the current component's appearance to change with other components // Unbind listener to avoid the current component's appearance to change with other components
model.removeChangeListener(changeListener); model.removeChangeListener(changeListener);
} }
}, new ActionListener() { }, new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent cancelEvent) { public void actionPerformed(ActionEvent cancelEvent) {
changeComponentColor(awtColor); setComponentColor(awtColor);
// Unbind listener to avoid the current component's appearance to change with other components // Unbind listener to avoid the current component's appearance to change with other components
model.removeChangeListener(changeListener); model.removeChangeListener(changeListener);
} }
@ -210,6 +221,28 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
} }
} }
private class ColorHexActionListener extends ColorActionListener implements ActionListener {
public ColorHexActionListener(final Object o, final String valueName) {
super(o, valueName);
}
@Override
public void actionPerformed(ActionEvent e) {
JTextField field = (JTextField) e.getSource();
String hex = field.getText();
try {
ORColor color = ColorConversion.fromHexColor(hex);
if (color == null) {
field.setText(ColorConversion.toHexColor(getComponentColor()));
return;
}
setComponentColor(ColorConversion.toAwtColor(color));
} catch (IllegalArgumentException ex) {
field.setText(ColorConversion.toHexColor(getComponentColor()));
}
}
}
/** /**
* Appearance panel for the appearance of a rocket component. * Appearance panel for the appearance of a rocket component.
* @param document current document * @param document current document
@ -271,10 +304,17 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
} }
final JButton figureColorButton = new JButton( final JButton figureColorButton = new JButton(
new ColorIcon(figureColor)); new ColorIcon(figureColor));
PlaceholderTextField figureColorHexField = new PlaceholderTextField(7);
figureColorHexField.setPlaceholder(trans.get("AppearanceCfg.placeholder.HexColor"));
figureColorHexField.setToolTipText(trans.get("AppearanceCfg.ttip.HexColor"));
figureColorHexField.setText(ColorConversion.toHexColor(c.getColor()));
ab.addChangeListener(new StateChangeListener() { ab.addChangeListener(new StateChangeListener() {
@Override @Override
public void stateChanged(EventObject e) { figureColorButton.setIcon(new ColorIcon(c.getColor())); } public void stateChanged(EventObject e) {
figureColorButton.setIcon(new ColorIcon(c.getColor()));
figureColorHexField.setText(ColorConversion.toHexColor(c.getColor()));
}
}); });
c.addChangeListener(new StateChangeListener() { c.addChangeListener(new StateChangeListener() {
@ -285,11 +325,12 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
col = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass()); col = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass());
} }
figureColorButton.setIcon(new ColorIcon(col)); figureColorButton.setIcon(new ColorIcon(col));
figureColorHexField.setText(ColorConversion.toHexColor(col));
} }
}); });
figureColorButton figureColorButton.addActionListener(new ColorButtonActionListener(c, "Color"));
.addActionListener(new ColorActionListener(c, "Color")); figureColorHexField.addActionListener(new ColorHexActionListener(c, "Color"));
BooleanModel fDefault = new BooleanModel(c.getColor() == null); BooleanModel fDefault = new BooleanModel(c.getColor() == null);
register(fDefault); register(fDefault);
@ -342,9 +383,15 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
{// Figure Color {// Figure Color
add(new JLabel(trans.get("RocketCompCfg.lbl.Componentcolor"))); add(new JLabel(trans.get("RocketCompCfg.lbl.Componentcolor")));
JPanel colorPanel = new JPanel(new MigLayout("ins 0"));
colorPanel.add(figureColorButton);
figureColorHexField.setColumns(7);
colorPanel.add(figureColorHexField);
fDefault.addEnableComponent(figureColorButton, false); fDefault.addEnableComponent(figureColorButton, false);
add(figureColorButton); fDefault.addEnableComponent(figureColorHexField, false);
add(colorPanel, "growx");
order.add(figureColorButton); order.add(figureColorButton);
order.add(figureColorHexField);
} }
order.add(saveAsDefault); order.add(saveAsDefault);
@ -532,8 +579,13 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
}); });
JButton colorButton = new JButton(new ColorIcon(builder.getPaint())); JButton colorButton = new JButton(new ColorIcon(builder.getPaint()));
PlaceholderTextField colorHexField = new PlaceholderTextField(7);
colorHexField.setPlaceholder(trans.get("AppearanceCfg.placeholder.HexColor"));
colorHexField.setToolTipText(trans.get("AppearanceCfg.ttip.HexColor"));
colorHexField.setText(ColorConversion.toHexColor(builder.getPaint()));
colorButton.addActionListener(new ColorActionListener(builder, "Paint")); colorButton.addActionListener(new ColorButtonActionListener(builder, "Paint"));
colorHexField.addActionListener(new ColorHexActionListener(builder, "Paint"));
// Texture Header Row // Texture Header Row
panel.add(new StyledLabel(trans.get("AppearanceCfg.lbl.Appearance"), panel.add(new StyledLabel(trans.get("AppearanceCfg.lbl.Appearance"),
@ -630,9 +682,15 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
// TODO: move the separate columns in two separate panels instead of adding them in a zig-zag way // TODO: move the separate columns in two separate panels instead of adding them in a zig-zag way
// Color // Color
panel.add(new JLabel(trans.get("AppearanceCfg.lbl.color.Color"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.color.Color")));
JPanel colorPanel = new JPanel(new MigLayout("ins 0"));
colorPanel.add(colorButton);
colorHexField.setColumns(7);
colorPanel.add(colorHexField);
mDefault.addEnableComponent(colorButton, false); mDefault.addEnableComponent(colorButton, false);
panel.add(colorButton); mDefault.addEnableComponent(colorHexField, false);
panel.add(colorPanel, "growx");
order.add(colorButton); order.add(colorButton);
order.add(colorHexField);
// Scale // Scale
panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.scale")), "gapleft para"); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.scale")), "gapleft para");
@ -674,7 +732,7 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
panel.add(spinShine, "split 3, w 60"); panel.add(spinShine, "split 3, w 60");
panel.add(unitShine); panel.add(unitShine);
panel.add(slideShine, "w 100lp"); panel.add(slideShine, "w 100lp");
order.add(order.indexOf(colorButton) + 1, ((SpinnerEditor) spinShine.getEditor()).getTextField()); order.add(order.indexOf(colorHexField) + 1, ((SpinnerEditor) spinShine.getEditor()).getTextField());
// Offset // Offset
panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset")), "gapleft para"); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset")), "gapleft para");
@ -749,6 +807,7 @@ public class AppearancePanel extends JPanel implements Invalidatable, Invalidati
@Override @Override
public void stateChanged(EventObject e) { public void stateChanged(EventObject e) {
colorButton.setIcon(new ColorIcon(builder.getPaint())); colorButton.setIcon(new ColorIcon(builder.getPaint()));
colorHexField.setText(ColorConversion.toHexColor(builder.getPaint()));
if (lastOpacity != builder.getOpacity()) { if (lastOpacity != builder.getOpacity()) {
opacityModel.stateChanged(null); opacityModel.stateChanged(null);
lastOpacity = builder.getOpacity(); lastOpacity = builder.getOpacity();

View File

@ -27,4 +27,28 @@ public class ColorConversion {
String hexColor = String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue()); String hexColor = String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue());
return String.format("<font color=\"%s\">%s</font>", hexColor, content); return String.format("<font color=\"%s\">%s</font>", hexColor, content);
} }
public static String toHexColor(ORColor c) {
if (c == null) {
return null;
}
return String.format("#%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue()).toUpperCase();
}
public static ORColor fromHexColor(String hexColor) {
if (hexColor == null || hexColor.isBlank()) {
return null;
}
if (hexColor.startsWith("#")) {
hexColor = hexColor.substring(1);
}
hexColor = hexColor.trim();
if (hexColor.length() != 6 || hexColor.matches("^#[0-9A-Fa-f]{6}$")) {
throw new IllegalArgumentException("Invalid hex color: " + hexColor);
}
int red = Integer.parseInt(hexColor.substring(0, 2), 16);
int green = Integer.parseInt(hexColor.substring(2, 4), 16);
int blue = Integer.parseInt(hexColor.substring(4, 6), 16);
return new ORColor(red, green, blue);
}
} }