Центрировать JavaFX ContextMenu в середине экрана?

Я пытаюсь создать ContextMenu, который появляется в середине экрана всякий раз, когда нажимается кнопка выхода на клавиатуре. Я хочу использовать его для обработки загрузки, сохранения и выхода из моей игры. Он будет вызываться из классов за пределами Main.

Проблема в том, что я не знаю, как узнать высоту и ширину ContextMenu в пикселях. поэтому я не понимаю, как центрировать его в окне. Я пытаюсь использовать Rectangle2D, чтобы найти центр, но он все еще немного смещен.

Как мне отобразить контекстное меню в центре экрана?

Отображает мое ContextMenu:

/**
 * Returns the Pane object so it can be made into a Scene object.
 * Each Scene has it's own class
 * Each class uses this method to create a Pane object for it.
 * @return Pane 
 */
public Pane getPane() {
   // create a GridPane object
   grid = new GridPane();

   // create ContextMenu and set anchor points.
   ContextMenu cm = Main.Org.createPopUpMenu();
   cm.setOnShowing(event -> {
      event.consume();
      cm.setAnchorX(cm.getAnchorX() - cm.getWidth() / 2.0);
      cm.setAnchorY(cm.getAnchorY() - cm.getHeight() / 2.0);
   });
   Rectangle2D bounds = Screen.getPrimary().getVisualBounds();

   // show ContextMenu if escape key is pressed.
   grid.setOnKeyReleased( event -> {
      if(event.getCode() == KeyCode.ESCAPE) {
         cm.show(grid, bounds.getWidth() / 2.0, bounds.getHeight() / 2.0);
      }
   });

   return grid;
}

Метод класса Organizer createPopUpMenu():

/**
 * Creates a pop-up menu for saving, loading, and exiting the game
 * @return ContextMenu 
 */
public ContextMenu createPopUpMenu() {
   ContextMenu contextMenu = new ContextMenu();

   MenuItem menuItem = new MenuItem();

   //create ImageViews
   image = new ImageView("/images/PopUpMenu_Exit.png");

   // add ImageViews to MenuItems
   menuItem.setGraphic(image);

   contextMenu.getItems().add(menuItem);

   menuItem.setOnAction( a -> {
      // quit the game
      Platform.exit();
      System.exit(0);
   });

   return contextMenu;
}

person LuminousNutria    schedule 15.12.2018    source источник
comment
Предоставьте минимально воспроизводимый пример, демонстрирующий проблему.   -  person kleopatra    schedule 15.12.2018
comment
@kleopatra Что, по вашему мнению, можно добавить, чтобы улучшить вопрос? Я мог бы привести пример кода, который можно запустить без изменений, но это будет несколько сотен строк кода.   -  person LuminousNutria    schedule 15.12.2018
comment
несколько сотен строк - это не минимум ;) Никто не хочет пробираться через тонны несвязанного кода, урезать суть до 50 или около того - этого всегда достаточно, чтобы продемонстрировать суть того, что вам нужно, и почему результат не тот, что вы ожидаете.   -  person kleopatra    schedule 15.12.2018
comment
@kleopatra Я попытаюсь это сделать, я не очень разбираюсь в JavaFX, поэтому не уверен, что этого будет достаточно.   -  person LuminousNutria    schedule 15.12.2018
comment
после написания такого примера вы будете более опытны :)   -  person kleopatra    schedule 15.12.2018
comment
@kleopatra Я сократил свои примеры, но при этом сделал сами методы работоспособными, хотя до тех пор, пока их окружает другой код. Я также изменил, чтобы включить мою версию кода, предложенного Slaw. Пожалуйста, дайте мне знать, если есть что-то еще, что я могу сделать, чтобы улучшить вопрос.   -  person LuminousNutria    schedule 15.12.2018
comment
Я не могу воспроизвести проблему. Я даже пытался использовать Node в качестве привязки и иметь изображение в качестве изображения MenuItem, как и вы. Может быть, это поможет, если вы опубликуете скриншот проблемы; бонус, если вы включите графику, показывающую, где вы хотите, чтобы ContextMenu был на самом деле. Возможно, то, что я считаю центрированным, не совсем то же самое, что вы считаете центрированным.   -  person Slaw    schedule 16.12.2018


Ответы (1)


Вот небольшой пример (некоторые пояснения в комментариях к коду):

import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;

public class Main extends Application {

  private ContextMenu menu;

  @Override
  public void start(Stage primaryStage) {
    primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
      if (event.getCode() == KeyCode.ESCAPE) {
        event.consume();
        showContextMenu(primaryStage);
      }
    });

    Label label = new Label("Press ESC to show ContextMenu.");
    primaryStage.setScene(new Scene(new StackPane(label), 500, 300));
    primaryStage.show();
  }

  private void showContextMenu(Window owner) {
    if (menu == null) {
      menu = new ContextMenu(
          new MenuItem("Copy"),
          new MenuItem("Cut"),
          new MenuItem("Paste"),
          new SeparatorMenuItem(),
          new MenuItem("Delete")
      );

      /*
       * Adjusts the ContextMenu's position once shown so that the center
       * of the ContextMenu is at the center of the screen (rather than
       * the top left corner). Do this here because the ContextMenu needs
       * to have been displayed in order to know the dimensions.
       *
       * Since the top left corner is already centered on the screen we don't need
       * to know the dimensions of the screen. We only need to move the ContextMenu
       * relative to itself.
       */
      menu.setOnShown(event -> {
        event.consume();
        menu.setAnchorX(menu.getAnchorX() - menu.getWidth() / 2.0);
        menu.setAnchorY(menu.getAnchorY() - menu.getHeight() / 2.0);
      });
    }

    /*
     * Displays the ContextMenu on the primary screen. The top left corner
     * of the ContextMenu will be at the center of the screen. Will need
     * to adjust the ContextMenu's position after it's shown.
     */
    Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
    menu.show(owner, bounds.getWidth() / 2.0, bounds.getHeight() / 2.0);
  }

}
person Slaw    schedule 15.12.2018
comment
Спасибо за подробный пример. К сожалению, это не работает для меня. Я думаю, это может быть потому, что я вызываю его из-за пределов основного класса. Я упомяну об этом в своем вопросе. Как вы думаете, есть что-то еще, что я мог бы попробовать? - person LuminousNutria; 15.12.2018
comment
@LuminousNutria Насколько я знаю, откуда вы вызываете этот код, не должно иметь значения. - person Slaw; 15.12.2018
comment
Я не против сделать это, но не могли бы вы предоставить больше информации о том, что вы хотели бы видеть? Я не могу предоставить пример, который можно запустить сам по себе без дополнительных нескольких сотен строк кода. - person LuminousNutria; 15.12.2018
comment
@LuminousNutria минимальный воспроизводимый пример должен быть специально и специально создан для переполнения стека. По сути, это должен быть (запускаемый) макет того, что вы пытаетесь сделать. Большая часть вашего текущего кода, скорее всего, не имеет отношения к рассматриваемой проблеме. Если в вашем стремлении создать MCVE проблема исчезнет, ​​вы даже сможете найти проблему самостоятельно. - person Slaw; 15.12.2018
comment
Это своего рода вопрос, который меня смущает. Я не знаю, как создать запускаемый пример в JavaFX без большого количества кода, который не имеет отношения к рассматриваемой проблеме. Я не всегда уверен, что актуально. - person LuminousNutria; 15.12.2018