Browse Source

COMPOSE-356 Support light theme for codeviewer example (#3853)

https://youtrack.jetbrains.com/issue/COMPOSE-356/Add-Dark-theme-to-codeviewer-examples

The MR does the following:
- Enables dark theme for iOS, Android and Desktop platforms
- Expands compose view to fullscreen and uses window padding to adjust
content location
- Adjusts toolbar clock style to application theme on mobile platforms
pull/3864/head
Andrei Salavei 1 year ago committed by GitHub
parent
commit
3f4a85491e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      examples/codeviewer/androidApp/src/androidMain/AndroidManifest.xml
  2. 2
      examples/codeviewer/androidApp/src/androidMain/assets/data/EditorView.kt
  3. 5
      examples/codeviewer/androidApp/src/androidMain/kotlin/org/jetbrains/codeviewer/MainActivity.kt
  4. 6
      examples/codeviewer/androidApp/src/androidMain/res/values-night/styles.xml
  5. 11
      examples/codeviewer/androidApp/src/androidMain/res/values/styles.xml
  6. 7
      examples/codeviewer/iosApp/iosApp/ContentView.swift
  7. 62
      examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/CodeViewerView.kt
  8. 19
      examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/MainView.kt
  9. 88
      examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/common/Theme.kt
  10. 4
      examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/editor/EditorTabsView.kt
  11. 16
      examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/editor/EditorView.kt
  12. 15
      examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/statusbar/StatusBar.kt
  13. 4
      examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/util/VerticalSplittable.kt
  14. 2
      examples/codeviewer/shared/src/iosMain/resources/EditorView.kt

4
examples/codeviewer/androidApp/src/androidMain/AndroidManifest.xml

@ -7,11 +7,11 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"> android:theme="@style/Theme.Application">
<activity <activity
android:exported="true" android:exported="true"
android:name="MainActivity" android:name="MainActivity"
> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

2
examples/codeviewer/androidApp/src/androidMain/assets/data/EditorView.kt

@ -48,7 +48,7 @@ fun EditorView(model: Editor, settings: Settings) = key(model) {
) )
.width(1.dp) .width(1.dp)
.fillMaxHeight() .fillMaxHeight()
.background(AppTheme.colors.backgroundLight) .background(AppTheme.colors.codeGuide)
) )
} }
} else { } else {

5
examples/codeviewer/androidApp/src/androidMain/kotlin/org/jetbrains/codeviewer/MainActivity.kt

@ -1,10 +1,11 @@
package org.jetbrains.codeviewer package org.jetbrains.codeviewer
import MainView
import android.os.Bundle import android.os.Bundle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import org.jetbrains.codeviewer.platform._HomeFolder import org.jetbrains.codeviewer.platform._HomeFolder
import MainView
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -14,6 +15,8 @@ class MainActivity : AppCompatActivity() {
copyAssets() copyAssets()
_HomeFolder = filesDir _HomeFolder = filesDir
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent { setContent {
MainView() MainView()
} }

6
examples/codeviewer/androidApp/src/androidMain/res/values-night/styles.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Application" parent="Theme.General">
<item name="android:windowLightStatusBar">false</item>
</style>
</resources>

11
examples/codeviewer/androidApp/src/androidMain/res/values/styles.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.General" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Application" parent="Theme.General">
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>

7
examples/codeviewer/iosApp/iosApp/ContentView.swift

@ -4,11 +4,8 @@ import shared
struct ContentView: View { struct ContentView: View {
var body: some View { var body: some View {
ZStack { ComposeView()
Color(#colorLiteral(red: 0.235, green: 0.247, blue: 0.255, alpha: 1)).ignoresSafeArea(.all) .ignoresSafeArea(.all)
ComposeView()
.ignoresSafeArea(.all, edges: .bottom) // Compose has own keyboard handler
}.preferredColorScheme(.dark)
} }
} }

62
examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/CodeViewerView.kt

@ -5,13 +5,25 @@ import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor import androidx.compose.material.LocalContentColor
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForward import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
@ -38,32 +50,36 @@ fun CodeViewerView(model: CodeViewer) {
).value ).value
} }
VerticalSplittable( Box(Modifier
Modifier.fillMaxSize(), .windowInsetsPadding(WindowInsets.safeDrawing)
panelState.splitter,
onResize = {
panelState.expandedSize =
(panelState.expandedSize + it).coerceAtLeast(panelState.expandedSizeMin)
}
) { ) {
ResizablePanel(Modifier.width(animatedSize).fillMaxHeight(), panelState) { VerticalSplittable(
Column { Modifier.fillMaxSize(),
FileTreeViewTabView() panelState.splitter,
FileTreeView(model.fileTree) onResize = {
panelState.expandedSize =
(panelState.expandedSize + it).coerceAtLeast(panelState.expandedSizeMin)
}
) {
ResizablePanel(Modifier.width(animatedSize).fillMaxHeight(), panelState) {
Column {
FileTreeViewTabView()
FileTreeView(model.fileTree)
}
} }
}
Box { Box {
if (model.editors.active != null) { if (model.editors.active != null) {
Column(Modifier.fillMaxSize()) { Column(Modifier.fillMaxSize()) {
EditorTabsView(model.editors) EditorTabsView(model.editors)
Box(Modifier.weight(1f)) { Box(Modifier.weight(1f)) {
EditorView(model.editors.active!!, model.settings) EditorView(model.editors.active!!, model.settings)
}
StatusBar(model.settings)
} }
StatusBar(model.settings) } else {
EditorEmptyView()
} }
} else {
EditorEmptyView()
} }
} }
} }

