From ef6a2db75d51403e61b678e6d6f883667798284a Mon Sep 17 00:00:00 2001 From: NamanPandey Date: Tue, 28 Jun 2022 15:36:29 +0530 Subject: [PATCH] Implement Basic Controls(Toggle Switch, Checkbox and Radio Button) (#214) * Implement Basic Controls(Toggle Switch, Checkbox and Radio Button) * Remove appcompat Dependency from Controls module * Resolving comments --- FluentUI.Demo/src/main/AndroidManifest.xml | 1 + .../com/example/theme/token/MyButtonTokens.kt | 6 +- .../java/com/microsoft/fluentuidemo/Demos.kt | 2 + .../demos/V2BasicControlsActivity.kt | 154 ++++++++++++++++++ .../demos/V2BasicInputsActivity.kt | 34 ++-- FluentUI/build.gradle | 1 + config.gradle | 2 + fluentui_controls/.gitignore | 1 + fluentui_controls/build.gradle | 71 ++++++++ fluentui_controls/consumer-rules.pro | 0 fluentui_controls/proguard-rules.pro | 21 +++ .../src/main/AndroidManifest.xml | 8 + .../microsoft/fluentui/controls}/Button.kt | 2 +- .../microsoft/fluentui/controls/Checkbox.kt | 117 +++++++++++++ .../controls}/FloatingActionButton.kt | 2 +- .../fluentui/controls/RadioButton.kt | 94 +++++++++++ .../fluentui/controls/ToggleSwitch.kt | 137 ++++++++++++++++ .../com/microsoft/fluentui/controls}/Utils.kt | 14 +- .../microsoft/fluentui/theme/FluentTheme.kt | 6 +- .../fluentui/theme/token/ControlTokens.kt | 9 +- .../token/controlTokens/CheckBoxTokens.kt | 95 +++++++++++ .../token/controlTokens/RadioButtonTokens.kt | 67 ++++++++ .../token/controlTokens/ToggleSwitchTokens.kt | 86 ++++++++++ fluentui_others/build.gradle | 19 --- settings.gradle | 1 + 25 files changed, 902 insertions(+), 48 deletions(-) create mode 100644 FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicControlsActivity.kt create mode 100644 fluentui_controls/.gitignore create mode 100644 fluentui_controls/build.gradle create mode 100644 fluentui_controls/consumer-rules.pro create mode 100644 fluentui_controls/proguard-rules.pro create mode 100644 fluentui_controls/src/main/AndroidManifest.xml rename {fluentui_others/src/main/java/com/microsoft/fluentui/button => fluentui_controls/src/main/java/com/microsoft/fluentui/controls}/Button.kt (99%) create mode 100644 fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Checkbox.kt rename {fluentui_others/src/main/java/com/microsoft/fluentui/button => fluentui_controls/src/main/java/com/microsoft/fluentui/controls}/FloatingActionButton.kt (99%) create mode 100644 fluentui_controls/src/main/java/com/microsoft/fluentui/controls/RadioButton.kt create mode 100644 fluentui_controls/src/main/java/com/microsoft/fluentui/controls/ToggleSwitch.kt rename {fluentui_others/src/main/java/com/microsoft/fluentui/button => fluentui_controls/src/main/java/com/microsoft/fluentui/controls}/Utils.kt (85%) create mode 100644 fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/CheckBoxTokens.kt create mode 100644 fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/RadioButtonTokens.kt create mode 100644 fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/ToggleSwitchTokens.kt diff --git a/FluentUI.Demo/src/main/AndroidManifest.xml b/FluentUI.Demo/src/main/AndroidManifest.xml index d272567b..eb6ca2db 100644 --- a/FluentUI.Demo/src/main/AndroidManifest.xml +++ b/FluentUI.Demo/src/main/AndroidManifest.xml @@ -34,6 +34,7 @@ + diff --git a/FluentUI.Demo/src/main/java/com/example/theme/token/MyButtonTokens.kt b/FluentUI.Demo/src/main/java/com/example/theme/token/MyButtonTokens.kt index 482d5aae..7e233b75 100644 --- a/FluentUI.Demo/src/main/java/com/example/theme/token/MyButtonTokens.kt +++ b/FluentUI.Demo/src/main/java/com/example/theme/token/MyButtonTokens.kt @@ -12,9 +12,9 @@ open class MyButtonTokens : ButtonTokens() { @Composable override fun fixedHeight(buttonInfo: ButtonInfo): Dp { return when (buttonInfo.size) { - ButtonSize.Small -> 50.dp - ButtonSize.Medium -> 60.dp - ButtonSize.Large -> 100.dp + ButtonSize.Small -> 40.dp + ButtonSize.Medium -> 48.dp + ButtonSize.Large -> 60.dp } } } \ No newline at end of file diff --git a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt index edd93fc7..ab26bfdd 100644 --- a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt +++ b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/Demos.kt @@ -15,6 +15,7 @@ const val AVATAR_VIEW = "AvatarView" const val AVATAR_GROUP_VIEW = "AvatarGroupView" const val BASIC_INPUTS = "Basic Inputs" const val V2BASIC_INPUTS = "V2 Basic Inputs" +const val V2BASIC_CONTROLS = "V2 Basic Controls" const val BOTTOM_NAVIGATION = "BottomNavigation" const val BOTTOM_SHEET = "BottomSheet" const val CALENDAR_VIEW = "CalendarView" @@ -42,6 +43,7 @@ val DEMOS = arrayListOf( Demo(AVATAR_GROUP_VIEW, AvatarGroupViewActivity::class), Demo(BASIC_INPUTS, BasicInputsActivity::class), Demo(V2BASIC_INPUTS, V2BasicInputsActivity::class), + Demo(V2BASIC_CONTROLS, V2BasicControlsActivity::class), Demo(BOTTOM_NAVIGATION, BottomNavigationActivity::class), Demo(BOTTOM_SHEET, BottomSheetActivity::class), Demo(CALENDAR_VIEW, CalendarViewActivity::class), diff --git a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicControlsActivity.kt b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicControlsActivity.kt new file mode 100644 index 00000000..1679c2c4 --- /dev/null +++ b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicControlsActivity.kt @@ -0,0 +1,154 @@ +package com.microsoft.fluentuidemo.demos + +import android.os.Bundle +import android.widget.Toast +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.selectable +import androidx.compose.material.Divider +import androidx.compose.material.Text +import androidx.compose.runtime.getValue +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.platform.ComposeView +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.example.theme.token.MyAliasTokens +import com.example.theme.token.MyGlobalTokens +import com.microsoft.fluentui.theme.AppThemeController +import com.microsoft.fluentui.theme.FluentTheme +import com.microsoft.fluentui.theme.ThemeMode +import com.microsoft.fluentui.theme.token.AliasTokens +import com.microsoft.fluentui.theme.token.GlobalTokens +import com.microsoft.fluentui.controls.CheckBox +import com.microsoft.fluentui.controls.RadioButton +import com.microsoft.fluentui.controls.ToggleSwitch +import com.microsoft.fluentuidemo.DemoActivity +import com.microsoft.fluentuidemo.R + +class V2BasicControlsActivity : DemoActivity() { + override val contentLayoutId: Int + get() = R.layout.v2_activity_compose + override val contentNeedsScrollableContainer: Boolean + get() = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val context = this + + val compose_here = findViewById(R.id.compose_here) + compose_here.setContent { + val globalTokens: GlobalTokens by AppThemeController.observeGlobalToken(initial = GlobalTokens()) + val aliasTokens: AliasTokens by AppThemeController.observeAliasToken(initial = AliasTokens()) + + FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens) { + Column(verticalArrangement = Arrangement.spacedBy(10.dp), + modifier = Modifier.padding(16.dp)) { + + var checked by remember { mutableStateOf(globalTokens !is MyGlobalTokens) } + var enabled by remember { mutableStateOf(false) } + val themes = listOf("Theme 1", "Theme 2") + val selectedOption = remember { mutableStateOf(themes[0]) } + + Row(horizontalArrangement = Arrangement.spacedBy(30.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth()) { + Text(text = "Toggle Switch enable", + fontWeight = FontWeight.Bold, + modifier = Modifier.weight(1F), + color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode = ThemeMode.Auto)) + ToggleSwitch(true, enabled, { + enabled = it + Toast.makeText(context, "Switch 1 Toggled", Toast.LENGTH_SHORT).show() + }) + } + + Divider() + + Row(horizontalArrangement = Arrangement.spacedBy(30.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth()) { + Text(text = "Toggle Global/Alias Theme", + fontWeight = FontWeight.Bold, + modifier = Modifier.weight(1F), + color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode = ThemeMode.Auto)) + ToggleSwitch(enabled, checked, { + checked = it + if (checked) { + AppThemeController.updateGlobalTokens(GlobalTokens()) + AppThemeController.updateAliasTokens(AliasTokens()) + selectedOption.value = themes[0] + } else { + AppThemeController.updateGlobalTokens(MyGlobalTokens()) + AppThemeController.updateAliasTokens(MyAliasTokens(MyGlobalTokens())) + selectedOption.value = themes[1] + } + Toast.makeText(context, "Switch 2 Toggled", Toast.LENGTH_SHORT).show() + }) + } + + Row(horizontalArrangement = Arrangement.spacedBy(30.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth()) { + Text(text = "Toggle Global/Alias Theme", + fontWeight = FontWeight.Bold, + modifier = Modifier.weight(1F), + color = AppThemeController.aliasTokens.value!!.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode = ThemeMode.Auto)) + CheckBox(enabled = enabled, checked = !checked, onCheckedChanged = { + checked = !it + if (checked) { + AppThemeController.updateAliasTokens(AliasTokens()) + AppThemeController.updateGlobalTokens(GlobalTokens()) + selectedOption.value = themes[0] + } else { + AppThemeController.updateAliasTokens(MyAliasTokens(MyGlobalTokens())) + AppThemeController.updateGlobalTokens(MyGlobalTokens()) + selectedOption.value = themes[1] + } + }) + } + + themes.forEach { theme -> + Row(horizontalArrangement = Arrangement.spacedBy(30.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = (theme == selectedOption.value), + onClick = { }, + role = Role.RadioButton, + interactionSource = MutableInteractionSource(), + indication = null + )) { + Text(text = theme, + fontWeight = FontWeight.Bold, + modifier = Modifier.weight(1F), + color = AppThemeController.aliasTokens.value!!.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode = ThemeMode.Auto)) + RadioButton(enabled = enabled, + selected = (selectedOption.value == theme), + onClick = { + selectedOption.value = theme + if (theme == "Theme 1") { + AppThemeController.updateAliasTokens(AliasTokens()) + AppThemeController.updateGlobalTokens(GlobalTokens()) + checked = true + } else { + AppThemeController.updateAliasTokens(MyAliasTokens(MyGlobalTokens())) + AppThemeController.updateGlobalTokens(MyGlobalTokens()) + checked = false + } + Toast.makeText(context, "Radio Button: ${theme} selected", Toast.LENGTH_SHORT).show() + } + ) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicInputsActivity.kt b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicInputsActivity.kt index 9d262183..d1417910 100644 --- a/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicInputsActivity.kt +++ b/FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2BasicInputsActivity.kt @@ -24,10 +24,11 @@ import androidx.compose.ui.unit.dp import com.example.theme.token.MyAliasTokens import com.example.theme.token.MyButtonTokens import com.example.theme.token.MyGlobalTokens -import com.microsoft.fluentui.button.Button -import com.microsoft.fluentui.button.FloatingActionButton +import com.microsoft.fluentui.controls.Button +import com.microsoft.fluentui.controls.FloatingActionButton import com.microsoft.fluentui.theme.AppThemeController import com.microsoft.fluentui.theme.FluentTheme +import com.microsoft.fluentui.theme.FluentTheme.themeMode import com.microsoft.fluentui.theme.ThemeMode import com.microsoft.fluentui.theme.token.AliasTokens import com.microsoft.fluentui.theme.token.ControlTokens @@ -61,7 +62,8 @@ class V2BasicInputsActivity : DemoActivity() { ) { FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens, controlTokens = controlTokens) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Button to update Theme via Global & Alias token") + Text("Button to update Theme via Global & Alias token", + color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode)) Row( horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally), @@ -71,9 +73,9 @@ class V2BasicInputsActivity : DemoActivity() { style = ButtonStyle.OutlinedButton, size = ButtonSize.Medium, onClick = { - AppThemeController.onGlobalChanged(GlobalTokens()) - AppThemeController.onAliasChanged(AliasTokens()) - AppThemeController.onControlChanged(ControlTokens().updateTokens(ControlTokens.ControlType.Button, ButtonTokens())) + AppThemeController.updateGlobalTokens(GlobalTokens()) + AppThemeController.updateAliasTokens(AliasTokens()) + AppThemeController.updateControlTokens(ControlTokens().updateTokens(ControlTokens.ControlType.Button, ButtonTokens())) }, text = "Set Default Theme" ) @@ -82,9 +84,9 @@ class V2BasicInputsActivity : DemoActivity() { style = ButtonStyle.OutlinedButton, size = ButtonSize.Medium, onClick = { - AppThemeController.onGlobalChanged(MyGlobalTokens()) - AppThemeController.onAliasChanged(MyAliasTokens(MyGlobalTokens())) - AppThemeController.onControlChanged(ControlTokens().updateTokens(ControlTokens.ControlType.Button, MyButtonTokens())) + AppThemeController.updateGlobalTokens(MyGlobalTokens()) + AppThemeController.updateAliasTokens(MyAliasTokens(MyGlobalTokens())) + AppThemeController.updateControlTokens(ControlTokens().updateTokens(ControlTokens.ControlType.Button, MyButtonTokens())) }, text = "Set New Theme" ) @@ -96,14 +98,16 @@ class V2BasicInputsActivity : DemoActivity() { LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp)) { item { - Text("Default Button from provided base token & Auto theme") + Text("Default Button from provided base token & Auto theme", + color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode)) FluentTheme { CreateButtons() } } item { - Text("Button with Selected Theme and Colorful mode") + Text("Button with Selected Theme and Colorful mode", + color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode)) FluentTheme( globalTokens = globalTokens, aliasTokens = aliasTokens, @@ -124,11 +128,13 @@ class V2BasicInputsActivity : DemoActivity() { } } item { - FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens, controlTokens = controlTokens) { - Text("Button with selected theme, auto mode and default control token") + FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens) { + Text("Button with selected theme, auto mode and default control token", + color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode)) CreateButtons() - Text("Button with selected theme, auto mode and overridden control token") + Text("Button with selected theme, auto mode and overridden control token", + color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode)) CreateButtons(MyButtonTokens()) } } diff --git a/FluentUI/build.gradle b/FluentUI/build.gradle index 59ebf892..8c71f5a2 100644 --- a/FluentUI/build.gradle +++ b/FluentUI/build.gradle @@ -31,6 +31,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) api project(':fluentui_calendar') api project(':fluentui_ccb') + api project(':fluentui_controls') api project(':fluentui_core') api project(':fluentui_drawer') api project(':fluentui_listitem') diff --git a/config.gradle b/config.gradle index 0bed1374..924ea2bb 100644 --- a/config.gradle +++ b/config.gradle @@ -12,6 +12,7 @@ * fluentui_drawer and FluentUI should increment their respective version ids */ project.ext.fluentui_calendar_versionid='0.0.23' +project.ext.fluentui_controls_versionid='0.1.0' project.ext.fluentui_core_versionid='0.1.0' project.ext.fluentui_listitem_versionid='0.0.23' project.ext.fluentui_tablayout_versionid='0.0.23' @@ -26,6 +27,7 @@ project.ext.fluentui_persona_versionid='0.0.23' project.ext.fluentui_progress_versionid='0.0.23' project.ext.FluentUI_versionid='0.1.0' project.ext.fluentui_calendar_version_code=23 +project.ext.fluentui_controls_version_code=1 project.ext.fluentui_core_version_code=23 project.ext.fluentui_listitem_version_code=23 project.ext.fluentui_tablayout_version_code=23 diff --git a/fluentui_controls/.gitignore b/fluentui_controls/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/fluentui_controls/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/fluentui_controls/build.gradle b/fluentui_controls/build.gradle new file mode 100644 index 00000000..f2787789 --- /dev/null +++ b/fluentui_controls/build.gradle @@ -0,0 +1,71 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +apply from: '../config.gradle' +apply from: '../publish.gradle' + +android { + compileSdkVersion constants.compileSdkVersion + defaultConfig { + minSdkVersion constants.minSdkVersion + targetSdkVersion constants.targetSdkVersion + versionCode project.ext.fluentui_controls_version_code + versionName project.ext.fluentui_controls_versionid + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + } + lintOptions { + abortOnError false + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + testOptions { + unitTests { + includeAndroidResources = true + } + } + productFlavors { + } + kotlinOptions { + jvmTarget = '1.8' + useIR = true + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion composeVersion + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation project(':fluentui_core') + + implementation 'androidx.core:core-ktx:1.7.0' + + implementation("androidx.compose.foundation:foundation:$composeVersion") + implementation("androidx.compose.material:material:$composeVersion") + implementation("androidx.compose.runtime:runtime:$composeVersion") + implementation("androidx.compose.ui:ui:$composeVersion") + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} + +task sourceJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier "sources" +} + +project.afterEvaluate { + project.ext.publishingFunc('fluentui_others') +} \ No newline at end of file diff --git a/fluentui_controls/consumer-rules.pro b/fluentui_controls/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/fluentui_controls/proguard-rules.pro b/fluentui_controls/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/fluentui_controls/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/fluentui_controls/src/main/AndroidManifest.xml b/fluentui_controls/src/main/AndroidManifest.xml new file mode 100644 index 00000000..cad8a9bd --- /dev/null +++ b/fluentui_controls/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/fluentui_others/src/main/java/com/microsoft/fluentui/button/Button.kt b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Button.kt similarity index 99% rename from fluentui_others/src/main/java/com/microsoft/fluentui/button/Button.kt rename to fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Button.kt index 59061015..9b680bf7 100644 --- a/fluentui_others/src/main/java/com/microsoft/fluentui/button/Button.kt +++ b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Button.kt @@ -1,4 +1,4 @@ -package com.microsoft.fluentui.button +package com.microsoft.fluentui.controls import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource diff --git a/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Checkbox.kt b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Checkbox.kt new file mode 100644 index 00000000..e9519790 --- /dev/null +++ b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Checkbox.kt @@ -0,0 +1,117 @@ +package com.microsoft.fluentui.controls + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.selection.triStateToggleable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Done +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.state.ToggleableState +import androidx.compose.ui.unit.dp +import com.microsoft.fluentui.controls.backgroundColor +import com.microsoft.fluentui.controls.borderStroke +import com.microsoft.fluentui.controls.iconColor +import com.microsoft.fluentui.theme.FluentTheme +import com.microsoft.fluentui.theme.token.ControlTokens.ControlType +import com.microsoft.fluentui.theme.token.controlTokens.CheckBoxInfo +import com.microsoft.fluentui.theme.token.controlTokens.CheckBoxTokens + +val LocalCheckBoxTokens = compositionLocalOf { CheckBoxTokens() } +val LocalCheckBoxInfo = compositionLocalOf { CheckBoxInfo() } + +@Composable +fun CheckBox(enabled: Boolean = true, + checked: Boolean = false, + onCheckedChanged: (Boolean) -> Unit?, + modifier: Modifier = Modifier, + checkBoxToken: CheckBoxTokens? = null, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }) { + + val token = checkBoxToken + ?: FluentTheme.controlTokens.tokens[ControlType.CheckBox] as CheckBoxTokens + + CompositionLocalProvider( + LocalCheckBoxTokens provides token, + LocalCheckBoxInfo provides CheckBoxInfo(checked) + ) { + val toggleModifier = + if (onCheckedChanged != null) { + Modifier.triStateToggleable( + state = ToggleableState(checked), + enabled = enabled, + onClick = { onCheckedChanged(!checked) }, + role = Role.Checkbox, + interactionSource = interactionSource, + indication = rememberRipple( + bounded = false, + radius = 24.dp + ) + ) + } else { + Modifier + } + + val backgroundColor: Color = backgroundColor(getCheckBoxToken(), getCheckBoxInfo(), + enabled, interactionSource) + val iconColor: Color = iconColor(getCheckBoxToken(), getCheckBoxInfo(), + enabled, interactionSource) + val shape: Shape = RoundedCornerShape(getCheckBoxToken().fixedBorderRadius) + + val borders: List = + borderStroke(getCheckBoxToken(), getCheckBoxInfo(), enabled, interactionSource) + var borderModifier: Modifier = Modifier + var borderWidth = 0.dp + for (border in borders) { + borderWidth += border.width + borderModifier = borderModifier.border(borderWidth, border.brush, shape) + } + + Box(modifier = Modifier.indication(interactionSource, null), + contentAlignment = Alignment.Center) { + Spacer(modifier = Modifier + .size(getCheckBoxToken().fixedSize) + .clip(shape) + .background(backgroundColor) + .then(borderModifier) + .then(toggleModifier)) + AnimatedVisibility(checked, enter = fadeIn(), exit = fadeOut()) { + Icon(Icons.Filled.Done, + "Done", + modifier = Modifier.size(getCheckBoxToken().fixedIconSize), + tint = iconColor) + } + } + + } +} + +@Composable +fun getCheckBoxToken(): CheckBoxTokens { + return LocalCheckBoxTokens.current +} + +@Composable +fun getCheckBoxInfo(): CheckBoxInfo { + return LocalCheckBoxInfo.current +} \ No newline at end of file diff --git a/fluentui_others/src/main/java/com/microsoft/fluentui/button/FloatingActionButton.kt b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/FloatingActionButton.kt similarity index 99% rename from fluentui_others/src/main/java/com/microsoft/fluentui/button/FloatingActionButton.kt rename to fluentui_controls/src/main/java/com/microsoft/fluentui/controls/FloatingActionButton.kt index e0b77082..41f55fcd 100644 --- a/fluentui_others/src/main/java/com/microsoft/fluentui/button/FloatingActionButton.kt +++ b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/FloatingActionButton.kt @@ -1,4 +1,4 @@ -package com.microsoft.fluentui.button +package com.microsoft.fluentui.controls import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.* diff --git a/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/RadioButton.kt b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/RadioButton.kt new file mode 100644 index 00000000..9f9156a4 --- /dev/null +++ b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/RadioButton.kt @@ -0,0 +1,94 @@ +package com.microsoft.fluentui.controls + +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.selection.selectable +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.drawscope.Fill +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import com.microsoft.fluentui.theme.FluentTheme +import com.microsoft.fluentui.theme.token.ControlTokens +import com.microsoft.fluentui.theme.token.controlTokens.RadioButtonInfo +import com.microsoft.fluentui.theme.token.controlTokens.RadioButtonTokens + +val LocalRadioButtonTokens = compositionLocalOf { RadioButtonTokens() } +val LocalRadioButtonInfo = compositionLocalOf { RadioButtonInfo() } + +@Composable +fun RadioButton(enabled: Boolean = true, + selected: Boolean = false, + onClick: (() -> Unit)?, + modifier: Modifier = Modifier, + radioButtonToken: RadioButtonTokens? = null, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } +) { + val token = radioButtonToken + ?: FluentTheme.controlTokens.tokens[ControlTokens.ControlType.RadioButton] as RadioButtonTokens + + CompositionLocalProvider( + LocalRadioButtonTokens provides token, + LocalRadioButtonInfo provides RadioButtonInfo(selected) + ) { + val dotRadius = animateDpAsState( + targetValue = if (selected) getRadioButtonTokens().innerCircleRadius else 0.dp, + animationSpec = tween(durationMillis = 100) + ) + + val selectableModifier = if (onClick != null) { + modifier.selectable(selected = selected, + enabled = enabled, + onClick = onClick, + role = Role.RadioButton, + interactionSource = interactionSource, + indication = rememberRipple( + bounded = false, + radius = 24.dp + ) + ) + } else { + modifier + } + + val outerStrokeColor = backgroundColor(getRadioButtonTokens(), getRadioButtonInfo(), + enabled, interactionSource) + val innerColor = iconColor(getRadioButtonTokens(), getRadioButtonInfo(), + enabled, interactionSource) + + val outerRadius = getRadioButtonTokens().outerCircleRadius + val strokeWidth = getRadioButtonTokens().strokeWidthInwards + + Canvas(modifier = Modifier + .then(selectableModifier) + .size(24.dp) + .wrapContentSize(Alignment.Center)) { + drawCircle(outerStrokeColor, (outerRadius - (strokeWidth / 2)).toPx(), style = Stroke(1.5.dp.toPx())) + + if (dotRadius.value > 0.dp) { + drawCircle(innerColor, (dotRadius.value).toPx(), style = Fill) + } + } + } +} + + +@Composable +fun getRadioButtonTokens(): RadioButtonTokens { + return LocalRadioButtonTokens.current +} + +@Composable +fun getRadioButtonInfo(): RadioButtonInfo { + return LocalRadioButtonInfo.current +} diff --git a/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/ToggleSwitch.kt b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/ToggleSwitch.kt new file mode 100644 index 00000000..8652f764 --- /dev/null +++ b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/ToggleSwitch.kt @@ -0,0 +1,137 @@ +package com.microsoft.fluentui.controls + +import androidx.compose.animation.core.TweenSpec +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.toggleable +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.FractionalThreshold +import androidx.compose.material.rememberSwipeableState +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material.swipeable +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import com.microsoft.fluentui.controls.backgroundColor +import com.microsoft.fluentui.controls.iconColor +import com.microsoft.fluentui.theme.FluentTheme +import com.microsoft.fluentui.theme.token.ControlTokens.ControlType +import com.microsoft.fluentui.theme.token.controlTokens.ToggleSwitchInfo +import com.microsoft.fluentui.theme.token.controlTokens.ToggleSwitchTokens +import kotlin.math.roundToInt + +val LocalToggleSwitchTokens = compositionLocalOf { ToggleSwitchTokens() } +val LocalToggleSwitchInfo = compositionLocalOf { ToggleSwitchInfo() } + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun ToggleSwitch(enabledSwitch: Boolean = true, + checkedState: Boolean = false, + onValueChange: ((Boolean) -> Unit)? = null, + switchTokens: ToggleSwitchTokens? = null, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } +) { + + val token = switchTokens + ?: FluentTheme.controlTokens.tokens[ControlType.ToggleSwitch] as ToggleSwitchTokens + + CompositionLocalProvider( + LocalToggleSwitchTokens provides token, + LocalToggleSwitchInfo provides ToggleSwitchInfo(checkedState) + ) { + + val backgroundColor: Color = backgroundColor(getToggleSwitchToken(), getToggleSwitchInfo(), + enabledSwitch, interactionSource) + val foregroundColor: Color = iconColor(getToggleSwitchToken(), getToggleSwitchInfo(), + enabledSwitch, interactionSource) + val padding: Dp = getToggleSwitchToken().paddingTrack + + + // Swipe Logic + val knobMovementWidth = 23.dp + val minBound = with(LocalDensity.current) { padding.toPx() } + val maxBound = with(LocalDensity.current) { knobMovementWidth.toPx() } + val AnimationSpec = TweenSpec(durationMillis = 100) + val swipeState = rememberSwipeableState(checkedState, AnimationSpec, confirmStateChange = { true }) + val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl + + val forceAnimationCheck = remember { mutableStateOf(false) } + LaunchedEffect(checkedState, forceAnimationCheck.value) { + if (checkedState != swipeState.currentValue) { + swipeState.animateTo(checkedState) + } + } + DisposableEffect(swipeState.currentValue) { + if (checkedState != swipeState.currentValue) { + onValueChange?.invoke(swipeState.currentValue) + forceAnimationCheck.value = !forceAnimationCheck.value + } + onDispose { } + } + + // Toggle Logic + val toggleModifier = + if (onValueChange != null) { + Modifier.toggleable(value = getToggleSwitchInfo().checked, + enabled = enabledSwitch, + role = Role.Switch, + onValueChange = onValueChange, + interactionSource = interactionSource, + indication = null) + } else + Modifier + + // UI Implementation + Box(modifier = Modifier + .then(toggleModifier) + .swipeable( + state = swipeState, + anchors = mapOf(minBound to false, maxBound to true), + thresholds = { _, _ -> FractionalThreshold(0.5f) }, + orientation = Orientation.Horizontal, + enabled = enabledSwitch && onValueChange != null, + reverseDirection = isRtl, + interactionSource = interactionSource, + resistance = null + ), contentAlignment = Alignment.CenterStart) { + Box(modifier = Modifier + .width(getToggleSwitchToken().fixedTrackWidth) + .height(getToggleSwitchToken().fixedTrackHeight) + .clip(CircleShape) + .background(backgroundColor)) + Spacer(modifier = Modifier + .offset { IntOffset(swipeState.offset.value.roundToInt(), 0) } + .indication( + interactionSource = interactionSource, + indication = rememberRipple(false, + getToggleSwitchToken().knobRippleRadius) + ) + .size(getToggleSwitchToken().fixedKnobDiameter) + .clip(CircleShape) + .background(foregroundColor)) + } + } +} + +@Composable +fun getToggleSwitchToken(): ToggleSwitchTokens { + return LocalToggleSwitchTokens.current +} + +@Composable +fun getToggleSwitchInfo(): ToggleSwitchInfo { + return LocalToggleSwitchInfo.current +} diff --git a/fluentui_others/src/main/java/com/microsoft/fluentui/button/Utils.kt b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Utils.kt similarity index 85% rename from fluentui_others/src/main/java/com/microsoft/fluentui/button/Utils.kt rename to fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Utils.kt index 06e3474b..6bebc802 100644 --- a/fluentui_others/src/main/java/com/microsoft/fluentui/button/Utils.kt +++ b/fluentui_controls/src/main/java/com/microsoft/fluentui/controls/Utils.kt @@ -1,4 +1,4 @@ -package com.microsoft.fluentui.button +package com.microsoft.fluentui.controls import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.interaction.InteractionSource @@ -12,10 +12,7 @@ import com.microsoft.fluentui.theme.token.ControlInfo import com.microsoft.fluentui.theme.token.ControlToken import com.microsoft.fluentui.theme.token.StateBorderStroke import com.microsoft.fluentui.theme.token.StateColor -import com.microsoft.fluentui.theme.token.controlTokens.ButtonInfo -import com.microsoft.fluentui.theme.token.controlTokens.ButtonTokens -import com.microsoft.fluentui.theme.token.controlTokens.FABInfo -import com.microsoft.fluentui.theme.token.controlTokens.FABTokens +import com.microsoft.fluentui.theme.token.controlTokens.* import java.security.InvalidParameterException @Composable @@ -53,6 +50,9 @@ fun backgroundColor( when (tokens) { is ButtonTokens -> tokens.backgroundColor(info as ButtonInfo) is FABTokens -> tokens.backgroundColor(info as FABInfo) + is ToggleSwitchTokens -> tokens.TrackColor(info as ToggleSwitchInfo) + is CheckBoxTokens -> tokens.backgroundColor(info as CheckBoxInfo) + is RadioButtonTokens -> tokens.backgroundColor(info as RadioButtonInfo) else -> throw InvalidParameterException() } @@ -70,6 +70,9 @@ fun iconColor( when (tokens) { is ButtonTokens -> tokens.iconColor(info as ButtonInfo) is FABTokens -> tokens.iconColor(info as FABInfo) + is ToggleSwitchTokens -> tokens.KnobColor(info as ToggleSwitchInfo) + is CheckBoxTokens -> tokens.iconColor(info as CheckBoxInfo) + is RadioButtonTokens -> tokens.iconColor(info as RadioButtonInfo) else -> throw InvalidParameterException() } @@ -104,6 +107,7 @@ fun borderStroke( when (tokens) { is ButtonTokens -> tokens.borderStroke(info as ButtonInfo) is FABTokens -> tokens.borderStroke(info as FABInfo) + is CheckBoxTokens -> tokens.borderStroke(info as CheckBoxInfo) else -> throw InvalidParameterException() } diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/FluentTheme.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/FluentTheme.kt index 96a92b3e..feb83f3c 100644 --- a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/FluentTheme.kt +++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/FluentTheme.kt @@ -63,15 +63,15 @@ object AppThemeController : ViewModel() { var aliasTokens: MutableLiveData = MutableLiveData(AliasTokens()) var controlTokens: MutableLiveData = MutableLiveData(ControlTokens()) - fun onGlobalChanged(overrideGlobalTokens: GlobalTokens) { + fun updateGlobalTokens(overrideGlobalTokens: GlobalTokens) { globalTokens.value = overrideGlobalTokens } - fun onAliasChanged(overrideAliasTokens: AliasTokens) { + fun updateAliasTokens(overrideAliasTokens: AliasTokens) { aliasTokens.value = overrideAliasTokens } - fun onControlChanged(overrideControlTokens: ControlTokens) { + fun updateControlTokens(overrideControlTokens: ControlTokens) { controlTokens.value = overrideControlTokens } diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt index c31a51e4..335a164d 100644 --- a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt +++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/ControlTokens.kt @@ -6,8 +6,7 @@ package com.microsoft.fluentui.theme.token import androidx.compose.runtime.compositionLocalOf -import com.microsoft.fluentui.theme.token.controlTokens.ButtonTokens -import com.microsoft.fluentui.theme.token.controlTokens.FABTokens +import com.microsoft.fluentui.theme.token.controlTokens.* interface ControlInfo @@ -18,6 +17,9 @@ class ControlTokens { enum class ControlType { Button, FloatingActionButton, + ToggleSwitch, + CheckBox, + RadioButton } val tokens: TokenSet by lazy { @@ -25,6 +27,9 @@ class ControlTokens { when (token) { ControlType.Button -> ButtonTokens() ControlType.FloatingActionButton -> FABTokens() + ControlType.ToggleSwitch -> ToggleSwitchTokens() + ControlType.CheckBox -> CheckBoxTokens() + ControlType.RadioButton -> RadioButtonTokens() } } } diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/CheckBoxTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/CheckBoxTokens.kt new file mode 100644 index 00000000..8e3eba64 --- /dev/null +++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/CheckBoxTokens.kt @@ -0,0 +1,95 @@ +package com.microsoft.fluentui.theme.token.controlTokens + +import android.os.Parcelable +import androidx.compose.foundation.BorderStroke +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.microsoft.fluentui.theme.FluentTheme +import com.microsoft.fluentui.theme.token.* +import kotlinx.parcelize.Parcelize + +data class CheckBoxInfo( + val checked: Boolean = false, +) : ControlInfo + +@Parcelize +open class CheckBoxTokens : ControlToken, Parcelable { + + companion object { + const val Type: String = "Checkbox" + } + + val fixedSize: Dp = 20.dp + val fixedIconSize: Dp = 12.dp + val fixedBorderRadius: Dp = 4.dp + + @Composable + open fun backgroundColor(checkBoxInfo: CheckBoxInfo): StateColor { + return when (checkBoxInfo.checked) { + true -> StateColor( + rest = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value( + themeMode = FluentTheme.themeMode + ), + pressed = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value( + themeMode = FluentTheme.themeMode + ), + disabled = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundDisabled].value( + themeMode = FluentTheme.themeMode + ) + ) + false -> StateColor() + } + } + + @Composable + open fun iconColor(checkBoxInfo: CheckBoxInfo): StateColor { + return when (checkBoxInfo.checked) { + true -> StateColor( + rest = FluentTheme.aliasToken.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value( + themeMode = FluentTheme.themeMode + ), + pressed = FluentTheme.aliasToken.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.ForegroundOnColor].value( + themeMode = FluentTheme.themeMode + ), + disabled = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundInvertedDisabled].value( + themeMode = FluentTheme.themeMode + ) + ) + false -> StateColor() + } + } + + @Composable + open fun borderStroke(checkBoxInfo: CheckBoxInfo): StateBorderStroke { + return when (checkBoxInfo.checked) { + true -> StateBorderStroke() + false -> StateBorderStroke( + rest = listOf( + BorderStroke( + 1.5.dp, + FluentTheme.aliasToken.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.StrokeAccessible].value( + themeMode = FluentTheme.themeMode + ) + ) + ), + pressed = listOf( + BorderStroke( + 1.5.dp, + FluentTheme.aliasToken.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.StrokeAccessible].value( + themeMode = FluentTheme.themeMode + ) + ) + ), + disabled = listOf( + BorderStroke( + 1.5.dp, + FluentTheme.aliasToken.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.StrokeDisabled].value( + themeMode = FluentTheme.themeMode + ) + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/RadioButtonTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/RadioButtonTokens.kt new file mode 100644 index 00000000..0241b0d1 --- /dev/null +++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/RadioButtonTokens.kt @@ -0,0 +1,67 @@ +package com.microsoft.fluentui.theme.token.controlTokens + +import android.os.Parcelable +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import com.microsoft.fluentui.theme.FluentTheme +import com.microsoft.fluentui.theme.token.AliasTokens +import com.microsoft.fluentui.theme.token.ControlInfo +import com.microsoft.fluentui.theme.token.ControlToken +import com.microsoft.fluentui.theme.token.StateColor +import kotlinx.parcelize.Parcelize + +data class RadioButtonInfo( + val selected: Boolean = false, +) : ControlInfo + +@Parcelize +open class RadioButtonTokens : ControlToken, Parcelable { + + companion object { + const val Type: String = "Checkbox" + } + + open var innerCircleRadius = 5.dp + open var outerCircleRadius = 10.dp + open var strokeWidthInwards = 1.5.dp + + @Composable + open fun backgroundColor(radioButtonInfo: RadioButtonInfo): StateColor { + return when (radioButtonInfo.selected) { + true -> StateColor( + rest = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value( + themeMode = FluentTheme.themeMode + ), + pressed = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value( + themeMode = FluentTheme.themeMode + ), + disabled = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundDisabled].value( + themeMode = FluentTheme.themeMode + ) + ) + false -> StateColor( + rest = FluentTheme.aliasToken.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.StrokeAccessible].value( + themeMode = FluentTheme.themeMode + ), + disabled = FluentTheme.aliasToken.neutralStrokeColor[AliasTokens.NeutralStrokeColorTokens.StrokeDisabled].value( + themeMode = FluentTheme.themeMode + ) + ) + } + } + + @Composable + open fun iconColor(radioButtonInfo: RadioButtonInfo): StateColor { + return when (radioButtonInfo.selected) { + true -> StateColor( + rest = FluentTheme.aliasToken.brandForegroundColor[AliasTokens.BrandForegroundColorTokens.BrandForeground1].value( + themeMode = FluentTheme.themeMode + ), + disabled = FluentTheme.aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundDisabled].value( + themeMode = FluentTheme.themeMode + ) + ) + false -> StateColor() + } + } +} \ No newline at end of file diff --git a/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/ToggleSwitchTokens.kt b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/ToggleSwitchTokens.kt new file mode 100644 index 00000000..7b6628db --- /dev/null +++ b/fluentui_core/src/main/java/com/microsoft/fluentui/theme/token/controlTokens/ToggleSwitchTokens.kt @@ -0,0 +1,86 @@ +package com.microsoft.fluentui.theme.token.controlTokens + +import android.os.Parcelable +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import com.microsoft.fluentui.theme.FluentTheme.aliasToken +import com.microsoft.fluentui.theme.FluentTheme.themeMode +import com.microsoft.fluentui.theme.token.AliasTokens +import com.microsoft.fluentui.theme.token.ControlInfo +import com.microsoft.fluentui.theme.token.ControlToken +import com.microsoft.fluentui.theme.token.StateColor +import kotlinx.parcelize.Parcelize + +data class ToggleSwitchInfo( + val checked: Boolean = true, +) : ControlInfo + +@Parcelize +open class ToggleSwitchTokens : ControlToken, Parcelable { + + companion object { + const val Type: String = "ToggleSwitch" + } + + @Composable + open fun TrackColor(switchInfo: ToggleSwitchInfo): StateColor { + return when (switchInfo.checked) { + true -> StateColor( + rest = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value( + themeMode = themeMode + ), + pressed = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackground1].value( + themeMode = themeMode + ), + disabled = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundDisabled].value( + themeMode = themeMode + ) + ) + false -> StateColor( + rest = aliasToken.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Background5].value( + themeMode = themeMode + ), + pressed = aliasToken.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Background5].value( + themeMode = themeMode + ), + disabled = aliasToken.neutralBackgroundColor[AliasTokens.NeutralBackgroundColorTokens.Background5].value( + themeMode = themeMode + ) + ) + } + } + + @Composable + open fun KnobColor(switchInfo: ToggleSwitchInfo): StateColor { + return when (switchInfo.checked) { + true -> StateColor( + rest = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundInverted].value( + themeMode = themeMode + ), + pressed = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundInverted].value( + themeMode = themeMode + ), + disabled = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundInvertedDisabled].value( + themeMode = themeMode + ) + ) + false -> StateColor( + rest = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundInverted].value( + themeMode = themeMode + ), + pressed = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundInverted].value( + themeMode = themeMode + ), + disabled = aliasToken.brandBackgroundColor[AliasTokens.BrandBackgroundColorTokens.BrandBackgroundInvertedDisabled].value( + themeMode = themeMode + ) + ) + } + } + + open val fixedTrackHeight = 32.dp + open val fixedTrackWidth = 52.dp + open val fixedKnobDiameter = 26.dp + open val knobRippleRadius = 24.dp + open val paddingTrack = 3.dp +} \ No newline at end of file diff --git a/fluentui_others/build.gradle b/fluentui_others/build.gradle index 198e30c0..2f95d07b 100644 --- a/fluentui_others/build.gradle +++ b/fluentui_others/build.gradle @@ -34,20 +34,6 @@ android { } productFlavors { } - kotlinOptions { - jvmTarget = '1.8' - useIR = true - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion composeVersion - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } gradle.taskGraph.whenReady { taskGraph -> @@ -71,11 +57,6 @@ dependencies { implementation "com.splitwise:tokenautocomplete:$tokenautocompleteVersion" implementation "com.microsoft.device:dualscreen-layout:$duoVersion" - implementation("androidx.compose.foundation:foundation:$composeVersion") - implementation("androidx.compose.material:material:$composeVersion") - implementation("androidx.compose.runtime:runtime:$composeVersion") - implementation("androidx.compose.ui:ui:$composeVersion") - testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$extJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" diff --git a/settings.gradle b/settings.gradle index e2d28c26..33d5ea94 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,3 +13,4 @@ include ':fluentui_tablayout' include ':fluentui_others' include ':FluentUI' include ':fluentui_ccb' +include ':fluentui_controls'