12

I am trying to create a Applet loader and I need to draw on top of the displayed Applet, however I can't seem to find a way to do that.

My original understanding was that Applet, by extending Component is just like any regular java.awt.Component that can be added inside Container that just have overriden paint method, but it seems like its not working.

In my initialization code I create a java.awt.Frame on which I add my custom implementation of java.awt.Container that has overriden all the paint methods so that they fill rect at x: 5, y:5 with size w:10, h:10 after calling parent method

However, when the applet is added, it always, no matter what is drawn on top of everything

public class AppletTest {

    public static void main(String[] args) {
        Frame frame = new Frame("Applet Test!");

        Container container = new Container() {

            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void paintAll(Graphics g) {
                super.paintAll(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void paintComponents(Graphics g) {
                super.paintComponents(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void print(Graphics g) {
                super.print(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void printComponents(Graphics g) {
                super.printComponents(g);
                g.fillRect(5, 5, 10, 10);
            }

            @Override
            public void update(Graphics g) {
                super.update(g);
                g.fillRect(5, 5, 10, 10);
            }

        };

        Dimension dimension = new Dimension(50, 50);
        container.setPreferredSize(dimension);

        Applet applet = new Applet() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.fillRect(0, 0, 10, 10);
            }
        };

        container.add(applet);

        applet.setBounds(0, 0, 50, 50);


        frame.add(container);
        frame.pack();
        frame.setVisible(true);

        applet.init();
        applet.start();


    }

}

What are the steps that are needed to be taken to be able to draw on top of the Applet from its parent Container?

Here is also a screenshot of result of running the above code

![enter image description here

If I however change the type of applet to Component such as

Component applet = new Component() {
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0, 0, 10, 10);
    }
};

the result is correct

enter image description here

The limitations of required solution is that I cannot modify the Applet itself, as its a legacy component that's only provided as binary. I know there's a solution by doing bytecode modification, but due to the many flavors of Applets, that's out of the question.

Ruuhkis
  • 1,924
  • 1
  • 27
  • 46
  • 2
    1) *"I am trying to create a Applet loader.."* **Why?** *"..and I need to draw on top of the displayed Applet"* Consider using a `JLayeredPane`. 2) For better help, post a [mcve]. – Andrew Thompson Jan 09 '17 at 17:47
  • 1) I am trying to create a layer to extend legacy Java binary applet and wanting to keep the 'API' similar to the swing as possible, I mean.. This is how its supposed to work? You add an component to a Container and the container draws the child, however with Applets, it doesn't seem to be so.. 2) The code I posted is minimal, complete and verifiable, I don't know anything else I could rip off the code.. It creates a frame, adds a container to it which draws a rectangle, that an applet is added on that also draws an rectangle.. I also posted the current and expected result of the run.. – Ruuhkis Jan 09 '17 at 17:55
  • @AndrewThompson There *is* a MCVE...!?. The goal may be questionable, but regardless of that, it's an interesting glitch. I tried it with different components, and added print statements (after the `super` calls). If the component is a `Container`, then it prints `"Paint component, paint container"`, and it works. If the component is a `Panel`, then it prints `"Paint container, paint component"`, and does **not** work. But there should be nothing in the `Panel` class that should cause *such* a different behavior. I haven't invested sooo much time, but would be curious to know what's going on – Marco13 Jan 09 '17 at 19:35
  • Very curious behavior indeed! I am pretty positive this has something to do with the Applet sandboxing.. I tied attaching a debugger to the Applet's onPaint method and the stack trace shows that it is not being drawn by the parent component, but by some RepaintArea that has received someking of repaint event.. Thanks for taking your time tho! – Ruuhkis Jan 10 '17 at 05:58
  • Is the current answer by Sergey acceptable for you, or are you looking for alternatives (and a possible explanation of the odd behavior)? – Marco13 Jan 13 '17 at 17:22
  • I am definetely looking for an explanation, if its unachiavable as now stated by many people. I couldn't find explanation this, not in even the Applet.java implementation even after looking at grepcode.. – Ruuhkis Jan 13 '17 at 18:02
  • @Ruuhkis The only explanation I could find for this exact problem was on [EE](https://www.experts-exchange.com/questions/20178591/Transparent-in-Java-Applet.html) – Dan Jan 13 '17 at 18:55

2 Answers2

7

Why container square is not visible

It's happen because of applet overlap drawaple area of container. You can see this, if setup background color of applet and change size:

applet.setBackground(Color.RED);
applet.setBounds(0, 0, 12, 12);

In result we can see red border (background of applet) under black square drawed on applet:

Part overlap

With your applet size and with red background of applet fully overlap container drawable area:

Full overlapp

If you change Applet to Component you can see black square from container becouse of class Component not have background. I.e. change type of applet variable:

Component applet = new Component() {
//...
};
applet.setBackground(Color.RED);

You can see image as in your experiment:

Component no background

Drawing on top of applet

What are the steps that are needed to be taken to be able to draw on top of the Applet from its parent Container?

It is not posible to draw on top of applet, except drawing directly on applet.

Using GlassPane not solve this problem for applets. I tried example from documentation

Documentation example

and replace code:

contentPane.add(new JButton("Button 1"));
contentPane.add(new JButton("Button 2"));

to:

Applet applet = new Applet() {
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0, 0, 10, 10);
    }
};
applet.setPreferredSize(new Dimension(100, 25));
applet.setBackground(Color.GREEN);
contentPane.add(applet);

