Замена Java 9 для SwingUtilities3.setDelegateRepaintManager

Пытаясь перенести код Swing в соответствие с системой модулей Java, я застрял, пытаясь заменить.

У меня есть компонент, который, когда любой из его дочерних элементов запрашивает перерисовку, мне нужно преобразовать регион (в частности, это пытается перенести код org.pbjar.jxlayer.plaf.ext.TransformUIдля всех, кто знает об этом). В настоящее время это делается путем установки диспетчера перерисовки делегата компонента и перехвата вызовов к addDirtyRegion.

Теперь с Java 9 способ сделать это больше не доступен в виде общедоступного API. Исходный код предоставил альтернативный метод, который изначально использовался для более старых версий Java, где SwingUtilities3.setDelegateRepaintManagerне было доступно, что просто заменило глобальную реализацию делегирующей. Он проверяет каждый вызов, содержится ли компонент в фактическом компоненте, который нуждается в преобразовании. Однако это решение отбрасывает все внутренние данные и приводит к сильному мерцанию при изменении размера кадра.

Вот сокращенная версия используемого в настоящее время кода:

      SwingUtilities3.setDelegateRepaintManager(component, new TransformRepaintManger());

...

class TransformRepaintManager extends RepaintManager {

    @Override
    public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
        if (c.isShowing()) {
            Point point = c.getLocationOnScreen();
            SwingUtilities.convertPointFromScreen(point, c);
            Rectangle transformPortRegion = transform(new Rectangle(x + point.x, y + point.y, w, h), c);
            RepaintManager.currentManager(c)
                .addDirtyRegion(c,
                    transformPortRegion.x, transformPortRegion.y,
                    transformPortRegion.width, transformPortRegion.height);
        }
    }
}

и альтернативный подход, который вызывает мерцание ( DelegateRepaintManager просто берет оригинал RepaintManager и перенаправляет на него все звонки):

      class TransformRPMFallBack extends DelegateRepaintManager {

    @Override
    public void addDirtyRegion(JComponent aComponent,int x, int y, int w, int h) {
        if (aComponent.isShowing()) {
            JComponent targetParent = findTargetParent(aComponent);
            if (targetParent != null) {
                Point point = aComponent.getLocationOnScreen();
                SwingUtilities.convertPointFromScreen(point, targetParent);
                Rectangle transformPortRegion = transform(new Rectangle(x + point.x, y + point.y, w, h));
                addDirtyRegion(targetParent,
                               transformPortRegion.x, transformPortRegion.y,
                               transformPortRegion.width, transformPortRegion.height);
                return;
            }
        }
        super.addDirtyRegion(aComponent, x, y, w, h);
    }
}

Я знаю, что можно просто добавить --add-exports java.desktop/com.sun.java.swing=<module name> к параметрам запуска, но поскольку это предназначено для библиотеки, заставляющей всех, кто ее использует, делать это, на мой взгляд, не лучший вариант.

