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. 212
      examples/imageviewer/shared/src/commonMain/kotlin/example/imageviewer/ImageViewer.common.kt
  5. 16
      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
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 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 {
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 {
ComposeView()
.ignoresSafeArea(.all) // Compose has own keyboard handler
VStack {
gradient.ignoresSafeArea(edges: .top).frame(height: 0)
Spacer()
}
}.preferredColorScheme(.dark)
}
}

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

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

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

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

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

@ -27,15 +27,13 @@ internal fun ImageViewerIos() {
getDependencies(ioScope, toastState)
}
ImageViewerTheme {
Surface(
modifier = Modifier.fillMaxSize()
) {
ImageViewerCommon(
dependencies = dependencies
)
Toast(toastState)
}
Surface(
modifier = Modifier.fillMaxSize()
) {
ImageViewerCommon(
dependencies = dependencies
)
Toast(toastState)
}
}

Loading…
Cancel
Save