Let's see your damned Qt widgets do this!

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
wearymemory
Chaos Rift Junior
Chaos Rift Junior
Posts: 209
Joined: Thu Feb 12, 2009 8:46 pm

Let's see your damned Qt widgets do this!

Post by wearymemory »

A slightly complex class that inherits from JRootPane, and utilizes a glass pane to extend the functionality of light-weight Swing components, providing the ability rotate, scale, or translate the content pane. The glass pane captures all mouse events, modifies them, and dispatches them to their respective components.

Example video:



TransformRootPane.java

Code: Select all

package misc;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;

import javax.swing.*;
import javax.swing.event.MouseInputAdapter;

public class TransformRootPane extends JRootPane {
    private double rotation = 0.0;
    private double scaleX = 1.0;
    private double scaleY = 1.0;
    private double translateX = 0.0;
    private double translateY = 0.0;

    public TransformRootPane() {
        TransformGlassPane gp = new TransformGlassPane(this);
        setGlassPane(gp);
        gp.setVisible(true);
        setLayout(new TransformRootPaneLayout());
    }
    
    protected static Dimension getMaximumSize(Component c) {
        Dimension size = c.getSize();
        if (size.getWidth() == 0.0 && size.getHeight() == 0.0)
            size = c.getPreferredSize();
        double dist = Point.distance(0.0, 0.0, size.getWidth(), size.getHeight());
        return new Dimension((int) dist, (int) dist);
    }

    public Point convertPoint(Point point) {
        Container cp = getContentPane();
        Dimension maxSize = getMaximumSize(cp);
        double w = maxSize.getWidth();
        double h = maxSize.getHeight();
        AffineTransform trans = AffineTransform.getScaleInstance(scaleX, scaleY);
        trans.rotate(rotation,
                (translateX + w / 2.0) * scaleX,
                (translateY + h / 2.0) * scaleY);
        Point2D ptSrc = new Point2D.Double(point.getX(), point.getY());
        Point2D ptDst = trans.transform(ptSrc, null);
        return new Point(
                (int) (ptDst.getX() + translateX + (w / 2 - cp.getWidth() / 2)),
                (int) (ptDst.getY() + translateY + (h / 2 - cp.getHeight() / 2)));
    }

    public Point revertPoint(Point point) {
        Container cp = getContentPane();
        Dimension maxSize = getMaximumSize(cp);
        double w = maxSize.getWidth();
        double h = maxSize.getHeight();
        AffineTransform trans = AffineTransform.getScaleInstance(
                scaleX == 0.0 ? 0.0 : 1.0 / scaleX,
                scaleY == 0.0 ? 0.0 : 1.0 / scaleY);
        trans.rotate(-rotation, 
                (translateX + w / 2.0) * scaleX,
                (translateY + h / 2.0) * scaleY);
        Point2D ptSrc = new Point2D.Double(point.getX(), point.getY());
        Point2D ptDst = trans.transform(ptSrc, null);
        return new Point(
                (int) (ptDst.getX() - translateX - (w / 2 - cp.getWidth() / 2)),
                (int) (ptDst.getY() - translateY - (h / 2 - cp.getHeight() / 2)));
    }

    @Override public Dimension getPreferredSize() {
        Container cp = getContentPane();
        Dimension cpd = cp.getPreferredSize();
        Dimension d = super.getPreferredSize();
        Dimension maxcpd = getMaximumSize(cp);
        return new Dimension(
                (int) (d.getWidth() - cpd.getWidth()
                + scaleX * (maxcpd.getWidth() + translateX)),
                (int) (d.getHeight() - cpd.getHeight()
                + scaleY * (maxcpd.getHeight() + translateY)));
    }

    public double getRotation()   { return rotation;   }
    public double getScaleX()     { return scaleX;     }
    public double getScaleY()     { return scaleY;     }
    public double getTranslateX() { return translateX; }
    public double getTranslateY() { return translateY; }

