¿Es esta la única manera de enseñar a un marco Java algo sobre la función Aero Snap de Windows?


Si minimizo un JFrame que fue Aero-ajustado a la izquierda de la pantalla haciendo clic en el botón minimizar de la decoración de ventanas de Windows y unminimizarlo mediante Alt-Tab o haciendo clic en la barra de tareas de Windows, el marco se restaura correctamente ajustado a la izquierda. ¡Bien!

Pero si minimizo el marco por

setExtendedState( getExtendedState() | Frame.ICONIFIED );

Y mira la vista previa al pasar el cursor sobre la barra de tareas de Windows, muestra el marco en una posición incorrecta. Después de unminimizing que Alt-Tabbing o haga clic en él en la barra de tareas de Windows, el marco se restaura en esta posición y tamaño incorrectos. Los límites del marco son los valores "no asignados", que Windows normalmente recuerda restaurar si arrastra el marco lejos del ScreenBorder.

Una grabación de pantalla del error:

introduzca la descripción de la imagen aquí

Mi conclusión es, que Java no sabe acerca de AeroSnap y entrega los límites incorrectos a Windows. (Por ejemplo Toolkit.getDefaultToolkit().isFrameStateSupported( Frame.MAXIMIZED_VERT ) ); devuelve false.)

Esta es mi corrección para el error:

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 );
      }
    } );
  }
}

Esto parece funcionar para todas las situaciones bajo Windows7, pero se siente como demasiado jugando con la gestión de ventanas. Y evité probar esto bajo Linux o macOS por alguna razón; -)

¿Hay una mejor manera de permitir que AeroSnap y Java Frames trabajen juntos?


Editar:

He presentado un error en Oracle: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8147840

Author: bobndrew, 2016-01-06

2 answers

¿Hay una mejor manera de permitir que AeroSnap y Java Frames trabajen juntos?

No mucho mejor. Establecer directamente el estado extendido evita el tratamiento del sistema operativo de configurarlo.

Si echas un vistazo al código fuente de JFrame#setExtendedState verás que llama al método FramePeer's setState. La implementación JFrame de la interfaz FramePeer del JDK es la clase WFramePeer, que declara su método setState como native. Así que no tienes suerte hasta que Oracle haga algo. acerca de él o se utiliza el código nativo (ver más abajo).

Afortunadamente, no necesariamente tiene que volverse loco con los oyentes de eventos y los límites de almacenamiento en caché. Ocultar y mostrar el marco es suficiente para "restablecer" el tamaño a lo que era antes de la minimización:

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));
    }
}

Aunque esto tiene un efecto secundario de no dar una vista previa detallada del contenido del marco:

introduzca la descripción de la imagen aquí

Solución usando código nativo

Si quieres usar JNA , entonces puedes imita completamente la minimización de la plataforma nativa. Necesitarás incluir jna.jar y jna-platform.jar en tu ruta de compilación.

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));
    }
}

Se explica por sí mismo. Se obtiene un puntero a la ventana y se utiliza el nativo CloseWindow (que en realidad minimiza, figura go) en ella. Tenga en cuenta que la forma minimalista en que lo escribí causará un pequeño retraso la primera vez que se presione el botón porque se carga la instancia User32. Puede cargarlo en el inicio para evitar este retraso por primera vez.

El crédito va a la respuesta aceptada aquí.

 6
Author: user1803551,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:23

Esto parece ser un error de Swing. El informe de error en la Base de datos de errores:

JDK-7029238 : componentResized no se llama cuando se rompe el formulario

En este informe el error no se pudo reproducir, ahora que se encontró con el mismo error (creo que es el mismo, o relacionado al menos), tal vez sea un buen momento para volver a abrir este informe. (No encontré ninguna otra referencia a esto, así que asumo que no ha sido arreglado)

 0
Author: Mayuso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-11 13:10:10