You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by jo...@apache.org on 2020/06/21 10:39:39 UTC
[isis] branch master updated: ISIS-2350 Reintroduce RoWindow,
RoManagerBootstrap in order to make RoDialogs transparent on move.
This is an automated email from the ASF dual-hosted git repository.
joergrade pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new d470286 ISIS-2350 Reintroduce RoWindow, RoManagerBootstrap in order to make RoDialogs transparent on move.
d470286 is described below
commit d47028603580bcf0ce3271aa642b8a733c012f0f
Author: Jörg Rade <jo...@kuehne-nagel.com>
AuthorDate: Sat May 30 13:13:55 2020 +0200
ISIS-2350 Reintroduce RoWindow, RoManagerBootstrap in order to make RoDialogs transparent on move.
---
.../apache/isis/client/kroviz/ui/kv/RoDialog.kt | 10 +-
.../isis/client/kroviz/ui/kv/RoManagerBootstrap.kt | 42 ++
.../apache/isis/client/kroviz/ui/kv/RoWindow.kt | 435 +++++++++++++++++++++
3 files changed, 483 insertions(+), 4 deletions(-)
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDialog.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDialog.kt
index 253c8ed..1ea6181 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDialog.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDialog.kt
@@ -1,10 +1,13 @@
package org.apache.isis.client.kroviz.ui.kv
import org.apache.isis.client.kroviz.to.ValueType
-import org.apache.isis.client.kroviz.ui.*
-import org.apache.isis.client.kroviz.utils.Point
+import org.apache.isis.client.kroviz.ui.Command
+import org.apache.isis.client.kroviz.ui.Displayable
+import org.apache.isis.client.kroviz.ui.FormItem
+import org.apache.isis.client.kroviz.ui.ImageDialog
import org.apache.isis.client.kroviz.utils.Direction
import org.apache.isis.client.kroviz.utils.IconManager
+import org.apache.isis.client.kroviz.utils.Point
import pl.treksoft.kvision.core.CssSize
import pl.treksoft.kvision.core.UNIT
import pl.treksoft.kvision.core.Widget
@@ -16,7 +19,6 @@ import pl.treksoft.kvision.panel.HPanel
import pl.treksoft.kvision.panel.vPanel
import pl.treksoft.kvision.utils.perc
import pl.treksoft.kvision.utils.px
-import pl.treksoft.kvision.window.Window
class RoDialog(
caption: String,
@@ -25,7 +27,7 @@ class RoDialog(
defaultAction: String = "OK",
widthPerc: Int = 30,
heightPerc: Int = 100) :
- Displayable, Window(caption = caption, closeButton = true) {
+ Displayable, RoWindow(caption = caption, closeButton = true) {
private val okButton = Button(
text = defaultAction,
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoManagerBootstrap.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoManagerBootstrap.kt
new file mode 100644
index 0000000..f6ecbae
--- /dev/null
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoManagerBootstrap.kt
@@ -0,0 +1,42 @@
+package org.apache.isis.client.kroviz.ui.kv
+
+/* (!) copied from kvision KVManagerBootstrap in order to make Dialogs transparent on move */
+
+import pl.treksoft.kvision.core.Component
+import pl.treksoft.kvision.utils.isIE11
+
+internal val roManagerBootstrapInit = RoManagerBootstrap.init()
+
+/**
+ * Internal singleton object which initializes and configures KVision Bootstrap module.
+ */
+internal object RoManagerBootstrap {
+ init {
+ pl.treksoft.kvision.require("bootstrap/dist/js/bootstrap.bundle.min.js")
+ pl.treksoft.kvision.require("awesome-bootstrap-checkbox")
+ }
+
+ private val elementResizeEvent = pl.treksoft.kvision.require("element-resize-event")
+
+ @Suppress("UnsafeCastFromDynamic")
+ internal fun setResizeEvent(component: Component, callback: () -> Unit) {
+ if (!isIE11()) {
+ component.getElement()?.let {
+ elementResizeEvent(it, callback)
+ }
+ }
+ }
+
+ @Suppress("UnsafeCastFromDynamic")
+ internal fun clearResizeEvent(component: Component) {
+ if (!isIE11()) {
+ if (component.getElement()?.asDynamic()?.__resizeTrigger__?.contentDocument != null) {
+ component.getElement()?.let {
+ elementResizeEvent.unbind(it)
+ }
+ }
+ }
+ }
+
+ internal fun init() {}
+}
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoWindow.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoWindow.kt
new file mode 100644
index 0000000..bd00774
--- /dev/null
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoWindow.kt
@@ -0,0 +1,435 @@
+package org.apache.isis.client.kroviz.ui.kv
+
+/* (!) copied from pl.treksoft.kvision.window.Window in order to make Dialogs transparent on move */
+
+import com.github.snabbdom.VNode
+import org.w3c.dom.events.Event
+import org.w3c.dom.events.MouseEvent
+import pl.treksoft.kvision.core.*
+import pl.treksoft.kvision.html.Icon
+import pl.treksoft.kvision.html.TAG
+import pl.treksoft.kvision.html.Tag
+import pl.treksoft.kvision.modal.CloseIcon
+import pl.treksoft.kvision.panel.SimplePanel
+import pl.treksoft.kvision.utils.obj
+import pl.treksoft.kvision.utils.px
+import pl.treksoft.kvision.window.MaximizeIcon
+import pl.treksoft.kvision.window.MinimizeIcon
+
+internal const val DEFAULT_Z_INDEX = 900
+internal const val WINDOW_HEADER_HEIGHT = 40
+internal const val WINDOW_CONTENT_MARGIN_BOTTOM = 11
+
+/**
+ * Floating window container.
+ *
+ * @constructor
+ * @param caption window title
+ * @param contentWidth window content width
+ * @param contentHeight window content height
+ * @param isResizable determines if the window is resizable
+ * @param isDraggable determines if the window is draggable
+ * @param closeButton determines if Close button is visible
+ * @param maximizeButton determines if Maximize button is visible
+ * @param minimizeButton determines if Minimize button is visible
+ * @param classes a set of CSS class names
+ * @param init an initializer extension function
+ */
+@Suppress("TooManyFunctions")
+open class RoWindow(
+ caption: String? = null,
+ contentWidth: CssSize? = CssSize(0, UNIT.auto),
+ contentHeight: CssSize? = CssSize(0, UNIT.auto),
+ isResizable: Boolean = true,
+ isDraggable: Boolean = true,
+ closeButton: Boolean = false,
+ maximizeButton: Boolean = false,
+ minimizeButton: Boolean = false,
+ icon: String? = null,
+ classes: Set<String> = setOf(),
+ init: (RoWindow.() -> Unit)? = null
+) :
+ SimplePanel(classes + setOf("modal-content", "kv-window")) {
+
+ /**
+ * Window caption text.
+ */
+ var caption
+ get() = captionTag.content
+ set(value) {
+ captionTag.content = value
+ checkHeaderVisibility()
+ }
+ /**
+ * Window content width.
+ */
+ var contentWidth
+ get() = width
+ set(value) {
+ width = value
+ }
+ /**
+ * Window content height.
+ */
+ var contentHeight
+ get() = content.height
+ set(value) {
+ content.height = value
+ }
+ /**
+ * Window content height.
+ */
+ var contentOverflow
+ get() = content.overflow
+ set(value) {
+ content.overflow = value
+ }
+ /**
+ * Determines if the window is resizable.
+ */
+ var isResizable by refreshOnUpdate(isResizable) { checkIsResizable() }
+ /**
+ * Determines if the window is draggable.
+ */
+ var isDraggable by refreshOnUpdate(isDraggable) { checkIsDraggable(); checkHeaderVisibility() }
+ /**
+ * Determines if Close button is visible.
+ */
+ var closeButton
+ get() = closeIcon.visible
+ set(value) {
+ closeIcon.visible = value
+ checkHeaderVisibility()
+ }
+ /**
+ * Determines if Maximize button is visible.
+ */
+ var maximizeButton
+ get() = maximizeIcon.visible
+ set(value) {
+ maximizeIcon.visible = value
+ checkHeaderVisibility()
+ }
+ /**
+ * Determines if Maximize button is visible.
+ */
+ var minimizeButton
+ get() = minimizeIcon.visible
+ set(value) {
+ minimizeIcon.visible = value
+ checkHeaderVisibility()
+ }
+ /**
+ * Window icon.
+ */
+ var icon
+ get() = if (windowIcon.icon == "") null else windowIcon.icon
+ set(value) {
+ windowIcon.icon = value ?: ""
+ windowIcon.visible = (value != null && value != "")
+ }
+
+ private val header = SimplePanel(setOf("modal-header"))
+
+ /**
+ * @suppress
+ * Internal property.
+ */
+ protected val content = SimplePanel().apply {
+ this.height = contentHeight
+ this.overflow = Overflow.AUTO
+ }
+ private val closeIcon = CloseIcon()
+ private val maximizeIcon = MaximizeIcon()
+ private val minimizeIcon = MinimizeIcon()
+ private val captionTag = Tag(TAG.H5, caption, classes = setOf("modal-title"))
+ private val iconsContainer = SimplePanel(setOf("kv-window-icons-container"))
+ private val windowIcon = Icon(icon ?: "").apply {
+ addCssClass("window-icon")
+ visible = (icon != null && icon != "")
+ }
+
+ private var isResizeEvent = false
+
+ init {
+ id = "kv_window_$counter"
+ @Suppress("LeakingThis")
+ position = Position.ABSOLUTE
+ @Suppress("LeakingThis")
+ overflow = Overflow.HIDDEN
+ @Suppress("LeakingThis")
+ width = contentWidth
+ @Suppress("LeakingThis")
+ zIndex = ++zIndexCounter
+ header.add(captionTag)
+ captionTag.add(windowIcon)
+ header.add(iconsContainer)
+ minimizeIcon.visible = minimizeButton
+ minimizeIcon.setEventListener<MinimizeIcon> {
+ click = { _ ->
+ @Suppress("UnsafeCastFromDynamic")
+ if (this@RoWindow.dispatchEvent("minimizeWindow", obj {}) != false) {
+ toggleMinimize()
+ }
+ }
+ mousedown = { e ->
+ e.stopPropagation()
+ }
+ }
+ iconsContainer.add(minimizeIcon)
+ maximizeIcon.visible = maximizeButton
+ maximizeIcon.setEventListener<MaximizeIcon> {
+ click = { _ ->
+ @Suppress("UnsafeCastFromDynamic")
+ if (this@RoWindow.dispatchEvent("maximizeWindow", obj {}) != false) {
+ toggleMaximize()
+ }
+ }
+ mousedown = { e ->
+ e.stopPropagation()
+ }
+ }
+ iconsContainer.add(maximizeIcon)
+ closeIcon.visible = closeButton
+ closeIcon.setEventListener<CloseIcon> {
+ click = { _ ->
+ @Suppress("UnsafeCastFromDynamic")
+ if (this@RoWindow.dispatchEvent("closeWindow", obj {}) != false) {
+ close()
+ }
+ }
+ mousedown = { e ->
+ e.stopPropagation()
+ }
+ }
+ iconsContainer.add(closeIcon)
+ checkHeaderVisibility()
+ addInternal(header)
+ addInternal(content)
+ checkIsDraggable()
+ if (isResizable) {
+ @Suppress("LeakingThis")
+ resize = Resize.BOTH
+ content.marginBottom = WINDOW_CONTENT_MARGIN_BOTTOM.px
+ }
+ @Suppress("LeakingThis")
+ setEventListener<RoWindow> {
+ click = {
+ toFront()
+ focus()
+ }
+ }
+ @Suppress("LeakingThis")
+ init?.invoke(this)
+ counter++
+ }
+
+ private fun checkHeaderVisibility() {
+ @Suppress("ComplexCondition")
+ if (!closeButton && !maximizeButton && !minimizeButton && caption == null && !isDraggable) {
+ header.hide()
+ } else {
+ header.show()
+ }
+ }
+
+ open fun checkIsDraggable() {
+ var isDrag: Boolean
+ if (isDraggable) {
+ header.setEventListener<SimplePanel> {
+ mousedown = { e ->
+ if (e.button.toInt() == 0) {
+ isDrag = true
+ val dragStartX = this@RoWindow.getElementJQuery()?.position()?.left?.toInt() ?: 0
+ val dragStartY = this@RoWindow.getElementJQuery()?.position()?.top?.toInt() ?: 0
+ val dragMouseX = e.pageX
+ val dragMouseY = e.pageY
+ val moveCallback = { me: Event ->
+ if (isDrag) {
+ setOpacity("0.3")
+ this@RoWindow.left = (dragStartX + (me as MouseEvent).pageX - dragMouseX).toInt().px
+ this@RoWindow.top = (dragStartY + (me).pageY - dragMouseY).toInt().px
+ }
+ }
+ kotlin.browser.window.addEventListener("mousemove", moveCallback)
+ var upCallback: ((Event) -> Unit)? = null
+ upCallback = {
+ isDrag = false
+ setOpacity("1.0")
+ kotlin.browser.window.removeEventListener("mousemove", moveCallback)
+ kotlin.browser.window.removeEventListener("mouseup", upCallback)
+ }
+ kotlin.browser.window.addEventListener("mouseup", upCallback)
+ }
+ }
+ }
+ } else {
+ isDrag = false
+ header.removeEventListeners()
+ }
+ }
+
+ private fun setOpacity(value: String) {
+ val opacity = value.toDouble()
+ this@RoWindow.getElementJQuery()?.css(
+ "background-color",
+ "rgba(255, 255, 255, $opacity)"
+ )
+ }
+
+
+ private fun checkIsResizable() {
+ checkResizablEventHandler()
+ if (isResizable) {
+ resize = Resize.BOTH
+ val intHeight = (getElementJQuery()?.height()?.toInt() ?: 0)
+ content.height = (intHeight - WINDOW_HEADER_HEIGHT - WINDOW_CONTENT_MARGIN_BOTTOM).px
+ content.marginBottom = WINDOW_CONTENT_MARGIN_BOTTOM.px
+ } else {
+ resize = Resize.NONE
+ val intHeight = (getElementJQuery()?.height()?.toInt() ?: 0)
+ content.height = (intHeight - WINDOW_HEADER_HEIGHT).px
+ content.marginBottom = 0.px
+ }
+ }
+
+ @Suppress("UnsafeCastFromDynamic")
+ private fun checkResizablEventHandler() {
+ if (isResizable) {
+ if (!isResizeEvent) {
+ isResizeEvent = true
+ RoManagerBootstrap.setResizeEvent(this) {
+ val eid = getElementJQuery()?.attr("id")
+ if (isResizable && eid == id) {
+ val outerWidth = (getElementJQuery()?.outerWidth()?.toInt() ?: 0)
+ val outerHeight = (getElementJQuery()?.outerHeight()?.toInt() ?: 0)
+ val intWidth = (getElementJQuery()?.width()?.toInt() ?: 0)
+ val intHeight = (getElementJQuery()?.height()?.toInt() ?: 0)
+ content.width = intWidth.px
+ content.height = (intHeight - WINDOW_HEADER_HEIGHT - WINDOW_CONTENT_MARGIN_BOTTOM).px
+ width = outerWidth.px
+ height = outerHeight.px
+ this.dispatchEvent("resizeWindow", obj {
+ detail = obj {
+ this.width = outerWidth
+ this.height = outerHeight
+ }
+ })
+ }
+ }
+ }
+ } else if (isResizeEvent) {
+ RoManagerBootstrap.clearResizeEvent(this)
+ isResizeEvent = false
+ }
+ }
+
+ override fun add(child: Component): SimplePanel {
+ content.add(child)
+ return this
+ }
+
+ override fun addAll(children: List<Component>): SimplePanel {
+ content.addAll(children)
+ return this
+ }
+
+ override fun remove(child: Component): SimplePanel {
+ content.remove(child)
+ return this
+ }
+
+ override fun removeAll(): SimplePanel {
+ content.removeAll()
+ return this
+ }
+
+ override fun getChildren(): List<Component> {
+ return content.getChildren()
+ }
+
+ override fun afterCreate(node: VNode) {
+ checkResizablEventHandler()
+ }
+
+ override fun afterDestroy() {
+ if (isResizeEvent) {
+ RoManagerBootstrap.clearResizeEvent(this)
+ isResizeEvent = false
+ }
+ }
+
+ /**
+ * Moves the current window to the front.
+ */
+ open fun toFront() {
+ if ((zIndex ?: 0) < zIndexCounter) zIndex = ++zIndexCounter
+ }
+
+ /**
+ * Makes the current window focused.
+ */
+ open fun focus() {
+ getElementJQuery()?.focus()
+ }
+
+ /**
+ * Close the window.
+ */
+ open fun close() {
+ hide()
+ }
+
+ /**
+ * Maximize or restore the window size.
+ */
+ open fun toggleMaximize() {
+ }
+
+ /**
+ * Minimize or restore the window size.
+ */
+ open fun toggleMinimize() {
+ }
+
+ companion object {
+ internal var counter = 0
+ internal var zIndexCounter = DEFAULT_Z_INDEX
+ }
+}
+
+/**
+ * DSL builder extension function.
+ *
+ * It takes the same parameters as the constructor of the built component.
+ */
+fun Container.window(
+ caption: String? = null,
+ contentWidth: CssSize? = CssSize(0, UNIT.auto),
+ contentHeight: CssSize? = CssSize(0, UNIT.auto),
+ isResizable: Boolean = true,
+ isDraggable: Boolean = true,
+ closeButton: Boolean = false,
+ maximizeButton: Boolean = false,
+ minimizeButton: Boolean = false,
+ icon: String? = null,
+ classes: Set<String> = setOf(),
+ init: (RoWindow.() -> Unit)? = null
+): RoWindow {
+ val window =
+ RoWindow(
+ caption,
+ contentWidth,
+ contentHeight,
+ isResizable,
+ isDraggable,
+ closeButton,
+ maximizeButton,
+ minimizeButton,
+ icon,
+ classes,
+ init
+ )
+ this.add(window)
+ return window
+}