    public void setRotation(double rotation)     { this.rotation = rotation;     }
    public void setScaleX(double scaleX)         { this.scaleX = scaleX;         }
    public void setScaleY(double scaleY)         { this.scaleY = scaleY;         }
    public void setTranslateX(double translateX) { this.translateX = translateX; }
    public void setTranslateY(double translateY) { this.translateY = translateY; }

    protected class TransformGlassPane extends JComponent {
        private JRootPane rootPane;

        public TransformGlassPane(JRootPane rootPane) {
            this.rootPane = rootPane;
            MouseInputDispatcher listener = new MouseInputDispatcher(
                    this, rootPane.getContentPane());
            addMouseListener(listener);
            addMouseMotionListener(listener);
            setVisible(true);
        }

        @Override protected void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            Container cp = rootPane.getContentPane();
            Dimension maxSize = getMaximumSize(cp);
            double w = maxSize.getWidth();
            double h = maxSize.getHeight();
            BufferedImage buffer = new BufferedImage(
                    (int) (w + translateX),
                    (int) (h + translateY),
                    BufferedImage.TYPE_INT_ARGB);
            Graphics2D bg2 = buffer.createGraphics();
            bg2.translate(
                    (w / 2.0 - cp.getWidth() / 2.0) + translateX,
                    (h / 2.0 - cp.getHeight() / 2.0) + translateY);
            cp.paint(bg2);
            bg2.dispose();
            g2.setColor(cp.getBackground());
            g2.fillRect(0, 0, getWidth(), getHeight());
            g2.rotate(rotation, 
                    (translateX + w / 2.0) * scaleX,
                    (translateY + h / 2.0) * scaleY);
            g2.scale(scaleX, scaleY);
            g2.drawImage(buffer, 0, 0, this);
        }
    }

    protected class MouseInputDispatcher extends MouseInputAdapter {
        private TransformGlassPane glassPane;
        private Container contentPane;
        private Component lastComponent;

        public MouseInputDispatcher(TransformGlassPane glassPane, Container contentPane) {
            this.glassPane = glassPane;
            this.contentPane = contentPane;
        }

        protected void dispatch(MouseEvent e) {
            Point glassPanePoint = revertPoint(e.getPoint());
            Point containerPoint = SwingUtilities.convertPoint(
                    glassPane, glassPanePoint, contentPane);
            Component component = SwingUtilities.getDeepestComponentAt(
                    contentPane, containerPoint.x, containerPoint.y);
            if (component == null)
                component = lastComponent;
            if (component != null) {
                Point componentPoint = SwingUtilities.convertPoint(
                        glassPane, glassPanePoint, component);
                component.dispatchEvent(new MouseEvent(component,
                        e.getID(),
                        e.getWhen(),
                        e.getModifiers(),
                        componentPoint.x,
                        componentPoint.y,
                        e.getClickCount(),
                        e.isPopupTrigger()));
            }
            lastComponent = component;
            glassPane.repaint();
        }

        @Override public void mouseClicked(MouseEvent e)  { dispatch(e); }
        @Override public void mousePressed(MouseEvent e)  { dispatch(e); }
        @Override public void mouseReleased(MouseEvent e) { dispatch(e); }
        @Override public void mouseEntered(MouseEvent e)  { dispatch(e); }
        @Override public void mouseExited(MouseEvent e)   { dispatch(e); }
        @Override public void mouseDragged(MouseEvent e)  { dispatch(e); }
        @Override public void mouseMoved(MouseEvent e)    { dispatch(e); }
    }

    protected class TransformRootPaneLayout extends RootLayout {
        
        @Override public void layoutContainer(Container parent) {
            Rectangle b = parent.getBounds();
            Insets i = getInsets();
            int contentY = 0;
            int w = b.width - i.right - i.left;
            int h = b.height - i.top - i.bottom;
            if (layeredPane != null)
                layeredPane.setBounds(i.left, i.top, w, h);
            if (glassPane != null)
                glassPane.setBounds(i.left, i.top, w, h);
            if (menuBar != null && menuBar.isVisible()) {
                Dimension mbd = menuBar.getPreferredSize();
                menuBar.setBounds(0, 0, w, mbd.height);
                contentY += mbd.height;
            }
            if (contentPane != null) {
                Dimension cpd = contentPane.getPreferredSize();
                contentPane.setBounds(0, contentY,
                        (int) cpd.getWidth(), (int) cpd.getHeight());
            }
        }
    }
    
    private static void createAndShowGUI() {
        JFrame f = new JFrame() {
            @Override protected JRootPane createRootPane() {
                final TransformRootPane rp = new TransformRootPane();
                new Timer(5, new ActionListener() {
                    final int ROTATIONS = 5;
                    final double RADIANS_360 = Math.toRadians(360);
                    double speed = 20.0;
                    double scale = 0.0;
                    int rotationsCount = 0;
                    public void actionPerformed(ActionEvent e) {
                        if (rotationsCount <= ROTATIONS) {
                            rp.setRotation(rp.getRotation() + Math.toRadians(speed));
                            rp.setScaleX(scale);
                            rp.setScaleY(scale);
                            scale += 0.0095;
                            if (rp.getRotation() / RADIANS_360 >= rotationsCount) {
                                rotationsCount++;
                                speed --;
                            }
                        }  else {
                            rp.setRotation(0.0);
                        }
                        repaint();
                    }
                }).start();
                return rp;
            }
        };
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setBackground(Color.DARK_GRAY);
        JPanel p = new JPanel(new BorderLayout());
        p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        p.setBackground(Color.LIGHT_GRAY);
        p.add(new JLabel("<html>Notice<p /><hr /><p /><p /></html>"), BorderLayout.NORTH);
        p.add(new JLabel("<html>Let's see your damned<p />"
                + "Qt Widgets do this!<p /><hr /><p /><p /></html>"), BorderLayout.CENTER);
        p.add(new JButton("OK"), BorderLayout.SOUTH);
        f.add(p);
        f.pack();
        f.setLocation(400, 400);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                    // no-op
                }
                createAndShowGUI();
            }
        });
    }
}
This is my simple take on manipulating components; to make the most out of this code, it may be necessary to change or further extend this class's capabilities. Also, I'm not interested in a debate on Qt or Java, I chose this title because it was fun, so don't take it too literally.
User avatar
GroundUpEngine
Chaos Rift Devotee
Chaos Rift Devotee
Posts: 835
Joined: Sun Nov 08, 2009 2:01 pm
Current Project: mixture
Favorite Gaming Platforms: PC
Programming Language of Choice: C++
Location: UK

