본문 바로가기

JavaFX

[JavaFX] JavaFX scene builder fxml 연동 메뉴바 생성

fxml에서 MenuBar만 생성하고 controller에서 MenuBar에 Menu를 추가하는 화면을 만들어보려 한다.

menu클릭 시 화면을 탭 형식으로 로드하는 형식으로 화면을 만들 생각이다.

 

 

 

fxml에서 AnchorPane안에 MenuBar을 생성 후 fx:id에 menuBar를 추가한다.

menuItem들은 controller에서 추가할 것이다.

 

<AnchorPane>
  <children>
    <MenuBar fx:id="menuBar" />
  </children>
</Anchorpane>

 

 

fxml에서 생성한 fx:id의 menuBar를 controller에서 변수 선언한다.

Menu를 초기화 하고 메뉴에 들어갈 MenuItem들을 만들고 setOnAction 이벤트를 추가한다.

menuItem을 Menu에 담고 Menu을 fxml에서 만든 menuBar에 담는다.

mItem 이벤트의 createTab메소드는 메뉴를 클릭 시 탭 형식으로 화면을 보여주기 위한 메소드이다.

 

@FXML
private MenuBar menuBar;

private static Map<String, DraggableTab> tabMap = new HashMap<String, DraggableTab>();

@Override
public void initialize(URL location, ResourceBundle resources) {
  Menu m1 = new Menu("메뉴1");
  Menu m2 = new Menu("메뉴2");
  Menu m3 = new Menu("메뉴3");
  
  MenuItem mItem1 = new MenuItem("메뉴1_1");
  mItem1.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent event) {
      createTab("", "", "");
    }
  });
  
  MenuItem mItem2 = new MenuItem("메뉴2_1");
  mItem2.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent event) {
      createTab("", "", "");
    }
  });
  
  MenuItem mItem3 = new MenuItem("메뉴3_1");
  mItem3.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent event) {
      createTab("", "", "");
    }
  });
  
  m1.getItems().addAll(mItem1);
  m2.getItems().addAll(mItem2);
  m3.getItems().addAll(mItem3);
  
  menuBar.getMenus().addAll(m1, m2, m3);
}

 

DraggbleTab을 생성하는 소스이다.

private void createTab(String viewName, String name, String viewPath) {
  try{
    if(!tabMap.containsKey(viewName) || tabMap.get(viewName).getTabPane() == null) {
      tab = new DraggableTab(name);
      view = FXMLLoader.load(getClass().getClassLoader().getResource(viewPath));
      tab.setContent(view);
      
      tabMap.put(viewName, tab);
      
      tabs.getTabs().add(tab);
      tabs.getSelectionModel().select(tab);
      
      tab.setOnClosed(e -> tabMap.remove(view));
    } else {
      tabs.getSelectionModel().select(tabMap.get(viewName));
      tabMap.get(viewName).getTabPane().getScene().getWindow().requestFocus();
    }
  } catch(Exception e) {
  
  }
}

 

 

DraggableTab 클래스 소스이다.

탭을 드래그로 분리 및 이동을 할 수 있도록 도와주는 소스이다.

이 소스를 활용해서 탭형식의 화면을 만들고 분리할 수 있도록 했다.

