All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mvysny.karibudsl.v8.ComponentContainer.kt Maven / Gradle / Ivy

The newest version!
package com.github.mvysny.karibudsl.v8

import com.vaadin.ui.*
import java.lang.IllegalStateException
import java.lang.reflect.Method

@VaadinDsl
public fun (@VaadinDsl HasComponents).verticalLayout(isMargin: Boolean = true, isSpacing: Boolean = true, block: (@VaadinDsl VerticalLayout).()->Unit = {}): VerticalLayout {
    val layout = VerticalLayout()
    layout.isSpacing = isSpacing
    layout.isMargin = isMargin
    return init(layout, block)
}

@VaadinDsl
public fun (@VaadinDsl HasComponents).horizontalLayout(isMargin: Boolean = false, isSpacing: Boolean = true, block: (@VaadinDsl HorizontalLayout).()->Unit = {}): HorizontalLayout {
    val layout = HorizontalLayout()
    layout.isMargin = isMargin
    layout.isSpacing = isSpacing
    return init(layout, block)
}

@VaadinDsl
public fun (@VaadinDsl HasComponents).formLayout(isSpacing: Boolean = true, block: (@VaadinDsl FormLayout).()->Unit = {}): FormLayout {
    val layout = FormLayout()
    layout.isSpacing = isSpacing
    return init(layout, block)
}

@VaadinDsl
public fun (@VaadinDsl HasComponents).absoluteLayout(block: (@VaadinDsl AbsoluteLayout).()->Unit = {}): AbsoluteLayout =
        init(AbsoluteLayout(), block)

@VaadinDsl
public fun (@VaadinDsl HasComponents).cssLayout(caption: String? = null, block: (@VaadinDsl CssLayout).()->Unit = {}): CssLayout {
    val layout = CssLayout()
    layout.caption = caption
    return init(layout, block)
}

@VaadinDsl
public fun (@VaadinDsl HasComponents).gridLayout(columns: Int = 1, rows: Int = 1, block: (@VaadinDsl GridLayout).()->Unit = {}): GridLayout =
        init(GridLayout(columns, rows), block)

/**
 * Adds a [child] to this component. Only concrete subclasses are supported:
 *
 * * [ComponentContainer]
 * * [SingleComponentContainer] (fails if the container already has a child)
 * * [PopupView]
 * * [AbstractSplitPanel]
 * * [SpecialContainer]
 *
 * The function will fail if the component
 * is already full (e.g. it is a split panel and it already contains two components).
 *
 * For custom containers just implement the [SpecialContainer] interface.
 */
public fun (@VaadinDsl HasComponents).addChild(child: Component) {
    @Suppress("DEPRECATION")
    when (this) {
        is ComponentContainer -> addComponent(child)
        is SpecialContainer -> addComponent(child)
        is SingleComponentContainer -> {
            if (componentCount >= 1) throw IllegalArgumentException("$this can only have one child")
            content = child
        }
        is PopupView -> popupComponent = child
        is AbstractSplitPanel -> when (componentCount) {
            0 -> firstComponent = child
            1 -> secondComponent = child
            else -> throw IllegalArgumentException("$this can only have 2 children")
        }
        is Composite -> compositeSetCompositionRoot(this, child)
        else -> throw IllegalArgumentException("Unsupported component container $this")
    }
}

/**
 * Removes [child] from this container. Does nothing if child has no parent or it is nested in some other container.
 */
public fun (@VaadinDsl HasComponents).removeChild(child: Component) {
    @Suppress("DEPRECATION")
    when (this) {
        is ComponentContainer -> removeComponent(child)
        is SpecialContainer -> removeComponent(child)
        is SingleComponentContainer -> {
            if (content == child) {
                content = null
            }
        }
        is PopupView -> if (popupComponent == child) popupComponent = Label("")
        is AbstractSplitPanel -> when {
            firstComponent == child -> firstComponent = null
            secondComponent == child -> secondComponent = null
        }
        is Composite -> throw IllegalStateException("Cannot set root to null in Composite")
        else -> throw IllegalArgumentException("Unsupported component container $this")
    }
}