Re: Let's see your damned Qt widgets do this!

Post by GroundUpEngine »

lol nice! 8-)
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: Let's see your damned Qt widgets do this!

Post by Falco Girgis »

Fuckin' awesome... buuuut

I'm pretty damn sure QT can do that.
User avatar
LeonBlade
Chaos Rift Demigod
Chaos Rift Demigod
Posts: 1314
Joined: Thu Jan 22, 2009 12:22 am
Current Project: Trying to make my first engine in C++ using OGL
Favorite Gaming Platforms: PS3
Programming Language of Choice: C++
Location: Blossvale, NY

Re: Let's see your damned Qt widgets do this!

Post by LeonBlade »

GyroVorbis wrote:Fuckin' awesome... buuuut

I'm pretty damn sure QT can do that.
I agree.
There's no place like ~/
wearymemory
Chaos Rift Junior
Chaos Rift Junior
Posts: 209
Joined: Thu Feb 12, 2009 8:46 pm

Re: Let's see your damned Qt widgets do this!

Post by wearymemory »

GyroVorbis wrote:Fuckin' awesome... buuuut

I'm pretty damn sure QT can do that.
I recall Arce saying in IRC that he would attempt it using Qt. I think that he was successful, but I'd like to hear that from him or M_D_K (maybe they'll show us their example).

I appreciate those of you who aren't taking the title too seriously. It could be easily misconstrued as contesting Qt's abilities, but that isn't my intention; I'm welcoming examples of what some filthy rich clients might expect to see in their software. I certainly could have avoided such a provocative name, but I enjoyed the humor in it.
Post Reply