JavaFX "Frames" in JavaFx - passende Komponente?

temi

Top Contributor
Hat jemand einen Tipp für mich, wie ich in JavaFx "Frames", die vom Benutzer frei platzier- und in der Größe veränderbar sein sollen, realisieren kann?

Der Benutzer soll einen Frame anklicken können und anschließend Handles (siehe blauer Frame im Bild) für die gewünschte Operation erhalten, so wie man es aus vielen Anwendungen kennt.

1655718962261.png
Ich bin dankbar für jeden Tipp oder Suchvorschlag.
 
Das klingt schon nach einem passenden Ansatz. Du kannst das Ganze natürlich auch selbst coden ohne zusätzliche Panes anzulegen. Über das Hintergrundbild wird ein einzelner Layer (Könnte sein, dass Glasspane nur für das komplette Fenster funktioniert, sonst würde ich das empfehlen) gelegt, auf den die Rahmen gezeichnet werden, der reagiert dann auch auf die Maus-Eingaben
Ist dann halt etwas Fummelarbeit, das richtige Objekt zu ermitteln (wenn mehrere übereinanderliegen), Drag& Drop der Eckpunkte muss selbst implementiert werden. Dafür hat man mehr Freiheiten.
 
naja du klickst

hast deinen mouse input von ( 10 , 10 ) und ziehst von der mouse zu ( -10 , -10 ) und lässt los

( wäre als drag oder mouse pressed und released gedacht ) und dann hast du sowas
Java:
var rect = new Rectangle();

linke obere ecke = was von mouse input anfang oder ende die linke obere ecke ist
rect.xProperty() = linke obere ecke .x
rect.yProperty() = linke obere ecke .y

rect.height = der andere input .y - linke obere ecke.y
rect.width = der andere input .x - linke obere ecke .x
du musst halt herausfinden was vom rechteck die linke obere ecke ist... ob es der mouse release war oder der mouse press

die "punkte zum draggen" wären ja dann einfach nur circles die deine height bzw width verändern

kannst dir ja ein widget bauen in fxml das die punkte hat und das rechteck
 
Kurzes Update zum aktuellen Stand:

Hier ist meine Lösung, die für meine Zwecke erst mal ausreichend gut funktioniert. Es gibt noch ein Problem, wenn ein kleinerer Frame vollständig "hinter" einem größeren Frame liegt, weil es sich dann nicht auswählen lässt. Darum kümmere ich mich später mal.
Java:
public class Frame extends Rectangle {

    private final static int RESIZE_MARGIN = 10;
    private final static double VIEW_ORDER_BACK = 0;
    private final static double VIEW_ORDER_FRONT = -1;
    private final static Effect UNFOCUSED_EFFECT = null;
    private final static Effect FOCUSED_EFFECT = new DropShadow();
    private double diffX;
    private double diffY;


    public Frame(double width, double height, Color color) {
        super(width, height, Color.TRANSPARENT);
        setStroke(color);
        setStrokeType(StrokeType.INSIDE);
        //setPickOnBounds(true);
        setFocusTraversable(true);
        setOnMouseMoved(this::onMouseMoved);
        setOnMousePressed(this::onMousePressed);
        setOnMouseDragged(this::onMouseDragged);
        focusedProperty().addListener(this::onFocusChange);
    }

    private void onFocusChange(ObservableValue<? extends Boolean> observable, boolean oldValue, boolean newValue) {
        if (newValue) {
            setViewOrder(VIEW_ORDER_FRONT);
            setEffect(FOCUSED_EFFECT);
        } else {
            setViewOrder(VIEW_ORDER_BACK);
            setMouseTransparent(false);
            setCursor(Cursor.DEFAULT);
            setEffect(UNFOCUSED_EFFECT);
        }
    }

    private void onMouseMoved(MouseEvent event) {
        if (isFocused()) {
            setCursor(getCursor(event));
        }
    }

    private void onMousePressed(MouseEvent event) {
        requestFocus();
        setCursor(getCursor(event));
        diffX = event.getSceneX() - getLayoutX();
        diffY = event.getSceneY() - getLayoutY();
    }

