ControlsFX: убедитесь, что стрелка PopOver всегда указывает на правильное место - PullRequest
0 голосов
/ 11 октября 2018

Я использую PopOver из ControlsFX, в TableView Если я запускаю startEdit ячейки, она должна вытолкнуть PopOver.Эта часть работает, проблема в том, что стрелка, указывающая на строку, не всегда находится в нужном месте.Если я выбираю строку из таблицы, которая находится внизу таблицы, она указывает на ячейку над ней.

Мне нужна эта стрелка, чтобы каждый раз указывать на правую ячейку в TableView.

ControlsFX , версия: 8.40.14

Как мне решить эту проблему?

Вот код, где вы можете увидеть, как это работает:

package stackoverflow.popover;

import com.sun.deploy.util.StringUtils;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.controlsfx.control.PopOver;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    @FXML
    private TableView<Model> table;
    @FXML
    private TableColumn<Model, ObservableList<String>> listCell;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Model model = new Model(FXCollections.observableArrayList("Apple", "Peach"));

        ObservableList<Model> items = FXCollections.observableArrayList();
        for (int i = 0; i < 50; i++) {
            items.add(model);
        }

        table.setItems(items);
        table.setEditable(true);
        listCell.setCellFactory(factory -> new ListTableCell(
                FXCollections.observableArrayList("Apple", "Orange", "Peach", "Banana", "Lemon", "Lime")));
        listCell.setCellValueFactory(data -> data.getValue().list);
    }

    private class ListTableCell extends TableCell<Model, ObservableList<String>> {

        private ObservableList<String> allItems;

        ListTableCell(ObservableList<String> allItems) {
            this.allItems = allItems;
        }

        @Override
        public void startEdit() {
            super.startEdit();
            PopOver popOver = new PopOver();
            popOver.setAutoHide(true);
            PopupController sc = new PopupController(allItems, new ArrayList<>(getItem()));
            popOver.setContentNode(new StackPane(sc.getPane()));
            popOver.setOnHiding(event -> commitEdit(sc.getItems()));
            popOver.show(this);
        }

        @Override
        protected void updateItem(ObservableList<String> item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
            } else {
                setText(StringUtils.join(item, ","));
            }
        }
    }

    private class Model {

        ListProperty<String> list;

        public Model(ObservableList<String> list) {
            this.list = new SimpleListProperty<>(list);
        }
    }

    private class PopupController {

        private BorderPane pane = new BorderPane();

        private ListView<String> left = new ListView<>();
        private ListView<String> right = new ListView<>();

        private Button toLeft = new Button("<");
        private Button toRight = new Button(">");

        PopupController(List<String> all, List<String> selected) {

            VBox leftBox = new VBox();
            leftBox.setSpacing(5);
            leftBox.getChildren().add(toRight);
            leftBox.getChildren().add(left);
            pane.setLeft(leftBox);

            VBox rightBox = new VBox();
            rightBox.setSpacing(5);
            rightBox.getChildren().add(toLeft);
            rightBox.getChildren().add(right);
            pane.setRight(rightBox);

            ObservableList<String> allItems = FXCollections.observableArrayList(all);
            allItems.removeAll(selected);

            left.setItems(allItems);
            right.setItems(FXCollections.observableArrayList(selected));

            toLeft.disableProperty().bind(right.getSelectionModel().selectedItemProperty().isNull());
            toRight.disableProperty().bind(left.getSelectionModel().selectedItemProperty().isNull());

            toLeft.setOnAction(event -> {
                String str = right.getSelectionModel().getSelectedItem();
                right.getItems().remove(str);
                left.getItems().add(str);
            });

            toRight.setOnAction(event -> {
                String str = left.getSelectionModel().getSelectedItem();
                left.getItems().remove(str);
                right.getItems().add(str);
            });
        }

        BorderPane getPane() {
            return pane;
        }

        ObservableList<String> getItems() {
            return right.getItems();
        }
    }

}

Вот два скриншота, чтобы показать, что я имею в виду:

enter image description here enter image description here Это даже хуже: (с setAutoFix(false)) enter image description here

Ответы [ 2 ]

0 голосов
/ 03 ноября 2018

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

    @Override
    public void startEdit() {
        super.startEdit();
        PopOver popOver = new PopOver();
        popOver.setAutoHide(true);
        // first set auto fix to false 
        // to manually set the arrow location
        popOver.setAutoFix(false);   
        PopupController sc = new PopupController(allItems, new ArrayList<>(getItem()));

        // set a specific height for our pane
        final double paneHeight = 300;

        StackPane popOverPane = new StackPane(sc.getPane());
        popOverPane.setPrefHeight(paneHeight);

        popOver.setContentNode(popOverPane);
        popOver.setOnHiding(event -> commitEdit(sc.getItems()));

        // find coordinates relative to the screen
        Bounds screenBounds = this.localToScreen(this.getBoundsInLocal());

        // get our current y position ( on screen )
        int yPos = (int) screenBounds.getMinY();

        // get screen size 
        Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
        int screenHeight = (int) primaryScreenBounds.getHeight();

        // if the PopOver height + the current position is greater than
        // the max screen's height then set the arrow position to bottom left
        if(screenHeight < yPos + paneHeight) {
            popOver.setArrowLocation(ArrowLocation.LEFT_BOTTOM);
        }

        popOver.show(this);
    }

Используя приведенный выше код, вы увидите некоторые вещи, которые необходимо изменить, и подумайте более тщательно.

  • Во-первых, вам нужно установить определенный размер для вашей StackPane или найти динамический способ его расчета.

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

  • Наконец, вам нужно будет сделать то же самое, когда окно находится на правой стороне экрана, потому что тогда ширина «Popover» будет превышать ширину вашего монитора

0 голосов
/ 11 октября 2018

Попробуйте установить setAutoFix (false) в экземпляре PopOver.Из документации свойства autoFix суперкласса PopOver PopupWindow:

Эта вспомогательная переменная указывает, должно ли при отображении всплывающего окна автоматически корректировать свою позицию так, чтобы оно не оказывалось отключеннымэкран.

...