/**
* 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.
*
* Copyright 2012-2015 the original author or authors.
*/
package org.assertj.swing.core;
import static javax.swing.SwingUtilities.convertRectangle;
import static org.assertj.core.util.Preconditions.checkNotNull;
import static org.assertj.swing.edt.GuiActionRunner.execute;
import java.awt.Component;
import java.awt.Container;
import java.awt.Rectangle;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JViewport;
/**
* Utility methods related to scrolling.
*
* @author Juhos Csaba-Zsolt
*/
public final class Scrolling {
/**
* Scrolls a {@code JComponent} into view within a container.
*
* @param robot simulates user input.
* @param c the given {@code JComponent}.
*/
public static void scrollToVisible(@Nonnull Robot robot, @Nonnull JComponent c) {
JComponent root = findClosestValidatingRootAncestor(c);
// scroll the component to view within each validating root ancestor, starting from the nearest
while (root != null) {
scrollToVisible(robot, root, c);
// find the next validating root
root = findClosestValidatingRootAncestor(root);
}
}
/**
* Returns a {@code JComponent}'s closest validating root ancestor in the AWT containment hierarchy.
*
* @param c the given {@code JComponent}.
* @return the found ancestor or {@code null} if there isn't one.
*/
private static @Nullable JComponent findClosestValidatingRootAncestor(@Nonnull JComponent c) {
// the candidate validating root at every iteration (candidate = not necessarily a root)
Container root = c;
// we go up to the top of the hierarchy
while (root != null) {
Container parent = root.getParent();
// the new candidate root becomes the parent of the previous one
root = parent;
// if the candidate isn't a JComponent, we're not interested in it (we need JComponent#scrollRectToVisible)
if (!(root instanceof JComponent)) {
continue;
}
// we don't have to take JFrame into account, it's not a JComponent (ant it's a top-level container anyway)
if (root instanceof JViewport || root instanceof JInternalFrame) {
return (JComponent) root;
}
}
return null;
}
/**
* Scrolls an AWT or Swing {@code Component} into view within a container.
*
* @param robot simulates user input.
* @param container the given container.
* @param target the given {@code Component}.
*/
private static void scrollToVisible(@Nonnull Robot robot, @Nonnull JComponent container, @Nonnull Component target) {
Rectangle r = convertRectangle(target.getParent(), target.getBounds(), container);
scrollToVisible(robot, container, checkNotNull(r));
}
/**
* Scrolls a rectangular region of a {@code JComponent} into view.
*
* @param robot simulates user input.
* @param c the {@code JComponent}.
* @param rectangle the rectangular region.
*/
private static void scrollToVisible(@Nonnull Robot robot, final @Nonnull JComponent c,
final @Nonnull Rectangle rectangle) {
execute(() -> c.scrollRectToVisible(rectangle));
robot.waitForIdle();
}
private Scrolling() {
}
}