package org.geogebra.common.euclidian; import org.geogebra.common.awt.GColor; import org.geogebra.common.awt.GDimension; import org.geogebra.common.awt.GFont; import org.geogebra.common.awt.GGraphics2D; import org.geogebra.common.awt.GRectangle; import org.geogebra.common.awt.font.GTextLayout; import org.geogebra.common.euclidian.draw.CanvasDrawable; import org.geogebra.common.factories.AwtFactory; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.geos.GeoButton; import org.geogebra.common.kernel.geos.GeoButton.Observer; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoText; import org.geogebra.common.kernel.geos.TextProperties; import org.geogebra.common.main.App; //import java.awt.Color; /** * Replaces Swing button in DrawButton */ public class MyButton implements Observer { private GeoButton geoButton; private EuclidianView view; private int x, y; private boolean selected; private String text; private final static int margin = 10; private final static int minSize = 24; private final static int arcSize = 10; private GFont font; private boolean pressed, draggedOrContext; private double textHeight; private double textWidth; private final static float marginTopMultiplier = 0.6f; private final static float marginBottomMultiplier = 0.5f; private final static float marginLeftMultiplier = 1f; private final static float marginRightMultiplier = 1f; /** * @param button * geo for this button * @param view * view */ public MyButton(GeoButton button, EuclidianView view) { this.geoButton = button; this.view = view; this.x = 20; this.y = 20; geoButton.setObserver(this); } private String getCaption() { if (geoButton.getFillImage() == null) { return geoButton.getCaption(StringTemplate.defaultTemplate); } return geoButton.getCaptionDescription(StringTemplate.defaultTemplate); } /** * Paint this on given graphics * * @param g * graphics * @param multiplier * font size multiplier */ public void paintComponent(GGraphics2D g, double multiplier) { boolean latex = CanvasDrawable.isLatexString(getCaption()); g.setAntialiasing(); font = font.deriveFont(geoButton.getFontStyle(), (int) (multiplier * 12)); g.setFont(font); boolean hasText = getCaption().length() > 0; int imgHeight = 0; int imgWidth = 0; int imgGap = 0; textHeight = 0; textWidth = 0; if (geoButton.getFillImage() != null) { imgHeight = geoButton.getFillImage().getHeight(); imgWidth = geoButton.getFillImage().getWidth(); if (hasText) { imgGap = 4; } } GTextLayout t = null; // get dimensions if (hasText) { if (latex) { GDimension d = CanvasDrawable.measureLatex( view.getApplication(), geoButton, font, getCaption()); textHeight = d.getHeight(); textWidth = d.getWidth(); } else { t = AwtFactory.getPrototype().newTextLayout(getCaption(), font, g.getFontRenderContext()); textHeight = t.getAscent() + t.getDescent(); textWidth = t.getAdvance(); } } // With fixed size the font are resized if is too big if (geoButton.isFixedSize() && ((int) textHeight + imgGap + (marginTopMultiplier + marginBottomMultiplier) * margin > geoButton.getHeight() || (int) textWidth + (marginLeftMultiplier + marginRightMultiplier) * margin > geoButton.getWidth())) { resize(g, imgGap); return; } int currentWidth = Math.max((int) (textWidth + (marginLeftMultiplier + marginRightMultiplier) * margin), minSize); currentWidth = Math.max(currentWidth, (int) (imgWidth + (marginLeftMultiplier + marginRightMultiplier) * margin)); int currentHeight = Math.max((int) (textHeight + imgHeight + imgGap + (marginTopMultiplier + marginBottomMultiplier) * margin), minSize); // Additional offset for image if button has fixed size int imgStart = 0; // Initial offset for subimage if button has fixed size int startX = 0; int startY = 0; double add = 0; if (!geoButton.isFixedSize()) { // Some combinations of style, serif / sans and letters // overflow from the drawing if the text is extra large if (geoButton.getFontStyle() >= 2) { add = Math.sin(0.50) * t.getDescent(); currentWidth += (int) add; } if (geoButton.isSerifFont()) { currentWidth += currentWidth / 10; } if (geoButton.isSerifFont() && geoButton.getFontStyle() >= 2) { add = -add; currentWidth += currentWidth / 4; } geoButton.setWidth(currentWidth); geoButton.setHeight(currentHeight); } else { // With fixed size the image is cut if is too big if (imgHeight > geoButton.getHeight() - textHeight - imgGap - (marginTopMultiplier + marginBottomMultiplier) * margin) { startY = imgHeight - (int) (geoButton.getHeight() - textHeight - imgGap - (marginTopMultiplier + marginBottomMultiplier) * margin); imgHeight = (int) (geoButton.getHeight() - textHeight - imgGap - (marginTopMultiplier + marginBottomMultiplier) * margin); if (imgHeight <= 0) { geoButton.setFillImage(""); } else { startY /= 2; } } if (imgWidth > geoButton.getWidth() - (marginLeftMultiplier + marginRightMultiplier) * margin) { startX = (int) (imgWidth - (geoButton.getWidth() - (marginLeftMultiplier + marginRightMultiplier) * margin)); imgWidth = (int) (geoButton.getWidth() - (marginLeftMultiplier + marginRightMultiplier) * margin); startX /= 2; } imgStart = (int) (geoButton.getHeight() - imgHeight - (marginTopMultiplier + marginBottomMultiplier) * margin - textHeight - imgGap) / 2; } // prepare colors and paint g.setColor(view.getBackgroundCommon()); GColor bg = geoButton.getBackgroundColor(); // background not set by user if (bg == null) { bg = GColor.WHITE; } // change background color on mouse click if (pressed) { if (bg.equals(GColor.WHITE)) { g.setPaint(GColor.LIGHTEST_GRAY); } else { g.setPaint(bg.darker()); } } else { g.setPaint(bg); } // fill background g.fillRoundRect(x, y, geoButton.getWidth() + (int) add - 1, geoButton.getHeight() - 1, arcSize, arcSize); // change border on mouseover if (isSelected()) { // default button design if (bg.equals(GColor.WHITE)) { // color for inner border g.setColor(GColor.GRAY); // inner border g.drawRoundRect(x + 1, y + 1, getWidth() + (int) add - 3, getHeight() - 3, arcSize - 3, arcSize - 3); // color for outer border g.setColor(GColor.BLACK); // user adjusted design } else { // color for inner border g.setColor(bg.darker()); // inner border g.drawRoundRect(x + 1, y + 1, getWidth() + (int) add - 3, getHeight() - 3, arcSize - 3, arcSize - 3); // color for outer border g.setColor(bg.darker().darker()); } // border color } else { // default button design if (bg.equals(GColor.WHITE)) { g.setColor(GColor.BLACK); // user adjusted design } else { g.setColor(bg.darker()); } } // draw border g.setStroke(EuclidianStatic.getDefaultStroke()); g.drawRoundRect(x, y, getWidth() + (int) add - 1, getHeight() - 1, arcSize, arcSize); // prepare to draw text g.setColor(geoButton.getObjectColor()); // draw image if (geoButton.getFillImage() != null) { geoButton.getFillImage().drawSubimage(startX, startY, imgWidth, imgHeight, g, x + (geoButton.getWidth() - imgWidth) / 2, (int) (y + marginTopMultiplier * margin + imgStart)); } // draw the text center-aligned to the button if (hasText) { if (geoButton.getFillImage() == null) { imgStart = (int) (geoButton.getHeight() - (marginTopMultiplier + marginBottomMultiplier) * margin - textHeight) / 2; } drawText(g, t, imgStart + imgGap + imgHeight, latex, add); } } private void drawText(GGraphics2D g, GTextLayout t, int imgEnd, boolean latex, double add) { int xPos = latex ? (int) (x + (geoButton.getWidth() - textWidth) / 2) : (int) (x + (geoButton.getWidth() - t.getAdvance() + add) / 2); // int yPos = (int) (y + marginTopMultiplier * margin + imgHeight + // imgGap + t.getAscent() + imgStart); int yPos = latex ? (int) (y + (geoButton.getHeight() - textHeight) / 2) + imgEnd : (int) (y + marginTopMultiplier * margin + imgEnd + t .getAscent()); if (geoButton.getFillImage() != null) { yPos = latex ? y + imgEnd : (int) (y + marginTopMultiplier * margin + t.getAscent() + imgEnd); } if (latex) { App app = view.getApplication(); g.setPaint(GColor.BLACK); app.getDrawEquation().drawEquation(app, geoButton, g, xPos, yPos, geoButton.getCaption(StringTemplate.defaultTemplate), font, false, geoButton.getObjectColor(), geoButton.getBackgroundColor(), false, false, null); } else { g.drawString(geoButton.getCaption(StringTemplate.defaultTemplate), xPos, yPos); } } private void resize(GGraphics2D g, int imgGap) { boolean latex = CanvasDrawable.isLatexString(getCaption()); // Reduces the font for attempts GTextLayout t = null; int i = GeoText.getFontSizeIndex( ((TextProperties) geoButton).getFontSizeMultiplier()); while (i > 0 && (int) textHeight + imgGap + (marginTopMultiplier + marginBottomMultiplier) * margin > geoButton.getHeight()) { i--; font = font.deriveFont(font.getStyle(), (int) (GeoText.getRelativeFontSize(i) * 12)); if (latex) { GDimension d = CanvasDrawable.measureLatex( view.getApplication(), geoButton, font, getCaption()); textHeight = d.getHeight(); textWidth = d.getWidth(); } else { t = AwtFactory.getPrototype().newTextLayout(getCaption(), font, g.getFontRenderContext()); textHeight = t.getAscent() + t.getDescent(); textWidth = t.getAdvance(); } } while (i > 0 && (int) textWidth + (marginLeftMultiplier + marginRightMultiplier) * margin > geoButton.getWidth()) { i--; font = font.deriveFont(font.getStyle(), (int) (GeoText.getRelativeFontSize(i) * 12)); if (latex) { GDimension d = CanvasDrawable.measureLatex( view.getApplication(), geoButton, font, getCaption()); textHeight = d.getHeight(); textWidth = d.getWidth(); } else { t = AwtFactory.getPrototype().newTextLayout(getCaption(), font, g.getFontRenderContext()); textHeight = t.getAscent() + t.getDescent(); textWidth = t.getAdvance(); } } double ret = GeoText.getRelativeFontSize(i); paintComponent(g, ret); } private boolean isSelected() { return selected; } /** * @return width in pixels */ public int getWidth() { return geoButton.getWidth(); } /** * @return height in pixels */ public int getHeight() { return geoButton.getHeight(); } /** * Resizes and moves the button * * @param labelRectangle * new bounds */ public void setBounds(GRectangle labelRectangle) { x = (int) labelRectangle.getMinX(); y = (int) labelRectangle.getMinY(); geoButton.setWidth((int) labelRectangle.getWidth()); geoButton.setHeight((int) labelRectangle.getHeight()); } /** * @return bounds of this button */ public GRectangle getBounds() { return AwtFactory.getPrototype().newRectangle(x, y, geoButton.getWidth(), geoButton.getHeight()); } /** * @param selected * new selected flag */ public void setSelected(boolean selected) { this.selected = selected; } /** * @return x-coord */ public int getX() { return x; } /** * @return y-coord */ public int getY() { return y; } /** * @param labelDesc * text for this button */ public void setText(String labelDesc) { text = labelDesc; } /** * @return text of this button */ public String getText() { return text; } /** * @param font * new font */ public void setFont(GFont font) { this.font = font; geoButton.getKernel().notifyRepaint(); } /** * @param b * new pressed flag */ public void setPressed(boolean b) { if (b) { draggedOrContext = false; } // releasing else if (!draggedOrContext) { // make sure that Input Boxes lose focus (and so update) before // running scripts // geoButton.getKernel().getApplication().getActiveEuclidianView().requestFocusInWindow(); // now called from EuclidianController // geoButton.getKernel().getApplication().runScripts(geoButton, // null); } pressed = b; } /** * @param b * new "dragged or context menu" flag */ public void setDraggedOrContext(boolean b) { draggedOrContext = b; } /** * @return "dragged or context menu" flag */ public boolean getDraggedOrContext() { return draggedOrContext; } /** * @return whether the button has fixed size */ public boolean isFixedSize() { return geoButton.isFixedSize(); } /** * @param fixedSize * change the button to have fixed size */ public void setFixedSize(boolean fixedSize) { geoButton.setFixedSize(fixedSize); } @Override public void notifySizeChanged() { geoButton.getKernel().notifyRepaint(); } /** * @return associated GeoButton */ public GeoElement getButton() { return geoButton; } }