3 votes

Utilisation de TextBubbleBorder dans une popup

Tout d'abord, un grand merci à Andrew Thompson pour sa solution concernant la bordure avec des coins arrondis.

Maintenant mon problème. J'essaie d'ajouter la fonction "Quoi de neuf" dans notre application (une petite bulle pointant le nouveau composant). Tout fonctionne bien, sauf la fonctionnalité de transparence de la bordure. Voici mon exemple de MCVE (désolé c'est un peu long, car il contient le code écrit par Andrew).

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import ... (le reste du code est omis pour des raisons de lisibilité) ...     

Voici le résultat que j'ai obtenu : enter image description here

Comme vous pouvez le constater, la partie inférieure de la popup n'est pas transparente. Je comprends pourquoi (la solution initiale n'est pas adaptée pour ce cas), mais je ne trouve pas de solution pour le corriger. Des idées ?

3voto

aterai Points 3252

Jetez un œil sur Fenêtres Swing transparentes et personnalisées | Java.net. Peut-être que cela vous aidera.

// Ajouter Popup à une JWindow transparente pour créer et afficher.
PopupFactory.setSharedInstance(new TranslucentPopupFactory());

// Le popup doit être fermé uniquement lorsque l'utilisateur clique sur le bouton "Fermer".
Popup popup = PopupFactory.getSharedInstance().getPopup(button, hintPanel, x, y);

PopupTryout2.java

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.TextAttribute;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import java.util.Collections;
import javax.swing.*;
import javax.swing.border.AbstractBorder;

public class PopupTryout2 {
  public static void main(String[] args) {
    JFrame frm = new JFrame("Test de Popup");
    JButton button = new JButton("Test");
    JPanel p = new JPanel();
    p.add(button);
    frm.add(p, BorderLayout.SOUTH);
    frm.add(new JScrollPane(new JTextArea()));

    JPanel hintPanel = new JPanel();
    hintPanel.add(new JLabel("Ce bouton n'a pas de fonction ;)"));
    hintPanel.setOpaque(false);
    JLabel closeBtn = new JLabel("Fermer");
    closeBtn.setForeground(Color.BLUE);
    closeBtn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    hintPanel.add(closeBtn);
    hintPanel.setBorder(new TextBubbleBorder(Color.BLACK, 1, 6, 8, false));
    hintPanel.setOpaque(false);

    frm.setSize(600, 500);
    frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frm.setLocationRelativeTo(null);
    frm.setVisible(true);
    Point pt = button.getLocationOnScreen();
    Dimension panelSize = hintPanel.getPreferredSize();
    Dimension buttonSize = button.getSize();
    int x = pt.x + buttonSize.width - panelSize.width;
    int y = pt.y - panelSize.height - 10;

    PopupFactory.setSharedInstance(new TranslucentPopupFactory());

    // Le popup doit être fermé uniquement lorsque l'utilisateur clique sur le bouton "Fermer".
    Popup popup = PopupFactory.getSharedInstance().getPopup(button, hintPanel, x, y);

    popup.show();
    // Bouton de style "lien"
    closeBtn.addMouseListener(new MouseAdapter() {
      @Override public void mouseEntered(MouseEvent e) {
        Font f = e.getComponent().getFont();
        e.getComponent().setFont(underlineOn(f));
      }

      @Override public void mouseExited(MouseEvent e) {
        e.getComponent().setFont(UIManager.getFont("Label.font"));
      }

      @Override public void mousePressed(MouseEvent e) {
        popup.hide();
      }
    });
  }

  public static Font underlineOn(Font aBaseFont) {
    return aBaseFont.deriveFont(Collections.singletonMap(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON));
  }

  static class TextBubbleBorder extends AbstractBorder {
    private Color color;
    private int thickness = 4;
    private int radii = 8;
    private int pointerSize = 7;
    private Insets insets = null;
    private BasicStroke stroke = null;
    private int strokePad;
    private int pointerPad = 4;
    private boolean left = true;
    RenderingHints hints;

    TextBubbleBorder(Color color) {
      this(color, 4, 8, 7);
    }

