From 51ec6af6ea78525a3a5244fbf23dba77c2395754 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Thu, 29 Jan 2015 16:02:21 -0500 Subject: [PATCH] more work on header/toolbar/etc --- app/src/processing/app/Editor.java | 23 +-- app/src/processing/app/EditorToolbar.java | 164 ++++++++++++++++++++-- app/src/processing/app/Mode.java | 36 +++++ app/src/processing/app/Toolkit.java | 63 +++++++-- build/shared/lib/theme.txt | 33 +++-- java/theme/theme.txt | 2 + 6 files changed, 279 insertions(+), 42 deletions(-) diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 8f03f37db..1d2632769 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -38,6 +38,8 @@ import java.util.List; import java.util.Timer; import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; import javax.swing.event.*; import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneUI; @@ -172,7 +174,9 @@ public abstract class Editor extends JFrame implements RunnerListener { buildMenuBar(); - backgroundGradient = Toolkit.getLibImage("vertical-gradient.png"); + /* + //backgroundGradient = Toolkit.getLibImage("vertical-gradient.png"); + backgroundGradient = mode.getGradient("editor", 400, 400); JPanel contentPain = new JPanel() { @Override public void paintComponent(Graphics g) { @@ -183,8 +187,10 @@ public abstract class Editor extends JFrame implements RunnerListener { // g.fillRect(0, 0, dim.width, dim.height); } }; + */ //contentPain.setBorder(new EmptyBorder(0, 0, 0, 0)); //System.out.println(contentPain.getBorder()); + JPanel contentPain = new JPanel(); // JFrame f = new JFrame(); // f.setContentPane(new JPanel() { @@ -207,6 +213,7 @@ public abstract class Editor extends JFrame implements RunnerListener { // pain.setOpaque(false); // pain.setLayout(new BorderLayout()); // contentPain.add(pain, BorderLayout.CENTER); +// contentPain.setBorder(new EmptyBorder(10, 10, 10, 10)); Box box = Box.createVerticalBox(); Box upper = Box.createVerticalBox(); @@ -257,8 +264,11 @@ public abstract class Editor extends JFrame implements RunnerListener { // remove any ugly borders added by PLAFs splitPane.setBorder(null); // necessary to let the gradient show through - splitPane.setOpaque(false); - +// splitPane.setOpaque(false); + + // remove an ugly border around anything in a SplitPane !$*&!% + UIManager.getDefaults().put("SplitPane.border", BorderFactory.createEmptyBorder()); + // override the look of the SplitPane so that it's identical across OSes splitPane.setUI(new BasicSplitPaneUI() { public BasicSplitPaneDivider createDefaultDivider() { return new BasicSplitPaneDivider(this) { @@ -266,12 +276,8 @@ public abstract class Editor extends JFrame implements RunnerListener { final Color dotColor = mode.getColor("divider.dot.color"); //new Color(80, 80, 80); int dotSize = mode.getInteger("divider.dot.diameter"); //3; - //public void setBorder(Border b) { } - @Override public void paint(Graphics g) { - //Graphics2D g2 = Toolkit.prepareGraphics(g); - //Toolkit.prepareGraphics(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); @@ -284,10 +290,7 @@ public abstract class Editor extends JFrame implements RunnerListener { g.setColor(dotColor); int x = w/2 - dotSize/2; int y = h/2 - dotSize/2; - //g2.fillOval(x, y, width, height); g.fillOval(x, y, dotSize, dotSize); - - super.paint(g); } }; } diff --git a/app/src/processing/app/EditorToolbar.java b/app/src/processing/app/EditorToolbar.java index 62add31bc..8a2e8ee72 100644 --- a/app/src/processing/app/EditorToolbar.java +++ b/app/src/processing/app/EditorToolbar.java @@ -23,34 +23,54 @@ package processing.app; -import java.awt.Component; +import java.awt.*; import java.awt.event.ActionEvent; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; +import java.util.Arrays; import javax.swing.Box; import javax.swing.JLabel; import javax.swing.JPanel; +import processing.core.PApplet; + /** * Run/Stop button plus Mode selection */ abstract public class EditorToolbar extends JPanel { + static final int HIGH = 80; + protected Editor editor; protected Base base; protected Mode mode; protected EditorButton runButton; protected EditorButton stopButton; + protected EditorButton currentButton; protected Box box; protected JLabel label; +// int GRADIENT_TOP = 192; +// int GRADIENT_BOTTOM = 246; + protected Image backgroundGradient; + protected Image reverseGradient; + public EditorToolbar(Editor editor) { this.editor = editor; base = editor.getBase(); mode = editor.getMode(); + //setOpaque(false); + //gradient = createGradient(); + //System.out.println(gradient); + + backgroundGradient = mode.getGradient("header", 400, HIGH); + reverseGradient = mode.getGradient("reversed", 100, EditorButton.DIM); + runButton = new EditorButton(mode, "/lib/toolbar/run", Language.text("toolbar.run"), @@ -74,16 +94,28 @@ abstract public class EditorToolbar extends JPanel { box = Box.createHorizontalBox(); box.add(runButton); - box.add(label = new JLabel()); - box.add(Box.createHorizontalGlue()); + label = new JLabel(); + label.setFont(mode.getFont("toolbar.sketch.font")); + label.setForeground(mode.getColor("toolbar.sketch.color")); + box.add(label); + currentButton = runButton; + + box.add(Box.createHorizontalGlue()); Component items = createModeButtons(); if (items != null) { box.add(items); } - box.add(createModeSelector()); - - add(box); + ModeSelector ms = new ModeSelector(); + box.add(ms); + add(box); + } + + + public void paintComponent(Graphics g) { +// super.paintComponent(g); + Dimension size = getSize(); + g.drawImage(backgroundGradient, 0, 0, size.width, size.height, this); } @@ -92,16 +124,19 @@ abstract public class EditorToolbar extends JPanel { } - public Component createModeSelector() { - return null; - } - +// public Component createModeSelector() { +// return new ModeSelector(); +// } + protected void swapButton(EditorButton replacement) { - box.remove(0); - box.add(replacement, 0); - box.revalidate(); - box.repaint(); // may be needed + if (currentButton != replacement) { + box.remove(0); + box.add(replacement, 0); + box.revalidate(); + box.repaint(); // may be needed + currentButton = replacement; + } } @@ -129,8 +164,109 @@ abstract public class EditorToolbar extends JPanel { abstract public void handleRun(); + abstract public void handleStop(); + + + class ModeSelector extends JPanel { + Image offscreen; + int width, height; + + String title; + Font titleFont; + Color titleColor; + int titleAscent; + int titleWidth; + + final int MODE_GAP_WIDTH = 13; + final int ARROW_GAP_WIDTH = 6; + final int ARROW_WIDTH = 8; + final int ARROW_TOP = 21; + final int ARROW_BOTTOM = 29; + + int[] triangleX = new int[3]; + int[] triangleY = new int[] { ARROW_TOP, ARROW_TOP, ARROW_BOTTOM }; + + + @SuppressWarnings("deprecation") + public ModeSelector() { + title = mode.getTitle(); //.toUpperCase(); + titleFont = mode.getFont("mode.title.font"); + titleColor = mode.getColor("mode.title.color"); + + // getGraphics() is null and no offscreen yet + titleWidth = getToolkit().getFontMetrics(titleFont).stringWidth(title); + +// setOpaque(false); + } + + @Override + public void paintComponent(Graphics screen) { +// Toolkit.debugOpacity(this); + + Dimension size = getSize(); + width = 0; + if (width != size.width || height != size.height) { + if (Toolkit.highResDisplay()) { + offscreen = createImage(size.width*2, size.height*2); + } else { + offscreen = createImage(size.width, size.height); + } + width = size.width; + height = size.height; + } + + Graphics g = offscreen.getGraphics(); + /*Graphics2D g2 =*/ Toolkit.prepareGraphics(g); + //Toolkit.clearGraphics(g, width, height); +// g.clearRect(0, 0, width, height); +// g.setColor(Color.GREEN); +// g.fillRect(0, 0, width, height); + + g.setFont(titleFont); + if (titleAscent == 0) { + titleAscent = (int) Toolkit.getAscent(g); //metrics.getAscent(); + } + FontMetrics metrics = g.getFontMetrics(); + titleWidth = metrics.stringWidth(title); + + g.drawImage(reverseGradient, 0, 0, width, height, this); + + g.setColor(titleColor); + g.drawString(title, MODE_GAP_WIDTH, (height + titleAscent) / 2); + + int x = MODE_GAP_WIDTH + titleWidth + ARROW_GAP_WIDTH; + triangleX[0] = x; + triangleX[1] = x + ARROW_WIDTH; + triangleX[2] = x + ARROW_WIDTH/2; + g.fillPolygon(triangleX, triangleY, 3); + +// screen.clearRect(0, 0, width, height); + screen.drawImage(offscreen, 0, 0, width, height, this); +// screen.setColor(Color.RED); +// screen.drawRect(0, 0, width-1, height-1); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(MODE_GAP_WIDTH + titleWidth + + ARROW_GAP_WIDTH + ARROW_WIDTH + MODE_GAP_WIDTH, + EditorButton.DIM); + } + + @Override + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + @Override + public Dimension getMaximumSize() { + return getPreferredSize(); + } + } } + + //public abstract class EditorToolbar extends JComponent implements MouseInputListener, KeyListener { // // /** Width of each toolbar button. */ diff --git a/app/src/processing/app/Mode.java b/app/src/processing/app/Mode.java index 4655ee720..de3329d7b 100644 --- a/app/src/processing/app/Mode.java +++ b/app/src/processing/app/Mode.java @@ -25,6 +25,8 @@ package processing.app; import java.awt.*; import java.awt.event.*; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; import java.io.*; import java.util.*; @@ -40,6 +42,7 @@ import processing.app.contrib.ContributionType; import processing.app.contrib.ExamplesContribution; import processing.app.syntax.*; import processing.core.PApplet; +import processing.core.PConstants; public abstract class Mode { @@ -1342,6 +1345,39 @@ public abstract class Mode { // return new SyntaxStyle(color, italic, bold); return new SyntaxStyle(color, bold); } + + + public Image getGradient(String attribute, int wide, int high) { + int top = getColor(attribute + ".gradient.top").getRGB(); + int bot = getColor(attribute + ".gradient.bottom").getRGB(); + +// float r1 = (top >> 16) & 0xff; +// float g1 = (top >> 8) & 0xff; +// float b1 = top & 0xff; +// float r2 = (bot >> 16) & 0xff; +// float g2 = (bot >> 8) & 0xff; +// float b2 = bot & 0xff; + + BufferedImage outgoing = + new BufferedImage(wide, high, BufferedImage.TYPE_INT_RGB); + int[] row = new int[wide]; + WritableRaster wr = outgoing.getRaster(); + for (int i = 0; i < high; i++) { +// Arrays.fill(row, (255 - (i + GRADIENT_TOP)) << 24); +// int r = (int) PApplet.map(i, 0, high-1, r1, r2); + int rgb = PApplet.lerpColor(top, bot, i / (float)(high-1), PConstants.RGB); + Arrays.fill(row, rgb); +// System.out.println(PApplet.hex(row[0])); + wr.setDataElements(0, i, wide, 1, row); + } +// Graphics g = outgoing.getGraphics(); +// for (int i = 0; i < steps; i++) { +// g.setColor(new Color(1, 1, 1, 255 - (i + GRADIENT_TOP))); +// //g.fillRect(0, i, EditorButton.DIM, 10); +// g.drawLine(0, i, EditorButton.DIM, i); +// } + return outgoing; + } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . diff --git a/app/src/processing/app/Toolkit.java b/app/src/processing/app/Toolkit.java index 961c0bfb7..76daff4d0 100644 --- a/app/src/processing/app/Toolkit.java +++ b/app/src/processing/app/Toolkit.java @@ -21,6 +21,7 @@ package processing.app; +import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; @@ -503,8 +504,8 @@ public class Toolkit { /** * Handles scaling for high-res displays, also sets text anti-aliasing - * options. Moved to a utility function because it's used in several classes. - * @param g + * options to be far less ugly than the defaults. + * Moved to a utility function because it's used in several classes. * @return a Graphics2D object, as a bit o sugar */ static public Graphics2D prepareGraphics(Graphics g) { @@ -514,12 +515,46 @@ public class Toolkit { // scale everything 2x, will be scaled down when drawn to the screen g2.scale(2, 2); } + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); + return g2; } + + +// /** +// * Prepare and offscreen image that's sized for this Component, 1x or 2x +// * depending on whether this is a retina display or not. +// * @param comp +// * @param image +// * @return +// */ +// static public Image prepareOffscreen(Component comp, Image image) { +// Dimension size = comp.getSize(); +// Image offscreen = image; +// if (image == null || +// image.getWidth(null) != size.width || +// image.getHeight(null) != size.height) { +// if (Toolkit.highResDisplay()) { +// offscreen = comp.createImage(size.width*2, size.height*2); +// } else { +// offscreen = comp.createImage(size.width, size.height); +// } +// } +// return offscreen; +// } + + + static final Color CLEAR_COLOR = new Color(0, true); + + static public void clearGraphics(Graphics g, int width, int height) { + g.setColor(CLEAR_COLOR); + g.fillRect(0, 0, width, height); + } // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -741,10 +776,16 @@ public class Toolkit { File fontFile = new File(System.getProperty("java.home"), "lib/fonts/" + filename); if (!fontFile.exists()) { // if we're debugging from Eclipse, grab it from the work folder (user.dir is /app) - //fontFile = new File(System.getProperty("user.dir"), "../build/shared/lib/fonts/" + filename); + fontFile = new File(System.getProperty("user.dir"), "../build/shared/lib/fonts/" + filename); + } + if (!fontFile.exists()) { // if we're debugging the new Java Mode from Eclipse, paths are different fontFile = new File(System.getProperty("user.dir"), "../../shared/lib/fonts/" + filename); } + if (!fontFile.exists()) { + Base.showError("Font Sadness", "Could not find required fonts", null); + } + BufferedInputStream input = new BufferedInputStream(new FileInputStream(fontFile)); Font font = Font.createFont(Font.TRUETYPE_FONT, input); input.close(); @@ -752,6 +793,10 @@ public class Toolkit { } + /** + * Synthesized replacement for FontMetrics.getAscent(), which is dreadfully + * inaccurate and inconsistent across platforms. + */ static double getAscent(Graphics g) { //, Font font) { Graphics2D g2 = (Graphics2D) g; FontRenderContext frc = g2.getFontRenderContext(); @@ -762,13 +807,15 @@ public class Toolkit { /** Do not use or rely upon presence of this method: not approved as final API. */ static public void debugOpacity(Component comp) { - Component parent = comp.getParent(); - while (parent != null) { + //Component parent = comp.getParent(); + while (comp != null) { //EditorConsole.systemOut.println("parent is " + parent + " " + parent.isOpaque()); - EditorConsole.systemOut.println(parent.getClass().getName() + " " + (parent.isOpaque() ? "OPAQUE" : "")); - parent = parent.getParent(); + //EditorConsole.systemOut.println(parent.getClass().getName() + " " + (parent.isOpaque() ? "OPAQUE" : "")); + System.out.println(comp.getClass().getName() + " " + (comp.isOpaque() ? "OPAQUE" : "")); + comp = comp.getParent(); } - EditorConsole.systemOut.println(); + //EditorConsole.systemOut.println(); + System.out.println(); } diff --git a/build/shared/lib/theme.txt b/build/shared/lib/theme.txt index 70fc94ced..95cbecf33 100644 --- a/build/shared/lib/theme.txt +++ b/build/shared/lib/theme.txt @@ -10,14 +10,13 @@ status.font = processing.sans,plain,14 # TABS # Settings for the tab area at the top. -header.bgcolor = #000000 -#header.hide.image = false # in preferences.txt -header.hide.color = #0E1B25 +header.text.font = processing.sans,plain,15 header.text.selected.color = #000000 header.text.unselected.color = #ffffff -header.text.font = processing.sans,plain,14 -header.tab.selected.color = #a2afba -header.tab.unselected.color = #2a4159 +header.tab.selected.color = #ffffff +header.tab.unselected.color = #657d87 +header.gradient.top = #132638 +header.gradient.bottom = #122535 # CONSOLE # The font is handled by preferences, so its size/etc are modifiable. @@ -31,6 +30,10 @@ buttons.bgcolor = #000000 #buttons.hide.color = #0E1B25 #buttons.bgimage = true +# for the debug and mode buttons +reversed.gradient.top = #10212f +reversed.gradient.bottom = #122637 + ## size of divider between editing area and the console #divider.size = 0 ## the larger divider on windows is ugly with the little arrows @@ -44,13 +47,20 @@ divider.dot.diameter = 3 divider.dot.color = #505050 # TOOLBAR BUTTON TEXT -buttons.status.font = processing.sans,bold,13 -buttons.status.color = #ffffff +#buttons.status.font = processing.sans,bold,13 +#buttons.status.color = #ffffff +toolbar.sketch.font = processing.sans,plain,24 +toolbar.sketch.color = #ffffff +toolbar.gradient.top = #142c40 +toolbar.gradient.bottom = #132638 # MODE SELECTOR -mode.button.font = processing.sans,bold,13 +mode.title.font = processing.sans,bold,15 +mode.title.color = #ffffff # outline color of the mode button -mode.button.color = #ffffff +#mode.button.color = #ffffff +#mode.button.gap = 13 +#mode.arrow.width # LINE STATUS # The editor line number status bar at the bottom of the screen @@ -66,6 +76,9 @@ linestatus.height = 20 editor.fgcolor = #000000 editor.bgcolor = #ffffff +editor.gradient.top = #122535 +editor.gradient.bottom = #020408 + # highlight for the current line editor.linehighlight.color=#e2e2e2 # highlight for the current line diff --git a/java/theme/theme.txt b/java/theme/theme.txt index 1a6179f5c..048ba97f9 100644 --- a/java/theme/theme.txt +++ b/java/theme/theme.txt @@ -32,3 +32,5 @@ editor.warningmarkercolor = #ffc30e errorbar.errorcolor = #ed2630 errorbar.warningcolor = #ffc30e errorbar.backgroundcolor = #2c343d + +# color of error tickmark on right-hand side in comp: #c20102