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
This commit is contained in:
Родитель
18d9e0432b
Коммит
ef6a2db75d
|
@ -34,6 +34,7 @@
|
||||||
<activity android:name="com.microsoft.fluentuidemo.demos.AvatarGroupViewActivity" />
|
<activity android:name="com.microsoft.fluentuidemo.demos.AvatarGroupViewActivity" />
|
||||||
<activity android:name="com.microsoft.fluentuidemo.demos.BasicInputsActivity" />
|
<activity android:name="com.microsoft.fluentuidemo.demos.BasicInputsActivity" />
|
||||||
<activity android:name="com.microsoft.fluentuidemo.demos.V2BasicInputsActivity" />
|
<activity android:name="com.microsoft.fluentuidemo.demos.V2BasicInputsActivity" />
|
||||||
|
<activity android:name="com.microsoft.fluentuidemo.demos.V2BasicControlsActivity" />
|
||||||
<activity android:name="com.microsoft.fluentuidemo.demos.BottomNavigationActivity" />
|
<activity android:name="com.microsoft.fluentuidemo.demos.BottomNavigationActivity" />
|
||||||
<activity android:name="com.microsoft.fluentuidemo.demos.BottomSheetActivity" />
|
<activity android:name="com.microsoft.fluentuidemo.demos.BottomSheetActivity" />
|
||||||
<activity android:name="com.microsoft.fluentuidemo.demos.CalendarViewActivity" />
|
<activity android:name="com.microsoft.fluentuidemo.demos.CalendarViewActivity" />
|
||||||
|
|
|
@ -12,9 +12,9 @@ open class MyButtonTokens : ButtonTokens() {
|
||||||
@Composable
|
@Composable
|
||||||
override fun fixedHeight(buttonInfo: ButtonInfo): Dp {
|
override fun fixedHeight(buttonInfo: ButtonInfo): Dp {
|
||||||
return when (buttonInfo.size) {
|
return when (buttonInfo.size) {
|
||||||
ButtonSize.Small -> 50.dp
|
ButtonSize.Small -> 40.dp
|
||||||
ButtonSize.Medium -> 60.dp
|
ButtonSize.Medium -> 48.dp
|
||||||
ButtonSize.Large -> 100.dp
|
ButtonSize.Large -> 60.dp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,7 @@ const val AVATAR_VIEW = "AvatarView"
|
||||||
const val AVATAR_GROUP_VIEW = "AvatarGroupView"
|
const val AVATAR_GROUP_VIEW = "AvatarGroupView"
|
||||||
const val BASIC_INPUTS = "Basic Inputs"
|
const val BASIC_INPUTS = "Basic Inputs"
|
||||||
const val V2BASIC_INPUTS = "V2 Basic Inputs"
|
const val V2BASIC_INPUTS = "V2 Basic Inputs"
|
||||||
|
const val V2BASIC_CONTROLS = "V2 Basic Controls"
|
||||||
const val BOTTOM_NAVIGATION = "BottomNavigation"
|
const val BOTTOM_NAVIGATION = "BottomNavigation"
|
||||||
const val BOTTOM_SHEET = "BottomSheet"
|
const val BOTTOM_SHEET = "BottomSheet"
|
||||||
const val CALENDAR_VIEW = "CalendarView"
|
const val CALENDAR_VIEW = "CalendarView"
|
||||||
|
@ -42,6 +43,7 @@ val DEMOS = arrayListOf(
|
||||||
Demo(AVATAR_GROUP_VIEW, AvatarGroupViewActivity::class),
|
Demo(AVATAR_GROUP_VIEW, AvatarGroupViewActivity::class),
|
||||||
Demo(BASIC_INPUTS, BasicInputsActivity::class),
|
Demo(BASIC_INPUTS, BasicInputsActivity::class),
|
||||||
Demo(V2BASIC_INPUTS, V2BasicInputsActivity::class),
|
Demo(V2BASIC_INPUTS, V2BasicInputsActivity::class),
|
||||||
|
Demo(V2BASIC_CONTROLS, V2BasicControlsActivity::class),
|
||||||
Demo(BOTTOM_NAVIGATION, BottomNavigationActivity::class),
|
Demo(BOTTOM_NAVIGATION, BottomNavigationActivity::class),
|
||||||
Demo(BOTTOM_SHEET, BottomSheetActivity::class),
|
Demo(BOTTOM_SHEET, BottomSheetActivity::class),
|
||||||
Demo(CALENDAR_VIEW, CalendarViewActivity::class),
|
Demo(CALENDAR_VIEW, CalendarViewActivity::class),
|
||||||
|
|
|
@ -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<ComposeView>(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()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,10 +24,11 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.example.theme.token.MyAliasTokens
|
import com.example.theme.token.MyAliasTokens
|
||||||
import com.example.theme.token.MyButtonTokens
|
import com.example.theme.token.MyButtonTokens
|
||||||
import com.example.theme.token.MyGlobalTokens
|
import com.example.theme.token.MyGlobalTokens
|
||||||
import com.microsoft.fluentui.button.Button
|
import com.microsoft.fluentui.controls.Button
|
||||||
import com.microsoft.fluentui.button.FloatingActionButton
|
import com.microsoft.fluentui.controls.FloatingActionButton
|
||||||
import com.microsoft.fluentui.theme.AppThemeController
|
import com.microsoft.fluentui.theme.AppThemeController
|
||||||
import com.microsoft.fluentui.theme.FluentTheme
|
import com.microsoft.fluentui.theme.FluentTheme
|
||||||
|
import com.microsoft.fluentui.theme.FluentTheme.themeMode
|
||||||
import com.microsoft.fluentui.theme.ThemeMode
|
import com.microsoft.fluentui.theme.ThemeMode
|
||||||
import com.microsoft.fluentui.theme.token.AliasTokens
|
import com.microsoft.fluentui.theme.token.AliasTokens
|
||||||
import com.microsoft.fluentui.theme.token.ControlTokens
|
import com.microsoft.fluentui.theme.token.ControlTokens
|
||||||
|
@ -61,7 +62,8 @@ class V2BasicInputsActivity : DemoActivity() {
|
||||||
) {
|
) {
|
||||||
FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens, controlTokens = controlTokens) {
|
FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens, controlTokens = controlTokens) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
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(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally),
|
horizontalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterHorizontally),
|
||||||
|
@ -71,9 +73,9 @@ class V2BasicInputsActivity : DemoActivity() {
|
||||||
style = ButtonStyle.OutlinedButton,
|
style = ButtonStyle.OutlinedButton,
|
||||||
size = ButtonSize.Medium,
|
size = ButtonSize.Medium,
|
||||||
onClick = {
|
onClick = {
|
||||||
AppThemeController.onGlobalChanged(GlobalTokens())
|
AppThemeController.updateGlobalTokens(GlobalTokens())
|
||||||
AppThemeController.onAliasChanged(AliasTokens())
|
AppThemeController.updateAliasTokens(AliasTokens())
|
||||||
AppThemeController.onControlChanged(ControlTokens().updateTokens(ControlTokens.ControlType.Button, ButtonTokens()))
|
AppThemeController.updateControlTokens(ControlTokens().updateTokens(ControlTokens.ControlType.Button, ButtonTokens()))
|
||||||
},
|
},
|
||||||
text = "Set Default Theme"
|
text = "Set Default Theme"
|
||||||
)
|
)
|
||||||
|
@ -82,9 +84,9 @@ class V2BasicInputsActivity : DemoActivity() {
|
||||||
style = ButtonStyle.OutlinedButton,
|
style = ButtonStyle.OutlinedButton,
|
||||||
size = ButtonSize.Medium,
|
size = ButtonSize.Medium,
|
||||||
onClick = {
|
onClick = {
|
||||||
AppThemeController.onGlobalChanged(MyGlobalTokens())
|
AppThemeController.updateGlobalTokens(MyGlobalTokens())
|
||||||
AppThemeController.onAliasChanged(MyAliasTokens(MyGlobalTokens()))
|
AppThemeController.updateAliasTokens(MyAliasTokens(MyGlobalTokens()))
|
||||||
AppThemeController.onControlChanged(ControlTokens().updateTokens(ControlTokens.ControlType.Button, MyButtonTokens()))
|
AppThemeController.updateControlTokens(ControlTokens().updateTokens(ControlTokens.ControlType.Button, MyButtonTokens()))
|
||||||
},
|
},
|
||||||
text = "Set New Theme"
|
text = "Set New Theme"
|
||||||
)
|
)
|
||||||
|
@ -96,14 +98,16 @@ class V2BasicInputsActivity : DemoActivity() {
|
||||||
|
|
||||||
LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
item {
|
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 {
|
FluentTheme {
|
||||||
CreateButtons()
|
CreateButtons()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
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(
|
FluentTheme(
|
||||||
globalTokens = globalTokens,
|
globalTokens = globalTokens,
|
||||||
aliasTokens = aliasTokens,
|
aliasTokens = aliasTokens,
|
||||||
|
@ -124,11 +128,13 @@ class V2BasicInputsActivity : DemoActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens, controlTokens = controlTokens) {
|
FluentTheme(globalTokens = globalTokens, aliasTokens = aliasTokens) {
|
||||||
Text("Button with selected theme, auto mode and default control token")
|
Text("Button with selected theme, auto mode and default control token",
|
||||||
|
color = aliasTokens.neutralForegroundColor[AliasTokens.NeutralForegroundColorTokens.Foreground1].value(themeMode))
|
||||||
CreateButtons()
|
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())
|
CreateButtons(MyButtonTokens())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
api project(':fluentui_calendar')
|
api project(':fluentui_calendar')
|
||||||
api project(':fluentui_ccb')
|
api project(':fluentui_ccb')
|
||||||
|
api project(':fluentui_controls')
|
||||||
api project(':fluentui_core')
|
api project(':fluentui_core')
|
||||||
api project(':fluentui_drawer')
|
api project(':fluentui_drawer')
|
||||||
api project(':fluentui_listitem')
|
api project(':fluentui_listitem')
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
* fluentui_drawer and FluentUI should increment their respective version ids
|
* fluentui_drawer and FluentUI should increment their respective version ids
|
||||||
*/
|
*/
|
||||||
project.ext.fluentui_calendar_versionid='0.0.23'
|
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_core_versionid='0.1.0'
|
||||||
project.ext.fluentui_listitem_versionid='0.0.23'
|
project.ext.fluentui_listitem_versionid='0.0.23'
|
||||||
project.ext.fluentui_tablayout_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_progress_versionid='0.0.23'
|
||||||
project.ext.FluentUI_versionid='0.1.0'
|
project.ext.FluentUI_versionid='0.1.0'
|
||||||
project.ext.fluentui_calendar_version_code=23
|
project.ext.fluentui_calendar_version_code=23
|
||||||
|
project.ext.fluentui_controls_version_code=1
|
||||||
project.ext.fluentui_core_version_code=23
|
project.ext.fluentui_core_version_code=23
|
||||||
project.ext.fluentui_listitem_version_code=23
|
project.ext.fluentui_listitem_version_code=23
|
||||||
project.ext.fluentui_tablayout_version_code=23
|
project.ext.fluentui_tablayout_version_code=23
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
|
@ -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')
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
~ Licensed under the MIT License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest
|
||||||
|
package="com.microsoft.fluentui.controls" />
|
|
@ -1,4 +1,4 @@
|
||||||
package com.microsoft.fluentui.button
|
package com.microsoft.fluentui.controls
|
||||||
|
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
@ -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> =
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.microsoft.fluentui.button
|
package com.microsoft.fluentui.controls
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
|
@ -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
|
||||||
|
}
|
|
@ -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<Float>(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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.microsoft.fluentui.button
|
package com.microsoft.fluentui.controls
|
||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.interaction.InteractionSource
|
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.ControlToken
|
||||||
import com.microsoft.fluentui.theme.token.StateBorderStroke
|
import com.microsoft.fluentui.theme.token.StateBorderStroke
|
||||||
import com.microsoft.fluentui.theme.token.StateColor
|
import com.microsoft.fluentui.theme.token.StateColor
|
||||||
import com.microsoft.fluentui.theme.token.controlTokens.ButtonInfo
|
import com.microsoft.fluentui.theme.token.controlTokens.*
|
||||||
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 java.security.InvalidParameterException
|
import java.security.InvalidParameterException
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -53,6 +50,9 @@ fun backgroundColor(
|
||||||
when (tokens) {
|
when (tokens) {
|
||||||
is ButtonTokens -> tokens.backgroundColor(info as ButtonInfo)
|
is ButtonTokens -> tokens.backgroundColor(info as ButtonInfo)
|
||||||
is FABTokens -> tokens.backgroundColor(info as FABInfo)
|
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()
|
else -> throw InvalidParameterException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,9 @@ fun iconColor(
|
||||||
when (tokens) {
|
when (tokens) {
|
||||||
is ButtonTokens -> tokens.iconColor(info as ButtonInfo)
|
is ButtonTokens -> tokens.iconColor(info as ButtonInfo)
|
||||||
is FABTokens -> tokens.iconColor(info as FABInfo)
|
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()
|
else -> throw InvalidParameterException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +107,7 @@ fun borderStroke(
|
||||||
when (tokens) {
|
when (tokens) {
|
||||||
is ButtonTokens -> tokens.borderStroke(info as ButtonInfo)
|
is ButtonTokens -> tokens.borderStroke(info as ButtonInfo)
|
||||||
is FABTokens -> tokens.borderStroke(info as FABInfo)
|
is FABTokens -> tokens.borderStroke(info as FABInfo)
|
||||||
|
is CheckBoxTokens -> tokens.borderStroke(info as CheckBoxInfo)
|
||||||
else -> throw InvalidParameterException()
|
else -> throw InvalidParameterException()
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,15 +63,15 @@ object AppThemeController : ViewModel() {
|
||||||
var aliasTokens: MutableLiveData<AliasTokens> = MutableLiveData(AliasTokens())
|
var aliasTokens: MutableLiveData<AliasTokens> = MutableLiveData(AliasTokens())
|
||||||
var controlTokens: MutableLiveData<ControlTokens> = MutableLiveData(ControlTokens())
|
var controlTokens: MutableLiveData<ControlTokens> = MutableLiveData(ControlTokens())
|
||||||
|
|
||||||
fun onGlobalChanged(overrideGlobalTokens: GlobalTokens) {
|
fun updateGlobalTokens(overrideGlobalTokens: GlobalTokens) {
|
||||||
globalTokens.value = overrideGlobalTokens
|
globalTokens.value = overrideGlobalTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onAliasChanged(overrideAliasTokens: AliasTokens) {
|
fun updateAliasTokens(overrideAliasTokens: AliasTokens) {
|
||||||
aliasTokens.value = overrideAliasTokens
|
aliasTokens.value = overrideAliasTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onControlChanged(overrideControlTokens: ControlTokens) {
|
fun updateControlTokens(overrideControlTokens: ControlTokens) {
|
||||||
controlTokens.value = overrideControlTokens
|
controlTokens.value = overrideControlTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
package com.microsoft.fluentui.theme.token
|
package com.microsoft.fluentui.theme.token
|
||||||
|
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import com.microsoft.fluentui.theme.token.controlTokens.ButtonTokens
|
import com.microsoft.fluentui.theme.token.controlTokens.*
|
||||||
import com.microsoft.fluentui.theme.token.controlTokens.FABTokens
|
|
||||||
|
|
||||||
interface ControlInfo
|
interface ControlInfo
|
||||||
|
|
||||||
|
@ -18,6 +17,9 @@ class ControlTokens {
|
||||||
enum class ControlType {
|
enum class ControlType {
|
||||||
Button,
|
Button,
|
||||||
FloatingActionButton,
|
FloatingActionButton,
|
||||||
|
ToggleSwitch,
|
||||||
|
CheckBox,
|
||||||
|
RadioButton
|
||||||
}
|
}
|
||||||
|
|
||||||
val tokens: TokenSet<ControlType, ControlToken> by lazy {
|
val tokens: TokenSet<ControlType, ControlToken> by lazy {
|
||||||
|
@ -25,6 +27,9 @@ class ControlTokens {
|
||||||
when (token) {
|
when (token) {
|
||||||
ControlType.Button -> ButtonTokens()
|
ControlType.Button -> ButtonTokens()
|
||||||
ControlType.FloatingActionButton -> FABTokens()
|
ControlType.FloatingActionButton -> FABTokens()
|
||||||
|
ControlType.ToggleSwitch -> ToggleSwitchTokens()
|
||||||
|
ControlType.CheckBox -> CheckBoxTokens()
|
||||||
|
ControlType.RadioButton -> RadioButtonTokens()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -34,20 +34,6 @@ android {
|
||||||
}
|
}
|
||||||
productFlavors {
|
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 ->
|
gradle.taskGraph.whenReady { taskGraph ->
|
||||||
|
@ -71,11 +57,6 @@ dependencies {
|
||||||
implementation "com.splitwise:tokenautocomplete:$tokenautocompleteVersion"
|
implementation "com.splitwise:tokenautocomplete:$tokenautocompleteVersion"
|
||||||
implementation "com.microsoft.device:dualscreen-layout:$duoVersion"
|
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"
|
testImplementation "junit:junit:$junitVersion"
|
||||||
androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
|
androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||||
|
|
|
@ -13,3 +13,4 @@ include ':fluentui_tablayout'
|
||||||
include ':fluentui_others'
|
include ':fluentui_others'
|
||||||
include ':FluentUI'
|
include ':FluentUI'
|
||||||
include ':fluentui_ccb'
|
include ':fluentui_ccb'
|
||||||
|
include ':fluentui_controls'
|
||||||
|
|
Загрузка…
Ссылка в новой задаче