19
examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/MainView.kt

@ -1,13 +1,16 @@
package org.jetbrains.codeviewer.ui package org.jetbrains.codeviewer.ui
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.text.selection.DisableSelection import androidx.compose.foundation.text.selection.DisableSelection
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import org.jetbrains.codeviewer.platform.HomeFolder import org.jetbrains.codeviewer.platform.HomeFolder
import org.jetbrains.codeviewer.ui.common.AppTheme import org.jetbrains.codeviewer.ui.common.LocalTheme
import org.jetbrains.codeviewer.ui.common.Settings import org.jetbrains.codeviewer.ui.common.Settings
import org.jetbrains.codeviewer.ui.common.Theme
import org.jetbrains.codeviewer.ui.editor.Editors import org.jetbrains.codeviewer.ui.editor.Editors
import org.jetbrains.codeviewer.ui.filetree.FileTree import org.jetbrains.codeviewer.ui.filetree.FileTree
@ -24,11 +27,17 @@ fun MainView() {
} }
DisableSelection { DisableSelection {
MaterialTheme( val theme = if (isSystemInDarkTheme()) Theme.dark else Theme.light
colors = AppTheme.colors.material
CompositionLocalProvider(
LocalTheme provides theme,
) { ) {
Surface { MaterialTheme(
CodeViewerView(codeViewer) colors = theme.materialColors
) {
Surface {
CodeViewerView(codeViewer)
}
} }
} }
} }

88
examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/common/Theme.kt

@ -1,32 +1,76 @@
package org.jetbrains.codeviewer.ui.common package org.jetbrains.codeviewer.ui.common
import androidx.compose.material.Colors
import androidx.compose.material.darkColors import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
object AppTheme { @Immutable
val colors: Colors = Colors() data class Theme(
val materialColors: Colors,
val colors: ExtendedColors,
val code: CodeStyle
) {
@Immutable
class ExtendedColors(
val codeGuide: Color
)
val code: Code = Code() @Immutable
data class CodeStyle(
val simple: SpanStyle,
val value: SpanStyle,
val keyword: SpanStyle,
val punctuation: SpanStyle,
val annotation: SpanStyle,
val comment: SpanStyle
)
class Colors( companion object {
val backgroundDark: Color = Color(0xFF2B2B2B), val dark = Theme(
val backgroundMedium: Color = Color(0xFF3C3F41), materialColors = darkColors(
val backgroundLight: Color = Color(0xFF4E5254), background = Color(0xFF2B2B2B),
surface = Color(0xFF3C3F41)
),
colors = ExtendedColors(
codeGuide = Color(0xFF4E5254)
),
code = CodeStyle(
simple = SpanStyle(Color(0xFFA9B7C6)),
value = SpanStyle(Color(0xFF6897BB)),
keyword = SpanStyle(Color(0xFFCC7832)),
punctuation = SpanStyle(Color(0xFFA1C17E)),
annotation = SpanStyle(Color(0xFFBBB529)),
comment = SpanStyle(Color(0xFF808080))
)
)
val material: androidx.compose.material.Colors = darkColors( val light = Theme(
background = backgroundDark, materialColors = lightColors(
surface = backgroundMedium, background = Color(0xFFF5F5F5),
primary = Color.White surface = Color(0xFFFFFFFF)
), ),
) colors = ExtendedColors(
codeGuide = Color(0xFF8E9294)
),
code = CodeStyle(
simple = SpanStyle(Color(0xFF000000)),
value = SpanStyle(Color(0xFF4A86E8)),
keyword = SpanStyle(Color(0xFF000080)),
punctuation = SpanStyle(Color(0xFFA1A1A1)),
annotation = SpanStyle(Color(0xFFBBB529)),
comment = SpanStyle(Color(0xFF808080))
)
)
}
}
class Code( val LocalTheme = staticCompositionLocalOf { Theme.dark }
val simple: SpanStyle = SpanStyle(Color(0xFFA9B7C6)),
val value: SpanStyle = SpanStyle(Color(0xFF6897BB)), val AppTheme
val keyword: SpanStyle = SpanStyle(Color(0xFFCC7832)), @Composable
val punctuation: SpanStyle = SpanStyle(Color(0xFFA1C17E)), get() = LocalTheme.current
val annotation: SpanStyle = SpanStyle(Color(0xFFBBB529)),
val comment: SpanStyle = SpanStyle(Color(0xFF808080))
)
}