Обновление: вот пример, демонстрирующий два вышеуказанных подхода. Он состоит из панели, которая поворачивается на 90 градусов в. Переключение флажков окрашивает левую (визуально верхнюю) или правую (визуально нижнюю) часть компонента в другой цвет. Различные подходы можно изменить, установив статический vairable в TransformLayerUI. Можно наблюдать следующее поведение:

  • : Установка флажков ведет себя должным образом. Изменение размера окна приводит к мерцанию (для такого небольшого примера это не очень плохо, но для больших приложений становится намного хуже)
  • SolutionApproach.ILLEGAL: Такой же как SolutionApproach.FLICKERING но без мерцания.
  • SolutionApproach.NONE: При установке флажков перерисовывается только четверть области, которая должна измениться. Это проблема, которую необходимо решить. Если TestPanel (или любые возможные дети) просит перекрасить правильную область JLayer надо перекрасить.
      public class TransformTest {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Transform Test");

            JPanel contentPanel = new JPanel(new BorderLayout());
            TestPanel testPanel = new TestPanel();
            JLayer<TestPanel> testLayer = new JLayer<>(testPanel, new TransformLayerUI());
            contentPanel.add(testLayer);
            frame.getContentPane().add(contentPanel, BorderLayout.CENTER);

            JCheckBox leftCheck = new JCheckBox("Left active");
            leftCheck.addActionListener(e -> testPanel.setLeftActive(leftCheck.isSelected()));
            JCheckBox rightCheck = new JCheckBox("Right active");
            rightCheck.addActionListener(e -> testPanel.setRightActive(rightCheck.isSelected()));
            JComponent buttonPanel = Box.createHorizontalBox();
            buttonPanel.add(leftCheck);
            buttonPanel.add(rightCheck);
            frame.getContentPane().add(buttonPanel, BorderLayout.SOUTH);

            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

    @SuppressWarnings("unchecked")
    static class TransformLayerUI extends LayerUI<TestPanel> {

        enum SolutionApproach {
            ILLEGAL,
            FLICKERING,
            NONE
        }

        static SolutionApproach approach = SolutionApproach.ILLEGAL;

        @Override
        public void installUI(JComponent c) {
            super.installUI(c);
            switch (approach) {
                case ILLEGAL:
                    SwingUtilities3.setDelegateRepaintManager(((JLayer<? extends JComponent>) c).getView(),
                            new TransformRepaintManager());
                    break;
                case FLICKERING:
                    if (!(RepaintManager.currentManager(c) instanceof FallbackTransformRepaintManger)) {
                        RepaintManager.setCurrentManager(
                                new FallbackTransformRepaintManger(RepaintManager.currentManager(c)));
                    }
                    break;
                case NONE:
                    break;
            }
        }

        private AffineTransform calcTransform(Dimension size) {
            AffineTransform at = new AffineTransform();
            Point2D center = new Point2D.Double(size.getWidth() / 2f, size.getHeight() / 2f);
            at.translate(center.getX(), center.getY());
            at.quadrantRotate(1);
            at.translate(-center.getX(), -center.getY());
            return at;
        }

        private Rectangle transform(Dimension size, final Rectangle rect) {
            Area area = new Area(rect);
            area.transform(calcTransform(size));
            return area.getBounds();
        }

        @Override
        public void paint(Graphics g, JComponent c) {
            ((Graphics2D) g).transform(calcTransform(c.getSize()));
            super.paint(g, c);
        }

        @Override
        public void doLayout(JLayer<? extends TestPanel> l) {
            l.getView().setBounds(transform(l.getSize(), new Rectangle(l.getSize())));
        }

        @Override
        public Dimension getPreferredSize(JComponent c) {
            return transform(((JLayer<? extends JComponent>) c).getView().getPreferredSize());
        }

        @Override
        public Dimension getMaximumSize(JComponent c) {
            return transform(((JLayer<? extends JComponent>) c).getView().getMaximumSize());
        }

        @Override
        public Dimension getMinimumSize(JComponent c) {
            return transform(((JLayer<? extends JComponent>) c).getView().getMinimumSize());
        }

        private Dimension transform(final Dimension size) {
            Area area = new Area(new Rectangle2D.Double(0, 0, size.getWidth(), size.getHeight()));
            area.transform(calcTransform(size));
            Rectangle2D bounds = area.getBounds2D();
            size.setSize(bounds.getWidth(), bounds.getHeight());
            return size;
        }

        class TransformRepaintManager extends RepaintManager {
            @Override
            public void addInvalidComponent(JComponent invalidComponent) {
                Container layer = SwingUtilities.getAncestorOfClass(JLayer.class, invalidComponent);
                RepaintManager.currentManager(layer).addInvalidComponent((JComponent) layer);
            }

            @Override
            public void addDirtyRegion(JComponent comp, int x, int y, int w, int h) {
                if (comp.isShowing()) {
                    Container layer = SwingUtilities.getAncestorOfClass(JLayer.class, comp);
                    dispatchRepaint(comp, layer, TransformLayerUI.this, new Rectangle(x, y, w, h));
                }
            }
        }

        static void dispatchRepaint(Component comp, Component layer, TransformLayerUI ui, Rectangle rect) {
            Point point = comp.getLocationOnScreen();
            SwingUtilities.convertPointFromScreen(point, layer);
            Rectangle transformPortRegion =
                    ui.transform(layer.getSize(),
                            new Rectangle(rect.x + point.x, rect.y + point.y, rect.width, rect.height));
            RepaintManager.currentManager(layer).addDirtyRegion((JComponent) layer,
                    transformPortRegion.x, transformPortRegion.y,
                    transformPortRegion.width, transformPortRegion.height);
        }

        static class FallbackTransformRepaintManger extends DelegateRepaintManager {

            FallbackTransformRepaintManger(RepaintManager delegate) {
                super(delegate);
            }

            @Override
            public void addDirtyRegion(JComponent aComponent, int x, int y, int w, int h) {
                if (aComponent.isShowing()) {
                    JLayer<?> layer = (JLayer<?>) SwingUtilities.getAncestorOfClass(JLayer.class, aComponent);
                    if (layer != null) {
                        LayerUI<?> layerUI = layer.getUI();
                        if (layerUI instanceof TransformLayerUI) {
                            TransformLayerUI ui = (TransformLayerUI) layerUI;
                            dispatchRepaint(aComponent, layer, ui, new Rectangle(x, y, w, h));
                            return;
                        }
                    }
                }
                super.addDirtyRegion(aComponent, x, y, w, h);
            }
        }

        static class DelegateRepaintManager extends RepaintManager {
            private final RepaintManager delegate;

            DelegateRepaintManager(RepaintManager delegate) {
                this.delegate = delegate;
            }

            @Override
            public void addInvalidComponent(JComponent invalidComponent) {
                delegate.addInvalidComponent(invalidComponent);
            }

            @Override
            public void removeInvalidComponent(JComponent component) {
                delegate.removeInvalidComponent(component);
            }

            @Override
            public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
                delegate.addDirtyRegion(c, x, y, w, h);
            }

            @Override
            public void addDirtyRegion(Window window, int x, int y, int w, int h) {
                delegate.addDirtyRegion(window, x, y, w, h);
            }

            @Override
            @Deprecated
            public void addDirtyRegion(Applet applet, int x, int y, int w, int h) {
                delegate.addDirtyRegion(applet, x, y, w, h);
            }

            @Override
            public Rectangle getDirtyRegion(final JComponent c) {
                return delegate.getDirtyRegion(c);
            }

            @Override
            public void markCompletelyDirty(final JComponent c) {
                delegate.markCompletelyDirty(c);
            }

            @Override
            public boolean isCompletelyDirty(final JComponent c) {
                return delegate.isCompletelyDirty(c);
            }

            @Override
            public Dimension getDoubleBufferMaximumSize() {
                return delegate.getDoubleBufferMaximumSize();
            }

            @Override
            public void markCompletelyClean(final JComponent c) {
                delegate.markCompletelyClean(c);
            }

            public RepaintManager getDelegateManager() {
                return delegate;
            }

            @Override
            public void setDoubleBufferMaximumSize(final Dimension d) {
                delegate.setDoubleBufferMaximumSize(d);
            }

            @Override
            public void validateInvalidComponents() {
                delegate.validateInvalidComponents();
            }

            @Override
            public void paintDirtyRegions() {
                delegate.paintDirtyRegions();
            }

            @Override
            public Image getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
                return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight);
            }

            @Override
            public Image getVolatileOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
                return delegate.getVolatileOffscreenBuffer(c, proposedWidth, proposedHeight);
            }

            @Override
            public boolean isDoubleBufferingEnabled() {
                return delegate.isDoubleBufferingEnabled();
            }

            @Override
            public void setDoubleBufferingEnabled(final boolean flag) {
                delegate.setDoubleBufferingEnabled(flag);
            }
        }
    }

    static class TestPanel extends JPanel {

        private boolean leftActive;
        private boolean rightActive;

        TestPanel() {
            setPreferredSize(new Dimension(600, 300));
            setMinimumSize(new Dimension(600, 300));
            setMaximumSize(new Dimension(600, 300));
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (leftActive) {
                g.setColor(Color.RED);
                g.fillRect(0, 0, getWidth() / 2, getHeight());
            }
            if (rightActive) {
                g.setColor(Color.GREEN);
                g.fillRect(getWidth() / 2, 0, getWidth() / 2, getHeight());
            }
            g.setColor(Color.BLACK);
            g.drawString("Left", (getWidth() - 50) / 4, getHeight() / 2 + 10);
            g.drawString("Right", getWidth() / 2 + (getWidth() - 50) / 4, getHeight() / 2 + 10);
        }

        public void setLeftActive(boolean leftActive) {
            this.leftActive = leftActive;
            repaint(0, 0, getWidth() / 2, getHeight());
        }

        public void setRightActive(boolean rightActive) {
            this.rightActive = rightActive;
            repaint(getWidth() / 2, 0, getWidth() / 2, getHeight());
        }
    }
}

0 ответов

Другие вопросы по тегам