/**
 * Removes the component from its parent. Does nothing if the component is not currently nested inside of a parent.
 */
public fun Component.removeFromParent() {
    parent?.removeChild(this)
}

/**
 * Workaround around [Composite.setCompositionRoot] being protected, so that [addChild] can insert components into [Composite].
 */
private val compositeSetCompositionRoot: Method = Composite::class.java.getDeclaredMethod("setCompositionRoot", Component::class.java).apply {
    isAccessible = true
}

/**
 * Removes all components from given container.
 */
public fun (@VaadinDsl HasComponents).removeAllComponents() {
    @Suppress("DEPRECATION")
    when (this) {
        is ComponentContainer -> removeAllComponents()
        is SpecialContainer -> removeAllComponents()
        is SingleComponentContainer -> {
            content = null
        }
        is AbstractSplitPanel -> {
            firstComponent = null
            secondComponent = null
        }
        is PopupView -> popupComponent = Label("")
        is Composite -> throw IllegalStateException("Cannot set root to null in Composite")
        else -> throw IllegalArgumentException("Unsupported component container $this")
    }
}

/**
 * Returns the child component count for this container. O(1) for [ComponentContainer] and [SingleComponentContainer]; falls back to
 * counting items in [HasComponents.iterator] otherwise - O(n).
 */
public fun HasComponents.getComponentCount(): Int = when (this) {
    is ComponentContainer -> componentCount
    is SingleComponentContainer -> componentCount
    else -> count()
}

/**
 * Returns true if this container hosts no child components. Optimized for [ComponentContainer] and [SingleComponentContainer]. O(1).
 */
public val HasComponents.isEmpty: Boolean get() = when (this) {
    is ComponentContainer -> componentCount == 0
    is SingleComponentContainer -> componentCount == 0
    else -> !iterator().hasNext()
}

/**
 * Returns true if this container contains any components. Optimized for [ComponentContainer] and [SingleComponentContainer]. O(1).
 */
public val HasComponents.isNotEmpty: Boolean get() = !isEmpty

/**
 * Sets the expand ratio of this component with respect to its parent layout. See [AbstractOrderedLayout.setExpandRatio] for more details.
 *
 * Fails if this component is not nested inside [AbstractOrderedLayout].
 */
public var (@VaadinDsl Component).expandRatio: Float
    get() = parent.asAbstractOrderedLayout().getExpandRatio(this)
    set(value) = parent.asAbstractOrderedLayout().setExpandRatio(this, value)

private fun HasComponents?.asAbstractOrderedLayout(): AbstractOrderedLayout {
    check(this != null) { "Setting expandRatio of a component only works when the component is already nested in the parent layout" }
    check(this !is FormLayout) { "Setting expandRatio of a component nested inside of a FormLayout does nothing (see the FormLayout class for more details), and it is therefore discouraged." }
    check(this is AbstractOrderedLayout) { "You're attempting to set expandRatio of a component but that's only supported when the component is nested inside VerticalLayout and HorizontalLayout, not for ${this.javaClass.simpleName}."}
    return this
}

/**
 * Sets the expand ratio of this component with respect to its parent layout. See [AbstractOrderedLayout.setExpandRatio] for more details.
 *
 * Fails if this component is not nested inside [AbstractOrderedLayout].
 */
public var (@VaadinDsl Component).isExpanded: Boolean
    get() = expandRatio > 0f
    set(value) { expandRatio = if (value) 1f else 0f }

/**
 * Sets or gets alignment for this component with respect to its parent layout. Use
 * predefined alignments from Alignment class. Fails if the component is not nested inside Layout, that implement [Layout.AlignmentHandler]
 */
