Browse Source

Examples for blogpost

igor.demin/retake-screenshots
Igor Demin 1 year ago
parent
commit
ccb9407360
  1. 78
      examples/imageviewer/desktopApp/src/jvmMain/kotlin/example/imageviewer/Main.kt
  2. 4
      examples/imageviewer/iosApp/iosApp/ContentView.swift
  3. 1
      examples/imageviewer/shared/build.gradle.kts
  4. 194
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt
  5. 2
      examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt

78
examples/imageviewer/desktopApp/src/jvmMain/kotlin/example/imageviewer/Main.kt

@ -1,8 +1,86 @@
package example.imageviewer package example.imageviewer
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposePanel
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.window.application import androidx.compose.ui.window.application
import example.imageviewer.view.ImageViewerDesktop import example.imageviewer.view.ImageViewerDesktop
import java.awt.Color.CYAN
import java.awt.Graphics
import java.awt.Graphics2D
import javax.swing.JComponent
import javax.swing.JFrame
import javax.swing.JLayeredPane
import javax.swing.JLayeredPane.DEFAULT_LAYER
import javax.swing.JLayeredPane.POPUP_LAYER
import javax.swing.JPanel
import javax.swing.JSplitPane
import javax.swing.OverlayLayout
import javax.swing.SwingUtilities
fun main() = application { fun main() = application {
ImageViewerDesktop() ImageViewerDesktop()
} }
fun main2() {
System.setProperty("compose.swing.render.on.graphics", "true")
SwingUtilities.invokeLater {
val composePanel = ComposePanel().apply {
setContent {
Box(modifier = Modifier.background(Color.Black).fillMaxSize())
}
}
val popup = object : JComponent() {
init {
isOpaque = false
}
override fun paintComponent(g: Graphics?) {
val scratchGraphics = g?.create() as? Graphics2D ?: return
try {
scratchGraphics.color = java.awt.Color.WHITE
scratchGraphics.fillRoundRect(5, 5, 90, 50, 16, 16)
scratchGraphics.color = java.awt.Color.BLACK
scratchGraphics.drawString("Popup", 30, 30)
} finally {
scratchGraphics.dispose()
}
}
}
val rightPanel = JLayeredPane()
rightPanel.layout = OverlayLayout(rightPanel)
rightPanel.add(composePanel)
rightPanel.add(popup)
rightPanel.setLayer(composePanel, DEFAULT_LAYER)
rightPanel.setLayer(popup, POPUP_LAYER)
val leftPanel = JPanel().apply { background = CYAN }
val splitter = JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
true,
leftPanel,
rightPanel
).apply {
setDividerLocation(500)
}
JFrame().apply {
add(splitter)
setSize(600, 600)
isVisible = true
}
}
}

4
examples/imageviewer/iosApp/iosApp/ContentView.swift

@ -7,10 +7,6 @@ struct ContentView: View {
ZStack { ZStack {
ComposeView() ComposeView()
.ignoresSafeArea(.all) // Compose has own keyboard handler .ignoresSafeArea(.all) // Compose has own keyboard handler
VStack {
gradient.ignoresSafeArea(edges: .top).frame(height: 0)
Spacer()
}
}.preferredColorScheme(.dark) }.preferredColorScheme(.dark)
} }
} }

1
examples/imageviewer/shared/build.gradle.kts