4
examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/editor/EditorTabsView.kt

@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -21,7 +22,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import org.jetbrains.codeviewer.ui.common.AppTheme
@Composable @Composable
fun EditorTabsView(model: Editors) = Row(Modifier.horizontalScroll(rememberScrollState())) { fun EditorTabsView(model: Editors) = Row(Modifier.horizontalScroll(rememberScrollState())) {
@ -33,7 +33,7 @@ fun EditorTabsView(model: Editors) = Row(Modifier.horizontalScroll(rememberScrol
@Composable @Composable
fun EditorTabView(model: Editor) = Surface( fun EditorTabView(model: Editor) = Surface(
color = if (model.isActive) { color = if (model.isActive) {
AppTheme.colors.backgroundDark MaterialTheme.colors.background
} else { } else {
Color.Transparent Color.Transparent
} }

16
examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/editor/EditorView.kt

@ -1,13 +1,22 @@
package org.jetbrains.codeviewer.ui.editor package org.jetbrains.codeviewer.ui.editor
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.selection.DisableSelection import androidx.compose.foundation.text.selection.DisableSelection
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.LocalContentColor import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -37,7 +46,7 @@ fun EditorView(model: Editor, settings: Settings) = key(model) {
SelectionContainer { SelectionContainer {
Surface( Surface(
Modifier.fillMaxSize(), Modifier.fillMaxSize(),
color = AppTheme.colors.backgroundDark, color = MaterialTheme.colors.background,
) { ) {
val lines by loadableScoped(model.lines) val lines by loadableScoped(model.lines)
@ -51,7 +60,7 @@ fun EditorView(model: Editor, settings: Settings) = key(model) {
) )
.width(1.dp) .width(1.dp)
.fillMaxHeight() .fillMaxHeight()
.background(AppTheme.colors.backgroundLight) .background(AppTheme.colors.codeGuide)
) )
} }
} else { } else {
@ -143,6 +152,7 @@ private fun LineContent(content: Editor.Content, modifier: Modifier, settings: S
softWrap = false softWrap = false
) )
@Composable
private fun codeString(str: String) = buildAnnotatedString { private fun codeString(str: String) = buildAnnotatedString {
withStyle(AppTheme.code.simple) { withStyle(AppTheme.code.simple) {
val strFormatted = str.replace("\t", " ") val strFormatted = str.replace("\t", " ")

15
examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/ui/statusbar/StatusBar.kt

@ -1,6 +1,12 @@
package org.jetbrains.codeviewer.ui.statusbar package org.jetbrains.codeviewer.ui.statusbar
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material.LocalContentColor import androidx.compose.material.LocalContentColor
import androidx.compose.material.Slider import androidx.compose.material.Slider
import androidx.compose.material.Text import androidx.compose.material.Text
@ -9,7 +15,11 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.unit.sp
import org.jetbrains.codeviewer.ui.common.Settings import org.jetbrains.codeviewer.ui.common.Settings
private val MinFontSize = 6.sp private val MinFontSize = 6.sp
@ -18,7 +28,6 @@ private val MaxFontSize = 40.sp
@Composable @Composable
fun StatusBar(settings: Settings) = Box( fun StatusBar(settings: Settings) = Box(
Modifier Modifier
.padding(16.dp, 4.dp, 16.dp, 16.dp)
.height(32.dp) .height(32.dp)
.fillMaxWidth() .fillMaxWidth()
) { ) {

4
examples/codeviewer/shared/src/commonMain/kotlin/org/jetbrains/codeviewer/util/VerticalSplittable.kt

@ -7,6 +7,7 @@ import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -19,7 +20,6 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.jetbrains.codeviewer.platform.cursorForHorizontalResize import org.jetbrains.codeviewer.platform.cursorForHorizontalResize
import org.jetbrains.codeviewer.ui.common.AppTheme
@Composable @Composable
fun VerticalSplittable( fun VerticalSplittable(
@ -60,7 +60,7 @@ class SplitterState {
fun VerticalSplitter( fun VerticalSplitter(
splitterState: SplitterState, splitterState: SplitterState,
onResize: (delta: Dp) -> Unit, onResize: (delta: Dp) -> Unit,
color: Color = AppTheme.colors.backgroundDark color: Color = MaterialTheme.colors.background
) = Box { ) = Box {
val density = LocalDensity.current val density = LocalDensity.current
Box( Box(

2
examples/codeviewer/shared/src/iosMain/resources/EditorView.kt

@ -48,7 +48,7 @@ fun EditorView(model: Editor, settings: Settings) = key(model) {
) )
.width(1.dp) .width(1.dp)
.fillMaxHeight() .fillMaxHeight()
.background(AppTheme.colors.backgroundLight) .background(AppTheme.colors.codeGuide)
) )
} }
} else { } else {

Loading…
Cancel
Save