diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java
index 68f94d44b..bfea138a5 100644
--- a/app/src/processing/app/Preferences.java
+++ b/app/src/processing/app/Preferences.java
@@ -28,6 +28,11 @@ import java.io.*;
import java.util.*;
import javax.swing.*;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.*;
import processing.core.*;
@@ -107,6 +112,8 @@ public class Preferences {
int wide, high;
JTextField sketchbookLocationField;
+ JTextField presentColor;
+ JTextField presentColorHex;
JCheckBox editorAntialiasBox;
JCheckBox deletePreviousBox;
JCheckBox whinyBox;
@@ -368,6 +375,80 @@ public class Preferences {
fontSizeField.setSelectedItem(Preferences.getFont("editor.font.size"));
top += d.height + GUI_BETWEEN;
+ // Here I am
+
+ Container colorBox = Box.createHorizontalBox();
+
+ label = new JLabel("Background color when Presenting: ");
+ colorBox.add(label);
+
+ final String colorTip = "" +
+ "Select the background color used when using Present.
" +
+ "Present is used to present a sketch in full-screen,
" +
+ "accessible from the Sketch menu.";
+ label.setToolTipText(colorTip);
+
+ presentColor = new JTextField(" ");
+ presentColor.setOpaque(true);
+ presentColor.setEnabled(false);
+ presentColor.setBorder(BorderFactory.createLineBorder(Color.BLACK));
+ presentColor.setBackground(Preferences.getColor("run.present.bgcolor"));
+ colorBox.add(presentColor);
+
+ colorBox.add(Box.createHorizontalStrut(GUI_SMALL));
+
+ label = new JLabel("#");
+ colorBox.add(label);
+
+ presentColorHex = new JTextField(6);
+ presentColorHex.setText(Preferences.get("run.present.bgcolor").substring(1));
+ presentColorHex.setEditable(false);
+ presentColorHex.setBackground(Color.WHITE);
+
+ final CustomColorChooser c = new CustomColorChooser(Preferences.getColor("run.present.bgcolor"),
+ presentColorHex, presentColor);
+
+ presentColorHex.addMouseListener(new MouseListener() {
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ c.show();
+ }
+ });
+
+ presentColorHex.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ c.hide();
+ }
+ });
+
+ colorBox.add(presentColorHex);
+
+ pain.add(colorBox);
+ d = colorBox.getPreferredSize();
+ colorBox.setBounds(left, top, d.width, d.height);
+
+ top += d.height + GUI_BETWEEN;
+
+ // till here
// [ ] Use smooth text in editor window
@@ -634,7 +715,643 @@ public class Preferences {
});
}
+ private class CustomColorChooser {
+ int hue, saturation, brightness; // range 360, 100, 100
+ int red, green, blue; // range 256, 256, 256
+
+ ColorRange range;
+ ColorSlider slider;
+
+ JTextField hueField, saturationField, brightnessField;
+ JTextField redField, greenField, blueField;
+
+ JTextField hexField;
+
+ JPanel colorPanel;
+ DocumentListener colorListener;
+
+ JDialog window;
+
+ JTextField externalHex, externalColor;
+
+
+ public CustomColorChooser(Color initialColor, final JTextField externalHex,
+ final JTextField externalColor) {
+ Frame owner = null;
+ this.externalHex = externalHex;
+ this.externalColor = externalColor;
+ window = new JDialog(owner, "Choose the background color for Present", true);
+ window.getContentPane().setLayout(new BorderLayout());
+
+ Box box = Box.createHorizontalBox();
+ box.setBorder(new EmptyBorder(12, 12, 12, 12));
+
+ range = new ColorRange();
+ range.init();
+ Box rangeBox = new Box(BoxLayout.Y_AXIS);
+ rangeBox.setAlignmentY(0);
+ rangeBox.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
+ rangeBox.add(range);
+ box.add(rangeBox);
+ box.add(Box.createHorizontalStrut(10));
+
+ slider = new ColorSlider();
+ slider.init();
+ Box sliderBox = new Box(BoxLayout.Y_AXIS);
+ sliderBox.setAlignmentY(0);
+ sliderBox.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
+ sliderBox.add(slider);
+ box.add(sliderBox);
+ box.add(Box.createHorizontalStrut(10));
+
+ box.add(createColorFields(PROMPT_OK, new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ externalHex.setText(getHexColor().substring(1));
+ externalColor.setBackground(getColor());
+ hide();
+ }
+ }, PROMPT_CANCEL, new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ hide();
+ }
+ }));
+
+ box.add(Box.createHorizontalStrut(10));
+
+ window.getContentPane().add(box, BorderLayout.CENTER);
+
+ window.pack();
+ window.setResizable(false);
+
+ window.setLocationRelativeTo(null);
+
+ window.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+ window.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ hide();
+ }
+ });
+ Toolkit.registerWindowCloseKeys(window.getRootPane(), new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ hide();
+ }
+ });
+
+ Toolkit.setIcon(window);
+
+ colorListener = new ColorListener();
+ hueField.getDocument().addDocumentListener(colorListener);
+ saturationField.getDocument().addDocumentListener(colorListener);
+ brightnessField.getDocument().addDocumentListener(colorListener);
+ redField.getDocument().addDocumentListener(colorListener);
+ greenField.getDocument().addDocumentListener(colorListener);
+ blueField.getDocument().addDocumentListener(colorListener);
+ hexField.getDocument().addDocumentListener(colorListener);
+
+ setColor(initialColor);
+ }
+
+
+ public void show() {
+ window.setVisible(true);
+ window.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+ }
+
+
+ public void hide() {
+ window.setVisible(false);
+ setColor(new Color(Integer.parseInt(externalHex.getText().substring(0, 2), 16),
+ Integer.parseInt(externalHex.getText().substring(2, 4), 16),
+ Integer.parseInt(externalHex.getText().substring(4, 6), 16)));
+ }
+
+
+ public Color getColor() {
+ return new Color(red, green, blue);
+ }
+
+
+ public void setColor(Color color) {
+ updateRGB(color.getRGB());
+ }
+
+
+ public String getHexColor() {
+ return "#" + PApplet.hex(red, 2) + PApplet.hex(green, 2) + PApplet.hex(blue, 2);
+ }
+
+
+ public class ColorListener implements DocumentListener {
+
+ public void changedUpdate(DocumentEvent e) {
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ }
+
+
+ boolean updating;
+
+ public void insertUpdate(DocumentEvent e) {
+ if (updating) return; // don't update forever recursively
+ updating = true;
+
+ Document doc = e.getDocument();
+ if (doc == hueField.getDocument()) {
+ hue = bounded(hue, hueField, 359);
+ updateRGB();
+ updateHex();
+
+ } else if (doc == saturationField.getDocument()) {
+ saturation = bounded(saturation, saturationField, 99);
+ updateRGB();
+ updateHex();
+
+ } else if (doc == brightnessField.getDocument()) {
+ brightness = bounded(brightness, brightnessField, 99);
+ updateRGB();
+ updateHex();
+
+ } else if (doc == redField.getDocument()) {
+ red = bounded(red, redField, 255);
+ updateHSB();
+ updateHex();
+
+ } else if (doc == greenField.getDocument()) {
+ green = bounded(green, greenField, 255);
+ updateHSB();
+ updateHex();
+
+ } else if (doc == blueField.getDocument()) {
+ blue = bounded(blue, blueField, 255);
+ updateHSB();
+ updateHex();
+
+ } else if (doc == hexField.getDocument()) {
+ String str = hexField.getText();
+ if (str.startsWith("#")) {
+ str = str.substring(1);
+ }
+ while (str.length() < 6) {
+ str += "0";
+ }
+ if (str.length() > 6) {
+ str = str.substring(0, 6);
+ }
+ updateRGB(Integer.parseInt(str, 16));
+ updateHSB();
+ }
+ range.redraw();
+ slider.redraw();
+ colorPanel.repaint();
+ updating = false;
+ }
+ }
+
+
+ /**
+ * Set the RGB values based on the current HSB values.
+ */
+ protected void updateRGB() {
+ updateRGB(Color.HSBtoRGB(hue / 359f,
+ saturation / 99f,
+ brightness / 99f));
+ }
+
+
+ /**
+ * Set the RGB values based on a calculated ARGB int.
+ * Used by both updateRGB() to set the color from the HSB values,
+ * and by updateHex(), to unpack the hex colors and assign them.
+ */
+ protected void updateRGB(int rgb) {
+ red = (rgb >> 16) & 0xff;
+ green = (rgb >> 8) & 0xff;
+ blue = rgb & 0xff;
+
+ redField.setText(String.valueOf(red));
+ greenField.setText(String.valueOf(green));
+ blueField.setText(String.valueOf(blue));
+ }
+
+
+ /**
+ * Set the HSB values based on the current RGB values.
+ */
+ protected void updateHSB() {
+ float hsb[] = new float[3];
+ Color.RGBtoHSB(red, green, blue, hsb);
+
+ hue = (int) (hsb[0] * 359.0f);
+ saturation = (int) (hsb[1] * 99.0f);
+ brightness = (int) (hsb[2] * 99.0f);
+
+ hueField.setText(String.valueOf(hue));
+ saturationField.setText(String.valueOf(saturation));
+ brightnessField.setText(String.valueOf(brightness));
+ }
+
+
+ protected void updateHex() {
+ hexField.setText(getHexColor());
+ }
+
+
+ /**
+ * Get the bounded value for a specific range. If the value is outside
+ * the max, you can't edit right away, so just act as if it's already
+ * been bounded and return the bounded value, then fire an event to set
+ * it to the value that was just returned.
+ */
+ protected int bounded(int current, final JTextField field, final int max) {
+ String text = field.getText();
+ if (text.length() == 0) {
+ return 0;
+ }
+ try {
+ int value = Integer.parseInt(text);
+ if (value > max) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ field.setText(String.valueOf(max));
+ }
+ });
+ return max;
+ }
+ return value;
+
+ } catch (NumberFormatException e) {
+ return current; // should not be reachable
+ }
+ }
+
+
+ protected Container createColorFields(String buttonName, ActionListener buttonListener,
+ String buttonName2, ActionListener buttonListener2) {
+ Box box = Box.createVerticalBox();
+ box.setAlignmentY(0);
+
+ final int GAP = Base.isWindows() ? 5 : 0;
+ final int BETWEEN = Base.isWindows() ? 8 : 6; //10;
+
+ Box row;
+
+ row = Box.createHorizontalBox();
+ if (Base.isMacOS()) {
+ row.add(Box.createHorizontalStrut(17));
+ } else {
+ row.add(createFixedLabel(""));
+ }
+ // Can't just set the bg color of the panel because it also tints the bevel
+ // (on OS X), which looks odd. So instead we override paintComponent().
+ colorPanel = new JPanel() {
+ public void paintComponent(Graphics g) {
+ g.setColor(new Color(red, green, blue));
+ Dimension size = getSize();
+ g.fillRect(0, 0, size.width, size.height);
+ }
+ };
+ colorPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
+ Dimension dim = new Dimension(70, 25);
+ colorPanel.setMinimumSize(dim);
+ colorPanel.setMaximumSize(dim);
+ colorPanel.setPreferredSize(dim);
+ row.add(colorPanel);
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(BETWEEN));
+
+ row = Box.createHorizontalBox();
+ row.add(createFixedLabel("H"));
+ row.add(hueField = new NumberField(4, false));
+ row.add(new JLabel(" \u00B0")); // degree symbol
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(GAP));
+
+ row = Box.createHorizontalBox();
+ row.add(createFixedLabel("S"));
+ row.add(saturationField = new NumberField(4, false));
+ row.add(new JLabel(" %"));
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(GAP));
+
+ row = Box.createHorizontalBox();
+ row.add(createFixedLabel("B"));
+ row.add(brightnessField = new NumberField(4, false));
+ row.add(new JLabel(" %"));
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(BETWEEN));
+
+ row = Box.createHorizontalBox();
+ row.add(createFixedLabel("R"));
+ row.add(redField = new NumberField(4, false));
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(GAP));
+
+ row = Box.createHorizontalBox();
+ row.add(createFixedLabel("G"));
+ row.add(greenField = new NumberField(4, false));
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(GAP));
+
+ row = Box.createHorizontalBox();
+ row.add(createFixedLabel("B"));
+ row.add(blueField = new NumberField(4, false));
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(BETWEEN));
+
+ row = Box.createHorizontalBox();
+ row.add(createFixedLabel(""));
+ // Windows needs extra space, OS X and Linux do not
+ // Mac OS X needs 6 because #CCCCCC is quite wide
+ final int hexCount = Base.isWindows() ? 7 : 6;
+ row.add(hexField = new NumberField(hexCount, true));
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+ box.add(Box.createVerticalStrut(GAP));
+
+ row = Box.createHorizontalBox();
+ if (Base.isMacOS()) {
+ row.add(Box.createHorizontalStrut(11));
+ } else {
+ row.add(createFixedLabel(""));
+ }
+ JButton button = new JButton(buttonName);
+ button.addActionListener(buttonListener);
+
+ row.add(button);
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+
+ row = Box.createHorizontalBox();
+ if (Base.isMacOS()) {
+ row.add(Box.createHorizontalStrut(11));
+ } else {
+ row.add(createFixedLabel(""));
+ }
+ JButton button2 = new JButton(buttonName2);
+ button2.addActionListener(buttonListener2);
+
+ row.add(button2);
+ row.add(Box.createHorizontalGlue());
+ box.add(row);
+
+ box.add(Box.createVerticalGlue());
+ return box;
+ }
+
+
+ int labelH;
+
+ /**
+ * return a label of a fixed width
+ */
+ protected JLabel createFixedLabel(String title) {
+ JLabel label = new JLabel(title);
+ if (labelH == 0) {
+ labelH = label.getPreferredSize().height;
+ }
+ Dimension dim = new Dimension(15, labelH);
+ label.setPreferredSize(dim);
+ label.setMinimumSize(dim);
+ label.setMaximumSize(dim);
+ return label;
+ }
+
+
+ public class ColorRange extends PApplet {
+
+ static final int WIDE = 256;
+ static final int HIGH = 256;
+
+ int lastX, lastY;
+
+
+ public void setup() {
+ size(WIDE, HIGH);
+ noLoop();
+
+ colorMode(HSB, 360, 256, 256);
+ noFill();
+ rectMode(CENTER);
+
+ loadPixels();
+ }
+
+ public void draw() {
+
+ if ((width != WIDE) || (height < HIGH)) {
+ return;
+ }
+
+ int index = 0;
+ for (int j = 0; j < 256; j++) {
+ for (int i = 0; i < 256; i++) {
+ pixels[index++] = color(hue, i, 255 - j);
+ }
+ }
+
+ updatePixels();
+ stroke((brightness > 50) ? 0 : 255);
+ rect(lastX, lastY, 9, 9);
+ }
+
+ public void mousePressed() {
+ updateMouse();
+ }
+
+ public void mouseDragged() {
+ updateMouse();
+ }
+
+ public void updateMouse() {
+ if ((mouseX >= 0) && (mouseX < 256) &&
+ (mouseY >= 0) && (mouseY < 256)) {
+ int nsaturation = (int) (100 * (mouseX / 255.0f));
+ int nbrightness = 100 - ((int) (100 * (mouseY / 255.0f)));
+ saturationField.setText(String.valueOf(nsaturation));
+ brightnessField.setText(String.valueOf(nbrightness));
+
+ lastX = mouseX;
+ lastY = mouseY;
+ }
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(WIDE, HIGH);
+ }
+
+ public Dimension getMinimumSize() {
+ return new Dimension(WIDE, HIGH);
+ }
+
+ public Dimension getMaximumSize() {
+ return new Dimension(WIDE, HIGH);
+ }
+
+ public void keyPressed() {
+ if (key == ESC) {
+ CustomColorChooser.this.hide();
+ // don't quit out of processing
+ // http://dev.processing.org/bugs/show_bug.cgi?id=1006
+ key = 0;
+ }
+ }
+ }
+
+
+ public class ColorSlider extends PApplet {
+
+ static final int WIDE = 20;
+ static final int HIGH = 256;
+
+ public void setup() {
+ size(WIDE, HIGH); //, P3D);
+ colorMode(HSB, 255, 100, 100);
+ noLoop();
+ loadPixels();
+ }
+
+ public void draw() {
+ if ((width != WIDE) || (height < HIGH)) {
+ return;
+ }
+
+ int index = 0;
+ int sel = 255 - (int) (255 * (hue / 359f));
+ for (int j = 0; j < 256; j++) {
+ int c = color(255 - j, 100, 100);
+ if (j == sel) c = 0xFF000000;
+ for (int i = 0; i < WIDE; i++) {
+ g.pixels[index++] = c;
+ }
+ }
+ updatePixels();
+ }
+
+ public void mousePressed() {
+ updateMouse();
+ }
+
+ public void mouseDragged() {
+ updateMouse();
+ }
+
+ public void updateMouse() {
+ if ((mouseX >= 0) && (mouseX < 256) &&
+ (mouseY >= 0) && (mouseY < 256)) {
+ int nhue = 359 - (int) (359 * (mouseY / 255.0f));
+ hueField.setText(String.valueOf(nhue));
+ }
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(WIDE, HIGH);
+ }
+
+ public Dimension getMinimumSize() {
+ return new Dimension(WIDE, HIGH);
+ }
+
+ public Dimension getMaximumSize() {
+ return new Dimension(WIDE, HIGH);
+ }
+
+ public void keyPressed() {
+ if (key == ESC) {
+ CustomColorChooser.this.hide();
+ // don't quit out of processing
+ // http://dev.processing.org/bugs/show_bug.cgi?id=1006
+ key = 0;
+ }
+ }
+ }
+
+
+ /**
+ * Extension of JTextField that only allows numbers
+ */
+ class NumberField extends JTextField {
+
+ public boolean allowHex;
+
+ public NumberField(int cols, boolean allowHex) {
+ super(cols);
+ this.allowHex = allowHex;
+ }
+
+ protected Document createDefaultModel() {
+ return new NumberDocument(this);
+ }
+
+ public Dimension getPreferredSize() {
+ if (!allowHex) {
+ return new Dimension(45, super.getPreferredSize().height);
+ }
+ return super.getPreferredSize();
+ }
+
+ public Dimension getMinimumSize() {
+ return getPreferredSize();
+ }
+
+ public Dimension getMaximumSize() {
+ return getPreferredSize();
+ }
+ }
+
+
+ /**
+ * Document model to go with JTextField that only allows numbers.
+ */
+ class NumberDocument extends PlainDocument {
+
+ NumberField parentField;
+
+ public NumberDocument(NumberField parentField) {
+ this.parentField = parentField;
+ }
+
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+
+ if (str == null) return;
+
+ char chars[] = str.toCharArray();
+ int charCount = 0;
+ // remove any non-digit chars
+ for (int i = 0; i < chars.length; i++) {
+ boolean ok = Character.isDigit(chars[i]);
+ if (parentField.allowHex) {
+ if ((chars[i] >= 'A') && (chars[i] <= 'F')) ok = true;
+ if ((chars[i] >= 'a') && (chars[i] <= 'f')) ok = true;
+ if ((offs == 0) && (i == 0) && (chars[i] == '#')) ok = true;
+ }
+ if (ok) {
+ if (charCount != i) { // shift if necessary
+ chars[charCount] = chars[i];
+ }
+ charCount++;
+ }
+ }
+ super.insertString(offs, new String(chars, 0, charCount), a);
+ }
+ }
+
+ }
+
+
public void sketchbookCallback(File file) {
if (file != null) {
sketchbookLocationField.setText(file.getAbsolutePath());