@ -31,6 +31,7 @@ kotlin {
implementation(compose.runtime) implementation(compose.runtime)
implementation(compose.foundation) implementation(compose.foundation)
implementation(compose.material) implementation(compose.material)
implementation(compose.material3)
//implementation(compose.materialIconsExtended) // TODO not working on iOS for now //implementation(compose.materialIconsExtended) // TODO not working on iOS for now
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
implementation(compose.components.resources) implementation(compose.components.resources)

194
examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt

@ -1,13 +1,34 @@
package example.imageviewer package example.imageviewer
import androidx.compose.animation.* import androidx.compose.foundation.background
import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.* import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.saveable.listSaver import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.foundation.layout.padding
import example.imageviewer.model.* import androidx.compose.foundation.layout.safeContent
import example.imageviewer.view.* import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
enum class ExternalImageViewerEvent { enum class ExternalImageViewerEvent {
Next, Next,
@ -19,106 +40,103 @@ enum class ExternalImageViewerEvent {
fun ImageViewerCommon( fun ImageViewerCommon(
dependencies: Dependencies dependencies: Dependencies
) { ) {
CompositionLocalProvider( MaterialTheme {
LocalLocalization provides dependencies.localization, Box(Modifier.fillMaxSize().background(Color.White)) {
LocalNotification provides dependencies.notification, CommonDialog()
LocalImageProvider provides dependencies.imageProvider, //NaturalScrolling()
LocalInternalEvents provides dependencies.externalEvents, //DynamicType()
LocalSharePicture provides dependencies.sharePicture, //TextFieldCapitalization()
) { //TestFramework()
ImageViewerWithProvidedDependencies(dependencies.pictures) }
} }
} }
@OptIn(ExperimentalAnimationApi::class)
@Composable @Composable
fun ImageViewerWithProvidedDependencies( fun CommonDialog() {
pictures: SnapshotStateList<PictureData> Box(Modifier.fillMaxSize().windowInsetsPadding(WindowInsets.safeContent)) {
) { var isDialogOpen by remember { mutableStateOf(false) }
// rememberSaveable is required to properly handle Android configuration changes (such as device rotation) Button(onClick = {
val selectedPictureIndex = rememberSaveable { mutableStateOf(0) } isDialogOpen = true
val navigationStack = rememberSaveable( }) {
saver = listSaver<NavigationStack<Page>, Page>( Text("Open")
restore = { NavigationStack(*it.toTypedArray()) }, }
save = { it.stack }, if (isDialogOpen) {
) AlertDialog(
) { onDismissRequest = { isDialogOpen = false },
NavigationStack(GalleryPage()) confirmButton = {
Button(onClick = { isDialogOpen = false }) {
Text("OK")
} }
},
val externalEvents = LocalInternalEvents.current title = { Text("Alert Dialog") },
LaunchedEffect(Unit) { text = { Text("Lorem Ipsum") },
externalEvents.collect { )
if (it == ExternalImageViewerEvent.ReturnBack) {
navigationStack.back()
} }
} }
} }
AnimatedContent(targetState = navigationStack.lastWithIndex(), transitionSpec = {
val previousIdx = initialState.index @Composable
val currentIdx = targetState.index fun NaturalScrolling() {
val multiplier = if (previousIdx < currentIdx) 1 else -1 val items = (1..30).map { "Item $it" }
if (initialState.value is GalleryPage && targetState.value is MemoryPage) { LazyColumn {
fadeIn() with fadeOut(tween(durationMillis = 500, 500)) items(items) {
} else if (initialState.value is MemoryPage && targetState.value is GalleryPage) { Text(
fadeIn() with fadeOut(tween(delayMillis = 150)) text = it,
} else { fontSize = 30.sp,
slideInHorizontally { w -> multiplier * w } with modifier = Modifier.padding(start = 20.dp)
slideOutHorizontally { w -> multiplier * -1 * w } )
}
}) { (_, page) ->
when (page) {
is GalleryPage -> {
GalleryScreen(
pictures = pictures,
selectedPictureIndex = selectedPictureIndex,
onClickPreviewPicture = { previewPictureIndex ->
navigationStack.push(MemoryPage(previewPictureIndex))
} }
) {
navigationStack.push(CameraPage())
} }
} }
is FullScreenPage -> { @Composable
FullscreenImageScreen( fun DynamicType() {
picture = pictures[page.pictureIndex], Text("This is some sample text", fontSize = 30.sp)
back = {
navigationStack.back()
} }
@Composable
fun TextFieldCapitalization() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences,
autoCorrect = false,
keyboardType = KeyboardType.Ascii,
),
) )
} }
is MemoryPage -> { @Composable
MemoryScreen( fun TestFramework(){
pictures = pictures, var searchText by remember { mutableStateOf("cats") }
memoryPage = page, val searchHistory = remember { mutableStateListOf<String>() }
onSelectRelatedMemory = { pictureIndex ->
navigationStack.push(MemoryPage(pictureIndex))
}, Column(modifier = Modifier.padding(30.dp)) {
onBack = { resetNavigation -> TextField(
if (resetNavigation) { modifier = Modifier.testTag("searchText"),
selectedPictureIndex.value = 0 value = searchText,
navigationStack.reset() onValueChange = {
} else { searchText = it
navigationStack.back()
} }
},
onHeaderClick = { pictureIndex ->
navigationStack.push(FullScreenPage(pictureIndex))
},
) )
Button(
modifier = Modifier.testTag("search"),
onClick = {
searchHistory.add("You searched for: $searchText")
} }
) {
is CameraPage -> { Text("Search")
CameraScreen( }
onBack = { resetSelectedPicture -> LazyColumn {
if (resetSelectedPicture) { items(searchHistory) {
selectedPictureIndex.value = 0 Text(
} text = it,
navigationStack.back() fontSize = 20.sp,
}, modifier = Modifier.padding(start = 10.dp).testTag("attempt")
) )
} }
} }

2
examples/imageviewer/shared/src/iosMain/kotlin/example/imageviewer/ImageViewer.ios.kt

@ -27,7 +27,6 @@ internal fun ImageViewerIos() {
getDependencies(ioScope, toastState) getDependencies(ioScope, toastState)
} }
ImageViewerTheme {
Surface( Surface(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
@ -37,7 +36,6 @@ internal fun ImageViewerIos() {
Toast(toastState) Toast(toastState)
} }
} }
}
fun getDependencies(ioScope: CoroutineScope, toastState: MutableState<ToastState>) = fun getDependencies(ioScope: CoroutineScope, toastState: MutableState<ToastState>) =
object : Dependencies() { object : Dependencies() {

Loading…
Cancel
Save