public DraggableTab(String text) {
  nameLabel = new Label(text);
  setGraphic(nameLabel);
  detachable = true;
  dragStage = new Stage();
  dragStage.initStyle(StageStyle.UNDECORATED);
  StackPane dragStagePane = new StackPane();
  dragStagePane.setStyle("-fx-background-color:#DDDDDD;");
  dragText = new Text(text);
  StackPane.setAlignment(dragText, Pos.CENTER);
  dragStagePane.getChildren().add(dragText);
  dragStage.setScene(new Scene(dragStagePane));
  nameLabel.setOnMouseDragged(new EventHandler<MouseEvent>() {
    #Override
    public void handle(MouseEvent t) {
      dragStage.setWidth(nameLabel.getWidth() + 10);
      dragStage.setHeight(nameLabel.getHeight() + 10);
      dragStage.setX(t.getScreenX());
      dragStage.setY(t.getScreenY());
      dragStage.show();
      Point2D screenPoint = new Point2D(t.getScreenX(), t.getScreenY());
      tabPanes.add(getTabPane());
      InsertData data = getInsertData(screenPoint);
      if(data == null || data.getInsertPane().getTabs().isEmpty()) {
      markerStage.hide();
    }
    else {
      int index = data.getIndex();
      boolean end = false;
      if(index == data.getInsertPane().getTabs().size()) {
      end = true;
      index--;
    }
  Rectangle2D rect = getAbsoluteRect(data.getInsertPane().getTabs().get(index));
  if(end) {
  	markerStage.setX(rect.getMaxX() + 13);
  }
  else {
  	markerStage.setX(rect.getMinX());
  }
  markerStage.setY(rect.getMaxY() + 10);
  markerStage.show();
  }
  }
  });
  nameLabel.setOnMouseReleased(new EventHandler<MouseEvent>() {
    #Override
    public void handle(MouseEvent t) {
    markerStage.hide();
    dragStage.hide();
    if(!t.isStillSincePress()) {
      Point2D screenPoint = new Point2D(t.getScreenX(), t.getScreenY());
      TabPane oldTabPane = getTabPane();
      int oldIndex = oldTabPane.getTabs().indexOf(DraggableTab.this);
      tabPanes.add(oldTabPane);
      InsertData insertData = getInsertData(screenPoint);
      if(insertData != null) {
      	int addIndex = insertData.getIndex();
      	if(oldTabPane == insertData.getInsertPane() && oldTabPane.getTabs().size() == 1) {
      	  return;
      	}
  	  oldTabPane.getTabs().remove(DraggableTab.this);
      if(oldIndex < addIndex && oldTabPane == insertData.getInsertPane()) {
        addIndex--;
      }
  if(addIndex > insertData.getInsertPane().getTabs().size()) {
  	addIndex = insertData.getInsertPane().getTabs().size();
  }
  insertData.getInsertPane().getTabs().add(addIndex, DraggableTab.this);
  insertData.getInsertPane().selectionModelProperty().get().select(addIndex);
  return;
  }
  if(!detachable) {
  	return;
  }
  final Stage newStage = new Stage();
  final TabPane pane = new TabPane();
  tabPanes.add(pane);
  newStage.setOnHiding(new EventHandler<WindowEvent>() {
    #Override
    public void handle(WindowEvent t) {
    	tabPanes.remove(pane);
    }
  });
  getTabPane().getTabs().remove(DraggableTab.this);
  pane.getTabs().add(DraggableTab.this);
  pane.getTabs().addListener(new ListChangeListener<Tab>() {
    #Override
    public void onChanged(ListChangeListener.Change<? extends Tab> change) {
    if(pane.getTabs().isEmpty()) {
    	newStage.hide();
    }
  }
  });
  newStage.setScene(new Scene(pane));
  newStage.initStyle(StageStyle.UTILITY);
  newStage.setX(t.getScreenX());
  newStage.setY(t.getScreenY());
  newStage.show();
  pane.requestLayout();
  pane.requestFocus();
  }
  }
  });
  }
  /**
  * Set whether it's possible to detach the tab from its pane and move it to
  * another pane or another window. Defaults to true.
  * <p>
  * #param detachable true if the tab should be detachable, false otherwise.
  */
  public void setDetachable(boolean detachable) {
  	this.detachable = detachable;
  }
  /**
  * Set the label text on this draggable tab. This must be used instead of
  * setText() to set the label, otherwise weird side effects will result!
  * <p>
  * #param text the label text for this tab.
  */
  public void setLabelText(String text) {
  	nameLabel.setText(text);
  	dragText.setText(text);
  }
  private InsertData getInsertData(Point2D screenPoint) {
  for(TabPane tabPane : tabPanes) {
  Rectangle2D tabAbsolute = getAbsoluteRect(tabPane);
  if(tabAbsolute.contains(screenPoint)) {
  int tabInsertIndex = 0;
  if(!tabPane.getTabs().isEmpty()) {
  Rectangle2D firstTabRect = getAbsoluteRect(tabPane.getTabs().get(0));
  if(firstTabRect.getMaxY()+60 < screenPoint.getY() || firstTabRect.getMinY() > screenPoint.getY()) {
  return null;
  }
  Rectangle2D lastTabRect = getAbsoluteRect(tabPane.getTabs().get(tabPane.getTabs().size() - 1));
  if(screenPoint.getX() < (firstTabRect.getMinX() + firstTabRect.getWidth() / 2)) {
  tabInsertIndex = 0;
  }
  else if(screenPoint.getX() > (lastTabRect.getMaxX() - lastTabRect.getWidth() / 2)) {
  tabInsertIndex = tabPane.getTabs().size();
  }
  else {
  for(int i = 0; i < tabPane.getTabs().size() - 1; i++) {
  Tab leftTab = tabPane.getTabs().get(i);
  Tab rightTab = tabPane.getTabs().get(i + 1);
  if(leftTab instanceof DraggableTab && rightTab instanceof DraggableTab) {
  Rectangle2D leftTabRect = getAbsoluteRect(leftTab);
  Rectangle2D rightTabRect = getAbsoluteRect(rightTab);
  if(betweenX(leftTabRect, rightTabRect, screenPoint.getX())) {
  tabInsertIndex = i + 1;
  break;
  }
  }
  }
  }
  }
  return new InsertData(tabInsertIndex, tabPane);
  }
  }
  return null;
  }
  private Rectangle2D getAbsoluteRect(Control node) {
  return new Rectangle2D(node.localToScene(node.getLayoutBounds().getMinX(), node.getLayoutBounds().getMinY()).getX() + node.getScene().getWindow().getX(),
  node.localToScene(node.getLayoutBounds().getMinX(), node.getLayoutBounds().getMinY()).getY() + node.getScene().getWindow().getY(),
  node.getWidth(),
  node.getHeight());
  }
  private Rectangle2D getAbsoluteRect(Tab tab) {
  Control node = ((DraggableTab) tab).getLabel();
  return getAbsoluteRect(node);
  }
  private Label getLabel() {
  return nameLabel;
  }
  private boolean betweenX(Rectangle2D r1, Rectangle2D r2, double xPoint) {
  double lowerBound = r1.getMinX() + r1.getWidth() / 2;
  double upperBound = r2.getMaxX() - r2.getWidth() / 2;
  return xPoint >= lowerBound && xPoint <= upperBound;
  }
  private static class InsertData {
  private final int index;
  private final TabPane insertPane;
  public InsertData(int index, TabPane insertPane) {
  this.index = index;
  this.insertPane = insertPane;
  }
  public int getIndex() {
  return index;
  }
  public TabPane getInsertPane() {
  return insertPane;
  }
  }
}