Skip to content

Commit

Permalink
Configuration tweaks for popup menu panels
Browse files Browse the repository at this point in the history
Add `CommandPopupMenuPanelPresentationModel` that is specifically targeting to configure the presentation of command panels embedded in command popup menus.

Update all the demos and the documentation.

This is part of #11
  • Loading branch information
kirill-grouchnikov committed Jan 31, 2022
1 parent 068fd97 commit 4582d80
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 94 deletions.
Expand Up @@ -141,7 +141,7 @@ private fun LazyListScope.columnOfItems(
/**
* Panel composable that hosts command buttons. Provides support for button groups,
* same icon state / dimension and column-fill / row-fill layout. Under
* [PanelLayoutFillMode.RowFill] layout mode, the buttons are laid out in rows, never
* [PanelLayoutSpec.RowFill] layout mode, the buttons are laid out in rows, never
* exceeding the available horizontal space. A vertical scroll bar will kick in once
* there is not enough vertical space to show all the buttons. The schematic below
* shows a row-fill command button panel:
Expand Down Expand Up @@ -173,7 +173,7 @@ private fun LazyListScope.columnOfItems(
* </p>
*
* <p>
* Under the [PanelLayoutFillMode.ColumnFill] layout mode, the buttons are laid
* Under the [PanelLayoutSpec.ColumnFill] layout mode, the buttons are laid
* out in columns, never exceeding the available vertical space. A horizontal scroll
* bar will kick in once there is not enough horizontal space to show all the
* buttons. The schematic below shows a column-fill command button panel:
Expand Down Expand Up @@ -234,10 +234,10 @@ internal fun AuroraCommandButtonPanel(
iconDisabledFilterStrategy = presentationModel.iconDisabledFilterStrategy
)

val extraEndPadding = if (presentationModel.layoutSpec is LayoutSpec.RowFill)
val extraEndPadding = if (presentationModel.layoutSpec is PanelLayoutSpec.RowFill)
ScrollBarSizingConstants.DefaultScrollBarThickness + ScrollBarSizingConstants.DefaultScrollBarMargin else 0.dp
val extraBottomPadding =
if (presentationModel.layoutSpec is LayoutSpec.ColumnFill)
if (presentationModel.layoutSpec is PanelLayoutSpec.ColumnFill)
ScrollBarSizingConstants.DefaultScrollBarThickness + ScrollBarSizingConstants.DefaultScrollBarMargin else 0.dp
val contentStartPadding =
presentationModel.contentPadding.calculateStartPadding(layoutDirection)
Expand All @@ -258,11 +258,11 @@ internal fun AuroraCommandButtonPanel(

val panelPlaceable: Placeable
when (presentationModel.layoutSpec) {
is LayoutSpec.RowFill -> {
is PanelLayoutSpec.RowFill -> {
val columnCount: Int = when (presentationModel.layoutSpec.rowFillSpec) {
is RowFillSpec.Fixed -> presentationModel.layoutSpec.rowFillSpec.count
is RowFillSpec.Adaptive -> (constraints.maxWidth - gapPx) /
(presentationModel.layoutSpec.rowFillSpec.minWidth.roundToPx() + gapPx)
is PanelRowFillSpec.Fixed -> presentationModel.layoutSpec.rowFillSpec.columnCount
is PanelRowFillSpec.Adaptive -> (constraints.maxWidth - gapPx) /
(presentationModel.layoutSpec.rowFillSpec.minColumnWidth.roundToPx() + gapPx)
}
val itemWidth = (constraints.maxWidth - gapPx * (columnCount + 1)) / columnCount

Expand Down Expand Up @@ -322,11 +322,11 @@ internal fun AuroraCommandButtonPanel(
}
}.first().measure(constraints)
}
is LayoutSpec.ColumnFill -> {
is PanelLayoutSpec.ColumnFill -> {
val rowCount: Int = when (presentationModel.layoutSpec.columnFillSpec) {
is ColumnFillSpec.Fixed -> presentationModel.layoutSpec.columnFillSpec.count
is ColumnFillSpec.Adaptive -> (constraints.maxHeight - gapPx) /
(presentationModel.layoutSpec.columnFillSpec.minHeight.roundToPx() + gapPx)
is PanelColumnFillSpec.Fixed -> presentationModel.layoutSpec.columnFillSpec.rowCount
is PanelColumnFillSpec.Adaptive -> (constraints.maxHeight - gapPx) /
(presentationModel.layoutSpec.columnFillSpec.minRowHeight.roundToPx() + gapPx)
}
val itemHeight = (constraints.maxHeight - gapPx * (rowCount + 1)) / rowCount

Expand Down Expand Up @@ -388,9 +388,9 @@ internal fun AuroraCommandButtonPanel(
}
}

internal fun getPreferredCommandButtonPanelSize(
internal fun getPreferredCommandPopupMenuPanelSize(
contentModel: CommandPanelContentModel,
presentationModel: CommandPanelPresentationModel,
presentationModel: CommandPopupMenuPanelPresentationModel,
buttonLayoutManager: CommandButtonLayoutManager,
layoutDirection: LayoutDirection,
density: Density
Expand All @@ -405,14 +405,14 @@ internal fun getPreferredCommandButtonPanelSize(
contentPadding = presentationModel.commandContentPadding,
presentationState = presentationModel.commandPresentationState,
iconDimension = presentationModel.commandIconDimension,
isMenu = presentationModel.isMenu,
backgroundAppearanceStrategy = presentationModel.backgroundAppearanceStrategy,
isMenu = true,
backgroundAppearanceStrategy = BackgroundAppearanceStrategy.Flat,
textStyle = presentationModel.commandTextStyle,
textOverflow = presentationModel.commandTextOverflow,
horizontalAlignment = presentationModel.commandHorizontalAlignment,
horizontalGapScaleFactor = presentationModel.commandHorizontalGapScaleFactor,
verticalGapScaleFactor = presentationModel.commandVerticalGapScaleFactor,
popupPlacementStrategy = presentationModel.popupPlacementStrategy,
popupPlacementStrategy = PopupPlacementStrategy.Downward,
iconActiveFilterStrategy = presentationModel.iconActiveFilterStrategy,
iconEnabledFilterStrategy = presentationModel.iconEnabledFilterStrategy,
iconDisabledFilterStrategy = presentationModel.iconDisabledFilterStrategy
Expand All @@ -435,10 +435,10 @@ internal fun getPreferredCommandButtonPanelSize(
}

val gap = (CommandPanelSizingConstants.DefaultGap.value * density.density)
var panelWidth = maxButtonWidth * presentationModel.maxColumns +
gap * (presentationModel.maxColumns + 1)
var panelHeight = maxButtonHeight * presentationModel.maxRows +
gap * (presentationModel.maxColumns + 1)
var panelWidth = maxButtonWidth * presentationModel.layoutSpec.columnCount +
gap * (presentationModel.layoutSpec.columnCount + 1)
var panelHeight = maxButtonHeight * presentationModel.layoutSpec.visibleRowCount +
gap * (presentationModel.layoutSpec.visibleRowCount + 1)

// Account for content padding
panelWidth += (presentationModel.contentPadding.calculateStartPadding(layoutDirection) +
Expand All @@ -449,11 +449,7 @@ internal fun getPreferredCommandButtonPanelSize(
// Account for scroll bar. For now the assumption is that it's always showing
val extraSpaceForScrollBar = (ScrollBarSizingConstants.DefaultScrollBarThickness +
ScrollBarSizingConstants.DefaultScrollBarMargin).value * density.density
if (presentationModel.layoutSpec is LayoutSpec.RowFill) {
panelWidth += extraSpaceForScrollBar
} else {
panelHeight += extraSpaceForScrollBar
}
panelWidth += extraSpaceForScrollBar

return Size(panelWidth, panelHeight)
}
Expand Down
Expand Up @@ -29,62 +29,51 @@ data class CommandPanelContentModel(
val commandActionPreview: CommandActionPreview? = null
) : ContentModel


enum class PanelLayoutFillMode {
/** The buttons are laid out in rows respecting the available width. */
RowFill,

/** The buttons are laid out in columns respecting the available height. */
ColumnFill
}

