You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by rw...@apache.org on 2017/12/19 15:28:09 UTC

svn commit: r1818680 - in /pivot/trunk/wtk/src/org/apache/pivot/wtk: NumberRuler.java NumberRulerListener.java Theme.java skin/NumberRulerSkin.java

Author: rwhitcomb
Date: Tue Dec 19 15:28:08 2017
New Revision: 1818680

URL: http://svn.apache.org/viewvc?rev=1818680&view=rev
Log:
PIVOT-1019: Add a NumberRuler component, along with its listener and
skin classes.  There is currently no demo / test for this, and there
remain some changes to make the horizontal drawing complete.

Added:
    pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRuler.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRulerListener.java
    pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/NumberRulerSkin.java
Modified:
    pivot/trunk/wtk/src/org/apache/pivot/wtk/Theme.java

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRuler.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRuler.java?rev=1818680&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRuler.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRuler.java Tue Dec 19 15:28:08 2017
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.wtk;
+
+import org.apache.pivot.util.ListenerList;
+import org.apache.pivot.util.Utils;
+
+/**
+ * A column or row heading component that display a line number or
+ * character column ruler suitable for use with scrolling text controls
+ * ({@link TextArea} or {@link TextPane}).
+ */
+public class NumberRuler extends Component {
+    private static class NumberRulerListenerList
+        extends ListenerList<NumberRulerListener>
+        implements NumberRulerListener {
+        @Override
+        public void orientationChanged(NumberRuler ruler) {
+            forEach(listener -> listener.orientationChanged(ruler));
+        }
+        @Override
+        public void textSizeChanged(NumberRuler ruler, int previousSize) {
+            forEach(listener -> listener.textSizeChanged(ruler, previousSize));
+        }
+    }
+
+    private Orientation orientation = Orientation.VERTICAL;
+    /** Maximum number of digits expected in the numbering. */
+    private int textSize = 5;
+
+    private NumberRulerListenerList rulerListeners = new NumberRulerListenerList();
+
+    public NumberRuler() {
+        installSkin(NumberRuler.class);
+    }
+
+    public Orientation getOrientation() {
+        return orientation;
+    }
+
+    public void setOrientation(Orientation orientation) {
+        Utils.checkNull(orientation, "orientation");
+
+        if (this.orientation != orientation) {
+            this.orientation = orientation;
+            rulerListeners.orientationChanged(this);
+        }
+    }
+
+    public int getTextSize() {
+        return textSize;
+    }
+
+    public void setTextSize(String size) {
+        Utils.checkNullOrEmpty(size, "size");
+
+        int newSize = Integer.parseInt(size);
+        if (newSize <= 0 || newSize > 20) {
+            throw new IllegalArgumentException("Text size must be positive and less or equal to 20.");
+        }
+
+        if (newSize != textSize) {
+            int previousSize = this.textSize;
+            this.textSize = newSize;
+            rulerListeners.textSizeChanged(this, previousSize);
+        }
+    }
+
+    public ListenerList<NumberRulerListener> getRulerListeners() {
+        return rulerListeners;
+    }
+
+}

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRulerListener.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRulerListener.java?rev=1818680&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRulerListener.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/NumberRulerListener.java Tue Dec 19 15:28:08 2017
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.wtk;
+
+/**
+ * Listener for changes to a {@link NumberRuler} that affect size and position.
+ */
+public interface NumberRulerListener {
+    public static class Adapter implements NumberRulerListener {
+        @Override
+        public void orientationChanged(NumberRuler ruler) {
+            // Empty block
+        }
+
+        @Override
+        public void textSizeChanged(NumberRuler ruler, int previousSize) {
+            // Empty block
+        }
+    }
+    public void orientationChanged(NumberRuler ruler);
+
+    public void textSizeChanged(NumberRuler ruler, int previousSize);
+}

Modified: pivot/trunk/wtk/src/org/apache/pivot/wtk/Theme.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/Theme.java?rev=1818680&r1=1818679&r2=1818680&view=diff
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/Theme.java (original)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/Theme.java Tue Dec 19 15:28:08 2017
@@ -35,6 +35,7 @@ import org.apache.pivot.wtk.skin.GridPan
 import org.apache.pivot.wtk.skin.ImageViewSkin;
 import org.apache.pivot.wtk.skin.LabelSkin;
 import org.apache.pivot.wtk.skin.MovieViewSkin;
