@@ -2,16 +2,15 @@ package com.codekotliners.memify.features.create.presentation.ui
22
33import android.content.Intent
44import androidx.compose.animation.AnimatedVisibility
5+ import androidx.compose.animation.core.animateFloatAsState
56import androidx.compose.foundation.Image
67import androidx.compose.foundation.background
78import androidx.compose.foundation.clickable
89import androidx.compose.foundation.gestures.rememberTransformableState
910import androidx.compose.foundation.gestures.transformable
10- import androidx.compose.foundation.layout.Arrangement
1111import androidx.compose.foundation.layout.Box
1212import androidx.compose.foundation.layout.Column
1313import androidx.compose.foundation.layout.PaddingValues
14- import androidx.compose.foundation.layout.Row
1514import androidx.compose.foundation.layout.Spacer
1615import androidx.compose.foundation.layout.WindowInsets
1716import androidx.compose.foundation.layout.aspectRatio
@@ -20,9 +19,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
2019import androidx.compose.foundation.layout.height
2120import androidx.compose.foundation.layout.padding
2221import androidx.compose.foundation.layout.size
22+ import androidx.compose.foundation.layout.wrapContentHeight
2323import androidx.compose.foundation.layout.wrapContentSize
24- import androidx.compose.foundation.lazy.LazyColumn
25- import androidx.compose.foundation.shape.CircleShape
2624import androidx.compose.foundation.shape.RoundedCornerShape
2725import androidx.compose.material.icons.Icons
2826import androidx.compose.material.icons.filled.KeyboardArrowDown
@@ -58,7 +56,6 @@ import androidx.compose.runtime.setValue
5856import androidx.compose.ui.Alignment
5957import androidx.compose.ui.Modifier
6058import androidx.compose.ui.draw.drawWithContent
61- import androidx.compose.ui.geometry.Offset
6259import androidx.compose.ui.graphics.Color
6360import androidx.compose.ui.graphics.ImageBitmap
6461import androidx.compose.ui.graphics.asAndroidBitmap
@@ -68,7 +65,6 @@ import androidx.compose.ui.graphics.layer.drawLayer
6865import androidx.compose.ui.graphics.rememberGraphicsLayer
6966import androidx.compose.ui.layout.ContentScale
7067import androidx.compose.ui.platform.LocalContext
71- import androidx.compose.ui.res.painterResource
7268import androidx.compose.ui.res.stringResource
7369import androidx.compose.ui.text.font.Font
7470import androidx.compose.ui.text.font.FontFamily
@@ -87,7 +83,6 @@ import com.codekotliners.memify.core.ui.components.AppScaffold
8783import com.codekotliners.memify.features.create.presentation.ui.components.ActionsRow
8884import com.codekotliners.memify.features.create.presentation.ui.components.DrawingRow
8985import com.codekotliners.memify.features.create.presentation.ui.components.EditingCanvasElements
90- import com.codekotliners.memify.features.create.presentation.ui.components.InstrumentsTextBox
9186import com.codekotliners.memify.features.create.presentation.ui.components.TextEditingRow
9287import com.codekotliners.memify.features.create.presentation.ui.components.TextInputDialog
9388import com.codekotliners.memify.features.create.presentation.viewmodel.CanvasViewModel
@@ -181,15 +176,21 @@ private fun CreateScreenBottomSheet(
181176 val bitmapState = remember { mutableStateOf<ImageBitmap ?>(null ) }
182177 val isPublishing by viewModelViewer.isPublishing.collectAsState()
183178
179+ var scale by remember { mutableFloatStateOf(1f ) }
180+
184181 BottomSheetScaffold (
185182 topBar = {
186183 CreateScreenTopBar (
187184 scrollBehavior,
188185 onMenuClick = {
189- coroutineScope.launch {
190- showImageViewer.value = true
191- val bitmapCompose = graphicsLayer.toImageBitmap()
192- bitmapState.value = bitmapCompose
186+ if (scale == 1f ) {
187+ coroutineScope.launch {
188+ showImageViewer.value = true
189+ val bitmapCompose = graphicsLayer.toImageBitmap()
190+ bitmapState.value = bitmapCompose
191+ }
192+ } else {
193+ scale = 1f
193194 }
194195 },
195196 )
@@ -206,13 +207,20 @@ private fun CreateScreenBottomSheet(
206207 coroutineScope.launch {
207208 bottomSheetState.partialExpand()
208209 }
210+ viewModel.clearCanvas()
209211 },
210212 )
211213 },
212214 sheetPeekHeight = 58 .dp,
213215 sheetSwipeEnabled = true ,
214216 ) { innerPadding ->
215- CreateScreenContent (innerPadding, viewModel, graphicsLayer)
217+ CreateScreenContent (
218+ innerPadding,
219+ viewModel,
220+ graphicsLayer,
221+ scale,
222+ onScaleChange = { newScale -> scale = newScale },
223+ )
216224
217225 if (showImageViewer.value && bitmapState.value != null ) {
218226 ImagePreviewDialog (
@@ -342,20 +350,22 @@ private fun CreateScreenTopBar(scrollBehavior: TopAppBarScrollBehavior, onMenuCl
342350}
343351
344352@Composable
345- private fun CreateScreenContent (innerPadding : PaddingValues , viewModel : CanvasViewModel , graphicsLayer : GraphicsLayer ) {
346- LazyColumn (
353+ private fun CreateScreenContent (
354+ innerPadding : PaddingValues ,
355+ viewModel : CanvasViewModel ,
356+ graphicsLayer : GraphicsLayer ,
357+ scale : Float ,
358+ onScaleChange : (Float ) -> Unit ,
359+ ) {
360+ Column (
347361 modifier =
348362 Modifier
349363 .fillMaxSize()
350364 .padding(innerPadding)
351365 .background(MaterialTheme .colorScheme.background),
352366 horizontalAlignment = Alignment .CenterHorizontally ,
353- contentPadding = PaddingValues (bottom = 80 .dp),
354367 ) {
355- item { ActionsRow (viewModel) }
356- item {
357- InteractiveCanvas (viewModel, graphicsLayer)
358- }
368+ InteractiveCanvas (viewModel, graphicsLayer, scale, onScaleChange)
359369 }
360370}
361371
@@ -385,7 +395,12 @@ fun BottomSheetHandle(bottomSheetState: SheetState) {
385395}
386396
387397@Composable
388- private fun InteractiveCanvas (viewModel : CanvasViewModel , graphicsLayer : GraphicsLayer ) {
398+ private fun InteractiveCanvas (
399+ viewModel : CanvasViewModel ,
400+ graphicsLayer : GraphicsLayer ,
401+ scale : Float ,
402+ onScaleChange : (Float ) -> Unit ,
403+ ) {
389404 val context = LocalContext .current
390405 val painter =
391406 rememberAsyncImagePainter(
@@ -405,107 +420,32 @@ private fun InteractiveCanvas(viewModel: CanvasViewModel, graphicsLayer: Graphic
405420 }
406421 }
407422
408- Column (
409- modifier = Modifier .fillMaxWidth(),
410- horizontalAlignment = Alignment .CenterHorizontally ,
411- verticalArrangement = Arrangement .spacedBy(10 .dp),
412- ) {
413- Tools (viewModel)
414-
415- ImageBox (viewModel, graphicsLayer, painter)
416-
417- if (viewModel.showTextInput) {
418- TextInputDialog (viewModel)
419- }
420-
421- AnimatedVisibility (
422- visible = (viewModel.isPaintingEnabled == false && viewModel.isWritingEnabled == false ),
423- ) {
424- InstrumentsTextBox ()
425- }
426-
427- AnimatedVisibility (visible = viewModel.isPaintingEnabled) {
428- DrawingRow (viewModel)
429- }
430-
431- AnimatedVisibility (visible = viewModel.isWritingEnabled) {
432- TextEditingRow (viewModel)
433- }
434- }
435- }
436-
437- @Composable
438- private fun Tools (viewModel : CanvasViewModel ) {
439- Row (
440- modifier =
441- Modifier
442- .padding(16 .dp)
443- .background(MaterialTheme .colorScheme.surface, CircleShape )
444- .padding(8 .dp),
445- horizontalArrangement = Arrangement .spacedBy(8 .dp),
423+ Box (
424+ modifier = Modifier .fillMaxSize(),
446425 ) {
447- // Состояния
448- val isPaintSelected = viewModel.isPaintingEnabled
449- val isWriteSelected = viewModel.isWritingEnabled
450-
451- // Кнопка Paint
452- IconButton (
453- onClick = {
454- viewModel.isPaintingEnabled = ! viewModel.isPaintingEnabled
455- viewModel.isWritingEnabled = false
456- },
457- modifier =
458- Modifier
459- .size(48 .dp)
460- .background(
461- if (isPaintSelected) {
462- MaterialTheme .colorScheme.primary
463- } else {
464- MaterialTheme .colorScheme.background
465- },
466- CircleShape ,
467- ),
468- ) {
469- Icon (
470- painter = painterResource(R .drawable.baseline_brush_24),
471- contentDescription = " Paint" ,
472- tint =
473- if (isPaintSelected) {
474- MaterialTheme .colorScheme.onPrimary
475- } else {
476- MaterialTheme .colorScheme.onSurface
477- },
478- )
479- }
426+ Box (
427+ modifier = Modifier .fillMaxSize(),
428+ contentAlignment = Alignment .Center ,
429+ ) { ImageBox (viewModel, graphicsLayer, painter, scale, onScaleChange) }
480430
481- // Кнопка Write
482- IconButton (
483- onClick = {
484- viewModel.isWritingEnabled = ! viewModel.isWritingEnabled
485- viewModel.isPaintingEnabled = false
486- },
431+ Column (
487432 modifier =
488433 Modifier
489- .size(48 .dp)
490- .background(
491- if (isWriteSelected) {
492- MaterialTheme .colorScheme.primary
493- } else {
494- MaterialTheme .colorScheme.background
495- },
496- CircleShape ,
497- ),
434+ .align(Alignment .TopCenter )
435+ .fillMaxWidth()
436+ .wrapContentHeight(),
437+ horizontalAlignment = Alignment .CenterHorizontally ,
498438 ) {
499- Icon (
500- painter = painterResource( R .drawable.round_text_fields_24),
501- contentDescription = " Write " ,
502- tint =
503- if (isWriteSelected ) {
504- MaterialTheme .colorScheme.onPrimary
505- } else {
506- MaterialTheme .colorScheme.onSurface
507- },
508- )
439+ ActionsRow (viewModel)
440+ AnimatedVisibility (visible = viewModel.isWritingEnabled) {
441+ TextEditingRow (viewModel)
442+ }
443+ AnimatedVisibility (visible = viewModel.isPaintingEnabled ) {
444+ DrawingRow (viewModel)
445+ }
446+ if (viewModel.showTextInput) {
447+ TextInputDialog (viewModel)
448+ }
509449 }
510450 }
511451}
@@ -515,46 +455,43 @@ private fun ImageBox(
515455 viewModel : CanvasViewModel ,
516456 graphicsLayer : GraphicsLayer ,
517457 painter : AsyncImagePainter ,
458+ scale : Float ,
459+ onScaleChange : (Float ) -> Unit ,
518460) {
519- var scale by remember { mutableFloatStateOf(1f ) }
520- var angle by remember { mutableFloatStateOf(0f ) }
521- var offset by remember { mutableStateOf(Offset .Zero ) }
461+ val animatedScale = animateFloatAsState(targetValue = scale)
522462 val state =
523- rememberTransformableState { scaleChange, offsetChange, rotationChange ->
524- scale * = scaleChange
525- angle + = rotationChange
526- offset + = offsetChange
463+ rememberTransformableState { scaleChange, _, _ ->
464+ onScaleChange(scale * scaleChange)
465+ }
466+
467+ val aspectRatio =
468+ if (viewModel.imageWidth > 0 && viewModel.imageHeight > 0 ) {
469+ viewModel.imageWidth / viewModel.imageHeight
470+ } else {
471+ 1f
527472 }
528473
529474 Box (
530475 modifier =
531476 Modifier
532477 .fillMaxWidth()
533- .aspectRatio(
534- if (viewModel.imageWidth > 0 && viewModel.imageHeight > 0 ) {
535- viewModel.imageWidth / viewModel.imageHeight
536- } else {
537- // Дефолтное соотношение, пока не загрузилось
538- 1f
539- },
540- ).padding(4 .dp)
478+ .aspectRatio(aspectRatio)
479+ .padding(4 .dp)
541480 .drawWithContent {
542481 graphicsLayer.record {
543482 this @drawWithContent.drawContent()
544483 }
545484 drawLayer(graphicsLayer)
546- }.then(
485+ }.clickable(onClick = { onScaleChange(1f ) })
486+ .then(
547487 if (viewModel.isWritingEnabled) {
548488 Modifier .clickable(onClick = { viewModel.startWriting() })
549489 } else {
550490 Modifier
551491 },
552492 ).graphicsLayer(
553- scaleX = scale,
554- scaleY = scale,
555- rotationZ = angle,
556- translationX = offset.x,
557- translationY = offset.y,
493+ scaleX = animatedScale.value,
494+ scaleY = animatedScale.value,
558495 ).transformable(state = state),
559496 ) {
560497 Image (
0 commit comments