2019-06-22 19:02:17 +00:00
|
|
|
package net.shadowfacts.cacao
|
2019-06-21 23:02:08 +00:00
|
|
|
|
2019-06-22 19:02:17 +00:00
|
|
|
import net.shadowfacts.cacao.geometry.Point
|
2019-06-23 15:41:32 +00:00
|
|
|
import net.shadowfacts.cacao.util.MouseButton
|
2019-06-22 19:02:17 +00:00
|
|
|
import net.shadowfacts.cacao.view.View
|
2019-06-28 15:12:46 +00:00
|
|
|
import net.shadowfacts.cacao.viewcontroller.ViewController
|
2019-06-26 23:54:23 +00:00
|
|
|
import net.shadowfacts.kiwidsl.dsl
|
|
|
|
import no.birkett.kiwi.Constraint
|
2019-06-21 23:02:08 +00:00
|
|
|
import no.birkett.kiwi.Solver
|
2019-06-26 23:54:23 +00:00
|
|
|
import no.birkett.kiwi.Variable
|
2019-06-21 23:02:08 +00:00
|
|
|
|
2019-06-22 14:59:18 +00:00
|
|
|
/**
|
2019-06-22 20:08:00 +00:00
|
|
|
* A Window is the object at the top of a Cacao view hierarchy. It occupies the entirety of the Minecraft screen size
|
|
|
|
* and provides the base coordinate system for its view hierarchy.
|
|
|
|
*
|
|
|
|
* The Window owns the Kiwi [Solver] object used for layout by all of its views.
|
|
|
|
*
|
2019-06-22 14:59:18 +00:00
|
|
|
* @author shadowfacts
|
2019-08-08 20:43:44 +00:00
|
|
|
*
|
|
|
|
* @param viewController The root view controller for this window.
|
2019-06-22 14:59:18 +00:00
|
|
|
*/
|
2019-08-08 20:43:44 +00:00
|
|
|
class Window(
|
|
|
|
/**
|
|
|
|
* The root view controller for this window.
|
|
|
|
*/
|
|
|
|
val viewController: ViewController
|
|
|
|
) {
|
2019-06-21 23:02:08 +00:00
|
|
|
|
2019-06-26 01:52:17 +00:00
|
|
|
/**
|
|
|
|
* The screen that this window belongs to.
|
|
|
|
* Not initialized until this window is added to a screen, using it before that point will throw a runtime exception.
|
|
|
|
*/
|
|
|
|
lateinit var screen: CacaoScreen
|
|
|
|
|
2019-06-26 23:54:23 +00:00
|
|
|
/**
|
|
|
|
* The constraint solver used by this window and all its views and subviews.
|
|
|
|
*/
|
2019-06-21 23:02:08 +00:00
|
|
|
var solver = Solver()
|
|
|
|
|
2019-06-26 23:54:23 +00:00
|
|
|
/**
|
|
|
|
* Layout anchor for the left edge of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val leftAnchor = Variable("left")
|
|
|
|
/**
|
|
|
|
* Layout anchor for the right edge of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val rightAnchor = Variable("right")
|
|
|
|
/**
|
|
|
|
* Layout anchor for the top edge of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val topAnchor = Variable("top")
|
|
|
|
/**
|
|
|
|
* Layout anchor for the bottom edge of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val bottomAnchor = Variable("bottom")
|
|
|
|
/**
|
|
|
|
* Layout anchor for the width of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val widthAnchor = Variable("width")
|
|
|
|
/**
|
|
|
|
* Layout anchor for the height of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val heightAnchor = Variable("height")
|
|
|
|
/**
|
|
|
|
* Layout anchor for the center X position of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val centerXAnchor = Variable("centerX")
|
|
|
|
/**
|
|
|
|
* Layout anchor for the center Y position of this view in the window's coordinate system.
|
|
|
|
*/
|
|
|
|
val centerYAnchor = Variable("centerY")
|
|
|
|
|
|
|
|
// internal constraints that specify the window size based on the MC screen size
|
|
|
|
// stored so that they can be removed when the screen is resized
|
|
|
|
private var widthConstraint: Constraint? = null
|
|
|
|
private var heightConstraint: Constraint? = null
|
|
|
|
|
2019-08-09 18:36:12 +00:00
|
|
|
private var currentDragReceiver: View? = null
|
|
|
|
|
2019-06-26 23:54:23 +00:00
|
|
|
init {
|
|
|
|
createInternalConstraints()
|
2019-06-28 15:12:46 +00:00
|
|
|
|
|
|
|
viewController.window = this
|
|
|
|
viewController.loadView()
|
|
|
|
|
|
|
|
viewController.view.window = this
|
|
|
|
viewController.view.solver = solver
|
|
|
|
viewController.view.wasAdded()
|
|
|
|
viewController.createConstraints {
|
|
|
|
viewController.view.leftAnchor equalTo leftAnchor
|
|
|
|
viewController.view.rightAnchor equalTo rightAnchor
|
|
|
|
viewController.view.topAnchor equalTo topAnchor
|
|
|
|
viewController.view.bottomAnchor equalTo bottomAnchor
|
|
|
|
}
|
|
|
|
|
|
|
|
viewController.viewDidLoad()
|
|
|
|
|
|
|
|
layout()
|
2019-06-26 23:54:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the internal constraints used by the window.
|
|
|
|
* If overridden, the super-class method must be called.
|
|
|
|
*/
|
2019-06-27 22:31:06 +00:00
|
|
|
protected fun createInternalConstraints() {
|
2019-06-26 23:54:23 +00:00
|
|
|
solver.dsl {
|
|
|
|
leftAnchor equalTo 0
|
|
|
|
topAnchor equalTo 0
|
|
|
|
|
|
|
|
rightAnchor equalTo (leftAnchor + widthAnchor)
|
|
|
|
bottomAnchor equalTo (topAnchor + heightAnchor)
|
|
|
|
centerXAnchor equalTo (leftAnchor + widthAnchor / 2)
|
|
|
|
centerYAnchor equalTo (topAnchor + heightAnchor / 2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called by the window's [screen] when the Minecraft screen is resized.
|
|
|
|
* Used to update the window's width and height constraints and re-layout views.
|
|
|
|
*/
|
|
|
|
internal fun resize(width: Int, height: Int) {
|
|
|
|
if (widthConstraint != null) solver.removeConstraint(widthConstraint)
|
|
|
|
if (heightConstraint != null) solver.removeConstraint(heightConstraint)
|
|
|
|
solver.dsl {
|
|
|
|
widthConstraint = (widthAnchor equalTo width)
|
|
|
|
heightConstraint = (heightAnchor equalTo height)
|
|
|
|
}
|
|
|
|
layout()
|
|
|
|
}
|
|
|
|
|
2019-06-26 01:52:17 +00:00
|
|
|
/**
|
|
|
|
* Convenience method that removes this window from its [screen].
|
|
|
|
*/
|
|
|
|
fun removeFromScreen() {
|
2019-06-28 15:12:46 +00:00
|
|
|
viewController.viewWillDisappear()
|
2019-06-26 01:52:17 +00:00
|
|
|
screen.removeWindow(this)
|
2019-06-28 15:12:46 +00:00
|
|
|
viewController.viewDidDisappear()
|
2019-06-23 15:41:32 +00:00
|
|
|
}
|
|
|
|
|
2019-06-22 20:08:00 +00:00
|
|
|
/**
|
|
|
|
* Instructs the solver to solve all of the provided constraints.
|
|
|
|
* Should be called after the view hierarchy is setup.
|
|
|
|
*/
|
2019-06-21 23:02:08 +00:00
|
|
|
fun layout() {
|
2019-06-28 15:12:46 +00:00
|
|
|
viewController.viewWillLayoutSubviews()
|
2019-08-08 20:43:44 +00:00
|
|
|
solver.updateVariables()
|
2019-06-28 15:12:46 +00:00
|
|
|
viewController.viewDidLayoutSubviews()
|
2019-06-21 23:02:08 +00:00
|
|
|
}
|
|
|
|
|
2019-06-22 20:08:00 +00:00
|
|
|
/**
|
|
|
|
* Draws this window and all of its views.
|
|
|
|
* This method is called by [CacaoScreen] and generally shouldn't be called directly.
|
|
|
|
*
|
|
|
|
* @param mouse The point in the coordinate system of the window.
|
|
|
|
* @param delta The time elapsed since the last frame.
|
|
|
|
*/
|
2019-06-21 23:02:08 +00:00
|
|
|
fun draw(mouse: Point, delta: Float) {
|
2019-06-28 15:12:46 +00:00
|
|
|
val mouseInView = Point(mouse.x - viewController.view.frame.left, mouse.y - viewController.view.frame.top)
|
|
|
|
viewController.view.draw(mouseInView, delta)
|
2019-06-23 15:41:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when a mouse button is clicked and this is the active window.
|
|
|
|
* This method is called by [CacaoScreen] and generally shouldn't be called directly.
|
|
|
|
*
|
|
|
|
* @param point The point in the window of the click.
|
|
|
|
* @param mouseButton The mouse button that was used to click.
|
2019-06-24 02:34:12 +00:00
|
|
|
* @return Whether the mouse click was handled by a view.
|
2019-06-23 15:41:32 +00:00
|
|
|
*/
|
2019-06-24 02:34:12 +00:00
|
|
|
fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
|
2019-06-28 15:12:46 +00:00
|
|
|
if (point in viewController.view.frame) {
|
|
|
|
val mouseInView = Point(point.x - viewController.view.frame.left, point.y - viewController.view.frame.top)
|
|
|
|
return viewController.view.mouseClicked(mouseInView, mouseButton)
|
2019-06-27 23:15:12 +00:00
|
|
|
} else {
|
|
|
|
// remove the window from the screen when the mouse clicks outside the window and this is not the primary window
|
|
|
|
if (screen.windows.size > 1) {
|
|
|
|
removeFromScreen()
|
|
|
|
}
|
2019-06-23 15:41:32 +00:00
|
|
|
}
|
2019-06-24 02:34:12 +00:00
|
|
|
return false
|
2019-06-21 23:02:08 +00:00
|
|
|
}
|
|
|
|
|
2019-08-09 18:36:12 +00:00
|
|
|
fun mouseDragged(startPoint: Point, delta: Point, mouseButton: MouseButton): Boolean {
|
|
|
|
val currentlyDraggedView = this.currentDragReceiver
|
|
|
|
if (currentlyDraggedView != null) {
|
|
|
|
return currentlyDraggedView.mouseDragged(startPoint, delta, mouseButton)
|
|
|
|
} else if (startPoint in viewController.view.frame) {
|
|
|
|
val startInView = Point(startPoint.x - viewController.view.frame.left, startPoint.y - viewController.view.frame.top)
|
|
|
|
lateinit var prevView: View
|
|
|
|
var view = viewController.view.subviewsAtPoint(startInView).maxBy(View::zIndex)
|
|
|
|
while (view != null && !view.respondsToDragging) {
|
|
|
|
prevView = view
|
|
|
|
val pointInView = viewController.view.convert(startInView, to = view)
|
|
|
|
view = view.subviewsAtPoint(pointInView).maxBy(View::zIndex)
|
|
|
|
}
|
|
|
|
// this.currentlyDraggedView = viewController.view.subviewsAtPoint(startInView).maxBy(View::zIndex)
|
|
|
|
this.currentDragReceiver = view ?: prevView
|
|
|
|
return this.currentDragReceiver?.mouseDragged(startPoint, delta, mouseButton) ?: false
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
fun mouseReleased(point: Point, mouseButton: MouseButton): Boolean {
|
|
|
|
val currentlyDraggedView = this.currentDragReceiver
|
|
|
|
if (currentlyDraggedView != null) {
|
|
|
|
this.currentDragReceiver = null
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-06-21 23:02:08 +00:00
|
|
|
}
|