    private void onMouseDragged(MouseEvent event) {
        if (!event.isPrimaryButtonDown()) return;

        if (isMoving()) {
            relocate(Math.min(Math.max(event.getSceneX() - diffX, 0), getParent().getLayoutBounds().getWidth() - getWidth()),
                     Math.min(Math.max(event.getSceneY() - diffY, 0), getParent().getLayoutBounds().getHeight() - getHeight()));
        } else {
            resize(event);
        }
    }

    private boolean isMoving() {
        return Cursor.MOVE.equals(getCursor());
    }

    private void resize(MouseEvent event) {
        if (Cursor.E_RESIZE.equals(getCursor()) ||
            Cursor.NE_RESIZE.equals(getCursor()) ||
            Cursor.SE_RESIZE.equals(getCursor())) resizeE(event);

        if (Cursor.S_RESIZE.equals(getCursor()) ||
            Cursor.SW_RESIZE.equals(getCursor()) ||
            Cursor.SE_RESIZE.equals(getCursor())) resizeS(event);

        if (Cursor.N_RESIZE.equals(getCursor()) ||
            Cursor.NE_RESIZE.equals(getCursor()) ||
            Cursor.NW_RESIZE.equals(getCursor())) resizeN(event);

        if (Cursor.W_RESIZE.equals(getCursor()) ||
            Cursor.NW_RESIZE.equals(getCursor()) ||
            Cursor.SW_RESIZE.equals(getCursor())) resizeW(event);

    }

    private void resizeE(MouseEvent event) {
        setWidth(event.getSceneX() - getLayoutX());
        setWidth(event.getSceneX() - getLayoutX());
    }

    private void resizeS(MouseEvent event) {
        setHeight(event.getSceneY() - getLayoutY());
        setHeight(event.getSceneY() - getLayoutY());
    }

    private void resizeN(MouseEvent event) {
        double diff = getLayoutY() - event.getSceneY();
        setLayoutY(event.getSceneY());
        setHeight(getHeight() + diff);
    }

    private void resizeW(MouseEvent event) {
        double diff = getLayoutX() - event.getSceneX();
        setLayoutX(event.getSceneX());
        setWidth(getWidth() + diff);
    }

    private Cursor getCursor(MouseEvent event) {
        if (event.getY() > (getHeight() - RESIZE_MARGIN)) {
            if (event.getX() > (getWidth() - RESIZE_MARGIN)) {
                return Cursor.SE_RESIZE;
            }

            if (event.getX() < RESIZE_MARGIN) {
                return Cursor.SW_RESIZE;
            }

            return Cursor.S_RESIZE;
        }

        if (event.getY() < RESIZE_MARGIN) {
            if (event.getX() > (getWidth() - RESIZE_MARGIN)) {
                return Cursor.NE_RESIZE;
            }

            if (event.getX() < RESIZE_MARGIN) {
                return Cursor.NW_RESIZE;
            }

            return Cursor.N_RESIZE;
        }

        if (event.getX() < RESIZE_MARGIN) {
            return Cursor.W_RESIZE;
        }

        if (event.getX() > (getWidth() - RESIZE_MARGIN)) {
            return Cursor.E_RESIZE;
        }

        return Cursor.MOVE;
    }
}
 
du kannst die mouse events transparent machen damit die maus klicks durch gehen durch nodes.. ansonsten entscheidet der node stapel wer das event kriegt
 
Da ich gerade etwas mit Kotlin experimentiere, hier die Kotlin-Variante, inkl. Erreichbarkeit von verdeckten Elementen (mit gedrückter Strg-Taste).
Java:
class Frame(width: Double, height: Double, color: Color) : Rectangle(width, height, Color.TRANSPARENT) {

    private val RESIZE_MARGIN = 10.0
    private val VIEW_ORDER_BACK = 0.0
    private val VIEW_ORDER_FRONT = -1.0
    private val UNFOCUSED_EFFECT: Effect? = null
    private val FOCUSED_EFFECT: Effect = DropShadow()

    private var xDiff = 0.0
    private var yDiff = 0.0

    init {
        stroke = color
        strokeType = StrokeType.INSIDE

        onMouseMoved = EventHandler<MouseEvent> {
            if (isFocused) cursor = getCursor(it.x, it.y)
        }

        onMousePressed = EventHandler<MouseEvent> {
            mousePressed(it)
        }

        onMouseDragged = EventHandler<MouseEvent> {
            mouseDragged(it)
        }

        focusedProperty().addListener { observable: ObservableValue<out Boolean>, oldValue: Boolean, newValue: Boolean ->
            onFocusChange(observable, oldValue, newValue)
        }

        onKeyPressed = EventHandler<KeyEvent> {
            isMouseTransparent = it.isControlDown
        }

        onKeyReleased = EventHandler<KeyEvent> {
            isMouseTransparent = false
        }
    }

    private fun mousePressed(event: MouseEvent) {
        requestFocus()
        cursor = getCursor(event.x, event.y)
        xDiff = event.sceneX - layoutX
        yDiff = event.sceneY - layoutY
    }

    private fun mouseDragged(event: MouseEvent) {
        if (!event.isPrimaryButtonDown) return

        if (isMoving()) {
            relocate(min(max(event.sceneX - xDiff, 0.0), parent.layoutBounds.width - width),
                     min(max(event.sceneY - yDiff, 0.0), parent.layoutBounds.height - height));
        } else {
            doResize(event.sceneX, event.sceneY)
        }
    }

    private fun onFocusChange(observable: ObservableValue<out Boolean>, oldValue: Boolean, newValue: Boolean) {
        if (newValue) {
            viewOrder = VIEW_ORDER_FRONT
            effect = FOCUSED_EFFECT
        } else {
            viewOrder = VIEW_ORDER_BACK
            effect = UNFOCUSED_EFFECT
            cursor = Cursor.DEFAULT
            isMouseTransparent = false
        }
    }

    private fun getCursor(x: Double, y: Double): Cursor {
        if (y > height - RESIZE_MARGIN) {
            if (x > width - RESIZE_MARGIN) return Cursor.SE_RESIZE
            return if (x < RESIZE_MARGIN) Cursor.SW_RESIZE else Cursor.S_RESIZE
        }

        if (y < RESIZE_MARGIN) {
            if (x > width - RESIZE_MARGIN) return Cursor.NE_RESIZE
            return if (x < RESIZE_MARGIN) Cursor.NW_RESIZE else Cursor.N_RESIZE
        }

        if (x < RESIZE_MARGIN) return Cursor.W_RESIZE

        if (x > width - RESIZE_MARGIN) return Cursor.E_RESIZE

        return Cursor.MOVE
    }

    private fun isMoving(): Boolean = Cursor.MOVE == cursor

    private fun doResize(x: Double, y: Double) {
        if (Cursor.E_RESIZE == cursor || Cursor.NE_RESIZE == cursor || Cursor.SE_RESIZE == cursor) {
            width = x - layoutX
            width = x - layoutX
        }

        if (Cursor.S_RESIZE == cursor || Cursor.SW_RESIZE == cursor || Cursor.SE_RESIZE == cursor) {
            height = y - layoutY
            height = y - layoutY
        }

        if (Cursor.N_RESIZE == cursor || Cursor.NE_RESIZE == cursor || Cursor.NW_RESIZE == cursor) {
            height = height + layoutY - y
            layoutY = y
        }

        if (Cursor.W_RESIZE == cursor || Cursor.NW_RESIZE == cursor || Cursor.SW_RESIZE == cursor) {
            width = width + layoutX - x
            layoutX = x
        }
    }
}

Kotlin ist gar nicht in der Liste der Sprachen verfügbar. Ist halt ein astreines Java-Forum hier 🙂
 
Zuletzt bearbeitet:
Da ich gerade etwas mit Kotlin experimentiere, hier die Kotlin-Variante, inkl. Erreichbarkeit von verdeckten Elementen (mit gedrückter Strg-Taste).
Java:
class Frame(width: Double, height: Double, color: Color) : Rectangle(width, height, Color.TRANSPARENT) {

    private val RESIZE_MARGIN = 10.0
    private val VIEW_ORDER_BACK = 0.0
    private val VIEW_ORDER_FRONT = -1.0
    private val UNFOCUSED_EFFECT: Effect? = null
    private val FOCUSED_EFFECT: Effect = DropShadow()

    private var xDiff = 0.0
    private var yDiff = 0.0