    TextBubbleBorder(Color color, int thickness, int radii, int pointerSize) {
      this.thickness = thickness;
      this.radii = radii;
      this.pointerSize = pointerSize;
      this.color = color;

      stroke = new BasicStroke(thickness);
      strokePad = thickness / 2;

      hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

      int pad = radii + strokePad;
      int bottomPad = pad + pointerSize + strokePad;
      insets = new Insets(pad, pad, bottomPad, pad);
    }

    TextBubbleBorder(Color color, int thickness, int radii, int pointerSize, boolean left) {
      this(color, thickness, radii, pointerSize);
      this.left = left;
    }

    @Override public Insets getBorderInsets(Component c) {
      return insets;
    }

    @Override public Insets getBorderInsets(Component c, Insets insets) {
      return getBorderInsets(c);
    }

    @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
      Graphics2D g2 = (Graphics2D) g;
      int bottomLineY = height - thickness - pointerSize;
      RoundRectangle2D.Double bubble =
        new RoundRectangle2D.Double(0 + strokePad, 0 + strokePad, width - thickness, bottomLineY, radii, radii);

      Polygon pointer = new Polygon();

      if (left) {
        pointer.addPoint(strokePad + radii + pointerPad, bottomLineY);
        pointer.addPoint(strokePad + radii + pointerPad + pointerSize, bottomLineY);
        pointer.addPoint(strokePad + radii + pointerPad + (pointerSize / 2), height - strokePad);
      } else {
        pointer.addPoint(width - (strokePad + radii + pointerPad), bottomLineY);
        pointer.addPoint(width - (strokePad + radii + pointerPad + pointerSize), bottomLineY);
        pointer.addPoint(width - (strokePad + radii + pointerPad + (pointerSize / 2)), height - strokePad);
      }

      Area area = new Area(bubble);
      area.add(new Area(pointer));

      g2.setRenderingHints(hints);

      // Peindre la couleur d'arrière-plan du parent, partout en dehors du clip
      // de la bulle de texte.
      Component parent = c.getParent();
      if (parent != null) {
        Color bg = parent.getBackground();
        // Rectangle rect = new Rectangle(0, 0, width, height);
        // Area borderRegion = new Area(rect);
        // borderRegion.subtract(area);
        // g2.setClip(borderRegion);
        // g2.setColor(bg);
        // g2.fillRect(0, 0, width, height);
        // g2.setClip(null);
        g2.setPaint(bg);
        g2.fill(area);
      }
      g2.setColor(color);
      g2.setStroke(stroke);
      g2.draw(area);
    }
  }
}

/*
@see http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html
https://web.archive.org/web/20150515015137/http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html
Fenêtres Swing transparentes et personnalisées | Java.net
*/
class TranslucentPopupFactory extends PopupFactory {
  @Override public Popup getPopup(Component owner, Component contents, int x, int y) {
    return new TranslucentPopup(owner, contents, x, y);
  }
}

class TranslucentPopup extends Popup {
  private final JWindow popupWindow;

  protected TranslucentPopup(Component owner, Component contents, int ownerX, int ownerY) {
    super(owner, contents, ownerX, ownerY);
    // Créer une nouvelle fenêtre lourde
    this.popupWindow = new JWindow();
    // Marquer le popup avec une opacité partielle
    // AWTUtilities.setWindowOpacity(popupWindow, (contents instanceof JToolTip) ? .8f : .95f);
    // popupWindow.setOpacity(.5f);
    // AWTUtilities.setWindowOpaque(popupWindow, false); // Java 1.6.0_10
    popupWindow.setBackground(new Color(0x0, true)); // Java 1.7.0
    // Déterminer l'emplacement du popup
    popupWindow.setLocation(ownerX, ownerY);
    // Ajouter le contenu au popup
    popupWindow.getContentPane().add(contents);
    contents.invalidate();
    // JComponent parent = (JComponent) contents.getParent();
    // Définir la bordure d'ombre
    // parent.setBorder(new ShadowPopupBorder());
  }

  @Override public void show() {
    System.out.println("Toujours lourde !");
    this.popupWindow.setVisible(true);
    this.popupWindow.pack();
    // Marquer la fenêtre comme non opaque, afin que les
    // pixels de la bordure d'ombre prennent en charge la translucidité par pixel.
    // AWTUtilities.setWindowOpaque(this.popupWindow, false);
  }

  @Override public void hide() {
    this.popupWindow.setVisible(false);
    this.popupWindow.removeAll();
    this.popupWindow.dispose();
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X