2014-11-01 2 views
3

Я фактически работаю над приложением ScalaFX без оформления окна. Что я хочу сделать, так это сделать его перетаскиваемым, чтобы пользователь мог его перемещать.Сделать ScalaFX scene draggable

Мне удалось переместить сцену, просто записав координаты перетаскивающей мышью в координаты X/Y сцены. Однако это привело к отстающему и мерцающему окну.

Как можно перетаскивать сцену плавно в ScalaFX?

+0

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

ответ

2

Вот пример, который хорошо подходит для меня. Он адаптирован из одного примера в книге «JavaFX 8 Введение по примеру». Как это сравнивается с вашей попыткой?

import scalafx.Includes._ 
import scalafx.application.JFXApp 
import scalafx.application.JFXApp.PrimaryStage 
import scalafx.geometry.Point2D 
import scalafx.scene.Scene 
import scalafx.scene.input.MouseEvent 
import scalafx.scene.paint.Color 
import scalafx.stage.{WindowEvent, StageStyle} 

object DraggableApp extends JFXApp { 

    private var anchorPt: Point2D = null 
    private var previousLocation: Point2D = null 

    stage = new PrimaryStage { 
    initStyle(StageStyle.TRANSPARENT) 
    scene = new Scene { 
     fill = Color.rgb(0, 0, 0, 1.0) 
    } 
    } 

    // Initialize stage to be movable via mouse 
    initMovablePlayer() 

    /** 
    * Initialize the stage to allow the mouse cursor to move the application 
    * using dragging. 
    */ 
    private def initMovablePlayer(): Unit = { 
    val scene = stage.getScene 

    scene.onMousePressed = (event: MouseEvent) => anchorPt = new Point2D(event.screenX, event.screenY) 

    scene.onMouseDragged = (event: MouseEvent) => 
     if (anchorPt != null && previousLocation != null) { 
     stage.x = previousLocation.x + event.screenX - anchorPt.x 
     stage.y = previousLocation.y + event.screenY - anchorPt.y 
     } 

    scene.onMouseReleased = (event: MouseEvent) => previousLocation = new Point2D(stage.getX, stage.getY) 

    stage.onShown = (event: WindowEvent) => previousLocation = new Point2D(stage.getX, stage.getY) 
    } 
} 

Edit: О вас вопрос на этапе изменения размера, я попробовал следующий вариант, который изменяет размер сцены при щелчке правой кнопкой мыши и перетаскиванием; Я не вижу мерцания (на OS X).

import javafx.scene.{input => jfxsi} 
// (...) 

object DraggableApp extends JFXApp { 

    private var previousHeight = 0.0 
    private var previousWidth = 0.0 
    // (...) 

    private def initMovablePlayer(): Unit = { 
    val scene = stage.getScene 

    scene.onMousePressed = (event: MouseEvent) => anchorPt = new Point2D(event.screenX, event.screenY) 

    scene.onMouseDragged = (event: MouseEvent) => 
     if (anchorPt != null && previousLocation != null) { 
     if (event.getButton == jfxsi.MouseButton.PRIMARY) { 
      stage.x = previousLocation.x + event.screenX - anchorPt.x 
      stage.y = previousLocation.y + event.screenY - anchorPt.y 
     } else if (event.getButton == jfxsi.MouseButton.SECONDARY) { 
      stage.width = previousWidth + event.screenX - anchorPt.x 
      stage.height = previousHeight + event.screenY - anchorPt.y 
     } 
     } 

    scene.onMouseReleased = (_: MouseEvent) => reset() 

    stage.onShown = (_: WindowEvent) => reset() 

    def reset(): Unit = { 
     previousLocation = new Point2D(stage.getX, stage.getY) 
     previousHeight = stage.getHeight 
     previousWidth = stage.getWidth 
    } 
    } 
} 
+0