    init {
        stroke = color
        strokeType = StrokeType.INSIDE

        onMouseMoved = EventHandler<MouseEvent> {
            if (isFocused) cursor = getCursor(it.x, it.y)
        }

        onMousePressed = EventHandler<MouseEvent> {
            mousePressed(it)
        }

        onMouseDragged = EventHandler<MouseEvent> {
            mouseDragged(it)
        }

        focusedProperty().addListener { observable: ObservableValue<out Boolean>, oldValue: Boolean, newValue: Boolean ->
            onFocusChange(observable, oldValue, newValue)
        }

        onKeyPressed = EventHandler<KeyEvent> {
            isMouseTransparent = it.isControlDown
        }

        onKeyReleased = EventHandler<KeyEvent> {
            isMouseTransparent = false
        }
    }

    private fun mousePressed(event: MouseEvent) {
        requestFocus()
        cursor = getCursor(event.x, event.y)
        xDiff = event.sceneX - layoutX
        yDiff = event.sceneY - layoutY
    }

    private fun mouseDragged(event: MouseEvent) {
        if (!event.isPrimaryButtonDown) return

        if (isMoving()) {
            relocate(min(max(event.sceneX - xDiff, 0.0), parent.layoutBounds.width - width),
                     min(max(event.sceneY - yDiff, 0.0), parent.layoutBounds.height - height));
        } else {
            doResize(event.sceneX, event.sceneY)
        }
    }

    private fun onFocusChange(observable: ObservableValue<out Boolean>, oldValue: Boolean, newValue: Boolean) {
        if (newValue) {
            viewOrder = VIEW_ORDER_FRONT
            effect = FOCUSED_EFFECT
        } else {
            viewOrder = VIEW_ORDER_BACK
            effect = UNFOCUSED_EFFECT
            cursor = Cursor.DEFAULT
            isMouseTransparent = false
        }
    }

    private fun getCursor(x: Double, y: Double): Cursor {
        if (y > height - RESIZE_MARGIN) {
            if (x > width - RESIZE_MARGIN) return Cursor.SE_RESIZE
            return if (x < RESIZE_MARGIN) Cursor.SW_RESIZE else Cursor.S_RESIZE
        }

        if (y < RESIZE_MARGIN) {
            if (x > width - RESIZE_MARGIN) return Cursor.NE_RESIZE
            return if (x < RESIZE_MARGIN) Cursor.NW_RESIZE else Cursor.N_RESIZE
        }

        if (x < RESIZE_MARGIN) return Cursor.W_RESIZE

        if (x > width - RESIZE_MARGIN) return Cursor.E_RESIZE

        return Cursor.MOVE
    }

    private fun isMoving(): Boolean = Cursor.MOVE == cursor

    private fun doResize(x: Double, y: Double) {
        if (Cursor.E_RESIZE == cursor || Cursor.NE_RESIZE == cursor || Cursor.SE_RESIZE == cursor) {
            width = x - layoutX
            width = x - layoutX
        }

        if (Cursor.S_RESIZE == cursor || Cursor.SW_RESIZE == cursor || Cursor.SE_RESIZE == cursor) {
            height = y - layoutY
            height = y - layoutY
        }

        if (Cursor.N_RESIZE == cursor || Cursor.NE_RESIZE == cursor || Cursor.NW_RESIZE == cursor) {
            height = height + layoutY - y
            layoutY = y
        }

        if (Cursor.W_RESIZE == cursor || Cursor.NW_RESIZE == cursor || Cursor.SW_RESIZE == cursor) {
            width = width + layoutX - x
            layoutX = x
        }
    }
}

Kotlin ist gar nicht in der Liste der Sprachen verfügbar. Ist halt ein astreines Java-Forum hier 🙂
warum keine bindings? tornadofx sollte die auch haben
 
du hast sozusagen sowas wie eine schnittstelle die dir es möglich macht die klassen aus javafx zu nutzen
Java:
  fun horizontalStreetPane(): StackPane {
        return stackpane {
            rectangle {
                width = 100.0
                height = 100.0
            }
            rectangle {
                width = 100.0
                height= 40.0
            }
        }
    }
}
Code:
private StackPane horizontal(){
    var pane = new StackPane();
    pane.getChildren().addAll(new Rectangle...)
}
nur du kannst halt die properties der objekte mit kotlin syntax setzen ... ansich halt eine übertragung von javafx von java nach kotlin
 

Zurück
Oben