public var (@VaadinDsl Component).alignment: Alignment
    get() = (parent as Layout.AlignmentHandler).getComponentAlignment(this)
    set(value) = (parent as Layout.AlignmentHandler).setComponentAlignment(this, value)

/**
 * Sets [AbsoluteLayout.ComponentPosition.zIndex]. Fails if this component is not nested inside [AbsoluteLayout]
 */
public var (@VaadinDsl Component).zIndex: Int
    get() = absolutePosition.zIndex
    set(value) {
        absolutePosition.zIndex = value
    }

private fun HasComponents?.asAbsoluteLayout(): AbsoluteLayout {
    check(this != null) { "Setting absolutePosition of a component only works when the component is already nested in the parent layout" }
    check(this is AbsoluteLayout) { "You're attempting to set absolutePosition of a component but that's only supported when the component is nested inside VerticalLayout and HorizontalLayout, not for ${this.javaClass.simpleName}."}
    return this
}

/**
 * Returns the [AbsoluteLayout.ComponentPosition] of this component. Fails if this component is not nested inside [AbsoluteLayout]
 */
public val (@VaadinDsl Component).absolutePosition: AbsoluteLayout.ComponentPosition
    get() = parent.asAbsoluteLayout().getPosition(this)
public var (@VaadinDsl AbsoluteLayout.ComponentPosition).top: Size
    get() = Size(topValue, topUnits)
    set(value) {
        topValue = value.size; topUnits = value.units
    }
public var (@VaadinDsl AbsoluteLayout.ComponentPosition).bottom: Size
    get() = Size(bottomValue, bottomUnits)
    set(value) {
        bottomValue = value.size; bottomUnits = value.units
    }
public var (@VaadinDsl AbsoluteLayout.ComponentPosition).left: Size
    get() = Size(leftValue, leftUnits)
    set(value) {
        leftValue = value.size; leftUnits = value.units
    }
public var (@VaadinDsl AbsoluteLayout.ComponentPosition).right: Size
    get() = Size(rightValue, rightUnits)
    set(value) {
        rightValue = value.size; rightUnits = value.units
    }

/**
 * Sets all four margins to given value.
 */
public var (@VaadinDsl AbstractOrderedLayout).isMargin: Boolean
    get() = margin.hasAll()
    set(value) { setMargin(value) }

/**
 * Returns component at given [index]. Optimized for [CssLayout] and [AbstractOrderedLayout]s, but works with any
 * [ComponentContainer].
 * @throws IndexOutOfBoundsException If the index is out of range.
 */
public fun (@VaadinDsl ComponentContainer).getComponentAt(index: Int): Component = when (this) {
    is CssLayout -> this.getComponent(index)
    is AbstractOrderedLayout -> this.getComponent(index)
    else -> toList()[index]
}

/**
 * Removes a component at given [index] from this container. Optimized for [CssLayout] and [AbstractOrderedLayout]s, but works with any
 * [ComponentContainer].
 * @throws IndexOutOfBoundsException If the index is out of range.
 */
public fun (@VaadinDsl ComponentContainer).removeComponentAt(index: Int) {
    removeComponent(getComponentAt(index))
}

/**
 * Returns an [IntRange] of the valid component indices for this container.
 */
public val (@VaadinDsl ComponentContainer).indices: IntRange get() = 0 until componentCount

/**
 * Removes components with given indices from the container.
 */
public fun (@VaadinDsl ComponentContainer).removeComponentsAt(indexRange: IntRange) {
    if (indexRange.isEmpty()) {
        // do nothing
    } else if (indexRange == indices) {
        removeAllComponents()
    } else if (this is CssLayout || this is AbstractOrderedLayout) {
        indexRange.reversed().forEach { removeComponentAt(it) }
    } else {
        removeAll(filterIndexed { index, _ -> index in indexRange })
    }
}

/**
 * Removes [components] from the container.
 */
public fun (@VaadinDsl ComponentContainer).removeAll(components: Iterable) {
    components.forEach { removeComponent(it) }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy