All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dorkbox.util.swing.SwingActiveRender Maven / Gradle / Ivy

Go to download

Linux, MacOS, or Windows (notification/growl/toast/) popups for the desktop for Java 8+

There is a newer version: 4.5
Show newest version
/*
 * Copyright 2015 dorkbox, llc
 *
 * Licensed 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 dorkbox.util.swing;

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Window;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.swing.JComponent;

import dorkbox.util.ActionHandlerLong;

/**
 * Contains all of the appropriate logic to setup and render via "Active" rendering (instead of "Passive" rendering). This permits us to
 * render Windows (and their contents), OFF of the EDT - even though there are other frames/components that are ON the EDT. 
Because we * still want to react to mouse events, etc on the EDT, we do not completely remove the EDT -- we merely allow us to "synchronize" the EDT * object to our thread. It's a little bit hacky, but it works beautifully, and permits MUCH nicer animations.
*

* It is also important to REMEMBER -- if you add a component to an actively managed Window, YOU MUST make sure to call {@link * JComponent#setIgnoreRepaint(boolean)} otherwise this component will "fight" on the EDT for updates. */ public final class SwingActiveRender { private static Thread activeRenderThread = null; static final List activeRenders = new ArrayList(); static final List activeRenderEvents = new CopyOnWriteArrayList(); // volatile, so that access triggers thread synchrony, since 1.6. See the Java Language Spec, Chapter 17 static volatile boolean hasActiveRenders = false; private static final Runnable renderLoop = new ActiveRenderLoop(); private SwingActiveRender() { } /** * Enables the window to to added to an "Active Render" thread, at a target "Frames-per-second". This is to support smooth, swing-based * animations.
This works by removing this object from EDT updates, and instead manually calls paint(g) on the window, updating it * on our own thread. * * @param window the window to add to the ActiveRender thread. */ public static void addActiveRender(final Window window) { // this should be on the EDT if (!EventQueue.isDispatchThread()) { throw new RuntimeException("adding a swing Window to be actively rendered must be done on the EDT."); } // setup double-buffering, so we can properly use Active-Rendering, so the animations will be smooth window.createBufferStrategy(2); // have to specify ALL children in Window to ignore EDT paint requests Deque components = new ArrayDeque(8); components.add(window); Component[] c; Component pop; while ((pop = components.poll()) != null) { pop.setIgnoreRepaint(true); if (pop instanceof JComponent) { c = ((JComponent) pop).getComponents(); Collections.addAll(components, c); } } synchronized (activeRenders) { if (!hasActiveRenders) { setupActiveRenderThread(); } hasActiveRenders = true; activeRenders.add(window); } } /** * Specifies an ActionHandler to be called when the ActiveRender thread starts to render at each tick. * * @param handler the handler to add */ public static void addActiveRenderFrameStart(final ActionHandlerLong handler) { synchronized (activeRenders) { activeRenderEvents.add(handler); } } /** * Potentially SLOW calculation, as it compares each entry in a queue for equality * * @param handler this is the handler to check * * @return true if this handler already exists in the active render, on-frame-start queue */ public static boolean containsActiveRenderFrameStart(final ActionHandlerLong handler) { synchronized (activeRenders) { return activeRenderEvents.contains(handler); } } /** * Removes the handler from the on-frame-start queue * * @param handler the handler to remove */ public static void removeActiveRenderFrameStart(final ActionHandlerLong handler) { synchronized (activeRenders) { activeRenderEvents.remove(handler); } } /** * Removes a window from the ActiveRender queue. This should happen when the window is closed. * * @param window the window to remove */ public static void removeActiveRender(final Window window) { synchronized (activeRenders) { activeRenders.remove(window); final boolean hadActiveRenders = !activeRenders.isEmpty(); hasActiveRenders = hadActiveRenders; if (!hadActiveRenders) { activeRenderThread = null; } } // have to specify ALL children in window to obey EDT paint requests Deque components = new ArrayDeque(8); components.add(window); Component[] c; Component pop; while ((pop = components.poll()) != null) { pop.setIgnoreRepaint(false); if (pop instanceof JComponent) { c = ((JComponent) pop).getComponents(); Collections.addAll(components, c); } } } /** * Creates (if necessary) the active-render thread. When there are no active-render targets, this thread will exit */ private static void setupActiveRenderThread() { if (activeRenderThread != null) { return; } SynchronizedEventQueue.install(); activeRenderThread = new Thread(renderLoop, "AWT-ActiveRender"); activeRenderThread.setDaemon(true); activeRenderThread.start(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy