Подтвердить что ты не робот

Является ли это единственным способом научить Java Frame чему-то об использовании функции Aero Snap Windows?

Если я минимизирую JFrame, который был привязан к левому краю экрана слева от экрана, нажав кнопку "Свернуть" окна WindowDecoration и исключив его с помощью Alt-Tabbing или щелкнув по нему в панели задач Windows, восстановлен правильно отрезан влево. Хорошо!

Но если я скрою кадр на

setExtendedState( getExtendedState() | Frame.ICONIFIED );

и просмотрите предварительный просмотр, наведя указатель мыши на панель задач Windows, она показывает фрейм неправильно. После unminimizing его Alt-Tabbing или щелкая его в Windows TaskBar, кадр восстанавливается в этом неправильном положении и размере. Фрейм-границы - это "нераскрытые" значения, которые Windows обычно запоминает для восстановления, если вы перетащите кадр из ScreenBorder.

Экранная запись ошибки:

введите описание изображения здесь

Мой вывод состоит в том, что Java не знает об AeroSnap и поставляет неправильные оценки для Windows. (Например, Toolkit.getDefaultToolkit().isFrameStateSupported( Frame.MAXIMIZED_VERT ) ); возвращает false.)

Это мое исправление для ошибки:

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
 * Fix for the "Frame does not know the AeroSnap feature of Windows"-Bug.
 *
 * @author bobndrew 20160106
 */
public class SwingFrameStateWindowsAeroSnapBug extends JFrame
{
  Point     location = null;
  Dimension size     = null;


  public SwingFrameStateWindowsAeroSnapBug( final String title )
  {
    super( title );
    initUI();
  }

  private void initUI()
  {
    setDefaultCloseOperation( EXIT_ON_CLOSE );
    setLayout( new FlowLayout() );
    final JButton minimize = new JButton( "Minimize" );
    final JButton maximize = new JButton( "Maximize" );
    final JButton normal = new JButton( "Normal" );
    add( normal );
    add( minimize );
    add( maximize );
    pack();
    setSize( 200, 200 );


    final ActionListener listener = actionEvent ->
    {
      if ( actionEvent.getSource() == normal )
      {
        setExtendedState( Frame.NORMAL );
      }
      else if ( actionEvent.getSource() == minimize )
      {
        //Size and Location have to be saved here, before the minimizing of an AeroSnapped WindowsWindow leads to wrong values:
        location = getLocation();
        size = getSize();
        System.out.println( "saving location (before iconify) " + size + " and " + location );

        setExtendedState( getExtendedState() | Frame.ICONIFIED );//used "getExtendedState() |" to preserve the MAXIMIZED_BOTH state

        //does not fix the bug; needs a Window-Drag after DeMinimzing before the size is applied:
        //          setSize( size );
        //          setLocation( location );
      }
      else if ( actionEvent.getSource() == maximize )
      {
        setExtendedState( getExtendedState() | Frame.MAXIMIZED_BOTH );
      }
    };

    minimize.addActionListener( listener );
    maximize.addActionListener( listener );
    normal.addActionListener( listener );

    addWindowStateListener( windowEvent ->
    {
      System.out.println( "oldState=" + windowEvent.getOldState() + "  newState=" + windowEvent.getNewState() );

      if ( size != null && location != null )
      {
        if ( windowEvent.getOldState() == Frame.ICONIFIED )
        {
          System.out.println( "Fixing (possibly) wrong size and location on de-iconifying to " + size + " and " + location + "\n" );
          setSize( size );
          setLocation( location );

          //Size and Location should only be applied once. Set NULL to avoid a wrong DeMinimizing of a following Windows-Decoration-Button-Minimize!
          size = null;
          location = null;
        }
        else if ( windowEvent.getOldState() == (Frame.ICONIFIED | Frame.MAXIMIZED_BOTH) )
        {
          System.out.println( "Set size and location to NULL (old values: " + size + " and " + location + ")" );
          //Size and Location does not have to be applied, Java can handle the MAXIMIZED_BOTH state. Set NULL to avoid a wrong DeMinimizing of a following Windows-Decoration-Button-Minimize!
          size = null;
          location = null;
        }
      }

    } );
  }


  public static void main( final String[] args )
  {
    SwingUtilities.invokeLater( new Runnable()
    {
      @Override
      public void run()
      {
        new SwingFrameStateWindowsAeroSnapBug( "AeroSnap and the Frame State" ).setVisible( true );
      }
    } );
  }
}

Это, похоже, работает для всех ситуаций под Windows7, но похоже, что слишком много возится с управлением окон. И я почему-то избегал проверять это под Linux или MacOS, -)

Есть ли лучший способ позволить AeroSnap и Java Frames работать вместе?


Edit:

Я зарегистрировал ошибку в Oracle: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8147840

4b9b3361

Ответ 1

Есть ли лучший способ позволить AeroSnap и Java Frames работать вместе?

Не намного лучше. Прямая настройка расширенного состояния обходит обработку ОС при настройке.

Если вы посмотрите на исходный код JFrame#setExtendedState, вы увидите, что он вызывает метод FramePeer setState. Реализация интерфейса FramePeer JDK JFrame представляет собой класс WFramePeer, который объявляет свой метод setState как native. Итак, вам не повезло, пока Oracle ничего не сделает, или вы используете собственный код (см. Ниже).

К счастью, вам не обязательно гадать с прослушивателями событий и ограничениями кеширования. Скрывать и показывать кадр достаточно, чтобы "reset" был размером до того, что было до минимизации:

public class AeroResize extends JFrame {

    public AeroResize(final String title) {

        super(title);
        initUI();
    }

    private void initUI() {

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        final JButton minimize = new JButton("Minimize");
        final JButton maximize = new JButton("Maximize");
        final JButton normal = new JButton("Normal");
        add(normal);
        add(minimize);
        add(maximize);
        pack();

        minimize.addActionListener(e -> {
            setVisible(false);
            setExtendedState(getExtendedState() | JFrame.ICONIFIED);
            setVisible(true);
//          setLocation(getLocationOnScreen()); // Needed only for the preview. See comments section below.
        });
    }

    public static void main(final String[] args) {

        SwingUtilities.invokeLater(() -> new AeroResize("AeroSnap and the Frame State").setVisible(true));
    }
}

Хотя это имеет побочный эффект, не давая подробного предварительного просмотра содержимого фрейма:

введите описание изображения здесь

Решение с использованием собственного кода

Если вы хотите использовать JNA, вы можете полностью имитировать минимизацию собственной платформы. Вам нужно включить jna.jar и jna-platform.jar в свой путь сборки.

import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;

public class AeroResize extends JFrame {

    public AeroResize(final String title) {

        super(title);
        initUI();
    }

    private void initUI() {

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        final JButton minimize = new JButton("Minimize");
        final JButton maximize = new JButton("Maximize");
        final JButton normal = new JButton("Normal");
        add(normal);
        add(minimize);
        add(maximize);
        pack();

        minimize.addActionListener(e -> {
            HWND windowHandle = new HWND(Native.getComponentPointer(AeroResize.this));
            User32.INSTANCE.CloseWindow(windowHandle);
        });
    }

    public static void main(final String[] args) {

        SwingUtilities.invokeLater(() -> new AeroResize("AeroSnap and the Frame State").setVisible(true));
    }
}

Это довольно объяснительно. Вы получаете указатель на окно и используете нативный CloseWindow (который на самом деле сводит к минимуму, идите на него). Обратите внимание, что минималистический способ, который я написал, приведет к небольшой задержке при первом нажатии кнопки, поскольку экземпляр User32 загружен. Вы можете загрузить его при запуске, чтобы избежать этой первой задержки.

Кредит переходит на принятый ответ здесь.

Ответ 2

Кажется, это ошибка Swing. Отчет об ошибке в базе данных ошибок:

JDK-7029238: componentResize не вызывается, когда форма привязана

В этом отчете ошибка не может быть воспроизведена, теперь вы столкнулись с такой же ошибкой (я думаю, что это то же самое или, по крайней мере, связано), возможно, самое время повторно открыть этот отчет. (Я не нашел никакой другой ссылки на это, поэтому я предполагаю, что он не был исправлен)