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
+}