object CommandPanelSizingConstants {
val DefaultContentPadding = PaddingValues(6.dp)
val DefaultGap = 4.dp
}

sealed class RowFillSpec {
class Fixed(val count: Int) : RowFillSpec()
class Adaptive(val minWidth: Dp) : RowFillSpec()
sealed class PanelRowFillSpec {
class Fixed(val columnCount: Int) : PanelRowFillSpec()
class Adaptive(val minColumnWidth: Dp) : PanelRowFillSpec()

override fun hashCode() = if (this is Fixed) {
31 + count
31 + columnCount
} else {
require(this is Adaptive)
62 + minWidth.hashCode()
62 + minColumnWidth.hashCode()
}

override fun equals(other: Any?) =
(this is Fixed && other is Fixed && this.count == other.count) ||
(this is Adaptive && other is Adaptive && this.minWidth == other.minWidth)
(this is Fixed && other is Fixed && this.columnCount == other.columnCount) ||
(this is Adaptive && other is Adaptive && this.minColumnWidth == other.minColumnWidth)
}

sealed class ColumnFillSpec {
class Fixed(val count: Int) : ColumnFillSpec()
class Adaptive(val minHeight: Dp) : ColumnFillSpec()
sealed class PanelColumnFillSpec {
class Fixed(val rowCount: Int) : PanelColumnFillSpec()
class Adaptive(val minRowHeight: Dp) : PanelColumnFillSpec()

override fun hashCode() = if (this is Fixed) {
31 + count
31 + rowCount
} else {
require(this is Adaptive)
62 + minHeight.hashCode()
62 + minRowHeight.hashCode()
}

override fun equals(other: Any?) =
(this is Fixed && other is Fixed && this.count == other.count) ||
(this is Adaptive && other is Adaptive && this.minHeight == other.minHeight)
(this is Fixed && other is Fixed && this.rowCount == other.rowCount) ||
(this is Adaptive && other is Adaptive && this.minRowHeight == other.minRowHeight)
}

sealed class LayoutSpec {
class RowFill(val rowFillSpec: RowFillSpec): LayoutSpec()
class ColumnFill(val columnFillSpec: ColumnFillSpec): LayoutSpec()
sealed class PanelLayoutSpec {
class RowFill(val rowFillSpec: PanelRowFillSpec) : PanelLayoutSpec()
class ColumnFill(val columnFillSpec: PanelColumnFillSpec) : PanelLayoutSpec()
}

data class CommandPanelPresentationModel(
val contentPadding: PaddingValues = CommandPanelSizingConstants.DefaultContentPadding,
val layoutSpec: LayoutSpec = LayoutSpec.RowFill(RowFillSpec.Adaptive(48.dp)),
val maxColumns: Int = -1, // only relevant when layoutFillMode is RowFill
val maxRows: Int = -1, // only relevant when layoutFillMode is ColumnFill
val layoutSpec: PanelLayoutSpec = PanelLayoutSpec.RowFill(PanelRowFillSpec.Adaptive(48.dp)),
val showGroupLabels: Boolean = true,
val commandPresentationState: CommandButtonPresentationState,
val commandIconDimension: Dp = 0.dp,
Expand All @@ -101,3 +90,43 @@ data class CommandPanelPresentationModel(
val popupPlacementStrategy: PopupPlacementStrategy = PopupPlacementStrategy.Downward,
val isMenu: Boolean = false
) : PresentationModel

data class MenuPopupPanelLayoutSpec(val columnCount: Int, val visibleRowCount: Int)

data class CommandPopupMenuPanelPresentationModel(
val layoutSpec: MenuPopupPanelLayoutSpec,
val contentPadding: PaddingValues = CommandPanelSizingConstants.DefaultContentPadding,
val showGroupLabels: Boolean = true,
val commandPresentationState: CommandButtonPresentationState,
val commandIconDimension: Dp = 0.dp,
val commandContentPadding: PaddingValues = CommandButtonSizingConstants.CompactButtonContentPadding,
val commandTextStyle: TextStyle? = null,
val commandTextOverflow: TextOverflow = TextOverflow.Clip,
val commandHorizontalAlignment: HorizontalAlignment = HorizontalAlignment.Center,
val commandHorizontalGapScaleFactor: Float = 1.0f,
val commandVerticalGapScaleFactor: Float = 1.0f,
val iconDisabledFilterStrategy: IconFilterStrategy = IconFilterStrategy.ThemedFollowColorScheme,
val iconEnabledFilterStrategy: IconFilterStrategy = IconFilterStrategy.Original,
val iconActiveFilterStrategy: IconFilterStrategy = IconFilterStrategy.Original
) : PresentationModel {
fun toCommandPanelPresentationModel(): CommandPanelPresentationModel {
return CommandPanelPresentationModel(
contentPadding = this.contentPadding,
layoutSpec = PanelLayoutSpec.RowFill(PanelRowFillSpec.Fixed(this.layoutSpec.columnCount)),
showGroupLabels = this.showGroupLabels,
commandPresentationState = this.commandPresentationState,
commandIconDimension = this.commandIconDimension,
commandContentPadding = this.commandContentPadding,
commandTextStyle = this.commandTextStyle,
commandTextOverflow = this.commandTextOverflow,
commandHorizontalAlignment = this.commandHorizontalAlignment,
commandHorizontalGapScaleFactor = this.commandHorizontalGapScaleFactor,
commandVerticalGapScaleFactor = this.commandVerticalGapScaleFactor,
backgroundAppearanceStrategy = BackgroundAppearanceStrategy.Flat,
iconDisabledFilterStrategy = this.iconDisabledFilterStrategy,
iconEnabledFilterStrategy = this.iconEnabledFilterStrategy,
iconActiveFilterStrategy = this.iconActiveFilterStrategy,
isMenu = true
)
}
}
Expand Up @@ -26,7 +26,7 @@ import org.pushingpixels.aurora.theming.IconFilterStrategy
import org.pushingpixels.aurora.theming.PopupPlacementStrategy

data class CommandPopupMenuPresentationModel(
val panelPresentationModel: CommandPanelPresentationModel? = null,
val panelPresentationModel: CommandPopupMenuPanelPresentationModel? = null,
val menuPresentationState: CommandButtonPresentationState =
DefaultCommandPopupMenuPresentationState,
val menuIconActiveFilterStrategy: IconFilterStrategy = IconFilterStrategy.Original,
Expand Down
Expand Up @@ -75,7 +75,7 @@ class CommandButtonPanelProjection(
fun project(modifier: Modifier = Modifier) {
require(
!presentationModel.showGroupLabels ||
(presentationModel.layoutSpec is LayoutSpec.RowFill)
(presentationModel.layoutSpec is PanelLayoutSpec.RowFill)
) {
"Column fill layout is not supported when group labels are shown"
}
Expand Down
Expand Up @@ -20,7 +20,10 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalContext
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposeWindow
import androidx.compose.ui.geometry.Rect
Expand Down Expand Up @@ -104,7 +107,7 @@ internal fun displayPopupContent(

// Compute the size of the popup content, accounting for the panel
val panelPreferredSize =
if (hasButtonPanel) getPreferredCommandButtonPanelSize(
if (hasButtonPanel) getPreferredCommandPopupMenuPanelSize(
contentModel = contentModel.value!!.panelContentModel!!,
presentationModel = presentationModel.panelPresentationModel!!,
buttonLayoutManager = panelButtonLayoutManager!!,
Expand Down Expand Up @@ -421,7 +424,7 @@ private fun TopLevelPopupContent(
if (hasPanel) {
AuroraCommandButtonPanel(
contentModel = menuContentModel.value!!.panelContentModel!!,
presentationModel = menuPresentationModel.panelPresentationModel!!,
presentationModel = menuPresentationModel.panelPresentationModel!!.toCommandPanelPresentationModel(),
extraAction = {
if (toDismissPopupsOnActivation) {
AuroraPopupManager.hidePopups(null)
Expand Down
Expand Up @@ -180,7 +180,7 @@ fun AuroraWindowScope.BreadcrumbContent(auroraSkinDefinition: MutableState<Auror
CommandButtonPanelProjection(
contentModel = commandPanelContentModel.value!!,
presentationModel = CommandPanelPresentationModel(
layoutSpec = LayoutSpec.RowFill(RowFillSpec.Adaptive(140.dp)),
layoutSpec = PanelLayoutSpec.RowFill(PanelRowFillSpec.Adaptive(140.dp)),
showGroupLabels = false,
backgroundAppearanceStrategy = BackgroundAppearanceStrategy.Flat,
commandPresentationState = CommandButtonPresentationState.Medium,
Expand Down
Expand Up @@ -292,16 +292,14 @@ fun CommandDemoEditStrip(
),
commandPaste to CommandButtonPresentationModel.Overlay(
popupMenuPresentationModel = CommandPopupMenuPresentationModel(
panelPresentationModel = CommandPanelPresentationModel(
panelPresentationModel = CommandPopupMenuPanelPresentationModel(
layoutSpec = MenuPopupPanelLayoutSpec(columnCount = 5, visibleRowCount = 3),
contentPadding = PaddingValues(0.dp),
showGroupLabels = true,
commandPresentationState = CommandButtonPresentationState.BigFitToIcon,
commandIconDimension = 24.dp,
backgroundAppearanceStrategy = BackgroundAppearanceStrategy.Flat,
iconActiveFilterStrategy = IconFilterStrategy.ThemedFollowText,
iconEnabledFilterStrategy = IconFilterStrategy.ThemedFollowText,
maxColumns = 5,
maxRows = 3
iconEnabledFilterStrategy = IconFilterStrategy.ThemedFollowText
)
)
)
Expand Down
Expand Up @@ -128,7 +128,7 @@ fun main() = auroraApplication {
) {
CommandButtonPanelProjection(
contentModel = commandPanelContentModel.value,
presentationModel = CommandPanelPresentationModel(layoutSpec = LayoutSpec.RowFill(RowFillSpec.Adaptive(140.dp)),
presentationModel = CommandPanelPresentationModel(layoutSpec = PanelLayoutSpec.RowFill(PanelRowFillSpec.Adaptive(140.dp)),
showGroupLabels = true,
backgroundAppearanceStrategy = BackgroundAppearanceStrategy.Flat,
commandPresentationState = CommandButtonPresentationState.Medium,
Expand All @@ -144,7 +144,7 @@ fun main() = auroraApplication {
CommandButtonPanelProjection(
contentModel = commandPanelContentModel.value,
presentationModel = CommandPanelPresentationModel(
layoutSpec = LayoutSpec.ColumnFill(ColumnFillSpec.Adaptive(80.dp)),
layoutSpec = PanelLayoutSpec.ColumnFill(PanelColumnFillSpec.Adaptive(80.dp)),
showGroupLabels = false,
backgroundAppearanceStrategy = BackgroundAppearanceStrategy.Flat,
commandPresentationState = CommandButtonPresentationState.Big,
Expand Down

0 comments on commit 4582d80

Please sign in to comment.