Во-первых, спасибо за ответ. Я не уверен, почему это не сработало для меня раньше, может быть, мой ноутбук был в напряжении и отставал, как ад? Ваше решение работает. Тем не менее, ваше решение также работает при удалении previousLocation и anchorPt, просто назначив event.screenX/Y в обработчике onMouseDragged на stage.x, y (по сути, у меня было до того, как я задал этот вопрос Oo). Зачем вам нужно previousLocation и achorPt? – smoes

+0

Просто оффтопический вопрос, если раньше вы работали с незадекларированными окнами: знаете ли вы, как избавиться от мерцания при изменении размера незадекларированной сцены (особенно на больших размерах шагов, например, при увеличении окна)? – smoes

+1

С 'stage.x = event.screenX; stage.y = event.screenY', верхняя левая точка сцены переходит в положение мыши, когда вы начинаете перетаскивание. Эффект более очевиден, если у вас большая сцена, и вы нажимаете возле нижнего правого угла перед тем, как начать перетаскивать. Идея здесь заключается в том, чтобы применить к сцене то же самое движение, которое было применено к курсору мыши, но исходная точка не то же самое (верхний левый угол сцены в исходном положении по сравнению с точкой, в которую вы нажали). Я посмотрю на проблему изменения размера, но я не могу пообещать, не имея большого опыта с этим. –

0

Я знаю, что это был дан ответ уже, но для полноты картины я хотел бы включить, как я уже делал это в прошлом.

Кредиты:
Draggable окно: Pro JavaFX 8 на примере
Resizable Window: Маленький ребенок - Allow user to resize an undecorated Stage
Восстановление вверх/вниз: Undecorator - UndecoratorController.java

Будьте готовы к стене (грязный) код

class UndecoratedWindowHelper(val target: Stage) { 

    var anchorPt: Point2D = null 
    var prevLoc: Point2D = null 
    private var savedBounds: BoundingBox = _ 
    private val _maximized: ReadOnlyBooleanWrapper = new ReadOnlyBooleanWrapper {value = false} 

    // begin init 
    val resizeListener = new ResizeListener(target, this) 
    target.scene.get.addEventHandler(jfxme.MOUSE_MOVED, resizeListener) 
    target.scene.get.addEventHandler(jfxme.MOUSE_PRESSED, resizeListener) 
    target.scene.get.addEventHandler(jfxme.MOUSE_DRAGGED, resizeListener) 
    // end init 

    def addWindowDragPoint(dragNode: Node): Unit = { 
     dragNode.onMousePressed = (event: MouseEvent) => { 
     anchorPt = new Point2D(event.screenX, event.screenY) 
     prevLoc = new Point2D(target.getX, target.getY) 
     } 

     dragNode.onMouseClicked = (event: MouseEvent) => 
     if (event.clickCount == 2) 
      maximizeOrRestore() 

     dragNode.onMouseDragged = (event: MouseEvent) => 
     if (resizeListener.cursorEvent == Cursor.DEFAULT && !maximized) { 
      target.x = prevLoc.x + event.screenX - anchorPt.x 
      target.y = prevLoc.y + event.screenY - anchorPt.y 
     } 

     target.onShown = (event: WindowEvent) => prevLoc = new Point2D(target.getX, target.getY) 
    } 


    def maximizeOrRestore() { 
     if (maximized) { 
     restoreSavedBounds() 
     savedBounds = null 
     _maximized set false 
     } else { 
     val screensForRectangle = Screen.screensForRectangle(target.getX, target.getY, target.getWidth, target.getHeight) 
     val screen = screensForRectangle.get(0) 
     val visualBounds = screen.visualBounds 
     savedBounds = new BoundingBox(target.getX, target.getY, target.getWidth, target.getHeight) 

     target.setX(visualBounds.getMinX) 
     target.setY(visualBounds.getMinY) 
     target.setWidth(visualBounds.getWidth) 
     target.setHeight(visualBounds.getHeight) 
     _maximized set true 
     } 
    } 
    def saveBounds() { 
     savedBounds = new BoundingBox(target.getX, target.getY, target.getWidth, target.getHeight) 
    } 