In result we can see applet, which owerlap darawed circle:

Glasspane overlaped

Circle is fully drawed, if we change type of applet variable to JLanel.

Glasspane draw full circle

Community
  • 1
  • 1
Sergei Bubenshchikov
  • 5,275
  • 3
  • 33
  • 60
  • Thanks for providing illustrated examples, but even if this answers the question abstractly - 'no you can't do it', it doesn't really tell me why? I took a look at the Grepcode of applet.java and still can't figure out what is going on there.. How is Applet given a special capability of drawing over everything? – Ruuhkis Jan 13 '17 at 18:05
  • I understand what do you asking for. Tomorrow I will research this question – Sergei Bubenshchikov Jan 13 '17 at 18:10
  • Sergey, as far as I can tell, the OP does not want to change the type from `applet` as they are using a premade applet and just reading it from a binary file. Correct me if I'm wrong @Ruuhkis – Dan Jan 13 '17 at 18:54
  • @Dan, I sense this, but I researched different ways to suggest appropriate solution and show most interesting results in examples. I understand, that changing of type - it's not solution – Sergei Bubenshchikov Jan 13 '17 at 19:03
  • @Ruuhkis, better way to drawing on top of applet what I found, is placing `Panel` as drawing region on top of applet. And draw on it without transparency. Also you can place [Translucent Window](https://docs.oracle.com/javase/tutorial/uiswing/misc/trans_shaped_windows.html) on top of frame and draw on it, but this solution looks like a hack – Sergei Bubenshchikov Jan 16 '17 at 04:56
  • 1
    this repeats the statement of the problem. With more colorful examples – gpasch Jan 21 '17 at 20:43
0

As Sergey said your Applet overlaps your Container and as it has a background you can't see the painted rectangle on the Container. However, as far as I know it is not possible to set this background to a transparent colour. You can see these posts, here and here, that discuss the topic and a possible solution.

The best way for a situation for when you can change the code of the applet but can't change the type of applet

import java.applet.Applet;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

@SuppressWarnings("serial")
public class AppletTest {
    public static void main(String[] args) {
        Frame frame = new Frame("Applet Test");
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                frame.dispose();
             }
         });

        int apHeight = 50;
        int apWidth = 50;

        Container container = new Container() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                Graphics2D graphs = (Graphics2D) g;
                graphs.setBackground(Color.WHITE);
                graphs.clearRect(0, 0, apWidth, apHeight);
                g.setColor(Color.RED);
                g.fillRect(5, 5, 10, 10);
            }
        };

        Dimension dimension = new Dimension(50, 50);
        container.setPreferredSize(dimension);

        frame.add(container);
        frame.pack();
        frame.setVisible(true);

        BufferedImage bufImage = new BufferedImage(dimension.width, dimension.height, BufferedImage.TYPE_INT_RGB);
        container.paint(bufImage.createGraphics());

        Applet applet = new Applet() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.drawImage(bufImage, 0, 0, this); 
                g.fillRect(0, 0, 10, 10);
            }
        };

        container.add(applet);
        applet.setBounds(0, 0, apWidth, apHeight);
        applet.init();
    }
}

The output of this looks like the following.

Red and black square

Edit for clarified question
If you are reading from a file the best way would be to declare your applet, create an image, and then paint the image onto the applet. I made an example that makes binary file and then reads the object from it. After which it paints the background on it.

import java.applet.Applet;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

