diff --git a/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildConfig.kt b/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildConfig.kt index 82edd0c8..86c9e752 100644 --- a/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildConfig.kt +++ b/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildConfig.kt @@ -18,6 +18,9 @@ object BuildConfig { var integerVersionName: String = "" internal set + var branch: String = "dev" + internal set + object Android { const val compileSdkVersion = 34 diff --git a/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildPlugin.kt b/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildPlugin.kt index 26c1f3e1..c3748422 100644 --- a/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildPlugin.kt +++ b/build-plugin/src/main/java/com/konyaco/fluent/plugin/build/BuildPlugin.kt @@ -1,5 +1,7 @@ package com.konyaco.fluent.plugin.build +import com.konyaco.fluent.plugin.build.BuildConfig.branch +import com.konyaco.fluent.plugin.build.BuildConfig.integerVersionName import com.konyaco.fluent.plugin.build.BuildConfig.isRelease import com.konyaco.fluent.plugin.build.BuildConfig.libraryVersion import com.konyaco.fluent.plugin.build.BuildConfig.snapshotLibraryVersion @@ -103,10 +105,20 @@ class BuildPlugin : Plugin { private fun setupLibraryVersion(target: Project) { val providers = target.providers + providers.exec { + commandLine("git", "branch", "--show-current") + isIgnoreExitValue = true + }.standardOutput + .asText + .orNull + ?.trim() + ?.let { branch = it } + val gitTag = providers.exec { commandLine("git", "describe", "--abbrev=0", "--tags") isIgnoreExitValue = true }.standardOutput.asText.get().trim() + val relativeCommitCount = providers.exec { commandLine("git", "describe", "--tags") isIgnoreExitValue = true @@ -125,7 +137,7 @@ class BuildPlugin : Plugin { else -> snapshotLibraryVersion } - BuildConfig.integerVersionName = libraryVersion + integerVersionName = libraryVersion .removePrefix("v") .removeSuffix("-SNAPSHOT") .substringBefore("-dev") diff --git a/build.gradle.kts b/build.gradle.kts index 357d3824..093d0d1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,7 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.build.konfig) apply false id("com.konyaco.fluent.plugin.build") } diff --git a/gallery/build.gradle.kts b/gallery/build.gradle.kts index 9d0dabe4..225e2ac6 100644 --- a/gallery/build.gradle.kts +++ b/gallery/build.gradle.kts @@ -1,4 +1,5 @@ import com.android.build.api.variant.impl.VariantOutputImpl +import com.codingfeline.buildkonfig.compiler.FieldSpec import com.konyaco.fluent.plugin.build.BuildConfig import com.konyaco.fluent.plugin.build.applyTargets import org.jetbrains.compose.desktop.application.dsl.TargetFormat @@ -10,6 +11,7 @@ plugins { alias(libs.plugins.compose) alias(libs.plugins.android.application) alias(libs.plugins.ksp) + alias(libs.plugins.build.konfig) } kotlin { @@ -159,6 +161,9 @@ compose.desktop { windows { iconFile.set(project.file("icons/icon.ico")) upgradeUuid = "a23572e1-c6fd-4b76-98ec-1e45953eb941" + shortcut = true + menu = true + perUserInstall = true } linux { iconFile.set(project.file("icons/icon.png")) @@ -167,6 +172,20 @@ compose.desktop { } } +buildkonfig { + packageName = "${BuildConfig.packageName}.build" + + defaultConfigs { + buildConfigField(FieldSpec.Type.STRING, "LIBRARY_VERSION", BuildConfig.libraryVersion, const = true) + buildConfigField(FieldSpec.Type.STRING, "GALLERY_VERSION", BuildConfig.integerVersionName, const = true) + buildConfigField(FieldSpec.Type.STRING, "COMPOSE_VERSION", libs.versions.compose.get(), const = true) + buildConfigField(FieldSpec.Type.STRING, "KOTLIN_VERSION", libs.versions.kotlin.get(), const = true) + buildConfigField(FieldSpec.Type.STRING, "HAZE_VERSION", libs.versions.haze.get(), const = true) + + buildConfigField(FieldSpec.Type.STRING, "CURRENT_BRANCH", BuildConfig.branch, const = true) + } +} + dependencies { val processor = project(":gallery-processor") add("kspCommonMainMetadata", processor) diff --git a/gallery/src/commonMain/composeResources/drawable/banner.png b/gallery/src/commonMain/composeResources/drawable/banner.png index e88b50df..074654b1 100644 Binary files a/gallery/src/commonMain/composeResources/drawable/banner.png and b/gallery/src/commonMain/composeResources/drawable/banner.png differ diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/App.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/App.kt index 84e4abd6..40f8d5f8 100644 --- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/App.kt +++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/App.kt @@ -147,6 +147,9 @@ fun App( } }, footer = { + val settingItem = remember(navigator) { + ComponentItem("Settings", group = "", description = "", icon = Icons.Default.Settings) { SettingsScreen(navigator) } + } NavigationItem(navigator.latestBackEntry, navigator::navigate, settingItem) } ) { @@ -237,6 +240,4 @@ private fun NavigationItem( } } ) -} - -private val settingItem = ComponentItem("Settings", group = "", description = "", icon = Icons.Default.Settings) { SettingsScreen() } \ No newline at end of file +} \ No newline at end of file diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/ProjectUrl.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/ProjectUrl.kt index 6219dc8b..c944b216 100644 --- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/ProjectUrl.kt +++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/ProjectUrl.kt @@ -1,5 +1,7 @@ package com.konyaco.fluent.gallery +import com.konyaco.fluent.build.BuildKonfig + object ProjectUrl { const val ROOT = "https://github.com/Konyaco/compose-fluent-ui" @@ -10,7 +12,7 @@ object ProjectUrl { const val FEED_BACK = "$ROOT/issues/new/choose" - private const val BRANCH = "master" + private const val BRANCH = BuildKonfig.CURRENT_BRANCH fun componentCodeOf(path: String): String { return "$ROOT/tree/$BRANCH/$path" diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/HomeScreen.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/HomeScreen.kt index e0fd9d90..e6176e2a 100644 --- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/HomeScreen.kt +++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/HomeScreen.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.konyaco.fluent.FluentTheme +import com.konyaco.fluent.build.BuildKonfig import com.konyaco.fluent.component.Icon import com.konyaco.fluent.component.Text import com.konyaco.fluent.gallery.LocalStore @@ -86,13 +87,25 @@ fun HomeScreen() { contentDescription = null, modifier = Modifier.scale(2.05f) ) - Text( - modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp), - text = "Compose\nFluent Design", - style = FluentTheme.typography.titleLarge, - textAlign = TextAlign.End, - color = FluentTheme.colors.text.text.primary - ) + Column( + Modifier.padding(16.dp).align(Alignment.BottomEnd), + horizontalAlignment = Alignment.End + ) { + Text( + modifier = Modifier, + text = "Compose\nFluent Design", + style = FluentTheme.typography.title, + textAlign = TextAlign.End, + color = FluentTheme.colors.text.text.primary + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = BuildKonfig.LIBRARY_VERSION, + style = FluentTheme.typography.body, + textAlign = TextAlign.End, + color = FluentTheme.colors.text.text.secondary + ) + } } Card( diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt index edf3ac2d..af556d1f 100644 --- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt +++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt @@ -1,491 +1,283 @@ package com.konyaco.fluent.gallery.screen.settings -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.Image +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.CutCornerShape +import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.unit.Density +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.dp import com.konyaco.fluent.FluentTheme -import com.konyaco.fluent.LocalContentColor -import com.konyaco.fluent.background.BackgroundSizing -import com.konyaco.fluent.background.Layer -import com.konyaco.fluent.component.AccentButton -import com.konyaco.fluent.component.Button -import com.konyaco.fluent.component.CheckBox -import com.konyaco.fluent.component.ContentDialog -import com.konyaco.fluent.component.DropdownMenu -import com.konyaco.fluent.component.DropdownMenuItem -import com.konyaco.fluent.component.FlyoutContainer -import com.konyaco.fluent.component.FlyoutPlacement +import com.konyaco.fluent.build.BuildKonfig +import com.konyaco.fluent.component.CardExpanderItem +import com.konyaco.fluent.component.Expander +import com.konyaco.fluent.component.ExpanderItem +import com.konyaco.fluent.component.ExpanderItemSeparator import com.konyaco.fluent.component.Icon -import com.konyaco.fluent.component.MenuFlyoutContainer -import com.konyaco.fluent.component.MenuFlyoutItem -import com.konyaco.fluent.component.MenuFlyoutSeparator -import com.konyaco.fluent.component.ProgressBar -import com.konyaco.fluent.component.ProgressRing -import com.konyaco.fluent.component.ProgressRingSize -import com.konyaco.fluent.component.RadioButton import com.konyaco.fluent.component.ScrollbarContainer -import com.konyaco.fluent.component.Slider -import com.konyaco.fluent.component.SubtleButton import com.konyaco.fluent.component.Switcher -import com.konyaco.fluent.component.TabItem -import com.konyaco.fluent.component.TabRow -import com.konyaco.fluent.component.TabViewDefaults import com.konyaco.fluent.component.Text -import com.konyaco.fluent.component.TextField import com.konyaco.fluent.component.rememberScrollbarAdapter import com.konyaco.fluent.gallery.LocalStore +import com.konyaco.fluent.gallery.ProjectUrl +import com.konyaco.fluent.gallery.component.ComponentItem +import com.konyaco.fluent.gallery.component.ComponentNavigator +import com.konyaco.fluent.gallery.screen.test.TestComponentScreen import com.konyaco.fluent.icons.Icons -import com.konyaco.fluent.icons.regular.Add -import com.konyaco.fluent.icons.regular.ArrowLeft -import com.konyaco.fluent.icons.regular.Checkmark -import com.konyaco.fluent.icons.regular.ClipboardMore -import com.konyaco.fluent.icons.regular.Delete -import com.konyaco.fluent.icons.regular.Dismiss +import com.konyaco.fluent.icons.regular.Blur +import com.konyaco.fluent.icons.regular.Bug +import com.konyaco.fluent.icons.regular.ChevronRight +import com.konyaco.fluent.icons.regular.Color import com.konyaco.fluent.icons.regular.List -import com.konyaco.fluent.icons.regular.Navigation -import com.konyaco.fluent.surface.Card +import fluentdesign.gallery.generated.resources.Res +import fluentdesign.gallery.generated.resources.icon +import org.jetbrains.compose.resources.painterResource @Composable -fun SettingsScreen() { - var displayDialog by remember { mutableStateOf(false) } - val density = LocalDensity.current - var scale by remember(density) { mutableStateOf(density.density) } - val store = LocalStore.current - - Column(Modifier.verticalScroll(rememberScrollState()).padding(16.dp), Arrangement.spacedBy(8.dp)) { - Controller( - scale = scale, - onScaleChange = { scale = it }, - darkMode = store.darkMode, - onDarkModeChange = { store.darkMode = it }, - acrylicPopupEnabled = store.enabledAcrylicPopup, - onAcrylicPopupChange = { store.enabledAcrylicPopup = it }, - compactModeEnabled = store.compactMode, - onCompactModeChange = { store.compactMode = it } +fun SettingsScreen(componentNavigator: ComponentNavigator) { + val scrollState = rememberScrollState() + Column { + Text( + text = "Settings", + style = FluentTheme.typography.titleLarge, + modifier = Modifier.alignHorizontalSpace() + .padding(top = 36.dp) ) - - CompositionLocalProvider(LocalDensity provides Density(scale)) { - Content() - } - - AccentButton(onClick = { - displayDialog = true - }) { Text("Display Dialog") } - - Box { - var expanded by remember { mutableStateOf(false) } - - Button(onClick = { - expanded = true - }) { - Text("Show DropdownMenu") - } - - fun close() { - expanded = false - } - - DropdownMenu(expanded, ::close) { - DropdownMenuItem(::close) { Text("Option 1") } - DropdownMenuItem(::close) { Text("Option 2") } - DropdownMenuItem(::close) { Text("Option 3") } - } - } - var currentPlacement by remember { - mutableStateOf(FlyoutPlacement.Auto) - } - Row { - - FlyoutContainer( - flyout = { - Text("this is a flyout") - }, - placement = currentPlacement, - content = { - Button( - onClick = { isFlyoutVisible = true } - ) { - Text("Open Flyout") - } - } - ) - Spacer(Modifier.width(8.dp)) - Box { - var isFlyoutPlacementDropdownMenuOpened by remember { - mutableStateOf(false) - } - Button(onClick = { - isFlyoutPlacementDropdownMenuOpened = true - }) { - Text("Flyout placement") - } - val item = @Composable { placement: FlyoutPlacement -> - DropdownMenuItem({ - currentPlacement = placement - isFlyoutPlacementDropdownMenuOpened = false - }) { - Icon( - Icons.Default.Checkmark, - contentDescription = null, - modifier = Modifier.padding(end = 8.dp) - .alpha(if (placement == currentPlacement) 1f else 0f) + ScrollbarContainer( + adapter = rememberScrollbarAdapter(scrollState) + ) { + val store = LocalStore.current + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier + .verticalScroll(scrollState) + .alignHorizontalSpace() + .padding(top = 8.dp) + .padding(bottom = 24.dp) + ) { + Header("Appearance & behavior") + CardExpanderItem( + heading = { + Text("App Theme") + }, + icon = { + Icon(Icons.Regular.Color, contentDescription = null) + }, + caption = { + Text("Select which app theme to display") + }, + trailing = { + Switcher( + checked = store.darkMode, + text = if (store.darkMode) "Dark" else "Light", + textBefore = true, + onCheckStateChange = { store.darkMode = it } ) - Text(text = placement.toString()) } - } - DropdownMenu( - isFlyoutPlacementDropdownMenuOpened, - { isFlyoutPlacementDropdownMenuOpened = false }) { - FlyoutPlacement.entries.forEach { item(it) } - } - } - } - - MenuFlyoutContainer( - placement = currentPlacement, - flyout = { - MenuFlyoutItem( - onClick = { - + ) + CardExpanderItem( + heading = { + Text("Acrylic Flyout") }, icon = { - Icon(Icons.Default.Delete, contentDescription = null) + Icon( + imageVector = Icons.Regular.Blur, + contentDescription = "Blur" + ) + }, + caption = { + Text("Enable Acrylic effect on Flyout") }, - text = { - Text("Delete") + trailing = { + Switcher( + checked = store.enabledAcrylicPopup, + text = if (store.enabledAcrylicPopup) "On" else "Off", + textBefore = true, + onCheckStateChange = { store.enabledAcrylicPopup = it } + ) } ) - MenuFlyoutSeparator() - MenuFlyoutItem( - onClick = { - + CardExpanderItem( + heading = { + Text("Compact Mode") }, icon = { - Icon(Icons.Default.Add, contentDescription = null) + Icon( + imageVector = Icons.Regular.List, + contentDescription = "List" + ) }, - text = { - Text("Add") + caption = { + Text("Adjust ListItem height") + }, + trailing = { + Switcher( + checked = store.compactMode, + text = if (store.compactMode) "Compact" else "Standard", + textBefore = true, + onCheckStateChange = { store.compactMode = it } + ) } ) - MenuFlyoutSeparator() - MenuFlyoutItem( - onClick = {}, - icon = {}, - text = { Text("Test") } - ) - MenuFlyoutItem( - items = { - MenuFlyoutItem( - onClick = { + // Hide this test component if gallery is release version. + if (!BuildKonfig.CURRENT_BRANCH.equals("master", false)) { + Header("Test") + CardExpanderItem( + heading = { + Text("Test Component") + }, + caption = { + Text("Test Component with some settings like scale fraction and dark theme") + }, + onClick = { + componentNavigator.navigate(testComponent) + }, + icon = { + Icon(Icons.Regular.Bug, contentDescription = null) + }, + dropdown = { + Icon(Icons.Regular.ChevronRight, null) + } + ) + } + Header("About") + Expander( + heading = { + Text("Compose Fluent Design Gallery") + }, + icon = { + Image( + painter = painterResource(Res.drawable.icon), + contentDescription = null, + modifier = Modifier.size(16.dp) + ) + }, + caption = {}, + trailing = { + Text(BuildKonfig.GALLERY_VERSION) + }, + expandContent = { + + ExpanderItem( + icon = {}, + heading = { + Text("To clone this repository") + }, + trailing = { + SelectionContainer { + Text("git clone ${ProjectUrl.ROOT}.git") + } + } + ) + ExpanderItemSeparator() + + ExpanderItem( + heading = { + Text("Compose Fluent Design") }, - icon = { - Icon(Icons.Default.Add, contentDescription = null) + trailing = { + Text(BuildKonfig.LIBRARY_VERSION) + } + ) + ExpanderItemSeparator() + ExpanderItem( + heading = { + Text("Kotlin") }, - text = { - Text("Add") + trailing = { + Text(BuildKonfig.KOTLIN_VERSION) } ) - }, - icon = { - Icon(Icons.Default.ClipboardMore, contentDescription = null) - }, - text = { - Text("More") + ExpanderItemSeparator() + ExpanderItem( + heading = { + Text("Compose Multiplatform") + }, + trailing = { + Text(BuildKonfig.COMPOSE_VERSION) + } + ) + ExpanderItemSeparator() + ExpanderItem( + heading = { + Text("Haze") + }, + trailing = { + Text(BuildKonfig.HAZE_VERSION) + }, + ) } ) - }, - content = { - Button( - onClick = { isFlyoutVisible = !isFlyoutVisible } - ) { - Text("Open MenuFlyout") - } } - ) - } - - ContentDialog( - title = "This is an example dialog", - visible = displayDialog, - primaryButtonText = "Confirm", - closeButtonText = "Cancel", - onButtonClick = { displayDialog = false }, - content = { - Text( - "This is body text. Windows 11 marks a visual evolution of the operating system. We have evolved our design language alongside with Fluent to create a design which is human, universal and truly feels like Windows. \n" + - "\n" + - "The design principles below have guided us throughout the journey of making Windows the best-in-class implementation of Fluent.\n" - ) } - ) -} - - -@Composable -private fun Controller( - scale: Float, - onScaleChange: (Float) -> Unit, - darkMode: Boolean, - onDarkModeChange: (Boolean) -> Unit, - acrylicPopupEnabled: Boolean, - onAcrylicPopupChange: (Boolean) -> Unit, - compactModeEnabled: Boolean, - onCompactModeChange: (Boolean) -> Unit -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - Text("Scale: ${scale.toString().take(4)}") - val density = LocalDensity.current - Button(onClick = { onScaleChange(density.density) }) { Text("Reset") } - Switcher(darkMode, text = "Dark Mode", onCheckStateChange = { onDarkModeChange(it) }) - Switcher(acrylicPopupEnabled, text = "Acrylic Popup", onCheckStateChange = { onAcrylicPopupChange(it) }) - Switcher(compactModeEnabled, text = "Compact Mode", onCheckStateChange = { onCompactModeChange(it) }) } - Slider( - modifier = Modifier.width(200.dp), - value = scale, - onValueChange = { onScaleChange(it) }, - valueRange = 1f..10f - ) } -@Composable -private fun Content() { - - var sliderValue by remember { mutableStateOf(0.5f) } - Slider( - modifier = Modifier.width(200.dp), - value = sliderValue, - onValueChange = { sliderValue = it }, - ) - Buttons() - - Controls() - - val layerScrollState = rememberScrollState() - ScrollbarContainer( - adapter = rememberScrollbarAdapter(layerScrollState), - isVertical = false - ) { - Row(modifier = Modifier.padding(bottom = 8.dp).horizontalScroll(layerScrollState)) { - Box { - Box(Modifier.size(32.dp).background(FluentTheme.colors.fillAccent.default)) - } - - Layer( - shape = FluentTheme.shapes.control, - color = FluentTheme.colors.fillAccent.default, - border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default), - content = { - Box(Modifier.size(32.dp)) - }, - backgroundSizing = BackgroundSizing.InnerBorderEdge - ) - Layer( - shape = FluentTheme.shapes.control, - color = FluentTheme.colors.fillAccent.default, - border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default), - content = { - Box(Modifier.size(32.dp)) - }, - backgroundSizing = BackgroundSizing.OuterBorderEdge - ) - - Layer( - shape = CutCornerShape(4.dp), - color = FluentTheme.colors.fillAccent.default, - border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default.copy(0.5f)), - content = { - Box(Modifier.size(32.dp)) - }, - backgroundSizing = BackgroundSizing.InnerBorderEdge - ) - Layer( - shape = CutCornerShape(4.dp), - color = FluentTheme.colors.fillAccent.default, - border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default.copy(0.5f)), - content = { - Box(Modifier.size(32.dp)) - }, - backgroundSizing = BackgroundSizing.OuterBorderEdge - ) - - Layer( - shape = CircleShape, - color = FluentTheme.colors.fillAccent.default, - border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default), - content = { - Box(Modifier.size(32.dp)) - }, - backgroundSizing = BackgroundSizing.InnerBorderEdge - ) - Layer( - shape = CircleShape, - color = FluentTheme.colors.fillAccent.default, - border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default), - content = { - Box(Modifier.size(32.dp)) - }, - backgroundSizing = BackgroundSizing.OuterBorderEdge - ) - - Card(Modifier) { - Box(Modifier.size(32.dp)) - } - } - } - var value by remember { mutableStateOf(TextFieldValue("Hello Fluent!")) } - TextField(value, onValueChange = { value = it }) - TextField( - value = value, onValueChange = { value = it }, enabled = false, - header = { Text("With Header") } - ) - - // ProgressRings - Row( - horizontalArrangement = Arrangement.spacedBy(32.dp), - verticalAlignment = Alignment.CenterVertically - ) { - ProgressRing(size = ProgressRingSize.Medium) - ProgressRing(progress = sliderValue) - AccentButton(onClick = {}) { - ProgressRing(size = ProgressRingSize.Small, color = LocalContentColor.current) - Text("Small") - } - } - - ProgressBar(sliderValue) - ProgressBar() - - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - for (imageVector in icons) { - Icon( - modifier = Modifier.size(18.dp), - imageVector = imageVector, contentDescription = null - ) - } - } - - val selectedKey = remember { mutableStateOf(0) } - val tabItems = remember { mutableStateListOf(0,1,2,3,4) } - TabRow( - selectedKey = { selectedKey.value }, - borderColor = FluentTheme.colors.stroke.card.default, - ) { - items(tabItems, key = { it }) { index -> - TabItem( - selected = index == selectedKey.value, - onSelectedChanged = { selectedKey.value = index }, - content = { Text(index.toString()) }, - colors = if (index == selectedKey.value) { - TabViewDefaults.selectedItemTitleBarColors() - } else { - TabViewDefaults.defaultItemTitleBarColors() - }, - endDividerVisible = index != selectedKey.value - 1, - modifier = Modifier.widthIn(60.dp) - ) - } - item { - TabViewDefaults.TabAddButton( - onClick = { tabItems.add(tabItems.size) } - ) - } - } -} +fun Modifier.alignHorizontalSpace() = then( + Modifier + .fillMaxWidth() + .wrapContentWidth(align = Alignment.CenterHorizontally) + .padding(horizontal = 32.dp) + .widthIn(max = 1000.dp) + .fillMaxWidth() +) @Composable -private fun Controls() { - var checked by remember { mutableStateOf(false) } - Switcher(checked, text = null, onCheckStateChange = { checked = it }) - - var checked2 by remember { mutableStateOf(true) } - Switcher(checked2, text = "With Label", onCheckStateChange = { checked2 = it }) - - var checked3 by remember { mutableStateOf(true) } - Switcher( - checked3, - text = "Before Label", - textBefore = true, - onCheckStateChange = { checked3 = it } +private fun Header(text: String) { + Text( + text = text, + style = FluentTheme.typography.bodyStrong, + modifier = Modifier.padding(top = 16.dp, bottom = 4.dp) ) - - var checked4 by remember { mutableStateOf(false) } - CheckBox(checked4) { checked4 = it } - - var checked5 by remember { mutableStateOf(true) } - CheckBox(checked5, label = "With Label") { checked5 = it } - - var selectedRadio by remember { mutableStateOf(0) } - RadioButton(selectedRadio == 0, onClick = { selectedRadio = 0 }) - RadioButton(selectedRadio == 1, onClick = { selectedRadio = 1 }, label = "With Label") } @Composable -private fun Buttons() { - var text by remember { mutableStateOf("Hello World") } - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - val onClick = { text = "Hello, Fluent Design!" } - Button(onClick) { Text(text) } - - AccentButton(onClick) { - Icon(Icons.Default.Checkmark, contentDescription = null) - Text(text) - } - - SubtleButton(onClick) { - Text("Text Button") - } - } - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - AccentButton({}, iconOnly = true) { - Icon(Icons.Default.Navigation, contentDescription = null) - } - Button({}, iconOnly = true) { - Icon(Icons.Default.Navigation, contentDescription = null) - } - SubtleButton({}, iconOnly = true) { - Icon(Icons.Default.Navigation, contentDescription = null) - } - } +private fun Expander( + heading: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + interactionSource: MutableInteractionSource? = null, + shape: Shape = FluentTheme.shapes.control, + icon: (@Composable () -> Unit)? = {}, + caption: @Composable () -> Unit = {}, + trailing: @Composable () -> Unit = {}, + expandContent: (@Composable ColumnScope.() -> Unit) = {}, +) { + var expanded by rememberSaveable { mutableStateOf(false) } + Expander( + expanded = expanded, + onExpandedChanged = { expanded = it }, + heading = heading, + modifier = modifier, + enabled = enabled, + interactionSource = interactionSource, + shape = shape, + icon = icon, + caption = caption, + trailing = trailing, + expandContent = expandContent + ) } -private val icons = arrayOf( - Icons.Default.Add, - Icons.Default.Delete, - Icons.Default.Dismiss, - Icons.Default.ArrowLeft, - Icons.Default.Navigation, - Icons.Default.List +private val testComponent = ComponentItem( + name = "Test Component", + description = "Test Component with some settings like scale fraction and dark theme", + group = "settings", + content = { TestComponentScreen() } ) \ No newline at end of file diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/HomeScreen.kt.bak b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/test/TestComponentScreen.kt similarity index 70% rename from gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/HomeScreen.kt.bak rename to gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/test/TestComponentScreen.kt index 44576c48..90ec0389 100644 --- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/HomeScreen.kt.bak +++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/test/TestComponentScreen.kt @@ -1,11 +1,29 @@ -package com.konyaco.fluent.gallery.screen - -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +package com.konyaco.fluent.gallery.screen.test + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CutCornerShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.* +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -17,25 +35,65 @@ import com.konyaco.fluent.FluentTheme import com.konyaco.fluent.LocalContentColor import com.konyaco.fluent.background.BackgroundSizing import com.konyaco.fluent.background.Layer -import com.konyaco.fluent.component.* +import com.konyaco.fluent.component.AccentButton +import com.konyaco.fluent.component.Button +import com.konyaco.fluent.component.CheckBox +import com.konyaco.fluent.component.ContentDialog +import com.konyaco.fluent.component.DropdownMenu +import com.konyaco.fluent.component.DropdownMenuItem +import com.konyaco.fluent.component.FlyoutContainer +import com.konyaco.fluent.component.FlyoutPlacement +import com.konyaco.fluent.component.Icon +import com.konyaco.fluent.component.MenuFlyoutContainer +import com.konyaco.fluent.component.MenuFlyoutItem +import com.konyaco.fluent.component.MenuFlyoutSeparator +import com.konyaco.fluent.component.ProgressBar +import com.konyaco.fluent.component.ProgressRing +import com.konyaco.fluent.component.ProgressRingSize +import com.konyaco.fluent.component.RadioButton +import com.konyaco.fluent.component.ScrollbarContainer +import com.konyaco.fluent.component.Slider +import com.konyaco.fluent.component.SubtleButton +import com.konyaco.fluent.component.Switcher +import com.konyaco.fluent.component.TabItem +import com.konyaco.fluent.component.TabRow +import com.konyaco.fluent.component.TabViewDefaults +import com.konyaco.fluent.component.Text +import com.konyaco.fluent.component.TextField import com.konyaco.fluent.component.rememberScrollbarAdapter import com.konyaco.fluent.gallery.LocalStore -import com.konyaco.fluent.gallery.annotation.Component import com.konyaco.fluent.icons.Icons -import com.konyaco.fluent.icons.regular.* +import com.konyaco.fluent.icons.regular.Add +import com.konyaco.fluent.icons.regular.ArrowLeft +import com.konyaco.fluent.icons.regular.Checkmark +import com.konyaco.fluent.icons.regular.ClipboardMore +import com.konyaco.fluent.icons.regular.Delete +import com.konyaco.fluent.icons.regular.Dismiss +import com.konyaco.fluent.icons.regular.List +import com.konyaco.fluent.icons.regular.Navigation import com.konyaco.fluent.surface.Card - -@Component(icon = "Home") @Composable -fun HomeScreen() { +fun TestComponentScreen() { var displayDialog by remember { mutableStateOf(false) } val density = LocalDensity.current var scale by remember(density) { mutableStateOf(density.density) } val store = LocalStore.current - Column(Modifier.verticalScroll(rememberScrollState()).padding(16.dp), Arrangement.spacedBy(8.dp)) { - Controller(scale, { scale = it }, store.darkMode, { store.darkMode = it }) + Column( + modifier = Modifier.verticalScroll(rememberScrollState()).padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Controller( + scale = scale, + onScaleChange = { scale = it }, + darkMode = store.darkMode, + onDarkModeChange = { store.darkMode = it }, + acrylicPopupEnabled = store.enabledAcrylicPopup, + onAcrylicPopupChange = { store.enabledAcrylicPopup = it }, + compactModeEnabled = store.compactMode, + onCompactModeChange = { store.compactMode = it } + ) CompositionLocalProvider(LocalDensity provides Density(scale)) { Content() @@ -144,8 +202,7 @@ fun HomeScreen() { MenuFlyoutItem( onClick = {}, icon = {}, - paddingIcon = true, - text = { Text("test") } + text = { Text("Test") } ) MenuFlyoutItem( items = { @@ -179,23 +236,17 @@ fun HomeScreen() { ) } - Dialog( + ContentDialog( title = "This is an example dialog", visible = displayDialog, - cancelButtonText = "Cancel", - confirmButtonText = "Confirm", - onCancel = { - displayDialog = false - }, - onConfirm = { - displayDialog = false - }, + primaryButtonText = "Confirm", + closeButtonText = "Cancel", + onButtonClick = { displayDialog = false }, content = { Text( "This is body text. Windows 11 marks a visual evolution of the operating system. We have evolved our design language alongside with Fluent to create a design which is human, universal and truly feels like Windows. \n" + "\n" + - "The design principles below have guided us throughout the journey of making Windows the best-in-class implementation of Fluent.\n", - color = LocalContentColor.current + "The design principles below have guided us throughout the journey of making Windows the best-in-class implementation of Fluent.\n" ) } ) @@ -207,16 +258,28 @@ private fun Controller( scale: Float, onScaleChange: (Float) -> Unit, darkMode: Boolean, - onDarkModeChange: (Boolean) -> Unit + onDarkModeChange: (Boolean) -> Unit, + acrylicPopupEnabled: Boolean, + onAcrylicPopupChange: (Boolean) -> Unit, + compactModeEnabled: Boolean, + onCompactModeChange: (Boolean) -> Unit ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - Text("Scale: %.2f".format(scale)) + Text("Scale: ${scale.toString().take(4)}") val density = LocalDensity.current Button(onClick = { onScaleChange(density.density) }) { Text("Reset") } Switcher(darkMode, text = "Dark Mode", onCheckStateChange = { onDarkModeChange(it) }) + Switcher( + acrylicPopupEnabled, + text = "Acrylic Popup", + onCheckStateChange = { onAcrylicPopupChange(it) }) + Switcher( + compactModeEnabled, + text = "Compact Mode", + onCheckStateChange = { onCompactModeChange(it) }) } Slider( modifier = Modifier.width(200.dp), @@ -250,7 +313,7 @@ private fun Content() { } Layer( - shape = RoundedCornerShape(4.dp), + shape = FluentTheme.shapes.control, color = FluentTheme.colors.fillAccent.default, border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default), content = { @@ -259,7 +322,7 @@ private fun Content() { backgroundSizing = BackgroundSizing.InnerBorderEdge ) Layer( - shape = RoundedCornerShape(4.dp), + shape = FluentTheme.shapes.control, color = FluentTheme.colors.fillAccent.default, border = BorderStroke(1.dp, FluentTheme.colors.stroke.control.default), content = { @@ -342,6 +405,33 @@ private fun Content() { ) } } + + val selectedKey = remember { mutableStateOf(0) } + val tabItems = remember { mutableStateListOf(0, 1, 2, 3, 4) } + TabRow( + selectedKey = { selectedKey.value }, + borderColor = FluentTheme.colors.stroke.card.default, + ) { + items(tabItems, key = { it }) { index -> + TabItem( + selected = index == selectedKey.value, + onSelectedChanged = { selectedKey.value = index }, + content = { Text(index.toString()) }, + colors = if (index == selectedKey.value) { + TabViewDefaults.selectedItemTitleBarColors() + } else { + TabViewDefaults.defaultItemTitleBarColors() + }, + endDividerVisible = index != selectedKey.value - 1, + modifier = Modifier.widthIn(60.dp) + ) + } + item { + TabViewDefaults.TabAddButton( + onClick = { tabItems.add(tabItems.size) } + ) + } + } } @Composable diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5069c2c8..d2d95b58 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,7 @@ windowStyler = "0.3.3-SNAPSHOT" highlights = "0.9.3" kotlinx-datetime = "0.6.1" jna = "5.13.0" +buildKonfig = "0.15.2" [libraries] androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } @@ -39,3 +40,5 @@ compose = { id = "org.jetbrains.compose", version.ref = "compose" } android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } + +build-konfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildKonfig" }