    def restoreSavedBounds() { 
     target.setX(savedBounds.getMinX) 
     target.setY(savedBounds.getMinY) 
     target.setWidth(savedBounds.getWidth) 
     target.setHeight(savedBounds.getHeight) 
     savedBounds = null 
    } 

    def maximized = _maximized.getValue 
    def maximizedProp = _maximized.getReadOnlyProperty 
} 

//formatter:off 
protected class ResizeListener(
            val stage: Stage, owner: UndecoratedWindowHelper) extends EventHandler[jfxme] { 

    var cursorEvent = Cursor.DEFAULT 
    val border = 4 
    var startX = 0d 
    var startY = 0d 
    //formatter:on 

    def handle(mouseEvent: jfxme) { 
     val mouseEventType = mouseEvent.getEventType 
     val scene = stage.getScene 

     val mouseEventX = mouseEvent.getSceneX 
     val mouseEventY = mouseEvent.getSceneY 
     val sceneWidth = scene.getWidth 
     val sceneHeight = scene.getHeight 

     if (jfxme.MOUSE_MOVED == mouseEventType) { 

     //@formatter:off 
     cursorEvent = 
       if (mouseEventX < border && mouseEventY < border) Cursor.NW_RESIZE 
       else if (mouseEventX < border && mouseEventY > sceneHeight - border) Cursor.SW_RESIZE 
       else if (mouseEventX > sceneWidth - border && mouseEventY < border) Cursor.NE_RESIZE 
       else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) Cursor.SE_RESIZE 
       else if (mouseEventX < border) Cursor.W_RESIZE 
       else if (mouseEventX > sceneWidth - border) Cursor.E_RESIZE 
       else if (mouseEventY < border) Cursor.N_RESIZE 
       else if (mouseEventY > sceneHeight - border) Cursor.S_RESIZE 
       else Cursor.DEFAULT 
     //@formatter:on 
     if (owner.maximized) 
      cursorEvent = Cursor.DEFAULT 


     scene.setCursor(cursorEvent) 
     } else if (mouseEventType == jfxme.MOUSE_PRESSED) { 

     startX = stage.getWidth - mouseEventX 
     startY = stage.getHeight - mouseEventY 

     } else if (mouseEventType == jfxme.MOUSE_DRAGGED) { 
     if (Cursor.DEFAULT != cursorEvent) { 


      if (Cursor.W_RESIZE != cursorEvent && Cursor.E_RESIZE != cursorEvent) {// not west or east 

       val minHeight = Math.max(stage.getMinHeight, border * 2) 


       if (Cursor.NW_RESIZE == cursorEvent || Cursor.N_RESIZE == cursorEvent || Cursor.NE_RESIZE == cursorEvent) {// NW or N or NE 

        val attemptedSize = stage.getY - mouseEvent.getScreenY + stage.getHeight 
        val actSize = Math.max(minHeight, attemptedSize) 
        val diff = actSize - attemptedSize 

        stage.setHeight(actSize) 
        stage.setY(mouseEvent.getScreenY - diff) 

       } else {// SW or S or SE 

        val attemptedSize = mouseEventY + startY 
        val actSize = Math.max(minHeight, attemptedSize) 

        stage.setHeight(actSize) 
       } 
      } 

      if (Cursor.N_RESIZE != cursorEvent && Cursor.S_RESIZE != cursorEvent) { 

       val minWidth = Math.max(stage.getMinWidth, border * 2) 

       if (Cursor.NW_RESIZE == cursorEvent || Cursor.W_RESIZE == cursorEvent || Cursor.SW_RESIZE == cursorEvent) { 
        val attemptedSize = stage.getX - mouseEvent.getScreenX + stage.getWidth 
        val actSize = Math.max(minWidth, attemptedSize) 
        val diff = actSize - attemptedSize 

        stage.setWidth(actSize) 
        stage.setX(mouseEvent.getScreenX - diff) 

       } else { 
        val attemptedSize = mouseEventX + startX 
        val actSize = Math.max(minWidth, attemptedSize) 

        stage.setWidth(actSize) 

       } 
      } 
      owner.saveBounds() 
     } 

     } 
    } 
} 
Смежные вопросы