+import org.apache.pivot.wtk.skin.NumberRulerSkin;
 import org.apache.pivot.wtk.skin.PanelSkin;
 import org.apache.pivot.wtk.skin.RulerSkin;
 import org.apache.pivot.wtk.skin.ScrollPaneSkin;
@@ -92,6 +93,7 @@ public abstract class Theme {
         componentSkinMap.put(ImageView.class, ImageViewSkin.class);
         componentSkinMap.put(Label.class, LabelSkin.class);
         componentSkinMap.put(MovieView.class, MovieViewSkin.class);
+        componentSkinMap.put(NumberRuler.class, NumberRulerSkin.class);
         componentSkinMap.put(Panel.class, PanelSkin.class);
         componentSkinMap.put(Ruler.class, RulerSkin.class);
         componentSkinMap.put(ScrollPane.class, ScrollPaneSkin.class);

Added: pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/NumberRulerSkin.java
URL: http://svn.apache.org/viewvc/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/NumberRulerSkin.java?rev=1818680&view=auto
==============================================================================
--- pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/NumberRulerSkin.java (added)
+++ pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/NumberRulerSkin.java Tue Dec 19 15:28:08 2017
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.wtk.skin;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.LineMetrics;
+import java.awt.geom.Rectangle2D;
+import java.text.StringCharacterIterator;
+import java.util.Arrays;
+
+import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.util.Utils;
+import org.apache.pivot.wtk.Component;
+import org.apache.pivot.wtk.GraphicsUtilities;
+import org.apache.pivot.wtk.NumberRuler;
+import org.apache.pivot.wtk.NumberRulerListener;
+import org.apache.pivot.wtk.Orientation;
+import org.apache.pivot.wtk.Platform;
+import org.apache.pivot.wtk.Theme;
+
+public class NumberRulerSkin extends ComponentSkin implements NumberRulerListener {
+    private Font font;
+    private Color color;
+    private Color backgroundColor;
+    private int padding = 2;
+
+    @Override
+    public void install(Component component) {
+        super.install(component);
+
+        Theme theme = Theme.getTheme();
+        font = theme.getFont();
+        color = theme.getColor(0);
+        backgroundColor = theme.getColor(19);
+
+        NumberRuler ruler = (NumberRuler) component;
+        ruler.getRulerListeners().add(this);
+    }
+
+    @Override
+    public void layout() {
+        // No-op
+    }
+
+    @Override
+    public int getPreferredHeight(int width) {
+        NumberRuler ruler = (NumberRuler) getComponent();
+        Orientation orientation = ruler.getOrientation();
+
+        return (orientation == Orientation.HORIZONTAL) ? 20 : 0;
+    }
+
+    @Override
+    public int getPreferredWidth(int height) {
+        NumberRuler ruler = (NumberRuler) getComponent();
+        Orientation orientation = ruler.getOrientation();
+
+        if (orientation == Orientation.VERTICAL) {
+            int textSize = ruler.getTextSize();
+
+            FontRenderContext fontRenderContext = Platform.getFontRenderContext();
+            char[] digits = new char[textSize];
+            Arrays.fill(digits, '0');
+            String text = new String(digits);
+
+            Rectangle2D stringBounds = font.getStringBounds(text, fontRenderContext);
+            return (int) Math.ceil(stringBounds.getWidth()) + padding;
+        }
+
+        return 0;
+    }
+
+    @Override
+    public void paint(Graphics2D graphics) {
+        int width = getWidth();
+        int height = getHeight();
+        Rectangle clipRect = graphics.getClipBounds();
+
+        NumberRuler ruler = (NumberRuler) getComponent();
+        int textSize = ruler.getTextSize();
+
+        graphics.setColor(backgroundColor);
+        graphics.fillRect(0, 0, width, height);
+
+        graphics.setColor(color);
+
+        Orientation orientation = ruler.getOrientation();
+        Rectangle fullRect = new Rectangle(width, height);
+        Rectangle clippedRect = fullRect.intersection(clipRect);
+
+        switch (orientation) {
+            case HORIZONTAL: {
+                Rectangle lineRect = new Rectangle(0, height - 1, width - 1, 0);
+                Rectangle clippedLineRect = lineRect.intersection(clipRect);
+                graphics.drawLine(clippedLineRect.x, clippedLineRect.y, clippedLineRect.x + clippedLineRect.width, clippedLineRect.y);
+
+                for (int i = 0, n = width / 5 + 1; i < n; i++) {
+                    int x = i * 5;
+
+                    if (i % 4 == 0) {
+                        graphics.drawLine(x, 0, x, height / 2);
+                    } else {
+                        graphics.drawLine(x, 0, x, height / 4);
+                    }
+                }
+                // TODO: put in the numbers every so often
+
+                break;
+            }
+
+            case VERTICAL: {
+                Rectangle lineRect = new Rectangle(width - 1, 0, 0, height - 1);
+                Rectangle clippedLineRect = lineRect.intersection(clipRect);
+                graphics.drawLine(clippedLineRect.x, clippedLineRect.y, clippedLineRect.x, clippedLineRect.y + clippedLineRect.height);
+
+                FontRenderContext fontRenderContext = Platform.getFontRenderContext();
+                LineMetrics lm = font.getLineMetrics("", fontRenderContext);
+                float descent = lm.getDescent();
+                int lineHeight = (int)Math.ceil(lm.getHeight());
+                graphics.setFont(font);
+
+                // Optimize drawing by only starting just above the current clip bounds
+                // down to the bottom (plus one) of the end of the clip bounds.
+                // This is a 100x speed improvement for 500,000 lines.
+                int linesAbove = clipRect.y / lineHeight;
+                int linesBelow = (height - (clipRect.y + clipRect.height)) / lineHeight;
+                int totalLines = height / lineHeight + 1;
+
+                for (int num = 1 + linesAbove, n = totalLines - (linesBelow - 1); num < n; num++) {
+                    float y = (float)(num * lineHeight) - descent;
+
+                    StringCharacterIterator line = new StringCharacterIterator(Integer.toString(num));
+                    GlyphVector glyphVector = font.createGlyphVector(fontRenderContext, line);
+                    Rectangle2D textBounds = glyphVector.getLogicalBounds();
+                    float lineWidth = (float) textBounds.getWidth();
+                    float x = (float)width - (lineWidth + (float)padding);
+                    graphics.drawGlyphVector(glyphVector, x, y);
+                }
+
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void orientationChanged(NumberRuler ruler) {
+        invalidateComponent();
+    }
+
+    @Override
+    public void textSizeChanged(NumberRuler ruler, int previousSize) {
+        invalidateComponent();
+    }
+
+    public Font getFont() {
+        return font;
+    }
+
+    /**
+     * Sets the font used in rendering the Ruler's text.
+     *
+     * @param font The new font to use.
+     */
+    public void setFont(Font font) {
+        Utils.checkNull(font, "font");
+
+        this.font = font;
+        invalidateComponent();
+    }
+
+    /**
+     * Sets the font used in rendering the Ruler's text
+     *
+     * @param font A {@link ComponentSkin#decodeFont(String) font specification}
+     */
+    public final void setFont(String font) {
+        setFont(decodeFont(font));
+    }
+
+    /**
+     * Sets the font used in rendering the Ruler's text
+     *
+     * @param font A dictionary {@link Theme#deriveFont describing a font}
+     */
+    public final void setFont(Dictionary<String, ?> font) {
+        setFont(Theme.deriveFont(font));
+    }
+
+    /**
+     * Returns the foreground color of the text of the ruler.
+     *
+     * @return The foreground (text) color.
+     */
+    public Color getColor() {
+        return color;
+    }
+
+    /**
+     * Sets the foreground color of the text of the ruler.
+     *
+     * @param color The foreground (that is, the text) color.
+     */
+    public void setColor(Color color) {
+        Utils.checkNull(color, "color");
+
+        this.color = color;
+        repaintComponent();
+    }
+
+    /**
+     * Sets the foreground color of the text of the ruler.
+     *
+     * @param color Any of the {@linkplain GraphicsUtilities#decodeColor color
+     * values recognized by Pivot}.
+     */
+    public final void setColor(String color) {
+        setColor(GraphicsUtilities.decodeColor(color, "color"));
+    }
+
+    /**
+     * Returns the background color of the ruler.
+     *
+     * @return The current background color.
+     */
+    public Color getBackgroundColor() {
+        return backgroundColor;
+    }
+
+    /**
+     * Sets the background color of the ruler.
+     *
+     * @param backgroundColor New background color value.
+     */
+    public void setBackgroundColor(Color backgroundColor) {
+        this.backgroundColor = backgroundColor;
+        repaintComponent();
+    }
+
+    /**
+     * Sets the background color of the ruler.
+     *
+     * @param backgroundColor Any of the
+     * {@linkplain GraphicsUtilities#decodeColor color values recognized by
+     * Pivot}.
+     */
+    public final void setBackgroundColor(String backgroundColor) {
+        setBackgroundColor(GraphicsUtilities.decodeColor(backgroundColor, "backgroundColor"));
+    }
+
+}