@SuppressWarnings("serial")
public class AppletTest {
    private static void createBinaryApplet() throws IOException {
        Applet applet = new Applet() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                g.fillRect(0, 0, 10, 10);
            }
        };

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("applet.dat"));
        oos.writeObject(applet);
        oos.close();
    }

    public static void main(String[] args) {
        Frame frame = new Frame("Applet Test");
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                frame.dispose();
             }
         });

        final int prefHeight = 50;
        final int prefWidth = 50;

        Container container = new Container() {
            @Override
            public void paint(Graphics g) {
                super.paint(g);
                Graphics2D graphs = (Graphics2D) g;
                graphs.setBackground(Color.WHITE);
                graphs.clearRect(0, 0, prefWidth, prefHeight);
                g.setColor(Color.RED);
                g.fillRect(5, 5, 10, 10);
            }
        };

        container.setPreferredSize(new Dimension(prefWidth, prefHeight));

        frame.add(container);
        frame.pack();
        frame.setVisible(true);

        BufferedImage bufImage = new BufferedImage(prefWidth, prefHeight, BufferedImage.TYPE_INT_RGB);
        container.paint(bufImage.createGraphics());

        try {
            createBinaryApplet();

            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("applet.dat"));
            Applet applet = (Applet) ois.readObject();
            ois.close();

            container.add(applet);
            applet.setBounds(0, 0, prefWidth, prefHeight);
            applet.init();

            Graphics g = applet.getGraphics();
            g.drawImage(bufImage, 0, 0, applet);
            applet.paint(g);
        } catch(ClassNotFoundException | IOException e) {
            System.out.println("Whoops");
        }
    }
}

This produces the same output as before.

Community
  • 1
  • 1
Dan
  • 7,286
  • 6
  • 49
  • 114
  • Sorry if I didn't make it too clear.. I can't modify the Java applet(s) as they are provided as binaries, and doing this for each applet by doing bytecode modification is a way too big chore to do. What do you mean by messing with JNA? How can I achieve this by accessing something native? – Ruuhkis Jan 13 '17 at 18:04
  • @Ruuhkis Ah okay. I see. Let me do a little more research and I will edit my answer with a new method to achieve the result and explain the problem, or at least using JNA to solve it a little better – Dan Jan 13 '17 at 18:15
  • @Ruuhkis May I ask where you are getting your binary applets from or for a copy of them to test with? – Dan Jan 13 '17 at 18:41
  • @Ruuhkis Also I was mistaken about JNA. That can not be used to make applets transparent, only root windows – Dan Jan 13 '17 at 18:50
  • @Ruuhkis I edited my answer for how to update the background on a "premade" applet. If I have assumed something wrong, let me know and I will try to amend my answer. Also if you use a different type of file where this does not work would it be possible for me to get a sample file that is similar so I can find a method of doing this for that file type – Dan Jan 14 '17 at 11:53
  • For the edit: The applet also have its own drawing cycle, so that doesn't really help, right? The draw could occur before or midst drawing the applets own content, so that really doesn't solve the problem :/ – Ruuhkis Jan 14 '17 at 12:40
  • @Ruuhkis I'm not entirely sure I understand the problem as it shouldn't really matter when it draws it as it should be drawn like the desired output you posted, but if you want to wait for everything to be ready you could use `EventQueue.invokeLater(() -> applet.paint(g));` instead of `applet.paint(g)`. However, if you want to draw the background as an overlay (I.e. Always on the very top of the applet) I suggest you try to implement what Andrew suggested in his comment or unless you post more information, you might not get the answer you are looking for. – Dan Jan 14 '17 at 12:58
  • I mean that some of the applets have their own drawing cycle, so they request their own repaint, meaning I can't know when the applet is repainting, so if i just randomly draw my overlay it wont always be visible and will be flickering – Ruuhkis Jan 14 '17 at 13:21
  • @Ruuhkis could you please post a more complete example of what you have done that demonstrates that? – Dan Jan 14 '17 at 13:23
  • Say you have an Applet that has an own thread and calls repaint() by itself, say every 30ms Now you want to draw on top of those frames, you don't know when those frames are drawn so if you just make your own loop with some random interval, it might be that you're drawing before the applet has drawn, causing flickering – Ruuhkis Jan 14 '17 at 13:51
  • @Ruuhkis, what do you want to draw on top of applet? Can you explain this? – Sergei Bubenshchikov Jan 14 '17 at 13:55
  • @Sergey certain metrics needs to be shown on top of the applet, imagine fraps, but per applet. – Ruuhkis Jan 14 '17 at 16:45
  • @Ruuhkis I see your problem now. I made such an overlay and will try get a "transparent" background on it tomorrow – Dan Jan 14 '17 at 21:34